|
|
|
/*
|
|
|
|
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
|
|
|
|
*
|
|
|
|
* 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 <tqfile.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tqstring.h>
|
|
|
|
|
|
|
|
#include <kdeversion.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kurl.h>
|
|
|
|
#include <kio/netaccess.h>
|
|
|
|
|
|
|
|
#include <tqcolor.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 TQ_UINT8 PIXEL_BLUE = 0;
|
|
|
|
const TQ_UINT8 PIXEL_GREEN = 1;
|
|
|
|
const TQ_UINT8 PIXEL_RED = 2;
|
|
|
|
const TQ_UINT8 PIXEL_ALPHA = 3;
|
|
|
|
|
|
|
|
static const TQ_UINT8 PIXEL_CYAN = 0;
|
|
|
|
static const TQ_UINT8 PIXEL_MAGENTA = 1;
|
|
|
|
static const TQ_UINT8 PIXEL_YELLOW = 2;
|
|
|
|
static const TQ_UINT8 PIXEL_BLACK = 3;
|
|
|
|
static const TQ_UINT8 PIXEL_CMYK_ALPHA = 4;
|
|
|
|
|
|
|
|
static const TQ_UINT8 PIXEL_GRAY = 0;
|
|
|
|
static const TQ_UINT8 PIXEL_GRAY_ALPHA = 1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make this more flexible -- although... ImageMagick
|
|
|
|
* isn't that flexible either.
|
|
|
|
*/
|
|
|
|
TQString 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 (TQString::compare(name, "icc") == 0) {
|
|
|
|
TQByteArray 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 (TQString::compare(name, "icc") == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
TQByteArray rawdata;
|
|
|
|
rawdata.resize(profile->length);
|
|
|
|
memcpy(rawdata.data(), profile->datum, profile->length);
|
|
|
|
|
|
|
|
annotation = new KisAnnotation(TQString(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 'chalk_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
|
|
|
|
TQByteArray rawdata;
|
|
|
|
int len = strlen(attr -> value) + 1;
|
|
|
|
rawdata.resize(len);
|
|
|
|
memcpy(rawdata.data(), attr -> value, len);
|
|
|
|
|
|
|
|
annotation = new KisAnnotation(
|
|
|
|
TQString("chalk_attribute:%1").tqarg(TQString(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() == TQString()) {
|
|
|
|
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("chalk_attribute:")) { // Attribute
|
|
|
|
if (!SetImageAttribute(dst,
|
|
|
|
(*it) -> type().mid(strlen("chalk_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, TQFile::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";
|
|
|
|
|
|
|
|
TQString 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) << "Chalk 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)
|
|
|
|
TQ_UINT8 opacity = OPACITY_OPAQUE;
|
|
|
|
const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]");
|
|
|
|
if (attr != 0) {
|
|
|
|
opacity = TQ_UINT8_MAX - Downscale(TQString(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)
|
|
|
|
TQ_INT32 x_offset = 0;
|
|
|
|
TQ_INT32 y_offset = 0;
|
|
|
|
|
|
|
|
attr = GetImageAttribute(image, "[layer-xpos]");
|
|
|
|
if (attr != 0) {
|
|
|
|
x_offset = TQString(attr->value).toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = GetImageAttribute(image, "[layer-ypos]");
|
|
|
|
if (attr != 0) {
|
|
|
|
y_offset = TQString(attr->value).toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (TQ_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())
|
|
|
|
{
|
|
|
|
TQ_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())
|
|
|
|
{
|
|
|
|
TQ_UINT16 *ptr = reinterpret_cast<TQ_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())
|
|
|
|
{
|
|
|
|
TQ_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())
|
|
|
|
{
|
|
|
|
TQ_UINT16 *ptr = reinterpret_cast<TQ_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())
|
|
|
|
{
|
|
|
|
TQ_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())
|
|
|
|
{
|
|
|
|
TQ_UINT16 *ptr = reinterpret_cast<TQ_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, tqApp -> mainWidget())) {
|
|
|
|
return KisImageBuilder_RESULT_NOT_EXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
|
|
|
|
TQString tmpFile;
|
|
|
|
|
|
|
|
if (KIO::NetAccess::download(uri, tmpFile, tqApp -> 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;
|
|
|
|
|
|
|
|
|
|
|
|
TQ_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
|
|
|
|
|
|
|
|
GetExceptionInfo(&ei);
|
|
|
|
|
|
|
|
ii = CloneImageInfo(0);
|
|
|
|
|
|
|
|
qstrncpy(ii -> filename, TQFile::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
|
|
|
|
|
|
|
|
TQ_INT32 y, height, width;
|
|
|
|
|
|
|
|
height = img -> height();
|
|
|
|
width = img -> width();
|
|
|
|
|
|
|
|
bool alpha = true;
|
|
|
|
TQString ext = TQFileInfo(TQFile::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 TQ_UINT16 *d = reinterpret_cast<const TQ_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()) {
|
|
|
|
|
|
|
|
TQ_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 TQ_UINT16 *d = reinterpret_cast<const TQ_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()) {
|
|
|
|
|
|
|
|
TQ_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 TQ_UINT16 *d = reinterpret_cast<const TQ_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()) {
|
|
|
|
TQ_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 Chalk 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 TQByteArray& 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.
|
|
|
|
*/
|
|
|
|
TQString KisImageMagickConverter::readFilters()
|
|
|
|
{
|
|
|
|
TQString s;
|
|
|
|
TQString all;
|
|
|
|
TQString name;
|
|
|
|
TQString 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KisImageMagickConverter::writeFilters()
|
|
|
|
{
|
|
|
|
TQString s;
|
|
|
|
TQString all;
|
|
|
|
TQString name;
|
|
|
|
TQString 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"
|
|
|
|
|