16#include <QImageReader>
18#include <QLoggingCategory>
23#ifndef XCF_QT5_SUPPORT
26#define USE_FLOAT_IMAGES
29#define MAX_IMAGE_WIDTH 300000
30#define MAX_IMAGE_HEIGHT 300000
33#define MAX_IMAGE_WIDTH 32767
34#define MAX_IMAGE_HEIGHT 32767
37#ifdef USE_FLOAT_IMAGES
38#include <qrgbafloat.h>
46Q_DECLARE_LOGGING_CATEGORY(XCFPLUGIN)
47Q_LOGGING_CATEGORY(XCFPLUGIN,
"kf.imageformats.plugins.xcf", QtWarningMsg)
50#define DISABLE_IMAGE_PROFILE
51#define DISABLE_TILE_PROFILE_CONV
52#define DISABLE_IMAGE_PROFILE_CONV
54const float INCHESPERMETER = (100.0f / 2.54f);
60 static constexpr int rand_r(
unsigned int *seed)
62 unsigned int next = *seed;
67 result = (
unsigned int)(next / 65536) % 2048;
72 result ^= (
unsigned int)(next / 65536) % 1024;
77 result ^= (
unsigned int)(next / 65536) % 1024;
84 constexpr RandomTable()
87 unsigned int next = RANDOM_SEED;
89 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
90 values[i] = rand_r(&next);
93 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
95 int swap = i + rand_r(&next) % (RANDOM_TABLE_SIZE - i);
97 values[i] = values[swap];
102 int values[RANDOM_TABLE_SIZE]{};
123 PROP_ACTIVE_LAYER = 2,
124 PROP_ACTIVE_CHANNEL = 3,
126 PROP_FLOATING_SELECTION = 5,
131 PROP_LOCK_ALPHA = 10,
132 PROP_APPLY_MASK = 11,
135 PROP_SHOW_MASKED = 14,
138 PROP_COMPRESSION = 17,
140 PROP_RESOLUTION = 19,
147 PROP_TEXT_LAYER_FLAGS = 26,
148 PROP_OLD_SAMPLE_POINTS = 27,
149 PROP_LOCK_CONTENT = 28,
150 PROP_GROUP_ITEM = 29,
152 PROP_GROUP_ITEM_FLAGS = 31,
153 PROP_LOCK_POSITION = 32,
154 PROP_FLOAT_OPACITY = 33,
156 PROP_COMPOSITE_MODE = 35,
157 PROP_COMPOSITE_SPACE = 36,
158 PROP_BLEND_SPACE = 37,
159 PROP_FLOAT_COLOR = 38,
160 PROP_SAMPLE_POINTS = 39,
161 MAX_SUPPORTED_PROPTYPE,
166 enum XcfCompressionType : qint8 {
167 COMPRESS_INVALID = -1,
171 COMPRESS_FRACTAL = 3,
173 Q_ENUM(XcfCompressionType)
175 enum LayerModeType : quint32 {
176 GIMP_LAYER_MODE_NORMAL_LEGACY,
177 GIMP_LAYER_MODE_DISSOLVE,
178 GIMP_LAYER_MODE_BEHIND_LEGACY,
179 GIMP_LAYER_MODE_MULTIPLY_LEGACY,
180 GIMP_LAYER_MODE_SCREEN_LEGACY,
181 GIMP_LAYER_MODE_OVERLAY_LEGACY,
182 GIMP_LAYER_MODE_DIFFERENCE_LEGACY,
183 GIMP_LAYER_MODE_ADDITION_LEGACY,
184 GIMP_LAYER_MODE_SUBTRACT_LEGACY,
185 GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY,
186 GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY,
187 GIMP_LAYER_MODE_HSV_HUE_LEGACY,
188 GIMP_LAYER_MODE_HSV_SATURATION_LEGACY,
189 GIMP_LAYER_MODE_HSL_COLOR_LEGACY,
190 GIMP_LAYER_MODE_HSV_VALUE_LEGACY,
191 GIMP_LAYER_MODE_DIVIDE_LEGACY,
192 GIMP_LAYER_MODE_DODGE_LEGACY,
193 GIMP_LAYER_MODE_BURN_LEGACY,
194 GIMP_LAYER_MODE_HARDLIGHT_LEGACY,
195 GIMP_LAYER_MODE_SOFTLIGHT_LEGACY,
196 GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY,
197 GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY,
198 GIMP_LAYER_MODE_COLOR_ERASE_LEGACY,
199 GIMP_LAYER_MODE_OVERLAY,
200 GIMP_LAYER_MODE_LCH_HUE,
201 GIMP_LAYER_MODE_LCH_CHROMA,
202 GIMP_LAYER_MODE_LCH_COLOR,
203 GIMP_LAYER_MODE_LCH_LIGHTNESS,
204 GIMP_LAYER_MODE_NORMAL,
205 GIMP_LAYER_MODE_BEHIND,
206 GIMP_LAYER_MODE_MULTIPLY,
207 GIMP_LAYER_MODE_SCREEN,
208 GIMP_LAYER_MODE_DIFFERENCE,
209 GIMP_LAYER_MODE_ADDITION,
210 GIMP_LAYER_MODE_SUBTRACT,
211 GIMP_LAYER_MODE_DARKEN_ONLY,
212 GIMP_LAYER_MODE_LIGHTEN_ONLY,
213 GIMP_LAYER_MODE_HSV_HUE,
214 GIMP_LAYER_MODE_HSV_SATURATION,
215 GIMP_LAYER_MODE_HSL_COLOR,
216 GIMP_LAYER_MODE_HSV_VALUE,
217 GIMP_LAYER_MODE_DIVIDE,
218 GIMP_LAYER_MODE_DODGE,
219 GIMP_LAYER_MODE_BURN,
220 GIMP_LAYER_MODE_HARDLIGHT,
221 GIMP_LAYER_MODE_SOFTLIGHT,
222 GIMP_LAYER_MODE_GRAIN_EXTRACT,
223 GIMP_LAYER_MODE_GRAIN_MERGE,
224 GIMP_LAYER_MODE_VIVID_LIGHT,
225 GIMP_LAYER_MODE_PIN_LIGHT,
226 GIMP_LAYER_MODE_LINEAR_LIGHT,
227 GIMP_LAYER_MODE_HARD_MIX,
228 GIMP_LAYER_MODE_EXCLUSION,
229 GIMP_LAYER_MODE_LINEAR_BURN,
230 GIMP_LAYER_MODE_LUMA_DARKEN_ONLY,
231 GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY,
232 GIMP_LAYER_MODE_LUMINANCE,
233 GIMP_LAYER_MODE_COLOR_ERASE,
234 GIMP_LAYER_MODE_ERASE,
235 GIMP_LAYER_MODE_MERGE,
236 GIMP_LAYER_MODE_SPLIT,
237 GIMP_LAYER_MODE_PASS_THROUGH,
238 GIMP_LAYER_MODE_COUNT,
240 Q_ENUM(LayerModeType)
243 enum GimpImageType : qint32 {
251 Q_ENUM(GimpImageType)
254 enum GimpColorSpace : qint32 {
260 Q_ENUM(GimpColorSpace);
263 enum GimpCompositeMode : qint32 {
266 CompositeClipBackdrop,
270 Q_ENUM(GimpCompositeMode);
272 enum GimpPrecision : qint32 {
273 GIMP_PRECISION_U8_LINEAR = 100,
274 GIMP_PRECISION_U8_NON_LINEAR = 150,
275 GIMP_PRECISION_U8_PERCEPTUAL = 175,
276 GIMP_PRECISION_U16_LINEAR = 200,
277 GIMP_PRECISION_U16_NON_LINEAR = 250,
278 GIMP_PRECISION_U16_PERCEPTUAL = 275,
279 GIMP_PRECISION_U32_LINEAR = 300,
280 GIMP_PRECISION_U32_NON_LINEAR = 350,
281 GIMP_PRECISION_U32_PERCEPTUAL = 375,
282 GIMP_PRECISION_HALF_LINEAR = 500,
283 GIMP_PRECISION_HALF_NON_LINEAR = 550,
284 GIMP_PRECISION_HALF_PERCEPTUAL = 575,
285 GIMP_PRECISION_FLOAT_LINEAR = 600,
286 GIMP_PRECISION_FLOAT_NON_LINEAR = 650,
287 GIMP_PRECISION_FLOAT_PERCEPTUAL = 675,
288 GIMP_PRECISION_DOUBLE_LINEAR = 700,
289 GIMP_PRECISION_DOUBLE_NON_LINEAR = 750,
290 GIMP_PRECISION_DOUBLE_PERCEPTUAL = 775,
292 Q_ENUM(GimpPrecision);
295 bool readXCF(QIODevice *device, QImage *image);
312 qint64 hierarchy_offset;
327 float opacityFloat = 1.f;
330 uchar red, green, blue;
331 float redF, greenF, blueF;
335 XcfCompressionType compression = COMPRESS_INVALID;
338 quint32 opacity = 255;
339 float opacityFloat = 1.f;
342 quint32 preserve_transparency;
343 quint32 apply_mask = 9;
350 LayerModeType mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
352 GimpColorSpace blendSpace = RgbLinearSpace;
353 GimpColorSpace compositeSpace = RgbLinearSpace;
354 GimpCompositeMode compositeMode = CompositeUnion;
357#ifdef USE_FLOAT_IMAGES
358 uchar tile[quint64(TILE_WIDTH * TILE_HEIGHT *
sizeof(QRgbaFloat32) * 1.5)];
360 uchar tile[quint64(TILE_WIDTH * TILE_HEIGHT *
sizeof(QRgba64) * 1.5)];
367 bool (*assignBytes)(Layer &layer, uint i, uint j,
const GimpPrecision &precision);
378 Layer(
const Layer &) =
delete;
379 Layer &operator=(
const Layer &) =
delete;
381 QImage::Format qimageFormat(
const GimpPrecision precision, uint num_colors = 0,
bool legacyMode =
false)
const
383 int bpc = bytesPerChannel(precision);
384#ifdef USE_FLOAT_IMAGES
385 bool float16 = !legacyMode && precision >= GIMP_PRECISION_HALF_LINEAR && precision <= GIMP_PRECISION_HALF_PERCEPTUAL;
386 bool float32 = !legacyMode && precision >= GIMP_PRECISION_FLOAT_LINEAR && precision <= GIMP_PRECISION_FLOAT_PERCEPTUAL;
390 bpc = std::min(bpc, 1);
395 if (opacity == OPAQUE_OPACITY) {
396#ifdef USE_FLOAT_IMAGES
401 return QImage::QImage::Format_RGBX32FPx4;
407 }
else if (bpc == 2 || bpc == 4) {
410 qCDebug(XCFPLUGIN) <<
"Layer has invalid bpc" << bpc << precision;
416#ifdef USE_FLOAT_IMAGES
421 return QImage::QImage::Format_RGBA32FPx4;
426 }
else if (bpc == 2 || bpc == 4) {
429 qCDebug(XCFPLUGIN) <<
"Layer has invalid bpc" << bpc;
435 if (opacity == OPAQUE_OPACITY) {
456 if (num_colors == 1 || num_colors == 2) {
463 case INDEXEDA_GIMAGE:
464 if (num_colors == 1) {
470 qCWarning(XCFPLUGIN) <<
"Unhandled layer mode" << XCFImageFormat::LayerModeType(type);
483 GimpPrecision precision = GIMP_PRECISION_U8_LINEAR;
489 XcfCompressionType compression = COMPRESS_RLE;
490 float x_resolution = -1;
491 float y_resolution = -1;
494 qint32 num_colors = 0;
503 QHash<QString,QByteArray> parasites;
512 return layer.qimageFormat(header.precision, num_colors,
true);
515 uint bytesPerChannel()
const
517 return XCFImageFormat::bytesPerChannel(header.precision);
522 static qint64 readOffsetPtr(QDataStream &stream)
540 static int random_table[RANDOM_TABLE_SIZE];
541 static bool random_table_initialized;
543 static constexpr RandomTable randomTable{};
548 static QList<QRgb> grayTable;
552 static int add_lut(
int,
int);
556 typedef void (*PixelCopyOperation)(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
559 typedef bool (*PixelMergeOperation)(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
561 static bool modeAffectsSourceAlpha(
const quint32 type);
563 bool loadImageProperties(QDataStream &xcf_io, XCFImage &image);
564 bool loadProperty(QDataStream &xcf_io, PropType &type, QByteArray &bytes, quint32 &rawType);
565 bool loadLayer(QDataStream &xcf_io, XCFImage &xcf_image);
566 bool loadLayerProperties(QDataStream &xcf_io, Layer &layer);
567 bool composeTiles(XCFImage &xcf_image);
568 void setGrayPalette(QImage &image);
569 void setPalette(XCFImage &xcf_image, QImage &image);
570 void setImageParasites(
const XCFImage &xcf_image, QImage &image);
571 static bool assignImageBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision);
572 bool loadHierarchy(QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision);
573 bool loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp,
const GimpPrecision precision);
574 static bool assignMaskBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision);
575 bool loadMask(QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision);
576 bool loadChannelProperties(QDataStream &xcf_io, Layer &layer);
577 bool initializeImage(XCFImage &xcf_image);
578 bool loadTileRLE(QDataStream &xcf_io, uchar *tile,
int size,
int data_length, qint32 bpp, qint64 *bytesParsed);
580 static void copyLayerToImage(XCFImage &xcf_image);
581 static void copyRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
582 static void copyGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
583 static void copyGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
584 static void copyGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
585 static void copyIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
586 static void copyIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
587 static void copyIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
589 static void mergeLayerIntoImage(XCFImage &xcf_image);
590 static bool mergeRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
591 static bool mergeGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
592 static bool mergeGrayAToGray(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
593 static bool mergeGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
594 static bool mergeGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
595 static bool mergeIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
596 static bool mergeIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
597 static bool mergeIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l, QImage &image,
int m,
int n);
599 static void initializeRandomTable();
600 static void dissolveRGBPixels(QImage &image,
int x,
int y);
601 static void dissolveAlphaPixels(QImage &image,
int x,
int y);
603 static uint bytesPerChannel(
const GimpPrecision precision)
606 case GIMP_PRECISION_U8_LINEAR:
607 case GIMP_PRECISION_U8_NON_LINEAR:
608 case GIMP_PRECISION_U8_PERCEPTUAL:
611 case GIMP_PRECISION_U16_LINEAR:
612 case GIMP_PRECISION_U16_NON_LINEAR:
613 case GIMP_PRECISION_U16_PERCEPTUAL:
614 case GIMP_PRECISION_HALF_LINEAR:
615 case GIMP_PRECISION_HALF_NON_LINEAR:
616 case GIMP_PRECISION_HALF_PERCEPTUAL:
620 case GIMP_PRECISION_U32_LINEAR:
621 case GIMP_PRECISION_U32_NON_LINEAR:
622 case GIMP_PRECISION_U32_PERCEPTUAL:
623 case GIMP_PRECISION_FLOAT_LINEAR:
624 case GIMP_PRECISION_FLOAT_NON_LINEAR:
625 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
628 case GIMP_PRECISION_DOUBLE_LINEAR:
629 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
630 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
635 qCDebug(XCFPLUGIN) <<
"Layer has invalid precision" << precision;
641 static bool readXCFHeader(QDataStream &ds, XCFImage::Header *header);
644int XCFImageFormat::random_table[RANDOM_TABLE_SIZE];
645bool XCFImageFormat::random_table_initialized;
647constexpr RandomTable XCFImageFormat::randomTable;
651bool XCFImageFormat::modeAffectsSourceAlpha(
const quint32 type)
654 case GIMP_LAYER_MODE_NORMAL_LEGACY:
655 case GIMP_LAYER_MODE_DISSOLVE:
656 case GIMP_LAYER_MODE_BEHIND_LEGACY:
659 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
660 case GIMP_LAYER_MODE_SCREEN_LEGACY:
661 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
662 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
663 case GIMP_LAYER_MODE_ADDITION_LEGACY:
664 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
665 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
666 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
667 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
668 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
669 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
670 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
671 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
672 case GIMP_LAYER_MODE_DODGE_LEGACY:
673 case GIMP_LAYER_MODE_BURN_LEGACY:
674 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
675 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
676 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
677 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
680 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
681 case GIMP_LAYER_MODE_OVERLAY:
682 case GIMP_LAYER_MODE_LCH_HUE:
683 case GIMP_LAYER_MODE_LCH_CHROMA:
684 case GIMP_LAYER_MODE_LCH_COLOR:
685 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
688 case GIMP_LAYER_MODE_NORMAL:
691 case GIMP_LAYER_MODE_BEHIND:
692 case GIMP_LAYER_MODE_MULTIPLY:
693 case GIMP_LAYER_MODE_SCREEN:
694 case GIMP_LAYER_MODE_DIFFERENCE:
695 case GIMP_LAYER_MODE_ADDITION:
696 case GIMP_LAYER_MODE_SUBTRACT:
697 case GIMP_LAYER_MODE_DARKEN_ONLY:
698 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
699 case GIMP_LAYER_MODE_HSV_HUE:
700 case GIMP_LAYER_MODE_HSV_SATURATION:
701 case GIMP_LAYER_MODE_HSL_COLOR:
702 case GIMP_LAYER_MODE_HSV_VALUE:
703 case GIMP_LAYER_MODE_DIVIDE:
704 case GIMP_LAYER_MODE_DODGE:
705 case GIMP_LAYER_MODE_BURN:
706 case GIMP_LAYER_MODE_HARDLIGHT:
707 case GIMP_LAYER_MODE_SOFTLIGHT:
708 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
709 case GIMP_LAYER_MODE_GRAIN_MERGE:
710 case GIMP_LAYER_MODE_VIVID_LIGHT:
711 case GIMP_LAYER_MODE_PIN_LIGHT:
712 case GIMP_LAYER_MODE_LINEAR_LIGHT:
713 case GIMP_LAYER_MODE_HARD_MIX:
714 case GIMP_LAYER_MODE_EXCLUSION:
715 case GIMP_LAYER_MODE_LINEAR_BURN:
716 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
717 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
718 case GIMP_LAYER_MODE_LUMINANCE:
719 case GIMP_LAYER_MODE_COLOR_ERASE:
720 case GIMP_LAYER_MODE_ERASE:
721 case GIMP_LAYER_MODE_MERGE:
722 case GIMP_LAYER_MODE_SPLIT:
723 case GIMP_LAYER_MODE_PASS_THROUGH:
727 qCWarning(XCFPLUGIN) <<
"Unhandled layer mode" << XCFImageFormat::LayerModeType(type);
733inline QRgb qRgba(
const QRgb rgb,
int a)
735 return ((a & 0xff) << 24 | (rgb & RGB_MASK));
741XCFImageFormat::XCFImageFormat()
743 static_assert(
sizeof(QRgb) == 4,
"the code assumes sizeof(QRgb) == 4, if that's not your case, help us fix it :)");
749void XCFImageFormat::initializeRandomTable()
754 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
755 random_table[i] = rand();
758 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
760 int swap = i + rand() % (RANDOM_TABLE_SIZE - i);
761 tmp = random_table[i];
762 random_table[i] = random_table[swap];
763 random_table[swap] = tmp;
767inline int XCFImageFormat::add_lut(
int a,
int b)
769 return qMin(a + b, 255);
772bool XCFImageFormat::readXCFHeader(
QDataStream &xcf_io, XCFImage::Header *header)
774 QByteArray tag(14,
'\0');
776 if (xcf_io.
readRawData(tag.data(), tag.size()) != tag.size()) {
777 qCDebug(XCFPLUGIN) <<
"XCF: read failure on header tag";
780 if (!tag.startsWith(
"gimp xcf") || !tag.endsWith(
'\0')) {
781 qCDebug(XCFPLUGIN) <<
"XCF: read called on non-XCF file";
788 if (tag.right(4) ==
"file") {
795 qCDebug(XCFPLUGIN) <<
"Failed to parse version" << tag;
799 qCDebug(XCFPLUGIN) <<
"version" << xcf_io.
version();
802 qCDebug(XCFPLUGIN) <<
"Unsupported version" << xcf_io.
version();
806 xcf_io >> header->width >> header->height >> header->type;
811 qCDebug(XCFPLUGIN) <<
"Precision" << GimpPrecision(precision);
815 precision = GIMP_PRECISION_U8_NON_LINEAR;
818 precision = GIMP_PRECISION_U16_NON_LINEAR;
821 precision = GIMP_PRECISION_U32_LINEAR;
824 precision = GIMP_PRECISION_HALF_LINEAR;
827 precision = GIMP_PRECISION_FLOAT_LINEAR;
830 if (precision < GIMP_PRECISION_U8_LINEAR) {
831 qCWarning(XCFPLUGIN) <<
"Invalid precision read" << precision;
834 qCDebug(XCFPLUGIN) <<
"Unexpected precision" << precision <<
"in version" << xcf_io.
version();
838 header->precision = GimpPrecision(precision);
840 qCDebug(XCFPLUGIN) <<
"tag:" << tag <<
" height: " << header->width <<
" width: " << header->height <<
" type: " << header->type;
842 if ((
sizeof(
void *) == 4 && qint64(header->width) * header->height > 16384 * 16384)) {
843 qCWarning(XCFPLUGIN) <<
"On 32-bits programs the maximum image size is limited to" << 16384 <<
"x" << 16384 <<
"px";
847 if (header->width > MAX_IMAGE_WIDTH || header->height > MAX_IMAGE_HEIGHT) {
848 qCWarning(XCFPLUGIN) <<
"The maximum image size is limited to" << MAX_IMAGE_WIDTH <<
"x" << MAX_IMAGE_HEIGHT <<
"px";
858 QDataStream xcf_io(device);
860 if (!readXCFHeader(xcf_io, &xcf_image.header)) {
864 if (!loadImageProperties(xcf_io, xcf_image)) {
874 QStack<qint64> layer_offsets;
877 const qint64 layer_offset = readOffsetPtr(xcf_io);
879 if (layer_offset == 0) {
883 if (layer_offset < 0) {
884 qCDebug(XCFPLUGIN) <<
"XCF: negative layer offset";
888 layer_offsets.
push(layer_offset);
891 xcf_image.num_layers = layer_offsets.
size();
893 if (layer_offsets.
size() == 0) {
894 qCDebug(XCFPLUGIN) <<
"XCF: no layers!";
897 qCDebug(XCFPLUGIN) << xcf_image.num_layers <<
"layers";
900 while (!layer_offsets.
isEmpty()) {
901 qint64 layer_offset = layer_offsets.
pop();
907 if (!loadLayer(xcf_io, xcf_image)) {
912 if (!xcf_image.initialized) {
913 qCDebug(XCFPLUGIN) <<
"XCF: no visible layers!";
918 setImageParasites(xcf_image, xcf_image.image);
920 *outImage = xcf_image.image;
931bool XCFImageFormat::loadImageProperties(
QDataStream &xcf_io, XCFImage &xcf_image)
938 if (!loadProperty(xcf_io, type, bytes, rawType)) {
939 qCDebug(XCFPLUGIN) <<
"XCF: error loading global image properties";
943 QDataStream property(bytes);
949 case PROP_COMPRESSION:
950 property >> xcf_image.compression;
953 case PROP_RESOLUTION:
955 property >> xcf_image.x_resolution >> xcf_image.y_resolution;
959 property >> xcf_image.tattoo;
963 while (!property.atEnd()) {
967 property.readBytes(tag, size);
971 property >> flags >> data;
983 property >> xcf_image.unit;
993 property >> xcf_image.num_colors;
994 if (xcf_image.num_colors < 0 || xcf_image.num_colors > 65535) {
998 xcf_image.palette = QList<QRgb>();
999 xcf_image.palette.reserve(xcf_image.num_colors);
1001 for (
int i = 0; i < xcf_image.num_colors; i++) {
1005 property >> r >> g >> b;
1006 xcf_image.palette.push_back(qRgb(r, g, b));
1011 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented image property" <<
type <<
"(" << rawType <<
")"
1012 <<
", size " << bytes.
size();
1025bool XCFImageFormat::loadProperty(
QDataStream &xcf_io, PropType &type,
QByteArray &bytes, quint32 &rawType)
1030 if (rawType >= MAX_SUPPORTED_PROPTYPE) {
1031 type = MAX_SUPPORTED_PROPTYPE;
1040 type = PropType(rawType);
1042 char *data =
nullptr;
1048 if (type == PROP_COLORMAP) {
1053 size = 3 * ncolors + 4;
1055 if (size > 65535 || size < 4) {
1059 data =
new char[size];
1064 data[2] = ncolors >> 8;
1065 data[3] = ncolors & 255;
1069 }
else if (type == PROP_USER_UNIT) {
1074 xcf_io >> size >> factor >> digits;
1076 for (
int i = 0; i < 5; i++) {
1079 xcf_io >> unit_strings;
1081 delete[] unit_strings;
1084 qCDebug(XCFPLUGIN) <<
"XCF: read failure on property " <<
type;
1092 if (size > 256000 * 4) {
1094 qCDebug(XCFPLUGIN) <<
"XCF: loadProperty skips" <<
type <<
"due to size being too large";
1097 data =
new char[size];
1098 const quint32 dataRead = xcf_io.
readRawData(data, size);
1099 if (dataRead < size) {
1100 qCDebug(XCFPLUGIN) <<
"XCF: loadProperty read less data than expected" << size << dataRead;
1101 memset(&data[dataRead], 0, size - dataRead);
1105 if (size != 0 && data) {
1106 bytes = QByteArray(data, size);
1122bool XCFImageFormat::loadLayer(
QDataStream &xcf_io, XCFImage &xcf_image)
1124 Layer &layer(xcf_image.layer);
1125 delete[] layer.name;
1127 xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
1130 layer.compression = XcfCompressionType(xcf_image.compression);
1132 if (!loadLayerProperties(xcf_io, layer)) {
1136 qCDebug(XCFPLUGIN) <<
"layer: \"" << layer.name <<
"\", size: " << layer.width <<
" x " << layer.height <<
", type: " << layer.type
1137 <<
", mode: " << layer.mode <<
", opacity: " << layer.opacity <<
", visible: " << layer.visible <<
", offset: " << layer.x_offset <<
", "
1138 << layer.y_offset <<
", compression" << layer.compression;
1144 if (layer.visible == 0) {
1150 layer.hierarchy_offset = readOffsetPtr(xcf_io);
1151 layer.mask_offset = readOffsetPtr(xcf_io);
1153 if (layer.hierarchy_offset < 0) {
1154 qCDebug(XCFPLUGIN) <<
"XCF: negative layer hierarchy_offset";
1158 if (layer.mask_offset < 0) {
1159 qCDebug(XCFPLUGIN) <<
"XCF: negative layer mask_offset";
1166 if (!composeTiles(xcf_image)) {
1169 xcf_io.
device()->
seek(layer.hierarchy_offset);
1175 layer.assignBytes = assignImageBytes;
1177 if (!loadHierarchy(xcf_io, layer, xcf_image.header.precision)) {
1181 if (layer.mask_offset != 0) {
1183 if (layer.apply_mask == 9) {
1184 layer.apply_mask = 1;
1189 if (!loadMask(xcf_io, layer, xcf_image.header.precision)) {
1194 layer.apply_mask = 0;
1201 if (!xcf_image.initialized) {
1202 if (!initializeImage(xcf_image)) {
1205 copyLayerToImage(xcf_image);
1206 xcf_image.initialized =
true;
1208 const QColorSpace colorspaceBefore = xcf_image.image.colorSpace();
1209 mergeLayerIntoImage(xcf_image);
1210 if (xcf_image.image.colorSpace() != colorspaceBefore) {
1211 qCDebug(XCFPLUGIN) <<
"Converting color space back to" << colorspaceBefore <<
"after layer composition";
1212 xcf_image.image.convertToColorSpace(colorspaceBefore);
1226bool XCFImageFormat::loadLayerProperties(
QDataStream &xcf_io, Layer &layer)
1233 if (!loadProperty(xcf_io, type, bytes, rawType)) {
1234 qCDebug(XCFPLUGIN) <<
"XCF: error loading layer properties";
1238 QDataStream property(bytes);
1244 case PROP_ACTIVE_LAYER:
1245 layer.active =
true;
1249 property >> layer.opacity;
1250 layer.opacity = std::min(layer.opacity, 255u);
1253 case PROP_FLOAT_OPACITY:
1256 if (bytes.
size() == 4) {
1257 layer.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
1259 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
1264 property >> layer.visible;
1268 property >> layer.linked;
1271 case PROP_LOCK_ALPHA:
1272 property >> layer.preserve_transparency;
1275 case PROP_APPLY_MASK:
1276 property >> layer.apply_mask;
1279 case PROP_EDIT_MASK:
1280 property >> layer.edit_mask;
1283 case PROP_SHOW_MASK:
1284 property >> layer.show_mask;
1288 property >> layer.x_offset >> layer.y_offset;
1292 property >> layer.mode;
1293 if (layer.mode >= GIMP_LAYER_MODE_COUNT) {
1294 qCDebug(XCFPLUGIN) <<
"Found layer with unsupported mode" << LayerModeType(layer.mode) <<
"Defaulting to mode 0";
1295 layer.mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
1300 property >> layer.tattoo;
1303 case PROP_COMPOSITE_SPACE:
1304 property >> layer.compositeSpace;
1305 if (layer.compositeSpace < 0) {
1306 layer.compositeSpace = GimpColorSpace(layer.compositeSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeSpace);
1310 case PROP_COMPOSITE_MODE:
1311 property >> layer.compositeMode;
1312 if (layer.compositeMode < 0) {
1313 layer.compositeMode =
1314 XCFImageFormat::GimpCompositeMode(layer.compositeMode == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeMode);
1318 case PROP_BLEND_SPACE:
1319 property >> layer.blendSpace;
1320 if (layer.blendSpace < 0) {
1321 layer.blendSpace = GimpColorSpace(layer.blendSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.blendSpace);
1326 case PROP_COLOR_TAG:
1330 case PROP_LOCK_CONTENT:
1331 case PROP_LOCK_POSITION:
1335 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented layer property " <<
type <<
"(" << rawType <<
")"
1336 <<
", size " << bytes.
size();
1347bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
1349 Layer &layer(xcf_image.layer);
1351 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
1352 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
1354 qCDebug(XCFPLUGIN) <<
"IMAGE: height=" << xcf_image.header.height <<
", width=" << xcf_image.header.width;
1355 qCDebug(XCFPLUGIN) <<
"LAYER: height=" << layer.height <<
", width=" << layer.width;
1356 qCDebug(XCFPLUGIN) <<
"LAYER: rows=" << layer.nrows <<
", columns=" << layer.ncols;
1363 if ((
sizeof(
void *) == 4 && qint64(layer.width) * layer.height > 16384 * 16384)) {
1364 qCWarning(XCFPLUGIN) <<
"On 32-bits programs the maximum layer size is limited to" << 16384 <<
"x" << 16384 <<
"px";
1367 if (layer.width > MAX_IMAGE_WIDTH || layer.height > MAX_IMAGE_HEIGHT) {
1368 qCWarning(XCFPLUGIN) <<
"The maximum layer size is limited to" << MAX_IMAGE_WIDTH <<
"x" << MAX_IMAGE_HEIGHT <<
"px";
1375 if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
1376 if (qint64(layer.width) * layer.height > 16384 * 16384) {
1377 qCWarning(XCFPLUGIN) <<
"Euristic sanity check: the image may be corrupted!";
1382#ifndef XCF_QT5_SUPPORT
1387 qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
1394 layer.image_tiles.resize(layer.nrows);
1396 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1397 layer.alpha_tiles.resize(layer.nrows);
1400 if (layer.mask_offset != 0) {
1401 layer.mask_tiles.resize(layer.nrows);
1404 for (uint j = 0; j < layer.nrows; j++) {
1405 layer.image_tiles[j].resize(layer.ncols);
1407 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1408 layer.alpha_tiles[j].resize(layer.ncols);
1411 if (layer.mask_offset != 0) {
1412 layer.mask_tiles[j].resize(layer.ncols);
1416 const QImage::Format format = layer.qimageFormat(xcf_image.header.precision);
1418 for (uint j = 0; j < layer.nrows; j++) {
1419 for (uint i = 0; i < layer.ncols; i++) {
1420 uint tile_width = (i + 1) * TILE_WIDTH <= layer.width ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
1422 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
1427 switch (layer.type) {
1430 layer.image_tiles[j][i] = QImage(tile_width, tile_height, format);
1431 if (layer.image_tiles[j][i].isNull()) {
1434 layer.image_tiles[j][i].setColorCount(0);
1439 if (layer.image_tiles[j][i].isNull()) {
1442 layer.image_tiles[j][i].setColorCount(256);
1443 setGrayPalette(layer.image_tiles[j][i]);
1448 layer.image_tiles[j][i].setColorCount(256);
1449 if (layer.image_tiles[j][i].isNull()) {
1452 setGrayPalette(layer.image_tiles[j][i]);
1455 if (layer.alpha_tiles[j][i].isNull()) {
1458 layer.alpha_tiles[j][i].setColorCount(256);
1459 setGrayPalette(layer.alpha_tiles[j][i]);
1462 case INDEXED_GIMAGE:
1464 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1465 if (layer.image_tiles[j][i].isNull()) {
1468 setPalette(xcf_image, layer.image_tiles[j][i]);
1471 case INDEXEDA_GIMAGE:
1473 if (layer.image_tiles[j][i].isNull()) {
1476 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1477 setPalette(xcf_image, layer.image_tiles[j][i]);
1480 if (layer.alpha_tiles[j][i].isNull()) {
1483 layer.alpha_tiles[j][i].setColorCount(256);
1484 setGrayPalette(layer.alpha_tiles[j][i]);
1486 if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
1487 qCWarning(XCFPLUGIN) <<
"Selected wrong tile format" << layer.image_tiles[j][i].format() <<
"expected" << format;
1491#ifndef DISABLE_TILE_PROFILE
1492 switch (xcf_image.header.precision) {
1493 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1494 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1495 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1496 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1497 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1498 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1501 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
1502 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
1503 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
1504 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
1505 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
1506 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
1509 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
1510 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
1511 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
1512 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
1513 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
1514 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
1519 if (layer.mask_offset != 0) {
1521 layer.mask_tiles[j][i].setColorCount(256);
1522 if (layer.mask_tiles[j][i].isNull()) {
1525 setGrayPalette(layer.mask_tiles[j][i]);
1538void XCFImageFormat::setGrayPalette(
QImage &image)
1540 if (grayTable.isEmpty()) {
1541 grayTable.resize(256);
1543 for (
int i = 0; i < 256; i++) {
1544 grayTable[i] = qRgb(i, i, i);
1556void XCFImageFormat::setPalette(XCFImage &xcf_image,
QImage &image)
1558 Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size());
1569void XCFImageFormat::setImageParasites(
const XCFImage &xcf_image,
QImage &image)
1571 auto&& p = xcf_image.parasites;
1572 auto keys = p.keys();
1573 for (
auto &&key : std::as_const(keys)) {
1574 auto value = p.value(key);
1575 if (value.isEmpty())
1584 if (key == QStringLiteral(
"icc-profile")) {
1596 if (key == QStringLiteral(
"gimp-comment")) {
1597 value.replace(
'\0', QByteArray());
1605 if (key == QStringLiteral(
"gimp-image-metadata")) {
1608 value.replace(
'\0', QByteArray());
1620 if (key == QStringLiteral(
"gimp-metadata")) {
1624 value.replace(
'\0', QByteArray());
1631#ifdef DISABLE_IMAGE_PROFILE
1634 switch (xcf_image.header.precision) {
1635 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1636 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1637 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1638 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1639 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1640 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1658bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
1660 QImage &image = layer.image_tiles[j][i];
1662 const uchar *tile = layer.tile;
1663 const int width = image.
width();
1664 const int height = image.
height();
1666 uchar *bits = image.
bits();
1669 if (layer.type == GRAYA_GIMAGE || layer.type == GRAY_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1670 auto bpc = bytesPerChannel(precision);
1671 for (
int y = 0; y < height; y++) {
1672 uchar *dataPtr = bits + y * bytesPerLine;
1673 uchar *alphaPtr =
nullptr;
1674 if (layer.alpha_tiles.size() > j && layer.alpha_tiles.at(j).size() > i) {
1675 QImage &alphaTile = layer.alpha_tiles[j][i];
1676 if (alphaTile.
width() >= width && alphaTile.
height() > y) {
1681#ifdef USE_FLOAT_IMAGES
1682 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1683 for (
int x = 0; x < width; x++) {
1684 auto src =
reinterpret_cast<const quint16 *
>(tile);
1685 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1687 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1688 tile +=
sizeof(quint16) * 2;
1690 tile +=
sizeof(quint16);
1694 for (
int x = 0; x < width; x++) {
1695 auto src =
reinterpret_cast<const float *
>(tile);
1696 *dataPtr++ = qFromBigEndian<float>(src[0]) * 255;
1698 *alphaPtr++ = qFromBigEndian<float>(src[1]) * 255;
1699 tile +=
sizeof(float) * 2;
1701 tile +=
sizeof(float);
1706 for (
int x = 0; x < width; x++) {
1707 auto src = (
const quint16 *)tile;
1708 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1710 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1711 tile +=
sizeof(quint16) * 2;
1713 tile +=
sizeof(quint16);
1717 }
else if (bpc == 2) {
1718#ifdef USE_FLOAT_IMAGES
1719 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1720 for (
int x = 0; x < width; x++) {
1721 auto src =
reinterpret_cast<const quint16 *
>(tile);
1722 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1724 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1725 tile +=
sizeof(QRgb);
1728 for (
int x = 0; x < width; x++) {
1729 auto src =
reinterpret_cast<const qfloat16 *
>(tile);
1730 *dataPtr++ = qFromBigEndian<qfloat16>(src[0]) * 255;
1732 *alphaPtr++ = qFromBigEndian<qfloat16>(src[1]) * 255;
1733 tile +=
sizeof(QRgb);
1737 for (
int x = 0; x < width; x++) {
1738 auto src =
reinterpret_cast<const quint16 *
>(tile);
1739 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1741 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1742 tile +=
sizeof(QRgb);
1746 for (
int x = 0; x < width; x++) {
1748 *dataPtr++ = tile[0];
1750 *alphaPtr++ = tile[1];
1751 tile +=
sizeof(QRgb);
1758 switch (image.
format()) {
1760 for (
int y = 0; y < height; y++) {
1761 uchar *dataPtr = image.
scanLine(y);
1762 for (
int x = 0; x < width * 4; x += 4, tile += 4) {
1763 dataPtr[x + 0] = tile[0];
1764 dataPtr[x + 1] = tile[1];
1765 dataPtr[x + 2] = tile[2];
1766 dataPtr[x + 3] = 255;
1771 for (
int y = 0; y < height; y++) {
1772 const size_t bpl = width * 4;
1773 memcpy(image.
scanLine(y), tile + y * bpl, bpl);
1777 for (
int y = 0; y < height; y++) {
1778 quint16 *dataPtr =
reinterpret_cast<quint16 *
>(image.
scanLine(y));
1779 const size_t bpl = width *
sizeof(QRgba64);
1780 const quint16 *src =
reinterpret_cast<const quint16 *
>(tile + y * bpl);
1781 for (
int x = 0; x < width * 4; x += 4) {
1782 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1783 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1784 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1785 dataPtr[x + 3] = 65535;
1789#ifdef USE_FLOAT_IMAGES
1791 for (
int y = 0; y < height; y++) {
1792 qfloat16 *dataPtr =
reinterpret_cast<qfloat16 *
>(image.
scanLine(y));
1793 const qfloat16 *src =
reinterpret_cast<const qfloat16 *
>(tile + y * width *
sizeof(QRgbaFloat16));
1794 for (
int x = 0; x < width * 4; x += 4) {
1795 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1796 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1797 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1798 dataPtr[x + 3] = qfloat16(1);
1803 static_assert(
sizeof(QRgbaFloat16) ==
sizeof(QRgba64),
"Different sizes for float and int 16 bit pixels");
1806 for (
int y = 0; y < height; y++) {
1807 const size_t bpl = width *
sizeof(QRgba64);
1808 qFromBigEndian<qint16>(tile + y * bpl, width * 4, image.
scanLine(y));
1811#ifdef USE_FLOAT_IMAGES
1813 for (
int y = 0; y < height; y++) {
1814 const size_t bpl = width *
sizeof(QRgbaFloat32);
1815 qFromBigEndian<qint32>(tile + y * bpl, width * 4, image.
scanLine(y));
1819 for (
int y = 0; y < height; y++) {
1820 float *dataPtr =
reinterpret_cast<float *
>(image.
scanLine(y));
1821 const float *src =
reinterpret_cast<const float *
>(tile + y * width *
sizeof(QRgbaFloat32));
1822 for (
int x = 0; x < width * 4; x += 4) {
1823 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1824 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1825 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1826 dataPtr[x + 3] = 1.f;
1832 for (
int y = 0; y < height; y++) {
1833 uchar *dataPtr = bits + y * bytesPerLine;
1834 for (
int x = 0; x < width; x++) {
1835 *dataPtr++ = tile[0];
1836 tile +=
sizeof(QRgb);
1841 qCWarning(XCFPLUGIN) <<
"Unhandled image format" << image.
format() <<
"and/or layer type" << layer.type;
1856bool XCFImageFormat::loadHierarchy(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
1862 xcf_io >> width >> height >> bpp;
1863 const qint64 offset = readOffsetPtr(xcf_io);
1865 qCDebug(XCFPLUGIN) <<
"width" << width <<
"height" << height <<
"bpp" << bpp <<
"offset" << offset;
1868 qCDebug(XCFPLUGIN) <<
"XCF: negative hierarchy offset";
1872 const bool isMask = layer.assignBytes == assignMaskBytes;
1875 switch (layer.type) {
1877 if (bpp != 3 * bytesPerChannel(precision)) {
1878 qCDebug(XCFPLUGIN) <<
"Found layer of type RGB but with bpp != 3" << bpp;
1886 if (bpp != 4 * bytesPerChannel(precision)) {
1887 qCDebug(XCFPLUGIN) <<
"Found layer of type RGBA but with bpp != 4, got" << bpp <<
"bpp";
1895 if (bpp != 1 * bytesPerChannel(precision)) {
1896 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray but with bpp != 1" << bpp;
1901 if (bpp != 2 * bytesPerChannel(precision)) {
1902 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray+Alpha but with bpp != 2" << bpp;
1909 case INDEXED_GIMAGE:
1910 if (bpp != 1 * bytesPerChannel(precision)) {
1911 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed but with bpp != 1" << bpp;
1915 case INDEXEDA_GIMAGE:
1916 if (bpp != 2 * bytesPerChannel(precision)) {
1917 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed+Alpha but with bpp != 2" << bpp;
1926 if (bpp > 4 * bytesPerChannel(precision)) {
1927 qCDebug(XCFPLUGIN) <<
"bpp is" << bpp <<
"We don't support layers with bpp > 4";
1940 qCDebug(XCFPLUGIN) <<
"XCF: read failure on layer " << layer.name <<
" level offsets";
1943 }
while (junk != 0);
1945 qint64 saved_pos = xcf_io.
device()->
pos();
1948 if (!loadLevel(xcf_io, layer, bpp, precision)) {
1956template<
typename SourceFormat>
1957static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
1959 SourceFormat *source = (SourceFormat *)(input);
1960 for (quint64 offset = 0; offset < outputSize; offset++) {
1961 (
reinterpret_cast<uint16_t *
>(output))[offset] = qToBigEndian(quint16(qBound(0., qFromBigEndian<SourceFormat>(source[offset]) * 65535. + 0.5, 65535.)));
1974bool XCFImageFormat::loadLevel(
QDataStream &xcf_io, Layer &layer, qint32 bpp,
const GimpPrecision precision)
1976 auto bpc = bytesPerChannel(precision);
1977 if ((bpc == 0) || (bpp % bpc)) {
1978 qCDebug(XCFPLUGIN) <<
"XCF: the stream seems corrupted";
1985 xcf_io >> width >> height;
1986 qint64 offset = readOffsetPtr(xcf_io);
1989 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
1996 for (uint j = 0; j < layer.nrows; j++) {
1997 for (uint i = 0; i < layer.ncols; i++) {
1999 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
2007 bool needConvert =
true;
2008 switch (precision) {
2009#ifdef USE_FLOAT_IMAGES
2010 case GIMP_PRECISION_HALF_LINEAR:
2011 case GIMP_PRECISION_HALF_NON_LINEAR:
2012 case GIMP_PRECISION_HALF_PERCEPTUAL:
2013 case GIMP_PRECISION_FLOAT_LINEAR:
2014 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2015 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2017 case GIMP_PRECISION_U8_LINEAR:
2018 case GIMP_PRECISION_U8_NON_LINEAR:
2019 case GIMP_PRECISION_U8_PERCEPTUAL:
2020 case GIMP_PRECISION_U16_LINEAR:
2021 case GIMP_PRECISION_U16_NON_LINEAR:
2022 case GIMP_PRECISION_U16_PERCEPTUAL:
2023 needConvert =
false;
2029 const uint blockSize = TILE_WIDTH * TILE_HEIGHT * bpp * 1.5;
2031 QList<uchar> buffer;
2033 buffer.
resize(blockSize * (bpp == 2 ? 2 : 1));
2035 for (uint j = 0; j < layer.nrows; j++) {
2036 for (uint i = 0; i < layer.ncols; i++) {
2038 qCDebug(XCFPLUGIN) <<
"XCF: incorrect number of tiles in layer " << layer.name;
2042 qint64 saved_pos = xcf_io.
device()->
pos();
2043 qint64 offset2 = readOffsetPtr(xcf_io);
2046 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2052 offset2 = offset + blockSize;
2056 qint64 bytesParsed = 0;
2058 switch (layer.compression) {
2059 case COMPRESS_NONE: {
2060 if (xcf_io.
version() > 11 ||
size_t(bpp) >
sizeof(QRgba64)) {
2061 qCDebug(XCFPLUGIN) <<
"Component reading not supported yet";
2064 const int data_size = bpp * TILE_WIDTH * TILE_HEIGHT;
2065 if (data_size >
int(blockSize)) {
2066 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2069 int dataRead = xcf_io.
readRawData(
reinterpret_cast<char *
>(layer.tile), data_size);
2070 if (dataRead < data_size) {
2071 qCDebug(XCFPLUGIN) <<
"short read, expected" << data_size <<
"got" << dataRead;
2074 bytesParsed = dataRead;
2077 case COMPRESS_RLE: {
2078 int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
2079 const uint data_size = size * bpp;
2081 if (data_size >=
unsigned(buffer.
size())) {
2082 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" << buffer.
size() <<
"but need" << data_size;
2086 if (data_size >
sizeof(layer.tile)) {
2087 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2090 if (blockSize >
sizeof(layer.tile)) {
2091 qCWarning(XCFPLUGIN) <<
"Too small tiles" <<
sizeof(layer.tile) <<
"this image requires" << blockSize <<
sizeof(QRgba64) << bpp;
2095 if (!loadTileRLE(xcf_io, needConvert ? buffer.
data() : layer.tile, size, offset2 - offset, bpp, &bytesParsed)) {
2096 qCDebug(XCFPLUGIN) <<
"Failed to read RLE";
2102 qCDebug(XCFPLUGIN) <<
"Unhandled compression" << layer.compression;
2107 if (bytesParsed > buffer.
size()) {
2108 qCDebug(XCFPLUGIN) <<
"Invalid number of bytes parsed" << bytesParsed << buffer.
size();
2112 switch (precision) {
2113 case GIMP_PRECISION_U32_LINEAR:
2114 case GIMP_PRECISION_U32_NON_LINEAR:
2115 case GIMP_PRECISION_U32_PERCEPTUAL: {
2116 quint32 *source =
reinterpret_cast<quint32 *
>(buffer.
data());
2117 for (quint64 offset = 0, len = buffer.
size() /
sizeof(quint32); offset < len; ++offset) {
2118 (
reinterpret_cast<quint16 *
>(layer.tile))[offset] = qToBigEndian<quint16>(qFromBigEndian(source[offset]) / 65537);
2122#ifndef USE_FLOAT_IMAGES
2123 case GIMP_PRECISION_HALF_LINEAR:
2124 case GIMP_PRECISION_HALF_NON_LINEAR:
2125 case GIMP_PRECISION_HALF_PERCEPTUAL:
2126 convertFloatTo16Bit<qfloat16>(layer.tile, buffer.
size() /
sizeof(qfloat16), buffer.
data());
2128 case GIMP_PRECISION_FLOAT_LINEAR:
2129 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2130 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2131 convertFloatTo16Bit<float>(layer.tile, buffer.
size() /
sizeof(
float), buffer.
data());
2133 case GIMP_PRECISION_DOUBLE_LINEAR:
2134 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2135 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2136 convertFloatTo16Bit<double>(layer.tile, buffer.
size() /
sizeof(
double), buffer.
data());
2139 case GIMP_PRECISION_DOUBLE_LINEAR:
2140 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2141 case GIMP_PRECISION_DOUBLE_PERCEPTUAL: {
2142 double *source =
reinterpret_cast<double *
>(buffer.
data());
2143 for (quint64 offset = 0, len = buffer.
size() /
sizeof(
double); offset < len; ++offset) {
2144 (
reinterpret_cast<float *
>(layer.tile))[offset] = qToBigEndian<float>(
float(qFromBigEndian(source[offset])));
2150 qCWarning(XCFPLUGIN) <<
"Unsupported precision" << precision;
2158 if (!layer.assignBytes(layer, i, j, precision)) {
2163 offset = readOffsetPtr(xcf_io);
2166 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2181bool XCFImageFormat::loadMask(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
2187 xcf_io >> width >> height >>
name;
2191 if (!loadChannelProperties(xcf_io, layer)) {
2195 const qint64 hierarchy_offset = readOffsetPtr(xcf_io);
2197 if (hierarchy_offset < 0) {
2198 qCDebug(XCFPLUGIN) <<
"XCF: negative mask hierarchy_offset";
2203 layer.assignBytes = assignMaskBytes;
2205 if (!loadHierarchy(xcf_io, layer, precision)) {
2235bool XCFImageFormat::loadTileRLE(
QDataStream &xcf_io, uchar *tile,
int image_size,
int data_length, qint32 bpp, qint64 *bytesParsed)
2241 uchar *xcfdatalimit;
2243 int step =
sizeof(QRgb);
2249 step =
sizeof(QRgb);
2253 step =
sizeof(QRgb) * 2;
2257 step =
sizeof(QRgb) * 4;
2260 qCDebug(XCFPLUGIN) <<
"XCF: unhandled bit depth" << bpp;
2264 if (data_length < 0 || data_length >
int(TILE_WIDTH * TILE_HEIGHT * step * 1.5)) {
2265 qCDebug(XCFPLUGIN) <<
"XCF: invalid tile data length" << data_length;
2269 xcfdata = xcfodata =
new uchar[data_length];
2271 const int dataRead = xcf_io.
readRawData((
char *)xcfdata, data_length);
2272 if (dataRead <= 0) {
2274 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile" << dataRead;
2278 if (dataRead < data_length) {
2279 memset(&xcfdata[dataRead], 0, data_length - dataRead);
2284 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile";
2288 xcfdatalimit = &xcfodata[data_length - 1];
2290 for (
int i = 0; i < bpp; ++i) {
2293 int size = image_size;
2296 if (xcfdata > xcfdatalimit) {
2300 uchar val = *xcfdata++;
2303 if (length >= 128) {
2304 length = 255 - (length - 1);
2305 if (length == 128) {
2306 if (xcfdata >= xcfdatalimit) {
2310 length = (*xcfdata << 8) + xcfdata[1];
2321 if (&xcfdata[length - 1] > xcfdatalimit) {
2325 while (length-- > 0) {
2331 if (length == 128) {
2332 if (xcfdata >= xcfdatalimit) {
2336 length = (*xcfdata << 8) + xcfdata[1];
2346 if (xcfdata > xcfdatalimit) {
2350 qintptr totalLength = qintptr(data - tile) + length * step;
2351 if (totalLength >= image_size * step * 1.5) {
2352 qCDebug(XCFPLUGIN) <<
"Ran out of space when trying to unpack image, over:" << totalLength - image_size << totalLength << image_size
2359 while (length-- > 0) {
2366 *bytesParsed = qintptr(data - tile);
2373 qCDebug(XCFPLUGIN) <<
"The run length encoding could not be decoded properly";
2385bool XCFImageFormat::loadChannelProperties(
QDataStream &xcf_io, Layer &layer)
2392 if (!loadProperty(xcf_io, type, bytes, rawType)) {
2393 qCDebug(XCFPLUGIN) <<
"XCF: error loading channel properties";
2397 QDataStream property(bytes);
2404 property >> layer.mask_channel.opacity;
2405 layer.mask_channel.opacity = std::min(layer.mask_channel.opacity, 255u);
2408 case PROP_FLOAT_OPACITY:
2411 if (bytes.
size() == 4) {
2412 layer.mask_channel.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
2414 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
2419 property >> layer.mask_channel.visible;
2422 case PROP_SHOW_MASKED:
2423 property >> layer.mask_channel.show_masked;
2427 property >> layer.mask_channel.red >> layer.mask_channel.green >> layer.mask_channel.blue;
2430 case PROP_FLOAT_COLOR:
2431 property >> layer.mask_channel.redF >> layer.mask_channel.greenF >> layer.mask_channel.blueF;
2435 property >> layer.mask_channel.tattoo;
2443 case PROP_COLOR_TAG:
2447 case PROP_LOCK_CONTENT:
2448 case PROP_LOCK_POSITION:
2452 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented channel property " <<
type <<
"(" << rawType <<
")"
2453 <<
", size " << bytes.
size();
2465bool XCFImageFormat::assignMaskBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
2467 QImage &image = layer.mask_tiles[j][i];
2468 if (image.
depth() != 8) {
2469 qCWarning(XCFPLUGIN) <<
"invalid bytes per pixel, we only do 8 bit masks" << image.
depth();
2473 uchar *tile = layer.tile;
2474 const int width = image.
width();
2475 const int height = image.
height();
2477 uchar *bits = image.
bits();
2478 auto bpc = bytesPerChannel(precision);
2483 for (
int y = 0; y < height; y++) {
2484 uchar *dataPtr = bits + y * bytesPerLine;
2485#ifdef USE_FLOAT_IMAGES
2487 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2488 for (
int x = 0; x < width; x++) {
2489 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2490 tile +=
sizeof(quint16);
2493 for (
int x = 0; x < width; x++) {
2494 *dataPtr++ = qFromBigEndian<float>(*
reinterpret_cast<const float *
>(tile)) * 255;
2495 tile +=
sizeof(QRgb);
2498 }
else if (bpc == 2) {
2500 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2501 for (
int x = 0; x < width; x++) {
2502 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2503 tile +=
sizeof(QRgb);
2506 for (
int x = 0; x < width; x++) {
2507 *dataPtr++ = qFromBigEndian<qfloat16>(*
reinterpret_cast<const qfloat16 *
>(tile)) * 255;
2508 tile +=
sizeof(QRgb);
2514 for (
int x = 0; x < width; x++) {
2515 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2516 tile +=
sizeof(QRgb);
2518 }
else if (bpc == 4) {
2519 for (
int x = 0; x < width; x++) {
2520 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2521 tile +=
sizeof(quint16);
2526 for (
int x = 0; x < width; x++) {
2527 *dataPtr++ = tile[0];
2528 tile +=
sizeof(QRgb);
2564bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
2567 Layer &layer(xcf_image.layer);
2568 QImage &image(xcf_image.image);
2570 switch (layer.type) {
2572 if (layer.opacity == OPAQUE_OPACITY) {
2578 setGrayPalette(image);
2586 image = imageAlloc(xcf_image.header.width, xcf_image.header.height, xcf_image.qimageFormat());
2597 case INDEXED_GIMAGE:
2610 if (xcf_image.num_colors <= 2) {
2617 setPalette(xcf_image, image);
2618 }
else if (xcf_image.num_colors <= 256) {
2625 setPalette(xcf_image, image);
2629 case INDEXEDA_GIMAGE:
2630 if (xcf_image.num_colors == 1) {
2632 xcf_image.num_colors++;
2633 xcf_image.palette.resize(xcf_image.num_colors);
2634 xcf_image.palette[1] = xcf_image.palette[0];
2635 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2643 setPalette(xcf_image, image);
2644 }
else if (xcf_image.num_colors < 256) {
2646 xcf_image.num_colors++;
2647 xcf_image.palette.resize(xcf_image.num_colors);
2648 for (
int c = xcf_image.num_colors - 1; c >= 1; c--) {
2649 xcf_image.palette[c] = xcf_image.palette[c - 1];
2652 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2659 setPalette(xcf_image, image);
2668 image.
fill(qRgba(255, 255, 255, 0));
2672 if (image.
format() != xcf_image.qimageFormat()) {
2673 qCWarning(XCFPLUGIN) <<
"Selected wrong format:" << image.
format() <<
"expected" << layer.qimageFormat(xcf_image.header.precision);
2679#ifndef DISABLE_IMAGE_PROFILE
2680 switch (xcf_image.header.precision) {
2681 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
2682 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
2683 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
2684 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
2685 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
2686 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
2689 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
2690 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
2691 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
2692 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
2693 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
2694 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
2697 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
2698 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
2699 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2700 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
2701 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
2702 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
2708 if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
2709 const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
2710 if (dpmx >
float(std::numeric_limits<int>::max())) {
2713 const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
2714 if (dpmy >
float(std::numeric_limits<int>::max())) {
2728void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
2730 Layer &layer(xcf_image.layer);
2731 QImage &image(xcf_image.image);
2732 PixelCopyOperation
copy =
nullptr;
2734 switch (layer.type) {
2737 copy = copyRGBToRGB;
2740 if (layer.opacity == OPAQUE_OPACITY) {
2741 copy = copyGrayToGray;
2743 copy = copyGrayToRGB;
2747 copy = copyGrayAToRGB;
2749 case INDEXED_GIMAGE:
2750 copy = copyIndexedToIndexed;
2752 case INDEXEDA_GIMAGE:
2753 if (xcf_image.image.depth() <= 8) {
2754 copy = copyIndexedAToIndexed;
2756 copy = copyIndexedAToRGB;
2766 for (uint j = 0; j < layer.nrows; j++) {
2767 qint32 y = qint32(j * TILE_HEIGHT);
2769 for (uint i = 0; i < layer.ncols; i++) {
2770 qint32 x = qint32(i * TILE_WIDTH);
2777 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
2778 if (!random_table_initialized) {
2779 initializeRandomTable();
2780 random_table_initialized =
true;
2782 if (layer.type == RGBA_GIMAGE) {
2783 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
2786 else if (layer.type == GRAYA_GIMAGE) {
2787 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
2792 if (copy == copyRGBToRGB && layer.apply_mask != 1) {
2793 QPainter painter(&image);
2794 painter.setOpacity(layer.opacity / 255.0);
2796 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
2797 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
2798 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
2803 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
2804 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
2805 int m = x + k + layer.x_offset;
2806 int n = y + l + layer.y_offset;
2808 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
2812 (*copy)(layer, i, j, k, l, image, m, n);
2832void XCFImageFormat::copyRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2834 if (image.
depth() == 32) {
2835 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2836 uchar src_a = layer.opacity;
2838 if (layer.type == RGBA_GIMAGE) {
2839 src_a = INT_MULT(src_a, qAlpha(src));
2844 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2845 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2848 image.
setPixel(m, n, qRgba(src, src_a));
2849 }
else if (image.
depth() == 64) {
2850 QRgba64 src = layer.image_tiles[j][i].pixelColor(k, l).rgba64();
2851 quint16 src_a = layer.opacity;
2853 if (layer.type == RGBA_GIMAGE) {
2854 src_a = INT_MULT(src_a, qAlpha(src));
2859 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2860 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2879void XCFImageFormat::copyGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2881 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2898void XCFImageFormat::copyGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2900 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2901 uchar src_a = layer.opacity;
2902 image.
setPixel(m, n, qRgba(src, src_a));
2918void XCFImageFormat::copyGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2920 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2921 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2922 src_a = INT_MULT(src_a, layer.opacity);
2926 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2927 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2930 image.
setPixel(m, n, qRgba(src, src_a));
2944void XCFImageFormat::copyIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2946 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2961void XCFImageFormat::copyIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2963 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
2964 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2965 src_a = INT_MULT(src_a, layer.opacity);
2967 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2968 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2993void XCFImageFormat::copyIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2995 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2996 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2997 src_a = INT_MULT(src_a, layer.opacity);
3000 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3001 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3008 src_a = OPAQUE_OPACITY;
3011 image.
setPixel(m, n, qRgba(src, src_a));
3018void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
3020 Layer &layer(xcf_image.layer);
3021 QImage &image(xcf_image.image);
3023 PixelMergeOperation
merge =
nullptr;
3025 if (!layer.opacity) {
3029 if (layer.blendSpace == XCFImageFormat::AutoColorSpace) {
3030 qCDebug(XCFPLUGIN) <<
"Auto blend space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3031 layer.blendSpace = XCFImageFormat::RgbLinearSpace;
3034 if (layer.blendSpace != XCFImageFormat::RgbLinearSpace) {
3035 qCDebug(XCFPLUGIN) <<
"Unimplemented blend color space" << layer.blendSpace;
3037 qCDebug(XCFPLUGIN) <<
"Blend color space" << layer.blendSpace;
3039 if (layer.compositeSpace == XCFImageFormat::AutoColorSpace) {
3040 qCDebug(XCFPLUGIN) <<
"Auto composite space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3041 layer.compositeSpace = XCFImageFormat::RgbLinearSpace;
3044 if (layer.compositeSpace != XCFImageFormat::RgbLinearSpace) {
3045 qCDebug(XCFPLUGIN) <<
"Unimplemented composite color space" << layer.compositeSpace;
3047 if (layer.compositeMode != XCFImageFormat::CompositeUnion) {
3048 qCDebug(XCFPLUGIN) <<
"Unhandled composite mode" << layer.compositeMode;
3051 switch (layer.type) {
3054 merge = mergeRGBToRGB;
3057 if (layer.opacity == OPAQUE_OPACITY && xcf_image.image.depth() <= 8) {
3058 merge = mergeGrayToGray;
3060 merge = mergeGrayToRGB;
3064 if (xcf_image.image.depth() <= 8) {
3065 merge = mergeGrayAToGray;
3067 merge = mergeGrayAToRGB;
3070 case INDEXED_GIMAGE:
3071 merge = mergeIndexedToIndexed;
3073 case INDEXEDA_GIMAGE:
3074 if (xcf_image.image.depth() <= 8) {
3075 merge = mergeIndexedAToIndexed;
3077 merge = mergeIndexedAToRGB;
3085 if (merge == mergeRGBToRGB && layer.apply_mask != 1) {
3086 int painterMode = -1;
3087 switch (layer.mode) {
3088 case GIMP_LAYER_MODE_NORMAL:
3089 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3092 case GIMP_LAYER_MODE_MULTIPLY:
3093 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3096 case GIMP_LAYER_MODE_SCREEN:
3097 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3100 case GIMP_LAYER_MODE_OVERLAY:
3101 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3104 case GIMP_LAYER_MODE_DIFFERENCE:
3105 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3108 case GIMP_LAYER_MODE_DARKEN_ONLY:
3109 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3112 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3113 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3116 case GIMP_LAYER_MODE_DODGE:
3117 case GIMP_LAYER_MODE_DODGE_LEGACY:
3120 case GIMP_LAYER_MODE_BURN:
3121 case GIMP_LAYER_MODE_BURN_LEGACY:
3124 case GIMP_LAYER_MODE_HARDLIGHT:
3125 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
3128 case GIMP_LAYER_MODE_SOFTLIGHT:
3129 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
3132 case GIMP_LAYER_MODE_ADDITION:
3133 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3136 case GIMP_LAYER_MODE_EXCLUSION:
3141 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3142 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
3143 case GIMP_LAYER_MODE_GRAIN_MERGE:
3144 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
3145 case GIMP_LAYER_MODE_COLOR_ERASE:
3146 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
3147 case GIMP_LAYER_MODE_LCH_HUE:
3148 case GIMP_LAYER_MODE_LCH_CHROMA:
3149 case GIMP_LAYER_MODE_LCH_COLOR:
3150 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
3151 case GIMP_LAYER_MODE_BEHIND:
3152 case GIMP_LAYER_MODE_BEHIND_LEGACY:
3153 case GIMP_LAYER_MODE_SUBTRACT:
3154 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3155 case GIMP_LAYER_MODE_HSV_HUE:
3156 case GIMP_LAYER_MODE_HSV_SATURATION:
3157 case GIMP_LAYER_MODE_HSL_COLOR:
3158 case GIMP_LAYER_MODE_HSV_VALUE:
3159 case GIMP_LAYER_MODE_DIVIDE:
3160 case GIMP_LAYER_MODE_VIVID_LIGHT:
3161 case GIMP_LAYER_MODE_PIN_LIGHT:
3162 case GIMP_LAYER_MODE_LINEAR_LIGHT:
3163 case GIMP_LAYER_MODE_HARD_MIX:
3164 case GIMP_LAYER_MODE_LINEAR_BURN:
3165 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
3166 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
3167 case GIMP_LAYER_MODE_LUMINANCE:
3168 case GIMP_LAYER_MODE_ERASE:
3169 case GIMP_LAYER_MODE_MERGE:
3170 case GIMP_LAYER_MODE_SPLIT:
3171 case GIMP_LAYER_MODE_PASS_THROUGH:
3172 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
3173 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
3174 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
3175 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
3176 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3177 qCDebug(XCFPLUGIN) <<
"No QPainter equivalent to" << layer.mode;
3181 case GIMP_LAYER_MODE_DISSOLVE:
3182 case GIMP_LAYER_MODE_COUNT:
3186 if (painterMode != -1) {
3187 QPainter painter(&image);
3188 painter.setOpacity(layer.opacity / 255.0);
3190 qCDebug(XCFPLUGIN) <<
"Using QPainter for mode" << layer.mode;
3192 for (uint j = 0; j < layer.nrows; j++) {
3193 qint32 y = qint32(j * TILE_HEIGHT);
3195 for (uint i = 0; i < layer.ncols; i++) {
3196 qint32 x = qint32(i * TILE_WIDTH);
3198 QImage &tile = layer.image_tiles[j][i];
3199 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3200 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3201 painter.drawImage(x + layer.x_offset, y + layer.y_offset, tile);
3210#ifndef DISABLE_IMAGE_PROFILE_CONV
3212 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3216 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3221 for (uint j = 0; j < layer.nrows; j++) {
3222 qint32 y = qint32(j * TILE_HEIGHT);
3224 for (uint i = 0; i < layer.ncols; i++) {
3225 qint32 x = qint32(i * TILE_WIDTH);
3232 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
3233 if (!random_table_initialized) {
3234 initializeRandomTable();
3235 random_table_initialized =
true;
3237 if (layer.type == RGBA_GIMAGE) {
3238 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
3241 else if (layer.type == GRAYA_GIMAGE) {
3242 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
3247 if (merge == mergeRGBToRGB && layer.apply_mask != 1 && layer.mode == GIMP_LAYER_MODE_NORMAL_LEGACY) {
3248 QPainter painter(&image);
3249 painter.setOpacity(layer.opacity / 255.0);
3251 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3252 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3253 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
3258#ifndef DISABLE_TILE_PROFILE_CONV
3259 QImage &tile = layer.image_tiles[j][i];
3268 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
3269 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
3270 int m = x + k + layer.x_offset;
3271 int n = y + l + layer.y_offset;
3273 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
3277 if (!(*merge)(layer, i, j, k, l, image, m, n)) {
3299bool XCFImageFormat::mergeRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3301 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3302 QRgb dst = image.
pixel(m, n);
3304 uchar src_r = qRed(src);
3305 uchar src_g = qGreen(src);
3306 uchar src_b = qBlue(src);
3307 uchar src_a = qAlpha(src);
3309 uchar dst_r = qRed(dst);
3310 uchar dst_g = qGreen(dst);
3311 uchar dst_b = qBlue(dst);
3312 uchar dst_a = qAlpha(dst);
3318 switch (layer.mode) {
3319 case GIMP_LAYER_MODE_NORMAL:
3320 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3322 case GIMP_LAYER_MODE_MULTIPLY:
3323 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3324 src_r = INT_MULT(src_r, dst_r);
3325 src_g = INT_MULT(src_g, dst_g);
3326 src_b = INT_MULT(src_b, dst_b);
3327 src_a = qMin(src_a, dst_a);
3329 case GIMP_LAYER_MODE_DIVIDE:
3330 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3331 src_r = qMin((dst_r * 256) / (1 + src_r), 255);
3332 src_g = qMin((dst_g * 256) / (1 + src_g), 255);
3333 src_b = qMin((dst_b * 256) / (1 + src_b), 255);
3334 src_a = qMin(src_a, dst_a);
3336 case GIMP_LAYER_MODE_SCREEN:
3337 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3338 src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
3339 src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
3340 src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
3341 src_a = qMin(src_a, dst_a);
3343 case GIMP_LAYER_MODE_OVERLAY:
3344 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3345 src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
3346 src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
3347 src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
3348 src_a = qMin(src_a, dst_a);
3350 case GIMP_LAYER_MODE_DIFFERENCE:
3351 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3352 src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
3353 src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
3354 src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
3355 src_a = qMin(src_a, dst_a);
3357 case GIMP_LAYER_MODE_ADDITION:
3358 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3359 src_r = add_lut(dst_r, src_r);
3360 src_g = add_lut(dst_g, src_g);
3361 src_b = add_lut(dst_b, src_b);
3362 src_a = qMin(src_a, dst_a);
3364 case GIMP_LAYER_MODE_SUBTRACT:
3365 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3366 src_r = dst_r > src_r ? dst_r - src_r : 0;
3367 src_g = dst_g > src_g ? dst_g - src_g : 0;
3368 src_b = dst_b > src_b ? dst_b - src_b : 0;
3369 src_a = qMin(src_a, dst_a);
3371 case GIMP_LAYER_MODE_DARKEN_ONLY:
3372 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3373 src_r = dst_r < src_r ? dst_r : src_r;
3374 src_g = dst_g < src_g ? dst_g : src_g;
3375 src_b = dst_b < src_b ? dst_b : src_b;
3376 src_a = qMin(src_a, dst_a);
3378 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3379 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3380 src_r = dst_r < src_r ? src_r : dst_r;
3381 src_g = dst_g < src_g ? src_g : dst_g;
3382 src_b = dst_b < src_b ? src_b : dst_b;
3383 src_a = qMin(src_a, dst_a);
3385 case GIMP_LAYER_MODE_HSV_HUE:
3386 case GIMP_LAYER_MODE_HSV_HUE_LEGACY: {
3387 uchar new_r = dst_r;
3388 uchar new_g = dst_g;
3389 uchar new_b = dst_b;
3391 RGBTOHSV(src_r, src_g, src_b);
3392 RGBTOHSV(new_r, new_g, new_b);
3396 HSVTORGB(new_r, new_g, new_b);
3401 src_a = qMin(src_a, dst_a);
3403 case GIMP_LAYER_MODE_HSV_SATURATION:
3404 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: {
3405 uchar new_r = dst_r;
3406 uchar new_g = dst_g;
3407 uchar new_b = dst_b;
3409 RGBTOHSV(src_r, src_g, src_b);
3410 RGBTOHSV(new_r, new_g, new_b);
3414 HSVTORGB(new_r, new_g, new_b);
3419 src_a = qMin(src_a, dst_a);
3421 case GIMP_LAYER_MODE_HSV_VALUE:
3422 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY: {
3423 uchar new_r = dst_r;
3424 uchar new_g = dst_g;
3425 uchar new_b = dst_b;
3427 RGBTOHSV(src_r, src_g, src_b);
3428 RGBTOHSV(new_r, new_g, new_b);
3432 HSVTORGB(new_r, new_g, new_b);
3437 src_a = qMin(src_a, dst_a);
3439 case GIMP_LAYER_MODE_HSL_COLOR:
3440 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: {
3441 uchar new_r = dst_r;
3442 uchar new_g = dst_g;
3443 uchar new_b = dst_b;
3445 RGBTOHLS(src_r, src_g, src_b);
3446 RGBTOHLS(new_r, new_g, new_b);
3451 HLSTORGB(new_r, new_g, new_b);
3456 src_a = qMin(src_a, dst_a);
3458 case GIMP_LAYER_MODE_DODGE:
3459 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3464 src_r = (uchar)qMin(tmp, 255u);
3468 src_g = (uchar)qMin(tmp, 255u);
3472 src_b = (uchar)qMin(tmp, 255u);
3474 src_a = qMin(src_a, dst_a);
3476 case GIMP_LAYER_MODE_BURN:
3477 case GIMP_LAYER_MODE_BURN_LEGACY: {
3480 tmp = (255 - dst_r) << 8;
3482 src_r = (uchar)qMin(tmp, 255u);
3483 src_r = 255 - src_r;
3485 tmp = (255 - dst_g) << 8;
3487 src_g = (uchar)qMin(tmp, 255u);
3488 src_g = 255 - src_g;
3490 tmp = (255 - dst_b) << 8;
3492 src_b = (uchar)qMin(tmp, 255u);
3493 src_b = 255 - src_b;
3495 src_a = qMin(src_a, dst_a);
3497 case GIMP_LAYER_MODE_HARDLIGHT:
3498 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3501 tmp = ((int)255 - dst_r) * ((
int)255 - ((src_r - 128) << 1));
3502 src_r = (uchar)qMin(255 - (tmp >> 8), 255u);
3504 tmp = (int)dst_r * ((
int)src_r << 1);
3505 src_r = (uchar)qMin(tmp >> 8, 255u);
3509 tmp = ((int)255 - dst_g) * ((
int)255 - ((src_g - 128) << 1));
3510 src_g = (uchar)qMin(255 - (tmp >> 8), 255u);
3512 tmp = (int)dst_g * ((
int)src_g << 1);
3513 src_g = (uchar)qMin(tmp >> 8, 255u);
3517 tmp = ((int)255 - dst_b) * ((
int)255 - ((src_b - 128) << 1));
3518 src_b = (uchar)qMin(255 - (tmp >> 8), 255u);
3520 tmp = (int)dst_b * ((
int)src_b << 1);
3521 src_b = (uchar)qMin(tmp >> 8, 255u);
3523 src_a = qMin(src_a, dst_a);
3525 case GIMP_LAYER_MODE_SOFTLIGHT:
3526 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3530 tmpM = INT_MULT(dst_r, src_r);
3531 tmpS = 255 - INT_MULT((255 - dst_r), (255 - src_r));
3532 src_r = INT_MULT((255 - dst_r), tmpM) + INT_MULT(dst_r, tmpS);
3534 tmpM = INT_MULT(dst_g, src_g);
3535 tmpS = 255 - INT_MULT((255 - dst_g), (255 - src_g));
3536 src_g = INT_MULT((255 - dst_g), tmpM) + INT_MULT(dst_g, tmpS);
3538 tmpM = INT_MULT(dst_b, src_b);
3539 tmpS = 255 - INT_MULT((255 - dst_b), (255 - src_b));
3540 src_b = INT_MULT((255 - dst_b), tmpM) + INT_MULT(dst_b, tmpS);
3542 src_a = qMin(src_a, dst_a);
3544 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3545 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3548 tmp = dst_r - src_r + 128;
3549 tmp = qMin(tmp, 255);
3553 tmp = dst_g - src_g + 128;
3554 tmp = qMin(tmp, 255);
3558 tmp = dst_b - src_b + 128;
3559 tmp = qMin(tmp, 255);
3563 src_a = qMin(src_a, dst_a);
3565 case GIMP_LAYER_MODE_GRAIN_MERGE:
3566 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3569 tmp = dst_r + src_r - 128;
3570 tmp = qMin(tmp, 255);
3574 tmp = dst_g + src_g - 128;
3575 tmp = qMin(tmp, 255);
3579 tmp = dst_b + src_b - 128;
3580 tmp = qMin(tmp, 255);
3584 src_a = qMin(src_a, dst_a);
3586 case GIMP_LAYER_MODE_LINEAR_LIGHT: {
3588 src_r = qBound(0, dst_r + 2 * src_r - 255, 255);
3590 src_r = qBound(0, dst_r + 2 * (src_r - 128), 255);
3593 src_g = qBound(0, dst_g + 2 * src_g - 255, 255);
3595 src_g = qBound(0, dst_g + 2 * (src_g - 127), 255);
3598 src_b = qBound(0, dst_b + 2 * src_b - 255, 255);
3600 src_b = qBound(0, dst_b + 2 * (src_b - 127), 255);
3603 case GIMP_LAYER_MODE_VIVID_LIGHT: {
3606 A[0] = src_r / 255.;
3607 A[1] = src_g / 255.;
3608 A[2] = src_b / 255.;
3610 B[0] = dst_r / 255.;
3611 B[1] = dst_g / 255.;
3612 B[2] = dst_b / 255.;
3614 for (
int i = 0; i < 3; i++) {
3617 C[i] = 1.f - (1.f - B[i]) / (2.f * A[i]);
3621 C[i] = B[i] / (2.f * (1.f - A[i]));
3625 src_r = qBound(0.f, C[0] * 255.f, 255.f);
3626 src_g = qBound(0.f, C[1] * 255.f, 255.f);
3627 src_b = qBound(0.f, C[2] * 255.f, 255.f);
3630 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3634 src_a = INT_MULT(src_a, layer.opacity);
3638 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3639 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3646 new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3648 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3649 float dst_ratio = 1.0 - src_ratio;
3651 new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
3652 new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
3653 new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
3655 if (!modeAffectsSourceAlpha(layer.mode)) {
3659 image.
setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
3674bool XCFImageFormat::mergeGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3676 int src = layer.image_tiles[j][i].pixelIndex(k, l);
3692bool XCFImageFormat::mergeGrayAToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3694 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3697 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3703 switch (layer.mode) {
3704 case GIMP_LAYER_MODE_MULTIPLY:
3705 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3706 src = INT_MULT(src, dst);
3708 case GIMP_LAYER_MODE_DIVIDE:
3709 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3710 src = qMin((dst * 256) / (1 + src), 255);
3712 case GIMP_LAYER_MODE_SCREEN:
3713 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3714 src = 255 - INT_MULT(255 - dst, 255 - src);
3716 case GIMP_LAYER_MODE_OVERLAY:
3717 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3718 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3720 case GIMP_LAYER_MODE_DIFFERENCE:
3721 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3722 src = dst > src ? dst - src : src - dst;
3724 case GIMP_LAYER_MODE_ADDITION:
3725 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3726 src = add_lut(dst, src);
3728 case GIMP_LAYER_MODE_SUBTRACT:
3729 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3730 src = dst > src ? dst - src : 0;
3732 case GIMP_LAYER_MODE_DARKEN_ONLY:
3733 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3734 src = dst < src ? dst : src;
3736 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3737 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3738 src = dst < src ? src : dst;
3740 case GIMP_LAYER_MODE_DODGE:
3741 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3742 uint tmp = dst << 8;
3744 src = (uchar)qMin(tmp, 255u);
3746 case GIMP_LAYER_MODE_BURN:
3747 case GIMP_LAYER_MODE_BURN_LEGACY: {
3748 uint tmp = (255 - dst) << 8;
3750 src = (uchar)qMin(tmp, 255u);
3753 case GIMP_LAYER_MODE_HARDLIGHT:
3754 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3757 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3758 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3760 tmp = (int)dst * ((
int)src << 1);
3761 src = (uchar)qMin(tmp >> 8, 255u);
3764 case GIMP_LAYER_MODE_SOFTLIGHT:
3765 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3769 tmpM = INT_MULT(dst, src);
3770 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3771 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3774 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3775 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3778 tmp = dst - src + 128;
3779 tmp = qMin(tmp, 255);
3784 case GIMP_LAYER_MODE_GRAIN_MERGE:
3785 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3788 tmp = dst + src - 128;
3789 tmp = qMin(tmp, 255);
3795 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3799 src_a = INT_MULT(src_a, layer.opacity);
3803 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3804 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3807 uchar new_a = OPAQUE_OPACITY;
3809 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3810 float dst_ratio = 1.0 - src_ratio;
3812 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3831bool XCFImageFormat::mergeGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3833 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3834 uchar src_a = layer.opacity;
3835 image.
setPixel(m, n, qRgba(src, src_a));
3852bool XCFImageFormat::mergeGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3854 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3855 int dst = qGray(image.
pixel(m, n));
3857 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3858 uchar dst_a = qAlpha(image.
pixel(m, n));
3864 switch (layer.mode) {
3865 case GIMP_LAYER_MODE_NORMAL:
3866 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3868 case GIMP_LAYER_MODE_MULTIPLY:
3869 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3870 src = INT_MULT(src, dst);
3871 src_a = qMin(src_a, dst_a);
3873 case GIMP_LAYER_MODE_DIVIDE:
3874 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3875 src = qMin((dst * 256) / (1 + src), 255);
3876 src_a = qMin(src_a, dst_a);
3878 case GIMP_LAYER_MODE_SCREEN:
3879 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3880 src = 255 - INT_MULT(255 - dst, 255 - src);
3881 src_a = qMin(src_a, dst_a);
3883 case GIMP_LAYER_MODE_OVERLAY:
3884 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3885 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3886 src_a = qMin(src_a, dst_a);
3888 case GIMP_LAYER_MODE_DIFFERENCE:
3889 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3890 src = dst > src ? dst - src : src - dst;
3891 src_a = qMin(src_a, dst_a);
3893 case GIMP_LAYER_MODE_ADDITION:
3894 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3895 src = add_lut(dst, src);
3896 src_a = qMin(src_a, dst_a);
3898 case GIMP_LAYER_MODE_SUBTRACT:
3899 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3900 src = dst > src ? dst - src : 0;
3901 src_a = qMin(src_a, dst_a);
3903 case GIMP_LAYER_MODE_DARKEN_ONLY:
3904 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3905 src = dst < src ? dst : src;
3906 src_a = qMin(src_a, dst_a);
3908 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3909 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3910 src = dst < src ? src : dst;
3911 src_a = qMin(src_a, dst_a);
3913 case GIMP_LAYER_MODE_DODGE:
3914 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3915 uint tmp = dst << 8;
3917 src = (uchar)qMin(tmp, 255u);
3918 src_a = qMin(src_a, dst_a);
3920 case GIMP_LAYER_MODE_BURN:
3921 case GIMP_LAYER_MODE_BURN_LEGACY: {
3922 uint tmp = (255 - dst) << 8;
3924 src = (uchar)qMin(tmp, 255u);
3926 src_a = qMin(src_a, dst_a);
3928 case GIMP_LAYER_MODE_HARDLIGHT:
3929 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3932 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3933 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3935 tmp = (int)dst * ((
int)src << 1);
3936 src = (uchar)qMin(tmp >> 8, 255u);
3938 src_a = qMin(src_a, dst_a);
3940 case GIMP_LAYER_MODE_SOFTLIGHT:
3941 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3945 tmpM = INT_MULT(dst, src);
3946 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3947 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3949 src_a = qMin(src_a, dst_a);
3951 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3952 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3955 tmp = dst - src + 128;
3956 tmp = qMin(tmp, 255);
3960 src_a = qMin(src_a, dst_a);
3962 case GIMP_LAYER_MODE_GRAIN_MERGE:
3963 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3966 tmp = dst + src - 128;
3967 tmp = qMin(tmp, 255);
3971 src_a = qMin(src_a, dst_a);
3974 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3978 src_a = INT_MULT(src_a, layer.opacity);
3981 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3982 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3985 uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3987 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3988 float dst_ratio = 1.0 - src_ratio;
3990 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3992 if (!modeAffectsSourceAlpha(layer.mode)) {
3996 image.
setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
4011bool XCFImageFormat::mergeIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4013 int src = layer.image_tiles[j][i].pixelIndex(k, l);
4029bool XCFImageFormat::mergeIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4031 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
4032 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4033 src_a = INT_MULT(src_a, layer.opacity);
4035 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4036 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4059bool XCFImageFormat::mergeIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4061 QRgb src = layer.image_tiles[j][i].pixel(k, l);
4062 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4063 src_a = INT_MULT(src_a, layer.opacity);
4066 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4067 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4074 src_a = OPAQUE_OPACITY;
4077 image.
setPixel(m, n, qRgba(src, src_a));
4088void XCFImageFormat::dissolveRGBPixels(
QImage &image,
int x,
int y)
4093 for (
int l = 0; l < image.
height(); l++) {
4094 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4096 for (
int k = 0; k < x; k++) {
4097 RandomTable::rand_r(&next);
4100 for (
int k = 0; k < image.
width(); k++) {
4101 int rand_val = RandomTable::rand_r(&next) & 0xff;
4102 QRgb pixel = image.
pixel(k, l);
4104 if (rand_val > qAlpha(pixel)) {
4105 image.
setPixel(k, l, qRgba(pixel, 0));
4120void XCFImageFormat::dissolveAlphaPixels(
QImage &image,
int x,
int y)
4125 for (
int l = 0; l < image.
height(); l++) {
4126 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4128 for (
int k = 0; k < x; k++) {
4129 RandomTable::rand_r(&next);
4132 for (
int k = 0; k < image.
width(); k++) {
4133 int rand_val = RandomTable::rand_r(&next) & 0xff;
4136 if (rand_val > alpha) {
4145XCFHandler::XCFHandler()
4149bool XCFHandler::canRead()
const
4151 if (canRead(device())) {
4158bool XCFHandler::read(
QImage *image)
4160 XCFImageFormat xcfif;
4161 auto ok = xcfif.readXCF(device(), image);
4162 m_imageSize = image->
size();
4166bool XCFHandler::write(
const QImage &)
4171bool XCFHandler::supportsOption(ImageOption option)
const
4178QVariant XCFHandler::option(ImageOption option)
const
4183 if (!m_imageSize.isEmpty()) {
4198 else if (
auto d = device()) {
4200 d->startTransaction();
4201 auto ba9 = d->read(9);
4202 auto ba5 = d->read(4+1);
4203 auto ba = d->read(8);
4204 d->rollbackTransaction();
4220bool XCFHandler::canRead(
QIODevice *device)
4223 qCDebug(XCFPLUGIN) <<
"XCFHandler::canRead() called with no device";
4230 const qint64 oldPos = device->
pos();
4233 XCFImageFormat::XCFImage::Header header;
4234 bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
4235 ds.setDevice(
nullptr);
4237 device->
seek(oldPos);
4242 switch (header.precision) {
4243 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
4244 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
4245 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
4246 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
4247 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
4248 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
4249 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
4250 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
4251 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
4252 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
4253 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
4254 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
4255 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
4256 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
4257 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
4259 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
4260 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
4261 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
4263 qCDebug(XCFPLUGIN) <<
"unsupported precision" << header.precision;
4272 if (format ==
"xcf") {
4283 if (device->
isReadable() && XCFHandler::canRead(device)) {
4300#include "moc_xcf_p.cpp"
Type type(const QSqlDatabase &db)
QFlags< Capability > Capabilities
QStringView merge(QStringView lhs, QStringView rhs)
QString name(StandardAction id)
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
bool isEmpty() const const
qsizetype size() const const
QColorSpace fromIccProfile(const QByteArray &iccProfile)
bool isValid() const const
QIODevice * device() const const
int readRawData(char *s, int len)
int version() const const
qsizetype bytesPerLine() const const
int colorCount() const const
QColorSpace colorSpace() const const
void convertToColorSpace(const QColorSpace &colorSpace)
void fill(Qt::GlobalColor color)
bool hasAlphaChannel() const const
bool isNull() const const
QRgb pixel(const QPoint &position) const const
int pixelIndex(const QPoint &position) const const
void setColorCount(int colorCount)
void setColorSpace(const QColorSpace &colorSpace)
void setColorTable(const QList< QRgb > &colors)
void setDotsPerMeterX(int x)
void setDotsPerMeterY(int y)
void setPixel(const QPoint &position, uint index_or_rgb)
void setText(const QString &key, const QString &text)
void setDevice(QIODevice *device)
virtual bool atEnd() const const
bool isOpen() const const
bool isReadable() const const
virtual bool isSequential() const const
virtual qint64 pos() const const
virtual bool seek(qint64 pos)
bool isEmpty() const const
void resize(qsizetype size)
qsizetype size() const const
void setAlpha(quint16 alpha)
QString fromUtf8(QByteArrayView str)
QVariant fromValue(T &&value)