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

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