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(len: 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(len: 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 xcf_io.device()->seek(pos: layer_offset);
904
905 if (!loadLayer(xcf_io, xcf_image)) {
906 return false;
907 }
908 }
909
910 if (!xcf_image.initialized) {
911 qCDebug(XCFPLUGIN) << "XCF: no visible layers!";
912 return false;
913 }
914
915 // The image was created: now I can set metadata and ICC color profile inside it.
916 setImageParasites(xcf_image, image&: xcf_image.image);
917
918 *outImage = xcf_image.image;
919 return true;
920}
921
922/*!
923 * An XCF file can contain an arbitrary number of properties associated
924 * with the image (and layer and mask).
925 * \param xcf_io the data stream connected to the XCF image
926 * \param xcf_image XCF image data.
927 * \return true if there were no I/O errors.
928 */
929bool XCFImageFormat::loadImageProperties(QDataStream &xcf_io, XCFImage &xcf_image)
930{
931 while (true) {
932 PropType type;
933 QByteArray bytes;
934 quint32 rawType;
935
936 if (!loadProperty(xcf_io, type, bytes, rawType)) {
937 qCDebug(XCFPLUGIN) << "XCF: error loading global image properties";
938 return false;
939 }
940
941 QDataStream property(bytes);
942
943 switch (type) {
944 case PROP_END:
945 return true;
946
947 case PROP_COMPRESSION:
948 property >> xcf_image.compression;
949 break;
950
951 case PROP_RESOLUTION:
952 property.setFloatingPointPrecision(QDataStream::SinglePrecision);
953 property >> xcf_image.x_resolution >> xcf_image.y_resolution;
954 break;
955
956 case PROP_TATTOO:
957 property >> xcf_image.tattoo;
958 break;
959
960 case PROP_PARASITES:
961 while (!property.atEnd()) {
962 char *tag;
963#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
964 quint32 size;
965#else
966 qint64 size;
967#endif
968
969 property.readBytes(tag, len&: size);
970
971 quint32 flags;
972 QByteArray data;
973 property >> flags >> data;
974
975 // WARNING: you cannot add metadata to QImage here because it can be null.
976 // Adding a metadata to a QImage when it is null, does nothing (metas are lost).
977 if (tag) // store metadata for future use
978 xcf_image.parasites.insert(key: QString::fromUtf8(utf8: tag), value: data);
979
980 delete[] tag;
981 }
982 break;
983
984 case PROP_UNIT:
985 property >> xcf_image.unit;
986 break;
987
988 case PROP_PATHS: // This property is ignored.
989 break;
990
991 case PROP_USER_UNIT: // This property is ignored.
992 break;
993
994 case PROP_COLORMAP:
995 property >> xcf_image.num_colors;
996 if (xcf_image.num_colors < 0 || xcf_image.num_colors > 65535) {
997 return false;
998 }
999
1000 xcf_image.palette = QList<QRgb>();
1001 xcf_image.palette.reserve(asize: xcf_image.num_colors);
1002
1003 for (int i = 0; i < xcf_image.num_colors; i++) {
1004 uchar r;
1005 uchar g;
1006 uchar b;
1007 property >> r >> g >> b;
1008 xcf_image.palette.push_back(t: qRgb(r, g, b));
1009 }
1010 break;
1011
1012 default:
1013 qCDebug(XCFPLUGIN) << "XCF: unimplemented image property" << type << "(" << rawType << ")"
1014 << ", size " << bytes.size();
1015 break;
1016 }
1017 }
1018}
1019
1020/*!
1021 * Read a single property from the image file. The property type is returned
1022 * in type and the data is returned in bytes.
1023 * \param xcf the image file data stream.
1024 * \param type returns with the property type.
1025 * \param bytes returns with the property data.
1026 * \return true if there were no IO errors. */
1027bool XCFImageFormat::loadProperty(QDataStream &xcf_io, PropType &type, QByteArray &bytes, quint32 &rawType)
1028{
1029 quint32 size;
1030
1031 xcf_io >> rawType;
1032 if (rawType >= MAX_SUPPORTED_PROPTYPE) {
1033 type = MAX_SUPPORTED_PROPTYPE;
1034 // we don't support the property, but we still need to read from the device, assume it's like all the
1035 // non custom properties that is data_length + data
1036 xcf_io >> size;
1037 xcf_io.skipRawData(len: size);
1038 // return true because we don't really want to totally fail on an unsupported property since it may not be fatal
1039 return true;
1040 }
1041
1042 type = PropType(rawType);
1043
1044 char *data = nullptr;
1045
1046 // The colormap property size is not the correct number of bytes:
1047 // The GIMP source xcf.c has size = 4 + ncolors, but it should be
1048 // 4 + 3 * ncolors
1049
1050 if (type == PROP_COLORMAP) {
1051 xcf_io >> size;
1052 quint32 ncolors;
1053 xcf_io >> ncolors;
1054
1055 size = 3 * ncolors + 4;
1056
1057 if (size > 65535 || size < 4) {
1058 return false;
1059 }
1060
1061 data = new char[size];
1062
1063 // since we already read "ncolors" from the stream, we put that data back
1064 data[0] = 0;
1065 data[1] = 0;
1066 data[2] = ncolors >> 8;
1067 data[3] = ncolors & 255;
1068
1069 // ... and read the remaining bytes from the stream
1070 xcf_io.readRawData(data + 4, len: size - 4);
1071 } else if (type == PROP_USER_UNIT) {
1072 // The USER UNIT property size is not correct. I'm not sure why, though.
1073 float factor;
1074 qint32 digits;
1075
1076 xcf_io >> size >> factor >> digits;
1077
1078 for (int i = 0; i < 5; i++) {
1079 char *unit_strings;
1080
1081 xcf_io >> unit_strings;
1082
1083 delete[] unit_strings;
1084
1085 if (xcf_io.device()->atEnd()) {
1086 qCDebug(XCFPLUGIN) << "XCF: read failure on property " << type;
1087 return false;
1088 }
1089 }
1090
1091 size = 0;
1092 } else {
1093 xcf_io >> size;
1094 if (size > 256000) {
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(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);
1307 }
1308 break;
1309
1310 case PROP_COMPOSITE_MODE:
1311 property >> layer.compositeMode;
1312 if (layer.compositeMode < 0) {
1313 layer.compositeMode = XCFImageFormat::GimpCompositeMode(-layer.compositeMode);
1314 }
1315 break;
1316
1317 case PROP_BLEND_SPACE:
1318 property >> layer.blendSpace;
1319 if (layer.blendSpace) {
1320 layer.blendSpace = GimpColorSpace(-layer.blendSpace);
1321 }
1322 break;
1323
1324 // Just for organization in the UI, doesn't influence rendering
1325 case PROP_COLOR_TAG:
1326 break;
1327
1328 // We don't support editing, so for now just ignore locking
1329 case PROP_LOCK_CONTENT:
1330 case PROP_LOCK_POSITION:
1331 break;
1332
1333 default:
1334 qCDebug(XCFPLUGIN) << "XCF: unimplemented layer property " << type << "(" << rawType << ")"
1335 << ", size " << bytes.size();
1336 break;
1337 }
1338 }
1339}
1340
1341/*!
1342 * Compute the number of tiles in the current layer and allocate
1343 * QImage structures for each of them.
1344 * \param xcf_image contains the current layer.
1345 */
1346bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
1347{
1348 Layer &layer(xcf_image.layer);
1349
1350 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
1351 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
1352
1353 qCDebug(XCFPLUGIN) << "IMAGE: height=" << xcf_image.header.height << ", width=" << xcf_image.header.width;
1354 qCDebug(XCFPLUGIN) << "LAYER: height=" << layer.height << ", width=" << layer.width;
1355 qCDebug(XCFPLUGIN) << "LAYER: rows=" << layer.nrows << ", columns=" << layer.ncols;
1356
1357 // NOTE: starting from GIMP 2.10, images can be very large. The 32K limit for width and height is obsolete
1358 // and it was changed to 300000 (the same as Photoshop Big image). This plugin was able to open an RGB
1359 // image of 108000x40000 pixels saved with GIMP 2.10
1360 // SANITY CHECK: Catch corrupted XCF image file where the width or height
1361 // of a tile is reported are bogus. See Bug# 234030.
1362 if ((sizeof(void *) == 4 && qint64(layer.width) * layer.height > 16384 * 16384)) {
1363 qCWarning(XCFPLUGIN) << "On 32-bits programs the maximum layer size is limited to" << 16384 << "x" << 16384 << "px";
1364 return false;
1365 }
1366 if (layer.width > MAX_IMAGE_WIDTH || layer.height > MAX_IMAGE_HEIGHT) {
1367 qCWarning(XCFPLUGIN) << "The maximum layer size is limited to" << MAX_IMAGE_WIDTH << "x" << MAX_IMAGE_HEIGHT << "px";
1368 return false;
1369 }
1370
1371 // NOTE: A layer is a named rectangular area of pixels which has a definite position with respect to the canvas.
1372 // It may extend beyond the canvas or (more commonly) only cover some of it.
1373 // SANITY CHECK: Avoid to load XCF with a layer grater than 10 times the final image
1374 if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
1375 if (qint64(layer.width) * layer.height > 16384 * 16384) { // large layers only
1376 qCWarning(XCFPLUGIN) << "Euristic sanity check: the image may be corrupted!";
1377 return false;
1378 }
1379 }
1380
1381#ifndef XCF_QT5_SUPPORT
1382 // Qt 6 image allocation limit calculation: we have to check the limit here because the image is splitted in
1383 // tiles of 64x64 pixels. The required memory to build the image is at least doubled because tiles are loaded
1384 // and then the final image is created by copying the tiles inside it.
1385 // NOTE: on Windows to open a 10GiB image the plugin uses 28GiB of RAM
1386 qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
1387 if (qint64(layer.width) * qint64(layer.height) * channels * 2ll / 1024ll / 1024ll > QImageReader::allocationLimit()) {
1388 qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit of" << QImageReader::allocationLimit() << "megabytes";
1389 return false;
1390 }
1391#endif
1392
1393 layer.image_tiles.resize(size: layer.nrows);
1394
1395 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1396 layer.alpha_tiles.resize(size: layer.nrows);
1397 }
1398
1399 if (layer.mask_offset != 0) {
1400 layer.mask_tiles.resize(size: layer.nrows);
1401 }
1402
1403 for (uint j = 0; j < layer.nrows; j++) {
1404 layer.image_tiles[j].resize(size: layer.ncols);
1405
1406 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1407 layer.alpha_tiles[j].resize(size: layer.ncols);
1408 }
1409
1410 if (layer.mask_offset != 0) {
1411 layer.mask_tiles[j].resize(size: layer.ncols);
1412 }
1413 }
1414
1415 const QImage::Format format = layer.qimageFormat(precision: xcf_image.header.precision);
1416
1417 for (uint j = 0; j < layer.nrows; j++) {
1418 for (uint i = 0; i < layer.ncols; i++) {
1419 uint tile_width = (i + 1) * TILE_WIDTH <= layer.width ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
1420
1421 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
1422
1423 // Try to create the most appropriate QImage (each GIMP layer
1424 // type is treated slightly differently)
1425
1426 switch (layer.type) {
1427 case RGB_GIMAGE:
1428 case RGBA_GIMAGE:
1429 layer.image_tiles[j][i] = QImage(tile_width, tile_height, format);
1430 if (layer.image_tiles[j][i].isNull()) {
1431 return false;
1432 }
1433 layer.image_tiles[j][i].setColorCount(0);
1434 break;
1435
1436 case GRAY_GIMAGE:
1437 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1438 if (layer.image_tiles[j][i].isNull()) {
1439 return false;
1440 }
1441 layer.image_tiles[j][i].setColorCount(256);
1442 setGrayPalette(layer.image_tiles[j][i]);
1443 break;
1444
1445 case GRAYA_GIMAGE:
1446 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1447 layer.image_tiles[j][i].setColorCount(256);
1448 if (layer.image_tiles[j][i].isNull()) {
1449 return false;
1450 }
1451 setGrayPalette(layer.image_tiles[j][i]);
1452
1453 layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1454 if (layer.alpha_tiles[j][i].isNull()) {
1455 return false;
1456 }
1457 layer.alpha_tiles[j][i].setColorCount(256);
1458 setGrayPalette(layer.alpha_tiles[j][i]);
1459 break;
1460
1461 case INDEXED_GIMAGE:
1462 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1463 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1464 if (layer.image_tiles[j][i].isNull()) {
1465 return false;
1466 }
1467 setPalette(xcf_image, image&: layer.image_tiles[j][i]);
1468 break;
1469
1470 case INDEXEDA_GIMAGE:
1471 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1472 if (layer.image_tiles[j][i].isNull()) {
1473 return false;
1474 }
1475 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1476 setPalette(xcf_image, image&: layer.image_tiles[j][i]);
1477
1478 layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1479 if (layer.alpha_tiles[j][i].isNull()) {
1480 return false;
1481 }
1482 layer.alpha_tiles[j][i].setColorCount(256);
1483 setGrayPalette(layer.alpha_tiles[j][i]);
1484 }
1485 if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
1486 qCWarning(XCFPLUGIN) << "Selected wrong tile format" << layer.image_tiles[j][i].format() << "expected" << format;
1487 return false;
1488 }
1489
1490#ifndef DISABLE_TILE_PROFILE
1491 switch (xcf_image.header.precision) {
1492 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1493 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1494 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1495 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1496 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1497 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1498 layer.image_tiles[j][i].setColorSpace(QColorSpace::SRgbLinear);
1499 break;
1500 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
1501 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
1502 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
1503 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
1504 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
1505 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
1506 layer.image_tiles[j][i].setColorSpace(QColorSpace::SRgb);
1507 break;
1508 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
1509 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
1510 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
1511 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
1512 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
1513 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
1514 layer.image_tiles[j][i].setColorSpace(QColorSpace::SRgb);
1515 break;
1516 }
1517#endif
1518 if (layer.mask_offset != 0) {
1519 layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
1520 layer.mask_tiles[j][i].setColorCount(256);
1521 if (layer.mask_tiles[j][i].isNull()) {
1522 return false;
1523 }
1524 setGrayPalette(layer.mask_tiles[j][i]);
1525 }
1526 }
1527 }
1528 return true;
1529}
1530
1531/*!
1532 * Apply a grayscale palette to the QImage. Note that Qt does not distinguish
1533 * between grayscale and indexed images. A grayscale image is just
1534 * an indexed image with a 256-color, grayscale palette.
1535 * \param image image to set to a grayscale palette.
1536 */
1537void XCFImageFormat::setGrayPalette(QImage &image)
1538{
1539 if (grayTable.isEmpty()) {
1540 grayTable.resize(size: 256);
1541
1542 for (int i = 0; i < 256; i++) {
1543 grayTable[i] = qRgb(r: i, g: i, b: i);
1544 }
1545 }
1546
1547 image.setColorTable(grayTable);
1548}
1549
1550/*!
1551 * Copy the indexed palette from the XCF image into the QImage.
1552 * \param xcf_image XCF image containing the palette read from the data stream.
1553 * \param image image to apply the palette to.
1554 */
1555void XCFImageFormat::setPalette(XCFImage &xcf_image, QImage &image)
1556{
1557 Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size());
1558
1559 image.setColorTable(xcf_image.palette);
1560}
1561
1562/*!
1563 * Copy the parasites info to QImage.
1564 * \param xcf_image XCF image containing the parasites read from the data stream.
1565 * \param image image to apply the parasites data.
1566 * \note Some comment taken from https://gitlab.gnome.org/GNOME/gimp/-/blob/master/devel-docs/parasites.txt
1567 */
1568void XCFImageFormat::setImageParasites(const XCFImage &xcf_image, QImage &image)
1569{
1570 auto&& p = xcf_image.parasites;
1571 auto keys = p.keys();
1572 for (auto &&key : std::as_const(t&: keys)) {
1573 auto value = p.value(key);
1574 if (value.isEmpty())
1575 continue;
1576
1577 // "icc-profile" (IMAGE, PERSISTENT | UNDOABLE)
1578 // This contains an ICC profile describing the color space the
1579 // image was produced in. TIFF images stored in PhotoShop do
1580 // oftentimes contain embedded profiles. An experimental color
1581 // manager exists to use this parasite, and it will be used
1582 // for interchange between TIFF and PNG (identical profiles)
1583 if (key == QStringLiteral("icc-profile")) {
1584 auto cs = QColorSpace::fromIccProfile(iccProfile: value);
1585 if (cs.isValid())
1586 image.setColorSpace(cs);
1587 continue;
1588 }
1589
1590 // "gimp-comment" (IMAGE, PERSISTENT)
1591 // Standard GIF-style image comments. This parasite should be
1592 // human-readable text in UTF-8 encoding. A trailing \0 might
1593 // be included and is not part of the comment. Note that image
1594 // comments may also be present in the "gimp-metadata" parasite.
1595 if (key == QStringLiteral("gimp-comment")) {
1596 value.replace(before: '\0', after: QByteArray());
1597 image.setText(QStringLiteral("Comment"), value: QString::fromUtf8(ba: value));
1598 continue;
1599 }
1600
1601 // "gimp-image-metadata"
1602 // Saved by GIMP 2.10.30 but it is not mentioned in the specification.
1603 // It is an XML block with the properties set using GIMP.
1604 if (key == QStringLiteral("gimp-image-metadata")) {
1605 // NOTE: I arbitrary defined the metadata "XML:org.gimp.xml" because it seems
1606 // a GIMP proprietary XML format (no xmlns defined)
1607 value.replace(before: '\0', after: QByteArray());
1608 image.setText(QStringLiteral("XML:org.gimp.xml"), value: QString::fromUtf8(ba: value));
1609 continue;
1610 }
1611
1612#if 0 // Unable to generate it using latest GIMP version
1613 // "gimp-metadata" (IMAGE, PERSISTENT)
1614 // The metadata associated with the image, serialized as one XMP
1615 // packet. This metadata includes the contents of any XMP, EXIF
1616 // and IPTC blocks from the original image, as well as
1617 // user-specified values such as image comment, copyright,
1618 // license, etc.
1619 if (key == QStringLiteral("gimp-metadata")) {
1620 // NOTE: "XML:com.adobe.xmp" is the meta set by Qt reader when an
1621 // XMP packet is found (e.g. when reading a PNG saved by Photoshop).
1622 // I reused the same key because some programs could search for it.
1623 value.replace('\0', QByteArray());
1624 image.setText(QStringLiteral("XML:com.adobe.xmp"), QString::fromUtf8(value));
1625 continue;
1626 }
1627#endif
1628 }
1629
1630#ifdef DISABLE_IMAGE_PROFILE
1631 // final colorspace checks
1632 if (!image.colorSpace().isValid()) {
1633 switch (xcf_image.header.precision) {
1634 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1635 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1636 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1637 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1638 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1639 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1640 image.setColorSpace(QColorSpace::SRgbLinear);
1641 break;
1642 default:
1643 image.setColorSpace(QColorSpace::SRgb);
1644 break;
1645 }
1646 }
1647#endif
1648}
1649
1650/*!
1651 * Copy the bytes from the tile buffer into the image tile QImage, taking into
1652 * account all the myriad different modes.
1653 * \param layer layer containing the tile buffer and the image tile matrix.
1654 * \param i column index of current tile.
1655 * \param j row index of current tile.
1656 */
1657bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPrecision &precision)
1658{
1659 QImage &image = layer.image_tiles[j][i];
1660
1661 const uchar *tile = layer.tile;
1662 const int width = image.width();
1663 const int height = image.height();
1664 const int bytesPerLine = image.bytesPerLine();
1665 uchar *bits = image.bits();
1666
1667 // Handle the special cases
1668 if (layer.type == GRAYA_GIMAGE || layer.type == GRAY_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1669 auto bpc = bytesPerChannel(precision);
1670 for (int y = 0; y < height; y++) {
1671 uchar *dataPtr = bits + y * bytesPerLine;
1672 uchar *alphaPtr = nullptr;
1673 if (!layer.alpha_tiles.isEmpty())
1674 alphaPtr = layer.alpha_tiles[j][i].scanLine(y);
1675 if (bpc == 4) {
1676#ifdef USE_FLOAT_IMAGES
1677 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1678 for (int x = 0; x < width; x++) {
1679 auto src = (const quint16 *)tile;
1680 *dataPtr++ = qFromBigEndian<quint16>(source: src[0]) / 257;
1681 if (alphaPtr) {
1682 *alphaPtr++ = qFromBigEndian<quint16>(source: src[1]) / 257;
1683 tile += sizeof(quint16) * 2;
1684 } else {
1685 tile += sizeof(quint16);
1686 }
1687 }
1688 } else {
1689 for (int x = 0; x < width; x++) {
1690 auto src = (const float *)tile;
1691 *dataPtr++ = qFromBigEndian<float>(source: src[0]) * 255;
1692 if (alphaPtr) {
1693 *alphaPtr++ = qFromBigEndian<float>(source: src[1]) * 255;
1694 tile += sizeof(float) * 2;
1695 } else {
1696 tile += sizeof(float);
1697 }
1698 }
1699 }
1700#else
1701 for (int x = 0; x < width; x++) {
1702 auto src = (const quint16 *)tile;
1703 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1704 if (alphaPtr) {
1705 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1706 tile += sizeof(quint16) * 2;
1707 } else {
1708 tile += sizeof(quint16);
1709 }
1710 }
1711#endif
1712 } else if (bpc == 2) {
1713#ifdef USE_FLOAT_IMAGES
1714 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1715 for (int x = 0; x < width; x++) {
1716 auto src = (const quint16 *)tile;
1717 *dataPtr++ = qFromBigEndian<quint16>(source: src[0]) / 257;
1718 if (alphaPtr)
1719 *alphaPtr++ = qFromBigEndian<quint16>(source: src[1]) / 257;
1720 tile += sizeof(QRgb);
1721 }
1722 } else {
1723 for (int x = 0; x < width; x++) {
1724 auto src = (const qfloat16 *)tile;
1725 *dataPtr++ = qFromBigEndian<qfloat16>(source: src[0]) * 255;
1726 if (alphaPtr)
1727 *alphaPtr++ = qFromBigEndian<qfloat16>(source: src[1]) * 255;
1728 tile += sizeof(QRgb);
1729 }
1730 }
1731#else
1732 for (int x = 0; x < width; x++) {
1733 auto src = (const quint16 *)tile;
1734 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1735 if (alphaPtr)
1736 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1737 tile += sizeof(QRgb);
1738 }
1739#endif
1740 } else {
1741 for (int x = 0; x < width; x++) {
1742 if (tile[0] < image.colorCount())
1743 *dataPtr++ = tile[0];
1744 if (alphaPtr)
1745 *alphaPtr++ = tile[1];
1746 tile += sizeof(QRgb);
1747 }
1748 }
1749 }
1750 return true;
1751 }
1752
1753 switch (image.format()) {
1754 case QImage::Format_RGBX8888:
1755 for (int y = 0; y < height; y++) {
1756 uchar *dataPtr = image.scanLine(y);
1757 for (int x = 0; x < width * 4; x += 4, tile += 4) {
1758 dataPtr[x + 0] = tile[0];
1759 dataPtr[x + 1] = tile[1];
1760 dataPtr[x + 2] = tile[2];
1761 dataPtr[x + 3] = 255;
1762 }
1763 }
1764 break;
1765 case QImage::Format_RGBA8888:
1766 for (int y = 0; y < height; y++) {
1767 const size_t bpl = width * 4;
1768 memcpy(dest: image.scanLine(y), src: tile + y * bpl, n: bpl);
1769 }
1770 break;
1771 case QImage::Format_RGBX64:
1772 for (int y = 0; y < height; y++) {
1773 quint16 *dataPtr = (quint16 *)image.scanLine(y);
1774 const size_t bpl = width * sizeof(QRgba64);
1775 const quint16 *src = (const quint16 *)(tile + y * bpl);
1776 for (int x = 0; x < width * 4; x += 4) {
1777 dataPtr[x + 0] = qFromBigEndian(source: src[x + 0]);
1778 dataPtr[x + 1] = qFromBigEndian(source: src[x + 1]);
1779 dataPtr[x + 2] = qFromBigEndian(source: src[x + 2]);
1780 dataPtr[x + 3] = 65535;
1781 }
1782 }
1783 break;
1784#ifdef USE_FLOAT_IMAGES
1785 case QImage::Format_RGBX16FPx4:
1786 for (int y = 0; y < height; y++) {
1787 qfloat16 *dataPtr = (qfloat16 *)image.scanLine(y);
1788 const qfloat16 *src = (const qfloat16 *)(tile + y * width * sizeof(QRgbaFloat16));
1789 for (int x = 0; x < width * 4; x += 4) {
1790 dataPtr[x + 0] = qFromBigEndian(source: src[x + 0]);
1791 dataPtr[x + 1] = qFromBigEndian(source: src[x + 1]);
1792 dataPtr[x + 2] = qFromBigEndian(source: src[x + 2]);
1793 dataPtr[x + 3] = qfloat16(1);
1794 }
1795 }
1796 break;
1797 case QImage::Format_RGBA16FPx4:
1798 static_assert(sizeof(QRgbaFloat16) == sizeof(QRgba64), "Different sizes for float and int 16 bit pixels");
1799#endif
1800 case QImage::Format_RGBA64:
1801 for (int y = 0; y < height; y++) {
1802 const size_t bpl = width * sizeof(QRgba64);
1803 qFromBigEndian<qint16>(source: tile + y * bpl, count: width * 4, dest: image.scanLine(y));
1804 }
1805 break;
1806#ifdef USE_FLOAT_IMAGES
1807 case QImage::Format_RGBA32FPx4:
1808 for (int y = 0; y < height; y++) {
1809 const size_t bpl = width * sizeof(QRgbaFloat32);
1810 qFromBigEndian<qint32>(source: tile + y * bpl, count: width * 4, dest: image.scanLine(y));
1811 }
1812 break;
1813 case QImage::Format_RGBX32FPx4:
1814 for (int y = 0; y < height; y++) {
1815 float *dataPtr = (float *)image.scanLine(y);
1816 const float *src = (const float *)(tile + y * width * sizeof(QRgbaFloat32));
1817 for (int x = 0; x < width * 4; x += 4) {
1818 dataPtr[x + 0] = qFromBigEndian(source: src[x + 0]);
1819 dataPtr[x + 1] = qFromBigEndian(source: src[x + 1]);
1820 dataPtr[x + 2] = qFromBigEndian(source: src[x + 2]);
1821 dataPtr[x + 3] = 1.f;
1822 }
1823 }
1824 break;
1825#endif
1826 case QImage::Format_Indexed8:
1827 for (int y = 0; y < height; y++) {
1828 uchar *dataPtr = bits + y * bytesPerLine;
1829 for (int x = 0; x < width; x++) {
1830 *dataPtr++ = tile[0];
1831 tile += sizeof(QRgb);
1832 }
1833 }
1834 break;
1835 default:
1836 qCWarning(XCFPLUGIN) << "Unhandled image format" << image.format() << "and/or layer type" << layer.type;
1837 return false;
1838 }
1839
1840 return true;
1841}
1842
1843/*!
1844 * The GIMP stores images in a "mipmap"-like hierarchy. As far as the QImage
1845 * is concerned, however, only the top level (i.e., the full resolution image)
1846 * is used.
1847 * \param xcf_io the data stream connected to the XCF image.
1848 * \param layer the layer to collect the image.
1849 * \return true if there were no I/O errors.
1850 */
1851bool XCFImageFormat::loadHierarchy(QDataStream &xcf_io, Layer &layer, const GimpPrecision precision)
1852{
1853 qint32 width;
1854 qint32 height;
1855 quint32 bpp;
1856
1857 xcf_io >> width >> height >> bpp;
1858 const qint64 offset = readOffsetPtr(stream&: xcf_io);
1859
1860 qCDebug(XCFPLUGIN) << "width" << width << "height" << height << "bpp" << bpp << "offset" << offset;
1861
1862 if (offset < 0) {
1863 qCDebug(XCFPLUGIN) << "XCF: negative hierarchy offset";
1864 return false;
1865 }
1866
1867 const bool isMask = layer.assignBytes == assignMaskBytes;
1868
1869 // make sure bpp is correct and complain if it is not
1870 switch (layer.type) {
1871 case RGB_GIMAGE:
1872 if (bpp != 3 * bytesPerChannel(precision)) {
1873 qCDebug(XCFPLUGIN) << "Found layer of type RGB but with bpp != 3" << bpp;
1874
1875 if (!isMask) {
1876 return false;
1877 }
1878 }
1879 break;
1880 case RGBA_GIMAGE:
1881 if (bpp != 4 * bytesPerChannel(precision)) {
1882 qCDebug(XCFPLUGIN) << "Found layer of type RGBA but with bpp != 4, got" << bpp << "bpp";
1883
1884 if (!isMask) {
1885 return false;
1886 }
1887 }
1888 break;
1889 case GRAY_GIMAGE:
1890 if (bpp != 1 * bytesPerChannel(precision)) {
1891 qCDebug(XCFPLUGIN) << "Found layer of type Gray but with bpp != 1" << bpp;
1892 return false;
1893 }
1894 break;
1895 case GRAYA_GIMAGE:
1896 if (bpp != 2 * bytesPerChannel(precision)) {
1897 qCDebug(XCFPLUGIN) << "Found layer of type Gray+Alpha but with bpp != 2" << bpp;
1898
1899 if (!isMask) {
1900 return false;
1901 }
1902 }
1903 break;
1904 case INDEXED_GIMAGE:
1905 if (bpp != 1 * bytesPerChannel(precision)) {
1906 qCDebug(XCFPLUGIN) << "Found layer of type Indexed but with bpp != 1" << bpp;
1907 return false;
1908 }
1909 break;
1910 case INDEXEDA_GIMAGE:
1911 if (bpp != 2 * bytesPerChannel(precision)) {
1912 qCDebug(XCFPLUGIN) << "Found layer of type Indexed+Alpha but with bpp != 2" << bpp;
1913
1914 if (!isMask) {
1915 return false;
1916 }
1917 }
1918 break;
1919 }
1920
1921 if (bpp > 4 * bytesPerChannel(precision)) {
1922 qCDebug(XCFPLUGIN) << "bpp is" << bpp << "We don't support layers with bpp > 4";
1923 return false;
1924 }
1925
1926 // GIMP stores images in a "mipmap"-like format (multiple levels of
1927 // increasingly lower resolution). Only the top level is used here,
1928 // however.
1929
1930 quint32 junk;
1931 do {
1932 xcf_io >> junk;
1933
1934 if (xcf_io.device()->atEnd()) {
1935 qCDebug(XCFPLUGIN) << "XCF: read failure on layer " << layer.name << " level offsets";
1936 return false;
1937 }
1938 } while (junk != 0);
1939
1940 qint64 saved_pos = xcf_io.device()->pos();
1941
1942 xcf_io.device()->seek(pos: offset);
1943 if (!loadLevel(xcf_io, layer, bpp, precision)) {
1944 return false;
1945 }
1946
1947 xcf_io.device()->seek(pos: saved_pos);
1948 return true;
1949}
1950
1951template<typename SourceFormat>
1952static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
1953{
1954 SourceFormat *source = (SourceFormat *)(input);
1955 for (quint64 offset = 0; offset < outputSize; offset++) {
1956 ((uint16_t *)output)[offset] = qToBigEndian(source: quint16(qBound(0., qFromBigEndian<SourceFormat>(source[offset]) * 65535. + 0.5, 65535.)));
1957 }
1958 return true;
1959}
1960
1961/*!
1962 * Load one level of the image hierarchy (but only the top level is ever used).
1963 * \param xcf_io the data stream connected to the XCF image.
1964 * \param layer the layer to collect the image.
1965 * \param bpp the number of bytes in a pixel.
1966 * \return true if there were no I/O errors.
1967 * \sa loadTileRLE().
1968 */
1969bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, const GimpPrecision precision)
1970{
1971 qint32 width;
1972 qint32 height;
1973
1974 xcf_io >> width >> height;
1975 qint64 offset = readOffsetPtr(stream&: xcf_io);
1976
1977 if (offset < 0) {
1978 qCDebug(XCFPLUGIN) << "XCF: negative level offset";
1979 return false;
1980 }
1981
1982 if (offset == 0) {
1983 // offset 0 with rowsxcols != 0 is probably an error since it means we have tiles
1984 // without data but just clear the bits for now instead of returning false
1985 for (uint j = 0; j < layer.nrows; j++) {
1986 for (uint i = 0; i < layer.ncols; i++) {
1987 layer.image_tiles[j][i].fill(color: Qt::transparent);
1988 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1989 layer.alpha_tiles[j][i].fill(color: Qt::transparent);
1990 }
1991 }
1992 }
1993 return true;
1994 }
1995
1996 bool needConvert = true;
1997 switch (precision) {
1998#ifdef USE_FLOAT_IMAGES
1999 case GIMP_PRECISION_HALF_LINEAR:
2000 case GIMP_PRECISION_HALF_NON_LINEAR:
2001 case GIMP_PRECISION_HALF_PERCEPTUAL:
2002 case GIMP_PRECISION_FLOAT_LINEAR:
2003 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2004 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2005#endif
2006 case GIMP_PRECISION_U8_LINEAR:
2007 case GIMP_PRECISION_U8_NON_LINEAR:
2008 case GIMP_PRECISION_U8_PERCEPTUAL:
2009 case GIMP_PRECISION_U16_LINEAR:
2010 case GIMP_PRECISION_U16_NON_LINEAR:
2011 case GIMP_PRECISION_U16_PERCEPTUAL:
2012 needConvert = false;
2013 break;
2014 default:
2015 break;
2016 }
2017
2018 const uint blockSize = TILE_WIDTH * TILE_HEIGHT * bpp * 1.5;
2019
2020 QList<uchar> buffer;
2021 if (needConvert) {
2022 buffer.resize(size: blockSize * (bpp == 2 ? 2 : 1));
2023 }
2024 for (uint j = 0; j < layer.nrows; j++) {
2025 for (uint i = 0; i < layer.ncols; i++) {
2026 if (offset == 0) {
2027 qCDebug(XCFPLUGIN) << "XCF: incorrect number of tiles in layer " << layer.name;
2028 return false;
2029 }
2030
2031 qint64 saved_pos = xcf_io.device()->pos();
2032 qint64 offset2 = readOffsetPtr(stream&: xcf_io);
2033
2034 if (offset2 < 0) {
2035 qCDebug(XCFPLUGIN) << "XCF: negative level offset";
2036 return false;
2037 }
2038
2039 // Evidently, RLE can occasionally expand a tile instead of compressing it!
2040 if (offset2 == 0) {
2041 offset2 = offset + blockSize;
2042 }
2043
2044 xcf_io.device()->seek(pos: offset);
2045 qint64 bytesParsed = 0;
2046
2047 switch (layer.compression) {
2048 case COMPRESS_NONE: {
2049 if (xcf_io.version() > 11 || size_t(bpp) > sizeof(QRgba64)) {
2050 qCDebug(XCFPLUGIN) << "Component reading not supported yet";
2051 return false;
2052 }
2053 const int data_size = bpp * TILE_WIDTH * TILE_HEIGHT;
2054 if (data_size > int(blockSize)) {
2055 qCDebug(XCFPLUGIN) << "Tile data too big, we can only fit" << sizeof(layer.tile) << "but need" << data_size;
2056 return false;
2057 }
2058 int dataRead = xcf_io.readRawData(reinterpret_cast<char *>(layer.tile), len: data_size);
2059 if (dataRead < data_size) {
2060 qCDebug(XCFPLUGIN) << "short read, expected" << data_size << "got" << dataRead;
2061 return false;
2062 }
2063 bytesParsed = dataRead;
2064 break;
2065 }
2066 case COMPRESS_RLE: {
2067 int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
2068 const uint data_size = size * bpp;
2069 if (needConvert) {
2070 if (data_size >= unsigned(buffer.size())) {
2071 qCDebug(XCFPLUGIN) << "Tile data too big, we can only fit" << buffer.size() << "but need" << data_size;
2072 return false;
2073 }
2074 } else {
2075 if (data_size > sizeof(layer.tile)) {
2076 qCDebug(XCFPLUGIN) << "Tile data too big, we can only fit" << sizeof(layer.tile) << "but need" << data_size;
2077 return false;
2078 }
2079 if (blockSize > sizeof(layer.tile)) {
2080 qCWarning(XCFPLUGIN) << "Too small tiles" << sizeof(layer.tile) << "this image requires" << blockSize << sizeof(QRgba64) << bpp;
2081 return false;
2082 }
2083 }
2084 if (!loadTileRLE(xcf_io, tile: needConvert ? buffer.data() : layer.tile, size, data_length: offset2 - offset, bpp, bytesParsed: &bytesParsed)) {
2085 qCDebug(XCFPLUGIN) << "Failed to read RLE";
2086 return false;
2087 }
2088 break;
2089 }
2090 default:
2091 qCDebug(XCFPLUGIN) << "Unhandled compression" << layer.compression;
2092 return false;
2093 }
2094
2095 if (needConvert) {
2096 if (bytesParsed > buffer.size()) {
2097 qCDebug(XCFPLUGIN) << "Invalid number of bytes parsed" << bytesParsed << buffer.size();
2098 return false;
2099 }
2100
2101 switch (precision) {
2102 case GIMP_PRECISION_U32_LINEAR:
2103 case GIMP_PRECISION_U32_NON_LINEAR:
2104 case GIMP_PRECISION_U32_PERCEPTUAL: {
2105 quint32 *source = (quint32 *)(buffer.data());
2106 for (quint64 offset = 0, len = buffer.size() / sizeof(quint32); offset < len; ++offset) {
2107 ((quint16 *)layer.tile)[offset] = qToBigEndian<quint16>(source: qFromBigEndian(source: source[offset]) / 65537);
2108 }
2109 break;
2110 }
2111#ifndef USE_FLOAT_IMAGES
2112 case GIMP_PRECISION_HALF_LINEAR:
2113 case GIMP_PRECISION_HALF_NON_LINEAR:
2114 case GIMP_PRECISION_HALF_PERCEPTUAL:
2115 convertFloatTo16Bit<qfloat16>(layer.tile, buffer.size() / sizeof(qfloat16), buffer.data());
2116 break;
2117 case GIMP_PRECISION_FLOAT_LINEAR:
2118 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2119 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2120 convertFloatTo16Bit<float>(layer.tile, buffer.size() / sizeof(float), buffer.data());
2121 break;
2122 case GIMP_PRECISION_DOUBLE_LINEAR:
2123 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2124 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2125 convertFloatTo16Bit<double>(layer.tile, buffer.size() / sizeof(double), buffer.data());
2126 break;
2127#else
2128 case GIMP_PRECISION_DOUBLE_LINEAR:
2129 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2130 case GIMP_PRECISION_DOUBLE_PERCEPTUAL: {
2131 double *source = (double *)(buffer.data());
2132 for (quint64 offset = 0, len = buffer.size() / sizeof(double); offset < len; ++offset) {
2133 ((float *)layer.tile)[offset] = qToBigEndian<float>(source: float(qFromBigEndian(source: source[offset])));
2134 }
2135 break;
2136 }
2137#endif
2138 default:
2139 qCWarning(XCFPLUGIN) << "Unsupported precision" << precision;
2140 return false;
2141 }
2142 }
2143
2144 // The bytes in the layer tile are juggled differently depending on
2145 // the target QImage. The caller has set layer.assignBytes to the
2146 // appropriate routine.
2147 if (!layer.assignBytes(layer, i, j, precision)) {
2148 return false;
2149 }
2150
2151 xcf_io.device()->seek(pos: saved_pos);
2152 offset = readOffsetPtr(stream&: xcf_io);
2153
2154 if (offset < 0) {
2155 qCDebug(XCFPLUGIN) << "XCF: negative level offset";
2156 return false;
2157 }
2158 }
2159 }
2160
2161 return true;
2162}
2163
2164/*!
2165 * A layer can have a one channel image which is used as a mask.
2166 * \param xcf_io the data stream connected to the XCF image.
2167 * \param layer the layer to collect the mask image.
2168 * \return true if there were no I/O errors.
2169 */
2170bool XCFImageFormat::loadMask(QDataStream &xcf_io, Layer &layer, const GimpPrecision precision)
2171{
2172 qint32 width;
2173 qint32 height;
2174 char *name;
2175
2176 xcf_io >> width >> height >> name;
2177
2178 delete[] name;
2179
2180 if (!loadChannelProperties(xcf_io, layer)) {
2181 return false;
2182 }
2183
2184 const qint64 hierarchy_offset = readOffsetPtr(stream&: xcf_io);
2185
2186 if (hierarchy_offset < 0) {
2187 qCDebug(XCFPLUGIN) << "XCF: negative mask hierarchy_offset";
2188 return false;
2189 }
2190
2191 xcf_io.device()->seek(pos: hierarchy_offset);
2192 layer.assignBytes = assignMaskBytes;
2193
2194 if (!loadHierarchy(xcf_io, layer, precision)) {
2195 return false;
2196 }
2197
2198 return true;
2199}
2200
2201/*!
2202 * This is the routine for which all the other code is simply
2203 * infrastructure. Read the image bytes out of the file and
2204 * store them in the tile buffer. This is passed a full 32-bit deep
2205 * buffer, even if bpp is smaller. The caller can figure out what to
2206 * do with the bytes.
2207 *
2208 * The tile is stored in "channels", i.e. the red component of all
2209 * pixels, then the green component of all pixels, then blue then
2210 * alpha, or, for indexed images, the color indices of all pixels then
2211 * the alpha of all pixels.
2212 *
2213 * The data is compressed with "run length encoding". Some simple data
2214 * integrity checks are made.
2215 *
2216 * \param xcf_io the data stream connected to the XCF image.
2217 * \param tile the buffer to expand the RLE into.
2218 * \param image_size number of bytes expected to be in the image tile.
2219 * \param data_length number of bytes expected in the RLE.
2220 * \param bpp number of bytes per pixel.
2221 * \return true if there were no I/O errors and no obvious corruption of
2222 * the RLE data.
2223 */
2224bool XCFImageFormat::loadTileRLE(QDataStream &xcf_io, uchar *tile, int image_size, int data_length, qint32 bpp, qint64 *bytesParsed)
2225{
2226 uchar *data = tile;
2227
2228 uchar *xcfdata;
2229 uchar *xcfodata;
2230 uchar *xcfdatalimit;
2231
2232 int step = sizeof(QRgb);
2233 switch (bpp) {
2234 case 1:
2235 case 2:
2236 case 3:
2237 case 4:
2238 step = sizeof(QRgb);
2239 break;
2240 case 6:
2241 case 8:
2242 step = sizeof(QRgb) * 2;
2243 break;
2244 case 12:
2245 case 16:
2246 step = sizeof(QRgb) * 4;
2247 break;
2248 default:
2249 qCDebug(XCFPLUGIN) << "XCF: unhandled bit depth" << bpp;
2250 return false;
2251 }
2252
2253 if (data_length < 0 || data_length > int(TILE_WIDTH * TILE_HEIGHT * step * 1.5)) {
2254 qCDebug(XCFPLUGIN) << "XCF: invalid tile data length" << data_length;
2255 return false;
2256 }
2257
2258 xcfdata = xcfodata = new uchar[data_length];
2259
2260 const int dataRead = xcf_io.readRawData((char *)xcfdata, len: data_length);
2261 if (dataRead <= 0) {
2262 delete[] xcfodata;
2263 qCDebug(XCFPLUGIN) << "XCF: read failure on tile" << dataRead;
2264 return false;
2265 }
2266
2267 if (dataRead < data_length) {
2268 memset(s: &xcfdata[dataRead], c: 0, n: data_length - dataRead);
2269 }
2270
2271 if (!xcf_io.device()->isOpen()) {
2272 delete[] xcfodata;
2273 qCDebug(XCFPLUGIN) << "XCF: read failure on tile";
2274 return false;
2275 }
2276
2277 xcfdatalimit = &xcfodata[data_length - 1];
2278
2279 for (int i = 0; i < bpp; ++i) {
2280 data = tile + i;
2281
2282 int size = image_size;
2283
2284 while (size > 0) {
2285 if (xcfdata > xcfdatalimit) {
2286 goto bogus_rle;
2287 }
2288
2289 uchar val = *xcfdata++;
2290 uint length = val;
2291
2292 if (length >= 128) {
2293 length = 255 - (length - 1);
2294 if (length == 128) {
2295 if (xcfdata >= xcfdatalimit) {
2296 goto bogus_rle;
2297 }
2298
2299 length = (*xcfdata << 8) + xcfdata[1];
2300
2301 xcfdata += 2;
2302 }
2303
2304 size -= length;
2305
2306 if (size < 0) {
2307 goto bogus_rle;
2308 }
2309
2310 if (&xcfdata[length - 1] > xcfdatalimit) {
2311 goto bogus_rle;
2312 }
2313
2314 while (length-- > 0) {
2315 *data = *xcfdata++;
2316 data += step;
2317 }
2318 } else {
2319 length += 1;
2320 if (length == 128) {
2321 if (xcfdata >= xcfdatalimit) {
2322 goto bogus_rle;
2323 }
2324
2325 length = (*xcfdata << 8) + xcfdata[1];
2326 xcfdata += 2;
2327 }
2328
2329 size -= length;
2330
2331 if (size < 0) {
2332 goto bogus_rle;
2333 }
2334
2335 if (xcfdata > xcfdatalimit) {
2336 goto bogus_rle;
2337 }
2338
2339 qintptr totalLength = qintptr(data - tile) + length * step;
2340 if (totalLength >= image_size * step * 1.5) {
2341 qCDebug(XCFPLUGIN) << "Ran out of space when trying to unpack image, over:" << totalLength - image_size << totalLength << image_size
2342 << length;
2343 goto bogus_rle;
2344 }
2345
2346 val = *xcfdata++;
2347
2348 while (length-- > 0) {
2349 *data = val;
2350 data += step;
2351 }
2352 }
2353 }
2354 }
2355 *bytesParsed = qintptr(data - tile);
2356
2357 delete[] xcfodata;
2358 return true;
2359
2360bogus_rle:
2361
2362 qCDebug(XCFPLUGIN) << "The run length encoding could not be decoded properly";
2363 delete[] xcfodata;
2364 return false;
2365}
2366
2367/*!
2368 * An XCF file can contain an arbitrary number of properties associated
2369 * with a channel. Note that this routine only reads mask channel properties.
2370 * \param xcf_io the data stream connected to the XCF image.
2371 * \param layer layer containing the mask channel to collect the properties.
2372 * \return true if there were no I/O errors.
2373 */
2374bool XCFImageFormat::loadChannelProperties(QDataStream &xcf_io, Layer &layer)
2375{
2376 while (true) {
2377 PropType type;
2378 QByteArray bytes;
2379 quint32 rawType;
2380
2381 if (!loadProperty(xcf_io, type, bytes, rawType)) {
2382 qCDebug(XCFPLUGIN) << "XCF: error loading channel properties";
2383 return false;
2384 }
2385
2386 QDataStream property(bytes);
2387
2388 switch (type) {
2389 case PROP_END:
2390 return true;
2391
2392 case PROP_OPACITY:
2393 property >> layer.mask_channel.opacity;
2394 layer.mask_channel.opacity = std::min(a: layer.mask_channel.opacity, b: 255u);
2395 break;
2396
2397 case PROP_FLOAT_OPACITY:
2398 // For some reason QDataStream isn't able to read the float (tried
2399 // setting the endianness manually)
2400 if (bytes.size() == 4) {
2401 layer.mask_channel.opacityFloat = qFromBigEndian(source: *reinterpret_cast<float *>(bytes.data()));
2402 } else {
2403 qCDebug(XCFPLUGIN) << "XCF: Invalid data size for float:" << bytes.size();
2404 }
2405 break;
2406
2407 case PROP_VISIBLE:
2408 property >> layer.mask_channel.visible;
2409 break;
2410
2411 case PROP_SHOW_MASKED:
2412 property >> layer.mask_channel.show_masked;
2413 break;
2414
2415 case PROP_COLOR:
2416 property >> layer.mask_channel.red >> layer.mask_channel.green >> layer.mask_channel.blue;
2417 break;
2418
2419 case PROP_FLOAT_COLOR:
2420 property >> layer.mask_channel.redF >> layer.mask_channel.greenF >> layer.mask_channel.blueF;
2421 break;
2422
2423 case PROP_TATTOO:
2424 property >> layer.mask_channel.tattoo;
2425 break;
2426
2427 // Only used in edit mode
2428 case PROP_LINKED:
2429 break;
2430
2431 // Just for organization in the UI, doesn't influence rendering
2432 case PROP_COLOR_TAG:
2433 break;
2434
2435 // We don't support editing, so for now just ignore locking
2436 case PROP_LOCK_CONTENT:
2437 case PROP_LOCK_POSITION:
2438 break;
2439
2440 default:
2441 qCDebug(XCFPLUGIN) << "XCF: unimplemented channel property " << type << "(" << rawType << ")"
2442 << ", size " << bytes.size();
2443 break;
2444 }
2445 }
2446}
2447
2448/*!
2449 * Copy the bytes from the tile buffer into the mask tile QImage.
2450 * \param layer layer containing the tile buffer and the mask tile matrix.
2451 * \param i column index of current tile.
2452 * \param j row index of current tile.
2453 */
2454bool XCFImageFormat::assignMaskBytes(Layer &layer, uint i, uint j, const GimpPrecision &precision)
2455{
2456 QImage &image = layer.mask_tiles[j][i];
2457 if (image.depth() != 8) {
2458 qCWarning(XCFPLUGIN) << "invalid bytes per pixel, we only do 8 bit masks" << image.depth();
2459 return false;
2460 }
2461
2462 uchar *tile = layer.tile;
2463 const int width = image.width();
2464 const int height = image.height();
2465 const int bytesPerLine = image.bytesPerLine();
2466 uchar *bits = image.bits();
2467 auto bpc = bytesPerChannel(precision);
2468
2469 // mask management is a house of cards: the mask is always treated as 8 bit by the plugin
2470 // (I don't want to twist the code) so it needs a conversion here.
2471 // If previously converted the step is the type size, otherwise is the one set in loadTileRLE().
2472 for (int y = 0; y < height; y++) {
2473 uchar *dataPtr = bits + y * bytesPerLine;
2474#ifdef USE_FLOAT_IMAGES
2475 if (bpc == 4) {
2476 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2477 for (int x = 0; x < width; x++) {
2478 *dataPtr++ = qFromBigEndian<quint16>(source: *(const quint16 *)tile) / 257;
2479 tile += sizeof(quint16); // was converted to 16 bits in loadLevel()
2480 }
2481 } else {
2482 for (int x = 0; x < width; x++) {
2483 *dataPtr++ = qFromBigEndian<float>(source: *(const float *)tile) * 255;
2484 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2485 }
2486 }
2487 } else if (bpc == 2) {
2488 // when not converted, the step of a
2489 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2490 for (int x = 0; x < width; x++) {
2491 *dataPtr++ = qFromBigEndian<quint16>(source: *(const quint16 *)tile) / 257;
2492 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2493 }
2494 } else {
2495 for (int x = 0; x < width; x++) {
2496 *dataPtr++ = qFromBigEndian<qfloat16>(source: *(const qfloat16 *)tile) * 255;
2497 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2498 }
2499 }
2500 }
2501#else
2502 if (bpc == 2) {
2503 for (int x = 0; x < width; x++) {
2504 *dataPtr++ = qFromBigEndian<quint16>(*(const quint16 *)tile) / 257;
2505 tile += sizeof(QRgb); // yeah! see loadTileRLE() / loadLevel()
2506 }
2507 } else if (bpc == 4) {
2508 for (int x = 0; x < width; x++) {
2509 *dataPtr++ = qFromBigEndian<quint16>(*(const quint16 *)tile) / 257;
2510 tile += sizeof(quint16); // was converted to 16 bits in loadLevel()
2511 }
2512 }
2513#endif
2514 else {
2515 for (int x = 0; x < width; x++) {
2516 *dataPtr++ = tile[0];
2517 tile += sizeof(QRgb); // yeah! see loadTileRLE()
2518 }
2519 }
2520 }
2521
2522 return true;
2523}
2524
2525/*!
2526 * Construct the QImage which will eventually be returned to the QImage
2527 * loader.
2528 *
2529 * There are a couple of situations which require that the QImage is not
2530 * exactly the same as The GIMP's representation. The full table is:
2531 * \verbatim
2532 * Grayscale opaque : 8 bpp indexed
2533 * Grayscale translucent : 32 bpp + alpha
2534 * Indexed opaque : 1 bpp if num_colors <= 2
2535 * : 8 bpp indexed otherwise
2536 * Indexed translucent : 8 bpp indexed + alpha if num_colors < 256
2537 * : 32 bpp + alpha otherwise
2538 * RGB opaque : 32 bpp
2539 * RGBA translucent : 32 bpp + alpha
2540 * \endverbatim
2541 * Whether the image is translucent or not is determined by the bottom layer's
2542 * alpha channel. However, even if the bottom layer lacks an alpha channel,
2543 * it can still have an opacity < 1. In this case, the QImage is promoted
2544 * to 32-bit. (Note this is different from the output from the GIMP image
2545 * exporter, which seems to ignore this attribute.)
2546 *
2547 * Independently, higher layers can be translucent, but the background of
2548 * the image will not show through if the bottom layer is opaque.
2549 *
2550 * For indexed images, translucency is an all or nothing effect.
2551 * \param xcf_image contains image info and bottom-most layer.
2552 */
2553bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
2554{
2555 // (Aliases to make the code look a little better.)
2556 Layer &layer(xcf_image.layer);
2557 QImage &image(xcf_image.image);
2558
2559 switch (layer.type) {
2560 case GRAY_GIMAGE:
2561 if (layer.opacity == OPAQUE_OPACITY) {
2562 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_Indexed8);
2563 image.setColorCount(256);
2564 if (image.isNull()) {
2565 return false;
2566 }
2567 setGrayPalette(image);
2568 image.fill(pixel: 255);
2569 break;
2570 } // else, fall through to 32-bit representation
2571 Q_FALLTHROUGH();
2572 case GRAYA_GIMAGE:
2573 case RGB_GIMAGE:
2574 case RGBA_GIMAGE:
2575 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: xcf_image.qimageFormat());
2576 if (image.isNull()) {
2577 return false;
2578 }
2579 if (image.hasAlphaChannel()) {
2580 image.fill(color: Qt::transparent);
2581 } else {
2582 image.fill(color: Qt::white);
2583 }
2584 break;
2585
2586 case INDEXED_GIMAGE:
2587 // As noted in the table above, there are quite a few combinations
2588 // which are possible with indexed images, depending on the
2589 // presence of transparency (note: not translucency, which is not
2590 // supported by The GIMP for indexed images) and the number of
2591 // individual colors.
2592
2593 // Note: Qt treats a bitmap with a Black and White color palette
2594 // as a mask, so only the "on" bits are drawn, regardless of the
2595 // order color table entries. Otherwise (i.e., at least one of the
2596 // color table entries is not black or white), it obeys the one-
2597 // or two-color palette. Have to ask about this...
2598
2599 if (xcf_image.num_colors <= 2) {
2600 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_MonoLSB);
2601 image.setColorCount(xcf_image.num_colors);
2602 if (image.isNull()) {
2603 return false;
2604 }
2605 image.fill(pixel: 0);
2606 setPalette(xcf_image, image);
2607 } else if (xcf_image.num_colors <= 256) {
2608 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_Indexed8);
2609 image.setColorCount(xcf_image.num_colors);
2610 if (image.isNull()) {
2611 return false;
2612 }
2613 image.fill(pixel: 0);
2614 setPalette(xcf_image, image);
2615 }
2616 break;
2617
2618 case INDEXEDA_GIMAGE:
2619 if (xcf_image.num_colors == 1) {
2620 // Plenty(!) of room to add a transparent color
2621 xcf_image.num_colors++;
2622 xcf_image.palette.resize(size: xcf_image.num_colors);
2623 xcf_image.palette[1] = xcf_image.palette[0];
2624 xcf_image.palette[0] = qRgba(r: 255, g: 255, b: 255, a: 0);
2625
2626 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_MonoLSB);
2627 image.setColorCount(xcf_image.num_colors);
2628 if (image.isNull()) {
2629 return false;
2630 }
2631 image.fill(pixel: 0);
2632 setPalette(xcf_image, image);
2633 } else if (xcf_image.num_colors < 256) {
2634 // Plenty of room to add a transparent color
2635 xcf_image.num_colors++;
2636 xcf_image.palette.resize(size: xcf_image.num_colors);
2637 for (int c = xcf_image.num_colors - 1; c >= 1; c--) {
2638 xcf_image.palette[c] = xcf_image.palette[c - 1];
2639 }
2640
2641 xcf_image.palette[0] = qRgba(r: 255, g: 255, b: 255, a: 0);
2642 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_Indexed8);
2643 image.setColorCount(xcf_image.num_colors);
2644 if (image.isNull()) {
2645 return false;
2646 }
2647 image.fill(pixel: 0);
2648 setPalette(xcf_image, image);
2649 } else {
2650 // No room for a transparent color, so this has to be promoted to
2651 // true color. (There is no equivalent PNG representation output
2652 // from The GIMP as of v1.2.)
2653 image = imageAlloc(width: xcf_image.header.width, height: xcf_image.header.height, format: QImage::Format_ARGB32);
2654 if (image.isNull()) {
2655 return false;
2656 }
2657 image.fill(pixel: qRgba(r: 255, g: 255, b: 255, a: 0));
2658 }
2659 break;
2660 }
2661 if (image.format() != xcf_image.qimageFormat()) {
2662 qCWarning(XCFPLUGIN) << "Selected wrong format:" << image.format() << "expected" << layer.qimageFormat(precision: xcf_image.header.precision);
2663 return false;
2664 }
2665
2666 // The final profile should be the one in the Parasite
2667 // NOTE: if not set here, the colorSpace is aet in setImageParasites() (if no one defined in the parasites)
2668#ifndef DISABLE_IMAGE_PROFILE
2669 switch (xcf_image.header.precision) {
2670 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
2671 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
2672 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
2673 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
2674 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
2675 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
2676 image.setColorSpace(QColorSpace::SRgbLinear);
2677 break;
2678 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
2679 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
2680 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
2681 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
2682 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
2683 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
2684 image.setColorSpace(QColorSpace::SRgb);
2685 break;
2686 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
2687 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
2688 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2689 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
2690 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
2691 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
2692 image.setColorSpace(QColorSpace::SRgb);
2693 break;
2694 }
2695#endif
2696
2697 if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
2698 const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
2699 if (dpmx > float(std::numeric_limits<int>::max())) {
2700 return false;
2701 }
2702 const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
2703 if (dpmy > float(std::numeric_limits<int>::max())) {
2704 return false;
2705 }
2706 image.setDotsPerMeterX((int)dpmx);
2707 image.setDotsPerMeterY((int)dpmy);
2708 }
2709 return true;
2710}
2711
2712/*!
2713 * Copy a layer into an image, taking account of the manifold modes. The
2714 * contents of the image are replaced.
2715 * \param xcf_image contains the layer and image to be replaced.
2716 */
2717void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
2718{
2719 Layer &layer(xcf_image.layer);
2720 QImage &image(xcf_image.image);
2721 PixelCopyOperation copy = nullptr;
2722
2723 switch (layer.type) {
2724 case RGB_GIMAGE:
2725 case RGBA_GIMAGE:
2726 copy = copyRGBToRGB;
2727 break;
2728 case GRAY_GIMAGE:
2729 if (layer.opacity == OPAQUE_OPACITY) {
2730 copy = copyGrayToGray;
2731 } else {
2732 copy = copyGrayToRGB;
2733 }
2734 break;
2735 case GRAYA_GIMAGE:
2736 copy = copyGrayAToRGB;
2737 break;
2738 case INDEXED_GIMAGE:
2739 copy = copyIndexedToIndexed;
2740 break;
2741 case INDEXEDA_GIMAGE:
2742 if (xcf_image.image.depth() <= 8) {
2743 copy = copyIndexedAToIndexed;
2744 } else {
2745 copy = copyIndexedAToRGB;
2746 }
2747 }
2748
2749 if (!copy) {
2750 return;
2751 }
2752
2753 // For each tile...
2754
2755 for (uint j = 0; j < layer.nrows; j++) {
2756 uint y = j * TILE_HEIGHT;
2757
2758 for (uint i = 0; i < layer.ncols; i++) {
2759 uint x = i * TILE_WIDTH;
2760
2761 // This seems the best place to apply the dissolve because it
2762 // depends on the global position of each tile's
2763 // pixels. Apparently it's the only mode which can apply to a
2764 // single layer.
2765
2766 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
2767 if (!random_table_initialized) {
2768 initializeRandomTable();
2769 random_table_initialized = true;
2770 }
2771 if (layer.type == RGBA_GIMAGE) {
2772 dissolveRGBPixels(image&: layer.image_tiles[j][i], x, y);
2773 }
2774
2775 else if (layer.type == GRAYA_GIMAGE) {
2776 dissolveAlphaPixels(image&: layer.alpha_tiles[j][i], x, y);
2777 }
2778 }
2779
2780 // Shortcut for common case
2781 if (copy == copyRGBToRGB && layer.apply_mask != 1) {
2782 QPainter painter(&image);
2783 painter.setOpacity(layer.opacity / 255.0);
2784 painter.setCompositionMode(QPainter::CompositionMode_Source);
2785 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
2786 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
2787 painter.drawImage(x: x + layer.x_offset, y: y + layer.y_offset, image: layer.image_tiles[j][i]);
2788 }
2789 continue;
2790 }
2791
2792 for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
2793 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
2794 int m = x + k + layer.x_offset;
2795 int n = y + l + layer.y_offset;
2796
2797 if (m < 0 || m >= image.width() || n < 0 || n >= image.height()) {
2798 continue;
2799 }
2800
2801 (*copy)(layer, i, j, k, l, image, m, n);
2802 }
2803 }
2804 }
2805 }
2806}
2807
2808/*!
2809 * Copy an RGB pixel from the layer to the RGB image. Straight-forward.
2810 * The only thing this has to take account of is the opacity of the
2811 * layer. Evidently, the GIMP exporter itself does not actually do this.
2812 * \param layer source layer.
2813 * \param i x tile index.
2814 * \param j y tile index.
2815 * \param k x pixel index of tile i,j.
2816 * \param l y pixel index of tile i,j.
2817 * \param image destination image.
2818 * \param m x pixel of destination image.
2819 * \param n y pixel of destination image.
2820 */
2821void XCFImageFormat::copyRGBToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2822{
2823 if (image.depth() == 32) {
2824 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
2825 uchar src_a = layer.opacity;
2826
2827 if (layer.type == RGBA_GIMAGE) {
2828 src_a = INT_MULT(a: src_a, b: qAlpha(rgb: src));
2829 }
2830
2831 // Apply the mask (if any)
2832
2833 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2834 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2835 }
2836
2837 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
2838 } else if (image.depth() == 64) {
2839 QRgba64 src = layer.image_tiles[j][i].pixelColor(x: k, y: l).rgba64();
2840 quint16 src_a = layer.opacity;
2841
2842 if (layer.type == RGBA_GIMAGE) {
2843 src_a = INT_MULT(a: src_a, b: qAlpha(rgb: src));
2844 }
2845
2846 // Apply the mask (if any)
2847
2848 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2849 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2850 }
2851 src.setAlpha(src_a);
2852
2853 image.setPixel(x: m, y: n, index_or_rgb: src);
2854 }
2855}
2856
2857/*!
2858 * Copy a Gray pixel from the layer to the Gray image. Straight-forward.
2859 * \param layer source layer.
2860 * \param i x tile index.
2861 * \param j y tile index.
2862 * \param k x pixel index of tile i,j.
2863 * \param l y pixel index of tile i,j.
2864 * \param image destination image.
2865 * \param m x pixel of destination image.
2866 * \param n y pixel of destination image.
2867 */
2868void XCFImageFormat::copyGrayToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2869{
2870 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
2871 image.setPixel(x: m, y: n, index_or_rgb: src);
2872}
2873
2874/*!
2875 * Copy a Gray pixel from the layer to an RGB image. Straight-forward.
2876 * The only thing this has to take account of is the opacity of the
2877 * layer. Evidently, the GIMP exporter itself does not actually do this.
2878 * \param layer source layer.
2879 * \param i x tile index.
2880 * \param j y tile index.
2881 * \param k x pixel index of tile i,j.
2882 * \param l y pixel index of tile i,j.
2883 * \param image destination image.
2884 * \param m x pixel of destination image.
2885 * \param n y pixel of destination image.
2886 */
2887void XCFImageFormat::copyGrayToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2888{
2889 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
2890 uchar src_a = layer.opacity;
2891 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
2892}
2893
2894/*!
2895 * Copy a GrayA pixel from the layer to an RGB image. Straight-forward.
2896 * The only thing this has to take account of is the opacity of the
2897 * layer. Evidently, the GIMP exporter itself does not actually do this.
2898 * \param layer source layer.
2899 * \param i x tile index.
2900 * \param j y tile index.
2901 * \param k x pixel index of tile i,j.
2902 * \param l y pixel index of tile i,j.
2903 * \param image destination image.
2904 * \param m x pixel of destination image.
2905 * \param n y pixel of destination image.
2906 */
2907void XCFImageFormat::copyGrayAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2908{
2909 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
2910 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
2911 src_a = INT_MULT(a: src_a, b: layer.opacity);
2912
2913 // Apply the mask (if any)
2914
2915 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2916 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2917 }
2918
2919 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
2920}
2921
2922/*!
2923 * Copy an Indexed pixel from the layer to the Indexed image. Straight-forward.
2924 * \param layer source layer.
2925 * \param i x tile index.
2926 * \param j y tile index.
2927 * \param k x pixel index of tile i,j.
2928 * \param l y pixel index of tile i,j.
2929 * \param image destination image.
2930 * \param m x pixel of destination image.
2931 * \param n y pixel of destination image.
2932 */
2933void XCFImageFormat::copyIndexedToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2934{
2935 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
2936 image.setPixel(x: m, y: n, index_or_rgb: src);
2937}
2938
2939/*!
2940 * Copy an IndexedA pixel from the layer to the Indexed image. Straight-forward.
2941 * \param layer source layer.
2942 * \param i x tile index.
2943 * \param j y tile index.
2944 * \param k x pixel index of tile i,j.
2945 * \param l y pixel index of tile i,j.
2946 * \param image destination image.
2947 * \param m x pixel of destination image.
2948 * \param n y pixel of destination image.
2949 */
2950void XCFImageFormat::copyIndexedAToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2951{
2952 uchar src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
2953 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
2954 src_a = INT_MULT(a: src_a, b: layer.opacity);
2955
2956 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2957 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2958 }
2959
2960 if (src_a > 127) {
2961 src++;
2962 } else {
2963 src = 0;
2964 }
2965
2966 image.setPixel(x: m, y: n, index_or_rgb: src);
2967}
2968
2969/*!
2970 * Copy an IndexedA pixel from the layer to an RGB image. Straight-forward.
2971 * The only thing this has to take account of is the opacity of the
2972 * layer. Evidently, the GIMP exporter itself does not actually do this.
2973 * \param layer source layer.
2974 * \param i x tile index.
2975 * \param j y tile index.
2976 * \param k x pixel index of tile i,j.
2977 * \param l y pixel index of tile i,j.
2978 * \param image destination image.
2979 * \param m x pixel of destination image.
2980 * \param n y pixel of destination image.
2981 */
2982void XCFImageFormat::copyIndexedAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
2983{
2984 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
2985 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
2986 src_a = INT_MULT(a: src_a, b: layer.opacity);
2987
2988 // Apply the mask (if any)
2989 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
2990 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
2991 }
2992
2993 // This is what appears in the GIMP window
2994 if (src_a <= 127) {
2995 src_a = 0;
2996 } else {
2997 src_a = OPAQUE_OPACITY;
2998 }
2999
3000 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
3001}
3002
3003/*!
3004 * Merge a layer into an image, taking account of the manifold modes.
3005 * \param xcf_image contains the layer and image to merge.
3006 */
3007void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
3008{
3009 Layer &layer(xcf_image.layer);
3010 QImage &image(xcf_image.image);
3011
3012 PixelMergeOperation merge = nullptr;
3013
3014 if (!layer.opacity) {
3015 return; // don't bother doing anything
3016 }
3017
3018 if (layer.blendSpace == XCFImageFormat::AutoColorSpace) {
3019 qCDebug(XCFPLUGIN) << "Auto blend space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3020 layer.blendSpace = XCFImageFormat::RgbLinearSpace;
3021 }
3022
3023 if (layer.blendSpace != XCFImageFormat::RgbLinearSpace) {
3024 qCDebug(XCFPLUGIN) << "Unimplemented blend color space" << layer.blendSpace;
3025 }
3026 qCDebug(XCFPLUGIN) << "Blend color space" << layer.blendSpace;
3027
3028 if (layer.compositeSpace == XCFImageFormat::AutoColorSpace) {
3029 qCDebug(XCFPLUGIN) << "Auto composite space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3030 layer.compositeSpace = XCFImageFormat::RgbLinearSpace;
3031 }
3032
3033 if (layer.compositeSpace != XCFImageFormat::RgbLinearSpace) {
3034 qCDebug(XCFPLUGIN) << "Unimplemented composite color space" << layer.compositeSpace;
3035 }
3036 if (layer.compositeMode != XCFImageFormat::CompositeUnion) {
3037 qCDebug(XCFPLUGIN) << "Unhandled composite mode" << layer.compositeMode;
3038 }
3039
3040 switch (layer.type) {
3041 case RGB_GIMAGE:
3042 case RGBA_GIMAGE:
3043 merge = mergeRGBToRGB;
3044 break;
3045 case GRAY_GIMAGE:
3046 if (layer.opacity == OPAQUE_OPACITY) {
3047 merge = mergeGrayToGray;
3048 } else {
3049 merge = mergeGrayToRGB;
3050 }
3051 break;
3052 case GRAYA_GIMAGE:
3053 if (xcf_image.image.depth() <= 8) {
3054 merge = mergeGrayAToGray;
3055 } else {
3056 merge = mergeGrayAToRGB;
3057 }
3058 break;
3059 case INDEXED_GIMAGE:
3060 merge = mergeIndexedToIndexed;
3061 break;
3062 case INDEXEDA_GIMAGE:
3063 if (xcf_image.image.depth() <= 8) {
3064 merge = mergeIndexedAToIndexed;
3065 } else {
3066 merge = mergeIndexedAToRGB;
3067 }
3068 }
3069
3070 if (!merge) {
3071 return;
3072 }
3073
3074 if (merge == mergeRGBToRGB && layer.apply_mask != 1) {
3075 int painterMode = -1;
3076 switch (layer.mode) {
3077 case GIMP_LAYER_MODE_NORMAL:
3078 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3079 painterMode = QPainter::CompositionMode_SourceOver;
3080 break;
3081 case GIMP_LAYER_MODE_MULTIPLY:
3082 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3083 painterMode = QPainter::CompositionMode_Multiply;
3084 break;
3085 case GIMP_LAYER_MODE_SCREEN:
3086 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3087 painterMode = QPainter::CompositionMode_Screen;
3088 break;
3089 case GIMP_LAYER_MODE_OVERLAY:
3090 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3091 painterMode = QPainter::CompositionMode_Overlay;
3092 break;
3093 case GIMP_LAYER_MODE_DIFFERENCE:
3094 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3095 painterMode = QPainter::CompositionMode_Difference;
3096 break;
3097 case GIMP_LAYER_MODE_DARKEN_ONLY:
3098 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3099 painterMode = QPainter::CompositionMode_Darken;
3100 break;
3101 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3102 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3103 painterMode = QPainter::CompositionMode_Lighten;
3104 break;
3105 case GIMP_LAYER_MODE_DODGE:
3106 case GIMP_LAYER_MODE_DODGE_LEGACY:
3107 painterMode = QPainter::CompositionMode_ColorDodge;
3108 break;
3109 case GIMP_LAYER_MODE_BURN:
3110 case GIMP_LAYER_MODE_BURN_LEGACY:
3111 painterMode = QPainter::CompositionMode_ColorBurn;
3112 break;
3113 case GIMP_LAYER_MODE_HARDLIGHT:
3114 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
3115 painterMode = QPainter::CompositionMode_HardLight;
3116 break;
3117 case GIMP_LAYER_MODE_SOFTLIGHT:
3118 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
3119 painterMode = QPainter::CompositionMode_SoftLight;
3120 break;
3121 case GIMP_LAYER_MODE_ADDITION:
3122 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3123 painterMode = QPainter::CompositionMode_Plus;
3124 break;
3125 case GIMP_LAYER_MODE_EXCLUSION:
3126 painterMode = QPainter::CompositionMode_Exclusion;
3127 break;
3128
3129 // Not bothered to find what the QPainter equivalent is, or there is none
3130 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3131 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
3132 case GIMP_LAYER_MODE_GRAIN_MERGE:
3133 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
3134 case GIMP_LAYER_MODE_COLOR_ERASE:
3135 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
3136 case GIMP_LAYER_MODE_LCH_HUE:
3137 case GIMP_LAYER_MODE_LCH_CHROMA:
3138 case GIMP_LAYER_MODE_LCH_COLOR:
3139 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
3140 case GIMP_LAYER_MODE_BEHIND:
3141 case GIMP_LAYER_MODE_BEHIND_LEGACY:
3142 case GIMP_LAYER_MODE_SUBTRACT:
3143 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3144 case GIMP_LAYER_MODE_HSV_HUE:
3145 case GIMP_LAYER_MODE_HSV_SATURATION:
3146 case GIMP_LAYER_MODE_HSL_COLOR:
3147 case GIMP_LAYER_MODE_HSV_VALUE:
3148 case GIMP_LAYER_MODE_DIVIDE:
3149 case GIMP_LAYER_MODE_VIVID_LIGHT:
3150 case GIMP_LAYER_MODE_PIN_LIGHT:
3151 case GIMP_LAYER_MODE_LINEAR_LIGHT:
3152 case GIMP_LAYER_MODE_HARD_MIX:
3153 case GIMP_LAYER_MODE_LINEAR_BURN:
3154 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
3155 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
3156 case GIMP_LAYER_MODE_LUMINANCE:
3157 case GIMP_LAYER_MODE_ERASE:
3158 case GIMP_LAYER_MODE_MERGE:
3159 case GIMP_LAYER_MODE_SPLIT:
3160 case GIMP_LAYER_MODE_PASS_THROUGH:
3161 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
3162 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
3163 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
3164 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
3165 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3166 qCDebug(XCFPLUGIN) << "No QPainter equivalent to" << layer.mode;
3167 break;
3168
3169 // Special
3170 case GIMP_LAYER_MODE_DISSOLVE:
3171 case GIMP_LAYER_MODE_COUNT:
3172 break;
3173 }
3174
3175 if (painterMode != -1) {
3176 QPainter painter(&image);
3177 painter.setOpacity(layer.opacity / 255.0);
3178 painter.setCompositionMode(QPainter::CompositionMode(painterMode));
3179 qCDebug(XCFPLUGIN) << "Using QPainter for mode" << layer.mode;
3180
3181 for (uint j = 0; j < layer.nrows; j++) {
3182 uint y = j * TILE_HEIGHT;
3183
3184 for (uint i = 0; i < layer.ncols; i++) {
3185 uint x = i * TILE_WIDTH;
3186
3187 QImage &tile = layer.image_tiles[j][i];
3188 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3189 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3190 painter.drawImage(x: x + layer.x_offset, y: y + layer.y_offset, image: tile);
3191 }
3192 }
3193 }
3194
3195 return;
3196 }
3197 }
3198
3199#ifndef DISABLE_IMAGE_PROFILE_CONV // The final profile should be the one in the Parasite
3200 if (layer.compositeSpace == XCFImageFormat::RgbPerceptualSpace && image.colorSpace() != QColorSpace::SRgb) {
3201 qCDebug(XCFPLUGIN) << "Converting to composite color space" << layer.compositeSpace;
3202 image.convertToColorSpace(QColorSpace::SRgb);
3203 }
3204 if (layer.compositeSpace == XCFImageFormat::RgbLinearSpace && image.colorSpace() != QColorSpace::SRgbLinear) {
3205 qCDebug(XCFPLUGIN) << "Converting to composite color space" << layer.compositeSpace;
3206 image.convertToColorSpace(QColorSpace::SRgbLinear);
3207 }
3208#endif
3209
3210 for (uint j = 0; j < layer.nrows; j++) {
3211 uint y = j * TILE_HEIGHT;
3212
3213 for (uint i = 0; i < layer.ncols; i++) {
3214 uint x = i * TILE_WIDTH;
3215
3216 // This seems the best place to apply the dissolve because it
3217 // depends on the global position of each tile's
3218 // pixels. Apparently it's the only mode which can apply to a
3219 // single layer.
3220
3221 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
3222 if (!random_table_initialized) {
3223 initializeRandomTable();
3224 random_table_initialized = true;
3225 }
3226 if (layer.type == RGBA_GIMAGE) {
3227 dissolveRGBPixels(image&: layer.image_tiles[j][i], x, y);
3228 }
3229
3230 else if (layer.type == GRAYA_GIMAGE) {
3231 dissolveAlphaPixels(image&: layer.alpha_tiles[j][i], x, y);
3232 }
3233 }
3234
3235 // Shortcut for common case
3236 if (merge == mergeRGBToRGB && layer.apply_mask != 1 && layer.mode == GIMP_LAYER_MODE_NORMAL_LEGACY) {
3237 QPainter painter(&image);
3238 painter.setOpacity(layer.opacity / 255.0);
3239 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
3240 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3241 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3242 painter.drawImage(x: x + layer.x_offset, y: y + layer.y_offset, image: layer.image_tiles[j][i]);
3243 }
3244 continue;
3245 }
3246
3247#ifndef DISABLE_TILE_PROFILE_CONV // not sure about that: left as old plugin
3248 QImage &tile = layer.image_tiles[j][i];
3249 if (layer.compositeSpace == XCFImageFormat::RgbPerceptualSpace && tile.colorSpace() != QColorSpace::SRgb) {
3250 tile.convertToColorSpace(QColorSpace::SRgb);
3251 }
3252 if (layer.compositeSpace == XCFImageFormat::RgbLinearSpace && tile.colorSpace() != QColorSpace::SRgbLinear) {
3253 tile.convertToColorSpace(QColorSpace::SRgbLinear);
3254 }
3255#endif
3256
3257 for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
3258 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
3259 int m = x + k + layer.x_offset;
3260 int n = y + l + layer.y_offset;
3261
3262 if (m < 0 || m >= image.width() || n < 0 || n >= image.height()) {
3263 continue;
3264 }
3265
3266 if (!(*merge)(layer, i, j, k, l, image, m, n)) {
3267 return;
3268 }
3269 }
3270 }
3271 }
3272 }
3273}
3274
3275/*!
3276 * Merge an RGB pixel from the layer to the RGB image. Straight-forward.
3277 * The only thing this has to take account of is the opacity of the
3278 * layer. Evidently, the GIMP exporter itself does not actually do this.
3279 * \param layer source layer.
3280 * \param i x tile index.
3281 * \param j y tile index.
3282 * \param k x pixel index of tile i,j.
3283 * \param l y pixel index of tile i,j.
3284 * \param image destination image.
3285 * \param m x pixel of destination image.
3286 * \param n y pixel of destination image.
3287 */
3288bool XCFImageFormat::mergeRGBToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3289{
3290 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
3291 QRgb dst = image.pixel(x: m, y: n);
3292
3293 uchar src_r = qRed(rgb: src);
3294 uchar src_g = qGreen(rgb: src);
3295 uchar src_b = qBlue(rgb: src);
3296 uchar src_a = qAlpha(rgb: src);
3297
3298 uchar dst_r = qRed(rgb: dst);
3299 uchar dst_g = qGreen(rgb: dst);
3300 uchar dst_b = qBlue(rgb: dst);
3301 uchar dst_a = qAlpha(rgb: dst);
3302
3303 if (!src_a) {
3304 return false; // nothing to merge
3305 }
3306
3307 switch (layer.mode) {
3308 case GIMP_LAYER_MODE_NORMAL:
3309 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3310 break;
3311 case GIMP_LAYER_MODE_MULTIPLY:
3312 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3313 src_r = INT_MULT(a: src_r, b: dst_r);
3314 src_g = INT_MULT(a: src_g, b: dst_g);
3315 src_b = INT_MULT(a: src_b, b: dst_b);
3316 src_a = qMin(a: src_a, b: dst_a);
3317 break;
3318 case GIMP_LAYER_MODE_DIVIDE:
3319 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3320 src_r = qMin(a: (dst_r * 256) / (1 + src_r), b: 255);
3321 src_g = qMin(a: (dst_g * 256) / (1 + src_g), b: 255);
3322 src_b = qMin(a: (dst_b * 256) / (1 + src_b), b: 255);
3323 src_a = qMin(a: src_a, b: dst_a);
3324 break;
3325 case GIMP_LAYER_MODE_SCREEN:
3326 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3327 src_r = 255 - INT_MULT(a: 255 - dst_r, b: 255 - src_r);
3328 src_g = 255 - INT_MULT(a: 255 - dst_g, b: 255 - src_g);
3329 src_b = 255 - INT_MULT(a: 255 - dst_b, b: 255 - src_b);
3330 src_a = qMin(a: src_a, b: dst_a);
3331 break;
3332 case GIMP_LAYER_MODE_OVERLAY:
3333 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3334 src_r = INT_MULT(a: dst_r, b: dst_r + INT_MULT(a: 2 * src_r, b: 255 - dst_r));
3335 src_g = INT_MULT(a: dst_g, b: dst_g + INT_MULT(a: 2 * src_g, b: 255 - dst_g));
3336 src_b = INT_MULT(a: dst_b, b: dst_b + INT_MULT(a: 2 * src_b, b: 255 - dst_b));
3337 src_a = qMin(a: src_a, b: dst_a);
3338 break;
3339 case GIMP_LAYER_MODE_DIFFERENCE:
3340 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3341 src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
3342 src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
3343 src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
3344 src_a = qMin(a: src_a, b: dst_a);
3345 break;
3346 case GIMP_LAYER_MODE_ADDITION:
3347 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3348 src_r = add_lut(a: dst_r, b: src_r);
3349 src_g = add_lut(a: dst_g, b: src_g);
3350 src_b = add_lut(a: dst_b, b: src_b);
3351 src_a = qMin(a: src_a, b: dst_a);
3352 break;
3353 case GIMP_LAYER_MODE_SUBTRACT:
3354 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3355 src_r = dst_r > src_r ? dst_r - src_r : 0;
3356 src_g = dst_g > src_g ? dst_g - src_g : 0;
3357 src_b = dst_b > src_b ? dst_b - src_b : 0;
3358 src_a = qMin(a: src_a, b: dst_a);
3359 break;
3360 case GIMP_LAYER_MODE_DARKEN_ONLY:
3361 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3362 src_r = dst_r < src_r ? dst_r : src_r;
3363 src_g = dst_g < src_g ? dst_g : src_g;
3364 src_b = dst_b < src_b ? dst_b : src_b;
3365 src_a = qMin(a: src_a, b: dst_a);
3366 break;
3367 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3368 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3369 src_r = dst_r < src_r ? src_r : dst_r;
3370 src_g = dst_g < src_g ? src_g : dst_g;
3371 src_b = dst_b < src_b ? src_b : dst_b;
3372 src_a = qMin(a: src_a, b: dst_a);
3373 break;
3374 case GIMP_LAYER_MODE_HSV_HUE:
3375 case GIMP_LAYER_MODE_HSV_HUE_LEGACY: {
3376 uchar new_r = dst_r;
3377 uchar new_g = dst_g;
3378 uchar new_b = dst_b;
3379
3380 RGBTOHSV(red&: src_r, green&: src_g, blue&: src_b);
3381 RGBTOHSV(red&: new_r, green&: new_g, blue&: new_b);
3382
3383 new_r = src_r;
3384
3385 HSVTORGB(hue&: new_r, saturation&: new_g, value&: new_b);
3386
3387 src_r = new_r;
3388 src_g = new_g;
3389 src_b = new_b;
3390 src_a = qMin(a: src_a, b: dst_a);
3391 } break;
3392 case GIMP_LAYER_MODE_HSV_SATURATION:
3393 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: {
3394 uchar new_r = dst_r;
3395 uchar new_g = dst_g;
3396 uchar new_b = dst_b;
3397
3398 RGBTOHSV(red&: src_r, green&: src_g, blue&: src_b);
3399 RGBTOHSV(red&: new_r, green&: new_g, blue&: new_b);
3400
3401 new_g = src_g;
3402
3403 HSVTORGB(hue&: new_r, saturation&: new_g, value&: new_b);
3404
3405 src_r = new_r;
3406 src_g = new_g;
3407 src_b = new_b;
3408 src_a = qMin(a: src_a, b: dst_a);
3409 } break;
3410 case GIMP_LAYER_MODE_HSV_VALUE:
3411 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY: {
3412 uchar new_r = dst_r;
3413 uchar new_g = dst_g;
3414 uchar new_b = dst_b;
3415
3416 RGBTOHSV(red&: src_r, green&: src_g, blue&: src_b);
3417 RGBTOHSV(red&: new_r, green&: new_g, blue&: new_b);
3418
3419 new_b = src_b;
3420
3421 HSVTORGB(hue&: new_r, saturation&: new_g, value&: new_b);
3422
3423 src_r = new_r;
3424 src_g = new_g;
3425 src_b = new_b;
3426 src_a = qMin(a: src_a, b: dst_a);
3427 } break;
3428 case GIMP_LAYER_MODE_HSL_COLOR:
3429 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: {
3430 uchar new_r = dst_r;
3431 uchar new_g = dst_g;
3432 uchar new_b = dst_b;
3433
3434 RGBTOHLS(red&: src_r, green&: src_g, blue&: src_b);
3435 RGBTOHLS(red&: new_r, green&: new_g, blue&: new_b);
3436
3437 new_r = src_r;
3438 new_b = src_b;
3439
3440 HLSTORGB(hue&: new_r, lightness&: new_g, saturation&: new_b);
3441
3442 src_r = new_r;
3443 src_g = new_g;
3444 src_b = new_b;
3445 src_a = qMin(a: src_a, b: dst_a);
3446 } break;
3447 case GIMP_LAYER_MODE_DODGE:
3448 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3449 uint tmp;
3450
3451 tmp = dst_r << 8;
3452 tmp /= 256 - src_r;
3453 src_r = (uchar)qMin(a: tmp, b: 255u);
3454
3455 tmp = dst_g << 8;
3456 tmp /= 256 - src_g;
3457 src_g = (uchar)qMin(a: tmp, b: 255u);
3458
3459 tmp = dst_b << 8;
3460 tmp /= 256 - src_b;
3461 src_b = (uchar)qMin(a: tmp, b: 255u);
3462
3463 src_a = qMin(a: src_a, b: dst_a);
3464 } break;
3465 case GIMP_LAYER_MODE_BURN:
3466 case GIMP_LAYER_MODE_BURN_LEGACY: {
3467 uint tmp;
3468
3469 tmp = (255 - dst_r) << 8;
3470 tmp /= src_r + 1;
3471 src_r = (uchar)qMin(a: tmp, b: 255u);
3472 src_r = 255 - src_r;
3473
3474 tmp = (255 - dst_g) << 8;
3475 tmp /= src_g + 1;
3476 src_g = (uchar)qMin(a: tmp, b: 255u);
3477 src_g = 255 - src_g;
3478
3479 tmp = (255 - dst_b) << 8;
3480 tmp /= src_b + 1;
3481 src_b = (uchar)qMin(a: tmp, b: 255u);
3482 src_b = 255 - src_b;
3483
3484 src_a = qMin(a: src_a, b: dst_a);
3485 } break;
3486 case GIMP_LAYER_MODE_HARDLIGHT:
3487 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3488 uint tmp;
3489 if (src_r > 128) {
3490 tmp = ((int)255 - dst_r) * ((int)255 - ((src_r - 128) << 1));
3491 src_r = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3492 } else {
3493 tmp = (int)dst_r * ((int)src_r << 1);
3494 src_r = (uchar)qMin(a: tmp >> 8, b: 255u);
3495 }
3496
3497 if (src_g > 128) {
3498 tmp = ((int)255 - dst_g) * ((int)255 - ((src_g - 128) << 1));
3499 src_g = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3500 } else {
3501 tmp = (int)dst_g * ((int)src_g << 1);
3502 src_g = (uchar)qMin(a: tmp >> 8, b: 255u);
3503 }
3504
3505 if (src_b > 128) {
3506 tmp = ((int)255 - dst_b) * ((int)255 - ((src_b - 128) << 1));
3507 src_b = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3508 } else {
3509 tmp = (int)dst_b * ((int)src_b << 1);
3510 src_b = (uchar)qMin(a: tmp >> 8, b: 255u);
3511 }
3512 src_a = qMin(a: src_a, b: dst_a);
3513 } break;
3514 case GIMP_LAYER_MODE_SOFTLIGHT:
3515 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3516 uint tmpS;
3517 uint tmpM;
3518
3519 tmpM = INT_MULT(a: dst_r, b: src_r);
3520 tmpS = 255 - INT_MULT(a: (255 - dst_r), b: (255 - src_r));
3521 src_r = INT_MULT(a: (255 - dst_r), b: tmpM) + INT_MULT(a: dst_r, b: tmpS);
3522
3523 tmpM = INT_MULT(a: dst_g, b: src_g);
3524 tmpS = 255 - INT_MULT(a: (255 - dst_g), b: (255 - src_g));
3525 src_g = INT_MULT(a: (255 - dst_g), b: tmpM) + INT_MULT(a: dst_g, b: tmpS);
3526
3527 tmpM = INT_MULT(a: dst_b, b: src_b);
3528 tmpS = 255 - INT_MULT(a: (255 - dst_b), b: (255 - src_b));
3529 src_b = INT_MULT(a: (255 - dst_b), b: tmpM) + INT_MULT(a: dst_b, b: tmpS);
3530
3531 src_a = qMin(a: src_a, b: dst_a);
3532 } break;
3533 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3534 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3535 int tmp;
3536
3537 tmp = dst_r - src_r + 128;
3538 tmp = qMin(a: tmp, b: 255);
3539 tmp = qMax(a: tmp, b: 0);
3540 src_r = (uchar)tmp;
3541
3542 tmp = dst_g - src_g + 128;
3543 tmp = qMin(a: tmp, b: 255);
3544 tmp = qMax(a: tmp, b: 0);
3545 src_g = (uchar)tmp;
3546
3547 tmp = dst_b - src_b + 128;
3548 tmp = qMin(a: tmp, b: 255);
3549 tmp = qMax(a: tmp, b: 0);
3550 src_b = (uchar)tmp;
3551
3552 src_a = qMin(a: src_a, b: dst_a);
3553 } break;
3554 case GIMP_LAYER_MODE_GRAIN_MERGE:
3555 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3556 int tmp;
3557
3558 tmp = dst_r + src_r - 128;
3559 tmp = qMin(a: tmp, b: 255);
3560 tmp = qMax(a: tmp, b: 0);
3561 src_r = (uchar)tmp;
3562
3563 tmp = dst_g + src_g - 128;
3564 tmp = qMin(a: tmp, b: 255);
3565 tmp = qMax(a: tmp, b: 0);
3566 src_g = (uchar)tmp;
3567
3568 tmp = dst_b + src_b - 128;
3569 tmp = qMin(a: tmp, b: 255);
3570 tmp = qMax(a: tmp, b: 0);
3571 src_b = (uchar)tmp;
3572
3573 src_a = qMin(a: src_a, b: dst_a);
3574 } break;
3575 case GIMP_LAYER_MODE_LINEAR_LIGHT: {
3576 if (src_r <= 128) {
3577 src_r = qBound(min: 0, val: dst_r + 2 * src_r - 255, max: 255);
3578 } else {
3579 src_r = qBound(min: 0, val: dst_r + 2 * (src_r - 128), max: 255);
3580 }
3581 if (src_g <= 128) {
3582 src_g = qBound(min: 0, val: dst_g + 2 * src_g - 255, max: 255);
3583 } else {
3584 src_g = qBound(min: 0, val: dst_g + 2 * (src_g - 127), max: 255);
3585 }
3586 if (src_b <= 128) {
3587 src_b = qBound(min: 0, val: dst_b + 2 * src_b - 255, max: 255);
3588 } else {
3589 src_b = qBound(min: 0, val: dst_b + 2 * (src_b - 127), max: 255);
3590 }
3591 } break;
3592 case GIMP_LAYER_MODE_VIVID_LIGHT: {
3593 // From http://www.simplefilter.de/en/basics/mixmods.html
3594 float A[3];
3595 A[0] = src_r / 255.;
3596 A[1] = src_g / 255.;
3597 A[2] = src_b / 255.;
3598 float B[3];
3599 B[0] = dst_r / 255.;
3600 B[1] = dst_g / 255.;
3601 B[2] = dst_b / 255.;
3602 float C[3]{};
3603 for (int i = 0; i < 3; i++) {
3604 if (A[i] <= 0.5f) {
3605 if (A[i] > 0.f) {
3606 C[i] = 1.f - (1.f - B[i]) / (2.f * A[i]);
3607 }
3608 } else {
3609 if (A[i] < 1.f) {
3610 C[i] = B[i] / (2.f * (1.f - A[i]));
3611 }
3612 }
3613 }
3614 src_r = qBound(min: 0.f, val: C[0] * 255.f, max: 255.f);
3615 src_g = qBound(min: 0.f, val: C[1] * 255.f, max: 255.f);
3616 src_b = qBound(min: 0.f, val: C[2] * 255.f, max: 255.f);
3617 } break;
3618 default:
3619 qCWarning(XCFPLUGIN) << "Unhandled mode" << layer.mode;
3620 return false;
3621 }
3622
3623 src_a = INT_MULT(a: src_a, b: layer.opacity);
3624
3625 // Apply the mask (if any)
3626
3627 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
3628 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
3629 }
3630
3631 uchar new_r;
3632 uchar new_g;
3633 uchar new_b;
3634 uchar new_a;
3635 new_a = dst_a + INT_MULT(a: OPAQUE_OPACITY - dst_a, b: src_a);
3636
3637 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3638 float dst_ratio = 1.0 - src_ratio;
3639
3640 new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
3641 new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
3642 new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
3643
3644 if (!modeAffectsSourceAlpha(type: layer.mode)) {
3645 new_a = dst_a;
3646 }
3647
3648 image.setPixel(x: m, y: n, index_or_rgb: qRgba(r: new_r, g: new_g, b: new_b, a: new_a));
3649 return true;
3650}
3651
3652/*!
3653 * Merge a Gray pixel from the layer to the Gray image. Straight-forward.
3654 * \param layer source layer.
3655 * \param i x tile index.
3656 * \param j y tile index.
3657 * \param k x pixel index of tile i,j.
3658 * \param l y pixel index of tile i,j.
3659 * \param image destination image.
3660 * \param m x pixel of destination image.
3661 * \param n y pixel of destination image.
3662 */
3663bool XCFImageFormat::mergeGrayToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3664{
3665 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
3666 image.setPixel(x: m, y: n, index_or_rgb: src);
3667 return true;
3668}
3669
3670/*!
3671 * Merge a GrayA pixel from the layer to the Gray image. Straight-forward.
3672 * \param layer source layer.
3673 * \param i x tile index.
3674 * \param j y tile index.
3675 * \param k x pixel index of tile i,j.
3676 * \param l y pixel index of tile i,j.
3677 * \param image destination image.
3678 * \param m x pixel of destination image.
3679 * \param n y pixel of destination image.
3680 */
3681bool XCFImageFormat::mergeGrayAToGray(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3682{
3683 int src = qGray(rgb: layer.image_tiles[j][i].pixel(x: k, y: l));
3684 int dst = image.pixelIndex(x: m, y: n);
3685
3686 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
3687
3688 if (!src_a) {
3689 return false; // nothing to merge
3690 }
3691
3692 switch (layer.mode) {
3693 case GIMP_LAYER_MODE_MULTIPLY:
3694 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3695 src = INT_MULT(a: src, b: dst);
3696 } break;
3697 case GIMP_LAYER_MODE_DIVIDE:
3698 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3699 src = qMin(a: (dst * 256) / (1 + src), b: 255);
3700 } break;
3701 case GIMP_LAYER_MODE_SCREEN:
3702 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3703 src = 255 - INT_MULT(a: 255 - dst, b: 255 - src);
3704 } break;
3705 case GIMP_LAYER_MODE_OVERLAY:
3706 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3707 src = INT_MULT(a: dst, b: dst + INT_MULT(a: 2 * src, b: 255 - dst));
3708 } break;
3709 case GIMP_LAYER_MODE_DIFFERENCE:
3710 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3711 src = dst > src ? dst - src : src - dst;
3712 } break;
3713 case GIMP_LAYER_MODE_ADDITION:
3714 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3715 src = add_lut(a: dst, b: src);
3716 } break;
3717 case GIMP_LAYER_MODE_SUBTRACT:
3718 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3719 src = dst > src ? dst - src : 0;
3720 } break;
3721 case GIMP_LAYER_MODE_DARKEN_ONLY:
3722 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3723 src = dst < src ? dst : src;
3724 } break;
3725 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3726 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3727 src = dst < src ? src : dst;
3728 } break;
3729 case GIMP_LAYER_MODE_DODGE:
3730 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3731 uint tmp = dst << 8;
3732 tmp /= 256 - src;
3733 src = (uchar)qMin(a: tmp, b: 255u);
3734 } break;
3735 case GIMP_LAYER_MODE_BURN:
3736 case GIMP_LAYER_MODE_BURN_LEGACY: {
3737 uint tmp = (255 - dst) << 8;
3738 tmp /= src + 1;
3739 src = (uchar)qMin(a: tmp, b: 255u);
3740 src = 255 - src;
3741 } break;
3742 case GIMP_LAYER_MODE_HARDLIGHT:
3743 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3744 uint tmp;
3745 if (src > 128) {
3746 tmp = ((int)255 - dst) * ((int)255 - ((src - 128) << 1));
3747 src = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3748 } else {
3749 tmp = (int)dst * ((int)src << 1);
3750 src = (uchar)qMin(a: tmp >> 8, b: 255u);
3751 }
3752 } break;
3753 case GIMP_LAYER_MODE_SOFTLIGHT:
3754 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3755 uint tmpS;
3756 uint tmpM;
3757
3758 tmpM = INT_MULT(a: dst, b: src);
3759 tmpS = 255 - INT_MULT(a: (255 - dst), b: (255 - src));
3760 src = INT_MULT(a: (255 - dst), b: tmpM) + INT_MULT(a: dst, b: tmpS);
3761
3762 } break;
3763 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3764 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3765 int tmp;
3766
3767 tmp = dst - src + 128;
3768 tmp = qMin(a: tmp, b: 255);
3769 tmp = qMax(a: tmp, b: 0);
3770
3771 src = (uchar)tmp;
3772 } break;
3773 case GIMP_LAYER_MODE_GRAIN_MERGE:
3774 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3775 int tmp;
3776
3777 tmp = dst + src - 128;
3778 tmp = qMin(a: tmp, b: 255);
3779 tmp = qMax(a: tmp, b: 0);
3780
3781 src = (uchar)tmp;
3782 } break;
3783 default:
3784 qCWarning(XCFPLUGIN) << "Unhandled mode" << layer.mode;
3785 return false;
3786 }
3787
3788 src_a = INT_MULT(a: src_a, b: layer.opacity);
3789
3790 // Apply the mask (if any)
3791
3792 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
3793 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
3794 }
3795
3796 uchar new_a = OPAQUE_OPACITY;
3797
3798 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3799 float dst_ratio = 1.0 - src_ratio;
3800
3801 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3802
3803 image.setPixel(x: m, y: n, index_or_rgb: new_g);
3804 return true;
3805}
3806
3807/*!
3808 * Merge a Gray pixel from the layer to an RGB image. Straight-forward.
3809 * The only thing this has to take account of is the opacity of the
3810 * layer. Evidently, the GIMP exporter itself does not actually do this.
3811 * \param layer source layer.
3812 * \param i x tile index.
3813 * \param j y tile index.
3814 * \param k x pixel index of tile i,j.
3815 * \param l y pixel index of tile i,j.
3816 * \param image destination image.
3817 * \param m x pixel of destination image.
3818 * \param n y pixel of destination image.
3819 */
3820bool XCFImageFormat::mergeGrayToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3821{
3822 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
3823 uchar src_a = layer.opacity;
3824 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
3825 return true;
3826}
3827
3828/*!
3829 * Merge a GrayA pixel from the layer to an RGB image. Straight-forward.
3830 * The only thing this has to take account of is the opacity of the
3831 * layer. Evidently, the GIMP exporter itself does not actually do this.
3832 * \param layer source layer.
3833 * \param i x tile index.
3834 * \param j y tile index.
3835 * \param k x pixel index of tile i,j.
3836 * \param l y pixel index of tile i,j.
3837 * \param image destination image.
3838 * \param m x pixel of destination image.
3839 * \param n y pixel of destination image.
3840 */
3841bool XCFImageFormat::mergeGrayAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3842{
3843 int src = qGray(rgb: layer.image_tiles[j][i].pixel(x: k, y: l));
3844 int dst = qGray(rgb: image.pixel(x: m, y: n));
3845
3846 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
3847 uchar dst_a = qAlpha(rgb: image.pixel(x: m, y: n));
3848
3849 if (!src_a) {
3850 return false; // nothing to merge
3851 }
3852
3853 switch (layer.mode) {
3854 case GIMP_LAYER_MODE_MULTIPLY:
3855 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3856 src = INT_MULT(a: src, b: dst);
3857 src_a = qMin(a: src_a, b: dst_a);
3858 } break;
3859 case GIMP_LAYER_MODE_DIVIDE:
3860 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3861 src = qMin(a: (dst * 256) / (1 + src), b: 255);
3862 src_a = qMin(a: src_a, b: dst_a);
3863 } break;
3864 case GIMP_LAYER_MODE_SCREEN:
3865 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3866 src = 255 - INT_MULT(a: 255 - dst, b: 255 - src);
3867 src_a = qMin(a: src_a, b: dst_a);
3868 } break;
3869 case GIMP_LAYER_MODE_OVERLAY:
3870 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3871 src = INT_MULT(a: dst, b: dst + INT_MULT(a: 2 * src, b: 255 - dst));
3872 src_a = qMin(a: src_a, b: dst_a);
3873 } break;
3874 case GIMP_LAYER_MODE_DIFFERENCE:
3875 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3876 src = dst > src ? dst - src : src - dst;
3877 src_a = qMin(a: src_a, b: dst_a);
3878 } break;
3879 case GIMP_LAYER_MODE_ADDITION:
3880 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3881 src = add_lut(a: dst, b: src);
3882 src_a = qMin(a: src_a, b: dst_a);
3883 } break;
3884 case GIMP_LAYER_MODE_SUBTRACT:
3885 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3886 src = dst > src ? dst - src : 0;
3887 src_a = qMin(a: src_a, b: dst_a);
3888 } break;
3889 case GIMP_LAYER_MODE_DARKEN_ONLY:
3890 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3891 src = dst < src ? dst : src;
3892 src_a = qMin(a: src_a, b: dst_a);
3893 } break;
3894 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3895 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3896 src = dst < src ? src : dst;
3897 src_a = qMin(a: src_a, b: dst_a);
3898 } break;
3899 case GIMP_LAYER_MODE_DODGE:
3900 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3901 uint tmp = dst << 8;
3902 tmp /= 256 - src;
3903 src = (uchar)qMin(a: tmp, b: 255u);
3904 src_a = qMin(a: src_a, b: dst_a);
3905 } break;
3906 case GIMP_LAYER_MODE_BURN:
3907 case GIMP_LAYER_MODE_BURN_LEGACY: {
3908 uint tmp = (255 - dst) << 8;
3909 tmp /= src + 1;
3910 src = (uchar)qMin(a: tmp, b: 255u);
3911 src = 255 - src;
3912 src_a = qMin(a: src_a, b: dst_a);
3913 } break;
3914 case GIMP_LAYER_MODE_HARDLIGHT:
3915 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3916 uint tmp;
3917 if (src > 128) {
3918 tmp = ((int)255 - dst) * ((int)255 - ((src - 128) << 1));
3919 src = (uchar)qMin(a: 255 - (tmp >> 8), b: 255u);
3920 } else {
3921 tmp = (int)dst * ((int)src << 1);
3922 src = (uchar)qMin(a: tmp >> 8, b: 255u);
3923 }
3924 src_a = qMin(a: src_a, b: dst_a);
3925 } break;
3926 case GIMP_LAYER_MODE_SOFTLIGHT:
3927 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3928 uint tmpS;
3929 uint tmpM;
3930
3931 tmpM = INT_MULT(a: dst, b: src);
3932 tmpS = 255 - INT_MULT(a: (255 - dst), b: (255 - src));
3933 src = INT_MULT(a: (255 - dst), b: tmpM) + INT_MULT(a: dst, b: tmpS);
3934
3935 src_a = qMin(a: src_a, b: dst_a);
3936 } break;
3937 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3938 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3939 int tmp;
3940
3941 tmp = dst - src + 128;
3942 tmp = qMin(a: tmp, b: 255);
3943 tmp = qMax(a: tmp, b: 0);
3944
3945 src = (uchar)tmp;
3946 src_a = qMin(a: src_a, b: dst_a);
3947 } break;
3948 case GIMP_LAYER_MODE_GRAIN_MERGE:
3949 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3950 int tmp;
3951
3952 tmp = dst + src - 128;
3953 tmp = qMin(a: tmp, b: 255);
3954 tmp = qMax(a: tmp, b: 0);
3955
3956 src = (uchar)tmp;
3957 src_a = qMin(a: src_a, b: dst_a);
3958 } break;
3959 default:
3960 qCWarning(XCFPLUGIN) << "Unhandled mode" << layer.mode;
3961 return false;
3962 }
3963
3964 src_a = INT_MULT(a: src_a, b: layer.opacity);
3965
3966 // Apply the mask (if any)
3967 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
3968 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
3969 }
3970
3971 uchar new_a = dst_a + INT_MULT(a: OPAQUE_OPACITY - dst_a, b: src_a);
3972
3973 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3974 float dst_ratio = 1.0 - src_ratio;
3975
3976 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3977
3978 if (!modeAffectsSourceAlpha(type: layer.mode)) {
3979 new_a = dst_a;
3980 }
3981
3982 image.setPixel(x: m, y: n, index_or_rgb: qRgba(r: new_g, g: new_g, b: new_g, a: new_a));
3983 return true;
3984}
3985
3986/*!
3987 * Merge an Indexed pixel from the layer to the Indexed image. Straight-forward.
3988 * \param layer source layer.
3989 * \param i x tile index.
3990 * \param j y tile index.
3991 * \param k x pixel index of tile i,j.
3992 * \param l y pixel index of tile i,j.
3993 * \param image destination image.
3994 * \param m x pixel of destination image.
3995 * \param n y pixel of destination image.
3996 */
3997bool XCFImageFormat::mergeIndexedToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
3998{
3999 int src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
4000 image.setPixel(x: m, y: n, index_or_rgb: src);
4001 return true;
4002}
4003
4004/*!
4005 * Merge an IndexedA pixel from the layer to the Indexed image. Straight-forward.
4006 * \param layer source layer.
4007 * \param i x tile index.
4008 * \param j y tile index.
4009 * \param k x pixel index of tile i,j.
4010 * \param l y pixel index of tile i,j.
4011 * \param image destination image.
4012 * \param m x pixel of destination image.
4013 * \param n y pixel of destination image.
4014 */
4015bool XCFImageFormat::mergeIndexedAToIndexed(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
4016{
4017 uchar src = layer.image_tiles[j][i].pixelIndex(x: k, y: l);
4018 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
4019 src_a = INT_MULT(a: src_a, b: layer.opacity);
4020
4021 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
4022 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
4023 }
4024
4025 if (src_a > 127) {
4026 src++;
4027 image.setPixel(x: m, y: n, index_or_rgb: src);
4028 }
4029 return true;
4030}
4031
4032/*!
4033 * Merge an IndexedA pixel from the layer to an RGB image. Straight-forward.
4034 * The only thing this has to take account of is the opacity of the
4035 * layer. Evidently, the GIMP exporter itself does not actually do this.
4036 * \param layer source layer.
4037 * \param i x tile index.
4038 * \param j y tile index.
4039 * \param k x pixel index of tile i,j.
4040 * \param l y pixel index of tile i,j.
4041 * \param image destination image.
4042 * \param m x pixel of destination image.
4043 * \param n y pixel of destination image.
4044 */
4045bool XCFImageFormat::mergeIndexedAToRGB(const Layer &layer, uint i, uint j, int k, int l, QImage &image, int m, int n)
4046{
4047 QRgb src = layer.image_tiles[j][i].pixel(x: k, y: l);
4048 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(x: k, y: l);
4049 src_a = INT_MULT(a: src_a, b: layer.opacity);
4050
4051 // Apply the mask (if any)
4052 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && layer.mask_tiles[j].size() > (int)i) {
4053 src_a = INT_MULT(a: src_a, b: layer.mask_tiles[j][i].pixelIndex(x: k, y: l));
4054 }
4055
4056 // This is what appears in the GIMP window
4057 if (src_a <= 127) {
4058 src_a = 0;
4059 } else {
4060 src_a = OPAQUE_OPACITY;
4061 }
4062
4063 image.setPixel(x: m, y: n, index_or_rgb: qRgba(rgb: src, a: src_a));
4064 return true;
4065}
4066
4067/*!
4068 * Dissolving pixels: pick a random number between 0 and 255. If the pixel's
4069 * alpha is less than that, make it transparent.
4070 * \param image the image tile to dissolve.
4071 * \param x the global x position of the tile.
4072 * \param y the global y position of the tile.
4073 */
4074void XCFImageFormat::dissolveRGBPixels(QImage &image, int x, int y)
4075{
4076 // The apparently spurious rand() calls are to wind the random
4077 // numbers up to the same point for each tile.
4078
4079 for (int l = 0; l < image.height(); l++) {
4080 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4081
4082 for (int k = 0; k < x; k++) {
4083 RandomTable::rand_r(seed: &next);
4084 }
4085
4086 for (int k = 0; k < image.width(); k++) {
4087 int rand_val = RandomTable::rand_r(seed: &next) & 0xff;
4088 QRgb pixel = image.pixel(x: k, y: l);
4089
4090 if (rand_val > qAlpha(rgb: pixel)) {
4091 image.setPixel(x: k, y: l, index_or_rgb: qRgba(rgb: pixel, a: 0));
4092 }
4093 }
4094 }
4095}
4096
4097/*!
4098 * Dissolving pixels: pick a random number between 0 and 255. If the pixel's
4099 * alpha is less than that, make it transparent. This routine works for
4100 * the GRAYA and INDEXEDA image types where the pixel alpha's are stored
4101 * separately from the pixel themselves.
4102 * \param image the alpha tile to dissolve.
4103 * \param x the global x position of the tile.
4104 * \param y the global y position of the tile.
4105 */
4106void XCFImageFormat::dissolveAlphaPixels(QImage &image, int x, int y)
4107{
4108 // The apparently spurious rand() calls are to wind the random
4109 // numbers up to the same point for each tile.
4110
4111 for (int l = 0; l < image.height(); l++) {
4112 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4113
4114 for (int k = 0; k < x; k++) {
4115 RandomTable::rand_r(seed: &next);
4116 }
4117
4118 for (int k = 0; k < image.width(); k++) {
4119 int rand_val = RandomTable::rand_r(seed: &next) & 0xff;
4120 uchar alpha = image.pixelIndex(x: k, y: l);
4121
4122 if (rand_val > alpha) {
4123 image.setPixel(x: k, y: l, index_or_rgb: 0);
4124 }
4125 }
4126 }
4127}
4128
4129///////////////////////////////////////////////////////////////////////////////
4130
4131XCFHandler::XCFHandler()
4132{
4133}
4134
4135bool XCFHandler::canRead() const
4136{
4137 if (canRead(device: device())) {
4138 setFormat("xcf");
4139 return true;
4140 }
4141 return false;
4142}
4143
4144bool XCFHandler::read(QImage *image)
4145{
4146 XCFImageFormat xcfif;
4147 return xcfif.readXCF(device: device(), outImage: image);
4148}
4149
4150bool XCFHandler::write(const QImage &)
4151{
4152 return false;
4153}
4154
4155bool XCFHandler::supportsOption(ImageOption option) const
4156{
4157 if (option == QImageIOHandler::Size)
4158 return true;
4159 return false;
4160}
4161
4162QVariant XCFHandler::option(ImageOption option) const
4163{
4164 QVariant v;
4165
4166 if (option == QImageIOHandler::Size) {
4167 /*
4168 * The image structure always starts at offset 0 in the XCF file.
4169 * byte[9] "gimp xcf " File type identification
4170 * byte[4] version XCF version
4171 * "file": version 0
4172 * "v001": version 1
4173 * "v002": version 2
4174 * "v003": version 3
4175 * byte 0 Zero marks the end of the version tag.
4176 * uint32 width Width of canvas
4177 * uint32 height Height of canvas
4178 */
4179 if (auto d = device()) {
4180 // transactions works on both random and sequential devices
4181 d->startTransaction();
4182 auto ba9 = d->read(maxlen: 9); // "gimp xcf "
4183 auto ba5 = d->read(maxlen: 4+1); // version + null terminator
4184 auto ba = d->read(maxlen: 8); // width and height
4185 d->rollbackTransaction();
4186 if (ba9 == QByteArray("gimp xcf ") && ba5.size() == 5) {
4187 QDataStream ds(ba);
4188 quint32 width;
4189 ds >> width;
4190 quint32 height;
4191 ds >> height;
4192 if (ds.status() == QDataStream::Ok)
4193 v = QVariant::fromValue(value: QSize(width, height));
4194 }
4195 }
4196 }
4197
4198 return v;
4199}
4200
4201bool XCFHandler::canRead(QIODevice *device)
4202{
4203 if (!device) {
4204 qCDebug(XCFPLUGIN) << "XCFHandler::canRead() called with no device";
4205 return false;
4206 }
4207 if (device->isSequential()) {
4208 return false;
4209 }
4210
4211 const qint64 oldPos = device->pos();
4212 if (!device->isSequential()) {
4213 QDataStream ds(device);
4214 XCFImageFormat::XCFImage::Header header;
4215 bool failed = !XCFImageFormat::readXCFHeader(xcf_io&: ds, header: &header);
4216 ds.setDevice(nullptr);
4217 device->seek(pos: oldPos);
4218 if (failed) {
4219 return false;
4220 }
4221
4222 switch (header.precision) {
4223 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
4224 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
4225 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
4226 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
4227 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
4228 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
4229 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
4230 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
4231 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
4232 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
4233 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
4234 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
4235 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
4236 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
4237 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
4238 break;
4239 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
4240 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
4241 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
4242 default:
4243 qCDebug(XCFPLUGIN) << "unsupported precision" << header.precision;
4244 return false;
4245 }
4246
4247 return true;
4248 }
4249
4250 char head[8];
4251 qint64 readBytes = device->read(data: head, maxlen: sizeof(head));
4252 if (readBytes != sizeof(head)) {
4253 if (device->isSequential()) {
4254 while (readBytes > 0) {
4255 device->ungetChar(c: head[readBytes-- - 1]);
4256 }
4257 } else {
4258 device->seek(pos: oldPos);
4259 }
4260 return false;
4261 }
4262
4263 if (device->isSequential()) {
4264 while (readBytes > 0) {
4265 device->ungetChar(c: head[readBytes-- - 1]);
4266 }
4267 } else {
4268 device->seek(pos: oldPos);
4269 }
4270
4271 return qstrncmp(str1: head, str2: "gimp xcf", len: 8) == 0;
4272}
4273
4274QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
4275{
4276 if (format == "xcf") {
4277 return Capabilities(CanRead);
4278 }
4279 if (!format.isEmpty()) {
4280 return {};
4281 }
4282 if (!device->isOpen()) {
4283 return {};
4284 }
4285
4286 Capabilities cap;
4287 if (device->isReadable() && XCFHandler::canRead(device)) {
4288 cap |= CanRead;
4289 }
4290 return cap;
4291}
4292
4293QImageIOHandler *XCFPlugin::create(QIODevice *device, const QByteArray &format) const
4294{
4295 QImageIOHandler *handler = new XCFHandler;
4296 handler->setDevice(device);
4297 handler->setFormat(format);
4298 return handler;
4299}
4300
4301// Just so I can get enum values printed
4302#include "xcf.moc"
4303
4304#include "moc_xcf_p.cpp"
4305

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