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

KDE's Doxygen guidelines are available online.