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);
1312 case PROP_COMPOSITE_MODE:
1313 property >> layer.compositeMode;
1314 if (layer.compositeMode < 0) {
1315 layer.compositeMode = XCFImageFormat::GimpCompositeMode(-layer.compositeMode);
1319 case PROP_BLEND_SPACE:
1320 property >> layer.blendSpace;
1321 if (layer.blendSpace) {
1322 layer.blendSpace = GimpColorSpace(-layer.blendSpace);
1327 case PROP_COLOR_TAG:
1331 case PROP_LOCK_CONTENT:
1332 case PROP_LOCK_POSITION:
1336 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented layer property " <<
type <<
"(" << rawType <<
")"
1337 <<
", size " << bytes.
size();
1348bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
1350 Layer &layer(xcf_image.layer);
1352 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
1353 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
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;
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";
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";
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) {
1378 qCWarning(XCFPLUGIN) <<
"Euristic sanity check: the image may be corrupted!";
1383#ifndef XCF_QT5_SUPPORT
1388 qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
1395 layer.image_tiles.resize(layer.nrows);
1397 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1398 layer.alpha_tiles.resize(layer.nrows);
1401 if (layer.mask_offset != 0) {
1402 layer.mask_tiles.resize(layer.nrows);
1405 for (uint j = 0; j < layer.nrows; j++) {
1406 layer.image_tiles[j].resize(layer.ncols);
1408 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1409 layer.alpha_tiles[j].resize(layer.ncols);
1412 if (layer.mask_offset != 0) {
1413 layer.mask_tiles[j].resize(layer.ncols);
1417 const QImage::Format format = layer.qimageFormat(xcf_image.header.precision);
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;
1423 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
1428 switch (layer.type) {
1431 layer.image_tiles[j][i] =
QImage(tile_width, tile_height, format);
1432 if (layer.image_tiles[j][i].isNull()) {
1435 layer.image_tiles[j][i].setColorCount(0);
1440 if (layer.image_tiles[j][i].isNull()) {
1443 layer.image_tiles[j][i].setColorCount(256);
1444 setGrayPalette(layer.image_tiles[j][i]);
1449 layer.image_tiles[j][i].setColorCount(256);
1450 if (layer.image_tiles[j][i].isNull()) {
1453 setGrayPalette(layer.image_tiles[j][i]);
1456 if (layer.alpha_tiles[j][i].isNull()) {
1459 layer.alpha_tiles[j][i].setColorCount(256);
1460 setGrayPalette(layer.alpha_tiles[j][i]);
1463 case INDEXED_GIMAGE:
1465 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1466 if (layer.image_tiles[j][i].isNull()) {
1469 setPalette(xcf_image, layer.image_tiles[j][i]);
1472 case INDEXEDA_GIMAGE:
1474 if (layer.image_tiles[j][i].isNull()) {
1477 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1478 setPalette(xcf_image, layer.image_tiles[j][i]);
1481 if (layer.alpha_tiles[j][i].isNull()) {
1484 layer.alpha_tiles[j][i].setColorCount(256);
1485 setGrayPalette(layer.alpha_tiles[j][i]);
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;
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:
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:
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:
1520 if (layer.mask_offset != 0) {
1522 layer.mask_tiles[j][i].setColorCount(256);
1523 if (layer.mask_tiles[j][i].isNull()) {
1526 setGrayPalette(layer.mask_tiles[j][i]);
1539void XCFImageFormat::setGrayPalette(
QImage &image)
1544 for (
int i = 0; i < 256; i++) {
1545 grayTable[i] = qRgb(i, i, i);
1557void XCFImageFormat::setPalette(XCFImage &xcf_image,
QImage &image)
1559 Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size());
1570void XCFImageFormat::setImageParasites(
const XCFImage &xcf_image,
QImage &image)
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())
1585 if (key == QStringLiteral(
"icc-profile")) {
1597 if (key == QStringLiteral(
"gimp-comment")) {
1606 if (key == QStringLiteral(
"gimp-image-metadata")) {
1621 if (key == QStringLiteral(
"gimp-metadata")) {
1632#ifdef DISABLE_IMAGE_PROFILE
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:
1659bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
1661 QImage &image = layer.image_tiles[j][i];
1663 const uchar *tile = layer.tile;
1664 const int width = image.
width();
1665 const int height = image.
height();
1667 uchar *bits = image.
bits();
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) {
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;
1688 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1689 tile +=
sizeof(quint16) * 2;
1691 tile +=
sizeof(quint16);
1695 for (
int x = 0; x < width; x++) {
1696 auto src =
reinterpret_cast<const float *
>(tile);
1697 *dataPtr++ = qFromBigEndian<float>(src[0]) * 255;
1699 *alphaPtr++ = qFromBigEndian<float>(src[1]) * 255;
1700 tile +=
sizeof(float) * 2;
1702 tile +=
sizeof(float);
1707 for (
int x = 0; x < width; x++) {
1708 auto src = (
const quint16 *)tile;
1709 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1711 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1712 tile +=
sizeof(quint16) * 2;
1714 tile +=
sizeof(quint16);
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;
1725 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1726 tile +=
sizeof(QRgb);
1729 for (
int x = 0; x < width; x++) {
1730 auto src =
reinterpret_cast<const qfloat16 *
>(tile);
1731 *dataPtr++ = qFromBigEndian<qfloat16>(src[0]) * 255;
1733 *alphaPtr++ = qFromBigEndian<qfloat16>(src[1]) * 255;
1734 tile +=
sizeof(QRgb);
1738 for (
int x = 0; x < width; x++) {
1739 auto src =
reinterpret_cast<const quint16 *
>(tile);
1740 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1742 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1743 tile +=
sizeof(QRgb);
1747 for (
int x = 0; x < width; x++) {
1749 *dataPtr++ = tile[0];
1751 *alphaPtr++ = tile[1];
1752 tile +=
sizeof(QRgb);
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;
1772 for (
int y = 0; y < height; y++) {
1773 const size_t bpl = width * 4;
1774 memcpy(image.
scanLine(y), tile + y * bpl, bpl);
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;
1790#ifdef USE_FLOAT_IMAGES
1792 for (
int y = 0; y < height; 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]);
1804 static_assert(
sizeof(QRgbaFloat16) ==
sizeof(
QRgba64),
"Different sizes for float and int 16 bit pixels");
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));
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));
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;
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);
1842 qCWarning(XCFPLUGIN) <<
"Unhandled image format" << image.
format() <<
"and/or layer type" << layer.type;
1857bool XCFImageFormat::loadHierarchy(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
1863 xcf_io >> width >> height >> bpp;
1864 const qint64 offset = readOffsetPtr(xcf_io);
1866 qCDebug(XCFPLUGIN) <<
"width" << width <<
"height" << height <<
"bpp" << bpp <<
"offset" << offset;
1869 qCDebug(XCFPLUGIN) <<
"XCF: negative hierarchy offset";
1873 const bool isMask = layer.assignBytes == assignMaskBytes;
1876 switch (layer.type) {
1878 if (bpp != 3 * bytesPerChannel(precision)) {
1879 qCDebug(XCFPLUGIN) <<
"Found layer of type RGB but with bpp != 3" << bpp;
1887 if (bpp != 4 * bytesPerChannel(precision)) {
1888 qCDebug(XCFPLUGIN) <<
"Found layer of type RGBA but with bpp != 4, got" << bpp <<
"bpp";
1896 if (bpp != 1 * bytesPerChannel(precision)) {
1897 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray but with bpp != 1" << bpp;
1902 if (bpp != 2 * bytesPerChannel(precision)) {
1903 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray+Alpha but with bpp != 2" << bpp;
1910 case INDEXED_GIMAGE:
1911 if (bpp != 1 * bytesPerChannel(precision)) {
1912 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed but with bpp != 1" << bpp;
1916 case INDEXEDA_GIMAGE:
1917 if (bpp != 2 * bytesPerChannel(precision)) {
1918 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed+Alpha but with bpp != 2" << bpp;
1927 if (bpp > 4 * bytesPerChannel(precision)) {
1928 qCDebug(XCFPLUGIN) <<
"bpp is" << bpp <<
"We don't support layers with bpp > 4";
1941 qCDebug(XCFPLUGIN) <<
"XCF: read failure on layer " << layer.name <<
" level offsets";
1944 }
while (junk != 0);
1946 qint64 saved_pos = xcf_io.
device()->
pos();
1949 if (!loadLevel(xcf_io, layer, bpp, precision)) {
1957template<
typename SourceFormat>
1958static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
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.)));
1975bool XCFImageFormat::loadLevel(
QDataStream &xcf_io, Layer &layer, qint32 bpp,
const GimpPrecision precision)
1977 auto bpc = bytesPerChannel(precision);
1978 if ((bpc == 0) || (bpp % bpc)) {
1979 qCDebug(XCFPLUGIN) <<
"XCF: the stream seems corrupted";
1986 xcf_io >> width >> height;
1987 qint64 offset = readOffsetPtr(xcf_io);
1990 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
1997 for (uint j = 0; j < layer.nrows; j++) {
1998 for (uint i = 0; i < layer.ncols; i++) {
2000 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
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:
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;
2030 const uint blockSize = TILE_WIDTH * TILE_HEIGHT * bpp * 1.5;
2034 buffer.
resize(blockSize * (bpp == 2 ? 2 : 1));
2036 for (uint j = 0; j < layer.nrows; j++) {
2037 for (uint i = 0; i < layer.ncols; i++) {
2039 qCDebug(XCFPLUGIN) <<
"XCF: incorrect number of tiles in layer " << layer.name;
2043 qint64 saved_pos = xcf_io.
device()->
pos();
2044 qint64 offset2 = readOffsetPtr(xcf_io);
2047 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2053 offset2 = offset + blockSize;
2057 qint64 bytesParsed = 0;
2059 switch (layer.compression) {
2060 case COMPRESS_NONE: {
2062 qCDebug(XCFPLUGIN) <<
"Component reading not supported yet";
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;
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;
2075 bytesParsed = dataRead;
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;
2082 if (data_size >=
unsigned(buffer.
size())) {
2083 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" << buffer.
size() <<
"but need" << data_size;
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;
2091 if (blockSize >
sizeof(layer.tile)) {
2092 qCWarning(XCFPLUGIN) <<
"Too small tiles" <<
sizeof(layer.tile) <<
"this image requires" << blockSize <<
sizeof(
QRgba64) << bpp;
2096 if (!loadTileRLE(xcf_io, needConvert ? buffer.
data() : layer.tile, size, offset2 - offset, bpp, &bytesParsed)) {
2097 qCDebug(XCFPLUGIN) <<
"Failed to read RLE";
2103 qCDebug(XCFPLUGIN) <<
"Unhandled compression" << layer.compression;
2108 if (bytesParsed > buffer.
size()) {
2109 qCDebug(XCFPLUGIN) <<
"Invalid number of bytes parsed" << bytesParsed << buffer.
size();
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);
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());
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());
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());
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])));
2151 qCWarning(XCFPLUGIN) <<
"Unsupported precision" << precision;
2159 if (!layer.assignBytes(layer, i, j, precision)) {
2164 offset = readOffsetPtr(xcf_io);
2167 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2182bool XCFImageFormat::loadMask(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
2188 xcf_io >> width >> height >>
name;
2192 if (!loadChannelProperties(xcf_io, layer)) {
2196 const qint64 hierarchy_offset = readOffsetPtr(xcf_io);
2198 if (hierarchy_offset < 0) {
2199 qCDebug(XCFPLUGIN) <<
"XCF: negative mask hierarchy_offset";
2204 layer.assignBytes = assignMaskBytes;
2206 if (!loadHierarchy(xcf_io, layer, precision)) {
2236bool XCFImageFormat::loadTileRLE(
QDataStream &xcf_io, uchar *tile,
int image_size,
int data_length, qint32 bpp, qint64 *bytesParsed)
2242 uchar *xcfdatalimit;
2244 int step =
sizeof(QRgb);
2250 step =
sizeof(QRgb);
2254 step =
sizeof(QRgb) * 2;
2258 step =
sizeof(QRgb) * 4;
2261 qCDebug(XCFPLUGIN) <<
"XCF: unhandled bit depth" << bpp;
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;
2270 xcfdata = xcfodata =
new uchar[data_length];
2272 const int dataRead = xcf_io.
readRawData((
char *)xcfdata, data_length);
2273 if (dataRead <= 0) {
2275 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile" << dataRead;
2279 if (dataRead < data_length) {
2280 memset(&xcfdata[dataRead], 0, data_length - dataRead);
2285 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile";
2289 xcfdatalimit = &xcfodata[data_length - 1];
2291 for (
int i = 0; i < bpp; ++i) {
2294 int size = image_size;
2297 if (xcfdata > xcfdatalimit) {
2301 uchar val = *xcfdata++;
2304 if (length >= 128) {
2305 length = 255 - (length - 1);
2306 if (length == 128) {
2307 if (xcfdata >= xcfdatalimit) {
2311 length = (*xcfdata << 8) + xcfdata[1];
2322 if (&xcfdata[length - 1] > xcfdatalimit) {
2326 while (length-- > 0) {
2332 if (length == 128) {
2333 if (xcfdata >= xcfdatalimit) {
2337 length = (*xcfdata << 8) + xcfdata[1];
2347 if (xcfdata > xcfdatalimit) {
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
2360 while (length-- > 0) {
2367 *bytesParsed = qintptr(data - tile);
2374 qCDebug(XCFPLUGIN) <<
"The run length encoding could not be decoded properly";
2386bool XCFImageFormat::loadChannelProperties(
QDataStream &xcf_io, Layer &layer)
2393 if (!loadProperty(xcf_io, type, bytes, rawType)) {
2394 qCDebug(XCFPLUGIN) <<
"XCF: error loading channel properties";
2405 property >> layer.mask_channel.opacity;
2406 layer.mask_channel.opacity = std::min(layer.mask_channel.opacity, 255u);
2409 case PROP_FLOAT_OPACITY:
2412 if (bytes.
size() == 4) {
2413 layer.mask_channel.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
2415 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
2420 property >> layer.mask_channel.visible;
2423 case PROP_SHOW_MASKED:
2424 property >> layer.mask_channel.show_masked;
2428 property >> layer.mask_channel.red >> layer.mask_channel.green >> layer.mask_channel.blue;
2431 case PROP_FLOAT_COLOR:
2432 property >> layer.mask_channel.redF >> layer.mask_channel.greenF >> layer.mask_channel.blueF;
2436 property >> layer.mask_channel.tattoo;
2444 case PROP_COLOR_TAG:
2448 case PROP_LOCK_CONTENT:
2449 case PROP_LOCK_POSITION:
2453 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented channel property " <<
type <<
"(" << rawType <<
")"
2454 <<
", size " << bytes.
size();
2466bool XCFImageFormat::assignMaskBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
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();
2474 uchar *tile = layer.tile;
2475 const int width = image.
width();
2476 const int height = image.
height();
2478 uchar *bits = image.
bits();
2479 auto bpc = bytesPerChannel(precision);
2484 for (
int y = 0; y < height; y++) {
2485 uchar *dataPtr = bits + y * bytesPerLine;
2486#ifdef USE_FLOAT_IMAGES
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);
2494 for (
int x = 0; x < width; x++) {
2495 *dataPtr++ = qFromBigEndian<float>(*
reinterpret_cast<const float *
>(tile)) * 255;
2496 tile +=
sizeof(QRgb);
2499 }
else if (bpc == 2) {
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);
2507 for (
int x = 0; x < width; x++) {
2508 *dataPtr++ = qFromBigEndian<qfloat16>(*
reinterpret_cast<const qfloat16 *
>(tile)) * 255;
2509 tile +=
sizeof(QRgb);
2515 for (
int x = 0; x < width; x++) {
2516 *dataPtr++ = qFromBigEndian<quint16>(*
reinterpret_cast<const quint16 *
>(tile)) / 257;
2517 tile +=
sizeof(QRgb);
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);
2527 for (
int x = 0; x < width; x++) {
2528 *dataPtr++ = tile[0];
2529 tile +=
sizeof(QRgb);
2565bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
2568 Layer &layer(xcf_image.layer);
2569 QImage &image(xcf_image.image);
2571 switch (layer.type) {
2573 if (layer.opacity == OPAQUE_OPACITY) {
2579 setGrayPalette(image);
2587 image = imageAlloc(xcf_image.header.width, xcf_image.header.height, xcf_image.qimageFormat());
2598 case INDEXED_GIMAGE:
2611 if (xcf_image.num_colors <= 2) {
2618 setPalette(xcf_image, image);
2619 }
else if (xcf_image.num_colors <= 256) {
2626 setPalette(xcf_image, image);
2630 case INDEXEDA_GIMAGE:
2631 if (xcf_image.num_colors == 1) {
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);
2644 setPalette(xcf_image, image);
2645 }
else if (xcf_image.num_colors < 256) {
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];
2653 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2660 setPalette(xcf_image, image);
2669 image.
fill(qRgba(255, 255, 255, 0));
2673 if (image.
format() != xcf_image.qimageFormat()) {
2674 qCWarning(XCFPLUGIN) <<
"Selected wrong format:" << image.
format() <<
"expected" << layer.qimageFormat(xcf_image.header.precision);
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:
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:
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:
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())) {
2714 const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
2715 if (dpmy >
float(std::numeric_limits<int>::max())) {
2729void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
2731 Layer &layer(xcf_image.layer);
2732 QImage &image(xcf_image.image);
2733 PixelCopyOperation
copy =
nullptr;
2735 switch (layer.type) {
2738 copy = copyRGBToRGB;
2741 if (layer.opacity == OPAQUE_OPACITY) {
2742 copy = copyGrayToGray;
2744 copy = copyGrayToRGB;
2748 copy = copyGrayAToRGB;
2750 case INDEXED_GIMAGE:
2751 copy = copyIndexedToIndexed;
2753 case INDEXEDA_GIMAGE:
2754 if (xcf_image.image.depth() <= 8) {
2755 copy = copyIndexedAToIndexed;
2757 copy = copyIndexedAToRGB;
2767 for (uint j = 0; j < layer.nrows; j++) {
2768 qint32 y = qint32(j * TILE_HEIGHT);
2770 for (uint i = 0; i < layer.ncols; i++) {
2771 qint32 x = qint32(i * TILE_WIDTH);
2778 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
2779 if (!random_table_initialized) {
2780 initializeRandomTable();
2781 random_table_initialized =
true;
2783 if (layer.type == RGBA_GIMAGE) {
2784 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
2787 else if (layer.type == GRAYA_GIMAGE) {
2788 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
2793 if (copy == copyRGBToRGB && layer.apply_mask != 1) {
2795 painter.setOpacity(layer.opacity / 255.0);
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]);
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;
2809 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
2813 (*copy)(layer, i, j, k, l, image, m, n);
2833void XCFImageFormat::copyRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2835 if (image.
depth() == 32) {
2836 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2837 uchar src_a = layer.opacity;
2839 if (layer.type == RGBA_GIMAGE) {
2840 src_a = INT_MULT(src_a, qAlpha(src));
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));
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;
2854 if (layer.type == RGBA_GIMAGE) {
2855 src_a = INT_MULT(src_a, qAlpha(src));
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));
2880void XCFImageFormat::copyGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2882 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2899void XCFImageFormat::copyGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
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));
2919void XCFImageFormat::copyGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
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);
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));
2931 image.
setPixel(m, n, qRgba(src, src_a));
2945void XCFImageFormat::copyIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2947 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2962void XCFImageFormat::copyIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
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);
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));
2994void XCFImageFormat::copyIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
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);
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));
3009 src_a = OPAQUE_OPACITY;
3012 image.
setPixel(m, n, qRgba(src, src_a));
3019void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
3021 Layer &layer(xcf_image.layer);
3022 QImage &image(xcf_image.image);
3024 PixelMergeOperation
merge =
nullptr;
3026 if (!layer.opacity) {
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;
3035 if (layer.blendSpace != XCFImageFormat::RgbLinearSpace) {
3036 qCDebug(XCFPLUGIN) <<
"Unimplemented blend color space" << layer.blendSpace;
3038 qCDebug(XCFPLUGIN) <<
"Blend color space" << layer.blendSpace;
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;
3045 if (layer.compositeSpace != XCFImageFormat::RgbLinearSpace) {
3046 qCDebug(XCFPLUGIN) <<
"Unimplemented composite color space" << layer.compositeSpace;
3048 if (layer.compositeMode != XCFImageFormat::CompositeUnion) {
3049 qCDebug(XCFPLUGIN) <<
"Unhandled composite mode" << layer.compositeMode;
3052 switch (layer.type) {
3055 merge = mergeRGBToRGB;
3058 if (layer.opacity == OPAQUE_OPACITY && xcf_image.image.depth() <= 8) {
3059 merge = mergeGrayToGray;
3061 merge = mergeGrayToRGB;
3065 if (xcf_image.image.depth() <= 8) {
3066 merge = mergeGrayAToGray;
3068 merge = mergeGrayAToRGB;
3071 case INDEXED_GIMAGE:
3072 merge = mergeIndexedToIndexed;
3074 case INDEXEDA_GIMAGE:
3075 if (xcf_image.image.depth() <= 8) {
3076 merge = mergeIndexedAToIndexed;
3078 merge = mergeIndexedAToRGB;
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:
3093 case GIMP_LAYER_MODE_MULTIPLY:
3094 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3097 case GIMP_LAYER_MODE_SCREEN:
3098 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3101 case GIMP_LAYER_MODE_OVERLAY:
3102 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3105 case GIMP_LAYER_MODE_DIFFERENCE:
3106 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3109 case GIMP_LAYER_MODE_DARKEN_ONLY:
3110 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3113 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3114 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3117 case GIMP_LAYER_MODE_DODGE:
3118 case GIMP_LAYER_MODE_DODGE_LEGACY:
3121 case GIMP_LAYER_MODE_BURN:
3122 case GIMP_LAYER_MODE_BURN_LEGACY:
3125 case GIMP_LAYER_MODE_HARDLIGHT:
3126 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
3129 case GIMP_LAYER_MODE_SOFTLIGHT:
3130 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
3133 case GIMP_LAYER_MODE_ADDITION:
3134 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3137 case GIMP_LAYER_MODE_EXCLUSION:
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;
3182 case GIMP_LAYER_MODE_DISSOLVE:
3183 case GIMP_LAYER_MODE_COUNT:
3187 if (painterMode != -1) {
3189 painter.setOpacity(layer.opacity / 255.0);
3191 qCDebug(XCFPLUGIN) <<
"Using QPainter for mode" << layer.mode;
3193 for (uint j = 0; j < layer.nrows; j++) {
3194 qint32 y = qint32(j * TILE_HEIGHT);
3196 for (uint i = 0; i < layer.ncols; i++) {
3197 qint32 x = qint32(i * TILE_WIDTH);
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);
3211#ifndef DISABLE_IMAGE_PROFILE_CONV
3213 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3217 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3222 for (uint j = 0; j < layer.nrows; j++) {
3223 qint32 y = qint32(j * TILE_HEIGHT);
3225 for (uint i = 0; i < layer.ncols; i++) {
3226 qint32 x = qint32(i * TILE_WIDTH);
3233 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
3234 if (!random_table_initialized) {
3235 initializeRandomTable();
3236 random_table_initialized =
true;
3238 if (layer.type == RGBA_GIMAGE) {
3239 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
3242 else if (layer.type == GRAYA_GIMAGE) {
3243 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
3248 if (merge == mergeRGBToRGB && layer.apply_mask != 1 && layer.mode == GIMP_LAYER_MODE_NORMAL_LEGACY) {
3250 painter.setOpacity(layer.opacity / 255.0);
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]);
3259#ifndef DISABLE_TILE_PROFILE_CONV
3260 QImage &tile = layer.image_tiles[j][i];
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;
3274 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
3278 if (!(*merge)(layer, i, j, k, l, image, m, n)) {
3300bool XCFImageFormat::mergeRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3302 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3303 QRgb dst = image.
pixel(m, n);
3305 uchar src_r = qRed(src);
3306 uchar src_g = qGreen(src);
3307 uchar src_b = qBlue(src);
3308 uchar src_a = qAlpha(src);
3310 uchar dst_r = qRed(dst);
3311 uchar dst_g = qGreen(dst);
3312 uchar dst_b = qBlue(dst);
3313 uchar dst_a = qAlpha(dst);
3319 switch (layer.mode) {
3320 case GIMP_LAYER_MODE_NORMAL:
3321 case GIMP_LAYER_MODE_NORMAL_LEGACY:
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);
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);
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);
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);
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);
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);
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);
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);
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);
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;
3392 RGBTOHSV(src_r, src_g, src_b);
3393 RGBTOHSV(new_r, new_g, new_b);
3397 HSVTORGB(new_r, new_g, new_b);
3402 src_a = qMin(src_a, dst_a);
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;
3410 RGBTOHSV(src_r, src_g, src_b);
3411 RGBTOHSV(new_r, new_g, new_b);
3415 HSVTORGB(new_r, new_g, new_b);
3420 src_a = qMin(src_a, dst_a);
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;
3428 RGBTOHSV(src_r, src_g, src_b);
3429 RGBTOHSV(new_r, new_g, new_b);
3433 HSVTORGB(new_r, new_g, new_b);
3438 src_a = qMin(src_a, dst_a);
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;
3446 RGBTOHLS(src_r, src_g, src_b);
3447 RGBTOHLS(new_r, new_g, new_b);
3452 HLSTORGB(new_r, new_g, new_b);
3457 src_a = qMin(src_a, dst_a);
3459 case GIMP_LAYER_MODE_DODGE:
3460 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3465 src_r = (uchar)qMin(tmp, 255u);
3469 src_g = (uchar)qMin(tmp, 255u);
3473 src_b = (uchar)qMin(tmp, 255u);
3475 src_a = qMin(src_a, dst_a);
3477 case GIMP_LAYER_MODE_BURN:
3478 case GIMP_LAYER_MODE_BURN_LEGACY: {
3481 tmp = (255 - dst_r) << 8;
3483 src_r = (uchar)qMin(tmp, 255u);
3484 src_r = 255 - src_r;
3486 tmp = (255 - dst_g) << 8;
3488 src_g = (uchar)qMin(tmp, 255u);
3489 src_g = 255 - src_g;
3491 tmp = (255 - dst_b) << 8;
3493 src_b = (uchar)qMin(tmp, 255u);
3494 src_b = 255 - src_b;
3496 src_a = qMin(src_a, dst_a);
3498 case GIMP_LAYER_MODE_HARDLIGHT:
3499 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3502 tmp = ((int)255 - dst_r) * ((
int)255 - ((src_r - 128) << 1));
3503 src_r = (uchar)qMin(255 - (tmp >> 8), 255u);
3505 tmp = (int)dst_r * ((
int)src_r << 1);
3506 src_r = (uchar)qMin(tmp >> 8, 255u);
3510 tmp = ((int)255 - dst_g) * ((
int)255 - ((src_g - 128) << 1));
3511 src_g = (uchar)qMin(255 - (tmp >> 8), 255u);
3513 tmp = (int)dst_g * ((
int)src_g << 1);
3514 src_g = (uchar)qMin(tmp >> 8, 255u);
3518 tmp = ((int)255 - dst_b) * ((
int)255 - ((src_b - 128) << 1));
3519 src_b = (uchar)qMin(255 - (tmp >> 8), 255u);
3521 tmp = (int)dst_b * ((
int)src_b << 1);
3522 src_b = (uchar)qMin(tmp >> 8, 255u);
3524 src_a = qMin(src_a, dst_a);
3526 case GIMP_LAYER_MODE_SOFTLIGHT:
3527 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
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);
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);
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);
3543 src_a = qMin(src_a, dst_a);
3545 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3546 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3549 tmp = dst_r - src_r + 128;
3550 tmp = qMin(tmp, 255);
3554 tmp = dst_g - src_g + 128;
3555 tmp = qMin(tmp, 255);
3559 tmp = dst_b - src_b + 128;
3560 tmp = qMin(tmp, 255);
3564 src_a = qMin(src_a, dst_a);
3566 case GIMP_LAYER_MODE_GRAIN_MERGE:
3567 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3570 tmp = dst_r + src_r - 128;
3571 tmp = qMin(tmp, 255);
3575 tmp = dst_g + src_g - 128;
3576 tmp = qMin(tmp, 255);
3580 tmp = dst_b + src_b - 128;
3581 tmp = qMin(tmp, 255);
3585 src_a = qMin(src_a, dst_a);
3587 case GIMP_LAYER_MODE_LINEAR_LIGHT: {
3589 src_r = qBound(0, dst_r + 2 * src_r - 255, 255);
3591 src_r = qBound(0, dst_r + 2 * (src_r - 128), 255);
3594 src_g = qBound(0, dst_g + 2 * src_g - 255, 255);
3596 src_g = qBound(0, dst_g + 2 * (src_g - 127), 255);
3599 src_b = qBound(0, dst_b + 2 * src_b - 255, 255);
3601 src_b = qBound(0, dst_b + 2 * (src_b - 127), 255);
3604 case GIMP_LAYER_MODE_VIVID_LIGHT: {
3607 A[0] = src_r / 255.;
3608 A[1] = src_g / 255.;
3609 A[2] = src_b / 255.;
3611 B[0] = dst_r / 255.;
3612 B[1] = dst_g / 255.;
3613 B[2] = dst_b / 255.;
3615 for (
int i = 0; i < 3; i++) {
3618 C[i] = 1.f - (1.f - B[i]) / (2.f * A[i]);
3622 C[i] = B[i] / (2.f * (1.f - A[i]));
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);
3631 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3635 src_a = INT_MULT(src_a, layer.opacity);
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));
3647 new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3649 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3650 float dst_ratio = 1.0 - src_ratio;
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);
3656 if (!modeAffectsSourceAlpha(layer.mode)) {
3660 image.
setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
3675bool XCFImageFormat::mergeGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3677 int src = layer.image_tiles[j][i].pixelIndex(k, l);
3693bool XCFImageFormat::mergeGrayAToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3695 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3698 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3704 switch (layer.mode) {
3705 case GIMP_LAYER_MODE_MULTIPLY:
3706 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3707 src = INT_MULT(src, dst);
3709 case GIMP_LAYER_MODE_DIVIDE:
3710 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3711 src = qMin((dst * 256) / (1 + src), 255);
3713 case GIMP_LAYER_MODE_SCREEN:
3714 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3715 src = 255 - INT_MULT(255 - dst, 255 - src);
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));
3721 case GIMP_LAYER_MODE_DIFFERENCE:
3722 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3723 src = dst > src ? dst - src : src - dst;
3725 case GIMP_LAYER_MODE_ADDITION:
3726 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3727 src = add_lut(dst, src);
3729 case GIMP_LAYER_MODE_SUBTRACT:
3730 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3731 src = dst > src ? dst - src : 0;
3733 case GIMP_LAYER_MODE_DARKEN_ONLY:
3734 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3735 src = dst < src ? dst : src;
3737 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3738 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3739 src = dst < src ? src : dst;
3741 case GIMP_LAYER_MODE_DODGE:
3742 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3743 uint tmp = dst << 8;
3745 src = (uchar)qMin(tmp, 255u);
3747 case GIMP_LAYER_MODE_BURN:
3748 case GIMP_LAYER_MODE_BURN_LEGACY: {
3749 uint tmp = (255 - dst) << 8;
3751 src = (uchar)qMin(tmp, 255u);
3754 case GIMP_LAYER_MODE_HARDLIGHT:
3755 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3758 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3759 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3761 tmp = (int)dst * ((
int)src << 1);
3762 src = (uchar)qMin(tmp >> 8, 255u);
3765 case GIMP_LAYER_MODE_SOFTLIGHT:
3766 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
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);
3775 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3776 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3779 tmp = dst - src + 128;
3780 tmp = qMin(tmp, 255);
3785 case GIMP_LAYER_MODE_GRAIN_MERGE:
3786 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3789 tmp = dst + src - 128;
3790 tmp = qMin(tmp, 255);
3796 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3800 src_a = INT_MULT(src_a, layer.opacity);
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));
3808 uchar new_a = OPAQUE_OPACITY;
3810 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3811 float dst_ratio = 1.0 - src_ratio;
3813 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3832bool XCFImageFormat::mergeGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
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));
3853bool XCFImageFormat::mergeGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3855 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3856 int dst = qGray(image.
pixel(m, n));
3858 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3859 uchar dst_a = qAlpha(image.
pixel(m, n));
3865 switch (layer.mode) {
3866 case GIMP_LAYER_MODE_NORMAL:
3867 case GIMP_LAYER_MODE_NORMAL_LEGACY:
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);
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);
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);
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);
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);
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);
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);
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);
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);
3914 case GIMP_LAYER_MODE_DODGE:
3915 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3916 uint tmp = dst << 8;
3918 src = (uchar)qMin(tmp, 255u);
3919 src_a = qMin(src_a, dst_a);
3921 case GIMP_LAYER_MODE_BURN:
3922 case GIMP_LAYER_MODE_BURN_LEGACY: {
3923 uint tmp = (255 - dst) << 8;
3925 src = (uchar)qMin(tmp, 255u);
3927 src_a = qMin(src_a, dst_a);
3929 case GIMP_LAYER_MODE_HARDLIGHT:
3930 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3933 tmp = ((int)255 - dst) * ((
int)255 - ((src - 128) << 1));
3934 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3936 tmp = (int)dst * ((
int)src << 1);
3937 src = (uchar)qMin(tmp >> 8, 255u);
3939 src_a = qMin(src_a, dst_a);
3941 case GIMP_LAYER_MODE_SOFTLIGHT:
3942 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
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);
3950 src_a = qMin(src_a, dst_a);
3952 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3953 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3956 tmp = dst - src + 128;
3957 tmp = qMin(tmp, 255);
3961 src_a = qMin(src_a, dst_a);
3963 case GIMP_LAYER_MODE_GRAIN_MERGE:
3964 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3967 tmp = dst + src - 128;
3968 tmp = qMin(tmp, 255);
3972 src_a = qMin(src_a, dst_a);
3975 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3979 src_a = INT_MULT(src_a, layer.opacity);
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));
3986 uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3988 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3989 float dst_ratio = 1.0 - src_ratio;
3991 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3993 if (!modeAffectsSourceAlpha(layer.mode)) {
3997 image.
setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
4012bool XCFImageFormat::mergeIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4014 int src = layer.image_tiles[j][i].pixelIndex(k, l);
4030bool XCFImageFormat::mergeIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
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);
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));
4060bool XCFImageFormat::mergeIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
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);
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));
4075 src_a = OPAQUE_OPACITY;
4078 image.
setPixel(m, n, qRgba(src, src_a));
4089void XCFImageFormat::dissolveRGBPixels(
QImage &image,
int x,
int y)
4094 for (
int l = 0; l < image.
height(); l++) {
4095 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4097 for (
int k = 0; k < x; k++) {
4098 RandomTable::rand_r(&next);
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);
4105 if (rand_val > qAlpha(pixel)) {
4106 image.
setPixel(k, l, qRgba(pixel, 0));
4121void XCFImageFormat::dissolveAlphaPixels(
QImage &image,
int x,
int y)
4126 for (
int l = 0; l < image.
height(); l++) {
4127 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4129 for (
int k = 0; k < x; k++) {
4130 RandomTable::rand_r(&next);
4133 for (
int k = 0; k < image.
width(); k++) {
4134 int rand_val = RandomTable::rand_r(&next) & 0xff;
4137 if (rand_val > alpha) {
4146XCFHandler::XCFHandler()
4150bool XCFHandler::canRead()
const
4152 if (canRead(device())) {
4159bool XCFHandler::read(
QImage *image)
4161 XCFImageFormat xcfif;
4162 auto ok = xcfif.readXCF(device(), image);
4163 m_imageSize = image->
size();
4167bool XCFHandler::write(
const QImage &)
4172bool XCFHandler::supportsOption(ImageOption option)
const
4179QVariant XCFHandler::option(ImageOption option)
const
4184 if (!m_imageSize.isEmpty()) {
4199 else if (
auto d = device()) {
4201 d->startTransaction();
4202 auto ba9 = d->read(9);
4203 auto ba5 = d->read(4+1);
4204 auto ba = d->read(8);
4205 d->rollbackTransaction();
4221bool XCFHandler::canRead(
QIODevice *device)
4224 qCDebug(XCFPLUGIN) <<
"XCFHandler::canRead() called with no device";
4231 const qint64 oldPos = device->
pos();
4234 XCFImageFormat::XCFImage::Header header;
4235 bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
4236 ds.setDevice(
nullptr);
4238 device->
seek(oldPos);
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:
4260 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
4261 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
4262 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
4264 qCDebug(XCFPLUGIN) <<
"unsupported precision" << header.precision;
4273 if (format ==
"xcf") {
4284 if (device->
isReadable() && XCFHandler::canRead(device)) {
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)
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)