16#include <QImageReader>
18#include <QLoggingCategory>
23#ifndef XCF_QT5_SUPPORT
26#define USE_FLOAT_IMAGES
29#define MAX_IMAGE_WIDTH 300000
30#define MAX_IMAGE_HEIGHT 300000
33#define MAX_IMAGE_WIDTH 32767
34#define MAX_IMAGE_HEIGHT 32767
37#ifdef USE_FLOAT_IMAGES
38#include <qrgbafloat.h>
46Q_DECLARE_LOGGING_CATEGORY(XCFPLUGIN)
47Q_LOGGING_CATEGORY(XCFPLUGIN,
"kf.imageformats.plugins.xcf", QtWarningMsg)
50#define DISABLE_IMAGE_PROFILE
51#define DISABLE_TILE_PROFILE_CONV
52#define DISABLE_IMAGE_PROFILE_CONV
54const float INCHESPERMETER = (100.0f / 2.54f);
60 static constexpr int rand_r(
unsigned int *seed)
62 unsigned int next = *seed;
67 result = (
unsigned int)(next / 65536) % 2048;
72 result ^= (
unsigned int)(next / 65536) % 1024;
77 result ^= (
unsigned int)(next / 65536) % 1024;
84 constexpr RandomTable()
87 unsigned int next = RANDOM_SEED;
89 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
90 values[i] = rand_r(&next);
93 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
95 int swap = i + rand_r(&next) % (RANDOM_TABLE_SIZE - i);
97 values[i] = values[swap];
102 int values[RANDOM_TABLE_SIZE]{};
123 PROP_ACTIVE_LAYER = 2,
124 PROP_ACTIVE_CHANNEL = 3,
126 PROP_FLOATING_SELECTION = 5,
131 PROP_LOCK_ALPHA = 10,
132 PROP_APPLY_MASK = 11,
135 PROP_SHOW_MASKED = 14,
138 PROP_COMPRESSION = 17,
140 PROP_RESOLUTION = 19,
147 PROP_TEXT_LAYER_FLAGS = 26,
148 PROP_OLD_SAMPLE_POINTS = 27,
149 PROP_LOCK_CONTENT = 28,
150 PROP_GROUP_ITEM = 29,
152 PROP_GROUP_ITEM_FLAGS = 31,
153 PROP_LOCK_POSITION = 32,
154 PROP_FLOAT_OPACITY = 33,
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,
166 enum XcfCompressionType : qint8 {
167 COMPRESS_INVALID = -1,
171 COMPRESS_FRACTAL = 3,
173 Q_ENUM(XcfCompressionType)
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,
240 Q_ENUM(LayerModeType)
243 enum GimpImageType : qint32 {
251 Q_ENUM(GimpImageType)
254 enum GimpColorSpace : qint32 {
260 Q_ENUM(GimpColorSpace);
263 enum GimpCompositeMode : qint32 {
266 CompositeClipBackdrop,
270 Q_ENUM(GimpCompositeMode);
272 enum GimpPrecision : qint32 {
273 GIMP_PRECISION_U8_LINEAR = 100,
274 GIMP_PRECISION_U8_NON_LINEAR = 150,
275 GIMP_PRECISION_U8_PERCEPTUAL = 175,
276 GIMP_PRECISION_U16_LINEAR = 200,
277 GIMP_PRECISION_U16_NON_LINEAR = 250,
278 GIMP_PRECISION_U16_PERCEPTUAL = 275,
279 GIMP_PRECISION_U32_LINEAR = 300,
280 GIMP_PRECISION_U32_NON_LINEAR = 350,
281 GIMP_PRECISION_U32_PERCEPTUAL = 375,
282 GIMP_PRECISION_HALF_LINEAR = 500,
283 GIMP_PRECISION_HALF_NON_LINEAR = 550,
284 GIMP_PRECISION_HALF_PERCEPTUAL = 575,
285 GIMP_PRECISION_FLOAT_LINEAR = 600,
286 GIMP_PRECISION_FLOAT_NON_LINEAR = 650,
287 GIMP_PRECISION_FLOAT_PERCEPTUAL = 675,
288 GIMP_PRECISION_DOUBLE_LINEAR = 700,
289 GIMP_PRECISION_DOUBLE_NON_LINEAR = 750,
290 GIMP_PRECISION_DOUBLE_PERCEPTUAL = 775,
292 Q_ENUM(GimpPrecision);
312 qint64 hierarchy_offset;
327 float opacityFloat = 1.f;
330 uchar red, green, blue;
331 float redF, greenF, blueF;
335 XcfCompressionType compression = COMPRESS_INVALID;
338 quint32 opacity = 255;
339 float opacityFloat = 1.f;
342 quint32 preserve_transparency;
343 quint32 apply_mask = 9;
350 LayerModeType mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
352 GimpColorSpace blendSpace = RgbLinearSpace;
353 GimpColorSpace compositeSpace = RgbLinearSpace;
354 GimpCompositeMode compositeMode = CompositeUnion;
357#ifdef USE_FLOAT_IMAGES
358 uchar tile[quint64(TILE_WIDTH * TILE_HEIGHT *
sizeof(QRgbaFloat32) * 1.5)];
360 uchar tile[quint64(TILE_WIDTH * TILE_HEIGHT *
sizeof(
QRgba64) * 1.5)];
367 bool (*assignBytes)(Layer &layer, uint i, uint j,
const GimpPrecision &precision);
378 Layer(
const Layer &) =
delete;
379 Layer &operator=(
const Layer &) =
delete;
381 QImage::Format qimageFormat(
const GimpPrecision precision, uint num_colors = 0,
bool legacyMode =
false)
const
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;
390 bpc = std::min(bpc, 1);
395 if (opacity == OPAQUE_OPACITY) {
396#ifdef USE_FLOAT_IMAGES
401 return QImage::QImage::Format_RGBX32FPx4;
407 }
else if (bpc == 2 || bpc == 4) {
410 qCDebug(XCFPLUGIN) <<
"Layer has invalid bpc" << bpc << precision;
416#ifdef USE_FLOAT_IMAGES
421 return QImage::QImage::Format_RGBA32FPx4;
426 }
else if (bpc == 2 || bpc == 4) {
429 qCDebug(XCFPLUGIN) <<
"Layer has invalid bpc" << bpc;
435 if (opacity == OPAQUE_OPACITY) {
456 if (num_colors == 1 || num_colors == 2) {
463 case INDEXEDA_GIMAGE:
464 if (num_colors == 1) {
470 qCWarning(XCFPLUGIN) <<
"Unhandled layer mode" << XCFImageFormat::LayerModeType(type);
483 GimpPrecision precision = GIMP_PRECISION_U8_LINEAR;
489 XcfCompressionType compression = COMPRESS_RLE;
490 float x_resolution = -1;
491 float y_resolution = -1;
494 qint32 num_colors = 0;
512 return layer.qimageFormat(header.precision, num_colors,
true);
515 uint bytesPerChannel()
const
517 return XCFImageFormat::bytesPerChannel(header.precision);
540 static int random_table[RANDOM_TABLE_SIZE];
541 static bool random_table_initialized;
543 static constexpr RandomTable randomTable{};
552 static int add_lut(
int,
int);
556 typedef void (*PixelCopyOperation)(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n);
559 typedef bool (*PixelMergeOperation)(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n);
561 static bool modeAffectsSourceAlpha(
const quint32 type);
563 bool loadImageProperties(
QDataStream &xcf_io, XCFImage &image);
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);
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);
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);
599 static void initializeRandomTable();
600 static void dissolveRGBPixels(
QImage &image,
int x,
int y);
601 static void dissolveAlphaPixels(
QImage &image,
int x,
int y);
603 static uint bytesPerChannel(
const GimpPrecision precision)
606 case GIMP_PRECISION_U8_LINEAR:
607 case GIMP_PRECISION_U8_NON_LINEAR:
608 case GIMP_PRECISION_U8_PERCEPTUAL:
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:
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:
628 case GIMP_PRECISION_DOUBLE_LINEAR:
629 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
630 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
635 qCDebug(XCFPLUGIN) <<
"Layer has invalid precision" << precision;
641 static bool readXCFHeader(
QDataStream &ds, XCFImage::Header *header);
644int XCFImageFormat::random_table[RANDOM_TABLE_SIZE];
645bool XCFImageFormat::random_table_initialized;
647constexpr RandomTable XCFImageFormat::randomTable;
651bool XCFImageFormat::modeAffectsSourceAlpha(
const quint32 type)
654 case GIMP_LAYER_MODE_NORMAL_LEGACY:
655 case GIMP_LAYER_MODE_DISSOLVE:
656 case GIMP_LAYER_MODE_BEHIND_LEGACY:
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:
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:
688 case GIMP_LAYER_MODE_NORMAL:
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:
727 qCWarning(XCFPLUGIN) <<
"Unhandled layer mode" << XCFImageFormat::LayerModeType(type);
733inline QRgb qRgba(
const QRgb rgb,
int a)
735 return ((a & 0xff) << 24 | (rgb & RGB_MASK));
741XCFImageFormat::XCFImageFormat()
743 static_assert(
sizeof(QRgb) == 4,
"the code assumes sizeof(QRgb) == 4, if that's not your case, help us fix it :)");
749void XCFImageFormat::initializeRandomTable()
754 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
755 random_table[i] = rand();
758 for (
int i = 0; i < RANDOM_TABLE_SIZE; i++) {
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;
767inline int XCFImageFormat::add_lut(
int a,
int b)
769 return qMin(a + b, 255);
772bool XCFImageFormat::readXCFHeader(
QDataStream &xcf_io, XCFImage::Header *header)
776 if (xcf_io.
readRawData(tag.data(), tag.size()) != tag.size()) {
777 qCDebug(XCFPLUGIN) <<
"XCF: read failure on header tag";
780 if (!tag.startsWith(
"gimp xcf") || !tag.endsWith(
'\0')) {
781 qCDebug(XCFPLUGIN) <<
"XCF: read called on non-XCF file";
788 if (tag.right(4) ==
"file") {
795 qCDebug(XCFPLUGIN) <<
"Failed to parse version" << tag;
799 qCDebug(XCFPLUGIN) <<
"version" << xcf_io.
version();
802 qCDebug(XCFPLUGIN) <<
"Unsupported version" << xcf_io.
version();
806 xcf_io >> header->width >> header->height >> header->type;
811 qCDebug(XCFPLUGIN) <<
"Precision" << GimpPrecision(precision);
815 precision = GIMP_PRECISION_U8_NON_LINEAR;
818 precision = GIMP_PRECISION_U16_NON_LINEAR;
821 precision = GIMP_PRECISION_U32_LINEAR;
824 precision = GIMP_PRECISION_HALF_LINEAR;
827 precision = GIMP_PRECISION_FLOAT_LINEAR;
830 if (precision < GIMP_PRECISION_U8_LINEAR) {
831 qCWarning(XCFPLUGIN) <<
"Invalid precision read" << precision;
834 qCDebug(XCFPLUGIN) <<
"Unexpected precision" << precision <<
"in version" << xcf_io.
version();
838 header->precision = GimpPrecision(precision);
840 qCDebug(XCFPLUGIN) <<
"tag:" << tag <<
" height: " << header->width <<
" width: " << header->height <<
" type: " << header->type;
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";
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";
860 if (!readXCFHeader(xcf_io, &xcf_image.header)) {
864 if (!loadImageProperties(xcf_io, xcf_image)) {
877 const qint64 layer_offset = readOffsetPtr(xcf_io);
879 if (layer_offset == 0) {
883 if (layer_offset < 0) {
884 qCDebug(XCFPLUGIN) <<
"XCF: negative layer offset";
888 layer_offsets.
push(layer_offset);
891 xcf_image.num_layers = layer_offsets.
size();
893 if (layer_offsets.
size() == 0) {
894 qCDebug(XCFPLUGIN) <<
"XCF: no layers!";
897 qCDebug(XCFPLUGIN) << xcf_image.num_layers <<
"layers";
900 while (!layer_offsets.
isEmpty()) {
901 qint64 layer_offset = layer_offsets.
pop();
905 if (!loadLayer(xcf_io, xcf_image)) {
910 if (!xcf_image.initialized) {
911 qCDebug(XCFPLUGIN) <<
"XCF: no visible layers!";
916 setImageParasites(xcf_image, xcf_image.image);
918 *outImage = xcf_image.image;
929bool XCFImageFormat::loadImageProperties(
QDataStream &xcf_io, XCFImage &xcf_image)
936 if (!loadProperty(xcf_io, type, bytes, rawType)) {
937 qCDebug(XCFPLUGIN) <<
"XCF: error loading global image properties";
947 case PROP_COMPRESSION:
948 property >> xcf_image.compression;
951 case PROP_RESOLUTION:
953 property >> xcf_image.x_resolution >> xcf_image.y_resolution;
957 property >> xcf_image.tattoo;
961 while (!property.atEnd()) {
963#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
969 property.readBytes(tag, size);
973 property >> flags >> data;
985 property >> xcf_image.unit;
995 property >> xcf_image.num_colors;
996 if (xcf_image.num_colors < 0 || xcf_image.num_colors > 65535) {
1001 xcf_image.palette.reserve(xcf_image.num_colors);
1003 for (
int i = 0; i < xcf_image.num_colors; i++) {
1007 property >> r >> g >> b;
1008 xcf_image.palette.push_back(qRgb(r, g, b));
1013 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented image property" <<
type <<
"(" << rawType <<
")"
1014 <<
", size " << bytes.
size();
1027bool XCFImageFormat::loadProperty(
QDataStream &xcf_io, PropType &type,
QByteArray &bytes, quint32 &rawType)
1032 if (rawType >= MAX_SUPPORTED_PROPTYPE) {
1033 type = MAX_SUPPORTED_PROPTYPE;
1042 type = PropType(rawType);
1044 char *data =
nullptr;
1050 if (type == PROP_COLORMAP) {
1055 size = 3 * ncolors + 4;
1057 if (size > 65535 || size < 4) {
1061 data =
new char[size];
1066 data[2] = ncolors >> 8;
1067 data[3] = ncolors & 255;
1071 }
else if (type == PROP_USER_UNIT) {
1076 xcf_io >> size >> factor >> digits;
1078 for (
int i = 0; i < 5; i++) {
1081 xcf_io >> unit_strings;
1083 delete[] unit_strings;
1086 qCDebug(XCFPLUGIN) <<
"XCF: read failure on property " <<
type;
1094 if (size > 256000 * 4) {
1096 qCDebug(XCFPLUGIN) <<
"XCF: loadProperty skips" <<
type <<
"due to size being too large";
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);
1107 if (size != 0 && data) {
1124bool XCFImageFormat::loadLayer(
QDataStream &xcf_io, XCFImage &xcf_image)
1126 Layer &layer(xcf_image.layer);
1127 delete[] layer.name;
1129 xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
1132 layer.compression = XcfCompressionType(xcf_image.compression);
1134 if (!loadLayerProperties(xcf_io, layer)) {
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;
1146 if (layer.visible == 0) {
1152 layer.hierarchy_offset = readOffsetPtr(xcf_io);
1153 layer.mask_offset = readOffsetPtr(xcf_io);
1155 if (layer.hierarchy_offset < 0) {
1156 qCDebug(XCFPLUGIN) <<
"XCF: negative layer hierarchy_offset";
1160 if (layer.mask_offset < 0) {
1161 qCDebug(XCFPLUGIN) <<
"XCF: negative layer mask_offset";
1168 if (!composeTiles(xcf_image)) {
1171 xcf_io.
device()->
seek(layer.hierarchy_offset);
1177 layer.assignBytes = assignImageBytes;
1179 if (!loadHierarchy(xcf_io, layer, xcf_image.header.precision)) {
1183 if (layer.mask_offset != 0) {
1185 if (layer.apply_mask == 9) {
1186 layer.apply_mask = 1;
1191 if (!loadMask(xcf_io, layer, xcf_image.header.precision)) {
1196 layer.apply_mask = 0;
1203 if (!xcf_image.initialized) {
1204 if (!initializeImage(xcf_image)) {
1207 copyLayerToImage(xcf_image);
1208 xcf_image.initialized =
true;
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);
1228bool XCFImageFormat::loadLayerProperties(
QDataStream &xcf_io, Layer &layer)
1235 if (!loadProperty(xcf_io, type, bytes, rawType)) {
1236 qCDebug(XCFPLUGIN) <<
"XCF: error loading layer properties";
1246 case PROP_ACTIVE_LAYER:
1247 layer.active =
true;
1251 property >> layer.opacity;
1252 layer.opacity = std::min(layer.opacity, 255u);
1255 case PROP_FLOAT_OPACITY:
1258 if (bytes.
size() == 4) {
1259 layer.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
1261 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
1266 property >> layer.visible;
1270 property >> layer.linked;
1273 case PROP_LOCK_ALPHA:
1274 property >> layer.preserve_transparency;
1277 case PROP_APPLY_MASK:
1278 property >> layer.apply_mask;
1281 case PROP_EDIT_MASK:
1282 property >> layer.edit_mask;
1285 case PROP_SHOW_MASK:
1286 property >> layer.show_mask;
1290 property >> layer.x_offset >> layer.y_offset;
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;
1302 property >> layer.tattoo;
1305 case PROP_COMPOSITE_SPACE:
1306 property >> layer.compositeSpace;
1307 if (layer.compositeSpace < 0) {
1308 layer.compositeSpace = GimpColorSpace(layer.compositeSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeSpace);
1312 case PROP_COMPOSITE_MODE:
1313 property >> layer.compositeMode;
1314 if (layer.compositeMode < 0) {
1315 layer.compositeMode =
1316 XCFImageFormat::GimpCompositeMode(layer.compositeMode == std::numeric_limits<qint32>::lowest() ? 0 : -layer.compositeMode);
1320 case PROP_BLEND_SPACE:
1321 property >> layer.blendSpace;
1322 if (layer.blendSpace < 0) {
1323 layer.blendSpace = GimpColorSpace(layer.blendSpace == std::numeric_limits<qint32>::lowest() ? 0 : -layer.blendSpace);
1328 case PROP_COLOR_TAG:
1332 case PROP_LOCK_CONTENT:
1333 case PROP_LOCK_POSITION:
1337 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented layer property " <<
type <<
"(" << rawType <<
")"
1338 <<
", size " << bytes.
size();
1349bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
1351 Layer &layer(xcf_image.layer);
1353 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
1354 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
1356 qCDebug(XCFPLUGIN) <<
"IMAGE: height=" << xcf_image.header.height <<
", width=" << xcf_image.header.width;
1357 qCDebug(XCFPLUGIN) <<
"LAYER: height=" << layer.height <<
", width=" << layer.width;
1358 qCDebug(XCFPLUGIN) <<
"LAYER: rows=" << layer.nrows <<
", columns=" << layer.ncols;
1365 if ((
sizeof(
void *) == 4 && qint64(layer.width) * layer.height > 16384 * 16384)) {
1366 qCWarning(XCFPLUGIN) <<
"On 32-bits programs the maximum layer size is limited to" << 16384 <<
"x" << 16384 <<
"px";
1369 if (layer.width > MAX_IMAGE_WIDTH || layer.height > MAX_IMAGE_HEIGHT) {
1370 qCWarning(XCFPLUGIN) <<
"The maximum layer size is limited to" << MAX_IMAGE_WIDTH <<
"x" << MAX_IMAGE_HEIGHT <<
"px";
1377 if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
1378 if (qint64(layer.width) * layer.height > 16384 * 16384) {
1379 qCWarning(XCFPLUGIN) <<
"Euristic sanity check: the image may be corrupted!";
1384#ifndef XCF_QT5_SUPPORT
1389 qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
1396 layer.image_tiles.resize(layer.nrows);
1398 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1399 layer.alpha_tiles.resize(layer.nrows);
1402 if (layer.mask_offset != 0) {
1403 layer.mask_tiles.resize(layer.nrows);
1406 for (uint j = 0; j < layer.nrows; j++) {
1407 layer.image_tiles[j].resize(layer.ncols);
1409 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1410 layer.alpha_tiles[j].resize(layer.ncols);
1413 if (layer.mask_offset != 0) {
1414 layer.mask_tiles[j].resize(layer.ncols);
1418 const QImage::Format format = layer.qimageFormat(xcf_image.header.precision);
1420 for (uint j = 0; j < layer.nrows; j++) {
1421 for (uint i = 0; i < layer.ncols; i++) {
1422 uint tile_width = (i + 1) * TILE_WIDTH <= layer.width ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
1424 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
1429 switch (layer.type) {
1432 layer.image_tiles[j][i] =
QImage(tile_width, tile_height, format);
1433 if (layer.image_tiles[j][i].isNull()) {
1436 layer.image_tiles[j][i].setColorCount(0);
1441 if (layer.image_tiles[j][i].isNull()) {
1444 layer.image_tiles[j][i].setColorCount(256);
1445 setGrayPalette(layer.image_tiles[j][i]);
1450 layer.image_tiles[j][i].setColorCount(256);
1451 if (layer.image_tiles[j][i].isNull()) {
1454 setGrayPalette(layer.image_tiles[j][i]);
1457 if (layer.alpha_tiles[j][i].isNull()) {
1460 layer.alpha_tiles[j][i].setColorCount(256);
1461 setGrayPalette(layer.alpha_tiles[j][i]);
1464 case INDEXED_GIMAGE:
1466 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1467 if (layer.image_tiles[j][i].isNull()) {
1470 setPalette(xcf_image, layer.image_tiles[j][i]);
1473 case INDEXEDA_GIMAGE:
1475 if (layer.image_tiles[j][i].isNull()) {
1478 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1479 setPalette(xcf_image, layer.image_tiles[j][i]);
1482 if (layer.alpha_tiles[j][i].isNull()) {
1485 layer.alpha_tiles[j][i].setColorCount(256);
1486 setGrayPalette(layer.alpha_tiles[j][i]);
1488 if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
1489 qCWarning(XCFPLUGIN) <<
"Selected wrong tile format" << layer.image_tiles[j][i].format() <<
"expected" << format;
1493#ifndef DISABLE_TILE_PROFILE
1494 switch (xcf_image.header.precision) {
1495 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1496 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1497 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1498 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1499 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1500 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1503 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
1504 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
1505 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
1506 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
1507 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
1508 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
1511 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
1512 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
1513 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
1514 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
1515 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
1516 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
1521 if (layer.mask_offset != 0) {
1523 layer.mask_tiles[j][i].setColorCount(256);
1524 if (layer.mask_tiles[j][i].isNull()) {
1527 setGrayPalette(layer.mask_tiles[j][i]);
1540void XCFImageFormat::setGrayPalette(
QImage &image)
1545 for (
int i = 0; i < 256; i++) {
1546 grayTable[i] = qRgb(i, i, i);
1558void XCFImageFormat::setPalette(XCFImage &xcf_image,
QImage &image)
1560 Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size());
1571void XCFImageFormat::setImageParasites(
const XCFImage &xcf_image,
QImage &image)
1573 auto&& p = xcf_image.parasites;
1574 auto keys = p.keys();
1575 for (
auto &&key : std::as_const(keys)) {
1576 auto value = p.value(key);
1577 if (value.isEmpty())
1586 if (key == QStringLiteral(
"icc-profile")) {
1598 if (key == QStringLiteral(
"gimp-comment")) {
1607 if (key == QStringLiteral(
"gimp-image-metadata")) {
1622 if (key == QStringLiteral(
"gimp-metadata")) {
1633#ifdef DISABLE_IMAGE_PROFILE
1636 switch (xcf_image.header.precision) {
1637 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1638 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1639 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1640 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1641 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1642 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1660bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
1662 QImage &image = layer.image_tiles[j][i];
1664 const uchar *tile = layer.tile;
1665 const int width = image.
width();
1666 const int height = image.
height();
1668 uchar *bits = image.
bits();
1671 if (layer.type == GRAYA_GIMAGE || layer.type == GRAY_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1672 auto bpc = bytesPerChannel(precision);
1673 for (
int y = 0; y < height; y++) {
1674 uchar *dataPtr = bits + y * bytesPerLine;
1675 uchar *alphaPtr =
nullptr;
1676 if (layer.alpha_tiles.size() > j && layer.alpha_tiles.at(j).size() > i) {
1677 QImage &alphaTile = layer.alpha_tiles[j][i];
1678 if (alphaTile.
width() >= width && alphaTile.
height() > y) {
1683#ifdef USE_FLOAT_IMAGES
1684 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1685 for (
int x = 0; x < width; x++) {
1686 auto src =
reinterpret_cast<const quint16 *
>(tile);
1687 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1689 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1690 tile +=
sizeof(quint16) * 2;
1692 tile +=
sizeof(quint16);
1696 for (
int x = 0; x < width; x++) {
1697 auto src =
reinterpret_cast<const float *
>(tile);
1698 *dataPtr++ = qFromBigEndian<float>(src[0]) * 255;
1700 *alphaPtr++ = qFromBigEndian<float>(src[1]) * 255;
1701 tile +=
sizeof(float) * 2;
1703 tile +=
sizeof(float);
1708 for (
int x = 0; x < width; x++) {
1709 auto src = (
const quint16 *)tile;
1710 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1712 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1713 tile +=
sizeof(quint16) * 2;
1715 tile +=
sizeof(quint16);
1719 }
else if (bpc == 2) {
1720#ifdef USE_FLOAT_IMAGES
1721 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1722 for (
int x = 0; x < width; x++) {
1723 auto src =
reinterpret_cast<const quint16 *
>(tile);
1724 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1726 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1727 tile +=
sizeof(QRgb);
1730 for (
int x = 0; x < width; x++) {
1731 auto src =
reinterpret_cast<const qfloat16 *
>(tile);
1732 *dataPtr++ = qFromBigEndian<qfloat16>(src[0]) * 255;
1734 *alphaPtr++ = qFromBigEndian<qfloat16>(src[1]) * 255;
1735 tile +=
sizeof(QRgb);
1739 for (
int x = 0; x < width; x++) {
1740 auto src =
reinterpret_cast<const quint16 *
>(tile);
1741 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1743 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1744 tile +=
sizeof(QRgb);
1748 for (
int x = 0; x < width; x++) {
1750 *dataPtr++ = tile[0];
1752 *alphaPtr++ = tile[1];
1753 tile +=
sizeof(QRgb);
1760 switch (image.
format()) {
1762 for (
int y = 0; y < height; y++) {
1763 uchar *dataPtr = image.
scanLine(y);
1764 for (
int x = 0; x < width * 4; x += 4, tile += 4) {
1765 dataPtr[x + 0] = tile[0];
1766 dataPtr[x + 1] = tile[1];
1767 dataPtr[x + 2] = tile[2];
1768 dataPtr[x + 3] = 255;
1773 for (
int y = 0; y < height; y++) {
1774 const size_t bpl = width * 4;
1775 memcpy(image.
scanLine(y), tile + y * bpl, bpl);
1779 for (
int y = 0; y < height; y++) {
1780 quint16 *dataPtr =
reinterpret_cast<quint16 *
>(image.
scanLine(y));
1781 const size_t bpl = width *
sizeof(
QRgba64);
1782 const quint16 *src =
reinterpret_cast<const quint16 *
>(tile + y * bpl);
1783 for (
int x = 0; x < width * 4; x += 4) {
1784 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1785 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1786 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1787 dataPtr[x + 3] = 65535;
1791#ifdef USE_FLOAT_IMAGES
1793 for (
int y = 0; y < height; y++) {
1795 const qfloat16 *src =
reinterpret_cast<const qfloat16 *
>(tile + y * width *
sizeof(QRgbaFloat16));
1796 for (
int x = 0; x < width * 4; x += 4) {
1797 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1798 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1799 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1805 static_assert(
sizeof(QRgbaFloat16) ==
sizeof(
QRgba64),
"Different sizes for float and int 16 bit pixels");
1808 for (
int y = 0; y < height; y++) {
1809 const size_t bpl = width *
sizeof(
QRgba64);
1810 qFromBigEndian<qint16>(tile + y * bpl, width * 4, image.
scanLine(y));
1813#ifdef USE_FLOAT_IMAGES
1815 for (
int y = 0; y < height; y++) {
1816 const size_t bpl = width *
sizeof(QRgbaFloat32);
1817 qFromBigEndian<qint32>(tile + y * bpl, width * 4, image.
scanLine(y));
1821 for (
int y = 0; y < height; y++) {
1822 float *dataPtr =
reinterpret_cast<float *
>(image.
scanLine(y));
1823 const float *src =
reinterpret_cast<const float *
>(tile + y * width *
sizeof(QRgbaFloat32));
1824 for (
int x = 0; x < width * 4; x += 4) {
1825 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1826 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1827 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1828 dataPtr[x + 3] = 1.f;
1834 for (
int y = 0; y < height; y++) {
1835 uchar *dataPtr = bits + y * bytesPerLine;
1836 for (
int x = 0; x < width; x++) {
1837 *dataPtr++ = tile[0];
1838 tile +=
sizeof(QRgb);
1843 qCWarning(XCFPLUGIN) <<
"Unhandled image format" << image.
format() <<
"and/or layer type" << layer.type;
1858bool XCFImageFormat::loadHierarchy(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
1864 xcf_io >> width >> height >> bpp;
1865 const qint64 offset = readOffsetPtr(xcf_io);
1867 qCDebug(XCFPLUGIN) <<
"width" << width <<
"height" << height <<
"bpp" << bpp <<
"offset" << offset;
1870 qCDebug(XCFPLUGIN) <<
"XCF: negative hierarchy offset";
1874 const bool isMask = layer.assignBytes == assignMaskBytes;
1877 switch (layer.type) {
1879 if (bpp != 3 * bytesPerChannel(precision)) {
1880 qCDebug(XCFPLUGIN) <<
"Found layer of type RGB but with bpp != 3" << bpp;
1888 if (bpp != 4 * bytesPerChannel(precision)) {
1889 qCDebug(XCFPLUGIN) <<
"Found layer of type RGBA but with bpp != 4, got" << bpp <<
"bpp";
1897 if (bpp != 1 * bytesPerChannel(precision)) {
1898 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray but with bpp != 1" << bpp;
1903 if (bpp != 2 * bytesPerChannel(precision)) {
1904 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray+Alpha but with bpp != 2" << bpp;
1911 case INDEXED_GIMAGE:
1912 if (bpp != 1 * bytesPerChannel(precision)) {
1913 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed but with bpp != 1" << bpp;
1917 case INDEXEDA_GIMAGE:
1918 if (bpp != 2 * bytesPerChannel(precision)) {
1919 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed+Alpha but with bpp != 2" << bpp;
1928 if (bpp > 4 * bytesPerChannel(precision)) {
1929 qCDebug(XCFPLUGIN) <<
"bpp is" << bpp <<
"We don't support layers with bpp > 4";
1942 qCDebug(XCFPLUGIN) <<
"XCF: read failure on layer " << layer.name <<
" level offsets";
1945 }
while (junk != 0);
1947 qint64 saved_pos = xcf_io.
device()->
pos();
1950 if (!loadLevel(xcf_io, layer, bpp, precision)) {
1958template<
typename SourceFormat>
1959static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
1961 SourceFormat *source = (SourceFormat *)(input);
1962 for (quint64 offset = 0; offset < outputSize; offset++) {
1963 (
reinterpret_cast<uint16_t *
>(output))[offset] = qToBigEndian(quint16(qBound(0., qFromBigEndian<SourceFormat>(source[offset]) * 65535. + 0.5, 65535.)));
1976bool XCFImageFormat::loadLevel(
QDataStream &xcf_io, Layer &layer, qint32 bpp,
const GimpPrecision precision)
1978 auto bpc = bytesPerChannel(precision);
1979 if ((bpc == 0) || (bpp % bpc)) {
1980 qCDebug(XCFPLUGIN) <<
"XCF: the stream seems corrupted";
1987 xcf_io >> width >> height;
1988 qint64 offset = readOffsetPtr(xcf_io);
1991 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
1998 for (uint j = 0; j < layer.nrows; j++) {
1999 for (uint i = 0; i < layer.ncols; i++) {
2001 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
2009 bool needConvert =
true;
2010 switch (precision) {
2011#ifdef USE_FLOAT_IMAGES
2012 case GIMP_PRECISION_HALF_LINEAR:
2013 case GIMP_PRECISION_HALF_NON_LINEAR:
2014 case GIMP_PRECISION_HALF_PERCEPTUAL:
2015 case GIMP_PRECISION_FLOAT_LINEAR:
2016 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2017 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2019 case GIMP_PRECISION_U8_LINEAR:
2020 case GIMP_PRECISION_U8_NON_LINEAR:
2021 case GIMP_PRECISION_U8_PERCEPTUAL:
2022 case GIMP_PRECISION_U16_LINEAR:
2023 case GIMP_PRECISION_U16_NON_LINEAR:
2024 case GIMP_PRECISION_U16_PERCEPTUAL:
2025 needConvert =
false;
2031 const uint blockSize = TILE_WIDTH * TILE_HEIGHT * bpp * 1.5;
2035 buffer.
resize(blockSize * (bpp == 2 ? 2 : 1));
2037 for (uint j = 0; j < layer.nrows; j++) {
2038 for (uint i = 0; i < layer.ncols; i++) {
2040 qCDebug(XCFPLUGIN) <<
"XCF: incorrect number of tiles in layer " << layer.name;
2044 qint64 saved_pos = xcf_io.
device()->
pos();
2045 qint64 offset2 = readOffsetPtr(xcf_io);
2048 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2054 offset2 = offset + blockSize;
2058 qint64 bytesParsed = 0;
2060 switch (layer.compression) {
2061 case COMPRESS_NONE: {
2063 qCDebug(XCFPLUGIN) <<
"Component reading not supported yet";
2066 const int data_size = bpp * TILE_WIDTH * TILE_HEIGHT;
2067 if (data_size >
int(blockSize)) {
2068 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2071 int dataRead = xcf_io.
readRawData(
reinterpret_cast<char *
>(layer.tile), data_size);
2072 if (dataRead < data_size) {
2073 qCDebug(XCFPLUGIN) <<
"short read, expected" << data_size <<
"got" << dataRead;
2076 bytesParsed = dataRead;
2079 case COMPRESS_RLE: {
2080 int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
2081 const uint data_size = size * bpp;
2083 if (data_size >=
unsigned(buffer.
size())) {
2084 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" << buffer.
size() <<
"but need" << data_size;
2088 if (data_size >
sizeof(layer.tile)) {
2089 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2092 if (blockSize >
sizeof(layer.tile)) {
2093 qCWarning(XCFPLUGIN) <<
"Too small tiles" <<
sizeof(layer.tile) <<
"this image requires" << blockSize <<
sizeof(
QRgba64) << bpp;
2097 if (!loadTileRLE(xcf_io, needConvert ? buffer.
data() : layer.tile, size, offset2 - offset, bpp, &bytesParsed)) {
2098 qCDebug(XCFPLUGIN) <<
"Failed to read RLE";
2104 qCDebug(XCFPLUGIN) <<
"Unhandled compression" << layer.compression;
2109 if (bytesParsed > buffer.
size()) {
2110 qCDebug(XCFPLUGIN) <<
"Invalid number of bytes parsed" << bytesParsed << buffer.
size();
2114 switch (precision) {
2115 case GIMP_PRECISION_U32_LINEAR:
2116 case GIMP_PRECISION_U32_NON_LINEAR:
2117 case GIMP_PRECISION_U32_PERCEPTUAL: {
2118 quint32 *source =
reinterpret_cast<quint32 *
>(buffer.
data());
2119 for (quint64 offset = 0, len = buffer.
size() /
sizeof(quint32); offset < len; ++offset) {
2120 (
reinterpret_cast<quint16 *
>(layer.tile))[offset] = qToBigEndian<quint16>(qFromBigEndian(source[offset]) / 65537);
2124#ifndef USE_FLOAT_IMAGES
2125 case GIMP_PRECISION_HALF_LINEAR:
2126 case GIMP_PRECISION_HALF_NON_LINEAR:
2127 case GIMP_PRECISION_HALF_PERCEPTUAL:
2128 convertFloatTo16Bit<qfloat16>(layer.tile, buffer.
size() /
sizeof(
qfloat16), buffer.
data());
2130 case GIMP_PRECISION_FLOAT_LINEAR:
2131 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2132 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2133 convertFloatTo16Bit<float>(layer.tile, buffer.
size() /
sizeof(
float), buffer.
data());
2135 case GIMP_PRECISION_DOUBLE_LINEAR:
2136 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2137 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2138 convertFloatTo16Bit<double>(layer.tile, buffer.
size() /
sizeof(
double), buffer.
data());
2141 case GIMP_PRECISION_DOUBLE_LINEAR:
2142 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2143 case GIMP_PRECISION_DOUBLE_PERCEPTUAL: {
2144 double *source =
reinterpret_cast<double *
>(buffer.
data());
2145 for (quint64 offset = 0, len = buffer.
size() /
sizeof(
double); offset < len; ++offset) {
2146 (
reinterpret_cast<float *
>(layer.tile))[offset] = qToBigEndian<float>(
float(qFromBigEndian(source[offset])));
2152 qCWarning(XCFPLUGIN) <<
"Unsupported precision" << precision;
2160 if (!layer.assignBytes(layer, i, j, precision)) {
2165 offset = readOffsetPtr(xcf_io);
2168 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2183bool XCFImageFormat::loadMask(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
2189 xcf_io >> width >> height >>
name;
2193 if (!loadChannelProperties(xcf_io, layer)) {
2197 const qint64 hierarchy_offset = readOffsetPtr(xcf_io);
2199 if (hierarchy_offset < 0) {
2200 qCDebug(XCFPLUGIN) <<
"XCF: negative mask hierarchy_offset";
2205 layer.assignBytes = assignMaskBytes;
2207 if (!loadHierarchy(xcf_io, layer, precision)) {
2237bool XCFImageFormat::loadTileRLE(
QDataStream &xcf_io, uchar *tile,
int image_size,
int data_length, qint32 bpp, qint64 *bytesParsed)
2243 uchar *xcfdatalimit;
2245 int step =
sizeof(QRgb);
2251 step =
sizeof(QRgb);
2255 step =
sizeof(QRgb) * 2;
2259 step =
sizeof(QRgb) * 4;
2262 qCDebug(XCFPLUGIN) <<
"XCF: unhandled bit depth" << bpp;
2266 if (data_length < 0 || data_length >
int(TILE_WIDTH * TILE_HEIGHT * step * 1.5)) {
2267 qCDebug(XCFPLUGIN) <<
"XCF: invalid tile data length" << data_length;
2271 xcfdata = xcfodata =
new uchar[data_length];
2273 const int dataRead = xcf_io.
readRawData((
char *)xcfdata, data_length);
2274 if (dataRead <= 0) {
2276 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile" << dataRead;
2280 if (dataRead < data_length) {
2281 memset(&xcfdata[dataRead], 0, data_length - dataRead);
2286 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile";
2290 xcfdatalimit = &xcfodata[data_length - 1];
2292 for (
int i = 0; i < bpp; ++i) {
2295 int size = image_size;
2298 if (xcfdata > xcfdatalimit) {
2302 uchar val = *xcfdata++;
2305 if (length >= 128) {
2306 length = 255 - (length - 1);
2307 if (length == 128) {
2308 if (xcfdata >= xcfdatalimit) {
2312 length = (*xcfdata << 8) + xcfdata[1];
2323 if (&xcfdata[length - 1] > xcfdatalimit) {
2327 while (length-- > 0) {
2333 if (length == 128) {
2334 if (xcfdata >= xcfdatalimit) {
2338 length = (*xcfdata << 8) + xcfdata[1];
2348 if (xcfdata > xcfdatalimit) {
2352 qintptr totalLength = qintptr(data - tile) + length * step;
2353 if (totalLength >= image_size * step * 1.5) {
2354 qCDebug(XCFPLUGIN) <<
"Ran out of space when trying to unpack image, over:" << totalLength - image_size << totalLength << image_size
2361 while (length-- > 0) {
2368 *bytesParsed = qintptr(data - tile);
2375 qCDebug(XCFPLUGIN) <<
"The run length encoding could not be decoded properly";
2387bool XCFImageFormat::loadChannelProperties(
QDataStream &xcf_io, Layer &layer)
2394 if (!loadProperty(xcf_io, type, bytes, rawType)) {
2395 qCDebug(XCFPLUGIN) <<
"XCF: error loading channel properties";
2406 property >> layer.mask_channel.opacity;
2407 layer.mask_channel.opacity = std::min(layer.mask_channel.opacity, 255u);
2410 case PROP_FLOAT_OPACITY:
2413 if (bytes.
size() == 4) {
2414 layer.mask_channel.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
2416 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
2421 property >> layer.mask_channel.visible;
2424 case PROP_SHOW_MASKED:
2425 property >> layer.mask_channel.show_masked;
2429 property >> layer.mask_channel.red >> layer.mask_channel.green >> layer.mask_channel.blue;
2432 case PROP_FLOAT_COLOR:
2433 property >> layer.mask_channel.redF >> layer.mask_channel.greenF >> layer.mask_channel.blueF;
2437 property >> layer.mask_channel.tattoo;
2445 case PROP_COLOR_TAG:
2449 case PROP_LOCK_CONTENT:
2450 case PROP_LOCK_POSITION:
2454 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented channel property " <<
type <<
"(" << rawType <<
")"
2455 <<
", size " << bytes.
size();
2467bool XCFImageFormat::assignMaskBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
2469 QImage &image = layer.mask_tiles[j][i];
2470 if (image.
depth() != 8) {
2471 qCWarning(XCFPLUGIN) <<
"invalid bytes per pixel, we only do 8 bit masks" << image.
depth();
2475 uchar *tile = layer.tile;
2476 const int width = image.
width();
2477 const int height = image.
height();
2479 uchar *bits = image.
bits();
2480 auto bpc = bytesPerChannel(precision);
2485 for (
int y = 0; y < height; y++) {
2486 uchar *dataPtr = bits + y * bytesPerLine;
2487#ifdef USE_FLOAT_IMAGES
2489 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2490 for (
int x = 0; x < width; x++) {
2491 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2492 tile +=
sizeof(quint16);
2495 for (
int x = 0; x < width; x++) {
2496 *dataPtr++ = qFromBigEndian<float>(*
reinterpret_cast<const float *
>(tile)) * 255;
2497 tile +=
sizeof(QRgb);
2500 }
else if (bpc == 2) {
2502 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2503 for (
int x = 0; x < width; x++) {
2504 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2505 tile +=
sizeof(QRgb);
2508 for (
int x = 0; x < width; x++) {
2509 *dataPtr++ = qFromBigEndian<qfloat16>(*
reinterpret_cast<const qfloat16 *
>(tile)) * 255;
2510 tile +=
sizeof(QRgb);
2516 for (
int x = 0; x < width; x++) {
2517 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2518 tile +=
sizeof(QRgb);
2520 }
else if (bpc == 4) {
2521 for (
int x = 0; x < width; x++) {
2522 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2523 tile +=
sizeof(quint16);
2528 for (
int x = 0; x < width; x++) {
2529 *dataPtr++ = tile[0];
2530 tile +=
sizeof(QRgb);
2566bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
2569 Layer &layer(xcf_image.layer);
2570 QImage &image(xcf_image.image);
2572 switch (layer.type) {
2574 if (layer.opacity == OPAQUE_OPACITY) {
2580 setGrayPalette(image);
2588 image = imageAlloc(xcf_image.header.width, xcf_image.header.height, xcf_image.qimageFormat());
2599 case INDEXED_GIMAGE:
2612 if (xcf_image.num_colors <= 2) {
2619 setPalette(xcf_image, image);
2620 }
else if (xcf_image.num_colors <= 256) {
2627 setPalette(xcf_image, image);
2631 case INDEXEDA_GIMAGE:
2632 if (xcf_image.num_colors == 1) {
2634 xcf_image.num_colors++;
2635 xcf_image.palette.resize(xcf_image.num_colors);
2636 xcf_image.palette[1] = xcf_image.palette[0];
2637 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2645 setPalette(xcf_image, image);
2646 }
else if (xcf_image.num_colors < 256) {
2648 xcf_image.num_colors++;
2649 xcf_image.palette.resize(xcf_image.num_colors);
2650 for (
int c = xcf_image.num_colors - 1; c >= 1; c--) {
2651 xcf_image.palette[c] = xcf_image.palette[c - 1];
2654 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2661 setPalette(xcf_image, image);
2670 image.
fill(qRgba(255, 255, 255, 0));
2674 if (image.
format() != xcf_image.qimageFormat()) {
2675 qCWarning(XCFPLUGIN) <<
"Selected wrong format:" << image.
format() <<
"expected" << layer.qimageFormat(xcf_image.header.precision);
2681#ifndef DISABLE_IMAGE_PROFILE
2682 switch (xcf_image.header.precision) {
2683 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
2684 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
2685 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
2686 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
2687 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
2688 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
2691 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
2692 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
2693 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
2694 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
2695 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
2696 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
2699 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
2700 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
2701 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2702 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
2703 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
2704 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
2710 if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
2711 const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
2712 if (dpmx >
float(std::numeric_limits<int>::max())) {
2715 const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
2716 if (dpmy >
float(std::numeric_limits<int>::max())) {
2730void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
2732 Layer &layer(xcf_image.layer);
2733 QImage &image(xcf_image.image);
2734 PixelCopyOperation
copy =
nullptr;
2736 switch (layer.type) {
2739 copy = copyRGBToRGB;
2742 if (layer.opacity == OPAQUE_OPACITY) {
2743 copy = copyGrayToGray;
2745 copy = copyGrayToRGB;
2749 copy = copyGrayAToRGB;
2751 case INDEXED_GIMAGE:
2752 copy = copyIndexedToIndexed;
2754 case INDEXEDA_GIMAGE:
2755 if (xcf_image.image.depth() <= 8) {
2756 copy = copyIndexedAToIndexed;
2758 copy = copyIndexedAToRGB;
2768 for (uint j = 0; j < layer.nrows; j++) {
2769 qint32 y = qint32(j * TILE_HEIGHT);
2771 for (uint i = 0; i < layer.ncols; i++) {
2772 qint32 x = qint32(i * TILE_WIDTH);
2779 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
2780 if (!random_table_initialized) {
2781 initializeRandomTable();
2782 random_table_initialized =
true;
2784 if (layer.type == RGBA_GIMAGE) {
2785 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
2788 else if (layer.type == GRAYA_GIMAGE) {
2789 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
2794 if (copy == copyRGBToRGB && layer.apply_mask != 1) {
2796 painter.setOpacity(layer.opacity / 255.0);
2798 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
2799 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
2800 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
2805 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
2806 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
2807 int m = x + k + layer.x_offset;
2808 int n = y + l + layer.y_offset;
2810 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
2814 (*copy)(layer, i, j, k, l, image, m, n);
2834void XCFImageFormat::copyRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2836 if (image.
depth() == 32) {
2837 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2838 uchar src_a = layer.opacity;
2840 if (layer.type == RGBA_GIMAGE) {
2841 src_a = INT_MULT(src_a, qAlpha(src));
2846 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2847 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2850 image.
setPixel(m, n, qRgba(src, src_a));
2851 }
else if (image.
depth() == 64) {
2852 QRgba64 src = layer.image_tiles[j][i].pixelColor(k, l).rgba64();
2853 quint16 src_a = layer.opacity;
2855 if (layer.type == RGBA_GIMAGE) {
2856 src_a = INT_MULT(src_a, qAlpha(src));
2861 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2862 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2881void XCFImageFormat::copyGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2883 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2900void XCFImageFormat::copyGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2902 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2903 uchar src_a = layer.opacity;
2904 image.
setPixel(m, n, qRgba(src, src_a));
2920void XCFImageFormat::copyGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2922 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2923 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2924 src_a = INT_MULT(src_a, layer.opacity);
2928 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2929 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2932 image.
setPixel(m, n, qRgba(src, src_a));
2946void XCFImageFormat::copyIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2948 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2963void XCFImageFormat::copyIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2965 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
2966 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2967 src_a = INT_MULT(src_a, layer.opacity);
2969 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2970 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2995void XCFImageFormat::copyIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2997 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2998 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2999 src_a = INT_MULT(src_a, layer.opacity);
3002 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3003 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3010 src_a = OPAQUE_OPACITY;
3013 image.
setPixel(m, n, qRgba(src, src_a));
3020void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
3022 Layer &layer(xcf_image.layer);
3023 QImage &image(xcf_image.image);
3025 PixelMergeOperation
merge =
nullptr;
3027 if (!layer.opacity) {
3031 if (layer.blendSpace == XCFImageFormat::AutoColorSpace) {
3032 qCDebug(XCFPLUGIN) <<
"Auto blend space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3033 layer.blendSpace = XCFImageFormat::RgbLinearSpace;
3036 if (layer.blendSpace != XCFImageFormat::RgbLinearSpace) {
3037 qCDebug(XCFPLUGIN) <<
"Unimplemented blend color space" << layer.blendSpace;
3039 qCDebug(XCFPLUGIN) <<
"Blend color space" << layer.blendSpace;
3041 if (layer.compositeSpace == XCFImageFormat::AutoColorSpace) {
3042 qCDebug(XCFPLUGIN) <<
"Auto composite space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3043 layer.compositeSpace = XCFImageFormat::RgbLinearSpace;
3046 if (layer.compositeSpace != XCFImageFormat::RgbLinearSpace) {
3047 qCDebug(XCFPLUGIN) <<
"Unimplemented composite color space" << layer.compositeSpace;
3049 if (layer.compositeMode != XCFImageFormat::CompositeUnion) {
3050 qCDebug(XCFPLUGIN) <<
"Unhandled composite mode" << layer.compositeMode;
3053 switch (layer.type) {
3056 merge = mergeRGBToRGB;
3059 if (layer.opacity == OPAQUE_OPACITY && xcf_image.image.depth() <= 8) {
3060 merge = mergeGrayToGray;
3062 merge = mergeGrayToRGB;
3066 if (xcf_image.image.depth() <= 8) {
3067 merge = mergeGrayAToGray;
3069 merge = mergeGrayAToRGB;
3072 case INDEXED_GIMAGE:
3073 merge = mergeIndexedToIndexed;
3075 case INDEXEDA_GIMAGE:
3076 if (xcf_image.image.depth() <= 8) {
3077 merge = mergeIndexedAToIndexed;
3079 merge = mergeIndexedAToRGB;
3087 if (merge == mergeRGBToRGB && layer.apply_mask != 1) {
3088 int painterMode = -1;
3089 switch (layer.mode) {
3090 case GIMP_LAYER_MODE_NORMAL:
3091 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3094 case GIMP_LAYER_MODE_MULTIPLY:
3095 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3098 case GIMP_LAYER_MODE_SCREEN:
3099 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3102 case GIMP_LAYER_MODE_OVERLAY:
3103 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3106 case GIMP_LAYER_MODE_DIFFERENCE:
3107 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3110 case GIMP_LAYER_MODE_DARKEN_ONLY:
3111 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3114 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3115 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3118 case GIMP_LAYER_MODE_DODGE:
3119 case GIMP_LAYER_MODE_DODGE_LEGACY:
3122 case GIMP_LAYER_MODE_BURN:
3123 case GIMP_LAYER_MODE_BURN_LEGACY:
3126 case GIMP_LAYER_MODE_HARDLIGHT:
3127 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
3130 case GIMP_LAYER_MODE_SOFTLIGHT:
3131 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
3134 case GIMP_LAYER_MODE_ADDITION:
3135 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3138 case GIMP_LAYER_MODE_EXCLUSION:
3143 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3144 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
3145 case GIMP_LAYER_MODE_GRAIN_MERGE:
3146 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
3147 case GIMP_LAYER_MODE_COLOR_ERASE:
3148 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
3149 case GIMP_LAYER_MODE_LCH_HUE:
3150 case GIMP_LAYER_MODE_LCH_CHROMA:
3151 case GIMP_LAYER_MODE_LCH_COLOR:
3152 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
3153 case GIMP_LAYER_MODE_BEHIND:
3154 case GIMP_LAYER_MODE_BEHIND_LEGACY:
3155 case GIMP_LAYER_MODE_SUBTRACT:
3156 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3157 case GIMP_LAYER_MODE_HSV_HUE:
3158 case GIMP_LAYER_MODE_HSV_SATURATION:
3159 case GIMP_LAYER_MODE_HSL_COLOR:
3160 case GIMP_LAYER_MODE_HSV_VALUE:
3161 case GIMP_LAYER_MODE_DIVIDE:
3162 case GIMP_LAYER_MODE_VIVID_LIGHT:
3163 case GIMP_LAYER_MODE_PIN_LIGHT:
3164 case GIMP_LAYER_MODE_LINEAR_LIGHT:
3165 case GIMP_LAYER_MODE_HARD_MIX:
3166 case GIMP_LAYER_MODE_LINEAR_BURN:
3167 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
3168 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
3169 case GIMP_LAYER_MODE_LUMINANCE:
3170 case GIMP_LAYER_MODE_ERASE:
3171 case GIMP_LAYER_MODE_MERGE:
3172 case GIMP_LAYER_MODE_SPLIT:
3173 case GIMP_LAYER_MODE_PASS_THROUGH:
3174 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
3175 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
3176 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
3177 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
3178 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3179 qCDebug(XCFPLUGIN) <<
"No QPainter equivalent to" << layer.mode;
3183 case GIMP_LAYER_MODE_DISSOLVE:
3184 case GIMP_LAYER_MODE_COUNT:
3188 if (painterMode != -1) {
3190 painter.setOpacity(layer.opacity / 255.0);
3192 qCDebug(XCFPLUGIN) <<
"Using QPainter for mode" << layer.mode;
3194 for (uint j = 0; j < layer.nrows; j++) {
3195 qint32 y = qint32(j * TILE_HEIGHT);
3197 for (uint i = 0; i < layer.ncols; i++) {
3198 qint32 x = qint32(i * TILE_WIDTH);
3200 QImage &tile = layer.image_tiles[j][i];
3201 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3202 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3203 painter.drawImage(x + layer.x_offset, y + layer.y_offset, tile);
3212#ifndef DISABLE_IMAGE_PROFILE_CONV
3214 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3218 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3223 for (uint j = 0; j < layer.nrows; j++) {
3224 qint32 y = qint32(j * TILE_HEIGHT);
3226 for (uint i = 0; i < layer.ncols; i++) {
3227 qint32 x = qint32(i * TILE_WIDTH);
3234 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
3235 if (!random_table_initialized) {
3236 initializeRandomTable();
3237 random_table_initialized =
true;
3239 if (layer.type == RGBA_GIMAGE) {
3240 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
3243 else if (layer.type == GRAYA_GIMAGE) {
3244 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
3249 if (merge == mergeRGBToRGB && layer.apply_mask != 1 && layer.mode == GIMP_LAYER_MODE_NORMAL_LEGACY) {
3251 painter.setOpacity(layer.opacity / 255.0);
3253 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3254 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3255 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
3260#ifndef DISABLE_TILE_PROFILE_CONV
3261 QImage &tile = layer.image_tiles[j][i];
3270 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
3271 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
3272 int m = x + k + layer.x_offset;
3273 int n = y + l + layer.y_offset;
3275 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
3279 if (!(*merge)(layer, i, j, k, l, image, m, n)) {
3301bool XCFImageFormat::mergeRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3303 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3304 QRgb dst = image.
pixel(m, n);
3306 uchar src_r = qRed(src);
3307 uchar src_g = qGreen(src);
3308 uchar src_b = qBlue(src);
3309 uchar src_a = qAlpha(src);
3311 uchar dst_r = qRed(dst);
3312 uchar dst_g = qGreen(dst);
3313 uchar dst_b = qBlue(dst);
3314 uchar dst_a = qAlpha(dst);
3320 switch (layer.mode) {
3321 case GIMP_LAYER_MODE_NORMAL:
3322 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3324 case GIMP_LAYER_MODE_MULTIPLY:
3325 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3326 src_r = INT_MULT(src_r, dst_r);
3327 src_g = INT_MULT(src_g, dst_g);
3328 src_b = INT_MULT(src_b, dst_b);
3329 src_a = qMin(src_a, dst_a);
3331 case GIMP_LAYER_MODE_DIVIDE:
3332 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3333 src_r = qMin((dst_r * 256) / (1 + src_r), 255);
3334 src_g = qMin((dst_g * 256) / (1 + src_g), 255);
3335 src_b = qMin((dst_b * 256) / (1 + src_b), 255);
3336 src_a = qMin(src_a, dst_a);
3338 case GIMP_LAYER_MODE_SCREEN:
3339 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3340 src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
3341 src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
3342 src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
3343 src_a = qMin(src_a, dst_a);
3345 case GIMP_LAYER_MODE_OVERLAY:
3346 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3347 src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
3348 src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
3349 src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
3350 src_a = qMin(src_a, dst_a);
3352 case GIMP_LAYER_MODE_DIFFERENCE:
3353 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3354 src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
3355 src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
3356 src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
3357 src_a = qMin(src_a, dst_a);
3359 case GIMP_LAYER_MODE_ADDITION:
3360 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3361 src_r = add_lut(dst_r, src_r);
3362 src_g = add_lut(dst_g, src_g);
3363 src_b = add_lut(dst_b, src_b);
3364 src_a = qMin(src_a, dst_a);
3366 case GIMP_LAYER_MODE_SUBTRACT:
3367 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3368 src_r = dst_r > src_r ? dst_r - src_r : 0;
3369 src_g = dst_g > src_g ? dst_g - src_g : 0;
3370 src_b = dst_b > src_b ? dst_b - src_b : 0;
3371 src_a = qMin(src_a, dst_a);
3373 case GIMP_LAYER_MODE_DARKEN_ONLY:
3374 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3375 src_r = dst_r < src_r ? dst_r : src_r;
3376 src_g = dst_g < src_g ? dst_g : src_g;
3377 src_b = dst_b < src_b ? dst_b : src_b;
3378 src_a = qMin(src_a, dst_a);
3380 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3381 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3382 src_r = dst_r < src_r ? src_r : dst_r;
3383 src_g = dst_g < src_g ? src_g : dst_g;
3384 src_b = dst_b < src_b ? src_b : dst_b;
3385 src_a = qMin(src_a, dst_a);
3387 case GIMP_LAYER_MODE_HSV_HUE:
3388 case GIMP_LAYER_MODE_HSV_HUE_LEGACY: {
3389 uchar new_r = dst_r;
3390 uchar new_g = dst_g;
3391 uchar new_b = dst_b;
3393 RGBTOHSV(src_r, src_g, src_b);
3394 RGBTOHSV(new_r, new_g, new_b);
3398 HSVTORGB(new_r, new_g, new_b);
3403 src_a = qMin(src_a, dst_a);
3405 case GIMP_LAYER_MODE_HSV_SATURATION:
3406 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: {
3407 uchar new_r = dst_r;
3408 uchar new_g = dst_g;
3409 uchar new_b = dst_b;
3411 RGBTOHSV(src_r, src_g, src_b);
3412 RGBTOHSV(new_r, new_g, new_b);
3416 HSVTORGB(new_r, new_g, new_b);
3421 src_a = qMin(src_a, dst_a);
3423 case GIMP_LAYER_MODE_HSV_VALUE:
3424 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY: {
3425 uchar new_r = dst_r;
3426 uchar new_g = dst_g;
3427 uchar new_b = dst_b;
3429 RGBTOHSV(src_r, src_g, src_b);
3430 RGBTOHSV(new_r, new_g, new_b);
3434 HSVTORGB(new_r, new_g, new_b);
3439 src_a = qMin(src_a, dst_a);
3441 case GIMP_LAYER_MODE_HSL_COLOR:
3442 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: {
3443 uchar new_r = dst_r;
3444 uchar new_g = dst_g;
3445 uchar new_b = dst_b;
3447 RGBTOHLS(src_r, src_g, src_b);
3448 RGBTOHLS(new_r, new_g, new_b);
3453 HLSTORGB(new_r, new_g, new_b);
3458 src_a = qMin(src_a, dst_a);
3460 case GIMP_LAYER_MODE_DODGE:
3461 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3466 src_r = (uchar)qMin(tmp, 255u);
3470 src_g = (uchar)qMin(tmp, 255u);
3474 src_b = (uchar)qMin(tmp, 255u);
3476 src_a = qMin(src_a, dst_a);
3478 case GIMP_LAYER_MODE_BURN:
3479 case GIMP_LAYER_MODE_BURN_LEGACY: {
3482 tmp = (255 - dst_r) << 8;
3484 src_r = (uchar)qMin(tmp, 255u);
3485 src_r = 255 - src_r;
3487 tmp = (255 - dst_g) << 8;
3489 src_g = (uchar)qMin(tmp, 255u);
3490 src_g = 255 - src_g;
3492 tmp = (255 - dst_b) << 8;
3494 src_b = (uchar)qMin(tmp, 255u);
3495 src_b = 255 - src_b;
3497 src_a = qMin(src_a, dst_a);
3499 case GIMP_LAYER_MODE_HARDLIGHT:
3500 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3503 tmp = ((int)255 - dst_r) * ((
int)255 - ((src_r - 128) << 1));
3504 src_r = (uchar)qMin(255 - (tmp >> 8), 255u);
3506 tmp = (int)dst_r * ((
int)src_r << 1);
3507 src_r = (uchar)qMin(tmp >> 8, 255u);
3511 tmp = ((int)255 - dst_g) * ((
int)255 - ((src_g - 128) << 1));
3512 src_g = (uchar)qMin(255 - (tmp >> 8), 255u);
3514 tmp = (int)dst_g * ((
int)src_g << 1);
3515 src_g = (uchar)qMin(tmp >> 8, 255u);
3519 tmp = ((int)255 - dst_b) * ((
int)255 - ((src_b - 128) << 1));
3520 src_b = (uchar)qMin(255 - (tmp >> 8), 255u);
3522 tmp = (int)dst_b * ((
int)src_b << 1);
3523 src_b = (uchar)qMin(tmp >> 8, 255u);
3525 src_a = qMin(src_a, dst_a);
3527 case GIMP_LAYER_MODE_SOFTLIGHT:
3528 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3532 tmpM = INT_MULT(dst_r, src_r);
3533 tmpS = 255 - INT_MULT((255 - dst_r), (255 - src_r));
3534 src_r = INT_MULT((255 - dst_r), tmpM) + INT_MULT(dst_r, tmpS);
3536 tmpM = INT_MULT(dst_g, src_g);
3537 tmpS = 255 - INT_MULT((255 - dst_g), (255 - src_g));
3538 src_g = INT_MULT((255 - dst_g), tmpM) + INT_MULT(dst_g, tmpS);
3540 tmpM = INT_MULT(dst_b, src_b);
3541 tmpS = 255 - INT_MULT((255 - dst_b), (255 - src_b));
3542 src_b = INT_MULT((255 - dst_b), tmpM) + INT_MULT(dst_b, tmpS);
3544 src_a = qMin(src_a, dst_a);
3546 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3547 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3550 tmp = dst_r - src_r + 128;
3551 tmp = qMin(tmp, 255);
3555 tmp = dst_g - src_g + 128;
3556 tmp = qMin(tmp, 255);
3560 tmp = dst_b - src_b + 128;
3561 tmp = qMin(tmp, 255);
3565 src_a = qMin(src_a, dst_a);
3567 case GIMP_LAYER_MODE_GRAIN_MERGE:
3568 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3571 tmp = dst_r + src_r - 128;
3572 tmp = qMin(tmp, 255);
3576 tmp = dst_g + src_g - 128;
3577 tmp = qMin(tmp, 255);
3581 tmp = dst_b + src_b - 128;
3582 tmp = qMin(tmp, 255);
3586 src_a = qMin(src_a, dst_a);
3588 case GIMP_LAYER_MODE_LINEAR_LIGHT: {
3590 src_r = qBound(0, dst_r + 2 * src_r - 255, 255);
3592 src_r = qBound(0, dst_r + 2 * (src_r - 128), 255);
3595 src_g = qBound(0, dst_g + 2 * src_g - 255, 255);
3597 src_g = qBound(0, dst_g + 2 * (src_g - 127), 255);
3600 src_b = qBound(0, dst_b + 2 * src_b - 255, 255);
3602 src_b = qBound(0, dst_b + 2 * (src_b - 127), 255);
3605 case GIMP_LAYER_MODE_VIVID_LIGHT: {
3608 A[0] = src_r / 255.;
3609 A[1] = src_g / 255.;
3610 A[2] = src_b / 255.;
3612 B[0] = dst_r / 255.;
3613 B[1] = dst_g / 255.;
3614 B[2] = dst_b / 255.;
3616 for (
int i = 0; i < 3; i++) {
3619 C[i] = 1.f - (1.f - B[i]) / (2.f * A[i]);
3623 C[i] = B[i] / (2.f * (1.f - A[i]));
3627 src_r = qBound(0.f, C[0] * 255.f, 255.f);
3628 src_g = qBound(0.f, C[1] * 255.f, 255.f);
3629 src_b = qBound(0.f, C[2] * 255.f, 255.f);
3632 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3636 src_a = INT_MULT(src_a, layer.opacity);
3640 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3641 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3648 new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3650 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3651 float dst_ratio = 1.0 - src_ratio;
3653 new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
3654 new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
3655 new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
3657 if (!modeAffectsSourceAlpha(layer.mode)) {
3661 image.
setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
3676bool XCFImageFormat::mergeGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3678 int src = layer.image_tiles[j][i].pixelIndex(k, l);
3694bool XCFImageFormat::mergeGrayAToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3696 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3699 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3705 switch (layer.mode) {
3706 case GIMP_LAYER_MODE_MULTIPLY:
3707 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3708 src = INT_MULT(src, dst);
3710 case GIMP_LAYER_MODE_DIVIDE:
3711 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3712 src = qMin((dst * 256) / (1 + src), 255);
3714 case GIMP_LAYER_MODE_SCREEN:
3715 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3716 src = 255 - INT_MULT(255 - dst, 255 - src);
3718 case GIMP_LAYER_MODE_OVERLAY:
3719 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3720 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3722 case GIMP_LAYER_MODE_DIFFERENCE:
3723 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3724 src = dst > src ? dst - src : src - dst;
3726 case GIMP_LAYER_MODE_ADDITION:
3727 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3728 src = add_lut(dst, src);
3730 case GIMP_LAYER_MODE_SUBTRACT:
3731 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3732 src = dst > src ? dst - src : 0;
3734 case GIMP_LAYER_MODE_DARKEN_ONLY:
3735 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3736 src = dst < src ? dst : src;
3738 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3739 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3740 src = dst < src ? src : dst;
3742 case GIMP_LAYER_MODE_DODGE:
3743 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3744 uint tmp = dst << 8;
3746 src = (uchar)qMin(tmp, 255u);
3748 case GIMP_LAYER_MODE_BURN:
3749 case GIMP_LAYER_MODE_BURN_LEGACY: {
3750 uint tmp = (255 - dst) << 8;
3752 src = (uchar)qMin(tmp, 255u);
3755 case GIMP_LAYER_MODE_HARDLIGHT:
3756 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3759 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3760 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3762 tmp = (int)dst * ((
int)src << 1);
3763 src = (uchar)qMin(tmp >> 8, 255u);
3766 case GIMP_LAYER_MODE_SOFTLIGHT:
3767 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3771 tmpM = INT_MULT(dst, src);
3772 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3773 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3776 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3777 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3780 tmp = dst - src + 128;
3781 tmp = qMin(tmp, 255);
3786 case GIMP_LAYER_MODE_GRAIN_MERGE:
3787 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3790 tmp = dst + src - 128;
3791 tmp = qMin(tmp, 255);
3797 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3801 src_a = INT_MULT(src_a, layer.opacity);
3805 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3806 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3809 uchar new_a = OPAQUE_OPACITY;
3811 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3812 float dst_ratio = 1.0 - src_ratio;
3814 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3833bool XCFImageFormat::mergeGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3835 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3836 uchar src_a = layer.opacity;
3837 image.
setPixel(m, n, qRgba(src, src_a));
3854bool XCFImageFormat::mergeGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3856 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3857 int dst = qGray(image.
pixel(m, n));
3859 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3860 uchar dst_a = qAlpha(image.
pixel(m, n));
3866 switch (layer.mode) {
3867 case GIMP_LAYER_MODE_NORMAL:
3868 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3870 case GIMP_LAYER_MODE_MULTIPLY:
3871 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3872 src = INT_MULT(src, dst);
3873 src_a = qMin(src_a, dst_a);
3875 case GIMP_LAYER_MODE_DIVIDE:
3876 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3877 src = qMin((dst * 256) / (1 + src), 255);
3878 src_a = qMin(src_a, dst_a);
3880 case GIMP_LAYER_MODE_SCREEN:
3881 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3882 src = 255 - INT_MULT(255 - dst, 255 - src);
3883 src_a = qMin(src_a, dst_a);
3885 case GIMP_LAYER_MODE_OVERLAY:
3886 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3887 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3888 src_a = qMin(src_a, dst_a);
3890 case GIMP_LAYER_MODE_DIFFERENCE:
3891 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3892 src = dst > src ? dst - src : src - dst;
3893 src_a = qMin(src_a, dst_a);
3895 case GIMP_LAYER_MODE_ADDITION:
3896 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3897 src = add_lut(dst, src);
3898 src_a = qMin(src_a, dst_a);
3900 case GIMP_LAYER_MODE_SUBTRACT:
3901 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3902 src = dst > src ? dst - src : 0;
3903 src_a = qMin(src_a, dst_a);
3905 case GIMP_LAYER_MODE_DARKEN_ONLY:
3906 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3907 src = dst < src ? dst : src;
3908 src_a = qMin(src_a, dst_a);
3910 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3911 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3912 src = dst < src ? src : dst;
3913 src_a = qMin(src_a, dst_a);
3915 case GIMP_LAYER_MODE_DODGE:
3916 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3917 uint tmp = dst << 8;
3919 src = (uchar)qMin(tmp, 255u);
3920 src_a = qMin(src_a, dst_a);
3922 case GIMP_LAYER_MODE_BURN:
3923 case GIMP_LAYER_MODE_BURN_LEGACY: {
3924 uint tmp = (255 - dst) << 8;
3926 src = (uchar)qMin(tmp, 255u);
3928 src_a = qMin(src_a, dst_a);
3930 case GIMP_LAYER_MODE_HARDLIGHT:
3931 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3934 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3935 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3937 tmp = (int)dst * ((
int)src << 1);
3938 src = (uchar)qMin(tmp >> 8, 255u);
3940 src_a = qMin(src_a, dst_a);
3942 case GIMP_LAYER_MODE_SOFTLIGHT:
3943 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3947 tmpM = INT_MULT(dst, src);
3948 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3949 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3951 src_a = qMin(src_a, dst_a);
3953 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3954 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3957 tmp = dst - src + 128;
3958 tmp = qMin(tmp, 255);
3962 src_a = qMin(src_a, dst_a);
3964 case GIMP_LAYER_MODE_GRAIN_MERGE:
3965 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3968 tmp = dst + src - 128;
3969 tmp = qMin(tmp, 255);
3973 src_a = qMin(src_a, dst_a);
3976 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3980 src_a = INT_MULT(src_a, layer.opacity);
3983 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3984 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3987 uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3989 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3990 float dst_ratio = 1.0 - src_ratio;
3992 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3994 if (!modeAffectsSourceAlpha(layer.mode)) {
3998 image.
setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
4013bool XCFImageFormat::mergeIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4015 int src = layer.image_tiles[j][i].pixelIndex(k, l);
4031bool XCFImageFormat::mergeIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4033 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
4034 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4035 src_a = INT_MULT(src_a, layer.opacity);
4037 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4038 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4061bool XCFImageFormat::mergeIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4063 QRgb src = layer.image_tiles[j][i].pixel(k, l);
4064 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4065 src_a = INT_MULT(src_a, layer.opacity);
4068 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4069 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4076 src_a = OPAQUE_OPACITY;
4079 image.
setPixel(m, n, qRgba(src, src_a));
4090void XCFImageFormat::dissolveRGBPixels(
QImage &image,
int x,
int y)
4095 for (
int l = 0; l < image.
height(); l++) {
4096 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4098 for (
int k = 0; k < x; k++) {
4099 RandomTable::rand_r(&next);
4102 for (
int k = 0; k < image.
width(); k++) {
4103 int rand_val = RandomTable::rand_r(&next) & 0xff;
4104 QRgb pixel = image.
pixel(k, l);
4106 if (rand_val > qAlpha(pixel)) {
4107 image.
setPixel(k, l, qRgba(pixel, 0));
4122void XCFImageFormat::dissolveAlphaPixels(
QImage &image,
int x,
int y)
4127 for (
int l = 0; l < image.
height(); l++) {
4128 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4130 for (
int k = 0; k < x; k++) {
4131 RandomTable::rand_r(&next);
4134 for (
int k = 0; k < image.
width(); k++) {
4135 int rand_val = RandomTable::rand_r(&next) & 0xff;
4138 if (rand_val > alpha) {
4147XCFHandler::XCFHandler()
4151bool XCFHandler::canRead()
const
4153 if (canRead(device())) {
4160bool XCFHandler::read(
QImage *image)
4162 XCFImageFormat xcfif;
4163 auto ok = xcfif.readXCF(device(), image);
4164 m_imageSize = image->
size();
4168bool XCFHandler::write(
const QImage &)
4173bool XCFHandler::supportsOption(ImageOption option)
const
4180QVariant XCFHandler::option(ImageOption option)
const
4185 if (!m_imageSize.isEmpty()) {
4200 else if (
auto d = device()) {
4202 d->startTransaction();
4203 auto ba9 = d->read(9);
4204 auto ba5 = d->read(4+1);
4205 auto ba = d->read(8);
4206 d->rollbackTransaction();
4222bool XCFHandler::canRead(
QIODevice *device)
4225 qCDebug(XCFPLUGIN) <<
"XCFHandler::canRead() called with no device";
4232 const qint64 oldPos = device->
pos();
4235 XCFImageFormat::XCFImage::Header header;
4236 bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
4237 ds.setDevice(
nullptr);
4239 device->
seek(oldPos);
4244 switch (header.precision) {
4245 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
4246 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
4247 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
4248 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
4249 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
4250 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
4251 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
4252 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
4253 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
4254 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
4255 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
4256 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
4257 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
4258 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
4259 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
4261 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
4262 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
4263 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
4265 qCDebug(XCFPLUGIN) <<
"unsupported precision" << header.precision;
4274 if (format ==
"xcf") {
4285 if (device->
isReadable() && XCFHandler::canRead(device)) {
4302#include "moc_xcf_p.cpp"
Type type(const QSqlDatabase &db)
QFlags< Capability > Capabilities
QStringView merge(QStringView lhs, QStringView rhs)
QString name(StandardAction id)
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
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)
int version() const const
qsizetype bytesPerLine() const const
int colorCount() const const
QColorSpace colorSpace() const const
void convertToColorSpace(const QColorSpace &colorSpace)
void fill(Qt::GlobalColor color)
bool hasAlphaChannel() const const
bool isNull() const const
QRgb pixel(const QPoint &position) const const
int pixelIndex(const QPoint &position) const const
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)
void setDevice(QIODevice *device)
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)
bool isEmpty() const const
void resize(qsizetype size)
qsizetype size() const const
void setAlpha(quint16 alpha)
QString fromUtf8(QByteArrayView str)
QVariant fromValue(T &&value)