diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /filters/krita | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'filters/krita')
122 files changed, 23847 insertions, 0 deletions
diff --git a/filters/krita/Makefile.am b/filters/krita/Makefile.am new file mode 100644 index 00000000..0d8732eb --- /dev/null +++ b/filters/krita/Makefile.am @@ -0,0 +1,34 @@ +if have_openexr +OPENEXR_SUBDIR=openexr +endif + +if include_imagemagick_filter +IMAGEMAGICK_SUBDIR=magick +endif + +if include_graphicsmagick_filter +GRAPHICSMAGICK_SUBDIR=gmagick +endif + +if have_png +PNG_SUBDIR=png +endif + +if include_jpeg_filter +JPEG_SUBDIR=jpeg +endif + +if include_tiff_filter +TIFF_SUBDIR=tiff +endif + +if have_exif +LIBKISEXIF=libkisexif +endif + +if include_PDF +PDF_SUBDIR=pdf +endif + +SUBDIRS = $(IMAGEMAGICK_SUBDIR) $(OPENEXR_SUBDIR) $(PNG_SUBDIR) $(LIBKISEXIF) \ + $(JPEG_SUBDIR) $(TIFF_SUBDIR) raw $(PDF_SUBDIR) $(GRAPHICSMAGICK_SUBDIR) diff --git a/filters/krita/configure.in.in b/filters/krita/configure.in.in new file mode 100644 index 00000000..6eb8d91e --- /dev/null +++ b/filters/krita/configure.in.in @@ -0,0 +1,66 @@ +# Check if the tiff lib is available +AC_FIND_TIFF +AM_CONDITIONAL(have_tiff, test -n "$LIBTIFF") + +AC_FIND_PNG +AM_CONDITIONAL(have_png, test -n "$LIBPNG") + +AC_FIND_JPEG +AM_CONDITIONAL(have_jpeg, test -n "$LIBJPEG") + +#--------------------------------------------------------- +# libexif detection +# taken from libkexif's configure.in.in +#--------------------------------------------------------- + +LIBEXIF=no + +#PKG_CHECK_MODULES(LIBEXIF, libexif >= 0.5.7, , +# [ AC_MSG_WARN([libexif >= 0.5.7 not found.]) +# LIBEXIF=yes ]) + + +#PKG_CHECK_MODULES(LIBEXIF06, libexif >= 0.6.9, +# AC_DEFINE(HAVE_EXIF06,1,[check for libexif > 0.6]), +# AC_MSG_WARN([Using old version of libexif.])) + +PKG_CHECK_MODULES(LIBEXIF, libexif >= 0.6.12 , , + [ AC_MSG_WARN([libexif >= 0.6.12 not found.]) + LIBEXIF=yes ]) + +AC_SUBST(LIBEXIF_LIBS) +AC_SUBST(LIBEXIF_CFLAGS) + +#--------------------------------------------------------- +# libexif detection +#--------------------------------------------------------- +AC_MSG_CHECKING([if C++ program with exif can be compiled]) +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +ac_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS $LIBEXIF_CFLAGS" +AC_CACHE_VAL(exif_build, +[ + AC_TRY_COMPILE([ + extern "C" { +#include <libexif/exif-loader.h> +#include <libexif/exif-utils.h> +} + ],[ + ExifLoader *l = exif_loader_new (); + exif_loader_write_file (l,"kikoo"); + return 0; + ], exif_build=yes, + exif_build=no) +]) +AC_MSG_RESULT($exif_build) +if test "$exif_build" = "no"; then + LIBEXIF="" +fi +CXXFLAGS="$ac_save_CXXFLAGS" +AC_LANG_RESTORE + + +AM_CONDITIONAL(have_exif, test -n "$LIBEXIF") +AM_CONDITIONAL(include_jpeg_filter, test -n "$LIBJPEG" -a -n "$LIBEXIF") +AM_CONDITIONAL(include_tiff_filter, test -n "$LIBTIFF" -a -n "$LIBEXIF") diff --git a/filters/krita/gmagick/Makefile.am b/filters/krita/gmagick/Makefile.am new file mode 100644 index 00000000..845ab503 --- /dev/null +++ b/filters/krita/gmagick/Makefile.am @@ -0,0 +1,40 @@ +kde_module_LTLIBRARIES = libkritagmagickimport.la libkritagmagickexport.la + +libkritagmagickexport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBGMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBGMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritagmagickexport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBGMAGICK_LIBS) \ + $(top_builddir)/krita/libkritacommon.la + +libkritagmagickimport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBGMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBGMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritagmagickimport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBGMAGICK_LIBS) \ + $(top_builddir)/krita/libkritacommon.la + +INCLUDES= \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) $(LIBGMAGICK_CPPFLAGS) \ + $(all_includes) + +service_DATA = krita_magick_import.desktop krita_magick_export.desktop +servicedir = $(kde_servicesdir) + +kdelnk_DATA = krita_magick.desktop +kdelnkdir = $(kde_appsdir)/.hidden + +libkritagmagickimport_la_SOURCES = magickimport.cpp kis_image_magick_converter.cc +libkritagmagickexport_la_SOURCES = magickexport.cpp kis_image_magick_converter.cc + +METASOURCES = AUTO + + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) diff --git a/filters/krita/gmagick/configure.in.bot b/filters/krita/gmagick/configure.in.bot new file mode 100644 index 00000000..d922dfd3 --- /dev/null +++ b/filters/krita/gmagick/configure.in.bot @@ -0,0 +1,23 @@ +if test -z "$LIBGMAGICK_LIBS" -a -z "$LIBMAGICK_LIBS"; then + echo "" + echo "You're missing GraphicsMagick (>=1.1.7). krita's GraphicsMagick import/export" + echo "filter will not be compiled. You can download GraphicsMagick from" + echo "http://www.graphicsmagick.org/. The GraphicsMagick filter allows krita to" + echo "read and write XCF, PSD, GIF, BMP, and many other image formats." + echo "" + echo "If you have problems compiling GraphicsMagick, please try configuring it using" + echo "the --without-magick-plus-plus flag, the C++ API isn't needed for krita." + echo "" + all_tests=bad + AC_DEFINE([include_imagemagick_filter],"",[don't use magick filter]) +fi + +if test -z "$LIBGMAGICK_LIBS" -a ! -z "$LIBMAGICK_LIBS"; then + + echo "" + echo "You're missing GraphicsMagick (>=1.1.7). krita's GraphicsMagick import/export" + echo "filter will not be compiled. But ImageMagick was found, which mean that krita" + echo "will be able to read and write XCF, PSD, GIF, BMP, and many other image formats." + echo "But the ImageMagick filter is deprecated and we strongly advise you to install" + echo "GraphicsMagick either from your distribution or from http://www.graphicsmagick.org/" +fi diff --git a/filters/krita/gmagick/kis_image_magick_converter.cc b/filters/krita/gmagick/kis_image_magick_converter.cc new file mode 100644 index 00000000..c94895f8 --- /dev/null +++ b/filters/krita/gmagick/kis_image_magick_converter.cc @@ -0,0 +1,1142 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <magick/api.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <qstring.h> + +#include <kdeversion.h> +#include <kdebug.h> +#include <kapplication.h> +#include <klocale.h> +#include <kurl.h> +#include <kio/netaccess.h> + +#include <qcolor.h> + +#include "kis_types.h" +#include "kis_global.h" +#include "kis_doc.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_undo_adapter.h" +#include "kis_image_magick_converter.h" +#include "kis_meta_registry.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_iterators_pixel.h" +#include "kis_colorspace.h" +#include "kis_profile.h" +#include "kis_annotation.h" +#include "kis_paint_layer.h" +#include "kis_group_layer.h" +#include "kis_paint_device.h" + +#include "../../../config.h" + +namespace { + + const Q_UINT8 PIXEL_BLUE = 0; + const Q_UINT8 PIXEL_GREEN = 1; + const Q_UINT8 PIXEL_RED = 2; + const Q_UINT8 PIXEL_ALPHA = 3; + + static const Q_UINT8 PIXEL_CYAN = 0; + static const Q_UINT8 PIXEL_MAGENTA = 1; + static const Q_UINT8 PIXEL_YELLOW = 2; + static const Q_UINT8 PIXEL_BLACK = 3; + static const Q_UINT8 PIXEL_CMYK_ALPHA = 4; + + static const Q_UINT8 PIXEL_GRAY = 0; + static const Q_UINT8 PIXEL_GRAY_ALPHA = 1; + + /** + * Make this more flexible -- although... ImageMagick + * isn't that flexible either. + */ + QString getColorSpaceName(ColorspaceType type, unsigned long imageDepth = 8) + { + + if (type == GRAYColorspace) { + if (imageDepth == 8) + return "GRAYA"; + else if ( imageDepth == 16 ) + return "GRAYA16" ; + } + else if (type == CMYKColorspace) { + if (imageDepth == 8) + return "CMYK"; + else if ( imageDepth == 16 ) { + return "CMYK16"; + } + } + else if (type == LABColorspace) { + kdDebug(41008) << "Lab!\n"; + return "LABA"; + } + else if (type == RGBColorspace || type == sRGBColorspace || type == TransparentColorspace) { + if (imageDepth == 8) + return "RGBA"; + else if (imageDepth == 16) + return "RGBA16"; + } + return ""; + + } + + ColorspaceType getColorTypeforColorSpace( KisColorSpace * cs ) + { + if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) return GRAYColorspace; + if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) return RGBColorspace; + if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") ) return CMYKColorspace; + if ( cs->id() == KisID("LABA") ) return LABColorspace; + + kdDebug(41008) << "Cannot export images in " + cs->id().name() + " yet.\n"; + return RGBColorspace; + + } + + KisProfile * getProfileForProfileInfo(const Image * image) + { + size_t length; + + const unsigned char * profiledata = GetImageProfile(image, "ICM", &length); + if( profiledata == NULL ) + return 0; + QByteArray rawdata; + rawdata.resize(length); + memcpy(rawdata.data(), profiledata, length); + + KisProfile* p = new KisProfile(rawdata); + return p; + +#if 0 + return 0; + + if (image->profiles == NULL) + return 0; + + const char *name; + const StringInfo *profile; + + KisProfile * p = 0; + + ResetImageProfileIterator(image); + for (name = GetNextImageProfile(image); name != (char *) NULL; ) + { + profile = GetImageProfile(image, name); + if (profile == (StringInfo *) NULL) + continue; + + // XXX: Hardcoded for icc type -- is that correct for us? + if (QString::compare(name, "icc") == 0) { + QByteArray rawdata; + rawdata.resize(profile->length); + memcpy(rawdata.data(), profile->datum, profile->length); + + p = new KisProfile(rawdata); + if (p == 0) + return 0; + } + name = GetNextImageProfile(image); + } + return p; +#endif + } + + void setAnnotationsForImage(const Image * src, KisImageSP image) + { + size_t length; + + const unsigned char * profiledata = GetImageProfile(src, "IPTC", &length); + if( profiledata != NULL ) + { + QByteArray rawdata; + rawdata.resize(length); + memcpy(rawdata.data(), profiledata, length); + + KisAnnotation* annotation = new KisAnnotation(QString("IPTC"), "", rawdata); + Q_CHECK_PTR(annotation); + + image -> addAnnotation(annotation); + } + for(int i = 0; i < src->generic_profiles; i++) + { + QByteArray rawdata; + rawdata.resize(length); + memcpy(rawdata.data(), src->generic_profile[i].info, src->generic_profile[i].length); + + KisAnnotation* annotation = new KisAnnotation(QString(src->generic_profile[i].name), "", rawdata); + Q_CHECK_PTR(annotation); + + image -> addAnnotation(annotation); + } + + const ImageAttribute* imgAttr = GetImageAttribute(src, NULL); + while(imgAttr) + { + QByteArray rawdata; + int len = strlen(imgAttr -> value) + 1; + rawdata.resize(len); + memcpy(rawdata.data(), imgAttr -> value, len); + + KisAnnotation* annotation = new KisAnnotation( QString("krita_attribute:%1").arg(QString(imgAttr -> key)), "", rawdata ); + Q_CHECK_PTR(annotation); + + image -> addAnnotation(annotation); + + imgAttr = imgAttr->next; + } +#if 0 + return; + if (src->profiles == NULL) + return; + + const char *name = 0; + const StringInfo *profile; + KisAnnotation* annotation = 0; + + // Profiles and so + ResetImageProfileIterator(src); + while((name = GetNextImageProfile(src))) { + profile = GetImageProfile(src, name); + if (profile == (StringInfo *) NULL) + continue; + + // XXX: icc will be written seperately? + if (QString::compare(name, "icc") == 0) + continue; + + QByteArray rawdata; + rawdata.resize(profile->length); + memcpy(rawdata.data(), profile->datum, profile->length); + + annotation = new KisAnnotation(QString(name), "", rawdata); + Q_CHECK_PTR(annotation); + + image -> addAnnotation(annotation); + } + + // Attributes, since we have no hint on if this is an attribute or a profile + // annotation, we prefix it with 'krita_attribute:'. XXX This needs to be rethought! + // The joys of imagemagick. From at version 6.2.1 (dfaure has 6.2.0 and confirms the + // old way of doing things) they changed the src -> attributes + // to void* and require us to use the iterator functions. So we #if around that, *sigh* +#if MagickLibVersion >= 0x621 + const ImageAttribute * attr; + ResetImageAttributeIterator(src); + while ( (attr = GetNextImageAttribute(src)) ) { +#else + ImageAttribute * attr = src -> attributes; + while (attr) { +#endif + QByteArray rawdata; + int len = strlen(attr -> value) + 1; + rawdata.resize(len); + memcpy(rawdata.data(), attr -> value, len); + + annotation = new KisAnnotation( + QString("krita_attribute:%1").arg(QString(attr -> key)), "", rawdata); + Q_CHECK_PTR(annotation); + + image -> addAnnotation(annotation); +#if MagickLibVersion < 0x620 + attr = attr -> next; +#endif + } + +#endif + } + } + + void exportAnnotationsForImage(Image * dst, vKisAnnotationSP_it& it, vKisAnnotationSP_it& annotationsEnd) + { + while(it != annotationsEnd) { + if (!(*it) || (*it) -> type() == QString()) { + kdDebug(41008) << "Warning: empty annotation" << endl; + ++it; + continue; + } + + kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl; + + if ((*it) -> type().startsWith("krita_attribute:")) { // Attribute + if (!SetImageAttribute(dst, + (*it) -> type().mid(strlen("krita_attribute:")).ascii(), + (*it) -> annotation() . data()) ) { + kdDebug(41008) << "Storing of attribute " << (*it) -> type() << "failed!\n"; + } + } else { // Profile + unsigned char * profiledata = new unsigned char[(*it) -> annotation() . size()]; + memcpy( profiledata, (*it) -> annotation() . data(), (*it) -> annotation() . size()); + if (!ProfileImage(dst, (*it) -> type().ascii(), + profiledata, (*it) -> annotation() . size(), MagickFalse)) { + kdDebug(41008) << "Storing failed!" << endl; + } + } + ++it; + } + } + + + void InitGlobalMagick() + { + static bool init = false; + + if (!init) { + KApplication *app = KApplication::kApplication(); + + InitializeMagick(*app -> argv()); + atexit(DestroyMagick); + init = true; + } + } + + /* + * ImageMagick progress monitor callback. Unfortunately it doesn't support passing in some user + * data which complicates things quite a bit. The plan was to allow the user start multiple + * import/scans if he/she so wished. However, without passing user data it's not possible to tell + * on which task we have made progress on. + * + * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held + * locks when calling user defined callbacks, this means that the same thread going back into IM + * would deadlock since it would try to acquire locks it already holds. + */ +#if 0 + MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *) + { + KApplication *app = KApplication::kApplication(); + + Q_ASSERT(app); + + if (app -> hasPendingEvents()) + app -> processEvents(); + + printf("%s\n", text); + return MagickTrue; + } +#else + unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *) + { + KApplication *app = KApplication::kApplication(); + + Q_ASSERT(app); + + if (app -> hasPendingEvents()) + app -> processEvents(); + + printf("%s\n", text); + return true; + } +#endif + + + +KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter) +{ + InitGlobalMagick(); + init(doc, adapter); + SetMonitorHandler(monitor); + m_stop = false; +} + +KisImageMagickConverter::~KisImageMagickConverter() +{ +} + +KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob) +{ + Image *image; + Image *images; + ExceptionInfo ei; + ImageInfo *ii; + + if (m_stop) { + m_img = 0; + return KisImageBuilder_RESULT_INTR; + } + + GetExceptionInfo(&ei); + ii = CloneImageInfo(0); + + if (isBlob) { + + // TODO : Test. Does BlobToImage even work? + Q_ASSERT(uri.isEmpty()); + images = BlobToImage(ii, &m_data[0], m_data.size(), &ei); + } else { + + qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1); + + if (ii -> filename[MaxTextExtent - 1]) { + emit notifyProgressError(); + return KisImageBuilder_RESULT_PATH; + } + + images = ReadImage(ii, &ei); + + } + + if (ei.severity != UndefinedException) + { + CatchException(&ei); + kdDebug(41008) << "Exceptions happen when loading" << endl; + return KisImageBuilder_RESULT_FAILURE; + } + + + if (images == 0) { + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + emit notifyProgressError(); + return KisImageBuilder_RESULT_FAILURE; + } + + emit notifyProgressStage(i18n("Importing..."), 0); + + m_img = 0; + + while ((image = RemoveFirstImageFromList(&images))) { + if(image->rows == 0 or image->columns == 0) return KisImageBuilder_RESULT_FAILURE; + ViewInfo *vi = OpenCacheView(image); + + // Determine image depth -- for now, all channels of an imported image are of the same depth + unsigned long imageDepth = image->depth; + kdDebug(41008) << "Image depth: " << imageDepth << "\n"; + + QString csName; + KisColorSpace * cs = 0; + ColorspaceType colorspaceType; + + // Determine image type -- rgb, grayscale or cmyk + if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) { + if (imageDepth == 8) + csName = "GRAYA"; + else if ( imageDepth == 16 ) + csName = "GRAYA16" ; + colorspaceType = GRAYColorspace; + } + else { + colorspaceType = image->colorspace; + csName = getColorSpaceName(image -> colorspace, imageDepth); + } + + kdDebug(41008) << "image has " << csName << " colorspace\n"; + + KisProfile * profile = getProfileForProfileInfo(image); + if (profile) + { + kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n"; + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile); + } + else + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),""); + + if (!cs) { + kdDebug(41008) << "Krita does not support colorspace " << image -> colorspace << "\n"; + CloseCacheView(vi); + DestroyImage(image); + DestroyExceptionInfo(&ei); + DestroyImageList(images); + DestroyImageInfo(ii); + emit notifyProgressError(); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + + if( ! m_img) { + m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image"); + Q_CHECK_PTR(m_img); + m_img->blockSignals(true); // Don't send out signals while we're building the image + + // XXX I'm assuming seperate layers won't have other profile things like EXIF + setAnnotationsForImage(image, m_img); + } + + if (image -> columns && image -> rows) { + + // Opacity (set by the photoshop import filter) + Q_UINT8 opacity = OPACITY_OPAQUE; + const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]"); + if (attr != 0) { + opacity = Q_UINT8_MAX - Downscale(QString(attr->value).toInt()); + } + + KisPaintLayerSP layer = 0; + + attr = GetImageAttribute(image, "[layer-name]"); + if (attr != 0) { + layer = new KisPaintLayer(m_img, attr->value, opacity); + } + else { + layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity); + } + + Q_ASSERT(layer); + + // Layerlocation (set by the photoshop import filter) + Q_INT32 x_offset = 0; + Q_INT32 y_offset = 0; + + attr = GetImageAttribute(image, "[layer-xpos]"); + if (attr != 0) { + x_offset = QString(attr->value).toInt(); + } + + attr = GetImageAttribute(image, "[layer-ypos]"); + if (attr != 0) { + y_offset = QString(attr->value).toInt(); + } + + + for (Q_UINT32 y = 0; y < image->rows; y ++) + { + const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei); + + if(!pp) + { + CloseCacheView(vi); + DestroyImageList(images); + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + emit notifyProgressError(); + return KisImageBuilder_RESULT_FAILURE; + } + + IndexPacket * indexes = GetCacheViewIndexes(vi); + + KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true); + + if (colorspaceType== CMYKColorspace) { + if (imageDepth == 8) { + int x = 0; + while (!hiter.isDone()) + { + Q_UINT8 *ptr= hiter.rawData(); + *(ptr++) = Downscale(pp->red); // cyan + *(ptr++) = Downscale(pp->green); // magenta + *(ptr++) = Downscale(pp->blue); // yellow + *(ptr++) = Downscale(indexes[x]); // Black +// XXX: Warning! This ifdef messes up the paren matching big-time! +#ifdef HAVE_MAGICK6 + if (image->matte != MagickFalse) { +#else + if (image->matte == true) { +#endif + *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity); + } + else { + *(ptr++) = OPACITY_OPAQUE; + } + ++x; + pp++; + ++hiter; + } + } + } + else if (colorspaceType == LABColorspace) { + while(! hiter.isDone()) + { + Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData()); + + *(ptr++) = ScaleQuantumToShort(pp->red); + *(ptr++) = ScaleQuantumToShort(pp->green); + *(ptr++) = ScaleQuantumToShort(pp->blue); + *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity); + + pp++; + ++hiter; + } + } + else if (colorspaceType == RGBColorspace || + colorspaceType == sRGBColorspace || + colorspaceType == TransparentColorspace) + { + if (imageDepth == 8) { + while(! hiter.isDone()) + { + Q_UINT8 *ptr= hiter.rawData(); + // XXX: not colorstrategy and bitdepth independent + *(ptr++) = Downscale(pp->blue); + *(ptr++) = Downscale(pp->green); + *(ptr++) = Downscale(pp->red); + *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity); + + pp++; + ++hiter; + } + } + else if (imageDepth == 16) { + while(! hiter.isDone()) + { + Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData()); + // XXX: not colorstrategy independent + *(ptr++) = ScaleQuantumToShort(pp->blue); + *(ptr++) = ScaleQuantumToShort(pp->green); + *(ptr++) = ScaleQuantumToShort(pp->red); + *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity); + + pp++; + ++hiter; + } + } + } + else if ( colorspaceType == GRAYColorspace) { + if (imageDepth == 8) { + while(! hiter.isDone()) + { + Q_UINT8 *ptr= hiter.rawData(); + // XXX: not colorstrategy and bitdepth independent + *(ptr++) = Downscale(pp->blue); + *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity); + + pp++; + ++hiter; + } + } + else if (imageDepth == 16) { + while(! hiter.isDone()) + { + Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData()); + // XXX: not colorstrategy independent + *(ptr++) = ScaleQuantumToShort(pp->blue); + *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity); + + pp++; + ++hiter; + } + } + } + + emit notifyProgress(y * 100 / image->rows); + + if (m_stop) { + CloseCacheView(vi); + DestroyImage(image); + DestroyImageList(images); + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + m_img = 0; + return KisImageBuilder_RESULT_INTR; + } + } + m_img->addLayer(layer.data(), m_img->rootLayer()); + layer->paintDevice()->move(x_offset, y_offset); + } + + emit notifyProgressDone(); + CloseCacheView(vi); + DestroyImage(image); + } + + emit notifyProgressDone(); + DestroyImageList(images); + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + return KisImageBuilder_RESULT_OK; + } + + KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri) + { + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) { + return KisImageBuilder_RESULT_NOT_EXIST; + } + + KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; + QString tmpFile; + + if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) { + KURL uriTF; + uriTF.setPath( tmpFile ); + result = decode(uriTF, false); + KIO::NetAccess::removeTempFile(tmpFile); + } + + return result; + } + + + KisImageSP KisImageMagickConverter::image() + { + return m_img; + } + + void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter) + { + m_doc = doc; + m_adapter = adapter; + m_job = 0; + } + + KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd) + { + Image *image; + ExceptionInfo ei; + ImageInfo *ii; + + if (!layer) + return KisImageBuilder_RESULT_INVALID_ARG; + + KisImageSP img = layer->image(); + if (!img) + return KisImageBuilder_RESULT_EMPTY; + + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!uri.isLocalFile()) + return KisImageBuilder_RESULT_NOT_LOCAL; + + + Q_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels(); + + GetExceptionInfo(&ei); + + ii = CloneImageInfo(0); + + qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1); + + if (ii -> filename[MaxTextExtent - 1]) { + emit notifyProgressError(); + return KisImageBuilder_RESULT_PATH; + } + + if (!img -> width() || !img -> height()) + return KisImageBuilder_RESULT_EMPTY; + + if (layerBytesPerChannel < 2) { + ii->depth = 8; + } + else { + ii->depth = 16; + } + + ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace()); + + image = AllocateImage(ii); +// SetImageColorspace(image, ii->colorspace); + image -> columns = img -> width(); + image -> rows = img -> height(); + + kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n"; + kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n"; + +#ifdef HAVE_MAGICK6 + // if ( layer-> hasAlpha() ) + image -> matte = MagickTrue; + // else + // image -> matte = MagickFalse; +#else + // image -> matte = layer -> hasAlpha(); + image -> matte = true; +#endif + + Q_INT32 y, height, width; + + height = img -> height(); + width = img -> width(); + + bool alpha = true; + QString ext = QFileInfo(QFile::encodeName(uri.path())).extension(false).upper(); + if (ext == "BMP") { + alpha = false; + qstrncpy(ii->magick, "BMP2", MaxTextExtent - 1); + } + else if (ext == "RGB") { + qstrncpy(ii->magick, "SGI", MaxTextExtent - 1); + } + + for (y = 0; y < height; y++) { + + // Allocate pixels for this scanline + PixelPacket * pp = SetImagePixels(image, 0, y, width, 1); + + if (!pp) { + DestroyExceptionInfo(&ei); + DestroyImage(image); + emit notifyProgressError(); + return KisImageBuilder_RESULT_FAILURE; + + } + + KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false); + if (alpha) + SetImageType(image, TrueColorMatteType); + else + SetImageType(image, TrueColorType); + + if (image->colorspace== CMYKColorspace) { + + IndexPacket * indexes = GetIndexes(image); + int x = 0; + if (layerBytesPerChannel == 2) { + while (!it.isDone()) { + + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]); + pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]); + pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]); + if (alpha) + pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]); + indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]); + x++; + pp++; + ++it; + } + } + else { + while (!it.isDone()) { + + Q_UINT8 * d = it.rawData(); + pp -> red = Upscale(d[PIXEL_CYAN]); + pp -> green = Upscale(d[PIXEL_MAGENTA]); + pp -> blue = Upscale(d[PIXEL_YELLOW]); + if (alpha) + pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]); + + indexes[x]= Upscale(d[PIXEL_BLACK]); + + x++; + pp++; + ++it; + } + } + } + else if (image->colorspace== RGBColorspace || + image->colorspace == sRGBColorspace || + image->colorspace == TransparentColorspace) + { + if (layerBytesPerChannel == 2) { + while (!it.isDone()) { + + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + pp -> red = ScaleShortToQuantum(d[PIXEL_RED]); + pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]); + pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]); + if (alpha) + pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]); + + pp++; + ++it; + } + } + else { + while (!it.isDone()) { + + Q_UINT8 * d = it.rawData(); + pp -> red = Upscale(d[PIXEL_RED]); + pp -> green = Upscale(d[PIXEL_GREEN]); + pp -> blue = Upscale(d[PIXEL_BLUE]); + if (alpha) + pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_ALPHA]); + + pp++; + ++it; + } + } + } + else if (image->colorspace == GRAYColorspace) + { + SetImageType(image, GrayscaleMatteType); + if (layerBytesPerChannel == 2) { + while (!it.isDone()) { + + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]); + pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]); + pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]); + if (alpha) + pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]); + + pp++; + ++it; + } + } + else { + while (!it.isDone()) { + Q_UINT8 * d = it.rawData(); + pp -> red = Upscale(d[PIXEL_GRAY]); + pp -> green = Upscale(d[PIXEL_GRAY]); + pp -> blue = Upscale(d[PIXEL_GRAY]); + if (alpha) + pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]); + + pp++; + ++it; + } + } + } + else { + kdDebug(41008) << "Unsupported image format\n"; + return KisImageBuilder_RESULT_INVALID_ARG; + } + + emit notifyProgressStage(i18n("Saving..."), y * 100 / height); + +#ifdef HAVE_MAGICK6 + if (SyncImagePixels(image) == MagickFalse) + kdDebug(41008) << "Syncing pixels failed\n"; +#else + if (!SyncImagePixels(image)) + kdDebug(41008) << "Syncing pixels failed\n"; +#endif + } + + // set the annotations + exportAnnotationsForImage(image, annotationsStart, annotationsEnd); + + // XXX: Write to a temp file, then have Krita use KIO to copy temp + // image to remote location. + + WriteImage(ii, image); + DestroyExceptionInfo(&ei); + DestroyImage(image); + emit notifyProgressDone(); + return KisImageBuilder_RESULT_OK; + } + + void KisImageMagickConverter::ioData(KIO::Job *job, const QByteArray& data) + { + if (data.isNull() || data.isEmpty()) { + emit notifyProgressStage(i18n("Loading..."), 0); + return; + } + + if (m_data.empty()) { + Image *image; + ImageInfo *ii; + ExceptionInfo ei; + + ii = CloneImageInfo(0); + GetExceptionInfo(&ei); + image = PingBlob(ii, data.data(), data.size(), &ei); + + if (image == 0 || ei.severity == BlobError) { + DestroyExceptionInfo(&ei); + DestroyImageInfo(ii); + job -> kill(); + emit notifyProgressError(); + return; + } + + DestroyImage(image); + DestroyExceptionInfo(&ei); + DestroyImageInfo(ii); + emit notifyProgressStage(i18n("Loading..."), 0); + } + + Q_ASSERT(data.size() + m_data.size() <= m_size); + memcpy(&m_data[m_data.size()], data.data(), data.count()); + m_data.resize(m_data.size() + data.count()); + emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size); + + if (m_stop) + job -> kill(); + } + + void KisImageMagickConverter::ioResult(KIO::Job *job) + { + m_job = 0; + + if (job -> error()) + emit notifyProgressError(); + + decode(KURL(), true); + } + + void KisImageMagickConverter::ioTotalSize(KIO::Job * /*job*/, KIO::filesize_t size) + { + m_size = size; + m_data.reserve(size); + emit notifyProgressStage(i18n("Loading..."), 0); + } + + void KisImageMagickConverter::cancel() + { + m_stop = true; + } + + /** + * @name readFilters + * @return Provide a list of file formats the application can read. + */ + QString KisImageMagickConverter::readFilters() + { + QString s; + QString all; + QString name; + QString description; + unsigned long matches; + +/*#ifdef HAVE_MAGICK6 +#ifdef HAVE_OLD_GETMAGICKINFOLIST + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches); +#else // HAVE_OLD_GETMAGICKINFOLIST + ExceptionInfo ei; + GetExceptionInfo(&ei); + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches, &ei); + DestroyExceptionInfo(&ei); +#endif // HAVE_OLD_GETMAGICKINFOLIST +#else // HAVE_MAGICK6*/ + const MagickInfo *mi; + ExceptionInfo ei; + GetExceptionInfo(&ei); + mi = GetMagickInfo("*", &ei); + DestroyExceptionInfo(&ei); +// #endif // HAVE_MAGICK6 + + if (!mi) + return s; + +/*#ifdef HAVE_MAGICK6 + for (unsigned long i = 0; i < matches; i++) { + const MagickInfo *info = mi[i]; + if (info -> stealth) + continue; + + if (info -> decoder) { + name = info -> name; + description = info -> description; + kdDebug(41008) << "Found import filter for: " << name << "\n"; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +#else*/ + for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) { + if (mi -> stealth) + continue; + if (mi -> decoder) { + name = mi -> name; + description = mi -> description; + kdDebug(41008) << "Found import filter for: " << name << "\n"; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +// #endif + + all += "|" + i18n("All Images"); + all += "\n"; + + return all + s; + } + + QString KisImageMagickConverter::writeFilters() + { + QString s; + QString all; + QString name; + QString description; + unsigned long matches; + +/*#ifdef HAVE_MAGICK6 +#ifdef HAVE_OLD_GETMAGICKINFOLIST + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches); +#else // HAVE_OLD_GETMAGICKINFOLIST + ExceptionInfo ei; + GetExceptionInfo(&ei); + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches, &ei); + DestroyExceptionInfo(&ei); +#endif // HAVE_OLD_GETMAGICKINFOLIST +#else // HAVE_MAGICK6*/ + const MagickInfo *mi; + ExceptionInfo ei; + GetExceptionInfo(&ei); + mi = GetMagickInfo("*", &ei); + DestroyExceptionInfo(&ei); +// #endif // HAVE_MAGICK6 + + if (!mi) { + kdDebug(41008) << "Eek, no magick info!\n"; + return s; + } + +/*#ifdef HAVE_MAGICK6 + for (unsigned long i = 0; i < matches; i++) { + const MagickInfo *info = mi[i]; + kdDebug(41008) << "Found export filter for: " << info -> name << "\n"; + if (info -> stealth) + continue; + + if (info -> encoder) { + name = info -> name; + + description = info -> description; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +#else*/ + for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) { + kdDebug(41008) << "Found export filter for: " << mi -> name << "\n"; + if (mi -> stealth) + continue; + + if (mi -> encoder) { + name = mi -> name; + + description = mi -> description; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +// #endif + + + all += "|" + i18n("All Images"); + all += "\n"; + + return all + s; + } + +#include "kis_image_magick_converter.moc" + diff --git a/filters/krita/gmagick/kis_image_magick_converter.h b/filters/krita/gmagick/kis_image_magick_converter.h new file mode 100644 index 00000000..9b3b6bc0 --- /dev/null +++ b/filters/krita/gmagick/kis_image_magick_converter.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef KIS_IMAGE_MAGICK_CONVERTER_H_ +#define KIS_IMAGE_MAGICK_CONVERTER_H_ + +#include <qobject.h> +#include <qvaluevector.h> + +#include <kio/job.h> + +#include "kis_types.h" +#include "kis_global.h" +#include "kis_progress_subject.h" + +class QString; +class KURL; +class KisDoc; +class KisNameServer; +class KisUndoAdapter; +/** + * Image import/export plugins can use these results to report about success or failure. + */ +enum KisImageBuilder_Result { + KisImageBuilder_RESULT_FAILURE = -400, + KisImageBuilder_RESULT_NOT_EXIST = -300, + KisImageBuilder_RESULT_NOT_LOCAL = -200, + KisImageBuilder_RESULT_BAD_FETCH = -100, + KisImageBuilder_RESULT_INVALID_ARG = -50, + KisImageBuilder_RESULT_OK = 0, + KisImageBuilder_RESULT_PROGRESS = 1, + KisImageBuilder_RESULT_EMPTY = 100, + KisImageBuilder_RESULT_BUSY = 150, + KisImageBuilder_RESULT_NO_URI = 200, + KisImageBuilder_RESULT_UNSUPPORTED = 300, + KisImageBuilder_RESULT_INTR = 400, + KisImageBuilder_RESULT_PATH = 500, + KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 +}; + + + +/** + * Build a KisImage representation of an image file. + */ +class KisImageMagickConverter : public KisProgressSubject { + typedef QObject super; + Q_OBJECT + +public: + KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter); + virtual ~KisImageMagickConverter(); + +public slots: + virtual void cancel(); + +public: + KisImageBuilder_Result buildImage(const KURL& uri); + KisImageBuilder_Result buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd); + KisImageSP image(); + +public: + static QString readFilters(); + static QString writeFilters(); + +private slots: + void ioData(KIO::Job *job, const QByteArray& data); + void ioResult(KIO::Job *job); + void ioTotalSize(KIO::Job *job, KIO::filesize_t size); + +private: + KisImageMagickConverter(const KisImageMagickConverter&); + KisImageMagickConverter& operator=(const KisImageMagickConverter&); + void init(KisDoc *doc, KisUndoAdapter *adapter); + KisImageBuilder_Result decode(const KURL& uri, bool isBlob); + +private: + KisImageSP m_img; + KisDoc *m_doc; + KisUndoAdapter *m_adapter; + QValueVector<Q_UINT8> m_data; + KIO::TransferJob *m_job; + KIO::filesize_t m_size; + bool m_stop; +}; + +#endif // KIS_IMAGE_MAGICK_CONVERTER_H_ + diff --git a/filters/krita/gmagick/krita_magick.desktop b/filters/krita/gmagick/krita_magick.desktop new file mode 100644 index 00000000..486b3645 --- /dev/null +++ b/filters/krita/gmagick/krita_magick.desktop @@ -0,0 +1,57 @@ +[Desktop Entry] +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Exec=krita %u +GenericName=Painting and Image Editing Application +GenericName[bg]=Редактор на графични изображения +GenericName[ca]=Programa de dibuix i manipulació d'imatges +GenericName[cs]=Malování a úpravy obrázků +GenericName[cy]=Cymhwysiad Peintio Golygu Delweddau +GenericName[da]=Male- og billedredigeringsprogram +GenericName[de]=Mal- und Bildbearbeitungsprogramm +GenericName[el]=Εφαρμογή επεξεργασίας εικόνων +GenericName[eo]=Aplikaĵo por Pentrado kaj Bildredaktado +GenericName[es]=Aplicación de pintura y de edición de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudien marrazketa eta ediziorako aplikazioa +GenericName[fa]=کاربرد ویرایش تصویر و نقاشی +GenericName[fi]=Maalaus- ja kuvankäsitelyohjelma +GenericName[fr]=Application de dessin et de manipulation d'images +GenericName[fy]=Ofbyldingsmanipulaasje +GenericName[gl]=Aplicación de Pintura e Manipulación de Imaxes +GenericName[he]=יישום לציור ועריכת תמונות +GenericName[hr]=Aplikacija za obradu slika i fotografija +GenericName[hu]=Képszerkesztő +GenericName[is]=Málun og myndritill +GenericName[it]=Applicazione di disegno e di modifica delle immagini +GenericName[ja]=描画と画像編集のためのアプリケーション +GenericName[km]=កម្មវិធីគូរគំនូរ និងកែសម្រួលរូបភាព +GenericName[lv]=Zīmēšanas un attēlu apstrādes programma +GenericName[nb]=Program for tegning og bilderedigering +GenericName[nds]=Programm för't Malen un Bildbewerken +GenericName[ne]=पेन्टीङ्ग र छवि सम्पादन अनुप्रयोग +GenericName[nl]=Afbeeldingsmanipulatie +GenericName[pl]=Program do edycji zdjęć oraz rysunków +GenericName[pt]=Aplicação de Pintura e Edição de Imagens +GenericName[pt_BR]=Aplicação de Pintura e Edição de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Málen- ja govvagieđahallanprográmma +GenericName[sk]=Program pre tvorbu a úpravu obrázkov +GenericName[sl]=Program za risanje in obdelavo slik +GenericName[sr]=Програм за цртање и уређивање слика +GenericName[sr@Latn]=Program za crtanje i uređivanje slika +GenericName[sv]=Målnings- och bildredigeringsprogram +GenericName[uk]=Програма для малювання і редагування зображень +GenericName[uz]=Rasmlar bilan ishlaydigan dastur +GenericName[uz@cyrillic]=Расмлар билан ишлайдиган дастур +GenericName[zh_CN]=绘图和图像编辑应用程序 +GenericName[zh_TW]=繪圖與影像處理程式 +MimeType=image/x-xcf-gimp;image/gif;image/cgm;image/x-bmp;image/x-ico;image/x-pcx;image/x-portable-pixmap;image/x-targa;image/x-xbm;image/x-xcf;image/x-xpm;image/x-vnd.adobe.photoshop;image/x-rgb +Type=Application +Icon=krita +Categories= +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi diff --git a/filters/krita/gmagick/krita_magick_export.desktop b/filters/krita/gmagick/krita_magick_export.desktop new file mode 100644 index 00000000..cd348c6a --- /dev/null +++ b/filters/krita/gmagick/krita_magick_export.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Name=Krita Magick Export Filter +Name[bg]=Филтър за експортиране от Krita в Magick +Name[br]=Sil ezporzh Magick evit Krita +Name[ca]=Filtre d'exportació Magick per a Krita +Name[cy]=Hidl Allforio Magick Krita +Name[da]=Krita Magick-eksportfilter +Name[de]=Krita Magick-Exportfilter +Name[el]=Φίλτρο εξαγωγής Magick του Krita +Name[eo]=Krita Magick-eksportfiltrilo +Name[es]=Filtro de exportación a Magick de Krita +Name[et]=Krita Magick'i ekspordifilter +Name[eu]=Krita-ren Magick esportaziorako iragazkia +Name[fa]=پالایۀ صادرات Krita Magick +Name[fi]=Krita Magick -vientisuodin +Name[fr]=Filtre d'exportation Magick de Krita +Name[fy]=Krita Magick Eksportfilter +Name[ga]=Scagaire Easpórtála Magick Krita +Name[gl]=Filtro de Exportación de Magick para Krita +Name[he]=מסנן יצוא מ־Krita ל־Magick +Name[hr]=Krita Magick filtar izvoza +Name[hu]=Krita Magick exportszűrő +Name[is]=Krita Magick útflutningssía +Name[it]=Filtro di esportazione Magick per Krita +Name[ja]=Krita Magick エクスポートフィルタ +Name[km]=តម្រងនាំចេញ Magick សម្រាប់ Krita +Name[lt]=Krita Magick eksportavimo filtras +Name[lv]=Krita Magick eksporta filtrs +Name[ms]=Penapis Eksport Krita Magick +Name[nb]=Magick-eksportfilter for Krita +Name[nds]=Magick-Exportfilter för Krita +Name[ne]=क्रिता म्याजिक निर्यात फिल्टर +Name[nl]=Krita Magick Exportfilter +Name[nn]=Magick-eksportfilter for Krita +Name[pl]=Filtr eksportu do formatu Magick z Krita +Name[pt]=Filtro de Exportação de Magick para o Krita +Name[pt_BR]=Filtro de exportação Magick para o Krita +Name[ru]=Фильтр экспорта рисунков Krita в Magick +Name[se]=Krita Magick-olggosfievrridansilli +Name[sk]=Magick filter pre export do Krita +Name[sl]=Izvozni filter Magick za Krito +Name[sr]=Krita-ин филтер за извоз у Magick +Name[sr@Latn]=Krita-in filter za izvoz u Magick +Name[sv]=Krita Magick-exportfilter +Name[uk]=Фільтр експорту Magick для Krita +Name[uz]=Krita Magick eksport filteri +Name[uz@cyrillic]=Krita Magick экспорт филтери +Name[zh_CN]=Krita Magick 导出过滤器 +Name[zh_TW]=Krita Magick 匯出過濾程式 +X-KDE-Export=image/gif,image/cgm,image/x-pcx,image/x-portable-pixmap,image/x-targa,image/x-xbm,image/x-xpm,image/x-rgb,image/x-eps +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Import=application/x-krita +X-KDE-Weight=1 +X-KDE-Library=libkritagmagickexport diff --git a/filters/krita/gmagick/krita_magick_import.desktop b/filters/krita/gmagick/krita_magick_import.desktop new file mode 100644 index 00000000..486a094b --- /dev/null +++ b/filters/krita/gmagick/krita_magick_import.desktop @@ -0,0 +1,61 @@ +[Desktop Entry] +Type=Service +Name=Krita Magick Import Filter +Name[bg]=Филтър за импортиране от Magick в Krita +Name[br]=Sil enporzh Magick evit Krita +Name[ca]=Filtre d'importació Magick per a Krita +Name[cs]=Importní filtr formátu Magick pro Kritu +Name[cy]=Hidl Mewnforio Krita Magick +Name[da]=Krita Magick-importfilter +Name[de]=Krita Magick-Importfilter +Name[el]=Φίλτρο εισαγωγής Magick του Krita +Name[eo]=Krita Magick-importfiltrilo +Name[es]=Filtro de importación a Magick de Krita +Name[et]=Krita Magick'i impordifilter +Name[eu]=Krita-ren Magick inportaziorako iragazkia +Name[fa]=پالایۀ واردات Krita Magick +Name[fi]=Krita Magick-tuontisuodin +Name[fr]=Filtre d'importation Magick de Krita +Name[fy]=Krita Magick Ymportfilter +Name[ga]=Scagaire Iompórtála Magick Krita +Name[gl]=Filtro de Importación de Magick para Krita +Name[he]=מסנן יבוא מ־Magick ל־Krita +Name[hi]=के-रीता मैजिक आयात फ़िल्टर +Name[hr]=Krita Magick filtar uvoza +Name[hu]=Krita Magick importszűrő +Name[is]=Krita Magick innflutningssía +Name[it]=Filtro di importazione Magick per Krita +Name[ja]=Krita Magick インポートフィルタ +Name[km]=តម្រងនាំចូល Magick សម្រាប់ Krita +Name[lo]= ຕົວຕອງການນຳເຂົ້າ WML ຂອງເອກະສານຂໍ້ຄວາມ K +Name[lt]=Krita Magick importavimo filtras +Name[lv]=Krita Magick importa filtrs +Name[ms]=Penapis Import Krita Magick +Name[nb]=Magick-importfilter for Krita +Name[nds]=Magick-Importfilter för Krita +Name[ne]=क्रिता म्याजिक आयात फिल्टर +Name[nl]=Krita Magick Importfilter +Name[nn]=Magick-importfilter for Krita +Name[pl]=Filtr importu formatu Magick do Krita +Name[pt]=Filtro de Importação de Magick para o Krita +Name[pt_BR]=Filtro de importação Magick para o Krita +Name[ru]=Фильтр импорта рисунков Magick в Krita +Name[se]=Krita Magick-sisafievrridansilli +Name[sk]=Magick filter pre import do Krita +Name[sl]=Uvozni filter Magick za Krito +Name[sr]=Krita-ин филтер за увоз из Magick-а +Name[sr@Latn]=Krita-in filter za uvoz iz Magick-a +Name[sv]=Krita Magick-importfilter +Name[ta]=Krita மாயk இறக்குமதி வடிகட்டி +Name[tg]=Филтри Воридоти Krita Magick +Name[tr]=Krita Magick Alma Filtresi +Name[uk]=Фільтр імпорту Magick для Krita +Name[uz]=Krita Magick import filteri +Name[uz@cyrillic]=Krita Magick импорт филтери +Name[zh_CN]=Krita Magick 导入过滤器 +Name[zh_TW]=Krita Magick 匯入過濾程式 +X-KDE-Export=application/x-krita +X-KDE-Import=image/x-xcf-gimp,image/gif,image/cgm,image/x-bmp,image/x-ico,image/x-pcx,image/x-portable-pixmap,image/x-targa,image/x-xbm,image/x-xcf,image/x-xpm,image/x-vnd.adobe.photoshop,image/x-rgb,image/x-eps +X-KDE-Weight=1 +X-KDE-Library=libkritagmagickimport +ServiceTypes=KOfficeFilter diff --git a/filters/krita/gmagick/magickexport.cpp b/filters/krita/gmagick/magickexport.cpp new file mode 100644 index 00000000..b47f198c --- /dev/null +++ b/filters/krita/gmagick/magickexport.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <magickexport.h> +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_paint_layer.h> +#include <kis_image.h> +#include <kis_annotation.h> +#include <kis_types.h> +#include <kis_image_magick_converter.h> + +typedef KGenericFactory<MagickExport, KoFilter> MagickExportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritagmagickexport, MagickExportFactory("kofficefilters")) + +MagickExport::MagickExport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +MagickExport::~MagickExport() +{ +} + +KoFilter::ConversionStatus MagickExport::convert(const QCString& from, const QCString& to) +{ + kdDebug(41008) << "magick export! From: " << from << ", To: " << to << "\n"; + + if (from != "application/x-krita") + return KoFilter::NotImplemented; + + // XXX: Add dialog about flattening layers here + + KisDoc *output = dynamic_cast<KisDoc*>(m_chain->inputDocument()); + QString filename = m_chain->outputFile(); + + if (!output) + return KoFilter::CreationError; + + if (filename.isEmpty()) return KoFilter::FileNotFound; + + KURL url; + url.setPath(filename); + + KisImageSP img = output->currentImage(); + + KisImageMagickConverter ib(output, output->undoAdapter()); + + KisPaintDeviceSP pd = new KisPaintDevice(*img->projection()); + KisPaintLayerSP l = new KisPaintLayer(img, "projection", OPACITY_OPAQUE, pd); + + vKisAnnotationSP_it beginIt = img->beginAnnotations(); + vKisAnnotationSP_it endIt = img->endAnnotations(); + if (ib.buildFile(url, l, beginIt, endIt) == KisImageBuilder_RESULT_OK) { + return KoFilter::OK; + } + return KoFilter::InternalError; +} + +#include <magickexport.moc> + diff --git a/filters/krita/gmagick/magickexport.h b/filters/krita/gmagick/magickexport.h new file mode 100644 index 00000000..3c499ebd --- /dev/null +++ b/filters/krita/gmagick/magickexport.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef MAGICKEXPORT_H_ +#define MAGICKEXPORT_H_ + +#include <KoFilter.h> + +class MagickExport : public KoFilter { + Q_OBJECT + +public: + MagickExport(KoFilter *parent, const char *name, const QStringList&); + virtual ~MagickExport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // MAGICKEXPORT_H_ + diff --git a/filters/krita/gmagick/magickimport.cpp b/filters/krita/gmagick/magickimport.cpp new file mode 100644 index 00000000..5b04d6ff --- /dev/null +++ b/filters/krita/gmagick/magickimport.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <qstring.h> + +#include <magickimport.h> +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_view.h> +#include <kis_image_magick_converter.h> +#include <kis_progress_display_interface.h> +#include <kis_image.h> +#include <kis_layer.h> + +typedef KGenericFactory<MagickImport, KoFilter> MagickImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritagmagickimport, MagickImportFactory("kofficefilters")) + +MagickImport::MagickImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +MagickImport::~MagickImport() +{ +} + +KoFilter::ConversionStatus MagickImport::convert(const QCString&, const QCString& to) +{ + kdDebug(41008) << "Importing using MagickImport!\n"; + + if (to != "application/x-krita") + return KoFilter::BadMimeType; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + KisView * view = static_cast<KisView*>(doc -> views().getFirst()); + + QString filename = m_chain -> inputFile(); + + if (!doc) + return KoFilter::CreationError; + + doc -> prepareForImport(); + + + if (!filename.isEmpty()) { + + KURL url; + url.setPath(filename); + + if (url.isEmpty()) + return KoFilter::FileNotFound; + + KisImageMagickConverter ib(doc, doc -> undoAdapter()); + + if (view != 0) + view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true); + + switch (ib.buildImage(url)) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KoFilter::NotImplemented; + break; + case KisImageBuilder_RESULT_INVALID_ARG: + return KoFilter::BadMimeType; + break; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_LOCAL: + return KoFilter::FileNotFound; + break; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KoFilter::ParsingError; + break; + case KisImageBuilder_RESULT_FAILURE: + return KoFilter::InternalError; + break; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KoFilter::OK; + default: + break; + } + + } + return KoFilter::StorageCreationError; +} + +#include <magickimport.moc> + diff --git a/filters/krita/gmagick/magickimport.h b/filters/krita/gmagick/magickimport.h new file mode 100644 index 00000000..01156642 --- /dev/null +++ b/filters/krita/gmagick/magickimport.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef MAGICKIMPORT_H_ +#define MAGICKIMPORT_H_ + +#include <KoFilter.h> + +class MagickImport : public KoFilter { + Q_OBJECT + +public: + MagickImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~MagickImport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // MAGICKIMPORT_H_ + diff --git a/filters/krita/jpeg/Makefile.am b/filters/krita/jpeg/Makefile.am new file mode 100644 index 00000000..5795b114 --- /dev/null +++ b/filters/krita/jpeg/Makefile.am @@ -0,0 +1,45 @@ +noinst_LTLIBRARIES = libkritaconverter.la +kde_module_LTLIBRARIES = libkritajpegimport.la libkritajpegexport.la + +libkritaconverter_la_LDFLAGS = $(all_libraries) +libkritaconverter_la_SOURCES = kis_jpeg_converter.cc iccjpeg.c +libkritaconverter_la_LIBADD = $(top_builddir)/filters/krita/libkisexif/libkisexif.la + +libkritajpegexport_la_LDFLAGS = -avoid-version -module -no-undefined \ + $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) +libkritajpegexport_la_LIBADD = $(top_builddir)/krita/libkritacommon.la \ + libkritaconverter.la $(KOFFICE_LIBS) -ljpeg -lexif + +libkritajpegimport_la_LDFLAGS = -avoid-version -module -no-undefined \ + $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) +libkritajpegimport_la_LIBADD = $(top_builddir)/krita/libkritacommon.la \ + libkritaconverter.la $(KOFFICE_LIBS) -ljpeg -lexif + +INCLUDES= \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + -I$(top_srcdir)/filters/krita/libkisexif \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) \ + $(all_includes) + +service_DATA = krita_jpeg_import.desktop krita_jpeg_export.desktop +servicedir = $(kde_servicesdir) + +kdelnk_DATA = krita_jpeg.desktop +kdelnkdir = $(kde_appsdir)/.hidden + +libkritajpegimport_la_SOURCES = kis_jpeg_import.cc +libkritajpegexport_la_SOURCES = kis_wdg_options_jpeg.ui kis_jpeg_export.cc + +METASOURCES = AUTO + + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + diff --git a/filters/krita/jpeg/configure.in.bot b/filters/krita/jpeg/configure.in.bot new file mode 100644 index 00000000..8ebad273 --- /dev/null +++ b/filters/krita/jpeg/configure.in.bot @@ -0,0 +1,7 @@ +if test -z "$LIBJPEG" -o -z "$LIBEXIF"; then + echo "" + echo "You're missing libjpeg or libexif 0.6.12 or later (binaries and/or headers)." + echo "krita won't be able to import/export jpeg" + echo "" + all_tests=bad +fi diff --git a/filters/krita/jpeg/iccjpeg.c b/filters/krita/jpeg/iccjpeg.c new file mode 100644 index 00000000..fefa9509 --- /dev/null +++ b/filters/krita/jpeg/iccjpeg.c @@ -0,0 +1,270 @@ +/* + * Little cms + * Copyright (C) 1998-2004 Marti Maria + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * iccprofile.c + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. If you need to do that, + * change all the "unsigned int" variables to "INT32". You'll also need + * to find a malloc() replacement that can allocate more than 64K. + */ + +#include "iccjpeg.h" +#include <stdlib.h> /* define malloc() */ + + +/* + * Since an ICC profile can be larger than the maximum size of a JPEG marker + * (64K), we need provisions to split it into multiple markers. The format + * defined by the ICC specifies one or more APP2 markers containing the + * following data: + * Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte) + * Number of markers Total number of APP2's used (1 byte) + * Profile data (remainder of APP2 data) + * Decoders should use the marker sequence numbers to reassemble the profile, + * rather than assuming that the APP2 markers appear in the correct sequence. + */ + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +void +write_icc_profile (j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len) +{ + unsigned int num_markers; /* total number of markers we'll write */ + int cur_marker = 1; /* per spec, counting starts at 1 */ + unsigned int length; /* number of bytes to write in this marker */ + + /* Calculate the number of markers we'll need, rounding up of course */ + num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER; + if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len) + num_markers++; + + while (icc_data_len > 0) { + /* length of profile to put in this marker */ + length = icc_data_len; + if (length > MAX_DATA_BYTES_IN_MARKER) + length = MAX_DATA_BYTES_IN_MARKER; + icc_data_len -= length; + + /* Write the JPEG marker header (APP2 code and marker length) */ + jpeg_write_m_header(cinfo, ICC_MARKER, + (unsigned int) (length + ICC_OVERHEAD_LEN)); + + /* Write the marker identifying string "ICC_PROFILE" (null-terminated). + * We code it in this less-than-transparent way so that the code works + * even if the local character set is not ASCII. + */ + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x5F); + jpeg_write_m_byte(cinfo, 0x50); + jpeg_write_m_byte(cinfo, 0x52); + jpeg_write_m_byte(cinfo, 0x4F); + jpeg_write_m_byte(cinfo, 0x46); + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x4C); + jpeg_write_m_byte(cinfo, 0x45); + jpeg_write_m_byte(cinfo, 0x0); + + /* Add the sequencing info */ + jpeg_write_m_byte(cinfo, cur_marker); + jpeg_write_m_byte(cinfo, (int) num_markers); + + /* Add the profile data */ + while (length--) { + jpeg_write_m_byte(cinfo, *icc_data_ptr); + icc_data_ptr++; + } + cur_marker++; + } +} + + +/* + * Prepare for reading an ICC profile + */ + +void +setup_read_icc_profile (j_decompress_ptr cinfo) +{ + /* Tell the library to keep any APP2 data it may find */ + jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF); +} + + +/* + * Handy subroutine to test whether a saved marker is an ICC profile marker. + */ + +static boolean +marker_is_icc (jpeg_saved_marker_ptr marker) +{ + return + marker->marker == ICC_MARKER && + marker->data_length >= ICC_OVERHEAD_LEN && + /* verify the identifying string */ + GETJOCTET(marker->data[0]) == 0x49 && + GETJOCTET(marker->data[1]) == 0x43 && + GETJOCTET(marker->data[2]) == 0x43 && + GETJOCTET(marker->data[3]) == 0x5F && + GETJOCTET(marker->data[4]) == 0x50 && + GETJOCTET(marker->data[5]) == 0x52 && + GETJOCTET(marker->data[6]) == 0x4F && + GETJOCTET(marker->data[7]) == 0x46 && + GETJOCTET(marker->data[8]) == 0x49 && + GETJOCTET(marker->data[9]) == 0x4C && + GETJOCTET(marker->data[10]) == 0x45 && + GETJOCTET(marker->data[11]) == 0x0; +} + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + * + * NOTE: if the file contains invalid ICC APP2 markers, we just silently + * return FALSE. You might want to issue an error message instead. + */ + +boolean +read_icc_profile (j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len) +{ + jpeg_saved_marker_ptr marker; + int num_markers = 0; + int seq_no; + JOCTET *icc_data; + unsigned int total_length; +#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */ + char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */ + unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */ + unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */ + + *icc_data_ptr = NULL; /* avoid confusion if FALSE return */ + *icc_data_len = 0; + + /* This first pass over the saved markers discovers whether there are + * any ICC markers and verifies the consistency of the marker numbering. + */ + + for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++) + marker_present[seq_no] = 0; + + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + if (num_markers == 0) + num_markers = GETJOCTET(marker->data[13]); + else if (num_markers != GETJOCTET(marker->data[13])) + return FALSE; /* inconsistent num_markers fields */ + seq_no = GETJOCTET(marker->data[12]); + if (seq_no <= 0 || seq_no > num_markers) + return FALSE; /* bogus sequence number */ + if (marker_present[seq_no]) + return FALSE; /* duplicate sequence numbers */ + marker_present[seq_no] = 1; + data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN; + } + } + + if (num_markers == 0) + return FALSE; + + /* Check for missing markers, count total space needed, + * compute offset of each marker's part of the data. + */ + + total_length = 0; + for (seq_no = 1; seq_no <= num_markers; seq_no++) { + if (marker_present[seq_no] == 0) + return FALSE; /* missing sequence number */ + data_offset[seq_no] = total_length; + total_length += data_length[seq_no]; + } + + if (total_length <= 0) + return FALSE; /* found only empty markers? */ + + /* Allocate space for assembled data */ + icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET)); + if (icc_data == NULL) + return FALSE; /* oops, out of memory */ + + /* and fill it in */ + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + JOCTET FAR *src_ptr; + JOCTET *dst_ptr; + unsigned int length; + seq_no = GETJOCTET(marker->data[12]); + dst_ptr = icc_data + data_offset[seq_no]; + src_ptr = marker->data + ICC_OVERHEAD_LEN; + length = data_length[seq_no]; + while (length--) { + *dst_ptr++ = *src_ptr++; + } + } + } + + *icc_data_ptr = icc_data; + *icc_data_len = total_length; + + return TRUE; +} diff --git a/filters/krita/jpeg/iccjpeg.h b/filters/krita/jpeg/iccjpeg.h new file mode 100644 index 00000000..8828dc99 --- /dev/null +++ b/filters/krita/jpeg/iccjpeg.h @@ -0,0 +1,99 @@ +/* + * Little cms + * Copyright (C) 1998-2004 Marti Maria + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * iccprofile.h + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. See iccprofile.c + * for details. + */ +#ifndef ICCJPEG +#define ICCJPEG + +#include <stdio.h> /* needed to define "FILE", "NULL" */ +#include "jpeglib.h" + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +extern void write_icc_profile JPP((j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len)); + + +/* + * Reading a JPEG file that may contain an ICC profile requires two steps: + * + * 1. After jpeg_create_decompress() but before jpeg_read_header(), + * call setup_read_icc_profile(). This routine tells the IJG library + * to save in memory any APP2 markers it may find in the file. + * + * 2. After jpeg_read_header(), call read_icc_profile() to find out + * whether there was a profile and obtain it if so. + */ + + +/* + * Prepare for reading an ICC profile + */ + +extern void setup_read_icc_profile JPP((j_decompress_ptr cinfo)); + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + */ + +extern boolean read_icc_profile JPP((j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len)); + +#endif diff --git a/filters/krita/jpeg/kis_jpeg_converter.cc b/filters/krita/jpeg/kis_jpeg_converter.cc new file mode 100644 index 00000000..2ceaa68c --- /dev/null +++ b/filters/krita/jpeg/kis_jpeg_converter.cc @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_jpeg_converter.h" + +#include <stdio.h> + +extern "C" { +#include <iccjpeg.h> +} + +#include <qfile.h> + +#include <kapplication.h> +#include <kmessagebox.h> +#include <klocale.h> + +#include <KoDocumentInfo.h> + +#include <kio/netaccess.h> + +#include <kis_abstract_colorspace.h> +#include <kis_colorspace_factory_registry.h> +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_iterators_pixel.h> +#include <kis_paint_layer.h> +#include <kis_group_layer.h> +#include <kis_meta_registry.h> +#include <kis_profile.h> + +#include <kis_exif_io.h> + +extern "C" { +#include <libexif/exif-loader.h> +#include <libexif/exif-utils.h> +} + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + +namespace { + + J_COLOR_SPACE getColorTypeforColorSpace( KisColorSpace * cs) + { + if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) + { + return JCS_GRAYSCALE; + } + if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) + { + return JCS_RGB; + } + if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") ) + { + return JCS_CMYK; + } + KMessageBox::error(0, i18n("Cannot export images in %1.\n").arg(cs->id().name()) ) ; + return JCS_UNKNOWN; + } + + QString getColorSpaceForColorType(J_COLOR_SPACE color_type) { + kdDebug(41008) << "color_type = " << color_type << endl; + if(color_type == JCS_GRAYSCALE) + { + return "GRAYA"; + } else if(color_type == JCS_RGB) { + return "RGBA"; + } else if(color_type == JCS_CMYK) { + return "CMYK"; + } + return ""; + } + +} + +KisJPEGConverter::KisJPEGConverter(KisDoc *doc, KisUndoAdapter *adapter) +{ + m_doc = doc; + m_adapter = adapter; + m_job = 0; + m_stop = false; +} + +KisJPEGConverter::~KisJPEGConverter() +{ +} + +KisImageBuilder_Result KisJPEGConverter::decode(const KURL& uri) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + // open the file + FILE *fp = fopen(QFile::encodeName(uri.path()), "rb"); + if (!fp) + { + return (KisImageBuilder_RESULT_NOT_EXIST); + } + jpeg_stdio_src(&cinfo, fp); + + jpeg_save_markers (&cinfo, JPEG_COM, 0xFFFF); + /* Save APP0..APP15 markers */ + for (int m = 0; m < 16; m++) + jpeg_save_markers (&cinfo, JPEG_APP0 + m, 0xFFFF); + + +// setup_read_icc_profile(&cinfo); + // read header + jpeg_read_header(&cinfo, true); + + // start reading + jpeg_start_decompress(&cinfo); + + // Get the colorspace + QString csName = getColorSpaceForColorType(cinfo.out_color_space); + if(csName.isEmpty()) { + kdDebug(41008) << "unsupported colorspace : " << cinfo.out_color_space << endl; + jpeg_destroy_decompress(&cinfo); + fclose(fp); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + uchar* profile_data; + uint profile_len; + KisProfile* profile = 0; + QByteArray profile_rawdata; + if( read_icc_profile (&cinfo, &profile_data, &profile_len)) + { + profile_rawdata.resize(profile_len); + memcpy(profile_rawdata.data(), profile_data, profile_len); + cmsHPROFILE hProfile = cmsOpenProfileFromMem(profile_data, (DWORD)profile_len); + + if (hProfile != (cmsHPROFILE) NULL) { + profile = new KisProfile( profile_rawdata); + Q_CHECK_PTR(profile); + kdDebug(41008) << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo() << endl; + if(!profile->isSuitableForOutput()) + { + kdDebug(41008) << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile" << endl; // TODO: in ko2 popup a selection menu to inform the user + } + } + } + + // Retrieve a pointer to the colorspace + KisColorSpace* cs; + if (profile && profile->isSuitableForOutput()) + { + kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n"; + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile); + } + else + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),""); + + if(cs == 0) + { + kdDebug(41008) << "unknown colorspace" << endl; + jpeg_destroy_decompress(&cinfo); + fclose(fp); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + + // Create the cmsTransform if needed + + cmsHTRANSFORM transform = 0; + if(profile && !profile->isSuitableForOutput()) + { + transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(), + cs->getProfile()->profile() , cs->colorSpaceType(), + INTENT_PERCEPTUAL, 0); + } + + // Creating the KisImageSP + if( ! m_img) { + m_img = new KisImage(m_doc->undoAdapter(), cinfo.image_width, cinfo.image_height, cs, "built image"); + Q_CHECK_PTR(m_img); + if(profile && !profile->isSuitableForOutput()) + { + m_img -> addAnnotation( new KisAnnotation( profile->productName(), "", profile_rawdata) ); + } + } + + KisPaintLayerSP layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), Q_UINT8_MAX); + + // Read exif information if any + + // Read data + JSAMPROW row_pointer = new JSAMPLE[cinfo.image_width *cinfo.num_components]; + + for (; cinfo.output_scanline < cinfo.image_height;) { + KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, cinfo.output_scanline, cinfo.image_width, true); + jpeg_read_scanlines(&cinfo, &row_pointer, 1); + Q_UINT8 *src = row_pointer; + switch(cinfo.out_color_space) + { + case JCS_GRAYSCALE: + while (!it.isDone()) { + Q_UINT8 *d = it.rawData(); + d[0] = *(src++); + if(transform) cmsDoTransform(transform, d, d, 1); + d[1] = Q_UINT8_MAX; + ++it; + } + break; + case JCS_RGB: + while (!it.isDone()) { + Q_UINT8 *d = it.rawData(); + d[2] = *(src++); + d[1] = *(src++); + d[0] = *(src++); + if(transform) cmsDoTransform(transform, d, d, 1); + d[3] = Q_UINT8_MAX; + ++it; + } + break; + case JCS_CMYK: + while (!it.isDone()) { + Q_UINT8 *d = it.rawData(); + d[0] = Q_UINT8_MAX - *(src++); + d[1] = Q_UINT8_MAX - *(src++); + d[2] = Q_UINT8_MAX - *(src++); + d[3] = Q_UINT8_MAX - *(src++); + if(transform) cmsDoTransform(transform, d, d, 1); + d[4] = Q_UINT8_MAX; + ++it; + } + break; + default: + return KisImageBuilder_RESULT_UNSUPPORTED; + } + } + + m_img->addLayer(layer.data(), m_img->rootLayer(), 0); + + // Read exif informations + + kdDebug(41008) << "Looking for exif information" << endl; + + for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != NULL; marker = marker->next) { + kdDebug(41008) << "Marker is " << marker->marker << endl; + if (marker->marker != (JOCTET) (JPEG_APP0 + 1) || + marker->data_length < 14) + continue; /* Exif data is in an APP1 marker of at least 14 octets */ + + if (GETJOCTET (marker->data[0]) != (JOCTET) 0x45 || + GETJOCTET (marker->data[1]) != (JOCTET) 0x78 || + GETJOCTET (marker->data[2]) != (JOCTET) 0x69 || + GETJOCTET (marker->data[3]) != (JOCTET) 0x66 || + GETJOCTET (marker->data[4]) != (JOCTET) 0x00 || + GETJOCTET (marker->data[5]) != (JOCTET) 0x00) + continue; /* no Exif header */ + kdDebug(41008) << "Found exif information of length : "<< marker->data_length << endl; + KisExifIO exifIO(layer->paintDevice()->exifInfo()); + exifIO.readExifFromMem( marker->data , marker->data_length ); + // Interpret orientation tag + ExifValue v; + if( layer->paintDevice()->exifInfo()->getValue("Orientation", v) && v.type() == ExifValue::EXIF_TYPE_SHORT) + { + switch(v.asShort(0)) // + { + case 2: + layer->paintDevice()->mirrorY(); + break; + case 3: + image()->rotate(M_PI, 0); + break; + case 4: + layer->paintDevice()->mirrorX(); + break; + case 5: + image()->rotate(M_PI/2, 0); + layer->paintDevice()->mirrorY(); + break; + case 6: + image()->rotate(M_PI/2, 0); + break; + case 7: + image()->rotate(M_PI/2, 0); + layer->paintDevice()->mirrorX(); + break; + case 8: + image()->rotate(-M_PI/2 + 2*M_PI, 0); + break; + default: + break; + } + v.setValue(0, (Q_UINT16)1); + layer->paintDevice()->exifInfo()->setValue("Orientation", v); + } + break; + } + + // Finish decompression + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(fp); + delete []row_pointer; + return KisImageBuilder_RESULT_OK; +} + + + +KisImageBuilder_Result KisJPEGConverter::buildImage(const KURL& uri) +{ + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) { + return KisImageBuilder_RESULT_NOT_EXIST; + } + + // We're not set up to handle asynchronous loading at the moment. + KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; + QString tmpFile; + + if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) { + KURL uriTF; + uriTF.setPath( tmpFile ); + result = decode(uriTF); + KIO::NetAccess::removeTempFile(tmpFile); + } + + return result; +} + + +KisImageSP KisJPEGConverter::image() +{ + return m_img; +} + + +KisImageBuilder_Result KisJPEGConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisJPEGOptions options, KisExifInfo* exifInfo) +{ + if (!layer) + return KisImageBuilder_RESULT_INVALID_ARG; + + KisImageSP img = layer -> image(); + if (!img) + return KisImageBuilder_RESULT_EMPTY; + + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!uri.isLocalFile()) + return KisImageBuilder_RESULT_NOT_LOCAL; + // Open file for writing + FILE *fp = fopen(QFile::encodeName(uri.path()), "wb"); + if (!fp) + { + return (KisImageBuilder_RESULT_FAILURE); + } + uint height = img->height(); + uint width = img->width(); + // Initialize structure + struct jpeg_compress_struct cinfo; + jpeg_create_compress(&cinfo); + // Initialize error output + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + // Initialize output stream + jpeg_stdio_dest(&cinfo, fp); + + cinfo.image_width = width; // image width and height, in pixels + cinfo.image_height = height; + cinfo.input_components = img->colorSpace()->nColorChannels(); // number of color channels per pixel */ + J_COLOR_SPACE color_type = getColorTypeforColorSpace(img->colorSpace()); + if(color_type == JCS_UNKNOWN) + { + KIO::del(uri); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + cinfo.in_color_space = color_type; // colorspace of input image + + + // Set default compression parameters + jpeg_set_defaults(&cinfo); + // Customize them + jpeg_set_quality(&cinfo, options.quality, true); + + if(options.progressive) + { + jpeg_simple_progression (&cinfo); + } + + // Start compression + jpeg_start_compress(&cinfo, true); + // Save exif information if any available + if(exifInfo) + { + kdDebug(41008) << "Trying to save exif information" << endl; + KisExifIO exifIO(exifInfo); + unsigned char* exif_data; + unsigned int exif_size; + exifIO.saveExifToMem( &exif_data, &exif_size); + kdDebug(41008) << "Exif informations size is " << exif_size << endl; + if (exif_size < MAX_DATA_BYTES_IN_MARKER) + { + jpeg_write_marker(&cinfo, JPEG_APP0 + 1, exif_data, exif_size); + } else { + kdDebug(41008) << "exif informations couldn't be saved." << endl; + } + } + + + // Save annotation + vKisAnnotationSP_it it = annotationsStart; + while(it != annotationsEnd) { + if (!(*it) || (*it) -> type() == QString()) { + kdDebug(41008) << "Warning: empty annotation" << endl; + ++it; + continue; + } + + kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl; + + if ((*it) -> type().startsWith("krita_attribute:")) { // Attribute + // FIXME + kdDebug(41008) << "can't save this annotation : " << (*it) -> type() << endl; + } else { // Profile + //char* name = new char[(*it)->type().length()+1]; + write_icc_profile(& cinfo, (uchar*)(*it)->annotation().data(), (*it)->annotation().size()); + } + ++it; + } + + + // Write data information + + JSAMPROW row_pointer = new JSAMPLE[width*cinfo.input_components]; + int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels(); + + for (; cinfo.next_scanline < height;) { + KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, cinfo.next_scanline, width, false); + Q_UINT8 *dst = row_pointer; + switch(color_type) + { + case JCS_GRAYSCALE: + if(color_nb_bits == 16) + { + while (!it.isDone()) { + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + *(dst++) = d[0] / Q_UINT8_MAX; + ++it; + } + } else { + while (!it.isDone()) { + const Q_UINT8 *d = it.rawData(); + *(dst++) = d[0]; + ++it; + } + } + break; + case JCS_RGB: + if(color_nb_bits == 16) + { + while (!it.isDone()) { + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + *(dst++) = d[2] / Q_UINT8_MAX; + *(dst++) = d[1] / Q_UINT8_MAX; + *(dst++) = d[0] / Q_UINT8_MAX; + ++it; + } + } else { + while (!it.isDone()) { + const Q_UINT8 *d = it.rawData(); + *(dst++) = d[2]; + *(dst++) = d[1]; + *(dst++) = d[0]; + ++it; + } + } + break; + case JCS_CMYK: + if(color_nb_bits == 16) + { + while (!it.isDone()) { + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + *(dst++) = Q_UINT8_MAX - d[0] / Q_UINT8_MAX; + *(dst++) = Q_UINT8_MAX - d[1] / Q_UINT8_MAX; + *(dst++) = Q_UINT8_MAX - d[2] / Q_UINT8_MAX; + *(dst++) = Q_UINT8_MAX - d[3] / Q_UINT8_MAX; + ++it; + } + } else { + while (!it.isDone()) { + const Q_UINT8 *d = it.rawData(); + *(dst++) = Q_UINT8_MAX - d[0]; + *(dst++) = Q_UINT8_MAX - d[1]; + *(dst++) = Q_UINT8_MAX - d[2]; + *(dst++) = Q_UINT8_MAX - d[3]; + ++it; + } + } + break; + default: + KIO::del(uri); + return KisImageBuilder_RESULT_UNSUPPORTED; + } + jpeg_write_scanlines(&cinfo, &row_pointer, 1); + } + + + // Writting is over + jpeg_finish_compress(&cinfo); + fclose(fp); + + delete [] row_pointer; + // Free memory + jpeg_destroy_compress(&cinfo); + + return KisImageBuilder_RESULT_OK; +} + + +void KisJPEGConverter::cancel() +{ + m_stop = true; +} + +#include "kis_jpeg_converter.moc" + diff --git a/filters/krita/jpeg/kis_jpeg_converter.h b/filters/krita/jpeg/kis_jpeg_converter.h new file mode 100644 index 00000000..99b7eadf --- /dev/null +++ b/filters/krita/jpeg/kis_jpeg_converter.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_JPEG_CONVERTER_H_ +#define _KIS_JPEG_CONVERTER_H_ + +#include <stdio.h> + +extern "C" { +#include <jpeglib.h> +} + +#include <qvaluevector.h> + +#include <kio/job.h> + +#include <kis_progress_subject.h> + +#include "kis_types.h" +#include "kis_global.h" +#include "kis_annotation.h" +class KisDoc; +class KisUndoAdapter; +class KisExifInfo; + +/** + * Image import/export plugins can use these results to report about success or failure. + */ +enum KisImageBuilder_Result { + KisImageBuilder_RESULT_FAILURE = -400, + KisImageBuilder_RESULT_NOT_EXIST = -300, + KisImageBuilder_RESULT_NOT_LOCAL = -200, + KisImageBuilder_RESULT_BAD_FETCH = -100, + KisImageBuilder_RESULT_INVALID_ARG = -50, + KisImageBuilder_RESULT_OK = 0, + KisImageBuilder_RESULT_PROGRESS = 1, + KisImageBuilder_RESULT_EMPTY = 100, + KisImageBuilder_RESULT_BUSY = 150, + KisImageBuilder_RESULT_NO_URI = 200, + KisImageBuilder_RESULT_UNSUPPORTED = 300, + KisImageBuilder_RESULT_INTR = 400, + KisImageBuilder_RESULT_PATH = 500, + KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 +}; + +struct KisJPEGOptions { + int quality; + bool progressive; +}; + +class KisJPEGConverter : public KisProgressSubject { + Q_OBJECT + public: + KisJPEGConverter(KisDoc *doc, KisUndoAdapter *adapter); + virtual ~KisJPEGConverter(); + public: + KisImageBuilder_Result buildImage(const KURL& uri); + KisImageBuilder_Result buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisJPEGOptions options, KisExifInfo* exifInfo); + /** Retrieve the constructed image + */ + KisImageSP image(); + public slots: + virtual void cancel(); + private: + KisImageBuilder_Result decode(const KURL& uri); + private: + KisImageSP m_img; + KisDoc *m_doc; + KisUndoAdapter *m_adapter; + bool m_stop; + KIO::TransferJob *m_job; +}; + +#endif diff --git a/filters/krita/jpeg/kis_jpeg_export.cc b/filters/krita/jpeg/kis_jpeg_export.cc new file mode 100644 index 00000000..39419833 --- /dev/null +++ b/filters/krita/jpeg/kis_jpeg_export.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_jpeg_export.h" + +#include <qcheckbox.h> +#include <qslider.h> + +#include <kapplication.h> +#include <kdialogbase.h> +#include <kgenericfactory.h> + +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_group_layer.h> +#include <kis_paint_layer.h> +#include <kis_progress_display_interface.h> +#include <kis_layer_visitor.h> + +#include "kis_jpeg_converter.h" +#include "kis_wdg_options_jpeg.h" + + +class KisExifInfoVisitor : public KisLayerVisitor +{ + public: + KisExifInfoVisitor() : + m_exifInfo(0), + m_countPaintLayer(0) + { }; + public: + virtual bool visit(KisPaintLayer* layer) { + m_countPaintLayer++; + if( layer->paintDevice()->hasExifInfo()) + m_exifInfo = layer->paintDevice()->exifInfo(); + return true; + }; + virtual bool visit(KisGroupLayer* layer) + { + kdDebug(41008) << "Visiting on grouplayer " << layer->name() << "\n"; + KisLayerSP child = layer->firstChild(); + while (child) { + child->accept(*this); + child = child->nextSibling(); + } + return true; + } + virtual bool visit(KisPartLayer *) { return true; }; + virtual bool visit(KisAdjustmentLayer* ) { return true; }; + public: + inline uint countPaintLayer() { return m_countPaintLayer; } + inline KisExifInfo* exifInfo() {return m_exifInfo; } + private: + KisExifInfo* m_exifInfo; + uint m_countPaintLayer; +}; + + +typedef KGenericFactory<KisJPEGExport, KoFilter> KisJPEGExportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritajpegexport, KisJPEGExportFactory("kofficefilters")) + +KisJPEGExport::KisJPEGExport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisJPEGExport::~KisJPEGExport() +{ +} + +KoFilter::ConversionStatus KisJPEGExport::convert(const QCString& from, const QCString& to) +{ + kdDebug(41008) << "JPEG export! From: " << from << ", To: " << to << "\n"; + + if (from != "application/x-krita") + return KoFilter::NotImplemented; + + + KDialogBase* kdb = new KDialogBase(0, "", false, i18n("JPEG Export Options"), KDialogBase::Ok | KDialogBase::Cancel); + + KisWdgOptionsJPEG* wdg = new KisWdgOptionsJPEG(kdb); + kdb->setMainWidget(wdg); + kapp->restoreOverrideCursor(); + if(kdb->exec() == QDialog::Rejected) + { + return KoFilter::OK; // FIXME Cancel doesn't exist :( + } + KisJPEGOptions options; + options.progressive = wdg->progressive->isChecked(); + options.quality = wdg->qualityLevel->value(); + + delete kdb; + // XXX: Add dialog about flattening layers here + + KisDoc *output = dynamic_cast<KisDoc*>(m_chain->inputDocument()); + QString filename = m_chain->outputFile(); + + if (!output) + return KoFilter::CreationError; + + + if (filename.isEmpty()) return KoFilter::FileNotFound; + + KURL url; + url.setPath(filename); + + KisImageSP img = output->currentImage(); + Q_CHECK_PTR(img); + + KisJPEGConverter kpc(output, output->undoAdapter()); + + KisPaintDeviceSP pd = new KisPaintDevice(*img->projection()); + KisPaintLayerSP l = new KisPaintLayer(img, "projection", OPACITY_OPAQUE, pd); + + vKisAnnotationSP_it beginIt = img->beginAnnotations(); + vKisAnnotationSP_it endIt = img->endAnnotations(); + KisImageBuilder_Result res; + + KisExifInfoVisitor eIV; + eIV.visit( img->rootLayer() ); + + KisExifInfo* eI = 0; + if(eIV.countPaintLayer() == 1) + eI = eIV.exifInfo(); + + if ( (res = kpc.buildFile(url, l, beginIt, endIt, options, eI)) == KisImageBuilder_RESULT_OK) { + kdDebug(41008) << "success !" << endl; + return KoFilter::OK; + } + kdDebug(41008) << " Result = " << res << endl; + return KoFilter::InternalError; +} + +#include <kis_jpeg_export.moc> + diff --git a/filters/krita/jpeg/kis_jpeg_export.h b/filters/krita/jpeg/kis_jpeg_export.h new file mode 100644 index 00000000..38e04b20 --- /dev/null +++ b/filters/krita/jpeg/kis_jpeg_export.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_JPEG_EXPORT_H_ +#define _KIS_JPEG_EXPORT_H_ + +#include <KoFilter.h> + +class KisJPEGExport : public KoFilter { + Q_OBJECT + public: + KisJPEGExport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisJPEGExport(); + public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif diff --git a/filters/krita/jpeg/kis_jpeg_import.cc b/filters/krita/jpeg/kis_jpeg_import.cc new file mode 100644 index 00000000..e9b867e0 --- /dev/null +++ b/filters/krita/jpeg/kis_jpeg_import.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_jpeg_import.h" + +#include <kgenericfactory.h> + +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_progress_display_interface.h> +#include <kis_view.h> + +#include "kis_jpeg_converter.h" + +typedef KGenericFactory<KisJPEGImport, KoFilter> JPEGImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritajpegimport, JPEGImportFactory("kofficefilters")) + +KisJPEGImport::KisJPEGImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisJPEGImport::~KisJPEGImport() +{ +} + +KoFilter::ConversionStatus KisJPEGImport::convert(const QCString&, const QCString& to) +{ + kdDebug(41008) << "Importing using JPEGImport!\n"; + + if (to != "application/x-krita") + return KoFilter::BadMimeType; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + KisView * view = static_cast<KisView*>(doc -> views().getFirst()); + + QString filename = m_chain -> inputFile(); + + if (!doc) + return KoFilter::CreationError; + + doc->prepareForImport(); + + + if (!filename.isEmpty()) { + + KURL url; + url.setPath(filename); + + if (url.isEmpty()) + return KoFilter::FileNotFound; + + KisJPEGConverter ib(doc, doc -> undoAdapter()); + + if (view != 0) + view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true); + + switch (ib.buildImage(url)) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KoFilter::NotImplemented; + break; + case KisImageBuilder_RESULT_INVALID_ARG: + return KoFilter::BadMimeType; + break; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_LOCAL: + return KoFilter::FileNotFound; + break; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KoFilter::ParsingError; + break; + case KisImageBuilder_RESULT_FAILURE: + return KoFilter::InternalError; + break; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KoFilter::OK; + default: + break; + } + + } + return KoFilter::StorageCreationError; +} + +#include <kis_jpeg_import.moc> + diff --git a/filters/krita/jpeg/kis_jpeg_import.h b/filters/krita/jpeg/kis_jpeg_import.h new file mode 100644 index 00000000..6e60c84b --- /dev/null +++ b/filters/krita/jpeg/kis_jpeg_import.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef _KIS_JPEG_IMPORT_H_ +#define _KIS_JPEG_IMPORT_H_ + +#include <KoFilter.h> + +class KisJPEGImport : public KoFilter { + Q_OBJECT + public: + KisJPEGImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisJPEGImport(); + public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif diff --git a/filters/krita/jpeg/kis_wdg_options_jpeg.ui b/filters/krita/jpeg/kis_wdg_options_jpeg.ui new file mode 100644 index 00000000..17f5eb36 --- /dev/null +++ b/filters/krita/jpeg/kis_wdg_options_jpeg.ui @@ -0,0 +1,149 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KisWdgOptionsJPEG</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KisWdgOptionsJPEG</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>167</width> + <height>87</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Quality:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider"> + <property name="name"> + <cstring>qualityLevel</cstring> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="maxValue"> + <number>100</number> + </property> + <property name="lineStep"> + <number>1</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="value"> + <number>80</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Below</enum> + </property> + <property name="tickInterval"> + <number>10</number> + </property> + <property name="toolTip" stdset="0"> + <string>These settings determine how much information is lost during compression</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Smallest</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Best</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>progressive</cstring> + </property> + <property name="text"> + <string>Pr&ogressive</string> + </property> + <property name="toolTip" stdset="0"> + <string>Use progressive when publishing on the Internet</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Progressive is useful if you intend to publish your image on the Internet.<br> +Enabling progressive will cause the image to be displayed by the browser even while downloading.</p></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/filters/krita/jpeg/krita_jpeg.desktop b/filters/krita/jpeg/krita_jpeg.desktop new file mode 100644 index 00000000..48d669ca --- /dev/null +++ b/filters/krita/jpeg/krita_jpeg.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Categories= +Exec=krita %u +GenericName=Application for Drawing and Handling of Images +GenericName[bg]=Приложение за рисуване и обработка на изображения +GenericName[ca]=Aplicació per a dibuix i modificació d'imatges +GenericName[da]=Tegne- og billedbehandlingsprogram +GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern +GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων +GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj +GenericName[es]=Aplicación para dibujo y manipulación de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر +GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn +GenericName[fr]=Application pour dessiner et manipuler des images +GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen +GenericName[gl]=Aplicación de Debuxo e Manipulación de Imaxes +GenericName[he]=יישום לצביעה וניהול תמונות +GenericName[hu]=Rajzoló és képkezelő +GenericName[is]=Teikni og myndvinnsluforrit +GenericName[it]=Applicazione di disegno e gestione di immagini +GenericName[ja]=描画と画像操作のためのアプリケーション +GenericName[km]=កម្មវិធីសម្រាប់គូរ និងដោះស្រាយនៃរូបភាព +GenericName[lv]=Programma zīmēšanai un attēlu apstrādei +GenericName[nb]=Program for tegning og bildehåndtering +GenericName[nds]=Programm för't Teken un Bildhanteren +GenericName[ne]=रेखाचित्र र छविहरूको ह्यान्डल गर्न अनुप्रयोग +GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken +GenericName[pl]=Program do rysowania i obróbki obrazków +GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens +GenericName[pt_BR]=Aplicação de Desenho e Manipulação de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Prográmma sárgumii ja govvegieđaheapmái +GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami +GenericName[sl]=Program za risanje in rokovanje s slikami +GenericName[sr]=Програм за цртање и обраду слика +GenericName[sr@Latn]=Program za crtanje i obradu slika +GenericName[sv]=Program för att rita och hantera bilder +GenericName[uk]=Програма для малювання і обробки зображень +GenericName[uz]=Rasm chizish dasturi +GenericName[uz@cyrillic]=Расм чизиш дастури +GenericName[zh_CN]=绘制和操纵图像的应用程序 +GenericName[zh_TW]=影像繪製與處理應用程式 +Icon=krita +MimeType=image/jpeg +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Path= +StartupNotify=true +Terminal=false +Type=Application +X-DCOP-ServiceType=multi +X-KDE-StartupNotify=true +X-KDE-SubstituteUID=false +X-KDE-Username= diff --git a/filters/krita/jpeg/krita_jpeg_export.desktop b/filters/krita/jpeg/krita_jpeg_export.desktop new file mode 100644 index 00000000..25929d8e --- /dev/null +++ b/filters/krita/jpeg/krita_jpeg_export.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Icon= +Name=Krita PNG Export Filter +Name[bg]=Филтър за експортиране от Krita в PNG +Name[br]=Sil ezporzh PNG evit Krita +Name[ca]=Filtre d'exportació PNG per a Krita +Name[da]=Krita PNG-eksportfilter +Name[de]=Krita PNG-Exportfilter +Name[el]=Φίλτρο εξαγωγής PNG του Krita +Name[eo]=Krita PNG-eksportfiltrilo +Name[es]=Filtro de exportación a PNG de Krita +Name[et]=Krita PNG ekspordifilter +Name[fa]=پالایۀ صادرات Krita PNG +Name[fi]=Krita PNG -viestisuodin +Name[fr]=Filtre d'exportation PNG de Krita +Name[fy]=Krita PNG Eksportfilter +Name[ga]=Scagaire Easpórtála PNG Krita +Name[gl]=Filtro de Exportación de PNG para Krita +Name[he]=Krita PNG מסנן יצוא +Name[hr]=Krita PNG filtar izvoza +Name[hu]=Krita PNG exportszűrő +Name[is]=Krita PNG útflutningssía +Name[it]=Filtro di esportazione PNG per Krita +Name[ja]=Krita PNG エクスポートフィルタ +Name[km]=តម្រងនាំចេញ PNG សម្រាប់ Krita +Name[lt]=Krita PNG eksportavimo filtras +Name[lv]=Krita PNG eksporta filtrs +Name[nb]=PNG-eksportfilter for Krita +Name[nds]=PNG-Exportfilter för Krita +Name[ne]=क्रिता पीएनजी निर्यात फिल्टर +Name[nl]=Krita PNG Exportfilter +Name[pl]=Filtr eksportu do formatu PNG dla Krita +Name[pt]=Filtro de Exportação de PNG para o Krita +Name[pt_BR]=Filtro de Exportação de PNG para o Krita +Name[ru]=Фильтр экспорта рисунков Krita в PNG +Name[se]=Krita PNG-olggosfievrridansilli +Name[sk]=Exportný filter Krita PNG +Name[sl]=Izvozni filter PNG za Krito +Name[sr]=Krita-ин филтер за извоз у PNG +Name[sr@Latn]=Krita-in filter za izvoz u PNG +Name[sv]=Krita PNG-exportfilter +Name[uk]=Фільтр експорту PNG для Krita +Name[uz]=Krita PNG eksport filteri +Name[uz@cyrillic]=Krita PNG экспорт филтери +Name[zh_CN]=Krita PNG 导出过滤器 +Name[zh_TW]=Krita PNG 匯出過濾程式 +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Export=image/jpeg +X-KDE-Import=application/x-krita +X-KDE-Library=libkritajpegexport +X-KDE-Weight=1 diff --git a/filters/krita/jpeg/krita_jpeg_import.desktop b/filters/krita/jpeg/krita_jpeg_import.desktop new file mode 100644 index 00000000..4b3772f7 --- /dev/null +++ b/filters/krita/jpeg/krita_jpeg_import.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Icon= +Name=Krita PNG Import Filter +Name[bg]=Филтър за импортиране от PNG в Krita +Name[br]=Sil enporzh PNG evit Krita +Name[ca]=Filtre d'importació PNG per a Krita +Name[da]=Krita PNG-importfilter +Name[de]=Krita PNG-Importfilter +Name[el]=Φίλτρο εισαγωγής PNG του Krita +Name[eo]=Krita PNG-importfiltrilo +Name[es]=Filtro de importación a PNG de Krita +Name[et]=Krita PNG impordifilter +Name[fa]=پالایۀ واردات Krita PNG +Name[fi]=Krita PNG -tuontisuodin +Name[fr]=Filtre d'importation PNG de Krita +Name[fy]=Krita PNG Ymportfilter +Name[ga]=Scagaire Iompórtála PNG Krita +Name[gl]=Filtro de Importación de PNG para Krita +Name[he]=Krita PNG מסנן יבוא +Name[hr]=Krita PNG filtar uvoza +Name[hu]=Krita PNG importszűrő +Name[is]=Krita PNG innflutningssía +Name[it]=Filtro di importazione PNG per Krita +Name[ja]=Krita PNG インポートフィルタ +Name[km]=តម្រងនាំចូល PNG សម្រាប់ Krita +Name[lt]=Krita PNG importavimo filtras +Name[lv]=Krita PNG importa filtrs +Name[nb]=PNG-importfilter for Krita +Name[nds]=PNG-Importfilter för Krita +Name[ne]=क्रिता पीएनजी आयात फिल्टर +Name[nl]=Krita PNG Importfilter +Name[pl]=Filtr importu z formatu PNG dla Krita +Name[pt]=Filtro de Importação de PNG para o Krita +Name[pt_BR]=Filtro de Importação de PNG para o Krita +Name[ru]=Фильтр импорта рисунков PNG в Krita +Name[se]=Krita PNG-olggosfievrridansilli +Name[sk]=PNG filter pre import do Krita +Name[sl]=Uvozni filter PNG za Krito +Name[sr]=Krita-ин филтер за увоз из PNG-а +Name[sr@Latn]=Krita-in filter za uvoz iz PNG-a +Name[sv]=Krita PNG-importfilter +Name[uk]=Фільтр імпорту PNG для Krita +Name[uz]=Krita PNG import filteri +Name[uz@cyrillic]=Krita PNG импорт филтери +Name[zh_CN]=Krita PNG 导入过滤器 +Name[zh_TW]=Krita PNG 匯入過濾程式 +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Export=application/x-krita +X-KDE-Import=image/jpeg +X-KDE-Library=libkritajpegimport +X-KDE-Weight=1 diff --git a/filters/krita/libkisexif/Makefile.am b/filters/krita/libkisexif/Makefile.am new file mode 100644 index 00000000..a7aa9b4b --- /dev/null +++ b/filters/krita/libkisexif/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) \ + $(all_includes) +METASOURCES = AUTO +libkisexif_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libkisexif.la + + +libkisexif_la_SOURCES = kis_exif_io.cpp diff --git a/filters/krita/libkisexif/kis_exif_io.cpp b/filters/krita/libkisexif/kis_exif_io.cpp new file mode 100644 index 00000000..a5e5dbd7 --- /dev/null +++ b/filters/krita/libkisexif/kis_exif_io.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_exif_io.h" + +#include <kdebug.h> + +extern "C" { +#include <libexif/exif-tag.h> +#include <libexif/exif-log.h> +} + +KisExifIO::KisExifIO(KisExifInfo* ei) : m_exifInfo(ei) +{ +} + +void KisExifIO::readExifFromFile( const char* fileName) +{ + readExifData( exif_data_new_from_file(fileName) ); +} + +void KisExifIO::readExifFromMem( const unsigned char* data , unsigned int size) +{ + readExifData( exif_data_new_from_data(data, size) ); +} + +void KisExifIO::saveExifToMem( unsigned char** data, unsigned int *size) +{ + ExifData* exifData = exif_data_new(); + writeExifData( exifData ); + exif_data_save_data( exifData, data, size); +} + +ExifValue::ExifType KisExifIO::format2type(ExifFormat format) +{ + switch(format) + { + case EXIF_FORMAT_BYTE: + return ExifValue::EXIF_TYPE_BYTE; + case EXIF_FORMAT_ASCII: + return ExifValue::EXIF_TYPE_ASCII; + case EXIF_FORMAT_SHORT: + return ExifValue::EXIF_TYPE_SHORT; + case EXIF_FORMAT_LONG: + return ExifValue::EXIF_TYPE_LONG; + case EXIF_FORMAT_RATIONAL: + return ExifValue::EXIF_TYPE_RATIONAL; + case EXIF_FORMAT_SBYTE: + return ExifValue::EXIF_TYPE_SBYTE; + case EXIF_FORMAT_SSHORT: + return ExifValue::EXIF_TYPE_SSHORT; + case EXIF_FORMAT_SLONG: + return ExifValue::EXIF_TYPE_SLONG; + case EXIF_FORMAT_SRATIONAL: + return ExifValue::EXIF_TYPE_SRATIONAL; + case EXIF_FORMAT_FLOAT: + return ExifValue::EXIF_TYPE_FLOAT; + case EXIF_FORMAT_DOUBLE: + return ExifValue::EXIF_TYPE_DOUBLE; + default: + case EXIF_FORMAT_UNDEFINED: + return ExifValue::EXIF_TYPE_UNDEFINED; + } +} + +void KisExifIO::readExifData( ExifData* exifData) +{ + ExifValue::ByteOrder bO; + if(exif_data_get_byte_order( exifData) == EXIF_BYTE_ORDER_MOTOROLA) + { + bO = ExifValue::BYTE_ORDER_MOTOROLA; + } else { + bO = ExifValue::BYTE_ORDER_INTEL; + } + static ExifIfd ifds[5] = { + EXIF_IFD_0, + EXIF_IFD_1, + EXIF_IFD_EXIF, + EXIF_IFD_INTEROPERABILITY, + EXIF_IFD_GPS + }; + for(int ifd = 0; ifd < 5; ifd ++) + { + ExifContent* content = exifData->ifd[ifds[ifd]]; + kdDebug() << "There are " << content->count << " values in ifd=" << ifd << endl; + for (uint i = 0; i < content->count; i++) + { + ExifEntry* entry = content->entries[i]; + QString tagname = exif_tag_get_name ( entry->tag ); +// kdDebug() << "found tag : " << tagname << endl; + // QString tagname = exif_tag_get_name_in_ifd ( entry->tag, EXIF_IFD_0 ); TODO: would be better to rely on 0.6.13 when it becomes more common, as it supports better other IFD (GPS and interoperrabilibity) + ExifValue value( format2type(entry->format), entry->data, entry->size, ifds[ifd], entry->components, bO ); +// exif_entry_dump( entry, 4); +// kdDebug() << "value = " << value.toString() << endl; + m_exifInfo->setValue( tagname, value); + } + } + +} + +ExifFormat KisExifIO::type2format( ExifValue::ExifType type) +{ + switch(type) + { + case ExifValue::EXIF_TYPE_BYTE: + return EXIF_FORMAT_BYTE; + case ExifValue::EXIF_TYPE_ASCII: + return EXIF_FORMAT_ASCII; + case ExifValue::EXIF_TYPE_SHORT: + return EXIF_FORMAT_SHORT; + case ExifValue::EXIF_TYPE_LONG: + return EXIF_FORMAT_LONG; + case ExifValue::EXIF_TYPE_RATIONAL: + return EXIF_FORMAT_RATIONAL; + case ExifValue::EXIF_TYPE_SBYTE: + return EXIF_FORMAT_SBYTE; + case ExifValue::EXIF_TYPE_SSHORT: + return EXIF_FORMAT_SSHORT; + case ExifValue::EXIF_TYPE_SLONG: + return EXIF_FORMAT_SLONG; + case ExifValue::EXIF_TYPE_SRATIONAL: + return EXIF_FORMAT_SRATIONAL; + case ExifValue::EXIF_TYPE_FLOAT: + return EXIF_FORMAT_FLOAT; + case ExifValue::EXIF_TYPE_DOUBLE: + return EXIF_FORMAT_DOUBLE; + default: + case ExifValue::EXIF_TYPE_UNDEFINED: + return EXIF_FORMAT_UNDEFINED; + } +} + + +void KisExifIO::writeExifData( ExifData* exifData) +{ + ExifValue::ByteOrder bO; + if(exif_data_get_byte_order( exifData) == EXIF_BYTE_ORDER_MOTOROLA) + { + bO = ExifValue::BYTE_ORDER_MOTOROLA; + } else { + bO = ExifValue::BYTE_ORDER_INTEL; + } + + for( KisExifInfo::evMap::const_iterator it = m_exifInfo->begin(); it != m_exifInfo->end(); ++it) + { + ExifValue ev = it.data(); + if(ev.ifd() != -1) + { + ExifEntry * entry = exif_entry_new(); + ExifContent* content = exifData->ifd[ev.ifd()]; + exif_content_add_entry(content, entry); + kdDebug() << "Saving tag:" << it.key() << " " << ev.toString() << endl; + ExifTag tag = exif_tag_from_name( it.key().ascii()); + entry->components = ev.components(); + entry->format = type2format( ev.type()); + entry->tag = tag; +// exif_entry_dump(entry, 2); + ev.convertToData(&entry->data, &entry->size, bO); + } + } +} diff --git a/filters/krita/libkisexif/kis_exif_io.h b/filters/krita/libkisexif/kis_exif_io.h new file mode 100644 index 00000000..58928811 --- /dev/null +++ b/filters/krita/libkisexif/kis_exif_io.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_EXIF_IO_H +#define KIS_EXIF_IO_H + +extern "C" { +#include <libexif/exif-data.h> +} + +#include <kis_exif_info.h> + +class KisExifIO { + public: + KisExifIO( KisExifInfo* ei); + public: + void readExifFromFile( const char* fileName ); + void readExifFromMem( const unsigned char* data , unsigned int size); + void saveExifToMem( unsigned char** data , unsigned int *size); + private: + void readExifData( ExifData* exifData ); + void writeExifData( ExifData* exifData ); + ExifFormat type2format( ExifValue::ExifType type); + ExifValue::ExifType format2type(ExifFormat format); + private: + KisExifInfo* m_exifInfo; +}; + +#endif diff --git a/filters/krita/magick/Makefile.am b/filters/krita/magick/Makefile.am new file mode 100644 index 00000000..f90c79e8 --- /dev/null +++ b/filters/krita/magick/Makefile.am @@ -0,0 +1,40 @@ +kde_module_LTLIBRARIES = libkritamagickimport.la libkritamagickexport.la + +libkritamagickexport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritamagickexport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBMAGICK_LIBS) \ + $(top_builddir)/krita/libkritacommon.la + +libkritamagickimport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritamagickimport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBMAGICK_LIBS) \ + $(top_builddir)/krita/libkritacommon.la + +INCLUDES= \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) $(LIBMAGICK_CPPFLAGS) \ + $(all_includes) + +service_DATA = krita_magick_import.desktop krita_magick_export.desktop +servicedir = $(kde_servicesdir) + +kdelnk_DATA = krita_magick.desktop +kdelnkdir = $(kde_appsdir)/Office + +libkritamagickimport_la_SOURCES = magickimport.cpp kis_image_magick_converter.cc +libkritamagickexport_la_SOURCES = magickexport.cpp kis_image_magick_converter.cc + +METASOURCES = AUTO + + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) diff --git a/filters/krita/magick/configure.in.bot b/filters/krita/magick/configure.in.bot new file mode 100644 index 00000000..a5767def --- /dev/null +++ b/filters/krita/magick/configure.in.bot @@ -0,0 +1,15 @@ +# ImageMagick is deprecated, we don't care anymore if it's not here +# +#if test -z "$LIBMAGICK_LIBS"; then +# echo "" +# echo "You're missing ImageMagick (>=6.1.0). krita's ImageMagick import/export" +# echo "filter will not be compiled. You can download ImageMagick from" +# echo "http://www.imagemagick.org/. The ImageMagick filter allows krita to" +# echo "read and write XCF, PSD, GIF, BMP, and many other image formats." +# echo "" +# echo "If you have problems compiling ImageMagick, please try configuring it using" +# echo "the --without-magick-plus-plus flag, the C++ API isn't needed for krita." +# echo "" +# all_tests=bad +#fi + diff --git a/filters/krita/magick/kis_image_magick_converter.cc b/filters/krita/magick/kis_image_magick_converter.cc new file mode 100644 index 00000000..e5407037 --- /dev/null +++ b/filters/krita/magick/kis_image_magick_converter.cc @@ -0,0 +1,1087 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <magick/api.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <qstring.h> + +#include <kdeversion.h> +#include <kdebug.h> +#include <kapplication.h> +#include <klocale.h> +#include <kurl.h> +#include <kio/netaccess.h> + +#include <qcolor.h> + +#include "kis_types.h" +#include "kis_global.h" +#include "kis_doc.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_undo_adapter.h" +#include "kis_image_magick_converter.h" +#include "kis_meta_registry.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_iterators_pixel.h" +#include "kis_colorspace.h" +#include "kis_profile.h" +#include "kis_annotation.h" +#include "kis_paint_layer.h" +#include "kis_group_layer.h" +#include "kis_paint_device.h" + +#include "../../../config.h" + +namespace { + + const Q_UINT8 PIXEL_BLUE = 0; + const Q_UINT8 PIXEL_GREEN = 1; + const Q_UINT8 PIXEL_RED = 2; + const Q_UINT8 PIXEL_ALPHA = 3; + + static const Q_UINT8 PIXEL_CYAN = 0; + static const Q_UINT8 PIXEL_MAGENTA = 1; + static const Q_UINT8 PIXEL_YELLOW = 2; + static const Q_UINT8 PIXEL_BLACK = 3; + static const Q_UINT8 PIXEL_CMYK_ALPHA = 4; + + static const Q_UINT8 PIXEL_GRAY = 0; + static const Q_UINT8 PIXEL_GRAY_ALPHA = 1; + + /** + * Make this more flexible -- although... ImageMagick + * isn't that flexible either. + */ + QString getColorSpaceName(ColorspaceType type, unsigned long imageDepth = 8) + { + + if (type == GRAYColorspace) { + if (imageDepth == 8) + return "GRAYA"; + else if ( imageDepth == 16 ) + return "GRAYA16" ; + } + else if (type == CMYKColorspace) { + if (imageDepth == 8) + return "CMYK"; + else if ( imageDepth == 16 ) { + return "CMYK16"; + } + } + else if (type == LABColorspace) { + kdDebug(41008) << "Lab!\n"; + return "LABA"; + } + else if (type == RGBColorspace || type == sRGBColorspace || type == TransparentColorspace) { + if (imageDepth == 8) + return "RGBA"; + else if (imageDepth == 16) + return "RGBA16"; + } + return ""; + + } + + ColorspaceType getColorTypeforColorSpace( KisColorSpace * cs ) + { + if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) return GRAYColorspace; + if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) return RGBColorspace; + if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") ) return CMYKColorspace; + if ( cs->id() == KisID("LABA") ) return LABColorspace; + + kdDebug(41008) << "Cannot export images in " + cs->id().name() + " yet.\n"; + return RGBColorspace; + + } + + KisProfile * getProfileForProfileInfo(const Image * image) + { +#ifndef HAVE_MAGICK6 + return 0; +#else + + if (image->profiles == NULL) + return 0; + + const char *name; + const StringInfo *profile; + + KisProfile * p = 0; + + ResetImageProfileIterator(image); + for (name = GetNextImageProfile(image); name != (char *) NULL; ) + { + profile = GetImageProfile(image, name); + if (profile == (StringInfo *) NULL) + continue; + + // XXX: Hardcoded for icc type -- is that correct for us? + if (QString::compare(name, "icc") == 0) { + QByteArray rawdata; + rawdata.resize(profile->length); + memcpy(rawdata.data(), profile->datum, profile->length); + + p = new KisProfile(rawdata); + if (p == 0) + return 0; + } + name = GetNextImageProfile(image); + } + return p; +#endif + } + + void setAnnotationsForImage(const Image * src, KisImageSP image) + { +#ifndef HAVE_MAGICK6 + return; +#else + if (src->profiles == NULL) + return; + + const char *name = 0; + const StringInfo *profile; + KisAnnotation* annotation = 0; + + // Profiles and so + ResetImageProfileIterator(src); + while((name = GetNextImageProfile(src))) { + profile = GetImageProfile(src, name); + if (profile == (StringInfo *) NULL) + continue; + + // XXX: icc will be written seperately? + if (QString::compare(name, "icc") == 0) + continue; + + QByteArray rawdata; + rawdata.resize(profile->length); + memcpy(rawdata.data(), profile->datum, profile->length); + + annotation = new KisAnnotation(QString(name), "", rawdata); + Q_CHECK_PTR(annotation); + + image -> addAnnotation(annotation); + } + + // Attributes, since we have no hint on if this is an attribute or a profile + // annotation, we prefix it with 'krita_attribute:'. XXX This needs to be rethought! + // The joys of imagemagick. From at version 6.2.1 (dfaure has 6.2.0 and confirms the + // old way of doing things) they changed the src -> attributes + // to void* and require us to use the iterator functions. So we #if around that, *sigh* +#if MagickLibVersion >= 0x621 + const ImageAttribute * attr; + ResetImageAttributeIterator(src); + while ( (attr = GetNextImageAttribute(src)) ) { +#else + ImageAttribute * attr = src -> attributes; + while (attr) { +#endif + QByteArray rawdata; + int len = strlen(attr -> value) + 1; + rawdata.resize(len); + memcpy(rawdata.data(), attr -> value, len); + + annotation = new KisAnnotation( + QString("krita_attribute:%1").arg(QString(attr -> key)), "", rawdata); + Q_CHECK_PTR(annotation); + + image -> addAnnotation(annotation); +#if MagickLibVersion < 0x620 + attr = attr -> next; +#endif + } + +#endif + } + } + + void exportAnnotationsForImage(Image * dst, vKisAnnotationSP_it& it, vKisAnnotationSP_it& annotationsEnd) + { +#ifndef HAVE_MAGICK6 + return; +#else + while(it != annotationsEnd) { + if (!(*it) || (*it) -> type() == QString()) { + kdDebug(41008) << "Warning: empty annotation" << endl; + ++it; + continue; + } + + kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl; + + if ((*it) -> type().startsWith("krita_attribute:")) { // Attribute + if (!SetImageAttribute(dst, + (*it) -> type().mid(strlen("krita_attribute:")).ascii(), + (*it) -> annotation() . data()) ) { + kdDebug(41008) << "Storing of attribute " << (*it) -> type() << "failed!\n"; + } + } else { // Profile + if (!ProfileImage(dst, (*it) -> type().ascii(), + (unsigned char*)(*it) -> annotation() . data(), + (*it) -> annotation() . size(), MagickFalse)) { + kdDebug(41008) << "Storing failed!" << endl; + } + } + ++it; + } +#endif + } + + + void InitGlobalMagick() + { + static bool init = false; + + if (!init) { + KApplication *app = KApplication::kApplication(); + + InitializeMagick(*app -> argv()); + atexit(DestroyMagick); + init = true; + } + } + + /* + * ImageMagick progress monitor callback. Unfortunately it doesn't support passing in some user + * data which complicates things quite a bit. The plan was to allow the user start multiple + * import/scans if he/she so wished. However, without passing user data it's not possible to tell + * on which task we have made progress on. + * + * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held + * locks when calling user defined callbacks, this means that the same thread going back into IM + * would deadlock since it would try to acquire locks it already holds. + */ +#ifdef HAVE_MAGICK6 + MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *) + { + KApplication *app = KApplication::kApplication(); + + Q_ASSERT(app); + + if (app -> hasPendingEvents()) + app -> processEvents(); + + printf("%s\n", text); + return MagickTrue; + } +#else + unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *) + { + KApplication *app = KApplication::kApplication(); + + Q_ASSERT(app); + + if (app -> hasPendingEvents()) + app -> processEvents(); + + printf("%s\n", text); + return true; + } +#endif + + + +KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter) +{ + InitGlobalMagick(); + init(doc, adapter); + SetMonitorHandler(monitor); + m_stop = false; +} + +KisImageMagickConverter::~KisImageMagickConverter() +{ +} + +KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob) +{ + Image *image; + Image *images; + ExceptionInfo ei; + ImageInfo *ii; + + if (m_stop) { + m_img = 0; + return KisImageBuilder_RESULT_INTR; + } + + GetExceptionInfo(&ei); + ii = CloneImageInfo(0); + + if (isBlob) { + + // TODO : Test. Does BlobToImage even work? + Q_ASSERT(uri.isEmpty()); + images = BlobToImage(ii, &m_data[0], m_data.size(), &ei); + } else { + + qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1); + + if (ii -> filename[MaxTextExtent - 1]) { + emit notifyProgressError(); + return KisImageBuilder_RESULT_PATH; + } + + images = ReadImage(ii, &ei); + + } + + if (ei.severity != UndefinedException) + CatchException(&ei); + + if (images == 0) { + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + emit notifyProgressError(); + return KisImageBuilder_RESULT_FAILURE; + } + + emit notifyProgressStage(i18n("Importing..."), 0); + + m_img = 0; + + while ((image = RemoveFirstImageFromList(&images))) { + ViewInfo *vi = OpenCacheView(image); + + // Determine image depth -- for now, all channels of an imported image are of the same depth + unsigned long imageDepth = image->depth; + kdDebug(41008) << "Image depth: " << imageDepth << "\n"; + + QString csName; + KisColorSpace * cs = 0; + ColorspaceType colorspaceType; + + // Determine image type -- rgb, grayscale or cmyk + if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) { + if (imageDepth == 8) + csName = "GRAYA"; + else if ( imageDepth == 16 ) + csName = "GRAYA16" ; + colorspaceType = GRAYColorspace; + } + else { + colorspaceType = image->colorspace; + csName = getColorSpaceName(image -> colorspace, imageDepth); + } + + kdDebug(41008) << "image has " << csName << " colorspace\n"; + + KisProfile * profile = getProfileForProfileInfo(image); + if (profile) + { + kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n"; + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile); + } + else + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),""); + + if (!cs) { + kdDebug(41008) << "Krita does not support colorspace " << image -> colorspace << "\n"; + CloseCacheView(vi); + DestroyImage(image); + DestroyExceptionInfo(&ei); + DestroyImageList(images); + DestroyImageInfo(ii); + emit notifyProgressError(); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + + if( ! m_img) { + m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image"); + Q_CHECK_PTR(m_img); + m_img->blockSignals(true); // Don't send out signals while we're building the image + + // XXX I'm assuming seperate layers won't have other profile things like EXIF + setAnnotationsForImage(image, m_img); + } + + if (image -> columns && image -> rows) { + + // Opacity (set by the photoshop import filter) + Q_UINT8 opacity = OPACITY_OPAQUE; + const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]"); + if (attr != 0) { + opacity = Q_UINT8_MAX - Downscale(QString(attr->value).toInt()); + } + + KisPaintLayerSP layer = 0; + + attr = GetImageAttribute(image, "[layer-name]"); + if (attr != 0) { + layer = new KisPaintLayer(m_img, attr->value, opacity); + } + else { + layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity); + } + + Q_ASSERT(layer); + + // Layerlocation (set by the photoshop import filter) + Q_INT32 x_offset = 0; + Q_INT32 y_offset = 0; + + attr = GetImageAttribute(image, "[layer-xpos]"); + if (attr != 0) { + x_offset = QString(attr->value).toInt(); + } + + attr = GetImageAttribute(image, "[layer-ypos]"); + if (attr != 0) { + y_offset = QString(attr->value).toInt(); + } + + + for (Q_UINT32 y = 0; y < image->rows; y ++) + { + const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei); + + if(!pp) + { + CloseCacheView(vi); + DestroyImageList(images); + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + emit notifyProgressError(); + return KisImageBuilder_RESULT_FAILURE; + } + + IndexPacket * indexes = GetCacheViewIndexes(vi); + + KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true); + + if (colorspaceType== CMYKColorspace) { + if (imageDepth == 8) { + int x = 0; + while (!hiter.isDone()) + { + Q_UINT8 *ptr= hiter.rawData(); + *(ptr++) = Downscale(pp->red); // cyan + *(ptr++) = Downscale(pp->green); // magenta + *(ptr++) = Downscale(pp->blue); // yellow + *(ptr++) = Downscale(indexes[x]); // Black +// XXX: Warning! This ifdef messes up the paren matching big-time! +#ifdef HAVE_MAGICK6 + if (image->matte != MagickFalse) { +#else + if (image->matte == true) { +#endif + *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity); + } + else { + *(ptr++) = OPACITY_OPAQUE; + } + ++x; + pp++; + ++hiter; + } + } + } + else if (colorspaceType == LABColorspace) { + while(! hiter.isDone()) + { + Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData()); + + *(ptr++) = ScaleQuantumToShort(pp->red); + *(ptr++) = ScaleQuantumToShort(pp->green); + *(ptr++) = ScaleQuantumToShort(pp->blue); + *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity); + + pp++; + ++hiter; + } + } + else if (colorspaceType == RGBColorspace || + colorspaceType == sRGBColorspace || + colorspaceType == TransparentColorspace) + { + if (imageDepth == 8) { + while(! hiter.isDone()) + { + Q_UINT8 *ptr= hiter.rawData(); + // XXX: not colorstrategy and bitdepth independent + *(ptr++) = Downscale(pp->blue); + *(ptr++) = Downscale(pp->green); + *(ptr++) = Downscale(pp->red); + *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity); + + pp++; + ++hiter; + } + } + else if (imageDepth == 16) { + while(! hiter.isDone()) + { + Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData()); + // XXX: not colorstrategy independent + *(ptr++) = ScaleQuantumToShort(pp->blue); + *(ptr++) = ScaleQuantumToShort(pp->green); + *(ptr++) = ScaleQuantumToShort(pp->red); + *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity); + + pp++; + ++hiter; + } + } + } + else if ( colorspaceType == GRAYColorspace) { + if (imageDepth == 8) { + while(! hiter.isDone()) + { + Q_UINT8 *ptr= hiter.rawData(); + // XXX: not colorstrategy and bitdepth independent + *(ptr++) = Downscale(pp->blue); + *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity); + + pp++; + ++hiter; + } + } + else if (imageDepth == 16) { + while(! hiter.isDone()) + { + Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData()); + // XXX: not colorstrategy independent + *(ptr++) = ScaleQuantumToShort(pp->blue); + *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity); + + pp++; + ++hiter; + } + } + } + + emit notifyProgress(y * 100 / image->rows); + + if (m_stop) { + CloseCacheView(vi); + DestroyImage(image); + DestroyImageList(images); + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + m_img = 0; + return KisImageBuilder_RESULT_INTR; + } + } + m_img->addLayer(layer.data(), m_img->rootLayer()); + layer->paintDevice()->move(x_offset, y_offset); + } + + emit notifyProgressDone(); + CloseCacheView(vi); + DestroyImage(image); + } + + emit notifyProgressDone(); + DestroyImageList(images); + DestroyImageInfo(ii); + DestroyExceptionInfo(&ei); + return KisImageBuilder_RESULT_OK; + } + + KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri) + { + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) { + return KisImageBuilder_RESULT_NOT_EXIST; + } + + KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; + QString tmpFile; + + if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) { + KURL uriTF; + uriTF.setPath( tmpFile ); + result = decode(uriTF, false); + KIO::NetAccess::removeTempFile(tmpFile); + } + + return result; + } + + + KisImageSP KisImageMagickConverter::image() + { + return m_img; + } + + void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter) + { + m_doc = doc; + m_adapter = adapter; + m_job = 0; + } + + KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd) + { + Image *image; + ExceptionInfo ei; + ImageInfo *ii; + + if (!layer) + return KisImageBuilder_RESULT_INVALID_ARG; + + KisImageSP img = layer->image(); + if (!img) + return KisImageBuilder_RESULT_EMPTY; + + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!uri.isLocalFile()) + return KisImageBuilder_RESULT_NOT_LOCAL; + + + Q_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels(); + + GetExceptionInfo(&ei); + + ii = CloneImageInfo(0); + + qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1); + + if (ii -> filename[MaxTextExtent - 1]) { + emit notifyProgressError(); + return KisImageBuilder_RESULT_PATH; + } + + if (!img -> width() || !img -> height()) + return KisImageBuilder_RESULT_EMPTY; + + if (layerBytesPerChannel < 2) { + ii->depth = 8; + } + else { + ii->depth = 16; + } + + ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace()); + + image = AllocateImage(ii); + SetImageColorspace(image, ii->colorspace); + image -> columns = img -> width(); + image -> rows = img -> height(); + + kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n"; + kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n"; + +#ifdef HAVE_MAGICK6 + // if ( layer-> hasAlpha() ) + image -> matte = MagickTrue; + // else + // image -> matte = MagickFalse; +#else + // image -> matte = layer -> hasAlpha(); + image -> matte = true; +#endif + + Q_INT32 y, height, width; + + height = img -> height(); + width = img -> width(); + + bool alpha = true; + QString ext = QFileInfo(QFile::encodeName(uri.path())).extension(false).upper(); + if (ext == "BMP") { + alpha = false; + qstrncpy(ii->magick, "BMP2", MaxTextExtent - 1); + } + else if (ext == "RGB") { + qstrncpy(ii->magick, "SGI", MaxTextExtent - 1); + } + + for (y = 0; y < height; y++) { + + // Allocate pixels for this scanline + PixelPacket * pp = SetImagePixels(image, 0, y, width, 1); + + if (!pp) { + DestroyExceptionInfo(&ei); + DestroyImage(image); + emit notifyProgressError(); + return KisImageBuilder_RESULT_FAILURE; + + } + + KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false); + if (alpha) + SetImageType(image, TrueColorMatteType); + else + SetImageType(image, TrueColorType); + + if (image->colorspace== CMYKColorspace) { + + IndexPacket * indexes = GetIndexes(image); + int x = 0; + if (layerBytesPerChannel == 2) { + while (!it.isDone()) { + + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]); + pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]); + pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]); + if (alpha) + pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]); + indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]); + x++; + pp++; + ++it; + } + } + else { + while (!it.isDone()) { + + Q_UINT8 * d = it.rawData(); + pp -> red = Upscale(d[PIXEL_CYAN]); + pp -> green = Upscale(d[PIXEL_MAGENTA]); + pp -> blue = Upscale(d[PIXEL_YELLOW]); + if (alpha) + pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]); + + indexes[x]= Upscale(d[PIXEL_BLACK]); + + x++; + pp++; + ++it; + } + } + } + else if (image->colorspace== RGBColorspace || + image->colorspace == sRGBColorspace || + image->colorspace == TransparentColorspace) + { + if (layerBytesPerChannel == 2) { + while (!it.isDone()) { + + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + pp -> red = ScaleShortToQuantum(d[PIXEL_RED]); + pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]); + pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]); + if (alpha) + pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]); + + pp++; + ++it; + } + } + else { + while (!it.isDone()) { + + Q_UINT8 * d = it.rawData(); + pp -> red = Upscale(d[PIXEL_RED]); + pp -> green = Upscale(d[PIXEL_GREEN]); + pp -> blue = Upscale(d[PIXEL_BLUE]); + if (alpha) + pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_ALPHA]); + + pp++; + ++it; + } + } + } + else if (image->colorspace == GRAYColorspace) + { + SetImageType(image, GrayscaleMatteType); + if (layerBytesPerChannel == 2) { + while (!it.isDone()) { + + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]); + pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]); + pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]); + if (alpha) + pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]); + + pp++; + ++it; + } + } + else { + while (!it.isDone()) { + Q_UINT8 * d = it.rawData(); + pp -> red = Upscale(d[PIXEL_GRAY]); + pp -> green = Upscale(d[PIXEL_GRAY]); + pp -> blue = Upscale(d[PIXEL_GRAY]); + if (alpha) + pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]); + + pp++; + ++it; + } + } + } + else { + kdDebug(41008) << "Unsupported image format\n"; + return KisImageBuilder_RESULT_INVALID_ARG; + } + + emit notifyProgressStage(i18n("Saving..."), y * 100 / height); + +#ifdef HAVE_MAGICK6 + if (SyncImagePixels(image) == MagickFalse) + kdDebug(41008) << "Syncing pixels failed\n"; +#else + if (!SyncImagePixels(image)) + kdDebug(41008) << "Syncing pixels failed\n"; +#endif + } + + // set the annotations + exportAnnotationsForImage(image, annotationsStart, annotationsEnd); + + // XXX: Write to a temp file, then have Krita use KIO to copy temp + // image to remote location. + + WriteImage(ii, image); + DestroyExceptionInfo(&ei); + DestroyImage(image); + emit notifyProgressDone(); + return KisImageBuilder_RESULT_OK; + } + + void KisImageMagickConverter::ioData(KIO::Job *job, const QByteArray& data) + { + if (data.isNull() || data.isEmpty()) { + emit notifyProgressStage(i18n("Loading..."), 0); + return; + } + + if (m_data.empty()) { + Image *image; + ImageInfo *ii; + ExceptionInfo ei; + + ii = CloneImageInfo(0); + GetExceptionInfo(&ei); + image = PingBlob(ii, data.data(), data.size(), &ei); + + if (image == 0 || ei.severity == BlobError) { + DestroyExceptionInfo(&ei); + DestroyImageInfo(ii); + job -> kill(); + emit notifyProgressError(); + return; + } + + DestroyImage(image); + DestroyExceptionInfo(&ei); + DestroyImageInfo(ii); + emit notifyProgressStage(i18n("Loading..."), 0); + } + + Q_ASSERT(data.size() + m_data.size() <= m_size); + memcpy(&m_data[m_data.size()], data.data(), data.count()); + m_data.resize(m_data.size() + data.count()); + emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size); + + if (m_stop) + job -> kill(); + } + + void KisImageMagickConverter::ioResult(KIO::Job *job) + { + m_job = 0; + + if (job -> error()) + emit notifyProgressError(); + + decode(KURL(), true); + } + + void KisImageMagickConverter::ioTotalSize(KIO::Job * /*job*/, KIO::filesize_t size) + { + m_size = size; + m_data.reserve(size); + emit notifyProgressStage(i18n("Loading..."), 0); + } + + void KisImageMagickConverter::cancel() + { + m_stop = true; + } + + /** + * @name readFilters + * @return Provide a list of file formats the application can read. + */ + QString KisImageMagickConverter::readFilters() + { + QString s; + QString all; + QString name; + QString description; + unsigned long matches; + +#ifdef HAVE_MAGICK6 +#ifdef HAVE_OLD_GETMAGICKINFOLIST + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches); +#else // HAVE_OLD_GETMAGICKINFOLIST + ExceptionInfo ei; + GetExceptionInfo(&ei); + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches, &ei); + DestroyExceptionInfo(&ei); +#endif // HAVE_OLD_GETMAGICKINFOLIST +#else // HAVE_MAGICK6 + const MagickInfo *mi; + ExceptionInfo ei; + GetExceptionInfo(&ei); + mi = GetMagickInfo("*", &ei); + DestroyExceptionInfo(&ei); +#endif // HAVE_MAGICK6 + + if (!mi) + return s; + +#ifdef HAVE_MAGICK6 + for (unsigned long i = 0; i < matches; i++) { + const MagickInfo *info = mi[i]; + if (info -> stealth) + continue; + + if (info -> decoder) { + name = info -> name; + description = info -> description; + kdDebug(41008) << "Found import filter for: " << name << "\n"; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +#else + for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) { + if (mi -> stealth) + continue; + if (mi -> decoder) { + name = mi -> name; + description = mi -> description; + kdDebug(41008) << "Found import filter for: " << name << "\n"; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +#endif + + all += "|" + i18n("All Images"); + all += "\n"; + + return all + s; + } + + QString KisImageMagickConverter::writeFilters() + { + QString s; + QString all; + QString name; + QString description; + unsigned long matches; + +#ifdef HAVE_MAGICK6 +#ifdef HAVE_OLD_GETMAGICKINFOLIST + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches); +#else // HAVE_OLD_GETMAGICKINFOLIST + ExceptionInfo ei; + GetExceptionInfo(&ei); + const MagickInfo **mi; + mi = GetMagickInfoList("*", &matches, &ei); + DestroyExceptionInfo(&ei); +#endif // HAVE_OLD_GETMAGICKINFOLIST +#else // HAVE_MAGICK6 + const MagickInfo *mi; + ExceptionInfo ei; + GetExceptionInfo(&ei); + mi = GetMagickInfo("*", &ei); + DestroyExceptionInfo(&ei); +#endif // HAVE_MAGICK6 + + if (!mi) { + kdDebug(41008) << "Eek, no magick info!\n"; + return s; + } + +#ifdef HAVE_MAGICK6 + for (unsigned long i = 0; i < matches; i++) { + const MagickInfo *info = mi[i]; + kdDebug(41008) << "Found export filter for: " << info -> name << "\n"; + if (info -> stealth) + continue; + + if (info -> encoder) { + name = info -> name; + + description = info -> description; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +#else + for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) { + kdDebug(41008) << "Found export filter for: " << mi -> name << "\n"; + if (mi -> stealth) + continue; + + if (mi -> encoder) { + name = mi -> name; + + description = mi -> description; + + if (!description.isEmpty() && !description.contains('/')) { + all += "*." + name.lower() + " *." + name + " "; + s += "*." + name.lower() + " *." + name + "|"; + s += i18n(description.utf8()); + s += "\n"; + } + } + } +#endif + + + all += "|" + i18n("All Images"); + all += "\n"; + + return all + s; + } + +#include "kis_image_magick_converter.moc" + diff --git a/filters/krita/magick/kis_image_magick_converter.h b/filters/krita/magick/kis_image_magick_converter.h new file mode 100644 index 00000000..9b3b6bc0 --- /dev/null +++ b/filters/krita/magick/kis_image_magick_converter.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef KIS_IMAGE_MAGICK_CONVERTER_H_ +#define KIS_IMAGE_MAGICK_CONVERTER_H_ + +#include <qobject.h> +#include <qvaluevector.h> + +#include <kio/job.h> + +#include "kis_types.h" +#include "kis_global.h" +#include "kis_progress_subject.h" + +class QString; +class KURL; +class KisDoc; +class KisNameServer; +class KisUndoAdapter; +/** + * Image import/export plugins can use these results to report about success or failure. + */ +enum KisImageBuilder_Result { + KisImageBuilder_RESULT_FAILURE = -400, + KisImageBuilder_RESULT_NOT_EXIST = -300, + KisImageBuilder_RESULT_NOT_LOCAL = -200, + KisImageBuilder_RESULT_BAD_FETCH = -100, + KisImageBuilder_RESULT_INVALID_ARG = -50, + KisImageBuilder_RESULT_OK = 0, + KisImageBuilder_RESULT_PROGRESS = 1, + KisImageBuilder_RESULT_EMPTY = 100, + KisImageBuilder_RESULT_BUSY = 150, + KisImageBuilder_RESULT_NO_URI = 200, + KisImageBuilder_RESULT_UNSUPPORTED = 300, + KisImageBuilder_RESULT_INTR = 400, + KisImageBuilder_RESULT_PATH = 500, + KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 +}; + + + +/** + * Build a KisImage representation of an image file. + */ +class KisImageMagickConverter : public KisProgressSubject { + typedef QObject super; + Q_OBJECT + +public: + KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter); + virtual ~KisImageMagickConverter(); + +public slots: + virtual void cancel(); + +public: + KisImageBuilder_Result buildImage(const KURL& uri); + KisImageBuilder_Result buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd); + KisImageSP image(); + +public: + static QString readFilters(); + static QString writeFilters(); + +private slots: + void ioData(KIO::Job *job, const QByteArray& data); + void ioResult(KIO::Job *job); + void ioTotalSize(KIO::Job *job, KIO::filesize_t size); + +private: + KisImageMagickConverter(const KisImageMagickConverter&); + KisImageMagickConverter& operator=(const KisImageMagickConverter&); + void init(KisDoc *doc, KisUndoAdapter *adapter); + KisImageBuilder_Result decode(const KURL& uri, bool isBlob); + +private: + KisImageSP m_img; + KisDoc *m_doc; + KisUndoAdapter *m_adapter; + QValueVector<Q_UINT8> m_data; + KIO::TransferJob *m_job; + KIO::filesize_t m_size; + bool m_stop; +}; + +#endif // KIS_IMAGE_MAGICK_CONVERTER_H_ + diff --git a/filters/krita/magick/krita_magick.desktop b/filters/krita/magick/krita_magick.desktop new file mode 100644 index 00000000..eed0aa2d --- /dev/null +++ b/filters/krita/magick/krita_magick.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Exec=krita %u +GenericName=Painting and Image Editing Application +GenericName[bg]=Редактор на графични изображения +GenericName[ca]=Programa de dibuix i manipulació d'imatges +GenericName[cs]=Malování a úpravy obrázků +GenericName[cy]=Cymhwysiad Peintio Golygu Delweddau +GenericName[da]=Male- og billedredigeringsprogram +GenericName[de]=Mal- und Bildbearbeitungsprogramm +GenericName[el]=Εφαρμογή επεξεργασίας εικόνων +GenericName[eo]=Aplikaĵo por Pentrado kaj Bildredaktado +GenericName[es]=Aplicación de pintura y de edición de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudien marrazketa eta ediziorako aplikazioa +GenericName[fa]=کاربرد ویرایش تصویر و نقاشی +GenericName[fi]=Maalaus- ja kuvankäsitelyohjelma +GenericName[fr]=Application de dessin et de manipulation d'images +GenericName[fy]=Ofbyldingsmanipulaasje +GenericName[gl]=Aplicación de Pintura e Manipulación de Imaxes +GenericName[he]=יישום לציור ועריכת תמונות +GenericName[hr]=Aplikacija za obradu slika i fotografija +GenericName[hu]=Képszerkesztő +GenericName[is]=Málun og myndritill +GenericName[it]=Applicazione di disegno e di modifica delle immagini +GenericName[ja]=描画と画像編集のためのアプリケーション +GenericName[km]=កម្មវិធីគូរគំនូរ និងកែសម្រួលរូបភាព +GenericName[lv]=Zīmēšanas un attēlu apstrādes programma +GenericName[nb]=Program for tegning og bilderedigering +GenericName[nds]=Programm för't Malen un Bildbewerken +GenericName[ne]=पेन्टीङ्ग र छवि सम्पादन अनुप्रयोग +GenericName[nl]=Afbeeldingsmanipulatie +GenericName[pl]=Program do edycji zdjęć oraz rysunków +GenericName[pt]=Aplicação de Pintura e Edição de Imagens +GenericName[pt_BR]=Aplicação de Pintura e Edição de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Málen- ja govvagieđahallanprográmma +GenericName[sk]=Program pre tvorbu a úpravu obrázkov +GenericName[sl]=Program za risanje in obdelavo slik +GenericName[sr]=Програм за цртање и уређивање слика +GenericName[sr@Latn]=Program za crtanje i uređivanje slika +GenericName[sv]=Målnings- och bildredigeringsprogram +GenericName[uk]=Програма для малювання і редагування зображень +GenericName[uz]=Rasmlar bilan ishlaydigan dastur +GenericName[uz@cyrillic]=Расмлар билан ишлайдиган дастур +GenericName[zh_CN]=绘图和图像编辑应用程序 +GenericName[zh_TW]=繪圖與影像處理程式 +MimeType=image/x-xcf-gimp;image/gif;image/cgm;image/x-bmp;image/x-ico;image/x-pcx;image/x-portable-pixmap;image/x-targa;image/x-xbm;image/x-xcf;image/x-xpm;image/x-vnd.adobe.photoshop;image/x-rgb +Type=Application +Icon=krita +Categories=Qt;KDE;Office;Graphics; +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +NoDisplay=true diff --git a/filters/krita/magick/krita_magick_export.desktop b/filters/krita/magick/krita_magick_export.desktop new file mode 100644 index 00000000..7df00026 --- /dev/null +++ b/filters/krita/magick/krita_magick_export.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Name=Krita Magick Export Filter +Name[bg]=Филтър за експортиране от Krita в Magick +Name[br]=Sil ezporzh Magick evit Krita +Name[ca]=Filtre d'exportació Magick per a Krita +Name[cy]=Hidl Allforio Magick Krita +Name[da]=Krita Magick-eksportfilter +Name[de]=Krita Magick-Exportfilter +Name[el]=Φίλτρο εξαγωγής Magick του Krita +Name[eo]=Krita Magick-eksportfiltrilo +Name[es]=Filtro de exportación a Magick de Krita +Name[et]=Krita Magick'i ekspordifilter +Name[eu]=Krita-ren Magick esportaziorako iragazkia +Name[fa]=پالایۀ صادرات Krita Magick +Name[fi]=Krita Magick -vientisuodin +Name[fr]=Filtre d'exportation Magick de Krita +Name[fy]=Krita Magick Eksportfilter +Name[ga]=Scagaire Easpórtála Magick Krita +Name[gl]=Filtro de Exportación de Magick para Krita +Name[he]=מסנן יצוא מ־Krita ל־Magick +Name[hr]=Krita Magick filtar izvoza +Name[hu]=Krita Magick exportszűrő +Name[is]=Krita Magick útflutningssía +Name[it]=Filtro di esportazione Magick per Krita +Name[ja]=Krita Magick エクスポートフィルタ +Name[km]=តម្រងនាំចេញ Magick សម្រាប់ Krita +Name[lt]=Krita Magick eksportavimo filtras +Name[lv]=Krita Magick eksporta filtrs +Name[ms]=Penapis Eksport Krita Magick +Name[nb]=Magick-eksportfilter for Krita +Name[nds]=Magick-Exportfilter för Krita +Name[ne]=क्रिता म्याजिक निर्यात फिल्टर +Name[nl]=Krita Magick Exportfilter +Name[nn]=Magick-eksportfilter for Krita +Name[pl]=Filtr eksportu do formatu Magick z Krita +Name[pt]=Filtro de Exportação de Magick para o Krita +Name[pt_BR]=Filtro de exportação Magick para o Krita +Name[ru]=Фильтр экспорта рисунков Krita в Magick +Name[se]=Krita Magick-olggosfievrridansilli +Name[sk]=Magick filter pre export do Krita +Name[sl]=Izvozni filter Magick za Krito +Name[sr]=Krita-ин филтер за извоз у Magick +Name[sr@Latn]=Krita-in filter za izvoz u Magick +Name[sv]=Krita Magick-exportfilter +Name[uk]=Фільтр експорту Magick для Krita +Name[uz]=Krita Magick eksport filteri +Name[uz@cyrillic]=Krita Magick экспорт филтери +Name[zh_CN]=Krita Magick 导出过滤器 +Name[zh_TW]=Krita Magick 匯出過濾程式 +X-KDE-Export=image/gif,image/cgm,image/x-ico,image/x-pcx,image/x-portable-pixmap,image/x-targa,image/x-xbm,image/x-xpm,image/x-rgb,image/x-eps +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Import=application/x-krita +X-KDE-Weight=1 +X-KDE-Library=libkritamagickexport diff --git a/filters/krita/magick/krita_magick_import.desktop b/filters/krita/magick/krita_magick_import.desktop new file mode 100644 index 00000000..702d67f7 --- /dev/null +++ b/filters/krita/magick/krita_magick_import.desktop @@ -0,0 +1,61 @@ +[Desktop Entry] +Type=Service +Name=Krita Magick Import Filter +Name[bg]=Филтър за импортиране от Magick в Krita +Name[br]=Sil enporzh Magick evit Krita +Name[ca]=Filtre d'importació Magick per a Krita +Name[cs]=Importní filtr formátu Magick pro Kritu +Name[cy]=Hidl Mewnforio Krita Magick +Name[da]=Krita Magick-importfilter +Name[de]=Krita Magick-Importfilter +Name[el]=Φίλτρο εισαγωγής Magick του Krita +Name[eo]=Krita Magick-importfiltrilo +Name[es]=Filtro de importación a Magick de Krita +Name[et]=Krita Magick'i impordifilter +Name[eu]=Krita-ren Magick inportaziorako iragazkia +Name[fa]=پالایۀ واردات Krita Magick +Name[fi]=Krita Magick-tuontisuodin +Name[fr]=Filtre d'importation Magick de Krita +Name[fy]=Krita Magick Ymportfilter +Name[ga]=Scagaire Iompórtála Magick Krita +Name[gl]=Filtro de Importación de Magick para Krita +Name[he]=מסנן יבוא מ־Magick ל־Krita +Name[hi]=के-रीता मैजिक आयात फ़िल्टर +Name[hr]=Krita Magick filtar uvoza +Name[hu]=Krita Magick importszűrő +Name[is]=Krita Magick innflutningssía +Name[it]=Filtro di importazione Magick per Krita +Name[ja]=Krita Magick インポートフィルタ +Name[km]=តម្រងនាំចូល Magick សម្រាប់ Krita +Name[lo]= ຕົວຕອງການນຳເຂົ້າ WML ຂອງເອກະສານຂໍ້ຄວາມ K +Name[lt]=Krita Magick importavimo filtras +Name[lv]=Krita Magick importa filtrs +Name[ms]=Penapis Import Krita Magick +Name[nb]=Magick-importfilter for Krita +Name[nds]=Magick-Importfilter för Krita +Name[ne]=क्रिता म्याजिक आयात फिल्टर +Name[nl]=Krita Magick Importfilter +Name[nn]=Magick-importfilter for Krita +Name[pl]=Filtr importu formatu Magick do Krita +Name[pt]=Filtro de Importação de Magick para o Krita +Name[pt_BR]=Filtro de importação Magick para o Krita +Name[ru]=Фильтр импорта рисунков Magick в Krita +Name[se]=Krita Magick-sisafievrridansilli +Name[sk]=Magick filter pre import do Krita +Name[sl]=Uvozni filter Magick za Krito +Name[sr]=Krita-ин филтер за увоз из Magick-а +Name[sr@Latn]=Krita-in filter za uvoz iz Magick-a +Name[sv]=Krita Magick-importfilter +Name[ta]=Krita மாயk இறக்குமதி வடிகட்டி +Name[tg]=Филтри Воридоти Krita Magick +Name[tr]=Krita Magick Alma Filtresi +Name[uk]=Фільтр імпорту Magick для Krita +Name[uz]=Krita Magick import filteri +Name[uz@cyrillic]=Krita Magick импорт филтери +Name[zh_CN]=Krita Magick 导入过滤器 +Name[zh_TW]=Krita Magick 匯入過濾程式 +X-KDE-Export=application/x-krita +X-KDE-Import=image/x-xcf-gimp,image/gif,image/cgm,image/x-bmp,image/x-ico,image/x-pcx,image/x-portable-pixmap,image/x-targa,image/x-xbm,image/x-xcf,image/x-xpm,image/x-vnd.adobe.photoshop,image/x-rgb,image/x-eps +X-KDE-Weight=1 +X-KDE-Library=libkritamagickimport +ServiceTypes=KOfficeFilter diff --git a/filters/krita/magick/magickexport.cpp b/filters/krita/magick/magickexport.cpp new file mode 100644 index 00000000..30113503 --- /dev/null +++ b/filters/krita/magick/magickexport.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <magickexport.h> +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_paint_layer.h> +#include <kis_image.h> +#include <kis_annotation.h> +#include <kis_types.h> +#include <kis_image_magick_converter.h> + +typedef KGenericFactory<MagickExport, KoFilter> MagickExportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritamagickexport, MagickExportFactory("kofficefilters")) + +MagickExport::MagickExport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +MagickExport::~MagickExport() +{ +} + +KoFilter::ConversionStatus MagickExport::convert(const QCString& from, const QCString& to) +{ + kdDebug(41008) << "magick export! From: " << from << ", To: " << to << "\n"; + + if (from != "application/x-krita") + return KoFilter::NotImplemented; + + // XXX: Add dialog about flattening layers here + + KisDoc *output = dynamic_cast<KisDoc*>(m_chain->inputDocument()); + QString filename = m_chain->outputFile(); + + if (!output) + return KoFilter::CreationError; + + if (filename.isEmpty()) return KoFilter::FileNotFound; + + KURL url; + url.setPath(filename); + + KisImageSP img = output->currentImage(); + + KisImageMagickConverter ib(output, output->undoAdapter()); + + KisPaintDeviceSP pd = new KisPaintDevice(*img->projection()); + KisPaintLayerSP l = new KisPaintLayer(img, "projection", OPACITY_OPAQUE, pd); + + vKisAnnotationSP_it beginIt = img->beginAnnotations(); + vKisAnnotationSP_it endIt = img->endAnnotations(); + if (ib.buildFile(url, l, beginIt, endIt) == KisImageBuilder_RESULT_OK) { + return KoFilter::OK; + } + return KoFilter::InternalError; +} + +#include <magickexport.moc> + diff --git a/filters/krita/magick/magickexport.h b/filters/krita/magick/magickexport.h new file mode 100644 index 00000000..3c499ebd --- /dev/null +++ b/filters/krita/magick/magickexport.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef MAGICKEXPORT_H_ +#define MAGICKEXPORT_H_ + +#include <KoFilter.h> + +class MagickExport : public KoFilter { + Q_OBJECT + +public: + MagickExport(KoFilter *parent, const char *name, const QStringList&); + virtual ~MagickExport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // MAGICKEXPORT_H_ + diff --git a/filters/krita/magick/magickimport.cpp b/filters/krita/magick/magickimport.cpp new file mode 100644 index 00000000..33177723 --- /dev/null +++ b/filters/krita/magick/magickimport.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <qstring.h> + +#include <magickimport.h> +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_view.h> +#include <kis_image_magick_converter.h> +#include <kis_progress_display_interface.h> +#include <kis_image.h> +#include <kis_layer.h> + +typedef KGenericFactory<MagickImport, KoFilter> MagickImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritamagickimport, MagickImportFactory("kofficefilters")) + +MagickImport::MagickImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +MagickImport::~MagickImport() +{ +} + +KoFilter::ConversionStatus MagickImport::convert(const QCString&, const QCString& to) +{ + kdDebug(41008) << "Importing using MagickImport!\n"; + + if (to != "application/x-krita") + return KoFilter::BadMimeType; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + KisView * view = static_cast<KisView*>(doc -> views().getFirst()); + + QString filename = m_chain -> inputFile(); + + if (!doc) + return KoFilter::CreationError; + + doc -> prepareForImport(); + + + if (!filename.isEmpty()) { + + KURL url; + url.setPath(filename); + + if (url.isEmpty()) + return KoFilter::FileNotFound; + + KisImageMagickConverter ib(doc, doc -> undoAdapter()); + + if (view != 0) + view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true); + + switch (ib.buildImage(url)) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KoFilter::NotImplemented; + break; + case KisImageBuilder_RESULT_INVALID_ARG: + return KoFilter::BadMimeType; + break; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_LOCAL: + return KoFilter::FileNotFound; + break; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KoFilter::ParsingError; + break; + case KisImageBuilder_RESULT_FAILURE: + return KoFilter::InternalError; + break; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KoFilter::OK; + default: + break; + } + + } + return KoFilter::StorageCreationError; +} + +#include <magickimport.moc> + diff --git a/filters/krita/magick/magickimport.h b/filters/krita/magick/magickimport.h new file mode 100644 index 00000000..01156642 --- /dev/null +++ b/filters/krita/magick/magickimport.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef MAGICKIMPORT_H_ +#define MAGICKIMPORT_H_ + +#include <KoFilter.h> + +class MagickImport : public KoFilter { + Q_OBJECT + +public: + MagickImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~MagickImport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // MAGICKIMPORT_H_ + diff --git a/filters/krita/openexr/Makefile.am b/filters/krita/openexr/Makefile.am new file mode 100644 index 00000000..ffdce031 --- /dev/null +++ b/filters/krita/openexr/Makefile.am @@ -0,0 +1,48 @@ +AM_CPPFLAGS= \ + -I$(srcdir) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + -I$(top_srcdir)/krita/colorspaces/rgb_f32 \ + -I$(top_srcdir)/krita/colorspaces/rgb_f16half \ + $(KOFFICE_INCLUDES) \ + -I$(interfacedir) \ + $(OPENEXR_CFLAGS) \ + $(all_includes) + +kde_module_LTLIBRARIES = libkrita_openexr_import.la libkrita_openexr_export.la + +libkrita_openexr_export_la_LDFLAGS = $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkrita_openexr_export_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(OPENEXR_LIBS) \ + $(top_builddir)/krita/libkritacommon.la \ + $(top_builddir)/krita/colorspaces/rgb_f32/libkrita_rgb_f32.la \ + $(top_builddir)/krita/colorspaces/rgb_f16half/libkrita_rgb_f16half.la + + +libkrita_openexr_import_la_LDFLAGS = $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkrita_openexr_import_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(OPENEXR_LIBS) \ + $(top_builddir)/krita/libkritacommon.la \ + $(top_builddir)/krita/colorspaces/rgb_f32/libkrita_rgb_f32.la \ + $(top_builddir)/krita/colorspaces/rgb_f16half/libkrita_rgb_f16half.la + + +service_DATA = krita_openexr_import.desktop krita_openexr_export.desktop +servicedir = $(kde_servicesdir) + +kdelnk_DATA = krita_openexr.desktop +kdelnkdir = $(kde_appsdir)/.hidden + +libkrita_openexr_import_la_SOURCES = kis_openexr_import.cpp +libkrita_openexr_export_la_SOURCES = kis_openexr_export.cpp + +METASOURCES = AUTO + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + diff --git a/filters/krita/openexr/configure.in.bot b/filters/krita/openexr/configure.in.bot new file mode 100644 index 00000000..cc420abb --- /dev/null +++ b/filters/krita/openexr/configure.in.bot @@ -0,0 +1,9 @@ +if test -z "$OPENEXR_LIBS"; then + echo "" + echo "You're missing the OpenEXR library. Krita's OpenEXR import/export filter will " + echo "not be compiled. You can download OpenEXR from http://www.openexr.com or " + echo "install it from an appropriate binary package." + echo "" + all_tests=bad +fi + diff --git a/filters/krita/openexr/kis_openexr_export.cpp b/filters/krita/openexr/kis_openexr_export.cpp new file mode 100644 index 00000000..724d07d0 --- /dev/null +++ b/filters/krita/openexr/kis_openexr_export.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2005 Adrian Page <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <qfile.h> + +#include <kmessagebox.h> + +#include <half.h> +#include <ImfRgbaFile.h> + +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include "kis_doc.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_paint_layer.h" +#include "kis_annotation.h" +#include "kis_types.h" +#include "kis_iterators_pixel.h" +#include "kis_abstract_colorspace.h" +#include "kis_paint_device.h" +#include "kis_rgb_f32_colorspace.h" +#include "kis_rgb_f16half_colorspace.h" + +#include "kis_openexr_export.h" + +using namespace std; +using namespace Imf; +using namespace Imath; + +typedef KGenericFactory<KisOpenEXRExport, KoFilter> KisOpenEXRExportFactory; +K_EXPORT_COMPONENT_FACTORY(libkrita_openexr_export, KisOpenEXRExportFactory("kofficefilters")) + +KisOpenEXRExport::KisOpenEXRExport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisOpenEXRExport::~KisOpenEXRExport() +{ +} + +KoFilter::ConversionStatus KisOpenEXRExport::convert(const QCString& from, const QCString& to) +{ + if (to != "image/x-exr" || from != "application/x-krita") { + return KoFilter::NotImplemented; + } + + kdDebug(41008) << "Krita exporting to OpenEXR\n"; + + // XXX: Add dialog about flattening layers here + + KisDoc *doc = dynamic_cast<KisDoc*>(m_chain -> inputDocument()); + QString filename = m_chain -> outputFile(); + + if (!doc) { + return KoFilter::CreationError; + } + + if (filename.isEmpty()) { + return KoFilter::FileNotFound; + } + + KisImageSP img = new KisImage(*doc -> currentImage()); + Q_CHECK_PTR(img); + + // Don't store this information in the document's undo adapter + bool undo = doc -> undoAdapter() -> undo(); + doc -> undoAdapter() -> setUndo(false); + + img -> flatten(); + + KisPaintLayerSP layer = dynamic_cast<KisPaintLayer*>(img->activeLayer().data()); + Q_ASSERT(layer); + + doc -> undoAdapter() -> setUndo(undo); + + //KisF32RgbColorSpace * cs = static_cast<KisF32RgbColorSpace *>((KisColorSpaceRegistry::instance() -> get(KisID("RGBAF32", "")))); + KisRgbF16HalfColorSpace *cs = dynamic_cast<KisRgbF16HalfColorSpace *>(layer->paintDevice()->colorSpace()); + + if (cs == 0) { + // We could convert automatically, but the conversion wants to be done with + // selectable profiles and rendering intent. + KMessageBox::information(0, i18n("The image is using an unsupported color space. " + "Please convert to 16-bit floating point RGB/Alpha " + "before saving in the OpenEXR format.")); + + // Don't show the couldn't save error dialog. + doc -> setErrorMessage("USER_CANCELED"); + + return KoFilter::WrongFormat; + } + + Box2i displayWindow(V2i(0, 0), V2i(img -> width() - 1, img -> height() - 1)); + + QRect dataExtent = layer -> exactBounds(); + int dataWidth = dataExtent.width(); + int dataHeight = dataExtent.height(); + + Box2i dataWindow(V2i(dataExtent.left(), dataExtent.top()), V2i(dataExtent.right(), dataExtent.bottom())); + + RgbaOutputFile file(QFile::encodeName(filename), displayWindow, dataWindow, WRITE_RGBA); + + QMemArray<Rgba> pixels(dataWidth); + + for (int y = 0; y < dataHeight; ++y) { + + file.setFrameBuffer(pixels.data() - dataWindow.min.x - (dataWindow.min.y + y) * dataWidth, 1, dataWidth); + + KisHLineIterator it = layer->paintDevice()->createHLineIterator(dataWindow.min.x, dataWindow.min.y + y, dataWidth, false); + Rgba *rgba = pixels.data(); + + while (!it.isDone()) { + + // XXX: Currently we use unmultiplied data so premult it. + half unmultipliedRed; + half unmultipliedGreen; + half unmultipliedBlue; + half alpha; + + cs -> getPixel(it.rawData(), &unmultipliedRed, &unmultipliedGreen, &unmultipliedBlue, &alpha); + rgba -> r = unmultipliedRed * alpha; + rgba -> g = unmultipliedGreen * alpha; + rgba -> b = unmultipliedBlue * alpha; + rgba -> a = alpha; + ++it; + ++rgba; + } + file.writePixels(); + } + + //vKisAnnotationSP_it beginIt = img -> beginAnnotations(); + //vKisAnnotationSP_it endIt = img -> endAnnotations(); + return KoFilter::OK; +} + +#include "kis_openexr_export.moc" + diff --git a/filters/krita/openexr/kis_openexr_export.h b/filters/krita/openexr/kis_openexr_export.h new file mode 100644 index 00000000..059a799d --- /dev/null +++ b/filters/krita/openexr/kis_openexr_export.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Adrian Page <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_OPENEXR_EXPORT_H_ +#define KIS_OPENEXR_EXPORT_H_ + +#include <KoFilter.h> + +class KisOpenEXRExport : public KoFilter { + Q_OBJECT + +public: + KisOpenEXRExport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisOpenEXRExport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // KIS_OPENEXR_EXPORT_H_ + diff --git a/filters/krita/openexr/kis_openexr_import.cpp b/filters/krita/openexr/kis_openexr_import.cpp new file mode 100644 index 00000000..c2db3385 --- /dev/null +++ b/filters/krita/openexr/kis_openexr_import.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2005 Adrian Page <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <qstring.h> +#include <qfile.h> + +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <half.h> +#include <ImfRgbaFile.h> +//#include <ImfStringAttribute.h> +//#include <ImfMatrixAttribute.h> +//#include <ImfArray.h> +//#include <drawImage.h> + +#include <iostream> + +#include "kis_types.h" +#include "kis_openexr_import.h" +#include "kis_doc.h" +#include "kis_image.h" +#include "kis_meta_registry.h" +#include "kis_layer.h" +#include "kis_paint_layer.h" +#include "kis_annotation.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_iterators_pixel.h" +#include "kis_abstract_colorspace.h" +#include "kis_rgb_f32_colorspace.h" +#include "kis_rgb_f16half_colorspace.h" + +using namespace std; +using namespace Imf; +using namespace Imath; + +typedef KGenericFactory<KisOpenEXRImport, KoFilter> KisOpenEXRImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkrita_openexr_import, KisOpenEXRImportFactory("kofficefilters")) + +KisOpenEXRImport::KisOpenEXRImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisOpenEXRImport::~KisOpenEXRImport() +{ +} + +KoFilter::ConversionStatus KisOpenEXRImport::convert(const QCString& from, const QCString& to) +{ + if (from != "image/x-exr" || to != "application/x-krita") { + return KoFilter::NotImplemented; + } + + kdDebug(41008) << "\n\n\nKrita importing from OpenEXR\n"; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + if (!doc) { + return KoFilter::CreationError; + } + + doc -> prepareForImport(); + + QString filename = m_chain -> inputFile(); + + if (filename.isEmpty()) { + return KoFilter::FileNotFound; + } + + RgbaInputFile file(QFile::encodeName(filename)); + Box2i dataWindow = file.dataWindow(); + Box2i displayWindow = file.displayWindow(); + + kdDebug(41008) << "Data window: " << QRect(dataWindow.min.x, dataWindow.min.y, dataWindow.max.x - dataWindow.min.x + 1, dataWindow.max.y - dataWindow.min.y + 1) << endl; + kdDebug(41008) << "Display window: " << QRect(displayWindow.min.x, displayWindow.min.y, displayWindow.max.x - displayWindow.min.x + 1, displayWindow.max.y - displayWindow.min.y + 1) << endl; + + int imageWidth = displayWindow.max.x - displayWindow.min.x + 1; + int imageHeight = displayWindow.max.y - displayWindow.min.y + 1; + + QString imageName = "Imported from OpenEXR"; + + int dataWidth = dataWindow.max.x - dataWindow.min.x + 1; + int dataHeight = dataWindow.max.y - dataWindow.min.y + 1; + + KisRgbF16HalfColorSpace *cs = static_cast<KisRgbF16HalfColorSpace *>((KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBAF16HALF", ""),""))); + + if (cs == 0) { + return KoFilter::InternalError; + } + + doc -> undoAdapter() -> setUndo(false); + + KisImageSP image = new KisImage(doc->undoAdapter(), imageWidth, imageHeight, cs, imageName); + + if (image == 0) { + return KoFilter::CreationError; + } + + KisPaintLayerSP layer = dynamic_cast<KisPaintLayer*>(image->newLayer(image -> nextLayerName(), OPACITY_OPAQUE).data()); + + if (layer == 0) { + return KoFilter::CreationError; + } + + QMemArray<Rgba> pixels(dataWidth); + + for (int y = 0; y < dataHeight; ++y) { + + file.setFrameBuffer(pixels.data() - dataWindow.min.x - (dataWindow.min.y + y) * dataWidth, 1, dataWidth); + file.readPixels(dataWindow.min.y + y); + + KisHLineIterator it = layer->paintDevice()->createHLineIterator(dataWindow.min.x, dataWindow.min.y + y, dataWidth, true); + Rgba *rgba = pixels.data(); + + while (!it.isDone()) { + + // XXX: For now unmultiply the alpha, though compositing will be faster if we + // keep it premultiplied. + half unmultipliedRed = rgba -> r; + half unmultipliedGreen = rgba -> g; + half unmultipliedBlue = rgba -> b; + + if (rgba -> a >= HALF_EPSILON) { + unmultipliedRed /= rgba -> a; + unmultipliedGreen /= rgba -> a; + unmultipliedBlue /= rgba -> a; + } + + cs -> setPixel(it.rawData(), unmultipliedRed, unmultipliedGreen, unmultipliedBlue, rgba -> a); + ++it; + ++rgba; + } + } + + layer->setDirty(); + doc -> setCurrentImage(image); + doc -> undoAdapter() -> setUndo(true); + doc -> setModified(false); + + return KoFilter::OK; +} + +#include "kis_openexr_import.moc" + diff --git a/filters/krita/openexr/kis_openexr_import.h b/filters/krita/openexr/kis_openexr_import.h new file mode 100644 index 00000000..98858537 --- /dev/null +++ b/filters/krita/openexr/kis_openexr_import.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Adrian Page <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_OPENEXR_IMPORT_H_ +#define KIS_OPENEXR_IMPORT_H_ + +#include <KoFilter.h> + +class KisOpenEXRImport : public KoFilter { + Q_OBJECT + +public: + KisOpenEXRImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisOpenEXRImport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // KIS_OPENEXR_IMPORT_H_ + diff --git a/filters/krita/openexr/krita_openexr.desktop b/filters/krita/openexr/krita_openexr.desktop new file mode 100644 index 00000000..ef026a84 --- /dev/null +++ b/filters/krita/openexr/krita_openexr.desktop @@ -0,0 +1,57 @@ +[Desktop Entry] +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Exec=krita %u +GenericName=Painting and Image Editing Application +GenericName[bg]=Редактор на графични изображения +GenericName[ca]=Programa de dibuix i manipulació d'imatges +GenericName[cs]=Malování a úpravy obrázků +GenericName[cy]=Cymhwysiad Peintio Golygu Delweddau +GenericName[da]=Male- og billedredigeringsprogram +GenericName[de]=Mal- und Bildbearbeitungsprogramm +GenericName[el]=Εφαρμογή επεξεργασίας εικόνων +GenericName[eo]=Aplikaĵo por Pentrado kaj Bildredaktado +GenericName[es]=Aplicación de pintura y de edición de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudien marrazketa eta ediziorako aplikazioa +GenericName[fa]=کاربرد ویرایش تصویر و نقاشی +GenericName[fi]=Maalaus- ja kuvankäsitelyohjelma +GenericName[fr]=Application de dessin et de manipulation d'images +GenericName[fy]=Ofbyldingsmanipulaasje +GenericName[gl]=Aplicación de Pintura e Manipulación de Imaxes +GenericName[he]=יישום לציור ועריכת תמונות +GenericName[hr]=Aplikacija za obradu slika i fotografija +GenericName[hu]=Képszerkesztő +GenericName[is]=Málun og myndritill +GenericName[it]=Applicazione di disegno e di modifica delle immagini +GenericName[ja]=描画と画像編集のためのアプリケーション +GenericName[km]=កម្មវិធីគូរគំនូរ និងកែសម្រួលរូបភាព +GenericName[lv]=Zīmēšanas un attēlu apstrādes programma +GenericName[nb]=Program for tegning og bilderedigering +GenericName[nds]=Programm för't Malen un Bildbewerken +GenericName[ne]=पेन्टीङ्ग र छवि सम्पादन अनुप्रयोग +GenericName[nl]=Afbeeldingsmanipulatie +GenericName[pl]=Program do edycji zdjęć oraz rysunków +GenericName[pt]=Aplicação de Pintura e Edição de Imagens +GenericName[pt_BR]=Aplicação de Pintura e Edição de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Málen- ja govvagieđahallanprográmma +GenericName[sk]=Program pre tvorbu a úpravu obrázkov +GenericName[sl]=Program za risanje in obdelavo slik +GenericName[sr]=Програм за цртање и уређивање слика +GenericName[sr@Latn]=Program za crtanje i uređivanje slika +GenericName[sv]=Målnings- och bildredigeringsprogram +GenericName[uk]=Програма для малювання і редагування зображень +GenericName[uz]=Rasmlar bilan ishlaydigan dastur +GenericName[uz@cyrillic]=Расмлар билан ишлайдиган дастур +GenericName[zh_CN]=绘图和图像编辑应用程序 +GenericName[zh_TW]=繪圖與影像處理程式 +MimeType=image/x-exr +Type=Application +Icon=krita +Categories= +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi diff --git a/filters/krita/openexr/krita_openexr_export.desktop b/filters/krita/openexr/krita_openexr_export.desktop new file mode 100644 index 00000000..3e2a8d7f --- /dev/null +++ b/filters/krita/openexr/krita_openexr_export.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=Krita OpenEXR Export Filter +Name[bg]=Филтър за експортиране от Krita в OpenEXR +Name[br]=Sil ezporzh OpenEXR evit Krita +Name[ca]=Filtre d'exportació OpenEXR per a Krita +Name[cy]=Hidl Allforio OpenEXR Krita +Name[da]=Krita OpenEXR-eksportfilter +Name[de]=Krita OpenEXR-Exportfilter +Name[el]=Φίλτρο εξαγωγής OpenEXR του Krita +Name[eo]=Krita OpenEXR-eksportfiltrilo +Name[es]=Filtro de exportación a OpenEXR de Krita +Name[et]=Krita OpenEXR'i ekspordifilter +Name[fa]=پالایۀ صادرات Krita OpenEXR +Name[fi]=Krita OpenEXR -vientisuodin +Name[fr]=Filtre d'exportation OpenEXR de Krita +Name[fy]=Krita OpenEXR Eksportfilter +Name[ga]=Scagaire Easpórtála OpenEXR Krita +Name[gl]=Filtro de Exportación de OpenEXR de Krita +Name[he]=Krita OpenEXR מסנן יצוא +Name[hr]=Krita OpenEXR filtar izvoza +Name[hu]=Krita OpenEXR exportszűrő +Name[is]=Krita OpenEXR útflutningssía +Name[it]=Filtro di esportazione OpenEXR per Krita +Name[ja]=Krita OpenEXR エクスポートフィルタ +Name[km]=តម្រងនាំចេញ OpenEXR សម្រាប់ Krita +Name[lt]=Krita OpenEXR eksportavimo filtras +Name[lv]=Krita OpenEXR eksporta filtrs +Name[nb]=OpenEXR-eksportfilter for Krita +Name[nds]=OpenEXR-Exportfilter för Krita +Name[ne]=क्रिता खुलाEXR निर्यात फिल्टर +Name[nl]=Krita OpenEXR Exportfilter +Name[pl]=Filtr eksportu do formatu OpenEXR z Krita +Name[pt]=Filtro de Exportação de OpenEXR do Krita +Name[pt_BR]=Filtro de Exportação de OpenEXR do Krita +Name[ru]=Фильтр экспорта рисунков Krita в OpenEXR +Name[se]=Krita OpenEXR-olggosfievrridansilli +Name[sk]=Exportný filter Krita OpenEXR +Name[sl]=Izvozni filter OpenEXR za Krito +Name[sr]=Krita-ин филтер за извоз у OpenEXR +Name[sr@Latn]=Krita-in filter za izvoz u OpenEXR +Name[sv]=Krita OpenEXR-exportfilter +Name[uk]=Фільтр експорту OpenEXR для Krita +Name[uz]=Krita OpenEXR eksport filteri +Name[uz@cyrillic]=Krita OpenEXR экспорт филтери +Name[zh_CN]=Krita OpenEXR 导出过滤器 +Name[zh_TW]=Krita OpenEXR 匯出過濾程式 +X-KDE-Export=image/x-exr +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Import=application/x-krita +X-KDE-Weight=1 +X-KDE-Library=libkrita_openexr_export diff --git a/filters/krita/openexr/krita_openexr_import.desktop b/filters/krita/openexr/krita_openexr_import.desktop new file mode 100644 index 00000000..521320de --- /dev/null +++ b/filters/krita/openexr/krita_openexr_import.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Type=Service +Name=Krita OpenEXR Import Filter +Name[bg]=Филтър за импортиране от OpenEXR в Krita +Name[br]=Sil enporzh OpenEXR evit Krita +Name[ca]=Filtre d'importació OpenEXR per a Krita +Name[cy]=Hidl Mewnforio OpenEXR Krita +Name[da]=Krita OpenEXR-importfilter +Name[de]=Krita OpenEXR-Importfilter +Name[el]=Φίλτρο εισαγωγής OpenEXR του Krita +Name[eo]=Krita OpenEXR-importfiltrilo +Name[es]=Filtro de importación a OpenEXR de Krita +Name[et]=Krita OpenEXR'i impordifilter +Name[fa]=پالایۀ واردات Krita OpenEXR +Name[fi]=Krita OpenEXR -tuontisuodin +Name[fr]=Filtre d'importation OpenEXT de Krita +Name[fy]=Krita OpenEXR Ymportfilter +Name[ga]=Scagaire Iompórtála OpenEXR Krita +Name[gl]=Filtro de Importación de OpenEXR para Krita +Name[he]=Krita OpenEXR מסנן יבוא +Name[hr]=Krita OpenEXR filtar uvoza +Name[hu]=Krita OpenEXR importszűrő +Name[is]=Krita OpenEXR innflutningssía +Name[it]=Filtro di importazione OpenEXR per Krita +Name[ja]=Krita OpenEXR インポートフィルタ +Name[km]=តម្រងនាំចូល OpenEXR សម្រាប់ Krita +Name[lt]=Krita OpenEXR importavimo filtras +Name[lv]=Krita OpenEXR importa filtrs +Name[nb]=OpenEXR-importfilter for Krita +Name[nds]=OpenEXR-Importfilter för Krita +Name[ne]=क्रिता खुलाEXR आयात फिल्टर +Name[nl]=Krita OpenEXR Importfilter +Name[pl]=Filtr importu formatu OpenEXR do Krita +Name[pt]=Filtro de Importação de OpenEXR para o Krita +Name[pt_BR]=Filtro de Importação de OpenEXR para o Krita +Name[ru]=Фильтр импорта рисунков OpenEXR в Krita +Name[se]=Krita OpenEXR-sisafievrridansilli +Name[sk]=OpenEXR filter pre import do Krita +Name[sl]=Uvozni filter OpenEXR za Krito +Name[sr]=Krita-ин филтер за увоз из OpenEXR-а +Name[sr@Latn]=Krita-in filter za uvoz iz OpenEXR-a +Name[sv]=Krita OpenEXR-importfilter +Name[uk]=Фільтр імпорту OpenEXR для Krita +Name[uz]=Krita OpenEXR import filteri +Name[uz@cyrillic]=Krita OpenEXR импорт филтери +Name[zh_CN]=Krita OpenEXR 导入过滤器 +Name[zh_TW]=Krita OpenEXR 匯入過濾程式 +X-KDE-Export=application/x-krita +X-KDE-Import=image/x-exr +X-KDE-Weight=1 +X-KDE-Library=libkrita_openexr_import +ServiceTypes=KOfficeFilter diff --git a/filters/krita/pdf/Makefile.am b/filters/krita/pdf/Makefile.am new file mode 100644 index 00000000..7f2366bc --- /dev/null +++ b/filters/krita/pdf/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) \ + $(all_includes) $(POPPLER_CFLAGS) + +servicedir = $(kde_servicesdir) + +kdelnkdir = $(kde_appsdir)/.hidden + +METASOURCES = AUTO +kde_module_LTLIBRARIES = libkritapdfimport.la +libkritapdfimport_la_LDFLAGS = -avoid-version -module -no-undefined $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) +libkritapdfimport_la_LIBADD = $(top_builddir)/krita/libkritacommon.la \ + -lpoppler-qt $(KOFFICE_LIBS) $(POPPLER_LIBS) + +noinst_HEADERS = kis_pdf_import.h kis_pdf_import_widget.h +libkritapdfimport_la_SOURCES = kis_pdf_import.cpp pdfimportwidgetbase.ui \ + kis_pdf_import_widget.cpp +kde_services_DATA = krita_pdf_import.desktop +kdelnk_DATA = krita_pdf.desktop diff --git a/filters/krita/pdf/configure.in.bot b/filters/krita/pdf/configure.in.bot new file mode 100644 index 00000000..9802779d --- /dev/null +++ b/filters/krita/pdf/configure.in.bot @@ -0,0 +1,7 @@ +if test -z "$POPPLER_LIBS"; then + echo "" + echo "You're missing libpoppler 0.5.1 or later (binaries and/or headers)." + echo "krita won't be able to import pdf" + echo "note that the qt-binding of libpoppler is required" + echo "" +fi diff --git a/filters/krita/pdf/configure.in.in b/filters/krita/pdf/configure.in.in new file mode 100644 index 00000000..e4343277 --- /dev/null +++ b/filters/krita/pdf/configure.in.in @@ -0,0 +1,4 @@ +# Compile the pdf import filter only if Poppler is available +PKG_CHECK_MODULES(POPPLER, poppler-qt >= 0.5.1, have_poppler=yes, have_poppler=no) + +AM_CONDITIONAL(include_PDF, test "x$have_poppler" = xyes) diff --git a/filters/krita/pdf/kis_pdf_import.cpp b/filters/krita/pdf/kis_pdf_import.cpp new file mode 100644 index 00000000..ad200414 --- /dev/null +++ b/filters/krita/pdf/kis_pdf_import.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_pdf_import.h" + +// poppler's headers +#include <poppler-qt.h> + +// Qt's headers +#include <qfile.h> +#include <qimage.h> // TODO that too +#include <qradiobutton.h> + +// KDE's headers +#include <kapplication.h> +#include <kdebug.h> +#include <kdialogbase.h> +#include <kgenericfactory.h> +#include <knuminput.h> +#include <kpassdlg.h> + +#include <kio/netaccess.h> + +// koffice's headers +#include <KoFilterChain.h> + +// krita's headers +#include <kis_doc.h> +#include <kis_colorspace.h> +#include <kis_colorspace_factory_registry.h> +#include <kis_group_layer.h> +#include <kis_image.h> +#include <kis_meta_registry.h> +#include <kis_paint_layer.h> + +// plugins's headers +#include "kis_pdf_import_widget.h" + +typedef KGenericFactory<KisPDFImport, KoFilter> PDFImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritapdfimport, PDFImportFactory("kofficefilters")) + +KisPDFImport::KisPDFImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisPDFImport::~KisPDFImport() +{ +} + +KisPDFImport::ConversionStatus KisPDFImport::convert(const QCString& , const QCString& ) +{ + QString filename = m_chain -> inputFile(); + kdDebug(41008) << "Importing using PDFImport!" << filename << endl; + + if (filename.isEmpty()) + return KoFilter::FileNotFound; + + + KURL url; + url.setPath(filename); + + if (!KIO::NetAccess::exists(url, false, qApp -> mainWidget())) { + return KoFilter::FileNotFound; + } + + // We're not set up to handle asynchronous loading at the moment. + QString tmpFile; + if (KIO::NetAccess::download(url, tmpFile, qApp -> mainWidget())) { + url.setPath( tmpFile ); + } + + Poppler::Document* pdoc = Poppler::Document::load( QFile::encodeName(url.path() ) ); + + + if ( !pdoc) + { + kdDebug(41008) << "Error when reading the PDF" << endl; + return KoFilter::StorageCreationError; + } + + + while( pdoc->isLocked() ) + { + QCString password; + int result = KPasswordDialog::getPassword(password, i18n("A password is required to read that pdf")); + if (result == KPasswordDialog::Accepted) + { + pdoc->unlock(password); + } else { + kdDebug(41008) << "Password canceled" << endl; + return KoFilter::StorageCreationError; + } + } + + KDialogBase* kdb = new KDialogBase(0, "", false, i18n("PDF Import Options"), KDialogBase::Ok | KDialogBase::Cancel); + + KisPDFImportWidget* wdg = new KisPDFImportWidget(pdoc, kdb); + kdb->setMainWidget(wdg); + kapp->restoreOverrideCursor(); + if(kdb->exec() == QDialog::Rejected) + { + delete pdoc; + delete kdb; + return KoFilter::StorageCreationError; // FIXME Cancel doesn't exist :( + } + + // Init kis's doc + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + if (!doc) + { + delete pdoc; + delete kdb; + return KoFilter::CreationError; + } + + doc -> prepareForImport(); + // Create the krita image + KisColorSpace* cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA"), ""); + int width = wdg->intWidth->value(); + int height = wdg->intHeight->value(); + KisImageSP img = new KisImage(doc->undoAdapter(), width, height, cs, "built image"); + img->blockSignals(true); // Don't send out signals while we're building the image + + // create a layer + QValueList<int> pages = wdg->pages(); + for(QValueList<int>::const_iterator it = pages.begin(); it != pages.end(); ++it) + { + KisPaintLayer* layer = new KisPaintLayer(img, QString(i18n("Page %1")).arg( QString::number(*it) + 1), Q_UINT8_MAX); + layer->paintDevice()->convertFromQImage( pdoc->getPage( *it )->renderToImage(wdg->intHorizontal->value(), wdg->intVertical->value() ), ""); + img->addLayer(layer, img->rootLayer(), 0); + } + + img->blockSignals(false); + doc -> setCurrentImage( img); + + KIO::NetAccess::removeTempFile(tmpFile); + + delete pdoc; + delete kdb; + return KoFilter::OK; +} + diff --git a/filters/krita/pdf/kis_pdf_import.h b/filters/krita/pdf/kis_pdf_import.h new file mode 100644 index 00000000..217319db --- /dev/null +++ b/filters/krita/pdf/kis_pdf_import.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_PDF_IMPORT_H +#define KIS_PDF_IMPORT_H + +#include <KoFilter.h> + +class KisPDFImport : public KoFilter{ + Q_OBJECT + public: + KisPDFImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisPDFImport(); + public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif diff --git a/filters/krita/pdf/kis_pdf_import_widget.cpp b/filters/krita/pdf/kis_pdf_import_widget.cpp new file mode 100644 index 00000000..b3d37015 --- /dev/null +++ b/filters/krita/pdf/kis_pdf_import_widget.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_pdf_import_widget.h" + +// poppler's headers +#include <poppler-qt.h> + +// Qt's headers +#include <qradiobutton.h> + +// KDE's headers +#include <kdebug.h> +#include <klistbox.h> +#include <knuminput.h> + +KisPDFImportWidget::KisPDFImportWidget(Poppler::Document* pdfDoc, QWidget * parent, const char * name) + : PDFImportWidgetBase(parent, name), m_pdfDoc(pdfDoc) +{ + m_pages.push_back(0); // The first page is selected + updateMaxCanvasSize(); + + for(int i = 1; i <= m_pdfDoc->getNumPages(); i++) + { + listPages->insertItem(QString::number( i ) ); + } + + connect(intWidth, SIGNAL( valueChanged ( int ) ), this, SLOT( updateHRes() ) ); + connect(intHeight, SIGNAL( valueChanged ( int ) ), this, SLOT( updateHVer() ) ); + connect(intHorizontal, SIGNAL( valueChanged ( int ) ), this, SLOT( updateWidth() ) ); + connect(intVertical, SIGNAL( valueChanged ( int ) ), this, SLOT( updateHeight() ) ); + connect(boolAllPages, SIGNAL( toggled ( bool ) ), this, SLOT( selectAllPages( bool ) ) ); + connect(boolFirstPage, SIGNAL( toggled ( bool ) ), this, SLOT( selectFirstPage( bool ) ) ); + connect(boolSelectionPage, SIGNAL( toggled ( bool ) ), this, SLOT( selectSelectionOfPages( bool ) ) ); + connect(listPages, SIGNAL(selectionChanged () ), this, SLOT(updateSelectionOfPages())); +} + + +KisPDFImportWidget::~KisPDFImportWidget() +{ +} + +void KisPDFImportWidget::selectAllPages(bool v) +{ + if(v) + { + m_pages.clear(); + for(int i = 0; i < m_pdfDoc->getNumPages(); i++) + { + m_pages.push_back(i); + } + updateMaxCanvasSize(); + } +} +void KisPDFImportWidget::selectFirstPage(bool v) +{ + if(v) + { + m_pages.clear(); + m_pages.push_back(0); // The first page is selected + } +} +void KisPDFImportWidget::selectSelectionOfPages(bool v) +{ + if(v) + { + updateSelectionOfPages(); + updateMaxCanvasSize(); + } + +} + +void KisPDFImportWidget::updateSelectionOfPages() +{ + if(! boolSelectionPage->isChecked ()) boolSelectionPage->toggle(); + m_pages.clear(); + for(int i = 0; i < m_pdfDoc->getNumPages(); i++) + { + if(listPages->isSelected(i)) m_pages.push_back(i); + } +} + + +void KisPDFImportWidget::updateMaxCanvasSize() { + m_maxWidthInch = 0., m_maxHeightInch =0.; + for(QValueList<int>::const_iterator it = m_pages.begin(); it != m_pages.end(); ++it) + { + Poppler::Page *p = m_pdfDoc->getPage(*it ); + QSize size = p->pageSize(); + if(size.width() > m_maxWidthInch) + { + m_maxWidthInch = size.width(); + } + if(size.height() > m_maxHeightInch) + { + m_maxHeightInch = size.height(); + } + } + m_maxWidthInch /= 72.; + m_maxHeightInch /= 72.; + kdDebug() << m_maxWidthInch << " " << m_maxHeightInch << endl; + updateWidth(); + updateHeight(); +} + +void KisPDFImportWidget::updateWidth() +{ + intWidth->blockSignals(true); + intWidth->setValue( (int) m_maxWidthInch * intHorizontal->value() + 1 ); + intWidth->blockSignals(false); +} +void KisPDFImportWidget::updateHeight() +{ + intHeight->blockSignals(true); + intHeight->setValue( (int) m_maxHeightInch * intVertical->value() + 1 ); + intHeight->blockSignals(false); +} +void KisPDFImportWidget::updateHRes() +{ + intHorizontal->blockSignals(true); + intHorizontal->setValue( (int) (intWidth->value() / m_maxWidthInch ) ); + intHorizontal->blockSignals(false); +} +void KisPDFImportWidget::updateHVer() +{ + intVertical->blockSignals(true); + intVertical->setValue( (int) (intHeight->value() / m_maxHeightInch ) ); + intVertical->blockSignals(false); +} + +#include "kis_pdf_import_widget.moc" diff --git a/filters/krita/pdf/kis_pdf_import_widget.h b/filters/krita/pdf/kis_pdf_import_widget.h new file mode 100644 index 00000000..debc0f0e --- /dev/null +++ b/filters/krita/pdf/kis_pdf_import_widget.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_PDF_IMPORT_WIDGET_H +#define KIS_PDF_IMPORT_WIDGET_H + +#include <pdfimportwidgetbase.h> + +namespace Poppler { +class Document; +} + +class KisPDFImportWidget : public PDFImportWidgetBase +{ + Q_OBJECT + public: + KisPDFImportWidget(Poppler::Document* pdfDoc, QWidget * parent, const char * name = ""); + + ~KisPDFImportWidget(); + public: + inline QValueList<int> pages() { return m_pages; } + private slots: + void selectAllPages(bool v); + void selectFirstPage(bool v); + void selectSelectionOfPages(bool v); + void updateSelectionOfPages(); + void updateWidth(); + void updateHeight(); + void updateHRes(); + void updateHVer(); + void updateMaxCanvasSize(); + private: + Poppler::Document* m_pdfDoc; + QValueList<int> m_pages; + double m_maxWidthInch, m_maxHeightInch; +}; + +#endif diff --git a/filters/krita/pdf/krita_pdf.desktop b/filters/krita/pdf/krita_pdf.desktop new file mode 100644 index 00000000..0f882866 --- /dev/null +++ b/filters/krita/pdf/krita_pdf.desktop @@ -0,0 +1,63 @@ +[Desktop Entry] +Categories= +Exec=krita %u +GenericName=Painting and Image Editing Application +GenericName[bg]=Редактор на графични изображения +GenericName[ca]=Programa de dibuix i manipulació d'imatges +GenericName[cs]=Malování a úpravy obrázků +GenericName[cy]=Cymhwysiad Peintio Golygu Delweddau +GenericName[da]=Male- og billedredigeringsprogram +GenericName[de]=Mal- und Bildbearbeitungsprogramm +GenericName[el]=Εφαρμογή επεξεργασίας εικόνων +GenericName[eo]=Aplikaĵo por Pentrado kaj Bildredaktado +GenericName[es]=Aplicación de pintura y de edición de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudien marrazketa eta ediziorako aplikazioa +GenericName[fa]=کاربرد ویرایش تصویر و نقاشی +GenericName[fi]=Maalaus- ja kuvankäsitelyohjelma +GenericName[fr]=Application de dessin et de manipulation d'images +GenericName[fy]=Ofbyldingsmanipulaasje +GenericName[gl]=Aplicación de Pintura e Manipulación de Imaxes +GenericName[he]=יישום לציור ועריכת תמונות +GenericName[hr]=Aplikacija za obradu slika i fotografija +GenericName[hu]=Képszerkesztő +GenericName[is]=Málun og myndritill +GenericName[it]=Applicazione di disegno e di modifica delle immagini +GenericName[ja]=描画と画像編集のためのアプリケーション +GenericName[km]=កម្មវិធីគូរគំនូរ និងកែសម្រួលរូបភាព +GenericName[lv]=Zīmēšanas un attēlu apstrādes programma +GenericName[nb]=Program for tegning og bilderedigering +GenericName[nds]=Programm för't Malen un Bildbewerken +GenericName[ne]=पेन्टीङ्ग र छवि सम्पादन अनुप्रयोग +GenericName[nl]=Afbeeldingsmanipulatie +GenericName[pl]=Program do edycji zdjęć oraz rysunków +GenericName[pt]=Aplicação de Pintura e Edição de Imagens +GenericName[pt_BR]=Aplicação de Pintura e Edição de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Málen- ja govvagieđahallanprográmma +GenericName[sk]=Program pre tvorbu a úpravu obrázkov +GenericName[sl]=Program za risanje in obdelavo slik +GenericName[sr]=Програм за цртање и уређивање слика +GenericName[sr@Latn]=Program za crtanje i uređivanje slika +GenericName[sv]=Målnings- och bildredigeringsprogram +GenericName[uk]=Програма для малювання і редагування зображень +GenericName[uz]=Rasmlar bilan ishlaydigan dastur +GenericName[uz@cyrillic]=Расмлар билан ишлайдиган дастур +GenericName[zh_CN]=绘图和图像编辑应用程序 +GenericName[zh_TW]=繪圖與影像處理程式 +Icon=krita +MimeType=application/pdf +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Path= +StartupNotify=true +Terminal=false +TerminalOptions= +Type=Application +X-DCOP-ServiceType=multi +X-KDE-StartupNotify=true +X-KDE-SubstituteUID=false +X-KDE-Username= diff --git a/filters/krita/pdf/krita_pdf_import.desktop b/filters/krita/pdf/krita_pdf_import.desktop new file mode 100644 index 00000000..fe49fb73 --- /dev/null +++ b/filters/krita/pdf/krita_pdf_import.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Type=Service +Name=Krita PDF Import Filter +Name[bg]=Филтър за импортиране от PDF в Krita +Name[br]=Sil enporzh PDF evit Krita +Name[ca]=Filtre d'importació PDF per a Krita +Name[da]=Krita PDF-importfilter +Name[de]=Krita PDF-Importfilter +Name[el]=Φίλτρο εισαγωγής PDF του Krita +Name[eo]=Krita PD-importfiltrilo +Name[es]=Filtro de importación a PDF de Krita +Name[et]=Krita PDF impordifilter +Name[fa]=پالایۀ واردات Krita PDF +Name[fr]=Filtre d'importation PDF de Krita +Name[fy]=Krita PDF Ymportfilter +Name[ga]=Scagaire Iompórtála PDF Krita +Name[gl]=Filtro de Importación de PDF para Krita +Name[hr]=Krita PDF filtar uvoza +Name[hu]=Krita PDF importszűrő +Name[it]=Filtro di importazione PDF per Krita +Name[ja]=Krita PDF インポートフィルタ +Name[km]=តម្រងនាំចូល PNG របស់រាប់ Krita +Name[lt]=Krita PDF importavimo filtras +Name[lv]=Krita PDF importa filtrs +Name[nb]=PDF-importfilter for Krita +Name[nds]=PDF-Importfilter för Krita +Name[ne]=क्रिता PDF आयात फिल्टर +Name[nl]=Krita PDF Importfilter +Name[pl]=Filtr importu formatu PDF do Krita +Name[pt]=Filtro de Importação de PDF para o Krita +Name[pt_BR]=Filtro de Importação de PDF para o Krita +Name[ru]=Фильтр импорта документов PDF в Krita +Name[se]=Krita PDF-sisafievrridansilli +Name[sk]=PDF filter pre import do Krita +Name[sl]=Uvozni filter PDF za Krito +Name[sr]=Krita-ин филтер за увоз из PDF-а +Name[sr@Latn]=Krita-in filter za uvoz iz PDF-a +Name[sv]=Krita PDF-importfilter +Name[uk]=Фільтр імпорту PDF для Krita +Name[uz]=Krita uchun PDF import filteri +Name[uz@cyrillic]=Krita учун PDF импорт филтери +Name[zh_TW]=Krita PDF 匯入過濾程式 +Comment= +Comment[uz]=PDF-fayllarni Krita bilan oʻqish vositasi +Comment[uz@cyrillic]=PDF-файлларни Krita билан ўқиш воситаси +ServiceTypes=KOfficeFilter +X-KDE-Available= +X-KDE-Export=application/x-krita +X-KDE-Import=application/pdf +X-KDE-Weight=1 +X-KDE-Library=libkritapdfimport diff --git a/filters/krita/pdf/pdfimportwidgetbase.ui b/filters/krita/pdf/pdfimportwidgetbase.ui new file mode 100644 index 00000000..148e72a2 --- /dev/null +++ b/filters/krita/pdf/pdfimportwidgetbase.ui @@ -0,0 +1,321 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>PDFImportWidgetBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>PDFImportWidgetBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>462</width> + <height>210</height> + </rect> + </property> + <property name="caption"> + <string>PDFImportWidget</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="1"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Dimensions</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Resolution:</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>intVerticalqsdf</cstring> + </property> + <property name="text"> + <string>Vertical:</string> + </property> + </widget> + <widget class="KComboBox" row="0" column="2"> + <item> + <property name="text"> + <string>Dots/inch</string> + </property> + </item> + <property name="name"> + <cstring>kComboBox1</cstring> + </property> + </widget> + <widget class="KIntSpinBox" row="0" column="1"> + <property name="name"> + <cstring>intHorizontal</cstring> + </property> + <property name="maxValue"> + <number>999</number> + </property> + <property name="value"> + <number>72</number> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>intVertical</cstring> + </property> + <property name="maxValue"> + <number>999</number> + </property> + <property name="value"> + <number>72</number> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>Horizontal:</string> + </property> + </widget> + <widget class="KComboBox" row="1" column="2"> + <item> + <property name="text"> + <string>Dots/inch</string> + </property> + </item> + <property name="name"> + <cstring>kComboBox2</cstring> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Size:</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>intHeight</cstring> + </property> + <property name="maxValue"> + <number>99999</number> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>intWidthzqffs</cstring> + </property> + <property name="text"> + <string>Width:</string> + </property> + </widget> + <widget class="KIntSpinBox" row="0" column="1"> + <property name="name"> + <cstring>intWidth</cstring> + </property> + <property name="maxValue"> + <number>99999</number> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>intHeightqsdfq</cstring> + </property> + <property name="text"> + <string>Height:</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>51</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QButtonGroup" row="0" column="0"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>Pages</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>boolAllPages</cstring> + </property> + <property name="text"> + <string>&All pages</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>boolFirstPage</cstring> + </property> + <property name="text"> + <string>&First page</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>boolSelectionPage</cstring> + </property> + <property name="text"> + <string>&Selection of page</string> + </property> + </widget> + <widget class="KListBox"> + <property name="name"> + <cstring>listPages</cstring> + </property> + <property name="selectionMode"> + <enum>Multi</enum> + </property> + </widget> + </vbox> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>boolFirstPage</tabstop> + <tabstop>listPages</tabstop> + <tabstop>intHorizontal</tabstop> + <tabstop>kComboBox1</tabstop> + <tabstop>intVertical</tabstop> + <tabstop>kComboBox2</tabstop> + <tabstop>intWidth</tabstop> + <tabstop>intHeight</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>klistbox.h</includehint> +</includehints> +</UI> diff --git a/filters/krita/png/Makefile.am b/filters/krita/png/Makefile.am new file mode 100644 index 00000000..88160156 --- /dev/null +++ b/filters/krita/png/Makefile.am @@ -0,0 +1,40 @@ +kde_module_LTLIBRARIES = libkritapngimport.la libkritapngexport.la + +libkritapngexport_la_LDFLAGS = $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritapngexport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(top_builddir)/krita/libkritacommon.la \ + -lpng + +libkritapngimport_la_LDFLAGS = $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritapngimport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(top_builddir)/krita/libkritacommon.la \ + -lpng + +INCLUDES= \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) \ + $(all_includes) + +service_DATA = krita_png_import.desktop krita_png_export.desktop +servicedir = $(kde_servicesdir) + +kdelnk_DATA = krita_png.desktop +kdelnkdir = $(kde_appsdir)/.hidden + +libkritapngimport_la_SOURCES = kis_png_import.cc kis_png_converter.cc +libkritapngexport_la_SOURCES = kis_wdg_options_png.ui kis_png_export.cc kis_png_converter.cc + +METASOURCES = AUTO + + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) diff --git a/filters/krita/png/configure.in.bot b/filters/krita/png/configure.in.bot new file mode 100644 index 00000000..0df5c7ca --- /dev/null +++ b/filters/krita/png/configure.in.bot @@ -0,0 +1,8 @@ +if test -z "$LIBPNG"; then + echo "" + echo "You're missing libpng (binaries and/or headers), krita won't be able" + echo "to import/export png" + echo "" + all_tests=bad +fi + diff --git a/filters/krita/png/kis_png_converter.cc b/filters/krita/png/kis_png_converter.cc new file mode 100644 index 00000000..0beb0b74 --- /dev/null +++ b/filters/krita/png/kis_png_converter.cc @@ -0,0 +1,794 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + // A big thank to Glenn Randers-Pehrson for it's wonderfull documentation of libpng available at http://www.libpng.org/pub/png/libpng-1.2.5-manual.html +#include "kis_png_converter.h" + +#include <stdio.h> + +#include <qfile.h> + +#include <kapplication.h> +#include <kmessagebox.h> +#include <klocale.h> + +#include <KoDocumentInfo.h> + +#include <kio/netaccess.h> + +#include <kis_abstract_colorspace.h> +#include <kis_colorspace_factory_registry.h> +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_iterators_pixel.h> +#include <kis_layer.h> +#include <kis_meta_registry.h> +#include <kis_profile.h> +#include <kis_paint_layer.h> +#include <kis_group_layer.h> + +namespace { + + const Q_UINT8 PIXEL_BLUE = 0; + const Q_UINT8 PIXEL_GREEN = 1; + const Q_UINT8 PIXEL_RED = 2; + const Q_UINT8 PIXEL_ALPHA = 3; + + + int getColorTypeforColorSpace( KisColorSpace * cs , bool alpha) + { + if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) + { + return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; + } + if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) + { + return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; + } + + KMessageBox::error(0, i18n("Cannot export images in %1.\n").arg(cs->id().name()) ) ; + return -1; + + } + + + QString getColorSpaceForColorType(int color_type,int color_nb_bits) { + if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + switch(color_nb_bits) + { + case 8: + return "GRAYA"; + case 16: + return "GRAYA16"; + } + } else if(color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) { + switch(color_nb_bits) + { + case 8: + return "RGBA"; + case 16: + return "RGBA16"; + } + } else if(color_type == PNG_COLOR_TYPE_PALETTE) { + return "RGBA"; // <-- we will convert the index image to RGBA + } + return ""; + } + + + void fillText(png_text* p_text, char* key, QString& text) + { + p_text->compression = PNG_TEXT_COMPRESSION_zTXt; + p_text->key = key; + char* textc = new char[text.length()+1]; + strcpy(textc, text.ascii()); + p_text->text = textc; + p_text->text_length = text.length()+1; + } + +} + +KisPNGConverter::KisPNGConverter(KisDoc *doc, KisUndoAdapter *adapter) +{ + Q_ASSERT(doc); + Q_ASSERT(adapter); + + m_doc = doc; + m_adapter = adapter; + m_stop = false; + m_max_row = 0; + m_img = 0; +} + +KisPNGConverter::~KisPNGConverter() +{ +} + +class KisPNGStream { + public: + KisPNGStream(Q_UINT8* buf, Q_UINT32 depth ) : m_posinc(8),m_depth(depth), m_buf(buf) { *m_buf = 0;}; + int nextValue() + { + if( m_posinc == 0) + { + m_posinc = 8; + m_buf++; + } + m_posinc -= m_depth; + return (( (*m_buf) >> (m_posinc) ) & ( ( 1 << m_depth ) - 1 ) ); + } + void setNextValue(int v) + { + if( m_posinc == 0) + { + m_posinc = 8; + m_buf++; + *m_buf = 0; + } + m_posinc -= m_depth; + *m_buf = (v << m_posinc) | *m_buf; + } + private: + Q_UINT32 m_posinc, m_depth; + Q_UINT8* m_buf; +}; + +KisImageBuilder_Result KisPNGConverter::decode(const KURL& uri) +{ + kdDebug(41008) << "Start decoding PNG File" << endl; + // open the file + kdDebug(41008) << QFile::encodeName(uri.path()) << " " << uri.path() << " " << uri << endl; + FILE *fp = fopen(QFile::encodeName(uri.path()), "rb"); + if (!fp) + { + return (KisImageBuilder_RESULT_NOT_EXIST); + } + png_byte signature[8]; + fread(signature, 1, 8, fp); + if (!png_check_sig(signature, 8)) + { + return (KisImageBuilder_RESULT_BAD_FETCH); + } + + // Initialize the internal structures + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); + if (!KisImageBuilder_RESULT_FAILURE) + return (KisImageBuilder_RESULT_FAILURE); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return (KisImageBuilder_RESULT_FAILURE); + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + return (KisImageBuilder_RESULT_FAILURE); + } + + // Catch errors + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return (KisImageBuilder_RESULT_FAILURE); + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + + // read all PNG info up to image data + png_read_info(png_ptr, info_ptr); + + // Read information about the png + png_uint_32 width, height; + int color_nb_bits, color_type, interlace_type; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, NULL, NULL); + kdDebug(41008) << "it's an " << color_nb_bits << " depth image" << endl; + + // swap byteorder on little endian machines. + #ifndef WORDS_BIGENDIAN + if (color_nb_bits > 8 ) + png_set_swap(png_ptr); + #endif + + // Determine the colorspace + QString csName = getColorSpaceForColorType(color_type, color_nb_bits); + if(csName.isEmpty()) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA); + + // Read image profile + png_charp profile_name, profile_data; + int compression_type; + png_uint_32 proflen; + int number_of_passes = 1; + + if (interlace_type == PNG_INTERLACE_ADAM7) + number_of_passes = png_set_interlace_handling(png_ptr); + + KisProfile* profile = 0; + if(png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) + { + QByteArray profile_rawdata; + // XXX: Hardcoded for icc type -- is that correct for us? + if (QString::compare(profile_name, "icc") == 0) { + profile_rawdata.resize(proflen); + memcpy(profile_rawdata.data(), profile_data, proflen); + profile = new KisProfile(profile_rawdata); + Q_CHECK_PTR(profile); + if (profile) { + kdDebug(41008) << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo() << endl; + if(!profile->isSuitableForOutput()) + { + kdDebug(41008) << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile" << endl; // TODO: in ko2 popup a selection menu to inform the user + } + } + } + } + + // Retrieve a pointer to the colorspace + KisColorSpace* cs; + if (profile && profile->isSuitableForOutput()) + { + kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n"; + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile); + } + else + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),""); + + if(cs == 0) + { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + + // Create the cmsTransform if needed + cmsHTRANSFORM transform = 0; + if(profile && !profile->isSuitableForOutput()) + { + transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(), + cs->getProfile()->profile() , cs->colorSpaceType(), + INTENT_PERCEPTUAL, 0); + } + + // Read comments/texts... + png_text* text_ptr; + int num_comments; + png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments); + KoDocumentInfo * info = m_doc->documentInfo(); + KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" )); + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author")); + kdDebug(41008) << "There are " << num_comments << " comments in the text" << endl; + for(int i = 0; i < num_comments; i++) + { + kdDebug(41008) << "key is " << text_ptr[i].key << " containing " << text_ptr[i].text << endl; + if(QString::compare(text_ptr[i].key, "title") == 0) + { + aboutPage->setTitle(text_ptr[i].text); + } else if(QString::compare(text_ptr[i].key, "abstract") == 0) + { + aboutPage->setAbstract(text_ptr[i].text); + } else if(QString::compare(text_ptr[i].key, "author") == 0) + { + authorPage->setFullName(text_ptr[i].text); + } + } + + // Read image data + png_bytep row_pointer = 0; + try + { + png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); + row_pointer = new png_byte[rowbytes]; + } + catch(std::bad_alloc& e) + { + // new png_byte[] may raise such an exception if the image + // is invalid / to large. + kdDebug(41008) << "bad alloc: " << e.what() << endl; + // Free only the already allocated png_byte instances. + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (KisImageBuilder_RESULT_FAILURE); + } + + // Read the palette if the file is indexed + png_colorp palette ; + int num_palette; + if(color_type == PNG_COLOR_TYPE_PALETTE) { + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + } +// png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL ); +// png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); // By using this function libpng will take care of freeing memory +// png_read_image(png_ptr, row_pointers); + + // Finish reading the file +// png_read_end(png_ptr, end_info); +// fclose(fp); + + // Creating the KisImageSP + if( ! m_img) { + m_img = new KisImage(m_doc->undoAdapter(), width, height, cs, "built image"); + m_img->blockSignals(true); // Don't send out signals while we're building the image + Q_CHECK_PTR(m_img); + if(profile && !profile->isSuitableForOutput()) + { + m_img -> addAnnotation( profile->annotation() ); + } + } + + KisPaintLayer* layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), Q_UINT8_MAX); + for (int i = 0; i < number_of_passes; i++) + { + for (png_uint_32 y = 0; y < height; y++) { + KisHLineIterator it = layer -> paintDevice() -> createHLineIterator(0, y, width, true); + png_read_rows(png_ptr, &row_pointer, NULL, 1); + + switch(color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: + if(color_nb_bits == 16) + { + Q_UINT16 *src = reinterpret_cast<Q_UINT16 *>(row_pointer); + while (!it.isDone()) { + Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData()); + d[0] = *(src++); + if(transform) cmsDoTransform(transform, d, d, 1); + if(hasalpha) d[1] = *(src++); + else d[1] = Q_UINT16_MAX; + ++it; + } + } else { + Q_UINT8 *src = row_pointer; + while (!it.isDone()) { + Q_UINT8 *d = it.rawData(); + d[0] = *(src++); + if(transform) cmsDoTransform(transform, d, d, 1); + if(hasalpha) d[1] = *(src++); + else d[1] = Q_UINT8_MAX; + ++it; + } + } + //FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits + break; + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + if(color_nb_bits == 16) + { + Q_UINT16 *src = reinterpret_cast<Q_UINT16 *>(row_pointer); + while (!it.isDone()) { + Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData()); + d[2] = *(src++); + d[1] = *(src++); + d[0] = *(src++); + if(transform) cmsDoTransform(transform, d, d, 1); + if(hasalpha) d[3] = *(src++); + else d[3] = Q_UINT16_MAX; + ++it; + } + } else { + Q_UINT8 *src = row_pointer; + while (!it.isDone()) { + Q_UINT8 *d = it.rawData(); + d[2] = *(src++); + d[1] = *(src++); + d[0] = *(src++); + if(transform) cmsDoTransform(transform, d, d, 1); + if(hasalpha) d[3] = *(src++); + else d[3] = Q_UINT8_MAX; + ++it; + } + } + break; + case PNG_COLOR_TYPE_PALETTE: + { + KisPNGStream stream(row_pointer, color_nb_bits); + while (!it.isDone()) { + Q_UINT8 *d = it.rawData(); + png_color c = palette[ stream.nextValue() ]; + d[2] = c.red; + d[1] = c.green; + d[0] = c.blue; + d[3] = Q_UINT8_MAX; + ++it; + } + } + break; + default: + return KisImageBuilder_RESULT_UNSUPPORTED; + } + } + } + m_img->addLayer(layer, m_img->rootLayer(), 0); + + png_read_end(png_ptr, end_info); + fclose(fp); + + // Freeing memory + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + + delete [] row_pointer; + + return KisImageBuilder_RESULT_OK; + +} + +KisImageBuilder_Result KisPNGConverter::buildImage(const KURL& uri) +{ + kdDebug(41008) << QFile::encodeName(uri.path()) << " " << uri.path() << " " << uri << endl; + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) { + return KisImageBuilder_RESULT_NOT_EXIST; + } + + // We're not set up to handle asynchronous loading at the moment. + KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; + QString tmpFile; + + if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) { + KURL uriTF; + uriTF.setPath( tmpFile ); + result = decode(uriTF); + KIO::NetAccess::removeTempFile(tmpFile); + } + + return result; +} + + +KisImageSP KisPNGConverter::image() +{ + return m_img; +} + +KisImageBuilder_Result KisPNGConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, int compression, bool interlace, bool alpha) +{ + kdDebug(41008) << "Start writing PNG File" << endl; + if (!layer) + return KisImageBuilder_RESULT_INVALID_ARG; + + KisImageSP img = layer -> image(); + if (!img) + return KisImageBuilder_RESULT_EMPTY; + + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!uri.isLocalFile()) + return KisImageBuilder_RESULT_NOT_LOCAL; + // Open file for writing + FILE *fp = fopen(QFile::encodeName(uri.path()), "wb"); + if (!fp) + { + return (KisImageBuilder_RESULT_FAILURE); + } + int height = img->height(); + int width = img->width(); + // Initialize structures + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); + if (!png_ptr) + { + KIO::del(uri); + return (KisImageBuilder_RESULT_FAILURE); + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + KIO::del(uri); + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return (KisImageBuilder_RESULT_FAILURE); + } + + // If an error occurs during writing, libpng will jump here + if (setjmp(png_jmpbuf(png_ptr))) + { + KIO::del(uri); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + return (KisImageBuilder_RESULT_FAILURE); + } + // Initialize the writing + png_init_io(png_ptr, fp); + // Setup the progress function +// FIXME png_set_write_status_fn(png_ptr, progress); +// setProgressTotalSteps(100/*height*/); + + + /* set the zlib compression level */ + png_set_compression_level(png_ptr, compression); + + /* set other zlib parameters */ + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + png_set_compression_buffer_size(png_ptr, 8192); + + int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels(); + int color_type = getColorTypeforColorSpace(layer->paintDevice()->colorSpace(), alpha); + + if(color_type == -1) + { + return KisImageBuilder_RESULT_UNSUPPORTED; + } + + // Try to compute a table of color if the colorspace is RGB8f + png_colorp palette ; + int num_palette = 0; + if(!alpha && layer->paintDevice()->colorSpace()->id() == KisID("RGBA") ) + { // png doesn't handle indexed images and alpha, and only have indexed for RGB8 + palette = new png_color[255]; + KisRectIteratorPixel it = layer->paintDevice()->createRectIterator(0,0, img->width(), img->height(), false); + bool toomuchcolor = false; + while( !it.isDone() ) + { + const Q_UINT8* c = it.rawData(); + bool findit = false; + for(int i = 0; i < num_palette; i++) + { + if(palette[i].red == c[2] && + palette[i].green == c[1] && + palette[i].blue == c[0] ) + { + findit = true; + break; + } + } + if(!findit) + { + if( num_palette == 255) + { + toomuchcolor = true; + break; + } + palette[num_palette].red = c[2]; + palette[num_palette].green = c[1]; + palette[num_palette].blue = c[0]; + num_palette++; + } + ++it; + } + if(!toomuchcolor) + { + kdDebug(41008) << "Found a palette of " << num_palette << " colors" << endl; + color_type = PNG_COLOR_TYPE_PALETTE; + if( num_palette <= 2) + { + color_nb_bits = 1; + } else if( num_palette <= 4) + { + color_nb_bits = 2; + } else if( num_palette <= 16) + { + color_nb_bits = 4; + } else { + color_nb_bits = 8; + } + } else { + delete palette; + } + } + + int interlacetype = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; + + png_set_IHDR(png_ptr, info_ptr, + width, + height, + color_nb_bits, + color_type, interlacetype, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); + // set the palette + if( color_type == PNG_COLOR_TYPE_PALETTE) + { + png_set_PLTE(png_ptr, info_ptr, palette, num_palette); + } + // Save annotation + vKisAnnotationSP_it it = annotationsStart; + while(it != annotationsEnd) { + if (!(*it) || (*it) -> type() == QString()) { + kdDebug(41008) << "Warning: empty annotation" << endl; + ++it; + continue; + } + + kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl; + + if ((*it) -> type().startsWith("krita_attribute:")) { // Attribute + // FIXME: it should be possible to save krita_attributes in the "CHUNKs" + kdDebug(41008) << "can't save this annotation : " << (*it) -> type() << endl; + } else { // Profile + char* name = new char[(*it)->type().length()+1]; + strcpy(name, (*it)->type().ascii()); + png_set_iCCP(png_ptr, info_ptr, name, PNG_COMPRESSION_TYPE_BASE, (char*)(*it)->annotation().data(), (*it) -> annotation() . size()); + } + ++it; + } + + // read comments from the document information + png_text texts[3]; + int nbtexts = 0; + KoDocumentInfo * info = m_doc->documentInfo(); + KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" )); + QString title = aboutPage->title(); + if(!title.isEmpty()) + { + fillText(texts+nbtexts, "title", title); + nbtexts++; + } + QString abstract = aboutPage->abstract(); + if(!abstract.isEmpty()) + { + fillText(texts+nbtexts, "abstract", abstract); + nbtexts++; + } + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" )); + QString author = authorPage->fullName(); + if(!author.isEmpty()) + { + fillText(texts+nbtexts, "author", author); + nbtexts++; + } + + png_set_text(png_ptr, info_ptr, texts, nbtexts); + + // Save the information to the file + png_write_info(png_ptr, info_ptr); + png_write_flush(png_ptr); + + // swap byteorder on little endian machines. + #ifndef WORDS_BIGENDIAN + if (color_nb_bits > 8 ) + png_set_swap(png_ptr); + #endif + + // Write the PNG +// png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + + // Fill the data structure + png_byte** row_pointers= new png_byte*[height]; + + for (int y = 0; y < height; y++) { + KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false); + row_pointers[y] = new png_byte[width*layer->paintDevice()->pixelSize()]; + switch(color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: + if(color_nb_bits == 16) + { + Q_UINT16 *dst = reinterpret_cast<Q_UINT16 *>(row_pointers[y]); + while (!it.isDone()) { + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + *(dst++) = d[0]; + if(alpha) *(dst++) = d[1]; + ++it; + } + } else { + Q_UINT8 *dst = row_pointers[y]; + while (!it.isDone()) { + const Q_UINT8 *d = it.rawData(); + *(dst++) = d[0]; + if(alpha) *(dst++) = d[1]; + ++it; + } + } + break; + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + if(color_nb_bits == 16) + { + Q_UINT16 *dst = reinterpret_cast<Q_UINT16 *>(row_pointers[y]); + while (!it.isDone()) { + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + *(dst++) = d[2]; + *(dst++) = d[1]; + *(dst++) = d[0]; + if(alpha) *(dst++) = d[3]; + ++it; + } + } else { + Q_UINT8 *dst = row_pointers[y]; + while (!it.isDone()) { + const Q_UINT8 *d = it.rawData(); + *(dst++) = d[2]; + *(dst++) = d[1]; + *(dst++) = d[0]; + if(alpha) *(dst++) = d[3]; + ++it; + } + } + break; + case PNG_COLOR_TYPE_PALETTE: + { + Q_UINT8 *dst = row_pointers[y]; + KisPNGStream writestream(dst, color_nb_bits); + while (!it.isDone()) { + const Q_UINT8 *d = it.rawData(); + int i; + for(i = 0; i < num_palette; i++) + { + if(palette[i].red == d[2] && + palette[i].green == d[1] && + palette[i].blue == d[0] ) + { + break; + } + } + writestream.setNextValue(i); + ++it; + } + } + break; + default: + kdDebug(41008) << "Unsupported color type for writting : " << color_type << endl; + KIO::del(uri); + return KisImageBuilder_RESULT_UNSUPPORTED; + } + } + + png_write_image(png_ptr, row_pointers); + + + // Writting is over + png_write_end(png_ptr, info_ptr); + + // Free memory + png_destroy_write_struct(&png_ptr, &info_ptr); + for (int y = 0; y < height; y++) { + delete[] row_pointers[y]; + } + delete[] row_pointers; + + if( color_type == PNG_COLOR_TYPE_PALETTE) + { + delete palette; + } + + fclose(fp); + + return KisImageBuilder_RESULT_OK; +} + + +void KisPNGConverter::cancel() +{ + m_stop = true; +} + +void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if(png_ptr == NULL || row_number > PNG_MAX_UINT || pass > 7) return; +// setProgress(row_number); +} + + +#include "kis_png_converter.moc" + diff --git a/filters/krita/png/kis_png_converter.h b/filters/krita/png/kis_png_converter.h new file mode 100644 index 00000000..6fd8b207 --- /dev/null +++ b/filters/krita/png/kis_png_converter.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_PNG_CONVERTER_H_ +#define _KIS_PNG_CONVERTER_H_ + +#include <png.h> + +#include <qvaluevector.h> + +#include <kio/job.h> + +#include <kis_progress_subject.h> + +#include "kis_types.h" +#include "kis_global.h" +#include "kis_annotation.h" +class KisDoc; +class KisUndoAdapter; + +/** + * Image import/export plugins can use these results to report about success or failure. + */ +enum KisImageBuilder_Result { + KisImageBuilder_RESULT_FAILURE = -400, + KisImageBuilder_RESULT_NOT_EXIST = -300, + KisImageBuilder_RESULT_NOT_LOCAL = -200, + KisImageBuilder_RESULT_BAD_FETCH = -100, + KisImageBuilder_RESULT_INVALID_ARG = -50, + KisImageBuilder_RESULT_OK = 0, + KisImageBuilder_RESULT_PROGRESS = 1, + KisImageBuilder_RESULT_EMPTY = 100, + KisImageBuilder_RESULT_BUSY = 150, + KisImageBuilder_RESULT_NO_URI = 200, + KisImageBuilder_RESULT_UNSUPPORTED = 300, + KisImageBuilder_RESULT_INTR = 400, + KisImageBuilder_RESULT_PATH = 500, + KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 +}; + +class KisPNGConverter : public KisProgressSubject { + Q_OBJECT + public: + KisPNGConverter(KisDoc *doc, KisUndoAdapter *adapter); + virtual ~KisPNGConverter(); + public: + KisImageBuilder_Result buildImage(const KURL& uri); + KisImageBuilder_Result buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, int compression, bool interlace, bool alpha); + /** Retrieve the constructed image + */ + KisImageSP image(); + public slots: + virtual void cancel(); + private: + KisImageBuilder_Result decode(const KURL& uri); + void progress(png_structp png_ptr, png_uint_32 row_number, int pass); + private: + png_uint_32 m_max_row; + KisImageSP m_img; + KisDoc *m_doc; + KisUndoAdapter *m_adapter; + bool m_stop; +}; + +#endif diff --git a/filters/krita/png/kis_png_export.cc b/filters/krita/png/kis_png_export.cc new file mode 100644 index 00000000..b1df098c --- /dev/null +++ b/filters/krita/png/kis_png_export.cc @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_png_export.h" + +#include <qcheckbox.h> +#include <qslider.h> + +#include <kapplication.h> +#include <kdialogbase.h> +#include <kgenericfactory.h> + +#include <KoFilterChain.h> + +#include <kis_colorspace.h> +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_iterators_pixel.h> +#include <kis_paint_layer.h> +#include <kis_progress_display_interface.h> + +#include "kis_png_converter.h" +#include "kis_wdg_options_png.h" + +typedef KGenericFactory<KisPNGExport, KoFilter> KisPNGExportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritapngexport, KisPNGExportFactory("kofficefilters")) + +KisPNGExport::KisPNGExport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisPNGExport::~KisPNGExport() +{ +} + +KoFilter::ConversionStatus KisPNGExport::convert(const QCString& from, const QCString& to) +{ + kdDebug(41008) << "Png export! From: " << from << ", To: " << to << "\n"; + + KisDoc *output = dynamic_cast<KisDoc*>(m_chain->inputDocument()); + QString filename = m_chain->outputFile(); + + if (!output) + return KoFilter::CreationError; + + + if (filename.isEmpty()) return KoFilter::FileNotFound; + + if (from != "application/x-krita") + return KoFilter::NotImplemented; + + + KDialogBase* kdb = new KDialogBase(0, "", false, i18n("PNG Export Options"), KDialogBase::Ok | KDialogBase::Cancel); + + KisImageSP img = output->currentImage(); + KisPaintDeviceSP pd = new KisPaintDevice(*img->projection()); + KisPaintLayerSP l = new KisPaintLayer(img, "projection", OPACITY_OPAQUE, pd); + + KisRectIteratorPixel it = l->paintDevice()->createRectIterator(0,0, img->width(), img->height(), false); + KisColorSpace* cs = l->paintDevice()->colorSpace(); + bool isThereAlpha = false; + while( !it.isDone() ) + { + if(cs->getAlpha( it.rawData() ) != 255) + { + isThereAlpha = true; + break; + } + ++it; + } + + KisWdgOptionsPNG* wdg = new KisWdgOptionsPNG(kdb); + wdg->alpha->setChecked(isThereAlpha); + wdg->alpha->setEnabled(isThereAlpha); + kdb->setMainWidget(wdg); + kapp->restoreOverrideCursor(); + if(kdb->exec() == QDialog::Rejected) + { + return KoFilter::OK; // FIXME Cancel doesn't exist :( + } + + bool alpha = wdg->alpha->isChecked(); + bool interlace = wdg->interlacing->isChecked(); + int compression = wdg->compressionLevel->value(); + + delete kdb; + + + KURL url; + url.setPath(filename); + + KisPNGConverter kpc(output, output->undoAdapter()); + + vKisAnnotationSP_it beginIt = img->beginAnnotations(); + vKisAnnotationSP_it endIt = img->endAnnotations(); + KisImageBuilder_Result res; + + + if ( (res = kpc.buildFile(url, l, beginIt, endIt, compression, interlace, alpha)) == KisImageBuilder_RESULT_OK) { + kdDebug(41008) << "success !" << endl; + return KoFilter::OK; + } + kdDebug(41008) << " Result = " << res << endl; + return KoFilter::InternalError; +} + +#include <kis_png_export.moc> + diff --git a/filters/krita/png/kis_png_export.h b/filters/krita/png/kis_png_export.h new file mode 100644 index 00000000..1c99d7c9 --- /dev/null +++ b/filters/krita/png/kis_png_export.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_PNG_EXPORT_H_ +#define _KIS_PNG_EXPORT_H_ + +#include <KoFilter.h> + +class KisPNGExport : public KoFilter { + Q_OBJECT + public: + KisPNGExport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisPNGExport(); + public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif diff --git a/filters/krita/png/kis_png_import.cc b/filters/krita/png/kis_png_import.cc new file mode 100644 index 00000000..5b824081 --- /dev/null +++ b/filters/krita/png/kis_png_import.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_png_import.h" + +#include <kgenericfactory.h> + +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_progress_display_interface.h> +#include <kis_view.h> + +#include "kis_png_converter.h" + +typedef KGenericFactory<KisPNGImport, KoFilter> PNGImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritapngimport, PNGImportFactory("kofficefilters")) + +KisPNGImport::KisPNGImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisPNGImport::~KisPNGImport() +{ +} + +KoFilter::ConversionStatus KisPNGImport::convert(const QCString&, const QCString& to) +{ + kdDebug(41008) << "Importing using PNGImport!\n"; + + if (to != "application/x-krita") + return KoFilter::BadMimeType; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + KisView * view = static_cast<KisView*>(doc -> views().getFirst()); + + QString filename = m_chain -> inputFile(); + + if (!doc) + return KoFilter::CreationError; + + doc -> prepareForImport(); + + + if (!filename.isEmpty()) { + + KURL url; + url.setPath(filename); + + if (url.isEmpty()) + return KoFilter::FileNotFound; + + KisPNGConverter ib(doc, doc -> undoAdapter()); + + if (view != 0) + view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true); + + switch (ib.buildImage(url)) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KoFilter::NotImplemented; + break; + case KisImageBuilder_RESULT_INVALID_ARG: + return KoFilter::BadMimeType; + break; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_LOCAL: + return KoFilter::FileNotFound; + break; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KoFilter::ParsingError; + break; + case KisImageBuilder_RESULT_FAILURE: + return KoFilter::InternalError; + break; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KoFilter::OK; + default: + break; + } + + } + return KoFilter::StorageCreationError; +} + +#include <kis_png_import.moc> + diff --git a/filters/krita/png/kis_png_import.h b/filters/krita/png/kis_png_import.h new file mode 100644 index 00000000..8c952c96 --- /dev/null +++ b/filters/krita/png/kis_png_import.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef _KIS_PNG_IMPORT_H_ +#define _KIS_PNG_IMPORT_H_ + +#include <KoFilter.h> + +class KisPNGImport : public KoFilter { + Q_OBJECT + public: + KisPNGImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisPNGImport(); + public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif diff --git a/filters/krita/png/kis_wdg_options_png.ui b/filters/krita/png/kis_wdg_options_png.ui new file mode 100644 index 00000000..b8d11c3a --- /dev/null +++ b/filters/krita/png/kis_wdg_options_png.ui @@ -0,0 +1,183 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KisWdgOptionsPNG</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KisWdgOptionsPNG</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>286</width> + <height>106</height> + </rect> + </property> + <property name="caption"> + <string>Options of Your PNG</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Compress:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + <property name="toolTip" stdset="0"> + <string>Note: the compression level does not change the quality of the result</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level does not change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider"> + <property name="name"> + <cstring>compressionLevel</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>9</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="value"> + <number>9</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Below</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Note: the compression level doesn't change the quality of the result</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Fast</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Small</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>interlacing</cstring> + </property> + <property name="text"> + <string>Interlacing</string> + </property> + <property name="toolTip" stdset="0"> + <string>Use interlacing when publishing on the Internet</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Interlacing is useful if you intend to publish your image on the Internet.<br> +Enabling interlacing will cause the image to be displayed by the browser even while downloading.</p></string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>alpha</cstring> + </property> + <property name="text"> + <string>Store alpha channel (transparency)</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Disable to get smaller files if your image has no transparency</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>The Portable Network Graphics (PNG) file format allows transparency in your image to be stored by saving an alpha channel. +You can uncheck the box if you are not using transparency and you want to make the resulting file smaller .<br>Always saving the alpha channel is recommended.</p></string> + </property> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/filters/krita/png/krita_png.desktop b/filters/krita/png/krita_png.desktop new file mode 100644 index 00000000..367dfcc0 --- /dev/null +++ b/filters/krita/png/krita_png.desktop @@ -0,0 +1,57 @@ +[Desktop Entry] +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Exec=krita %u +GenericName=Painting and Image Editing Application +GenericName[bg]=Редактор на графични изображения +GenericName[ca]=Programa de dibuix i manipulació d'imatges +GenericName[cs]=Malování a úpravy obrázků +GenericName[cy]=Cymhwysiad Peintio Golygu Delweddau +GenericName[da]=Male- og billedredigeringsprogram +GenericName[de]=Mal- und Bildbearbeitungsprogramm +GenericName[el]=Εφαρμογή επεξεργασίας εικόνων +GenericName[eo]=Aplikaĵo por Pentrado kaj Bildredaktado +GenericName[es]=Aplicación de pintura y de edición de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudien marrazketa eta ediziorako aplikazioa +GenericName[fa]=کاربرد ویرایش تصویر و نقاشی +GenericName[fi]=Maalaus- ja kuvankäsitelyohjelma +GenericName[fr]=Application de dessin et de manipulation d'images +GenericName[fy]=Ofbyldingsmanipulaasje +GenericName[gl]=Aplicación de Pintura e Manipulación de Imaxes +GenericName[he]=יישום לציור ועריכת תמונות +GenericName[hr]=Aplikacija za obradu slika i fotografija +GenericName[hu]=Képszerkesztő +GenericName[is]=Málun og myndritill +GenericName[it]=Applicazione di disegno e di modifica delle immagini +GenericName[ja]=描画と画像編集のためのアプリケーション +GenericName[km]=កម្មវិធីគូរគំនូរ និងកែសម្រួលរូបភាព +GenericName[lv]=Zīmēšanas un attēlu apstrādes programma +GenericName[nb]=Program for tegning og bilderedigering +GenericName[nds]=Programm för't Malen un Bildbewerken +GenericName[ne]=पेन्टीङ्ग र छवि सम्पादन अनुप्रयोग +GenericName[nl]=Afbeeldingsmanipulatie +GenericName[pl]=Program do edycji zdjęć oraz rysunków +GenericName[pt]=Aplicação de Pintura e Edição de Imagens +GenericName[pt_BR]=Aplicação de Pintura e Edição de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Málen- ja govvagieđahallanprográmma +GenericName[sk]=Program pre tvorbu a úpravu obrázkov +GenericName[sl]=Program za risanje in obdelavo slik +GenericName[sr]=Програм за цртање и уређивање слика +GenericName[sr@Latn]=Program za crtanje i uređivanje slika +GenericName[sv]=Målnings- och bildredigeringsprogram +GenericName[uk]=Програма для малювання і редагування зображень +GenericName[uz]=Rasmlar bilan ishlaydigan dastur +GenericName[uz@cyrillic]=Расмлар билан ишлайдиган дастур +GenericName[zh_CN]=绘图和图像编辑应用程序 +GenericName[zh_TW]=繪圖與影像處理程式 +MimeType=image/png +Type=Application +Icon=krita +Categories= +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi diff --git a/filters/krita/png/krita_png_export.desktop b/filters/krita/png/krita_png_export.desktop new file mode 100644 index 00000000..7109175c --- /dev/null +++ b/filters/krita/png/krita_png_export.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Name=Krita PNG Export Filter +Name[bg]=Филтър за експортиране от Krita в PNG +Name[br]=Sil ezporzh PNG evit Krita +Name[ca]=Filtre d'exportació PNG per a Krita +Name[da]=Krita PNG-eksportfilter +Name[de]=Krita PNG-Exportfilter +Name[el]=Φίλτρο εξαγωγής PNG του Krita +Name[eo]=Krita PNG-eksportfiltrilo +Name[es]=Filtro de exportación a PNG de Krita +Name[et]=Krita PNG ekspordifilter +Name[fa]=پالایۀ صادرات Krita PNG +Name[fi]=Krita PNG -viestisuodin +Name[fr]=Filtre d'exportation PNG de Krita +Name[fy]=Krita PNG Eksportfilter +Name[ga]=Scagaire Easpórtála PNG Krita +Name[gl]=Filtro de Exportación de PNG para Krita +Name[he]=Krita PNG מסנן יצוא +Name[hr]=Krita PNG filtar izvoza +Name[hu]=Krita PNG exportszűrő +Name[is]=Krita PNG útflutningssía +Name[it]=Filtro di esportazione PNG per Krita +Name[ja]=Krita PNG エクスポートフィルタ +Name[km]=តម្រងនាំចេញ PNG សម្រាប់ Krita +Name[lt]=Krita PNG eksportavimo filtras +Name[lv]=Krita PNG eksporta filtrs +Name[nb]=PNG-eksportfilter for Krita +Name[nds]=PNG-Exportfilter för Krita +Name[ne]=क्रिता पीएनजी निर्यात फिल्टर +Name[nl]=Krita PNG Exportfilter +Name[pl]=Filtr eksportu do formatu PNG dla Krita +Name[pt]=Filtro de Exportação de PNG para o Krita +Name[pt_BR]=Filtro de Exportação de PNG para o Krita +Name[ru]=Фильтр экспорта рисунков Krita в PNG +Name[se]=Krita PNG-olggosfievrridansilli +Name[sk]=Exportný filter Krita PNG +Name[sl]=Izvozni filter PNG za Krito +Name[sr]=Krita-ин филтер за извоз у PNG +Name[sr@Latn]=Krita-in filter za izvoz u PNG +Name[sv]=Krita PNG-exportfilter +Name[uk]=Фільтр експорту PNG для Krita +Name[uz]=Krita PNG eksport filteri +Name[uz@cyrillic]=Krita PNG экспорт филтери +Name[zh_CN]=Krita PNG 导出过滤器 +Name[zh_TW]=Krita PNG 匯出過濾程式 +X-KDE-Export=image/png +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Import=application/x-krita +X-KDE-Weight=1 +X-KDE-Library=libkritapngexport diff --git a/filters/krita/png/krita_png_import.desktop b/filters/krita/png/krita_png_import.desktop new file mode 100644 index 00000000..99944a77 --- /dev/null +++ b/filters/krita/png/krita_png_import.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Type=Service +Name=Krita PNG Import Filter +Name[bg]=Филтър за импортиране от PNG в Krita +Name[br]=Sil enporzh PNG evit Krita +Name[ca]=Filtre d'importació PNG per a Krita +Name[da]=Krita PNG-importfilter +Name[de]=Krita PNG-Importfilter +Name[el]=Φίλτρο εισαγωγής PNG του Krita +Name[eo]=Krita PNG-importfiltrilo +Name[es]=Filtro de importación a PNG de Krita +Name[et]=Krita PNG impordifilter +Name[fa]=پالایۀ واردات Krita PNG +Name[fi]=Krita PNG -tuontisuodin +Name[fr]=Filtre d'importation PNG de Krita +Name[fy]=Krita PNG Ymportfilter +Name[ga]=Scagaire Iompórtála PNG Krita +Name[gl]=Filtro de Importación de PNG para Krita +Name[he]=Krita PNG מסנן יבוא +Name[hr]=Krita PNG filtar uvoza +Name[hu]=Krita PNG importszűrő +Name[is]=Krita PNG innflutningssía +Name[it]=Filtro di importazione PNG per Krita +Name[ja]=Krita PNG インポートフィルタ +Name[km]=តម្រងនាំចូល PNG សម្រាប់ Krita +Name[lt]=Krita PNG importavimo filtras +Name[lv]=Krita PNG importa filtrs +Name[nb]=PNG-importfilter for Krita +Name[nds]=PNG-Importfilter för Krita +Name[ne]=क्रिता पीएनजी आयात फिल्टर +Name[nl]=Krita PNG Importfilter +Name[pl]=Filtr importu z formatu PNG dla Krita +Name[pt]=Filtro de Importação de PNG para o Krita +Name[pt_BR]=Filtro de Importação de PNG para o Krita +Name[ru]=Фильтр импорта рисунков PNG в Krita +Name[se]=Krita PNG-olggosfievrridansilli +Name[sk]=PNG filter pre import do Krita +Name[sl]=Uvozni filter PNG za Krito +Name[sr]=Krita-ин филтер за увоз из PNG-а +Name[sr@Latn]=Krita-in filter za uvoz iz PNG-a +Name[sv]=Krita PNG-importfilter +Name[uk]=Фільтр імпорту PNG для Krita +Name[uz]=Krita PNG import filteri +Name[uz@cyrillic]=Krita PNG импорт филтери +Name[zh_CN]=Krita PNG 导入过滤器 +Name[zh_TW]=Krita PNG 匯入過濾程式 +X-KDE-Export=application/x-krita +X-KDE-Import=image/png +X-KDE-Weight=1 +X-KDE-Library=libkritapngimport +ServiceTypes=KOfficeFilter diff --git a/filters/krita/raw/Makefile.am b/filters/krita/raw/Makefile.am new file mode 100644 index 00000000..cd29902b --- /dev/null +++ b/filters/krita/raw/Makefile.am @@ -0,0 +1,33 @@ +AM_CPPFLAGS= \ + -I$(srcdir) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + -I$(top_srcdir)/filters/krita/magick \ + $(KOFFICE_INCLUDES) \ + $(all_includes) + +kde_module_LTLIBRARIES = libkrita_raw_import.la + +libkrita_raw_import_la_LDFLAGS = $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkrita_raw_import_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(raw_LIBS) \ + $(top_builddir)/krita/libkritacommon.la + + +service_DATA = krita_raw_import.desktop +servicedir = $(kde_servicesdir) + +kdelnk_DATA = krita_raw.desktop +kdelnkdir = $(kde_appsdir)/.hidden + +libkrita_raw_import_la_SOURCES = kis_raw_import.cpp wdgrawimport.ui + +METASOURCES = AUTO + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + + diff --git a/filters/krita/raw/dcraw.1 b/filters/krita/raw/dcraw.1 new file mode 100644 index 00000000..10a690b8 --- /dev/null +++ b/filters/krita/raw/dcraw.1 @@ -0,0 +1,182 @@ +.\" +.\" Man page for dcraw (Raw Photo Decoder) +.\" +.\" Copyright (c) 2005 by David Coffin +.\" +.\" You may distribute without restriction. +.\" +.\" David Coffin +.\" dcoffin a cybercom o net +.\" http://www.cybercom.net/~dcoffin +.\" +.TH dcraw 1 "September 29, 2005" +.LO 1 +.SH NAME +dcraw - convert raw digital photos to PPM format +.SH SYNOPSIS +.B dcraw +[\fIOPTION\fR]... [\fIFILE\fR]... +.SH DESCRIPTION +.B dcraw +converts raw digital photos to +.BR ppm (5) +format. +.SH OPTIONS +.TP +.B -v +Print verbose messages. The default is to print only warnings +and errors. +.TP +.B -z +Change the access and modification times of a JPEG or raw file to +when the photo was taken, assuming that the camera clock was set +to Universal Time. +.TP +.B -i +Identify files but don't decode them. +Exit status is 0 if +.B dcraw +can decode the last file, 1 if it can't. +.TP +.B "" +.B dcraw +cannot decode JPEG files!! +.TP +.B -c +Write binary image data to standard output. +By default, +.B dcraw +creates files with a ".ppm" extension. +.TP +.B -d +Show the raw data as a grayscale image with no interpolation. +Good for photographing black-and-white documents. +.TP +.B -q [0-3] +Set the interpolation quality (default is 3): + +.B \t0 +\ \ Bilinear (very fast, low quality) +.br +.B \t1 +\ \ Reserved +.br +.B \t2 +\ \ Variable Number of Gradients (VNG) +.br +.B \t3 +\ \ Adaptive Homogeneity-Directed (AHD) +.TP +.B -h +Output a half-size image. Twice as fast as +.BR -q\ 0 . +.TP +.B -f +Interpolate RGB as four colors. This blurs the image a little, +but it eliminates false 2x2 mesh patterns. +.TP +.B -B sigma_domain sigma_range +Use a bilateral filter to smooth noise while preserving edges. +.B sigma_domain +is in units of pixels, while +.B sigma_range +is in units of CIELab colorspace. +Try +.B -B\ 2\ 4 +to start. +.TP +.B -a +Automatic color balance. The default is to use a fixed +color balance based on a white card photographed in sunlight. +.TP +.B -w +Use the color balance specified by the camera. +If this can't be found, +.B dcraw +prints a warning and reverts to the default. +.TP +.B -r red_mul -l blue_mul +Further adjust the color balance by multiplying the red and +blue output channels by these values. Both default to 1.0. +.TP +.B -b brightness +Change the output brightness. Default is 1.0. +.TP +.B -k black +Set the black point. Default depends on the camera. +.TP +.B -n +By default, +.B dcraw +clips all colors to prevent pink hues in the highlights. +Combine this option with +.B -b 0.25 +to leave the image data completely unclipped. +.TP +.B -m +Write raw camera colors to the output file. By default, +.B dcraw +converts to sRGB colorspace. +.TP +.B -j +For Fuji\ Super\ CCD cameras, show the image tilted 45 degrees +so that each output pixel corresponds to one raw pixel. +.TP +.B -s +For Fuji\ Super\ CCD\ SR cameras, use the secondary sensors, in +effect underexposing the image by four stops to reveal detail +in the highlights. +.TP +.B "" +For all other cameras, +.B -j +and +.B -s +are silently ignored. +.TP +.B -t [0-7] +Flip the output image. The most common flips are 5 +(90 degrees CCW) and 6 (90 degrees clockwise). By default, +dcraw tries to use the flip specified by the camera. +.RB \^" -t\ 0 \^" +forces +.B dcraw +not to flip images. +.TP +.B -2 +Write eight bits per color value with a 99th-percentile white +point and the standard 0.45 gamma curve. Double the height if +necessary to correct the aspect ratio. This is the default. +.TP +.B -4 +Write sixteen bits per color value. Output is linear with +input -- no white point, no gamma, same aspect ratio. +.TP +.B -3 +Same image as +.BR -4 , +written in Adobe PhotoShop format. File extension is ".psd". +.SH "SEE ALSO" +.BR ppm (5), +.BR ppm2tiff (1), +.BR pnmtotiff (1), +.BR pnmtopng (1), +.BR gphoto2 (1), +.BR djpeg (1) +.SH BUGS +The +.B -w +option does not work with many cameras. +.P +No attempt is made to save camera settings or thumbnail images. +.P +The author stubbornly refuses to add more output formats. +.P +Don't expect +.B dcraw +to produce the same images as software provided by the camera +vendor. Sometimes +.B dcraw +gives better results! +.SH AUTHOR +Written by David Coffin, dcoffin a cybercom o net diff --git a/filters/krita/raw/dcraw.c b/filters/krita/raw/dcraw.c new file mode 100644 index 00000000..b562dd74 --- /dev/null +++ b/filters/krita/raw/dcraw.c @@ -0,0 +1,5999 @@ +/* + dcraw.c -- Dave Coffin's raw photo decoder + Copyright 1997-2005 by Dave Coffin, dcoffin a cybercom o net + + This is a command-line ANSI C program to convert raw photos from + any digital camera on any computer running any operating system. + + Attention! Some parts of this program are restricted under the + terms of the GNU General Public License. Such code is enclosed + in "BEGIN GPL BLOCK" and "END GPL BLOCK" declarations. + Any code not declared GPL is free for all uses. + + Starting in Revision 1.237, the code to support Foveon cameras + is under GPL. + + To lawfully redistribute dcraw.c, you must either (a) include + full source code for all executable files containing restricted + functions, (b) remove these functions, re-implement them, or + copy them from an earlier, non-GPL Revision of dcraw.c, or (c) + purchase a license from the author. + + $Revision: 1.296 $ + $Date: 2005/11/04 07:11:14 $ + */ + +#define _GNU_SOURCE +#define _USE_MATH_DEFINES +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <float.h> +#include <limits.h> +#include <math.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +/* + By defining NO_JPEG, you lose only the ability to + decode compressed .KDC files from the Kodak DC120. + */ +#ifndef NO_JPEG +#include <jpeglib.h> +#endif + +#ifdef __CYGWIN__ +#include <io.h> +#endif +#ifdef WIN32 +#include <sys/utime.h> +#include <winsock2.h> +#pragma comment(lib, "ws2_32.lib") +#define strcasecmp stricmp +typedef __int64 INT64; +typedef unsigned __int64 UINT64; +#else +#include <unistd.h> +#include <utime.h> +#include <netinet/in.h> +typedef long long INT64; +typedef unsigned long long UINT64; +#endif + +#ifdef LJPEG_DECODE +#error Please compile dcraw.c by itself. +#error Do not link it with ljpeg_decode. +#endif + +#ifndef LONG_BIT +#define LONG_BIT (8 * sizeof (long)) +#endif + +#define ushort UshORt +typedef unsigned char uchar; +typedef unsigned short ushort; + +/* + All global variables are defined here, and all functions that + access them are prefixed with "CLASS". Note that a thread-safe + C++ class cannot have non-const static local variables. + */ +FILE *ifp; +short order; +char *ifname, make[64], model[70], model2[64], *meta_data; +float flash_used, canon_5814; +time_t timestamp; +unsigned shot_order, kodak_cbpp; +int data_offset, meta_offset, meta_length, nikon_curve_offset; +int tiff_bps, tiff_data_compression, kodak_data_compression; +int raw_height, raw_width, top_margin, left_margin; +int height, width, fuji_width, colors, tiff_samples; +int black, maximum, clip_max, clip_color=1; +int iheight, iwidth, shrink; +int dng_version, is_foveon, raw_color, use_gamma; +int flip, xmag, ymag; +int zero_after_ff; +unsigned filters; +ushort (*image)[4], white[8][8], curve[0x1000]; +void (*load_raw)(); +float bright=1, red_scale=1, blue_scale=1, sigma_d=0, sigma_r=0; +int four_color_rgb=0, document_mode=0; +int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_rgb=0; +int fuji_layout, fuji_secondary, use_secondary=0; +float cam_mul[4], pre_mul[4], rgb_cam[3][4]; /* RGB from camera color */ +const double xyz_rgb[3][3] = { /* XYZ from RGB */ + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 0.019334, 0.119193, 0.950227 } }; +#define camera_red cam_mul[0] +#define camera_blue cam_mul[2] +int histogram[3][0x2000]; +void write_ppm(FILE *); +void (*write_fun)(FILE *) = write_ppm; +jmp_buf failure; + +#define USE_LCMS +#ifdef USE_LCMS +#include <lcms.h> +int profile_offset, profile_length; +#endif + +struct decode { + struct decode *branch[2]; + int leaf; +} first_decode[2048], *second_decode, *free_decode; + +#define CLASS + +#define FORC3 for (c=0; c < 3; c++) +#define FORC4 for (c=0; c < 4; c++) +#define FORCC for (c=0; c < colors; c++) + +#define SQR(x) ((x)*(x)) +#define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define LIM(x,min,max) MAX(min,MIN(x,max)) +#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y)) +#define CLIP(x) LIM(x,0,clip_max) + +/* + In order to inline this calculation, I make the risky + assumption that all filter patterns can be described + by a repeating pattern of eight rows and two columns + + Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2 + */ +#define FC(row,col) \ + (filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3) + +#define BAYER(row,col) \ + image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)] + +/* + PowerShot 600 PowerShot A50 PowerShot Pro70 Pro90 & G1 + 0xe1e4e1e4: 0x1b4e4b1e: 0x1e4b4e1b: 0xb4b4b4b4: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 G M G M G M 0 C Y C Y C Y 0 Y C Y C Y C 0 G M G M G M + 1 C Y C Y C Y 1 M G M G M G 1 M G M G M G 1 Y C Y C Y C + 2 M G M G M G 2 Y C Y C Y C 2 C Y C Y C Y + 3 C Y C Y C Y 3 G M G M G M 3 G M G M G M + 4 C Y C Y C Y 4 Y C Y C Y C + PowerShot A5 5 G M G M G M 5 G M G M G M + 0x1e4e1e4e: 6 Y C Y C Y C 6 C Y C Y C Y + 7 M G M G M G 7 M G M G M G + 0 1 2 3 4 5 + 0 C Y C Y C Y + 1 G M G M G M + 2 C Y C Y C Y + 3 M G M G M G + + All RGB cameras use one of these Bayer grids: + + 0x16161616: 0x61616161: 0x49494949: 0x94949494: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 B G B G B G 0 G R G R G R 0 G B G B G B 0 R G R G R G + 1 G R G R G R 1 B G B G B G 1 R G R G R G 1 G B G B G B + 2 B G B G B G 2 G R G R G R 2 G B G B G B 2 R G R G R G + 3 G R G R G R 3 B G B G B G 3 R G R G R G 3 G B G B G B + */ + +#ifndef __GLIBC__ +char *memmem (char *haystack, size_t haystacklen, + char *needle, size_t needlelen) +{ + char *c; + for (c = haystack; c <= haystack + haystacklen - needlelen; c++) + if (!memcmp (c, needle, needlelen)) + return c; + return NULL; +} +#endif + +void CLASS merror (void *ptr, char *where) +{ + if (ptr) return; + fprintf (stderr, "%s: Out of memory in %s\n", ifname, where); + longjmp (failure, 1); +} + +ushort CLASS sget2 (uchar *s) +{ + if (order == 0x4949) /* "II" means little-endian */ + return s[0] | s[1] << 8; + else /* "MM" means big-endian */ + return s[0] << 8 | s[1]; +} + +ushort CLASS get2() +{ + uchar str[2] = { 0xff,0xff }; + fread (str, 1, 2, ifp); + return sget2(str); +} + +int CLASS sget4 (uchar *s) +{ + if (order == 0x4949) + return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; + else + return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]; +} +#define sget4(s) sget4((uchar *)s) + +int CLASS get4() +{ + uchar str[4] = { 0xff,0xff,0xff,0xff }; + fread (str, 1, 4, ifp); + return sget4(str); +} + +double CLASS getrat() +{ + double num = get4(); + return num / get4(); +} + +float CLASS int_to_float (int i) +{ + union { int i; float f; } u; + u.i = i; + return u.f; +} + +void CLASS read_shorts (ushort *pixel, int count) +{ + fread (pixel, 2, count, ifp); + if ((order == 0x4949) == (ntohs(0x1234) == 0x1234)) + swab (pixel, pixel, count*2); +} + +void CLASS canon_600_fixed_wb (int temp) +{ + static const short mul[4][5] = { + { 667, 358,397,565,452 }, + { 731, 390,367,499,517 }, + { 1119, 396,348,448,537 }, + { 1399, 485,431,508,688 } }; + int lo, hi, i; + float frac=0; + + for (lo=4; --lo; ) + if (*mul[lo] <= temp) break; + for (hi=0; hi < 3; hi++) + if (*mul[hi] >= temp) break; + if (lo != hi) + frac = (float) (temp - *mul[lo]) / (*mul[hi] - *mul[lo]); + for (i=1; i < 5; i++) + pre_mul[i-1] = 1 / (frac * mul[hi][i] + (1-frac) * mul[lo][i]); +} + +/* Return values: 0 = white 1 = near white 2 = not white */ +int CLASS canon_600_color (int ratio[2], int mar) +{ + int clipped=0, target, miss; + + if (flash_used) { + if (ratio[1] < -104) + { ratio[1] = -104; clipped = 1; } + if (ratio[1] > 12) + { ratio[1] = 12; clipped = 1; } + } else { + if (ratio[1] < -264 || ratio[1] > 461) return 2; + if (ratio[1] < -50) + { ratio[1] = -50; clipped = 1; } + if (ratio[1] > 307) + { ratio[1] = 307; clipped = 1; } + } + target = flash_used || ratio[1] < 197 + ? -38 - (398 * ratio[1] >> 10) + : -123 + (48 * ratio[1] >> 10); + if (target - mar <= ratio[0] && + target + 20 >= ratio[0] && !clipped) return 0; + miss = target - ratio[0]; + if (abs(miss) >= mar*4) return 2; + if (miss < -20) miss = -20; + if (miss > mar) miss = mar; + ratio[0] = target - miss; + return 1; +} + +void CLASS canon_600_auto_wb () +{ + int mar, row, col, i, j, st, count[] = { 0,0 }; + int test[8], total[2][8], ratio[2][2], stat[2]; + + memset (&total, 0, sizeof total); + i = canon_5814 + 0.5; + if (i < 10) mar = 150; + else if (i > 12) mar = 20; + else mar = 280 - 20 * i; + if (flash_used) mar = 80; + for (row=14; row < height-14; row+=4) + for (col=10; col < width; col+=2) { + for (i=0; i < 8; i++) + test[(i & 4) + FC(row+(i >> 1),col+(i & 1))] = + BAYER(row+(i >> 1),col+(i & 1)); + for (i=0; i < 8; i++) + if (test[i] < 150 || test[i] > 1500) goto next; + for (i=0; i < 4; i++) + if (abs(test[i] - test[i+4]) > 50) goto next; + for (i=0; i < 2; i++) { + for (j=0; j < 4; j+=2) + ratio[i][j >> 1] = ((test[i*4+j+1]-test[i*4+j]) << 10) / test[i*4+j]; + stat[i] = canon_600_color (ratio[i], mar); + } + if ((st = stat[0] | stat[1]) > 1) goto next; + for (i=0; i < 2; i++) + if (stat[i]) + for (j=0; j < 2; j++) + test[i*4+j*2+1] = test[i*4+j*2] * (0x400 + ratio[i][j]) >> 10; + for (i=0; i < 8; i++) + total[st][i] += test[i]; + count[st]++; +next: continue; + } + if (count[0] | count[1]) { + st = count[0]*200 < count[1]; + for (i=0; i < 4; i++) + pre_mul[i] = 1.0 / (total[st][i] + total[st][i+4]); + } +} + +void CLASS canon_600_coeff () +{ + static const short table[6][12] = { + { -190,702,-1878,2390, 1861,-1349,905,-393, -432,944,2617,-2105 }, + { -1203,1715,-1136,1648, 1388,-876,267,245, -1641,2153,3921,-3409 }, + { -615,1127,-1563,2075, 1437,-925,509,3, -756,1268,2519,-2007 }, + { -190,702,-1886,2398, 2153,-1641,763,-251, -452,964,3040,-2528 }, + { -190,702,-1878,2390, 1861,-1349,905,-393, -432,944,2617,-2105 }, + { -807,1319,-1785,2297, 1388,-876,769,-257, -230,742,2067,-1555 } }; + int t=0, i, c; + float mc, yc; + + mc = pre_mul[1] / pre_mul[2]; + yc = pre_mul[3] / pre_mul[2]; + if (mc > 1 && mc <= 1.28 && yc < 0.8789) t=1; + if (mc > 1.28 && mc <= 2) { + if (yc < 0.8789) t=3; + else if (yc <= 2) t=4; + } + if (flash_used) t=5; + for (raw_color = i=0; i < 3; i++) + FORCC rgb_cam[i][c] = table[t][i*4 + c] / 1024.0; +} + +void CLASS canon_600_load_raw() +{ + uchar data[1120], *dp; + ushort pixel[896], *pix; + int irow, row, col, val; + static const short mul[4][2] = + { { 1141,1145 }, { 1128,1109 }, { 1178,1149 }, { 1128,1109 } }; + + for (irow=row=0; irow < height; irow++) + { + fread (data, 1120, 1, ifp); + for (dp=data, pix=pixel; dp < data+1120; dp+=10, pix+=8) + { + pix[0] = (dp[0] << 2) + (dp[1] >> 6 ); + pix[1] = (dp[2] << 2) + (dp[1] >> 4 & 3); + pix[2] = (dp[3] << 2) + (dp[1] >> 2 & 3); + pix[3] = (dp[4] << 2) + (dp[1] & 3); + pix[4] = (dp[5] << 2) + (dp[9] & 3); + pix[5] = (dp[6] << 2) + (dp[9] >> 2 & 3); + pix[6] = (dp[7] << 2) + (dp[9] >> 4 & 3); + pix[7] = (dp[8] << 2) + (dp[9] >> 6 ); + } + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + for (col=width; col < 896; col++) + black += pixel[col]; + if ((row+=2) > height) row = 1; + } + black = black / ((896 - width) * height) - 4; + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + val = (BAYER(row,col) - black) * mul[row & 3][col & 1] >> 9; + if (val < 0) val = 0; + BAYER(row,col) = val; + } + canon_600_fixed_wb(1311); + canon_600_auto_wb(); + canon_600_coeff(); + maximum = (0x3ff - black) * 1109 >> 9; + black = 0; +} + +void CLASS canon_a5_load_raw() +{ + uchar data[1940], *dp; + ushort pixel[1552], *pix; + int row, col; + + for (row=0; row < height; row++) { + fread (data, raw_width * 10 / 8, 1, ifp); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=10, pix+=8) + { + pix[0] = (dp[1] << 2) + (dp[0] >> 6); + pix[1] = (dp[0] << 4) + (dp[3] >> 4); + pix[2] = (dp[3] << 6) + (dp[2] >> 2); + pix[3] = (dp[2] << 8) + (dp[5] ); + pix[4] = (dp[4] << 2) + (dp[7] >> 6); + pix[5] = (dp[7] << 4) + (dp[6] >> 4); + pix[6] = (dp[6] << 6) + (dp[9] >> 2); + pix[7] = (dp[9] << 8) + (dp[8] ); + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0x3ff); + for (col=width; col < raw_width; col++) + black += pixel[col] & 0x3ff; + } + if (raw_width > width) + black /= (raw_width - width) * height; + maximum = 0x3ff; +} + +/* + getbits(-1) initializes the buffer + getbits(n) where 0 <= n <= 25 returns an n-bit integer + */ +unsigned CLASS getbits (int nbits) +{ + static unsigned bitbuf=0; + static int vbits=0, reset=0; + unsigned c; + + if (nbits == -1) + return bitbuf = vbits = reset = 0; + if (nbits == 0 || reset) return 0; + while (vbits < nbits) { + c = fgetc(ifp); + if ((reset = zero_after_ff && c == 0xff && fgetc(ifp))) return 0; + bitbuf = (bitbuf << 8) + c; + vbits += 8; + } + vbits -= nbits; + return bitbuf << (32-nbits-vbits) >> (32-nbits); +} + +void CLASS init_decoder () +{ + memset (first_decode, 0, sizeof first_decode); + free_decode = first_decode; +} + +/* + Construct a decode tree according the specification in *source. + The first 16 bytes specify how many codes should be 1-bit, 2-bit + 3-bit, etc. Bytes after that are the leaf values. + + For example, if the source is + + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + + then the code is + + 00 0x04 + 010 0x03 + 011 0x05 + 100 0x06 + 101 0x02 + 1100 0x07 + 1101 0x01 + 11100 0x08 + 11101 0x09 + 11110 0x00 + 111110 0x0a + 1111110 0x0b + 1111111 0xff + */ +uchar * CLASS make_decoder (const uchar *source, int level) +{ + struct decode *cur; + static int leaf; + int i, next; + + if (level==0) leaf=0; + cur = free_decode++; + if (free_decode > first_decode+2048) { + fprintf (stderr, "%s: decoder table overflow\n", ifname); + longjmp (failure, 2); + } + for (i=next=0; i <= leaf && next < 16; ) + i += source[next++]; + if (i > leaf) { + if (level < next) { + cur->branch[0] = free_decode; + make_decoder (source, level+1); + cur->branch[1] = free_decode; + make_decoder (source, level+1); + } else + cur->leaf = source[16 + leaf++]; + } + return (uchar *) source + 16 + leaf; +} + +void CLASS crw_init_tables (unsigned table) +{ + static const uchar first_tree[3][29] = { + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + { 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0, + 0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b,0xff }, + { 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0, + 0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b,0xff }, + }; + static const uchar second_tree[3][180] = { + { 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0,139, + 0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08, + 0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a,0xf0, + 0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42, + 0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57, + 0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61,0xf9, + 0x71,0x78,0x75,0x96,0x97,0x49,0xb7,0x53,0xd7,0x74,0xb6,0x98, + 0x47,0x48,0x95,0x69,0x99,0x91,0xfa,0xb8,0x68,0xb5,0xb9,0xd6, + 0xf7,0xd8,0x67,0x46,0x45,0x94,0x89,0xf8,0x81,0xd5,0xf6,0xb4, + 0x88,0xb1,0x2a,0x44,0x72,0xd9,0x87,0x66,0xd4,0xf5,0x3a,0xa7, + 0x73,0xa9,0xa8,0x86,0x62,0xc7,0x65,0xc8,0xc9,0xa1,0xf4,0xd1, + 0xe9,0x5a,0x92,0x85,0xa6,0xe7,0x93,0xe8,0xc1,0xc6,0x7a,0x64, + 0xe1,0x4a,0x6a,0xe6,0xb3,0xf1,0xd3,0xa5,0x8a,0xb2,0x9a,0xba, + 0x84,0xa4,0x63,0xe5,0xc5,0xf3,0xd2,0xc4,0x82,0xaa,0xda,0xe4, + 0xf2,0xca,0x83,0xa3,0xa2,0xc3,0xea,0xc2,0xe2,0xe3,0xff,0xff }, + { 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0,140, + 0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06, + 0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32, + 0x0a,0x16,0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51, + 0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26, + 0x38,0x1a,0x37,0x81,0x27,0x91,0x79,0x55,0x45,0x28,0x72,0x59, + 0xa1,0xb1,0x44,0x69,0x54,0x58,0xd1,0xfa,0x57,0xe1,0xf1,0xb9, + 0x49,0x47,0x63,0x6a,0xf9,0x56,0x46,0xa8,0x2a,0x4a,0x78,0x99, + 0x3a,0x75,0x74,0x86,0x65,0xc1,0x76,0xb6,0x96,0xd6,0x89,0x85, + 0xc9,0xf5,0x95,0xb4,0xc7,0xf7,0x8a,0x97,0xb8,0x73,0xb7,0xd8, + 0xd9,0x87,0xa7,0x7a,0x48,0x82,0x84,0xea,0xf4,0xa6,0xc5,0x5a, + 0x94,0xa4,0xc6,0x92,0xc3,0x68,0xb5,0xc8,0xe4,0xe5,0xe6,0xe9, + 0xa2,0xa3,0xe3,0xc2,0x66,0x67,0x93,0xaa,0xd4,0xd5,0xe7,0xf8, + 0x88,0x9a,0xd7,0x77,0xc4,0x64,0xe2,0x98,0xa5,0xca,0xda,0xe8, + 0xf3,0xf6,0xa9,0xb2,0xb3,0xf2,0xd2,0x83,0xba,0xd3,0xff,0xff }, + { 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117, + 0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08, + 0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,0xf0,0x00,0x22, + 0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34, + 0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41, + 0x29,0x77,0x26,0x42,0x76,0x99,0x1a,0x55,0x98,0x97,0xf9,0x48, + 0x54,0x96,0x89,0x47,0xb7,0x49,0xfa,0x75,0x68,0xb6,0x67,0x69, + 0xb9,0xb8,0xd8,0x52,0xd7,0x88,0xb5,0x74,0x51,0x46,0xd9,0xf8, + 0x3a,0xd6,0x87,0x45,0x7a,0x95,0xd5,0xf6,0x86,0xb4,0xa9,0x94, + 0x53,0x2a,0xa8,0x43,0xf5,0xf7,0xd4,0x66,0xa7,0x5a,0x44,0x8a, + 0xc9,0xe8,0xc8,0xe7,0x9a,0x6a,0x73,0x4a,0x61,0xc7,0xf4,0xc6, + 0x65,0xe9,0x72,0xe6,0x71,0x91,0x93,0xa6,0xda,0x92,0x85,0x62, + 0xf3,0xc5,0xb2,0xa4,0x84,0xba,0x64,0xa5,0xb3,0xd2,0x81,0xe5, + 0xd3,0xaa,0xc4,0xca,0xf2,0xb1,0xe4,0xd1,0x83,0x63,0xea,0xc3, + 0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff } + }; + if (table > 2) table = 2; + init_decoder(); + make_decoder ( first_tree[table], 0); + second_decode = free_decode; + make_decoder (second_tree[table], 0); +} + +/* + Return 0 if the image starts with compressed data, + 1 if it starts with uncompressed low-order bits. + + In Canon compressed data, 0xff is always followed by 0x00. + */ +int CLASS canon_has_lowbits() +{ + uchar test[0x4000]; + int ret=1, i; + + fseek (ifp, 0, SEEK_SET); + fread (test, 1, sizeof test, ifp); + for (i=540; i < sizeof test - 1; i++) + if (test[i] == 0xff) { + if (test[i+1]) return 1; + ret=0; + } + return ret; +} + +void CLASS canon_compressed_load_raw() +{ + ushort *pixel, *prow; + int lowbits, i, row, r, col, save, val; + unsigned irow, icol; + struct decode *decode, *dindex; + int block, diffbuf[64], leaf, len, diff, carry=0, pnum=0, base[2]; + uchar c; + + pixel = calloc (raw_width*8, sizeof *pixel); + merror (pixel, "canon_compressed_load_raw()"); + lowbits = canon_has_lowbits(); + if (!lowbits) maximum = 0x3ff; + fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET); + zero_after_ff = 1; + getbits(-1); + for (row = 0; row < raw_height; row += 8) { + for (block=0; block < raw_width >> 3; block++) { + memset (diffbuf, 0, sizeof diffbuf); + decode = first_decode; + for (i=0; i < 64; i++ ) { + for (dindex=decode; dindex->branch[0]; ) + dindex = dindex->branch[getbits(1)]; + leaf = dindex->leaf; + decode = second_decode; + if (leaf == 0 && i) break; + if (leaf == 0xff) continue; + i += leaf >> 4; + len = leaf & 15; + if (len == 0) continue; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + if (i < 64) diffbuf[i] = diff; + } + diffbuf[0] += carry; + carry = diffbuf[0]; + for (i=0; i < 64; i++ ) { + if (pnum++ % raw_width == 0) + base[0] = base[1] = 512; + pixel[(block << 6) + i] = ( base[i & 1] += diffbuf[i] ); + } + } + if (lowbits) { + save = ftell(ifp); + fseek (ifp, 26 + row*raw_width/4, SEEK_SET); + for (prow=pixel, i=0; i < raw_width*2; i++) { + c = fgetc(ifp); + for (r=0; r < 8; r+=2, prow++) { + val = (*prow << 2) + ((c >> r) & 3); + if (raw_width == 2672 && val < 512) val += 2; + *prow = val; + } + } + fseek (ifp, save, SEEK_SET); + } + for (r=0; r < 8; r++) { + irow = row - top_margin + r; + if (irow >= height) continue; + for (col = 0; col < raw_width; col++) { + icol = col - left_margin; + if (icol < width) + BAYER(irow,icol) = pixel[r*raw_width+col]; + else + black += pixel[r*raw_width+col]; + } + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; +} + +/* + Not a full implementation of Lossless JPEG, just + enough to decode Canon, Kodak and Adobe DNG images. + */ +struct jhead { + int bits, high, wide, clrs, restart, vpred[4]; + struct decode *huff[4]; + ushort *row; +}; + +int CLASS ljpeg_start (struct jhead *jh) +{ + int i, tag, len; + uchar data[256], *dp; + + init_decoder(); + for (i=0; i < 4; i++) + jh->huff[i] = free_decode; + jh->restart = INT_MAX; + fread (data, 2, 1, ifp); + if (data[0] != 0xff || data[1] != 0xd8) return 0; + do { + fread (data, 2, 2, ifp); + tag = data[0] << 8 | data[1]; + len = (data[2] << 8 | data[3]) - 2; + if (tag <= 0xff00 || len > 255) return 0; + fread (data, 1, len, ifp); + switch (tag) { + case 0xffc3: + jh->bits = data[0]; + jh->high = data[1] << 8 | data[2]; + jh->wide = data[3] << 8 | data[4]; + jh->clrs = data[5]; + break; + case 0xffc4: + for (dp = data; dp < data+len && *dp < 4; ) { + jh->huff[*dp] = free_decode; + dp = make_decoder (++dp, 0); + } + break; + case 0xffdd: + jh->restart = data[0] << 8 | data[1]; + } + } while (tag != 0xffda); + jh->row = calloc (jh->wide*jh->clrs, 2); + merror (jh->row, " jpeg_start()"); + zero_after_ff = 1; + return 1; +} + +int CLASS ljpeg_diff (struct decode *dindex) +{ + int len, diff; + + while (dindex->branch[0]) + dindex = dindex->branch[getbits(1)]; + len = dindex->leaf; + if (len == 16 && (!dng_version || dng_version >= 0x1010000)) + return -32768; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + return diff; +} + +void CLASS ljpeg_row (int jrow, struct jhead *jh) +{ + int col, c, diff; + ushort *outp=jh->row; + + if (jrow * jh->wide % jh->restart == 0) { + FORC4 jh->vpred[c] = 1 << (jh->bits-1); + if (jrow) get2(); /* Eat the FF Dx marker */ + getbits(-1); + } + for (col=0; col < jh->wide; col++) + for (c=0; c < jh->clrs; c++) { + diff = ljpeg_diff (jh->huff[c]); + *outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff); + outp++; + } +} + +void CLASS lossless_jpeg_load_raw() +{ + int jwide, jrow, jcol, val, jidx, i, row, col; + struct jhead jh; + int min=INT_MAX; + + if (!ljpeg_start (&jh)) return; + jwide = jh.wide * jh.clrs; + + for (jrow=0; jrow < jh.high; jrow++) { + ljpeg_row (jrow, &jh); + for (jcol=0; jcol < jwide; jcol++) { + val = jh.row[jcol]; + if (jh.bits <= 12) + val = curve[val]; + jidx = jrow*jwide + jcol; + if (raw_width == 5108) { + i = jidx / (1680*jh.high); + if (i < 2) { + row = jidx / 1680 % jh.high; + col = jidx % 1680 + i*1680; + } else { + jidx -= 2*1680*jh.high; + row = jidx / 1748; + col = jidx % 1748 + 2*1680; + } + } else if (raw_width == 4476 || raw_width == 3516) { + row = jidx / (raw_width/2); + col = jidx % (raw_width/2); + if (row >= raw_height) { + row -= raw_height; + col += raw_width/2; + } + } else { + row = jidx / raw_width; + col = jidx % raw_width; + } + if ((unsigned) (row-top_margin) >= height) continue; + if ((unsigned) (col-left_margin) < width) { + BAYER(row-top_margin,col-left_margin) = val; + if (min > val) min = val; + } else + black += val; + } + } + free (jh.row); + if (raw_width > width) + black /= (raw_width - width) * height; + if (!strcasecmp(make,"KODAK")) + black = min; +} + +void CLASS adobe_copy_pixel (int row, int col, ushort **rp) +{ + unsigned r, c; + + r = row -= top_margin; + c = col -= left_margin; + if (fuji_secondary && use_secondary) (*rp)++; + if (filters) { + if (fuji_width) { + r = row + fuji_width - 1 - (col >> 1); + c = row + ((col+1) >> 1); + } + if (r < height && c < width) + BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp; + *rp += 1 + fuji_secondary; + } else { + if (r < height && c < width) + for (c=0; c < tiff_samples; c++) + image[row*width+col][c] = (*rp)[c] < 0x1000 ? curve[(*rp)[c]]:(*rp)[c]; + *rp += tiff_samples; + } + if (fuji_secondary && use_secondary) (*rp)--; +} + +void CLASS adobe_dng_load_raw_lj() +{ + int save, twide, trow=0, tcol=0, jrow, jcol; + struct jhead jh; + ushort *rp; + + while (1) { + save = ftell(ifp); + fseek (ifp, get4(), SEEK_SET); + if (!ljpeg_start (&jh)) break; + if (trow >= raw_height) break; + if (jh.high > raw_height-trow) + jh.high = raw_height-trow; + twide = jh.wide; + if (filters) twide *= jh.clrs; + else colors = jh.clrs; + if (fuji_secondary) twide /= 2; + if (twide > raw_width-tcol) + twide = raw_width-tcol; + + for (jrow=0; jrow < jh.high; jrow++) { + ljpeg_row (jrow, &jh); + for (rp=jh.row, jcol=0; jcol < twide; jcol++) + adobe_copy_pixel (trow+jrow, tcol+jcol, &rp); + } + fseek (ifp, save+4, SEEK_SET); + if ((tcol += twide) >= raw_width) { + tcol = 0; + trow += jh.high; + } + free (jh.row); + } +} + +void CLASS adobe_dng_load_raw_nc() +{ + ushort *pixel, *rp; + int row, col; + + pixel = calloc (raw_width * tiff_samples, sizeof *pixel); + merror (pixel, "adobe_dng_load_raw_nc()"); + for (row=0; row < raw_height; row++) { + if (tiff_bps == 16) + read_shorts (pixel, raw_width * tiff_samples); + else { + getbits(-1); + for (col=0; col < raw_width * tiff_samples; col++) + pixel[col] = getbits(tiff_bps); + } + for (rp=pixel, col=0; col < raw_width; col++) + adobe_copy_pixel (row, col, &rp); + } + free (pixel); +} + +void CLASS nikon_compressed_load_raw() +{ + static const uchar nikon_tree[] = { + 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0, + 5,4,3,6,2,7,1,0,8,9,11,10,12 + }; + int csize, row, col, i, diff; + ushort vpred[4], hpred[2], *curve; + + init_decoder(); + make_decoder (nikon_tree, 0); + + fseek (ifp, nikon_curve_offset, SEEK_SET); + read_shorts (vpred, 4); + csize = get2(); + curve = calloc (csize, sizeof *curve); + merror (curve, "nikon_compressed_load_raw()"); + read_shorts (curve, csize); + + fseek (ifp, data_offset, SEEK_SET); + getbits(-1); + + for (row=0; row < height; row++) + for (col=0; col < raw_width; col++) + { + diff = ljpeg_diff (first_decode); + if (col < 2) { + i = 2*(row & 1) + (col & 1); + vpred[i] += diff; + hpred[col] = vpred[i]; + } else + hpred[col & 1] += diff; + if ((unsigned) (col-left_margin) >= width) continue; + diff = hpred[col & 1]; + if (diff >= csize) diff = csize-1; + BAYER(row,col-left_margin) = curve[diff]; + } + free (curve); +} + +void CLASS nikon_load_raw() +{ + int irow, row, col, i; + + getbits(-1); + for (irow=0; irow < height; irow++) { + row = irow; + if (model[0] == 'E') { + row = irow * 2 % height + irow / (height/2); + if (row == 1 && data_offset == 0) { + fseek (ifp, 0, SEEK_END); + fseek (ifp, ftell(ifp)/2, SEEK_SET); + getbits(-1); + } + } + for (col=0; col < raw_width; col++) { + i = getbits(12); + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = i; + if (tiff_data_compression == 34713 && (col % 10) == 9) + getbits(8); + } + } +} + +/* + Figure out if a NEF file is compressed. These fancy heuristics + are only needed for the D100, thanks to a bug in some cameras + that tags all images as "compressed". + */ +int CLASS nikon_is_compressed() +{ + uchar test[256]; + int i; + + if (tiff_data_compression != 34713) + return 0; + if (strcmp(model,"D100")) + return 1; + fseek (ifp, data_offset, SEEK_SET); + fread (test, 1, 256, ifp); + for (i=15; i < 256; i+=16) + if (test[i]) return 1; + return 0; +} + +/* + Returns 1 for a Coolpix 990, 0 for a Coolpix 995. + */ +int CLASS nikon_e990() +{ + int i, histo[256]; + const uchar often[] = { 0x00, 0x55, 0xaa, 0xff }; + + memset (histo, 0, sizeof histo); + fseek (ifp, 2064*1540*3/4, SEEK_SET); + for (i=0; i < 2000; i++) + histo[fgetc(ifp)]++; + for (i=0; i < 4; i++) + if (histo[often[i]] > 400) + return 1; + return 0; +} + +/* + Returns 1 for a Coolpix 2100, 0 for anything else. + */ +int CLASS nikon_e2100() +{ + uchar t[12]; + int i; + + fseek (ifp, 0, SEEK_SET); + for (i=0; i < 1024; i++) { + fread (t, 1, 12, ifp); + if (((t[2] & t[4] & t[7] & t[9]) >> 4 + & t[1] & t[6] & t[8] & t[11] & 3) != 3) + return 0; + } + return 1; +} + +/* + Returns 0 for a Pentax Optio 33WR, + 1 for a Nikon E3700, + 2 for an Olympus C740UZ. + */ +int CLASS nikon_3700() +{ + int i, sum[] = { 0, 0 }; + uchar tail[952]; + + fseek (ifp, -sizeof tail, SEEK_END); + fread (tail, 1, sizeof tail, ifp); + for (i=0; i < sizeof tail; i++) + sum[(i>>2) & 1] += tail[i]; + if (sum[1] > 4*sum[0]) return 2; + return sum[0] > 4*sum[1]; +} + +/* + Separates a Minolta DiMAGE Z2 from a Nikon E4300. + */ +int CLASS minolta_z2() +{ + int i; + char tail[424]; + + fseek (ifp, -sizeof tail, SEEK_END); + fread (tail, 1, sizeof tail, ifp); + for (i=0; i < sizeof tail; i++) + if (tail[i]) return 1; + return 0; +} + +/* Here raw_width is in bytes, not pixels. */ +void CLASS nikon_e900_load_raw() +{ + int offset=0, irow, row, col; + + for (irow=0; irow < height; irow++) { + row = irow * 2 % height; + if (row == 1) + offset = - (-offset & -4096); + fseek (ifp, offset, SEEK_SET); + offset += raw_width; + getbits(-1); + for (col=0; col < width; col++) + BAYER(row,col) = getbits(10); + } +} + +void CLASS nikon_e2100_load_raw() +{ + uchar data[3456], *dp; + ushort pixel[2304], *pix; + int row, col; + + for (row=0; row <= height; row+=2) { + if (row == height) { + fseek (ifp, ((width==1616) << 13) - (-ftell(ifp) & -2048), SEEK_SET); + row = 1; + } + fread (data, 1, width*3/2, ifp); + for (dp=data, pix=pixel; pix < pixel+width; dp+=12, pix+=8) { + pix[0] = (dp[2] >> 4) + (dp[ 3] << 4); + pix[1] = (dp[2] << 8) + dp[ 1]; + pix[2] = (dp[7] >> 4) + (dp[ 0] << 4); + pix[3] = (dp[7] << 8) + dp[ 6]; + pix[4] = (dp[4] >> 4) + (dp[ 5] << 4); + pix[5] = (dp[4] << 8) + dp[11]; + pix[6] = (dp[9] >> 4) + (dp[10] << 4); + pix[7] = (dp[9] << 8) + dp[ 8]; + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0xfff); + } +} + +/* + The Fuji Super CCD is just a Bayer grid rotated 45 degrees. + */ +void CLASS fuji_load_raw() +{ + ushort *pixel; + int row, col, r, c; + + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "fuji_load_raw()"); + for (row=0; row < raw_height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < fuji_width << !fuji_layout; col++) { + if (fuji_layout) { + r = fuji_width - 1 - col + (row >> 1); + c = col + ((row+1) >> 1); + } else { + r = fuji_width - 1 + row - (col >> 1); + c = row + ((col+1) >> 1); + } + BAYER(r,c) = pixel[col]; + } + } + free (pixel); +} + +void CLASS rollei_load_raw() +{ + uchar pixel[10]; + unsigned iten=0, isix, i, buffer=0, row, col, todo[16]; + + isix = raw_width * raw_height * 5 / 8; + while (fread (pixel, 1, 10, ifp) == 10) { + for (i=0; i < 10; i+=2) { + todo[i] = iten++; + todo[i+1] = pixel[i] << 8 | pixel[i+1]; + buffer = pixel[i] >> 2 | buffer << 6; + } + for ( ; i < 16; i+=2) { + todo[i] = isix++; + todo[i+1] = buffer >> (14-i)*5; + } + for (i=0; i < 16; i+=2) { + row = todo[i] / raw_width - top_margin; + col = todo[i] % raw_width - left_margin; + if (row < height && col < width) + BAYER(row,col) = (todo[i+1] & 0x3ff); + } + } + maximum = 0x3ff; +} + +void CLASS phase_one_load_raw() +{ + int row, col, a, b; + ushort *pixel, akey, bkey, mask; + + fseek (ifp, nikon_curve_offset, SEEK_SET); + akey = get2(); + bkey = get2(); + mask = tiff_data_compression == 1 ? 0x5555:0x1354; + fseek (ifp, data_offset + top_margin*raw_width*2, SEEK_SET); + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "phase_one_load_raw()"); + for (row=0; row < height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < raw_width; col+=2) { + a = pixel[col+0] ^ akey; + b = pixel[col+1] ^ bkey; + pixel[col+0] = (a & mask) | (b & ~mask); + pixel[col+1] = (b & mask) | (a & ~mask); + } + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col+left_margin]; + } + free (pixel); + maximum = 0xffff; +} + +unsigned CLASS ph1_bits (int nbits) +{ + static UINT64 bitbuf=0; + static int vbits=0; + + if (nbits == 0) + return bitbuf = vbits = 0; + if (vbits < nbits) { + bitbuf = bitbuf << 32 | (unsigned) get4(); + vbits += 32; + } + vbits -= nbits; + return bitbuf << (64 - nbits - vbits) >> (64 - nbits); +} + +void CLASS phase_one_load_raw_c() +{ + static const int length[] = { 8,7,6,9,11,10,5,12,14,13 }; + int len[2], pred[2], row, col, ncols, i, j; + ushort *pixel; + + ncols = (raw_width + 7) & -8; + pixel = calloc (ncols, sizeof *pixel); + merror (pixel, "phase_one_load_raw_c()"); + for (row=0; row < raw_height; row++) { + ph1_bits(0); + pred[0] = pred[1] = 0; + for (col=0; col < ncols; col++) { + if (col >= (raw_width & -8)) + len[0] = len[1] = 14; + else if ((col & 7) == 0) + for (i=0; i < 2; i++) { + for (j=0; j < 5 && !ph1_bits(1); j++); + if (j--) len[i] = length[j*2 + ph1_bits(1)]; + } + if ((i = len[col & 1]) == 14) + pixel[col] = pred[col & 1] = ph1_bits(16); + else + pixel[col] = pred[col & 1] += ph1_bits(i) + 1 - (1 << (i - 1)); + } + if ((unsigned) (row-top_margin) < height) + for (col=0; col < width; col++) + BAYER(row-top_margin,col) = pixel[col+left_margin]; + } + free (pixel); + maximum = 0x3fff; +} + +void CLASS leaf_load_raw() +{ + ushort *pixel; + int r, c, row, col; + + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "leaf_load_raw()"); + for (r=0; r < height-32; r+=32) + FORC3 for (row=r; row < r+32; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < width; col++) + image[row*width+col][c] = pixel[col]; + } + free (pixel); +} + +/* Here raw_width is in bytes, not pixels. */ +void CLASS packed_12_load_raw() +{ + int row, col; + + getbits(-1); + for (row=0; row < height; row++) { + for (col=0; col < width; col++) + BAYER(row,col) = getbits(12); + for (col = width*3/2; col < raw_width; col++) + getbits(8); + } +} + +void CLASS unpacked_load_raw() +{ + ushort *pixel; + int row, col; + + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "unpacked_load_raw()"); + for (row=0; row < height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); +} + +void CLASS olympus_e300_load_raw() +{ + uchar *data, *dp; + ushort *pixel, *pix; + int dwide, row, col; + + dwide = raw_width * 16 / 10; + data = malloc (dwide + raw_width*2); + merror (data, "olympus_e300_load_raw()"); + pixel = (ushort *) (data + dwide); + for (row=0; row < height; row++) { + fread (data, 1, dwide, ifp); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) { + if (((dp-data) & 15) == 15) dp++; + pix[0] = dp[1] << 8 | dp[0]; + pix[1] = dp[2] << 4 | dp[1] >> 4; + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0xfff); + for (col=width+4; col < raw_width; col++) + black += pixel[col] & 0xfff; + } + black /= (raw_width - width - 4) * height; + free (data); +} + +void CLASS olympus_cseries_load_raw() +{ + int irow, row, col; + + for (irow=0; irow < height; irow++) { + row = irow * 2 % height + irow / (height/2); + if (row < 2) { + fseek (ifp, data_offset - row*(-width*height*3/4 & -2048), SEEK_SET); + getbits(-1); + } + for (col=0; col < width; col++) + BAYER(row,col) = getbits(12); + } +} + +void CLASS minolta_rd175_load_raw() +{ + uchar pixel[768]; + unsigned irow, box, row, col; + + for (irow=0; irow < 1481; irow++) { + fread (pixel, 1, 768, ifp); + box = irow / 82; + row = irow % 82 * 12 + ((box < 12) ? box | 1 : (box-12)*2); + switch (irow) { + case 1477: case 1479: continue; + case 1476: row = 984; break; + case 1480: row = 985; break; + case 1478: row = 985; box = 1; + } + if ((box < 12) && (box & 1)) { + for (col=0; col < 1533; col++, row ^= 1) + if (col != 1) BAYER(row,col) = (col+1) & 2 ? + pixel[col/2-1] + pixel[col/2+1] : pixel[col/2] << 1; + BAYER(row,1) = pixel[1] << 1; + BAYER(row,1533) = pixel[765] << 1; + } else + for (col=row & 1; col < 1534; col+=2) + BAYER(row,col) = pixel[col/2] << 1; + } + maximum = 0xff << 1; +} + +void CLASS eight_bit_load_raw() +{ + uchar *pixel; + int row, col; + + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "eight_bit_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 1, raw_width, ifp); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); + maximum = 0xff; +} + +void CLASS casio_qv5700_load_raw() +{ + uchar data[3232], *dp; + ushort pixel[2576], *pix; + int row, col; + + for (row=0; row < height; row++) { + fread (data, 1, 3232, ifp); + for (dp=data, pix=pixel; dp < data+3220; dp+=5, pix+=4) { + pix[0] = (dp[0] << 2) + (dp[1] >> 6); + pix[1] = (dp[1] << 4) + (dp[2] >> 4); + pix[2] = (dp[2] << 6) + (dp[3] >> 2); + pix[3] = (dp[3] << 8) + (dp[4] ); + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0x3ff); + } + maximum = 0x3fc; +} + +void CLASS nucore_load_raw() +{ + ushort *pixel; + int irow, row, col; + + pixel = calloc (width, 2); + merror (pixel, "nucore_load_raw()"); + for (irow=0; irow < height; irow++) { + read_shorts (pixel, width); + row = irow/2 + height/2 * (irow & 1); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); +} + +const int * CLASS make_decoder_int (const int *source, int level) +{ + struct decode *cur; + + cur = free_decode++; + if (level < source[0]) { + cur->branch[0] = free_decode; + source = make_decoder_int (source, level+1); + cur->branch[1] = free_decode; + source = make_decoder_int (source, level+1); + } else { + cur->leaf = source[1]; + source += 2; + } + return source; +} + +int CLASS radc_token (int tree) +{ + int t; + static struct decode *dstart[18], *dindex; + static const int *s, source[] = { + 1,1, 2,3, 3,4, 4,2, 5,7, 6,5, 7,6, 7,8, + 1,0, 2,1, 3,3, 4,4, 5,2, 6,7, 7,6, 8,5, 8,8, + 2,1, 2,3, 3,0, 3,2, 3,4, 4,6, 5,5, 6,7, 6,8, + 2,0, 2,1, 2,3, 3,2, 4,4, 5,6, 6,7, 7,5, 7,8, + 2,1, 2,4, 3,0, 3,2, 3,3, 4,7, 5,5, 6,6, 6,8, + 2,3, 3,1, 3,2, 3,4, 3,5, 3,6, 4,7, 5,0, 5,8, + 2,3, 2,6, 3,0, 3,1, 4,4, 4,5, 4,7, 5,2, 5,8, + 2,4, 2,7, 3,3, 3,6, 4,1, 4,2, 4,5, 5,0, 5,8, + 2,6, 3,1, 3,3, 3,5, 3,7, 3,8, 4,0, 5,2, 5,4, + 2,0, 2,1, 3,2, 3,3, 4,4, 4,5, 5,6, 5,7, 4,8, + 1,0, 2,2, 2,-2, + 1,-3, 1,3, + 2,-17, 2,-5, 2,5, 2,17, + 2,-7, 2,2, 2,9, 2,18, + 2,-18, 2,-9, 2,-2, 2,7, + 2,-28, 2,28, 3,-49, 3,-9, 3,9, 4,49, 5,-79, 5,79, + 2,-1, 2,13, 2,26, 3,39, 4,-16, 5,55, 6,-37, 6,76, + 2,-26, 2,-13, 2,1, 3,-39, 4,16, 5,-55, 6,-76, 6,37 + }; + + if (free_decode == first_decode) + for (s=source, t=0; t < 18; t++) { + dstart[t] = free_decode; + s = make_decoder_int (s, 0); + } + if (tree == 18) { + if (kodak_cbpp == 243) + return (getbits(6) << 2) + 2; /* most DC50 photos */ + else + return (getbits(5) << 3) + 4; /* DC40, Fotoman Pixtura */ + } + for (dindex = dstart[tree]; dindex->branch[0]; ) + dindex = dindex->branch[getbits(1)]; + return dindex->leaf; +} + +#define FORYX for (y=1; y < 3; y++) for (x=col+1; x >= col; x--) + +#define PREDICTOR (c ? (buf[c][y-1][x] + buf[c][y][x+1]) / 2 \ +: (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4) + +void CLASS kodak_radc_load_raw() +{ + int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val; + short last[3] = { 16,16,16 }, mul[3], buf[3][3][386]; + + init_decoder(); + getbits(-1); + for (i=0; i < sizeof(buf)/sizeof(short); i++) + buf[0][0][i] = 2048; + for (row=0; row < height; row+=4) { + FORC3 mul[c] = getbits(6); + FORC3 { + val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c]; + s = val > 65564 ? 10:12; + x = ~(-1 << (s-1)); + val <<= 12-s; + for (i=0; i < sizeof(buf[0])/sizeof(short); i++) + buf[c][0][i] = (buf[c][0][i] * val + x) >> s; + last[c] = mul[c]; + for (r=0; r <= !c; r++) { + buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7; + for (tree=1, col=width/2; col > 0; ) { + if ((tree = radc_token(tree))) { + col -= 2; + if (tree == 8) + FORYX buf[c][y][x] = radc_token(tree+10) * mul[c]; + else + FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR; + } else + do { + nreps = (col > 2) ? radc_token(9) + 1 : 1; + for (rep=0; rep < 8 && rep < nreps && col > 0; rep++) { + col -= 2; + FORYX buf[c][y][x] = PREDICTOR; + if (rep & 1) { + step = radc_token(10) << 4; + FORYX buf[c][y][x] += step; + } + } + } while (nreps == 9); + } + for (y=0; y < 2; y++) + for (x=0; x < width/2; x++) { + val = (buf[c][y+1][x] << 4) / mul[c]; + if (val < 0) val = 0; + if (c) + BAYER(row+y*2+c-1,x*2+2-c) = val; + else + BAYER(row+r*2+y,x*2+y) = val; + } + memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c); + } + } + for (y=row; y < row+4; y++) + for (x=0; x < width; x++) + if ((x+y) & 1) { + val = (BAYER(y,x)-2048)*2 + (BAYER(y,x-1)+BAYER(y,x+1))/2; + if (val < 0) val = 0; + BAYER(y,x) = val; + } + } + maximum = 10000; +} + +#undef FORYX +#undef PREDICTOR + +#ifdef NO_JPEG +void CLASS kodak_jpeg_load_raw() {} +#else + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + static uchar jpeg_buffer[4096]; + size_t nbytes; + + nbytes = fread (jpeg_buffer, 1, 4096, ifp); + swab (jpeg_buffer, jpeg_buffer, nbytes); + cinfo->src->next_input_byte = jpeg_buffer; + cinfo->src->bytes_in_buffer = nbytes; + return TRUE; +} + +void CLASS kodak_jpeg_load_raw() +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPARRAY buf; + JSAMPLE (*pixel)[3]; + int row, col; + + cinfo.err = jpeg_std_error (&jerr); + jpeg_create_decompress (&cinfo); + jpeg_stdio_src (&cinfo, ifp); + cinfo.src->fill_input_buffer = fill_input_buffer; + jpeg_read_header (&cinfo, TRUE); + jpeg_start_decompress (&cinfo); + if ((cinfo.output_width != width ) || + (cinfo.output_height*2 != height ) || + (cinfo.output_components != 3 )) { + fprintf (stderr, "%s: incorrect JPEG dimensions\n", ifname); + jpeg_destroy_decompress (&cinfo); + longjmp (failure, 3); + } + buf = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, width*3, 1); + + while (cinfo.output_scanline < cinfo.output_height) { + row = cinfo.output_scanline * 2; + jpeg_read_scanlines (&cinfo, buf, 1); + pixel = (void *) buf[0]; + for (col=0; col < width; col+=2) { + BAYER(row+0,col+0) = pixel[col+0][1] << 1; + BAYER(row+1,col+1) = pixel[col+1][1] << 1; + BAYER(row+0,col+1) = pixel[col][0] + pixel[col+1][0]; + BAYER(row+1,col+0) = pixel[col][2] + pixel[col+1][2]; + } + } + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + maximum = 0xff << 1; +} + +#endif + +void CLASS kodak_dc120_load_raw() +{ + static const int mul[4] = { 162, 192, 187, 92 }; + static const int add[4] = { 0, 636, 424, 212 }; + uchar pixel[848]; + int row, shift, col; + + for (row=0; row < height; row++) { + fread (pixel, 848, 1, ifp); + shift = row * mul[row & 3] + add[row & 3]; + for (col=0; col < width; col++) + BAYER(row,col) = (ushort) pixel[(col + shift) % 848]; + } + maximum = 0xff; +} + +void CLASS kodak_easy_load_raw() +{ + uchar *pixel; + unsigned row, col, icol; + + if (raw_width > width) + black = 0; + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "kodak_easy_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 1, raw_width, ifp); + for (col=0; col < raw_width; col++) { + icol = col - left_margin; + if (icol < width) + BAYER(row,icol) = (ushort) curve[pixel[col]]; + else + black += curve[pixel[col]]; + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; + if (!strncmp(model,"DC2",3)) + black = 0; + maximum = curve[0xff]; +} + +void CLASS kodak_compressed_load_raw() +{ + uchar c, blen[256]; + ushort raw[6]; + unsigned row, col, len, save, i, israw=0, bits=0, pred[2]; + INT64 bitbuf=0; + int diff; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) + { + if ((col & 255) == 0) { /* Get the bit-lengths of the */ + len = width - col; /* next 256 pixel values */ + if (len > 256) len = 256; + save = ftell(ifp); + for (israw=i=0; i < len; i+=2) { + c = fgetc(ifp); + if ((blen[i+0] = c & 15) > 12 || + (blen[i+1] = c >> 4) > 12 ) + israw = 1; + } + bitbuf = bits = pred[0] = pred[1] = 0; + if (len % 8 == 4) { + bitbuf = fgetc(ifp) << 8; + bitbuf += fgetc(ifp); + bits = 16; + } + if (israw) + fseek (ifp, save, SEEK_SET); + } + if (israw) { /* If the data is not compressed */ + switch (col & 7) { + case 0: + read_shorts (raw, 6); + diff = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12; + break; + case 1: + diff = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12; + break; + default: + diff = raw[(col & 7) - 2] & 0xfff; + } + } else { /* If the data is compressed */ + len = blen[col & 255]; /* Number of bits for this pixel */ + if (bits < len) { /* Got enough bits in the buffer? */ + for (i=0; i < 32; i+=8) + bitbuf += (INT64) fgetc(ifp) << (bits+(i^8)); + bits += 32; + } + diff = bitbuf & (0xffff >> (16-len)); /* Pull bits from buffer */ + bitbuf >>= len; + bits -= len; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + pred[col & 1] += diff; + diff = pred[col & 1]; + } + BAYER(row,col) = curve[diff]; + } +} + +void CLASS kodak_yuv_load_raw() +{ + uchar c, blen[384]; + unsigned row, col, len, bits=0; + INT64 bitbuf=0; + int i, li=0, si, diff, six[6], y[4], cb=0, cr=0, rgb[3]; + ushort *ip; + + for (row=0; row < height; row+=2) + for (col=0; col < width; col+=2) { + if ((col & 127) == 0) { + len = (width - col + 1) * 3 & -4; + if (len > 384) len = 384; + for (i=0; i < len; ) { + c = fgetc(ifp); + blen[i++] = c & 15; + blen[i++] = c >> 4; + } + li = bitbuf = bits = y[1] = y[3] = cb = cr = 0; + if (len % 8 == 4) { + bitbuf = fgetc(ifp) << 8; + bitbuf += fgetc(ifp); + bits = 16; + } + } + for (si=0; si < 6; si++) { + len = blen[li++]; + if (bits < len) { + for (i=0; i < 32; i+=8) + bitbuf += (INT64) fgetc(ifp) << (bits+(i^8)); + bits += 32; + } + diff = bitbuf & (0xffff >> (16-len)); + bitbuf >>= len; + bits -= len; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + six[si] = diff; + } + y[0] = six[0] + y[1]; + y[1] = six[1] + y[0]; + y[2] = six[2] + y[3]; + y[3] = six[3] + y[2]; + cb += six[4]; + cr += six[5]; + for (i=0; i < 4; i++) { + ip = image[(row+(i >> 1))*width + col+(i & 1)]; + rgb[0] = y[i] + cr; + rgb[1] = y[i]; + rgb[2] = y[i] + cb; + FORC3 if (rgb[c] > 0) ip[c] = curve[rgb[c]]; + } + } +} + +void CLASS sony_decrypt (unsigned *data, int len, int start, int key) +{ + static unsigned pad[128], p; + + if (start) { + for (p=0; p < 4; p++) + pad[p] = key = key * 48828125 + 1; + pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31; + for (p=4; p < 127; p++) + pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31; + for (p=0; p < 127; p++) + pad[p] = htonl(pad[p]); + } + while (len--) + *data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127]; +} + +void CLASS sony_load_raw() +{ + uchar head[40]; + ushort *pixel; + unsigned i, key, row, col; + + fseek (ifp, 200896, SEEK_SET); + fseek (ifp, (unsigned) fgetc(ifp)*4 - 1, SEEK_CUR); + order = 0x4d4d; + key = get4(); + fseek (ifp, 164600, SEEK_SET); + fread (head, 1, 40, ifp); + sony_decrypt ((void *) head, 10, 1, key); + for (i=26; i-- > 22; ) + key = key << 8 | head[i]; + fseek (ifp, data_offset, SEEK_SET); + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "sony_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 2, raw_width, ifp); + sony_decrypt ((void *) pixel, raw_width/2, !row, key); + for (col=9; col < left_margin; col++) + black += ntohs(pixel[col]); + for (col=0; col < width; col++) + BAYER(row,col) = ntohs(pixel[col+left_margin]); + } + free (pixel); + if (left_margin > 9) + black /= (left_margin-9) * height; + maximum = 0x3ff0; +} + +#define HOLE(row) ((holes >> (((row) - raw_height) & 7)) & 1) + +/* Kudos to Rich Taylor for figuring out SMaL's compression algorithm. */ +void CLASS smal_decode_segment (unsigned seg[2][2], int holes) +{ + uchar hist[3][13] = { + { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 }, + { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 }, + { 3, 3, 0, 0, 63, 47, 31, 15, 0 } }; + int low, high=0xff, carry=0, nbits=8; + int s, count, bin, next, i, sym[3]; + uchar diff, pred[]={0,0}; + ushort data=0, range=0; + unsigned pix, row, col; + + fseek (ifp, seg[0][1]+1, SEEK_SET); + getbits(-1); + for (pix=seg[0][0]; pix < seg[1][0]; pix++) { + for (s=0; s < 3; s++) { + data = data << nbits | getbits(nbits); + if (carry < 0) + carry = (nbits += carry+1) < 1 ? nbits-1 : 0; + while (--nbits >= 0) + if ((data >> nbits & 0xff) == 0xff) break; + if (nbits > 0) + data = ((data & ((1 << (nbits-1)) - 1)) << 1) | + ((data + (((data & (1 << (nbits-1)))) << 1)) & (-1 << nbits)); + if (nbits >= 0) { + data += getbits(1); + carry = nbits - 8; + } + count = ((((data-range+1) & 0xffff) << 2) - 1) / (high >> 4); + for (bin=0; hist[s][bin+5] > count; bin++); + low = hist[s][bin+5] * (high >> 4) >> 2; + if (bin) high = hist[s][bin+4] * (high >> 4) >> 2; + high -= low; + for (nbits=0; high << nbits < 128; nbits++); + range = (range+low) << nbits; + high <<= nbits; + next = hist[s][1]; + if (++hist[s][2] > hist[s][3]) { + next = (next+1) & hist[s][0]; + hist[s][3] = (hist[s][next+4] - hist[s][next+5]) >> 2; + hist[s][2] = 1; + } + if (hist[s][hist[s][1]+4] - hist[s][hist[s][1]+5] > 1) { + if (bin < hist[s][1]) + for (i=bin; i < hist[s][1]; i++) hist[s][i+5]--; + else if (next <= bin) + for (i=hist[s][1]; i < bin; i++) hist[s][i+5]++; + } + hist[s][1] = next; + sym[s] = bin; + } + diff = sym[2] << 5 | sym[1] << 2 | (sym[0] & 3); + if (sym[0] & 4) + diff = diff ? -diff : 0x80; + if (ftell(ifp) + 12 >= seg[1][1]) + diff = 0; + pred[pix & 1] += diff; + row = pix / raw_width - top_margin; + col = pix % raw_width - left_margin; + if (row < height && col < width) + BAYER(row,col) = pred[pix & 1]; + if (!(pix & 1) && HOLE(row)) pix += 2; + } + maximum = 0xff; +} + +void CLASS smal_v6_load_raw() +{ + unsigned seg[2][2]; + + fseek (ifp, 16, SEEK_SET); + seg[0][0] = 0; + seg[0][1] = get2(); + seg[1][0] = raw_width * raw_height; + seg[1][1] = INT_MAX; + smal_decode_segment (seg, 0); + use_gamma = 0; +} + +int CLASS median4 (int *p) +{ + int min, max, sum, i; + + min = max = sum = p[0]; + for (i=1; i < 4; i++) { + sum += p[i]; + if (min > p[i]) min = p[i]; + if (max < p[i]) max = p[i]; + } + return (sum - min - max) >> 1; +} + +void CLASS fill_holes (int holes) +{ + int row, col, val[4]; + + for (row=2; row < height-2; row++) { + if (!HOLE(row)) continue; + for (col=1; col < width-1; col+=4) { + val[0] = BAYER(row-1,col-1); + val[1] = BAYER(row-1,col+1); + val[2] = BAYER(row+1,col-1); + val[3] = BAYER(row+1,col+1); + BAYER(row,col) = median4(val); + } + for (col=2; col < width-2; col+=4) + if (HOLE(row-2) || HOLE(row+2)) + BAYER(row,col) = (BAYER(row,col-2) + BAYER(row,col+2)) >> 1; + else { + val[0] = BAYER(row,col-2); + val[1] = BAYER(row,col+2); + val[2] = BAYER(row-2,col); + val[3] = BAYER(row+2,col); + BAYER(row,col) = median4(val); + } + } +} + +void CLASS smal_v9_load_raw() +{ + unsigned seg[256][2], offset, nseg, holes, i; + + fseek (ifp, 67, SEEK_SET); + offset = get4(); + nseg = fgetc(ifp); + fseek (ifp, offset, SEEK_SET); + for (i=0; i < nseg*2; i++) + seg[0][i] = get4() + data_offset*(i & 1); + fseek (ifp, 78, SEEK_SET); + holes = fgetc(ifp); + fseek (ifp, 88, SEEK_SET); + seg[nseg][0] = raw_height * raw_width; + seg[nseg][1] = get4() + data_offset; + for (i=0; i < nseg; i++) + smal_decode_segment (seg+i, holes); + if (holes) fill_holes (holes); +} + +/* BEGIN GPL BLOCK */ + +void CLASS foveon_decoder (unsigned huff[1024], unsigned code) +{ + struct decode *cur; + int i, len; + + cur = free_decode++; + if (free_decode > first_decode+2048) { + fprintf (stderr, "%s: decoder table overflow\n", ifname); + longjmp (failure, 2); + } + if (code) + for (i=0; i < 1024; i++) + if (huff[i] == code) { + cur->leaf = i; + return; + } + if ((len = code >> 27) > 26) return; + code = (len+1) << 27 | (code & 0x3ffffff) << 1; + + cur->branch[0] = free_decode; + foveon_decoder (huff, code); + cur->branch[1] = free_decode; + foveon_decoder (huff, code+1); +} + +void CLASS foveon_load_camf() +{ + unsigned key, i, val; + + fseek (ifp, meta_offset, SEEK_SET); + key = get4(); + fread (meta_data, 1, meta_length, ifp); + for (i=0; i < meta_length; i++) { + key = (key * 1597 + 51749) % 244944; + val = key * (INT64) 301593171 >> 24; + meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17; + } +} + +void CLASS foveon_load_raw() +{ + struct decode *dindex; + short diff[1024], pred[3]; + unsigned huff[1024], bitbuf=0; + int fixed, row, col, bit=-1, c, i; + + fixed = get4(); + read_shorts ((ushort *) diff, 1024); + if (!fixed) { + for (i=0; i < 1024; i++) + huff[i] = get4(); + init_decoder(); + foveon_decoder (huff, 0); + } + for (row=0; row < height; row++) { + memset (pred, 0, sizeof pred); + if (!bit && !fixed) get4(); + for (col=bit=0; col < width; col++) { + if (fixed) { + bitbuf = get4(); + FORC3 pred[2-c] += diff[bitbuf >> c*10 & 0x3ff]; + } + else FORC3 { + for (dindex=first_decode; dindex->branch[0]; ) { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + fgetc(ifp); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += diff[dindex->leaf]; + } + FORC3 image[row*width+col][c] = pred[c]; + } + } + foveon_load_camf(); + maximum = clip_max = 0xffff; +} + +char * CLASS foveon_camf_param (char *block, char *param) +{ + unsigned idx, num; + char *pos, *cp, *dp; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'P') continue; + if (strcmp (block, pos+sget4(pos+12))) continue; + cp = pos + sget4(pos+16); + num = sget4(cp); + dp = pos + sget4(cp+4); + while (num--) { + cp += 8; + if (!strcmp (param, dp+sget4(cp))) + return dp+sget4(cp+4); + } + } + return NULL; +} + +void * CLASS foveon_camf_matrix (int dim[3], char *name) +{ + unsigned i, idx, type, ndim, size, *mat; + char *pos, *cp, *dp; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'M') continue; + if (strcmp (name, pos+sget4(pos+12))) continue; + dim[0] = dim[1] = dim[2] = 1; + cp = pos + sget4(pos+16); + type = sget4(cp); + if ((ndim = sget4(cp+4)) > 3) break; + dp = pos + sget4(cp+8); + for (i=ndim; i--; ) { + cp += 12; + dim[i] = sget4(cp); + } + if ((size = dim[0]*dim[1]*dim[2]) > meta_length/4) break; + mat = malloc (size * 4); + merror (mat, "foveon_camf_matrix()"); + for (i=0; i < size; i++) + if (type && type != 6) + mat[i] = sget4(dp + i*4); + else + mat[i] = sget4(dp + i*2) & 0xffff; + return mat; + } + fprintf (stderr, "%s: \"%s\" matrix not found!\n", ifname, name); + return NULL; +} + +int CLASS foveon_fixed (void *ptr, int size, char *name) +{ + void *dp; + int dim[3]; + + dp = foveon_camf_matrix (dim, name); + if (!dp) return 0; + memcpy (ptr, dp, size*4); + free (dp); + return 1; +} + +float CLASS foveon_avg (short *pix, int range[2], float cfilt) +{ + int i; + float val, min=FLT_MAX, max=-FLT_MAX, sum=0; + + for (i=range[0]; i <= range[1]; i++) { + sum += val = pix[i*4] + (pix[i*4]-pix[(i-1)*4]) * cfilt; + if (min > val) min = val; + if (max < val) max = val; + } + return (sum - min - max) / (range[1] - range[0] - 1); +} + +short * CLASS foveon_make_curve (double max, double mul, double filt) +{ + short *curve; + int i, size; + double x; + + if (!filt) filt = 0.8; + size = 4*M_PI*max / filt; + curve = calloc (size+1, sizeof *curve); + merror (curve, "foveon_make_curve()"); + curve[0] = size; + for (i=0; i < size; i++) { + x = i*filt/max/4; + curve[i+1] = (cos(x)+1)/2 * tanh(i*filt/mul) * mul + 0.5; + } + return curve; +} + +void CLASS foveon_make_curves + (short **curvep, float dq[3], float div[3], float filt) +{ + double mul[3], max=0; + int c; + + FORC3 mul[c] = dq[c]/div[c]; + FORC3 if (max < mul[c]) max = mul[c]; + FORC3 curvep[c] = foveon_make_curve (max, mul[c], filt); +} + +int CLASS foveon_apply_curve (short *curve, int i) +{ + if (abs(i) >= curve[0]) return 0; + return i < 0 ? -curve[1-i] : curve[1+i]; +} + +#define image ((short (*)[4]) image) + +void CLASS foveon_interpolate() +{ + static const short hood[] = { -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 }; + short *pix, prev[3], *curve[8], (*shrink)[3]; + float cfilt=0, ddft[3][3][2], ppm[3][3][3]; + float cam_xyz[3][3], correct[3][3], last[3][3], trans[3][3]; + float chroma_dq[3], color_dq[3], diag[3][3], div[3]; + float (*black)[3], (*sgain)[3], (*sgrow)[3]; + float fsum[3], val, frow, num; + int row, col, c, i, j, diff, sgx, irow, sum, min, max, limit; + int dim[3], dscr[2][2], dstb[4], (*smrow[7])[3], total[4], ipix[3]; + int work[3][3], smlast, smred, smred_p=0, dev[3]; + int satlev[3], keep[4], active[4]; + unsigned *badpix; + double dsum=0, trsum[3]; + char str[128], *cp; + + if (verbose) + fprintf (stderr, "Foveon interpolation...\n"); + + foveon_fixed (dscr, 4, "DarkShieldColRange"); + foveon_fixed (ppm[0][0], 27, "PostPolyMatrix"); + foveon_fixed (satlev, 3, "SaturationLevel"); + foveon_fixed (keep, 4, "KeepImageArea"); + foveon_fixed (active, 4, "ActiveImageArea"); + foveon_fixed (chroma_dq, 3, "ChromaDQ"); + foveon_fixed (color_dq, 3, + foveon_camf_param ("IncludeBlocks", "ColorDQ") ? + "ColorDQ" : "ColorDQCamRGB"); + if (foveon_camf_param ("IncludeBlocks", "ColumnFilter")) + foveon_fixed (&cfilt, 1, "ColumnFilter"); + + memset (ddft, 0, sizeof ddft); + if (!foveon_camf_param ("IncludeBlocks", "DarkDrift") + || !foveon_fixed (ddft[1][0], 12, "DarkDrift")) + for (i=0; i < 2; i++) { + foveon_fixed (dstb, 4, i ? "DarkShieldBottom":"DarkShieldTop"); + for (row = dstb[1]; row <= dstb[3]; row++) + for (col = dstb[0]; col <= dstb[2]; col++) + FORC3 ddft[i+1][c][1] += (short) image[row*width+col][c]; + FORC3 ddft[i+1][c][1] /= (dstb[3]-dstb[1]+1) * (dstb[2]-dstb[0]+1); + } + + if (!(cp = foveon_camf_param ("WhiteBalanceIlluminants", model2))) + { fprintf (stderr, "%s: Invalid white balance \"%s\"\n", ifname, model2); + return; } + foveon_fixed (cam_xyz, 9, cp); + foveon_fixed (correct, 9, + foveon_camf_param ("WhiteBalanceCorrections", model2)); + memset (last, 0, sizeof last); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 last[i][j] += correct[i][c] * cam_xyz[c][j]; + + sprintf (str, "%sRGBNeutral", model2); + if (foveon_camf_param ("IncludeBlocks", str)) + foveon_fixed (div, 3, str); + else { + #define LAST(x,y) last[(i+x)%3][(c+y)%3] + for (i=0; i < 3; i++) + FORC3 diag[c][i] = LAST(1,1)*LAST(2,2) - LAST(1,2)*LAST(2,1); + #undef LAST + FORC3 div[c] = diag[c][0]*0.3127 + diag[c][1]*0.329 + diag[c][2]*0.3583; + } + num = 0; + FORC3 if (num < div[c]) num = div[c]; + FORC3 div[c] /= num; + + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += rgb_cam[i][c] * last[c][j] * div[j]; + FORC3 trsum[c] = trans[c][0] + trans[c][1] + trans[c][2]; + dsum = (6*trsum[0] + 11*trsum[1] + 3*trsum[2]) / 20; + for (i=0; i < 3; i++) + FORC3 last[i][c] = trans[i][c] * dsum / trsum[i]; + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += (i==c ? 32 : -1) * last[c][j] / 30; + + foveon_make_curves (curve, color_dq, div, cfilt); + FORC3 chroma_dq[c] /= 3; + foveon_make_curves (curve+3, chroma_dq, div, cfilt); + FORC3 dsum += chroma_dq[c] / div[c]; + curve[6] = foveon_make_curve (dsum, dsum, cfilt); + curve[7] = foveon_make_curve (dsum*2, dsum*2, cfilt); + + sgain = foveon_camf_matrix (dim, "SpatialGain"); + if (!sgain) return; + sgrow = calloc (dim[1], sizeof *sgrow); + sgx = (width + dim[1]-2) / (dim[1]-1); + + black = calloc (height, sizeof *black); + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + FORC3 black[row][c] = + ( foveon_avg (image[row*width]+c, dscr[0], cfilt) + + foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3 + - ddft[0][c][0] ) / 4 - ddft[0][c][1]; + } + memcpy (black, black+8, sizeof *black*8); + memcpy (black+height-11, black+height-22, 11*sizeof *black); + memcpy (last, black, sizeof last); + + for (row=1; row < height-1; row++) { + FORC3 if (last[1][c] > last[0][c]) { + if (last[1][c] > last[2][c]) + black[row][c] = (last[0][c] > last[2][c]) ? last[0][c]:last[2][c]; + } else + if (last[1][c] < last[2][c]) + black[row][c] = (last[0][c] < last[2][c]) ? last[0][c]:last[2][c]; + memmove (last, last+1, 2*sizeof last[0]); + memcpy (last[2], black[row+1], sizeof last[2]); + } + FORC3 black[row][c] = (last[0][c] + last[1][c])/2; + FORC3 black[0][c] = (black[1][c] + black[3][c])/2; + + val = 1 - exp(-1/24.0); + memcpy (fsum, black, sizeof fsum); + for (row=1; row < height; row++) + FORC3 fsum[c] += black[row][c] = + (black[row][c] - black[row-1][c])*val + black[row-1][c]; + memcpy (last[0], black[height-1], sizeof last[0]); + FORC3 fsum[c] /= height; + for (row = height; row--; ) + FORC3 last[0][c] = black[row][c] = + (black[row][c] - fsum[c] - last[0][c])*val + last[0][c]; + + memset (total, 0, sizeof total); + for (row=2; row < height; row+=4) + for (col=2; col < width; col+=4) { + FORC3 total[c] += (short) image[row*width+col][c]; + total[3]++; + } + for (row=0; row < height; row++) + FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0); + + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + pix = image[row*width]; + memcpy (prev, pix, sizeof prev); + frow = row / (height-1.0) * (dim[2]-1); + if ((irow = frow) == dim[2]-1) irow--; + frow -= irow; + for (i=0; i < dim[1]; i++) + FORC3 sgrow[i][c] = sgain[ irow *dim[1]+i][c] * (1-frow) + + sgain[(irow+1)*dim[1]+i][c] * frow; + for (col=0; col < width; col++) { + FORC3 { + diff = pix[c] - prev[c]; + prev[c] = pix[c]; + ipix[c] = pix[c] + floor ((diff + (diff*diff >> 14)) * cfilt + - ddft[0][c][1] - ddft[0][c][0] * ((float) col/width - 0.5) + - black[row][c] ); + } + FORC3 { + work[0][c] = ipix[c] * ipix[c] >> 14; + work[2][c] = ipix[c] * work[0][c] >> 14; + work[1][2-c] = ipix[(c+1) % 3] * ipix[(c+2) % 3] >> 14; + } + FORC3 { + for (val=i=0; i < 3; i++) + for ( j=0; j < 3; j++) + val += ppm[c][i][j] * work[i][j]; + ipix[c] = floor ((ipix[c] + floor(val)) * + ( sgrow[col/sgx ][c] * (sgx - col%sgx) + + sgrow[col/sgx+1][c] * (col%sgx) ) / sgx / div[c]); + if (ipix[c] > 32000) ipix[c] = 32000; + pix[c] = ipix[c]; + } + pix += 4; + } + } + free (black); + free (sgrow); + free (sgain); + + if ((badpix = foveon_camf_matrix (dim, "BadPixels"))) { + for (i=0; i < dim[0]; i++) { + col = (badpix[i] >> 8 & 0xfff) - keep[0]; + row = (badpix[i] >> 20 ) - keep[1]; + if ((unsigned)(row-1) > height-3 || (unsigned)(col-1) > width-3) + continue; + memset (fsum, 0, sizeof fsum); + for (sum=j=0; j < 8; j++) + if (badpix[i] & (1 << j)) { + FORC3 fsum[c] += (short) + image[(row+hood[j*2])*width+col+hood[j*2+1]][c]; + sum++; + } + if (sum) FORC3 image[row*width+col][c] = fsum[c]/sum; + } + free (badpix); + } + + /* Array for 5x5 Gaussian averaging of red values */ + smrow[6] = calloc (width*5, sizeof **smrow); + merror (smrow[6], "foveon_interpolate()"); + for (i=0; i < 5; i++) + smrow[i] = smrow[6] + i*width; + + /* Sharpen the reds against these Gaussian averages */ + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + smrow[4][col][0] = + (pix[0]*6 + (pix[-4]+pix[4])*4 + pix[-8]+pix[8] + 8) >> 4; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + smred = ( 6 * smrow[2][col][0] + + 4 * (smrow[1][col][0] + smrow[3][col][0]) + + smrow[0][col][0] + smrow[4][col][0] + 8 ) >> 4; + if (col == 2) + smred_p = smred; + i = pix[0] + ((pix[0] - ((smred*7 + smred_p) >> 3)) >> 3); + if (i > 32000) i = 32000; + pix[0] = i; + smred_p = smred; + pix += 4; + } + } + + /* Adjust the brighter pixels for better linearity */ + min = 0xffff; + FORC3 { + i = satlev[c] / div[c]; + if (min > i) min = i; + } + limit = min * 9 >> 4; + for (pix=image[0]; pix < image[height*width]; pix+=4) { + if (pix[0] <= limit || pix[1] <= limit || pix[2] <= limit) + continue; + min = max = pix[0]; + for (c=1; c < 3; c++) { + if (min > pix[c]) min = pix[c]; + if (max < pix[c]) max = pix[c]; + } + if (min >= limit*2) { + pix[0] = pix[1] = pix[2] = max; + } else { + i = 0x4000 - ((min - limit) << 14) / limit; + i = 0x4000 - (i*i >> 14); + i = i*i >> 14; + FORC3 pix[c] += (max - pix[c]) * i >> 14; + } + } +/* + Because photons that miss one detector often hit another, + the sum R+G+B is much less noisy than the individual colors. + So smooth the hues without smoothing the total. + */ + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = (pix[c-4]+2*pix[c]+pix[c+4]+2) >> 2; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + FORC3 dev[c] = -foveon_apply_curve (curve[7], pix[c] - + ((smrow[1][col][c] + 2*smrow[2][col][c] + smrow[3][col][c]) >> 2)); + sum = (dev[0] + dev[1] + dev[2]) >> 3; + FORC3 pix[c] += dev[c] - sum; + pix += 4; + } + } + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = + (pix[c-8]+pix[c-4]+pix[c]+pix[c+4]+pix[c+8]+2) >> 2; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + for (total[3]=375, sum=60, c=0; c < 3; c++) { + for (total[c]=i=0; i < 5; i++) + total[c] += smrow[i][col][c]; + total[3] += total[c]; + sum += pix[c]; + } + if (sum < 0) sum = 0; + j = total[3] > 375 ? (sum << 16) / total[3] : sum * 174; + FORC3 pix[c] += foveon_apply_curve (curve[6], + ((j*total[c] + 0x8000) >> 16) - pix[c]); + pix += 4; + } + } + + /* Transform the image to a different colorspace */ + for (pix=image[0]; pix < image[height*width]; pix+=4) { + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]); + sum = (pix[0]+pix[1]+pix[1]+pix[2]) >> 2; + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]-sum); + FORC3 { + for (dsum=i=0; i < 3; i++) + dsum += trans[c][i] * pix[i]; + if (dsum < 0) dsum = 0; + if (dsum > 24000) dsum = 24000; + ipix[c] = dsum + 0.5; + } + FORC3 pix[c] = ipix[c]; + } + + /* Smooth the image bottom-to-top and save at 1/4 scale */ + shrink = calloc ((width/4) * (height/4), sizeof *shrink); + merror (shrink, "foveon_interpolate()"); + for (row = height/4; row--; ) + for (col=0; col < width/4; col++) { + ipix[0] = ipix[1] = ipix[2] = 0; + for (i=0; i < 4; i++) + for (j=0; j < 4; j++) + FORC3 ipix[c] += image[(row*4+i)*width+col*4+j][c]; + FORC3 + if (row+2 > height/4) + shrink[row*(width/4)+col][c] = ipix[c] >> 4; + else + shrink[row*(width/4)+col][c] = + (shrink[(row+1)*(width/4)+col][c]*1840 + ipix[c]*141 + 2048) >> 12; + } + /* From the 1/4-scale image, smooth right-to-left */ + for (row=0; row < (height & ~3); row++) { + ipix[0] = ipix[1] = ipix[2] = 0; + if ((row & 3) == 0) + for (col = width & ~3 ; col--; ) + FORC3 smrow[0][col][c] = ipix[c] = + (shrink[(row/4)*(width/4)+col/4][c]*1485 + ipix[c]*6707 + 4096) >> 13; + + /* Then smooth left-to-right */ + ipix[0] = ipix[1] = ipix[2] = 0; + for (col=0; col < (width & ~3); col++) + FORC3 smrow[1][col][c] = ipix[c] = + (smrow[0][col][c]*1485 + ipix[c]*6707 + 4096) >> 13; + + /* Smooth top-to-bottom */ + if (row == 0) + memcpy (smrow[2], smrow[1], sizeof **smrow * width); + else + for (col=0; col < (width & ~3); col++) + FORC3 smrow[2][col][c] = + (smrow[2][col][c]*6707 + smrow[1][col][c]*1485 + 4096) >> 13; + + /* Adjust the chroma toward the smooth values */ + for (col=0; col < (width & ~3); col++) { + for (i=j=30, c=0; c < 3; c++) { + i += smrow[2][col][c]; + j += image[row*width+col][c]; + } + j = (j << 16) / i; + for (sum=c=0; c < 3; c++) { + ipix[c] = foveon_apply_curve (curve[c+3], + ((smrow[2][col][c] * j + 0x8000) >> 16) - image[row*width+col][c]); + sum += ipix[c]; + } + sum >>= 3; + FORC3 { + i = image[row*width+col][c] + ipix[c] - sum; + if (i < 0) i = 0; + image[row*width+col][c] = i; + } + } + } + free (shrink); + free (smrow[6]); + for (i=0; i < 8; i++) + free (curve[i]); + + /* Trim off the black border */ + active[1] -= keep[1]; + active[3] -= 2; + i = active[2] - active[0]; + for (row = 0; row < active[3]-active[1]; row++) + memcpy (image[row*i], image[(row+active[1])*width+active[0]], + i * sizeof *image); + width = i; + height = row; +} +#undef image + +/* END GPL BLOCK */ + +/* + Seach from the current directory up to the root looking for + a ".badpixels" file, and fix those pixels now. + */ +void CLASS bad_pixels() +{ + FILE *fp=NULL; + char *fname, *cp, line[128]; + int len, time, row, col, r, c, rad, tot, n, fixed=0; + + if (!filters) return; + for (len=16 ; ; len *= 2) { + fname = malloc (len); + if (!fname) return; + if (getcwd (fname, len-12)) break; + free (fname); + if (errno != ERANGE) return; + } +#ifdef WIN32 + if (fname[1] == ':') + memmove (fname, fname+2, len-2); + for (cp=fname; *cp; cp++) + if (*cp == '\\') *cp = '/'; +#endif + cp = fname + strlen(fname); + if (cp[-1] == '/') cp--; + while (*fname == '/') { + strcpy (cp, "/.badpixels"); + if ((fp = fopen (fname, "r"))) break; + if (cp == fname) break; + while (*--cp != '/'); + } + free (fname); + if (!fp) return; + while (fgets (line, 128, fp)) { + cp = strchr (line, '#'); + if (cp) *cp = 0; + if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue; + if ((unsigned) col >= width || (unsigned) row >= height) continue; + if (time > timestamp) continue; + for (tot=n=0, rad=1; rad < 3 && n==0; rad++) + for (r = row-rad; r <= row+rad; r++) + for (c = col-rad; c <= col+rad; c++) + if ((unsigned) r < height && (unsigned) c < width && + (r != row || c != col) && FC(r,c) == FC(row,col)) { + tot += BAYER(r,c); + n++; + } + BAYER(row,col) = tot/n; + if (verbose) { + if (!fixed++) + fprintf (stderr, "Fixed bad pixels at:"); + fprintf (stderr, " %d,%d", col, row); + } + } + if (fixed) fputc ('\n', stderr); + fclose (fp); +} + +void CLASS pseudoinverse (const double (*in)[3], double (*out)[3], int size) +{ + double work[3][6], num; + int i, j, k; + + for (i=0; i < 3; i++) { + for (j=0; j < 6; j++) + work[i][j] = j == i+3; + for (j=0; j < 3; j++) + for (k=0; k < size; k++) + work[i][j] += in[k][i] * in[k][j]; + } + for (i=0; i < 3; i++) { + num = work[i][i]; + for (j=0; j < 6; j++) + work[i][j] /= num; + for (k=0; k < 3; k++) { + if (k==i) continue; + num = work[k][i]; + for (j=0; j < 6; j++) + work[k][j] -= work[i][j] * num; + } + } + for (i=0; i < size; i++) + for (j=0; j < 3; j++) + for (out[i][j]=k=0; k < 3; k++) + out[i][j] += work[j][k+3] * in[i][k]; +} + +void CLASS cam_xyz_coeff (double cam_xyz[4][3]) +{ + double cam_rgb[4][3], inverse[4][3], num; + int i, j, k; + + for (i=0; i < colors; i++) /* Multiply out XYZ colorspace */ + for (j=0; j < 3; j++) + for (cam_rgb[i][j] = k=0; k < 3; k++) + cam_rgb[i][j] += cam_xyz[i][k] * xyz_rgb[k][j]; + + for (i=0; i < colors; i++) { /* Normalize cam_rgb so that */ + for (num=j=0; j < 3; j++) /* cam_rgb * (1,1,1) is (1,1,1,1) */ + num += cam_rgb[i][j]; + for (j=0; j < 3; j++) + cam_rgb[i][j] /= num; + pre_mul[i] = 1 / num; + } + pseudoinverse ((const double (*)[3]) cam_rgb, inverse, colors); + for (raw_color = i=0; i < 3; i++) + for (j=0; j < colors; j++) + rgb_cam[i][j] = inverse[j][i]; +} + +#ifdef COLORCHECK +void CLASS colorcheck() +{ +#define NSQ 24 +// Coordinates of the GretagMacbeth ColorChecker squares +// width, height, 1st_column, 1st_row + static const int cut[NSQ][4] = { + { 241, 231, 234, 274 }, + { 251, 235, 534, 274 }, + { 255, 239, 838, 272 }, + { 255, 240, 1146, 274 }, + { 251, 237, 1452, 278 }, + { 243, 238, 1758, 288 }, + { 253, 253, 218, 558 }, + { 255, 249, 524, 562 }, + { 261, 253, 830, 562 }, + { 260, 255, 1144, 564 }, + { 261, 255, 1450, 566 }, + { 247, 247, 1764, 576 }, + { 255, 251, 212, 862 }, + { 259, 259, 518, 862 }, + { 263, 261, 826, 864 }, + { 265, 263, 1138, 866 }, + { 265, 257, 1450, 872 }, + { 257, 255, 1762, 874 }, + { 257, 253, 212, 1164 }, + { 262, 251, 516, 1172 }, + { 263, 257, 826, 1172 }, + { 263, 255, 1136, 1176 }, + { 255, 252, 1452, 1182 }, + { 257, 253, 1760, 1180 } }; +// ColorChecker Chart under 6500-kelvin illumination + static const double gmb_xyz[NSQ][3] = { + { 11.078, 9.870, 6.738 }, // Dark Skin + { 37.471, 35.004, 26.057 }, // Light Skin + { 18.187, 19.306, 35.425 }, // Blue Sky + { 10.825, 13.827, 7.600 }, // Foliage + { 24.769, 23.304, 43.943 }, // Blue Flower + { 31.174, 42.684, 45.277 }, // Bluish Green + { 36.238, 29.188, 6.222 }, // Orange + { 13.661, 11.845, 38.929 }, // Purplish Blue + { 27.999, 19.272, 14.265 }, // Moderate Red + { 8.398, 6.309, 14.211 }, // Purple + { 33.692, 44.346, 11.288 }, // Yellow Green + { 45.000, 42.144, 8.429 }, // Orange Yellow + { 8.721, 6.130, 31.181 }, // Blue + { 14.743, 24.049, 9.778 }, // Green + { 19.777, 11.530, 5.101 }, // Red + { 55.978, 59.599, 10.047 }, // Yellow + { 29.421, 19.271, 31.167 }, // Magenta + { 13.972, 18.952, 37.646 }, // Cyan + { 82.819, 87.727, 94.479 }, // White + { 55.950, 58.959, 64.375 }, // Neutral 8 + { 32.877, 34.536, 38.097 }, // Neutral 6.5 + { 18.556, 19.701, 21.487 }, // Neutral 5 + { 8.353, 8.849, 9.812 }, // Neutral 3.5 + { 2.841, 2.980, 3.332 } }; // Black + double inverse[NSQ][3], gmb_cam[NSQ][4], cam_xyz[4][3]; + double num, error, minerr=DBL_MAX, best[4][3]; + int b, c, i, j, k, sq, row, col, count[4]; + + memset (gmb_cam, 0, sizeof gmb_cam); + for (sq=0; sq < NSQ; sq++) { + FORCC count[c] = 0; + for (row=cut[sq][3]; row < cut[sq][3]+cut[sq][1]; row++) + for (col=cut[sq][2]; col < cut[sq][2]+cut[sq][0]; col++) { + c = FC(row,col); + if (c >= colors) c -= 2; + gmb_cam[sq][c] += BAYER(row,col); + count[c]++; + } + FORCC gmb_cam[sq][c] /= count[c]; + } + for (b=0; b < 2000; b++) { + pseudoinverse (gmb_xyz, inverse, NSQ); + for (i=0; i < colors; i++) + for (j=0; j < 3; j++) + for (cam_xyz[i][j] = k=0; k < NSQ; k++) + cam_xyz[i][j] += gmb_cam[k][i] * inverse[k][j]; + + for (error=sq=0; sq < NSQ; sq++) + FORCC { + for (num=j=0; j < 3; j++) + num += cam_xyz[c][j] * gmb_xyz[sq][j]; + if (num < 0) num=0; + error += pow (num - gmb_cam[sq][c], 2); + gmb_cam[sq][c]--; // for the next black value + } + if (error < minerr) { + black = b; + minerr = error; + memcpy (best, cam_xyz, sizeof best); + } + } + cam_xyz_coeff (best); + if (verbose) { + fprintf (stderr, " { \"%s %s\",\n\t{ ", make, model); + num = 10000 / (best[1][0] + best[1][1] + best[1][2]); + FORCC for (j=0; j < 3; j++) + fprintf (stderr, "%d,", (int) (best[c][j] * num + 0.5)); + fprintf (stderr, "\b } },\n"); + } +#undef NSQ +} +#endif + +void CLASS scale_colors() +{ + int row, col, c, val, shift=0; + int min[4], max[4], count[4]; + double sum[4], dmin; + + maximum -= black; + if (use_auto_wb || (use_camera_wb && camera_red == -1)) { + FORC4 min[c] = INT_MAX; + FORC4 max[c] = count[c] = sum[c] = 0; + for (row=0; row < height; row++) + for (col=0; col < width; col++) + FORC4 { + val = image[row*width+col][c]; + if (!val) continue; + if (min[c] > val) min[c] = val; + if (max[c] < val) max[c] = val; + val -= black; + if (val > maximum-25) continue; + if (val < 0) val = 0; + sum[c] += val; + count[c]++; + } + FORC4 if (sum[c]) pre_mul[c] = count[c] / sum[c]; + } + if (use_camera_wb && camera_red != -1) { + FORC4 count[c] = sum[c] = 0; + for (row=0; row < 8; row++) + for (col=0; col < 8; col++) { + c = FC(row,col); + if ((val = white[row][col] - black) > 0) + sum[c] += val; + count[c]++; + } + if (sum[0] && sum[1] && sum[2] && sum[3]) + FORC4 pre_mul[c] = count[c] / sum[c]; + else if (camera_red && camera_blue) + memcpy (pre_mul, cam_mul, sizeof pre_mul); + else + fprintf (stderr, "%s: Cannot use camera white balance.\n", ifname); + } + if (raw_color) { + pre_mul[0] *= red_scale; + pre_mul[2] *= blue_scale; + } + if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1; + dmin = DBL_MAX; + FORC4 if (dmin > pre_mul[c]) + dmin = pre_mul[c]; + FORC4 pre_mul[c] /= dmin; + + while (maximum << shift < 0x8000) shift++; + FORC4 pre_mul[c] *= 1 << shift; + maximum <<= shift; + + if (write_fun != write_ppm || bright < 1) { + maximum *= bright; + if (maximum > 0xffff) + maximum = 0xffff; + FORC4 pre_mul[c] *= bright; + } + if (verbose) { + fprintf (stderr, "Scaling with black=%d, pre_mul[] =", black); + FORC4 fprintf (stderr, " %f", pre_mul[c]); + fputc ('\n', stderr); + } + clip_max = clip_color ? maximum : 0xffff; + for (row=0; row < height; row++) + for (col=0; col < width; col++) + FORC4 { + val = image[row*width+col][c]; + if (!val) continue; + val -= black; + val *= pre_mul[c]; + image[row*width+col][c] = CLIP(val); + } + if (filters && colors == 3) { + if (four_color_rgb) { + colors++; + FORC3 rgb_cam[c][3] = rgb_cam[c][1] /= 2; + } else { + for (row = FC(1,0) >> 1; row < height; row+=2) + for (col = FC(row,1) & 1; col < width; col+=2) + image[row*width+col][1] = image[row*width+col][3]; + filters &= ~((filters & 0x55555555) << 1); + } + } +} + +void CLASS border_interpolate (int border) +{ + unsigned row, col, y, x, c, sum[8]; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + if (col==border && row >= border && row < height-border) + col = width-border; + memset (sum, 0, sizeof sum); + for (y=row-1; y != row+2; y++) + for (x=col-1; x != col+2; x++) + if (y < height && x < width) { + sum[FC(y,x)] += BAYER(y,x); + sum[FC(y,x)+4]++; + } + FORCC if (c != FC(row,col)) + image[row*width+col][c] = sum[c] / sum[c+4]; + } +} + +void CLASS lin_interpolate() +{ + int code[8][2][32], *ip, sum[4]; + int c, i, x, y, row, col, shift, color; + ushort *pix; + + if (verbose) fprintf (stderr, "Bilinear interpolation...\n"); + + border_interpolate(1); + for (row=0; row < 8; row++) + for (col=0; col < 2; col++) { + ip = code[row][col]; + memset (sum, 0, sizeof sum); + for (y=-1; y <= 1; y++) + for (x=-1; x <= 1; x++) { + shift = (y==0) + (x==0); + if (shift == 2) continue; + color = FC(row+y,col+x); + *ip++ = (width*y + x)*4 + color; + *ip++ = shift; + *ip++ = color; + sum[color] += 1 << shift; + } + FORCC + if (c != FC(row,col)) { + *ip++ = c; + *ip++ = sum[c]; + } + } + for (row=1; row < height-1; row++) + for (col=1; col < width-1; col++) { + pix = image[row*width+col]; + ip = code[row & 7][col & 1]; + memset (sum, 0, sizeof sum); + for (i=8; i--; ip+=3) + sum[ip[2]] += pix[ip[0]] << ip[1]; + for (i=colors; --i; ip+=2) + pix[ip[0]] = sum[ip[0]] / ip[1]; + } +} + +/* + This algorithm is officially called: + + "Interpolation using a Threshold-based variable number of gradients" + + described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html + + I've extended the basic idea to work with non-Bayer filter arrays. + Gradients are numbered clockwise from NW=0 to W=7. + */ +void CLASS vng_interpolate() +{ + static const signed char *cp, terms[] = { + -2,-2,+0,-1,0,0x01, -2,-2,+0,+0,1,0x01, -2,-1,-1,+0,0,0x01, + -2,-1,+0,-1,0,0x02, -2,-1,+0,+0,0,0x03, -2,-1,+0,+1,1,0x01, + -2,+0,+0,-1,0,0x06, -2,+0,+0,+0,1,0x02, -2,+0,+0,+1,0,0x03, + -2,+1,-1,+0,0,0x04, -2,+1,+0,-1,1,0x04, -2,+1,+0,+0,0,0x06, + -2,+1,+0,+1,0,0x02, -2,+2,+0,+0,1,0x04, -2,+2,+0,+1,0,0x04, + -1,-2,-1,+0,0,0x80, -1,-2,+0,-1,0,0x01, -1,-2,+1,-1,0,0x01, + -1,-2,+1,+0,1,0x01, -1,-1,-1,+1,0,0x88, -1,-1,+1,-2,0,0x40, + -1,-1,+1,-1,0,0x22, -1,-1,+1,+0,0,0x33, -1,-1,+1,+1,1,0x11, + -1,+0,-1,+2,0,0x08, -1,+0,+0,-1,0,0x44, -1,+0,+0,+1,0,0x11, + -1,+0,+1,-2,1,0x40, -1,+0,+1,-1,0,0x66, -1,+0,+1,+0,1,0x22, + -1,+0,+1,+1,0,0x33, -1,+0,+1,+2,1,0x10, -1,+1,+1,-1,1,0x44, + -1,+1,+1,+0,0,0x66, -1,+1,+1,+1,0,0x22, -1,+1,+1,+2,0,0x10, + -1,+2,+0,+1,0,0x04, -1,+2,+1,+0,1,0x04, -1,+2,+1,+1,0,0x04, + +0,-2,+0,+0,1,0x80, +0,-1,+0,+1,1,0x88, +0,-1,+1,-2,0,0x40, + +0,-1,+1,+0,0,0x11, +0,-1,+2,-2,0,0x40, +0,-1,+2,-1,0,0x20, + +0,-1,+2,+0,0,0x30, +0,-1,+2,+1,1,0x10, +0,+0,+0,+2,1,0x08, + +0,+0,+2,-2,1,0x40, +0,+0,+2,-1,0,0x60, +0,+0,+2,+0,1,0x20, + +0,+0,+2,+1,0,0x30, +0,+0,+2,+2,1,0x10, +0,+1,+1,+0,0,0x44, + +0,+1,+1,+2,0,0x10, +0,+1,+2,-1,1,0x40, +0,+1,+2,+0,0,0x60, + +0,+1,+2,+1,0,0x20, +0,+1,+2,+2,0,0x10, +1,-2,+1,+0,0,0x80, + +1,-1,+1,+1,0,0x88, +1,+0,+1,+2,0,0x08, +1,+0,+2,-1,0,0x40, + +1,+0,+2,+1,0,0x10 + }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 }; + ushort (*brow[5])[4], *pix; + int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; + int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + + lin_interpolate(); + if (verbose) fprintf (stderr, "VNG interpolation...\n"); + + for (row=0; row < 8; row++) { /* Precalculate for VNG */ + for (col=0; col < 2; col++) { + ip = code[row][col]; + for (cp=terms, t=0; t < 64; t++) { + y1 = *cp++; x1 = *cp++; + y2 = *cp++; x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = FC(row+y1,col+x1); + if (FC(row+y2,col+x2) != color) continue; + diag = (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1; + if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue; + *ip++ = (y1*width + x1)*4 + color; + *ip++ = (y2*width + x2)*4 + color; + *ip++ = weight; + for (g=0; g < 8; g++) + if (grads & 1<<g) *ip++ = g; + *ip++ = -1; + } + *ip++ = INT_MAX; + for (cp=chood, g=0; g < 8; g++) { + y = *cp++; x = *cp++; + *ip++ = (y*width + x) * 4; + color = FC(row,col); + if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color) + *ip++ = (y*width + x) * 8 + color; + else + *ip++ = 0; + } + } + } + brow[4] = calloc (width*3, sizeof **brow); + merror (brow[4], "vng_interpolate()"); + for (row=0; row < 3; row++) + brow[row] = brow[4] + row*width; + for (row=2; row < height-2; row++) { /* Do VNG interpolation */ + for (col=2; col < width-2; col++) { + pix = image[row*width+col]; + ip = code[row & 7][col & 1]; + memset (gval, 0, sizeof gval); + while ((g = ip[0]) != INT_MAX) { /* Calculate gradients */ + diff = ABS(pix[g] - pix[ip[1]]) << ip[2]; + gval[ip[3]] += diff; + ip += 5; + if ((g = ip[-1]) == -1) continue; + gval[g] += diff; + while ((g = *ip++) != -1) + gval[g] += diff; + } + ip++; + gmin = gmax = gval[0]; /* Choose a threshold */ + for (g=1; g < 8; g++) { + if (gmin > gval[g]) gmin = gval[g]; + if (gmax < gval[g]) gmax = gval[g]; + } + if (gmax == 0) { + memcpy (brow[2][col], pix, sizeof *image); + continue; + } + thold = gmin + (gmax >> 1); + memset (sum, 0, sizeof sum); + color = FC(row,col); + for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */ + if (gval[g] <= thold) { + FORCC + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + FORCC { /* Save to buffer */ + t = pix[color]; + if (c != color) + t += (sum[c] - sum[color]) / num; + brow[2][col][c] = CLIP(t); + } + } + if (row > 3) /* Write buffer to image */ + memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); + for (g=0; g < 4; g++) + brow[(g-1) & 3] = brow[g]; + } + memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); + memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image); + free (brow[4]); +} + +void CLASS cam_to_cielab (ushort cam[4], float lab[3]) +{ + int c, i, j, k; + float r, xyz[3]; + static const float d65[3] = { 0.950456, 1, 1.088754 }; + static float cbrt[0x10000], xyz_cam[3][4]; + + if (cam == NULL) { + for (i=0; i < 0x10000; i++) { + r = (float) i / maximum; + cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0; + } + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (xyz_cam[i][j] = k=0; k < 3; k++) + xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65[i]; + } else { + for (i=0; i < 3; i++) { + for (xyz[i]=0.5, c=0; c < colors; c++) + xyz[i] += xyz_cam[i][c] * cam[c]; + xyz[i] = cbrt[CLIP((int) xyz[i])]; + } + lab[0] = 116 * xyz[1] - 16; + lab[1] = 500 * (xyz[0] - xyz[1]); + lab[2] = 200 * (xyz[1] - xyz[2]); + } +} + +/* + Adaptive Homogeneity-Directed interpolation is based on + the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. + */ +#define TS 256 /* Tile Size */ + +void CLASS ahd_interpolate() +{ + int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; + ushort (*pix)[4], (*rix)[3]; + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float flab[3]; + ushort (*rgb)[TS][TS][3]; + short (*lab)[TS][TS][3]; + char (*homo)[TS][TS], *buffer; + + if (verbose) fprintf (stderr, "AHD interpolation...\n"); + + border_interpolate(3); + buffer = malloc (26*TS*TS); /* 1664 kB */ + merror (buffer, "ahd_interpolate()"); + rgb = (void *) buffer; + lab = (void *) (buffer + 12*TS*TS); + homo = (void *) (buffer + 24*TS*TS); + + for (top=0; top < height; top += TS-6) + for (left=0; left < width; left += TS-6) { + memset (rgb, 0, 12*TS*TS); + +/* Interpolate green horizontally and vertically: */ + for (row = top < 2 ? 2:top; row < top+TS && row < height-2; row++) { + col = left + (FC(row,left) == 1); + if (col < 2) col += 2; + for (fc = FC(row,col); col < left+TS && col < width-2; col+=2) { + pix = image + row*width+col; + val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 + - pix[-2][fc] - pix[2][fc]) >> 2; + rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]); + val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 + - pix[-2*width][fc] - pix[2*width][fc]) >> 2; + rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]); + } + } +/* Interpolate red and blue, and convert to CIELab: */ + for (d=0; d < 2; d++) + for (row=top+1; row < top+TS-1 && row < height-1; row++) + for (col=left+1; col < left+TS-1 && col < width-1; col++) { + pix = image + row*width+col; + rix = &rgb[d][row-top][col-left]; + if ((c = 2 - FC(row,col)) == 1) { + c = FC(row+1,col); + val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c] + - rix[-1][1] - rix[1][1] ) >> 1); + rix[0][2-c] = CLIP(val); + val = pix[0][1] + (( pix[-width][c] + pix[width][c] + - rix[-TS][1] - rix[TS][1] ) >> 1); + } else + val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c] + + pix[+width-1][c] + pix[+width+1][c] + - rix[-TS-1][1] - rix[-TS+1][1] + - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2); + rix[0][c] = CLIP(val); + c = FC(row,col); + rix[0][c] = pix[0][c]; + cam_to_cielab (rix[0], flab); + FORC3 lab[d][row-top][col-left][c] = 64*flab[c]; + } +/* Build homogeneity maps from the CIELab images: */ + memset (homo, 0, 2*TS*TS); + for (row=top+2; row < top+TS-2 && row < height; row++) { + tr = row-top; + for (col=left+2; col < left+TS-2 && col < width; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + ldiff[d][i] = ABS(lab[d][tr][tc][0]-lab[d][tr][tc+dir[i]][0]); + leps = MIN(MAX(ldiff[0][0],ldiff[0][1]), + MAX(ldiff[1][2],ldiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (i >> 1 == d || ldiff[d][i] <= leps) + abdiff[d][i] = SQR(lab[d][tr][tc][1]-lab[d][tr][tc+dir[i]][1]) + + SQR(lab[d][tr][tc][2]-lab[d][tr][tc+dir[i]][2]); + abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]), + MAX(abdiff[1][2],abdiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } +/* Combine the most homogenous pixels for the final result: */ + for (row=top+3; row < top+TS-3 && row < height-3; row++) { + tr = row-top; + for (col=left+3; col < left+TS-3 && col < width-3; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (hm[d]=0, i=tr-1; i <= tr+1; i++) + for (j=tc-1; j <= tc+1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c]; + else + FORC3 image[row*width+col][c] = + (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1; + } + } + } + free (buffer); +} +#undef TS + +/* + Bilateral Filtering was developed by C. Tomasi and R. Manduchi. + */ +void CLASS bilateral_filter() +{ + float (**window)[7], *kernel, scale_r, elut[1024], sum[5]; + int c, i, wr, ws, wlast, row, col, y, x; + unsigned sep; + + if (verbose) fprintf (stderr, "Bilateral filtering...\n"); + + wr = ceil(sigma_d*2); /* window radius */ + ws = 2*wr + 1; /* window size */ + window = calloc ((ws+1)*sizeof *window + + ws*width*sizeof **window + ws*sizeof *kernel, 1); + merror (window, "bilateral_filter()"); + for (i=0; i <= ws; i++) + window[i] = (float(*)[7]) (window+ws+1) + i*width; + kernel = (float *) window[ws] + wr; + for (i=-wr; i <= wr; i++) + kernel[i] = 256 / (2*SQR(sigma_d)) * i*i + 0.25; + scale_r = 256 / (2*SQR(sigma_r)); + for (i=0; i < 1024; i++) + elut[i] = exp (-i/256.0); + + for (wlast=-1, row=0; row < height; row++) { + while (wlast < row+wr) { + wlast++; + for (i=0; i <= ws; i++) /* rotate window rows */ + window[(ws+i) % (ws+1)] = window[i]; + if (wlast < height) + for (col=0; col < width; col++) { + FORCC window[ws-1][col][c] = image[wlast*width+col][c]; + cam_to_cielab (image[wlast*width+col], window[ws-1][col]+4); + } + } + for (col=0; col < width; col++) { + memset (sum, 0, sizeof sum); + for (y=-wr; y <= wr; y++) + if ((unsigned)(row+y) < height) + for (x=-wr; x <= wr; x++) + if ((unsigned)(col+x) < width) { + sep = ( SQR(window[wr+y][col+x][4] - window[wr][col][4]) + + SQR(window[wr+y][col+x][5] - window[wr][col][5]) + + SQR(window[wr+y][col+x][6] - window[wr][col][6]) ) + * scale_r + kernel[y] + kernel[x]; + if (sep < 1024) { + FORCC sum[c] += elut[sep] * window[wr+y][col+x][c]; + sum[4] += elut[sep]; + } + } + FORCC image[row*width+col][c] = sum[c]/sum[4]; + } + } + free (window); +} + +void CLASS parse_makernote() +{ + static const uchar xlat[2][256] = { + { 0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d, + 0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d, + 0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f, + 0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f, + 0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1, + 0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17, + 0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89, + 0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f, + 0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b, + 0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb, + 0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3, + 0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f, + 0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35, + 0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43, + 0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5, + 0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 }, + { 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c, + 0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34, + 0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad, + 0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05, + 0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee, + 0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d, + 0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b, + 0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b, + 0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc, + 0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33, + 0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8, + 0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6, + 0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c, + 0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49, + 0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb, + 0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f } }; + unsigned base=0, offset=0, entries, tag, type, len, save, c; + unsigned ver97=0, serial=0, i; + uchar buf97[324], ci, cj, ck; + static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 }; + short sorder; + char buf[10]; +/* + The MakerNote might have its own TIFF header (possibly with + its own byte-order!), or it might just be a table. + */ + sorder = order; + fread (buf, 1, 10, ifp); + if (!strncmp (buf,"KC" ,2) || /* these aren't TIFF format */ + !strncmp (buf,"MLY",3)) return; + if (!strcmp (buf,"Nikon")) { + base = ftell(ifp); + order = get2(); + if (get2() != 42) goto quit; + offset = get4(); + fseek (ifp, offset-8, SEEK_CUR); + } else if (!strncmp (buf,"FUJIFILM",8) || + !strcmp (buf,"Panasonic")) { + order = 0x4949; + fseek (ifp, 2, SEEK_CUR); + } else if (!strcmp (buf,"OLYMP") || + !strcmp (buf,"LEICA") || + !strcmp (buf,"EPSON")) + fseek (ifp, -2, SEEK_CUR); + else if (!strcmp (buf,"AOC") || + !strcmp (buf,"QVC")) + fseek (ifp, -4, SEEK_CUR); + else fseek (ifp, -10, SEEK_CUR); + + entries = get2(); + while (entries--) { + tag = get2(); + type = get2(); + len = get4(); + save = ftell(ifp); + if (len * size[type < 13 ? type:0] > 4) + fseek (ifp, get4()+base, SEEK_SET); + + if (tag == 8 && type == 4) + shot_order = get4(); + if (tag == 0xc && len == 4) { + camera_red = getrat(); + camera_blue = getrat(); + } + if (tag == 0x14 && len == 2560 && type == 7) { + fseek (ifp, 1248, SEEK_CUR); + goto get2_256; + } + if (strstr(make,"PENTAX")) { + if (tag == 0x1b) tag = 0x1018; + if (tag == 0x1c) tag = 0x1017; + } + if (tag == 0x1d) + while ((c = fgetc(ifp))) + serial = serial*10 + (isdigit(c) ? c - '0' : c % 10); + if (tag == 0x8c) + nikon_curve_offset = ftell(ifp) + 2112; + if (tag == 0x96) + nikon_curve_offset = ftell(ifp) + 2; + if (tag == 0x97) { + for (i=0; i < 4; i++) + ver97 = (ver97 << 4) + fgetc(ifp)-'0'; + switch (ver97) { + case 0x100: + fseek (ifp, 68, SEEK_CUR); + FORC4 cam_mul[(c >> 1) | ((c & 1) << 1)] = get2(); + break; + case 0x102: + fseek (ifp, 6, SEEK_CUR); + goto get2_rggb; + case 0x103: + fseek (ifp, 16, SEEK_CUR); + FORC4 cam_mul[c] = get2(); + } + if (ver97 >> 8 == 2) { + if (ver97 != 0x205) fseek (ifp, 280, SEEK_CUR); + fread (buf97, 324, 1, ifp); + } + } + if (tag == 0xa7 && ver97 >> 8 == 2) { + ci = xlat[0][serial & 0xff]; + cj = xlat[1][fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp)]; + ck = 0x60; + for (i=0; i < 324; i++) + buf97[i] ^= (cj += ci * ck++); + FORC4 cam_mul[c ^ (c >> 1)] = + sget2 (buf97 + (ver97 == 0x205 ? 14:6) + c*2); + } + if (tag == 0xe0 && len == 17) { + get2(); + raw_width = get2(); + raw_height = get2(); + } + if (tag == 0x200 && len == 4) + black = (get2()+get2()+get2()+get2())/4; + if (tag == 0x201 && len == 4) + goto get2_rggb; + if (tag == 0x401 && len == 4) { + black = (get4()+get4()+get4()+get4())/4; + } + if (tag == 0xe01) { /* Nikon Capture Note */ + type = order; + order = 0x4949; + fseek (ifp, 22, SEEK_CUR); + for (offset=22; offset+22 < len; offset += 22+i) { + tag = get4(); + fseek (ifp, 14, SEEK_CUR); + i = get4()-4; + if (tag == 0x76a43207) flip = get2(); + else fseek (ifp, i, SEEK_CUR); + } + order = type; + } + if (tag == 0xe80 && len == 256 && type == 7) { + fseek (ifp, 48, SEEK_CUR); + camera_red = get2() * 508 * 1.078 / 0x10000; + camera_blue = get2() * 382 * 1.173 / 0x10000; + } + if (tag == 0xf00 && len == 614 && type == 7) { + fseek (ifp, 188, SEEK_CUR); + goto get2_256; + } + if (tag == 0x1017) + camera_red = get2() / 256.0; + if (tag == 0x1018) + camera_blue = get2() / 256.0; + if (tag == 0x2011 && len == 2) { +get2_256: + order = 0x4d4d; + camera_red = get2() / 256.0; + camera_blue = get2() / 256.0; + } + if (tag == 0x4001) { + fseek (ifp, strstr(model,"EOS-1D") ? 68 : + strstr(model,"EOS 5D") ? 126 : 50, SEEK_CUR); +get2_rggb: + FORC4 cam_mul[c ^ (c >> 1)] = get2(); + } + fseek (ifp, save+4, SEEK_SET); + } +quit: + order = sorder; +} + +/* + Since the TIFF DateTime string has no timezone information, + assume that the camera's clock was set to Universal Time. + */ +void CLASS get_timestamp (int reversed) +{ + struct tm t; + char str[20]; + int i; + + if (timestamp) return; + str[19] = 0; + if (reversed) + for (i=19; i--; ) str[i] = fgetc(ifp); + else + fread (str, 19, 1, ifp); + if (sscanf (str, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != 6) + return; + t.tm_year -= 1900; + t.tm_mon -= 1; + if (mktime(&t) > 0) + timestamp = mktime(&t); +} + +void CLASS parse_exif (int base) +{ + int entries, tag, type, len, val, save; + + entries = get2(); + while (entries--) { + tag = get2(); + type = get2(); + len = get4(); + val = get4(); + save = ftell(ifp); + fseek (ifp, base+val, SEEK_SET); + if (tag == 0x9003 || tag == 0x9004) + get_timestamp(0); + if (tag == 0x927c) + parse_makernote(); + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS parse_mos (int offset); +void CLASS sony_decrypt (unsigned *data, int len, int start, int key); + +int CLASS parse_tiff_ifd (int base, int level) +{ + unsigned entries, tag, type, len, plen=16, save; + int done=0, use_cm=0, cfa, i, j, c; + static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 }; + char software[64], *cbuf, *cp; + static const int flip_map[] = { 0,1,3,2,4,6,7,5 }; + uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256]; + double dblack, cc[4][4], cm[4][3], cam_xyz[4][3]; + double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 }; + unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; + FILE *sfp; + + for (j=0; j < 4; j++) + for (i=0; i < 4; i++) + cc[j][i] = i == j; + entries = get2(); + if (entries > 512) return 1; + while (entries--) { + tag = get2(); + type = get2(); + len = get4(); + save = ftell(ifp); + if (tag > 50700 && tag < 50800) done = 1; + if (len * size[type < 13 ? type:0] > 4) + fseek (ifp, get4()+base, SEEK_SET); + switch (tag) { + case 0x11: + case 0x12: + if (type == 3 && len == 1) + cam_mul[(tag-0x11)*2] = get2() / 256.0; + break; + case 0x27: + if (len < 50) break; + fseek (ifp, 12, SEEK_CUR); + FORC3 cam_mul[c] = get2(); + break; + case 0x2: + case 0x100: /* ImageWidth */ + if ((strcmp(make,"Canon") || level) && len == 1) + raw_width = type==3 ? get2() : get4(); + break; + case 0x3: + case 0x101: /* ImageHeight */ + if ((strcmp(make,"Canon") || level) && len == 1) + raw_height = type==3 ? get2() : get4(); + break; + case 0x102: /* Bits per sample */ + fuji_secondary = len == 2; + maximum = (1 << (tiff_bps = get2())) - 1; + break; + case 0x103: /* Compression */ + tiff_data_compression = get2(); + break; + case 0x106: /* Kodak color format */ + kodak_data_compression = get2(); + break; + case 0x10f: /* Make */ + fgets (make, 64, ifp); + break; + case 0x110: /* Model */ + fgets (model, 64, ifp); + break; + case 0x111: /* StripOffset */ + data_offset = get4(); + break; + case 0x112: /* Orientation */ + flip = flip_map[(get2()-1) & 7]; + break; + case 0x115: /* SamplesPerPixel */ + tiff_samples = get2(); + break; + case 0x131: /* Software tag */ + fgets (software, 64, ifp); + if (!strncmp(software,"Adobe",5) || + !strncmp(software,"Bibble",6) || + !strcmp (software,"Digital Photo Professional")) + make[0] = 0; + break; + case 0x132: /* DateTime tag */ + get_timestamp(0); + break; + case 0x144: /* TileOffsets */ + if (level) { + data_offset = ftell(ifp); + } else { + data_offset = get4(); + done = 1; + } + break; + case 0x14a: /* SubIFD tag */ + if (len > 2 && !dng_version && !strcmp(make,"Kodak")) + len = 2; + while (len--) { + i = ftell(ifp); + fseek (ifp, get4()+base, SEEK_SET); + if (parse_tiff_ifd (base, level+1)) break; + fseek (ifp, i+4, SEEK_SET); + } + break; + case 29184: sony_offset = get4(); break; + case 29185: sony_length = get4(); break; + case 29217: sony_key = get4(); break; + case 29443: + FORC4 cam_mul[c ^ (c < 2)] = get2(); + break; + case 33405: /* Model2 */ + fgets (model2, 64, ifp); + break; + case 33422: /* CFAPattern */ + if ((plen=len) > 16) plen = 16; + fread (cfa_pat, 1, plen, ifp); + for (colors=cfa=i=0; i < plen; i++) { + colors += !(cfa & (1 << cfa_pat[i])); + cfa |= 1 << cfa_pat[i]; + } + if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3); /* CMY */ + if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4); /* GMCY */ + goto guess_cfa_pc; + case 34310: + parse_mos (ftell(ifp)); + break; + case 34665: /* EXIF tag */ + fseek (ifp, get4()+base, SEEK_SET); + parse_exif (base); + break; + case 37122: /* CompressedBitsPerPixel */ + kodak_cbpp = get4(); + break; + case 37400: + for (raw_color = i=0; i < 3; i++) { + getrat(); + FORC3 rgb_cam[i][c] = getrat(); + } + break; + case 46275: + strcpy (make, "Imacon"); + data_offset = ftell(ifp); + raw_width = 4090; + raw_height = len / raw_width / 2; + done = 1; + break; + case 50454: /* Sinar tag */ + case 50455: + if (!(cbuf = malloc(len))) break; + fread (cbuf, 1, len, ifp); + for (cp = cbuf-1; cp && cp < cbuf+len; cp = strchr(cp,'\n')) + if (!strncmp (++cp,"Neutral ",8)) + sscanf (cp+8, "%f %f %f", cam_mul, cam_mul+1, cam_mul+2); + free (cbuf); + break; + case 50706: /* DNGVersion */ + FORC4 dng_version = (dng_version << 8) + fgetc(ifp); + break; + case 50710: /* CFAPlaneColor */ + if (len > 4) len = 4; + colors = len; + fread (cfa_pc, 1, colors, ifp); +guess_cfa_pc: + FORCC tab[cfa_pc[c]] = c; + for (i=16; i--; ) + filters = filters << 2 | tab[cfa_pat[i % plen]]; + break; + case 50711: /* CFALayout */ + if (get2() == 2) { + fuji_width = (raw_width+1)/2; + filters = 0x49494949; + } + break; + case 0x123: + case 0x90d: + case 50712: /* LinearizationTable */ + if (len > 0x1000) + len = 0x1000; + read_shorts (curve, len); + for (i=len; i < 0x1000; i++) + maximum = curve[i] = curve[i-1]; + break; + case 50714: /* BlackLevel */ + case 50715: /* BlackLevelDeltaH */ + case 50716: /* BlackLevelDeltaV */ + for (dblack=i=0; i < len; i++) + dblack += getrat(); + black += dblack/len + 0.5; + break; + case 50717: /* WhiteLevel */ + maximum = get2(); + break; + case 50718: /* DefaultScale */ + i = get4(); + j = get4() * get4(); + i *= get4(); + if (i > j) xmag = i / j; + else ymag = j / i; + break; + case 50721: /* ColorMatrix1 */ + case 50722: /* ColorMatrix2 */ + FORCC for (j=0; j < 3; j++) + cm[c][j] = getrat(); + use_cm = 1; + break; + case 50723: /* CameraCalibration1 */ + case 50724: /* CameraCalibration2 */ + for (i=0; i < colors; i++) + FORCC cc[i][c] = getrat(); + case 50727: /* AnalogBalance */ + FORCC ab[c] = getrat(); + break; + case 50728: /* AsShotNeutral */ + FORCC asn[c] = getrat(); + break; + case 50729: /* AsShotWhiteXY */ + xyz[0] = getrat(); + xyz[1] = getrat(); + xyz[2] = 1 - xyz[0] - xyz[1]; + break; + case 50740: /* DNGPrivateData */ + if (dng_version) break; + fseek (ifp, get4()+base, SEEK_SET); + parse_tiff_ifd (base, level+1); + break; + case 50829: /* ActiveArea */ + top_margin = get4(); + left_margin = get4(); + height = get4() - top_margin; + width = get4() - left_margin; + } + fseek (ifp, save+4, SEEK_SET); + } + if (sony_length && (buf = malloc(sony_length))) { + fseek (ifp, sony_offset, SEEK_SET); + fread (buf, sony_length, 1, ifp); + sony_decrypt (buf, sony_length/4, 1, sony_key); + sfp = ifp; + if ((ifp = tmpfile())) { + fwrite (buf, sony_length, 1, ifp); + fseek (ifp, 0, SEEK_SET); + parse_tiff_ifd (-sony_offset, level); + fclose (ifp); + } + ifp = sfp; + free (buf); + } + if (!(base | level | dng_version) && + (strstr(make,"Minolta") || strstr(make,"MINOLTA"))) make[0] = 0; + for (i=0; i < colors; i++) + FORCC cc[i][c] *= ab[i]; + if (use_cm) { + FORCC for (i=0; i < 3; i++) + for (cam_xyz[c][i]=j=0; j < colors; j++) + cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i]; + cam_xyz_coeff (cam_xyz); + } + if (asn[0]) + FORCC pre_mul[c] = 1 / asn[c]; + if (!use_cm) + FORCC pre_mul[c] /= cc[c][c]; + return done; +} + +void CLASS parse_tiff (int base) +{ + int doff, maxifd=1000; + + fseek (ifp, base, SEEK_SET); + order = get2(); + if (order != 0x4949 && order != 0x4d4d) return; + get2(); + while ((doff = get4()) && maxifd--) { + fseek (ifp, doff+base, SEEK_SET); + if (parse_tiff_ifd (base, 0)) break; + if (!dng_version && data_offset == 8) make[0] = 0; + } + if (!dng_version && !strncmp(make,"Kodak",5)) { + fseek (ifp, 12+base, SEEK_SET); + parse_tiff_ifd (base, 2); + } +} + +void CLASS parse_minolta() +{ + int save, tag, len, offset, high=0, wide=0, i, c; + + fseek (ifp, 4, SEEK_SET); + offset = get4() + 8; + while ((save=ftell(ifp)) < offset) { + tag = get4(); + len = get4(); + switch (tag) { + case 0x505244: /* PRD */ + fseek (ifp, 8, SEEK_CUR); + high = get2(); + wide = get2(); + break; + case 0x574247: /* WBG */ + get4(); + i = strstr(model,"A200") ? 3:0; + FORC4 cam_mul[c ^ (c >> 1) ^ i] = get2(); + break; + case 0x545457: /* TTW */ + parse_tiff (ftell(ifp)); + } + fseek (ifp, save+len+8, SEEK_SET); + } + raw_height = high; + raw_width = wide; + data_offset = offset; +} + +/* + Many cameras have a "debug mode" that writes JPEG and raw + at the same time. The raw file has no header, so try to + to open the matching JPEG file and read its metadata. + */ +void CLASS parse_external_jpeg() +{ + char *file, *ext, *jname, *jfile, *jext; + FILE *save=ifp; + + ext = strrchr (ifname, '.'); + file = strrchr (ifname, '/'); + if (!file) file = strrchr (ifname, '\\'); + if (!file) file = ifname-1; + file++; + if (strlen(ext) != 4 || ext-file != 8) return; + jname = malloc (strlen(ifname) + 1); + merror (jname, "parse_external()"); + strcpy (jname, ifname); + jfile = file - ifname + jname; + jext = ext - ifname + jname; + if (strcasecmp (ext, ".jpg")) { + strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg"); + memcpy (jfile, file+4, 4); + memcpy (jfile+4, file, 4); + } else + while (isdigit(*--jext)) { + if (*jext != '9') { + (*jext)++; + break; + } + *jext = '0'; + } + if (strcmp (jname, ifname)) { + if ((ifp = fopen (jname, "rb"))) { + if (verbose) + fprintf (stderr, "Reading metadata from %s...\n", jname); + parse_tiff (12); + fclose (ifp); + } + } + if (!timestamp) + fprintf (stderr, "Failed to read metadata from %s\n", jname); + free (jname); + ifp = save; +} + +/* + CIFF block 0x1030 contains an 8x8 white sample. + Load this into white[][] for use in scale_colors(). + */ +void CLASS ciff_block_1030() +{ + static const ushort key[] = { 0x410, 0x45f3 }; + int i, bpp, row, col, vbits=0; + unsigned long bitbuf=0; + + get2(); + if (get4() != 0x80008) return; + if (get4() == 0) return; + bpp = get2(); + if (bpp != 10 && bpp != 12) return; + for (i=row=0; row < 8; row++) + for (col=0; col < 8; col++) { + if (vbits < bpp) { + bitbuf = bitbuf << 16 | (get2() ^ key[i++ & 1]); + vbits += 16; + } + white[row][col] = + bitbuf << (LONG_BIT - vbits) >> (LONG_BIT - bpp); + vbits -= bpp; + } +} + +/* + Parse a CIFF file, better known as Canon CRW format. + */ +void CLASS parse_ciff (int offset, int length) +{ + int tboff, nrecs, i, c, type, len, roff, aoff, save, wbi=-1; + static const int remap[] = { 1,2,3,4,5,1 }; + static const int remap_10d[] = { 0,1,3,4,5,6,0,0,2,8 }; + static const int remap_s70[] = { 0,1,2,9,4,3,6,7,8,9,10,0,0,0,7,0,0,8 }; + ushort key[] = { 0x410, 0x45f3 }; + + if (strcmp(model,"Canon PowerShot G6") && + strcmp(model,"Canon PowerShot S60") && + strcmp(model,"Canon PowerShot S70") && + strcmp(model,"Canon PowerShot Pro1")) + key[0] = key[1] = 0; + fseek (ifp, offset+length-4, SEEK_SET); + tboff = get4() + offset; + fseek (ifp, tboff, SEEK_SET); + nrecs = get2(); + if (nrecs > 100) return; + for (i = 0; i < nrecs; i++) { + type = get2(); + len = get4(); + roff = get4(); + aoff = offset + roff; + save = ftell(ifp); + if (type == 0x080a) { /* Get the camera make and model */ + fseek (ifp, aoff, SEEK_SET); + fread (make, 64, 1, ifp); + fseek (ifp, aoff+strlen(make)+1, SEEK_SET); + fread (model, 64, 1, ifp); + } + if (type == 0x102a) { /* Find the White Balance index */ + fseek (ifp, aoff+14, SEEK_SET); /* 0=auto, 1=daylight, 2=cloudy ... */ + wbi = get2(); + if (((!strcmp(model,"Canon EOS DIGITAL REBEL") || + !strcmp(model,"Canon EOS 300D DIGITAL"))) && wbi == 6) + wbi++; + } + if (type == 0x102c) { /* Get white balance (G2) */ + if (!strcmp(model,"Canon PowerShot G1") || + !strcmp(model,"Canon PowerShot Pro90 IS")) { + fseek (ifp, aoff+120, SEEK_SET); + FORC4 cam_mul[c ^ 2] = get2(); + } else { + fseek (ifp, aoff+100, SEEK_SET); + goto common; + } + } + if (type == 0x0032) { /* Get white balance (D30 & G3) */ + if (!strcmp(model,"Canon EOS D30")) { + fseek (ifp, aoff+72, SEEK_SET); +common: + camera_red = get2() ^ key[0]; + camera_red =(get2() ^ key[1]) / camera_red; + camera_blue = get2() ^ key[0]; + camera_blue /= get2() ^ key[1]; + if (!wbi) camera_red = -1; /* Use my auto WB for this photo */ + } else if (!strcmp(model,"Canon PowerShot G6") || + !strcmp(model,"Canon PowerShot S60") || + !strcmp(model,"Canon PowerShot S70")) { + fseek (ifp, aoff+96 + remap_s70[wbi]*8, SEEK_SET); + goto common; + } else if (!strcmp(model,"Canon PowerShot Pro1")) { + fseek (ifp, aoff+96 + wbi*8, SEEK_SET); + goto common; + } else { + fseek (ifp, aoff+80 + (wbi < 6 ? remap[wbi]*8 : 0), SEEK_SET); + if (!camera_red) + goto common; + } + } + if (type == 0x10a9) { /* Get white balance (D60) */ + if (!strcmp(model,"Canon EOS 10D")) + wbi = remap_10d[wbi]; + fseek (ifp, aoff+2 + wbi*8, SEEK_SET); + camera_red = get2(); + camera_red /= get2(); + camera_blue = get2(); + camera_blue = get2() / camera_blue; + } + if (type == 0x1030 && (wbi == 6 || wbi == 15)) { + fseek (ifp, aoff, SEEK_SET); /* Get white sample */ + ciff_block_1030(); + } + if (type == 0x1031) { /* Get the raw width and height */ + fseek (ifp, aoff+2, SEEK_SET); + raw_width = get2(); + raw_height = get2(); + } + if (type == 0x180e) { /* Get the timestamp */ + fseek (ifp, aoff, SEEK_SET); + timestamp = get4(); + } + if (type == 0x5817) + shot_order = len; + if (type == 0x580e) + timestamp = len; +#ifdef LOCALTIME + if ((type | 0x4000) == 0x580e) + timestamp = mktime (gmtime (×tamp)); +#endif + if (type == 0x5813) + flash_used = int_to_float(len); + if (type == 0x5814) + canon_5814 = int_to_float(len); + if (type == 0x1810) { /* Get the rotation */ + fseek (ifp, aoff+12, SEEK_SET); + flip = get4(); + } + if (type == 0x1835) { /* Get the decoder table */ + fseek (ifp, aoff, SEEK_SET); + crw_init_tables (get4()); + } + if (type >> 8 == 0x28 || type >> 8 == 0x30) /* Get sub-tables */ + parse_ciff(aoff, len); + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS parse_rollei() +{ + char line[128], *val; + int tx=0, ty=0; + struct tm t; + + fseek (ifp, 0, SEEK_SET); + do { + fgets (line, 128, ifp); + if ((val = strchr(line,'='))) + *val++ = 0; + else + val = line + strlen(line); + if (!strcmp(line,"DAT")) + sscanf (val, "%d.%d.%d", &t.tm_mday, &t.tm_mon, &t.tm_year); + if (!strcmp(line,"TIM")) + sscanf (val, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec); + if (!strcmp(line,"HDR")) + data_offset = atoi(val); + if (!strcmp(line,"X ")) + raw_width = atoi(val); + if (!strcmp(line,"Y ")) + raw_height = atoi(val); + if (!strcmp(line,"TX ")) + tx = atoi(val); + if (!strcmp(line,"TY ")) + ty = atoi(val); + } while (strncmp(line,"EOHD",4)); + t.tm_year -= 1900; + t.tm_mon -= 1; + if (mktime(&t) > 0) + timestamp = mktime(&t); + data_offset += tx * ty * 2; + strcpy (make, "Rollei"); + strcpy (model,"d530flex"); +} + +void CLASS parse_mos (int offset) +{ + char data[40]; + int skip, from, i, c, neut[4]; + static const unsigned bayer[] = + { 0x94949494, 0x61616161, 0x16161616, 0x49494949 }; + + fseek (ifp, offset, SEEK_SET); + while (1) { + fread (data, 1, 8, ifp); + if (strcmp(data,"PKTS")) break; + if (!make[0]) strcpy(make,"Leaf"); + fread (data, 1, 40, ifp); + skip = get4(); + from = ftell(ifp); +#ifdef USE_LCMS + if (!strcmp(data,"icc_camera_profile")) { + profile_length = skip; + profile_offset = from; + } +#endif + if (!strcmp(data,"CaptProf_number_of_planes")) { + fscanf (ifp, "%d", &i); + if (i > 1) filters = 0; + } + if (!strcmp(data,"CaptProf_raw_data_rotation") && filters) { + fscanf (ifp, "%d", &i); + filters = bayer[i/90]; + } + if (!strcmp(data,"NeutObj_neutrals")) { + for (i=0; i < 4; i++) + fscanf (ifp, "%d", neut+i); + FORC3 cam_mul[c] = 1.0 / neut[c+1]; + } + parse_mos (from); + fseek (ifp, skip+from, SEEK_SET); + } +} + +void CLASS parse_phase_one (int base) +{ + unsigned entries, tag, type, len, data, save, i, c; + char *cp; + + fseek (ifp, base, SEEK_SET); + order = get4() & 0xffff; + if (get4() >> 8 != 0x526177) return; /* "Raw" */ + fseek (ifp, base+get4(), SEEK_SET); + entries = get4(); + get4(); + while (entries--) { + tag = get4(); + type = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + fseek (ifp, base+data, SEEK_SET); + switch (tag) { + case 0x106: + for (raw_color = i=0; i < 3; i++) + FORC3 rgb_cam[i][c] = int_to_float(get4()); + break; + case 0x107: + FORC3 cam_mul[c] = pre_mul[c] = int_to_float(get4()); + break; + case 0x108: raw_width = data; break; + case 0x109: raw_height = data; break; + case 0x10a: left_margin = data; break; + case 0x10b: top_margin = data; break; + case 0x10c: width = data; break; + case 0x10d: height = data; break; + case 0x10e: tiff_data_compression = data; break; + case 0x10f: data_offset = data+base; break; + case 0x112: + nikon_curve_offset = save - 4; break; + case 0x301: + fread (model, 64, 1, ifp); + cp = strstr(model," camera"); + if (cp && cp < model+64) *cp = 0; + } + fseek (ifp, save, SEEK_SET); + } + load_raw = tiff_data_compression < 3 ? + phase_one_load_raw:phase_one_load_raw_c; + strcpy (make, "Phase One"); + if (model[0]) return; + sprintf (model, "%dx%d", width, height); + switch (raw_height) { + case 2060: strcpy (model,"LightPhase"); break; + case 2682: strcpy (model,"H 10"); break; + case 4128: strcpy (model,"H 20"); break; + case 5488: strcpy (model,"H 25"); break; + } +} + +void CLASS parse_fuji (int offset) +{ + int entries, tag, len, save, c; + + fseek (ifp, offset, SEEK_SET); + entries = get4(); + if (entries > 255) return; + while (entries--) { + tag = get2(); + len = get2(); + save = ftell(ifp); + if (tag == 0x100) { + raw_height = get2(); + raw_width = get2(); + } else if (tag == 0x121) { + height = get2(); + if ((width = get2()) == 4284) width += 3; + } else if (tag == 0x130) + fuji_layout = fgetc(ifp) >> 7; + if (tag == 0x2ff0) + FORC4 cam_mul[c ^ 1] = get2(); + fseek (ifp, save+len, SEEK_SET); + } + if (fuji_layout) { + height *= 2; + width /= 2; + } +} + +int CLASS parse_jpeg (int offset) +{ + int len, save, hlen; + + fseek (ifp, offset, SEEK_SET); + if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) return 0; + + while (fgetc(ifp) == 0xff && fgetc(ifp) >> 4 != 0xd) { + order = 0x4d4d; + len = get2() - 2; + save = ftell(ifp); + order = get2(); + hlen = get4(); + if (get4() == 0x48454150) /* "HEAP" */ + parse_ciff (save+hlen, len-hlen); + parse_tiff (save+6); + fseek (ifp, save+len, SEEK_SET); + } + return 1; +} + +void CLASS parse_riff() +{ + unsigned i, size, end; + char tag[4], date[64], month[64]; + static const char mon[12][4] = + { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; + struct tm t; + + order = 0x4949; + fread (tag, 4, 1, ifp); + size = get4(); + if (!memcmp(tag,"RIFF",4) || !memcmp(tag,"LIST",4)) { + end = ftell(ifp) + size; + get4(); + while (ftell(ifp) < end) + parse_riff(); + } else if (!memcmp(tag,"IDIT",4) && size < 64) { + fread (date, 64, 1, ifp); + date[size] = 0; + if (sscanf (date, "%*s %s %d %d:%d:%d %d", month, &t.tm_mday, + &t.tm_hour, &t.tm_min, &t.tm_sec, &t.tm_year) == 6) { + for (i=0; i < 12 && strcmp(mon[i],month); i++); + t.tm_mon = i; + t.tm_year -= 1900; + if (mktime(&t) > 0) + timestamp = mktime(&t); + } + } else + fseek (ifp, size, SEEK_CUR); +} + +void CLASS parse_smal (int offset, int fsize) +{ + int ver; + + fseek (ifp, offset+2, SEEK_SET); + order = 0x4949; + ver = fgetc(ifp); + if (ver == 6) + fseek (ifp, 5, SEEK_CUR); + if (get4() != fsize) return; + if (ver > 6) data_offset = get4(); + raw_height = height = get2(); + raw_width = width = get2(); + strcpy (make, "SMaL"); + sprintf (model, "v%d %dx%d", ver, width, height); + if (ver == 6) load_raw = smal_v6_load_raw; + if (ver == 9) load_raw = smal_v9_load_raw; +} + +char * CLASS foveon_gets (int offset, char *str, int len) +{ + int i; + fseek (ifp, offset, SEEK_SET); + for (i=0; i < len-1; i++) + if ((str[i] = get2()) == 0) break; + str[i] = 0; + return str; +} + +void CLASS parse_foveon() +{ + int entries, off, len, tag, save, i, wide, high, pent, poff[256][2]; + char name[64]; + + order = 0x4949; /* Little-endian */ + fseek (ifp, 36, SEEK_SET); + flip = get4(); + fseek (ifp, -4, SEEK_END); + fseek (ifp, get4(), SEEK_SET); + if (get4() != 0x64434553) return; /* SECd */ + get4(); + entries = get4(); + while (entries--) { + off = get4(); + len = get4(); + tag = get4(); + save = ftell(ifp); + fseek (ifp, off, SEEK_SET); + if (get4() != (0x20434553 | (tag << 24))) return; + switch (tag) { + case 0x47414d49: /* IMAG */ + case 0x32414d49: /* IMA2 */ + fseek (ifp, 12, SEEK_CUR); + wide = get4(); + high = get4(); + if (wide > raw_width && high > raw_height) { + raw_width = wide; + raw_height = high; + data_offset = off + 24; + } + break; + case 0x464d4143: /* CAMF */ + meta_offset = off + 24; + meta_length = len - 28; + if (meta_length > 0x20000) + meta_length = 0x20000; + break; + case 0x504f5250: /* PROP */ + get4(); + pent = get4(); + fseek (ifp, 12, SEEK_CUR); + off += pent*8 + 24; + if (pent > 256) pent=256; + for (i=0; i < pent*2; i++) + poff[0][i] = off + get4()*2; + for (i=0; i < pent; i++) { + foveon_gets (poff[i][0], name, 64); + if (!strcmp (name, "CAMMANUF")) + foveon_gets (poff[i][1], make, 64); + if (!strcmp (name, "CAMMODEL")) + foveon_gets (poff[i][1], model, 64); + if (!strcmp (name, "WB_DESC")) + foveon_gets (poff[i][1], model2, 64); + if (!strcmp (name, "TIME")) + timestamp = atoi (foveon_gets (poff[i][1], name, 64)); + } +#ifdef LOCALTIME + timestamp = mktime (gmtime (×tamp)); +#endif + } + fseek (ifp, save, SEEK_SET); + } + is_foveon = 1; +} + +/* + Thanks to Adobe for providing these excellent CAM -> XYZ matrices! + */ +void CLASS adobe_coeff() +{ + static const struct { + const char *prefix; + short trans[12]; + } table[] = { + { "Canon EOS D2000", + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Canon EOS D6000", + { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } }, + { "Canon EOS D30", + { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } }, + { "Canon EOS D60", + { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } }, + { "Canon EOS 5D", + { 6228,-404,-967,-8314,16108,2312,-1923,2179,7499 } }, + { "Canon EOS 20D", + { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } }, + { "Canon EOS 350D", + { 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 } }, + { "Canon EOS DIGITAL REBEL XT", + { 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 } }, + { "Canon EOS-1Ds Mark II", + { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } }, + { "Canon EOS-1D Mark II", + { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } }, + { "Canon EOS-1DS", + { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } }, + { "Canon EOS-1D", + { 6806,-179,-1020,-8097,16415,1687,-3267,4236,7690 } }, + { "Canon EOS", + { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } }, + { "Canon PowerShot A50", + { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } }, + { "Canon PowerShot A5", + { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } }, + { "Canon PowerShot G1", + { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } }, + { "Canon PowerShot G2", + { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } }, + { "Canon PowerShot G3", + { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } }, + { "Canon PowerShot G5", + { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } }, + { "Canon PowerShot G6", + { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }, + { "Canon PowerShot Pro1", + { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } }, + { "Canon PowerShot Pro70", + { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } }, + { "Canon PowerShot Pro90", + { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } }, + { "Canon PowerShot S30", + { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } }, + { "Canon PowerShot S40", + { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } }, + { "Canon PowerShot S45", + { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } }, + { "Canon PowerShot S50", + { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } }, + { "Canon PowerShot S60", + { 8795,-2482,-797,-7804,15403,2573,-1422,1996,7082 } }, + { "Canon PowerShot S70", + { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } }, + { "Contax N Digital", + { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } }, + { "EPSON R-D1", + { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } }, + { "FUJIFILM FinePix E550", + { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } }, + { "FUJIFILM FinePix F8", + { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } }, + { "FUJIFILM FinePix F7", + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S20Pro", + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S2Pro", + { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } }, + { "FUJIFILM FinePix S3Pro", + { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } }, + { "FUJIFILM FinePix S5000", + { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } }, + { "FUJIFILM FinePix S5100", + { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } }, + { "FUJIFILM FinePix S7000", + { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } }, + { "FUJIFILM FinePix S9", /* copied from S7000 */ + { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } }, + { "KODAK NC2000F", /* DJC */ + { 16475,-6903,-1218,-851,10375,477,2505,-7,1020 } }, + { "Kodak DCS315C", + { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } }, + { "Kodak DCS330C", + { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } }, + { "KODAK DCS420", + { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } }, + { "KODAK DCS460", + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS1", + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS3B", + { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } }, + { "Kodak DCS520C", + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Kodak DCS560C", + { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } }, + { "Kodak DCS620C", + { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } }, + { "Kodak DCS620X", + { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } }, + { "Kodak DCS660C", + { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } }, + { "Kodak DCS720X", + { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } }, + { "Kodak DCS760C", + { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } }, + { "Kodak DCS Pro SLR", + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14nx", + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14", + { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } }, + { "Kodak ProBack645", + { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } }, + { "Kodak ProBack", + { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } }, + { "LEICA DIGILUX 2", + { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } }, + { "Leaf Valeo", + { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } }, + { "Minolta DiMAGE 5", + { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } }, + { "Minolta DiMAGE 7Hi", + { 11368,-3894,-1242,-6521,14358,2339,-2475,3056,7285 } }, + { "Minolta DiMAGE 7", + { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } }, + { "Minolta DiMAGE A1", + { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } }, + { "MINOLTA DiMAGE A200", + { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } }, + { "Minolta DiMAGE A2", + { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } }, + { "Minolta DiMAGE Z2", + { 11428,-3512,-1706,-5895,13815,2266,-2382,2719,5651 } }, + { "MINOLTA DYNAX 5", + { 10284,-3283,-1086,-7957,15762,2316,-829,882,6644 } }, + { "MINOLTA DYNAX 7", + { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } }, + { "NIKON D100", + { 5902,-933,-782,-8983,16719,2354,-1402,1455,6464 } }, + { "NIKON D1H", + { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } }, + { "NIKON D1X", + { 7702,-2245,-975,-9114,17242,1875,-2679,3055,8521 } }, + { "NIKON D1", + { 7559,-2130,-965,-7611,15713,1972,-2478,3042,8290 } }, + { "NIKON D2H", + { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } }, + { "NIKON D2X", + { 10231,-2769,-1255,-8301,15900,2552,-797,680,7148 } }, + { "NIKON D50", + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON D70", + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON E995", /* copied from E5000 */ + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E2500", + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E4500", + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5000", + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5400", + { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } }, + { "NIKON E5700", + { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } }, + { "NIKON E8400", + { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } }, + { "NIKON E8700", + { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } }, + { "NIKON E8800", + { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } }, + { "OLYMPUS C5050", + { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } }, + { "OLYMPUS C5060", + { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } }, + { "OLYMPUS C70", + { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } }, + { "OLYMPUS C80", + { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } }, + { "OLYMPUS E-10", + { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } }, + { "OLYMPUS E-1", + { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } }, + { "OLYMPUS E-20", + { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } }, + { "OLYMPUS E-300", + { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } }, + { "OLYMPUS E-500", /* copied from E-300 */ + { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } }, + { "PENTAX *ist DS", + { 10371,-2333,-1206,-8688,16231,2602,-1230,1116,11282 } }, + { "PENTAX *ist D", + { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } }, + { "Panasonic DMC-FZ30", + { 10976,-4029,-1141,-7918,15491,2600,-1670,2071,8246 } }, + { "Panasonic DMC-LC1", + { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } }, + { "Panasonic DMC-LX1", + { 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 } }, + { "SONY DSC-F828", + { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } }, + { "SONY DSC-R1", /* DJC */ + { 10528,-3695,-517,-2822,10699,2124,406,1240,5342 } }, + { "SONY DSC-V3", + { 7511,-2571,-692,-7894,15088,3060,-948,1111,8128 } } + }; + double cam_xyz[4][3]; + char name[130]; + int i, j; + + sprintf (name, "%s %s", make, model); + for (i=0; i < sizeof table / sizeof *table; i++) + if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) { + for (j=0; j < 12; j++) + cam_xyz[0][j] = table[i].trans[j]; + cam_xyz_coeff (cam_xyz); + break; + } +} + +void CLASS simple_coeff (int index) +{ + static const float table[][12] = { + /* index 0 -- all Foveon cameras */ + { 1.4032,-0.2231,-0.1016,-0.5263,1.4816,0.017,-0.0112,0.0183,0.9113 }, + /* index 1 -- Kodak DC20 and DC25 */ + { 2.25,0.75,-1.75,-0.25,-0.25,0.75,0.75,-0.25,-0.25,-1.75,0.75,2.25 }, + /* index 2 -- Logitech Fotoman Pixtura */ + { 1.893,-0.418,-0.476,-0.495,1.773,-0.278,-1.017,-0.655,2.672 }, + /* index 3 -- Nikon E700, E800, and E950 */ + { -1.936280, 1.800443, -1.448486, 2.584324, + 1.405365, -0.524955, -0.289090, 0.408680, + -1.204965, 1.082304, 2.941367, -1.818705 } + }; + int i, c; + + for (raw_color = i=0; i < 3; i++) + FORCC rgb_cam[i][c] = table[index][i*colors+c]; +} + +short CLASS guess_byte_order (int words) +{ + uchar test[4][2]; + int t=2, msb; + double diff, sum[2] = {0,0}; + + fread (test[0], 2, 2, ifp); + for (words-=2; words--; ) { + fread (test[t], 2, 1, ifp); + for (msb=0; msb < 2; msb++) { + diff = (test[t^2][msb] << 8 | test[t^2][!msb]) + - (test[t ][msb] << 8 | test[t ][!msb]); + sum[msb] += diff*diff; + } + t = (t+1) & 3; + } + return sum[0] < sum[1] ? 0x4d4d : 0x4949; +} + +/* + Identify which camera created this file, and set global variables + accordingly. Return nonzero if the file cannot be decoded. + */ +int CLASS identify (int no_decode) +{ + char head[32], *cp; + unsigned hlen, fsize, i, c, is_jpeg=0, is_canon; + static const struct { + int fsize; + char make[12], model[15], withjpeg; + } table[] = { + { 62464, "Kodak", "DC20" ,0 }, + { 124928, "Kodak", "DC20" ,0 }, + { 311696, "ST Micro", "STV680 VGA" ,0 }, /* SPYz */ + { 787456, "Creative", "PC-CAM 600" ,0 }, + { 1138688, "Minolta", "RD175" ,0 }, + { 3840000, "Foculus", "531C" ,0 }, + { 1920000, "AVT", "F-201C" ,0 }, + { 5067304, "AVT", "F-510C" ,0 }, + { 10134608, "AVT", "F-510C" ,0 }, + { 16157136, "AVT", "F-810C" ,0 }, + { 6624000, "Pixelink", "A782" ,0 }, + { 13248000, "Pixelink", "A782" ,0 }, + { 6291456, "RoverShot","3320AF" ,0 }, + { 5939200, "OLYMPUS", "C770UZ" ,0 }, + { 1581060, "NIKON", "E900" ,1 }, /* or E900s,E910 */ + { 2465792, "NIKON", "E950" ,1 }, /* or E800,E700 */ + { 2940928, "NIKON", "E2100" ,1 }, /* or E2500 */ + { 4771840, "NIKON", "E990" ,1 }, /* or E995 */ + { 4775936, "NIKON", "E3700" ,1 }, /* or Optio 33WR */ + { 5869568, "NIKON", "E4300" ,1 }, /* or DiMAGE Z2 */ + { 5865472, "NIKON", "E4500" ,1 }, + { 7438336, "NIKON", "E5000" ,1 }, /* or E5700 */ + { 1976352, "CASIO", "QV-2000UX" ,1 }, + { 3217760, "CASIO", "QV-3*00EX" ,1 }, + { 6218368, "CASIO", "QV-5700" ,1 }, + { 7530816, "CASIO", "QV-R51" ,1 }, + { 7684000, "CASIO", "QV-4000" ,1 }, + { 4948608, "CASIO", "EX-S100" ,1 }, + { 7542528, "CASIO", "EX-Z50" ,1 }, + { 7753344, "CASIO", "EX-Z55" ,1 }, + { 7426656, "CASIO", "EX-P505" ,1 }, + { 9313536, "CASIO", "EX-P600" ,1 }, + { 10979200, "CASIO", "EX-P700" ,1 }, + { 3178560, "PENTAX", "Optio S" ,1 }, /* 8-bit */ + { 4841984, "PENTAX", "Optio S" ,1 }, /* 12-bit */ + { 6114240, "PENTAX", "Optio S4" ,1 }, /* or S4i */ + { 12582980, "Sinar", "" ,0 } }; + static const char *corp[] = + { "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX", + "MINOLTA", "Minolta", "Konica", "CASIO", "Sinar" }; + +/* What format is this file? Set make[] if we recognize it. */ + + load_raw = NULL; + raw_height = raw_width = fuji_width = flip = 0; + height = width = top_margin = left_margin = 0; + make[0] = model[0] = model2[0] = 0; + memset (white, 0, sizeof white); + data_offset = meta_length = tiff_bps = tiff_data_compression = 0; + kodak_cbpp = zero_after_ff = dng_version = fuji_secondary = 0; + timestamp = shot_order = tiff_samples = black = is_foveon = 0; + raw_color = use_gamma = xmag = ymag = 1; + filters = UINT_MAX; /* 0 = no filters, UINT_MAX = unknown */ + for (i=0; i < 4; i++) { + cam_mul[i] = i == 1; + pre_mul[i] = i < 3; + FORC3 rgb_cam[c][i] = c == i; + } + colors = 3; + for (i=0; i < 0x1000; i++) curve[i] = i; + maximum = 0xfff; +#ifdef USE_LCMS + profile_length = 0; +#endif + + order = get2(); + hlen = get4(); + fseek (ifp, 0, SEEK_SET); + fread (head, 1, 32, ifp); + fseek (ifp, 0, SEEK_END); + fsize = ftell(ifp); + if ((cp = memmem (head, 32, "MMMM", 4)) || + (cp = memmem (head, 32, "IIII", 4))) + parse_phase_one (cp-head); + else if (order == 0x4949 || order == 0x4d4d) { + if (!memcmp (head+6,"HEAPCCDR",8)) { + data_offset = hlen; + parse_ciff (hlen, fsize - hlen); + } else { + parse_tiff(0); + if (!dng_version && !strncmp(make,"NIKON",5) && filters == UINT_MAX) + make[0] = 0; + } + } else if (!memcmp (head,"\xff\xd8\xff\xe1",4) && + !memcmp (head+6,"Exif",4)) { + fseek (ifp, 4, SEEK_SET); + fseek (ifp, 4 + get2(), SEEK_SET); + if (fgetc(ifp) != 0xff) + parse_tiff(12); + } else if (!memcmp (head,"BM",2) && + head[26] == 1 && head[28] == 16 && head[30] == 0) { + data_offset = 0x1000; + order = 0x4949; + fseek (ifp, 38, SEEK_SET); + if (get4() == 2834 && get4() == 2834 && get4() == 0 && get4() == 4096) { + strcpy (model, "BMQ"); + flip = 3; + goto nucore; + } + } else if (!memcmp (head,"BR",2)) { + strcpy (model, "RAW"); +nucore: + strcpy (make, "Nucore"); + order = 0x4949; + fseek (ifp, 10, SEEK_SET); + data_offset += get4(); + get4(); + raw_width = get4(); + raw_height = get4(); + if (model[0] == 'B' && raw_width == 2597) { + raw_width++; + data_offset -= 0x1000; + } + } else if (!memcmp (head+25,"ARECOYK",7)) { + strcpy (make, "Contax"); + strcpy (model,"N Digital"); + fseek (ifp, 33, SEEK_SET); + get_timestamp(1); + fseek (ifp, 60, SEEK_SET); + FORC4 cam_mul[c ^ (c >> 1)] = get4(); + } else if (!strcmp (head, "PXN")) { + strcpy (make, "Logitech"); + strcpy (model,"Fotoman Pixtura"); + } else if (!memcmp (head,"FUJIFILM",8)) { + fseek (ifp, 92, SEEK_SET); + parse_fuji (get4()); + fseek (ifp, 84, SEEK_SET); + if ((hlen = get4()) > 120) { + fseek (ifp, 120, SEEK_SET); + fuji_secondary = (i = get4()) && 1; + if (fuji_secondary && use_secondary) + parse_fuji (i); + } + fseek (ifp, 100, SEEK_SET); + i = get4(); + parse_tiff (hlen+12); + data_offset = i; + } else if (!memcmp (head,"RIFF",4)) { + fseek (ifp, 0, SEEK_SET); + parse_riff(); + } else if (!memcmp (head,"DSC-Image",9)) + parse_rollei(); + else if (!memcmp (head,"\0MRM",4)) + parse_minolta(); + else if (!memcmp (head,"FOVb",4)) + parse_foveon(); + else + for (i=0; i < sizeof table / sizeof *table; i++) + if (fsize == table[i].fsize) { + strcpy (make, table[i].make ); + strcpy (model, table[i].model); + if (table[i].withjpeg) + parse_external_jpeg(); + } + parse_mos(8); + parse_mos(3472); + if (make[0] == 0) parse_smal (0, fsize); + if (make[0] == 0) is_jpeg = parse_jpeg(0); + if (no_decode) return !timestamp; + + for (i=0; i < sizeof corp / sizeof *corp; i++) + if (strstr (make, corp[i])) /* Simplify company names */ + strcpy (make, corp[i]); + if (!strncmp (make,"KODAK",5)) + make[16] = model[16] = 0; + cp = make + strlen(make); /* Remove trailing spaces */ + while (*--cp == ' ') *cp = 0; + cp = model + strlen(model); + while (*--cp == ' ') *cp = 0; + i = strlen(make); /* Remove make from model */ + if (!strncmp (model, make, i) && model[i++] == ' ') + memmove (model, model+i, 64-i); + make[63] = model[63] = model2[63] = 0; + + if (make[0] == 0) { + fprintf (stderr, "%s: unsupported file format.\n", ifname); + return 1; + } + if ((raw_height | raw_width) < 0) + raw_height = raw_width = 0; + if (!height) height = raw_height; + if (!width) width = raw_width; + if (fuji_width) { + width = height + fuji_width; + height = width - 1; + xmag = ymag = 1; + } + if (dng_version) { + strcat (model," DNG"); + if (filters == UINT_MAX) filters = 0; + if (!filters) + colors = tiff_samples; + if (tiff_data_compression == 1) + load_raw = adobe_dng_load_raw_nc; + if (tiff_data_compression == 7) + load_raw = adobe_dng_load_raw_lj; + FORC4 cam_mul[c] = pre_mul[c]; + goto dng_skip; + } + +/* We'll try to decode anything from Canon or Nikon. */ + + if (filters == UINT_MAX) filters = 0x94949494; + if ((is_canon = !strcmp(make,"Canon"))) { + load_raw = memcmp (head+6,"HEAPCCDR",8) ? + lossless_jpeg_load_raw : canon_compressed_load_raw; + maximum = 0xfff; + } + if (!strcmp(make,"NIKON")) + load_raw = nikon_is_compressed() ? + nikon_compressed_load_raw : nikon_load_raw; + if (!strncmp (make,"OLYMPUS",7)) + height += height & 1; + +/* Set parameters based on camera name (for non-DNG files). */ + + if (is_foveon) { + if (height*2 < width) ymag = 2; + if (height > width) xmag = 2; + filters = 0; + load_raw = foveon_load_raw; + simple_coeff(0); + } else if (!strcmp(model,"PowerShot 600")) { + height = 613; + width = 854; + colors = 4; + filters = 0xe1e4e1e4; + load_raw = canon_600_load_raw; + } else if (!strcmp(model,"PowerShot A5") || + !strcmp(model,"PowerShot A5 Zoom")) { + height = 773; + width = 960; + raw_width = 992; + colors = 4; + filters = 0x1e4e1e4e; + load_raw = canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A50")) { + height = 968; + width = 1290; + raw_width = 1320; + colors = 4; + filters = 0x1b4e4b1e; + load_raw = canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot Pro70")) { + height = 1024; + width = 1552; + colors = 4; + filters = 0x1e4b4e1b; + load_raw = canon_a5_load_raw; + black = 34; + } else if (!strcmp(model,"PowerShot Pro90 IS")) { + width = 1896; + colors = 4; + filters = 0xb4b4b4b4; + } else if (is_canon && raw_width == 2144) { + height = 1550; + width = 2088; + top_margin = 8; + left_margin = 4; + if (!strcmp(model,"PowerShot G1")) { + colors = 4; + filters = 0xb4b4b4b4; + } + } else if (is_canon && raw_width == 2224) { + height = 1448; + width = 2176; + top_margin = 6; + left_margin = 48; + } else if (is_canon && raw_width == 2376) { + height = 1720; + width = 2312; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 2672) { + height = 1960; + width = 2616; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 3152) { + height = 2056; + width = 3088; + top_margin = 12; + left_margin = 64; + maximum = 0xfa0; + } else if (is_canon && raw_width == 3160) { + height = 2328; + width = 3112; + top_margin = 12; + left_margin = 44; + } else if (is_canon && raw_width == 3344) { + height = 2472; + width = 3288; + top_margin = 6; + left_margin = 4; + } else if (!strcmp(model,"EOS D2000C")) { + filters = 0x61616161; + black = curve[200]; + } else if (!strcmp(model,"EOS-1D")) { + raw_height = height = 1662; + raw_width = width = 2496; + data_offset = 288912; + filters = 0x61616161; + } else if (!strcmp(model,"EOS-1DS")) { + raw_height = height = 2718; + raw_width = width = 4082; + data_offset = 289168; + filters = 0x61616161; + } else if (is_canon && raw_width == 3516) { + top_margin = 14; + left_margin = 42; + goto canon_cr2; + } else if (is_canon && raw_width == 3596) { + top_margin = 12; + left_margin = 74; + goto canon_cr2; + } else if (is_canon && raw_width == 4476) { + top_margin = 34; + left_margin = 90; + goto canon_cr2; + } else if (is_canon && raw_width == 5108) { + top_margin = 13; + left_margin = 98; + maximum = 0xe80; +canon_cr2: + height = raw_height - top_margin; + width = raw_width - left_margin; + } else if (!strcmp(model,"D1")) { + camera_red *= 256/527.0; + camera_blue *= 256/317.0; + } else if (!strcmp(model,"D1X")) { + width = 4024; + ymag = 2; + } else if (!strcmp(model,"D70")) { + maximum = 0xf53; + } else if (!strcmp(model,"D100")) { + if (tiff_data_compression == 34713 && load_raw == nikon_load_raw) + raw_width = (width += 3) + 3; + maximum = 0xf44; + } else if (!strcmp(model,"D2H")) { + width = 2482; + left_margin = 6; + } else if (!strcmp(model,"D2X")) { + width = 4312; + } else if (fsize == 1581060) { + height = 963; + width = 1287; + raw_width = 1632; + load_raw = nikon_e900_load_raw; + maximum = 0x3f4; + colors = 4; + filters = 0x1e1e1e1e; + simple_coeff(3); + pre_mul[0] = 1.2085; + pre_mul[1] = 1.0943; + pre_mul[3] = 1.1103; + } else if (fsize == 2465792) { + height = 1203; + width = 1616; + raw_width = 2048; + load_raw = nikon_e900_load_raw; + maximum = 0x3dd; + colors = 4; + filters = 0x4b4b4b4b; + simple_coeff(3); + pre_mul[0] = 1.18193; + pre_mul[2] = 1.16452; + pre_mul[3] = 1.17250; + } else if (!strcmp(model,"E880") || + !strcmp(model,"E990")) { + if (!timestamp && !nikon_e990()) goto cp_e995; + height = 1540; + width = 2064; + colors = 4; + filters = 0xb4b4b4b4; + simple_coeff(3); + pre_mul[0] = 1.196; + pre_mul[1] = 1.246; + pre_mul[2] = 1.018; + } else if (!strcmp(model,"E995")) { +cp_e995: + strcpy (model, "E995"); + height = 1540; + width = 2064; + colors = 4; + filters = 0xe1e1e1e1; + } else if (!strcmp(model,"E2100")) { + if (!timestamp && !nikon_e2100()) goto cp_e2500; + height = 1206; + width = 1616; + load_raw = nikon_e2100_load_raw; + pre_mul[0] = 1.945; + pre_mul[2] = 1.040; + } else if (!strcmp(model,"E2500")) { +cp_e2500: + strcpy (model, "E2500"); + height = 1204; + width = 1616; + colors = 4; + filters = 0x4b4b4b4b; + } else if (fsize == 4775936) { + height = 1542; + width = 2064; + load_raw = nikon_e2100_load_raw; + pre_mul[0] = 1.818; + pre_mul[2] = 1.618; + if ((i = nikon_3700()) == 2) { + strcpy (make, "OLYMPUS"); + strcpy (model, "C740UZ"); + } else if (i == 0) { + strcpy (make, "PENTAX"); + strcpy (model,"Optio 33WR"); + flip = 1; + filters = 0x16161616; + pre_mul[0] = 1.331; + pre_mul[2] = 1.820; + } + } else if (!strcmp(model,"E4300")) { + if (!timestamp && minolta_z2()) goto dimage_z2; + height = 1710; + width = 2288; + filters = 0x16161616; + pre_mul[0] = 508; + pre_mul[1] = 256; + pre_mul[2] = 322; + } else if (!strcmp(model,"DiMAGE Z2")) { +dimage_z2: + strcpy (make, "MINOLTA"); + strcpy (model,"DiMAGE Z2"); + height = 1710; + width = 2288; + filters = 0x16161616; + load_raw = nikon_e2100_load_raw; + black = 68; + } else if (!strcmp(model,"E4500")) { + height = 1708; + width = 2288; + colors = 4; + filters = 0xb4b4b4b4; + } else if (fsize == 7438336) { + height = 1924; + width = 2576; + colors = 4; + filters = 0xb4b4b4b4; + } else if (!strcmp(model,"R-D1")) { + tiff_data_compression = 34713; + load_raw = nikon_load_raw; + } else if (!strcmp(model,"FinePix S5100") || + !strcmp(model,"FinePix S5500")) { + load_raw = unpacked_load_raw; + maximum = 0xffff; + } else if (!strncmp(model,"FinePix",7)) { + if (!strcmp(model+7,"S2Pro")) { + strcpy (model+7," S2Pro"); + height = 2144; + width = 2880; + black = 128; + flip = 6; + } else + maximum = 0x3e00; + top_margin = (raw_height - height)/2; + left_margin = (raw_width - width )/2; + data_offset += (top_margin*raw_width + left_margin) * 2; + if (fuji_secondary) + data_offset += use_secondary * ( strcmp(model+7," S3Pro") + ? (raw_width *= 2) : raw_height*raw_width*2 ); + fuji_width = width >> !fuji_layout; + width = (height >> fuji_layout) + fuji_width; + raw_height = height; + height = width - 1; + load_raw = fuji_load_raw; + if (!(fuji_width & 1)) filters = 0x49494949; + } else if (!strcmp(model,"RD175")) { + height = 986; + width = 1534; + data_offset = 513; + filters = 0x61616161; + load_raw = minolta_rd175_load_raw; + } else if (!strcmp(model,"Digital Camera KD-400Z")) { + height = 1712; + width = 2312; + raw_width = 2336; + data_offset = 4034; + fseek (ifp, 2032, SEEK_SET); + goto konica_400z; + } else if (!strcmp(model,"Digital Camera KD-510Z")) { + data_offset = 4032; + pre_mul[0] = 1.297; + pre_mul[2] = 1.438; + fseek (ifp, 2032, SEEK_SET); + goto konica_510z; + } else if (!strcasecmp(make,"MINOLTA")) { + load_raw = unpacked_load_raw; + maximum = 0xf7d; + if (!strncmp(model,"DiMAGE A",8)) { + if (!strcmp(model,"DiMAGE A200")) + filters = 0x49494949; + load_raw = packed_12_load_raw; + maximum = model[8] == '1' ? 0xf8b : 0xfff; + } else if (!strncmp(model,"ALPHA",5) || + !strncmp(model,"DYNAX",5) || + !strncmp(model,"MAXXUM",6)) { + sprintf (model, "DYNAX %s", model+6 + (model[0]=='M')); + load_raw = packed_12_load_raw; + maximum = 0xffb; + } else if (!strncmp(model,"DiMAGE G",8)) { + if (model[8] == '4') { + data_offset = 5056; + pre_mul[0] = 1.602; + pre_mul[2] = 1.441; + fseek (ifp, 2078, SEEK_SET); + height = 1716; + width = 2304; + } else if (model[8] == '5') { + data_offset = 4016; + fseek (ifp, 1936, SEEK_SET); +konica_510z: + height = 1956; + width = 2607; + raw_width = 2624; + } else if (model[8] == '6') { + data_offset = 4032; + fseek (ifp, 2030, SEEK_SET); + height = 2136; + width = 2848; + } + filters = 0x61616161; +konica_400z: + load_raw = unpacked_load_raw; + maximum = 0x3df; + order = 0x4d4d; + FORC4 cam_mul[(c >> 1) | ((c & 1) << 1)] = get2(); + } + if (pre_mul[0] == 1 && pre_mul[2] == 1) { + pre_mul[0] = 1.42; + pre_mul[2] = 1.25; + } + } else if (!strncmp(model,"*ist D",6)) { + load_raw = model[6] ? packed_12_load_raw : unpacked_load_raw; + if (model[6] == 'S') height -= 2; + } else if (!strcmp(model,"Optio S")) { + if (fsize == 3178560) { + height = 1540; + width = 2064; + load_raw = eight_bit_load_raw; + camera_red *= 4; + camera_blue *= 4; + pre_mul[0] = 1.391; + pre_mul[2] = 1.188; + } else { + height = 1544; + width = 2068; + raw_width = 3136; + load_raw = packed_12_load_raw; + maximum = 0xf7c; + pre_mul[0] = 1.137; + pre_mul[2] = 1.453; + } + } else if (!strncmp(model,"Optio S4",8)) { + height = 1737; + width = 2324; + raw_width = 3520; + load_raw = packed_12_load_raw; + maximum = 0xf7a; + pre_mul[0] = 1.980; + pre_mul[2] = 1.570; + } else if (!strcmp(model,"STV680 VGA")) { + height = 484; + width = 644; + load_raw = eight_bit_load_raw; + flip = 2; + filters = 0x16161616; + black = 16; + pre_mul[0] = 1.097; + pre_mul[2] = 1.128; + } else if (!strcmp(model,"531C")) { + height = 1200; + width = 1600; + load_raw = unpacked_load_raw; + filters = 0x49494949; + pre_mul[1] = 1.218; + } else if (!strcmp(model,"F-201C")) { + height = 1200; + width = 1600; + load_raw = eight_bit_load_raw; + } else if (!strcmp(model,"F-510C")) { + height = 1958; + width = 2588; + load_raw = (fsize < 7500000) ? eight_bit_load_raw : unpacked_load_raw; + maximum = 0xfff0; + } else if (!strcmp(model,"F-810C")) { + height = 2469; + width = 3272; + load_raw = unpacked_load_raw; + maximum = 0xfff0; + } else if (!strcmp(model,"A782")) { + height = 3000; + width = 2208; + filters = 0x61616161; + load_raw = (fsize < 10000000) ? eight_bit_load_raw : unpacked_load_raw; + maximum = 0xffc0; + } else if (!strcmp(model,"3320AF")) { + height = 1536; + raw_width = width = 2048; + filters = 0x61616161; + load_raw = unpacked_load_raw; + maximum = 0x3ff; + pre_mul[0] = 1.717; + pre_mul[2] = 1.138; + fseek (ifp, 0x300000, SEEK_SET); + if ((order = guess_byte_order(0x10000)) == 0x4d4d) { + data_offset = (2048 * 16 + 28) * 2; + height -= 16; + width -= 28; + maximum = 0xf5c0; + strcpy (make, "ISG"); + sprintf (model, "%dx%d", width, height); + } + } else if (!strcmp(make,"Imacon")) { + height = raw_height - 6; + width = raw_width - 10; + data_offset += 6 + raw_width*12; + flip = height > width+10 ? 5:3; + sprintf (model, "Ixpress %d-Mp", height*width/1000000); + filters = 0x61616161; + load_raw = unpacked_load_raw; + maximum = 0xffff; + pre_mul[0] = 1.963; + pre_mul[2] = 1.430; + } else if (!strcmp(make,"Sinar")) { + if (!memcmp(head,"8BPS",4)) { + fseek (ifp, 14, SEEK_SET); + height = get4(); + width = get4(); + filters = 0x61616161; + data_offset = 68; + } + load_raw = unpacked_load_raw; + maximum = 0x3fff; + } else if (!strcmp(make,"Leaf")) { + load_raw = unpacked_load_raw; + if (tiff_data_compression == 99) + load_raw = lossless_jpeg_load_raw; + maximum = 0x3fff; + strcpy (model, "Valeo"); + if (filters == 0) { + load_raw = leaf_load_raw; + maximum = 0xffff; + strcpy (model, "Volare"); + } + } else if (!strcmp(make,"LEICA") || !strcmp(make,"Panasonic")) { + if (width == 3880) { + data_offset += 12; + maximum = 0xf7f0; + width -= 22; + } else if (width == 3304) { + maximum = 0xf94c; + width -= 16; + } else maximum = 0xfff0; + load_raw = unpacked_load_raw; + } else if (!strcmp(model,"E-1")) { + filters = 0x61616161; + load_raw = unpacked_load_raw; + maximum = 0xfff0; + black = 1024; + } else if (!strcmp(model,"E-10")) { + load_raw = unpacked_load_raw; + maximum = 0xfff0; + black = 2048; + } else if (!strncmp(model,"E-20",4)) { + load_raw = unpacked_load_raw; + maximum = 0xffc0; + black = 2560; + } else if (!strcmp(model,"E-300") || + !strcmp(model,"E-500")) { + width -= 20; + load_raw = olympus_e300_load_raw; + maximum = 0xfff; + if (fsize > 15728640) { + load_raw = unpacked_load_raw; + maximum = 0xfc30; + } + } else if (!strcmp(model,"C770UZ")) { + height = 1718; + width = 2304; + filters = 0x16161616; + load_raw = nikon_e2100_load_raw; + } else if (!strcmp(make,"OLYMPUS")) { + load_raw = olympus_cseries_load_raw; + if (!strcmp(model,"C5050Z") || + !strcmp(model,"C8080WZ")) + filters = 0x16161616; + } else if (!strcmp(model,"N Digital")) { + height = 2047; + width = 3072; + filters = 0x61616161; + data_offset = 0x1a00; + load_raw = packed_12_load_raw; + maximum = 0xf1e; + } else if (!strcmp(model,"DSC-F828")) { + width = 3288; + left_margin = 5; + data_offset = 862144; + load_raw = sony_load_raw; + filters = 0x9c9c9c9c; + colors = 4; + black = 491; + } else if (!strcmp(model,"DSC-V3")) { + width = 3109; + left_margin = 59; + data_offset = 787392; + load_raw = sony_load_raw; + } else if (!strcmp(model,"DSC-R1")) { + width = 3925; + order = 0x4d4d; + load_raw = unpacked_load_raw; + black = 512; + } else if (!strncmp(model,"P850",4)) { + height = 1950; + width = 2608; + data_offset = 76456; + filters = 0x16161616; + load_raw = packed_12_load_raw; + } else if (!strcasecmp(make,"KODAK")) { + filters = 0x61616161; + if (!strcmp(model,"NC2000F")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"EOSDCS3B")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"EOSDCS1")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS315C")) { + black = 8; + } else if (!strcmp(model,"DCS330C")) { + black = 8; + } else if (!strcmp(model,"DCS420")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460A")) { + width -= 4; + left_margin = 2; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS520C")) { + black = 180; + } else if (!strcmp(model,"DCS560C")) { + black = 188; + } else if (!strcmp(model,"DCS620C")) { + black = 180; + } else if (!strcmp(model,"DCS620X")) { + black = 185; + } else if (!strcmp(model,"DCS660C")) { + black = 214; + } else if (!strcmp(model,"DCS660M")) { + black = 214; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS760M")) { + colors = 1; + filters = 0; + } + switch (tiff_data_compression) { + case 0: /* No compression */ + case 1: + load_raw = kodak_easy_load_raw; + break; + case 7: /* Lossless JPEG */ + load_raw = lossless_jpeg_load_raw; + case 32867: + break; + case 65000: /* Kodak DCR compression */ + if (kodak_data_compression == 32803) + load_raw = kodak_compressed_load_raw; + else { + load_raw = kodak_yuv_load_raw; + height = (height+1) & -2; + width = (width +1) & -2; + filters = 0; + } + break; + default: + fprintf (stderr, "%s: %s %s uses unsupported compression method %d.\n", + ifname, make, model, tiff_data_compression); + return 1; + } + if (strstr(model,"DC25")) { + strcpy (model, "DC25"); + data_offset = 15424; + } + if (!strncmp(model,"DC2",3)) { + height = 242; + if (fsize < 100000) { + raw_width = 256; width = 249; + } else { + raw_width = 512; width = 501; + } + data_offset += raw_width + 1; + colors = 4; + filters = 0x8d8d8d8d; + simple_coeff(1); + pre_mul[1] = 1.179; + pre_mul[2] = 1.209; + pre_mul[3] = 1.036; + load_raw = kodak_easy_load_raw; + } else if (!strcmp(model,"Digital Camera 40")) { + strcpy (model, "DC40"); + height = 512; + width = 768; + data_offset = 1152; + load_raw = kodak_radc_load_raw; + } else if (strstr(model,"DC50")) { + strcpy (model, "DC50"); + height = 512; + width = 768; + data_offset = 19712; + load_raw = kodak_radc_load_raw; + } else if (strstr(model,"DC120")) { + strcpy (model, "DC120"); + height = 976; + width = 848; + if (tiff_data_compression == 7) + load_raw = kodak_jpeg_load_raw; + else + load_raw = kodak_dc120_load_raw; + } + } else if (!strcmp(model,"Fotoman Pixtura")) { + height = 512; + width = 768; + data_offset = 3632; + load_raw = kodak_radc_load_raw; + filters = 0x61616161; + simple_coeff(2); + } else if (!strcmp(make,"Rollei")) { + switch (raw_width) { + case 1316: + height = 1030; + width = 1300; + top_margin = 1; + left_margin = 6; + break; + case 2568: + height = 1960; + width = 2560; + top_margin = 2; + left_margin = 8; + } + filters = 0x16161616; + load_raw = rollei_load_raw; + pre_mul[0] = 1.8; + pre_mul[2] = 1.3; + } else if (!strcmp(model,"PC-CAM 600")) { + height = 768; + data_offset = width = 1024; + filters = 0x49494949; + load_raw = eight_bit_load_raw; + pre_mul[0] = 1.14; + pre_mul[2] = 2.73; + } else if (!strcmp(model,"QV-2000UX")) { + height = 1208; + width = 1632; + data_offset = width * 2; + load_raw = eight_bit_load_raw; + } else if (fsize == 3217760) { + height = 1546; + width = 2070; + raw_width = 2080; + load_raw = eight_bit_load_raw; + } else if (!strcmp(model,"QV-4000")) { + height = 1700; + width = 2260; + load_raw = unpacked_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"QV-5700")) { + height = 1924; + width = 2576; + load_raw = casio_qv5700_load_raw; + } else if (!strcmp(model,"QV-R51")) { + height = 1926; + width = 2576; + raw_width = 3904; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.340; + pre_mul[2] = 1.672; + } else if (!strcmp(model,"EX-S100")) { + height = 1544; + width = 2058; + raw_width = 3136; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.631; + pre_mul[2] = 1.106; + } else if (!strcmp(model,"EX-Z50")) { + height = 1931; + width = 2570; + raw_width = 3904; + load_raw = packed_12_load_raw; + pre_mul[0] = 2.529; + pre_mul[2] = 1.185; + } else if (!strcmp(model,"EX-Z55")) { + height = 1960; + width = 2570; + raw_width = 3904; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.520; + pre_mul[2] = 1.316; + } else if (!strcmp(model,"EX-P505")) { + height = 1928; + width = 2568; + raw_width = 3852; + load_raw = packed_12_load_raw; + pre_mul[0] = 2.07; + pre_mul[2] = 1.88; + } else if (!strcmp(model,"EX-P600")) { + height = 2142; + width = 2844; + raw_width = 4288; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.797; + pre_mul[2] = 1.219; + } else if (!strcmp(model,"EX-P700")) { + height = 2318; + width = 3082; + raw_width = 4672; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.758; + pre_mul[2] = 1.504; + } else if (!strcmp(make,"Nucore")) { + filters = 0x61616161; + load_raw = unpacked_load_raw; + if (width == 2598) { + filters = 0x16161616; + load_raw = nucore_load_raw; + flip = 2; + } + } + if (raw_color) adobe_coeff(); +dng_skip: + if (!load_raw || !height || is_jpeg) { + fprintf (stderr, "%s: Cannot decode %s %s%s images.\n", + ifname, make, model, is_jpeg ? " JPEG":""); + return 1; + } +#ifdef NO_JPEG + if (load_raw == kodak_jpeg_load_raw) { + fprintf (stderr, "%s: decoder was not linked with libjpeg.\n", ifname); + return 1; + } +#endif + if (!raw_height) raw_height = height; + if (!raw_width ) raw_width = width; + raw_color |= use_camera_rgb && colors == 3; + FORCC { /* Apply user-selected color balance */ + rgb_cam[0][c] *= red_scale; + rgb_cam[2][c] *= blue_scale; + } + if (filters && colors == 3) + for (i=0; i < 32; i+=4) { + if ((filters >> i & 15) == 9) + filters |= 2 << i; + if ((filters >> i & 15) == 6) + filters |= 8 << i; + } + fseek (ifp, data_offset, SEEK_SET); + return 0; +} + +#ifdef USE_LCMS +void CLASS apply_profile (char *pfname) +{ + char *prof; + cmsHPROFILE hInProfile=NULL, hOutProfile; + cmsHTRANSFORM hTransform; + + if (pfname) + hInProfile = cmsOpenProfileFromFile (pfname, "r"); + else if (profile_length) { + prof = malloc (profile_length); + merror (prof, "apply_profile()"); + fseek (ifp, profile_offset, SEEK_SET); + fread (prof, 1, profile_length, ifp); + hInProfile = cmsOpenProfileFromMem (prof, profile_length); + free (prof); + } + if (!hInProfile) return; + if (verbose) + fprintf (stderr, "Applying color profile...\n"); + maximum = 0xffff; + use_gamma = 0; + raw_color = 1; /* Don't use rgb_cam with a profile */ + + hOutProfile = cmsCreate_sRGBProfile(); + hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16, + hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0); + cmsDoTransform (hTransform, image, image, width*height); + + cmsDeleteTransform (hTransform); + cmsCloseProfile (hInProfile); + cmsCloseProfile (hOutProfile); +} +#endif + +/* + Convert the entire image to RGB colorspace and build a histogram. + */ +void CLASS convert_to_rgb() +{ + int row, col, c, i, fc=0; + ushort *img; + float rgb[3]; + + if (verbose) + fprintf (stderr, raw_color ? + "Building histograms...\n" : "Converting to sRGB colorspace...\n"); + + if (document_mode) + colors = 1; + memset (histogram, 0, sizeof histogram); + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) { + img = image[row*width+col]; + if (document_mode) + fc = FC(row,col); + if (colors == 4 && raw_color) /* Recombine the greens */ + img[1] = (img[1] + img[3]) >> 1; + if (colors == 1) /* RGB from grayscale */ + FORC3 rgb[c] = img[fc]; + else if (raw_color) /* RGB from RGB (easy) */ + goto norgb; + else FORC3 /* RGB via rgb_cam */ + for (rgb[c]=i=0; i < colors; i++) + rgb[c] += img[i] * rgb_cam[c][i]; + FORC3 img[c] = CLIP((int) rgb[c]); +norgb: + FORC3 histogram[c][img[c] >> 3]++; + } +} + +void CLASS fuji_rotate() +{ + int i, wide, high, row, col; + double step; + float r, c, fr, fc; + unsigned ur, uc; + ushort (*img)[4], (*pix)[4]; + + if (!fuji_width) return; + if (verbose) + fprintf (stderr, "Rotating image 45 degrees...\n"); + fuji_width = (fuji_width - 1 + shrink) >> shrink; + step = sqrt(0.5); + wide = fuji_width / step; + high = (height - fuji_width) / step; + img = calloc (wide*high, sizeof *img); + merror (img, "fuji_rotate()"); + + for (row=0; row < high; row++) + for (col=0; col < wide; col++) { + ur = r = fuji_width + (row-col)*step; + uc = c = (row+col)*step; + if (ur > height-2 || uc > width-2) continue; + fr = r - ur; + fc = c - uc; + pix = image + ur*width + uc; + for (i=0; i < colors; i++) + img[row*wide+col][i] = + (pix[ 0][i]*(1-fc) + pix[ 1][i]*fc) * (1-fr) + + (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr; + } + free (image); + width = wide; + height = high; + image = img; + fuji_width = 0; +} + +void CLASS flip_image() +{ + unsigned *flag; + int size, base, dest, next, row, col, temp; + INT64 *img, hold; + + if (verbose) + fprintf (stderr, "Flipping image %c:%c:%c...\n", + flip & 1 ? 'H':'0', flip & 2 ? 'V':'0', flip & 4 ? 'T':'0'); + + img = (INT64 *) image; + size = height * width; + flag = calloc ((size+31) >> 5, sizeof *flag); + merror (flag, "flip_image()"); + for (base = 0; base < size; base++) { + if (flag[base >> 5] & (1 << (base & 31))) + continue; + dest = base; + hold = img[base]; + while (1) { + if (flip & 4) { + row = dest % height; + col = dest / height; + } else { + row = dest / width; + col = dest % width; + } + if (flip & 2) + row = height - 1 - row; + if (flip & 1) + col = width - 1 - col; + next = row * width + col; + if (next == base) break; + flag[next >> 5] |= 1 << (next & 31); + img[dest] = img[next]; + dest = next; + } + img[dest] = hold; + } + free (flag); + if (flip & 4) { + temp = height; + height = width; + width = temp; + temp = ymag; + ymag = xmag; + xmag = temp; + } +} + +/* + Write the image to an 8-bit PPM file. + */ +void CLASS write_ppm (FILE *ofp) +{ + uchar (*ppm)[3], lut[0x10000]; + int perc, c, val, total, i, row, col; + float white=0, r; + + fprintf (ofp, "P6\n%d %d\n255\n", xmag*width, ymag*height); + ppm = calloc (width, 3*xmag); + merror (ppm, "write_ppm()"); + + perc = width * height * 0.01; /* 99th percentile white point */ + if (fuji_width) perc /= 2; + FORC3 { + for (val=0x2000, total=0; --val > 32; ) + if ((total += histogram[c][val]) > perc) break; + if (white < val) white = val; + } + white *= 8 / bright; + for (i=0; i < 0x10000; i++) { + r = i / white; + val = 256 * ( !use_gamma ? r : +#ifdef SRGB_GAMMA + r <= 0.00304 ? r*12.92 : pow(r,2.5/6)*1.055-0.055 ); +#else + r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 ); +#endif + if (val > 255) val = 255; + lut[i] = val; + } + for (row=0; row < height; row++) { + for (col=0; col < width; col++) + FORC3 for (i=0; i < xmag; i++) + ppm[xmag*col+i][c] = lut[image[row*width+col][c]]; + for (i=0; i < ymag; i++) + fwrite (ppm, width, 3*xmag, ofp); + } + free (ppm); +} + +/* + Write the image to a 16-bit Photoshop file. + */ +void CLASS write_psd (FILE *ofp) +{ + char head[] = { + '8','B','P','S', /* signature */ + 0,1,0,0,0,0,0,0, /* version and reserved */ + 0,3, /* number of channels */ + 0,0,0,0, /* height, big-endian */ + 0,0,0,0, /* width, big-endian */ + 0,16, /* 16-bit color */ + 0,3, /* mode (1=grey, 3=rgb) */ + 0,0,0,0, /* color mode data */ + 0,0,0,0, /* image resources */ + 0,0,0,0, /* layer/mask info */ + 0,0 /* no compression */ + }; + int hw[2], psize, row, col, c; + ushort *buffer, *pred; + + hw[0] = htonl(height); /* write the header */ + hw[1] = htonl(width); + memcpy (head+14, hw, sizeof hw); + fwrite (head, 40, 1, ofp); + + psize = height*width; + buffer = calloc (6, psize); + merror (buffer, "write_psd()"); + pred = buffer; + + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) { + FORC3 pred[c*psize] = htons(image[row*width+col][c]); + pred++; + } + fwrite(buffer, psize, 6, ofp); + free (buffer); +} + +/* + Write the image to a 16-bit PPM file. + */ +void CLASS write_ppm16 (FILE *ofp) +{ + int row, col, c; + ushort (*ppm)[3]; + + if (maximum < 256) maximum = 256; + fprintf (ofp, "P6\n%d %d\n%d\n", width, height, maximum); + + ppm = calloc (width, 6); + merror (ppm, "write_ppm16()"); + + for (row = 0; row < height; row++) { + for (col = 0; col < width; col++) + FORC3 ppm[col][c] = htons(image[row*width+col][c]); + fwrite (ppm, width, 6, ofp); + } + free (ppm); +} + +int CLASS main (int argc, char **argv) +{ + int arg, status=0, user_flip=-1, user_black=-1, user_qual=-1; + int timestamp_only=0, identify_only=0, write_to_stdout=0; + int half_size=0, use_fuji_rotate=1, quality; + char opt, *ofname, *cp; + struct utimbuf ut; + const char *write_ext = ".ppm"; + FILE *ofp = stdout; +#ifdef USE_LCMS + char *profile = NULL; +#endif + +#ifndef LOCALTIME + putenv ("TZ=UTC"); +#endif + if (argc == 1) + { + fprintf (stderr, + "\nRaw Photo Decoder \"dcraw\" v7.82" + "\nby Dave Coffin, dcoffin a cybercom o net" + "\n\nUsage: %s [options] file1 file2 ...\n" + "\nValid options:" + "\n-v Print verbose messages" + "\n-z Change file dates to camera timestamp" + "\n-i Identify files without decoding them" + "\n-c Write to standard output" + "\n-a Use automatic white balance" + "\n-w Use camera white balance, if possible" + "\n-r <num> Set red multiplier (default = 1.0)" + "\n-l <num> Set blue multiplier (default = 1.0)" + "\n-b <num> Set brightness (default = 1.0)" + "\n-k <num> Set black point" + "\n-n Don't clip colors" + "\n-m Don't convert camera RGB to sRGB" +#ifdef USE_LCMS + "\n-p <file> Apply color profile from file" +#endif + "\n-d Document Mode (no color, no interpolation)" + "\n-q [0-3] Set the interpolation quality" + "\n-h Half-size color image (twice as fast as \"-q 0\")" + "\n-f Interpolate RGGB as four colors" + "\n-B <domain> <range> Apply bilateral filter to reduce noise" + "\n-j Show Fuji Super CCD images tilted 45 degrees" + "\n-s Use secondary pixels (Fuji Super CCD SR only)" + "\n-t [0-7] Flip image (0 = none, 3 = 180, 5 = 90CCW, 6 = 90CW)" + "\n-2 Write 8-bit PPM with 0.45 gamma (default)" + "\n-3 Write 16-bit linear PSD (Adobe Photoshop)" + "\n-4 Write 16-bit linear PPM" + "\n\n", argv[0]); + return 1; + } + + argv[argc] = ""; + for (arg=1; argv[arg][0] == '-'; ) { + opt = argv[arg++][1]; + if ((strchr("Bbrlktq", opt) && !isdigit(argv[arg][0])) || + (opt == 'B' && !isdigit(argv[arg+1][0]))) { + fprintf (stderr, "Non-numeric argument to \"-%c\"\n", opt); + return 1; + } + switch (opt) + { + case 'B': sigma_d = atof(argv[arg++]); + sigma_r = atof(argv[arg++]); break; + case 'b': bright = atof(argv[arg++]); break; + case 'r': red_scale = atof(argv[arg++]); break; + case 'l': blue_scale = atof(argv[arg++]); break; + case 'k': user_black = atoi(argv[arg++]); break; + case 't': user_flip = atoi(argv[arg++]); break; + case 'q': user_qual = atoi(argv[arg++]); break; +#ifdef USE_LCMS + case 'p': profile = argv[arg++] ; break; +#endif + case 'z': timestamp_only = 1; break; + case 'i': identify_only = 1; break; + case 'c': write_to_stdout = 1; break; + case 'v': verbose = 1; break; + case 'h': half_size = 1; /* "-h" implies "-f" */ + case 'f': four_color_rgb = 1; break; + case 'd': document_mode = 1; break; + case 'a': use_auto_wb = 1; break; + case 'w': use_camera_wb = 1; break; + case 'j': use_fuji_rotate = 0; break; + case 's': use_secondary = 1; break; + case 'n': clip_color = 0; break; + case 'm': use_camera_rgb = 1; break; + + case '2': write_fun = write_ppm; write_ext = ".ppm"; break; + case '3': write_fun = write_psd; write_ext = ".psd"; break; + case '4': write_fun = write_ppm16; write_ext = ".ppm"; break; + + default: + fprintf (stderr, "Unknown option \"-%c\".\n", opt); + return 1; + } + } + if (arg == argc) { + fprintf (stderr, "No files to process.\n"); + return 1; + } + if (write_to_stdout) { + if (isatty(1)) { + fprintf (stderr, "Will not write an image to the terminal!\n"); + return 1; + } +#if defined(WIN32) || defined(DJGPP) || defined(__CYGWIN__) + if (setmode(1,O_BINARY) < 0) { + perror("setmode()"); + return 1; + } +#endif + } + for ( ; arg < argc; arg++) { + status = 1; + image = NULL; + if (setjmp (failure)) { + if (fileno(ifp) > 2) fclose(ifp); + if (fileno(ofp) > 2) fclose(ofp); + if (image) free (image); + status = 1; + continue; + } + ifname = argv[arg]; + if (!(ifp = fopen (ifname, "rb"))) { + perror (ifname); + continue; + } + if (timestamp_only) { + if ((status = identify(1))) + fprintf (stderr, "%s has no timestamp.\n", ifname); + else if (identify_only) + printf ("%10ld%10d %s\n", timestamp, shot_order, ifname); + else { + if (verbose) + fprintf (stderr, "%s time set to %d.\n", ifname, (int) timestamp); + ut.actime = ut.modtime = timestamp; + utime (ifname, &ut); + } + goto next; + } + if ((status = identify(0))) goto next; + if (user_flip >= 0) + flip = user_flip; + switch ((flip+3600) % 360) { + case 270: flip = 5; break; + case 180: flip = 3; break; + case 90: flip = 6; + } + if (identify_only) { + fprintf (stderr, "%s is a %s %s image.\n", ifname, make, model); +next: + fclose(ifp); + continue; + } + shrink = half_size && filters; + iheight = (height + shrink) >> shrink; + iwidth = (width + shrink) >> shrink; + image = calloc (iheight*iwidth*sizeof *image + meta_length, 1); + merror (image, "main()"); + meta_data = (char *) (image + iheight*iwidth); + if (verbose) + fprintf (stderr, + "Loading %s %s image from %s...\n", make, model, ifname); + (*load_raw)(); + bad_pixels(); + height = iheight; + width = iwidth; +#ifdef COLORCHECK + colorcheck(); +#endif + quality = 2 + !fuji_width; + if (user_qual >= 0) quality = user_qual; + if (user_black >= 0) black = user_black; + if (is_foveon) foveon_interpolate(); + else scale_colors(); + if (shrink) filters = 0; + cam_to_cielab (NULL,NULL); + if (filters && !document_mode) { + if (quality == 0) + lin_interpolate(); + else if (quality < 3 || colors > 3) + vng_interpolate(); + else ahd_interpolate(); + } + if (sigma_d > 0 && sigma_r > 0) bilateral_filter(); + if (use_fuji_rotate) fuji_rotate(); +#ifdef USE_LCMS + apply_profile (profile); +#endif + convert_to_rgb(); + if (flip) flip_image(); + fclose(ifp); + ofname = malloc (strlen(ifname) + 16); + merror (ofname, "main()"); + if (write_to_stdout) + strcpy (ofname, "standard output"); + else { + strcpy (ofname, ifname); + if ((cp = strrchr (ofname, '.'))) *cp = 0; + strcat (ofname, write_ext); + ofp = fopen (ofname, "wb"); + if (!ofp) { + status = 1; + perror(ofname); + goto cleanup; + } + } + if (verbose) + fprintf (stderr, "Writing data to %s...\n", ofname); + (*write_fun)(ofp); + if (ofp != stdout) + fclose(ofp); +cleanup: + free (ofname); + free (image); + } + return status; +} diff --git a/filters/krita/raw/kis_raw_import.cpp b/filters/krita/raw/kis_raw_import.cpp new file mode 100644 index 00000000..f9c1f5ea --- /dev/null +++ b/filters/krita/raw/kis_raw_import.cpp @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "config.h" + +#ifdef HAVE_SYS_TYPES_H + #include <sys/types.h> +#endif + +#include <sys/types.h> +#include <netinet/in.h> + +#include <qstring.h> +#include <qfile.h> +#include <qimage.h> +#include <qradiobutton.h> +#include <qgroupbox.h> +#include <qbuttongroup.h> +#include <qpushbutton.h> +#include <qlabel.h> +#include <qcheckbox.h> +#include <qapplication.h> +#include <qcursor.h> +#include <qeventloop.h> +#include <qprogressdialog.h> +#include <qtimer.h> + +#include <kglobal.h> +#include <kconfig.h> +#include <knuminput.h> +#include <kgenericfactory.h> +#include <kdialogbase.h> +#include <kdialog.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kprocess.h> + +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include "imageviewer.h" +#include "kis_config.h" +#include "kis_cmb_idlist.h" +#include "kis_types.h" +#include "kis_raw_import.h" +#include "kis_doc.h" +#include "kis_image.h" +#include "kis_meta_registry.h" +#include "kis_layer.h" +#include "kis_annotation.h" +#include "kis_profile.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_iterators_pixel.h" +#include "kis_abstract_colorspace.h" +#include "kis_paint_device.h" +#include "kis_paint_layer.h" +#include "wdgrawimport.h" + +typedef KGenericFactory<KisRawImport, KoFilter> KisRawImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkrita_raw_import, KisRawImportFactory("kofficefilters")) + +KisRawImport::KisRawImport(KoFilter *, const char *, const QStringList&) + : KoFilter() + , m_data(0) + , m_process(0) + , m_progress(0) + , m_err(false) +{ + m_dialog = new KDialogBase(); + m_dialog->enableButtonApply(false); + m_page = new WdgRawImport(m_dialog); + m_dialog -> setMainWidget(m_page); + + connect(m_page->bnPreview, SIGNAL(clicked()), this, SLOT(slotUpdatePreview())); + connect(m_page->grpColorSpace, SIGNAL(clicked( int )), this, SLOT(slotFillCmbProfiles())); + connect(m_page->grpChannelDepth, SIGNAL(clicked( int )), this, SLOT(slotFillCmbProfiles())); + + KisConfig cfg; + QString monitorProfileName = cfg.monitorProfile(); + m_monitorProfile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName); + + slotFillCmbProfiles(); +} + +KisRawImport::~KisRawImport() +{ + delete m_dialog; + delete m_process; +} + +KoFilter::ConversionStatus KisRawImport::convert(const QCString& from, const QCString& to) +{ + if (from != "image/x-raw" || to != "application/x-krita") { + return KoFilter::NotImplemented; + } + + if (m_err) { + return KoFilter::CreationError; + } + + kdDebug(41008) << "Krita importing from Raw\n"; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + if (!doc) { + return KoFilter::CreationError; + } + + doc -> prepareForImport(); + + QString filename = m_chain -> inputFile(); + + if (filename.isEmpty()) { + return KoFilter::FileNotFound; + } + + slotUpdatePreview(); + + // Show dialog + m_dialog->setCursor(Qt::ArrowCursor); + QApplication::setOverrideCursor(Qt::ArrowCursor); + + KConfig * cfg = KGlobal::config(); + cfg->setGroup("rawimport"); + + m_page->radioGray->setChecked(cfg->readBoolEntry("gray", false)); + m_page->radioRGB->setChecked(cfg->readBoolEntry("rgb", true)); + m_page->radio8->setChecked(cfg->readBoolEntry("8bit", false)); + m_page->radio16->setChecked(cfg->readBoolEntry("16bit", true)); + m_page->chkFourColorRGB->setChecked( cfg->readBoolEntry("four_color_rgb", false)); + m_page->chkCameraColors->setChecked( cfg->readBoolEntry("camera_colors", false)); + m_page->chkBrightness->setChecked( cfg->readBoolEntry("do_brightness", false)); + m_page->chkBlackpoint->setChecked( cfg->readBoolEntry("do_blackpoint", false)); + m_page->chkRed->setChecked( cfg->readBoolEntry("do_red", false)); + m_page->chkBlue->setChecked( cfg->readBoolEntry("do_blue", false)); + m_page->radioFixed->setChecked( cfg->readBoolEntry("fixed_wb", true)); + m_page->radioAutomatic->setChecked( cfg->readBoolEntry("automatic_wb", false)); + m_page->radioCamera->setChecked( cfg->readBoolEntry("camera_wb", false)); + m_page->chkClip->setChecked( cfg->readBoolEntry("clip", true)); + m_page->chkProfile->setChecked(cfg->readBoolEntry("useprofile", false)); + m_page->dblBrightness->setValue(cfg->readDoubleNumEntry("brightness", 1.0)); + m_page->dblBlackpoint->setValue(cfg->readDoubleNumEntry("blackpoint", 0)); + m_page->dblRed->setValue(cfg->readDoubleNumEntry("red", 1.0)); + m_page->dblBlue->setValue(cfg->readDoubleNumEntry("blue", 1.0)); + + if (m_dialog->exec() == QDialog::Accepted) { + + cfg->writeEntry("gray", m_page->radioGray->isChecked()); + cfg->writeEntry("rgb", m_page->radioRGB->isChecked()); + cfg->writeEntry("8bit", m_page->radio8->isChecked()); + cfg->writeEntry("16bit", m_page->radio16->isChecked()); + cfg->writeEntry("four_color_rgb", m_page->chkFourColorRGB -> isChecked()); + cfg->writeEntry("camera_colors", m_page->chkCameraColors -> isChecked()); + cfg->writeEntry("do_brightness", m_page->chkBrightness -> isChecked()); + cfg->writeEntry("do_blackpoint", m_page->chkBlackpoint -> isChecked()); + cfg->writeEntry("do_red", m_page->chkRed -> isChecked()); + cfg->writeEntry("do_blue", m_page->chkBlue -> isChecked()); + cfg->writeEntry("fixed_wb", m_page->radioFixed -> isChecked()); + cfg->writeEntry("automatic_wb", m_page->radioAutomatic -> isChecked()); + cfg->writeEntry("camera_wb", m_page->radioCamera -> isChecked()); + cfg->writeEntry("clip", m_page->chkClip->isChecked()); + cfg->writeEntry("useprofile", m_page->chkProfile->isChecked()); + cfg->writeEntry("brightness", m_page->dblBrightness->value()); + cfg->writeEntry("blackpoint", m_page->dblBlackpoint->value()); + cfg->writeEntry("red", m_page->dblRed->value()); + cfg->writeEntry("blue", m_page->dblBlue->value()); + + QApplication::setOverrideCursor(Qt::waitCursor); + // Create a busy indicator to show that we didn't die or so + m_progress = new QProgressDialog(); + m_progress -> setTotalSteps(0); + m_progress -> setCancelButton(0); + QTimer timer; + connect(&timer, SIGNAL(timeout()), this, SLOT(incrementProgress())); + timer.start(200); + + doc -> undoAdapter() -> setUndo(false); + + getImageData(createArgumentList(false)); + + KisImageSP image = 0; + KisPaintLayerSP layer = 0; + KisPaintDeviceSP device = 0; + + QApplication::restoreOverrideCursor(); + + delete m_progress; + m_progress = 0; + + if (m_page->radio8->isChecked()) { + // 8 bits + + QImage img; + img.loadFromData(*m_data); + + KisColorSpace * cs = 0; + if (m_page->radioGray->isChecked()) { + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA"), profile() ); + } + else { + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA"), profile() ); + } + if (cs == 0) { kdDebug() << "No CS\n"; return KoFilter::InternalError; } + + image = new KisImage(doc->undoAdapter(), img.width(), img.height(), cs, filename); + if (image == 0) return KoFilter::CreationError; + image->blockSignals(true); // Don't send out signals while we're building the image + + layer = dynamic_cast<KisPaintLayer*>( image->newLayer(image -> nextLayerName(), OPACITY_OPAQUE).data() ); + if (layer == 0) return KoFilter::CreationError; + + device = layer->paintDevice(); + if (device == 0) return KoFilter::CreationError; + + device->convertFromQImage(img, ""); + + } else { + // 16 bits + + Q_UINT32 startOfImagedata = 0; + QSize sz = determineSize(startOfImagedata); + + kdDebug(41008) << "Total bytes: " << m_data->size() + << "\n start of image data: " << startOfImagedata + << "\n bytes for pixels left: " << m_data->size() - startOfImagedata + << "\n total pixels: " << sz.width() * sz.height() + << "\n total pixel bytes: " << sz.width() * sz.height() * 6 + << "\n total necessary bytes: " << (sz.width() * sz.height() * 6) + startOfImagedata + << "\n"; + + + char * data = m_data->data() + startOfImagedata; + + KisColorSpace * cs = 0; + if (m_page->radioGray->isChecked()) { + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA16"), profile() ); + } + else { + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA16"), profile() ); + } + if (cs == 0) return KoFilter::InternalError; + + image = new KisImage( doc->undoAdapter(), sz.width(), sz.height(), cs, filename); + if (image == 0)return KoFilter::CreationError; + + layer = dynamic_cast<KisPaintLayer*> (image->newLayer(image -> nextLayerName(), OPACITY_OPAQUE).data()); + if (layer == 0) return KoFilter::CreationError; + + device = layer->paintDevice(); + if (device == 0) return KoFilter::CreationError; + + // Copy the colordata to the pixels + int pos = 0; + + for (int line = 0; line < sz.height(); ++line) { + KisHLineIterator it = device->createHLineIterator(0, line, sz.width(), true); + + while (!it.isDone()) { + if (m_page->radioGray->isChecked()) { + Q_UINT16 d = (Q_INT16)*(data + pos); + d = ntohs(d); + memcpy(it.rawData(), &d, 2); + pos += 2; + } + else { + // Red + Q_UINT16 d = (Q_INT16)*(data + pos); + d = ntohs(d); + memcpy(it.rawData() + 4, &d, 2); + + // Green + pos += 2; + d = (Q_INT16)*(data + pos ); + d = ntohs(d); + memcpy(it.rawData() + 2, &d, 2); + + // Blue + pos += 2; + d = (Q_INT16)*(data + pos ); + d = ntohs(d); + memcpy(it.rawData(), &d, 2); + + pos += 2; + } + cs->setAlpha(it.rawData(), OPACITY_OPAQUE, 1); + ++it; + } + } + } + layer->setDirty(); + kdDebug() << "going to set image\n"; + doc -> setCurrentImage(image); + doc -> undoAdapter() -> setUndo(true); + doc -> setModified(false); + kdDebug() << "everything ok\n"; + + QApplication::restoreOverrideCursor(); + return KoFilter::OK; + } + + QApplication::restoreOverrideCursor(); + return KoFilter::UserCancelled; +} + +void KisRawImport::incrementProgress() +{ + m_progress -> setProgress(m_progress -> progress() + 10); +} + + +void KisRawImport::slotUpdatePreview() +{ + QApplication::setOverrideCursor(Qt::waitCursor); + getImageData(createArgumentList(true)); + + kdDebug(41008) << "Retrieved " << m_data->size() << " bytes of image data\n"; + + if (m_data->isNull()) return; + + QImage img; + + if (m_page->radio8->isChecked()) { + // 8 bits + img.loadFromData(*m_data); + + } else { + // 16 bits + + Q_UINT32 startOfImagedata = 0; + QSize sz = determineSize(startOfImagedata); + + kdDebug(41008) << "Total bytes: " << m_data->size() + << "\n start of image data: " << startOfImagedata + << "\n bytes for pixels left: " << m_data->size() - startOfImagedata + << "\n total pixels: " << sz.width() * sz.height() + << "\n total pixel bytes: " << sz.width() * sz.height() * 6 + << "\n total necessary bytes: " << (sz.width() * sz.height() * 6) + startOfImagedata + << "\n"; + + char * data = m_data->data() + startOfImagedata; + + KisColorSpace * cs = 0; + if (m_page->radioGray->isChecked()) { + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA16"), profile() ); + } + else { + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA16"), profile() ); + } + KisPaintDevice * dev = new KisPaintDevice(cs, "preview"); + // Copy the colordata to the pixels + int pos = 0; + + for (int line = 0; line < sz.height(); ++line) { + KisHLineIterator it = dev->createHLineIterator(0, line, sz.width(), true); + + while (!it.isDone()) { + if (m_page->radioGray->isChecked()) { + Q_UINT16 d = (Q_INT16)*(data + pos); + d = ntohs(d); + memcpy(it.rawData(), &d, 2); + pos += 2; + } + else { + // Red + Q_UINT16 d = (Q_INT16)*(data + pos); + d = ntohs(d); + memcpy(it.rawData() + 4, &d, 2); + + // Green + pos += 2; + d = (Q_INT16)*(data + pos ); + d = ntohs(d); + memcpy(it.rawData() + 2, &d, 2); + + // Blue + pos += 2; + d = (Q_INT16)*(data + pos ); + d = ntohs(d); + memcpy(it.rawData(), &d, 2); + + pos += 2; + } + cs->setAlpha(it.rawData(), OPACITY_OPAQUE, 1); + ++it; + } + } + + img = dev->convertToQImage(m_monitorProfile); + } + + m_page->lblPreview->setImage(img); + QApplication::restoreOverrideCursor(); +} + + +void KisRawImport::getImageData( QStringList arguments ) +{ + // delete m_process; + delete m_data; + + kdDebug(41008) << "getImageData " << arguments.join(" ") << "\n"; + KProcess process (this); + m_data = new QByteArray(0); + + for (QStringList::iterator it = arguments.begin(); it != arguments.end(); ++it) { + process << *it; + } + + process.setUseShell(true); + connect(&process, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(slotReceivedStdout(KProcess *, char *, int))); + connect(&process, SIGNAL(receivedStderr(KProcess *, char *, int)), this, SLOT(slotReceivedStderr(KProcess *, char *, int))); + connect(&process, SIGNAL(processExited(KProcess *)), this, SLOT(slotProcessDone())); + + + kdDebug(41008) << "Starting process\n"; + + if (!process.start(KProcess::NotifyOnExit, KProcess::AllOutput)) { + KMessageBox::error( 0, i18n("Cannot convert RAW files because the dcraw executable could not be started.")); + } + while (process.isRunning()) { + //kdDebug(41008) << "Waiting...\n"; + qApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); + //process.wait(2); + } + + if (process.normalExit()) { + kdDebug(41008) << "Return value of process: " << process.exitStatus() << "\n"; + } + else { + kdDebug(41008) << "Process did not exit normally. Exit signal: " << process.exitSignal() << "\n"; + m_err = true; + } + +} + + +void KisRawImport::slotProcessDone() +{ + kdDebug(41008) << "process done!\n"; +} + +void KisRawImport::slotReceivedStdout(KProcess *, char *buffer, int buflen) +{ + //kdDebug(41008) << "stdout received " << buflen << " bytes on stdout.\n"; + //kdDebug(41008) << QString::fromAscii(buffer, buflen) << "\n"; + int oldSize = m_data->size(); + m_data->resize(oldSize + buflen, QGArray::SpeedOptim); + memcpy(m_data->data() + oldSize, buffer, buflen); +} + +void KisRawImport::slotReceivedStderr(KProcess *, char *buffer, int buflen) +{ + QByteArray b(buflen); + memcpy(b.data(), buffer, buflen); + kdDebug(41008) << QString(b) << "\n"; + KMessageBox::error(0, i18n("Error: Dcraw cannot load this image. Message: ") + QString(b)); + m_err = true; +} + + +QStringList KisRawImport::createArgumentList(bool forPreview) +{ + QStringList args; + + args.append("dcraw"); // XXX: Create a kritadcraw so we can count on it being available + + //args.append("-v"); // Verbose + + args.append("-c"); // Write to stdout + + if (forPreview) { + args.append("-h"); // Fast, half size image + } + + if (m_page->radio8->isChecked()) { + args.append("-2"); // 8 bits + } + else { + args.append("-4"); // 16 bits + } + + if (m_page->radioGray->isChecked()) { + args.append("-d"); // Create grayscale image + } + + if (m_page->chkCameraColors->isChecked()) { + args.append("-m"); // Use camera raw colors instead of sRGB + } + + if (m_page->radioAutomatic->isChecked()) { + args.append("-a"); // Automatic white balancing + } + + if (m_page->radioCamera->isChecked()) { + args.append("-w"); // Use camera white balance, if present + } + + if (m_page->chkFourColorRGB->isChecked()) { + args.append("-f"); // Interpolate RGB as four colors + } + + if (!m_page->chkClip->isChecked()) { + args.append("-n"); // Do not clip colors + } + + if (m_page->chkBrightness->isChecked()) { + args.append("-b " + QString::number(m_page->dblBrightness->value())); + } + + if (m_page->chkBlackpoint->isChecked()) { + args.append("-k " + QString::number(m_page->dblBlackpoint->value())); + } + + if (m_page->chkRed->isChecked()) { + args.append("-r " + QString::number(m_page->dblRed->value())); + } + + if (m_page->chkBlue->isChecked()) { + args.append("-l " + QString::number(m_page->dblBlue->value())); + } + + + KisProfile * pf = profile(); + if (m_page->chkProfile->isChecked()) { + if (!pf->filename().isNull()) { + // Use the user-set profile, if it's not an lcms internal + // profile. This does not add the profile to the image, we + // need to do that later. + args.append("-p \"" + pf->filename() + "\""); + } + } + + // Don't forget the filename + args.append("\"" + m_chain -> inputFile() + "\""); + + return args; +} + +QSize KisRawImport::determineSize(Q_UINT32& startOfImageData) +{ + if (m_data->isNull() || m_data->size() < 2048) { + startOfImageData = 0; + return QSize(0,0); + } + + QString magick = QString::fromAscii(m_data->data(), 2); + if (magick != "P6") { + kdDebug(41008) << " Bad magick! " << magick << "\n"; + startOfImageData = 0; + return QSize(0,0); + } + + // Find the third newline that marks the header end in a dcraw generated ppm. + Q_UINT32 i = 0; + Q_UINT32 counter = 0; + + while (true) { + if (counter == 3) break; + if (m_data->data()[i] == '\n') { + counter++; + } + ++i; + } + + QString size = QStringList::split("\n", QString::fromAscii(m_data->data(), i))[1]; + kdDebug(41008) << "Header: " << QString::fromAscii(m_data->data(), i) << "\n"; + QStringList sizelist = QStringList::split(" ", size); + Q_INT32 w = sizelist[0].toInt(); + Q_INT32 h = sizelist[1].toInt(); + + startOfImageData = i; + return QSize(w, h); + +} + +KisProfile * KisRawImport::profile() +{ + if (m_page->chkProfile->isChecked()) { + return KisMetaRegistry::instance()->csRegistry()->getProfileByName(m_page->cmbProfile->currentText()); + } + else + return 0; +} + +void KisRawImport::slotFillCmbProfiles() +{ + KisID s = getColorSpace(); + + KisColorSpaceFactory * csf = KisMetaRegistry::instance()->csRegistry() -> get(s); + m_page -> cmbProfile -> clear(); + QValueVector<KisProfile *> profileList = KisMetaRegistry::instance()->csRegistry()->profilesFor( csf ); + QValueVector<KisProfile *> ::iterator it; + for ( it = profileList.begin(); it != profileList.end(); ++it ) { + m_page -> cmbProfile -> insertItem((*it) -> productName()); + } +} + +KisID KisRawImport::getColorSpace() +{ + if (m_page->radioRGB->isChecked()) { + if (m_page->radio16->isChecked()) { + return KisID( "RGBA16" ); + } + } + else { + if (m_page->radio16->isChecked()) { + return KisID( "GRAYA16" ); + } + else { + return KisID( "GRAYA" ); + } + } + return KisID("RGBA"); +} + +#include "kis_raw_import.moc" diff --git a/filters/krita/raw/kis_raw_import.h b/filters/krita/raw/kis_raw_import.h new file mode 100644 index 00000000..cff059f7 --- /dev/null +++ b/filters/krita/raw/kis_raw_import.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_RAW_IMPORT_H_ +#define KIS_RAW_IMPORT_H_ + +#include <KoFilter.h> + +class KProcess; +class KDialogBase; +class WdgRawImport; +class KisProfile; +class QProgressDialog; + +class KisRawImport : public KoFilter { + Q_OBJECT + +public: + KisRawImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisRawImport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); + + +private slots: + + void slotUpdatePreview(); + void slotFillCmbProfiles(); + void slotProcessDone(); + void slotReceivedStdout(KProcess *proc, char *buffer, int buflen); + void slotReceivedStderr(KProcess *proc, char *buffer, int buflen); + void incrementProgress(); + +private: + + QStringList createArgumentList(bool forPreview = false); + QSize determineSize(Q_UINT32& startOfImageData); + void getImageData(QStringList arguments); + KisProfile * profile(); + KisID getColorSpace(); + +private: + QByteArray * m_data; + KDialogBase * m_dialog; + WdgRawImport * m_page; + KisProfile * m_monitorProfile; + KProcess * m_process; + QProgressDialog* m_progress; + bool m_err; // Set to true when slotReceivedStderr is called +}; + +#endif // KIS_RAW_IMPORT_H_ + diff --git a/filters/krita/raw/krita_raw.desktop b/filters/krita/raw/krita_raw.desktop new file mode 100644 index 00000000..4b4fd1fc --- /dev/null +++ b/filters/krita/raw/krita_raw.desktop @@ -0,0 +1,57 @@ +[Desktop Entry] +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Exec=krita %u +GenericName=Painting and Image Editing Application +GenericName[bg]=Редактор на графични изображения +GenericName[ca]=Programa de dibuix i manipulació d'imatges +GenericName[cs]=Malování a úpravy obrázků +GenericName[cy]=Cymhwysiad Peintio Golygu Delweddau +GenericName[da]=Male- og billedredigeringsprogram +GenericName[de]=Mal- und Bildbearbeitungsprogramm +GenericName[el]=Εφαρμογή επεξεργασίας εικόνων +GenericName[eo]=Aplikaĵo por Pentrado kaj Bildredaktado +GenericName[es]=Aplicación de pintura y de edición de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudien marrazketa eta ediziorako aplikazioa +GenericName[fa]=کاربرد ویرایش تصویر و نقاشی +GenericName[fi]=Maalaus- ja kuvankäsitelyohjelma +GenericName[fr]=Application de dessin et de manipulation d'images +GenericName[fy]=Ofbyldingsmanipulaasje +GenericName[gl]=Aplicación de Pintura e Manipulación de Imaxes +GenericName[he]=יישום לציור ועריכת תמונות +GenericName[hr]=Aplikacija za obradu slika i fotografija +GenericName[hu]=Képszerkesztő +GenericName[is]=Málun og myndritill +GenericName[it]=Applicazione di disegno e di modifica delle immagini +GenericName[ja]=描画と画像編集のためのアプリケーション +GenericName[km]=កម្មវិធីគូរគំនូរ និងកែសម្រួលរូបភាព +GenericName[lv]=Zīmēšanas un attēlu apstrādes programma +GenericName[nb]=Program for tegning og bilderedigering +GenericName[nds]=Programm för't Malen un Bildbewerken +GenericName[ne]=पेन्टीङ्ग र छवि सम्पादन अनुप्रयोग +GenericName[nl]=Afbeeldingsmanipulatie +GenericName[pl]=Program do edycji zdjęć oraz rysunków +GenericName[pt]=Aplicação de Pintura e Edição de Imagens +GenericName[pt_BR]=Aplicação de Pintura e Edição de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Málen- ja govvagieđahallanprográmma +GenericName[sk]=Program pre tvorbu a úpravu obrázkov +GenericName[sl]=Program za risanje in obdelavo slik +GenericName[sr]=Програм за цртање и уређивање слика +GenericName[sr@Latn]=Program za crtanje i uređivanje slika +GenericName[sv]=Målnings- och bildredigeringsprogram +GenericName[uk]=Програма для малювання і редагування зображень +GenericName[uz]=Rasmlar bilan ishlaydigan dastur +GenericName[uz@cyrillic]=Расмлар билан ишлайдиган дастур +GenericName[zh_CN]=绘图和图像编辑应用程序 +GenericName[zh_TW]=繪圖與影像處理程式 +MimeType=image/x-raw +Type=Application +Icon=krita +Categories= +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi diff --git a/filters/krita/raw/krita_raw_import.desktop b/filters/krita/raw/krita_raw_import.desktop new file mode 100644 index 00000000..61cb4da1 --- /dev/null +++ b/filters/krita/raw/krita_raw_import.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Type=Service +Name=Krita RAW Import Filter +Name[bg]=Филтър за импортиране на RAW в Krita +Name[br]=Sil enporzh RAW evit Krita +Name[ca]=Filtre d'importació RAW per a Krita +Name[da]=Krita RAW-importfilter +Name[de]=Krita RAW-Importfilter +Name[el]=Φίλτρο εισαγωγής RAW του Krita +Name[eo]=Krita RAW-importfiltrilo +Name[es]=Filtro de importación a RAW de Krita +Name[et]=Krita toorpiltide impordifilter +Name[fa]=پالایۀ واردات Krita RAW +Name[fi]=Krita RAW -tuontisuodin +Name[fr]=Filtre d'importation RAW de Krita +Name[fy]=Krita RAW Ymportfilter +Name[ga]=Scagaire Iompórtála RAW Krita +Name[gl]=Filtro de Importación RAW para Krita +Name[he]=Krita RAW מסנן יבוא +Name[hr]=Krita RAW filtar uvoza +Name[hu]=Krita RAW importszűrő +Name[is]=Krita RAW innflutningssía +Name[it]=Filtro di importazione di formati grezzi per Krita +Name[ja]=Krita RAW インポートフィルタ +Name[km]=តម្រងនាំចូល RAW សម្រាប់ Krita +Name[lt]=Krita RAW importavimo filtras +Name[lv]=Krita RAW importa filtrs +Name[nb]=RAW importfilter for Krita +Name[nds]=RAW-Importfilter för Krita +Name[ne]=क्रिता RAW आयात फिल्टर +Name[nl]=Krita RAW Importfilter +Name[pl]=Filtr importu formatu RAW dla Krita +Name[pt]=Filtro de Importação RAW para o Krita +Name[pt_BR]=Filtro de Importação RAW para o Krita +Name[ru]=Фильтр импорта рисунков RAW в Krita +Name[se]=Krita RAW-sisafievrridansilli +Name[sk]=RAW filter pre import do Krita +Name[sl]=Uvozni filter RAW za Krito +Name[sr]=Krita-ин филтер за увоз из RAW-а +Name[sr@Latn]=Krita-in filter za uvoz iz RAW-a +Name[sv]=Krita RAW-importfilter +Name[uk]=Фільтр імпорту RAW для Krita +Name[uz]=Krita RAW import filteri +Name[uz@cyrillic]=Krita RAW импорт филтери +Name[zh_CN]=Krita RAW 导入过滤器 +Name[zh_TW]=Krita RAW 匯入過濾程式 +X-KDE-Export=application/x-krita +X-KDE-Import=image/x-raw +X-KDE-Weight=1 +X-KDE-Library=libkrita_raw_import +ServiceTypes=KOfficeFilter diff --git a/filters/krita/raw/wdgrawimport.ui b/filters/krita/raw/wdgrawimport.ui new file mode 100644 index 00000000..af2163ad --- /dev/null +++ b/filters/krita/raw/wdgrawimport.ui @@ -0,0 +1,496 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>WdgRawImport</class> +<widget class="QWidget"> + <property name="name"> + <cstring>WdgRawImport</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>835</width> + <height>596</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton" row="2" column="2"> + <property name="name"> + <cstring>bnPreview</cstring> + </property> + <property name="text"> + <string>&Update Preview</string> + </property> + </widget> + <widget class="ImageViewer" row="0" column="1" rowspan="2" colspan="2"> + <property name="name"> + <cstring>lblPreview</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>150</height> + </size> + </property> + </widget> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>grpColorSettings</cstring> + </property> + <property name="title"> + <string>Color Settings</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>chkBlackpoint</cstring> + </property> + <property name="text"> + <string>Blackpoint:</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>chkRed</cstring> + </property> + <property name="text"> + <string>Red multiplier:</string> + </property> + </widget> + <widget class="KDoubleSpinBox" row="2" column="1"> + <property name="name"> + <cstring>dblRed</cstring> + </property> + <property name="minValue"> + <number>0.1</number> + </property> + <property name="value"> + <number>1</number> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>chkBlue</cstring> + </property> + <property name="text"> + <string>Blue multiplier:</string> + </property> + </widget> + <widget class="KDoubleSpinBox" row="3" column="1"> + <property name="name"> + <cstring>dblBlue</cstring> + </property> + <property name="minValue"> + <number>0.1</number> + </property> + <property name="value"> + <number>1</number> + </property> + </widget> + <widget class="KDoubleSpinBox" row="1" column="1"> + <property name="name"> + <cstring>dblBlackpoint</cstring> + </property> + </widget> + <widget class="KDoubleSpinBox" row="0" column="1"> + <property name="name"> + <cstring>dblBrightness</cstring> + </property> + <property name="minValue"> + <number>0.1</number> + </property> + <property name="value"> + <number>2</number> + </property> + <property name="toolTip" stdset="0"> + <string>Brightness. 1.0 is default</string> + </property> + </widget> + <widget class="QButtonGroup" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>grpWhiteBalance</cstring> + </property> + <property name="title"> + <string>&White Balance</string> + </property> + <property name="exclusive"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioFixed</cstring> + </property> + <property name="text"> + <string>White card in sunlight</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioAutomatic</cstring> + </property> + <property name="text"> + <string>Automatic</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Automatic color balance. The default is to use a fixed color balance based on a white card photographed in sunlight.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioCamera</cstring> + </property> + <property name="text"> + <string>From camera</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string> + Use the color balance specified by the camera. If this cannot be found, dcraw prints a warning and reverts to the default. </string> + </property> + </widget> + </vbox> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>chkBrightness</cstring> + </property> + <property name="text"> + <string>Brightness:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>grpColorSpace</cstring> + </property> + <property name="title"> + <string>Colorspace</string> + </property> + <property name="exclusive"> + <bool>true</bool> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>radioGray</cstring> + </property> + <property name="text"> + <string>&Document mode</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>radioRGB</cstring> + </property> + <property name="text"> + <string>&RGB</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>grpChannelDepth</cstring> + </property> + <property name="title"> + <string>Channel Depth</string> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <property name="exclusive"> + <bool>true</bool> + </property> + <property name="selectedId" stdset="0"> + <number>0</number> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>radio16</cstring> + </property> + <property name="text"> + <string>&16 bits per channel</string> + </property> + <property name="accel"> + <string>Alt+1</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>radio8</cstring> + </property> + <property name="text"> + <string>&8 bits per channel</string> + </property> + <property name="accel"> + <string>Alt+8</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkFourColorRGB</cstring> + </property> + <property name="text"> + <string>&Interpolate RGB as four colors</string> + </property> + <property name="toolTip" stdset="0"> + <string>Interpolate RGB as four colors. This blurs the image a little, but it eliminates false 2x2 mesh patterns. </string> + </property> + </widget> + </vbox> + </widget> + <spacer row="2" column="1"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>500</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="1" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>120</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkClip</cstring> + </property> + <property name="text"> + <string>Clip colors to prevent pink highlights</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>By default, dcraw clips all colors to prevent pink hues in the highlights. Combine this option with -b 0.25 to leave the image data completely unclipped.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkProfile</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="KisCmbIDList"> + <property name="name"> + <cstring>cmbProfile</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkCameraColors</cstring> + </property> + <property name="text"> + <string>Use camera raw colors, not sRGB</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>41</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </grid> +</widget> +<customwidgets> + <customwidget> + <class>KisCmbIDList</class> + <header location="local">kis_cmb_idlist.h</header> + <sizehint> + <width>1</width> + <height>24</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> + <customwidget> + <class>ImageViewer</class> + <header location="local">imageviewer.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image1</pixmap> + <signal>moved(QPoint)</signal> + <signal>moving(QPoint)</signal> + <signal>startMoving(QPoint)</signal> + <slot access="public" specifier="">zoomIn()</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">zoomOut()</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">slotMoving(QPoint)</slot> + <slot access="public" specifier="">slotMoved(QPoint)</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">slotStartMoving(QPoint)</slot> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data> + </image> + <image name="image1"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> +</images> +<tabstops> + <tabstop>radioRGB</tabstop> + <tabstop>radio16</tabstop> + <tabstop>chkBrightness</tabstop> + <tabstop>dblBrightness</tabstop> + <tabstop>chkBlackpoint</tabstop> + <tabstop>dblBlackpoint</tabstop> + <tabstop>chkRed</tabstop> + <tabstop>dblRed</tabstop> + <tabstop>chkBlue</tabstop> + <tabstop>dblBlue</tabstop> + <tabstop>radioFixed</tabstop> + <tabstop>chkFourColorRGB</tabstop> + <tabstop>chkClip</tabstop> + <tabstop>chkProfile</tabstop> + <tabstop>chkCameraColors</tabstop> + <tabstop>bnPreview</tabstop> + <tabstop>radioAutomatic</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/filters/krita/tiff/Makefile.am b/filters/krita/tiff/Makefile.am new file mode 100644 index 00000000..13eb5810 --- /dev/null +++ b/filters/krita/tiff/Makefile.am @@ -0,0 +1,49 @@ +kde_module_LTLIBRARIES = libkritatiffimport.la libkritatiffexport.la + +libkritatiffexport_la_LDFLAGS = -avoid-version -module -no-undefined \ + $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) +libkritatiffexport_la_LIBADD = $(top_builddir)/krita/libkritacommon.la \ + libkritatiffconverter.la $(KOFFICE_LIBS) -ltiff + +libkritatiffimport_la_LDFLAGS = -avoid-version -module -no-undefined \ + $(KDE_PLUGIN) $(KDE_RPATH) $(all_libraries) +libkritatiffimport_la_LIBADD = $(top_builddir)/krita/libkritacommon.la \ + libkritatiffconverter.la $(KOFFICE_LIBS) -ltiff + +INCLUDES= \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) \ + $(all_includes) + + +servicedir = $(kde_servicesdir) + + +kdelnkdir = $(kde_appsdir)/.hidden + + + + +METASOURCES = AUTO + + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) +libkritatiffimport_la_SOURCES = kis_tiff_import.cc +libkritatiffexport_la_SOURCES = kis_tiff_export.cc kis_wdg_options_tiff.ui \ + kis_dlg_options_tiff.cpp +service_DATA = krita_tiff_export.desktop krita_tiff_import.desktop +kdelnk_DATA = krita_tiff.desktop +noinst_HEADERS = kis_dlg_options_tiff.h kis_tiff_writer_visitor.h \ + kis_tiff_ycbcr_reader.h +libkritatiffconverter_la_LDFLAGS = -no-undefined $(all_libraries) +noinst_LTLIBRARIES = libkritatiffconverter.la +libkritatiffconverter_la_SOURCES = kis_tiff_converter.cc kis_tiff_stream.cc \ + kis_tiff_writer_visitor.cpp kis_tiff_reader.cc kis_tiff_ycbcr_reader.cc diff --git a/filters/krita/tiff/configure.in.bot b/filters/krita/tiff/configure.in.bot new file mode 100644 index 00000000..aea71e8c --- /dev/null +++ b/filters/krita/tiff/configure.in.bot @@ -0,0 +1,7 @@ +if test -z "$LIBTIFF"; then + echo "" + echo "You're missing libtiff (binaries and/or headers), krita won't be able" + echo "to import/export tiff" + echo "" + all_tests=bad +fi diff --git a/filters/krita/tiff/kis_dlg_options_tiff.cpp b/filters/krita/tiff/kis_dlg_options_tiff.cpp new file mode 100644 index 00000000..6b9f3027 --- /dev/null +++ b/filters/krita/tiff/kis_dlg_options_tiff.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_dlg_options_tiff.h" + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qslider.h> +#include <qwidgetstack.h> + +#include <kapplication.h> +#include <kcombobox.h> +#include <klocale.h> + +#include "kis_wdg_options_tiff.h" + +KisDlgOptionsTIFF::KisDlgOptionsTIFF(QWidget *parent, const char *name) + : KDialogBase(parent, name, false, i18n("TIFF Export Options"), KDialogBase::Ok | KDialogBase::Cancel) +{ + optionswdg = new KisWdgOptionsTIFF(this); + activated(0); + connect(optionswdg->kComboBoxCompressionType, SIGNAL(activated ( int )), this, SLOT(activated ( int ) )); + connect(optionswdg->flatten, SIGNAL(toggled(bool)), this, SLOT(flattenToggled( bool) ) ); + setMainWidget(optionswdg); + kapp->restoreOverrideCursor(); + setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum) ); +} + +KisDlgOptionsTIFF::~KisDlgOptionsTIFF() +{ +} + +void KisDlgOptionsTIFF::activated ( int index ) +{ +/* optionswdg->groupBoxJPEG->hide(); + optionswdg->groupBoxDeflate->hide(); + optionswdg->groupBoxCCITGroupCCITG3->hide(); + optionswdg->groupBoxPixarLog->hide();*/ + switch(index) + { + case 1: + optionswdg->codecsOptionsStack->raiseWidget(1); +// optionswdg->groupBoxJPEG->show(); + break; + case 2: + optionswdg->codecsOptionsStack->raiseWidget(2); +// optionswdg->groupBoxDeflate->show(); + break; + case 6: + optionswdg->codecsOptionsStack->raiseWidget(3); +// optionswdg->groupBoxCCITGroupCCITG3->show(); + break; + case 8: + optionswdg->codecsOptionsStack->raiseWidget(4); +// optionswdg->groupBoxPixarLog->show(); + break; + default: + optionswdg->codecsOptionsStack->raiseWidget(0); + } +} + +void KisDlgOptionsTIFF::flattenToggled(bool t) +{ + optionswdg->alpha->setEnabled(t); + if(!t) + { + optionswdg->alpha->setChecked(true); + } +} + + +KisTIFFOptions KisDlgOptionsTIFF::options() +{ + KisTIFFOptions options; + switch(optionswdg->kComboBoxCompressionType->currentItem ()) + { + case 0: + options.compressionType = COMPRESSION_NONE; + break; + case 1: + options.compressionType = COMPRESSION_JPEG; + break; + case 2: + options.compressionType = COMPRESSION_DEFLATE; + break; + case 3: + options.compressionType = COMPRESSION_LZW; + break; +#ifdef COMPRESSION_JP2000 + case 4: + options.compressionType = COMPRESSION_JP2000; + break; +#endif + case 5: + options.compressionType = COMPRESSION_CCITTRLE; + break; + case 6: + options.compressionType = COMPRESSION_CCITTFAX3; + break; + case 7: + options.compressionType = COMPRESSION_CCITTFAX4; + break; + case 8: + options.compressionType = COMPRESSION_PIXARLOG; + break; + } + options.predictor = optionswdg->kComboBoxPredictor->currentItem() + 1; + options.alpha = optionswdg->alpha->isChecked(); + options.flatten = optionswdg->flatten->isChecked(); + options.jpegQuality = optionswdg->qualityLevel->value(); + options.deflateCompress = optionswdg->compressionLevelDeflate->value(); + options.faxMode = optionswdg->kComboBoxFaxMode->currentItem() + 1; + options.pixarLogCompress = optionswdg->compressionLevelPixarLog->value(); + + return options; +} + +#include "kis_dlg_options_tiff.moc" diff --git a/filters/krita/tiff/kis_dlg_options_tiff.h b/filters/krita/tiff/kis_dlg_options_tiff.h new file mode 100644 index 00000000..a81bd65f --- /dev/null +++ b/filters/krita/tiff/kis_dlg_options_tiff.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_DLG_OPTIONS_TIFF_H +#define KIS_DLG_OPTIONS_TIFF_H + +#include <kdialogbase.h> +#include <kis_tiff_converter.h> + +class KisWdgOptionsTIFF; +/** + @author Cyrille Berger <[email protected]> +*/ +class KisDlgOptionsTIFF : public KDialogBase +{ + Q_OBJECT + public: + KisDlgOptionsTIFF(QWidget *parent=0, const char *name=0); + ~KisDlgOptionsTIFF(); + public slots: + void activated ( int index ); + void flattenToggled(bool); + KisTIFFOptions options(); + public: + KisWdgOptionsTIFF* optionswdg; +}; + +#endif diff --git a/filters/krita/tiff/kis_tiff_converter.cc b/filters/krita/tiff/kis_tiff_converter.cc new file mode 100644 index 00000000..955ab9ef --- /dev/null +++ b/filters/krita/tiff/kis_tiff_converter.cc @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_tiff_converter.h" + +#include <stdio.h> + +#include <config.h> +#include LCMS_HEADER + +#include <qfile.h> + +#include <kapplication.h> +#include <KoDocumentInfo.h> + +#include <kio/netaccess.h> + +#include <kis_abstract_colorspace.h> +#include <kis_colorspace_factory_registry.h> +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_iterators_pixel.h> +#include <kis_layer.h> +#include <kis_meta_registry.h> +#include <kis_profile.h> +#include <kis_group_layer.h> +#include <kis_paint_layer.h> + +#include "kis_tiff_reader.h" +#include "kis_tiff_ycbcr_reader.h" +#include "kis_tiff_stream.h" +#include "kis_tiff_writer_visitor.h" + +namespace { + + QString getColorSpaceForColorType(uint16 color_type, uint16 color_nb_bits, TIFF *image, uint16 &nbchannels, uint16 &extrasamplescount, uint8 &destDepth, uint16 sampletype) { + if(color_type == PHOTOMETRIC_MINISWHITE || color_type == PHOTOMETRIC_MINISBLACK) + { + if(nbchannels == 0) nbchannels = 1; + extrasamplescount = nbchannels - 1; // FIX the extrasamples count in case of + if(color_nb_bits <= 8) + { + destDepth = 8; + return "GRAYA"; + } else { + destDepth = 16; + return "GRAYA16"; + } + } else if(color_type == PHOTOMETRIC_RGB /*|| color_type == */ ) { + if(nbchannels == 0) nbchannels = 3; + extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of + if(sampletype == SAMPLEFORMAT_IEEEFP) + { + if(color_nb_bits == 16) + { + destDepth = 16; + return "RGBAF16HALF"; + } else if( color_nb_bits == 32) { + destDepth = 32; + return "RGBAF32"; + } + return ""; + } else { + if(color_nb_bits <= 8) + { + destDepth = 8; + return "RGBA"; + } else { + destDepth = 16; + return "RGBA16"; + } + } + } else if(color_type == PHOTOMETRIC_YCBCR ) { + if(nbchannels == 0) nbchannels = 3; + extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of + if(color_nb_bits <= 8) + { + destDepth = 8; + return "YCbCrAU8"; + } else { + destDepth = 16; + return "YCbCrAU16"; + } + } else if(color_type == PHOTOMETRIC_SEPARATED ) { + if(nbchannels == 0) nbchannels = 4; + // SEPARATED is in general CMYK but not allways, so we check + uint16 inkset; + if((TIFFGetField(image, TIFFTAG_INKSET, &inkset) == 0)){ + kdDebug(41008) << "Image does not define the inkset." << endl; + inkset = 2; + } + if(inkset != INKSET_CMYK) + { + kdDebug(41008) << "Unsupported inkset (right now, only CMYK is supported)" << endl; + char** ink_names; + uint16 numberofinks; + if( TIFFGetField(image, TIFFTAG_INKNAMES, &ink_names) && TIFFGetField(image, TIFFTAG_NUMBEROFINKS, &numberofinks) ) + { + kdDebug(41008) << "Inks are : " << endl; + for(uint i = 0; i < numberofinks; i++) + { + kdDebug(41008) << ink_names[i] << endl; + } + } else { + kdDebug(41008) << "inknames aren't defined !" << endl; + // To be able to read stupid adobe files, if there are no information about inks and four channels, then it's a CMYK file : + if( nbchannels - extrasamplescount != 4) + { + return ""; + } + } + } + if(color_nb_bits <= 8) + { + destDepth = 8; + return "CMYK"; + } else { + destDepth = 16; + return "CMYKA16"; + } + } else if(color_type == PHOTOMETRIC_CIELAB +#ifdef PHOTOMETRIC_ICCLAB + || color_type == PHOTOMETRIC_ICCLAB +#endif + ) { + destDepth = 16; + if(nbchannels == 0) nbchannels = 3; + extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of + return "LABA"; // TODO add support for a 8bit LAB colorspace when it is written + } else if(color_type == PHOTOMETRIC_PALETTE) { + destDepth = 16; + if(nbchannels == 0) nbchannels = 2; + extrasamplescount = nbchannels - 2; // FIX the extrasamples count in case of + // <-- we will convert the index image to RGBA16 as the palette is allways on 16bits colors + return "RGBA16"; + } + return ""; + } +} + +KisTIFFConverter::KisTIFFConverter(KisDoc *doc, KisUndoAdapter *adapter) +{ + m_doc = doc; + m_adapter = adapter; + m_job = 0; + m_stop = false; +} + +KisTIFFConverter::~KisTIFFConverter() +{ +} + +KisImageBuilder_Result KisTIFFConverter::decode(const KURL& uri) +{ + kdDebug(41008) << "Start decoding TIFF File" << endl; + // Opent the TIFF file + TIFF *image = 0; + if((image = TIFFOpen(QFile::encodeName(uri.path()), "r")) == NULL){ + kdDebug(41008) << "Could not open the file, either it doesn't exist, either it is not a TIFF : " << uri.path() << endl; + + return (KisImageBuilder_RESULT_BAD_FETCH); + } + do { + kdDebug(41008) << "Read new sub-image" << endl; + KisImageBuilder_Result result = readTIFFDirectory(image); + if(result != KisImageBuilder_RESULT_OK){ + return result; + } + } while (TIFFReadDirectory(image)); + // Freeing memory + TIFFClose(image); + return KisImageBuilder_RESULT_OK; +} + +KisImageBuilder_Result KisTIFFConverter::readTIFFDirectory( TIFF* image) +{ + // Read information about the tiff + uint32 width, height; + if(TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width) == 0){ + kdDebug(41008) << "Image does not define its width" << endl; + TIFFClose(image); + return KisImageBuilder_RESULT_INVALID_ARG; + } + if(TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height) == 0){ + kdDebug(41008) << "Image does not define its height" << endl; + TIFFClose(image); + return KisImageBuilder_RESULT_INVALID_ARG; + } + uint16 depth; + if((TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &depth) == 0)){ + kdDebug(41008) << "Image does not define its depth" << endl; + depth = 1; + } + uint16 sampletype; + if((TIFFGetField(image, TIFFTAG_SAMPLEFORMAT, &sampletype) == 0)){ + kdDebug(41008) << "Image does not define its sample type" << endl; + sampletype = SAMPLEFORMAT_UINT; + } + // Determine the number of channels (usefull to know if a file has an alpha or not + uint16 nbchannels; + if(TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &nbchannels) == 0){ + kdDebug(41008) << "Image has an undefined number of samples per pixel" << endl; + nbchannels = 0; + } + // Get the number of extrasamples and information about them + uint16 *sampleinfo, extrasamplescount; + if(TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extrasamplescount, &sampleinfo) == 0) + { + extrasamplescount = 0; + } + // Determine the colorspace + uint16 color_type; + if(TIFFGetField(image, TIFFTAG_PHOTOMETRIC, &color_type) == 0){ + kdDebug(41008) << "Image has an undefined photometric interpretation" << endl; + color_type = PHOTOMETRIC_MINISWHITE; + } + uint8 dstDepth; + QString csName = getColorSpaceForColorType(color_type, depth, image, nbchannels, extrasamplescount, dstDepth,sampletype); + if(csName.isEmpty()) { + kdDebug(41008) << "Image has an unsupported colorspace : " << color_type << " for this depth : "<< depth << endl; + TIFFClose(image); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + kdDebug(41008) << "Colorspace is : " << csName << " with a depth of " << depth << " and with a nb of channels of " << nbchannels << endl; + + // Read image profile + kdDebug() << "Reading profile" << endl; + KisProfile* profile = 0; + DWORD EmbedLen; + LPBYTE EmbedBuffer; + + if (TIFFGetField(image, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer)) { + kdDebug(41008) << "Profile found" << endl; + QByteArray rawdata; + rawdata.resize(EmbedLen); + memcpy(rawdata.data(), EmbedBuffer, EmbedLen); + profile = new KisProfile(rawdata); + } else { + kdDebug(41008) << "No Profile found" << endl; + } + + // Retrieve a pointer to the colorspace + KisColorSpace* cs = 0; + if (profile && profile->isSuitableForOutput()) + { + kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n"; + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile); + } + else + cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),""); + + if(cs == 0) { + kdDebug(41008) << "Colorspace " << csName << " is not available, please check your installation." << endl; + TIFFClose(image); + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } + + // Create the cmsTransform if needed + cmsHTRANSFORM transform = 0; + if(profile && !profile->isSuitableForOutput()) + { + kdDebug(41008) << "The profile can't be used in krita, need conversion" << endl; + transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(), + cs->getProfile()->profile() , cs->colorSpaceType(), + INTENT_PERCEPTUAL, 0); + } + + + // Check if there is an alpha channel + int8 alphapos = -1; // <- no alpha + // Check which extra is alpha if any + kdDebug(41008) << "There are " << nbchannels << " channels and " << extrasamplescount << " extra channels" << endl; + if(sampleinfo) // index images don't have any sampleinfo, and therefor sampleinfo == 0 + { + for(int i = 0; i < extrasamplescount; i ++) + { + kdDebug(41008) << i << " " << extrasamplescount << " " << (cs->nColorChannels()) << nbchannels << " " << sampleinfo[i] << endl; + if(sampleinfo[i] == EXTRASAMPLE_ASSOCALPHA) + { + // XXX: dangelo: the color values are already multiplied with + // the alpha value. This needs to be reversed later (postprocessor?) + alphapos = i; + } + + if (sampleinfo[i] == EXTRASAMPLE_UNASSALPHA) + { + // color values are not premultiplied with alpha, and can be used as they are. + alphapos = i; + } + } + } + + // Read META Information + KoDocumentInfo * info = m_doc->documentInfo(); + KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" )); + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author")); + char* text; + if (TIFFGetField(image, TIFFTAG_ARTIST, &text)) { + authorPage->setFullName(text); + } + if (TIFFGetField(image, TIFFTAG_DOCUMENTNAME, &text)) { + aboutPage->setTitle(text); + } + if (TIFFGetField(image,TIFFTAG_IMAGEDESCRIPTION,&text) ) { + aboutPage->setAbstract( text ); + } + + + // Get the planar configuration + uint16 planarconfig; + if(TIFFGetField(image, TIFFTAG_PLANARCONFIG, &planarconfig) == 0) + { + kdDebug(41008) << "Plannar configuration is not define" << endl; + TIFFClose(image); + return KisImageBuilder_RESULT_INVALID_ARG; + } + // Creating the KisImageSP + if( ! m_img ) { + m_img = new KisImage(m_doc->undoAdapter(), width, height, cs, "built image"); + Q_CHECK_PTR(m_img); + m_img->blockSignals(true); // Don't send out signals while we're building the image + if(profile) + { + m_img -> addAnnotation( profile->annotation() ); + } + } else { + if( m_img->width() < (Q_INT32)width || m_img->height() < (Q_INT32)height) + { + Q_UINT32 newwidth = (m_img->width() < (Q_INT32)width) ? width : m_img->width(); + Q_UINT32 newheight = (m_img->height() < (Q_INT32)height) ? height : m_img->height(); + m_img->resize(newwidth, newheight, false); + } + } + KisPaintLayer* layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), Q_UINT8_MAX); + tdata_t buf = 0; + tdata_t* ps_buf = 0; // used only for planar configuration seperated + TIFFStreamBase* tiffstream; + + KisTIFFReaderBase* tiffReader = 0; + + Q_UINT8 poses[5]; + KisTIFFPostProcessor* postprocessor = 0; + + // Configure poses + uint8 nbcolorsamples = nbchannels - extrasamplescount; + switch(color_type) + { + case PHOTOMETRIC_MINISWHITE: + { + poses[0] = 0; poses[1] = 1; + postprocessor = new KisTIFFPostProcessorInvert(nbcolorsamples); + } + break; + case PHOTOMETRIC_MINISBLACK: + { + poses[0] = 0; poses[1] = 1; + postprocessor = new KisTIFFPostProcessor(nbcolorsamples); + } + break; + case PHOTOMETRIC_CIELAB: + { + poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3; + postprocessor = new KisTIFFPostProcessorICCLABtoCIELAB(nbcolorsamples); + } + break; +#ifdef PHOTOMETRIC_ICCLAB + case PHOTOMETRIC_ICCLAB: + { + poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3; + postprocessor = new KisTIFFPostProcessor(nbcolorsamples); + } + break; +#endif + case PHOTOMETRIC_RGB: + { + poses[0] = 2; poses[1] = 1; poses[2] = 0; poses[3] = 3; + postprocessor = new KisTIFFPostProcessor(nbcolorsamples); + } + break; + case PHOTOMETRIC_SEPARATED: + { + poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3; poses[4] = 4; + postprocessor = new KisTIFFPostProcessor(nbcolorsamples); + } + break; + default: + break; + } + + + // Initisalize tiffReader + uint16 * lineSizeCoeffs = new uint16[nbchannels]; + uint16 vsubsampling = 1; + uint16 hsubsampling = 1; + for(uint i = 0; i < nbchannels; i++) + { + lineSizeCoeffs[i] = 1; + } + if( color_type == PHOTOMETRIC_PALETTE) + { + uint16 *red; // No need to free them they are free by libtiff + uint16 *green; + uint16 *blue; + if ((TIFFGetField(image, TIFFTAG_COLORMAP, &red, &green, &blue)) == 0) + { + kdDebug(41008) << "Indexed image does not define a palette" << endl; + TIFFClose(image); + return KisImageBuilder_RESULT_INVALID_ARG; + } + + tiffReader = new KisTIFFReaderFromPalette( layer->paintDevice(), red, green, blue, poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); + } else if(color_type == PHOTOMETRIC_YCBCR ) { + TIFFGetFieldDefaulted( image, TIFFTAG_YCBCRSUBSAMPLING, &hsubsampling, &vsubsampling ); + lineSizeCoeffs[1] = hsubsampling; + lineSizeCoeffs[2] = hsubsampling; + uint16 position; + TIFFGetFieldDefaulted( image, TIFFTAG_YCBCRPOSITIONING, &position ); + if( dstDepth == 8 ) + { + tiffReader = new KisTIFFYCbCrReaderTarget8Bit(layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor, hsubsampling, vsubsampling, (KisTIFFYCbCr::Position)position); + } else if( dstDepth == 16 ) + { + tiffReader = new KisTIFFYCbCrReaderTarget16Bit( layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor, hsubsampling, vsubsampling, (KisTIFFYCbCr::Position)position); + } + } else if(dstDepth == 8) + { + tiffReader = new KisTIFFReaderTarget8bit( layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); + } else if(dstDepth == 16) { + tiffReader = new KisTIFFReaderTarget16bit( layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); + } else if(dstDepth == 32) { + tiffReader = new KisTIFFReaderTarget32bit( layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); + } + + if(TIFFIsTiled(image)) + { + kdDebug(41008) << "tiled image" << endl; + uint32 tileWidth, tileHeight; + uint32 x, y; + TIFFGetField(image, TIFFTAG_TILEWIDTH, &tileWidth); + TIFFGetField(image, TIFFTAG_TILELENGTH, &tileHeight); + uint32 linewidth = (tileWidth * depth * nbchannels) / 8; + if(planarconfig == PLANARCONFIG_CONTIG) + { + buf = _TIFFmalloc(TIFFTileSize(image)); + if(depth < 16) + { + tiffstream = new TIFFStreamContigBelow16((uint8*)buf, depth, linewidth); + } else if(depth < 32) + { + tiffstream = new TIFFStreamContigBelow32((uint8*)buf, depth, linewidth); + } else { + tiffstream = new TIFFStreamContigAbove32((uint8*)buf, depth, linewidth); + } + } else { + ps_buf = new tdata_t[nbchannels]; + uint32 * lineSizes = new uint32[nbchannels]; + uint16 baseSize = TIFFTileSize(image)/nbchannels; + for(uint i = 0; i < nbchannels; i++) + { + ps_buf[i] = _TIFFmalloc(baseSize); + lineSizes[i] = baseSize / lineSizeCoeffs[i]; + } + tiffstream = new TIFFStreamSeperate( (uint8**) ps_buf, nbchannels, depth, lineSizes); + delete [] lineSizes; + } + kdDebug(41008) << linewidth << " " << nbchannels << " " << layer->paintDevice()->colorSpace()->nColorChannels() << endl; + for (y = 0; y < height; y+= tileHeight) + { + for (x = 0; x < width; x += tileWidth) + { + kdDebug(41008) << "Reading tile x = " << x << " y = " << y << endl; + if( planarconfig == PLANARCONFIG_CONTIG ) + { + TIFFReadTile(image, buf, x, y, 0, (tsample_t) -1); + } else { + for(uint i = 0; i < nbchannels; i++) + { + TIFFReadTile(image, ps_buf[i], x, y, 0, i); + } + } + uint32 realTileWidth = (x + tileWidth) < width ? tileWidth : width - x; + for (uint yintile = 0; y + yintile < height && yintile < tileHeight/vsubsampling; ) { + tiffReader->copyDataToChannels( x, y + yintile , realTileWidth, tiffstream); + yintile += 1; + tiffstream->moveToLine( yintile ); + } + tiffstream->restart(); + } + } + } else { + kdDebug(41008) << "striped image" << endl; + tsize_t stripsize = TIFFStripSize(image); + uint32 rowsPerStrip; + TIFFGetFieldDefaulted(image, TIFFTAG_ROWSPERSTRIP, &rowsPerStrip); + kdDebug() << rowsPerStrip << " " << height << endl; + rowsPerStrip = QMIN(rowsPerStrip, height); // when TIFFNumberOfStrips(image) == 1 it might happen that rowsPerStrip is incorrectly set + if(planarconfig == PLANARCONFIG_CONTIG) + { + buf = _TIFFmalloc(stripsize); + if(depth < 16) + { + tiffstream = new TIFFStreamContigBelow16((uint8*)buf, depth, stripsize/rowsPerStrip); + } else if(depth < 32) + { + tiffstream = new TIFFStreamContigBelow32((uint8*)buf, depth, stripsize/rowsPerStrip); + } else { + tiffstream = new TIFFStreamContigAbove32((uint8*)buf, depth, stripsize/rowsPerStrip); + } + } else { + ps_buf = new tdata_t[nbchannels]; + uint32 scanLineSize = stripsize/rowsPerStrip; + kdDebug(41008) << " scanLineSize for each plan = " << scanLineSize << endl; + uint32 * lineSizes = new uint32[nbchannels]; + for(uint i = 0; i < nbchannels; i++) + { + ps_buf[i] = _TIFFmalloc(stripsize); + lineSizes[i] = scanLineSize / lineSizeCoeffs[i]; + } + tiffstream = new TIFFStreamSeperate( (uint8**) ps_buf, nbchannels, depth, lineSizes); + delete [] lineSizes; + } + + kdDebug(41008) << "Scanline size = " << TIFFRasterScanlineSize(image) << " / strip size = " << TIFFStripSize(image) << " / rowsPerStrip = " << rowsPerStrip << " stripsize/rowsPerStrip = " << stripsize/rowsPerStrip << endl; + uint32 y = 0; + kdDebug(41008) << " NbOfStrips = " << TIFFNumberOfStrips(image) << " rowsPerStrip = " << rowsPerStrip << " stripsize = " << stripsize << endl; + for (uint32 strip = 0; y < height; strip++) + { + if( planarconfig == PLANARCONFIG_CONTIG ) + { + TIFFReadEncodedStrip(image, TIFFComputeStrip( image, y, 0 ) , buf, (tsize_t) -1); + } else { + for(uint i = 0; i < nbchannels; i++) + { + TIFFReadEncodedStrip(image, TIFFComputeStrip( image, y, i ), ps_buf[i], (tsize_t) -1); + } + } + for( uint32 yinstrip = 0 ; yinstrip < rowsPerStrip && y < height ; ) + { + uint linesread = tiffReader->copyDataToChannels( 0, y, width, tiffstream); + y += linesread; + yinstrip += linesread; + tiffstream->moveToLine( yinstrip ); + } + tiffstream->restart(); + } + } + tiffReader->finalize(); + delete lineSizeCoeffs; + delete tiffReader; + delete tiffstream; + if( planarconfig == PLANARCONFIG_CONTIG ) + { + _TIFFfree(buf); + } else { + for(uint i = 0; i < nbchannels; i++) + { + _TIFFfree(ps_buf[i]); + } + delete[] ps_buf; + } + + m_img->addLayer(layer, m_img->rootLayer(), 0); + return KisImageBuilder_RESULT_OK; +} + +KisImageBuilder_Result KisTIFFConverter::buildImage(const KURL& uri) +{ + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) { + return KisImageBuilder_RESULT_NOT_EXIST; + } + + // We're not set up to handle asynchronous loading at the moment. + KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; + QString tmpFile; + + if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) { + KURL uriTF; + uriTF.setPath( tmpFile ); + result = decode(uriTF); + KIO::NetAccess::removeTempFile(tmpFile); + } + + return result; +} + + +KisImageSP KisTIFFConverter::image() +{ + return m_img; +} + + +KisImageBuilder_Result KisTIFFConverter::buildFile(const KURL& uri, KisImageSP img, KisTIFFOptions options) +{ + kdDebug(41008) << "Start writing TIFF File" << endl; + if (!img) + return KisImageBuilder_RESULT_EMPTY; + + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!uri.isLocalFile()) + return KisImageBuilder_RESULT_NOT_LOCAL; + + // Open file for writing + TIFF *image; + if((image = TIFFOpen(QFile::encodeName(uri.path()), "w")) == NULL){ + kdDebug(41008) << "Could not open the file for writting " << uri.path() << endl; + TIFFClose(image); + return (KisImageBuilder_RESULT_FAILURE); + } + + // Set the document informations + KoDocumentInfo * info = m_doc->documentInfo(); + KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" )); + QString title = aboutPage->title(); + if(!title.isEmpty()) + { + TIFFSetField(image, TIFFTAG_DOCUMENTNAME, title.ascii()); + } + QString abstract = aboutPage->abstract(); + if(!abstract.isEmpty()) + { + TIFFSetField(image, TIFFTAG_IMAGEDESCRIPTION, abstract.ascii()); + } + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" )); + QString author = authorPage->fullName(); + if(!author.isEmpty()) + { + TIFFSetField(image, TIFFTAG_ARTIST, author.ascii()); + } + + KisTIFFWriterVisitor* visitor = new KisTIFFWriterVisitor(image, &options); + KisGroupLayer* root = dynamic_cast<KisGroupLayer*>(img->rootLayer().data()); + if(root == 0) + { + KIO::del(uri); + TIFFClose(image); + return KisImageBuilder_RESULT_FAILURE; + } + if(!visitor->visit( root )) + { + KIO::del(uri); + TIFFClose(image); + return KisImageBuilder_RESULT_FAILURE; + } + + TIFFClose(image); + return KisImageBuilder_RESULT_OK; +} + + +void KisTIFFConverter::cancel() +{ + m_stop = true; +} + +#include "kis_tiff_converter.moc" diff --git a/filters/krita/tiff/kis_tiff_converter.h b/filters/krita/tiff/kis_tiff_converter.h new file mode 100644 index 00000000..fbda4abd --- /dev/null +++ b/filters/krita/tiff/kis_tiff_converter.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_TIFF_CONVERTER_H_ +#define _KIS_TIFF_CONVERTER_H_ + +#include <stdio.h> +#include <tiffio.h> + +#include <qvaluevector.h> + +#include <kio/job.h> + +#include <kis_progress_subject.h> + +#include "kis_types.h" +#include "kis_global.h" +#include "kis_annotation.h" +class KisDoc; +class KisUndoAdapter; + +/** + * Image import/export plugins can use these results to report about success or failure. + */ +enum KisImageBuilder_Result { + KisImageBuilder_RESULT_FAILURE = -400, + KisImageBuilder_RESULT_NOT_EXIST = -300, + KisImageBuilder_RESULT_NOT_LOCAL = -200, + KisImageBuilder_RESULT_BAD_FETCH = -100, + KisImageBuilder_RESULT_INVALID_ARG = -50, + KisImageBuilder_RESULT_OK = 0, + KisImageBuilder_RESULT_PROGRESS = 1, + KisImageBuilder_RESULT_EMPTY = 100, + KisImageBuilder_RESULT_BUSY = 150, + KisImageBuilder_RESULT_NO_URI = 200, + KisImageBuilder_RESULT_UNSUPPORTED = 300, + KisImageBuilder_RESULT_INTR = 400, + KisImageBuilder_RESULT_PATH = 500, + KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 +}; + +struct KisTIFFOptions { + Q_UINT16 compressionType; + Q_UINT16 predictor; + bool alpha; + bool flatten; + Q_UINT16 jpegQuality; + Q_UINT16 deflateCompress; + Q_UINT16 faxMode; + Q_UINT16 pixarLogCompress; +}; + +class KisTIFFConverter : public KisProgressSubject { + Q_OBJECT + public: + KisTIFFConverter(KisDoc *doc, KisUndoAdapter *adapter); + virtual ~KisTIFFConverter(); + public: + KisImageBuilder_Result buildImage(const KURL& uri); + KisImageBuilder_Result buildFile(const KURL& uri, KisImageSP layer, KisTIFFOptions); + /** Retrieve the constructed image + */ + KisImageSP image(); + public slots: + virtual void cancel(); + private: + KisImageBuilder_Result decode(const KURL& uri); + KisImageBuilder_Result readTIFFDirectory( TIFF* image); + private: + KisImageSP m_img; + KisDoc *m_doc; + KisUndoAdapter *m_adapter; + bool m_stop; + KIO::TransferJob *m_job; +}; + +#endif diff --git a/filters/krita/tiff/kis_tiff_export.cc b/filters/krita/tiff/kis_tiff_export.cc new file mode 100644 index 00000000..ee715c6a --- /dev/null +++ b/filters/krita/tiff/kis_tiff_export.cc @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_tiff_export.h" + +#include <qcheckbox.h> +#include <qslider.h> + +#include <kapplication.h> +#include <kcombobox.h> +#include <kdialogbase.h> +#include <kgenericfactory.h> + +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_group_layer.h> +#include <kis_image.h> +#include <kis_paint_layer.h> +#include <kis_progress_display_interface.h> + +#include "kis_tiff_converter.h" +#include "kis_dlg_options_tiff.h" +#include "kis_wdg_options_tiff.h" + +typedef KGenericFactory<KisTIFFExport, KoFilter> KisTIFFExportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritatiffexport, KisTIFFExportFactory("kofficefilters")) + +KisTIFFExport::KisTIFFExport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisTIFFExport::~KisTIFFExport() +{ +} + +KoFilter::ConversionStatus KisTIFFExport::convert(const QCString& from, const QCString& to) +{ + kdDebug(41008) << "Tiff export! From: " << from << ", To: " << to << "\n"; + + if (from != "application/x-krita") + return KoFilter::NotImplemented; + + + KisDlgOptionsTIFF* kdb = new KisDlgOptionsTIFF(0, "options dialog for tiff"); + + KisDoc *output = dynamic_cast<KisDoc*>(m_chain->inputDocument()); + + KisColorSpace* cs = output->currentImage()->colorSpace(); + KisChannelInfo::enumChannelValueType type = cs->channels()[0]->channelValueType(); + if( type == KisChannelInfo::FLOAT16 || type == KisChannelInfo::FLOAT32) + { + kdb->optionswdg->kComboBoxPredictor->removeItem(1); + } else { + kdb->optionswdg->kComboBoxPredictor->removeItem(2); + } + + if(kdb->exec() == QDialog::Rejected) + { + return KoFilter::OK; // FIXME Cancel doesn't exist :( + } + + KisTIFFOptions options = kdb->options(); + + if( ( type == KisChannelInfo::FLOAT16 || type == KisChannelInfo::FLOAT32) && options.predictor == 2 ) + { // FIXME THIS IS AN HACK FIX THAT IN 2.0 !! + options.predictor = 3; + } + delete kdb; + + QString filename = m_chain->outputFile(); + + if (!output) + return KoFilter::CreationError; + + if (filename.isEmpty()) return KoFilter::FileNotFound; + + KURL url; + url.setPath(filename); + + KisImageSP img; + + if(options.flatten) + { + img = new KisImage(0, output->currentImage()->width(), output->currentImage()->height(), output->currentImage()->colorSpace(), ""); + KisPaintDeviceSP pd = new KisPaintDevice(*output->currentImage()->projection()); + KisPaintLayerSP l = new KisPaintLayer(img, "projection", OPACITY_OPAQUE, pd); + img->addLayer(l.data(), img->rootLayer(), 0); + } else { + img = output->currentImage(); + } + + + KisTIFFConverter ktc(output, output->undoAdapter()); +/* vKisAnnotationSP_it beginIt = img->beginAnnotations(); + vKisAnnotationSP_it endIt = img->endAnnotations();*/ + KisImageBuilder_Result res; + if ( (res = ktc.buildFile(url, img, options)) == KisImageBuilder_RESULT_OK) { + kdDebug(41008) << "success !" << endl; + return KoFilter::OK; + } + kdDebug(41008) << " Result = " << res << endl; + return KoFilter::InternalError; +} + +#include <kis_tiff_export.moc> + diff --git a/filters/krita/tiff/kis_tiff_export.h b/filters/krita/tiff/kis_tiff_export.h new file mode 100644 index 00000000..586c2390 --- /dev/null +++ b/filters/krita/tiff/kis_tiff_export.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_TIFF_EXPORT_H_ +#define _KIS_TIFF_EXPORT_H_ + +#include <KoFilter.h> + +class KisTIFFExport : public KoFilter { + Q_OBJECT + public: + KisTIFFExport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisTIFFExport(); + public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif diff --git a/filters/krita/tiff/kis_tiff_import.cc b/filters/krita/tiff/kis_tiff_import.cc new file mode 100644 index 00000000..155cf67d --- /dev/null +++ b/filters/krita/tiff/kis_tiff_import.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_tiff_import.h" + +#include <kgenericfactory.h> + +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_progress_display_interface.h> +#include <kis_view.h> + +#include "kis_tiff_converter.h" + +typedef KGenericFactory<KisTIFFImport, KoFilter> TIFFImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritatiffimport, TIFFImportFactory("kofficefilters")) + +KisTIFFImport::KisTIFFImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +KisTIFFImport::~KisTIFFImport() +{ +} + +KoFilter::ConversionStatus KisTIFFImport::convert(const QCString&, const QCString& to) +{ + kdDebug(41008) << "Importing using TIFFImport!\n"; + + if (to != "application/x-krita") + return KoFilter::BadMimeType; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + KisView * view = static_cast<KisView*>(doc -> views().getFirst()); + + QString filename = m_chain -> inputFile(); + + if (!doc) + return KoFilter::CreationError; + + doc -> prepareForImport(); + + + if (!filename.isEmpty()) { + + KURL url; + url.setPath(filename); + + if (url.isEmpty()) + return KoFilter::FileNotFound; + + KisTIFFConverter ib(doc, doc -> undoAdapter()); + + if (view != 0) + view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true); + + switch (ib.buildImage(url)) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KoFilter::NotImplemented; + break; + case KisImageBuilder_RESULT_INVALID_ARG: + return KoFilter::BadMimeType; + break; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_LOCAL: + return KoFilter::FileNotFound; + break; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KoFilter::ParsingError; + break; + case KisImageBuilder_RESULT_FAILURE: + return KoFilter::InternalError; + break; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KoFilter::OK; + default: + break; + } + + } + return KoFilter::StorageCreationError; +} + +#include <kis_tiff_import.moc> + diff --git a/filters/krita/tiff/kis_tiff_import.h b/filters/krita/tiff/kis_tiff_import.h new file mode 100644 index 00000000..75953edc --- /dev/null +++ b/filters/krita/tiff/kis_tiff_import.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef _KIS_TIFF_IMPORT_H_ +#define _KIS_TIFF_IMPORT_H_ + +#include <KoFilter.h> + +class KisTIFFImport : public KoFilter { + Q_OBJECT + public: + KisTIFFImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~KisTIFFImport(); + public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif diff --git a/filters/krita/tiff/kis_tiff_reader.cc b/filters/krita/tiff/kis_tiff_reader.cc new file mode 100644 index 00000000..bec7f184 --- /dev/null +++ b/filters/krita/tiff/kis_tiff_reader.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_tiff_reader.h" + +#include <kdebug.h> + +#include <kis_iterators_pixel.h> +#include <kis_paint_device.h> + +#include "kis_tiff_stream.h" + + uint KisTIFFReaderTarget8bit::copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) + { + KisHLineIterator it = paintDevice() -> createHLineIterator(x, y, dataWidth, true); + double coeff = Q_UINT8_MAX / (double)( pow(2, sourceDepth() ) - 1 ); +// kdDebug(41008) << " depth expension coefficient : " << coeff << endl; + while (!it.isDone()) { + Q_UINT8 *d = it.rawData(); + Q_UINT8 i; + for(i = 0; i < nbColorsSamples() ; i++) + { + d[poses()[i]] = (Q_UINT8)( tiffstream->nextValue() * coeff ); + } + postProcessor()->postProcess8bit( d); + if(transform()) cmsDoTransform(transform(), d, d, 1); + d[poses()[i]] = Q_UINT8_MAX; + for(int k = 0; k < nbExtraSamples(); k++) + { + if(k == alphaPos()) + d[poses()[i]] = (Q_UINT32) ( tiffstream->nextValue() * coeff ); + else + tiffstream->nextValue(); + } + ++it; + } + return 1; + } + uint KisTIFFReaderTarget16bit::copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) + { + KisHLineIterator it = paintDevice() -> createHLineIterator(x, y, dataWidth, true); + double coeff = Q_UINT16_MAX / (double)( pow(2, sourceDepth() ) - 1 ); +// kdDebug(41008) << " depth expension coefficient : " << coeff << endl; + while (!it.isDone()) { + Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData()); + Q_UINT8 i; + for(i = 0; i < nbColorsSamples(); i++) + { + d[poses()[i]] = (Q_UINT16)( tiffstream->nextValue() * coeff ); + } + postProcessor()->postProcess16bit( d); + if(transform()) cmsDoTransform(transform(), d, d, 1); + d[poses()[i]] = Q_UINT16_MAX; + for(int k = 0; k < nbExtraSamples(); k++) + { + if(k == alphaPos()) + d[poses()[i]] = (Q_UINT16) ( tiffstream->nextValue() * coeff ); + else + tiffstream->nextValue(); + } + ++it; + } + return 1; + } + + uint KisTIFFReaderTarget32bit::copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) + { + KisHLineIterator it = paintDevice() -> createHLineIterator(x, y, dataWidth, true); + double coeff = Q_UINT32_MAX / (double)( pow(2, sourceDepth() ) - 1 ); +// kdDebug(41008) << " depth expension coefficient : " << coeff << endl; + while (!it.isDone()) { + Q_UINT32 *d = reinterpret_cast<Q_UINT32 *>(it.rawData()); + Q_UINT8 i; + for(i = 0; i < nbColorsSamples(); i++) + { + d[poses()[i]] = (Q_UINT32)( tiffstream->nextValue() * coeff ); + } + postProcessor()->postProcess32bit( d); + if(transform()) cmsDoTransform(transform(), d, d, 1); + d[poses()[i]] = Q_UINT32_MAX; + for(int k = 0; k < nbExtraSamples(); k++) + { + if(k == alphaPos()) + d[poses()[i]] = (Q_UINT32) ( tiffstream->nextValue() * coeff ); + else + tiffstream->nextValue(); + } + ++it; + } + return 1; + } + uint KisTIFFReaderFromPalette::copyDataToChannels(Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) + { + KisHLineIterator it = paintDevice() -> createHLineIterator(x, y, dataWidth, true); + while (!it.isDone()) { + Q_UINT16* d = reinterpret_cast<Q_UINT16 *>(it.rawData()); + uint32 index = tiffstream->nextValue(); + d[2] = m_red[index]; + d[1] = m_green[index]; + d[0] = m_blue[index]; + d[3] = Q_UINT16_MAX; + ++it; + } + return 1; + } diff --git a/filters/krita/tiff/kis_tiff_reader.h b/filters/krita/tiff/kis_tiff_reader.h new file mode 100644 index 00000000..d107d21e --- /dev/null +++ b/filters/krita/tiff/kis_tiff_reader.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_TIFF_READER_H_ +#define _KIS_TIFF_READER_H_ + +// On some platforms, tiffio.h #defines NULL in a bad +// way for C++, as (void *)0 instead of using the correct +// C++ value 0. Include stdio.h first to get the right one. +#include <stdio.h> +#include <tiffio.h> + +// #include <qvaluevector.h> +// #include <kio/job.h> +// #include <kis_progress_subject.h> + +#include <kis_paint_device.h> + +#include "kis_types.h" +#include "kis_global.h" +// #include "kis_annotation.h" + +#include <kis_iterator.h> +#include <kis_paint_device.h> + +#define Q_UINT32_MAX 4294967295u + +class TIFFStreamBase; + +class KisTIFFPostProcessor { + public: + KisTIFFPostProcessor(uint8 nbcolorssamples) : m_nbcolorssamples(nbcolorssamples) { } + public: + virtual void postProcess8bit( Q_UINT8* ) { }; + virtual void postProcess16bit( Q_UINT16* ) { }; + virtual void postProcess32bit( Q_UINT32* ) { }; + protected: + inline uint8 nbColorsSamples() { return m_nbcolorssamples; } + private: + uint8 m_nbcolorssamples; +}; + +class KisTIFFPostProcessorInvert : public KisTIFFPostProcessor { + public: + KisTIFFPostProcessorInvert(uint8 nbcolorssamples) : KisTIFFPostProcessor(nbcolorssamples) {} + public: + virtual void postProcess8bit( Q_UINT8* data ) + { + for(int i = 0; i < nbColorsSamples(); i++) + { + data[i] = Q_UINT8_MAX - data[i]; + } + } + virtual void postProcess16bit( Q_UINT16* data ) + { + Q_UINT16* d = (Q_UINT16*) data; + for(int i = 0; i < nbColorsSamples(); i++) + { + d[i] = Q_UINT16_MAX - d[i]; + } + } + virtual void postProcess32bit( Q_UINT32* data ) + { + Q_UINT32* d = (Q_UINT32*) data; + for(int i = 0; i < nbColorsSamples(); i++) + { + d[i] = Q_UINT32_MAX - d[i]; + } + } +}; + +class KisTIFFPostProcessorICCLABtoCIELAB : public KisTIFFPostProcessor { + public: + KisTIFFPostProcessorICCLABtoCIELAB(uint8 nbcolorssamples) : KisTIFFPostProcessor(nbcolorssamples) {} + public: + void postProcess8bit(Q_UINT8* data) + { + Q_INT8* ds = (Q_INT8*) data; + for(int i = 1; i < nbColorsSamples(); i++) + { + ds[i] = data[i] - Q_UINT8_MAX/2; + } + } + void postProcess16bit(Q_UINT16* data) + { + Q_UINT16* d = (Q_UINT16*) data; + Q_INT16* ds = (Q_INT16*) data; + for(int i = 1; i < nbColorsSamples(); i++) + { + ds[i] = d[i] - Q_UINT16_MAX /2; + } + } + void postProcess32bit(Q_UINT32* data) + { + Q_UINT32* d = (Q_UINT32*) data; + Q_INT32* ds = (Q_INT32*) data; + for(int i = 1; i < nbColorsSamples(); i++) + { + ds[i] = d[i] - Q_UINT32_MAX /2; + } + } +}; + + +class KisTIFFReaderBase { + public: + KisTIFFReaderBase( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor) : m_device(device), m_alphapos(alphapos), m_sourceDepth(sourceDepth), m_nbcolorssamples(nbcolorssamples), m_nbextrasamples(extrasamplescount), m_poses(poses), m_transformProfile(transformProfile), m_postprocess(postprocessor) + { + + } + public: + /** + * This function copy data from the tiff stream to the paint device starting at the given position. + * @param x horizontal start position + * @param y vertical start position + * @param dataWidth width of the data to copy + * @param tiffstream source of data + * + * @return the number of line which were copied + */ + virtual uint copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) =0; + /** + * This function is called when all data has been read and should be used for any postprocessing. + */ + virtual void finalize() { }; + protected: + inline KisPaintDeviceSP paintDevice() { return m_device; } + inline Q_UINT8 alphaPos() { return m_alphapos; } + inline Q_UINT8 sourceDepth() { return m_sourceDepth; } + inline Q_UINT8 nbColorsSamples() { return m_nbcolorssamples; } + inline Q_UINT8 nbExtraSamples() { return m_nbextrasamples; } + inline Q_UINT8* poses() { return m_poses; } + inline cmsHTRANSFORM transform() { return m_transformProfile; } + inline KisTIFFPostProcessor* postProcessor() { return m_postprocess; } + private: + KisPaintDeviceSP m_device; + Q_UINT8 m_alphapos; + Q_UINT8 m_sourceDepth; + Q_UINT8 m_nbcolorssamples; + Q_UINT8 m_nbextrasamples; + Q_UINT8* m_poses; + cmsHTRANSFORM m_transformProfile; + KisTIFFPostProcessor* m_postprocess; + Q_UINT32 m_tiffDataWidth; +}; + +class KisTIFFReaderTarget8bit : public KisTIFFReaderBase { + public: + KisTIFFReaderTarget8bit( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor) : KisTIFFReaderBase(device, poses, alphapos, sourceDepth, nbcolorssamples, extrasamplescount, transformProfile, postprocessor ) + { + + } + public: + virtual uint copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream); +}; + + +class KisTIFFReaderTarget16bit : public KisTIFFReaderBase { + public: + KisTIFFReaderTarget16bit( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor) : KisTIFFReaderBase(device, poses, alphapos, sourceDepth, nbcolorssamples, extrasamplescount, transformProfile, postprocessor ) + { + + } + public: + virtual uint copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) ; +}; + +class KisTIFFReaderTarget32bit : public KisTIFFReaderBase { + public: + KisTIFFReaderTarget32bit( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor) : KisTIFFReaderBase(device, poses, alphapos, sourceDepth, nbcolorssamples, extrasamplescount, transformProfile, postprocessor ) + { + + } + public: + virtual uint copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) ; +}; + +class KisTIFFReaderFromPalette : public KisTIFFReaderBase { + public: + KisTIFFReaderFromPalette( KisPaintDeviceSP device, uint16 *red, uint16 *green, uint16 *blue, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor) : KisTIFFReaderBase(device, poses, alphapos, sourceDepth, nbcolorssamples, extrasamplescount, transformProfile, postprocessor ), m_red(red), m_green(green), m_blue(blue) + { + + } + public: + virtual uint copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) ; + private: + uint16 *m_red, *m_green, *m_blue; +}; + +#endif diff --git a/filters/krita/tiff/kis_tiff_stream.cc b/filters/krita/tiff/kis_tiff_stream.cc new file mode 100644 index 00000000..3d52d4dc --- /dev/null +++ b/filters/krita/tiff/kis_tiff_stream.cc @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_tiff_stream.h" + +TIFFStreamContigBase::TIFFStreamContigBase( uint8* src, uint16 depth, uint32 lineSize ) : TIFFStreamBase(depth), m_src(src), m_lineSize(lineSize) { restart(); } + +void TIFFStreamContigBase::restart() +{ + m_srcit = m_src; + m_posinc = 8; +} + +void TIFFStreamContigBase::moveToLine(uint32 lineNumber) +{ + m_srcit = m_src + lineNumber * m_lineSize; + m_posinc = 8; +} + +uint32 TIFFStreamContigBelow16::nextValue() +{ + register uint8 remain; + register uint32 value; + remain = m_depth; + value = 0; + while (remain > 0) + { + register uint8 toread; + toread = remain; + if (toread > m_posinc) toread = m_posinc; + remain -= toread; + m_posinc -= toread; + value = (value << toread) | (( (*m_srcit) >> (m_posinc) ) & ( ( 1 << toread ) - 1 ) ); + if (m_posinc == 0) + { + m_srcit++; + m_posinc=8; + } + } + return value; +} + +uint32 TIFFStreamContigBelow32::nextValue() +{ + register uint8 remain; + register uint32 value; + remain = m_depth; + value = 0; + while (remain > 0) + { + register uint8 toread; + toread = remain; + if (toread > m_posinc) toread = m_posinc; + remain -= toread; + m_posinc -= toread; + value = (value) | ( (( (*m_srcit) >> (m_posinc) ) & ( ( 1 << toread ) - 1 ) ) << ( m_depth - 8 - remain ) ); + if (m_posinc == 0) + { + m_srcit++; + m_posinc=8; + } + } + return value; +} + +uint32 TIFFStreamContigAbove32::nextValue() +{ + register uint8 remain; + register uint32 value; + remain = m_depth; + value = 0; + while (remain > 0) + { + register uint8 toread; + toread = remain; + if (toread > m_posinc) toread = m_posinc; + remain -= toread; + m_posinc -= toread; + if(remain < 32 ) + { + value = (value) | ( (( (*m_srcit) >> (m_posinc) ) & ( ( 1 << toread ) - 1 ) ) << ( 24 - remain ) ); + } + if (m_posinc == 0) + { + m_srcit++; + m_posinc=8; + } + } + return value; +} + +TIFFStreamSeperate::TIFFStreamSeperate( uint8** srcs, uint8 nb_samples ,uint16 depth, uint32* lineSize) : TIFFStreamBase(depth), m_nb_samples(nb_samples) +{ + streams = new TIFFStreamContigBase*[nb_samples]; + if(depth < 16) + { + for(uint8 i = 0; i < m_nb_samples; i++) + { + streams[i] = new TIFFStreamContigBelow16(srcs[i], depth, lineSize[i]); + } + } else if( depth < 32 ) + { + for(uint8 i = 0; i < m_nb_samples; i++) + { + streams[i] = new TIFFStreamContigBelow32(srcs[i], depth, lineSize[i]); + } + } else { + for(uint8 i = 0; i < m_nb_samples; i++) + { + streams[i] = new TIFFStreamContigAbove32(srcs[i], depth, lineSize[i]); + } + } + restart(); +} + +TIFFStreamSeperate::~TIFFStreamSeperate() +{ + for(uint8 i = 0; i < m_nb_samples; i++) + { + delete streams[i]; + } + delete[] streams; +} + +uint32 TIFFStreamSeperate::nextValue() +{ + uint32 value = streams[ m_current_sample ]->nextValue(); + if( (++m_current_sample) >= m_nb_samples) + m_current_sample = 0; + return value; +} + +void TIFFStreamSeperate::restart() +{ + m_current_sample = 0; + for(uint8 i = 0; i < m_nb_samples; i++) + { + streams[i]->restart(); + } +} + +void TIFFStreamSeperate::moveToLine(uint32 lineNumber) +{ + for(uint8 i = 0; i < m_nb_samples; i++) + { + streams[i]->moveToLine(lineNumber); + } +} diff --git a/filters/krita/tiff/kis_tiff_stream.h b/filters/krita/tiff/kis_tiff_stream.h new file mode 100644 index 00000000..f203568e --- /dev/null +++ b/filters/krita/tiff/kis_tiff_stream.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005-2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIFFSTREAM_H_ +#define TIFFSTREAM_H_ + +#include <tiffio.h> + +class TIFFStreamBase { + public: + TIFFStreamBase( uint16 depth ) : m_depth(depth) {}; + virtual uint32 nextValue() =0; + virtual void restart() =0; + virtual void moveToLine(uint32 lineNumber) =0; + protected: + uint16 m_depth; +}; + +class TIFFStreamContigBase : public TIFFStreamBase { + public: + TIFFStreamContigBase( uint8* src, uint16 depth, uint32 lineSize ); + virtual void restart(); + virtual void moveToLine(uint32 lineNumber); + protected: + uint8* m_src; + uint8* m_srcit; + uint8 m_posinc; + uint32 m_lineSize; +}; + +class TIFFStreamContigBelow16 : public TIFFStreamContigBase { + public: + TIFFStreamContigBelow16( uint8* src, uint16 depth, uint32 lineSize ) : TIFFStreamContigBase(src, depth, lineSize) { } + public: + virtual uint32 nextValue(); +}; + +class TIFFStreamContigBelow32 : public TIFFStreamContigBase { + public: + TIFFStreamContigBelow32( uint8* src, uint16 depth, uint32 lineSize ) : TIFFStreamContigBase(src, depth, lineSize) { } + public: + virtual uint32 nextValue(); +}; + +class TIFFStreamContigAbove32 : public TIFFStreamContigBase { + public: + TIFFStreamContigAbove32( uint8* src, uint16 depth, uint32 lineSize ) : TIFFStreamContigBase(src, depth, lineSize) { } + public: + virtual uint32 nextValue(); +}; + + +class TIFFStreamSeperate : public TIFFStreamBase { + public: + TIFFStreamSeperate( uint8** srcs, uint8 nb_samples ,uint16 depth, uint32* lineSize); + ~TIFFStreamSeperate(); + virtual uint32 nextValue(); + virtual void restart(); + virtual void moveToLine(uint32 lineNumber); + private: + TIFFStreamContigBase** streams; + uint8 m_current_sample, m_nb_samples; +}; + +#endif diff --git a/filters/krita/tiff/kis_tiff_writer_visitor.cpp b/filters/krita/tiff/kis_tiff_writer_visitor.cpp new file mode 100644 index 00000000..64f7e252 --- /dev/null +++ b/filters/krita/tiff/kis_tiff_writer_visitor.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_tiff_writer_visitor.h" + +#include <kmessagebox.h> +#include <klocale.h> + +#include <kis_annotation.h> +#include <kis_colorspace.h> +#include <kis_group_layer.h> +#include <kis_image.h> +#include <kis_paint_layer.h> +#include <kis_types.h> + +#include "kis_tiff_converter.h" + +namespace { + bool writeColorSpaceInformation( TIFF* image, KisColorSpace * cs, uint16& color_type, uint16& sample_format ) + { + sample_format = SAMPLEFORMAT_UINT; + if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) + { + color_type = PHOTOMETRIC_MINISBLACK; + return true; + } + if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) + { + color_type = PHOTOMETRIC_RGB; + return true; + } + if ( cs->id() == KisID("RGBAF16HALF") || cs->id() == KisID("RGBAF32") ) + { + color_type = PHOTOMETRIC_RGB; + sample_format = SAMPLEFORMAT_IEEEFP; + return true; + } + if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYKA16") ) + { + color_type = PHOTOMETRIC_SEPARATED; + TIFFSetField(image, TIFFTAG_INKSET, INKSET_CMYK); + return true; + } + if ( cs->id() == KisID("LABA") ) + { + color_type = PHOTOMETRIC_CIELAB; + return true; + } + + KMessageBox::error(0, i18n("Cannot export images in %1.\n").arg(cs->id().name()) ) ; + return false; + + } +} + +KisTIFFWriterVisitor::KisTIFFWriterVisitor(TIFF*img, KisTIFFOptions* options) : m_image(img), m_options(options) +{ +} + +KisTIFFWriterVisitor::~KisTIFFWriterVisitor() +{ +} + +bool KisTIFFWriterVisitor::saveAlpha() { return m_options->alpha; } + +bool KisTIFFWriterVisitor::copyDataToStrips( KisHLineIterator it, tdata_t buff, uint8 depth, uint8 nbcolorssamples, Q_UINT8* poses) +{ + if(depth == 32) + { + Q_UINT32 *dst = reinterpret_cast<Q_UINT32 *>(buff); + while (!it.isDone()) { + const Q_UINT32 *d = reinterpret_cast<const Q_UINT32 *>(it.rawData()); + int i; + for(i = 0; i < nbcolorssamples; i++) + { + *(dst++) = d[poses[i]]; + } + if(saveAlpha()) *(dst++) = d[poses[i]]; + ++it; + } + return true; + } else if(depth == 16) + { + Q_UINT16 *dst = reinterpret_cast<Q_UINT16 *>(buff); + while (!it.isDone()) { + const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData()); + int i; + for(i = 0; i < nbcolorssamples; i++) + { + *(dst++) = d[poses[i]]; + } + if(saveAlpha()) *(dst++) = d[poses[i]]; + ++it; + } + return true; + } else if(depth == 8) { + Q_UINT8 *dst = reinterpret_cast<Q_UINT8 *>(buff); + while (!it.isDone()) { + const Q_UINT8 *d = it.rawData(); + int i; + for(i = 0; i < nbcolorssamples; i++) + { + *(dst++) = d[poses[i]]; + } + if(saveAlpha()) *(dst++) = d[poses[i]]; + ++it; + } + return true; + } + return false; +} + + +bool KisTIFFWriterVisitor::visit(KisPaintLayer *layer) +{ + kdDebug(41008) << "visiting on paint layer " << layer->name() << "\n"; + KisPaintDeviceSP pd = layer->paintDevice(); + // Save depth + int depth = 8 * pd->pixelSize() / pd->nChannels(); + TIFFSetField(image(), TIFFTAG_BITSPERSAMPLE, depth); + // Save number of samples + if(saveAlpha()) + { + TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->nChannels()); + uint16 sampleinfo[1] = { EXTRASAMPLE_UNASSALPHA }; + TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo); + } else { + TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->nChannels() - 1); + TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 0); + } + // Save colorspace information + uint16 color_type; + uint16 sample_format; + if(!writeColorSpaceInformation(image(), pd->colorSpace(), color_type, sample_format)) + { // unsupported colorspace + return false; + } + TIFFSetField(image(), TIFFTAG_PHOTOMETRIC, color_type); + TIFFSetField(image(), TIFFTAG_SAMPLEFORMAT, sample_format); + TIFFSetField(image(), TIFFTAG_IMAGEWIDTH, layer->image()->width()); + TIFFSetField(image(), TIFFTAG_IMAGELENGTH, layer->image()->height()); + + // Set the compression options + TIFFSetField(image(), TIFFTAG_COMPRESSION, m_options->compressionType); + TIFFSetField(image(), TIFFTAG_FAXMODE, m_options->faxMode); + TIFFSetField(image(), TIFFTAG_JPEGQUALITY, m_options->jpegQuality); + TIFFSetField(image(), TIFFTAG_ZIPQUALITY, m_options->deflateCompress); + TIFFSetField(image(), TIFFTAG_PIXARLOGQUALITY, m_options->pixarLogCompress); + + // Set the predictor + TIFFSetField(image(), TIFFTAG_PREDICTOR, m_options->predictor); + + // Use contiguous configuration + TIFFSetField(image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + // Use 8 rows per strip + TIFFSetField(image(), TIFFTAG_ROWSPERSTRIP, 8); + + // Save profile + KisProfile* profile = pd->colorSpace()->getProfile(); + if(profile) + { + QByteArray ba = profile->annotation()->annotation(); + TIFFSetField(image(), TIFFTAG_ICCPROFILE, ba.size(),ba.data()); + } + tsize_t stripsize = TIFFStripSize(image()); + tdata_t buff = _TIFFmalloc(stripsize); + Q_INT32 height = layer->image()->height(); + Q_INT32 width = layer->image()->width(); + bool r = true; + for (int y = 0; y < height; y++) { + KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false); + switch(color_type) + { + case PHOTOMETRIC_MINISBLACK: + { + Q_UINT8 poses[]={ 0,1 }; + r = copyDataToStrips(it, buff, depth, 1, poses); + } + break; + case PHOTOMETRIC_RGB: + { + Q_UINT8 poses[]={ 2, 1, 0, 3}; + r = copyDataToStrips(it, buff, depth, 3, poses); + } + break; + case PHOTOMETRIC_SEPARATED: + { + Q_UINT8 poses[]={ 0, 1, 2, 3, 4 }; + r = copyDataToStrips(it, buff, depth, 4, poses); + } + break; + case PHOTOMETRIC_CIELAB: + { + Q_UINT8 poses[]={ 0, 1, 2, 3 }; + r = copyDataToStrips(it, buff, depth, 3, poses); + } + break; + return false; + } + if(!r) return false; + TIFFWriteScanline(image(), buff, y, (tsample_t) -1); + } + _TIFFfree(buff); + TIFFWriteDirectory(image()); + return true; +} +bool KisTIFFWriterVisitor::visit(KisGroupLayer *layer) +{ + kdDebug(41008) << "Visiting on grouplayer " << layer->name() << "\n"; + KisLayerSP child = layer->firstChild(); + while (child) { + child->accept(*this); + child = child->nextSibling(); + } + return true; +} + +bool KisTIFFWriterVisitor::visit(KisPartLayer *) +{ + return true; +} diff --git a/filters/krita/tiff/kis_tiff_writer_visitor.h b/filters/krita/tiff/kis_tiff_writer_visitor.h new file mode 100644 index 00000000..c05c8d78 --- /dev/null +++ b/filters/krita/tiff/kis_tiff_writer_visitor.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_TIFF_WRITER_VISITOR_H +#define KIS_TIFF_WRITER_VISITOR_H + +#include <kis_layer_visitor.h> + +#include <kis_iterators_pixel.h> + +#include <tiffio.h> + +class KisTIFFOptions; + +/** + @author Cyrille Berger <[email protected]> +*/ +class KisTIFFWriterVisitor : public KisLayerVisitor +{ + public: + KisTIFFWriterVisitor(TIFF*img, KisTIFFOptions* options); + ~KisTIFFWriterVisitor(); + public: + virtual bool visit(KisPaintLayer *layer); + virtual bool visit(KisGroupLayer *layer); + virtual bool visit(KisPartLayer *layer); + virtual bool visit(KisAdjustmentLayer* ) { return true; } + private: + inline TIFF* image() { return m_image; } + inline bool saveAlpha(); + bool copyDataToStrips( KisHLineIterator it, tdata_t buff, uint8 depth, uint8 nbcolorssamples, Q_UINT8* poses); + private: + TIFF* m_image; + KisTIFFOptions* m_options; +}; + +#endif diff --git a/filters/krita/tiff/kis_tiff_ycbcr_reader.cc b/filters/krita/tiff/kis_tiff_ycbcr_reader.cc new file mode 100644 index 00000000..5609f887 --- /dev/null +++ b/filters/krita/tiff/kis_tiff_ycbcr_reader.cc @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_tiff_ycbcr_reader.h" + +#include <kis_iterators_pixel.h> +#include <kis_paint_device.h> + +#include "kis_tiff_stream.h" + + +KisTIFFYCbCrReaderTarget8Bit::KisTIFFYCbCrReaderTarget8Bit( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor, uint16 hsub, uint16 vsub, KisTIFFYCbCr::Position position ) : KisTIFFReaderBase(device, poses, alphapos, sourceDepth, nbcolorssamples, extrasamplescount, transformProfile, postprocessor), m_hsub(hsub), m_vsub(vsub), m_position(position) +{ + // Initialize the buffer + Q_INT32 imagewidth = device->image()->width(); + if(2*(imagewidth / 2) != imagewidth) imagewidth++; + m_bufferWidth = imagewidth / m_hsub; + Q_INT32 imageheight = device->image()->height(); + if(2*(imageheight / 2) != imageheight) imageheight++; + m_bufferHeight = imageheight / m_vsub; + m_bufferCb = new Q_UINT8[ m_bufferWidth * m_bufferHeight ]; + m_bufferCr = new Q_UINT8[ m_bufferWidth * m_bufferHeight ]; +} + +KisTIFFYCbCrReaderTarget8Bit::~KisTIFFYCbCrReaderTarget8Bit() +{ + delete[] m_bufferCb; + delete[] m_bufferCr; +} + +uint KisTIFFYCbCrReaderTarget8Bit::copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) +{ + int numcols = dataWidth / m_hsub; + double coeff = Q_UINT8_MAX / (double)( pow(2, sourceDepth() ) - 1 ); +// kdDebug(41008) << " depth expension coefficient : " << coeff << endl; +// kdDebug(41008) << " y = " << y << endl; + uint buffPos = y / m_vsub * m_bufferWidth + x / m_hsub ; + for(int index = 0; index < numcols; index++) + { + KisHLineIterator it = paintDevice() -> createHLineIterator(x + m_hsub * index, y, m_hsub, true); + for( int vindex = 0; vindex < m_vsub; vindex++) + { + while( !it.isDone() ) + { + Q_UINT8 *d = it.rawData(); + d[0] = (Q_UINT8)( tiffstream->nextValue() * coeff ); + d[3] = Q_UINT8_MAX; + for(int k = 0; k < nbExtraSamples(); k++) + { + if(k == alphaPos()) + d[3] = (Q_UINT32) ( tiffstream->nextValue() * coeff ); + else + tiffstream->nextValue(); + } + ++it; + } + it.nextRow(); + } + m_bufferCb[ buffPos ] = (Q_UINT8)(tiffstream->nextValue() * coeff); + m_bufferCr[ buffPos ] = (Q_UINT8)(tiffstream->nextValue() * coeff); + buffPos ++; + } + return m_vsub; +} + +void KisTIFFYCbCrReaderTarget8Bit::finalize() +{ + KisHLineIterator it = paintDevice() -> createHLineIterator(0, 0, paintDevice()->image()->width(), true); + for(int y = 0; y < paintDevice()->image()->height(); y++) + { + int x = 0; + while(!it.isDone()) + { + Q_UINT8 *d = it.rawData(); + int index = x/m_hsub + y/m_vsub * m_bufferWidth; + d[1] = m_bufferCb[ index ]; + d[2] = m_bufferCr[ index ]; + ++it; ++x; + } + it.nextRow(); + } +} + +KisTIFFYCbCrReaderTarget16Bit::KisTIFFYCbCrReaderTarget16Bit( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor, uint16 hsub, uint16 vsub, KisTIFFYCbCr::Position position ) : KisTIFFReaderBase(device, poses, alphapos, sourceDepth, nbcolorssamples, extrasamplescount, transformProfile, postprocessor), m_hsub(hsub), m_vsub(vsub), m_position(position) +{ + // Initialize the buffer + Q_INT32 imagewidth = device->image()->width(); + if(2*(imagewidth / 2) != imagewidth) imagewidth++; + m_bufferWidth = imagewidth / m_hsub; + Q_INT32 imageheight = device->image()->height(); + if(2*(imageheight / 2) != imageheight) imageheight++; + m_bufferHeight = imageheight / m_vsub; + m_bufferCb = new Q_UINT16[ m_bufferWidth * m_bufferHeight ]; + m_bufferCr = new Q_UINT16[ m_bufferWidth * m_bufferHeight ]; +} + +KisTIFFYCbCrReaderTarget16Bit::~KisTIFFYCbCrReaderTarget16Bit() +{ + delete[] m_bufferCb; + delete[] m_bufferCr; +} + +uint KisTIFFYCbCrReaderTarget16Bit::copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream) +{ + int numcols = dataWidth / m_hsub; + double coeff = Q_UINT16_MAX / (double)( pow(2, sourceDepth() ) - 1 ); +// kdDebug(41008) << " depth expension coefficient : " << coeff << endl; +// kdDebug(41008) << " y = " << y << endl; + uint buffPos = y / m_vsub * m_bufferWidth + x / m_hsub ; + for(int index = 0; index < numcols; index++) + { + KisHLineIterator it = paintDevice() -> createHLineIterator(x + m_hsub * index, y, m_hsub, true); + for( int vindex = 0; vindex < m_vsub; vindex++) + { + while( !it.isDone() ) + { + Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData()); + d[0] = (Q_UINT16)( tiffstream->nextValue() * coeff ); + d[3] = Q_UINT16_MAX; + for(int k = 0; k < nbExtraSamples(); k++) + { + if(k == alphaPos()) + d[3] = (Q_UINT32) ( tiffstream->nextValue() * coeff ); + else + tiffstream->nextValue(); + } + ++it; + } + it.nextRow(); + } + m_bufferCb[ buffPos ] = (Q_UINT16)(tiffstream->nextValue() * coeff); + m_bufferCr[ buffPos ] = (Q_UINT16)(tiffstream->nextValue() * coeff); + buffPos ++; + } + return m_vsub; +} + +void KisTIFFYCbCrReaderTarget16Bit::finalize() +{ + KisHLineIterator it = paintDevice() -> createHLineIterator(0, 0, paintDevice()->image()->width(), true); + for(int y = 0; y < paintDevice()->image()->height(); y++) + { + int x = 0; + while(!it.isDone()) + { + Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData()); + int index = x/m_hsub + y/m_vsub * m_bufferWidth; + d[1] = m_bufferCb[ index ]; + d[2] = m_bufferCr[ index ]; + ++it; ++x; + } + it.nextRow(); + } +} diff --git a/filters/krita/tiff/kis_tiff_ycbcr_reader.h b/filters/krita/tiff/kis_tiff_ycbcr_reader.h new file mode 100644 index 00000000..b640228c --- /dev/null +++ b/filters/krita/tiff/kis_tiff_ycbcr_reader.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_TIFF_YCBCR_READER_H_ +#define _KIS_TIFF_YCBCR_READER_H_ + +#include "kis_tiff_reader.h" + +namespace KisTIFFYCbCr { + enum Position { + POSITION_CENTERED = 1, + POSITION_COSITED = 2 + }; +} + +class KisTIFFYCbCrReaderTarget8Bit : public KisTIFFReaderBase { + public: + /** + * @param hsub horizontal subsampling of Cb and Cr + * @param hsub vertical subsampling of Cb and Cr + */ + KisTIFFYCbCrReaderTarget8Bit( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor, uint16 hsub, uint16 vsub, KisTIFFYCbCr::Position position ); + ~KisTIFFYCbCrReaderTarget8Bit(); + virtual uint copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream); + virtual void finalize(); + private: + Q_UINT8* m_bufferCb; + Q_UINT8* m_bufferCr; + Q_UINT32 m_bufferWidth, m_bufferHeight; + uint16 m_hsub; + uint16 m_vsub; + KisTIFFYCbCr::Position m_position; + +}; + +class KisTIFFYCbCrReaderTarget16Bit : public KisTIFFReaderBase { + public: + /** + * @param hsub horizontal subsampling of Cb and Cr + * @param hsub vertical subsampling of Cb and Cr + */ + KisTIFFYCbCrReaderTarget16Bit( KisPaintDeviceSP device, Q_UINT8* poses, int8 alphapos, uint8 sourceDepth, uint8 nbcolorssamples, uint8 extrasamplescount, cmsHTRANSFORM transformProfile, KisTIFFPostProcessor* postprocessor, uint16 hsub, uint16 vsub, KisTIFFYCbCr::Position position ); + ~KisTIFFYCbCrReaderTarget16Bit(); + virtual uint copyDataToChannels( Q_UINT32 x, Q_UINT32 y, Q_UINT32 dataWidth, TIFFStreamBase* tiffstream); + virtual void finalize(); + private: + Q_UINT16* m_bufferCb; + Q_UINT16* m_bufferCr; + Q_UINT32 m_bufferWidth, m_bufferHeight; + uint16 m_hsub; + uint16 m_vsub; + KisTIFFYCbCr::Position m_position; + +}; + + +#endif diff --git a/filters/krita/tiff/kis_wdg_options_tiff.ui b/filters/krita/tiff/kis_wdg_options_tiff.ui new file mode 100644 index 00000000..2e1f1c80 --- /dev/null +++ b/filters/krita/tiff/kis_wdg_options_tiff.ui @@ -0,0 +1,741 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KisWdgOptionsTIFF</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KisWdgOptionsTIFF</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>267</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Options of Your TIFF</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>TIFF Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Compression type:</string> + </property> + </widget> + <widget class="KComboBox"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>JPEG DCT Compression</string> + </property> + </item> + <item> + <property name="text"> + <string>Deflate (ZIP)</string> + </property> + </item> + <item> + <property name="text"> + <string>Lempel-Ziv & Welch (LZW)</string> + </property> + </item> + <item> + <property name="text"> + <string>Leadtools JPEG2000</string> + </property> + </item> + <item> + <property name="text"> + <string>CCITT Modified Huffman RLE</string> + </property> + </item> + <item> + <property name="text"> + <string>CCITT Group 3 Fax Encoding</string> + </property> + </item> + <item> + <property name="text"> + <string>CCITT Group 4 Fax Encoding</string> + </property> + </item> + <item> + <property name="text"> + <string>Pixar Log</string> + </property> + </item> + <property name="name"> + <cstring>kComboBoxCompressionType</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Predictor:</string> + </property> + </widget> + <widget class="KComboBox"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>Horizontal Differencing</string> + </property> + </item> + <item> + <property name="text"> + <string>Floating Point Horizontal Differencing</string> + </property> + </item> + <property name="name"> + <cstring>kComboBoxPredictor</cstring> + </property> + <property name="currentItem"> + <number>0</number> + </property> + <property name="toolTip" stdset="0"> + <string>Using a predictor can improve the compression (mostly for LZW and deflate)</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>alpha</cstring> + </property> + <property name="text"> + <string>Store alpha &channel (transparency)</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Disable to get smaller files if your image has no transparancy</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>The Portable Network Graphics (PNG) file format allows transparancy in your image to be stored by saving an alpha channel. +You can uncheck the box if you are not using transparancy and you want to make the resulting file smaller .<br>Always saving the alpha channel is recommended.</p></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>flatten</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Flatten the &image</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>This option will merge all layers. It is advisable to check this option, otherwise other applications might not be able to read your file correctly.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidgetStack"> + <property name="name"> + <cstring>codecsOptionsStack</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>0</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame4</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>1</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBoxJPEG</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>JPEG Compression Options</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Quality:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider"> + <property name="name"> + <cstring>qualityLevel</cstring> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="maxValue"> + <number>100</number> + </property> + <property name="lineStep"> + <number>1</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="value"> + <number>80</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>10</number> + </property> + <property name="toolTip" stdset="0"> + <string>These settings determine how much information is lost during compression</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4_2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Smallest</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Best</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>2</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBoxDeflate</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Deflate Compression Options</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Compress:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + <property name="toolTip" stdset="0"> + <string>Note: the compression level does not change the quality of the result</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level does not change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5_2</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider"> + <property name="name"> + <cstring>compressionLevelDeflate</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>9</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="value"> + <number>6</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Note: the compression level doesn't change the quality of the result</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>Fast</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4_2</cstring> + </property> + <property name="text"> + <string>Small</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>3</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBoxCCITGroupCCITG3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>CCITT Group 3 fax encoding Options</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Fax mode:</string> + </property> + </widget> + <widget class="KComboBox"> + <item> + <property name="text"> + <string>Classic</string> + </property> + </item> + <item> + <property name="text"> + <string>No RTC</string> + </property> + </item> + <item> + <property name="text"> + <string>No EOL</string> + </property> + </item> + <property name="name"> + <cstring>kComboBoxFaxMode</cstring> + </property> + </widget> + </hbox> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>4</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBoxPixarLog</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Pixar Log Compression Options</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_4_2</cstring> + </property> + <property name="text"> + <string>Compress:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + <property name="toolTip" stdset="0"> + <string>Note: the compression level does not change the quality of the result</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level does not change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5_2_2</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider"> + <property name="name"> + <cstring>compressionLevelPixarLog</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>9</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="value"> + <number>6</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Note: the compression level doesn't change the quality of the result</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4_3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_2_2</cstring> + </property> + <property name="text"> + <string>Fast</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4_2_2</cstring> + </property> + <property name="text"> + <string>Small</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level doesn't change the quality of the result.</p></string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </hbox> + </widget> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>kComboBoxCompressionType</tabstop> + <tabstop>kComboBoxPredictor</tabstop> + <tabstop>alpha</tabstop> + <tabstop>flatten</tabstop> + <tabstop>qualityLevel</tabstop> + <tabstop>compressionLevelDeflate</tabstop> + <tabstop>kComboBoxFaxMode</tabstop> + <tabstop>compressionLevelPixarLog</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> +</includehints> +</UI> diff --git a/filters/krita/tiff/kis_ycbcr_colorspace.h b/filters/krita/tiff/kis_ycbcr_colorspace.h new file mode 100644 index 00000000..0b4ece87 --- /dev/null +++ b/filters/krita/tiff/kis_ycbcr_colorspace.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006 Cyrille Berger <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_YCBCR_COLORSPACE_H +#define KIS_YCBCR_COLORSPACE_H +namespace { + const Q_INT32 MAX_CHANNEL_YCbCr = 3; + const Q_INT32 MAX_CHANNEL_YCbCrA = 4; +} +#endif + diff --git a/filters/krita/tiff/krita_tiff.desktop b/filters/krita/tiff/krita_tiff.desktop new file mode 100644 index 00000000..455f6caa --- /dev/null +++ b/filters/krita/tiff/krita_tiff.desktop @@ -0,0 +1,63 @@ +[Desktop Entry] +Categories= +Exec=krita %u +GenericName=Painting and Image Editing Application +GenericName[bg]=Редактор на графични изображения +GenericName[ca]=Programa de dibuix i manipulació d'imatges +GenericName[cs]=Malování a úpravy obrázků +GenericName[cy]=Cymhwysiad Peintio Golygu Delweddau +GenericName[da]=Male- og billedredigeringsprogram +GenericName[de]=Mal- und Bildbearbeitungsprogramm +GenericName[el]=Εφαρμογή επεξεργασίας εικόνων +GenericName[eo]=Aplikaĵo por Pentrado kaj Bildredaktado +GenericName[es]=Aplicación de pintura y de edición de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudien marrazketa eta ediziorako aplikazioa +GenericName[fa]=کاربرد ویرایش تصویر و نقاشی +GenericName[fi]=Maalaus- ja kuvankäsitelyohjelma +GenericName[fr]=Application de dessin et de manipulation d'images +GenericName[fy]=Ofbyldingsmanipulaasje +GenericName[gl]=Aplicación de Pintura e Manipulación de Imaxes +GenericName[he]=יישום לציור ועריכת תמונות +GenericName[hr]=Aplikacija za obradu slika i fotografija +GenericName[hu]=Képszerkesztő +GenericName[is]=Málun og myndritill +GenericName[it]=Applicazione di disegno e di modifica delle immagini +GenericName[ja]=描画と画像編集のためのアプリケーション +GenericName[km]=កម្មវិធីគូរគំនូរ និងកែសម្រួលរូបភាព +GenericName[lv]=Zīmēšanas un attēlu apstrādes programma +GenericName[nb]=Program for tegning og bilderedigering +GenericName[nds]=Programm för't Malen un Bildbewerken +GenericName[ne]=पेन्टीङ्ग र छवि सम्पादन अनुप्रयोग +GenericName[nl]=Afbeeldingsmanipulatie +GenericName[pl]=Program do edycji zdjęć oraz rysunków +GenericName[pt]=Aplicação de Pintura e Edição de Imagens +GenericName[pt_BR]=Aplicação de Pintura e Edição de Imagens +GenericName[ru]=Растровые изображения +GenericName[se]=Málen- ja govvagieđahallanprográmma +GenericName[sk]=Program pre tvorbu a úpravu obrázkov +GenericName[sl]=Program za risanje in obdelavo slik +GenericName[sr]=Програм за цртање и уређивање слика +GenericName[sr@Latn]=Program za crtanje i uređivanje slika +GenericName[sv]=Målnings- och bildredigeringsprogram +GenericName[uk]=Програма для малювання і редагування зображень +GenericName[uz]=Rasmlar bilan ishlaydigan dastur +GenericName[uz@cyrillic]=Расмлар билан ишлайдиган дастур +GenericName[zh_CN]=绘图和图像编辑应用程序 +GenericName[zh_TW]=繪圖與影像處理程式 +Icon=krita +MimeType=image/tiff +Name=Krita +Name[hi]=के-रिता +Name[km]= Krita +Name[lo]=ກຣິຕາ +Name[ne]=क्रिता +Path= +StartupNotify=true +Terminal=false +TerminalOptions= +Type=Application +X-DCOP-ServiceType=multi +X-KDE-StartupNotify=true +X-KDE-SubstituteUID=false +X-KDE-Username= diff --git a/filters/krita/tiff/krita_tiff_export.desktop b/filters/krita/tiff/krita_tiff_export.desktop new file mode 100644 index 00000000..e696cd5a --- /dev/null +++ b/filters/krita/tiff/krita_tiff_export.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Icon= +Name=Krita TIFF Export Filter +Name[bg]=Филтър за експортиране от Krita в TIFF +Name[br]=Sil ezporzh TIFF evit Krita +Name[ca]=Filtre d'exportació TIFF per a Krita +Name[da]=Krita TIFF-eksportfilter +Name[de]=Krita TIFF-Exportfilter +Name[el]=Φίλτρο εξαγωγής TIFF του Krita +Name[eo]=Krita TIFF-eksportfiltrilo +Name[es]=Filtro de exportación a TIFF de Krita +Name[et]=Krita TIFF-i ekspordifilter +Name[fa]=پالایۀ صادرات Krita TIFF +Name[fi]=Krita TIFF -vientisuodin +Name[fr]=Filtre d'exportation TIFF de Krita +Name[fy]=Krita TIFF Eksportfilter +Name[ga]=Scagaire Easpórtála TIFF Krita +Name[gl]=Filtro de Exportación de TIFF para Krita +Name[he]=Krita TIFF מסנן יצוא +Name[hr]=Krita TIFF filtar izvoza +Name[hu]=Krita TIFF exportszűrő +Name[is]=Krita TIFF útflutningssía +Name[it]=Filtro di esportazione TIFF per Krita +Name[ja]=Krita TIFF エクスポートフィルタ +Name[km]=តម្រងនាំចេញ TIFF សម្រាប់ Krita +Name[lt]=Krita TIFF eksportavimo filtras +Name[lv]=Krita TIFF eksporta filtrs +Name[nb]=TIFF-eksportfilter for Krita +Name[nds]=TIFF-Exportfilter för Krita +Name[ne]=क्रिता TIFF निर्यात फिल्टर +Name[nl]=Krita TIFF Exportfilter +Name[pl]=Filtr eksportu do formatu TIFF dla Krita +Name[pt]=Filtro de Exportação de TIFF para o Krita +Name[pt_BR]=Filtro de Exportação de TIFF para o Krita +Name[ru]=Фильтр экспорта рисунков Krita в TIFF +Name[se]=Krita Tiff-olggosfievrridansilli +Name[sk]=Exportný filter Krita TIFF +Name[sl]=Izvozni filter TIFF za Krito +Name[sr]=Krita-ин филтер за извоз у TIFF +Name[sr@Latn]=Krita-in filter za izvoz u TIFF +Name[sv]=Krita TIFF-exportfilter +Name[uk]=Фільтр експорту TIFF для Krita +Name[uz]=Krita TIFF eksport filteri +Name[uz@cyrillic]=Krita TIFF экспорт филтери +Name[zh_CN]=Krita TIFF 导出过滤器 +Name[zh_TW]=Krita TIFF 匯出過濾程式 +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Export=image/tiff +X-KDE-Import=application/x-krita +X-KDE-Library=libkritatiffexport +X-KDE-Weight=1 diff --git a/filters/krita/tiff/krita_tiff_import.desktop b/filters/krita/tiff/krita_tiff_import.desktop new file mode 100644 index 00000000..65e7ae6c --- /dev/null +++ b/filters/krita/tiff/krita_tiff_import.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Icon= +Name=Krita TIFF Import Filter +Name[bg]=Филтър за импортиране от TIFF в Krita +Name[br]=Sil enporzh TIFF evit Krita +Name[ca]=Filtre d'importació TIFF per a Krita +Name[da]=Krita TIFF-importfilter +Name[de]=Krita TIFF-Importfilter +Name[el]=Φίλτρο εισαγωγής TIFF του Krita +Name[eo]=Krita TIFF-importfiltrilo +Name[es]=Filtro de importación desde TIFF de Krita +Name[et]=Krita TIFF-i impordifilter +Name[fa]=پالایۀ واردات Krita TIFF +Name[fi]=Krita TIFF -tuontisuodin +Name[fr]=Filtre d'importation TIFF de Krita +Name[fy]=Krita TIFF Ymportfilter +Name[ga]=Scagaire Iompórtála TIFF Krita +Name[gl]=Filtro de Importación de TIFF para Krita +Name[he]=Krita TIFF מסנן יבוא +Name[hr]=Krita TIFF filtar uvoza +Name[hu]=Krita TIFF importszűrő +Name[is]=Krita TIFF innflutningssía +Name[it]=Filtro di importazione TIFF per Krita +Name[ja]=Krita TIFF インポートフィルタ +Name[km]=តម្រងនាំចូល TIFF សម្រាប់ Krita +Name[lt]=Krita TIFF importavimo filtras +Name[lv]=Krita TIFF importa filtrs +Name[nb]=TIFF-importfilter for Krita +Name[nds]=TIFF-Importfilter för Krita +Name[ne]=क्रिता TIFF आयात फिल्टर +Name[nl]=Krita TIFF Importfilter +Name[pl]=Filtr importu formatu TIFF dla Krita +Name[pt]=Filtro de Importação de TIFF para o Krita +Name[pt_BR]=Filtro de Importação de TIFF para o Krita +Name[ru]=Фильтр импорта рисунков TIFF в Krita +Name[se]=Krita TIFF-sisafievrridansilli +Name[sk]=TIFF filter pre import do Krita +Name[sl]=Uvozni filter TIFF za Krito +Name[sr]=Krita-ин филтер за увоз из TIFF-а +Name[sr@Latn]=Krita-in filter za uvoz iz TIFF-a +Name[sv]=Krita TIFF-importfilter +Name[uk]=Фільтр імпорту TIFF для Krita +Name[uz]=Krita TIFF import filteri +Name[uz@cyrillic]=Krita TIFF импорт филтери +Name[zh_CN]=Krita TIFF 导入过滤器 +Name[zh_TW]=Krita TIFF 匯入過濾程式 +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Export=application/x-krita +X-KDE-Import=image/tiff +X-KDE-Library=libkritatiffimport +X-KDE-Weight=1 diff --git a/filters/krita/xcf/Makefile.am b/filters/krita/xcf/Makefile.am new file mode 100644 index 00000000..7443abb3 --- /dev/null +++ b/filters/krita/xcf/Makefile.am @@ -0,0 +1,38 @@ +kde_module_LTLIBRARIES = libkritaxcfimport.la libkritaxcfexport.la + +libkritaxcfexport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritaxcfexport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBMAGICK_LIBS) \ + $(top_builddir)/krita/libkritacommon.la + +libkritaxcfimport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined +libkritaxcfimport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBMAGICK_LIBS) \ + $(top_builddir)/krita/libkritacommon.la + +INCLUDES= \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/krita \ + -I$(top_srcdir)/krita/core \ + -I$(top_srcdir)/krita/sdk \ + -I$(top_srcdir)/krita/core/tiles \ + -I$(top_srcdir)/krita/kritacolor \ + -I$(top_srcdir)/krita/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) $(LIBMAGICK_CPPFLAGS) \ + $(all_includes) + +service_DATA = krita_xcf_import.desktop krita_xcf_export.desktop +servicedir = $(kde_servicesdir) + +libkritaxcfimport_la_SOURCES = xcfimport.cpp +libkritaxcfexport_la_SOURCES = xcfexport.cpp + +METASOURCES = AUTO + +SUBDIRS=xcf + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) diff --git a/filters/krita/xcf/krita_xcf_export.desktop b/filters/krita/xcf/krita_xcf_export.desktop new file mode 100644 index 00000000..f4cde2ec --- /dev/null +++ b/filters/krita/xcf/krita_xcf_export.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=Krita XCF Export Filter +Name[bg]=Филтър за експортиране от Krita в XCF +Name[br]=Sil ezporzh XCF evit Krita +Name[ca]=Filtre d'exportació XCF per a Krita +Name[cy]=Hidl Allforio XCF Krita +Name[da]=Krita XCF-eksportfilter +Name[de]=Krita XCF-Exportfilter +Name[el]=Φίλτρο εξαγωγής XCF του Krita +Name[eo]=Krita XCF-importfiltrilo +Name[es]=Filtro de exportación a XCF de Krita +Name[et]=Krita XCF-i ekspordifilter +Name[fa]=پالایۀ صادرات Krita XCF +Name[fi]=Krita XCF -vientisuodin +Name[fr]=Filtre d'exportation XCF de Krita +Name[fy]=Krita XCF Eksportfilter +Name[ga]=Scagaire Easpórtála XCF Krita +Name[gl]=Filtro de Exportación de XCF para Krita +Name[he]=Krita XCF מסנן יצוא +Name[hr]=Krita XCF filtar izvoza +Name[hu]=Krita XCF exportszűrő +Name[is]=Krita XCF útflutningssía +Name[it]=Filtro di esportazione XCF per Krita +Name[ja]=Krita XCF エクスポートフィルタ +Name[km]=តម្រងនាំចេញ XCF សម្រាប់ Krita +Name[lt]=Krita XCF eksportavimo filtras +Name[lv]=Krita XCF eksporta filtrs +Name[nb]=XCF-eksportfilter for Krita +Name[nds]=XCF-Exportfilter för Krita +Name[ne]=क्रिता XFC निर्यात फिल्टर +Name[nl]=Krita XCF Exportfilter +Name[pl]=Filtr eksportu do formatu XCF dla Krita +Name[pt]=Filtro de Exportação de XCF para o Krita +Name[pt_BR]=Filtro de Exportação de XCF para o Krita +Name[ru]=Фильтр экспорта рисунков Krita в XCF +Name[se]=Krita XCF-olggosfievrridansilli +Name[sk]=Exportný filter Krita XCF +Name[sl]=Izvozni filter XCF za Krito +Name[sr]=Krita-ин филтер за извоз у XCF +Name[sr@Latn]=Krita-in filter za izvoz u XCF +Name[sv]=Krita XCF-exportfilter +Name[uk]=Фільтр експорту XCF для Krita +Name[uz]=Krita XCF eksport filteri +Name[uz@cyrillic]=Krita XCF экспорт филтери +Name[zh_CN]=Krita XCF 导出过滤器 +Name[zh_TW]=Krita XCF 匯出過濾程式 +X-KDE-Export=image/x-xcf-gimp +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Import=application/x-krita +X-KDE-Weight=1 +X-KDE-Library=libkritaxcfexport diff --git a/filters/krita/xcf/krita_xcf_import.desktop b/filters/krita/xcf/krita_xcf_import.desktop new file mode 100644 index 00000000..7edbcf2c --- /dev/null +++ b/filters/krita/xcf/krita_xcf_import.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Type=Service +Name=Krita XCF Import Filter +Name[bg]=Филтър за импортиране от XCF в Krita +Name[br]=Sil enporzh XCF evit Krita +Name[ca]=Filtre d'importació XCF per a Krita +Name[cy]=Hidl Mewnforio XCF Krita +Name[da]=Krita XCF-importfilter +Name[de]=Krita XCF-Importfilter +Name[el]=Φίλτρο εισαγωγής XCF του Krita +Name[eo]=Krita XCF-importfiltrilo +Name[es]=Filtro de importación a XCF de Krita +Name[et]=Krita XCF-i impordifilter +Name[fa]=پالایۀ واردات Krita XCF +Name[fi]=Krita XCF -tuontisuodin +Name[fr]=Filtre d'importation XCF de Krita +Name[fy]=Krita XCF Ymportfilter +Name[ga]=Scagaire Iompórtála XCF Krita +Name[gl]=Filtro de Importación de XCF para Krita +Name[he]=Krita XCF מסנן יבוא +Name[hr]=Krita XCF filtar uvoza +Name[hu]=Krita XCF importszűrő +Name[is]=Krita XCF innflutningssía +Name[it]=Filtro di importazione XCF per Krita +Name[ja]=Krita XCF インポートフィルタ +Name[km]=តម្រងនាំចូល XCF សម្រាប់ Krita +Name[lt]=Krita XCF importavimo filtras +Name[lv]=Krita XCF importa filtrs +Name[nb]=XCF-importfilter for Krita +Name[nds]=XCF-Importfilter för Krita +Name[ne]=क्रिता XCF आयात फिल्टर +Name[nl]=Krita XCF Importfilter +Name[pl]=Filtr importu formatu XCF dla Krita +Name[pt]=Filtro de Importação de XCF para o Krita +Name[pt_BR]=Filtro de Importação de XCF para o Krita +Name[ru]=Фильтр импорта рисунков XCF в Krita +Name[se]=Krita XCF-sisafievrridansilli +Name[sk]=XCF filter pre import do Krita +Name[sl]=Uvozni filter XCF za Krito +Name[sr]=Krita-ин филтер за увоз из XCF-а +Name[sr@Latn]=Krita-in filter za uvoz iz XCF-a +Name[sv]=Krita XCF-importfilter +Name[uk]=Фільтр імпорту XCF для Krita +Name[uz]=Krita XCF import filteri +Name[uz@cyrillic]=Krita XCF импорт филтери +Name[zh_CN]=Krita XCF 导入过滤器 +Name[zh_TW]=Krita XCF 匯入過濾程式 +X-KDE-Export=application/x-krita +X-KDE-Import=image/x-xcf-gimp +X-KDE-Weight=1 +X-KDE-Library=libkritaxcfimport +ServiceTypes=KOfficeFilter diff --git a/filters/krita/xcf/xcf/README b/filters/krita/xcf/xcf/README new file mode 100644 index 00000000..567d2ab1 --- /dev/null +++ b/filters/krita/xcf/xcf/README @@ -0,0 +1,2 @@ +Note: this is source copy of 6 feb. 2006; when updated, use a diff from the gimp cvs +to check for changes in their file format. diff --git a/filters/krita/xcf/xcf/xcf-load.cc b/filters/krita/xcf/xcf/xcf-load.cc new file mode 100644 index 00000000..dded6883 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-load.cc @@ -0,0 +1,1740 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> /* strcmp, memcmp */ + +//#include <glib-object.h> + +//#include "libgimpbase/gimpbase.h" +//#include "libgimpcolor/gimpcolor.h" + +//#include "core/core-types.h" + +//#include "base/tile.h" +//#include "base/tile-manager.h" +//#include "base/tile-manager-private.h" + +//#include "config/gimpcoreconfig.h" + +//#include "core/gimp.h" +//#include "core/gimpcontainer.h" +//#include "core/gimpdrawable.h" +//#include "core/gimpgrid.h" +//#include "core/gimpimage.h" +//#include "core/gimpimage-grid.h" +//#include "core/gimpimage-guides.h" +//#include "core/gimplayer.h" +//#include "core/gimplayer-floating-sel.h" +//#include "core/gimplayermask.h" +//#include "core/gimpparasitelist.h" +//#include "core/gimpselection.h" +//#include "core/gimptemplate.h" +//#include "core/gimpunit.h" + +//#include "text/gimptextlayer.h" +//#include "text/gimptextlayer-xcf.h" + +//#include "vectors/gimpanchor.h" +//#include "vectors/gimpstroke.h" +//#include "vectors/gimpbezierstroke.h" +//#include "vectors/gimpvectors.h" +//#include "vectors/gimpvectors-compat.h" + +#include "xcf-private.h" +#include "xcf-load.h" +#include "xcf-read.h" +#include "xcf-seek.h" + +//#include "gimp-intl.h" + +static bool xcf_load_image_props (XcfInfo * info, KisImage * gimage); +static bool xcf_load_layer_props (XcfInfo * info, + KisImage * gimage, + KisLayer * layer, + bool * apply_mask, + bool * edit_mask, + bool * show_mask, + Q_INT32 * text_layer_flags); +static bool xcf_load_channel_props (XcfInfo * info, + KisImage * gimage, + GimpChannel ** channel); +static bool xcf_load_prop (XcfInfo * info, + PropType * prop_type, Q_INT32 * prop_size); +static KisLayer *xcf_load_layer (XcfInfo * info, KisImage * gimage); +//static GimpChannel * xcf_load_channel (XcfInfo *info, +// KisImage *gimage); +//static GimpLayerMask * xcf_load_layer_mask (XcfInfo *info, +// KisImage *gimage); +static bool xcf_load_hierarchy (XcfInfo * info, TileManager * tiles); +static bool xcf_load_level (XcfInfo * info, TileManager * tiles); +static bool xcf_load_tile (XcfInfo * info, Tile * tile); +static bool xcf_load_tile_rle (XcfInfo * info, + Tile * tile, Q_INT32 data_length); +//static GimpParasite * xcf_load_parasite (XcfInfo *info); +static bool xcf_load_old_paths (XcfInfo * info, KisImage * gimage); +static bool xcf_load_old_path (XcfInfo * info, KisImage * gimage); +static bool xcf_load_vectors (XcfInfo * info, KisImage * gimage); +static bool xcf_load_vector (XcfInfo * info, KisImage * gimage); + +#ifdef SWAP_FROM_FILE +static bool xcf_swap_func (Q_INT32 fd, + Tile * tile, Q_INT32 cmd, gpointer user_data); +#endif + + +KisImage * +xcf_load_image (XcfInfo * info) +{ + KisImage *gimage; + KisLayer *layer; + //GimpChannel *channel; + //KisAnnotation *parasite; + Q_INT32 saved_pos; + Q_INT32 offset; + Q_INT32 width; + Q_INT32 height; + Q_INT32 image_type; + Q_INT32 num_successful_elements = 0; + + /* read in the image width, height and type */ + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & height, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & image_type, 1); + + gimage = gimp_create_image (gimp, width, height, image_type, FALSE); + + gimp_image_undo_disable (gimage); + + /* read the image properties */ + if (!xcf_load_image_props (info, gimage)) + goto hard_error; + + /* check for a GimpGrid parasite */ + parasite = gimp_image_parasite_find (GIMP_IMAGE (gimage), + gimp_grid_parasite_name ()); + if (parasite) + { + GimpGrid *grid = gimp_grid_from_parasite (parasite); + + if (grid) + { + gimp_parasite_list_remove (GIMP_IMAGE (gimage)->parasites, + gimp_parasite_name (parasite)); + + gimp_image_set_grid (GIMP_IMAGE (gimage), grid, FALSE); + } + } + + while (TRUE) + { + /* read in the offset of the next layer */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + + /* if the offset is 0 then we are at the end + * of the layer list. + */ + if (offset == 0) + break; + + /* save the current position as it is where the + * next layer offset is stored. + */ + saved_pos = info->cp; + + /* seek to the layer offset */ + if (!xcf_seek_pos (info, offset, NULL)) + goto error; + + /* read in the layer */ + layer = xcf_load_layer (info, gimage); + if (!layer) + goto error; + + num_successful_elements++; + + /* add the layer to the image if its not the floating selection */ + if (layer != info->floating_sel) + gimp_image_add_layer (gimage, layer, + gimp_container_num_children (gimage->layers)); + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + goto error; + } + + while (TRUE) + { + /* read in the offset of the next channel */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + + /* if the offset is 0 then we are at the end + * of the channel list. + */ + if (offset == 0) + break; + + /* save the current position as it is where the + * next channel offset is stored. + */ + saved_pos = info->cp; + + /* seek to the channel offset */ + if (!xcf_seek_pos (info, offset, NULL)) + goto error; + + /* read in the layer */ + channel = xcf_load_channel (info, gimage); + if (!channel) + goto error; + + num_successful_elements++; + + /* add the channel to the image if its not the selection */ + if (channel != gimage->selection_mask) + gimp_image_add_channel (gimage, channel, -1); + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + goto error; + } + + if (info->floating_sel && info->floating_sel_drawable) + floating_sel_attach (info->floating_sel, info->floating_sel_drawable); + + if (info->active_layer) + gimp_image_set_active_layer (gimage, info->active_layer); + + if (info->active_channel) + gimp_image_set_active_channel (gimage, info->active_channel); + + gimp_image_set_filename (gimage, info->filename); + + if (info->tattoo_state > 0) + gimp_image_set_tattoo_state (gimage, info->tattoo_state); + + gimp_image_undo_enable (gimage); + + return gimage; + +error: + if (num_successful_elements == 0) + goto hard_error; + + g_message ("XCF: This file is corrupt! I have loaded as much\n" + "of it as I can, but it is incomplete."); + + gimp_image_undo_enable (gimage); + + return gimage; + +hard_error: + g_message ("XCF: This file is corrupt! I could not even\n" + "salvage any partial image data from it."); + + g_object_unref (gimage); + + return NULL; +} + +static bool +xcf_load_image_props (XcfInfo * info, KisImage * gimage) +{ + PropType prop_type; + Q_INT32 prop_size; + + while (TRUE) + { + if (!xcf_load_prop (info, &prop_type, &prop_size)) + return FALSE; + + switch (prop_type) + { + case PROP_END: + return TRUE; + + case PROP_COLORMAP: + if (info->file_version == 0) + { + Q_INT32 i; + + g_message (_("XCF warning: version 0 of XCF file format\n" + "did not save indexed colormaps correctly.\n" + "Substituting grayscale map.")); + info->cp += + xcf_read_int32 (info->fp, (Q_INT32 *) & gimage->num_cols, 1); + gimage->cmap = g_new (guchar, gimage->num_cols * 3); + if (!xcf_seek_pos (info, info->cp + gimage->num_cols, NULL)) + return FALSE; + + for (i = 0; i < gimage->num_cols; i++) + { + gimage->cmap[i * 3 + 0] = i; + gimage->cmap[i * 3 + 1] = i; + gimage->cmap[i * 3 + 2] = i; + } + } + else + { + info->cp += + xcf_read_int32 (info->fp, (Q_INT32 *) & gimage->num_cols, 1); + gimage->cmap = g_new (guchar, gimage->num_cols * 3); + info->cp += + xcf_read_int8 (info->fp, + (Q_UINT8 *) gimage->cmap, + gimage->num_cols * 3); + } + + /* discard color map, if image is not indexed, this is just + * sanity checking to make sure gimp doesn't end up with an + * image state that is impossible. + */ + if (gimp_image_base_type (gimage) != GIMP_INDEXED) + { + g_free (gimage->cmap); + gimage->cmap = NULL; + gimage->num_cols = 0; + } + break; + + case PROP_COMPRESSION: + { + Q_UINT8 compression; + + info->cp += + xcf_read_int8 (info->fp, (Q_UINT8 *) & compression, 1); + + if ((compression != COMPRESS_NONE) && + (compression != COMPRESS_RLE) && + (compression != COMPRESS_ZLIB) && + (compression != COMPRESS_FRACTAL)) + { + g_message ("unknown compression type: %d", (int) compression); + return FALSE; + } + + info->compression = compression; + } + break; + + case PROP_GUIDES: + { + Q_INT3232 position; + Q_INT328 orientation; + Q_INT32 i, nguides; + + nguides = prop_size / (4 + 1); + for (i = 0; i < nguides; i++) + { + info->cp += + xcf_read_int32 (info->fp, (Q_INT32 *) & position, 1); + info->cp += + xcf_read_int8 (info->fp, (Q_UINT8 *) & orientation, 1); + + /* skip -1 guides from old XCFs */ + if (position < 0) + continue; + + switch (orientation) + { + case XCF_ORIENTATION_HORIZONTAL: + gimp_image_add_hguide (gimage, position, FALSE); + break; + + case XCF_ORIENTATION_VERTICAL: + gimp_image_add_vguide (gimage, position, FALSE); + break; + + default: + g_message ("guide orientation out of range in XCF file"); + continue; + } + } + + /* this is silly as the order of guides doesn't really matter, + * but it restores the list to it's original order, which + * cannot be wrong --Mitch + */ + gimage->guides = g_list_reverse (gimage->guides); + } + break; + + case PROP_RESOLUTION: + { + float xres, yres; + + info->cp += xcf_read_float (info->fp, &xres, 1); + info->cp += xcf_read_float (info->fp, &yres, 1); + if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION || + yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION) + { + g_message ("Warning, resolution out of range in XCF file"); + xres = gimage->gimp->config->default_image->xresolution; + yres = gimage->gimp->config->default_image->yresolution; + } + gimage->xresolution = xres; + gimage->yresolution = yres; + } + break; + + case PROP_TATTOO: + { + info->cp += xcf_read_int32 (info->fp, &info->tattoo_state, 1); + } + break; + + case PROP_PARASITES: + { + glong base = info->cp; + KisAnnotation *p; + + while (info->cp - base < prop_size) + { + p = xcf_load_parasite (info); + gimp_image_parasite_attach (gimage, p); + gimp_parasite_free (p); + } + if (info->cp - base != prop_size) + g_message ("Error while loading an image's parasites"); + } + break; + + case PROP_UNIT: + { + Q_INT32 unit; + + info->cp += xcf_read_int32 (info->fp, &unit, 1); + + if ((unit <= GIMP_UNIT_PIXEL) || + (unit >= + _gimp_unit_get_number_of_built_in_units (gimage->gimp))) + { + g_message ("Warning, unit out of range in XCF file, " + "falling back to inches"); + unit = GIMP_UNIT_INCH; + } + + gimage->resolution_unit = unit; + } + break; + + case PROP_PATHS: + xcf_load_old_paths (info, gimage); + break; + + case PROP_USER_UNIT: + { + QCString *unit_strings[5]; + float factor; + Q_INT32 digits; + GimpUnit unit; + Q_INT32 num_units; + Q_INT32 i; + + info->cp += xcf_read_float (info->fp, &factor, 1); + info->cp += xcf_read_int32 (info->fp, &digits, 1); + info->cp += xcf_read_string (info->fp, unit_strings, 5); + + for (i = 0; i < 5; i++) + if (unit_strings[i] == NULL) + unit_strings[i] = g_strdup (""); + + num_units = _gimp_unit_get_number_of_units (gimage->gimp); + + for (unit = + _gimp_unit_get_number_of_built_in_units (gimage->gimp); + unit < num_units; unit++) + { + /* if the factor and the identifier match some unit + * in unitrc, use the unitrc unit + */ + if ((ABS (_gimp_unit_get_factor (gimage->gimp, + unit) - factor) < 1e-5) && + (strcmp (unit_strings[0], + _gimp_unit_get_identifier (gimage->gimp, + unit)) == 0)) + { + break; + } + } + + /* no match */ + if (unit == num_units) + unit = _gimp_unit_new (gimage->gimp, + unit_strings[0], + factor, + digits, + unit_strings[1], + unit_strings[2], + unit_strings[3], unit_strings[4]); + + gimage->resolution_unit = unit; + + for (i = 0; i < 5; i++) + g_free (unit_strings[i]); + } + break; + + case PROP_VECTORS: + { + Q_INT32 base = info->cp; + + if (xcf_load_vectors (info, gimage)) + { + if (base + prop_size != info->cp) + { + g_warning + ("Mismatch in PROP_VECTORS size: skipping %d bytes.", + base + prop_size - info->cp); + xcf_seek_pos (info, base + prop_size, NULL); + } + } + else + { + /* skip silently since we don't understand the format and + * xcf_load_vectors already explained what was wrong + */ + xcf_seek_pos (info, base + prop_size, NULL); + } + } + break; + + default: +#ifdef GIMP_UNSTABLE + g_printerr ("unexpected/unknown image property: %d (skipping)", + prop_type); +#endif + { + Q_UINT8 buf[16]; + Q_UINT32 amount; + + while (prop_size > 0) + { + amount = MIN (16, prop_size); + info->cp += xcf_read_int8 (info->fp, buf, amount); + prop_size -= MIN (16, amount); + } + } + break; + } + } + + return FALSE; +} + +static bool +xcf_load_layer_props (XcfInfo * info, + KisImage * gimage, + KisLayer * layer, + bool * apply_mask, + bool * edit_mask, + bool * show_mask, Q_INT32 * text_layer_flags) +{ + PropType prop_type; + Q_INT32 prop_size; + + while (TRUE) + { + if (!xcf_load_prop (info, &prop_type, &prop_size)) + return FALSE; + + switch (prop_type) + { + case PROP_END: + return TRUE; + + case PROP_ACTIVE_LAYER: + info->active_layer = layer; + break; + + case PROP_FLOATING_SELECTION: + info->floating_sel = layer; + info->cp += + xcf_read_int32 (info->fp, + (Q_INT32 *) & info->floating_sel_offset, 1); + break; + + case PROP_OPACITY: + { + Q_INT32 opacity; + + info->cp += xcf_read_int32 (info->fp, &opacity, 1); + layer->opacity = CLAMP ((gdouble) opacity / 255.0, + GIMP_OPACITY_TRANSPARENT, + GIMP_OPACITY_OPAQUE); + } + break; + + case PROP_VISIBLE: + { + bool visible; + + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & visible, 1); + gimp_item_set_visible (GIMP_ITEM (layer), + visible ? TRUE : FALSE, FALSE); + } + break; + + case PROP_LINKED: + { + bool linked; + + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & linked, 1); + gimp_item_set_linked (GIMP_ITEM (layer), + linked ? TRUE : FALSE, FALSE); + } + break; + + case PROP_LOCK_ALPHA: + info->cp += + xcf_read_int32 (info->fp, (Q_INT32 *) & layer->lock_alpha, 1); + break; + + case PROP_APPLY_MASK: + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) apply_mask, 1); + break; + + case PROP_EDIT_MASK: + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) edit_mask, 1); + break; + + case PROP_SHOW_MASK: + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) show_mask, 1); + break; + + case PROP_OFFSETS: + info->cp += + xcf_read_int32 (info->fp, + (Q_INT32 *) & GIMP_ITEM (layer)->offset_x, 1); + info->cp += + xcf_read_int32 (info->fp, + (Q_INT32 *) & GIMP_ITEM (layer)->offset_y, 1); + break; + + case PROP_MODE: + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & layer->mode, 1); + break; + + case PROP_TATTOO: + info->cp += xcf_read_int32 (info->fp, + (Q_INT32 *) & GIMP_ITEM (layer)->tattoo, + 1); + break; + + case PROP_PARASITES: + { + glong base = info->cp; + KisAnnotation *p; + + while (info->cp - base < prop_size) + { + p = xcf_load_parasite (info); + gimp_item_parasite_attach (GIMP_ITEM (layer), p); + gimp_parasite_free (p); + } + if (info->cp - base != prop_size) + g_message ("Error while loading a layer's parasites"); + } + break; + + case PROP_TEXT_LAYER_FLAGS: + info->cp += xcf_read_int32 (info->fp, text_layer_flags, 1); + break; + + default: + { + Q_UINT8 buf[16]; + Q_UINT32 amount; + +#ifdef GIMP_UNSTABLE + g_printerr ("unexpected/unknown layer property: %d (skipping)", + prop_type); +#endif + while (prop_size > 0) + { + amount = MIN (16, prop_size); + info->cp += xcf_read_int8 (info->fp, buf, amount); + prop_size -= MIN (16, amount); + } + } + break; + } + } + + return FALSE; +} + +static bool +xcf_load_channel_props (XcfInfo * info, + KisImage * gimage, GimpChannel ** channel) +{ + PropType prop_type; + Q_INT32 prop_size; + + while (TRUE) + { + if (!xcf_load_prop (info, &prop_type, &prop_size)) + return FALSE; + + switch (prop_type) + { + case PROP_END: + return TRUE; + + case PROP_ACTIVE_CHANNEL: + info->active_channel = *channel; + break; + + case PROP_SELECTION: + g_object_unref (gimage->selection_mask); + gimage->selection_mask = + gimp_selection_new (gimage, + gimp_item_width (GIMP_ITEM (*channel)), + gimp_item_height (GIMP_ITEM (*channel))); + g_object_ref (gimage->selection_mask); + gimp_item_sink (GIMP_ITEM (gimage->selection_mask)); + + tile_manager_unref (GIMP_DRAWABLE (gimage->selection_mask)->tiles); + GIMP_DRAWABLE (gimage->selection_mask)->tiles = + GIMP_DRAWABLE (*channel)->tiles; + GIMP_DRAWABLE (*channel)->tiles = NULL; + g_object_unref (*channel); + *channel = gimage->selection_mask; + (*channel)->boundary_known = FALSE; + (*channel)->bounds_known = FALSE; + break; + + case PROP_OPACITY: + { + Q_INT32 opacity; + + info->cp += xcf_read_int32 (info->fp, &opacity, 1); + (*channel)->color.a = opacity / 255.0; + } + break; + + case PROP_VISIBLE: + { + bool visible; + + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & visible, 1); + gimp_item_set_visible (GIMP_ITEM (*channel), + visible ? TRUE : FALSE, FALSE); + } + break; + + case PROP_LINKED: + { + bool linked; + + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & linked, 1); + gimp_item_set_linked (GIMP_ITEM (*channel), + linked ? TRUE : FALSE, FALSE); + } + break; + + case PROP_SHOW_MASKED: + { + bool show_masked; + + info->cp += + xcf_read_int32 (info->fp, (Q_INT32 *) & show_masked, 1); + gimp_channel_set_show_masked (*channel, show_masked); + } + break; + + case PROP_COLOR: + { + guchar col[3]; + + info->cp += xcf_read_int8 (info->fp, (Q_UINT8 *) col, 3); + + gimp_rgb_set_uchar (&(*channel)->color, col[0], col[1], col[2]); + } + break; + + case PROP_TATTOO: + info->cp += + xcf_read_int32 (info->fp, &GIMP_ITEM (*channel)->tattoo, 1); + break; + + case PROP_PARASITES: + { + glong base = info->cp; + KisAnnotation *p; + + while ((info->cp - base) < prop_size) + { + p = xcf_load_parasite (info); + gimp_item_parasite_attach (GIMP_ITEM (*channel), p); + gimp_parasite_free (p); + } + if (info->cp - base != prop_size) + g_message ("Error while loading a channel's parasites"); + } + break; + + default: +#ifdef GIMP_UNSTABLE + g_printerr ("unexpected/unknown channel property: %d (skipping)", + prop_type); +#endif + + { + Q_UINT8 buf[16]; + Q_UINT32 amount; + + while (prop_size > 0) + { + amount = MIN (16, prop_size); + info->cp += xcf_read_int8 (info->fp, buf, amount); + prop_size -= MIN (16, amount); + } + } + break; + } + } + + return FALSE; +} + +static bool +xcf_load_prop (XcfInfo * info, PropType * prop_type, Q_INT32 * prop_size) +{ + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) prop_type, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) prop_size, 1); + return TRUE; +} + +static KisLayer * +xcf_load_layer (XcfInfo * info, KisImage * gimage) +{ + KisLayer *layer; + GimpLayerMask *layer_mask; + Q_INT32 hierarchy_offset; + Q_INT32 layer_mask_offset; + bool apply_mask = TRUE; + bool edit_mask = FALSE; + bool show_mask = FALSE; + bool active; + bool floating; + Q_INT32 text_layer_flags = 0; + Q_INT32 width; + Q_INT32 height; + Q_INT32 type; + bool is_fs_drawable; + QCString *name; + + /* check and see if this is the drawable the floating selection + * is attached to. if it is then we'll do the attachment in our caller. + */ + is_fs_drawable = (info->cp == info->floating_sel_offset); + + /* read in the layer width, height, type and name */ + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & height, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & type, 1); + info->cp += xcf_read_string (info->fp, &name, 1); + + /* create a new layer */ + layer = gimp_layer_new (gimage, width, height, + type, name, 255, GIMP_NORMAL_MODE); + g_free (name); + if (!layer) + return NULL; + + /* read in the layer properties */ + if (!xcf_load_layer_props (info, gimage, layer, + &apply_mask, &edit_mask, &show_mask, + &text_layer_flags)) + goto error; + + /* call the evil text layer hack that might change our layer pointer */ + active = (info->active_layer == layer); + floating = (info->floating_sel == layer); + + if (gimp_text_layer_xcf_load_hack (&layer)) + { + gimp_text_layer_set_xcf_flags (GIMP_TEXT_LAYER (layer), + text_layer_flags); + + if (active) + info->active_layer = layer; + if (floating) + info->floating_sel = layer; + } + + /* read the hierarchy and layer mask offsets */ + info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1); + info->cp += xcf_read_int32 (info->fp, &layer_mask_offset, 1); + + /* read in the hierarchy */ + if (!xcf_seek_pos (info, hierarchy_offset, NULL)) + goto error; + + if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (layer)->tiles)) + goto error; + + /* read in the layer mask */ + if (layer_mask_offset != 0) + { + if (!xcf_seek_pos (info, layer_mask_offset, NULL)) + goto error; + + layer_mask = xcf_load_layer_mask (info, gimage); + if (!layer_mask) + goto error; + + layer_mask->apply_mask = apply_mask; + layer_mask->edit_mask = edit_mask; + layer_mask->show_mask = show_mask; + + gimp_layer_add_mask (layer, layer_mask, FALSE); + } + + /* attach the floating selection... */ + if (is_fs_drawable) + info->floating_sel_drawable = GIMP_DRAWABLE (layer); + + return layer; + +error: + g_object_unref (layer); + return NULL; +} + +static GimpChannel * +xcf_load_channel (XcfInfo * info, KisImage * gimage) +{ + GimpChannel *channel; + Q_INT32 hierarchy_offset; + Q_INT32 width; + Q_INT32 height; + bool is_fs_drawable; + QCString *name; + GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE }; + + /* check and see if this is the drawable the floating selection + * is attached to. if it is then we'll do the attachment in our caller. + */ + is_fs_drawable = (info->cp == info->floating_sel_offset); + + /* read in the layer width, height and name */ + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & height, 1); + info->cp += xcf_read_string (info->fp, &name, 1); + + /* create a new channel */ + channel = gimp_channel_new (gimage, width, height, name, &color); + g_free (name); + if (!channel) + return NULL; + + /* read in the channel properties */ + if (!xcf_load_channel_props (info, gimage, &channel)) + goto error; + + /* read the hierarchy and layer mask offsets */ + info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1); + + /* read in the hierarchy */ + if (!xcf_seek_pos (info, hierarchy_offset, NULL)) + goto error; + + if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (channel)->tiles)) + goto error; + + if (is_fs_drawable) + info->floating_sel_drawable = GIMP_DRAWABLE (channel); + + return channel; + +error: + g_object_unref (channel); + return NULL; +} + +static GimpLayerMask * +xcf_load_layer_mask (XcfInfo * info, KisImage * gimage) +{ + GimpLayerMask *layer_mask; + GimpChannel *channel; + Q_INT32 hierarchy_offset; + Q_INT32 width; + Q_INT32 height; + bool is_fs_drawable; + QCString *name; + GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE }; + + /* check and see if this is the drawable the floating selection + * is attached to. if it is then we'll do the attachment in our caller. + */ + is_fs_drawable = (info->cp == info->floating_sel_offset); + + /* read in the layer width, height and name */ + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & height, 1); + info->cp += xcf_read_string (info->fp, &name, 1); + + /* create a new layer mask */ + layer_mask = gimp_layer_mask_new (gimage, width, height, name, &color); + g_free (name); + if (!layer_mask) + return NULL; + + /* read in the layer_mask properties */ + channel = GIMP_CHANNEL (layer_mask); + if (!xcf_load_channel_props (info, gimage, &channel)) + goto error; + + /* read the hierarchy and layer mask offsets */ + info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1); + + /* read in the hierarchy */ + if (!xcf_seek_pos (info, hierarchy_offset, NULL)) + goto error; + + if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (layer_mask)->tiles)) + goto error; + + /* attach the floating selection... */ + if (is_fs_drawable) + info->floating_sel_drawable = GIMP_DRAWABLE (layer_mask); + + return layer_mask; + +error: + g_object_unref (layer_mask); + return NULL; +} + +static bool +xcf_load_hierarchy (XcfInfo * info, TileManager * tiles) +{ + Q_INT32 saved_pos; + Q_INT32 offset; + Q_INT32 junk; + Q_INT32 width; + Q_INT32 height; + Q_INT32 bpp; + + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & height, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & bpp, 1); + + /* make sure the values in the file correspond to the values + * calculated when the TileManager was created. + */ + if (width != tile_manager_width (tiles) || + height != tile_manager_height (tiles) || + bpp != tile_manager_bpp (tiles)) + return FALSE; + + /* load in the levels...we make sure that the number of levels + * calculated when the TileManager was created is the same + * as the number of levels found in the file. + */ + + info->cp += xcf_read_int32 (info->fp, &offset, 1); /* top level */ + + /* discard offsets for layers below first, if any. + */ + do + { + info->cp += xcf_read_int32 (info->fp, &junk, 1); + } + while (junk != 0); + + /* save the current position as it is where the + * next level offset is stored. + */ + saved_pos = info->cp; + + /* seek to the level offset */ + if (!xcf_seek_pos (info, offset, NULL)) + return FALSE; + + /* read in the level */ + if (!xcf_load_level (info, tiles)) + return FALSE; + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + return FALSE; + + return TRUE; +} + + +static bool +xcf_load_level (XcfInfo * info, TileManager * tiles) +{ + Q_INT32 saved_pos; + Q_INT32 offset, offset2; + Q_UINT32 ntiles; + Q_INT32 width; + Q_INT32 height; + Q_INT32 i; + Q_INT32 fail; + Tile *previous; + Tile *tile; + + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & height, 1); + + if (width != tile_manager_width (tiles) || + height != tile_manager_height (tiles)) + return FALSE; + + /* read in the first tile offset. + * if it is '0', then this tile level is empty + * and we can simply return. + */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + if (offset == 0) + return TRUE; + + /* Initialise the reference for the in-memory tile-compression + */ + previous = NULL; + + ntiles = tiles->ntile_rows * tiles->ntile_cols; + for (i = 0; i < ntiles; i++) + { + fail = FALSE; + + if (offset == 0) + { + g_message ("not enough tiles found in level"); + return FALSE; + } + + /* save the current position as it is where the + * next tile offset is stored. + */ + saved_pos = info->cp; + + /* read in the offset of the next tile so we can calculate the amount + of data needed for this tile */ + info->cp += xcf_read_int32 (info->fp, &offset2, 1); + + /* if the offset is 0 then we need to read in the maximum possible + allowing for negative compression */ + if (offset2 == 0) + offset2 = offset + TILE_WIDTH * TILE_WIDTH * 4 * 1.5; + /* 1.5 is probably more + than we need to allow */ + + /* seek to the tile offset */ + if (!xcf_seek_pos (info, offset, NULL)) + return FALSE; + + /* get the tile from the tile manager */ + tile = tile_manager_get (tiles, i, TRUE, TRUE); + + /* read in the tile */ + switch (info->compression) + { + case COMPRESS_NONE: + if (!xcf_load_tile (info, tile)) + fail = TRUE; + break; + case COMPRESS_RLE: + if (!xcf_load_tile_rle (info, tile, offset2 - offset)) + fail = TRUE; + break; + case COMPRESS_ZLIB: + g_error ("xcf: zlib compression unimplemented"); + fail = TRUE; + break; + case COMPRESS_FRACTAL: + g_error ("xcf: fractal compression unimplemented"); + fail = TRUE; + break; + } + + if (fail) + { + tile_release (tile, TRUE); + return FALSE; + } + + /* To potentially save memory, we compare the + * newly-fetched tile against the last one, and + * if they're the same we copy-on-write mirror one against + * the other. + */ + if (previous != NULL) + { + tile_lock (previous); + if (tile_ewidth (tile) == tile_ewidth (previous) && + tile_eheight (tile) == tile_eheight (previous) && + tile_bpp (tile) == tile_bpp (previous) && + memcmp (tile_data_pointer (tile, 0, 0), + tile_data_pointer (previous, 0, 0), + tile_size (tile)) == 0) + tile_manager_map (tiles, i, previous); + tile_release (previous, FALSE); + } + tile_release (tile, TRUE); + previous = tile_manager_get (tiles, i, FALSE, FALSE); + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + return FALSE; + + /* read in the offset of the next tile */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + } + + if (offset != 0) + { + g_message ("encountered garbage after reading level: %d", offset); + return FALSE; + } + + return TRUE; +} + +static bool +xcf_load_tile (XcfInfo * info, Tile * tile) +{ +#ifdef SWAP_FROM_FILE + + if (!info->swap_num) + { + info->ref_count = g_new (int, 1); + info->swap_num = tile_swap_add (info->filename, + xcf_swap_func, info->ref_count); + } + + tile->swap_num = info->swap_num; + tile->swap_offset = info->cp; + *info->ref_count += 1; + +#else + + info->cp += xcf_read_int8 (info->fp, tile_data_pointer (tile, 0, 0), + tile_size (tile)); + +#endif + + return TRUE; +} + +static bool +xcf_load_tile_rle (XcfInfo * info, Tile * tile, int data_length) +{ + guchar *data; + guchar val; + Q_INT32 size; + Q_INT32 count; + Q_INT32 length; + Q_INT32 bpp; + Q_INT32 i, j; + Q_INT32 nmemb_read_successfully; + guchar *xcfdata, *xcfodata, *xcfdatalimit; + + data = tile_data_pointer (tile, 0, 0); + bpp = tile_bpp (tile); + + xcfdata = xcfodata = g_malloc (data_length); + + /* we have to use fread instead of xcf_read_* because we may be + reading past the end of the file here */ + nmemb_read_successfully = fread ((QCString *) xcfdata, sizeof (QCString), + data_length, info->fp); + info->cp += nmemb_read_successfully; + + xcfdatalimit = &xcfodata[nmemb_read_successfully - 1]; + + for (i = 0; i < bpp; i++) + { + data = (guchar *) tile_data_pointer (tile, 0, 0) + i; + size = tile_ewidth (tile) * tile_eheight (tile); + count = 0; + + while (size > 0) + { + if (xcfdata > xcfdatalimit) + { + goto bogus_rle; + } + + val = *xcfdata++; + + length = val; + if (length >= 128) + { + length = 255 - (length - 1); + if (length == 128) + { + if (xcfdata >= xcfdatalimit) + { + goto bogus_rle; + } + + length = (*xcfdata << 8) + xcfdata[1]; + xcfdata += 2; + } + + count += length; + size -= length; + + if (size < 0) + { + goto bogus_rle; + } + + if (&xcfdata[length - 1] > xcfdatalimit) + { + goto bogus_rle; + } + + while (length-- > 0) + { + *data = *xcfdata++; + data += bpp; + } + } + else + { + length += 1; + if (length == 128) + { + if (xcfdata >= xcfdatalimit) + { + goto bogus_rle; + } + + length = (*xcfdata << 8) + xcfdata[1]; + xcfdata += 2; + } + + count += length; + size -= length; + + if (size < 0) + { + goto bogus_rle; + } + + if (xcfdata > xcfdatalimit) + { + goto bogus_rle; + } + + val = *xcfdata++; + + for (j = 0; j < length; j++) + { + *data = val; + data += bpp; + } + } + } + } + g_free (xcfodata); + return TRUE; + +bogus_rle: + if (xcfodata) + g_free (xcfodata); + return FALSE; +} + +static KisAnnotation * +xcf_load_parasite (XcfInfo * info) +{ + KisAnnotation *p; + + p = g_new (KisAnnotation, 1); + info->cp += xcf_read_string (info->fp, &p->name, 1); + info->cp += xcf_read_int32 (info->fp, &p->flags, 1); + info->cp += xcf_read_int32 (info->fp, &p->size, 1); + p->data = g_new (QCString, p->size); + info->cp += xcf_read_int8 (info->fp, p->data, p->size); + + return p; +} + +static bool +xcf_load_old_paths (XcfInfo * info, KisImage * gimage) +{ + Q_INT32 num_paths; + Q_INT32 last_selected_row; + GimpVectors *active_vectors; + + info->cp += xcf_read_int32 (info->fp, &last_selected_row, 1); + info->cp += xcf_read_int32 (info->fp, &num_paths, 1); + + while (num_paths-- > 0) + xcf_load_old_path (info, gimage); + + active_vectors = (GimpVectors *) + gimp_container_get_child_by_index (gimage->vectors, last_selected_row); + + if (active_vectors) + gimp_image_set_active_vectors (gimage, active_vectors); + + return TRUE; +} + +static bool +xcf_load_old_path (XcfInfo * info, KisImage * gimage) +{ + QCString *name; + Q_INT32 locked; + Q_UINT8 state; + Q_INT32 closed; + Q_INT32 num_points; + Q_INT32 version; /* changed from num_paths */ + GimpTattoo tattoo = 0; + GimpVectors *vectors; + GimpVectorsCompatPoint *points; + Q_INT32 i; + + info->cp += xcf_read_string (info->fp, &name, 1); + info->cp += xcf_read_int32 (info->fp, &locked, 1); + info->cp += xcf_read_int8 (info->fp, &state, 1); + info->cp += xcf_read_int32 (info->fp, &closed, 1); + info->cp += xcf_read_int32 (info->fp, &num_points, 1); + info->cp += xcf_read_int32 (info->fp, &version, 1); + + if (version == 2) + { + Q_INT32 dummy; + + /* Had extra type field and points are stored as doubles */ + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & dummy, 1); + } + else if (version == 3) + { + Q_INT32 dummy; + + /* Has extra tatto field */ + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & dummy, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & tattoo, 1); + } + else if (version != 1) + { + g_warning ("Unknown path type. Possibly corrupt XCF file"); + + return FALSE; + } + + /* skip empty compatibility paths */ + if (num_points == 0) + return FALSE; + + points = g_new0 (GimpVectorsCompatPoint, num_points); + + for (i = 0; i < num_points; i++) + { + if (version == 1) + { + Q_INT3232 x; + Q_INT3232 y; + + info->cp += xcf_read_int32 (info->fp, &points[i].type, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & x, 1); + info->cp += xcf_read_int32 (info->fp, (Q_INT32 *) & y, 1); + + points[i].x = x; + points[i].y = y; + } + else + { + float x; + float y; + + info->cp += xcf_read_int32 (info->fp, &points[i].type, 1); + info->cp += xcf_read_float (info->fp, &x, 1); + info->cp += xcf_read_float (info->fp, &y, 1); + + points[i].x = x; + points[i].y = y; + } + } + + vectors = + gimp_vectors_compat_new (gimage, name, points, num_points, closed); + + g_free (name); + g_free (points); + + GIMP_ITEM (vectors)->linked = locked; + + if (tattoo) + GIMP_ITEM (vectors)->tattoo = tattoo; + + gimp_image_add_vectors (gimage, vectors, + gimp_container_num_children (gimage->vectors)); + + return TRUE; +} + +static bool +xcf_load_vectors (XcfInfo * info, KisImage * gimage) +{ + Q_INT32 version; + Q_INT32 active_index; + Q_INT32 num_paths; + GimpVectors *active_vectors; + Q_INT32 base; + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("xcf_load_vectors\n"); +#endif + + base = info->cp; + + info->cp += xcf_read_int32 (info->fp, &version, 1); + + if (version != 1) + { + g_message ("Unknown vectors version: %d (skipping)", version); + return FALSE; + } + + info->cp += xcf_read_int32 (info->fp, &active_index, 1); + info->cp += xcf_read_int32 (info->fp, &num_paths, 1); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("%d paths (active: %d)\n", num_paths, active_index); +#endif + + while (num_paths-- > 0) + if (!xcf_load_vector (info, gimage)) + return FALSE; + + active_vectors = (GimpVectors *) + gimp_container_get_child_by_index (gimage->vectors, active_index); + + if (active_vectors) + gimp_image_set_active_vectors (gimage, active_vectors); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("xcf_load_vectors: loaded %d bytes\n", info->cp - base); +#endif + return TRUE; +} + +static bool +xcf_load_vector (XcfInfo * info, KisImage * gimage) +{ + QCString *name; + GimpTattoo tattoo = 0; + Q_INT32 visible; + Q_INT32 linked; + Q_INT32 num_parasites; + Q_INT32 num_strokes; + GimpVectors *vectors; + Q_INT32 i; + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("xcf_load_vector\n"); +#endif + + info->cp += xcf_read_string (info->fp, &name, 1); + info->cp += xcf_read_int32 (info->fp, &tattoo, 1); + info->cp += xcf_read_int32 (info->fp, &visible, 1); + info->cp += xcf_read_int32 (info->fp, &linked, 1); + info->cp += xcf_read_int32 (info->fp, &num_parasites, 1); + info->cp += xcf_read_int32 (info->fp, &num_strokes, 1); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr + ("name: %s, tattoo: %d, visible: %d, linked: %d, num_parasites %d, " + "num_strokes %d\n", name, tattoo, visible, linked, num_parasites, + num_strokes); +#endif + + vectors = gimp_vectors_new (gimage, name); + + GIMP_ITEM (vectors)->visible = visible ? TRUE : FALSE; + GIMP_ITEM (vectors)->linked = linked ? TRUE : FALSE; + + if (tattoo) + GIMP_ITEM (vectors)->tattoo = tattoo; + + for (i = 0; i < num_parasites; i++) + { + KisAnnotation *parasite; + + parasite = xcf_load_parasite (info); + + if (!parasite) + return FALSE; + + gimp_item_parasite_attach (GIMP_ITEM (vectors), parasite); + gimp_parasite_free (parasite); + } + + for (i = 0; i < num_strokes; i++) + { + Q_INT32 stroke_type_id; + Q_INT32 closed; + Q_INT32 num_axes; + Q_INT32 num_control_points; + Q_INT32 type; + float coords[6] = { 0.0, 0.0, 1.0, 0.5, 0.5, 0.5 }; + GimpStroke *stroke; + Q_INT32 j; + + GValueArray *control_points; + GValue value = { 0, }; + GimpAnchor anchor; + GType stroke_type; + + g_value_init (&value, GIMP_TYPE_ANCHOR); + + info->cp += xcf_read_int32 (info->fp, &stroke_type_id, 1); + info->cp += xcf_read_int32 (info->fp, &closed, 1); + info->cp += xcf_read_int32 (info->fp, &num_axes, 1); + info->cp += xcf_read_int32 (info->fp, &num_control_points, 1); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("stroke_type: %d, closed: %d, num_axes %d, len %d\n", + stroke_type_id, closed, num_axes, num_control_points); +#endif + + switch (stroke_type_id) + { + case XCF_STROKETYPE_BEZIER_STROKE: + stroke_type = GIMP_TYPE_BEZIER_STROKE; + break; + + default: + g_printerr ("skipping unknown stroke type\n"); + xcf_seek_pos (info, + info->cp + 4 * num_axes * num_control_points, NULL); + continue; + } + + control_points = g_value_array_new (num_control_points); + + anchor.selected = FALSE; + + for (j = 0; j < num_control_points; j++) + { + info->cp += xcf_read_int32 (info->fp, &type, 1); + info->cp += xcf_read_float (info->fp, coords, num_axes); + + anchor.type = type; + anchor.position.x = coords[0]; + anchor.position.y = coords[1]; + anchor.position.pressure = coords[2]; + anchor.position.xtilt = coords[3]; + anchor.position.ytilt = coords[4]; + anchor.position.wheel = coords[5]; + + g_value_set_boxed (&value, &anchor); + g_value_array_append (control_points, &value); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("Anchor: %d, (%f, %f, %f, %f, %f, %f)\n", type, + coords[0], coords[1], coords[2], coords[3], + coords[4], coords[5]); +#endif + } + + g_value_unset (&value); + + stroke = g_object_new (stroke_type, + "closed", closed, + "control-points", control_points, NULL); + + gimp_vectors_stroke_add (vectors, stroke); + } + + gimp_image_add_vectors (gimage, vectors, + gimp_container_num_children (gimage->vectors)); + + return TRUE; +} + +#ifdef SWAP_FROM_FILE + +static bool +xcf_swap_func (Q_INT32 fd, Tile * tile, Q_INT32 cmd, gpointer user_data) +{ + Q_INT32 bytes; + Q_INT32 err; + Q_INT32 nleft; + Q_INT32 *ref_count; + + switch (cmd) + { + case SWAP_IN: + lseek (fd, tile->swap_offset, SEEK_SET); + + bytes = tile_size (tile); + tile_alloc (tile); + + nleft = bytes; + while (nleft > 0) + { + do + { + err = read (fd, tile->data + bytes - nleft, nleft); + } + while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR))); + + if (err <= 0) + { + g_message ("unable to read tile data from xcf file: " + "%d ( %d ) bytes read", err, nleft); + return FALSE; + } + + nleft -= err; + } + break; + + case SWAP_OUT: + case SWAP_DELETE: + case SWAP_COMPRESS: + ref_count = user_data; + *ref_count -= 1; + if (*ref_count == 0) + { + tile_swap_remove (tile->swap_num); + g_free (ref_count); + } + + tile->swap_num = 1; + tile->swap_offset = -1; + + return TRUE; + } + + return FALSE; +} + +#endif diff --git a/filters/krita/xcf/xcf/xcf-load.h b/filters/krita/xcf/xcf/xcf-load.h new file mode 100644 index 00000000..75bc0e9c --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-load.h @@ -0,0 +1,27 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __XCF_LOAD_H__ +#define __XCF_LOAD_H__ + +class KisImage; +class XcfInfo; + +KisImage * xcf_load_image (XcfInfo *info); + +#endif /* __XCF_LOAD_H__ */ diff --git a/filters/krita/xcf/xcf/xcf-private.h b/filters/krita/xcf/xcf/xcf-private.h new file mode 100644 index 00000000..d568f052 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-private.h @@ -0,0 +1,95 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __XCF_PRIVATE_H__ +#define __XCF_PRIVATE_H__ + +#include <qcstring.h> + +typedef enum +{ + PROP_END = 0, + PROP_COLORMAP = 1, + PROP_ACTIVE_LAYER = 2, + PROP_ACTIVE_CHANNEL = 3, + PROP_SELECTION = 4, + PROP_FLOATING_SELECTION = 5, + PROP_OPACITY = 6, + PROP_MODE = 7, + PROP_VISIBLE = 8, + PROP_LINKED = 9, + PROP_LOCK_ALPHA = 10, + PROP_APPLY_MASK = 11, + PROP_EDIT_MASK = 12, + PROP_SHOW_MASK = 13, + PROP_SHOW_MASKED = 14, + PROP_OFFSETS = 15, + PROP_COLOR = 16, + PROP_COMPRESSION = 17, + PROP_GUIDES = 18, + PROP_RESOLUTION = 19, + PROP_TATTOO = 20, + PROP_PARASITES = 21, + PROP_UNIT = 22, + PROP_PATHS = 23, + PROP_USER_UNIT = 24, + PROP_VECTORS = 25, + PROP_TEXT_LAYER_FLAGS = 26 +} PropType; + +typedef enum +{ + COMPRESS_NONE = 0, + COMPRESS_RLE = 1, + COMPRESS_ZLIB = 2, /* unused */ + COMPRESS_FRACTAL = 3 /* unused */ +} XcfCompressionType; + +typedef enum +{ + XCF_ORIENTATION_HORIZONTAL = 1, + XCF_ORIENTATION_VERTICAL = 2 +} XcfOrientationType; + +typedef enum +{ + XCF_STROKETYPE_STROKE = 0, + XCF_STROKETYPE_BEZIER_STROKE = 1 +} XcfStrokeType; + +typedef struct _XcfInfo XcfInfo; + +struct _XcfInfo +{ + FILE *fp; + Q_UINT32 cp; + const QCString filename; + //GimpTattoo tattoo_state; + KisLayer *active_layer; + //GimpChannel *active_channel; + //GimpDrawable *floating_sel_drawable; + KisLayer *floating_sel; + Q_UINT32 floating_sel_offset; + Q_INT32 swap_num; + Q_INT32 *ref_count; + XcfCompressionType compression; + Q_INT32 file_version; +}; + + +#endif /* __XCF_PRIVATE_H__ */ diff --git a/filters/krita/xcf/xcf/xcf-read.cc b/filters/krita/xcf/xcf/xcf-read.cc new file mode 100644 index 00000000..04972294 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-read.cc @@ -0,0 +1,118 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <stdio.h> + +#include <glib-object.h> + +#include "libgimpbase/gimpbase.h" + +#include "xcf-read.h" + +#include "gimp-intl.h" + + +Q_UINT32 +xcf_read_int32 (FILE *fp, + Q_INT32 *data, + Q_INT32 count) +{ + Q_UINT32 total; + + total = count; + if (count > 0) + { + xcf_read_int8 (fp, (Q_UINT8 *) data, count * 4); + + while (count--) + { + *data = g_ntohl (*data); + data++; + } + } + + return total * 4; +} + +Q_UINT32 +xcf_read_float (FILE *fp, + float *data, + Q_INT32 count) +{ + return xcf_read_int32 (fp, (Q_INT32 *) ((void *) data), count); +} + +Q_UINT32 +xcf_read_int8 (FILE *fp, + Q_UINT8 *data, + Q_INT32 count) +{ + Q_UINT32 total; + Q_INT32 bytes; + + total = count; + while (count > 0) + { + bytes = fread ((char *) data, sizeof (char), count, fp); + if (bytes <= 0) /* something bad happened */ + break; + count -= bytes; + data += bytes; + } + + return total; +} + +Q_UINT32 +xcf_read_string (FILE *fp, + QCString **data, + Q_INT32 count) +{ + Q_INT32 tmp; + Q_UINT32 total; + Q_INT32 i; + + total = 0; + for (i = 0; i < count; i++) + { + total += xcf_read_int32 (fp, &tmp, 1); + if (tmp > 0) + { + QCString *str; + + str = g_new (QCString, tmp); + total += xcf_read_int8 (fp, (Q_UINT8*) str, tmp); + + if (str[tmp - 1] != '\0') + str[tmp - 1] = '\0'; + + data[i] = gimp_any_to_utf8 (str, -1, + _("Invalid UTF-8 string in XCF file")); + + g_free (str); + } + else + { + data[i] = NULL; + } + } + + return total; +} diff --git a/filters/krita/xcf/xcf/xcf-read.h b/filters/krita/xcf/xcf/xcf-read.h new file mode 100644 index 00000000..3038ca44 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-read.h @@ -0,0 +1,37 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __XCF_READ_H__ +#define __XCF_READ_H__ + + +Q_UINT32 xcf_read_int32 (FILE *fp, + Q_INT32 *data, + Q_INT32 count); +Q_UINT32 xcf_read_float (FILE *fp, + float *data, + Q_INT32 count); +Q_UINT32 xcf_read_int8 (FILE *fp, + Q_UINT8 *data, + Q_INT32 count); +Q_UINT32 xcf_read_string (FILE *fp, + QCString **data, + Q_INT32 count); + + +#endif /* __XCF_READ_H__ */ diff --git a/filters/krita/xcf/xcf/xcf-save.cc b/filters/krita/xcf/xcf/xcf-save.cc new file mode 100644 index 00000000..420578c7 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-save.cc @@ -0,0 +1,1826 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> /* strcpy, strlen */ + +#include <glib-object.h> + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core/core-types.h" + +#include "base/tile.h" +#include "base/tile-manager.h" +#include "base/tile-manager-private.h" + +#include "core/gimpchannel.h" +#include "core/gimpdrawable.h" +#include "core/gimpgrid.h" +#include "core/gimpimage.h" +#include "core/gimpimage-grid.h" +#include "core/gimpimage-guides.h" +#include "core/gimplayer.h" +#include "core/gimplayer-floating-sel.h" +#include "core/gimplayermask.h" +#include "core/gimplist.h" +#include "core/gimpparasitelist.h" +#include "core/gimpunit.h" + +#include "text/gimptextlayer.h" +#include "text/gimptextlayer-xcf.h" + +#include "vectors/gimpanchor.h" +#include "vectors/gimpstroke.h" +#include "vectors/gimpbezierstroke.h" +#include "vectors/gimpvectors.h" +#include "vectors/gimpvectors-compat.h" + +#include "xcf-private.h" +#include "xcf-read.h" +#include "xcf-seek.h" +#include "xcf-write.h" + +#include "gimp-intl.h" + + +static bool xcf_save_image_props (XcfInfo *info, + KisImage *gimage, + GError **error); +static bool xcf_save_layer_props (XcfInfo *info, + KisImage *gimage, + KisLayer *layer, + GError **error); +static bool xcf_save_channel_props (XcfInfo *info, + KisImage *gimage, + GimpChannel *channel, + GError **error); +static bool xcf_save_prop (XcfInfo *info, + KisImage *gimage, + PropType prop_type, + GError **error, + ...); +static bool xcf_save_layer (XcfInfo *info, + KisImage *gimage, + KisLayer *layer, + GError **error); +static bool xcf_save_channel (XcfInfo *info, + KisImage *gimage, + GimpChannel *channel, + GError **error); +static bool xcf_save_hierarchy (XcfInfo *info, + TileManager *tiles, + GError **error); +static bool xcf_save_level (XcfInfo *info, + TileManager *tiles, + GError **error); +static bool xcf_save_tile (XcfInfo *info, + Tile *tile, + GError **error); +static bool xcf_save_tile_rle (XcfInfo *info, + Tile *tile, + guchar *rlebuf, + GError **error); +static bool xcf_save_parasite (XcfInfo *info, + KisAnnotation *parasite, + GError **error); +static bool xcf_save_parasite_list (XcfInfo *info, + KisAnnotationList *parasite, + GError **error); +static bool xcf_save_old_paths (XcfInfo *info, + KisImage *gimage, + GError **error); +static bool xcf_save_vectors (XcfInfo *info, + KisImage *gimage, + GError **error); + + +/* private convenience macros */ +#define xcf_write_int32_check_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_int32 (info->fp, data, count, &tmp_error); \ + if (tmp_error) \ + { \ + g_propagate_error (error, tmp_error); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_int8_check_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_int8 (info->fp, data, count, &tmp_error); \ + if (tmp_error) \ + { \ + g_propagate_error (error, tmp_error); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_float_check_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_float (info->fp, data, count, &tmp_error); \ + if (tmp_error) \ + { \ + g_propagate_error (error, tmp_error); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_string_check_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_string (info->fp, data, count, &tmp_error); \ + if (tmp_error) \ + { \ + g_propagate_error (error, tmp_error); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_int32_print_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_int32 (info->fp, data, count, &error); \ + if (error) \ + { \ + g_message (_("Error saving XCF file: %s"), \ + error->message); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_int8_print_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_int8 (info->fp, data, count, &error); \ + if (error) \ + { \ + g_message (_("Error saving XCF file: %s"), \ + error->message); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_float_print_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_float (info->fp, data, count, &error); \ + if (error) \ + { \ + g_message (_("Error saving XCF file: %s"), \ + error->message); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_string_print_error(info, data, count) G_STMT_START { \ + info->cp += xcf_write_string (info->fp, data, count, &error); \ + if (error) \ + { \ + g_message (_("Error saving XCF file: %s"), \ + error->message); \ + return FALSE; \ + } \ + } G_STMT_END + +#define xcf_write_prop_type_check_error(info, prop_type) G_STMT_START { \ + Q_INT32 _prop_int32 = prop_type; \ + xcf_write_int32_check_error (info, &_prop_int32, 1); \ + } G_STMT_END + +#define xcf_write_prop_type_print_error(info, prop_type) G_STMT_START { \ + Q_INT32 _prop_int32 = prop_type; \ + xcf_write_int32_print_error (info, &_prop_int32, 1); \ + } G_STMT_END + +#define xcf_check_error(x) G_STMT_START { \ + if (! (x)) \ + return FALSE; \ + } G_STMT_END + +#define xcf_print_error(x) G_STMT_START { \ + if (! (x)) \ + { \ + g_message (_("Error saving XCF file: %s"), \ + error->message); \ + return FALSE; \ + } \ + } G_STMT_END + + +void +xcf_save_choose_format (XcfInfo *info, + KisImage *gimage) +{ + KisLayer *layer; + GList *list; + + Q_INT32 save_version = 0; /* default to oldest */ + + if (gimage->cmap) + save_version = 1; /* need version 1 for colormaps */ + + for (list = GIMP_LIST (gimage->layers)->list; + list && save_version < 2; + list = g_list_next (list)) + { + layer = GIMP_LAYER (list->data); + + switch (layer->mode) + { + /* new layer modes not supported by gimp-1.2 */ + case GIMP_SOFTLIGHT_MODE: + case GIMP_GRAIN_EXTRACT_MODE: + case GIMP_GRAIN_MERGE_MODE: + case GIMP_COLOR_ERASE_MODE: + save_version = 2; + break; + + default: + break; + } + } + + info->file_version = save_version; +} + +Q_INT32 +xcf_save_image (XcfInfo *info, + KisImage *gimage) +{ + KisLayer *layer; + KisLayer *floating_layer; + GimpChannel *channel; + Q_INT32 saved_pos; + Q_INT32 offset; + Q_UINT32 nlayers; + Q_UINT32 nchannels; + GList *list; + bool have_selection; + Q_INT32 t1, t2, t3, t4; + QCString version_tag[14]; + GError *error = NULL; + + floating_layer = gimp_image_floating_sel (gimage); + if (floating_layer) + floating_sel_relax (floating_layer, FALSE); + + /* write out the tag information for the image */ + if (info->file_version > 0) + { + sprintf (version_tag, "gimp xcf v%03d", info->file_version); + } + else + { + strcpy (version_tag, "gimp xcf file"); + } + xcf_write_int8_print_error (info, (Q_UINT8 *) version_tag, 14); + + /* write out the width, height and image type information for the image */ + xcf_write_int32_print_error (info, (Q_INT32 *) &gimage->width, 1); + xcf_write_int32_print_error (info, (Q_INT32 *) &gimage->height, 1); + xcf_write_int32_print_error (info, (Q_INT32 *) &gimage->base_type, 1); + + /* determine the number of layers and channels in the image */ + nlayers = (Q_UINT32) gimp_container_num_children (gimage->layers); + nchannels = (Q_UINT32) gimp_container_num_children (gimage->channels); + + /* check and see if we have to save out the selection */ + have_selection = gimp_channel_bounds (gimp_image_get_mask (gimage), + &t1, &t2, &t3, &t4); + if (have_selection) + nchannels += 1; + + /* write the property information for the image. + */ + + xcf_print_error (xcf_save_image_props (info, gimage, &error)); + + /* save the current file position as it is the start of where + * we place the layer offset information. + */ + saved_pos = info->cp; + + /* seek to after the offset lists */ + xcf_print_error (xcf_seek_pos (info, + info->cp + (nlayers + nchannels + 2) * 4, + &error)); + + for (list = GIMP_LIST (gimage->layers)->list; + list; + list = g_list_next (list)) + { + layer = (KisLayer *) list->data; + + /* save the start offset of where we are writing + * out the next layer. + */ + offset = info->cp; + + /* write out the layer. */ + xcf_print_error (xcf_save_layer (info, gimage, layer, &error)); + + /* seek back to where we are to write out the next + * layer offset and write it out. + */ + xcf_print_error (xcf_seek_pos (info, saved_pos, &error)); + xcf_write_int32_print_error (info, &offset, 1); + + /* increment the location we are to write out the + * next offset. + */ + saved_pos = info->cp; + + /* seek to the end of the file which is where + * we will write out the next layer. + */ + xcf_print_error (xcf_seek_end (info, &error)); + } + + /* write out a '0' offset position to indicate the end + * of the layer offsets. + */ + offset = 0; + xcf_print_error (xcf_seek_pos (info, saved_pos, &error)); + xcf_write_int32_print_error (info, &offset, 1); + saved_pos = info->cp; + xcf_print_error (xcf_seek_end (info, &error)); + + list = GIMP_LIST (gimage->channels)->list; + + while (list || have_selection) + { + if (list) + { + channel = (GimpChannel *) list->data; + + list = g_list_next (list); + } + else + { + channel = gimage->selection_mask; + have_selection = FALSE; + } + + /* save the start offset of where we are writing + * out the next channel. + */ + offset = info->cp; + + /* write out the layer. */ + xcf_print_error (xcf_save_channel (info, gimage, channel, &error)); + + /* seek back to where we are to write out the next + * channel offset and write it out. + */ + xcf_print_error (xcf_seek_pos (info, saved_pos, &error)); + xcf_write_int32_print_error (info, &offset, 1); + + /* increment the location we are to write out the + * next offset. + */ + saved_pos = info->cp; + + /* seek to the end of the file which is where + * we will write out the next channel. + */ + xcf_print_error (xcf_seek_end (info, &error)); + } + + /* write out a '0' offset position to indicate the end + * of the channel offsets. + */ + offset = 0; + xcf_print_error (xcf_seek_pos (info, saved_pos, &error)); + xcf_write_int32_print_error (info, &offset, 1); + saved_pos = info->cp; + + if (floating_layer) + floating_sel_rigor (floating_layer, FALSE); + + return !ferror(info->fp); +} + +static bool +xcf_save_image_props (XcfInfo *info, + KisImage *gimage, + GError **error) +{ + KisAnnotation *parasite = NULL; + GimpUnit unit = gimp_image_get_unit (gimage); + + /* check and see if we should save the colormap property */ + if (gimage->cmap) + xcf_check_error (xcf_save_prop (info, gimage, PROP_COLORMAP, error, + gimage->num_cols, gimage->cmap)); + + if (info->compression != COMPRESS_NONE) + xcf_check_error (xcf_save_prop (info, gimage, PROP_COMPRESSION, + error, info->compression)); + + if (gimage->guides) + xcf_check_error (xcf_save_prop (info, gimage, PROP_GUIDES, + error, gimage->guides)); + + xcf_check_error (xcf_save_prop (info, gimage, PROP_RESOLUTION, error, + gimage->xresolution, gimage->yresolution)); + + xcf_check_error (xcf_save_prop (info, gimage, PROP_TATTOO, error, + gimage->tattoo_state)); + + if (gimp_parasite_list_length (gimage->parasites) > 0) + xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES, + error, gimage->parasites)); + + if (unit < _gimp_unit_get_number_of_built_in_units (gimage->gimp)) + xcf_check_error (xcf_save_prop (info, gimage, PROP_UNIT, error, unit)); + + if (gimp_container_num_children (gimage->vectors) > 0) + { + if (gimp_vectors_compat_is_compatible (gimage)) + xcf_check_error (xcf_save_prop (info, gimage, PROP_PATHS, error)); + else + xcf_check_error (xcf_save_prop (info, gimage, PROP_VECTORS, error)); + } + + if (unit >= _gimp_unit_get_number_of_built_in_units (gimage->gimp)) + xcf_check_error (xcf_save_prop (info, gimage, PROP_USER_UNIT, error, unit)); + + if (GIMP_IS_GRID (gimage->grid)) + { + GimpGrid *grid = gimp_image_get_grid (gimage); + + parasite = gimp_grid_to_parasite (grid); + gimp_parasite_list_add (GIMP_IMAGE (gimage)->parasites, parasite); + } + + if (gimp_parasite_list_length (GIMP_IMAGE (gimage)->parasites) > 0) + { + xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES, error, + GIMP_IMAGE (gimage)->parasites)); + } + + if (parasite) + { + gimp_parasite_list_remove (GIMP_IMAGE (gimage)->parasites, + gimp_parasite_name (parasite)); + gimp_parasite_free (parasite); + } + + xcf_check_error (xcf_save_prop (info, gimage, PROP_END, error)); + + return TRUE; +} + +static bool +xcf_save_layer_props (XcfInfo *info, + KisImage *gimage, + KisLayer *layer, + GError **error) +{ + KisAnnotation *parasite = NULL; + + if (layer == gimp_image_get_active_layer (gimage)) + xcf_check_error (xcf_save_prop (info, gimage, PROP_ACTIVE_LAYER, error)); + + if (layer == gimp_image_floating_sel (gimage)) + { + info->floating_sel_drawable = layer->fs.drawable; + xcf_check_error (xcf_save_prop (info, gimage, PROP_FLOATING_SELECTION, + error)); + } + + xcf_check_error (xcf_save_prop (info, gimage, PROP_OPACITY, error, + layer->opacity)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_VISIBLE, error, + gimp_item_get_visible (GIMP_ITEM (layer)))); + xcf_check_error (xcf_save_prop (info, gimage, PROP_LINKED, error, + gimp_item_get_linked (GIMP_ITEM (layer)))); + xcf_check_error (xcf_save_prop (info, gimage, PROP_LOCK_ALPHA, + error, layer->lock_alpha)); + + if (layer->mask) + { + xcf_check_error (xcf_save_prop (info, gimage, PROP_APPLY_MASK, + error, layer->mask->apply_mask)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_EDIT_MASK, + error, layer->mask->edit_mask)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_SHOW_MASK, + error, layer->mask->show_mask)); + } + else + { + xcf_check_error (xcf_save_prop (info, gimage, PROP_APPLY_MASK, + error, FALSE)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_EDIT_MASK, + error, FALSE)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_SHOW_MASK, + error, FALSE)); + } + + xcf_check_error (xcf_save_prop (info, gimage, PROP_OFFSETS, error, + GIMP_ITEM (layer)->offset_x, + GIMP_ITEM (layer)->offset_y)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_MODE, error, + layer->mode)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_TATTOO, error, + GIMP_ITEM (layer)->tattoo)); + + if (GIMP_IS_TEXT_LAYER (layer) && GIMP_TEXT_LAYER (layer)->text) + { + GimpTextLayer *text_layer = GIMP_TEXT_LAYER (layer); + Q_INT32 flags = gimp_text_layer_get_xcf_flags (text_layer); + + gimp_text_layer_xcf_save_prepare (text_layer); + + if (flags) + xcf_check_error (xcf_save_prop (info, + gimage, PROP_TEXT_LAYER_FLAGS, error, + flags)); + } + + if (gimp_parasite_list_length (GIMP_ITEM (layer)->parasites) > 0) + { + xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES, error, + GIMP_ITEM (layer)->parasites)); + } + + if (parasite) + { + gimp_parasite_list_remove (GIMP_ITEM (layer)->parasites, + gimp_parasite_name (parasite)); + gimp_parasite_free (parasite); + } + + xcf_check_error (xcf_save_prop (info, gimage, PROP_END, error)); + + return TRUE; +} + +static bool +xcf_save_channel_props (XcfInfo *info, + KisImage *gimage, + GimpChannel *channel, + GError **error) +{ + guchar col[3]; + + if (channel == gimp_image_get_active_channel (gimage)) + xcf_check_error (xcf_save_prop (info, gimage, PROP_ACTIVE_CHANNEL, error)); + + if (channel == gimage->selection_mask) + xcf_check_error (xcf_save_prop (info, gimage, PROP_SELECTION, error)); + + xcf_check_error (xcf_save_prop (info, gimage, PROP_OPACITY, error, + channel->color.a)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_VISIBLE, error, + gimp_item_get_visible (GIMP_ITEM (channel)))); + xcf_check_error (xcf_save_prop (info, gimage, PROP_LINKED, error, + gimp_item_get_linked (GIMP_ITEM (channel)))); + xcf_check_error (xcf_save_prop (info, gimage, PROP_SHOW_MASKED, error, + channel->show_masked)); + + gimp_rgb_get_uchar (&channel->color, &col[0], &col[1], &col[2]); + xcf_check_error (xcf_save_prop (info, gimage, PROP_COLOR, error, col)); + + xcf_check_error (xcf_save_prop (info, gimage, PROP_TATTOO, error, + GIMP_ITEM (channel)->tattoo)); + + if (gimp_parasite_list_length (GIMP_ITEM (channel)->parasites) > 0) + xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES, error, + GIMP_ITEM (channel)->parasites)); + + xcf_check_error (xcf_save_prop (info, gimage, PROP_END, error)); + + return TRUE; +} + +static bool +xcf_save_prop (XcfInfo *info, + KisImage *gimage, + PropType prop_type, + GError **error, + ...) +{ + Q_INT32 size; + va_list args; + + GError *tmp_error = NULL; + + va_start (args, error); + + switch (prop_type) + { + case PROP_END: + size = 0; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + break; + + case PROP_COLORMAP: + { + Q_INT32 ncolors; + guchar *colors; + + ncolors = va_arg (args, Q_INT32); + colors = va_arg (args, guchar*); + size = 4 + ncolors * 3; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &ncolors, 1); + xcf_write_int8_check_error (info, colors, ncolors * 3); + } + break; + + case PROP_ACTIVE_LAYER: + case PROP_ACTIVE_CHANNEL: + case PROP_SELECTION: + size = 0; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + break; + + case PROP_FLOATING_SELECTION: + { + Q_INT32 dummy; + + dummy = 0; + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + info->floating_sel_offset = info->cp; + xcf_write_int32_check_error (info, &dummy, 1); + } + break; + + case PROP_OPACITY: + { + gdouble opacity; + Q_INT32 uint_opacity; + + opacity = va_arg (args, gdouble); + + uint_opacity = opacity * 255.999; + + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &uint_opacity, 1); + } + break; + + case PROP_MODE: + { + Q_INT3232 mode; + + mode = va_arg (args, Q_INT3232); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, (Q_INT32 *) &mode, 1); + } + break; + + case PROP_VISIBLE: + { + Q_INT32 visible; + + visible = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &visible, 1); + } + break; + + case PROP_LINKED: + { + Q_INT32 linked; + + linked = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &linked, 1); + } + break; + + case PROP_LOCK_ALPHA: + { + Q_INT32 lock_alpha; + + lock_alpha = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &lock_alpha, 1); + } + break; + + case PROP_APPLY_MASK: + { + Q_INT32 apply_mask; + + apply_mask = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &apply_mask, 1); + } + break; + + case PROP_EDIT_MASK: + { + Q_INT32 edit_mask; + + edit_mask = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &edit_mask, 1); + } + break; + + case PROP_SHOW_MASK: + { + Q_INT32 show_mask; + + show_mask = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &show_mask, 1); + } + break; + + case PROP_SHOW_MASKED: + { + Q_INT32 show_masked; + + show_masked = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &show_masked, 1); + } + break; + + case PROP_OFFSETS: + { + Q_INT3232 offsets[2]; + + offsets[0] = va_arg (args, Q_INT3232); + offsets[1] = va_arg (args, Q_INT3232); + size = 8; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, (Q_INT32 *) offsets, 2); + } + break; + + case PROP_COLOR: + { + guchar *color; + + color = va_arg (args, guchar*); + size = 3; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int8_check_error (info, color, 3); + } + break; + + case PROP_COMPRESSION: + { + Q_UINT8 compression; + + compression = (Q_UINT8) va_arg (args, Q_INT32); + size = 1; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int8_check_error (info, &compression, 1); + } + break; + + case PROP_GUIDES: + { + GList *guides; + GimpGuide *guide; + Q_INT3232 position; + Q_INT328 orientation; + Q_INT32 nguides; + + guides = va_arg (args, GList *); + nguides = g_list_length (guides); + + size = nguides * (4 + 1); + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + + for (; guides; guides = g_list_next (guides)) + { + guide = (GimpGuide *) guides->data; + + position = guide->position; + + switch (guide->orientation) + { + case GIMP_ORIENTATION_HORIZONTAL: + orientation = XCF_ORIENTATION_HORIZONTAL; + break; + + case GIMP_ORIENTATION_VERTICAL: + orientation = XCF_ORIENTATION_VERTICAL; + break; + + default: + g_warning ("%s: skipping guide with bad orientation", + G_STRFUNC); + continue; + } + + xcf_write_int32_check_error (info, (Q_INT32 *) &position, 1); + xcf_write_int8_check_error (info, (Q_UINT8 *) &orientation, 1); + } + } + break; + + case PROP_RESOLUTION: + { + float xresolution, yresolution; + + /* we pass in floats, + but they are promoted to double by the compiler */ + xresolution = va_arg (args, double); + yresolution = va_arg (args, double); + + size = 4*2; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + + xcf_write_float_check_error (info, &xresolution, 1); + xcf_write_float_check_error (info, &yresolution, 1); + } + break; + + case PROP_TATTOO: + { + Q_INT32 tattoo; + + tattoo = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &tattoo, 1); + } + break; + + case PROP_PARASITES: + { + KisAnnotationList *list; + Q_INT32 base, length; + long pos; + + list = va_arg (args, KisAnnotationList *); + + if (gimp_parasite_list_persistent_length (list) > 0) + { + xcf_write_prop_type_check_error (info, prop_type); + + /* because we don't know how much room the parasite list will take + * we save the file position and write the length later + */ + pos = info->cp; + xcf_write_int32_check_error (info, &length, 1); + base = info->cp; + + xcf_check_error (xcf_save_parasite_list (info, list, error)); + + length = info->cp - base; + /* go back to the saved position and write the length */ + xcf_check_error (xcf_seek_pos (info, pos, error)); + xcf_write_int32 (info->fp, &length, 1, &tmp_error); + if (tmp_error) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + + xcf_check_error (xcf_seek_end (info, error)); + } + } + break; + + case PROP_UNIT: + { + Q_INT32 unit; + + unit = va_arg (args, Q_INT32); + + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &unit, 1); + } + break; + + case PROP_PATHS: + { + Q_INT32 base, length; + glong pos; + + xcf_write_prop_type_check_error (info, prop_type); + + /* because we don't know how much room the paths list will take + * we save the file position and write the length later + */ + pos = info->cp; + xcf_write_int32_check_error (info, &length, 1); + + base = info->cp; + + xcf_check_error (xcf_save_old_paths (info, gimage, error)); + + length = info->cp - base; + + /* go back to the saved position and write the length */ + xcf_check_error (xcf_seek_pos (info, pos, error)); + xcf_write_int32 (info->fp, &length, 1, &tmp_error); + if (tmp_error) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + + xcf_check_error (xcf_seek_end (info, error)); + } + break; + + case PROP_USER_UNIT: + { + GimpUnit unit; + const QCString *unit_strings[5]; + float factor; + Q_INT32 digits; + + unit = va_arg (args, Q_INT32); + + /* write the entire unit definition */ + unit_strings[0] = _gimp_unit_get_identifier (gimage->gimp, unit); + factor = _gimp_unit_get_factor (gimage->gimp, unit); + digits = _gimp_unit_get_digits (gimage->gimp, unit); + unit_strings[1] = _gimp_unit_get_symbol (gimage->gimp, unit); + unit_strings[2] = _gimp_unit_get_abbreviation (gimage->gimp, unit); + unit_strings[3] = _gimp_unit_get_singular (gimage->gimp, unit); + unit_strings[4] = _gimp_unit_get_plural (gimage->gimp, unit); + + size = + 2 * 4 + + strlen (unit_strings[0]) ? strlen (unit_strings[0]) + 5 : 4 + + strlen (unit_strings[1]) ? strlen (unit_strings[1]) + 5 : 4 + + strlen (unit_strings[2]) ? strlen (unit_strings[2]) + 5 : 4 + + strlen (unit_strings[3]) ? strlen (unit_strings[3]) + 5 : 4 + + strlen (unit_strings[4]) ? strlen (unit_strings[4]) + 5 : 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_float_check_error (info, &factor, 1); + xcf_write_int32_check_error (info, &digits, 1); + xcf_write_string_check_error (info, (QCString **) unit_strings, 5); + } + break; + + case PROP_VECTORS: + { + Q_INT32 base, length; + glong pos; + + xcf_write_prop_type_check_error (info, prop_type); + + /* because we don't know how much room the paths list will take + * we save the file position and write the length later + */ + pos = info->cp; + xcf_write_int32_check_error (info, &length, 1); + + base = info->cp; + + xcf_check_error (xcf_save_vectors (info, gimage, error)); + + length = info->cp - base; + + /* go back to the saved position and write the length */ + xcf_check_error (xcf_seek_pos (info, pos, error)); + xcf_write_int32 (info->fp, &length, 1, &tmp_error); + if (tmp_error) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + + xcf_check_error (xcf_seek_end (info, error)); + } + break; + + case PROP_TEXT_LAYER_FLAGS: + { + Q_INT32 flags; + + flags = va_arg (args, Q_INT32); + size = 4; + + xcf_write_prop_type_check_error (info, prop_type); + xcf_write_int32_check_error (info, &size, 1); + xcf_write_int32_check_error (info, &flags, 1); + } + break; + } + + va_end (args); + + return TRUE; +} + +static bool +xcf_save_layer (XcfInfo *info, + KisImage *gimage, + KisLayer *layer, + GError **error) +{ + Q_INT32 saved_pos; + Q_INT32 offset; + + GError *tmp_error = NULL; + + /* check and see if this is the drawable that the floating + * selection is attached to. + */ + if (GIMP_DRAWABLE (layer) == info->floating_sel_drawable) + { + saved_pos = info->cp; + xcf_check_error (xcf_seek_pos (info, info->floating_sel_offset, error)); + xcf_write_int32_check_error (info, &saved_pos, 1); + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + } + + /* write out the width, height and image type information for the layer */ + xcf_write_int32_check_error (info, + (Q_INT32 *) &GIMP_ITEM (layer)->width, 1); + xcf_write_int32_check_error (info, + (Q_INT32 *) &GIMP_ITEM (layer)->height, 1); + xcf_write_int32_check_error (info, + (Q_INT32 *) &GIMP_DRAWABLE (layer)->type, 1); + + /* write out the layers name */ + xcf_write_string_check_error (info, &GIMP_OBJECT (layer)->name, 1); + + /* write out the layer properties */ + xcf_save_layer_props (info, gimage, layer, error); + + /* save the current position which is where the hierarchy offset + * will be stored. + */ + saved_pos = info->cp; + + /* write out the layer tile hierarchy */ + xcf_check_error (xcf_seek_pos (info, info->cp + 8, error)); + offset = info->cp; + + xcf_check_error (xcf_save_hierarchy (info, + GIMP_DRAWABLE(layer)->tiles, error)); + + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + xcf_write_int32_check_error (info, &offset, 1); + saved_pos = info->cp; + + /* write out the layer mask */ + if (layer->mask) + { + xcf_check_error (xcf_seek_end (info, error)); + offset = info->cp; + + xcf_check_error (xcf_save_channel (info, + gimage, GIMP_CHANNEL(layer->mask), + error)); + } + else + offset = 0; + + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + xcf_write_int32_check_error (info, &offset, 1); + + return TRUE; +} + +static bool +xcf_save_channel (XcfInfo *info, + KisImage *gimage, + GimpChannel *channel, + GError **error) +{ + Q_INT32 saved_pos; + Q_INT32 offset; + + GError *tmp_error = NULL; + + /* check and see if this is the drawable that the floating + * selection is attached to. + */ + if (GIMP_DRAWABLE (channel) == info->floating_sel_drawable) + { + saved_pos = info->cp; + xcf_check_error (xcf_seek_pos (info, info->floating_sel_offset, error)); + xcf_write_int32_check_error (info, &saved_pos, 1); + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + } + + /* write out the width and height information for the channel */ + xcf_write_int32_check_error (info, + (Q_INT32 *) &GIMP_ITEM (channel)->width, 1); + xcf_write_int32_check_error (info, + (Q_INT32 *) &GIMP_ITEM (channel)->height, 1); + + /* write out the channels name */ + xcf_write_string_check_error (info, &GIMP_OBJECT (channel)->name, 1); + + /* write out the channel properties */ + xcf_save_channel_props (info, gimage, channel, error); + + /* save the current position which is where the hierarchy offset + * will be stored. + */ + saved_pos = info->cp; + + /* write out the channel tile hierarchy */ + xcf_check_error (xcf_seek_pos (info, info->cp + 4, error)); + offset = info->cp; + + xcf_check_error (xcf_save_hierarchy (info, + GIMP_DRAWABLE (channel)->tiles, error)); + + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + xcf_write_int32_check_error (info, &offset, 1); + saved_pos = info->cp; + + return TRUE; +} + +static Q_INT32 +xcf_calc_levels (Q_INT32 size, + Q_INT32 tile_size) +{ + int levels; + + levels = 1; + while (size > tile_size) + { + size /= 2; + levels += 1; + } + + return levels; +} + + +static bool +xcf_save_hierarchy (XcfInfo *info, + TileManager *tiles, + GError **error) +{ + Q_INT32 saved_pos; + Q_INT32 offset; + Q_INT32 width; + Q_INT32 height; + Q_INT32 bpp; + Q_INT32 i; + Q_INT32 nlevels; + Q_INT32 tmp1, tmp2; + + GError *tmp_error = NULL; + + width = tile_manager_width (tiles); + height = tile_manager_height (tiles); + bpp = tile_manager_bpp (tiles); + + xcf_write_int32_check_error (info, (Q_INT32 *) &width, 1); + xcf_write_int32_check_error (info, (Q_INT32 *) &height, 1); + xcf_write_int32_check_error (info, (Q_INT32 *) &bpp, 1); + + saved_pos = info->cp; + + tmp1 = xcf_calc_levels (width, TILE_WIDTH); + tmp2 = xcf_calc_levels (height, TILE_HEIGHT); + nlevels = MAX (tmp1, tmp2); + + xcf_check_error (xcf_seek_pos (info, info->cp + (1 + nlevels) * 4, error)); + + for (i = 0; i < nlevels; i++) + { + offset = info->cp; + + if (i == 0) + { + /* write out the level. */ + xcf_check_error (xcf_save_level (info, tiles, error)); + } + else + { + /* fake an empty level */ + tmp1 = 0; + width /= 2; + height /= 2; + xcf_write_int32_check_error (info, (Q_INT32 *) &width, 1); + xcf_write_int32_check_error (info, (Q_INT32 *) &height, 1); + xcf_write_int32_check_error (info, (Q_INT32 *) &tmp1, 1); + } + + /* seek back to where we are to write out the next + * level offset and write it out. + */ + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + xcf_write_int32_check_error (info, &offset, 1); + + /* increment the location we are to write out the + * next offset. + */ + saved_pos = info->cp; + + /* seek to the end of the file which is where + * we will write out the next level. + */ + xcf_check_error (xcf_seek_end (info, error)); + } + + /* write out a '0' offset position to indicate the end + * of the level offsets. + */ + offset = 0; + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + xcf_write_int32_check_error (info, &offset, 1); + + return TRUE; +} + +static bool +xcf_save_level (XcfInfo *info, + TileManager *level, + GError **error) +{ + Q_INT32 saved_pos; + Q_INT32 offset; + Q_INT32 width; + Q_INT32 height; + Q_UINT32 ntiles; + Q_INT32 i; + guchar *rlebuf; + + GError *tmp_error = NULL; + + width = tile_manager_width (level); + height = tile_manager_height (level); + + xcf_write_int32_check_error (info, (Q_INT32 *) &width, 1); + xcf_write_int32_check_error (info, (Q_INT32 *) &height, 1); + + saved_pos = info->cp; + + /* allocate a temporary buffer to store the rle data before it is + written to disk */ + rlebuf = + g_malloc (TILE_WIDTH * TILE_HEIGHT * tile_manager_bpp (level) * 1.5); + + if (level->tiles) + { + ntiles = level->ntile_rows * level->ntile_cols; + xcf_check_error (xcf_seek_pos (info, info->cp + (ntiles + 1) * 4, error)); + + for (i = 0; i < ntiles; i++) + { + /* save the start offset of where we are writing + * out the next tile. + */ + offset = info->cp; + + /* write out the tile. */ + switch (info->compression) + { + case COMPRESS_NONE: + xcf_check_error(xcf_save_tile (info, level->tiles[i], error)); + break; + case COMPRESS_RLE: + xcf_check_error (xcf_save_tile_rle (info, level->tiles[i], + rlebuf, error)); + break; + case COMPRESS_ZLIB: + g_error ("xcf: zlib compression unimplemented"); + break; + case COMPRESS_FRACTAL: + g_error ("xcf: fractal compression unimplemented"); + break; + } + + /* seek back to where we are to write out the next + * tile offset and write it out. + */ + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + xcf_write_int32_check_error (info, &offset, 1); + + /* increment the location we are to write out the + * next offset. + */ + saved_pos = info->cp; + + /* seek to the end of the file which is where + * we will write out the next tile. + */ + xcf_check_error (xcf_seek_end (info, error)); + } + } + + g_free (rlebuf); + + /* write out a '0' offset position to indicate the end + * of the level offsets. + */ + offset = 0; + xcf_check_error (xcf_seek_pos (info, saved_pos, error)); + xcf_write_int32_check_error (info, &offset, 1); + + return TRUE; + +} + +static bool +xcf_save_tile (XcfInfo *info, + Tile *tile, + GError **error) +{ + GError *tmp_error = NULL; + + tile_lock (tile); + xcf_write_int8_check_error (info, tile_data_pointer (tile, 0, 0), + tile_size (tile)); + tile_release (tile, FALSE); + + return TRUE; +} + +static bool +xcf_save_tile_rle (XcfInfo *info, + Tile *tile, + guchar *rlebuf, + GError **error) +{ + guchar *data, *t; + unsigned int last; + Q_INT32 state; + Q_INT32 length; + Q_INT32 count; + Q_INT32 size; + Q_INT32 bpp; + Q_INT32 i, j; + Q_INT32 len = 0; + + GError *tmp_error = NULL; + + tile_lock (tile); + + bpp = tile_bpp (tile); + + for (i = 0; i < bpp; i++) + { + data = (guchar*) tile_data_pointer (tile, 0, 0) + i; + + state = 0; + length = 0; + count = 0; + size = tile_ewidth(tile) * tile_eheight(tile); + last = -1; + + while (size > 0) + { + switch (state) + { + case 0: + /* in state 0 we try to find a long sequence of + * matching values. + */ + if ((length == 32768) || + ((size - length) <= 0) || + ((length > 1) && (last != *data))) + { + count += length; + if (length >= 128) + { + rlebuf[len++] = 127; + rlebuf[len++] = (length >> 8); + rlebuf[len++] = length & 0x00FF; + rlebuf[len++] = last; + } + else + { + rlebuf[len++] = length - 1; + rlebuf[len++] = last; + } + size -= length; + length = 0; + } + else if ((length == 1) && (last != *data)) + state = 1; + break; + + case 1: + /* in state 1 we try and find a long sequence of + * non-matching values. + */ + if ((length == 32768) || + ((size - length) == 0) || + ((length > 0) && (last == *data) && + ((size - length) == 1 || last == data[bpp]))) + { + count += length; + state = 0; + + if (length >= 128) + { + rlebuf[len++] = 255 - 127; + rlebuf[len++] = (length >> 8); + rlebuf[len++] = length & 0x00FF; + } + else + { + rlebuf[len++] = 255 - (length - 1); + } + + t = data - length * bpp; + for (j = 0; j < length; j++) + { + rlebuf[len++] = *t; + t += bpp; + } + + size -= length; + length = 0; + } + break; + } + + if (size > 0) { + length += 1; + last = *data; + data += bpp; + } + } + + if (count != (tile_ewidth (tile) * tile_eheight (tile))) + g_message ("xcf: uh oh! xcf rle tile saving error: %d", count); + } + xcf_write_int8_check_error (info, rlebuf, len); + tile_release (tile, FALSE); + + return TRUE; +} + +static bool +xcf_save_parasite (XcfInfo *info, + KisAnnotation *parasite, + GError **error) +{ + if (gimp_parasite_is_persistent (parasite)) + { + GError *tmp_error = NULL; + + xcf_write_string_check_error (info, ¶site->name, 1); + xcf_write_int32_check_error (info, ¶site->flags, 1); + xcf_write_int32_check_error (info, ¶site->size, 1); + xcf_write_int8_check_error (info, parasite->data, parasite->size); + } + + return TRUE; +} + +typedef struct +{ + XcfInfo *info; + GError *error; +} XcfParasiteData; + +static void +xcf_save_parasite_func (QCString *key, + KisAnnotation *parasite, + XcfParasiteData *data) +{ + if (! data->error) + xcf_save_parasite (data->info, parasite, &data->error); +} + +static bool +xcf_save_parasite_list (XcfInfo *info, + KisAnnotationList *list, + GError **error) +{ + XcfParasiteData data; + + data.info = info; + data.error = NULL; + + gimp_parasite_list_foreach (list, (GHFunc) xcf_save_parasite_func, &data); + + if (data.error) + { + g_propagate_error (error, data.error); + return FALSE; + } + + return TRUE; +} + +static bool +xcf_save_old_paths (XcfInfo *info, + KisImage *gimage, + GError **error) +{ + GimpVectors *active_vectors; + Q_INT32 num_paths; + Q_INT32 active_index = 0; + GList *list; + GError *tmp_error = NULL; + + /* Write out the following:- + * + * last_selected_row (Q_INT32) + * number_of_paths (Q_INT32) + * + * then each path:- + */ + + num_paths = gimp_container_num_children (gimage->vectors); + + active_vectors = gimp_image_get_active_vectors (gimage); + + if (active_vectors) + active_index = gimp_container_get_child_index (gimage->vectors, + GIMP_OBJECT (active_vectors)); + + xcf_write_int32_check_error (info, &active_index, 1); + xcf_write_int32_check_error (info, &num_paths, 1); + + for (list = GIMP_LIST (gimage->vectors)->list; + list; + list = g_list_next (list)) + { + GimpVectors *vectors = list->data; + QCString *name; + Q_INT32 locked; + Q_UINT8 state; + Q_INT32 version; + Q_INT32 pathtype; + Q_INT32 tattoo; + GimpVectorsCompatPoint *points; + Q_INT3232 num_points; + Q_INT3232 closed; + Q_INT32 i; + + /* + * name (string) + * locked (Q_INT32) + * state (QCString) + * closed (Q_INT32) + * number points (Q_INT32) + * version (Q_INT32) + * pathtype (Q_INT32) + * tattoo (Q_INT32) + * then each point. + */ + + points = gimp_vectors_compat_get_points (vectors, &num_points, &closed); + + /* if no points are generated because of a faulty path we should + * skip saving the path - this is unfortunately impossible, because + * we already saved the number of paths and I wont start seeking + * around to fix that cruft */ + + name = (QCString *) gimp_object_get_name (GIMP_OBJECT (vectors)); + locked = gimp_item_get_linked (GIMP_ITEM (vectors)); + state = closed ? 4 : 2; /* EDIT : ADD (editing state, 1.2 compat) */ + version = 3; + pathtype = 1; /* BEZIER (1.2 compat) */ + tattoo = gimp_item_get_tattoo (GIMP_ITEM (vectors)); + + xcf_write_string_check_error (info, &name, 1); + xcf_write_int32_check_error (info, &locked, 1); + xcf_write_int8_check_error (info, &state, 1); + xcf_write_int32_check_error (info, &closed, 1); + xcf_write_int32_check_error (info, &num_points, 1); + xcf_write_int32_check_error (info, &version, 1); + xcf_write_int32_check_error (info, &pathtype, 1); + xcf_write_int32_check_error (info, &tattoo, 1); + + for (i = 0; i < num_points; i++) + { + float x; + float y; + + x = points[i].x; + y = points[i].y; + + /* + * type (Q_INT32) + * x (float) + * y (float) + */ + + xcf_write_int32_check_error (info, &points[i].type, 1); + xcf_write_float_check_error (info, &x, 1); + xcf_write_float_check_error (info, &y, 1); + } + + g_free (points); + } + + return TRUE; +} + +static bool +xcf_save_vectors (XcfInfo *info, + KisImage *gimage, + GError **error) +{ + GimpVectors *active_vectors; + Q_INT32 version = 1; + Q_INT32 active_index = 0; + Q_INT32 num_paths; + GList *list; + GList *stroke_list; + GError *tmp_error = NULL; + + /* Write out the following:- + * + * version (Q_INT32) + * active_index (Q_INT32) + * num_paths (Q_INT32) + * + * then each path:- + */ + + active_vectors = gimp_image_get_active_vectors (gimage); + + if (active_vectors) + active_index = gimp_container_get_child_index (gimage->vectors, + GIMP_OBJECT (active_vectors)); + + num_paths = gimp_container_num_children (gimage->vectors); + + xcf_write_int32_check_error (info, &version, 1); + xcf_write_int32_check_error (info, &active_index, 1); + xcf_write_int32_check_error (info, &num_paths, 1); + + for (list = GIMP_LIST (gimage->vectors)->list; + list; + list = g_list_next (list)) + { + GimpVectors *vectors = list->data; + KisAnnotationList *parasites; + QCString *name; + Q_INT32 tattoo; + Q_INT32 visible; + Q_INT32 linked; + Q_INT32 num_parasites; + Q_INT32 num_strokes; + + /* + * name (string) + * tattoo (Q_INT32) + * visible (Q_INT32) + * linked (Q_INT32) + * num_parasites (Q_INT32) + * num_strokes (Q_INT32) + * + * then each parasite + * then each stroke + */ + + parasites = GIMP_ITEM (vectors)->parasites; + + name = (QCString *) gimp_object_get_name (GIMP_OBJECT (vectors)); + visible = gimp_item_get_visible (GIMP_ITEM (vectors)); + linked = gimp_item_get_linked (GIMP_ITEM (vectors)); + tattoo = gimp_item_get_tattoo (GIMP_ITEM (vectors)); + num_parasites = gimp_parasite_list_persistent_length (parasites); + num_strokes = g_list_length (vectors->strokes); + + xcf_write_string_check_error (info, &name, 1); + xcf_write_int32_check_error (info, &tattoo, 1); + xcf_write_int32_check_error (info, &visible, 1); + xcf_write_int32_check_error (info, &linked, 1); + xcf_write_int32_check_error (info, &num_parasites, 1); + xcf_write_int32_check_error (info, &num_strokes, 1); + + xcf_check_error (xcf_save_parasite_list (info, parasites, error)); + + for (stroke_list = g_list_first (vectors->strokes); + stroke_list; + stroke_list = g_list_next (stroke_list)) + { + GimpStroke *stroke; + Q_INT32 stroke_type; + Q_INT32 closed; + Q_INT32 num_axes; + GArray *control_points; + Q_INT32 i; + + Q_INT32 type; + float coords[6]; + + /* + * stroke_type (Q_INT32) + * closed (Q_INT32) + * num_axes (Q_INT32) + * num_control_points (Q_INT32) + * + * then each control point. + */ + + stroke = GIMP_STROKE (stroke_list->data); + + if (GIMP_IS_BEZIER_STROKE (stroke)) + { + stroke_type = XCF_STROKETYPE_BEZIER_STROKE; + num_axes = 2; /* hardcoded, might be increased later */ + } + else + { + g_printerr ("Skipping unknown stroke type!\n"); + continue; + } + + control_points = gimp_stroke_control_points_get (stroke, &closed); + + xcf_write_int32_check_error (info, &stroke_type, 1); + xcf_write_int32_check_error (info, &closed, 1); + xcf_write_int32_check_error (info, &num_axes, 1); + xcf_write_int32_check_error (info, &control_points->len, 1); + + for (i = 0; i < control_points->len; i++) + { + GimpAnchor *anchor; + + anchor = & (g_array_index (control_points, GimpAnchor, i)); + + type = anchor->type; + coords[0] = anchor->position.x; + coords[1] = anchor->position.y; + coords[2] = anchor->position.pressure; + coords[3] = anchor->position.xtilt; + coords[4] = anchor->position.ytilt; + coords[5] = anchor->position.wheel; + + /* + * type (Q_INT32) + * + * the first num_axis elements of: + * [0] x (float) + * [1] y (float) + * [2] pressure (float) + * [3] xtilt (float) + * [4] ytilt (float) + * [5] wheel (float) + */ + + xcf_write_int32_check_error (info, &type, 1); + xcf_write_float_check_error (info, coords, num_axes); + } + + g_array_free (control_points, TRUE); + } + } + + return TRUE; +} diff --git a/filters/krita/xcf/xcf/xcf-save.h b/filters/krita/xcf/xcf/xcf-save.h new file mode 100644 index 00000000..74c5278c --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-save.h @@ -0,0 +1,29 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __XCF_SAVE_H__ +#define __XCF_SAVE_H__ + + +void xcf_save_choose_format (XcfInfo *info, + KisImage *gimage); +Q_INT32 xcf_save_image (XcfInfo *info, + KisImage *gimage); + + +#endif /* __XCF_SAVE_H__ */ diff --git a/filters/krita/xcf/xcf/xcf-seek.cc b/filters/krita/xcf/xcf/xcf-seek.cc new file mode 100644 index 00000000..25e9e344 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-seek.cc @@ -0,0 +1,79 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <errno.h> + +#include <glib-object.h> + +#include "core/core-types.h" + +#include "xcf-private.h" +#include "xcf-seek.h" + +#include "gimp-intl.h" + +bool +xcf_seek_pos (XcfInfo *info, + Q_UINT32 pos, + GError **error) +{ + if (info->cp != pos) + { + info->cp = pos; + if (fseek (info->fp, info->cp, SEEK_SET) == -1) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not seek in XCF file: %s"), + g_strerror (errno)); + + return FALSE; + } + } + + return TRUE; +} + +bool +xcf_seek_end (XcfInfo *info, + GError **error) +{ + if (fseek (info->fp, 0, SEEK_END) == -1) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not seek in XCF file: %s"), + g_strerror (errno)); + + return FALSE; + } + + info->cp = ftell (info->fp); + + if (fseek (info->fp, 0, SEEK_END) == -1) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not seek in XCF file: %s"), + g_strerror (errno)); + + return FALSE; + } + + return TRUE; +} diff --git a/filters/krita/xcf/xcf/xcf-seek.h b/filters/krita/xcf/xcf/xcf-seek.h new file mode 100644 index 00000000..99f961bd --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-seek.h @@ -0,0 +1,30 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __XCF_SEEK_H__ +#define __XCF_SEEK_H__ + + +bool xcf_seek_pos (XcfInfo *info, + Q_UINT32 pos, + GError **error); +bool xcf_seek_end (XcfInfo *info, + GError **error); + + +#endif /* __XCF_SEEK_H__ */ diff --git a/filters/krita/xcf/xcf/xcf-write.cc b/filters/krita/xcf/xcf/xcf-write.cc new file mode 100644 index 00000000..89aa2889 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-write.cc @@ -0,0 +1,104 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> /* strlen */ +#include <errno.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <netinet/in.h> +#include <limits.h> +#include <stdlib.h> +#include <cfloat> + +#include "xcf-write.h" + +Q_UINT32 xcf_write_int32 (FILE *fp, Q_INT32 *data, Q_INT32 count); +{ + Q_INT32 tmp; + Q_INT32 i; + + if (count > 0) + { + for (i = 0; i < count; i++) + { + tmp = htonl (data[i]); + xcf_write_int8 (fp, (Q_UINT8*) &tmp, 4); + + if (fp->status() != IO_Ok) + { + return i * 4; + } + } + } + + return count * 4; +} + +Q_UINT32 xcf_write_float (FILE *fp, float *data, Q_INT32 count); +{ + return xcf_write_int32 (fp, (Q_INT32 *)((void *)data), count, error); +} + +Q_UINT32 xcf_write_int8 (FILE *fp, Q_UINT8 *data, Q_INT32 count); +{ + Q_INT32 bytes; + bytes = fp->writeBlock( data, count ); + return bytes; +} + +Q_UINT32 xcf_write_string (FILE *fp, QCString *data, Q_INT32 count); +{ + GError *tmp_error = NULL; + Q_INT32 tmp; + Q_UINT32 total; + Q_INT32 i; + + total = 0; + for (i = 0; i < count; i++) + { + if (data[i]) + tmp = strlen (data[i]) + 1; + else + tmp = 0; + + xcf_write_int32 (fp, &tmp, 1, &tmp_error); + if (tmp_error) + { + g_propagate_error (error, tmp_error); + return total; + } + + if (tmp > 0) + xcf_write_int8 (fp, (Q_UINT8*) data[i], tmp, &tmp_error); + if (tmp_error) + { + g_propagate_error (error, tmp_error); + return total; + } + + total += 4 + tmp; + } + + return total; +} diff --git a/filters/krita/xcf/xcf/xcf-write.h b/filters/krita/xcf/xcf/xcf-write.h new file mode 100644 index 00000000..5e470d45 --- /dev/null +++ b/filters/krita/xcf/xcf/xcf-write.h @@ -0,0 +1,39 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __XCF_WRITE_H__ +#define __XCF_WRITE_H__ + +#include <qstring.h> +#include <qstringlist.h> +#include <qfile.h> + +// Write count integers to the file +Q_UINT32 xcf_write_int32 (QFile *fp, Q_INT32 *data, Q_INT32 count); + +// Write count of floats to the file +Q_UINT32 xcf_write_float (QFile *fp, float *data, Q_INT32 count); + +// Write count chars to the file +Q_UINT32 xcf_write_int8 (QFile *fp, Q_UINT8 *data, Q_INT32 count); + +// Write count zero-terminated strings to the file, each string preceded by its length as an integer +Q_UINT32 xcf_write_string (QFile *fp, QCString *data, Q_INT32 count); + + +#endif /* __XCF_WRITE_H__ */ diff --git a/filters/krita/xcf/xcfexport.cpp b/filters/krita/xcf/xcfexport.cpp new file mode 100644 index 00000000..8274e2b2 --- /dev/null +++ b/filters/krita/xcf/xcfexport.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <xcfexport.h> + +#include <kurl.h> +#include <kgenericfactory.h> + +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_annotation.h> +#include <kis_types.h> +#include <kis_xcf_converter.h> + +typedef KGenericFactory<XCFExport, KoFilter> XCFExportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritaxcfexport, XCFExportFactory("kofficefilters")) + +XCFExport::XCFExport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +XCFExport::~XCFExport() +{ +} + +KoFilter::ConversionStatus XCFExport::convert(const QCString& from, const QCString& to) +{ + kdDebug(41008) << "xcf export! From: " << from << ", To: " << to << "\n"; + + if (from != "application/x-krita") + return KoFilter::NotImplemented; + + KisDoc *output = dynamic_cast<KisDoc*>(m_chain->inputDocument()); + QString filename = m_chain->outputFile(); + + if (!output) + return KoFilter::CreationError; + + + if (filename.isEmpty()) return KoFilter::FileNotFound; + + KURL url(filename); + + KisImageSP img = output->currentImage(); + if (!img) return KoFilter::ParsingError; + + KisXCFConverter ib(output, output->undoAdapter()); + + vKisAnnotationSP_it beginIt = img->beginAnnotations(); + vKisAnnotationSP_it endIt = img->endAnnotations(); + + if (ib.buildFile(url, img, beginIt, endIt) == KisImageBuilder_RESULT_OK) { + return KoFilter::OK; + } + + return KoFilter::InternalError; +} + +#include <xcfexport.moc> + diff --git a/filters/krita/xcf/xcfexport.h b/filters/krita/xcf/xcfexport.h new file mode 100644 index 00000000..e2efe6af --- /dev/null +++ b/filters/krita/xcf/xcfexport.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef XCFEXPORT_H_ +#define XCFEXPORT_H_ + +#include <KoFilter.h> + +class XCFExport : public KoFilter { + Q_OBJECT + +public: + XCFExport(KoFilter *parent, const char *name, const QStringList&); + virtual ~XCFExport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // XCFEXPORT_H_ + diff --git a/filters/krita/xcf/xcfimport.cpp b/filters/krita/xcf/xcfimport.cpp new file mode 100644 index 00000000..da63f041 --- /dev/null +++ b/filters/krita/xcf/xcfimport.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <qstring.h> + +#include <xcfimport.h> +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_view.h> +#include <kis_xcf_converter.h> +#include <kis_progress_display_interface.h> + +typedef KGenericFactory<XCFImport, KoFilter> XCFImportFactory; +K_EXPORT_COMPONENT_FACTORY(libkritaxcfimport, XCFImportFactory("kofficefilters")) + +XCFImport::XCFImport(KoFilter *, const char *, const QStringList&) : KoFilter() +{ +} + +XCFImport::~XCFImport() +{ +} + +KoFilter::ConversionStatus XCFImport::convert(const QCString&, const QCString& to) +{ + kdDebug(41008) << "Importing using XCFImport!\n"; + + if (to != "application/x-krita") + return KoFilter::BadMimeType; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + KisView * view = static_cast<KisView*>(doc -> views().getFirst()); + + QString filename = m_chain -> inputFile(); + + if (!doc) + return KoFilter::CreationError; + + doc -> prepareForImport(); + + + if (!filename.isEmpty()) { + + KURL url(filename); + + if (url.isEmpty()) + return KoFilter::FileNotFound; + + KisXCFConverter ib(doc, doc -> undoAdapter()); + + switch (ib.buildImage(url)) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KoFilter::NotImplemented; + break; + case KisImageBuilder_RESULT_INVALID_ARG: + return KoFilter::BadMimeType; + break; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_LOCAL: + return KoFilter::FileNotFound; + break; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KoFilter::ParsingError; + break; + case KisImageBuilder_RESULT_FAILURE: + return KoFilter::InternalError; + break; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KoFilter::OK; + default: + break; + } + + } + return KoFilter::StorageCreationError; +} + +#include <xcfimport.moc> + diff --git a/filters/krita/xcf/xcfimport.h b/filters/krita/xcf/xcfimport.h new file mode 100644 index 00000000..22ef6a8f --- /dev/null +++ b/filters/krita/xcf/xcfimport.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef XCFIMPORT_H_ +#define XCFIMPORT_H_ + +#include <KoFilter.h> + +class XCFImport : public KoFilter { + Q_OBJECT + +public: + XCFImport(KoFilter *parent, const char *name, const QStringList&); + virtual ~XCFImport(); + +public: + virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to); +}; + +#endif // XCFIMPORT_H_ + |