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) {
1097 data =
new char[size];
1098 const quint32 dataRead = xcf_io.
readRawData(data, size);
1099 if (dataRead < size) {
1100 qCDebug(XCFPLUGIN) <<
"XCF: loadProperty read less data than expected" << size << dataRead;
1101 memset(&data[dataRead], 0, size - dataRead);
1105 if (size != 0 && data) {
1122bool XCFImageFormat::loadLayer(
QDataStream &xcf_io, XCFImage &xcf_image)
1124 Layer &layer(xcf_image.layer);
1125 delete[] layer.name;
1127 xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
1130 layer.compression = XcfCompressionType(xcf_image.compression);
1132 if (!loadLayerProperties(xcf_io, layer)) {
1136 qCDebug(XCFPLUGIN) <<
"layer: \"" << layer.name <<
"\", size: " << layer.width <<
" x " << layer.height <<
", type: " << layer.type
1137 <<
", mode: " << layer.mode <<
", opacity: " << layer.opacity <<
", visible: " << layer.visible <<
", offset: " << layer.x_offset <<
", "
1138 << layer.y_offset <<
", compression" << layer.compression;
1144 if (layer.visible == 0) {
1150 layer.hierarchy_offset = readOffsetPtr(xcf_io);
1151 layer.mask_offset = readOffsetPtr(xcf_io);
1153 if (layer.hierarchy_offset < 0) {
1154 qCDebug(XCFPLUGIN) <<
"XCF: negative layer hierarchy_offset";
1158 if (layer.mask_offset < 0) {
1159 qCDebug(XCFPLUGIN) <<
"XCF: negative layer mask_offset";
1166 if (!composeTiles(xcf_image)) {
1169 xcf_io.
device()->
seek(layer.hierarchy_offset);
1175 layer.assignBytes = assignImageBytes;
1177 if (!loadHierarchy(xcf_io, layer, xcf_image.header.precision)) {
1181 if (layer.mask_offset != 0) {
1183 if (layer.apply_mask == 9) {
1184 layer.apply_mask = 1;
1189 if (!loadMask(xcf_io, layer, xcf_image.header.precision)) {
1194 layer.apply_mask = 0;
1201 if (!xcf_image.initialized) {
1202 if (!initializeImage(xcf_image)) {
1205 copyLayerToImage(xcf_image);
1206 xcf_image.initialized =
true;
1208 const QColorSpace colorspaceBefore = xcf_image.image.colorSpace();
1209 mergeLayerIntoImage(xcf_image);
1210 if (xcf_image.image.colorSpace() != colorspaceBefore) {
1211 qCDebug(XCFPLUGIN) <<
"Converting color space back to" << colorspaceBefore <<
"after layer composition";
1212 xcf_image.image.convertToColorSpace(colorspaceBefore);
1226bool XCFImageFormat::loadLayerProperties(
QDataStream &xcf_io, Layer &layer)
1233 if (!loadProperty(xcf_io, type, bytes, rawType)) {
1234 qCDebug(XCFPLUGIN) <<
"XCF: error loading layer properties";
1244 case PROP_ACTIVE_LAYER:
1245 layer.active =
true;
1249 property >> layer.opacity;
1250 layer.opacity = std::min(layer.opacity, 255u);
1253 case PROP_FLOAT_OPACITY:
1256 if (bytes.
size() == 4) {
1257 layer.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
1259 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
1264 property >> layer.visible;
1268 property >> layer.linked;
1271 case PROP_LOCK_ALPHA:
1272 property >> layer.preserve_transparency;
1275 case PROP_APPLY_MASK:
1276 property >> layer.apply_mask;
1279 case PROP_EDIT_MASK:
1280 property >> layer.edit_mask;
1283 case PROP_SHOW_MASK:
1284 property >> layer.show_mask;
1288 property >> layer.x_offset >> layer.y_offset;
1292 property >> layer.mode;
1293 if (layer.mode >= GIMP_LAYER_MODE_COUNT) {
1294 qCDebug(XCFPLUGIN) <<
"Found layer with unsupported mode" << LayerModeType(layer.mode) <<
"Defaulting to mode 0";
1295 layer.mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
1300 property >> layer.tattoo;
1303 case PROP_COMPOSITE_SPACE:
1304 property >> layer.compositeSpace;
1305 if (layer.compositeSpace < 0) {
1306 layer.compositeSpace = GimpColorSpace(-layer.compositeSpace);
1310 case PROP_COMPOSITE_MODE:
1311 property >> layer.compositeMode;
1312 if (layer.compositeMode < 0) {
1313 layer.compositeMode = XCFImageFormat::GimpCompositeMode(-layer.compositeMode);
1317 case PROP_BLEND_SPACE:
1318 property >> layer.blendSpace;
1319 if (layer.blendSpace) {
1320 layer.blendSpace = GimpColorSpace(-layer.blendSpace);
1325 case PROP_COLOR_TAG:
1329 case PROP_LOCK_CONTENT:
1330 case PROP_LOCK_POSITION:
1334 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented layer property " <<
type <<
"(" << rawType <<
")"
1335 <<
", size " << bytes.
size();
1346bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
1348 Layer &layer(xcf_image.layer);
1350 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
1351 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
1353 qCDebug(XCFPLUGIN) <<
"IMAGE: height=" << xcf_image.header.height <<
", width=" << xcf_image.header.width;
1354 qCDebug(XCFPLUGIN) <<
"LAYER: height=" << layer.height <<
", width=" << layer.width;
1355 qCDebug(XCFPLUGIN) <<
"LAYER: rows=" << layer.nrows <<
", columns=" << layer.ncols;
1362 if ((
sizeof(
void *) == 4 && qint64(layer.width) * layer.height > 16384 * 16384)) {
1363 qCWarning(XCFPLUGIN) <<
"On 32-bits programs the maximum layer size is limited to" << 16384 <<
"x" << 16384 <<
"px";
1366 if (layer.width > MAX_IMAGE_WIDTH || layer.height > MAX_IMAGE_HEIGHT) {
1367 qCWarning(XCFPLUGIN) <<
"The maximum layer size is limited to" << MAX_IMAGE_WIDTH <<
"x" << MAX_IMAGE_HEIGHT <<
"px";
1374 if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
1375 if (qint64(layer.width) * layer.height > 16384 * 16384) {
1376 qCWarning(XCFPLUGIN) <<
"Euristic sanity check: the image may be corrupted!";
1381#ifndef XCF_QT5_SUPPORT
1386 qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
1393 layer.image_tiles.resize(layer.nrows);
1395 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1396 layer.alpha_tiles.resize(layer.nrows);
1399 if (layer.mask_offset != 0) {
1400 layer.mask_tiles.resize(layer.nrows);
1403 for (uint j = 0; j < layer.nrows; j++) {
1404 layer.image_tiles[j].resize(layer.ncols);
1406 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1407 layer.alpha_tiles[j].resize(layer.ncols);
1410 if (layer.mask_offset != 0) {
1411 layer.mask_tiles[j].resize(layer.ncols);
1415 const QImage::Format format = layer.qimageFormat(xcf_image.header.precision);
1417 for (uint j = 0; j < layer.nrows; j++) {
1418 for (uint i = 0; i < layer.ncols; i++) {
1419 uint tile_width = (i + 1) * TILE_WIDTH <= layer.width ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
1421 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
1426 switch (layer.type) {
1429 layer.image_tiles[j][i] =
QImage(tile_width, tile_height, format);
1430 if (layer.image_tiles[j][i].isNull()) {
1433 layer.image_tiles[j][i].setColorCount(0);
1438 if (layer.image_tiles[j][i].isNull()) {
1441 layer.image_tiles[j][i].setColorCount(256);
1442 setGrayPalette(layer.image_tiles[j][i]);
1447 layer.image_tiles[j][i].setColorCount(256);
1448 if (layer.image_tiles[j][i].isNull()) {
1451 setGrayPalette(layer.image_tiles[j][i]);
1454 if (layer.alpha_tiles[j][i].isNull()) {
1457 layer.alpha_tiles[j][i].setColorCount(256);
1458 setGrayPalette(layer.alpha_tiles[j][i]);
1461 case INDEXED_GIMAGE:
1463 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1464 if (layer.image_tiles[j][i].isNull()) {
1467 setPalette(xcf_image, layer.image_tiles[j][i]);
1470 case INDEXEDA_GIMAGE:
1472 if (layer.image_tiles[j][i].isNull()) {
1475 layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
1476 setPalette(xcf_image, layer.image_tiles[j][i]);
1479 if (layer.alpha_tiles[j][i].isNull()) {
1482 layer.alpha_tiles[j][i].setColorCount(256);
1483 setGrayPalette(layer.alpha_tiles[j][i]);
1485 if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
1486 qCWarning(XCFPLUGIN) <<
"Selected wrong tile format" << layer.image_tiles[j][i].format() <<
"expected" << format;
1490#ifndef DISABLE_TILE_PROFILE
1491 switch (xcf_image.header.precision) {
1492 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1493 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1494 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1495 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1496 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1497 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1500 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
1501 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
1502 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
1503 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
1504 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
1505 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
1508 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
1509 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
1510 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
1511 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
1512 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
1513 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
1518 if (layer.mask_offset != 0) {
1520 layer.mask_tiles[j][i].setColorCount(256);
1521 if (layer.mask_tiles[j][i].isNull()) {
1524 setGrayPalette(layer.mask_tiles[j][i]);
1537void XCFImageFormat::setGrayPalette(
QImage &image)
1542 for (
int i = 0; i < 256; i++) {
1543 grayTable[i] = qRgb(i, i, i);
1555void XCFImageFormat::setPalette(XCFImage &xcf_image,
QImage &image)
1557 Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size());
1568void XCFImageFormat::setImageParasites(
const XCFImage &xcf_image,
QImage &image)
1570 auto&& p = xcf_image.parasites;
1571 auto keys = p.keys();
1572 for (
auto &&key :
std::as_const(keys)) {
1573 auto value = p.value(key);
1574 if (value.isEmpty())
1583 if (key == QStringLiteral(
"icc-profile")) {
1595 if (key == QStringLiteral(
"gimp-comment")) {
1604 if (key == QStringLiteral(
"gimp-image-metadata")) {
1619 if (key == QStringLiteral(
"gimp-metadata")) {
1630#ifdef DISABLE_IMAGE_PROFILE
1633 switch (xcf_image.header.precision) {
1634 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
1635 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
1636 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
1637 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
1638 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
1639 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
1657bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
1659 QImage &image = layer.image_tiles[j][i];
1661 const uchar *tile = layer.tile;
1662 const int width = image.
width();
1663 const int height = image.
height();
1665 uchar *bits = image.
bits();
1668 if (layer.type == GRAYA_GIMAGE || layer.type == GRAY_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1669 auto bpc = bytesPerChannel(precision);
1670 for (
int y = 0; y < height; y++) {
1671 uchar *dataPtr = bits + y * bytesPerLine;
1672 uchar *alphaPtr =
nullptr;
1673 if (!layer.alpha_tiles.isEmpty())
1674 alphaPtr = layer.alpha_tiles[j][i].scanLine(y);
1676#ifdef USE_FLOAT_IMAGES
1677 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1678 for (
int x = 0; x < width; x++) {
1679 auto src = (
const quint16 *)tile;
1680 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1682 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1683 tile +=
sizeof(quint16) * 2;
1685 tile +=
sizeof(quint16);
1689 for (
int x = 0; x < width; x++) {
1690 auto src = (
const float *)tile;
1691 *dataPtr++ = qFromBigEndian<float>(src[0]) * 255;
1693 *alphaPtr++ = qFromBigEndian<float>(src[1]) * 255;
1694 tile +=
sizeof(float) * 2;
1696 tile +=
sizeof(float);
1701 for (
int x = 0; x < width; x++) {
1702 auto src = (
const quint16 *)tile;
1703 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1705 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1706 tile +=
sizeof(quint16) * 2;
1708 tile +=
sizeof(quint16);
1712 }
else if (bpc == 2) {
1713#ifdef USE_FLOAT_IMAGES
1714 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
1715 for (
int x = 0; x < width; x++) {
1716 auto src = (
const quint16 *)tile;
1717 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1719 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1720 tile +=
sizeof(QRgb);
1723 for (
int x = 0; x < width; x++) {
1725 *dataPtr++ = qFromBigEndian<qfloat16>(src[0]) * 255;
1727 *alphaPtr++ = qFromBigEndian<qfloat16>(src[1]) * 255;
1728 tile +=
sizeof(QRgb);
1732 for (
int x = 0; x < width; x++) {
1733 auto src = (
const quint16 *)tile;
1734 *dataPtr++ = qFromBigEndian<quint16>(src[0]) / 257;
1736 *alphaPtr++ = qFromBigEndian<quint16>(src[1]) / 257;
1737 tile +=
sizeof(QRgb);
1741 for (
int x = 0; x < width; x++) {
1743 *dataPtr++ = tile[0];
1745 *alphaPtr++ = tile[1];
1746 tile +=
sizeof(QRgb);
1753 switch (image.
format()) {
1755 for (
int y = 0; y < height; y++) {
1756 uchar *dataPtr = image.
scanLine(y);
1757 for (
int x = 0; x < width * 4; x += 4, tile += 4) {
1758 dataPtr[x + 0] = tile[0];
1759 dataPtr[x + 1] = tile[1];
1760 dataPtr[x + 2] = tile[2];
1761 dataPtr[x + 3] = 255;
1766 for (
int y = 0; y < height; y++) {
1767 const size_t bpl = width * 4;
1768 memcpy(image.
scanLine(y), tile + y * bpl, bpl);
1772 for (
int y = 0; y < height; y++) {
1773 quint16 *dataPtr = (quint16 *)image.
scanLine(y);
1774 const size_t bpl = width *
sizeof(
QRgba64);
1775 const quint16 *src = (
const quint16 *)(tile + y * bpl);
1776 for (
int x = 0; x < width * 4; x += 4) {
1777 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1778 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1779 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1780 dataPtr[x + 3] = 65535;
1784#ifdef USE_FLOAT_IMAGES
1786 for (
int y = 0; y < height; y++) {
1788 const qfloat16 *src = (
const qfloat16 *)(tile + y * width *
sizeof(QRgbaFloat16));
1789 for (
int x = 0; x < width * 4; x += 4) {
1790 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1791 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1792 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1798 static_assert(
sizeof(QRgbaFloat16) ==
sizeof(
QRgba64),
"Different sizes for float and int 16 bit pixels");
1801 for (
int y = 0; y < height; y++) {
1802 const size_t bpl = width *
sizeof(
QRgba64);
1803 qFromBigEndian<qint16>(tile + y * bpl, width * 4, image.
scanLine(y));
1806#ifdef USE_FLOAT_IMAGES
1808 for (
int y = 0; y < height; y++) {
1809 const size_t bpl = width *
sizeof(QRgbaFloat32);
1810 qFromBigEndian<qint32>(tile + y * bpl, width * 4, image.
scanLine(y));
1814 for (
int y = 0; y < height; y++) {
1815 float *dataPtr = (
float *)image.
scanLine(y);
1816 const float *src = (
const float *)(tile + y * width *
sizeof(QRgbaFloat32));
1817 for (
int x = 0; x < width * 4; x += 4) {
1818 dataPtr[x + 0] = qFromBigEndian(src[x + 0]);
1819 dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
1820 dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
1821 dataPtr[x + 3] = 1.f;
1827 for (
int y = 0; y < height; y++) {
1828 uchar *dataPtr = bits + y * bytesPerLine;
1829 for (
int x = 0; x < width; x++) {
1830 *dataPtr++ = tile[0];
1831 tile +=
sizeof(QRgb);
1836 qCWarning(XCFPLUGIN) <<
"Unhandled image format" << image.
format() <<
"and/or layer type" << layer.type;
1851bool XCFImageFormat::loadHierarchy(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
1857 xcf_io >> width >> height >> bpp;
1858 const qint64 offset = readOffsetPtr(xcf_io);
1860 qCDebug(XCFPLUGIN) <<
"width" << width <<
"height" << height <<
"bpp" << bpp <<
"offset" << offset;
1863 qCDebug(XCFPLUGIN) <<
"XCF: negative hierarchy offset";
1867 const bool isMask = layer.assignBytes == assignMaskBytes;
1870 switch (layer.type) {
1872 if (bpp != 3 * bytesPerChannel(precision)) {
1873 qCDebug(XCFPLUGIN) <<
"Found layer of type RGB but with bpp != 3" << bpp;
1881 if (bpp != 4 * bytesPerChannel(precision)) {
1882 qCDebug(XCFPLUGIN) <<
"Found layer of type RGBA but with bpp != 4, got" << bpp <<
"bpp";
1890 if (bpp != 1 * bytesPerChannel(precision)) {
1891 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray but with bpp != 1" << bpp;
1896 if (bpp != 2 * bytesPerChannel(precision)) {
1897 qCDebug(XCFPLUGIN) <<
"Found layer of type Gray+Alpha but with bpp != 2" << bpp;
1904 case INDEXED_GIMAGE:
1905 if (bpp != 1 * bytesPerChannel(precision)) {
1906 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed but with bpp != 1" << bpp;
1910 case INDEXEDA_GIMAGE:
1911 if (bpp != 2 * bytesPerChannel(precision)) {
1912 qCDebug(XCFPLUGIN) <<
"Found layer of type Indexed+Alpha but with bpp != 2" << bpp;
1921 if (bpp > 4 * bytesPerChannel(precision)) {
1922 qCDebug(XCFPLUGIN) <<
"bpp is" << bpp <<
"We don't support layers with bpp > 4";
1935 qCDebug(XCFPLUGIN) <<
"XCF: read failure on layer " << layer.name <<
" level offsets";
1938 }
while (junk != 0);
1940 qint64 saved_pos = xcf_io.
device()->
pos();
1943 if (!loadLevel(xcf_io, layer, bpp, precision)) {
1951template<
typename SourceFormat>
1952static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
1954 SourceFormat *source = (SourceFormat *)(input);
1955 for (quint64 offset = 0; offset < outputSize; offset++) {
1956 ((uint16_t *)output)[offset] = qToBigEndian(quint16(qBound(0., qFromBigEndian<SourceFormat>(source[offset]) * 65535. + 0.5, 65535.)));
1969bool XCFImageFormat::loadLevel(
QDataStream &xcf_io, Layer &layer, qint32 bpp,
const GimpPrecision precision)
1974 xcf_io >> width >> height;
1975 qint64 offset = readOffsetPtr(xcf_io);
1978 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
1985 for (uint j = 0; j < layer.nrows; j++) {
1986 for (uint i = 0; i < layer.ncols; i++) {
1988 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
1996 bool needConvert =
true;
1997 switch (precision) {
1998#ifdef USE_FLOAT_IMAGES
1999 case GIMP_PRECISION_HALF_LINEAR:
2000 case GIMP_PRECISION_HALF_NON_LINEAR:
2001 case GIMP_PRECISION_HALF_PERCEPTUAL:
2002 case GIMP_PRECISION_FLOAT_LINEAR:
2003 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2004 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2006 case GIMP_PRECISION_U8_LINEAR:
2007 case GIMP_PRECISION_U8_NON_LINEAR:
2008 case GIMP_PRECISION_U8_PERCEPTUAL:
2009 case GIMP_PRECISION_U16_LINEAR:
2010 case GIMP_PRECISION_U16_NON_LINEAR:
2011 case GIMP_PRECISION_U16_PERCEPTUAL:
2012 needConvert =
false;
2018 const uint blockSize = TILE_WIDTH * TILE_HEIGHT * bpp * 1.5;
2022 buffer.
resize(blockSize * (bpp == 2 ? 2 : 1));
2024 for (uint j = 0; j < layer.nrows; j++) {
2025 for (uint i = 0; i < layer.ncols; i++) {
2027 qCDebug(XCFPLUGIN) <<
"XCF: incorrect number of tiles in layer " << layer.name;
2031 qint64 saved_pos = xcf_io.
device()->
pos();
2032 qint64 offset2 = readOffsetPtr(xcf_io);
2035 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2041 offset2 = offset + blockSize;
2045 qint64 bytesParsed = 0;
2047 switch (layer.compression) {
2048 case COMPRESS_NONE: {
2050 qCDebug(XCFPLUGIN) <<
"Component reading not supported yet";
2053 const int data_size = bpp * TILE_WIDTH * TILE_HEIGHT;
2054 if (data_size >
int(blockSize)) {
2055 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2058 int dataRead = xcf_io.
readRawData(
reinterpret_cast<char *
>(layer.tile), data_size);
2059 if (dataRead < data_size) {
2060 qCDebug(XCFPLUGIN) <<
"short read, expected" << data_size <<
"got" << dataRead;
2063 bytesParsed = dataRead;
2066 case COMPRESS_RLE: {
2067 int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
2068 const uint data_size = size * bpp;
2070 if (data_size >=
unsigned(buffer.
size())) {
2071 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" << buffer.
size() <<
"but need" << data_size;
2075 if (data_size >
sizeof(layer.tile)) {
2076 qCDebug(XCFPLUGIN) <<
"Tile data too big, we can only fit" <<
sizeof(layer.tile) <<
"but need" << data_size;
2079 if (blockSize >
sizeof(layer.tile)) {
2080 qCWarning(XCFPLUGIN) <<
"Too small tiles" <<
sizeof(layer.tile) <<
"this image requires" << blockSize <<
sizeof(
QRgba64) << bpp;
2084 if (!loadTileRLE(xcf_io, needConvert ? buffer.
data() : layer.tile, size, offset2 - offset, bpp, &bytesParsed)) {
2085 qCDebug(XCFPLUGIN) <<
"Failed to read RLE";
2091 qCDebug(XCFPLUGIN) <<
"Unhandled compression" << layer.compression;
2096 if (bytesParsed > buffer.
size()) {
2097 qCDebug(XCFPLUGIN) <<
"Invalid number of bytes parsed" << bytesParsed << buffer.
size();
2101 switch (precision) {
2102 case GIMP_PRECISION_U32_LINEAR:
2103 case GIMP_PRECISION_U32_NON_LINEAR:
2104 case GIMP_PRECISION_U32_PERCEPTUAL: {
2105 quint32 *source = (quint32 *)(buffer.
data());
2106 for (quint64 offset = 0, len = buffer.
size() /
sizeof(quint32); offset < len; ++offset) {
2107 ((quint16 *)layer.tile)[offset] = qToBigEndian<quint16>(qFromBigEndian(source[offset]) / 65537);
2111#ifndef USE_FLOAT_IMAGES
2112 case GIMP_PRECISION_HALF_LINEAR:
2113 case GIMP_PRECISION_HALF_NON_LINEAR:
2114 case GIMP_PRECISION_HALF_PERCEPTUAL:
2115 convertFloatTo16Bit<qfloat16>(layer.tile, buffer.
size() /
sizeof(
qfloat16), buffer.
data());
2117 case GIMP_PRECISION_FLOAT_LINEAR:
2118 case GIMP_PRECISION_FLOAT_NON_LINEAR:
2119 case GIMP_PRECISION_FLOAT_PERCEPTUAL:
2120 convertFloatTo16Bit<float>(layer.tile, buffer.
size() /
sizeof(
float), buffer.
data());
2122 case GIMP_PRECISION_DOUBLE_LINEAR:
2123 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2124 case GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2125 convertFloatTo16Bit<double>(layer.tile, buffer.
size() /
sizeof(
double), buffer.
data());
2128 case GIMP_PRECISION_DOUBLE_LINEAR:
2129 case GIMP_PRECISION_DOUBLE_NON_LINEAR:
2130 case GIMP_PRECISION_DOUBLE_PERCEPTUAL: {
2131 double *source = (
double *)(buffer.
data());
2132 for (quint64 offset = 0, len = buffer.
size() /
sizeof(double); offset < len; ++offset) {
2133 ((
float *)layer.tile)[offset] = qToBigEndian<float>(
float(qFromBigEndian(source[offset])));
2139 qCWarning(XCFPLUGIN) <<
"Unsupported precision" << precision;
2147 if (!layer.assignBytes(layer, i, j, precision)) {
2152 offset = readOffsetPtr(xcf_io);
2155 qCDebug(XCFPLUGIN) <<
"XCF: negative level offset";
2170bool XCFImageFormat::loadMask(
QDataStream &xcf_io, Layer &layer,
const GimpPrecision precision)
2176 xcf_io >> width >> height >>
name;
2180 if (!loadChannelProperties(xcf_io, layer)) {
2184 const qint64 hierarchy_offset = readOffsetPtr(xcf_io);
2186 if (hierarchy_offset < 0) {
2187 qCDebug(XCFPLUGIN) <<
"XCF: negative mask hierarchy_offset";
2192 layer.assignBytes = assignMaskBytes;
2194 if (!loadHierarchy(xcf_io, layer, precision)) {
2224bool XCFImageFormat::loadTileRLE(
QDataStream &xcf_io, uchar *tile,
int image_size,
int data_length, qint32 bpp, qint64 *bytesParsed)
2230 uchar *xcfdatalimit;
2232 int step =
sizeof(QRgb);
2238 step =
sizeof(QRgb);
2242 step =
sizeof(QRgb) * 2;
2246 step =
sizeof(QRgb) * 4;
2249 qCDebug(XCFPLUGIN) <<
"XCF: unhandled bit depth" << bpp;
2253 if (data_length < 0 || data_length >
int(TILE_WIDTH * TILE_HEIGHT * step * 1.5)) {
2254 qCDebug(XCFPLUGIN) <<
"XCF: invalid tile data length" << data_length;
2258 xcfdata = xcfodata =
new uchar[data_length];
2260 const int dataRead = xcf_io.
readRawData((
char *)xcfdata, data_length);
2261 if (dataRead <= 0) {
2263 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile" << dataRead;
2267 if (dataRead < data_length) {
2268 memset(&xcfdata[dataRead], 0, data_length - dataRead);
2273 qCDebug(XCFPLUGIN) <<
"XCF: read failure on tile";
2277 xcfdatalimit = &xcfodata[data_length - 1];
2279 for (
int i = 0; i < bpp; ++i) {
2282 int size = image_size;
2285 if (xcfdata > xcfdatalimit) {
2289 uchar val = *xcfdata++;
2292 if (length >= 128) {
2293 length = 255 - (length - 1);
2294 if (length == 128) {
2295 if (xcfdata >= xcfdatalimit) {
2299 length = (*xcfdata << 8) + xcfdata[1];
2310 if (&xcfdata[length - 1] > xcfdatalimit) {
2314 while (length-- > 0) {
2320 if (length == 128) {
2321 if (xcfdata >= xcfdatalimit) {
2325 length = (*xcfdata << 8) + xcfdata[1];
2335 if (xcfdata > xcfdatalimit) {
2339 qintptr totalLength = qintptr(data - tile) + length * step;
2340 if (totalLength >= image_size * step * 1.5) {
2341 qCDebug(XCFPLUGIN) <<
"Ran out of space when trying to unpack image, over:" << totalLength - image_size << totalLength << image_size
2348 while (length-- > 0) {
2355 *bytesParsed = qintptr(data - tile);
2362 qCDebug(XCFPLUGIN) <<
"The run length encoding could not be decoded properly";
2374bool XCFImageFormat::loadChannelProperties(
QDataStream &xcf_io, Layer &layer)
2381 if (!loadProperty(xcf_io, type, bytes, rawType)) {
2382 qCDebug(XCFPLUGIN) <<
"XCF: error loading channel properties";
2393 property >> layer.mask_channel.opacity;
2394 layer.mask_channel.opacity = std::min(layer.mask_channel.opacity, 255u);
2397 case PROP_FLOAT_OPACITY:
2400 if (bytes.
size() == 4) {
2401 layer.mask_channel.opacityFloat = qFromBigEndian(*
reinterpret_cast<float *
>(bytes.
data()));
2403 qCDebug(XCFPLUGIN) <<
"XCF: Invalid data size for float:" << bytes.
size();
2408 property >> layer.mask_channel.visible;
2411 case PROP_SHOW_MASKED:
2412 property >> layer.mask_channel.show_masked;
2416 property >> layer.mask_channel.red >> layer.mask_channel.green >> layer.mask_channel.blue;
2419 case PROP_FLOAT_COLOR:
2420 property >> layer.mask_channel.redF >> layer.mask_channel.greenF >> layer.mask_channel.blueF;
2424 property >> layer.mask_channel.tattoo;
2432 case PROP_COLOR_TAG:
2436 case PROP_LOCK_CONTENT:
2437 case PROP_LOCK_POSITION:
2441 qCDebug(XCFPLUGIN) <<
"XCF: unimplemented channel property " <<
type <<
"(" << rawType <<
")"
2442 <<
", size " << bytes.
size();
2454bool XCFImageFormat::assignMaskBytes(Layer &layer, uint i, uint j,
const GimpPrecision &precision)
2456 QImage &image = layer.mask_tiles[j][i];
2457 if (image.
depth() != 8) {
2458 qCWarning(XCFPLUGIN) <<
"invalid bytes per pixel, we only do 8 bit masks" << image.
depth();
2462 uchar *tile = layer.tile;
2463 const int width = image.
width();
2464 const int height = image.
height();
2466 uchar *bits = image.
bits();
2467 auto bpc = bytesPerChannel(precision);
2472 for (
int y = 0; y < height; y++) {
2473 uchar *dataPtr = bits + y * bytesPerLine;
2474#ifdef USE_FLOAT_IMAGES
2476 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2477 for (
int x = 0; x < width; x++) {
2478 *dataPtr++ = qFromBigEndian<quint16>(*(
const quint16 *)tile) / 257;
2479 tile +=
sizeof(quint16);
2482 for (
int x = 0; x < width; x++) {
2483 *dataPtr++ = qFromBigEndian<float>(*(
const float *)tile) * 255;
2484 tile +=
sizeof(QRgb);
2487 }
else if (bpc == 2) {
2489 if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
2490 for (
int x = 0; x < width; x++) {
2491 *dataPtr++ = qFromBigEndian<quint16>(*(
const quint16 *)tile) / 257;
2492 tile +=
sizeof(QRgb);
2495 for (
int x = 0; x < width; x++) {
2496 *dataPtr++ = qFromBigEndian<qfloat16>(*(
const qfloat16 *)tile) * 255;
2497 tile +=
sizeof(QRgb);
2503 for (
int x = 0; x < width; x++) {
2504 *dataPtr++ = qFromBigEndian<quint16>(*(
const quint16 *)tile) / 257;
2505 tile +=
sizeof(QRgb);
2507 }
else if (bpc == 4) {
2508 for (
int x = 0; x < width; x++) {
2509 *dataPtr++ = qFromBigEndian<quint16>(*(
const quint16 *)tile) / 257;
2510 tile +=
sizeof(quint16);
2515 for (
int x = 0; x < width; x++) {
2516 *dataPtr++ = tile[0];
2517 tile +=
sizeof(QRgb);
2553bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
2556 Layer &layer(xcf_image.layer);
2557 QImage &image(xcf_image.image);
2559 switch (layer.type) {
2561 if (layer.opacity == OPAQUE_OPACITY) {
2567 setGrayPalette(image);
2575 image = imageAlloc(xcf_image.header.width, xcf_image.header.height, xcf_image.qimageFormat());
2586 case INDEXED_GIMAGE:
2599 if (xcf_image.num_colors <= 2) {
2606 setPalette(xcf_image, image);
2607 }
else if (xcf_image.num_colors <= 256) {
2614 setPalette(xcf_image, image);
2618 case INDEXEDA_GIMAGE:
2619 if (xcf_image.num_colors == 1) {
2621 xcf_image.num_colors++;
2622 xcf_image.palette.resize(xcf_image.num_colors);
2623 xcf_image.palette[1] = xcf_image.palette[0];
2624 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2632 setPalette(xcf_image, image);
2633 }
else if (xcf_image.num_colors < 256) {
2635 xcf_image.num_colors++;
2636 xcf_image.palette.resize(xcf_image.num_colors);
2637 for (
int c = xcf_image.num_colors - 1; c >= 1; c--) {
2638 xcf_image.palette[c] = xcf_image.palette[c - 1];
2641 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
2648 setPalette(xcf_image, image);
2657 image.
fill(qRgba(255, 255, 255, 0));
2661 if (image.
format() != xcf_image.qimageFormat()) {
2662 qCWarning(XCFPLUGIN) <<
"Selected wrong format:" << image.
format() <<
"expected" << layer.qimageFormat(xcf_image.header.precision);
2668#ifndef DISABLE_IMAGE_PROFILE
2669 switch (xcf_image.header.precision) {
2670 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
2671 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
2672 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
2673 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
2674 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
2675 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
2678 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
2679 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
2680 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
2681 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
2682 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
2683 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
2686 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
2687 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
2688 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
2689 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
2690 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
2691 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
2697 if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
2698 const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
2699 if (dpmx >
float(std::numeric_limits<int>::max())) {
2702 const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
2703 if (dpmy >
float(std::numeric_limits<int>::max())) {
2717void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
2719 Layer &layer(xcf_image.layer);
2720 QImage &image(xcf_image.image);
2721 PixelCopyOperation
copy =
nullptr;
2723 switch (layer.type) {
2726 copy = copyRGBToRGB;
2729 if (layer.opacity == OPAQUE_OPACITY) {
2730 copy = copyGrayToGray;
2732 copy = copyGrayToRGB;
2736 copy = copyGrayAToRGB;
2738 case INDEXED_GIMAGE:
2739 copy = copyIndexedToIndexed;
2741 case INDEXEDA_GIMAGE:
2742 if (xcf_image.image.depth() <= 8) {
2743 copy = copyIndexedAToIndexed;
2745 copy = copyIndexedAToRGB;
2755 for (uint j = 0; j < layer.nrows; j++) {
2756 uint y = j * TILE_HEIGHT;
2758 for (uint i = 0; i < layer.ncols; i++) {
2759 uint x = i * TILE_WIDTH;
2766 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
2767 if (!random_table_initialized) {
2768 initializeRandomTable();
2769 random_table_initialized =
true;
2771 if (layer.type == RGBA_GIMAGE) {
2772 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
2775 else if (layer.type == GRAYA_GIMAGE) {
2776 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
2781 if (copy == copyRGBToRGB && layer.apply_mask != 1) {
2783 painter.setOpacity(layer.opacity / 255.0);
2785 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
2786 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
2787 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
2792 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
2793 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
2794 int m = x + k + layer.x_offset;
2795 int n = y + l + layer.y_offset;
2797 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
2801 (*copy)(layer, i, j, k, l, image, m, n);
2821void XCFImageFormat::copyRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2823 if (image.
depth() == 32) {
2824 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2825 uchar src_a = layer.opacity;
2827 if (layer.type == RGBA_GIMAGE) {
2828 src_a = INT_MULT(src_a, qAlpha(src));
2833 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2834 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2837 image.
setPixel(m, n, qRgba(src, src_a));
2838 }
else if (image.
depth() == 64) {
2839 QRgba64 src = layer.image_tiles[j][i].pixelColor(k, l).rgba64();
2840 quint16 src_a = layer.opacity;
2842 if (layer.type == RGBA_GIMAGE) {
2843 src_a = INT_MULT(src_a, qAlpha(src));
2848 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2849 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2868void XCFImageFormat::copyGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2870 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2887void XCFImageFormat::copyGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2889 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2890 uchar src_a = layer.opacity;
2891 image.
setPixel(m, n, qRgba(src, src_a));
2907void XCFImageFormat::copyGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2909 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2910 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2911 src_a = INT_MULT(src_a, layer.opacity);
2915 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2916 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2919 image.
setPixel(m, n, qRgba(src, src_a));
2933void XCFImageFormat::copyIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2935 int src = layer.image_tiles[j][i].pixelIndex(k, l);
2950void XCFImageFormat::copyIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2952 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
2953 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2954 src_a = INT_MULT(src_a, layer.opacity);
2956 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2957 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2982void XCFImageFormat::copyIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
2984 QRgb src = layer.image_tiles[j][i].pixel(k, l);
2985 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
2986 src_a = INT_MULT(src_a, layer.opacity);
2989 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
2990 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
2997 src_a = OPAQUE_OPACITY;
3000 image.
setPixel(m, n, qRgba(src, src_a));
3007void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
3009 Layer &layer(xcf_image.layer);
3010 QImage &image(xcf_image.image);
3012 PixelMergeOperation
merge =
nullptr;
3014 if (!layer.opacity) {
3018 if (layer.blendSpace == XCFImageFormat::AutoColorSpace) {
3019 qCDebug(XCFPLUGIN) <<
"Auto blend space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3020 layer.blendSpace = XCFImageFormat::RgbLinearSpace;
3023 if (layer.blendSpace != XCFImageFormat::RgbLinearSpace) {
3024 qCDebug(XCFPLUGIN) <<
"Unimplemented blend color space" << layer.blendSpace;
3026 qCDebug(XCFPLUGIN) <<
"Blend color space" << layer.blendSpace;
3028 if (layer.compositeSpace == XCFImageFormat::AutoColorSpace) {
3029 qCDebug(XCFPLUGIN) <<
"Auto composite space, defaulting to RgbLinearSpace (same as Gimp when writing this)";
3030 layer.compositeSpace = XCFImageFormat::RgbLinearSpace;
3033 if (layer.compositeSpace != XCFImageFormat::RgbLinearSpace) {
3034 qCDebug(XCFPLUGIN) <<
"Unimplemented composite color space" << layer.compositeSpace;
3036 if (layer.compositeMode != XCFImageFormat::CompositeUnion) {
3037 qCDebug(XCFPLUGIN) <<
"Unhandled composite mode" << layer.compositeMode;
3040 switch (layer.type) {
3043 merge = mergeRGBToRGB;
3046 if (layer.opacity == OPAQUE_OPACITY) {
3047 merge = mergeGrayToGray;
3049 merge = mergeGrayToRGB;
3053 if (xcf_image.image.depth() <= 8) {
3054 merge = mergeGrayAToGray;
3056 merge = mergeGrayAToRGB;
3059 case INDEXED_GIMAGE:
3060 merge = mergeIndexedToIndexed;
3062 case INDEXEDA_GIMAGE:
3063 if (xcf_image.image.depth() <= 8) {
3064 merge = mergeIndexedAToIndexed;
3066 merge = mergeIndexedAToRGB;
3074 if (merge == mergeRGBToRGB && layer.apply_mask != 1) {
3075 int painterMode = -1;
3076 switch (layer.mode) {
3077 case GIMP_LAYER_MODE_NORMAL:
3078 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3081 case GIMP_LAYER_MODE_MULTIPLY:
3082 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3085 case GIMP_LAYER_MODE_SCREEN:
3086 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3089 case GIMP_LAYER_MODE_OVERLAY:
3090 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3093 case GIMP_LAYER_MODE_DIFFERENCE:
3094 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3097 case GIMP_LAYER_MODE_DARKEN_ONLY:
3098 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3101 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3102 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3105 case GIMP_LAYER_MODE_DODGE:
3106 case GIMP_LAYER_MODE_DODGE_LEGACY:
3109 case GIMP_LAYER_MODE_BURN:
3110 case GIMP_LAYER_MODE_BURN_LEGACY:
3113 case GIMP_LAYER_MODE_HARDLIGHT:
3114 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
3117 case GIMP_LAYER_MODE_SOFTLIGHT:
3118 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
3121 case GIMP_LAYER_MODE_ADDITION:
3122 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3125 case GIMP_LAYER_MODE_EXCLUSION:
3130 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3131 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
3132 case GIMP_LAYER_MODE_GRAIN_MERGE:
3133 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
3134 case GIMP_LAYER_MODE_COLOR_ERASE:
3135 case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY:
3136 case GIMP_LAYER_MODE_LCH_HUE:
3137 case GIMP_LAYER_MODE_LCH_CHROMA:
3138 case GIMP_LAYER_MODE_LCH_COLOR:
3139 case GIMP_LAYER_MODE_LCH_LIGHTNESS:
3140 case GIMP_LAYER_MODE_BEHIND:
3141 case GIMP_LAYER_MODE_BEHIND_LEGACY:
3142 case GIMP_LAYER_MODE_SUBTRACT:
3143 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3144 case GIMP_LAYER_MODE_HSV_HUE:
3145 case GIMP_LAYER_MODE_HSV_SATURATION:
3146 case GIMP_LAYER_MODE_HSL_COLOR:
3147 case GIMP_LAYER_MODE_HSV_VALUE:
3148 case GIMP_LAYER_MODE_DIVIDE:
3149 case GIMP_LAYER_MODE_VIVID_LIGHT:
3150 case GIMP_LAYER_MODE_PIN_LIGHT:
3151 case GIMP_LAYER_MODE_LINEAR_LIGHT:
3152 case GIMP_LAYER_MODE_HARD_MIX:
3153 case GIMP_LAYER_MODE_LINEAR_BURN:
3154 case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY:
3155 case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY:
3156 case GIMP_LAYER_MODE_LUMINANCE:
3157 case GIMP_LAYER_MODE_ERASE:
3158 case GIMP_LAYER_MODE_MERGE:
3159 case GIMP_LAYER_MODE_SPLIT:
3160 case GIMP_LAYER_MODE_PASS_THROUGH:
3161 case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
3162 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
3163 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY:
3164 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
3165 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3166 qCDebug(XCFPLUGIN) <<
"No QPainter equivalent to" << layer.mode;
3170 case GIMP_LAYER_MODE_DISSOLVE:
3171 case GIMP_LAYER_MODE_COUNT:
3175 if (painterMode != -1) {
3177 painter.setOpacity(layer.opacity / 255.0);
3179 qCDebug(XCFPLUGIN) <<
"Using QPainter for mode" << layer.mode;
3181 for (uint j = 0; j < layer.nrows; j++) {
3182 uint y = j * TILE_HEIGHT;
3184 for (uint i = 0; i < layer.ncols; i++) {
3185 uint x = i * TILE_WIDTH;
3187 QImage &tile = layer.image_tiles[j][i];
3188 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3189 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3190 painter.drawImage(x + layer.x_offset, y + layer.y_offset, tile);
3199#ifndef DISABLE_IMAGE_PROFILE_CONV
3201 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3205 qCDebug(XCFPLUGIN) <<
"Converting to composite color space" << layer.compositeSpace;
3210 for (uint j = 0; j < layer.nrows; j++) {
3211 uint y = j * TILE_HEIGHT;
3213 for (uint i = 0; i < layer.ncols; i++) {
3214 uint x = i * TILE_WIDTH;
3221 if (layer.mode == GIMP_LAYER_MODE_DISSOLVE) {
3222 if (!random_table_initialized) {
3223 initializeRandomTable();
3224 random_table_initialized =
true;
3226 if (layer.type == RGBA_GIMAGE) {
3227 dissolveRGBPixels(layer.image_tiles[j][i], x, y);
3230 else if (layer.type == GRAYA_GIMAGE) {
3231 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
3236 if (merge == mergeRGBToRGB && layer.apply_mask != 1 && layer.mode == GIMP_LAYER_MODE_NORMAL_LEGACY) {
3238 painter.setOpacity(layer.opacity / 255.0);
3240 if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
3241 y + layer.y_offset < MAX_IMAGE_HEIGHT) {
3242 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
3247#ifndef DISABLE_TILE_PROFILE_CONV
3248 QImage &tile = layer.image_tiles[j][i];
3257 for (
int l = 0; l < layer.image_tiles[j][i].height(); l++) {
3258 for (
int k = 0; k < layer.image_tiles[j][i].width(); k++) {
3259 int m = x + k + layer.x_offset;
3260 int n = y + l + layer.y_offset;
3262 if (m < 0 || m >= image.
width() || n < 0 || n >= image.
height()) {
3266 if (!(*merge)(layer, i, j, k, l, image, m, n)) {
3288bool XCFImageFormat::mergeRGBToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3290 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3291 QRgb dst = image.
pixel(m, n);
3293 uchar src_r = qRed(src);
3294 uchar src_g = qGreen(src);
3295 uchar src_b = qBlue(src);
3296 uchar src_a = qAlpha(src);
3298 uchar dst_r = qRed(dst);
3299 uchar dst_g = qGreen(dst);
3300 uchar dst_b = qBlue(dst);
3301 uchar dst_a = qAlpha(dst);
3307 switch (layer.mode) {
3308 case GIMP_LAYER_MODE_NORMAL:
3309 case GIMP_LAYER_MODE_NORMAL_LEGACY:
3311 case GIMP_LAYER_MODE_MULTIPLY:
3312 case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
3313 src_r = INT_MULT(src_r, dst_r);
3314 src_g = INT_MULT(src_g, dst_g);
3315 src_b = INT_MULT(src_b, dst_b);
3316 src_a = qMin(src_a, dst_a);
3318 case GIMP_LAYER_MODE_DIVIDE:
3319 case GIMP_LAYER_MODE_DIVIDE_LEGACY:
3320 src_r = qMin((dst_r * 256) / (1 + src_r), 255);
3321 src_g = qMin((dst_g * 256) / (1 + src_g), 255);
3322 src_b = qMin((dst_b * 256) / (1 + src_b), 255);
3323 src_a = qMin(src_a, dst_a);
3325 case GIMP_LAYER_MODE_SCREEN:
3326 case GIMP_LAYER_MODE_SCREEN_LEGACY:
3327 src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
3328 src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
3329 src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
3330 src_a = qMin(src_a, dst_a);
3332 case GIMP_LAYER_MODE_OVERLAY:
3333 case GIMP_LAYER_MODE_OVERLAY_LEGACY:
3334 src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
3335 src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
3336 src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
3337 src_a = qMin(src_a, dst_a);
3339 case GIMP_LAYER_MODE_DIFFERENCE:
3340 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
3341 src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
3342 src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
3343 src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
3344 src_a = qMin(src_a, dst_a);
3346 case GIMP_LAYER_MODE_ADDITION:
3347 case GIMP_LAYER_MODE_ADDITION_LEGACY:
3348 src_r = add_lut(dst_r, src_r);
3349 src_g = add_lut(dst_g, src_g);
3350 src_b = add_lut(dst_b, src_b);
3351 src_a = qMin(src_a, dst_a);
3353 case GIMP_LAYER_MODE_SUBTRACT:
3354 case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
3355 src_r = dst_r > src_r ? dst_r - src_r : 0;
3356 src_g = dst_g > src_g ? dst_g - src_g : 0;
3357 src_b = dst_b > src_b ? dst_b - src_b : 0;
3358 src_a = qMin(src_a, dst_a);
3360 case GIMP_LAYER_MODE_DARKEN_ONLY:
3361 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
3362 src_r = dst_r < src_r ? dst_r : src_r;
3363 src_g = dst_g < src_g ? dst_g : src_g;
3364 src_b = dst_b < src_b ? dst_b : src_b;
3365 src_a = qMin(src_a, dst_a);
3367 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3368 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
3369 src_r = dst_r < src_r ? src_r : dst_r;
3370 src_g = dst_g < src_g ? src_g : dst_g;
3371 src_b = dst_b < src_b ? src_b : dst_b;
3372 src_a = qMin(src_a, dst_a);
3374 case GIMP_LAYER_MODE_HSV_HUE:
3375 case GIMP_LAYER_MODE_HSV_HUE_LEGACY: {
3376 uchar new_r = dst_r;
3377 uchar new_g = dst_g;
3378 uchar new_b = dst_b;
3380 RGBTOHSV(src_r, src_g, src_b);
3381 RGBTOHSV(new_r, new_g, new_b);
3385 HSVTORGB(new_r, new_g, new_b);
3390 src_a = qMin(src_a, dst_a);
3392 case GIMP_LAYER_MODE_HSV_SATURATION:
3393 case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: {
3394 uchar new_r = dst_r;
3395 uchar new_g = dst_g;
3396 uchar new_b = dst_b;
3398 RGBTOHSV(src_r, src_g, src_b);
3399 RGBTOHSV(new_r, new_g, new_b);
3403 HSVTORGB(new_r, new_g, new_b);
3408 src_a = qMin(src_a, dst_a);
3410 case GIMP_LAYER_MODE_HSV_VALUE:
3411 case GIMP_LAYER_MODE_HSV_VALUE_LEGACY: {
3412 uchar new_r = dst_r;
3413 uchar new_g = dst_g;
3414 uchar new_b = dst_b;
3416 RGBTOHSV(src_r, src_g, src_b);
3417 RGBTOHSV(new_r, new_g, new_b);
3421 HSVTORGB(new_r, new_g, new_b);
3426 src_a = qMin(src_a, dst_a);
3428 case GIMP_LAYER_MODE_HSL_COLOR:
3429 case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: {
3430 uchar new_r = dst_r;
3431 uchar new_g = dst_g;
3432 uchar new_b = dst_b;
3434 RGBTOHLS(src_r, src_g, src_b);
3435 RGBTOHLS(new_r, new_g, new_b);
3440 HLSTORGB(new_r, new_g, new_b);
3445 src_a = qMin(src_a, dst_a);
3447 case GIMP_LAYER_MODE_DODGE:
3448 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3453 src_r = (uchar)qMin(tmp, 255u);
3457 src_g = (uchar)qMin(tmp, 255u);
3461 src_b = (uchar)qMin(tmp, 255u);
3463 src_a = qMin(src_a, dst_a);
3465 case GIMP_LAYER_MODE_BURN:
3466 case GIMP_LAYER_MODE_BURN_LEGACY: {
3469 tmp = (255 - dst_r) << 8;
3471 src_r = (uchar)qMin(tmp, 255u);
3472 src_r = 255 - src_r;
3474 tmp = (255 - dst_g) << 8;
3476 src_g = (uchar)qMin(tmp, 255u);
3477 src_g = 255 - src_g;
3479 tmp = (255 - dst_b) << 8;
3481 src_b = (uchar)qMin(tmp, 255u);
3482 src_b = 255 - src_b;
3484 src_a = qMin(src_a, dst_a);
3486 case GIMP_LAYER_MODE_HARDLIGHT:
3487 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3490 tmp = ((int)255 - dst_r) * ((int)255 - ((src_r - 128) << 1));
3491 src_r = (uchar)qMin(255 - (tmp >> 8), 255u);
3493 tmp = (int)dst_r * ((
int)src_r << 1);
3494 src_r = (uchar)qMin(tmp >> 8, 255u);
3498 tmp = ((int)255 - dst_g) * ((int)255 - ((src_g - 128) << 1));
3499 src_g = (uchar)qMin(255 - (tmp >> 8), 255u);
3501 tmp = (int)dst_g * ((
int)src_g << 1);
3502 src_g = (uchar)qMin(tmp >> 8, 255u);
3506 tmp = ((int)255 - dst_b) * ((int)255 - ((src_b - 128) << 1));
3507 src_b = (uchar)qMin(255 - (tmp >> 8), 255u);
3509 tmp = (int)dst_b * ((
int)src_b << 1);
3510 src_b = (uchar)qMin(tmp >> 8, 255u);
3512 src_a = qMin(src_a, dst_a);
3514 case GIMP_LAYER_MODE_SOFTLIGHT:
3515 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3519 tmpM = INT_MULT(dst_r, src_r);
3520 tmpS = 255 - INT_MULT((255 - dst_r), (255 - src_r));
3521 src_r = INT_MULT((255 - dst_r), tmpM) + INT_MULT(dst_r, tmpS);
3523 tmpM = INT_MULT(dst_g, src_g);
3524 tmpS = 255 - INT_MULT((255 - dst_g), (255 - src_g));
3525 src_g = INT_MULT((255 - dst_g), tmpM) + INT_MULT(dst_g, tmpS);
3527 tmpM = INT_MULT(dst_b, src_b);
3528 tmpS = 255 - INT_MULT((255 - dst_b), (255 - src_b));
3529 src_b = INT_MULT((255 - dst_b), tmpM) + INT_MULT(dst_b, tmpS);
3531 src_a = qMin(src_a, dst_a);
3533 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3534 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3537 tmp = dst_r - src_r + 128;
3538 tmp = qMin(tmp, 255);
3542 tmp = dst_g - src_g + 128;
3543 tmp = qMin(tmp, 255);
3547 tmp = dst_b - src_b + 128;
3548 tmp = qMin(tmp, 255);
3552 src_a = qMin(src_a, dst_a);
3554 case GIMP_LAYER_MODE_GRAIN_MERGE:
3555 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3558 tmp = dst_r + src_r - 128;
3559 tmp = qMin(tmp, 255);
3563 tmp = dst_g + src_g - 128;
3564 tmp = qMin(tmp, 255);
3568 tmp = dst_b + src_b - 128;
3569 tmp = qMin(tmp, 255);
3573 src_a = qMin(src_a, dst_a);
3575 case GIMP_LAYER_MODE_LINEAR_LIGHT: {
3577 src_r = qBound(0, dst_r + 2 * src_r - 255, 255);
3579 src_r = qBound(0, dst_r + 2 * (src_r - 128), 255);
3582 src_g = qBound(0, dst_g + 2 * src_g - 255, 255);
3584 src_g = qBound(0, dst_g + 2 * (src_g - 127), 255);
3587 src_b = qBound(0, dst_b + 2 * src_b - 255, 255);
3589 src_b = qBound(0, dst_b + 2 * (src_b - 127), 255);
3592 case GIMP_LAYER_MODE_VIVID_LIGHT: {
3595 A[0] = src_r / 255.;
3596 A[1] = src_g / 255.;
3597 A[2] = src_b / 255.;
3599 B[0] = dst_r / 255.;
3600 B[1] = dst_g / 255.;
3601 B[2] = dst_b / 255.;
3603 for (
int i = 0; i < 3; i++) {
3606 C[i] = 1.f - (1.f - B[i]) / (2.f * A[i]);
3610 C[i] = B[i] / (2.f * (1.f - A[i]));
3614 src_r = qBound(0.f, C[0] * 255.f, 255.f);
3615 src_g = qBound(0.f, C[1] * 255.f, 255.f);
3616 src_b = qBound(0.f, C[2] * 255.f, 255.f);
3619 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3623 src_a = INT_MULT(src_a, layer.opacity);
3627 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3628 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3635 new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3637 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3638 float dst_ratio = 1.0 - src_ratio;
3640 new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
3641 new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
3642 new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
3644 if (!modeAffectsSourceAlpha(layer.mode)) {
3648 image.
setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
3663bool XCFImageFormat::mergeGrayToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3665 int src = layer.image_tiles[j][i].pixelIndex(k, l);
3681bool XCFImageFormat::mergeGrayAToGray(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3683 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3686 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3692 switch (layer.mode) {
3693 case GIMP_LAYER_MODE_MULTIPLY:
3694 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3695 src = INT_MULT(src, dst);
3697 case GIMP_LAYER_MODE_DIVIDE:
3698 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3699 src = qMin((dst * 256) / (1 + src), 255);
3701 case GIMP_LAYER_MODE_SCREEN:
3702 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3703 src = 255 - INT_MULT(255 - dst, 255 - src);
3705 case GIMP_LAYER_MODE_OVERLAY:
3706 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3707 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3709 case GIMP_LAYER_MODE_DIFFERENCE:
3710 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3711 src = dst > src ? dst - src : src - dst;
3713 case GIMP_LAYER_MODE_ADDITION:
3714 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3715 src = add_lut(dst, src);
3717 case GIMP_LAYER_MODE_SUBTRACT:
3718 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3719 src = dst > src ? dst - src : 0;
3721 case GIMP_LAYER_MODE_DARKEN_ONLY:
3722 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3723 src = dst < src ? dst : src;
3725 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3726 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3727 src = dst < src ? src : dst;
3729 case GIMP_LAYER_MODE_DODGE:
3730 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3731 uint tmp = dst << 8;
3733 src = (uchar)qMin(tmp, 255u);
3735 case GIMP_LAYER_MODE_BURN:
3736 case GIMP_LAYER_MODE_BURN_LEGACY: {
3737 uint tmp = (255 - dst) << 8;
3739 src = (uchar)qMin(tmp, 255u);
3742 case GIMP_LAYER_MODE_HARDLIGHT:
3743 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3746 tmp = ((int)255 - dst) * ((int)255 - ((src - 128) << 1));
3747 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3749 tmp = (int)dst * ((
int)src << 1);
3750 src = (uchar)qMin(tmp >> 8, 255u);
3753 case GIMP_LAYER_MODE_SOFTLIGHT:
3754 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3758 tmpM = INT_MULT(dst, src);
3759 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3760 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3763 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3764 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3767 tmp = dst - src + 128;
3768 tmp = qMin(tmp, 255);
3773 case GIMP_LAYER_MODE_GRAIN_MERGE:
3774 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3777 tmp = dst + src - 128;
3778 tmp = qMin(tmp, 255);
3784 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3788 src_a = INT_MULT(src_a, layer.opacity);
3792 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3793 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3796 uchar new_a = OPAQUE_OPACITY;
3798 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3799 float dst_ratio = 1.0 - src_ratio;
3801 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3820bool XCFImageFormat::mergeGrayToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3822 QRgb src = layer.image_tiles[j][i].pixel(k, l);
3823 uchar src_a = layer.opacity;
3824 image.
setPixel(m, n, qRgba(src, src_a));
3841bool XCFImageFormat::mergeGrayAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3843 int src = qGray(layer.image_tiles[j][i].pixel(k, l));
3844 int dst = qGray(image.
pixel(m, n));
3846 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
3847 uchar dst_a = qAlpha(image.
pixel(m, n));
3853 switch (layer.mode) {
3854 case GIMP_LAYER_MODE_MULTIPLY:
3855 case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
3856 src = INT_MULT(src, dst);
3857 src_a = qMin(src_a, dst_a);
3859 case GIMP_LAYER_MODE_DIVIDE:
3860 case GIMP_LAYER_MODE_DIVIDE_LEGACY: {
3861 src = qMin((dst * 256) / (1 + src), 255);
3862 src_a = qMin(src_a, dst_a);
3864 case GIMP_LAYER_MODE_SCREEN:
3865 case GIMP_LAYER_MODE_SCREEN_LEGACY: {
3866 src = 255 - INT_MULT(255 - dst, 255 - src);
3867 src_a = qMin(src_a, dst_a);
3869 case GIMP_LAYER_MODE_OVERLAY:
3870 case GIMP_LAYER_MODE_OVERLAY_LEGACY: {
3871 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
3872 src_a = qMin(src_a, dst_a);
3874 case GIMP_LAYER_MODE_DIFFERENCE:
3875 case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: {
3876 src = dst > src ? dst - src : src - dst;
3877 src_a = qMin(src_a, dst_a);
3879 case GIMP_LAYER_MODE_ADDITION:
3880 case GIMP_LAYER_MODE_ADDITION_LEGACY: {
3881 src = add_lut(dst, src);
3882 src_a = qMin(src_a, dst_a);
3884 case GIMP_LAYER_MODE_SUBTRACT:
3885 case GIMP_LAYER_MODE_SUBTRACT_LEGACY: {
3886 src = dst > src ? dst - src : 0;
3887 src_a = qMin(src_a, dst_a);
3889 case GIMP_LAYER_MODE_DARKEN_ONLY:
3890 case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: {
3891 src = dst < src ? dst : src;
3892 src_a = qMin(src_a, dst_a);
3894 case GIMP_LAYER_MODE_LIGHTEN_ONLY:
3895 case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: {
3896 src = dst < src ? src : dst;
3897 src_a = qMin(src_a, dst_a);
3899 case GIMP_LAYER_MODE_DODGE:
3900 case GIMP_LAYER_MODE_DODGE_LEGACY: {
3901 uint tmp = dst << 8;
3903 src = (uchar)qMin(tmp, 255u);
3904 src_a = qMin(src_a, dst_a);
3906 case GIMP_LAYER_MODE_BURN:
3907 case GIMP_LAYER_MODE_BURN_LEGACY: {
3908 uint tmp = (255 - dst) << 8;
3910 src = (uchar)qMin(tmp, 255u);
3912 src_a = qMin(src_a, dst_a);
3914 case GIMP_LAYER_MODE_HARDLIGHT:
3915 case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: {
3918 tmp = ((int)255 - dst) * ((int)255 - ((src - 128) << 1));
3919 src = (uchar)qMin(255 - (tmp >> 8), 255u);
3921 tmp = (int)dst * ((
int)src << 1);
3922 src = (uchar)qMin(tmp >> 8, 255u);
3924 src_a = qMin(src_a, dst_a);
3926 case GIMP_LAYER_MODE_SOFTLIGHT:
3927 case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: {
3931 tmpM = INT_MULT(dst, src);
3932 tmpS = 255 - INT_MULT((255 - dst), (255 - src));
3933 src = INT_MULT((255 - dst), tmpM) + INT_MULT(dst, tmpS);
3935 src_a = qMin(src_a, dst_a);
3937 case GIMP_LAYER_MODE_GRAIN_EXTRACT:
3938 case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: {
3941 tmp = dst - src + 128;
3942 tmp = qMin(tmp, 255);
3946 src_a = qMin(src_a, dst_a);
3948 case GIMP_LAYER_MODE_GRAIN_MERGE:
3949 case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: {
3952 tmp = dst + src - 128;
3953 tmp = qMin(tmp, 255);
3957 src_a = qMin(src_a, dst_a);
3960 qCWarning(XCFPLUGIN) <<
"Unhandled mode" << layer.mode;
3964 src_a = INT_MULT(src_a, layer.opacity);
3967 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
3968 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
3971 uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
3973 const float src_ratio = new_a == 0 ? 1.0 : (float)src_a / new_a;
3974 float dst_ratio = 1.0 - src_ratio;
3976 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
3978 if (!modeAffectsSourceAlpha(layer.mode)) {
3982 image.
setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
3997bool XCFImageFormat::mergeIndexedToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
3999 int src = layer.image_tiles[j][i].pixelIndex(k, l);
4015bool XCFImageFormat::mergeIndexedAToIndexed(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4017 uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
4018 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4019 src_a = INT_MULT(src_a, layer.opacity);
4021 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4022 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4045bool XCFImageFormat::mergeIndexedAToRGB(
const Layer &layer, uint i, uint j,
int k,
int l,
QImage &image,
int m,
int n)
4047 QRgb src = layer.image_tiles[j][i].pixel(k, l);
4048 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
4049 src_a = INT_MULT(src_a, layer.opacity);
4052 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (
int)j && layer.mask_tiles[j].size() > (
int)i) {
4053 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
4060 src_a = OPAQUE_OPACITY;
4063 image.
setPixel(m, n, qRgba(src, src_a));
4074void XCFImageFormat::dissolveRGBPixels(
QImage &image,
int x,
int y)
4079 for (
int l = 0; l < image.
height(); l++) {
4080 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4082 for (
int k = 0; k < x; k++) {
4083 RandomTable::rand_r(&next);
4086 for (
int k = 0; k < image.
width(); k++) {
4087 int rand_val = RandomTable::rand_r(&next) & 0xff;
4088 QRgb pixel = image.
pixel(k, l);
4090 if (rand_val > qAlpha(pixel)) {
4091 image.
setPixel(k, l, qRgba(pixel, 0));
4106void XCFImageFormat::dissolveAlphaPixels(
QImage &image,
int x,
int y)
4111 for (
int l = 0; l < image.
height(); l++) {
4112 unsigned int next = randomTable.values[(l + y) % RANDOM_TABLE_SIZE];
4114 for (
int k = 0; k < x; k++) {
4115 RandomTable::rand_r(&next);
4118 for (
int k = 0; k < image.
width(); k++) {
4119 int rand_val = RandomTable::rand_r(&next) & 0xff;
4122 if (rand_val > alpha) {
4131XCFHandler::XCFHandler()
4135bool XCFHandler::canRead()
const
4137 if (canRead(device())) {
4144bool XCFHandler::read(
QImage *image)
4146 XCFImageFormat xcfif;
4147 return xcfif.readXCF(device(), image);
4150bool XCFHandler::write(
const QImage &)
4155bool XCFHandler::supportsOption(ImageOption option)
const
4162QVariant XCFHandler::option(ImageOption option)
const
4179 if (
auto d = device()) {
4181 d->startTransaction();
4182 auto ba9 = d->read(9);
4183 auto ba5 = d->read(4+1);
4184 auto ba = d->read(8);
4185 d->rollbackTransaction();
4201bool XCFHandler::canRead(
QIODevice *device)
4204 qCDebug(XCFPLUGIN) <<
"XCFHandler::canRead() called with no device";
4211 const qint64 oldPos = device->
pos();
4214 XCFImageFormat::XCFImage::Header header;
4215 bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
4216 ds.setDevice(
nullptr);
4217 device->
seek(oldPos);
4222 switch (header.precision) {
4223 case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
4224 case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
4225 case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
4226 case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
4227 case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
4228 case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
4229 case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
4230 case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
4231 case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
4232 case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
4233 case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
4234 case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
4235 case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
4236 case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
4237 case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
4239 case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
4240 case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
4241 case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
4243 qCDebug(XCFPLUGIN) <<
"unsupported precision" << header.precision;
4251 qint64 readBytes = device->
read(head,
sizeof(head));
4252 if (readBytes !=
sizeof(head)) {
4254 while (readBytes > 0) {
4255 device->
ungetChar(head[readBytes-- - 1]);
4258 device->
seek(oldPos);
4264 while (readBytes > 0) {
4265 device->
ungetChar(head[readBytes-- - 1]);
4268 device->
seek(oldPos);
4271 return qstrncmp(head,
"gimp xcf", 8) == 0;
4276 if (format ==
"xcf") {
4287 if (device->
isReadable() && XCFHandler::canRead(device)) {
4304#include "moc_xcf_p.cpp"
AKONADI_MIME_EXPORT const char Header[]
Type type(const QSqlDatabase &db)
QFlags< Capability > Capabilities
QStringView merge(QStringView lhs, QStringView rhs)
const QList< QKeySequence > & next()
QString name(StandardShortcut id)
const QList< QKeySequence > & copy()
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
QByteArray read(qint64 maxSize)
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)