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()) {
965#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
971 property.readBytes(tag, size);
975 property >> flags >> data;
987 property >> xcf_image.unit;
997 property >> xcf_image.num_colors;
998 if (xcf_image.num_colors < 0 || xcf_image.num_colors > 65535) {
1002 xcf_image.palette = QList<QRgb>();
1003 xcf_image.palette.reserve(xcf_image.num_colors);
1005 for (
int i = 0; i < xcf_image.num_colors; i++) {
1009 property >> r >> g >> b;
1010 xcf_image.palette.push_back(qRgb(r, g, b));
1015 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented image property" <<
type <<
"(" << rawType <<
")"
1016 <<
", size " << bytes.
size();
1029bool XCFImageFormat::loadProperty(
QDataStream &xcf_io, PropType &type,
QByteArray &bytes, quint32 &rawType)
1034 if (rawType >= MAX_SUPPORTED_PROPTYPE) {
1035 type = MAX_SUPPORTED_PROPTYPE;
1044 type = PropType(rawType);
1046 char *data =
nullptr;
1052 if (type == PROP_COLORMAP) {
1057 size = 3 * ncolors + 4;
1059 if (size > 65535 || size < 4) {
1063 data =
new char[size];
1068 data[2] = ncolors >> 8;
1069 data[3] = ncolors & 255;
1073 }
else if (type == PROP_USER_UNIT) {
1078 xcf_io >> size >> factor >> digits;
1080 for (
int i = 0; i < 5; i++) {
1083 xcf_io >> unit_strings;
1085 delete[] unit_strings;
1088 qCDebug(XCFPLUGIN) <<
"XCF: read failure on property " <<
type;
1096 if (size > 256000 * 4) {
1098 qCDebug(XCFPLUGIN) <<
"XCF: loadProperty skips" <<
type <<
"due to size being too large";
1101 data =
new char[size];
1102 const quint32 dataRead = xcf_io.
readRawData(data, size);
1103 if (dataRead < size) {
1104 qCDebug(XCFPLUGIN) <<
"XCF: loadProperty read less data than expected" << size << dataRead;
1105 memset(&data[dataRead], 0, size - dataRead);
1109 if (size != 0 && data) {
1110 bytes = QByteArray(data, size);
1126bool XCFImageFormat::loadLayer(
QDataStream &xcf_io, XCFImage &xcf_image)
1128 Layer &layer(xcf_image.layer);
1129 delete[] layer.name;
1131 xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
1134 layer.compression = XcfCompressionType(xcf_image.compression);
1136 if (!loadLayerProperties(xcf_io, layer)) {
1140 qCDebug(XCFPLUGIN) <<
"layer: \"" << layer.name <<
"\", size: " << layer.width <<
" x " << layer.height <<
", type: " << layer.type
1141 <<
", mode: " << layer.mode <<
", opacity: " << layer.opacity <<
", visible: " << layer.visible <<
", offset: " << layer.x_offset <<
", "
1142 << layer.y_offset <<
", compression" << layer.compression;
1148 if (layer.visible == 0) {
1154 layer.hierarchy_offset = readOffsetPtr(xcf_io);
1155 layer.mask_offset = readOffsetPtr(xcf_io);
1157 if (layer.hierarchy_offset < 0) {
1158 qCDebug(XCFPLUGIN) <<
"XCF: negative layer hierarchy_offset";
1162 if (layer.mask_offset < 0) {
1163 qCDebug(XCFPLUGIN) <<
"XCF: negative layer mask_offset";
1170 if (!composeTiles(xcf_image)) {
1173 xcf_io.
device()->
seek(layer.hierarchy_offset);
1179 layer.assignBytes = assignImageBytes;
1181 if (!loadHierarchy(xcf_io, layer, xcf_image.header.precision)) {
1185 if (layer.mask_offset != 0) {
1187 if (layer.apply_mask == 9) {
1188 layer.apply_mask = 1;
1193 if (!loadMask(xcf_io, layer, xcf_image.header.precision)) {
1198 layer.apply_mask = 0;
1205 if (!xcf_image.initialized) {
1206 if (!initializeImage(xcf_image)) {
1209 copyLayerToImage(xcf_image);
1210 xcf_image.initialized =
true;
1212 const QColorSpace colorspaceBefore = xcf_image.image.colorSpace();
1213 mergeLayerIntoImage(xcf_image);
1214 if (xcf_image.image.colorSpace() != colorspaceBefore) {
1215 qCDebug(XCFPLUGIN) <<
"Converting color space back to" << colorspaceBefore <<
"after layer composition";
1216 xcf_image.image.convertToColorSpace(colorspaceBefore);
1230bool XCFImageFormat::loadLayerProperties(
QDataStream &xcf_io, Layer &layer)
1237 if (!loadProperty(xcf_io, type, bytes, rawType)) {
1238 qCDebug(XCFPLUGIN) <<
"XCF: error loading layer properties";
1242 QDataStream property(bytes);
1248 case PROP_ACTIVE_LAYER:
1249 layer.active =
true;
1253 property >> layer.opacity;
1254 layer.opacity = std::min(layer.opacity, 255u);
1257 case PROP_FLOAT_OPACITY:
1260 if (bytes.
size() == 4) {
1261 layer.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
1263 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
1268 property >> layer.visible;
1272 property >> layer.linked;
1275 case PROP_LOCK_ALPHA:
1276 property >> layer.preserve_transparency;
1279 case PROP_APPLY_MASK:
1280 property >> layer.apply_mask;
1283 case PROP_EDIT_MASK:
1284 property >> layer.edit_mask;
1287 case PROP_SHOW_MASK:
1288 property >> layer.show_mask;
1292 property >> layer.x_offset >> layer.y_offset;
1296 property >> layer.mode;
1297 if (layer.mode >= GIMP_LAYER_MODE_COUNT) {
1298 qCDebug(XCFPLUGIN) <<
"Found layer with unsupported mode" << LayerModeType(layer.mode) <<
"Defaulting to mode 0";
1299 layer.mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
1304 property >> layer.tattoo;
1307 case PROP_COMPOSITE_SPACE:
1308 property >> layer.compositeSpace;
1309 if (layer.compositeSpace < 0) {
1310 layer.compositeSpace = GimpColorSpace(layer.compositeSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeSpace);
1314 case PROP_COMPOSITE_MODE:
1315 property >> layer.compositeMode;
1316 if (layer.compositeMode < 0) {
1317 layer.compositeMode =
1318 XCFImageFormat::GimpCompositeMode(layer.compositeMode == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeMode);
1322 case PROP_BLEND_SPACE:
1323 property >> layer.blendSpace;
1324 if (layer.blendSpace < 0) {
1325 layer.blendSpace = GimpColorSpace(layer.blendSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.blendSpace);
1330 case PROP_COLOR_TAG:
1334 case PROP_LOCK_CONTENT:
1335 case PROP_LOCK_POSITION:
1339 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented layer property " <<
type <<
"(" << rawType <<
")"
1340 <<
", size " << bytes.
size();
1351bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
1353 Layer &layer(xcf_image.layer);
1355 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
1356 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
1358 qCDebug(XCFPLUGIN) <<
"IMAGE: height=" << xcf_image.header.height <<
", width=" << xcf_image.header.width;
1359 qCDebug(XCFPLUGIN) <<
"LAYER: height=" << layer.height <<
", width=" << layer.width;
1360 qCDebug(XCFPLUGIN) <<
"LAYER: rows=" << layer.nrows <<
", columns=" << layer.ncols;
1367 if ((
sizeof(
void *) == 4 && qint64(layer.width) * layer.height > 16384 * 16384)) {
1368 qCWarning(XCFPLUGIN) <<
"On 32-bits programs the maximum layer size is limited to" << 16384 <<
"x" << 16384 <<
"px";
1371 if (layer.width > MAX_IMAGE_WIDTH || layer.height > MAX_IMAGE_HEIGHT) {
1372 qCWarning(XCFPLUGIN) <<
"The maximum layer size is limited to" << MAX_IMAGE_WIDTH <<
"x" << MAX_IMAGE_HEIGHT <<
"px";
1379 if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
1380 if (qint64(layer.width) * layer.height > 16384 * 16384) {
1381 qCWarning(XCFPLUGIN) <<
"Euristic sanity check: the image may be corrupted!";
1386#ifndef XCF_QT5_SUPPORT
1391 qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
1398 layer.image_tiles.resize(layer.nrows);
1400 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1401 layer.alpha_tiles.resize(layer.nrows);
1404 if (layer.mask_offset != 0) {
1405 layer.mask_tiles.resize(layer.nrows);
1408 for (uint j = 0; j < layer.nrows; j++) {
1409 layer.image_tiles[j].resize(layer.ncols);
1411 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1412 layer.alpha_tiles[j].resize(layer.ncols);
1415 if (layer.mask_offset != 0) {
1416 layer.mask_tiles[j].resize(layer.ncols);
1420 const QImage::Format format = layer.qimageFormat(xcf_image.header.precision);
1422 for (uint j = 0; j < layer.nrows; j++) {
1423 for (uint i = 0; i < layer.ncols; i++) {
1424 uint tile_width = (i + 1) * TILE_WIDTH <= layer.width ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
1426 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
1431 switch (layer.type) {
1434 layer.image_tiles[j][i] = QImage(tile_width, tile_height, format);
1435 if (layer.image_tiles[j][i].isNull()) {
1438 layer.image_tiles[j][i].setColorCount(0);
1443 if (layer.image_tiles[j][i].isNull()) {
1446 layer.image_tiles[j][i].setColorCount(256);
1447 setGrayPalette(layer.image_tiles[j][i]);
1452 layer.image_tiles[j][i].setColorCount(256);
1453 if (layer.image_tiles[j][i].isNull()) {
1456 setGrayPalette(layer.image_tiles[j][i]);
1459 if (layer.alpha_tiles[j][i].isNull()) {
1462 layer.alpha_tiles[j][i].setColorCount(256);
1463 setGrayPalette(layer.alpha_tiles[j][i]);
1466 case INDEXED_GIMAGE:
1468 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1469 if (layer.image_tiles[j][i].isNull()) {
1472 setPalette(xcf_image, layer.image_tiles[j][i]);
1475 case INDEXEDA_GIMAGE:
1477 if (layer.image_tiles[j][i].isNull()) {
1480 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1481 setPalette(xcf_image, layer.image_tiles[j][i]);
1484 if (layer.alpha_tiles[j][i].isNull()) {
1487 layer.alpha_tiles[j][i].setColorCount(256);
1488 setGrayPalette(layer.alpha_tiles[j][i]);
1490 if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
1491 qCWarning(XCFPLUGIN) <<
"Selected wrong tile format" << layer.image_tiles[j][i].format() <<
"expected" << format;
1495#ifndef DISABLE_TILE_PROFILE
1496 switch (xcf_image.header.precision) {
1497 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1498 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1499 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1500 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1501 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1502 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1505 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
1506 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
1507 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
1508 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
1509 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
1510 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
1513 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
1514 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
1515 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
1516 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
1517 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
1518 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
1523 if (layer.mask_offset != 0) {
1525 layer.mask_tiles[j][i].setColorCount(256);
1526 if (layer.mask_tiles[j][i].isNull()) {
1529 setGrayPalette(layer.mask_tiles[j][i]);
1542void XCFImageFormat::setGrayPalette(
QImage &image)
1544 if (grayTable.isEmpty()) {
1545 grayTable.resize(256);
1547 for (
int i = 0; i < 256; i++) {
1548 grayTable[i] = qRgb(i, i, i);
1560void XCFImageFormat::setPalette(XCFImage &xcf_image,
QImage &image)
1562 Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size());
1573void XCFImageFormat::setImageParasites(
const XCFImage &xcf_image,
QImage &image)
1575 auto&& p = xcf_image.parasites;
1576 auto keys = p.keys();
1577 for (
auto &&key : std::as_const(keys)) {
1578 auto value = p.value(key);
1579 if (value.isEmpty())
1588 if (key == QStringLiteral(
"icc-profile")) {
1600 if (key == QStringLiteral(
"gimp-comment")) {
1601 value.replace(
'\0', QByteArray());
1609 if (key == QStringLiteral(
"gimp-image-metadata")) {
1612 value.replace(
'\0', QByteArray());
1624 if (key == QStringLiteral(
"gimp-metadata")) {
1628 value.replace(
'\0', QByteArray());
1635#ifdef DISABLE_IMAGE_PROFILE
1638 switch (xcf_image.header.precision) {
1639 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1640 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1641 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1642 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1643 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1644 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1662bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
1664 QImage &image = layer.image_tiles[j][i];
1666 const uchar *tile = layer.tile;
1667 const int width = image.
width();
1668 const int height = image.
height();
1670 uchar *bits = image.
bits();
1673 if (layer.type == GRAYA_GIMAGE || layer.type == GRAY_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1674 auto bpc = bytesPerChannel(precision);
1675 for (
int y = 0; y < height; y++) {
1676 uchar *dataPtr = bits + y * bytesPerLine;
1677 uchar *alphaPtr =
nullptr;
1678 if (layer.alpha_tiles.size() > j && layer.alpha_tiles.at(j).size() > i) {
1679 QImage &alphaTile = layer.alpha_tiles[j][i];
1680 if (alphaTile.
width() >= width && alphaTile.
height() > y) {
1685#ifdef USE_FLOAT_IMAGES
1686 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1687 for (
int x = 0; x < width; x++) {
1688 auto src =
reinterpret_cast<const quint16 *
>(tile);
1689 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1691 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1692 tile +=
sizeof(quint16) * 2;
1694 tile +=
sizeof(quint16);
1698 for (
int x = 0; x < width; x++) {
1699 auto src =
reinterpret_cast<const float *
>(tile);
1700 *dataPtr++ = qFromBigEndian<float>(src[0]) * 255;
1702 *alphaPtr++ = qFromBigEndian<float>(src[1]) * 255;
1703 tile +=
sizeof(float) * 2;
1705 tile +=
sizeof(float);
1710 for (
int x = 0; x < width; x++) {
1711 auto src = (
const quint16 *)tile;
1712 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1714 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1715 tile +=
sizeof(quint16) * 2;
1717 tile +=
sizeof(quint16);
1721 }
else if (bpc == 2) {
1722#ifdef USE_FLOAT_IMAGES
1723 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1724 for (
int x = 0; x < width; x++) {
1725 auto src =
reinterpret_cast<const quint16 *
>(tile);
1726 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1728 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1729 tile +=
sizeof(QRgb);
1732 for (
int x = 0; x < width; x++) {
1733 auto src =
reinterpret_cast<const qfloat16 *
>(tile);
1734 *dataPtr++ = qFromBigEndian<qfloat16>(src[0]) * 255;
1736 *alphaPtr++ = qFromBigEndian<qfloat16>(src[1]) * 255;
1737 tile +=
sizeof(QRgb);
1741 for (
int x = 0; x < width; x++) {
1742 auto src =
reinterpret_cast<const quint16 *
>(tile);
1743 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1745 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1746 tile +=
sizeof(QRgb);
1750 for (
int x = 0; x < width; x++) {
1752 *dataPtr++ = tile[0];
1754 *alphaPtr++ = tile[1];
1755 tile +=
sizeof(QRgb);
1762 switch (image.
format()) {
1764 for (
int y = 0; y < height; y++) {
1765 uchar *dataPtr = image.
scanLine(y);
1766 for (
int x = 0; x < width * 4; x += 4, tile += 4) {
1767 dataPtr[x + 0] = tile[0];
1768 dataPtr[x + 1] = tile[1];
1769 dataPtr[x + 2] = tile[2];
1770 dataPtr[x + 3] = 255;
1775 for (
int y = 0; y < height; y++) {
1776 const size_t bpl = width * 4;
1777 memcpy(image.
scanLine(y), tile + y * bpl, bpl);
1781 for (
int y = 0; y < height; y++) {
1782 quint16 *dataPtr =
reinterpret_cast<quint16 *
>(image.
scanLine(y));
1783 const size_t bpl = width *
sizeof(QRgba64);
1784 const quint16 *src =
reinterpret_cast<const quint16 *
>(tile + y * bpl);
1785 for (
int x = 0; x < width * 4; x += 4) {
1786 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1787 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1788 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1789 dataPtr[x + 3] = 65535;
1793#ifdef USE_FLOAT_IMAGES
1795 for (
int y = 0; y < height; y++) {
1796 qfloat16 *dataPtr =
reinterpret_cast<qfloat16 *
>(image.
scanLine(y));
1797 const qfloat16 *src =
reinterpret_cast<const qfloat16 *
>(tile + y * width *
sizeof(QRgbaFloat16));
1798 for (
int x = 0; x < width * 4; x += 4) {
1799 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1800 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1801 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1802 dataPtr[x + 3] = qfloat16(1);
1807 static_assert(
sizeof(QRgbaFloat16) ==
sizeof(QRgba64),
"Different sizes for float and int 16 bit pixels");
1810 for (
int y = 0; y < height; y++) {
1811 const size_t bpl = width *
sizeof(QRgba64);
1812 qFromBigEndian<qint16>(tile + y * bpl, width * 4, image.
scanLine(y));
1815#ifdef USE_FLOAT_IMAGES
1817 for (
int y = 0; y < height; y++) {
1818 const size_t bpl = width *
sizeof(QRgbaFloat32);
1819 qFromBigEndian<qint32>(tile + y * bpl, width * 4, image.
scanLine(y));
1823 for (
int y = 0; y < height; y++) {
1824 float *dataPtr =
reinterpret_cast<float *
>(image.
scanLine(y));
1825 const float *src =
reinterpret_cast<const float *
>(tile + y * width *
sizeof(QRgbaFloat32));
1826 for (
int x = 0; x < width * 4; x += 4) {
1827 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1828 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1829 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1830 dataPtr[x + 3] = 1.f;
1836 for (
int y = 0; y < height; y++) {
1837 uchar *dataPtr = bits + y * bytesPerLine;
1838 for (
int x = 0; x < width; x++) {
1839 *dataPtr++ = tile[0];
1840 tile +=
sizeof(QRgb);
1845 qCWarning(XCFPLUGIN) <<
"Unhandled image format" << image.
format() <<
"and/or layer type" << layer.type;
1860bool XCFImageFormat::loadHierarchy(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
1866 xcf_io >> width >> height >> bpp;
1867 const qint64 offset = readOffsetPtr(xcf_io);
1869 qCDebug(XCFPLUGIN) <<
"width" << width <<
"height" << height <<
"bpp" << bpp <<
"offset" << offset;
1872 qCDebug(XCFPLUGIN) <<
"XCF: negative hierarchy offset";
1876 const bool isMask = layer.assignBytes == assignMaskBytes;
1879 switch (layer.type) {
1881 if (bpp != 3 * bytesPerChannel(precision)) {
1882 qCDebug(XCFPLUGIN) <<
"Found layer of type RGB but with bpp != 3" << bpp;
1890 if (bpp != 4 * bytesPerChannel(precision)) {
1891 qCDebug(XCFPLUGIN) <<
"Found layer of type RGBA but with bpp != 4, got" << bpp <<
"bpp";
1899 if (bpp != 1 * bytesPerChannel(precision)) {
1900 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray but with bpp != 1" << bpp;
1905 if (bpp != 2 * bytesPerChannel(precision)) {
1906 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray+Alpha but with bpp != 2" << bpp;
1913 case INDEXED_GIMAGE:
1914 if (bpp != 1 * bytesPerChannel(precision)) {
1915 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed but with bpp != 1" << bpp;
1919 case INDEXEDA_GIMAGE:
1920 if (bpp != 2 * bytesPerChannel(precision)) {
1921 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed+Alpha but with bpp != 2" << bpp;
1930 if (bpp > 4 * bytesPerChannel(precision)) {
1931 qCDebug(XCFPLUGIN) <<
"bpp is" << bpp <<
"We don't support layers with bpp > 4";
1944 qCDebug(XCFPLUGIN) <<
"XCF: read failure on layer " << layer.name <<
" level offsets";
1947 }
while (junk != 0);
1949 qint64 saved_pos = xcf_io.
device()->
pos();
1952 if (!loadLevel(xcf_io, layer, bpp, precision)) {
1960template<
typename SourceFormat>
1961static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
1963 SourceFormat *source = (SourceFormat *)(input);
1964 for (quint64 offset = 0; offset < outputSize; offset++) {
1965 (
reinterpret_cast<uint16_t *
>(output))[offset] = qToBigEndian(quint16(qBound(0., qFromBigEndian<SourceFormat>(source[offset]) * 65535. + 0.5, 65535.)));
1978bool XCFImageFormat::loadLevel(
QDataStream &xcf_io, Layer &layer, qint32 bpp,
const GimpPrecision precision)
1980 auto bpc = bytesPerChannel(precision);
1981 if ((bpc == 0) || (bpp % bpc)) {
1982 qCDebug(XCFPLUGIN) <<
"XCF: the stream seems corrupted";
1989 xcf_io >> width >> height;
1990 qint64 offset = readOffsetPtr(xcf_io);
1993 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2000 for (uint j = 0; j < layer.nrows; j++) {
2001 for (uint i = 0; i < layer.ncols; i++) {
2003 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
2011 bool needConvert =
true;
2012 switch (precision) {
2013#ifdef USE_FLOAT_IMAGES
2014 case GIMP_PRECISION_HALF_LINEAR:
2015 case GIMP_PRECISION_HALF_NON_LINEAR:
2016 case GIMP_PRECISION_HALF_PERCEPTUAL:
2017 case GIMP_PRECISION_FLOAT_LINEAR:
2018 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2019 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2021 case GIMP_PRECISION_U8_LINEAR:
2022 case GIMP_PRECISION_U8_NON_LINEAR:
2023 case GIMP_PRECISION_U8_PERCEPTUAL:
2024 case GIMP_PRECISION_U16_LINEAR:
2025 case GIMP_PRECISION_U16_NON_LINEAR:
2026 case GIMP_PRECISION_U16_PERCEPTUAL:
2027 needConvert =
false;
2033 const uint blockSize = TILE_WIDTH * TILE_HEIGHT * bpp * 1.5;
2035 QList<uchar> buffer;
2037 buffer.
resize(blockSize * (bpp == 2 ? 2 : 1));
2039 for (uint j = 0; j < layer.nrows; j++) {
2040 for (uint i = 0; i < layer.ncols; i++) {
2042 qCDebug(XCFPLUGIN) <<
"XCF: incorrect number of tiles in layer " << layer.name;
2046 qint64 saved_pos = xcf_io.
device()->
pos();
2047 qint64 offset2 = readOffsetPtr(xcf_io);
2050 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2056 offset2 = offset + blockSize;
2060 qint64 bytesParsed = 0;
2062 switch (layer.compression) {
2063 case COMPRESS_NONE: {
2064 if (xcf_io.
version() > 11 ||
size_t(bpp) >
sizeof(QRgba64)) {
2065 qCDebug(XCFPLUGIN) <<
"Component reading not supported yet";
2068 const int data_size = bpp * TILE_WIDTH * TILE_HEIGHT;
2069 if (data_size >
int(blockSize)) {
2070 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2073 int dataRead = xcf_io.
readRawData(
reinterpret_cast<char *
>(layer.tile), data_size);
2074 if (dataRead < data_size) {
2075 qCDebug(XCFPLUGIN) <<
"short read, expected" << data_size <<
"got" << dataRead;
2078 bytesParsed = dataRead;
2081 case COMPRESS_RLE: {
2082 int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
2083 const uint data_size = size * bpp;
2085 if (data_size >=
unsigned(buffer.
size())) {
2086 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" << buffer.
size() <<
"but need" << data_size;
2090 if (data_size >
sizeof(layer.tile)) {
2091 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2094 if (blockSize >
sizeof(layer.tile)) {
2095 qCWarning(XCFPLUGIN) <<
"Too small tiles" <<
sizeof(layer.tile) <<
"this image requires" << blockSize <<
sizeof(QRgba64) << bpp;
2099 if (!loadTileRLE(xcf_io, needConvert ? buffer.
data() : layer.tile, size, offset2 - offset, bpp, &bytesParsed)) {
2100 qCDebug(XCFPLUGIN) <<
"Failed to read RLE";
2106 qCDebug(XCFPLUGIN) <<
"Unhandled compression" << layer.compression;
2111 if (bytesParsed > buffer.
size()) {
2112 qCDebug(XCFPLUGIN) <<
"Invalid number of bytes parsed" << bytesParsed << buffer.
size();
2116 switch (precision) {
2117 case GIMP_PRECISION_U32_LINEAR:
2118 case GIMP_PRECISION_U32_NON_LINEAR:
2119 case GIMP_PRECISION_U32_PERCEPTUAL: {
2120 quint32 *source =
reinterpret_cast<quint32 *
>(buffer.
data());
2121 for (quint64 offset = 0, len = buffer.
size() /
sizeof(quint32); offset < len; ++offset) {
2122 (
reinterpret_cast<quint16 *
>(layer.tile))[offset] = qToBigEndian<quint16>(qFromBigEndian(source[offset]) / 65537);
2126#ifndef USE_FLOAT_IMAGES
2127 case GIMP_PRECISION_HALF_LINEAR:
2128 case GIMP_PRECISION_HALF_NON_LINEAR:
2129 case GIMP_PRECISION_HALF_PERCEPTUAL:
2130 convertFloatTo16Bit<qfloat16>(layer.tile, buffer.
size() /
sizeof(qfloat16), buffer.
data());
2132 case GIMP_PRECISION_FLOAT_LINEAR:
2133 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2134 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2135 convertFloatTo16Bit<float>(layer.tile, buffer.
size() /
sizeof(
float), buffer.
data());
2137 case GIMP_PRECISION_DOUBLE_LINEAR:
2138 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2139 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2140 convertFloatTo16Bit<double>(layer.tile, buffer.
size() /
sizeof(
double), buffer.
data());
2143 case GIMP_PRECISION_DOUBLE_LINEAR:
2144 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2145 case GIMP_PRECISION_DOUBLE_PERCEPTUAL: {
2146 double *source =
reinterpret_cast<double *
>(buffer.
data());
2147 for (quint64 offset = 0, len = buffer.
size() /
sizeof(
double); offset < len; ++offset) {
2148 (
reinterpret_cast<float *
>(layer.tile))[offset] = qToBigEndian<float>(
float(qFromBigEndian(source[offset])));
2154 qCWarning(XCFPLUGIN) <<
"Unsupported precision" << precision;
2162 if (!layer.assignBytes(layer, i, j, precision)) {
2167 offset = readOffsetPtr(xcf_io);
2170 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2185bool XCFImageFormat::loadMask(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
2191 xcf_io >> width >> height >>
name;
2195 if (!loadChannelProperties(xcf_io, layer)) {
2199 const qint64 hierarchy_offset = readOffsetPtr(xcf_io);
2201 if (hierarchy_offset < 0) {
2202 qCDebug(XCFPLUGIN) <<
"XCF: negative mask hierarchy_offset";
2207 layer.assignBytes = assignMaskBytes;
2209 if (!loadHierarchy(xcf_io, layer, precision)) {
2239bool XCFImageFormat::loadTileRLE(
QDataStream &xcf_io, uchar *tile,
int image_size,
int data_length, qint32 bpp, qint64 *bytesParsed)
2245 uchar *xcfdatalimit;
2247 int step =
sizeof(QRgb);
2253 step =
sizeof(QRgb);
2257 step =
sizeof(QRgb) * 2;
2261 step =
sizeof(QRgb) * 4;
2264 qCDebug(XCFPLUGIN) <<
"XCF: unhandled bit depth" << bpp;
2268 if (data_length < 0 || data_length >
int(TILE_WIDTH * TILE_HEIGHT * step * 1.5)) {
2269 qCDebug(XCFPLUGIN) <<
"XCF: invalid tile data length" << data_length;
2273 xcfdata = xcfodata =
new uchar[data_length];
2275 const int dataRead = xcf_io.
readRawData((
char *)xcfdata, data_length);
2276 if (dataRead <= 0) {
2278 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile" << dataRead;
2282 if (dataRead < data_length) {
2283 memset(&xcfdata[dataRead], 0, data_length - dataRead);
2288 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile";
2292 xcfdatalimit = &xcfodata[data_length - 1];
2294 for (
int i = 0; i < bpp; ++i) {
2297 int size = image_size;
2300 if (xcfdata > xcfdatalimit) {
2304 uchar val = *xcfdata++;
2307 if (length >= 128) {
2308 length = 255 - (length - 1);
2309 if (length == 128) {
2310 if (xcfdata >= xcfdatalimit) {
2314 length = (*xcfdata << 8) + xcfdata[1];
2325 if (&xcfdata[length - 1] > xcfdatalimit) {
2329 while (length-- > 0) {
2335 if (length == 128) {
2336 if (xcfdata >= xcfdatalimit) {
2340 length = (*xcfdata << 8) + xcfdata[1];
2350 if (xcfdata > xcfdatalimit) {
2354 qintptr totalLength = qintptr(data - tile) + length * step;
2355 if (totalLength >= image_size * step * 1.5) {
2356 qCDebug(XCFPLUGIN) <<
"Ran out of space when trying to unpack image, over:" << totalLength - image_size << totalLength << image_size
2363 while (length-- > 0) {
2370 *bytesParsed = qintptr(data - tile);
2377 qCDebug(XCFPLUGIN) <<
"The run length encoding could not be decoded properly";
2389bool XCFImageFormat::loadChannelProperties(
QDataStream &xcf_io, Layer &layer)
2396 if (!loadProperty(xcf_io, type, bytes, rawType)) {
2397 qCDebug(XCFPLUGIN) <<
"XCF: error loading channel properties";
2401 QDataStream property(bytes);
2408 property >> layer.mask_channel.opacity;
2409 layer.mask_channel.opacity = std::min(layer.mask_channel.opacity, 255u);
2412 case PROP_FLOAT_OPACITY:
2415 if (bytes.
size() == 4) {
2416 layer.mask_channel.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
2418 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
2423 property >> layer.mask_channel.visible;
2426 case PROP_SHOW_MASKED:
2427 property >> layer.mask_channel.show_masked;
2431 property >> layer.mask_channel.red >> layer.mask_channel.green >> layer.mask_channel.blue;
2434 case PROP_FLOAT_COLOR:
2435 property >> layer.mask_channel.redF >> layer.mask_channel.greenF >> layer.mask_channel.blueF;
2439 property >> layer.mask_channel.tattoo;
2447 case PROP_COLOR_TAG:
2451 case PROP_LOCK_CONTENT:
2452 case PROP_LOCK_POSITION:
2456 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented channel property " <<
type <<
"(" << rawType <<
")"
2457 <<
", size " << bytes.
size();
2469bool XCFImageFormat::assignMaskBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
2471 QImage &image = layer.mask_tiles[j][i];
2472 if (image.
depth() != 8) {
2473 qCWarning(XCFPLUGIN) <<
"invalid bytes per pixel, we only do 8 bit masks" << image.
depth();
2477 uchar *tile = layer.tile;
2478 const int width = image.
width();
2479 const int height = image.
height();
2481 uchar *bits = image.
bits();
2482 auto bpc = bytesPerChannel(precision);
2487 for (
int y = 0; y < height; y++) {
2488 uchar *dataPtr = bits + y * bytesPerLine;
2489#ifdef USE_FLOAT_IMAGES
2491 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2492 for (
int x = 0; x < width; x++) {
2493 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2494 tile +=
sizeof(quint16);
2497 for (
int x = 0; x < width; x++) {
2498 *dataPtr++ = qFromBigEndian<float>(*
reinterpret_cast<const float *
>(tile)) * 255;
2499 tile +=
sizeof(QRgb);
2502 }
else if (bpc == 2) {
2504 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2505 for (
int x = 0; x < width; x++) {
2506 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2507 tile +=
sizeof(QRgb);
2510 for (
int x = 0; x < width; x++) {
2511 *dataPtr++ = qFromBigEndian<qfloat16>(*
reinterpret_cast<const qfloat16 *
>(tile)) * 255;
2512 tile +=
sizeof(QRgb);
2518 for (
int x = 0; x < width; x++) {
2519 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2520 tile +=
sizeof(QRgb);
2522 }
else if (bpc == 4) {
2523 for (
int x = 0; x < width; x++) {
2524 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2525 tile +=
sizeof(quint16);
2530 for (
int x = 0; x < width; x++) {
2531 *dataPtr++ = tile[0];
2532 tile +=
sizeof(QRgb);
2568bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
2571 Layer &layer(xcf_image.layer);
2572 QImage &image(xcf_image.image);
2574 switch (layer.type) {
2576 if (layer.opacity == OPAQUE_OPACITY) {
2582 setGrayPalette(image);
2590 image = imageAlloc(xcf_image.header.width, xcf_image.header.height, xcf_image.qimageFormat());
2601 case INDEXED_GIMAGE:
2614 if (xcf_image.num_colors <= 2) {
2621 setPalette(xcf_image, image);
2622 }
else if (xcf_image.num_colors <= 256) {
2629 setPalette(xcf_image, image);
2633 case INDEXEDA_GIMAGE:
2634 if (xcf_image.num_colors == 1) {
2636 xcf_image.num_colors++;
2637 xcf_image.palette.resize(xcf_image.num_colors);
2638 xcf_image.palette[1] = xcf_image.palette[0];
2639 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2647 setPalette(xcf_image, image);
2648 }
else if (xcf_image.num_colors < 256) {
2650 xcf_image.num_colors++;
2651 xcf_image.palette.resize(xcf_image.num_colors);
2652 for (
int c = xcf_image.num_colors - 1; c >= 1; c--) {
2653 xcf_image.palette[c] = xcf_image.palette[c - 1];
2656 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2663 setPalette(xcf_image, image);
2672 image.
fill(qRgba(255, 255, 255, 0));
2676 if (image.
format() != xcf_image.qimageFormat()) {
2677 qCWarning(XCFPLUGIN) <<
"Selected wrong format:" << image.
format() <<
"expected" << layer.qimageFormat(xcf_image.header.precision);
2683#ifndef DISABLE_IMAGE_PROFILE
2684 switch (xcf_image.header.precision) {
2685 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
2686 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
2687 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
2688 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
2689 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
2690 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
2693 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
2694 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
2695 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
2696 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
2697 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
2698 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
2701 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
2702 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
2703 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2704 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
2705 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
2706 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
2712 if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
2713 const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
2714 if (dpmx >
float(std::numeric_limits<int>::max())) {
2717 const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
2718 if (dpmy >
float(std::numeric_limits<int>::max())) {
2732void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
2734 Layer &layer(xcf_image.layer);
2735 QImage &image(xcf_image.image);
2736 PixelCopyOperation
copy =
nullptr;
2738 switch (layer.type) {
2741 copy = copyRGBToRGB;
2744 if (layer.opacity == OPAQUE_OPACITY) {
2745 copy = copyGrayToGray;
2747 copy = copyGrayToRGB;
2751 copy = copyGrayAToRGB;
2753 case INDEXED_GIMAGE:
2754 copy = copyIndexedToIndexed;
2756 case INDEXEDA_GIMAGE:
2757 if (xcf_image.image.depth() <= 8) {
2758 copy = copyIndexedAToIndexed;
2760 copy = copyIndexedAToRGB;
2770 for (uint j = 0; j < layer.nrows; j++) {
2771 qint32 y = qint32(j * TILE_HEIGHT);
2773 for (uint i = 0; i < layer.ncols; i++) {
2774 qint32 x = qint32(i * TILE_WIDTH);
2781 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
2782 if (!random_table_initialized) {
2783 initializeRandomTable();
2784 random_table_initialized =
true;
2786 if (layer.type == RGBA_GIMAGE) {
2787 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
2790 else if (layer.type == GRAYA_GIMAGE) {
2791 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
2796 if (copy == copyRGBToRGB && layer.apply_mask != 1) {
2797 QPainter painter(&image);
2798 painter.setOpacity(layer.opacity / 255.0);
2800 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
2801 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
2802 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
2807 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
2808 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
2809 int m = x + k + layer.x_offset;
2810 int n = y + l + layer.y_offset;
2812 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
2816 (*copy)(layer, i, j, k, l, image, m, n);
2836void XCFImageFormat::copyRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2838 if (image.
depth() == 32) {
2839 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2840 uchar src_a = layer.opacity;
2842 if (layer.type == RGBA_GIMAGE) {
2843 src_a = INT_MULT(src_a, qAlpha(src));
2848 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2849 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2852 image.
setPixel(m, n, qRgba(src, src_a));
2853 }
else if (image.
depth() == 64) {
2854 QRgba64 src = layer.image_tiles[j][i].pixelColor(k, l).rgba64();
2855 quint16 src_a = layer.opacity;
2857 if (layer.type == RGBA_GIMAGE) {
2858 src_a = INT_MULT(src_a, qAlpha(src));
2863 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2864 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2883void XCFImageFormat::copyGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2885 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2902void XCFImageFormat::copyGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2904 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2905 uchar src_a = layer.opacity;
2906 image.
setPixel(m, n, qRgba(src, src_a));
2922void XCFImageFormat::copyGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2924 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2925 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2926 src_a = INT_MULT(src_a, layer.opacity);
2930 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2931 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2934 image.
setPixel(m, n, qRgba(src, src_a));
2948void XCFImageFormat::copyIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2950 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2965void XCFImageFormat::copyIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2967 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
2968 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2969 src_a = INT_MULT(src_a, layer.opacity);
2971 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2972 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2997void XCFImageFormat::copyIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2999 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3000 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3001 src_a = INT_MULT(src_a, layer.opacity);
3004 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3005 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3012 src_a = OPAQUE_OPACITY;
3015 image.
setPixel(m, n, qRgba(src, src_a));
3022void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
3024 Layer &layer(xcf_image.layer);
3025 QImage &image(xcf_image.image);
3027 PixelMergeOperation
merge =
nullptr;
3029 if (!layer.opacity) {
3033 if (layer.blendSpace == XCFImageFormat::AutoColorSpace) {
3034 qCDebug(XCFPLUGIN) <<
"Auto blend space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3035 layer.blendSpace = XCFImageFormat::RgbLinearSpace;
3038 if (layer.blendSpace != XCFImageFormat::RgbLinearSpace) {
3039 qCDebug(XCFPLUGIN) <<
"Unimplemented blend color space" << layer.blendSpace;
3041 qCDebug(XCFPLUGIN) <<
"Blend color space" << layer.blendSpace;
3043 if (layer.compositeSpace == XCFImageFormat::AutoColorSpace) {
3044 qCDebug(XCFPLUGIN) <<
"Auto composite space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3045 layer.compositeSpace = XCFImageFormat::RgbLinearSpace;
3048 if (layer.compositeSpace != XCFImageFormat::RgbLinearSpace) {
3049 qCDebug(XCFPLUGIN) <<
"Unimplemented composite color space" << layer.compositeSpace;
3051 if (layer.compositeMode != XCFImageFormat::CompositeUnion) {
3052 qCDebug(XCFPLUGIN) <<
"Unhandled composite mode" << layer.compositeMode;
3055 switch (layer.type) {
3058 merge = mergeRGBToRGB;
3061 if (layer.opacity == OPAQUE_OPACITY && xcf_image.image.depth() <= 8) {
3062 merge = mergeGrayToGray;
3064 merge = mergeGrayToRGB;
3068 if (xcf_image.image.depth() <= 8) {
3069 merge = mergeGrayAToGray;
3071 merge = mergeGrayAToRGB;
3074 case INDEXED_GIMAGE:
3075 merge = mergeIndexedToIndexed;
3077 case INDEXEDA_GIMAGE:
3078 if (xcf_image.image.depth() <= 8) {
3079 merge = mergeIndexedAToIndexed;
3081 merge = mergeIndexedAToRGB;
3089 if (merge == mergeRGBToRGB && layer.apply_mask != 1) {
3090 int painterMode = -1;
3091 switch (layer.mode) {
3092 case GIMP_LAYER_MODE_NORMAL:
3093 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3096 case GIMP_LAYER_MODE_MULTIPLY:
3097 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3100 case GIMP_LAYER_MODE_SCREEN:
3101 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3104 case GIMP_LAYER_MODE_OVERLAY:
3105 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3108 case GIMP_LAYER_MODE_DIFFERENCE:
3109 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3112 case GIMP_LAYER_MODE_DARKEN_ONLY:
3113 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3116 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3117 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3120 case GIMP_LAYER_MODE_DODGE:
3121 case GIMP_LAYER_MODE_DODGE_LEGACY:
3124 case GIMP_LAYER_MODE_BURN:
3125 case GIMP_LAYER_MODE_BURN_LEGACY:
3128 case GIMP_LAYER_MODE_HARDLIGHT:
3129 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
3132 case GIMP_LAYER_MODE_SOFTLIGHT:
3133 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
3136 case GIMP_LAYER_MODE_ADDITION:
3137 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3140 case GIMP_LAYER_MODE_EXCLUSION:
3145 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3146 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
3147 case GIMP_LAYER_MODE_GRAIN_MERGE:
3148 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
3149 case GIMP_LAYER_MODE_COLOR_ERASE:
3150 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
3151 case GIMP_LAYER_MODE_LCH_HUE:
3152 case GIMP_LAYER_MODE_LCH_CHROMA:
3153 case GIMP_LAYER_MODE_LCH_COLOR:
3154 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
3155 case GIMP_LAYER_MODE_BEHIND:
3156 case GIMP_LAYER_MODE_BEHIND_LEGACY:
3157 case GIMP_LAYER_MODE_SUBTRACT:
3158 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3159 case GIMP_LAYER_MODE_HSV_HUE:
3160 case GIMP_LAYER_MODE_HSV_SATURATION:
3161 case GIMP_LAYER_MODE_HSL_COLOR:
3162 case GIMP_LAYER_MODE_HSV_VALUE:
3163 case GIMP_LAYER_MODE_DIVIDE:
3164 case GIMP_LAYER_MODE_VIVID_LIGHT:
3165 case GIMP_LAYER_MODE_PIN_LIGHT:
3166 case GIMP_LAYER_MODE_LINEAR_LIGHT:
3167 case GIMP_LAYER_MODE_HARD_MIX:
3168 case GIMP_LAYER_MODE_LINEAR_BURN:
3169 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
3170 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
3171 case GIMP_LAYER_MODE_LUMINANCE:
3172 case GIMP_LAYER_MODE_ERASE:
3173 case GIMP_LAYER_MODE_MERGE:
3174 case GIMP_LAYER_MODE_SPLIT:
3175 case GIMP_LAYER_MODE_PASS_THROUGH:
3176 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
3177 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
3178 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
3179 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
3180 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3181 qCDebug(XCFPLUGIN) <<
"No QPainter equivalent to" << layer.mode;
3185 case GIMP_LAYER_MODE_DISSOLVE:
3186 case GIMP_LAYER_MODE_COUNT:
3190 if (painterMode != -1) {
3191 QPainter painter(&image);
3192 painter.setOpacity(layer.opacity / 255.0);
3194 qCDebug(XCFPLUGIN) <<
"Using QPainter for mode" << layer.mode;
3196 for (uint j = 0; j < layer.nrows; j++) {
3197 qint32 y = qint32(j * TILE_HEIGHT);
3199 for (uint i = 0; i < layer.ncols; i++) {
3200 qint32 x = qint32(i * TILE_WIDTH);
3202 QImage &tile = layer.image_tiles[j][i];
3203 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3204 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3205 painter.drawImage(x + layer.x_offset, y + layer.y_offset, tile);
3214#ifndef DISABLE_IMAGE_PROFILE_CONV
3216 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3220 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3225 for (uint j = 0; j < layer.nrows; j++) {
3226 qint32 y = qint32(j * TILE_HEIGHT);
3228 for (uint i = 0; i < layer.ncols; i++) {
3229 qint32 x = qint32(i * TILE_WIDTH);
3236 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
3237 if (!random_table_initialized) {
3238 initializeRandomTable();
3239 random_table_initialized =
true;
3241 if (layer.type == RGBA_GIMAGE) {
3242 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
3245 else if (layer.type == GRAYA_GIMAGE) {
3246 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
3251 if (merge == mergeRGBToRGB && layer.apply_mask != 1 && layer.mode == GIMP_LAYER_MODE_NORMAL_LEGACY) {
3252 QPainter painter(&image);
3253 painter.setOpacity(layer.opacity / 255.0);
3255 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3256 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3257 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
3262#ifndef DISABLE_TILE_PROFILE_CONV
3263 QImage &tile = layer.image_tiles[j][i];
3272 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
3273 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
3274 int m = x + k + layer.x_offset;
3275 int n = y + l + layer.y_offset;
3277 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
3281 if (!(*merge)(layer, i, j, k, l, image, m, n)) {
3303bool XCFImageFormat::mergeRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3305 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3306 QRgb dst = image.
pixel(m, n);
3308 uchar src_r = qRed(src);
3309 uchar src_g = qGreen(src);
3310 uchar src_b = qBlue(src);
3311 uchar src_a = qAlpha(src);
3313 uchar dst_r = qRed(dst);
3314 uchar dst_g = qGreen(dst);
3315 uchar dst_b = qBlue(dst);
3316 uchar dst_a = qAlpha(dst);
3322 switch (layer.mode) {
3323 case GIMP_LAYER_MODE_NORMAL:
3324 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3326 case GIMP_LAYER_MODE_MULTIPLY:
3327 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3328 src_r = INT_MULT(src_r, dst_r);
3329 src_g = INT_MULT(src_g, dst_g);
3330 src_b = INT_MULT(src_b, dst_b);
3331 src_a = qMin(src_a, dst_a);
3333 case GIMP_LAYER_MODE_DIVIDE:
3334 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3335 src_r = qMin((dst_r * 256) / (1 + src_r), 255);
3336 src_g = qMin((dst_g * 256) / (1 + src_g), 255);
3337 src_b = qMin((dst_b * 256) / (1 + src_b), 255);
3338 src_a = qMin(src_a, dst_a);
3340 case GIMP_LAYER_MODE_SCREEN:
3341 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3342 src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
3343 src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
3344 src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
3345 src_a = qMin(src_a, dst_a);
3347 case GIMP_LAYER_MODE_OVERLAY:
3348 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3349 src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
3350 src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
3351 src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
3352 src_a = qMin(src_a, dst_a);
3354 case GIMP_LAYER_MODE_DIFFERENCE:
3355 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3356 src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
3357 src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
3358 src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
3359 src_a = qMin(src_a, dst_a);
3361 case GIMP_LAYER_MODE_ADDITION:
3362 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3363 src_r = add_lut(dst_r, src_r);
3364 src_g = add_lut(dst_g, src_g);
3365 src_b = add_lut(dst_b, src_b);
3366 src_a = qMin(src_a, dst_a);
3368 case GIMP_LAYER_MODE_SUBTRACT:
3369 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3370 src_r = dst_r > src_r ? dst_r - src_r : 0;
3371 src_g = dst_g > src_g ? dst_g - src_g : 0;
3372 src_b = dst_b > src_b ? dst_b - src_b : 0;
3373 src_a = qMin(src_a, dst_a);
3375 case GIMP_LAYER_MODE_DARKEN_ONLY:
3376 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3377 src_r = dst_r < src_r ? dst_r : src_r;
3378 src_g = dst_g < src_g ? dst_g : src_g;
3379 src_b = dst_b < src_b ? dst_b : src_b;
3380 src_a = qMin(src_a, dst_a);
3382 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3383 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3384 src_r = dst_r < src_r ? src_r : dst_r;
3385 src_g = dst_g < src_g ? src_g : dst_g;
3386 src_b = dst_b < src_b ? src_b : dst_b;
3387 src_a = qMin(src_a, dst_a);
3389 case GIMP_LAYER_MODE_HSV_HUE:
3390 case GIMP_LAYER_MODE_HSV_HUE_LEGACY: {
3391 uchar new_r = dst_r;
3392 uchar new_g = dst_g;
3393 uchar new_b = dst_b;
3395 RGBTOHSV(src_r, src_g, src_b);
3396 RGBTOHSV(new_r, new_g, new_b);
3400 HSVTORGB(new_r, new_g, new_b);
3405 src_a = qMin(src_a, dst_a);
3407 case GIMP_LAYER_MODE_HSV_SATURATION:
3408 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: {
3409 uchar new_r = dst_r;
3410 uchar new_g = dst_g;
3411 uchar new_b = dst_b;
3413 RGBTOHSV(src_r, src_g, src_b);
3414 RGBTOHSV(new_r, new_g, new_b);
3418 HSVTORGB(new_r, new_g, new_b);
3423 src_a = qMin(src_a, dst_a);
3425 case GIMP_LAYER_MODE_HSV_VALUE:
3426 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY: {
3427 uchar new_r = dst_r;
3428 uchar new_g = dst_g;
3429 uchar new_b = dst_b;
3431 RGBTOHSV(src_r, src_g, src_b);
3432 RGBTOHSV(new_r, new_g, new_b);
3436 HSVTORGB(new_r, new_g, new_b);
3441 src_a = qMin(src_a, dst_a);
3443 case GIMP_LAYER_MODE_HSL_COLOR:
3444 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: {
3445 uchar new_r = dst_r;
3446 uchar new_g = dst_g;
3447 uchar new_b = dst_b;
3449 RGBTOHLS(src_r, src_g, src_b);
3450 RGBTOHLS(new_r, new_g, new_b);
3455 HLSTORGB(new_r, new_g, new_b);
3460 src_a = qMin(src_a, dst_a);
3462 case GIMP_LAYER_MODE_DODGE:
3463 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3468 src_r = (uchar)qMin(tmp, 255u);
3472 src_g = (uchar)qMin(tmp, 255u);
3476 src_b = (uchar)qMin(tmp, 255u);
3478 src_a = qMin(src_a, dst_a);
3480 case GIMP_LAYER_MODE_BURN:
3481 case GIMP_LAYER_MODE_BURN_LEGACY: {
3484 tmp = (255 - dst_r) << 8;
3486 src_r = (uchar)qMin(tmp, 255u);
3487 src_r = 255 - src_r;
3489 tmp = (255 - dst_g) << 8;
3491 src_g = (uchar)qMin(tmp, 255u);
3492 src_g = 255 - src_g;
3494 tmp = (255 - dst_b) << 8;
3496 src_b = (uchar)qMin(tmp, 255u);
3497 src_b = 255 - src_b;
3499 src_a = qMin(src_a, dst_a);
3501 case GIMP_LAYER_MODE_HARDLIGHT:
3502 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3505 tmp = ((int)255 - dst_r) * ((
int)255 - ((src_r - 128) << 1));
3506 src_r = (uchar)qMin(255 - (tmp >> 8), 255u);
3508 tmp = (int)dst_r * ((
int)src_r << 1);
3509 src_r = (uchar)qMin(tmp >> 8, 255u);
3513 tmp = ((int)255 - dst_g) * ((
int)255 - ((src_g - 128) << 1));
3514 src_g = (uchar)qMin(255 - (tmp >> 8), 255u);
3516 tmp = (int)dst_g * ((
int)src_g << 1);
3517 src_g = (uchar)qMin(tmp >> 8, 255u);
3521 tmp = ((int)255 - dst_b) * ((
int)255 - ((src_b - 128) << 1));
3522 src_b = (uchar)qMin(255 - (tmp >> 8), 255u);
3524 tmp = (int)dst_b * ((
int)src_b << 1);
3525 src_b = (uchar)qMin(tmp >> 8, 255u);
3527 src_a = qMin(src_a, dst_a);
3529 case GIMP_LAYER_MODE_SOFTLIGHT:
3530 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3534 tmpM = INT_MULT(dst_r, src_r);
3535 tmpS = 255 - INT_MULT((255 - dst_r), (255 - src_r));
3536 src_r = INT_MULT((255 - dst_r), tmpM) + INT_MULT(dst_r, tmpS);
3538 tmpM = INT_MULT(dst_g, src_g);
3539 tmpS = 255 - INT_MULT((255 - dst_g), (255 - src_g));
3540 src_g = INT_MULT((255 - dst_g), tmpM) + INT_MULT(dst_g, tmpS);
3542 tmpM = INT_MULT(dst_b, src_b);
3543 tmpS = 255 - INT_MULT((255 - dst_b), (255 - src_b));
3544 src_b = INT_MULT((255 - dst_b), tmpM) + INT_MULT(dst_b, tmpS);
3546 src_a = qMin(src_a, dst_a);
3548 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3549 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3552 tmp = dst_r - src_r + 128;
3553 tmp = qMin(tmp, 255);
3557 tmp = dst_g - src_g + 128;
3558 tmp = qMin(tmp, 255);
3562 tmp = dst_b - src_b + 128;
3563 tmp = qMin(tmp, 255);
3567 src_a = qMin(src_a, dst_a);
3569 case GIMP_LAYER_MODE_GRAIN_MERGE:
3570 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3573 tmp = dst_r + src_r - 128;
3574 tmp = qMin(tmp, 255);
3578 tmp = dst_g + src_g - 128;
3579 tmp = qMin(tmp, 255);
3583 tmp = dst_b + src_b - 128;
3584 tmp = qMin(tmp, 255);
3588 src_a = qMin(src_a, dst_a);
3590 case GIMP_LAYER_MODE_LINEAR_LIGHT: {
3592 src_r = qBound(0, dst_r + 2 * src_r - 255, 255);
3594 src_r = qBound(0, dst_r + 2 * (src_r - 128), 255);
3597 src_g = qBound(0, dst_g + 2 * src_g - 255, 255);
3599 src_g = qBound(0, dst_g + 2 * (src_g - 127), 255);
3602 src_b = qBound(0, dst_b + 2 * src_b - 255, 255);
3604 src_b = qBound(0, dst_b + 2 * (src_b - 127), 255);
3607 case GIMP_LAYER_MODE_VIVID_LIGHT: {
3610 A[0] = src_r / 255.;
3611 A[1] = src_g / 255.;
3612 A[2] = src_b / 255.;
3614 B[0] = dst_r / 255.;
3615 B[1] = dst_g / 255.;
3616 B[2] = dst_b / 255.;
3618 for (
int i = 0; i < 3; i++) {
3621 C[i] = 1.f - (1.f - B[i]) / (2.f * A[i]);
3625 C[i] = B[i] / (2.f * (1.f - A[i]));
3629 src_r = qBound(0.f, C[0] * 255.f, 255.f);
3630 src_g = qBound(0.f, C[1] * 255.f, 255.f);
3631 src_b = qBound(0.f, C[2] * 255.f, 255.f);
3634 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3638 src_a = INT_MULT(src_a, layer.opacity);
3642 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3643 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3650 new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3652 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3653 float dst_ratio = 1.0 - src_ratio;
3655 new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
3656 new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
3657 new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
3659 if (!modeAffectsSourceAlpha(layer.mode)) {
3663 image.
setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
3678bool XCFImageFormat::mergeGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3680 int src = layer.image_tiles[j][i].pixelIndex(k, l);
3696bool XCFImageFormat::mergeGrayAToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3698 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3701 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3707 switch (layer.mode) {
3708 case GIMP_LAYER_MODE_MULTIPLY:
3709 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3710 src = INT_MULT(src, dst);
3712 case GIMP_LAYER_MODE_DIVIDE:
3713 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3714 src = qMin((dst * 256) / (1 + src), 255);
3716 case GIMP_LAYER_MODE_SCREEN:
3717 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3718 src = 255 - INT_MULT(255 - dst, 255 - src);
3720 case GIMP_LAYER_MODE_OVERLAY:
3721 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3722 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3724 case GIMP_LAYER_MODE_DIFFERENCE:
3725 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3726 src = dst > src ? dst - src : src - dst;
3728 case GIMP_LAYER_MODE_ADDITION:
3729 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3730 src = add_lut(dst, src);
3732 case GIMP_LAYER_MODE_SUBTRACT:
3733 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3734 src = dst > src ? dst - src : 0;
3736 case GIMP_LAYER_MODE_DARKEN_ONLY:
3737 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3738 src = dst < src ? dst : src;
3740 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3741 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3742 src = dst < src ? src : dst;
3744 case GIMP_LAYER_MODE_DODGE:
3745 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3746 uint tmp = dst << 8;
3748 src = (uchar)qMin(tmp, 255u);
3750 case GIMP_LAYER_MODE_BURN:
3751 case GIMP_LAYER_MODE_BURN_LEGACY: {
3752 uint tmp = (255 - dst) << 8;
3754 src = (uchar)qMin(tmp, 255u);
3757 case GIMP_LAYER_MODE_HARDLIGHT:
3758 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3761 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3762 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3764 tmp = (int)dst * ((
int)src << 1);
3765 src = (uchar)qMin(tmp >> 8, 255u);
3768 case GIMP_LAYER_MODE_SOFTLIGHT:
3769 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3773 tmpM = INT_MULT(dst, src);
3774 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3775 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3778 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3779 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3782 tmp = dst - src + 128;
3783 tmp = qMin(tmp, 255);
3788 case GIMP_LAYER_MODE_GRAIN_MERGE:
3789 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3792 tmp = dst + src - 128;
3793 tmp = qMin(tmp, 255);
3799 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3803 src_a = INT_MULT(src_a, layer.opacity);
3807 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3808 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3811 uchar new_a = OPAQUE_OPACITY;
3813 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3814 float dst_ratio = 1.0 - src_ratio;
3816 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3835bool XCFImageFormat::mergeGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3837 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3838 uchar src_a = layer.opacity;
3839 image.
setPixel(m, n, qRgba(src, src_a));
3856bool XCFImageFormat::mergeGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3858 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3859 int dst = qGray(image.
pixel(m, n));
3861 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3862 uchar dst_a = qAlpha(image.
pixel(m, n));
3868 switch (layer.mode) {
3869 case GIMP_LAYER_MODE_NORMAL:
3870 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3872 case GIMP_LAYER_MODE_MULTIPLY:
3873 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3874 src = INT_MULT(src, dst);
3875 src_a = qMin(src_a, dst_a);
3877 case GIMP_LAYER_MODE_DIVIDE:
3878 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3879 src = qMin((dst * 256) / (1 + src), 255);
3880 src_a = qMin(src_a, dst_a);
3882 case GIMP_LAYER_MODE_SCREEN:
3883 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3884 src = 255 - INT_MULT(255 - dst, 255 - src);
3885 src_a = qMin(src_a, dst_a);
3887 case GIMP_LAYER_MODE_OVERLAY:
3888 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3889 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3890 src_a = qMin(src_a, dst_a);
3892 case GIMP_LAYER_MODE_DIFFERENCE:
3893 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3894 src = dst > src ? dst - src : src - dst;
3895 src_a = qMin(src_a, dst_a);
3897 case GIMP_LAYER_MODE_ADDITION:
3898 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3899 src = add_lut(dst, src);
3900 src_a = qMin(src_a, dst_a);
3902 case GIMP_LAYER_MODE_SUBTRACT:
3903 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3904 src = dst > src ? dst - src : 0;
3905 src_a = qMin(src_a, dst_a);
3907 case GIMP_LAYER_MODE_DARKEN_ONLY:
3908 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3909 src = dst < src ? dst : src;
3910 src_a = qMin(src_a, dst_a);
3912 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3913 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3914 src = dst < src ? src : dst;
3915 src_a = qMin(src_a, dst_a);
3917 case GIMP_LAYER_MODE_DODGE:
3918 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3919 uint tmp = dst << 8;
3921 src = (uchar)qMin(tmp, 255u);
3922 src_a = qMin(src_a, dst_a);
3924 case GIMP_LAYER_MODE_BURN:
3925 case GIMP_LAYER_MODE_BURN_LEGACY: {
3926 uint tmp = (255 - dst) << 8;
3928 src = (uchar)qMin(tmp, 255u);
3930 src_a = qMin(src_a, dst_a);
3932 case GIMP_LAYER_MODE_HARDLIGHT:
3933 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3936 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3937 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3939 tmp = (int)dst * ((
int)src << 1);
3940 src = (uchar)qMin(tmp >> 8, 255u);
3942 src_a = qMin(src_a, dst_a);
3944 case GIMP_LAYER_MODE_SOFTLIGHT:
3945 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3949 tmpM = INT_MULT(dst, src);
3950 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3951 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3953 src_a = qMin(src_a, dst_a);
3955 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3956 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3959 tmp = dst - src + 128;
3960 tmp = qMin(tmp, 255);
3964 src_a = qMin(src_a, dst_a);
3966 case GIMP_LAYER_MODE_GRAIN_MERGE:
3967 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3970 tmp = dst + src - 128;
3971 tmp = qMin(tmp, 255);
3975 src_a = qMin(src_a, dst_a);
3978 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3982 src_a = INT_MULT(src_a, layer.opacity);
3985 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3986 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3989 uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3991 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3992 float dst_ratio = 1.0 - src_ratio;
3994 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3996 if (!modeAffectsSourceAlpha(layer.mode)) {
4000 image.
setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
4015bool XCFImageFormat::mergeIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4017 int src = layer.image_tiles[j][i].pixelIndex(k, l);
4033bool XCFImageFormat::mergeIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4035 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
4036 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4037 src_a = INT_MULT(src_a, layer.opacity);
4039 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4040 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4063bool XCFImageFormat::mergeIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4065 QRgb src = layer.image_tiles[j][i].pixel(k, l);
4066 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4067 src_a = INT_MULT(src_a, layer.opacity);
4070 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4071 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4078 src_a = OPAQUE_OPACITY;
4081 image.
setPixel(m, n, qRgba(src, src_a));
4092void XCFImageFormat::dissolveRGBPixels(
QImage &image,
int x,
int y)
4097 for (
int l = 0; l < image.
height(); l++) {
4098 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4100 for (
int k = 0; k < x; k++) {
4101 RandomTable::rand_r(&next);
4104 for (
int k = 0; k < image.
width(); k++) {
4105 int rand_val = RandomTable::rand_r(&next) & 0xff;
4106 QRgb pixel = image.
pixel(k, l);
4108 if (rand_val > qAlpha(pixel)) {
4109 image.
setPixel(k, l, qRgba(pixel, 0));
4124void XCFImageFormat::dissolveAlphaPixels(
QImage &image,
int x,
int y)
4129 for (
int l = 0; l < image.
height(); l++) {
4130 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4132 for (
int k = 0; k < x; k++) {
4133 RandomTable::rand_r(&next);
4136 for (
int k = 0; k < image.
width(); k++) {
4137 int rand_val = RandomTable::rand_r(&next) & 0xff;
4140 if (rand_val > alpha) {
4149XCFHandler::XCFHandler()
4153bool XCFHandler::canRead()
const
4155 if (canRead(device())) {
4162bool XCFHandler::read(
QImage *image)
4164 XCFImageFormat xcfif;
4165 auto ok = xcfif.readXCF(device(), image);
4166 m_imageSize = image->
size();
4170bool XCFHandler::write(
const QImage &)
4175bool XCFHandler::supportsOption(ImageOption option)
const
4182QVariant XCFHandler::option(ImageOption option)
const
4187 if (!m_imageSize.isEmpty()) {
4202 else if (
auto d = device()) {
4204 d->startTransaction();
4205 auto ba9 = d->read(9);
4206 auto ba5 = d->read(4+1);
4207 auto ba = d->read(8);
4208 d->rollbackTransaction();
4224bool XCFHandler::canRead(
QIODevice *device)
4227 qCDebug(XCFPLUGIN) <<
"XCFHandler::canRead() called with no device";
4234 const qint64 oldPos = device->
pos();
4237 XCFImageFormat::XCFImage::Header header;
4238 bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
4239 ds.setDevice(
nullptr);
4241 device->
seek(oldPos);
4246 switch (header.precision) {
4247 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
4248 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
4249 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
4250 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
4251 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
4252 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
4253 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
4254 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
4255 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
4256 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
4257 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
4258 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
4259 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
4260 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
4261 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
4263 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
4264 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
4265 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
4267 qCDebug(XCFPLUGIN) <<
"unsupported precision" << header.precision;
4276 if (format ==
"xcf") {
4287 if (device->
isReadable() && XCFHandler::canRead(device)) {
4304#include "moc_xcf_p.cpp"
QFlags< Capability > Capabilities
QStringView merge(QStringView lhs, QStringView rhs)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
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)