1/*
2 xcf.cpp: A Qt 5 plug-in for reading GIMP XCF image files
3 SPDX-FileCopyrightText: 2001 lignum Computing Inc. <allen@lignumcomputing.com>
4 SPDX-FileCopyrightText: 2004 Melchior FRANZ <mfranz@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7*/
8
9#include "util_p.h"
10#include "xcf_p.h"
11
12#include <QColorSpace>
13#include <QIODevice>
14#include <QImage>
15#include <QImageReader>
16#include <QList>
17#include <QLoggingCategory>
18#include <QPainter>
19#include <QStack>
20#include <QtEndian>
21
22#ifndef XCF_QT5_SUPPORT
23// Float images are not supported by Qt 5 and can be disabled in QT 6 to reduce memory usage.
24// Unfortunately enabling/disabling this define results in slightly different images, so leave the default if possible.
25#define USE_FLOAT_IMAGES // default uncommented
26
27// Let's set a "reasonable" maximum size
28#ifndef XCF_MAX_IMAGE_WIDTH
29#define XCF_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
30#endif
31#ifndef XCF_MAX_IMAGE_HEIGHT
32#define XCF_MAX_IMAGE_HEIGHT XCF_MAX_IMAGE_WIDTH
33#endif
34#else
35// While it is possible to have images larger than 32767 pixels, QPainter seems unable to go beyond this threshold using Qt 5.
36#define XCF_MAX_IMAGE_WIDTH 32767
37#define XCF_MAX_IMAGE_HEIGHT 32767
38#endif
39
40#ifdef USE_FLOAT_IMAGES
41#include <qrgbafloat.h>
42#endif
43
44#include <stdlib.h>
45#include <string.h>
46
47#include "gimp_p.h"
48
49Q_DECLARE_LOGGING_CATEGORY(XCFPLUGIN)
50Q_LOGGING_CATEGORY(XCFPLUGIN, "kf.imageformats.plugins.xcf", QtWarningMsg)
51
52//#define DISABLE_TILE_PROFILE // default commented (comment to use the conversion as intended by Martin)
53#define DISABLE_IMAGE_PROFILE // default uncommented (comment to use the conversion as intended by Martin)
54#define DISABLE_TILE_PROFILE_CONV // default uncommented (comment to use the conversion as intended by Martin)
55#define DISABLE_IMAGE_PROFILE_CONV // default uncommented (comment to use the conversion as intended by Martin)
56
57namespace
58{
59struct RandomTable {
60 // From glibc
61 static constexpr int rand_r(unsigned int *seed)
62 {
63 unsigned int next = *seed;
64 int result = 0;
65
66 next *= 1103515245;
67 next += 12345;
68 result = (unsigned int)(next / 65536) % 2048;
69
70 next *= 1103515245;
71 next += 12345;
72 result <<= 10;
73 result ^= (unsigned int)(next / 65536) % 1024;
74
75 next *= 1103515245;
76 next += 12345;
77 result <<= 10;
78 result ^= (unsigned int)(next / 65536) % 1024;
79
80 *seed = next;
81
82 return result;
83 }
84
85 constexpr RandomTable()
86 : values{}
87 {
88 unsigned int next = RANDOM_SEED;
89
90 for (int i = 0; i < RANDOM_TABLE_SIZE; i++) {
91 values[i] = rand_r(seed: &next);
92 }
93
94 for (int i = 0; i < RANDOM_TABLE_SIZE; i++) {
95 int tmp{};
96 int swap = i + rand_r(seed: &next) % (RANDOM_TABLE_SIZE - i);
97 tmp = values[i];
98 values[i] = values[swap];
99 values[swap] = tmp;
100 }
101 }
102
103 int values[RANDOM_TABLE_SIZE]{};
104};
105} // namespace {
106
107/*!
108 * Each layer in an XCF file is stored as a matrix of
109 * 64-pixel by 64-pixel images. The GIMP has a sophisticated
110 * method of handling very large images as well as implementing
111 * parallel processing on a tile-by-tile basis. Here, though,
112 * we just read them in en-masse and store them in a matrix.
113 */
114typedef QList<QList<QImage>> Tiles;
115
116class XCFImageFormat
117{
118 Q_GADGET
119public:
120 //! Properties which can be stored in an XCF file.
121 enum PropType {
122 PROP_END = 0,
123 PROP_COLORMAP = 1,
124 PROP_ACTIVE_LAYER = 2,
125 PROP_ACTIVE_CHANNEL = 3,
126 PROP_SELECTION = 4,
127 PROP_FLOATING_SELECTION = 5,
128 PROP_OPACITY = 6,
129 PROP_MODE = 7,
130 PROP_VISIBLE = 8,
131 PROP_LINKED = 9,
132 PROP_LOCK_ALPHA = 10,
133 PROP_APPLY_MASK = 11,
134 PROP_EDIT_MASK = 12,
135 PROP_SHOW_MASK = 13,
136 PROP_SHOW_MASKED = 14,
137 PROP_OFFSETS = 15,
138 PROP_COLOR = 16,
139 PROP_COMPRESSION = 17,
140 PROP_GUIDES = 18,
141 PROP_RESOLUTION = 19,
142 PROP_TATTOO = 20,
143 PROP_PARASITES = 21,
144 PROP_UNIT = 22,
145 PROP_PATHS = 23,
146 PROP_USER_UNIT = 24,
147 PROP_VECTORS = 25,
148 PROP_TEXT_LAYER_FLAGS = 26,
149 PROP_OLD_SAMPLE_POINTS = 27,
150 PROP_LOCK_CONTENT = 28,
151 PROP_GROUP_ITEM = 29,
152 PROP_ITEM_PATH = 30,
153 PROP_GROUP_ITEM_FLAGS = 31,
154 PROP_LOCK_POSITION = 32,
155 PROP_FLOAT_OPACITY = 33,
156 PROP_COLOR_TAG = 34,
157 PROP_COMPOSITE_MODE = 35,
158 PROP_COMPOSITE_SPACE = 36,
159 PROP_BLEND_SPACE = 37,
160 PROP_FLOAT_COLOR = 38,
161 PROP_SAMPLE_POINTS = 39,
162 MAX_SUPPORTED_PROPTYPE, // should always be at the end so its value is last + 1
163 };
164 Q_ENUM(PropType)
165
166 //! Compression type used in layer tiles.
167 enum XcfCompressionType : qint8 {
168 COMPRESS_INVALID = -1, /* our own */
169 COMPRESS_NONE = 0,
170 COMPRESS_RLE = 1,
171 COMPRESS_ZLIB = 2, /* unused */
172 COMPRESS_FRACTAL = 3, /* unused */
173 };
174 Q_ENUM(XcfCompressionType)
175
176 enum LayerModeType : quint32 {
177 GIMP_LAYER_MODE_NORMAL_LEGACY,
178 GIMP_LAYER_MODE_DISSOLVE,
179 GIMP_LAYER_MODE_BEHIND_LEGACY,
180 GIMP_LAYER_MODE_MULTIPLY_LEGACY,
181 GIMP_LAYER_MODE_SCREEN_LEGACY,
182 GIMP_LAYER_MODE_OVERLAY_LEGACY,
183 GIMP_LAYER_MODE_DIFFERENCE_LEGACY,
184 GIMP_LAYER_MODE_ADDITION_LEGACY,
185 GIMP_LAYER_MODE_SUBTRACT_LEGACY,
186 GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY,
187 GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY,
188 GIMP_LAYER_MODE_HSV_HUE_LEGACY,
189 GIMP_LAYER_MODE_HSV_SATURATION_LEGACY,
190 GIMP_LAYER_MODE_HSL_COLOR_LEGACY,
191 GIMP_LAYER_MODE_HSV_VALUE_LEGACY,
192 GIMP_LAYER_MODE_DIVIDE_LEGACY,
193 GIMP_LAYER_MODE_DODGE_LEGACY,
194 GIMP_LAYER_MODE_BURN_LEGACY,
195 GIMP_LAYER_MODE_HARDLIGHT_LEGACY,
196 GIMP_LAYER_MODE_SOFTLIGHT_LEGACY,
197 GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY,
198 GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY,
199 GIMP_LAYER_MODE_COLOR_ERASE_LEGACY,
200 GIMP_LAYER_MODE_OVERLAY,
201 GIMP_LAYER_MODE_LCH_HUE,
202 GIMP_LAYER_MODE_LCH_CHROMA,
203 GIMP_LAYER_MODE_LCH_COLOR,
204 GIMP_LAYER_MODE_LCH_LIGHTNESS,
205 GIMP_LAYER_MODE_NORMAL,
206 GIMP_LAYER_MODE_BEHIND,
207 GIMP_LAYER_MODE_MULTIPLY,
208 GIMP_LAYER_MODE_SCREEN,
209 GIMP_LAYER_MODE_DIFFERENCE,
210 GIMP_LAYER_MODE_ADDITION,
211 GIMP_LAYER_MODE_SUBTRACT,
212 GIMP_LAYER_MODE_DARKEN_ONLY,
213 GIMP_LAYER_MODE_LIGHTEN_ONLY,
214 GIMP_LAYER_MODE_HSV_HUE,
215 GIMP_LAYER_MODE_HSV_SATURATION,
216 GIMP_LAYER_MODE_HSL_COLOR,
217 GIMP_LAYER_MODE_HSV_VALUE,
218 GIMP_LAYER_MODE_DIVIDE,
219 GIMP_LAYER_MODE_DODGE,
220 GIMP_LAYER_MODE_BURN,
221 GIMP_LAYER_MODE_HARDLIGHT,
222 GIMP_LAYER_MODE_SOFTLIGHT,
223 GIMP_LAYER_MODE_GRAIN_EXTRACT,
224 GIMP_LAYER_MODE_GRAIN_MERGE,
225 GIMP_LAYER_MODE_VIVID_LIGHT,
226 GIMP_LAYER_MODE_PIN_LIGHT,
227 GIMP_LAYER_MODE_LINEAR_LIGHT,
228 GIMP_LAYER_MODE_HARD_MIX,
229 GIMP_LAYER_MODE_EXCLUSION,
230 GIMP_LAYER_MODE_LINEAR_BURN,
231 GIMP_LAYER_MODE_LUMA_DARKEN_ONLY,
232 GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY,
233 GIMP_LAYER_MODE_LUMINANCE,
234 GIMP_LAYER_MODE_COLOR_ERASE,
235 GIMP_LAYER_MODE_ERASE,
236 GIMP_LAYER_MODE_MERGE,
237 GIMP_LAYER_MODE_SPLIT,
238 GIMP_LAYER_MODE_PASS_THROUGH,
239 GIMP_LAYER_MODE_COUNT,
240 };
241 Q_ENUM(LayerModeType)
242
243 //! Type of individual layers in an XCF file.
244 enum GimpImageType : qint32 {
245 RGB_GIMAGE,
246 RGBA_GIMAGE,
247 GRAY_GIMAGE,
248 GRAYA_GIMAGE,
249 INDEXED_GIMAGE,
250 INDEXEDA_GIMAGE,
251 };
252 Q_ENUM(GimpImageType)
253
254 //! Type of individual layers in an XCF file.
255 enum GimpColorSpace : qint32 {
256 AutoColorSpace,
257 RgbLinearSpace,
258 RgbPerceptualSpace,
259 LabSpace,
260 };
261 Q_ENUM(GimpColorSpace);
262
263 //! Mode to use when compositing layer
264 enum GimpCompositeMode : qint32 {
265 CompositeAuto,
266 CompositeUnion,
267 CompositeClipBackdrop,
268 CompositeClipLayer,
269 CompositeIntersect,
270 };
271 Q_ENUM(GimpCompositeMode);
272
273 enum GimpPrecision : qint32 {
274 GIMP_PRECISION_U8_LINEAR = 100, /*< desc="8-bit linear integer" >*/
275 GIMP_PRECISION_U8_NON_LINEAR = 150, /*< desc="8-bit non-linear integer" >*/
276 GIMP_PRECISION_U8_PERCEPTUAL = 175, /*< desc="8-bit perceptual integer" >*/
277 GIMP_PRECISION_U16_LINEAR = 200, /*< desc="16-bit linear integer" >*/
278 GIMP_PRECISION_U16_NON_LINEAR = 250, /*< desc="16-bit non-linear integer" >*/
279 GIMP_PRECISION_U16_PERCEPTUAL = 275, /*< desc="16-bit perceptual integer" >*/
280 GIMP_PRECISION_U32_LINEAR = 300, /*< desc="32-bit linear integer" >*/
281 GIMP_PRECISION_U32_NON_LINEAR = 350, /*< desc="32-bit non-linear integer" >*/
282 GIMP_PRECISION_U32_PERCEPTUAL = 375, /*< desc="32-bit perceptual integer" >*/
283 GIMP_PRECISION_HALF_LINEAR = 500, /*< desc="16-bit linear floating point" >*/
284 GIMP_PRECISION_HALF_NON_LINEAR = 550, /*< desc="16-bit non-linear floating point" >*/
285 GIMP_PRECISION_HALF_PERCEPTUAL = 575, /*< desc="16-bit perceptual floating point" >*/
286 GIMP_PRECISION_FLOAT_LINEAR = 600, /*< desc="32-bit linear floating point" >*/
287 GIMP_PRECISION_FLOAT_NON_LINEAR = 650, /*< desc="32-bit non-linear floating point" >*/
288 GIMP_PRECISION_FLOAT_PERCEPTUAL = 675, /*< desc="32-bit perceptual floating point" >*/
289 GIMP_PRECISION_DOUBLE_LINEAR = 700, /*< desc="64-bit linear floating point" >*/
290 GIMP_PRECISION_DOUBLE_NON_LINEAR = 750, /*< desc="64-bit non-linear floating point" >*/
291 GIMP_PRECISION_DOUBLE_PERCEPTUAL = 775, /*< desc="64-bit perceptual floating point" >*/
292 };
293 Q_ENUM(GimpPrecision);
294
295 XCFImageFormat();
296 bool readXCF(QIODevice *device, QImage *image);
297
298 /*!
299 * Each GIMP image is composed of one or more layers. A layer can
300 * be one of any three basic types: RGB, grayscale or indexed. With an
301 * optional alpha channel, there are six possible types altogether.
302 *
303 * Note: there is only ever one instance of this structure. The
304 * layer info is discarded after it is merged into the final QImage.
305 */
306 class Layer
307 {
308 public:
309 quint32 width; //!< Width of the layer
310 quint32 height; //!< Height of the layer
311 GimpImageType type; //!< Type of the layer (GimpImageType)
312 char *name; //!< Name of the layer
313 qint64 hierarchy_offset; //!< File position of Tile hierarchy
314 qint64 mask_offset; //!< File position of mask image
315
316 uint nrows; //!< Number of rows of tiles (y direction)
317 uint ncols; //!< Number of columns of tiles (x direction)
318
319 Tiles image_tiles; //!< The basic image
320 //! For Grayscale and Indexed images, the alpha channel is stored
321 //! separately (in this data structure, anyway).
322 Tiles alpha_tiles;
323 Tiles mask_tiles; //!< The layer mask (optional)
324
325 //! Additional information about a layer mask.
326 struct {
327 quint32 opacity;
328 float opacityFloat = 1.f;
329 quint32 visible;
330 quint32 show_masked;
331 uchar red, green, blue;
332 float redF, greenF, blueF; // Floats should override
333 quint32 tattoo;
334 } mask_channel;
335
336 XcfCompressionType compression = COMPRESS_INVALID; //!< tile compression method (CompressionType)
337
338 bool active; //!< Is this layer the active layer?
339 quint32 opacity = 255; //!< The opacity of the layer
340 float opacityFloat = 1.f; //!< The opacity of the layer, but floating point (both are set)
341 quint32 visible = 1; //!< Is the layer visible?
342 quint32 linked; //!< Is this layer linked (geometrically)
343 quint32 preserve_transparency; //!< Preserve alpha when drawing on layer?
344 quint32 apply_mask = 9; //!< Apply the layer mask? Use 9 as "uninitilized". Spec says "If the property does not appear for a layer which has a layer
345 //!< mask, it defaults to true (1).
346 // Robust readers should force this to false if the layer has no layer mask.
347 quint32 edit_mask; //!< Is the layer mask the being edited?
348 quint32 show_mask; //!< Show the layer mask rather than the image?
349 qint32 x_offset = 0; //!< x offset of the layer relative to the image
350 qint32 y_offset = 0; //!< y offset of the layer relative to the image
351 LayerModeType mode = GIMP_LAYER_MODE_NORMAL_LEGACY; //!< Combining mode of layer (LayerModeEffects)
352 quint32 tattoo; //!< (unique identifier?)
353 GimpColorSpace blendSpace = RgbLinearSpace; //!< What colorspace to use when blending
354 GimpColorSpace compositeSpace = RgbLinearSpace; //!< What colorspace to use when compositing
355 GimpCompositeMode compositeMode = CompositeUnion; //!< How to composite layer (union, clip, etc.)
356
357 //! As each tile is read from the file, it is buffered here.
358#ifdef USE_FLOAT_IMAGES
359 uchar tile[quint64(TILE_WIDTH * TILE_HEIGHT * sizeof(QRgbaFloat32) * 1.5)];
360#else
361 uchar tile[quint64(TILE_WIDTH * TILE_HEIGHT * sizeof(QRgba64) * 1.5)];
362#endif
363
364 //! The data from tile buffer is copied to the Tile by this
365 //! method. Depending on the type of the tile (RGB, Grayscale,
366 //! Indexed) and use (image or mask), the bytes in the buffer are
367 //! copied in different ways.
368 bool (*assignBytes)(Layer &layer, uint i, uint j, const GimpPrecision &precision);
369
370 Layer(void)
371 : name(nullptr)
372 {
373 }
374 ~Layer(void)
375 {
376 delete[] name;
377 }
378
379 Layer(const Layer &) = delete;
380 Layer &operator=(const Layer &) = delete;
381
382 QImage::Format qimageFormat(const GimpPrecision precision, uint num_colors = 0, bool legacyMode = false) const
383 {
384 int bpc = bytesPerChannel(precision);
385#ifdef USE_FLOAT_IMAGES
386 bool float16 = !legacyMode && precision >= GIMP_PRECISION_HALF_LINEAR && precision <= GIMP_PRECISION_HALF_PERCEPTUAL;
387 bool float32 = !legacyMode && precision >= GIMP_PRECISION_FLOAT_LINEAR && precision <= GIMP_PRECISION_FLOAT_PERCEPTUAL;
388#endif
389
390 if (legacyMode) {
391 bpc = std::min(a: bpc, b: 1);
392 }
393
394 switch (type) {
395 case RGB_GIMAGE:
396 if (opacity == OPAQUE_OPACITY) {
397#ifdef USE_FLOAT_IMAGES
398 if (float16) {
399 return QImage::Format_RGBX16FPx4;
400 }
401 if (float32) {
402 return QImage::QImage::Format_RGBX32FPx4;
403 }
404#endif
405
406 if (bpc == 1) {
407 return QImage::Format_RGBX8888;
408 } else if (bpc == 2 || bpc == 4) {
409 return QImage::Format_RGBX64;
410 } else {
411 qCDebug(XCFPLUGIN) << "Layer has invalid bpc" << bpc << precision;
412 return QImage::Format_Invalid;
413 }
414 }
415 Q_FALLTHROUGH();
416 case RGBA_GIMAGE:
417#ifdef USE_FLOAT_IMAGES
418 if (float16) {
419 return QImage::Format_RGBA16FPx4;
420 }
421 if (float32) {
422 return QImage::QImage::Format_RGBA32FPx4;
423 }
424#endif
425 if (bpc == 1) {
426 return QImage::Format_RGBA8888;
427 } else if (bpc == 2 || bpc == 4) {
428 return QImage::Format_RGBA64;
429 } else {
430 qCDebug(XCFPLUGIN) << "Layer has invalid bpc" << bpc;
431 return QImage::Format_Invalid;
432 }
433 break;
434
435 case GRAY_GIMAGE:
436 if (opacity == OPAQUE_OPACITY) {
437 return QImage::Format_Indexed8;
438 } // else, fall through to 32-bit representation
439 Q_FALLTHROUGH();
440 case GRAYA_GIMAGE:
441 return QImage::Format_RGBA8888;
442 break;
443
444 case INDEXED_GIMAGE:
445 // As noted in the table above, there are quite a few combinations
446 // which are possible with indexed images, depending on the
447 // presence of transparency (note: not translucency, which is not
448 // supported by The GIMP for indexed images) and the number of
449 // individual colors.
450
451 // Note: Qt treats a bitmap with a Black and White color palette
452 // as a mask, so only the "on" bits are drawn, regardless of the
453 // order color table entries. Otherwise (i.e., at least one of the
454 // color table entries is not black or white), it obeys the one-
455 // or two-color palette. Have to ask about this...
456
457 if (num_colors == 1 || num_colors == 2) {
458 return QImage::Format_MonoLSB;
459 } else {
460 return QImage::Format_Indexed8;
461 }
462 break;
463
464 case INDEXEDA_GIMAGE:
465 if (num_colors == 1) {
466 return QImage::Format_MonoLSB;
467 } else {
468 return QImage::Format_Indexed8;
469 }
470 }
471 qCWarning(XCFPLUGIN) << "Unhandled layer mode" << XCFImageFormat::LayerModeType(type);
472 return QImage::Format_Invalid;
473 }
474 };
475
476 /*!
477 * The in-memory representation of the XCF Image. It contains a few
478 * metadata items, but is mostly a container for the layer information.
479 */
480 class XCFImage
481 {
482 public:
483 struct Header {
484 GimpPrecision precision = GIMP_PRECISION_U8_LINEAR; //!< Default precision (GimpPrecision)
485 quint32 width; //!< width of the XCF image
486 quint32 height; //!< height of the XCF image
487 qint32 type; //!< type of the XCF image (GimpImageBaseType)
488 } header;
489
490 XcfCompressionType compression = COMPRESS_RLE; //!< tile compression method (CompressionType)
491 float x_resolution = -1; //!< x resolution in dots per inch
492 float y_resolution = -1; //!< y resolution in dots per inch
493 qint32 tattoo; //!< (unique identifier?)
494 quint32 unit; //!< Units of The GIMP (inch, mm, pica, etc...)
495 qint32 num_colors = 0; //!< number of colors in an indexed image
496 QList<QRgb> palette; //!< indexed image color palette
497
498 int num_layers; //!< number of layers
499 Layer layer; //!< most recently read layer
500
501 bool initialized; //!< Is the QImage initialized?
502 QImage image; //!< final QImage
503
504 QHash<QString,QByteArray> parasites; //!< parasites data
505
506 XCFImage(void)
507 : initialized(false)
508 {
509 }
510
511 QImage::Format qimageFormat() const
512 {
513 return layer.qimageFormat(precision: header.precision, num_colors, legacyMode: true);
514 }
515
516 uint bytesPerChannel() const
517 {
518 return XCFImageFormat::bytesPerChannel(precision: header.precision);
519 }
520 };
521
522private:
523 static qint64 readOffsetPtr(QDataStream &stream)
524 {
525 if (stream.version() >= 11) {
526 qint64 ret;
527 stream >> ret;
528 return ret;
529 } else {
530 quint32 ret;
531 stream >> ret;
532 return ret;
533 }
534 }
535
536 //! In layer DISSOLVE mode, a random number is chosen to compare to a
537 //! pixel's alpha. If the alpha is greater than the random number, the
538 //! pixel is drawn. This table merely contains the random number seeds
539 //! for each ROW of an image. Therefore, the random numbers chosen
540 //! are consistent from run to run.
541 static int random_table[RANDOM_TABLE_SIZE];
542 static bool random_table_initialized;
543
544 static constexpr RandomTable randomTable{};
545
546 //! This table is used as a shared grayscale ramp to be set on grayscale
547 //! images. This is because Qt does not differentiate between indexed and
548 //! grayscale images.
549 static QList<QRgb> grayTable;
550
551 //! This table provides the add_pixel saturation values (i.e. 250 + 250 = 255).
552 // static int add_lut[256][256]; - this is so lame waste of 256k of memory
553 static int add_lut(int, int);
554
555 //! The bottom-most layer is copied into the final QImage by this
556 //! routine.
557 typedef void (*PixelCopyOperation)(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
558
559 //! Higher layers are merged into the final QImage by this routine.
560 typedef bool (*PixelMergeOperation)(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
561
562 static bool modeAffectsSourceAlpha(const quint32 type);
563
564 bool loadImageProperties(QDataStream &xcf_io, XCFImage &image);
565 bool loadProperty(QDataStream &xcf_io, PropType &type, QByteArray &bytes, quint32 &rawType);
566 bool loadLayer(QDataStream &xcf_io, XCFImage &xcf_image);
567 bool loadLayerProperties(QDataStream &xcf_io, Layer &layer);
568 bool composeTiles(XCFImage &xcf_image);
569 void setGrayPalette(QImage &image);
570 void setPalette(XCFImage &xcf_image, QImage &image);
571 void setImageParasites(const XCFImage &xcf_image, QImage &image);
572 static bool assignImageBytes(Layer &layer, uint i, uint j, const GimpPrecision &precision);
573 bool loadHierarchy(QDataStream &xcf_io, Layer &layer, const GimpPrecision precision);
574 bool loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, const GimpPrecision precision);
575 static bool assignMaskBytes(Layer &layer, uint i, uint j, const GimpPrecision &precision);
576 bool loadMask(QDataStream &xcf_io, Layer &layer, const GimpPrecision precision);
577 bool loadChannelProperties(QDataStream &xcf_io, Layer &layer);
578 bool initializeImage(XCFImage &xcf_image);
579 bool loadTileRLE(QDataStream &xcf_io, uchar *tile, int size, int data_length, qint32 bpp, qint64 *bytesParsed);
580
581 static void copyLayerToImage(XCFImage &xcf_image);
582 static void copyRGBToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
583 static void copyGrayToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
584 static void copyGrayToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
585 static void copyGrayAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
586 static void copyIndexedToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
587 static void copyIndexedAToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
588 static void copyIndexedAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
589
590 static void mergeLayerIntoImage(XCFImage &xcf_image);
591 static bool mergeRGBToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
592 static bool mergeGrayToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
593 static bool mergeGrayAToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
594 static bool mergeGrayToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
595 static bool mergeGrayAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
596 static bool mergeIndexedToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
597 static bool mergeIndexedAToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
598 static bool mergeIndexedAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n);
599
600 static void initializeRandomTable();
601 static void dissolveRGBPixels(QImage &image, int x, int y);
602 static void dissolveAlphaPixels(QImage &image, int x, int y);
603
604 static uint bytesPerChannel(const GimpPrecision precision)
605 {
606 switch (precision) {
607 case GIMP_PRECISION_U8_LINEAR:
608 case GIMP_PRECISION_U8_NON_LINEAR:
609 case GIMP_PRECISION_U8_PERCEPTUAL:
610 return 1;
611 break;
612 case GIMP_PRECISION_U16_LINEAR:
613 case GIMP_PRECISION_U16_NON_LINEAR:
614 case GIMP_PRECISION_U16_PERCEPTUAL:
615 case GIMP_PRECISION_HALF_LINEAR:
616 case GIMP_PRECISION_HALF_NON_LINEAR:
617 case GIMP_PRECISION_HALF_PERCEPTUAL:
618 return 2;
619 break;
620
621 case GIMP_PRECISION_U32_LINEAR:
622 case GIMP_PRECISION_U32_NON_LINEAR:
623 case GIMP_PRECISION_U32_PERCEPTUAL:
624 case GIMP_PRECISION_FLOAT_LINEAR:
625 case GIMP_PRECISION_FLOAT_NON_LINEAR:
626 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
627 return 4;
628 break;
629 case GIMP_PRECISION_DOUBLE_LINEAR:
630 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
631 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
632 return 8;
633 break;
634
635 default:
636 qCDebug(XCFPLUGIN) << "Layer has invalid precision" << precision;
637 return 0;
638 }
639 }
640
641public:
642 static bool readXCFHeader(QDataStream &ds, XCFImage::Header *header);
643};
644
645int XCFImageFormat::random_table[RANDOM_TABLE_SIZE];
646bool XCFImageFormat::random_table_initialized;
647
648constexpr RandomTable XCFImageFormat::randomTable;
649
650QList<QRgb> XCFImageFormat::grayTable;
651
652bool XCFImageFormat::modeAffectsSourceAlpha(const quint32 type)
653{
654 switch (type) {
655 case GIMP_LAYER_MODE_NORMAL_LEGACY:
656 case GIMP_LAYER_MODE_DISSOLVE:
657 case GIMP_LAYER_MODE_BEHIND_LEGACY:
658 return true;
659
660 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
661 case GIMP_LAYER_MODE_SCREEN_LEGACY:
662 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
663 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
664 case GIMP_LAYER_MODE_ADDITION_LEGACY:
665 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
666 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
667 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
668 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
669 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
670 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
671 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
672 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
673 case GIMP_LAYER_MODE_DODGE_LEGACY:
674 case GIMP_LAYER_MODE_BURN_LEGACY:
675 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
676 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
677 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
678 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
679 return false;
680
681 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
682 case GIMP_LAYER_MODE_OVERLAY:
683 case GIMP_LAYER_MODE_LCH_HUE:
684 case GIMP_LAYER_MODE_LCH_CHROMA:
685 case GIMP_LAYER_MODE_LCH_COLOR:
686 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
687 return false;
688
689 case GIMP_LAYER_MODE_NORMAL:
690 return true;
691
692 case GIMP_LAYER_MODE_BEHIND:
693 case GIMP_LAYER_MODE_MULTIPLY:
694 case GIMP_LAYER_MODE_SCREEN:
695 case GIMP_LAYER_MODE_DIFFERENCE:
696 case GIMP_LAYER_MODE_ADDITION:
697 case GIMP_LAYER_MODE_SUBTRACT:
698 case GIMP_LAYER_MODE_DARKEN_ONLY:
699 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
700 case GIMP_LAYER_MODE_HSV_HUE:
701 case GIMP_LAYER_MODE_HSV_SATURATION:
702 case GIMP_LAYER_MODE_HSL_COLOR:
703 case GIMP_LAYER_MODE_HSV_VALUE:
704 case GIMP_LAYER_MODE_DIVIDE:
705 case GIMP_LAYER_MODE_DODGE:
706 case GIMP_LAYER_MODE_BURN:
707 case GIMP_LAYER_MODE_HARDLIGHT:
708 case GIMP_LAYER_MODE_SOFTLIGHT:
709 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
710 case GIMP_LAYER_MODE_GRAIN_MERGE:
711 case GIMP_LAYER_MODE_VIVID_LIGHT:
712 case GIMP_LAYER_MODE_PIN_LIGHT:
713 case GIMP_LAYER_MODE_LINEAR_LIGHT:
714 case GIMP_LAYER_MODE_HARD_MIX:
715 case GIMP_LAYER_MODE_EXCLUSION:
716 case GIMP_LAYER_MODE_LINEAR_BURN:
717 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
718 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
719 case GIMP_LAYER_MODE_LUMINANCE:
720 case GIMP_LAYER_MODE_COLOR_ERASE:
721 case GIMP_LAYER_MODE_ERASE:
722 case GIMP_LAYER_MODE_MERGE:
723 case GIMP_LAYER_MODE_SPLIT:
724 case GIMP_LAYER_MODE_PASS_THROUGH:
725 return false;
726
727 default:
728 qCWarning(XCFPLUGIN) << "Unhandled layer mode" << XCFImageFormat::LayerModeType(type);
729 return false;
730 }
731}
732
733//! Change a QRgb value's alpha only.
734inline QRgb qRgba(const QRgb rgb, int a)
735{
736 return ((a & 0xff) << 24 | (rgb & RGB_MASK));
737}
738
739/*!
740 * The constructor for the XCF image loader.
741 */
742XCFImageFormat::XCFImageFormat()
743{
744 static_assert(sizeof(QRgb) == 4, "the code assumes sizeof(QRgb) == 4, if that's not your case, help us fix it :)");
745}
746
747/*!
748 * This initializes the tables used in the layer dissolving routines.
749 */
750void XCFImageFormat::initializeRandomTable()
751{
752 // From GIMP "paint_funcs.c" v1.2
753 srand(seed: RANDOM_SEED);
754
755 for (int i = 0; i < RANDOM_TABLE_SIZE; i++) {
756 random_table[i] = rand();
757 }
758
759 for (int i = 0; i < RANDOM_TABLE_SIZE; i++) {
760 int tmp;
761 int swap = i + rand() % (RANDOM_TABLE_SIZE - i);
762 tmp = random_table[i];
763 random_table[i] = random_table[swap];
764 random_table[swap] = tmp;
765 }
766}
767
768inline int XCFImageFormat::add_lut(int a, int b)
769{
770 return qMin(a: a + b, b: 255);
771}
772
773bool XCFImageFormat::readXCFHeader(QDataStream &xcf_io, XCFImage::Header *header)
774{
775 QByteArray tag(14, '\0');
776
777 if (xcf_io.readRawData(tag.data(), len: tag.size()) != tag.size()) {
778 qCDebug(XCFPLUGIN) << "XCF: read failure on header tag";
779 return false;
780 }
781 if (!tag.startsWith(bv: "gimp xcf") || !tag.endsWith(c: '\0')) {
782 qCDebug(XCFPLUGIN) << "XCF: read called on non-XCF file";
783 return false;
784 }
785
786 // Remove null terminator
787 tag.chop(n: 1);
788
789 if (tag.right(n: 4) == "file") {
790 xcf_io.setVersion(0);
791 } else {
792 // Version 1 and onwards use the format "gimp xcf v###" instead of "gimp xcf file"
793 bool ok;
794 xcf_io.setVersion(tag.right(n: 3).toInt(ok: &ok));
795 if (!ok) {
796 qCDebug(XCFPLUGIN) << "Failed to parse version" << tag;
797 return false;
798 }
799 }
800 qCDebug(XCFPLUGIN) << "version" << xcf_io.version();
801
802 if (xcf_io.version() > 12) {
803 qCDebug(XCFPLUGIN) << "Unsupported version" << xcf_io.version();
804 return false;
805 }
806
807 xcf_io >> header->width >> header->height >> header->type;
808
809 if (xcf_io.version() >= 4) {
810 int precision;
811 xcf_io >> precision;
812 qCDebug(XCFPLUGIN) << "Precision" << GimpPrecision(precision);
813 if (xcf_io.version() < 7) {
814 switch (precision) {
815 case 0:
816 precision = GIMP_PRECISION_U8_NON_LINEAR;
817 break;
818 case 1:
819 precision = GIMP_PRECISION_U16_NON_LINEAR;
820 break;
821 case 2:
822 precision = GIMP_PRECISION_U32_LINEAR;
823 break;
824 case 3:
825 precision = GIMP_PRECISION_HALF_LINEAR;
826 break;
827 case 4:
828 precision = GIMP_PRECISION_FLOAT_LINEAR;
829 break;
830 default:
831 if (precision < GIMP_PRECISION_U8_LINEAR) {
832 qCWarning(XCFPLUGIN) << "Invalid precision read" << precision;
833 return false;
834 } else {
835 qCDebug(XCFPLUGIN) << "Unexpected precision" << precision << "in version" << xcf_io.version();
836 }
837 }
838 }
839 header->precision = GimpPrecision(precision);
840 }
841 qCDebug(XCFPLUGIN) << "tag:" << tag << " height: " << header->width << " width: " << header->height << " type: " << header->type;
842
843 if ((sizeof(void *) == 4 && qint64(header->width) * header->height > 16384 * 16384)) {
844 qCWarning(XCFPLUGIN) << "On 32-bits programs the maximum image size is limited to" << 16384 << "x" << 16384 << "px";
845 return false;
846 }
847
848 if (header->width > XCF_MAX_IMAGE_WIDTH || header->height > XCF_MAX_IMAGE_HEIGHT) {
849 qCWarning(XCFPLUGIN) << "The maximum image size is limited to" << XCF_MAX_IMAGE_WIDTH << "x" << XCF_MAX_IMAGE_HEIGHT << "px";
850 return false;
851 }
852
853 return true;
854}
855
856bool XCFImageFormat::readXCF(QIODevice *device, QImage *outImage)
857{
858 XCFImage xcf_image;
859 QDataStream xcf_io(device);
860
861 if (!readXCFHeader(xcf_io, header: &xcf_image.header)) {
862 return false;
863 }
864
865 if (!loadImageProperties(xcf_io, image&: xcf_image)) {
866 return false;
867 }
868
869 // The layers appear to be stored in top-to-bottom order. This is
870 // the reverse of how a merged image must be computed. So, the layer
871 // offsets are pushed onto a LIFO stack (thus, we don't have to load
872 // all the data of all layers before beginning to construct the
873 // merged image).
874
875 QStack<qint64> layer_offsets;
876
877 while (true) {
878 const qint64 layer_offset = readOffsetPtr(stream&: xcf_io);
879
880 if (layer_offset == 0) {
881 break;
882 }
883
884 if (layer_offset < 0) {
885 qCDebug(XCFPLUGIN) << "XCF: negative layer offset";
886 return false;
887 }
888
889 layer_offsets.push(t: layer_offset);
890 }
891
892 xcf_image.num_layers = layer_offsets.size();
893
894 if (layer_offsets.size() == 0) {
895 qCDebug(XCFPLUGIN) << "XCF: no layers!";
896 return false;
897 }
898 qCDebug(XCFPLUGIN) << xcf_image.num_layers << "layers";
899
900 // Load each layer and add it to the image
901 while (!layer_offsets.isEmpty()) {
902 qint64 layer_offset = layer_offsets.pop();
903
904 if (!xcf_io.device()->seek(pos: layer_offset)) {
905 return false;
906 }
907
908 if (!loadLayer(xcf_io, xcf_image)) {
909 return false;
910 }
911 }
912
913 if (!xcf_image.initialized) {
914 qCDebug(XCFPLUGIN) << "XCF: no visible layers!";
915 return false;
916 }
917
918 // The image was created: now I can set metadata and ICC color profile inside it.
919 setImageParasites(xcf_image, image&: xcf_image.image);
920
921 *outImage = xcf_image.image;
922 return true;
923}
924
925/*!
926 * An XCF file can contain an arbitrary number of properties associated
927 * with the image (and layer and mask).
928 * \param xcf_io the data stream connected to the XCF image
929 * \param xcf_image XCF image data.
930 * \return true if there were no I/O errors.
931 */
932bool XCFImageFormat::loadImageProperties(QDataStream &xcf_io, XCFImage &xcf_image)
933{
934 while (true) {
935 PropType type;
936 QByteArray bytes;
937 quint32 rawType;
938
939 if (!loadProperty(xcf_io, type, bytes, rawType)) {
940 qCDebug(XCFPLUGIN) << "XCF: error loading global image properties";
941 return false;
942 }
943
944 QDataStream property(bytes);
945
946 switch (type) {
947 case PROP_END:
948 return true;
949
950 case PROP_COMPRESSION:
951 property >> xcf_image.compression;
952 break;
953
954 case PROP_RESOLUTION:
955 property.setFloatingPointPrecision(QDataStream::SinglePrecision);
956 property >> xcf_image.x_resolution >> xcf_image.y_resolution;
957 break;
958
959 case PROP_TATTOO:
960 property >> xcf_image.tattoo;
961 break;
962
963 case PROP_PARASITES:
964 while (!property.atEnd()) {
965 char *tag;
966 qint64 size;
967
968 property.readBytes(tag, len&: size);
969
970 quint32 flags;
971 QByteArray data;
972 property >> flags >> data;
973
974 // WARNING: you cannot add metadata to QImage here because it can be null.
975 // Adding a metadata to a QImage when it is null, does nothing (metas are lost).
976 if (tag) // store metadata for future use
977 xcf_image.parasites.insert(key: QString::fromUtf8(utf8: tag), value: data);
978
979 delete[] tag;
980 }
981 break;
982
983 case PROP_UNIT:
984 property >> xcf_image.unit;
985 break;
986
987 case PROP_PATHS: // This property is ignored.
988 break;
989
990 case PROP_USER_UNIT: // This property is ignored.
991 break;
992
993 case PROP_COLORMAP:
994 property >> xcf_image.num_colors;
995 if (xcf_image.num_colors < 0 || xcf_image.num_colors > 65535) {
996 return false;
997 }
998
999 xcf_image.palette = QList<QRgb>();
1000 xcf_image.palette.reserve(asize: xcf_image.num_colors);
1001
1002 for (int i = 0; i < xcf_image.num_colors; i++) {
1003 uchar r;
1004 uchar g;
1005 uchar b;
1006 property >> r >> g >> b;
1007 xcf_image.palette.push_back(t: qRgb(r, g, b));
1008 }
1009 break;
1010
1011 default:
1012 qCDebug(XCFPLUGIN) << "XCF: unimplemented image property" << type << "(" << rawType << ")"
1013 << ", size " << bytes.size();
1014 break;
1015 }
1016 }
1017}
1018
1019/*!
1020 * Read a single property from the image file. The property type is returned
1021 * in type and the data is returned in bytes.
1022 * \param xcf the image file data stream.
1023 * \param type returns with the property type.
1024 * \param bytes returns with the property data.
1025 * \return true if there were no IO errors. */
1026bool XCFImageFormat::loadProperty(QDataStream &xcf_io, PropType &type, QByteArray &bytes, quint32 &rawType)
1027{
1028 quint32 size;
1029
1030 xcf_io >> rawType;
1031 if (rawType >= MAX_SUPPORTED_PROPTYPE) {
1032 type = MAX_SUPPORTED_PROPTYPE;
1033 // we don't support the property, but we still need to read from the device, assume it's like all the
1034 // non custom properties that is data_length + data
1035 xcf_io >> size;
1036 xcf_io.skipRawData(len: size);
1037 // return true because we don't really want to totally fail on an unsupported property since it may not be fatal
1038 return true;
1039 }
1040
1041 type = PropType(rawType);
1042
1043 char *data = nullptr;
1044
1045 // The colormap property size is not the correct number of bytes:
1046 // The GIMP source xcf.c has size = 4 + ncolors, but it should be
1047 // 4 + 3 * ncolors
1048
1049 if (type == PROP_COLORMAP) {
1050 xcf_io >> size;
1051 quint32 ncolors;
1052 xcf_io >> ncolors;
1053
1054 size = 3 * ncolors + 4;
1055
1056 if (size > 65535 || size < 4) {
1057 return false;
1058 }
1059
1060 data = new char[size];
1061
1062 // since we already read "ncolors" from the stream, we put that data back
1063 data[0] = 0;
1064 data[1] = 0;
1065 data[2] = ncolors >> 8;
1066 data[3] = ncolors & 255;
1067
1068 // ... and read the remaining bytes from the stream
1069 xcf_io.readRawData(data + 4, len: size - 4);
1070 } else if (type == PROP_USER_UNIT) {
1071 // The USER UNIT property size is not correct. I'm not sure why, though.
1072 float factor;
1073 qint32 digits;
1074
1075 xcf_io >> size >> factor >> digits;
1076
1077 for (int i = 0; i < 5; i++) {
1078 char *unit_strings;
1079
1080 xcf_io >> unit_strings;
1081
1082 delete[] unit_strings;
1083
1084 if (xcf_io.device()->atEnd()) {
1085 qCDebug(XCFPLUGIN) << "XCF: read failure on property " << type;
1086 return false;
1087 }
1088 }
1089
1090 size = 0;
1091 } else {
1092 xcf_io >> size;
1093 if (size > 256000 * 4) {
1094 // NOTE: I didn't find any reference to maximum property dimensions in the specs, so I assume it's just a sanity check.
1095 qCDebug(XCFPLUGIN) << "XCF: loadProperty skips" << type << "due to size being too large";
1096 return false;
1097 }
1098 data = new char[size];
1099 const quint32 dataRead = xcf_io.readRawData(data, len: size);
1100 if (dataRead < size) {
1101 qCDebug(XCFPLUGIN) << "XCF: loadProperty read less data than expected" << size << dataRead;
1102 memset(s: &data[dataRead], c: 0, n: size - dataRead);
1103 }
1104 }
1105
1106 if (size != 0 && data) {
1107 bytes = QByteArray(data, size);
1108 }
1109
1110 delete[] data;
1111
1112 return true;
1113}
1114
1115/*!
1116 * Load a layer from the XCF file. The data stream must be positioned at
1117 * the beginning of the layer data.
1118 * \param xcf_io the image file data stream.
1119 * \param xcf_image contains the layer and the color table
1120 * (if the image is indexed).
1121 * \return true if there were no I/O errors.
1122 */
1123bool XCFImageFormat::loadLayer(QDataStream &xcf_io, XCFImage &xcf_image)
1124{
1125 Layer &layer(xcf_image.layer);
1126 delete[] layer.name;
1127
1128 xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
1129
1130 // Don't want to keep passing this around, dumb XCF format
1131 layer.compression = XcfCompressionType(xcf_image.compression);
1132
1133 if (!loadLayerProperties(xcf_io, layer)) {
1134 return false;
1135 }
1136
1137 qCDebug(XCFPLUGIN) << "layer: \"" << layer.name << "\", size: " << layer.width << " x " << layer.height << ", type: " << layer.type
1138 << ", mode: " << layer.mode << ", opacity: " << layer.opacity << ", visible: " << layer.visible << ", offset: " << layer.x_offset << ", "
1139 << layer.y_offset << ", compression" << layer.compression;
1140
1141 // Skip reading the rest of it if it is not visible. Typically, when
1142 // you export an image from the The GIMP it flattens (or merges) only
1143 // the visible layers into the output image.
1144
1145 if (layer.visible == 0) {
1146 return true;
1147 }
1148
1149 // If there are any more layers, merge them into the final QImage.
1150
1151 layer.hierarchy_offset = readOffsetPtr(stream&: xcf_io);
1152 layer.mask_offset = readOffsetPtr(stream&: xcf_io);
1153
1154 if (layer.hierarchy_offset < 0) {
1155 qCDebug(XCFPLUGIN) << "XCF: negative layer hierarchy_offset";
1156 return false;
1157 }
1158
1159 if (layer.mask_offset < 0) {
1160 qCDebug(XCFPLUGIN) << "XCF: negative layer mask_offset";
1161 return false;
1162 }
1163
1164 // Allocate the individual tile QImages based on the size and type
1165 // of this layer.
1166
1167 if (!composeTiles(xcf_image)) {
1168 return false;
1169 }
1170 if (!xcf_io.device()->seek(pos: layer.hierarchy_offset)) {
1171 return false;
1172 }
1173
1174 // As tiles are loaded, they are copied into the layers tiles by
1175 // this routine. (loadMask(), below, uses a slightly different
1176 // version of assignBytes().)
1177
1178 layer.assignBytes = assignImageBytes;
1179
1180 if (!loadHierarchy(xcf_io, layer, precision: xcf_image.header.precision)) {
1181 return false;
1182 }
1183
1184 if (layer.mask_offset != 0) {
1185 // 9 means its not on the file. Spec says "If the property does not appear for a layer which has a layer mask, it defaults to true (1).
1186 if (layer.apply_mask == 9) {
1187 layer.apply_mask = 1;
1188 }
1189
1190 if (!xcf_io.device()->seek(pos: layer.mask_offset)) {
1191 return false;
1192 }
1193
1194 if (!loadMask(xcf_io, layer, precision: xcf_image.header.precision)) {
1195 return false;
1196 }
1197 } else {
1198 // Spec says "Robust readers should force this to false if the layer has no layer mask."
1199 layer.apply_mask = 0;
1200 }
1201
1202 // Now we should have enough information to initialize the final
1203 // QImage. The first visible layer determines the attributes
1204 // of the QImage.
1205
1206 if (!xcf_image.initialized) {
1207 if (!initializeImage(xcf_image)) {
1208 return false;
1209 }
1210 copyLayerToImage(xcf_image);
1211 xcf_image.initialized = true;
1212 } else {
1213 const QColorSpace colorspaceBefore = xcf_image.image.colorSpace();
1214 mergeLayerIntoImage(xcf_image);
1215 if (xcf_image.image.colorSpace() != colorspaceBefore) {
1216 qCDebug(XCFPLUGIN) << "Converting color space back to" << colorspaceBefore << "after layer composition";
1217 xcf_image.image.convertToColorSpace(colorSpace: colorspaceBefore);
1218 }
1219 }
1220
1221 return true;
1222}
1223
1224/*!
1225 * An XCF file can contain an arbitrary number of properties associated
1226 * with a layer.
1227 * \param xcf_io the data stream connected to the XCF image.
1228 * \param layer layer to collect the properties.
1229 * \return true if there were no I/O errors.
1230 */
1231bool XCFImageFormat::loadLayerProperties(QDataStream &xcf_io, Layer &layer)
1232{
1233 while (true) {
1234 PropType type;
1235 QByteArray bytes;
1236 quint32 rawType;
1237
1238 if (!loadProperty(xcf_io, type, bytes, rawType)) {
1239 qCDebug(XCFPLUGIN) << "XCF: error loading layer properties";
1240 return false;
1241 }
1242
1243 QDataStream property(bytes);
1244
1245 switch (type) {
1246 case PROP_END:
1247 return true;
1248
1249 case PROP_ACTIVE_LAYER:
1250 layer.active = true;
1251 break;
1252
1253 case PROP_OPACITY:
1254 property >> layer.opacity;
1255 layer.opacity = std::min(a: layer.opacity, b: 255u);
1256 break;
1257
1258 case PROP_FLOAT_OPACITY:
1259 // For some reason QDataStream isn't able to read the float (tried
1260 // setting the endianness manually)
1261 if (bytes.size() == 4) {
1262 layer.opacityFloat = qFromBigEndian(source: *reinterpret_cast<float *>(bytes.data()));
1263 } else {
1264 qCDebug(XCFPLUGIN) << "XCF: Invalid data size for float:" << bytes.size();
1265 }
1266 break;
1267
1268 case PROP_VISIBLE:
1269 property >> layer.visible;
1270 break;
1271
1272 case PROP_LINKED:
1273 property >> layer.linked;
1274 break;
1275
1276 case PROP_LOCK_ALPHA:
1277 property >> layer.preserve_transparency;
1278 break;
1279
1280 case PROP_APPLY_MASK:
1281 property >> layer.apply_mask;
1282 break;
1283
1284 case PROP_EDIT_MASK:
1285 property >> layer.edit_mask;
1286 break;
1287
1288 case PROP_SHOW_MASK:
1289 property >> layer.show_mask;
1290 break;
1291
1292 case PROP_OFFSETS:
1293 property >> layer.x_offset >> layer.y_offset;
1294 break;
1295
1296 case PROP_MODE:
1297 property >> layer.mode;
1298 if (layer.mode >= GIMP_LAYER_MODE_COUNT) {
1299 qCDebug(XCFPLUGIN) << "Found layer with unsupported mode" << LayerModeType(layer.mode) << "Defaulting to mode 0";
1300 layer.mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
1301 }
1302 break;
1303
1304 case PROP_TATTOO:
1305 property >> layer.tattoo;
1306 break;
1307
1308 case PROP_COMPOSITE_SPACE:
1309 property >> layer.compositeSpace;
1310 if (layer.compositeSpace < 0) {
1311 layer.compositeSpace = GimpColorSpace(layer.compositeSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeSpace);
1312 }
1313 break;
1314
1315 case PROP_COMPOSITE_MODE:
1316 property >> layer.compositeMode;
1317 if (layer.compositeMode < 0) {
1318 layer.compositeMode =
1319 XCFImageFormat::GimpCompositeMode(layer.compositeMode == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeMode);
1320 }
1321 break;
1322
1323 case PROP_BLEND_SPACE:
1324 property >> layer.blendSpace;
1325 if (layer.blendSpace < 0) {
1326 layer.blendSpace = GimpColorSpace(layer.blendSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.blendSpace);
1327 }
1328 break;
1329
1330 // Just for organization in the UI, doesn't influence rendering
1331 case PROP_COLOR_TAG:
1332 break;
1333
1334 // We don't support editing, so for now just ignore locking
1335 case PROP_LOCK_CONTENT:
1336 case PROP_LOCK_POSITION:
1337 break;
1338
1339 default:
1340 qCDebug(XCFPLUGIN) << "XCF: unimplemented layer property " << type << "(" << rawType << ")"
1341 << ", size " << bytes.size();
1342 break;
1343 }
1344 }
1345}
1346
1347/*!
1348 * Compute the number of tiles in the current layer and allocate
1349 * QImage structures for each of them.
1350 * \param xcf_image contains the current layer.
1351 */
1352bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
1353{
1354 Layer &layer(xcf_image.layer);
1355
1356 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
1357 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
1358
1359 qCDebug(XCFPLUGIN) << "IMAGE: height=" << xcf_image.header.height << ", width=" << xcf_image.header.width;
1360 qCDebug(XCFPLUGIN) << "LAYER: height=" << layer.height << ", width=" << layer.width;
1361 qCDebug(XCFPLUGIN) << "LAYER: rows=" << layer.nrows << ", columns=" << layer.ncols;
1362
1363 // NOTE: starting from GIMP 2.10, images can be very large. The 32K limit for width and height is obsolete
1364 // and it was changed to 300000 (the same as Photoshop Big image). This plugin was able to open an RGB
1365 // image of 108000x40000 pixels saved with GIMP 2.10
1366 // SANITY CHECK: Catch corrupted XCF image file where the width or height
1367 // of a tile is reported are bogus. See Bug# 234030.
1368 if ((sizeof(void *) == 4 && qint64(layer.width) * layer.height > 16384 * 16384)) {
1369 qCWarning(XCFPLUGIN) << "On 32-bits programs the maximum layer size is limited to" << 16384 << "x" << 16384 << "px";
1370 return false;
1371 }
1372 if (layer.width > XCF_MAX_IMAGE_WIDTH || layer.height > XCF_MAX_IMAGE_HEIGHT) {
1373 qCWarning(XCFPLUGIN) << "The maximum layer size is limited to" << XCF_MAX_IMAGE_WIDTH << "x" << XCF_MAX_IMAGE_HEIGHT << "px";
1374 return false;
1375 }
1376
1377 // NOTE: A layer is a named rectangular area of pixels which has a definite position with respect to the canvas.
1378 // It may extend beyond the canvas or (more commonly) only cover some of it.
1379 // SANITY CHECK: Avoid to load XCF with a layer grater than 10 times the final image
1380 if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
1381 if (qint64(layer.width) * layer.height > 16384 * 16384) { // large layers only
1382 qCWarning(XCFPLUGIN) << "Heuristic sanity check: the image may be corrupted!";
1383 return false;
1384 }
1385 }
1386
1387#ifndef XCF_QT5_SUPPORT
1388 // Qt 6 image allocation limit calculation: we have to check the limit here because the image is split in
1389 // tiles of 64x64 pixels. The required memory to build the image is at least doubled because tiles are loaded
1390 // and then the final image is created by copying the tiles inside it.
1391 // NOTE: on Windows to open a 10GiB image the plugin uses 28GiB of RAM
1392 const qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
1393 const int allocationLimit = QImageReader::allocationLimit();
1394 if (allocationLimit > 0) {
1395 if (qint64(layer.width) * qint64(layer.height) * channels * 2ll / 1024ll / 1024ll > allocationLimit) {
1396 qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit of" << allocationLimit << "megabytes";
1397 return false;
1398 }
1399 }
1400#endif
1401
1402 layer.image_tiles.resize(size: layer.nrows);
1403
1404 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1405 layer.alpha_tiles.resize(size: layer.nrows);
1406 }
1407
1408 if (layer.mask_offset != 0) {
1409 layer.mask_tiles.resize(size: layer.nrows);
1410 }
1411
1412 for (uint j = 0; j < layer.nrows; j++) {
1413 layer.image_tiles[j].resize(size: layer.ncols);
1414
1415 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1416 layer.alpha_tiles[j].resize(size: layer.ncols);
1417 }
1418
1419 if (layer.mask_offset != 0) {
1420 layer.mask_tiles[j].resize(size: layer.ncols);
1421 }
1422 }
1423
1424 const QImage::Format format = layer.qimageFormat(precision: xcf_image.header.precision);
1425
1426 for (uint j = 0; j < layer.nrows; j++) {
1427 for (uint i = 0; i < layer.ncols; i++) {
1428 uint tile_width = (i + 1) * TILE_WIDTH <= layer.width ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
1429
1430 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
1431
1432 // Try to create the most appropriate QImage (each GIMP layer
1433 // type is treated slightly differently)
1434
1435 switch (layer.type) {
1436 case RGB_GIMAGE:
1437 case RGBA_GIMAGE:
1438 layer.image_tiles[j][i] = QImage(tile_width, tile_height, format);
1439 if (layer.image_tiles[j][i].isNull()) {
1440 return false;
1441 }
1442 layer.image_tiles[j][i].setColorCount(0);
1443 layer.image_tiles[j][i].fill(pixel: 0);
1444 break;
1445
1446 case GRAY_GIMAGE:
1447 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1448 if (layer.image_tiles[j][i].isNull()) {
1449 return false;
1450 }
1451 layer.image_tiles[j][i].setColorCount(256);
1452 layer.image_tiles[j][i].fill(pixel: 0);
1453 setGrayPalette(layer.image_tiles[j][i]);
1454 break;
1455
1456 case GRAYA_GIMAGE:
1457 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1458 if (layer.image_tiles[j][i].isNull()) {
1459 return false;
1460 }
1461 layer.image_tiles[j][i].setColorCount(256);
1462 layer.image_tiles[j][i].fill(pixel: 0);
1463 setGrayPalette(layer.image_tiles[j][i]);
1464
1465 layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1466 if (layer.alpha_tiles[j][i].isNull()) {
1467 return false;
1468 }
1469 layer.alpha_tiles[j][i].setColorCount(256);
1470 layer.alpha_tiles[j][i].fill(pixel: 0);
1471 setGrayPalette(layer.alpha_tiles[j][i]);
1472 break;
1473
1474 case INDEXED_GIMAGE:
1475 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1476 if (layer.image_tiles[j][i].isNull()) {
1477 return false;
1478 }
1479 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1480 layer.image_tiles[j][i].fill(pixel: 0);
1481 setPalette(xcf_image, image&: layer.image_tiles[j][i]);
1482 break;
1483
1484 case INDEXEDA_GIMAGE:
1485 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1486 if (layer.image_tiles[j][i].isNull()) {
1487 return false;
1488 }
1489 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1490 layer.image_tiles[j][i].fill(pixel: 0);
1491 setPalette(xcf_image, image&: layer.image_tiles[j][i]);
1492
1493 layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1494 if (layer.alpha_tiles[j][i].isNull()) {
1495 return false;
1496 }
1497 layer.alpha_tiles[j][i].setColorCount(256);
1498 layer.alpha_tiles[j][i].fill(pixel: 0);
1499 setGrayPalette(layer.alpha_tiles[j][i]);
1500 }
1501 if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
1502 qCWarning(XCFPLUGIN) << "Selected wrong tile format" << layer.image_tiles[j][i].format() << "expected" << format;
1503 return false;
1504 }
1505
1506#ifndef DISABLE_TILE_PROFILE
1507 switch (xcf_image.header.precision) {
1508 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1509 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1510 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1511 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1512 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1513 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1514 layer.image_tiles[j][i].setColorSpace(QColorSpace::SRgbLinear);
1515 break;
1516 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
1517 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
1518 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
1519 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
1520 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
1521 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
1522 layer.image_tiles[j][i].setColorSpace(QColorSpace::SRgb);
1523 break;
1524 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
1525 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
1526 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
1527 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
1528 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
1529 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
1530 layer.image_tiles[j][i].setColorSpace(QColorSpace::SRgb);
1531 break;
1532 }
1533#endif
1534 if (layer.mask_offset != 0) {
1535 layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1536 if (layer.mask_tiles[j][i].isNull()) {
1537 return false;
1538 }
1539 layer.mask_tiles[j][i].setColorCount(256);
1540 layer.mask_tiles[j][i].fill(pixel: 0);
1541 setGrayPalette(layer.mask_tiles[j][i]);
1542 }
1543 }
1544 }
1545 return true;
1546}
1547
1548/*!
1549 * Apply a grayscale palette to the QImage. Note that Qt does not distinguish
1550 * between grayscale and indexed images. A grayscale image is just
1551 * an indexed image with a 256-color, grayscale palette.
1552 * \param image image to set to a grayscale palette.
1553 */
1554void XCFImageFormat::setGrayPalette(QImage &image)
1555{
1556 if (grayTable.isEmpty()) {
1557 grayTable.resize(size: 256);
1558
1559 for (int i = 0; i < 256; i++) {
1560 grayTable[i] = qRgb(r: i, g: i, b: i);
1561 }
1562 }
1563
1564 image.setColorTable(grayTable);
1565}
1566
1567/*!
1568 * Copy the indexed palette from the XCF image into the QImage.
1569 * \param xcf_image XCF image containing the palette read from the data stream.
1570 * \param image image to apply the palette to.
1571 */
1572void XCFImageFormat::setPalette(XCFImage &xcf_image, QImage &image)
1573{
1574 Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size());
1575
1576 image.setColorTable(xcf_image.palette);
1577}
1578
1579/*!
1580 * Copy the parasites info to QImage.
1581 * \param xcf_image XCF image containing the parasites read from the data stream.
1582 * \param image image to apply the parasites data.
1583 * \note Some comment taken from https://gitlab.gnome.org/GNOME/gimp/-/blob/master/devel-docs/parasites.txt
1584 */
1585void XCFImageFormat::setImageParasites(const XCFImage &xcf_image, QImage &image)
1586{
1587 auto&& p = xcf_image.parasites;
1588 auto keys = p.keys();
1589 for (auto &&key : std::as_const(t&: keys)) {
1590 auto value = p.value(key);
1591 if (value.isEmpty())
1592 continue;
1593
1594 // "icc-profile" (IMAGE, PERSISTENT | UNDOABLE)
1595 // This contains an ICC profile describing the color space the
1596 // image was produced in. TIFF images stored in PhotoShop do
1597 // oftentimes contain embedded profiles. An experimental color
1598 // manager exists to use this parasite, and it will be used
1599 // for interchange between TIFF and PNG (identical profiles)
1600 if (key == QStringLiteral("icc-profile")) {
1601 auto cs = QColorSpace::fromIccProfile(iccProfile: value);
1602 if (cs.isValid())
1603 image.setColorSpace(cs);
1604 continue;
1605 }
1606
1607 // "gimp-comment" (IMAGE, PERSISTENT)
1608 // Standard GIF-style image comments. This parasite should be
1609 // human-readable text in UTF-8 encoding. A trailing \0 might
1610 // be included and is not part of the comment. Note that image
1611 // comments may also be present in the "gimp-metadata" parasite.
1612 if (key == QStringLiteral("gimp-comment")) {
1613 value.replace(before: '\0', after: QByteArray());
1614 image.setText(QStringLiteral(META_KEY_COMMENT), value: QString::fromUtf8(ba: value));
1615 continue;
1616 }
1617
1618 // "gimp-image-metadata"
1619 // Saved by GIMP 2.10.30 but it is not mentioned in the specification.
1620 // It is an XML block with the properties set using GIMP.
1621 if (key == QStringLiteral("gimp-image-metadata")) {
1622 // NOTE: I arbitrary defined the metadata "XML:org.gimp.xml" because it seems
1623 // a GIMP proprietary XML format (no xmlns defined)
1624 value.replace(before: '\0', after: QByteArray());
1625 image.setText(QStringLiteral(META_KEY_XML_GIMP), value: QString::fromUtf8(ba: value));
1626 continue;
1627 }
1628
1629#if 0 // Unable to generate it using latest GIMP version
1630 // "gimp-metadata" (IMAGE, PERSISTENT)
1631 // The metadata associated with the image, serialized as one XMP
1632 // packet. This metadata includes the contents of any XMP, EXIF
1633 // and IPTC blocks from the original image, as well as
1634 // user-specified values such as image comment, copyright,
1635 // license, etc.
1636 if (key == QStringLiteral("gimp-metadata")) {
1637 // NOTE: "XML:com.adobe.xmp" is the meta set by Qt reader when an
1638 // XMP packet is found (e.g. when reading a PNG saved by Photoshop).
1639 // I reused the same key because some programs could search for it.
1640 value.replace('\0', QByteArray());
1641 image.setText(QStringLiteral(META_KEY_XMP_ADOBE), QString::fromUtf8(value));
1642 continue;
1643 }
1644#endif
1645 }
1646
1647#ifdef DISABLE_IMAGE_PROFILE
1648 // final colorspace checks
1649 if (!image.colorSpace().isValid()) {
1650 switch (xcf_image.header.precision) {
1651 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1652 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1653 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1654 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1655 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1656 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1657 image.setColorSpace(QColorSpace::SRgbLinear);
1658 break;
1659 default:
1660 image.setColorSpace(QColorSpace::SRgb);
1661 break;
1662 }
1663 }
1664#endif
1665}
1666
1667/*!
1668 * Copy the bytes from the tile buffer into the image tile QImage, taking into
1669 * account all the myriad different modes.
1670 * \param layer layer containing the tile buffer and the image tile matrix.
1671 * \param i column index of current tile.
1672 * \param j row index of current tile.
1673 */
1674bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPrecision &precision)
1675{
1676 QImage &image = layer.image_tiles[j][i];
1677
1678 const uchar *tile = layer.tile;
1679 const int width = image.width();
1680 const int height = image.height();
1681 const int bytesPerLine = image.bytesPerLine();
1682 uchar *bits = image.bits();
1683
1684 // Handle the special cases
1685 if (layer.type == GRAYA_GIMAGE || layer.type == GRAY_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1686 auto bpc = bytesPerChannel(precision);
1687 for (int y = 0; y < height; y++) {
1688 uchar *dataPtr = bits + y * bytesPerLine;
1689 uchar *alphaPtr = nullptr;
1690 if (layer.alpha_tiles.size() > j && layer.alpha_tiles.at(i: j).size() > i) {
1691 QImage &alphaTile = layer.alpha_tiles[j][i];
1692 if (alphaTile.width() >= width && alphaTile.height() > y) {
1693 alphaPtr = alphaTile.scanLine(y);
1694 }
1695 }
1696 if (bpc == 4) {
1697#ifdef USE_FLOAT_IMAGES
1698 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1699 for (int x = 0; x < width; x++) {
1700 auto src = reinterpret_cast<const quint16 *>(tile);
1701 *dataPtr++ = qFromBigEndian<quint16>(source: src[0]) / 257;
1702 if (alphaPtr) {
1703 *alphaPtr++ = qFromBigEndian<quint16>(source: src[1]) / 257;
1704 tile += sizeof(quint16) * 2;
1705 } else {
1706 tile += sizeof(quint16);
1707 }
1708 }
1709 } else {
1710 for (int x = 0; x < width; x++) {
1711 auto src = reinterpret_cast<const float *>(tile);
1712 *dataPtr++ = qFromBigEndian<float>(source: src[0]) * 255;
1713 if (alphaPtr) {
1714 *alphaPtr++ = qFromBigEndian<float>(source: src[1]) * 255;
1715 tile += sizeof(float) * 2;
1716 } else {
1717 tile += sizeof(float);
1718 }
1719 }
1720 }
1721#else
1722 for (int x = 0; x < width; x++) {
1723 auto src = (const quint16 *)tile;
1724 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1725 if (alphaPtr) {
1726 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1727 tile += sizeof(quint16) * 2;
1728 } else {
1729 tile += sizeof(quint16);
1730 }
1731 }
1732#endif
1733 } else if (bpc == 2) {
1734#ifdef USE_FLOAT_IMAGES
1735 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1736 for (int x = 0; x < width; x++) {
1737 auto src = reinterpret_cast<const quint16 *>(tile);
1738 *dataPtr++ = qFromBigEndian<quint16>(source: src[0]) / 257;
1739 if (alphaPtr)
1740 *alphaPtr++ = qFromBigEndian<quint16>(source: src[1]) / 257;
1741 tile += sizeof(QRgb);
1742 }
1743 } else {
1744 for (int x = 0; x < width; x++) {
1745 auto src = reinterpret_cast<const qfloat16 *>(tile);
1746 *dataPtr++ = qFromBigEndian<qfloat16>(source: src[0]) * 255;
1747 if (alphaPtr)
1748 *alphaPtr++ = qFromBigEndian<qfloat16>(source: src[1]) * 255;
1749 tile += sizeof(QRgb);
1750 }
1751 }
1752#else
1753 for (int x = 0; x < width; x++) {
1754 auto src = reinterpret_cast<const quint16 *>(tile);
1755 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1756 if (alphaPtr)
1757 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1758 tile += sizeof(QRgb);
1759 }
1760#endif
1761 } else {
1762 for (int x = 0; x < width; x++) {
1763 if (tile[0] < image.colorCount())
1764 *dataPtr++ = tile[0];
1765 if (alphaPtr)
1766 *alphaPtr++ = tile[1];
1767 tile += sizeof(QRgb);
1768 }
1769 }
1770 }
1771 return true;
1772 }
1773
1774 switch (image.format()) {
1775 case QImage::Format_RGBX8888:
1776 for (int y = 0; y < height; y++) {
1777 uchar *dataPtr = image.scanLine(y);
1778 for (int x = 0; x < width * 4; x += 4, tile += 4) {
1779 dataPtr[x + 0] = tile[0];
1780 dataPtr[x + 1] = tile[1];
1781 dataPtr[x + 2] = tile[2];
1782 dataPtr[x + 3] = 255;
1783 }
1784 }
1785 break;
1786 case QImage::Format_RGBA8888:
1787 for (int y = 0; y < height; y++) {
1788 const size_t bpl = width * 4;
1789 memcpy(dest: image.scanLine(y), src: tile + y * bpl, n: bpl);
1790 }
1791 break;
1792 case QImage::Format_RGBX64:
1793 for (int y = 0; y < height; y++) {
1794 quint16 *dataPtr = reinterpret_cast<quint16 *>(image.scanLine(y));
1795 const size_t bpl = width * sizeof(QRgba64);
1796 const quint16 *src = reinterpret_cast<const quint16 *>(tile + y * bpl);
1797 for (int x = 0; x < width * 4; x += 4) {
1798 dataPtr[x + 0] = qFromBigEndian(source: src[x + 0]);
1799 dataPtr[x + 1] = qFromBigEndian(source: src[x + 1]);
1800 dataPtr[x + 2] = qFromBigEndian(source: src[x + 2]);
1801 dataPtr[x + 3] = 65535;
1802 }
1803 }
1804 break;
1805#ifdef USE_FLOAT_IMAGES
1806 case QImage::Format_RGBX16FPx4:
1807 for (int y = 0; y < height; y++) {
1808 qfloat16 *dataPtr = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1809 const qfloat16 *src = reinterpret_cast<const qfloat16 *>(tile + y * width * sizeof(QRgbaFloat16));
1810 for (int x = 0; x < width * 4; x += 4) {
1811 dataPtr[x + 0] = qFromBigEndian(source: src[x + 0]);
1812 dataPtr[x + 1] = qFromBigEndian(source: src[x + 1]);
1813 dataPtr[x + 2] = qFromBigEndian(source: src[x + 2]);
1814 dataPtr[x + 3] = qfloat16(1);
1815 if (!dataPtr[x + 0].isFinite() || !dataPtr[x + 1].isFinite() || !dataPtr[x + 2].isFinite()) {
1816 return false;
1817 }
1818 }
1819 }
1820 break;
1821 case QImage::Format_RGBA16FPx4:
1822 for (int y = 0; y < height; y++) {
1823 const size_t bpl = width * sizeof(QRgbaFloat16);
1824 qFromBigEndian<qint16>(source: tile + y * bpl, count: width * 4, dest: image.scanLine(y));
1825
1826 const qfloat16 *dataPtr = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1827 for (int x = 0; x < width * 4; ++x) {
1828 if (!dataPtr[x].isFinite()) {
1829 return false;
1830 }
1831 }
1832 }
1833 break;
1834#endif
1835 case QImage::Format_RGBA64:
1836 for (int y = 0; y < height; y++) {
1837 const size_t bpl = width * sizeof(QRgba64);
1838 qFromBigEndian<qint16>(source: tile + y * bpl, count: width * 4, dest: image.scanLine(y));
1839 }
1840 break;
1841#ifdef USE_FLOAT_IMAGES
1842 case QImage::Format_RGBA32FPx4:
1843 for (int y = 0; y < height; y++) {
1844 const size_t bpl = width * sizeof(QRgbaFloat32);
1845 qFromBigEndian<qint32>(source: tile + y * bpl, count: width * 4, dest: image.scanLine(y));
1846
1847 const float *dataPtr = reinterpret_cast<float *>(image.scanLine(y));
1848 for (int x = 0; x < width * 4; ++x) {
1849 if (!std::isfinite(x: dataPtr[x])) {
1850 return false;
1851 }
1852 }
1853 }
1854 break;
1855 case QImage::Format_RGBX32FPx4:
1856 for (int y = 0; y < height; y++) {
1857 float *dataPtr = reinterpret_cast<float *>(image.scanLine(y));
1858 const float *src = reinterpret_cast<const float *>(tile + y * width * sizeof(QRgbaFloat32));
1859 for (int x = 0; x < width * 4; x += 4) {
1860 dataPtr[x + 0] = qFromBigEndian(source: src[x + 0]);
1861 dataPtr[x + 1] = qFromBigEndian(source: src[x + 1]);
1862 dataPtr[x + 2] = qFromBigEndian(source: src[x + 2]);
1863 dataPtr[x + 3] = 1.f;
1864 if (!std::isfinite(x: dataPtr[x + 0]) || !std::isfinite(x: dataPtr[x + 1]) || !std::isfinite(x: dataPtr[x + 2])) {
1865 return false;
1866 }
1867 }
1868 }
1869 break;
1870#endif
1871 case QImage::Format_Indexed8:
1872 for (int y = 0; y < height; y++) {
1873 uchar *dataPtr = bits + y * bytesPerLine;
1874 for (int x = 0; x < width; x++) {
1875 *dataPtr++ = tile[0];
1876 tile += sizeof(QRgb);
1877 }
1878 }
1879 break;
1880 default:
1881 qCWarning(XCFPLUGIN) << "Unhandled image format" << image.format() << "and/or layer type" << layer.type;
1882 return false;
1883 }
1884
1885 return true;
1886}
1887
1888/*!
1889 * The GIMP stores images in a "mipmap"-like hierarchy. As far as the QImage
1890 * is concerned, however, only the top level (i.e., the full resolution image)
1891 * is used.
1892 * \param xcf_io the data stream connected to the XCF image.
1893 * \param layer the layer to collect the image.
1894 * \return true if there were no I/O errors.
1895 */
1896bool XCFImageFormat::loadHierarchy(QDataStream &xcf_io, Layer &layer, const GimpPrecision precision)
1897{
1898 qint32 width;
1899 qint32 height;
1900 quint32 bpp;
1901
1902 xcf_io >> width >> height >> bpp;
1903 const qint64 offset = readOffsetPtr(stream&: xcf_io);
1904
1905 qCDebug(XCFPLUGIN) << "width" << width << "height" << height << "bpp" << bpp << "offset" << offset;
1906
1907 if (offset < 0) {
1908 qCDebug(XCFPLUGIN) << "XCF: negative hierarchy offset";
1909 return false;
1910 }
1911
1912 const bool isMask = layer.assignBytes == assignMaskBytes;
1913
1914 // make sure bpp is correct and complain if it is not
1915 switch (layer.type) {
1916 case RGB_GIMAGE:
1917 if (bpp != 3 * bytesPerChannel(precision)) {
1918 qCDebug(XCFPLUGIN) << "Found layer of type RGB but with bpp != 3" << bpp;
1919
1920 if (!isMask) {
1921 return false;
1922 }
1923 }
1924 break;
1925 case RGBA_GIMAGE:
1926 if (bpp != 4 * bytesPerChannel(precision)) {
1927 qCDebug(XCFPLUGIN) << "Found layer of type RGBA but with bpp != 4, got" << bpp << "bpp";
1928
1929 if (!isMask) {
1930 return false;
1931 }
1932 }
1933 break;
1934 case GRAY_GIMAGE:
1935 if (bpp != 1 * bytesPerChannel(precision)) {
1936 qCDebug(XCFPLUGIN) << "Found layer of type Gray but with bpp != 1" << bpp;
1937 return false;
1938 }
1939 break;
1940 case GRAYA_GIMAGE:
1941 if (bpp != 2 * bytesPerChannel(precision)) {
1942 qCDebug(XCFPLUGIN) << "Found layer of type Gray+Alpha but with bpp != 2" << bpp;
1943
1944 if (!isMask) {
1945 return false;
1946 }
1947 }
1948 break;
1949 case INDEXED_GIMAGE:
1950 if (bpp != 1 * bytesPerChannel(precision)) {
1951 qCDebug(XCFPLUGIN) << "Found layer of type Indexed but with bpp != 1" << bpp;
1952 return false;
1953 }
1954 break;
1955 case INDEXEDA_GIMAGE:
1956 if (bpp != 2 * bytesPerChannel(precision)) {
1957 qCDebug(XCFPLUGIN) << "Found layer of type Indexed+Alpha but with bpp != 2" << bpp;
1958
1959 if (!isMask) {
1960 return false;
1961 }
1962 }
1963 break;
1964 }
1965
1966 if (bpp > 4 * bytesPerChannel(precision)) {
1967 qCDebug(XCFPLUGIN) << "bpp is" << bpp << "We don't support layers with bpp > 4";
1968 return false;
1969 }
1970
1971 // GIMP stores images in a "mipmap"-like format (multiple levels of
1972 // increasingly lower resolution). Only the top level is used here,
1973 // however.
1974
1975 quint32 junk;
1976 do {
1977 xcf_io >> junk;
1978
1979 if (xcf_io.device()->atEnd()) {
1980 qCDebug(XCFPLUGIN) << "XCF: read failure on layer " << layer.name << " level offsets";
1981 return false;
1982 }
1983 } while (junk != 0);
1984
1985 qint64 saved_pos = xcf_io.device()->pos();
1986
1987 if (!xcf_io.device()->seek(pos: offset)) {
1988 return false;
1989 }
1990 if (!loadLevel(xcf_io, layer, bpp, precision)) {
1991 return false;
1992 }
1993 if (!xcf_io.device()->seek(pos: saved_pos)) {
1994 return false;
1995 }
1996 return true;
1997}
1998
1999template<typename SourceFormat>
2000static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
2001{
2002 SourceFormat *source = (SourceFormat *)(input);
2003 for (quint64 offset = 0; offset < outputSize; offset++) {
2004 (reinterpret_cast<uint16_t *>(output))[offset] = qToBigEndian(source: quint16(qBound(0., qFromBigEndian<SourceFormat>(source[offset]) * 65535. + 0.5, 65535.)));
2005 }
2006 return true;
2007}
2008
2009/*!
2010 * Load one level of the image hierarchy (but only the top level is ever used).
2011 * \param xcf_io the data stream connected to the XCF image.
2012 * \param layer the layer to collect the image.
2013 * \param bpp the number of bytes in a pixel.
2014 * \return true if there were no I/O errors.
2015 * \sa loadTileRLE().
2016 */
2017bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, const GimpPrecision precision)
2018{
2019 auto bpc = bytesPerChannel(precision);
2020 if ((bpc == 0) || (bpp % bpc)) {
2021 qCDebug(XCFPLUGIN) << "XCF: the stream seems corrupted";
2022 return false;
2023 }
2024
2025 qint32 width;
2026 qint32 height;
2027
2028 xcf_io >> width >> height;
2029 qint64 offset = readOffsetPtr(stream&: xcf_io);
2030
2031 if (offset < 0) {
2032 qCDebug(XCFPLUGIN) << "XCF: negative level offset";
2033 return false;
2034 }
2035
2036 if (offset == 0) {
2037 // offset 0 with rowsxcols != 0 is probably an error since it means we have tiles
2038 // without data but just clear the bits for now instead of returning false
2039 for (uint j = 0; j < layer.nrows; j++) {
2040 for (uint i = 0; i < layer.ncols; i++) {
2041 layer.image_tiles[j][i].fill(color: Qt::transparent);
2042 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
2043 layer.alpha_tiles[j][i].fill(color: Qt::transparent);
2044 }
2045 }
2046 }
2047 return true;
2048 }
2049
2050 bool needConvert = true;
2051 switch (precision) {
2052#ifdef USE_FLOAT_IMAGES
2053 case GIMP_PRECISION_HALF_LINEAR:
2054 case GIMP_PRECISION_HALF_NON_LINEAR:
2055 case GIMP_PRECISION_HALF_PERCEPTUAL:
2056 case GIMP_PRECISION_FLOAT_LINEAR:
2057 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2058 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2059#endif
2060 case GIMP_PRECISION_U8_LINEAR:
2061 case GIMP_PRECISION_U8_NON_LINEAR:
2062 case GIMP_PRECISION_U8_PERCEPTUAL:
2063 case GIMP_PRECISION_U16_LINEAR:
2064 case GIMP_PRECISION_U16_NON_LINEAR:
2065 case GIMP_PRECISION_U16_PERCEPTUAL:
2066 needConvert = false;
2067 break;
2068 default:
2069 break;
2070 }
2071
2072 const uint blockSize = TILE_WIDTH * TILE_HEIGHT * bpp * 1.5;
2073
2074 QList<uchar> buffer;
2075 if (needConvert) {
2076 buffer.resize(size: blockSize * (bpp == 2 ? 2 : 1));
2077 buffer.fill(t: uchar());
2078 }
2079 for (uint j = 0; j < layer.nrows; j++) {
2080 for (uint i = 0; i < layer.ncols; i++) {
2081 if (offset == 0) {
2082 qCDebug(XCFPLUGIN) << "XCF: incorrect number of tiles in layer " << layer.name;
2083 return false;
2084 }
2085
2086 qint64 saved_pos = xcf_io.device()->pos();
2087 qint64 offset2 = readOffsetPtr(stream&: xcf_io);
2088
2089 if (offset2 < 0) {
2090 qCDebug(XCFPLUGIN) << "XCF: negative level offset";
2091 return false;
2092 }
2093
2094 // Evidently, RLE can occasionally expand a tile instead of compressing it!
2095 if (offset2 == 0) {
2096 offset2 = offset + blockSize;
2097 }
2098
2099 if (!xcf_io.device()->seek(pos: offset)) {
2100 return false;
2101 }
2102 qint64 bytesParsed = 0;
2103
2104 switch (layer.compression) {
2105 case COMPRESS_NONE: {
2106 if (xcf_io.version() > 11 || size_t(bpp) > sizeof(QRgba64)) {
2107 qCDebug(XCFPLUGIN) << "Component reading not supported yet";
2108 return false;
2109 }
2110 const int data_size = bpp * TILE_WIDTH * TILE_HEIGHT;
2111 if (data_size > int(blockSize)) {
2112 qCDebug(XCFPLUGIN) << "Tile data too big, we can only fit" << sizeof(layer.tile) << "but need" << data_size;
2113 return false;
2114 }
2115 int dataRead = xcf_io.readRawData(reinterpret_cast<char *>(layer.tile), len: data_size);
2116 if (dataRead < data_size) {
2117 qCDebug(XCFPLUGIN) << "short read, expected" << data_size << "got" << dataRead;
2118 return false;
2119 }
2120 bytesParsed = dataRead;
2121 break;
2122 }
2123 case COMPRESS_RLE: {
2124 int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
2125 const uint data_size = size * bpp;
2126 if (needConvert) {
2127 if (data_size >= unsigned(buffer.size())) {
2128 qCDebug(XCFPLUGIN) << "Tile data too big, we can only fit" << buffer.size() << "but need" << data_size;
2129 return false;
2130 }
2131 } else {
2132 if (data_size > sizeof(layer.tile)) {
2133 qCDebug(XCFPLUGIN) << "Tile data too big, we can only fit" << sizeof(layer.tile) << "but need" << data_size;
2134 return false;
2135 }
2136 if (blockSize > sizeof(layer.tile)) {
2137 qCWarning(XCFPLUGIN) << "Too small tiles" << sizeof(layer.tile) << "this image requires" << blockSize << sizeof(QRgba64) << bpp;
2138 return false;
2139 }
2140 }
2141 if (!loadTileRLE(xcf_io, tile: needConvert ? buffer.data() : layer.tile, size, data_length: offset2 - offset, bpp, bytesParsed: &bytesParsed)) {
2142 qCDebug(XCFPLUGIN) << "Failed to read RLE";
2143 return false;
2144 }
2145 break;
2146 }
2147 default:
2148 qCDebug(XCFPLUGIN) << "Unhandled compression" << layer.compression;
2149 return false;
2150 }
2151
2152 if (needConvert) {
2153 if (bytesParsed > buffer.size()) {
2154 qCDebug(XCFPLUGIN) << "Invalid number of bytes parsed" << bytesParsed << buffer.size();
2155 return false;
2156 }
2157
2158 switch (precision) {
2159 case GIMP_PRECISION_U32_LINEAR:
2160 case GIMP_PRECISION_U32_NON_LINEAR:
2161 case GIMP_PRECISION_U32_PERCEPTUAL: {
2162 quint32 *source = reinterpret_cast<quint32 *>(buffer.data());
2163 for (quint64 offset = 0, len = buffer.size() / sizeof(quint32); offset < len; ++offset) {
2164 (reinterpret_cast<quint16 *>(layer.tile))[offset] = qToBigEndian<quint16>(source: qFromBigEndian(source: source[offset]) / 65537);
2165 }
2166 break;
2167 }
2168#ifndef USE_FLOAT_IMAGES
2169 case GIMP_PRECISION_HALF_LINEAR:
2170 case GIMP_PRECISION_HALF_NON_LINEAR:
2171 case GIMP_PRECISION_HALF_PERCEPTUAL:
2172 convertFloatTo16Bit<qfloat16>(layer.tile, buffer.size() / sizeof(qfloat16), buffer.data());
2173 break;
2174 case GIMP_PRECISION_FLOAT_LINEAR:
2175 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2176 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2177 convertFloatTo16Bit<float>(layer.tile, buffer.size() / sizeof(float), buffer.data());
2178 break;
2179 case GIMP_PRECISION_DOUBLE_LINEAR:
2180 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2181 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2182 convertFloatTo16Bit<double>(layer.tile, buffer.size() / sizeof(double), buffer.data());
2183 break;
2184#else
2185 case GIMP_PRECISION_DOUBLE_LINEAR:
2186 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2187 case GIMP_PRECISION_DOUBLE_PERCEPTUAL: {
2188 double *source = reinterpret_cast<double *>(buffer.data());
2189 for (quint64 offset = 0, len = buffer.size() / sizeof(double); offset < len; ++offset) {
2190 (reinterpret_cast<float *>(layer.tile))[offset] = qToBigEndian<float>(source: float(qFromBigEndian(source: source[offset])));
2191 }
2192 break;
2193 }
2194#endif
2195 default:
2196 qCWarning(XCFPLUGIN) << "Unsupported precision" << precision;
2197 return false;
2198 }
2199 }
2200
2201 // The bytes in the layer tile are juggled differently depending on
2202 // the target QImage. The caller has set layer.assignBytes to the
2203 // appropriate routine.
2204 if (!layer.assignBytes(layer, i, j, precision)) {
2205 return false;
2206 }
2207
2208 if (!xcf_io.device()->seek(pos: saved_pos)) {
2209 return false;
2210 }
2211 offset = readOffsetPtr(stream&: xcf_io);
2212
2213 if (offset < 0) {
2214 qCDebug(XCFPLUGIN) << "XCF: negative level offset";
2215 return false;
2216 }
2217 }
2218 }
2219
2220 return true;
2221}
2222
2223/*!
2224 * A layer can have a one channel image which is used as a mask.
2225 * \param xcf_io the data stream connected to the XCF image.
2226 * \param layer the layer to collect the mask image.
2227 * \return true if there were no I/O errors.
2228 */
2229bool XCFImageFormat::loadMask(QDataStream &xcf_io, Layer &layer, const GimpPrecision precision)
2230{
2231 qint32 width;
2232 qint32 height;
2233 char *name;
2234
2235 xcf_io >> width >> height >> name;
2236
2237 delete[] name;
2238
2239 if (!loadChannelProperties(xcf_io, layer)) {
2240 return false;
2241 }
2242
2243 const qint64 hierarchy_offset = readOffsetPtr(stream&: xcf_io);
2244
2245 if (hierarchy_offset < 0) {
2246 qCDebug(XCFPLUGIN) << "XCF: negative mask hierarchy_offset";
2247 return false;
2248 }
2249
2250 if (!xcf_io.device()->seek(pos: hierarchy_offset)) {
2251 return false;
2252 }
2253 layer.assignBytes = assignMaskBytes;
2254
2255 if (!loadHierarchy(xcf_io, layer, precision)) {
2256 return false;
2257 }
2258
2259 return true;
2260}
2261
2262/*!
2263 * This is the routine for which all the other code is simply
2264 * infrastructure. Read the image bytes out of the file and
2265 * store them in the tile buffer. This is passed a full 32-bit deep
2266 * buffer, even if bpp is smaller. The caller can figure out what to
2267 * do with the bytes.
2268 *
2269 * The tile is stored in "channels", i.e. the red component of all
2270 * pixels, then the green component of all pixels, then blue then
2271 * alpha, or, for indexed images, the color indices of all pixels then
2272 * the alpha of all pixels.
2273 *
2274 * The data is compressed with "run length encoding". Some simple data
2275 * integrity checks are made.
2276 *
2277 * \param xcf_io the data stream connected to the XCF image.
2278 * \param tile the buffer to expand the RLE into.
2279 * \param image_size number of bytes expected to be in the image tile.
2280 * \param data_length number of bytes expected in the RLE.
2281 * \param bpp number of bytes per pixel.
2282 * \return true if there were no I/O errors and no obvious corruption of
2283 * the RLE data.
2284 */
2285bool XCFImageFormat::loadTileRLE(QDataStream &xcf_io, uchar *tile, int image_size, int data_length, qint32 bpp, qint64 *bytesParsed)
2286{
2287 uchar *data = tile;
2288
2289 uchar *xcfdata;
2290 uchar *xcfodata;
2291 uchar *xcfdatalimit;
2292
2293 int step = sizeof(QRgb);
2294 switch (bpp) {
2295 case 1:
2296 case 2:
2297 case 3:
2298 case 4:
2299 step = sizeof(QRgb);
2300 break;
2301 case 6:
2302 case 8:
2303 step = sizeof(QRgb) * 2;
2304 break;
2305 case 12:
2306 case 16:
2307 step = sizeof(QRgb) * 4;
2308 break;
2309 default:
2310 qCDebug(XCFPLUGIN) << "XCF: unhandled bit depth" << bpp;
2311 return false;
2312 }
2313
2314 if (data_length < 0 || data_length > int(TILE_WIDTH * TILE_HEIGHT * step * 1.5)) {
2315 qCDebug(XCFPLUGIN) << "XCF: invalid tile data length" << data_length;
2316 return false;
2317 }
2318
2319 xcfdata = xcfodata = new uchar[data_length];
2320
2321 const int dataRead = xcf_io.readRawData((char *)xcfdata, len: data_length);
2322 if (dataRead <= 0) {
2323 delete[] xcfodata;
2324 qCDebug(XCFPLUGIN) << "XCF: read failure on tile" << dataRead;
2325 return false;
2326 }
2327
2328 if (dataRead < data_length) {
2329 memset(s: &xcfdata[dataRead], c: 0, n: data_length - dataRead);
2330 }
2331
2332 if (!xcf_io.device()->isOpen()) {
2333 delete[] xcfodata;
2334 qCDebug(XCFPLUGIN) << "XCF: read failure on tile";
2335 return false;
2336 }
2337
2338 xcfdatalimit = &xcfodata[data_length - 1];
2339
2340 for (int i = 0; i < bpp; ++i) {
2341 data = tile + i;
2342
2343 int size = image_size;
2344
2345 while (size > 0) {
2346 if (xcfdata > xcfdatalimit) {
2347 goto bogus_rle;
2348 }
2349
2350 uchar val = *xcfdata++;
2351 uint length = val;
2352
2353 if (length >= 128) {
2354 length = 255 - (length - 1);
2355 if (length == 128) {
2356 if (xcfdata >= xcfdatalimit) {
2357 goto bogus_rle;
2358 }
2359
2360 length = (*xcfdata << 8) + xcfdata[1];
2361
2362 xcfdata += 2;
2363 }
2364
2365 size -= length;
2366
2367 if (size < 0) {
2368 goto bogus_rle;
2369 }
2370
2371 if (&xcfdata[length - 1] > xcfdatalimit) {
2372 goto bogus_rle;
2373 }
2374
2375 while (length-- > 0) {
2376 *data = *xcfdata++;
2377 data += step;
2378 }
2379 } else {
2380 length += 1;
2381 if (length == 128) {
2382 if (xcfdata >= xcfdatalimit) {
2383 goto bogus_rle;
2384 }
2385
2386 length = (*xcfdata << 8) + xcfdata[1];
2387 xcfdata += 2;
2388 }
2389
2390 size -= length;
2391
2392 if (size < 0) {
2393 goto bogus_rle;
2394 }
2395
2396 if (xcfdata > xcfdatalimit) {
2397 goto bogus_rle;
2398 }
2399
2400 qintptr totalLength = qintptr(data - tile) + length * step;
2401 if (totalLength >= image_size * step * 1.5) {
2402 qCDebug(XCFPLUGIN) << "Ran out of space when trying to unpack image, over:" << totalLength - image_size << totalLength << image_size
2403 << length;
2404 goto bogus_rle;
2405 }
2406
2407 val = *xcfdata++;
2408
2409 while (length-- > 0) {
2410 *data = val;
2411 data += step;
2412 }
2413 }
2414 }
2415 }
2416 *bytesParsed = qintptr(data - tile);
2417
2418 delete[] xcfodata;
2419 return true;
2420
2421bogus_rle:
2422
2423 qCDebug(XCFPLUGIN) << "The run length encoding could not be decoded properly";
2424 delete[] xcfodata;
2425 return false;
2426}
2427
2428/*!
2429 * An XCF file can contain an arbitrary number of properties associated
2430 * with a channel. Note that this routine only reads mask channel properties.
2431 * \param xcf_io the data stream connected to the XCF image.
2432 * \param layer layer containing the mask channel to collect the properties.
2433 * \return true if there were no I/O errors.
2434 */
2435bool XCFImageFormat::loadChannelProperties(QDataStream &xcf_io, Layer &layer)
2436{
2437 while (true) {
2438 PropType type;
2439 QByteArray bytes;
2440 quint32 rawType;
2441
2442 if (!loadProperty(xcf_io, type, bytes, rawType)) {
2443 qCDebug(XCFPLUGIN) << "XCF: error loading channel properties";
2444 return false;
2445 }
2446
2447 QDataStream property(bytes);
2448
2449 switch (type) {
2450 case PROP_END:
2451 return true;
2452
2453 case PROP_OPACITY:
2454 property >> layer.mask_channel.opacity;
2455 layer.mask_channel.opacity = std::min(a: layer.mask_channel.opacity, b: 255u);
2456 break;
2457
2458 case PROP_FLOAT_OPACITY:
2459 // For some reason QDataStream isn't able to read the float (tried
2460 // setting the endianness manually)
2461 if (bytes.size() == 4) {
2462 layer.mask_channel.opacityFloat = qFromBigEndian(source: *reinterpret_cast<float *>(bytes.data()));
2463 } else {
2464 qCDebug(XCFPLUGIN) << "XCF: Invalid data size for float:" << bytes.size();
2465 }
2466 break;
2467
2468 case PROP_VISIBLE:
2469 property >> layer.mask_channel.visible;
2470 break;
2471
2472 case PROP_SHOW_MASKED:
2473 property >> layer.mask_channel.show_masked;
2474 break;
2475
2476 case PROP_COLOR:
2477 property >> layer.mask_channel.red >> layer.mask_channel.green >> layer.mask_channel.blue;
2478 break;
2479
2480 case PROP_FLOAT_COLOR:
2481 property >> layer.mask_channel.redF >> layer.mask_channel.greenF >> layer.mask_channel.blueF;
2482 break;
2483
2484 case PROP_TATTOO:
2485 property >> layer.mask_channel.tattoo;
2486 break;
2487
2488 // Only used in edit mode
2489 case PROP_LINKED:
2490 break;
2491
2492 // Just for organization in the UI, doesn't influence rendering
2493 case PROP_COLOR_TAG:
2494 break;
2495
2496 // We don't support editing, so for now just ignore locking
2497 case PROP_LOCK_CONTENT:
2498 case PROP_LOCK_POSITION:
2499 break;
2500
2501 default:
2502 qCDebug(XCFPLUGIN) << "XCF: unimplemented channel property " << type << "(" << rawType << ")"
2503 << ", size " << bytes.size();
2504 break;
2505 }
2506 }
2507}
2508
2509/*!
2510 * Copy the bytes from the tile buffer into the mask tile QImage.
2511 * \param layer layer containing the tile buffer and the mask tile matrix.
2512 * \param i column index of current tile.
2513 * \param j row index of current tile.
2514 */
2515bool XCFImageFormat::assignMaskBytes(Layer &layer, uint i, uint j, const GimpPrecision &precision)
2516{
2517 QImage &image = layer.mask_tiles[j][i];
2518 if (image.depth() != 8) {
2519 qCWarning(XCFPLUGIN) << "invalid bytes per pixel, we only do 8 bit masks" << image.depth();
2520 return false;
2521 }
2522
2523 uchar *tile = layer.tile;
2524 const int width = image.width();
2525 const int height = image.height();
2526 const int bytesPerLine = image.bytesPerLine();
2527 uchar *bits = image.bits();
2528 auto bpc = bytesPerChannel(precision);
2529
2530 // mask management is a house of cards: the mask is always treated as 8 bit by the plugin
2531 // (I don't want to twist the code) so it needs a conversion here.
2532 // If previously converted the step is the type size, otherwise is the one set in loadTileRLE().
2533 for (int y = 0; y < height; y++) {
2534 uchar *dataPtr = bits + y * bytesPerLine;
2535#ifdef USE_FLOAT_IMAGES
2536 if (bpc == 4) {
2537 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2538 for (int x = 0; x < width; x++) {
2539 *dataPtr++ = qFromBigEndian<quint16>(source: *reinterpret_cast<const quint16 *>(tile)) / 257;
2540 tile += sizeof(quint16); // was converted to 16 bits in loadLevel()
2541 }
2542 } else {
2543 for (int x = 0; x < width; x++) {
2544 *dataPtr++ = qFromBigEndian<float>(source: *reinterpret_cast<const float *>(tile)) * 255;
2545 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2546 }
2547 }
2548 } else if (bpc == 2) {
2549 // when not converted, the step of a
2550 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2551 for (int x = 0; x < width; x++) {
2552 *dataPtr++ = qFromBigEndian<quint16>(source: *reinterpret_cast<const quint16 *>(tile)) / 257;
2553 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2554 }
2555 } else {
2556 for (int x = 0; x < width; x++) {
2557 *dataPtr++ = qFromBigEndian<qfloat16>(source: *reinterpret_cast<const qfloat16 *>(tile)) * 255;
2558 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2559 }
2560 }
2561 }
2562#else
2563 if (bpc == 2) {
2564 for (int x = 0; x < width; x++) {
2565 *dataPtr++ = qFromBigEndian<quint16>(*reinterpret_cast<const quint16 *>(tile)) / 257;
2566 tile += sizeof(QRgb); // yeah! see loadTileRLE() / loadLevel()
2567 }
2568 } else if (bpc == 4) {
2569 for (int x = 0; x < width; x++) {
2570 *dataPtr++ = qFromBigEndian<quint16>(*reinterpret_cast<const quint16 *>(tile)) / 257;
2571 tile += sizeof(quint16); // was converted to 16 bits in loadLevel()
2572 }
2573 }
2574#endif
2575 else {
2576 for (int x = 0; x < width; x++) {
2577 *dataPtr++ = tile[0];
2578 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2579 }
2580 }
2581 }
2582
2583 return true;
2584}
2585
2586/*!
2587 * Construct the QImage which will eventually be returned to the QImage
2588 * loader.
2589 *
2590 * There are a couple of situations which require that the QImage is not
2591 * exactly the same as The GIMP's representation. The full table is:
2592 * \verbatim
2593 * Grayscale opaque : 8 bpp indexed
2594 * Grayscale translucent : 32 bpp + alpha
2595 * Indexed opaque : 1 bpp if num_colors <= 2
2596 * : 8 bpp indexed otherwise
2597 * Indexed translucent : 8 bpp indexed + alpha if num_colors < 256
2598 * : 32 bpp + alpha otherwise
2599 * RGB opaque : 32 bpp
2600 * RGBA translucent : 32 bpp + alpha
2601 * \endverbatim
2602 * Whether the image is translucent or not is determined by the bottom layer's
2603 * alpha channel. However, even if the bottom layer lacks an alpha channel,
2604 * it can still have an opacity < 1. In this case, the QImage is promoted
2605 * to 32-bit. (Note this is different from the output from the GIMP image
2606 * exporter, which seems to ignore this attribute.)
2607 *
2608 * Independently, higher layers can be translucent, but the background of
2609 * the image will not show through if the bottom layer is opaque.
2610 *
2611 * For indexed images, translucency is an all or nothing effect.
2612 * \param xcf_image contains image info and bottom-most layer.
2613 */
2614bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
2615{
2616 // (Aliases to make the code look a little better.)
2617 Layer &layer(xcf_image.layer);
2618 QImage &image(xcf_image.image);
2619
2620 switch (layer.type) {
2621 case GRAY_GIMAGE:
2622 if (layer.opacity == OPAQUE_OPACITY) {
2623 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_Indexed8);
2624 image.setColorCount(256);
2625 if (image.isNull()) {
2626 return false;
2627 }
2628 setGrayPalette(image);
2629 image.fill(pixel: 255);
2630 break;
2631 } // else, fall through to 32-bit representation
2632 Q_FALLTHROUGH();
2633 case GRAYA_GIMAGE:
2634 case RGB_GIMAGE:
2635 case RGBA_GIMAGE:
2636 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: xcf_image.qimageFormat());
2637 if (image.isNull()) {
2638 return false;
2639 }
2640 if (image.hasAlphaChannel()) {
2641 image.fill(color: Qt::transparent);
2642 } else {
2643 image.fill(color: Qt::white);
2644 }
2645 break;
2646
2647 case INDEXED_GIMAGE:
2648 // As noted in the table above, there are quite a few combinations
2649 // which are possible with indexed images, depending on the
2650 // presence of transparency (note: not translucency, which is not
2651 // supported by The GIMP for indexed images) and the number of
2652 // individual colors.
2653
2654 // Note: Qt treats a bitmap with a Black and White color palette
2655 // as a mask, so only the "on" bits are drawn, regardless of the
2656 // order color table entries. Otherwise (i.e., at least one of the
2657 // color table entries is not black or white), it obeys the one-
2658 // or two-color palette. Have to ask about this...
2659
2660 if (xcf_image.num_colors <= 2) {
2661 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_MonoLSB);
2662 image.setColorCount(xcf_image.num_colors);
2663 if (image.isNull()) {
2664 return false;
2665 }
2666 image.fill(pixel: 0);
2667 setPalette(xcf_image, image);
2668 } else if (xcf_image.num_colors <= 256) {
2669 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_Indexed8);
2670 image.setColorCount(xcf_image.num_colors);
2671 if (image.isNull()) {
2672 return false;
2673 }
2674 image.fill(pixel: 0);
2675 setPalette(xcf_image, image);
2676 }
2677 break;
2678
2679 case INDEXEDA_GIMAGE:
2680 if (xcf_image.num_colors == 1) {
2681 // Plenty(!) of room to add a transparent color
2682 xcf_image.num_colors++;
2683 xcf_image.palette.resize(size: xcf_image.num_colors);
2684 xcf_image.palette[1] = xcf_image.palette[0];
2685 xcf_image.palette[0] = qRgba(r: 255, g: 255, b: 255, a: 0);
2686
2687 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_MonoLSB);
2688 image.setColorCount(xcf_image.num_colors);
2689 if (image.isNull()) {
2690 return false;
2691 }
2692 image.fill(pixel: 0);
2693 setPalette(xcf_image, image);
2694 } else if (xcf_image.num_colors < 256) {
2695 // Plenty of room to add a transparent color
2696 xcf_image.num_colors++;
2697 xcf_image.palette.resize(size: xcf_image.num_colors);
2698 for (int c = xcf_image.num_colors - 1; c >= 1; c--) {
2699 xcf_image.palette[c] = xcf_image.palette[c - 1];
2700 }
2701
2702 xcf_image.palette[0] = qRgba(r: 255, g: 255, b: 255, a: 0);
2703 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_Indexed8);
2704 image.setColorCount(xcf_image.num_colors);
2705 if (image.isNull()) {
2706 return false;
2707 }
2708 image.fill(pixel: 0);
2709 setPalette(xcf_image, image);
2710 } else {
2711 // No room for a transparent color, so this has to be promoted to
2712 // true color. (There is no equivalent PNG representation output
2713 // from The GIMP as of v1.2.)
2714 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_ARGB32);
2715 if (image.isNull()) {
2716 return false;
2717 }
2718 image.fill(pixel: qRgba(r: 255, g: 255, b: 255, a: 0));
2719 }
2720 break;
2721 }
2722 if (image.format() != xcf_image.qimageFormat()) {
2723 qCWarning(XCFPLUGIN) << "Selected wrong format:" << image.format() << "expected" << layer.qimageFormat(precision: xcf_image.header.precision);
2724 return false;
2725 }
2726
2727 // The final profile should be the one in the Parasite
2728 // NOTE: if not set here, the colorSpace is aet in setImageParasites() (if no one defined in the parasites)
2729#ifndef DISABLE_IMAGE_PROFILE
2730 switch (xcf_image.header.precision) {
2731 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
2732 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
2733 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
2734 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
2735 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
2736 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
2737 image.setColorSpace(QColorSpace::SRgbLinear);
2738 break;
2739 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
2740 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
2741 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
2742 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
2743 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
2744 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
2745 image.setColorSpace(QColorSpace::SRgb);
2746 break;
2747 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
2748 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
2749 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2750 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
2751 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
2752 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
2753 image.setColorSpace(QColorSpace::SRgb);
2754 break;
2755 }
2756#endif
2757
2758 const qint32 dpmx = dpi2ppm(dpi: xcf_image.x_resolution);
2759 if (dpmx > 0) {
2760 image.setDotsPerMeterX(dpmx);
2761 }
2762 const qint32 dpmy = dpi2ppm(dpi: xcf_image.y_resolution);
2763 if (dpmy > 0) {
2764 image.setDotsPerMeterY(dpmy);
2765 }
2766 return true;
2767}
2768
2769/*!
2770 * Copy a layer into an image, taking account of the manifold modes. The
2771 * contents of the image are replaced.
2772 * \param xcf_image contains the layer and image to be replaced.
2773 */
2774void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
2775{
2776 Layer &layer(xcf_image.layer);
2777 QImage &image(xcf_image.image);
2778 PixelCopyOperation copy = nullptr;
2779
2780 switch (layer.type) {
2781 case RGB_GIMAGE:
2782 case RGBA_GIMAGE:
2783 copy = copyRGBToRGB;
2784 break;
2785 case GRAY_GIMAGE:
2786 if (layer.opacity == OPAQUE_OPACITY) {
2787 copy = copyGrayToGray;
2788 } else {
2789 copy = copyGrayToRGB;
2790 }
2791 break;
2792 case GRAYA_GIMAGE:
2793 copy = copyGrayAToRGB;
2794 break;
2795 case INDEXED_GIMAGE:
2796 copy = copyIndexedToIndexed;
2797 break;
2798 case INDEXEDA_GIMAGE:
2799 if (xcf_image.image.depth() <= 8) {
2800 copy = copyIndexedAToIndexed;
2801 } else {
2802 copy = copyIndexedAToRGB;
2803 }
2804 }
2805
2806 if (!copy) {
2807 return;
2808 }
2809
2810 // For each tile...
2811
2812 for (uint j = 0; j < layer.nrows; j++) {
2813 qint32 y = qint32(j * TILE_HEIGHT);
2814
2815 for (uint i = 0; i < layer.ncols; i++) {
2816 qint32 x = qint32(i * TILE_WIDTH);
2817
2818 // This seems the best place to apply the dissolve because it
2819 // depends on the global position of each tile's
2820 // pixels. Apparently it's the only mode which can apply to a
2821 // single layer.
2822
2823 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
2824 if (!random_table_initialized) {
2825 initializeRandomTable();
2826 random_table_initialized = true;
2827 }
2828 if (layer.type == RGBA_GIMAGE) {
2829 dissolveRGBPixels(image&: layer.image_tiles[j][i], x, y);
2830 }
2831
2832 else if (layer.type == GRAYA_GIMAGE) {
2833 dissolveAlphaPixels(image&: layer.alpha_tiles[j][i], x, y);
2834 }
2835 }
2836
2837 // Shortcut for common case
2838 if (copy == copyRGBToRGB && layer.apply_mask != 1) {
2839 QPainter painter(&image);
2840 painter.setOpacity(layer.opacity / 255.0);
2841 painter.setCompositionMode(QPainter::CompositionMode_Source);
2842 if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) {
2843 painter.drawImage(x: x + layer.x_offset, y: y + layer.y_offset, image: layer.image_tiles[j][i]);
2844 }
2845 continue;
2846 }
2847
2848 for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
2849 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
2850 int m = x + k + layer.x_offset;
2851 int n = y + l + layer.y_offset;
2852
2853 if (m < 0 || m >= image.width() || n < 0 || n >= image.height()) {
2854 continue;
2855 }
2856
2857 (*copy)(layer, i, j, k, l, image, m, n);
2858 }
2859 }
2860 }
2861 }
2862}
2863
2864/*!
2865 * Copy an RGB pixel from the layer to the RGB image. Straight-forward.
2866 * The only thing this has to take account of is the opacity of the
2867 * layer. Evidently, the GIMP exporter itself does not actually do this.
2868 * \param layer source layer.
2869 * \param i x tile index.
2870 * \param j y tile index.
2871 * \param k x pixel index of tile i,j.
2872 * \param l y pixel index of tile i,j.
2873 * \param image destination image.
2874 * \param m x pixel of destination image.
2875 * \param n y pixel of destination image.
2876 */
2877void XCFImageFormat::copyRGBToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2878{
2879 if (image.depth() == 32) {
2880 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
2881 uchar src_a = layer.opacity;
2882
2883 if (layer.type == RGBA_GIMAGE) {
2884 src_a = INT_MULT(a: src_a, b: qAlpha(rgb: src));
2885 }
2886
2887 // Apply the mask (if any)
2888
2889 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2890 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2891 }
2892
2893 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
2894 } else if (image.depth() == 64) {
2895 QRgba64 src = layer.image_tiles[j][i].pixelColor(x: k, y: l).rgba64();
2896 quint16 src_a = layer.opacity;
2897
2898 if (layer.type == RGBA_GIMAGE) {
2899 src_a = INT_MULT(a: src_a, b: qAlpha(rgb: src));
2900 }
2901
2902 // Apply the mask (if any)
2903
2904 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2905 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2906 }
2907 src.setAlpha(src_a);
2908
2909 image.setPixel(x: m, y: n, index_or_rgb: src);
2910 }
2911}
2912
2913/*!
2914 * Copy a Gray pixel from the layer to the Gray image. Straight-forward.
2915 * \param layer source layer.
2916 * \param i x tile index.
2917 * \param j y tile index.
2918 * \param k x pixel index of tile i,j.
2919 * \param l y pixel index of tile i,j.
2920 * \param image destination image.
2921 * \param m x pixel of destination image.
2922 * \param n y pixel of destination image.
2923 */
2924void XCFImageFormat::copyGrayToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2925{
2926 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
2927 image.setPixel(x: m, y: n, index_or_rgb: src);
2928}
2929
2930/*!
2931 * Copy a Gray pixel from the layer to an RGB image. Straight-forward.
2932 * The only thing this has to take account of is the opacity of the
2933 * layer. Evidently, the GIMP exporter itself does not actually do this.
2934 * \param layer source layer.
2935 * \param i x tile index.
2936 * \param j y tile index.
2937 * \param k x pixel index of tile i,j.
2938 * \param l y pixel index of tile i,j.
2939 * \param image destination image.
2940 * \param m x pixel of destination image.
2941 * \param n y pixel of destination image.
2942 */
2943void XCFImageFormat::copyGrayToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2944{
2945 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
2946 uchar src_a = layer.opacity;
2947 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
2948}
2949
2950/*!
2951 * Copy a GrayA pixel from the layer to an RGB image. Straight-forward.
2952 * The only thing this has to take account of is the opacity of the
2953 * layer. Evidently, the GIMP exporter itself does not actually do this.
2954 * \param layer source layer.
2955 * \param i x tile index.
2956 * \param j y tile index.
2957 * \param k x pixel index of tile i,j.
2958 * \param l y pixel index of tile i,j.
2959 * \param image destination image.
2960 * \param m x pixel of destination image.
2961 * \param n y pixel of destination image.
2962 */
2963void XCFImageFormat::copyGrayAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2964{
2965 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
2966 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
2967 src_a = INT_MULT(a: src_a, b: layer.opacity);
2968
2969 // Apply the mask (if any)
2970
2971 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2972 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2973 }
2974
2975 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
2976}
2977
2978/*!
2979 * Copy an Indexed pixel from the layer to the Indexed image. Straight-forward.
2980 * \param layer source layer.
2981 * \param i x tile index.
2982 * \param j y tile index.
2983 * \param k x pixel index of tile i,j.
2984 * \param l y pixel index of tile i,j.
2985 * \param image destination image.
2986 * \param m x pixel of destination image.
2987 * \param n y pixel of destination image.
2988 */
2989void XCFImageFormat::copyIndexedToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2990{
2991 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
2992 image.setPixel(x: m, y: n, index_or_rgb: src);
2993}
2994
2995/*!
2996 * Copy an IndexedA pixel from the layer to the Indexed image. Straight-forward.
2997 * \param layer source layer.
2998 * \param i x tile index.
2999 * \param j y tile index.
3000 * \param k x pixel index of tile i,j.
3001 * \param l y pixel index of tile i,j.
3002 * \param image destination image.
3003 * \param m x pixel of destination image.
3004 * \param n y pixel of destination image.
3005 */
3006void XCFImageFormat::copyIndexedAToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3007{
3008 uchar src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
3009 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
3010 src_a = INT_MULT(a: src_a, b: layer.opacity);
3011
3012 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
3013 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
3014 }
3015
3016 if (src_a > 127) {
3017 src++;
3018 } else {
3019 src = 0;
3020 }
3021
3022 image.setPixel(x: m, y: n, index_or_rgb: src);
3023}
3024
3025/*!
3026 * Copy an IndexedA pixel from the layer to an RGB image. Straight-forward.
3027 * The only thing this has to take account of is the opacity of the
3028 * layer. Evidently, the GIMP exporter itself does not actually do this.
3029 * \param layer source layer.
3030 * \param i x tile index.
3031 * \param j y tile index.
3032 * \param k x pixel index of tile i,j.
3033 * \param l y pixel index of tile i,j.
3034 * \param image destination image.
3035 * \param m x pixel of destination image.
3036 * \param n y pixel of destination image.
3037 */
3038void XCFImageFormat::copyIndexedAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3039{
3040 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
3041 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
3042 src_a = INT_MULT(a: src_a, b: layer.opacity);
3043
3044 // Apply the mask (if any)
3045 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
3046 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
3047 }
3048
3049 // This is what appears in the GIMP window
3050 if (src_a <= 127) {
3051 src_a = 0;
3052 } else {
3053 src_a = OPAQUE_OPACITY;
3054 }
3055
3056 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
3057}
3058
3059/*!
3060 * Merge a layer into an image, taking account of the manifold modes.
3061 * \param xcf_image contains the layer and image to merge.
3062 */
3063void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
3064{
3065 Layer &layer(xcf_image.layer);
3066 QImage &image(xcf_image.image);
3067
3068 PixelMergeOperation merge = nullptr;
3069
3070 if (!layer.opacity) {
3071 return; // don't bother doing anything
3072 }
3073
3074 if (layer.blendSpace == XCFImageFormat::AutoColorSpace) {
3075 qCDebug(XCFPLUGIN) << "Auto blend space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3076 layer.blendSpace = XCFImageFormat::RgbLinearSpace;
3077 }
3078
3079 if (layer.blendSpace != XCFImageFormat::RgbLinearSpace) {
3080 qCDebug(XCFPLUGIN) << "Unimplemented blend color space" << layer.blendSpace;
3081 }
3082 qCDebug(XCFPLUGIN) << "Blend color space" << layer.blendSpace;
3083
3084 if (layer.compositeSpace == XCFImageFormat::AutoColorSpace) {
3085 qCDebug(XCFPLUGIN) << "Auto composite space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3086 layer.compositeSpace = XCFImageFormat::RgbLinearSpace;
3087 }
3088
3089 if (layer.compositeSpace != XCFImageFormat::RgbLinearSpace) {
3090 qCDebug(XCFPLUGIN) << "Unimplemented composite color space" << layer.compositeSpace;
3091 }
3092 if (layer.compositeMode != XCFImageFormat::CompositeUnion) {
3093 qCDebug(XCFPLUGIN) << "Unhandled composite mode" << layer.compositeMode;
3094 }
3095
3096 switch (layer.type) {
3097 case RGB_GIMAGE:
3098 case RGBA_GIMAGE:
3099 merge = mergeRGBToRGB;
3100 break;
3101 case GRAY_GIMAGE:
3102 if (layer.opacity == OPAQUE_OPACITY && xcf_image.image.depth() <= 8) {
3103 merge = mergeGrayToGray;
3104 } else {
3105 merge = mergeGrayToRGB;
3106 }
3107 break;
3108 case GRAYA_GIMAGE:
3109 if (xcf_image.image.depth() <= 8) {
3110 merge = mergeGrayAToGray;
3111 } else {
3112 merge = mergeGrayAToRGB;
3113 }
3114 break;
3115 case INDEXED_GIMAGE:
3116 merge = mergeIndexedToIndexed;
3117 break;
3118 case INDEXEDA_GIMAGE:
3119 if (xcf_image.image.depth() <= 8) {
3120 merge = mergeIndexedAToIndexed;
3121 } else {
3122 merge = mergeIndexedAToRGB;
3123 }
3124 }
3125
3126 if (!merge) {
3127 return;
3128 }
3129
3130 if (merge == mergeRGBToRGB && layer.apply_mask != 1) {
3131 int painterMode = -1;
3132 switch (layer.mode) {
3133 case GIMP_LAYER_MODE_NORMAL:
3134 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3135 painterMode = QPainter::CompositionMode_SourceOver;
3136 break;
3137 case GIMP_LAYER_MODE_MULTIPLY:
3138 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3139 painterMode = QPainter::CompositionMode_Multiply;
3140 break;
3141 case GIMP_LAYER_MODE_SCREEN:
3142 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3143 painterMode = QPainter::CompositionMode_Screen;
3144 break;
3145 case GIMP_LAYER_MODE_OVERLAY:
3146 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3147 painterMode = QPainter::CompositionMode_Overlay;
3148 break;
3149 case GIMP_LAYER_MODE_DIFFERENCE:
3150 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3151 painterMode = QPainter::CompositionMode_Difference;
3152 break;
3153 case GIMP_LAYER_MODE_DARKEN_ONLY:
3154 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3155 painterMode = QPainter::CompositionMode_Darken;
3156 break;
3157 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3158 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3159 painterMode = QPainter::CompositionMode_Lighten;
3160 break;
3161 case GIMP_LAYER_MODE_DODGE:
3162 case GIMP_LAYER_MODE_DODGE_LEGACY:
3163 painterMode = QPainter::CompositionMode_ColorDodge;
3164 break;
3165 case GIMP_LAYER_MODE_BURN:
3166 case GIMP_LAYER_MODE_BURN_LEGACY:
3167 painterMode = QPainter::CompositionMode_ColorBurn;
3168 break;
3169 case GIMP_LAYER_MODE_HARDLIGHT:
3170 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
3171 painterMode = QPainter::CompositionMode_HardLight;
3172 break;
3173 case GIMP_LAYER_MODE_SOFTLIGHT:
3174 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
3175 painterMode = QPainter::CompositionMode_SoftLight;
3176 break;
3177 case GIMP_LAYER_MODE_ADDITION:
3178 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3179 painterMode = QPainter::CompositionMode_Plus;
3180 break;
3181 case GIMP_LAYER_MODE_EXCLUSION:
3182 painterMode = QPainter::CompositionMode_Exclusion;
3183 break;
3184
3185 // Not bothered to find what the QPainter equivalent is, or there is none
3186 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3187 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
3188 case GIMP_LAYER_MODE_GRAIN_MERGE:
3189 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
3190 case GIMP_LAYER_MODE_COLOR_ERASE:
3191 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
3192 case GIMP_LAYER_MODE_LCH_HUE:
3193 case GIMP_LAYER_MODE_LCH_CHROMA:
3194 case GIMP_LAYER_MODE_LCH_COLOR:
3195 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
3196 case GIMP_LAYER_MODE_BEHIND:
3197 case GIMP_LAYER_MODE_BEHIND_LEGACY:
3198 case GIMP_LAYER_MODE_SUBTRACT:
3199 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3200 case GIMP_LAYER_MODE_HSV_HUE:
3201 case GIMP_LAYER_MODE_HSV_SATURATION:
3202 case GIMP_LAYER_MODE_HSL_COLOR:
3203 case GIMP_LAYER_MODE_HSV_VALUE:
3204 case GIMP_LAYER_MODE_DIVIDE:
3205 case GIMP_LAYER_MODE_VIVID_LIGHT:
3206 case GIMP_LAYER_MODE_PIN_LIGHT:
3207 case GIMP_LAYER_MODE_LINEAR_LIGHT:
3208 case GIMP_LAYER_MODE_HARD_MIX:
3209 case GIMP_LAYER_MODE_LINEAR_BURN:
3210 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
3211 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
3212 case GIMP_LAYER_MODE_LUMINANCE:
3213 case GIMP_LAYER_MODE_ERASE:
3214 case GIMP_LAYER_MODE_MERGE:
3215 case GIMP_LAYER_MODE_SPLIT:
3216 case GIMP_LAYER_MODE_PASS_THROUGH:
3217 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
3218 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
3219 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
3220 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
3221 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3222 qCDebug(XCFPLUGIN) << "No QPainter equivalent to" << layer.mode;
3223 break;
3224
3225 // Special
3226 case GIMP_LAYER_MODE_DISSOLVE:
3227 case GIMP_LAYER_MODE_COUNT:
3228 break;
3229 }
3230
3231 if (painterMode != -1) {
3232 QPainter painter(&image);
3233 painter.setOpacity(layer.opacity / 255.0);
3234 painter.setCompositionMode(QPainter::CompositionMode(painterMode));
3235 qCDebug(XCFPLUGIN) << "Using QPainter for mode" << layer.mode;
3236
3237 for (uint j = 0; j < layer.nrows; j++) {
3238 qint32 y = qint32(j * TILE_HEIGHT);
3239
3240 for (uint i = 0; i < layer.ncols; i++) {
3241 qint32 x = qint32(i * TILE_WIDTH);
3242
3243 const QImage &tile = layer.image_tiles[j][i];
3244 if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) {
3245 painter.drawImage(x: x + layer.x_offset, y: y + layer.y_offset, image: tile);
3246 }
3247 }
3248 }
3249
3250 return;
3251 }
3252 }
3253
3254#ifndef DISABLE_IMAGE_PROFILE_CONV // The final profile should be the one in the Parasite
3255 if (layer.compositeSpace == XCFImageFormat::RgbPerceptualSpace && image.colorSpace() != QColorSpace::SRgb) {
3256 qCDebug(XCFPLUGIN) << "Converting to composite color space" << layer.compositeSpace;
3257 image.convertToColorSpace(QColorSpace::SRgb);
3258 }
3259 if (layer.compositeSpace == XCFImageFormat::RgbLinearSpace && image.colorSpace() != QColorSpace::SRgbLinear) {
3260 qCDebug(XCFPLUGIN) << "Converting to composite color space" << layer.compositeSpace;
3261 image.convertToColorSpace(QColorSpace::SRgbLinear);
3262 }
3263#endif
3264
3265 for (uint j = 0; j < layer.nrows; j++) {
3266 qint32 y = qint32(j * TILE_HEIGHT);
3267
3268 for (uint i = 0; i < layer.ncols; i++) {
3269 qint32 x = qint32(i * TILE_WIDTH);
3270
3271 // This seems the best place to apply the dissolve because it
3272 // depends on the global position of each tile's
3273 // pixels. Apparently it's the only mode which can apply to a
3274 // single layer.
3275
3276 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
3277 if (!random_table_initialized) {
3278 initializeRandomTable();
3279 random_table_initialized = true;
3280 }
3281 if (layer.type == RGBA_GIMAGE) {
3282 dissolveRGBPixels(image&: layer.image_tiles[j][i], x, y);
3283 }
3284
3285 else if (layer.type == GRAYA_GIMAGE) {
3286 dissolveAlphaPixels(image&: layer.alpha_tiles[j][i], x, y);
3287 }
3288 }
3289
3290 // Shortcut for common case
3291 if (merge == mergeRGBToRGB && layer.apply_mask != 1 && layer.mode == GIMP_LAYER_MODE_NORMAL_LEGACY) {
3292 QPainter painter(&image);
3293 painter.setOpacity(layer.opacity / 255.0);
3294 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
3295 if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) {
3296 painter.drawImage(x: x + layer.x_offset, y: y + layer.y_offset, image: layer.image_tiles[j][i]);
3297 }
3298 continue;
3299 }
3300
3301#ifndef DISABLE_TILE_PROFILE_CONV // not sure about that: left as old plugin
3302 QImage &tile = layer.image_tiles[j][i];
3303 if (layer.compositeSpace == XCFImageFormat::RgbPerceptualSpace && tile.colorSpace() != QColorSpace::SRgb) {
3304 tile.convertToColorSpace(QColorSpace::SRgb);
3305 }
3306 if (layer.compositeSpace == XCFImageFormat::RgbLinearSpace && tile.colorSpace() != QColorSpace::SRgbLinear) {
3307 tile.convertToColorSpace(QColorSpace::SRgbLinear);
3308 }
3309#endif
3310
3311 for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
3312 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
3313 int m = x + k + layer.x_offset;
3314 int n = y + l + layer.y_offset;
3315
3316 if (m < 0 || m >= image.width() || n < 0 || n >= image.height()) {
3317 continue;
3318 }
3319
3320 if (!(*merge)(layer, i, j, k, l, image, m, n)) {
3321 return;
3322 }
3323 }
3324 }
3325 }
3326 }
3327}
3328
3329/*!
3330 * Merge an RGB pixel from the layer to the RGB image. Straight-forward.
3331 * The only thing this has to take account of is the opacity of the
3332 * layer. Evidently, the GIMP exporter itself does not actually do this.
3333 * \param layer source layer.
3334 * \param i x tile index.
3335 * \param j y tile index.
3336 * \param k x pixel index of tile i,j.
3337 * \param l y pixel index of tile i,j.
3338 * \param image destination image.
3339 * \param m x pixel of destination image.
3340 * \param n y pixel of destination image.
3341 */
3342bool XCFImageFormat::mergeRGBToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3343{
3344 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
3345 QRgb dst = image.pixel(x: m, y: n);
3346
3347 uchar src_r = qRed(rgb: src);
3348 uchar src_g = qGreen(rgb: src);
3349 uchar src_b = qBlue(rgb: src);
3350 uchar src_a = qAlpha(rgb: src);
3351
3352 uchar dst_r = qRed(rgb: dst);
3353 uchar dst_g = qGreen(rgb: dst);
3354 uchar dst_b = qBlue(rgb: dst);
3355 uchar dst_a = qAlpha(rgb: dst);
3356
3357 if (!src_a) {
3358 return false; // nothing to merge
3359 }
3360
3361 switch (layer.mode) {
3362 case GIMP_LAYER_MODE_NORMAL:
3363 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3364 break;
3365 case GIMP_LAYER_MODE_MULTIPLY:
3366 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3367 src_r = INT_MULT(a: src_r, b: dst_r);
3368 src_g = INT_MULT(a: src_g, b: dst_g);
3369 src_b = INT_MULT(a: src_b, b: dst_b);
3370 src_a = qMin(a: src_a, b: dst_a);
3371 break;
3372 case GIMP_LAYER_MODE_DIVIDE:
3373 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3374 src_r = qMin(a: (dst_r * 256) / (1 + src_r), b: 255);
3375 src_g = qMin(a: (dst_g * 256) / (1 + src_g), b: 255);
3376 src_b = qMin(a: (dst_b * 256) / (1 + src_b), b: 255);
3377 src_a = qMin(a: src_a, b: dst_a);
3378 break;
3379 case GIMP_LAYER_MODE_SCREEN:
3380 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3381 src_r = 255 - INT_MULT(a: 255 - dst_r, b: 255 - src_r);
3382 src_g = 255 - INT_MULT(a: 255 - dst_g, b: 255 - src_g);
3383 src_b = 255 - INT_MULT(a: 255 - dst_b, b: 255 - src_b);
3384 src_a = qMin(a: src_a, b: dst_a);
3385 break;
3386 case GIMP_LAYER_MODE_OVERLAY:
3387 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3388 src_r = INT_MULT(a: dst_r, b: dst_r + INT_MULT(a: 2 * src_r, b: 255 - dst_r));
3389 src_g = INT_MULT(a: dst_g, b: dst_g + INT_MULT(a: 2 * src_g, b: 255 - dst_g));
3390 src_b = INT_MULT(a: dst_b, b: dst_b + INT_MULT(a: 2 * src_b, b: 255 - dst_b));
3391 src_a = qMin(a: src_a, b: dst_a);
3392 break;
3393 case GIMP_LAYER_MODE_DIFFERENCE:
3394 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3395 src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
3396 src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
3397 src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
3398 src_a = qMin(a: src_a, b: dst_a);
3399 break;
3400 case GIMP_LAYER_MODE_ADDITION:
3401 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3402 src_r = add_lut(a: dst_r, b: src_r);
3403 src_g = add_lut(a: dst_g, b: src_g);
3404 src_b = add_lut(a: dst_b, b: src_b);
3405 src_a = qMin(a: src_a, b: dst_a);
3406 break;
3407 case GIMP_LAYER_MODE_SUBTRACT:
3408 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3409 src_r = dst_r > src_r ? dst_r - src_r : 0;
3410 src_g = dst_g > src_g ? dst_g - src_g : 0;
3411 src_b = dst_b > src_b ? dst_b - src_b : 0;
3412 src_a = qMin(a: src_a, b: dst_a);
3413 break;
3414 case GIMP_LAYER_MODE_DARKEN_ONLY:
3415 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3416 src_r = dst_r < src_r ? dst_r : src_r;
3417 src_g = dst_g < src_g ? dst_g : src_g;
3418 src_b = dst_b < src_b ? dst_b : src_b;
3419 src_a = qMin(a: src_a, b: dst_a);
3420 break;
3421 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3422 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3423 src_r = dst_r < src_r ? src_r : dst_r;
3424 src_g = dst_g < src_g ? src_g : dst_g;
3425 src_b = dst_b < src_b ? src_b : dst_b;
3426 src_a = qMin(a: src_a, b: dst_a);
3427 break;
3428 case GIMP_LAYER_MODE_HSV_HUE:
3429 case GIMP_LAYER_MODE_HSV_HUE_LEGACY: {
3430 uchar new_r = dst_r;
3431 uchar new_g = dst_g;
3432 uchar new_b = dst_b;
3433
3434 RGBTOHSV(red&: src_r, green&: src_g, blue&: src_b);
3435 RGBTOHSV(red&: new_r, green&: new_g, blue&: new_b);
3436
3437 new_r = src_r;
3438
3439 HSVTORGB(hue&: new_r, saturation&: new_g, value&: new_b);
3440
3441 src_r = new_r;
3442 src_g = new_g;
3443 src_b = new_b;
3444 src_a = qMin(a: src_a, b: dst_a);
3445 } break;
3446 case GIMP_LAYER_MODE_HSV_SATURATION:
3447 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: {
3448 uchar new_r = dst_r;
3449 uchar new_g = dst_g;
3450 uchar new_b = dst_b;
3451
3452 RGBTOHSV(red&: src_r, green&: src_g, blue&: src_b);
3453 RGBTOHSV(red&: new_r, green&: new_g, blue&: new_b);
3454
3455 new_g = src_g;
3456
3457 HSVTORGB(hue&: new_r, saturation&: new_g, value&: new_b);
3458
3459 src_r = new_r;
3460 src_g = new_g;
3461 src_b = new_b;
3462 src_a = qMin(a: src_a, b: dst_a);
3463 } break;
3464 case GIMP_LAYER_MODE_HSV_VALUE:
3465 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY: {
3466 uchar new_r = dst_r;
3467 uchar new_g = dst_g;
3468 uchar new_b = dst_b;
3469
3470 RGBTOHSV(red&: src_r, green&: src_g, blue&: src_b);
3471 RGBTOHSV(red&: new_r, green&: new_g, blue&: new_b);
3472
3473 new_b = src_b;
3474
3475 HSVTORGB(hue&: new_r, saturation&: new_g, value&: new_b);
3476
3477 src_r = new_r;
3478 src_g = new_g;
3479 src_b = new_b;
3480 src_a = qMin(a: src_a, b: dst_a);
3481 } break;
3482 case GIMP_LAYER_MODE_HSL_COLOR:
3483 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: {
3484 uchar new_r = dst_r;
3485 uchar new_g = dst_g;
3486 uchar new_b = dst_b;
3487
3488 RGBTOHLS(red&: src_r, green&: src_g, blue&: src_b);
3489 RGBTOHLS(red&: new_r, green&: new_g, blue&: new_b);
3490
3491 new_r = src_r;
3492 new_b = src_b;
3493
3494 HLSTORGB(hue&: new_r, lightness&: new_g, saturation&: new_b);
3495
3496 src_r = new_r;
3497 src_g = new_g;
3498 src_b = new_b;
3499 src_a = qMin(a: src_a, b: dst_a);
3500 } break;
3501 case GIMP_LAYER_MODE_DODGE:
3502 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3503 uint tmp;
3504
3505 tmp = dst_r << 8;
3506 tmp /= 256 - src_r;
3507 src_r = (uchar)qMin(a: tmp, b: 255u);
3508
3509 tmp = dst_g << 8;
3510 tmp /= 256 - src_g;
3511 src_g = (uchar)qMin(a: tmp, b: 255u);
3512
3513 tmp = dst_b << 8;
3514 tmp /= 256 - src_b;
3515 src_b = (uchar)qMin(a: tmp, b: 255u);
3516
3517 src_a = qMin(a: src_a, b: dst_a);
3518 } break;
3519 case GIMP_LAYER_MODE_BURN:
3520 case GIMP_LAYER_MODE_BURN_LEGACY: {
3521 uint tmp;
3522
3523 tmp = (255 - dst_r) << 8;
3524 tmp /= src_r + 1;
3525 src_r = (uchar)qMin(a: tmp, b: 255u);
3526 src_r = 255 - src_r;
3527
3528 tmp = (255 - dst_g) << 8;
3529 tmp /= src_g + 1;
3530 src_g = (uchar)qMin(a: tmp, b: 255u);
3531 src_g = 255 - src_g;
3532
3533 tmp = (255 - dst_b) << 8;
3534 tmp /= src_b + 1;
3535 src_b = (uchar)qMin(a: tmp, b: 255u);
3536 src_b = 255 - src_b;
3537
3538 src_a = qMin(a: src_a, b: dst_a);
3539 } break;
3540 case GIMP_LAYER_MODE_HARDLIGHT:
3541 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3542 uint tmp;
3543 if (src_r > 128) {
3544 tmp = ((int)255 - dst_r) * ((int)255 - ((src_r - 128) << 1));
3545 src_r = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3546 } else {
3547 tmp = (int)dst_r * ((int)src_r << 1);
3548 src_r = (uchar)qMin(a: tmp >> 8, b: 255u);
3549 }
3550
3551 if (src_g > 128) {
3552 tmp = ((int)255 - dst_g) * ((int)255 - ((src_g - 128) << 1));
3553 src_g = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3554 } else {
3555 tmp = (int)dst_g * ((int)src_g << 1);
3556 src_g = (uchar)qMin(a: tmp >> 8, b: 255u);
3557 }
3558
3559 if (src_b > 128) {
3560 tmp = ((int)255 - dst_b) * ((int)255 - ((src_b - 128) << 1));
3561 src_b = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3562 } else {
3563 tmp = (int)dst_b * ((int)src_b << 1);
3564 src_b = (uchar)qMin(a: tmp >> 8, b: 255u);
3565 }
3566 src_a = qMin(a: src_a, b: dst_a);
3567 } break;
3568 case GIMP_LAYER_MODE_SOFTLIGHT:
3569 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3570 uint tmpS;
3571 uint tmpM;
3572
3573 tmpM = INT_MULT(a: dst_r, b: src_r);
3574 tmpS = 255 - INT_MULT(a: (255 - dst_r), b: (255 - src_r));
3575 src_r = INT_MULT(a: (255 - dst_r), b: tmpM) + INT_MULT(a: dst_r, b: tmpS);
3576
3577 tmpM = INT_MULT(a: dst_g, b: src_g);
3578 tmpS = 255 - INT_MULT(a: (255 - dst_g), b: (255 - src_g));
3579 src_g = INT_MULT(a: (255 - dst_g), b: tmpM) + INT_MULT(a: dst_g, b: tmpS);
3580
3581 tmpM = INT_MULT(a: dst_b, b: src_b);
3582 tmpS = 255 - INT_MULT(a: (255 - dst_b), b: (255 - src_b));
3583 src_b = INT_MULT(a: (255 - dst_b), b: tmpM) + INT_MULT(a: dst_b, b: tmpS);
3584
3585 src_a = qMin(a: src_a, b: dst_a);
3586 } break;
3587 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3588 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3589 int tmp;
3590
3591 tmp = dst_r - src_r + 128;
3592 tmp = qMin(a: tmp, b: 255);
3593 tmp = qMax(a: tmp, b: 0);
3594 src_r = (uchar)tmp;
3595
3596 tmp = dst_g - src_g + 128;
3597 tmp = qMin(a: tmp, b: 255);
3598 tmp = qMax(a: tmp, b: 0);
3599 src_g = (uchar)tmp;
3600
3601 tmp = dst_b - src_b + 128;
3602 tmp = qMin(a: tmp, b: 255);
3603 tmp = qMax(a: tmp, b: 0);
3604 src_b = (uchar)tmp;
3605
3606 src_a = qMin(a: src_a, b: dst_a);
3607 } break;
3608 case GIMP_LAYER_MODE_GRAIN_MERGE:
3609 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3610 int tmp;
3611
3612 tmp = dst_r + src_r - 128;
3613 tmp = qMin(a: tmp, b: 255);
3614 tmp = qMax(a: tmp, b: 0);
3615 src_r = (uchar)tmp;
3616
3617 tmp = dst_g + src_g - 128;
3618 tmp = qMin(a: tmp, b: 255);
3619 tmp = qMax(a: tmp, b: 0);
3620 src_g = (uchar)tmp;
3621
3622 tmp = dst_b + src_b - 128;
3623 tmp = qMin(a: tmp, b: 255);
3624 tmp = qMax(a: tmp, b: 0);
3625 src_b = (uchar)tmp;
3626
3627 src_a = qMin(a: src_a, b: dst_a);
3628 } break;
3629 case GIMP_LAYER_MODE_LINEAR_LIGHT: {
3630 if (src_r <= 128) {
3631 src_r = qBound(min: 0, val: dst_r + 2 * src_r - 255, max: 255);
3632 } else {
3633 src_r = qBound(min: 0, val: dst_r + 2 * (src_r - 128), max: 255);
3634 }
3635 if (src_g <= 128) {
3636 src_g = qBound(min: 0, val: dst_g + 2 * src_g - 255, max: 255);
3637 } else {
3638 src_g = qBound(min: 0, val: dst_g + 2 * (src_g - 127), max: 255);
3639 }
3640 if (src_b <= 128) {
3641 src_b = qBound(min: 0, val: dst_b + 2 * src_b - 255, max: 255);
3642 } else {
3643 src_b = qBound(min: 0, val: dst_b + 2 * (src_b - 127), max: 255);
3644 }
3645 } break;
3646 case GIMP_LAYER_MODE_VIVID_LIGHT: {
3647 // From http://www.simplefilter.de/en/basics/mixmods.html
3648 float A[3];
3649 A[0] = src_r / 255.;
3650 A[1] = src_g / 255.;
3651 A[2] = src_b / 255.;
3652 float B[3];
3653 B[0] = dst_r / 255.;
3654 B[1] = dst_g / 255.;
3655 B[2] = dst_b / 255.;
3656 float C[3]{};
3657 for (int i = 0; i < 3; i++) {
3658 if (A[i] <= 0.5f) {
3659 if (A[i] > 0.f) {
3660 C[i] = 1.f - (1.f - B[i]) / (2.f * A[i]);
3661 }
3662 } else {
3663 if (A[i] < 1.f) {
3664 C[i] = B[i] / (2.f * (1.f - A[i]));
3665 }
3666 }
3667 }
3668 src_r = qBound(min: 0.f, val: C[0] * 255.f, max: 255.f);
3669 src_g = qBound(min: 0.f, val: C[1] * 255.f, max: 255.f);
3670 src_b = qBound(min: 0.f, val: C[2] * 255.f, max: 255.f);
3671 } break;
3672 default:
3673 qCWarning(XCFPLUGIN) << "Unhandled mode" << layer.mode;
3674 return false;
3675 }
3676
3677 src_a = INT_MULT(a: src_a, b: layer.opacity);
3678
3679 // Apply the mask (if any)
3680
3681 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
3682 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
3683 }
3684
3685 uchar new_r;
3686 uchar new_g;
3687 uchar new_b;
3688 uchar new_a;
3689 new_a = dst_a + INT_MULT(a: OPAQUE_OPACITY - dst_a, b: src_a);
3690
3691 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3692 float dst_ratio = 1.0 - src_ratio;
3693
3694 new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
3695 new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
3696 new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
3697
3698 if (!modeAffectsSourceAlpha(type: layer.mode)) {
3699 new_a = dst_a;
3700 }
3701
3702 image.setPixel(x: m, y: n, index_or_rgb: qRgba(r: new_r, g: new_g, b: new_b, a: new_a));
3703 return true;
3704}
3705
3706/*!
3707 * Merge a Gray pixel from the layer to the Gray image. Straight-forward.
3708 * \param layer source layer.
3709 * \param i x tile index.
3710 * \param j y tile index.
3711 * \param k x pixel index of tile i,j.
3712 * \param l y pixel index of tile i,j.
3713 * \param image destination image.
3714 * \param m x pixel of destination image.
3715 * \param n y pixel of destination image.
3716 */
3717bool XCFImageFormat::mergeGrayToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3718{
3719 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
3720 image.setPixel(x: m, y: n, index_or_rgb: src);
3721 return true;
3722}
3723
3724/*!
3725 * Merge a GrayA pixel from the layer to the Gray image. Straight-forward.
3726 * \param layer source layer.
3727 * \param i x tile index.
3728 * \param j y tile index.
3729 * \param k x pixel index of tile i,j.
3730 * \param l y pixel index of tile i,j.
3731 * \param image destination image.
3732 * \param m x pixel of destination image.
3733 * \param n y pixel of destination image.
3734 */
3735bool XCFImageFormat::mergeGrayAToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3736{
3737 int src = qGray(rgb: layer.image_tiles[j][i].pixel(x: k, y: l));
3738 int dst = image.pixelIndex(x: m, y: n);
3739
3740 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
3741
3742 if (!src_a) {
3743 return false; // nothing to merge
3744 }
3745
3746 switch (layer.mode) {
3747 case GIMP_LAYER_MODE_MULTIPLY:
3748 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3749 src = INT_MULT(a: src, b: dst);
3750 } break;
3751 case GIMP_LAYER_MODE_DIVIDE:
3752 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3753 src = qMin(a: (dst * 256) / (1 + src), b: 255);
3754 } break;
3755 case GIMP_LAYER_MODE_SCREEN:
3756 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3757 src = 255 - INT_MULT(a: 255 - dst, b: 255 - src);
3758 } break;
3759 case GIMP_LAYER_MODE_OVERLAY:
3760 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3761 src = INT_MULT(a: dst, b: dst + INT_MULT(a: 2 * src, b: 255 - dst));
3762 } break;
3763 case GIMP_LAYER_MODE_DIFFERENCE:
3764 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3765 src = dst > src ? dst - src : src - dst;
3766 } break;
3767 case GIMP_LAYER_MODE_ADDITION:
3768 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3769 src = add_lut(a: dst, b: src);
3770 } break;
3771 case GIMP_LAYER_MODE_SUBTRACT:
3772 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3773 src = dst > src ? dst - src : 0;
3774 } break;
3775 case GIMP_LAYER_MODE_DARKEN_ONLY:
3776 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3777 src = dst < src ? dst : src;
3778 } break;
3779 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3780 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3781 src = dst < src ? src : dst;
3782 } break;
3783 case GIMP_LAYER_MODE_DODGE:
3784 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3785 uint tmp = dst << 8;
3786 tmp /= 256 - src;
3787 src = (uchar)qMin(a: tmp, b: 255u);
3788 } break;
3789 case GIMP_LAYER_MODE_BURN:
3790 case GIMP_LAYER_MODE_BURN_LEGACY: {
3791 uint tmp = (255 - dst) << 8;
3792 tmp /= src + 1;
3793 src = (uchar)qMin(a: tmp, b: 255u);
3794 src = 255 - src;
3795 } break;
3796 case GIMP_LAYER_MODE_HARDLIGHT:
3797 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3798 uint tmp;
3799 if (src > 128) {
3800 tmp = ((int)255 - dst) * ((int)255 - ((src - 128) << 1));
3801 src = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3802 } else {
3803 tmp = (int)dst * ((int)src << 1);
3804 src = (uchar)qMin(a: tmp >> 8, b: 255u);
3805 }
3806 } break;
3807 case GIMP_LAYER_MODE_SOFTLIGHT:
3808 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3809 uint tmpS;
3810 uint tmpM;
3811
3812 tmpM = INT_MULT(a: dst, b: src);
3813 tmpS = 255 - INT_MULT(a: (255 - dst), b: (255 - src));
3814 src = INT_MULT(a: (255 - dst), b: tmpM) + INT_MULT(a: dst, b: tmpS);
3815
3816 } break;
3817 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3818 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3819 int tmp;
3820
3821 tmp = dst - src + 128;
3822 tmp = qMin(a: tmp, b: 255);
3823 tmp = qMax(a: tmp, b: 0);
3824
3825 src = (uchar)tmp;
3826 } break;
3827 case GIMP_LAYER_MODE_GRAIN_MERGE:
3828 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3829 int tmp;
3830
3831 tmp = dst + src - 128;
3832 tmp = qMin(a: tmp, b: 255);
3833 tmp = qMax(a: tmp, b: 0);
3834
3835 src = (uchar)tmp;
3836 } break;
3837 default:
3838 qCWarning(XCFPLUGIN) << "Unhandled mode" << layer.mode;
3839 return false;
3840 }
3841
3842 src_a = INT_MULT(a: src_a, b: layer.opacity);
3843
3844 // Apply the mask (if any)
3845
3846 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
3847 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
3848 }
3849
3850 uchar new_a = OPAQUE_OPACITY;
3851
3852 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3853 float dst_ratio = 1.0 - src_ratio;
3854
3855 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3856
3857 image.setPixel(x: m, y: n, index_or_rgb: new_g);
3858 return true;
3859}
3860
3861/*!
3862 * Merge a Gray pixel from the layer to an RGB image. Straight-forward.
3863 * The only thing this has to take account of is the opacity of the
3864 * layer. Evidently, the GIMP exporter itself does not actually do this.
3865 * \param layer source layer.
3866 * \param i x tile index.
3867 * \param j y tile index.
3868 * \param k x pixel index of tile i,j.
3869 * \param l y pixel index of tile i,j.
3870 * \param image destination image.
3871 * \param m x pixel of destination image.
3872 * \param n y pixel of destination image.
3873 */
3874bool XCFImageFormat::mergeGrayToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3875{
3876 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
3877 uchar src_a = layer.opacity;
3878 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
3879 return true;
3880}
3881
3882/*!
3883 * Merge a GrayA pixel from the layer to an RGB image. Straight-forward.
3884 * The only thing this has to take account of is the opacity of the
3885 * layer. Evidently, the GIMP exporter itself does not actually do this.
3886 * \param layer source layer.
3887 * \param i x tile index.
3888 * \param j y tile index.
3889 * \param k x pixel index of tile i,j.
3890 * \param l y pixel index of tile i,j.
3891 * \param image destination image.
3892 * \param m x pixel of destination image.
3893 * \param n y pixel of destination image.
3894 */
3895bool XCFImageFormat::mergeGrayAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3896{
3897 int src = qGray(rgb: layer.image_tiles[j][i].pixel(x: k, y: l));
3898 int dst = qGray(rgb: image.pixel(x: m, y: n));
3899
3900 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
3901 uchar dst_a = qAlpha(rgb: image.pixel(x: m, y: n));
3902
3903 if (!src_a) {
3904 return false; // nothing to merge
3905 }
3906
3907 switch (layer.mode) {
3908 case GIMP_LAYER_MODE_NORMAL:
3909 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3910 break;
3911 case GIMP_LAYER_MODE_MULTIPLY:
3912 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3913 src = INT_MULT(a: src, b: dst);
3914 src_a = qMin(a: src_a, b: dst_a);
3915 } break;
3916 case GIMP_LAYER_MODE_DIVIDE:
3917 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3918 src = qMin(a: (dst * 256) / (1 + src), b: 255);
3919 src_a = qMin(a: src_a, b: dst_a);
3920 } break;
3921 case GIMP_LAYER_MODE_SCREEN:
3922 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3923 src = 255 - INT_MULT(a: 255 - dst, b: 255 - src);
3924 src_a = qMin(a: src_a, b: dst_a);
3925 } break;
3926 case GIMP_LAYER_MODE_OVERLAY:
3927 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3928 src = INT_MULT(a: dst, b: dst + INT_MULT(a: 2 * src, b: 255 - dst));
3929 src_a = qMin(a: src_a, b: dst_a);
3930 } break;
3931 case GIMP_LAYER_MODE_DIFFERENCE:
3932 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3933 src = dst > src ? dst - src : src - dst;
3934 src_a = qMin(a: src_a, b: dst_a);
3935 } break;
3936 case GIMP_LAYER_MODE_ADDITION:
3937 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3938 src = add_lut(a: dst, b: src);
3939 src_a = qMin(a: src_a, b: dst_a);
3940 } break;
3941 case GIMP_LAYER_MODE_SUBTRACT:
3942 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3943 src = dst > src ? dst - src : 0;
3944 src_a = qMin(a: src_a, b: dst_a);
3945 } break;
3946 case GIMP_LAYER_MODE_DARKEN_ONLY:
3947 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3948 src = dst < src ? dst : src;
3949 src_a = qMin(a: src_a, b: dst_a);
3950 } break;
3951 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3952 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3953 src = dst < src ? src : dst;
3954 src_a = qMin(a: src_a, b: dst_a);
3955 } break;
3956 case GIMP_LAYER_MODE_DODGE:
3957 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3958 uint tmp = dst << 8;
3959 tmp /= 256 - src;
3960 src = (uchar)qMin(a: tmp, b: 255u);
3961 src_a = qMin(a: src_a, b: dst_a);
3962 } break;
3963 case GIMP_LAYER_MODE_BURN:
3964 case GIMP_LAYER_MODE_BURN_LEGACY: {
3965 uint tmp = (255 - dst) << 8;
3966 tmp /= src + 1;
3967 src = (uchar)qMin(a: tmp, b: 255u);
3968 src = 255 - src;
3969 src_a = qMin(a: src_a, b: dst_a);
3970 } break;
3971 case GIMP_LAYER_MODE_HARDLIGHT:
3972 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3973 uint tmp;
3974 if (src > 128) {
3975 tmp = ((int)255 - dst) * ((int)255 - ((src - 128) << 1));
3976 src = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3977 } else {
3978 tmp = (int)dst * ((int)src << 1);
3979 src = (uchar)qMin(a: tmp >> 8, b: 255u);
3980 }
3981 src_a = qMin(a: src_a, b: dst_a);
3982 } break;
3983 case GIMP_LAYER_MODE_SOFTLIGHT:
3984 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3985 uint tmpS;
3986 uint tmpM;
3987
3988 tmpM = INT_MULT(a: dst, b: src);
3989 tmpS = 255 - INT_MULT(a: (255 - dst), b: (255 - src));
3990 src = INT_MULT(a: (255 - dst), b: tmpM) + INT_MULT(a: dst, b: tmpS);
3991
3992 src_a = qMin(a: src_a, b: dst_a);
3993 } break;
3994 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3995 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3996 int tmp;
3997
3998 tmp = dst - src + 128;
3999 tmp = qMin(a: tmp, b: 255);
4000 tmp = qMax(a: tmp, b: 0);
4001
4002 src = (uchar)tmp;
4003 src_a = qMin(a: src_a, b: dst_a);
4004 } break;
4005 case GIMP_LAYER_MODE_GRAIN_MERGE:
4006 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
4007 int tmp;
4008
4009 tmp = dst + src - 128;
4010 tmp = qMin(a: tmp, b: 255);
4011 tmp = qMax(a: tmp, b: 0);
4012
4013 src = (uchar)tmp;
4014 src_a = qMin(a: src_a, b: dst_a);
4015 } break;
4016 default:
4017 qCWarning(XCFPLUGIN) << "Unhandled mode" << layer.mode;
4018 return false;
4019 }
4020
4021 src_a = INT_MULT(a: src_a, b: layer.opacity);
4022
4023 // Apply the mask (if any)
4024 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
4025 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
4026 }
4027
4028 uchar new_a = dst_a + INT_MULT(a: OPAQUE_OPACITY - dst_a, b: src_a);
4029
4030 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
4031 float dst_ratio = 1.0 - src_ratio;
4032
4033 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
4034
4035 if (!modeAffectsSourceAlpha(type: layer.mode)) {
4036 new_a = dst_a;
4037 }
4038
4039 image.setPixel(x: m, y: n, index_or_rgb: qRgba(r: new_g, g: new_g, b: new_g, a: new_a));
4040 return true;
4041}
4042
4043/*!
4044 * Merge an Indexed pixel from the layer to the Indexed image. Straight-forward.
4045 * \param layer source layer.
4046 * \param i x tile index.
4047 * \param j y tile index.
4048 * \param k x pixel index of tile i,j.
4049 * \param l y pixel index of tile i,j.
4050 * \param image destination image.
4051 * \param m x pixel of destination image.
4052 * \param n y pixel of destination image.
4053 */
4054bool XCFImageFormat::mergeIndexedToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
4055{
4056 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
4057 image.setPixel(x: m, y: n, index_or_rgb: src);
4058 return true;
4059}
4060
4061/*!
4062 * Merge an IndexedA pixel from the layer to the Indexed image. Straight-forward.
4063 * \param layer source layer.
4064 * \param i x tile index.
4065 * \param j y tile index.
4066 * \param k x pixel index of tile i,j.
4067 * \param l y pixel index of tile i,j.
4068 * \param image destination image.
4069 * \param m x pixel of destination image.
4070 * \param n y pixel of destination image.
4071 */
4072bool XCFImageFormat::mergeIndexedAToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
4073{
4074 uchar src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
4075 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
4076 src_a = INT_MULT(a: src_a, b: layer.opacity);
4077
4078 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
4079 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
4080 }
4081
4082 if (src_a > 127) {
4083 src++;
4084 image.setPixel(x: m, y: n, index_or_rgb: src);
4085 }
4086 return true;
4087}
4088
4089/*!
4090 * Merge an IndexedA pixel from the layer to an RGB image. Straight-forward.
4091 * The only thing this has to take account of is the opacity of the
4092 * layer. Evidently, the GIMP exporter itself does not actually do this.
4093 * \param layer source layer.
4094 * \param i x tile index.
4095 * \param j y tile index.
4096 * \param k x pixel index of tile i,j.
4097 * \param l y pixel index of tile i,j.
4098 * \param image destination image.
4099 * \param m x pixel of destination image.
4100 * \param n y pixel of destination image.
4101 */
4102bool XCFImageFormat::mergeIndexedAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
4103{
4104 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
4105 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
4106 src_a = INT_MULT(a: src_a, b: layer.opacity);
4107
4108 // Apply the mask (if any)
4109 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
4110 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
4111 }
4112
4113 // This is what appears in the GIMP window
4114 if (src_a <= 127) {
4115 src_a = 0;
4116 } else {
4117 src_a = OPAQUE_OPACITY;
4118 }
4119
4120 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
4121 return true;
4122}
4123
4124/*!
4125 * Dissolving pixels: pick a random number between 0 and 255. If the pixel's
4126 * alpha is less than that, make it transparent.
4127 * \param image the image tile to dissolve.
4128 * \param x the global x position of the tile.
4129 * \param y the global y position of the tile.
4130 */
4131void XCFImageFormat::dissolveRGBPixels(QImage &image, int x, int y)
4132{
4133 // The apparently spurious rand() calls are to wind the random
4134 // numbers up to the same point for each tile.
4135
4136 for (int l = 0; l < image.height(); l++) {
4137 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4138
4139 for (int k = 0; k < x; k++) {
4140 RandomTable::rand_r(seed: &next);
4141 }
4142
4143 for (int k = 0; k < image.width(); k++) {
4144 int rand_val = RandomTable::rand_r(seed: &next) & 0xff;
4145 QRgb pixel = image.pixel(x: k, y: l);
4146
4147 if (rand_val > qAlpha(rgb: pixel)) {
4148 image.setPixel(x: k, y: l, index_or_rgb: qRgba(rgb: pixel, a: 0));
4149 }
4150 }
4151 }
4152}
4153
4154/*!
4155 * Dissolving pixels: pick a random number between 0 and 255. If the pixel's
4156 * alpha is less than that, make it transparent. This routine works for
4157 * the GRAYA and INDEXEDA image types where the pixel alpha's are stored
4158 * separately from the pixel themselves.
4159 * \param image the alpha tile to dissolve.
4160 * \param x the global x position of the tile.
4161 * \param y the global y position of the tile.
4162 */
4163void XCFImageFormat::dissolveAlphaPixels(QImage &image, int x, int y)
4164{
4165 // The apparently spurious rand() calls are to wind the random
4166 // numbers up to the same point for each tile.
4167
4168 for (int l = 0; l < image.height(); l++) {
4169 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4170
4171 for (int k = 0; k < x; k++) {
4172 RandomTable::rand_r(seed: &next);
4173 }
4174
4175 for (int k = 0; k < image.width(); k++) {
4176 int rand_val = RandomTable::rand_r(seed: &next) & 0xff;
4177 uchar alpha = image.pixelIndex(x: k, y: l);
4178
4179 if (rand_val > alpha) {
4180 image.setPixel(x: k, y: l, index_or_rgb: 0);
4181 }
4182 }
4183 }
4184}
4185
4186///////////////////////////////////////////////////////////////////////////////
4187
4188XCFHandler::XCFHandler()
4189{
4190}
4191
4192bool XCFHandler::canRead() const
4193{
4194 if (canRead(device: device())) {
4195 setFormat("xcf");
4196 return true;
4197 }
4198 return false;
4199}
4200
4201bool XCFHandler::read(QImage *image)
4202{
4203 XCFImageFormat xcfif;
4204 auto ok = xcfif.readXCF(device: device(), outImage: image);
4205 m_imageSize = image->size();
4206 return ok;
4207}
4208
4209bool XCFHandler::write(const QImage &)
4210{
4211 return false;
4212}
4213
4214bool XCFHandler::supportsOption(ImageOption option) const
4215{
4216 if (option == QImageIOHandler::Size)
4217 return true;
4218 return false;
4219}
4220
4221QVariant XCFHandler::option(ImageOption option) const
4222{
4223 QVariant v;
4224
4225 if (option == QImageIOHandler::Size) {
4226 if (!m_imageSize.isEmpty()) {
4227 return m_imageSize;
4228 }
4229 /*
4230 * The image structure always starts at offset 0 in the XCF file.
4231 * byte[9] "gimp xcf " File type identification
4232 * byte[4] version XCF version
4233 * "file": version 0
4234 * "v001": version 1
4235 * "v002": version 2
4236 * "v003": version 3
4237 * byte 0 Zero marks the end of the version tag.
4238 * uint32 width Width of canvas
4239 * uint32 height Height of canvas
4240 */
4241 else if (auto d = device()) {
4242 // transactions works on both random and sequential devices
4243 d->startTransaction();
4244 auto ba9 = d->read(maxlen: 9); // "gimp xcf "
4245 auto ba5 = d->read(maxlen: 4+1); // version + null terminator
4246 auto ba = d->read(maxlen: 8); // width and height
4247 d->rollbackTransaction();
4248 if (ba9 == QByteArray("gimp xcf ") && ba5.size() == 5) {
4249 QDataStream ds(ba);
4250 quint32 width;
4251 ds >> width;
4252 quint32 height;
4253 ds >> height;
4254 if (ds.status() == QDataStream::Ok)
4255 v = QVariant::fromValue(value: QSize(width, height));
4256 }
4257 }
4258 }
4259
4260 return v;
4261}
4262
4263bool XCFHandler::canRead(QIODevice *device)
4264{
4265 if (!device) {
4266 qCDebug(XCFPLUGIN) << "XCFHandler::canRead() called with no device";
4267 return false;
4268 }
4269 if (device->isSequential()) {
4270 return false;
4271 }
4272
4273 const qint64 oldPos = device->pos();
4274
4275 QDataStream ds(device);
4276 XCFImageFormat::XCFImage::Header header;
4277 bool failed = !XCFImageFormat::readXCFHeader(xcf_io&: ds, header: &header);
4278 ds.setDevice(nullptr);
4279
4280 if (!device->seek(pos: oldPos)) {
4281 return false;
4282 }
4283 if (failed) {
4284 return false;
4285 }
4286
4287 switch (header.precision) {
4288 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
4289 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
4290 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
4291 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
4292 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
4293 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
4294 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
4295 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
4296 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
4297 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
4298 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
4299 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
4300 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
4301 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
4302 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
4303 break;
4304 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
4305 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
4306 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
4307 default:
4308 qCDebug(XCFPLUGIN) << "unsupported precision" << header.precision;
4309 return false;
4310 }
4311
4312 return true;
4313}
4314
4315QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
4316{
4317 if (format == "xcf") {
4318 return Capabilities(CanRead);
4319 }
4320 if (!format.isEmpty()) {
4321 return {};
4322 }
4323 if (!device->isOpen()) {
4324 return {};
4325 }
4326
4327 Capabilities cap;
4328 if (device->isReadable() && XCFHandler::canRead(device)) {
4329 cap |= CanRead;
4330 }
4331 return cap;
4332}
4333
4334QImageIOHandler *XCFPlugin::create(QIODevice *device, const QByteArray &format) const
4335{
4336 QImageIOHandler *handler = new XCFHandler;
4337 handler->setDevice(device);
4338 handler->setFormat(format);
4339 return handler;
4340}
4341
4342// Just so I can get enum values printed
4343#include "xcf.moc"
4344
4345#include "moc_xcf_p.cpp"
4346

source code of kimageformats/src/imageformats/xcf.cpp