KImageFormats

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

KDE's Doxygen guidelines are available online.