mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-07-02 08:59:10 -04:00
Add support for CD-I IFF images
This commit is contained in:
@@ -357,6 +357,8 @@ The plugin supports the following image data:
|
|||||||
type 4.
|
type 4.
|
||||||
- FORM PBM: PBM is a chunky version of IFF pictures. It supports 8-bit images
|
- FORM PBM: PBM is a chunky version of IFF pictures. It supports 8-bit images
|
||||||
with color map only.
|
with color map only.
|
||||||
|
- FORM IMAG (Compact Disc-Interactive): It supports CLut4, CLut8 and DYuv
|
||||||
|
formats.
|
||||||
- FOR4 CIMG (Maya Image File Format): It supports 24/48-bit RGB and 32/64-bit
|
- FOR4 CIMG (Maya Image File Format): It supports 24/48-bit RGB and 32/64-bit
|
||||||
RGBA images.
|
RGBA images.
|
||||||
|
|
||||||
|
|||||||
BIN
autotests/read/iff/cdi_cl7.iff
Normal file
BIN
autotests/read/iff/cdi_cl7.iff
Normal file
Binary file not shown.
BIN
autotests/read/iff/cdi_cl7.png
Normal file
BIN
autotests/read/iff/cdi_cl7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -311,10 +311,18 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
|
|||||||
chunk = QSharedPointer<IFFChunk>(new ICCNChunk());
|
chunk = QSharedPointer<IFFChunk>(new ICCNChunk());
|
||||||
} else if (cid == ICCP_CHUNK) {
|
} else if (cid == ICCP_CHUNK) {
|
||||||
chunk = QSharedPointer<IFFChunk>(new ICCPChunk());
|
chunk = QSharedPointer<IFFChunk>(new ICCPChunk());
|
||||||
|
} else if (cid == IDAT_CHUNK) {
|
||||||
|
chunk = QSharedPointer<IFFChunk>(new IDATChunk());
|
||||||
|
} else if (cid == IHDR_CHUNK) {
|
||||||
|
chunk = QSharedPointer<IFFChunk>(new IHDRChunk());
|
||||||
|
} else if (cid == IPAR_CHUNK) {
|
||||||
|
chunk = QSharedPointer<IFFChunk>(new IPARChunk());
|
||||||
} else if (cid == NAME_CHUNK) {
|
} else if (cid == NAME_CHUNK) {
|
||||||
chunk = QSharedPointer<IFFChunk>(new NAMEChunk());
|
chunk = QSharedPointer<IFFChunk>(new NAMEChunk());
|
||||||
} else if (cid == PCHG_CHUNK) {
|
} else if (cid == PCHG_CHUNK) {
|
||||||
chunk = QSharedPointer<IFFChunk>(new PCHGChunk());
|
chunk = QSharedPointer<IFFChunk>(new PCHGChunk());
|
||||||
|
} else if (cid == PLTE_CHUNK) {
|
||||||
|
chunk = QSharedPointer<IFFChunk>(new PLTEChunk());
|
||||||
} else if (cid == RAST_CHUNK) {
|
} else if (cid == RAST_CHUNK) {
|
||||||
chunk = QSharedPointer<IFFChunk>(new RASTChunk());
|
chunk = QSharedPointer<IFFChunk>(new RASTChunk());
|
||||||
} else if (cid == RGBA_CHUNK) {
|
} else if (cid == RGBA_CHUNK) {
|
||||||
@@ -1406,16 +1414,13 @@ bool FORMChunk::innerReadStructure(QIODevice *d)
|
|||||||
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
||||||
} else if (_type == RGBN_FORM_TYPE) {
|
} else if (_type == RGBN_FORM_TYPE) {
|
||||||
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
||||||
|
} else if (_type == IMAG_FORM_TYPE) {
|
||||||
|
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray FORMChunk::formType() const
|
QImage::Format FORMChunk::iffFormat() const
|
||||||
{
|
|
||||||
return _type;
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage::Format FORMChunk::format() const
|
|
||||||
{
|
{
|
||||||
auto headers = IFFChunk::searchT<BMHDChunk>(chunks());
|
auto headers = IFFChunk::searchT<BMHDChunk>(chunks());
|
||||||
if (headers.isEmpty()) {
|
if (headers.isEmpty()) {
|
||||||
@@ -1464,13 +1469,67 @@ QImage::Format FORMChunk::format() const
|
|||||||
return QImage::Format_Invalid;
|
return QImage::Format_Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage::Format FORMChunk::cdiFormat() const
|
||||||
|
{
|
||||||
|
auto headers = IFFChunk::searchT<IHDRChunk>(chunks());
|
||||||
|
if (headers.isEmpty()) {
|
||||||
|
return QImage::Format_Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto &&h = headers.first()) {
|
||||||
|
if (h->model() == IHDRChunk::Rgb555 && h->depth() == 16) { // no test case
|
||||||
|
return QImage::Format_RGB555;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h->depth() == 4) {
|
||||||
|
if (h->model() == IHDRChunk::CLut4 || h->model() == IHDRChunk::CLut3) { // CLut3: no test case
|
||||||
|
return QImage::Format_Indexed8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h->depth() == 8) {
|
||||||
|
if (h->model() == IHDRChunk::CLut8 || h->model() == IHDRChunk::CLut7) { // CLut7: no test case
|
||||||
|
return QImage::Format_Indexed8;
|
||||||
|
}
|
||||||
|
if (h->model() == IHDRChunk::Rgb888) { // no test case
|
||||||
|
return FORMAT_RGB_8BIT;
|
||||||
|
}
|
||||||
|
if (h->model() == IHDRChunk::DYuv && h->yuvKind() == IHDRChunk::One) {
|
||||||
|
return FORMAT_RGB_8BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QImage::Format_Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray FORMChunk::formType() const
|
||||||
|
{
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage::Format FORMChunk::format() const
|
||||||
|
{
|
||||||
|
if (formType() == IMAG_FORM_TYPE) {
|
||||||
|
return cdiFormat();
|
||||||
|
}
|
||||||
|
return iffFormat();
|
||||||
|
}
|
||||||
|
|
||||||
QSize FORMChunk::size() const
|
QSize FORMChunk::size() const
|
||||||
{
|
{
|
||||||
auto headers = IFFChunk::searchT<BMHDChunk>(chunks());
|
if (formType() == IMAG_FORM_TYPE) {
|
||||||
if (headers.isEmpty()) {
|
auto ihdrs = IFFChunk::searchT<IHDRChunk>(chunks());
|
||||||
return {};
|
if (!ihdrs.isEmpty()) {
|
||||||
|
return ihdrs.first()->size();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto bmhds = IFFChunk::searchT<BMHDChunk>(chunks());
|
||||||
|
if (!bmhds.isEmpty()) {
|
||||||
|
return bmhds.first()->size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return headers.first()->size();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ******************
|
/* ******************
|
||||||
@@ -1583,6 +1642,8 @@ bool CATChunk::innerReadStructure(QIODevice *d)
|
|||||||
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
||||||
} else if (_type == RGBN_FORM_TYPE) {
|
} else if (_type == RGBN_FORM_TYPE) {
|
||||||
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
||||||
|
} else if (_type == IMAG_FORM_TYPE) {
|
||||||
|
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@@ -2371,6 +2432,398 @@ bool XMP0Chunk::innerReadStructure(QIODevice *d)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************
|
||||||
|
* *** IHDR Chunk ***
|
||||||
|
* ****************** */
|
||||||
|
|
||||||
|
IHDRChunk::~IHDRChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IHDRChunk::IHDRChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IHDRChunk::isValid() const
|
||||||
|
{
|
||||||
|
if (dataBytes() < 14) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return chunkId() == IHDRChunk::defaultChunkId();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IHDRChunk::width() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(1), data().at(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IHDRChunk::height() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(5), data().at(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize IHDRChunk::size() const
|
||||||
|
{
|
||||||
|
return QSize(width(), height());
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IHDRChunk::lineSize() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(3), data().at(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 IHDRChunk::depth() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(9), data().at(8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
IHDRChunk::Model IHDRChunk::model() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return IHDRChunk::Model::Invalid;
|
||||||
|
}
|
||||||
|
return IHDRChunk::Model(ui16(data().at(7), data().at(6)));
|
||||||
|
}
|
||||||
|
|
||||||
|
IHDRChunk::DYuvKind IHDRChunk::yuvKind() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return IHDRChunk::DYuvKind::One;
|
||||||
|
}
|
||||||
|
return IHDRChunk::DYuvKind(data().at(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
IHDRChunk::Yuv IHDRChunk::yuvStart() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return{};
|
||||||
|
}
|
||||||
|
return(Yuv(data().at(11), data().at(12), data().at(13)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IHDRChunk::innerReadStructure(QIODevice *d)
|
||||||
|
{
|
||||||
|
return cacheData(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************
|
||||||
|
* *** IPAR Chunk ***
|
||||||
|
* ****************** */
|
||||||
|
|
||||||
|
IPARChunk::~IPARChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IPARChunk::IPARChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPARChunk::isValid() const
|
||||||
|
{
|
||||||
|
if (dataBytes() < 22) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return chunkId() == IPARChunk::defaultChunkId();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IPARChunk::xOffset() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(1), data().at(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IPARChunk::yOffset() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(3), data().at(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
double IPARChunk::aspectRatio() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (auto xr = ui16(data().at(5), data().at(4))) {
|
||||||
|
auto yr = double(ui16(data().at(7), data().at(6)));
|
||||||
|
return yr / xr;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IPARChunk::xPage() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(9), data().at(8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IPARChunk::yPage() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(11), data().at(10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IPARChunk::xGrub() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(13), data().at(12)));
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 IPARChunk::yGrub() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint32(ui16(data().at(15), data().at(14)));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPARChunk::Rgb IPARChunk::mask() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return Rgb(data().at(16), data().at(17), data().at(18));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPARChunk::Rgb IPARChunk::transparency() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return Rgb(data().at(19), data().at(20), data().at(21));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPARChunk::innerReadStructure(QIODevice *d)
|
||||||
|
{
|
||||||
|
return cacheData(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************
|
||||||
|
* *** PLTE Chunk ***
|
||||||
|
* ****************** */
|
||||||
|
|
||||||
|
PLTEChunk::~PLTEChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PLTEChunk::PLTEChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PLTEChunk::isValid() const
|
||||||
|
{
|
||||||
|
if (dataBytes() < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dataBytes() - 4 < total() * 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return chunkId() == PLTEChunk::defaultChunkId();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 PLTEChunk::count() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return total() - count();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 PLTEChunk::offset() const
|
||||||
|
{
|
||||||
|
return qint32(ui16(data().at(1), data().at(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 PLTEChunk::total() const
|
||||||
|
{
|
||||||
|
return qint32(ui16(data().at(3), data().at(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QRgb> PLTEChunk::innerPalette() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return{};
|
||||||
|
}
|
||||||
|
QList<QRgb> l;
|
||||||
|
auto &&d = data();
|
||||||
|
for (qint32 i = offset(), n = total(); i < n; ++i) {
|
||||||
|
auto i3 = 4 + i * 3;
|
||||||
|
l << qRgb(d.at(i3), d.at(i3 + 1), d.at(i3 + 2));
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************
|
||||||
|
* *** IDAT Chunk ***
|
||||||
|
* ****************** */
|
||||||
|
|
||||||
|
IDATChunk::~IDATChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IDATChunk::IDATChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDATChunk::isValid() const
|
||||||
|
{
|
||||||
|
return chunkId() == IDATChunk::defaultChunkId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Converts a YUV pixel to RGB.
|
||||||
|
*/
|
||||||
|
inline IPARChunk::Rgb yuvToRgb(IHDRChunk::Yuv yuv) {
|
||||||
|
IPARChunk::Rgb rgb;
|
||||||
|
|
||||||
|
// Green Book Cap. V Par. 4.4.2.2
|
||||||
|
const auto b = yuv.y + (yuv.u - 128.) * 1.733;
|
||||||
|
const auto r = yuv.y + (yuv.v - 128.) * 1.371;
|
||||||
|
const auto g = (yuv.y - 0.299 * r - 0.114 * b) / 0.587;
|
||||||
|
|
||||||
|
rgb.r = quint8(std::clamp(r + 0.5, 0., 255.));
|
||||||
|
rgb.g = quint8(std::clamp(g + 0.5, 0., 255.));
|
||||||
|
rgb.b = quint8(std::clamp(b + 0.5, 0., 255.));
|
||||||
|
|
||||||
|
return rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray IDATChunk::strideRead(QIODevice *d, qint32 y, const IHDRChunk *header, const IPARChunk *params) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(y)
|
||||||
|
Q_UNUSED(params)
|
||||||
|
if (!isValid() || header == nullptr || d == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto read = strideSize(header);
|
||||||
|
for (auto nextPos = nextChunkPos(); !d->atEnd() && d->pos() < nextPos;) {
|
||||||
|
auto rr = d->read(read);
|
||||||
|
|
||||||
|
if (header->model() == IHDRChunk::CLut4) {
|
||||||
|
if (rr.size() < header->width() / 2) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
QByteArray tmp(header->width(), char());
|
||||||
|
for (auto x = 0, w = header->width(); x < w; ++x) {
|
||||||
|
auto i8 = quint8(rr.at(x / 2));
|
||||||
|
tmp[x] = x & 1 ? i8 & 0xF : (i8 >> 4) & 0xF;
|
||||||
|
}
|
||||||
|
rr = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header->model() == IHDRChunk::Rgb555) {
|
||||||
|
for(qint32 x = 0, w = rr.size() - 1; x < w; x += 2) {
|
||||||
|
std::swap(rr[x], rr[x + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header->model() == IHDRChunk::DYuv) {
|
||||||
|
if (rr.size() < header->width()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// delta table: Green Book Cap. V Par. 3.4.1.3
|
||||||
|
// NOTE 1: using the wrong delta table creates visible artifacts on the image.
|
||||||
|
// NOTE 2: using { 0, 1, 4, 9, 16, 27, 44, 79, -128, -79, -44, -27, -16, -9, -4, -1 }
|
||||||
|
// table gives the same result (when assigned to an uint8).
|
||||||
|
static const qint32 deltaTable[16] = {
|
||||||
|
0, 1, 4, 9, 16, 27, 44, 79, 128, 177, 212, 229, 240, 247, 252, 255
|
||||||
|
};
|
||||||
|
|
||||||
|
auto yuv = header->yuvStart();
|
||||||
|
QByteArray tmp(header->width() * 3, char());
|
||||||
|
for (auto x = 0, w = header->width() - 1; x < w; x += 2) {
|
||||||
|
// nibble order from Green Book Cap. V Par. 6.5.1.1
|
||||||
|
// NOTE: using the wrong nibble order creates visible artifacts on the image.
|
||||||
|
const auto du = deltaTable[(rr.at(x) >> 4) & 0x0F];
|
||||||
|
const auto d1 = deltaTable[rr.at(x) & 0x0F];
|
||||||
|
const auto dv = deltaTable[(rr.at(x + 1) >> 4) & 0x0F];
|
||||||
|
const auto d2 = deltaTable[rr.at(x + 1) & 0x0F];
|
||||||
|
|
||||||
|
// pixel 1
|
||||||
|
yuv.y = d1 + yuv.y;
|
||||||
|
yuv.u = du + yuv.u;
|
||||||
|
yuv.v = dv + yuv.v;
|
||||||
|
auto rgb = yuvToRgb(yuv);
|
||||||
|
tmp[x * 3] = rgb.r;
|
||||||
|
tmp[x * 3 + 1] = rgb.g;
|
||||||
|
tmp[x * 3 + 2] = rgb.b;
|
||||||
|
|
||||||
|
// pixel 2
|
||||||
|
yuv.y = d2 + yuv.y;
|
||||||
|
rgb = yuvToRgb(yuv);
|
||||||
|
tmp[(x + 1) * 3] = rgb.r;
|
||||||
|
tmp[(x + 1) * 3 + 1] = rgb.g;
|
||||||
|
tmp[(x + 1) * 3 + 2] = rgb.b;
|
||||||
|
}
|
||||||
|
rr = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDATChunk::resetStrideRead(QIODevice *d) const
|
||||||
|
{
|
||||||
|
return seek(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 IDATChunk::strideSize(const IHDRChunk *header) const
|
||||||
|
{
|
||||||
|
if (header == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rs = (header->width() * header->depth() + 7) / 8;
|
||||||
|
|
||||||
|
// No padding bytes are inserted in the data.
|
||||||
|
if (header->model() == IHDRChunk::Rgb888) {
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first pixel of each scan line must begin in a longword boundary.
|
||||||
|
if (auto mod = rs % 4)
|
||||||
|
rs += (4 - mod);
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ******************
|
/* ******************
|
||||||
* *** BEAM Chunk ***
|
* *** BEAM Chunk ***
|
||||||
* ****************** */
|
* ****************** */
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
|||||||
#define CMAP_CHUNK QByteArray("CMAP")
|
#define CMAP_CHUNK QByteArray("CMAP")
|
||||||
#define CMYK_CHUNK QByteArray("CMYK") // https://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.CMYK
|
#define CMYK_CHUNK QByteArray("CMYK") // https://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.CMYK
|
||||||
#define DPI__CHUNK QByteArray("DPI ")
|
#define DPI__CHUNK QByteArray("DPI ")
|
||||||
|
#define IDAT_CHUNK QByteArray("IDAT")
|
||||||
|
#define IHDR_CHUNK QByteArray("IHDR")
|
||||||
|
#define IPAR_CHUNK QByteArray("IPAR")
|
||||||
|
#define PLTE_CHUNK QByteArray("PLTE")
|
||||||
#define XBMI_CHUNK QByteArray("XBMI")
|
#define XBMI_CHUNK QByteArray("XBMI")
|
||||||
|
|
||||||
// Different palette for scanline
|
// Different palette for scanline
|
||||||
@@ -82,11 +86,13 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
|||||||
#define VERS_CHUNK QByteArray("VERS")
|
#define VERS_CHUNK QByteArray("VERS")
|
||||||
#define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata
|
#define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata
|
||||||
|
|
||||||
|
// FORM types
|
||||||
#define ACBM_FORM_TYPE QByteArray("ACBM")
|
#define ACBM_FORM_TYPE QByteArray("ACBM")
|
||||||
#define ILBM_FORM_TYPE QByteArray("ILBM")
|
#define ILBM_FORM_TYPE QByteArray("ILBM")
|
||||||
#define PBM__FORM_TYPE QByteArray("PBM ")
|
#define PBM__FORM_TYPE QByteArray("PBM ")
|
||||||
#define RGB8_FORM_TYPE QByteArray("RGB8")
|
#define RGB8_FORM_TYPE QByteArray("RGB8")
|
||||||
#define RGBN_FORM_TYPE QByteArray("RGBN")
|
#define RGBN_FORM_TYPE QByteArray("RGBN")
|
||||||
|
#define IMAG_FORM_TYPE QByteArray("IMAG")
|
||||||
|
|
||||||
#define CIMG_FOR4_TYPE QByteArray("CIMG")
|
#define CIMG_FOR4_TYPE QByteArray("CIMG")
|
||||||
#define TBMP_FOR4_TYPE QByteArray("TBMP")
|
#define TBMP_FOR4_TYPE QByteArray("TBMP")
|
||||||
@@ -756,10 +762,11 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
* \brief readStride
|
* \brief readStride
|
||||||
* \param d The device.
|
* \param d The device.
|
||||||
* \param header The bitmap header.
|
|
||||||
* \param y The current scanline.
|
* \param y The current scanline.
|
||||||
|
* \param header The bitmap header.
|
||||||
* \param camg The CAMG chunk (optional)
|
* \param camg The CAMG chunk (optional)
|
||||||
* \param cmap The CMAP chunk (optional)
|
* \param cmap The CMAP chunk (optional)
|
||||||
|
* \param ipal The per-line palette chunk (optional)
|
||||||
* \param formType The type of the current form chunk.
|
* \param formType The type of the current form chunk.
|
||||||
* \return The scanline as requested for QImage.
|
* \return The scanline as requested for QImage.
|
||||||
* \warning Call resetStrideRead() once before this one.
|
* \warning Call resetStrideRead() once before this one.
|
||||||
@@ -776,8 +783,6 @@ public:
|
|||||||
* \brief resetStrideRead
|
* \brief resetStrideRead
|
||||||
* Reset the stride read set the position at the beginning of the data and reset all buffers.
|
* Reset the stride read set the position at the beginning of the data and reset all buffers.
|
||||||
* \param d The device.
|
* \param d The device.
|
||||||
* \param header The BMHDChunk chunk (mandatory)
|
|
||||||
* \param camg The CAMG chunk (optional)
|
|
||||||
* \return True on success, otherwise false.
|
* \return True on success, otherwise false.
|
||||||
* \sa strideRead
|
* \sa strideRead
|
||||||
* \note Must be called once before strideRead().
|
* \note Must be called once before strideRead().
|
||||||
@@ -788,6 +793,7 @@ public:
|
|||||||
* \brief safeModeId
|
* \brief safeModeId
|
||||||
* \param header The header.
|
* \param header The header.
|
||||||
* \param camg The CAMG chunk.
|
* \param camg The CAMG chunk.
|
||||||
|
* \param cmap The CMAP chunk.
|
||||||
* \return The most likely ModeId if not explicitly specified.
|
* \return The most likely ModeId if not explicitly specified.
|
||||||
*/
|
*/
|
||||||
static CAMGChunk::ModeIds safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap = nullptr);
|
static CAMGChunk::ModeIds safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap = nullptr);
|
||||||
@@ -921,6 +927,11 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool innerReadStructure(QIODevice *d) override;
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage::Format iffFormat() const;
|
||||||
|
|
||||||
|
QImage::Format cdiFormat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1400,6 +1411,267 @@ protected:
|
|||||||
virtual bool innerReadStructure(QIODevice *d) override;
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* *** I-CD IFF CHUNKS ***
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The IHDRChunk class
|
||||||
|
* Image Header
|
||||||
|
*/
|
||||||
|
class IHDRChunk: public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Model {
|
||||||
|
Invalid = 0, /**< Invalid model. */
|
||||||
|
Rgb888 = 1, /**< red, green, blue - 8 bits per color. */
|
||||||
|
Rgb555 = 2, /**< Green Book absolute RGB. */
|
||||||
|
DYuv = 3, /**< Green Book Delta YUV. */
|
||||||
|
CLut8 = 4, /**< Green Book 8 bit CLUT. */
|
||||||
|
CLut7 = 5, /**< Green Book 7 bit CLUT. */
|
||||||
|
CLut4 = 6, /**< Green Book 4 bit CLUT. */
|
||||||
|
CLut3 = 7, /**< Green Book 3 bit CLUT. */
|
||||||
|
Rle7 = 8, /**< Green Book runlength coded 7 bit CLUT. */
|
||||||
|
Rle3 = 9, /**< Green Book runlength coded 3 bit CLUT. */
|
||||||
|
PaletteOnly = 10 /**< color palette only. */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DYuvKind {
|
||||||
|
One = 0,
|
||||||
|
Each = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Yuv {
|
||||||
|
Yuv(quint8 y0 = 0, quint8 u0 = 0, quint8 v0 = 0) : y(y0), u(u0), v(v0) {}
|
||||||
|
quint8 y;
|
||||||
|
quint8 u;
|
||||||
|
quint8 v;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~IHDRChunk() override;
|
||||||
|
|
||||||
|
IHDRChunk();
|
||||||
|
IHDRChunk(const IHDRChunk& other) = default;
|
||||||
|
IHDRChunk& operator =(const IHDRChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief width
|
||||||
|
* \return Width of the bitmap in pixels.
|
||||||
|
*/
|
||||||
|
qint32 width() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief height
|
||||||
|
* \return Height of the bitmap in pixels.
|
||||||
|
*/
|
||||||
|
qint32 height() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief size
|
||||||
|
* \return Size in pixels.
|
||||||
|
*/
|
||||||
|
QSize size() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief lineSize
|
||||||
|
* Physical width of image (number of bytes in each scan line, including any data required at
|
||||||
|
* the end of each scan line for padding [see description of each model’s IDAT chunk for padding
|
||||||
|
* rules]) This field is not used when model() = Rle7 or Rle3.
|
||||||
|
* When model() = Rgb555, this field defines the size of one scan line of the upper
|
||||||
|
* or lower portion of the pixel data, but not the size of them both together.
|
||||||
|
*/
|
||||||
|
qint32 lineSize() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief model
|
||||||
|
* Image model (coding method)
|
||||||
|
*/
|
||||||
|
Model model() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief depth
|
||||||
|
* Physical size of pixel (number of bits per pixel used in storing image data) When
|
||||||
|
* model() = Rle7 or Rle3, this value only represents the size of a
|
||||||
|
* single pixel; the size of a run of pixels is indeterminate.
|
||||||
|
*/
|
||||||
|
quint16 depth() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yuvKind
|
||||||
|
* if model() = DYuv, indicates whether there is one DYUV start value for all
|
||||||
|
* scan lines (in yuvStart()), or whether each scan line has its own start value in the
|
||||||
|
* YUVS chunk which follows.
|
||||||
|
*/
|
||||||
|
DYuvKind yuvKind() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yuvStart
|
||||||
|
* Start values for DYUV image if model() = DYuv and dYuvKind() = One
|
||||||
|
*/
|
||||||
|
Yuv yuvStart() const;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(IHDR_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The IHDRChunk class
|
||||||
|
*/
|
||||||
|
class IPARChunk: public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Rgb {
|
||||||
|
Rgb(quint8 r0 = 0, quint8 g0 = 0, quint8 b0 = 0) : r(r0), g(g0), b(b0) {}
|
||||||
|
quint8 r;
|
||||||
|
quint8 g;
|
||||||
|
quint8 b;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~IPARChunk() override;
|
||||||
|
|
||||||
|
IPARChunk();
|
||||||
|
IPARChunk(const IPARChunk& other) = default;
|
||||||
|
IPARChunk& operator =(const IPARChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief xOffset
|
||||||
|
* X offset of origin in source image [0 < xOffset() < xPage()]
|
||||||
|
*/
|
||||||
|
qint32 xOffset() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yOffset
|
||||||
|
* \returnX offset of origin in source image [0 < yOffset() < yPage()]
|
||||||
|
*/
|
||||||
|
qint32 yOffset() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief aspectRatio
|
||||||
|
* Aspect ratio of pixels in source image.
|
||||||
|
*/
|
||||||
|
double aspectRatio() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief xPage
|
||||||
|
* X size of source image.
|
||||||
|
*/
|
||||||
|
qint32 xPage() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yPage
|
||||||
|
* Y size of source image.
|
||||||
|
*/
|
||||||
|
qint32 yPage() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief xGrub
|
||||||
|
* X location of hot spot within image.
|
||||||
|
*/
|
||||||
|
qint32 xGrub() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yGrub
|
||||||
|
* Y location of hot spot within image.
|
||||||
|
*/
|
||||||
|
qint32 yGrub() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief transparency
|
||||||
|
* Transparent color.
|
||||||
|
*/
|
||||||
|
Rgb transparency() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief mask
|
||||||
|
* Mask color.
|
||||||
|
*/
|
||||||
|
Rgb mask() const;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(IPAR_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The PLTEChunk class
|
||||||
|
*/
|
||||||
|
class PLTEChunk : public CMAPChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~PLTEChunk() override;
|
||||||
|
PLTEChunk();
|
||||||
|
PLTEChunk(const PLTEChunk& other) = default;
|
||||||
|
PLTEChunk& operator =(const PLTEChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief count
|
||||||
|
* \return The number of color in the palette.
|
||||||
|
*/
|
||||||
|
virtual qint32 count() const override;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(PLTE_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
qint32 offset() const;
|
||||||
|
|
||||||
|
qint32 total() const;
|
||||||
|
|
||||||
|
virtual QList<QRgb> innerPalette() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The IDATChunk class
|
||||||
|
*/
|
||||||
|
class IDATChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IDATChunk() override;
|
||||||
|
IDATChunk();
|
||||||
|
IDATChunk(const IDATChunk& other) = default;
|
||||||
|
IDATChunk& operator =(const IDATChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(IDAT_CHUNK)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief readStride
|
||||||
|
* \param d The device.
|
||||||
|
* \param y The current scanline.
|
||||||
|
* \param header The bitmap header.
|
||||||
|
* \param params The additional parameters (optional)
|
||||||
|
* \return The scanline as requested for QImage.
|
||||||
|
* \warning Call resetStrideRead() once before this one.
|
||||||
|
*/
|
||||||
|
QByteArray strideRead(QIODevice *d,
|
||||||
|
qint32 y,
|
||||||
|
const IHDRChunk *header,
|
||||||
|
const IPARChunk *params = nullptr) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief resetStrideRead
|
||||||
|
* Reset the stride read set the position at the beginning of the data and reset all buffers.
|
||||||
|
* \param d The device.
|
||||||
|
* \return True on success, otherwise false.
|
||||||
|
* \sa strideRead
|
||||||
|
* \note Must be called once before strideRead().
|
||||||
|
*/
|
||||||
|
bool resetStrideRead(QIODevice *d) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
quint32 strideSize(const IHDRChunk *header) const;
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* *** UNDOCUMENTED CHUNKS ***
|
* *** UNDOCUMENTED CHUNKS ***
|
||||||
|
|||||||
@@ -262,14 +262,21 @@ static void addMetadata(QImage &img, const IFOR_Chunk *form)
|
|||||||
|
|
||||||
// if no explicit resolution was found, apply the aspect ratio to the default one
|
// if no explicit resolution was found, apply the aspect ratio to the default one
|
||||||
if (!resChanged) {
|
if (!resChanged) {
|
||||||
auto headers = IFFChunk::searchT<BMHDChunk>(form);
|
if (form->formType() == IMAG_FORM_TYPE) {
|
||||||
if (!headers.isEmpty()) {
|
auto params = IFFChunk::searchT<IPARChunk>(form);
|
||||||
auto xr = headers.first()->xAspectRatio();
|
if (!params.isEmpty()) {
|
||||||
auto yr = headers.first()->yAspectRatio();
|
img.setDotsPerMeterY(img.dotsPerMeterY() * params.first()->aspectRatio());
|
||||||
if (xr > 0 && yr > 0 && xr > yr) {
|
}
|
||||||
img.setDotsPerMeterX(img.dotsPerMeterX() * yr / xr);
|
} else {
|
||||||
} else if (xr > 0 && yr > 0 && xr < yr) {
|
auto headers = IFFChunk::searchT<BMHDChunk>(form);
|
||||||
img.setDotsPerMeterY(img.dotsPerMeterY() * xr / yr);
|
if (!headers.isEmpty()) {
|
||||||
|
auto xr = headers.first()->xAspectRatio();
|
||||||
|
auto yr = headers.first()->yAspectRatio();
|
||||||
|
if (xr > 0 && yr > 0 && xr > yr) {
|
||||||
|
img.setDotsPerMeterX(img.dotsPerMeterX() * yr / xr);
|
||||||
|
} else if (xr > 0 && yr > 0 && xr < yr) {
|
||||||
|
img.setDotsPerMeterY(img.dotsPerMeterY() * xr / yr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -475,6 +482,67 @@ bool IFFHandler::readMayaImage(QImage *image)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IFFHandler::readCDIImage(QImage *image)
|
||||||
|
{
|
||||||
|
auto forms = d->searchForms<FORMChunk>();
|
||||||
|
if (forms.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cin = qBound(0, currentImageNumber(), int(forms.size() - 1));
|
||||||
|
auto &&form = forms.at(cin);
|
||||||
|
|
||||||
|
// show the first one (I don't have a sample with many images)
|
||||||
|
auto headers = IFFChunk::searchT<IHDRChunk>(form);
|
||||||
|
if (headers.isEmpty()) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): no supported image found";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the image
|
||||||
|
auto &&header = headers.first();
|
||||||
|
auto img = imageAlloc(header->size(), form->format());
|
||||||
|
if (img.isNull()) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while allocating the image";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the palette
|
||||||
|
if (img.format() == QImage::Format_Indexed8) {
|
||||||
|
auto pltes = IFFChunk::searchT<PLTEChunk>(form);
|
||||||
|
if (!pltes.isEmpty()) {
|
||||||
|
img.setColorTable(pltes.first()->palette());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decoding the image
|
||||||
|
auto bodies = IFFChunk::searchT<IDATChunk>(form);
|
||||||
|
if (bodies.isEmpty()) {
|
||||||
|
img.fill(0);
|
||||||
|
} else {
|
||||||
|
auto &&body = bodies.first();
|
||||||
|
if (!body->resetStrideRead(device())) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while reading image data";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto pars = IFFChunk::searchT<IPARChunk>(form);
|
||||||
|
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||||
|
auto line = reinterpret_cast<char*>(img.scanLine(y));
|
||||||
|
auto ba = body->strideRead(device(), y, header, pars.isEmpty() ? nullptr : pars.first());
|
||||||
|
if (ba.isEmpty()) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while reading image scanline";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(line, ba.constData(), std::min(img.bytesPerLine(), ba.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set metadata (including image resolution)
|
||||||
|
addMetadata(img, form);
|
||||||
|
|
||||||
|
*image = img;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool IFFHandler::read(QImage *image)
|
bool IFFHandler::read(QImage *image)
|
||||||
{
|
{
|
||||||
if (!d->readStructure(device())) {
|
if (!d->readStructure(device())) {
|
||||||
@@ -490,6 +558,10 @@ bool IFFHandler::read(QImage *image)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (readCDIImage(image)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read(): no supported image found";
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read(): no supported image found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ private:
|
|||||||
|
|
||||||
bool readMayaImage(QImage *image);
|
bool readMayaImage(QImage *image);
|
||||||
|
|
||||||
|
bool readCDIImage(QImage *image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QScopedPointer<IFFHandlerPrivate> d;
|
const QScopedPointer<IFFHandlerPrivate> d;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user