diff --git a/autotests/write/format/pic/Format_RGBA16FPx4.pic b/autotests/write/format/pic/Format_RGBA16FPx4.pic index 1680941..e6ca58d 100644 Binary files a/autotests/write/format/pic/Format_RGBA16FPx4.pic and b/autotests/write/format/pic/Format_RGBA16FPx4.pic differ diff --git a/autotests/write/format/rgb/Format_RGBA16FPx4.rgb b/autotests/write/format/rgb/Format_RGBA16FPx4.rgb index bd86117..7c5d9d8 100644 Binary files a/autotests/write/format/rgb/Format_RGBA16FPx4.rgb and b/autotests/write/format/rgb/Format_RGBA16FPx4.rgb differ diff --git a/src/imageformats/CMakeLists.txt b/src/imageformats/CMakeLists.txt index 3bda87f..e02347f 100644 --- a/src/imageformats/CMakeLists.txt +++ b/src/imageformats/CMakeLists.txt @@ -101,11 +101,11 @@ endif() ################################## -kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp) +kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp scanlineconverter.cpp) ################################## -kimageformats_add_plugin(kimg_pic SOURCES pic.cpp) +kimageformats_add_plugin(kimg_pic SOURCES pic.cpp scanlineconverter.cpp) ################################## @@ -129,7 +129,7 @@ kimageformats_add_plugin(kimg_ras SOURCES ras.cpp) ################################## -kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp) +kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp scanlineconverter.cpp) ################################## diff --git a/src/imageformats/pcx.cpp b/src/imageformats/pcx.cpp index 20a0cfe..5a9d8de 100644 --- a/src/imageformats/pcx.cpp +++ b/src/imageformats/pcx.cpp @@ -6,6 +6,7 @@ */ #include "pcx_p.h" +#include "scanlineconverter_p.h" #include "util_p.h" #include @@ -621,30 +622,38 @@ static bool writeLine(QDataStream &s, QByteArray &buf) return (s.status() == QDataStream::Ok); } -static bool writeImage1(QImage &img, QDataStream &s, PCXHEADER &header) +static bool writeImage1(const QImage &image, QDataStream &s, PCXHEADER &header) { - if (img.format() != QImage::Format_Mono) { - img.convertTo(QImage::Format_Mono); - } - if (img.isNull() || img.colorCount() < 1) { + auto tfmt = image.format(); + if (tfmt != QImage::Format_Mono) + tfmt = QImage::Format_Mono; + + if (image.isNull() || image.colorCount() < 1) { return false; } - auto rgb = img.color(0); - auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127; + + ScanLineConverter scl(tfmt); + if (image.height() > 0) { + scl.convertedScanLine(image, 0); // required to calculate bytesPerLine + } header.Bpp = 1; header.NPlanes = 1; - header.BytesPerLine = img.bytesPerLine(); + header.BytesPerLine = scl.bytesPerLine(); + if (header.BytesPerLine == 0) { + header.BytesPerLine = image.bytesPerLine(); + } if (header.BytesPerLine == 0) { return false; } s << header; + auto rgb = image.color(0); + auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127; QByteArray buf(header.BytesPerLine, 0); - for (int y = 0; y < header.height(); ++y) { - auto p = img.constScanLine(y); + auto p = scl.convertedScanLine(image, y); // Invert as QImage uses reverse palette for monochrome images? for (int i = 0; i < header.BytesPerLine; ++i) { @@ -658,7 +667,7 @@ static bool writeImage1(QImage &img, QDataStream &s, PCXHEADER &header) return true; } -static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header) +static bool writeImage4(const QImage &image, QDataStream &s, PCXHEADER &header) { header.Bpp = 1; header.NPlanes = 4; @@ -668,7 +677,7 @@ static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header) } for (int i = 0; i < 16; ++i) { - header.ColorMap.setColor(i, img.color(i)); + header.ColorMap.setColor(i, image.color(i)); } s << header; @@ -680,7 +689,7 @@ static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header) } for (int y = 0; y < header.height(); ++y) { - auto p = img.constScanLine(y); + auto p = image.constScanLine(y); for (int i = 0; i < 4; ++i) { buf[i].fill(0); @@ -703,18 +712,23 @@ static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header) return true; } -static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header) +static bool writeImage8(const QImage &image, QDataStream &s, PCXHEADER &header) { - if (img.format() == QImage::Format_Grayscale16) { - img.convertTo(QImage::Format_Grayscale8); - } - if (img.isNull()) { - return false; + auto tfmt = image.format(); + if (tfmt == QImage::Format_Grayscale16) + tfmt = QImage::Format_Grayscale8; + + ScanLineConverter scl(tfmt); + if (image.height() > 0) { + scl.convertedScanLine(image, 0); // required to calculate bytesPerLine } header.Bpp = 8; header.NPlanes = 1; - header.BytesPerLine = img.bytesPerLine(); + header.BytesPerLine = scl.bytesPerLine(); + if (header.BytesPerLine == 0) { + header.BytesPerLine = image.bytesPerLine(); + } if (header.BytesPerLine == 0) { return false; } @@ -724,7 +738,7 @@ static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header) QByteArray buf(header.BytesPerLine, 0); for (int y = 0; y < header.height(); ++y) { - auto p = img.constScanLine(y); + auto p = scl.convertedScanLine(image, y); for (int i = 0; i < header.BytesPerLine; ++i) { buf[i] = p[i]; @@ -741,18 +755,18 @@ static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header) // Write palette for (int i = 0; i < 256; ++i) { - if (img.format() != QImage::Format_Indexed8) + if (tfmt != QImage::Format_Indexed8) s << RGB::from(qRgb(i, i, i)); else - s << RGB::from(img.color(i)); + s << RGB::from(image.color(i)); } return (s.status() == QDataStream::Ok); } -static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header) +static bool writeImage24(const QImage &image, QDataStream &s, PCXHEADER &header) { - auto hasAlpha = img.hasAlphaChannel(); + auto hasAlpha = image.hasAlphaChannel(); header.Bpp = 8; header.NPlanes = hasAlpha ? 4 : 3; header.BytesPerLine = header.width(); @@ -760,11 +774,17 @@ static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header) return false; } - if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB32) { - img.convertTo(hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32); +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + auto cs = image.colorSpace(); + auto tcs = QColorSpace(); + auto tfmt = image.format(); + if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) { + tcs = QColorSpace(QColorSpace::SRgb); + tfmt = QImage::Format_RGB32; } - if (img.isNull()) { - return false; +#endif + if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) { + tfmt = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; } s << header; @@ -774,8 +794,10 @@ static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header) QByteArray b_buf(header.width(), 0); QByteArray a_buf(header.width(), char(0xFF)); + ScanLineConverter scl(tfmt); + scl.setTargetColorSpace(tcs); for (int y = 0; y < header.height(); ++y) { - auto p = reinterpret_cast(img.constScanLine(y)); + auto p = reinterpret_cast(scl.convertedScanLine(image, y)); for (int x = 0; x < header.width(); ++x) { auto &&rgb = p[x]; @@ -877,16 +899,8 @@ bool PCXHandler::write(const QImage &image) QDataStream s(device()); s.setByteOrder(QDataStream::LittleEndian); - QImage img = image; -#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) - auto cs = image.colorSpace(); - if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) { - img = image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb)); - } -#endif - - const int w = img.width(); - const int h = img.height(); + const int w = image.width(); + const int h = image.height(); if (w > 65536 || h > 65536) { return false; @@ -907,14 +921,14 @@ bool PCXHandler::write(const QImage &image) header.PaletteInfo = 1; auto ok = false; - if (img.depth() == 1) { - ok = writeImage1(img, s, header); - } else if (img.format() == QImage::Format_Indexed8 && img.colorCount() <= 16) { - ok = writeImage4(img, s, header); - } else if (img.depth() == 8 || img.format() == QImage::Format_Grayscale16) { - ok = writeImage8(img, s, header); - } else if (img.depth() >= 16) { - ok = writeImage24(img, s, header); + if (image.depth() == 1) { + ok = writeImage1(image, s, header); + } else if (image.format() == QImage::Format_Indexed8 && image.colorCount() <= 16) { + ok = writeImage4(image, s, header); + } else if (image.depth() == 8 || image.format() == QImage::Format_Grayscale16) { + ok = writeImage8(image, s, header); + } else if (image.depth() >= 16) { + ok = writeImage24(image, s, header); } return ok; diff --git a/src/imageformats/pic.cpp b/src/imageformats/pic.cpp index ecd1ef0..03d5915 100644 --- a/src/imageformats/pic.cpp +++ b/src/imageformats/pic.cpp @@ -15,6 +15,7 @@ #include "pic_p.h" #include "rle_p.h" +#include "scanlineconverter_p.h" #include "util_p.h" #include @@ -263,18 +264,20 @@ bool SoftimagePICHandler::read(QImage *image) return true; } -bool SoftimagePICHandler::write(const QImage &_image) +bool SoftimagePICHandler::write(const QImage &image) { - bool alpha = _image.hasAlphaChannel(); - QImage image; + bool alpha = image.hasAlphaChannel(); #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) - auto cs = _image.colorSpace(); - if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && _image.format() == QImage::Format_CMYK8888) { - image = _image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb), QImage::Format_RGB32); + auto cs = image.colorSpace(); + auto tfmt = image.format(); + auto tcs = QColorSpace(); + if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) { + tcs = QColorSpace(QColorSpace::SRgb); + tfmt = QImage::Format_RGB32; } #endif - if (image.isNull()) { - image = _image.convertToFormat(alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32); + if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) { + tfmt = alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; } if (image.width() < 0 || image.height() < 0) { @@ -299,8 +302,10 @@ bool SoftimagePICHandler::write(const QImage &_image) } stream << channels; + ScanLineConverter scl(tfmt); + scl.setTargetColorSpace(tcs); for (int r = 0; r < image.height(); r++) { - const QRgb *row = reinterpret_cast(image.constScanLine(r)); + const QRgb *row = reinterpret_cast(scl.convertedScanLine(image, r)); /* Write the RGB part of the scanline */ auto rgbEqual = [](QRgb p1, QRgb p2) -> bool { diff --git a/src/imageformats/rgb.cpp b/src/imageformats/rgb.cpp index 9bb6c3c..2c409f6 100644 --- a/src/imageformats/rgb.cpp +++ b/src/imageformats/rgb.cpp @@ -20,6 +20,7 @@ */ #include "rgb_p.h" +#include "scanlineconverter_p.h" #include "util_p.h" #include @@ -133,8 +134,8 @@ private: bool writeHeader(); bool writeRle(); - bool writeVerbatim(const QImage &); - bool scanData(const QImage &); + bool writeVerbatim(const QImage &, const QImage::Format&, const QColorSpace&); + bool scanData(const QImage &, const QImage::Format&, const QColorSpace&); uint compact(uchar *, uchar *); uchar intensity(uchar); }; @@ -457,7 +458,7 @@ uint SGIImagePrivate::compact(uchar *d, uchar *s) return dest - d; } -bool SGIImagePrivate::scanData(const QImage &img) +bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, const QColorSpace &tcs) { quint32 *start = _starttab; QByteArray lineguard(_xsize * 2, 0); @@ -469,6 +470,8 @@ bool SGIImagePrivate::scanData(const QImage &img) unsigned y; uint len; + ScanLineConverter scl(tfmt); + scl.setTargetColorSpace(tcs); for (y = 0; y < _ysize; y++) { const int yPos = _ysize - y - 1; // scanline doesn't do any sanity checking if (yPos >= img.height()) { @@ -476,7 +479,7 @@ bool SGIImagePrivate::scanData(const QImage &img) return false; } - c = reinterpret_cast(img.scanLine(yPos)); + c = reinterpret_cast(scl.convertedScanLine(img, yPos)); for (x = 0; x < _xsize; x++) { buf[x] = intensity(qRed(*c++)); @@ -497,7 +500,7 @@ bool SGIImagePrivate::scanData(const QImage &img) return false; } - c = reinterpret_cast(img.scanLine(yPos)); + c = reinterpret_cast(scl.convertedScanLine(img, yPos)); for (x = 0; x < _xsize; x++) { buf[x] = intensity(qGreen(*c++)); } @@ -512,7 +515,7 @@ bool SGIImagePrivate::scanData(const QImage &img) return false; } - c = reinterpret_cast(img.scanLine(yPos)); + c = reinterpret_cast(scl.convertedScanLine(img, yPos)); for (x = 0; x < _xsize; x++) { buf[x] = intensity(qBlue(*c++)); } @@ -532,7 +535,7 @@ bool SGIImagePrivate::scanData(const QImage &img) return false; } - c = reinterpret_cast(img.scanLine(yPos)); + c = reinterpret_cast(scl.convertedScanLine(img, yPos)); for (x = 0; x < _xsize; x++) { buf[x] = intensity(qAlpha(*c++)); } @@ -683,7 +686,7 @@ bool SGIImagePrivate::writeRle() return _stream.status() == QDataStream::Ok; } -bool SGIImagePrivate::writeVerbatim(const QImage &img) +bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfmt, const QColorSpace &tcs) { _rle = 0; if (!writeHeader()) { @@ -694,11 +697,15 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img) unsigned x; unsigned y; + ScanLineConverter scl(tfmt); + scl.setTargetColorSpace(tcs); + QByteArray ba(_xsize, char()); for (y = 0; y < _ysize; y++) { - c = reinterpret_cast(img.scanLine(_ysize - y - 1)); + c = reinterpret_cast(scl.convertedScanLine(img, _ysize - y - 1)); for (x = 0; x < _xsize; x++) { - _stream << quint8(qRed(*c++)); + ba[x] = char(qRed(*c++)); } + _stream.writeRawData(ba.data(), ba.size()); } if (_zsize == 1) { @@ -707,17 +714,19 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img) if (_zsize != 2) { for (y = 0; y < _ysize; y++) { - c = reinterpret_cast(img.scanLine(_ysize - y - 1)); + c = reinterpret_cast(scl.convertedScanLine(img, _ysize - y - 1)); for (x = 0; x < _xsize; x++) { - _stream << quint8(qGreen(*c++)); + ba[x] = char(qGreen(*c++)); } + _stream.writeRawData(ba.data(), ba.size()); } for (y = 0; y < _ysize; y++) { - c = reinterpret_cast(img.scanLine(_ysize - y - 1)); + c = reinterpret_cast(scl.convertedScanLine(img, _ysize - y - 1)); for (x = 0; x < _xsize; x++) { - _stream << quint8(qBlue(*c++)); + ba[x] = char(qBlue(*c++)); } + _stream.writeRawData(ba.data(), ba.size()); } if (_zsize == 3) { @@ -726,10 +735,11 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img) } for (y = 0; y < _ysize; y++) { - c = reinterpret_cast(img.scanLine(_ysize - y - 1)); + c = reinterpret_cast(scl.convertedScanLine(img, _ysize - y - 1)); for (x = 0; x < _xsize; x++) { - _stream << quint8(qAlpha(*c++)); + ba[x] = char(qAlpha(*c++)); } + _stream.writeRawData(ba.data(), ba.size()); } return _stream.status() == QDataStream::Ok; @@ -738,37 +748,35 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img) bool SGIImagePrivate::writeImage(const QImage &image) { // qDebug() << "writing "; // TODO add filename - QImage img = image; - if (img.allGray()) { + if (image.allGray()) { _dim = 2, _zsize = 1; } else { _dim = 3, _zsize = 3; } - auto hasAlpha = img.hasAlphaChannel(); + auto hasAlpha = image.hasAlphaChannel(); if (hasAlpha) { _dim = 3, _zsize++; } #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) auto cs = image.colorSpace(); - if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) { - img = image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb), QImage::Format_RGB32); - } else if (hasAlpha && img.format() != QImage::Format_ARGB32) { + auto tcs = QColorSpace(); + auto tfmt = image.format(); + if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) { + tcs = QColorSpace(QColorSpace::SRgb); + tfmt = QImage::Format_RGB32; + } else if (hasAlpha && tfmt != QImage::Format_ARGB32) { #else - if (hasAlpha && img.format() != QImage::Format_ARGB32) { + if (hasAlpha && tfmt != QImage::Format_ARGB32) { #endif - img = img.convertToFormat(QImage::Format_ARGB32); - } else if (!hasAlpha && img.format() != QImage::Format_RGB32) { - img = img.convertToFormat(QImage::Format_RGB32); - } - if (img.isNull()) { - // qDebug() << "can't convert image to depth 32"; - return false; + tfmt = QImage::Format_ARGB32; + } else if (!hasAlpha && tfmt != QImage::Format_RGB32) { + tfmt = QImage::Format_RGB32; } - const int w = img.width(); - const int h = img.height(); + const int w = image.width(); + const int h = image.height(); if (w > 65535 || h > 65535) { return false; @@ -785,7 +793,7 @@ bool SGIImagePrivate::writeImage(const QImage &image) _starttab = new quint32[_numrows]; _rlemap.setBaseOffset(512 + _numrows * 2 * sizeof(quint32)); - if (!scanData(img)) { + if (!scanData(image, tfmt, tcs)) { // qDebug() << "this can't happen"; return false; } @@ -799,7 +807,7 @@ bool SGIImagePrivate::writeImage(const QImage &image) } if (verbatim_size <= rle_size) { - return writeVerbatim(img); + return writeVerbatim(image, tfmt, tcs); } return writeRle(); }