mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-07-15 11:14:18 -04:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
df82311a10 | |||
1e47a751df | |||
09b9ff7bf9 | |||
ee77e349e3 | |||
4168c46964 | |||
f9f29304d8 | |||
ec0918d962 | |||
dadff2791c |
@ -6,7 +6,6 @@ include:
|
|||||||
file:
|
file:
|
||||||
- /gitlab-templates/linux.yml
|
- /gitlab-templates/linux.yml
|
||||||
- /gitlab-templates/linux-static.yml
|
- /gitlab-templates/linux-static.yml
|
||||||
- /gitlab-templates/android.yml
|
|
||||||
- /gitlab-templates/freebsd.yml
|
- /gitlab-templates/freebsd.yml
|
||||||
- /gitlab-templates/windows.yml
|
- /gitlab-templates/windows.yml
|
||||||
- /gitlab-templates/windows-static.yml
|
- /gitlab-templates/windows-static.yml
|
||||||
|
@ -619,7 +619,15 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
QImage tmpgrayimage = image.convertToFormat(tmpformat);
|
QImage tmpgrayimage = image.convertToFormat(tmpformat);
|
||||||
|
|
||||||
avif = avifImageCreate(tmpgrayimage.width(), tmpgrayimage.height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
|
avif = avifImageCreate(tmpgrayimage.width(), tmpgrayimage.height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
|
||||||
|
#if AVIF_VERSION >= 110000
|
||||||
|
res = avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
||||||
|
if (res != AVIF_RESULT_OK) {
|
||||||
|
qWarning("ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (tmpgrayimage.colorSpace().isValid()) {
|
if (tmpgrayimage.colorSpace().isValid()) {
|
||||||
avif->colorPrimaries = (avifColorPrimaries)1;
|
avif->colorPrimaries = (avifColorPrimaries)1;
|
||||||
@ -806,7 +814,15 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
avif->transferCharacteristics = transfer_to_save;
|
avif->transferCharacteristics = transfer_to_save;
|
||||||
|
|
||||||
if (iccprofile.size() > 0) {
|
if (iccprofile.size() > 0) {
|
||||||
|
#if AVIF_VERSION >= 1000000
|
||||||
|
res = avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||||
|
if (res != AVIF_RESULT_OK) {
|
||||||
|
qWarning("ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
avifRGBImage rgb;
|
avifRGBImage rgb;
|
||||||
@ -971,6 +987,8 @@ bool QAVIFHandler::jumpToNextImage()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avifResult decodeResult;
|
||||||
|
|
||||||
if (m_decoder->imageIndex >= 0) {
|
if (m_decoder->imageIndex >= 0) {
|
||||||
if (m_decoder->imageCount < 2) {
|
if (m_decoder->imageCount < 2) {
|
||||||
m_parseState = ParseAvifSuccess;
|
m_parseState = ParseAvifSuccess;
|
||||||
@ -978,11 +996,16 @@ bool QAVIFHandler::jumpToNextImage()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
|
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
|
||||||
avifDecoderReset(m_decoder);
|
decodeResult = avifDecoderReset(m_decoder);
|
||||||
|
if (decodeResult != AVIF_RESULT_OK) {
|
||||||
|
qWarning("ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
|
||||||
|
m_parseState = ParseAvifError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
avifResult decodeResult = avifDecoderNextImage(m_decoder);
|
decodeResult = avifDecoderNextImage(m_decoder);
|
||||||
|
|
||||||
if (decodeResult != AVIF_RESULT_OK) {
|
if (decodeResult != AVIF_RESULT_OK) {
|
||||||
qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
||||||
|
@ -68,8 +68,8 @@
|
|||||||
class K_IStream : public Imf::IStream
|
class K_IStream : public Imf::IStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
K_IStream(QIODevice *dev, const QByteArray &fileName)
|
K_IStream(QIODevice *dev)
|
||||||
: IStream(fileName.data())
|
: IStream("K_IStream")
|
||||||
, m_dev(dev)
|
, m_dev(dev)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ bool EXRHandler::read(QImage *outImage)
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
||||||
K_IStream istr(device(), QByteArray());
|
K_IStream istr(device());
|
||||||
Imf::RgbaInputFile file(istr);
|
Imf::RgbaInputFile file(istr);
|
||||||
Imath::Box2i dw = file.dataWindow();
|
Imath::Box2i dw = file.dataWindow();
|
||||||
bool isRgba = file.channels() & Imf::RgbaChannels::WRITE_A;
|
bool isRgba = file.channels() & Imf::RgbaChannels::WRITE_A;
|
||||||
@ -271,6 +271,13 @@ bool EXRHandler::canRead(QIODevice *device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENEXR_VERSION_MAJOR == 3 && OPENEXR_VERSION_MINOR > 2
|
||||||
|
// openexpr >= 3.3 uses seek and tell extensively
|
||||||
|
if (device->isSequential()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const QByteArray head = device->peek(4);
|
const QByteArray head = device->peek(4);
|
||||||
|
|
||||||
return Imf::isImfMagic(head.data());
|
return Imf::isImfMagic(head.data());
|
||||||
|
@ -476,8 +476,17 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool hasAlphaChannel = heif_image_handle_has_alpha_channel(handle);
|
|
||||||
const int bit_depth = heif_image_handle_get_luma_bits_per_pixel(handle);
|
const int bit_depth = heif_image_handle_get_luma_bits_per_pixel(handle);
|
||||||
|
|
||||||
|
if (bit_depth < 8) {
|
||||||
|
m_parseState = ParseHeicError;
|
||||||
|
heif_image_handle_release(handle);
|
||||||
|
heif_context_free(ctx);
|
||||||
|
qWarning() << "HEIF image with undefined or unsupported bit depth.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool hasAlphaChannel = heif_image_handle_has_alpha_channel(handle);
|
||||||
heif_chroma chroma;
|
heif_chroma chroma;
|
||||||
|
|
||||||
QImage::Format target_image_format;
|
QImage::Format target_image_format;
|
||||||
@ -502,11 +511,7 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
m_parseState = ParseHeicError;
|
m_parseState = ParseHeicError;
|
||||||
heif_image_handle_release(handle);
|
heif_image_handle_release(handle);
|
||||||
heif_context_free(ctx);
|
heif_context_free(ctx);
|
||||||
if (bit_depth > 0) {
|
qWarning() << "Unsupported bit depth:" << bit_depth;
|
||||||
qWarning() << "Unsupported bit depth:" << bit_depth;
|
|
||||||
} else {
|
|
||||||
qWarning() << "Undefined bit depth.";
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,6 +524,16 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
struct heif_image *img = nullptr;
|
struct heif_image *img = nullptr;
|
||||||
err = heif_decode_image(handle, &img, heif_colorspace_RGB, chroma, decoder_option);
|
err = heif_decode_image(handle, &img, heif_colorspace_RGB, chroma, decoder_option);
|
||||||
|
|
||||||
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
|
if (err.code == heif_error_Invalid_input && err.subcode == heif_suberror_Unknown_NCLX_matrix_coefficients && img == nullptr && buffer.contains("Xiaomi")) {
|
||||||
|
qWarning() << "Non-standard HEIF image with invalid matrix_coefficients, probably made by a Xiaomi device!";
|
||||||
|
|
||||||
|
// second try to decode with strict decoding disabled
|
||||||
|
decoder_option->strict_decoding = 0;
|
||||||
|
err = heif_decode_image(handle, &img, heif_colorspace_RGB, chroma, decoder_option);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (decoder_option) {
|
if (decoder_option) {
|
||||||
heif_decoding_options_free(decoder_option);
|
heif_decoding_options_free(decoder_option);
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ bool QJpegXLHandler::countALLFrames()
|
|||||||
}
|
}
|
||||||
|
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
if (m_basicinfo.uses_original_profile == JXL_FALSE) {
|
if (m_basicinfo.uses_original_profile == JXL_FALSE && m_basicinfo.have_animation == JXL_FALSE) {
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE);
|
JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE);
|
||||||
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
|
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
|
||||||
}
|
}
|
||||||
@ -960,13 +960,7 @@ bool QJpegXLHandler::rewind()
|
|||||||
|
|
||||||
JxlDecoderCloseInput(m_decoder);
|
JxlDecoderCloseInput(m_decoder);
|
||||||
|
|
||||||
if (m_basicinfo.uses_original_profile) {
|
if (m_basicinfo.uses_original_profile == JXL_FALSE && m_basicinfo.have_animation == JXL_FALSE) {
|
||||||
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
|
||||||
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
|
||||||
m_parseState = ParseJpegXLError;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
||||||
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
||||||
m_parseState = ParseJpegXLError;
|
m_parseState = ParseJpegXLError;
|
||||||
@ -983,6 +977,12 @@ bool QJpegXLHandler::rewind()
|
|||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE);
|
JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE);
|
||||||
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
|
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
|
||||||
|
} else {
|
||||||
|
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
||||||
|
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
||||||
|
m_parseState = ParseJpegXLError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -308,6 +308,11 @@ static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (header.BytesPerLine < (header.width() + 7) / 8) {
|
||||||
|
qWarning() << "PCX image has invalid BytesPerLine value";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
return false;
|
return false;
|
||||||
@ -418,6 +423,8 @@ static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width()));
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
return false;
|
return false;
|
||||||
@ -434,7 +441,8 @@ static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint *p = (uint *)img.scanLine(y);
|
uint *p = (uint *)img.scanLine(y);
|
||||||
for (int x = 0; x < header.width(); ++x) {
|
|
||||||
|
for (unsigned int x = 0; x < bpl; ++x) {
|
||||||
p[x] = qRgb(r_buf[x], g_buf[x], b_buf[x]);
|
p[x] = qRgb(r_buf[x], g_buf[x], b_buf[x]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -807,12 +807,10 @@ QVariant RAWHandler::option(ImageOption option) const
|
|||||||
rawProcessor->imgdata.rawparams.shot_select = currentImageNumber();
|
rawProcessor->imgdata.rawparams.shot_select = currentImageNumber();
|
||||||
#endif
|
#endif
|
||||||
if (rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS) {
|
if (rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS) {
|
||||||
if (rawProcessor->unpack() == LIBRAW_SUCCESS) {
|
auto w = libraw_get_iwidth(&rawProcessor->imgdata);
|
||||||
auto w = libraw_get_iwidth(&rawProcessor->imgdata);
|
auto h = libraw_get_iheight(&rawProcessor->imgdata);
|
||||||
auto h = libraw_get_iheight(&rawProcessor->imgdata);
|
// flip & 4: taken from LibRaw code
|
||||||
// flip & 4: taken from LibRaw code
|
v = (rawProcessor->imgdata.sizes.flip & 4) ? QSize(h, w) : QSize(w, h);
|
||||||
v = (rawProcessor->imgdata.sizes.flip & 4) ? QSize(h, w) : QSize(w, h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
d->rollbackTransaction();
|
d->rollbackTransaction();
|
||||||
}
|
}
|
||||||
|
@ -1093,7 +1093,9 @@ bool XCFImageFormat::loadProperty(QDataStream &xcf_io, PropType &type, QByteArra
|
|||||||
size = 0;
|
size = 0;
|
||||||
} else {
|
} else {
|
||||||
xcf_io >> size;
|
xcf_io >> size;
|
||||||
if (size > 256000) {
|
if (size > 256000 * 4) {
|
||||||
|
// NOTE: I didn't find any reference to maximum property dimensions in the specs, so I assume it's just a sanity check.
|
||||||
|
qCDebug(XCFPLUGIN) << "XCF: loadProperty skips" << type << "due to size being too large";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
data = new char[size];
|
data = new char[size];
|
||||||
@ -1672,8 +1674,12 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
|
|||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
uchar *dataPtr = bits + y * bytesPerLine;
|
uchar *dataPtr = bits + y * bytesPerLine;
|
||||||
uchar *alphaPtr = nullptr;
|
uchar *alphaPtr = nullptr;
|
||||||
if (!layer.alpha_tiles.isEmpty())
|
if (layer.alpha_tiles.size() > j && layer.alpha_tiles.at(j).size() > i) {
|
||||||
alphaPtr = layer.alpha_tiles[j][i].scanLine(y);
|
QImage &alphaTile = layer.alpha_tiles[j][i];
|
||||||
|
if (alphaTile.width() >= width && alphaTile.height() > y) {
|
||||||
|
alphaPtr = alphaTile.scanLine(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (bpc == 4) {
|
if (bpc == 4) {
|
||||||
#ifdef USE_FLOAT_IMAGES
|
#ifdef USE_FLOAT_IMAGES
|
||||||
if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
|
if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
|
||||||
@ -1970,6 +1976,12 @@ static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
|
|||||||
*/
|
*/
|
||||||
bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, const GimpPrecision precision)
|
bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, const GimpPrecision precision)
|
||||||
{
|
{
|
||||||
|
auto bpc = bytesPerChannel(precision);
|
||||||
|
if ((bpc == 0) || (bpp % bpc)) {
|
||||||
|
qCDebug(XCFPLUGIN) << "XCF: the stream seems corrupted";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
qint32 width;
|
qint32 width;
|
||||||
qint32 height;
|
qint32 height;
|
||||||
|
|
||||||
@ -2755,10 +2767,10 @@ void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
|
|||||||
// For each tile...
|
// For each tile...
|
||||||
|
|
||||||
for (uint j = 0; j < layer.nrows; j++) {
|
for (uint j = 0; j < layer.nrows; j++) {
|
||||||
uint y = j * TILE_HEIGHT;
|
qint32 y = qint32(j * TILE_HEIGHT);
|
||||||
|
|
||||||
for (uint i = 0; i < layer.ncols; i++) {
|
for (uint i = 0; i < layer.ncols; i++) {
|
||||||
uint x = i * TILE_WIDTH;
|
qint32 x = qint32(i * TILE_WIDTH);
|
||||||
|
|
||||||
// This seems the best place to apply the dissolve because it
|
// This seems the best place to apply the dissolve because it
|
||||||
// depends on the global position of each tile's
|
// depends on the global position of each tile's
|
||||||
@ -3045,7 +3057,7 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
|
|||||||
merge = mergeRGBToRGB;
|
merge = mergeRGBToRGB;
|
||||||
break;
|
break;
|
||||||
case GRAY_GIMAGE:
|
case GRAY_GIMAGE:
|
||||||
if (layer.opacity == OPAQUE_OPACITY) {
|
if (layer.opacity == OPAQUE_OPACITY && xcf_image.image.depth() <= 8) {
|
||||||
merge = mergeGrayToGray;
|
merge = mergeGrayToGray;
|
||||||
} else {
|
} else {
|
||||||
merge = mergeGrayToRGB;
|
merge = mergeGrayToRGB;
|
||||||
@ -3181,10 +3193,10 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
|
|||||||
qCDebug(XCFPLUGIN) << "Using QPainter for mode" << layer.mode;
|
qCDebug(XCFPLUGIN) << "Using QPainter for mode" << layer.mode;
|
||||||
|
|
||||||
for (uint j = 0; j < layer.nrows; j++) {
|
for (uint j = 0; j < layer.nrows; j++) {
|
||||||
uint y = j * TILE_HEIGHT;
|
qint32 y = qint32(j * TILE_HEIGHT);
|
||||||
|
|
||||||
for (uint i = 0; i < layer.ncols; i++) {
|
for (uint i = 0; i < layer.ncols; i++) {
|
||||||
uint x = i * TILE_WIDTH;
|
qint32 x = qint32(i * TILE_WIDTH);
|
||||||
|
|
||||||
QImage &tile = layer.image_tiles[j][i];
|
QImage &tile = layer.image_tiles[j][i];
|
||||||
if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
|
if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
|
||||||
@ -3210,10 +3222,10 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (uint j = 0; j < layer.nrows; j++) {
|
for (uint j = 0; j < layer.nrows; j++) {
|
||||||
uint y = j * TILE_HEIGHT;
|
qint32 y = qint32(j * TILE_HEIGHT);
|
||||||
|
|
||||||
for (uint i = 0; i < layer.ncols; i++) {
|
for (uint i = 0; i < layer.ncols; i++) {
|
||||||
uint x = i * TILE_WIDTH;
|
qint32 x = qint32(i * TILE_WIDTH);
|
||||||
|
|
||||||
// This seems the best place to apply the dissolve because it
|
// This seems the best place to apply the dissolve because it
|
||||||
// depends on the global position of each tile's
|
// depends on the global position of each tile's
|
||||||
@ -3853,6 +3865,9 @@ bool XCFImageFormat::mergeGrayAToRGB(const Layer &layer, uint i, uint j, int k,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (layer.mode) {
|
switch (layer.mode) {
|
||||||
|
case GIMP_LAYER_MODE_NORMAL:
|
||||||
|
case GIMP_LAYER_MODE_NORMAL_LEGACY:
|
||||||
|
break;
|
||||||
case GIMP_LAYER_MODE_MULTIPLY:
|
case GIMP_LAYER_MODE_MULTIPLY:
|
||||||
case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
|
case GIMP_LAYER_MODE_MULTIPLY_LEGACY: {
|
||||||
src = INT_MULT(src, dst);
|
src = INT_MULT(src, dst);
|
||||||
@ -4146,7 +4161,9 @@ bool XCFHandler::canRead() const
|
|||||||
bool XCFHandler::read(QImage *image)
|
bool XCFHandler::read(QImage *image)
|
||||||
{
|
{
|
||||||
XCFImageFormat xcfif;
|
XCFImageFormat xcfif;
|
||||||
return xcfif.readXCF(device(), image);
|
auto ok = xcfif.readXCF(device(), image);
|
||||||
|
m_imageSize = image->size();
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XCFHandler::write(const QImage &)
|
bool XCFHandler::write(const QImage &)
|
||||||
@ -4166,6 +4183,9 @@ QVariant XCFHandler::option(ImageOption option) const
|
|||||||
QVariant v;
|
QVariant v;
|
||||||
|
|
||||||
if (option == QImageIOHandler::Size) {
|
if (option == QImageIOHandler::Size) {
|
||||||
|
if (!m_imageSize.isEmpty()) {
|
||||||
|
return m_imageSize;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* The image structure always starts at offset 0 in the XCF file.
|
* The image structure always starts at offset 0 in the XCF file.
|
||||||
* byte[9] "gimp xcf " File type identification
|
* byte[9] "gimp xcf " File type identification
|
||||||
@ -4178,7 +4198,7 @@ QVariant XCFHandler::option(ImageOption option) const
|
|||||||
* uint32 width Width of canvas
|
* uint32 width Width of canvas
|
||||||
* uint32 height Height of canvas
|
* uint32 height Height of canvas
|
||||||
*/
|
*/
|
||||||
if (auto d = device()) {
|
else if (auto d = device()) {
|
||||||
// transactions works on both random and sequential devices
|
// transactions works on both random and sequential devices
|
||||||
d->startTransaction();
|
d->startTransaction();
|
||||||
auto ba9 = d->read(9); // "gimp xcf "
|
auto ba9 = d->read(9); // "gimp xcf "
|
||||||
|
@ -24,6 +24,13 @@ public:
|
|||||||
QVariant option(QImageIOHandler::ImageOption option) const override;
|
QVariant option(QImageIOHandler::ImageOption option) const override;
|
||||||
|
|
||||||
static bool canRead(QIODevice *device);
|
static bool canRead(QIODevice *device);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
* \brief m_imageSize
|
||||||
|
* Image size cache used by option()
|
||||||
|
*/
|
||||||
|
QSize m_imageSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
class XCFPlugin : public QImageIOPlugin
|
class XCFPlugin : public QImageIOPlugin
|
||||||
|
Reference in New Issue
Block a user