static inline bool isMaskSuitable(const ICNSEntry &mask, const ICNSEntry &icon, ICNSEntry::Depth target)
{
- return mask.depth == target && mask.height == icon.height && mask.width == icon.width;
+ return mask.variant == icon.variant && mask.depth == target
+ && mask.height == icon.height && mask.width == icon.width;
}
static inline QByteArray nameFromOSType(quint32 ostype)
if (option == SubType) {
if (imageCount() > 0 && m_currentIconIndex <= imageCount()) {
const ICNSEntry &icon = m_icons.at(m_currentIconIndex);
+ if (icon.variant != 0)
+ return nameFromOSType(icon.variant) + '-' + nameFromOSType(icon.ostype);
return nameFromOSType(icon.ostype);
}
}
return m_state == ScanSuccess;
}
-bool QICNSHandler::addEntry(const ICNSBlockHeader &header, qint64 imgDataOffset)
+bool QICNSHandler::addEntry(const ICNSBlockHeader &header, qint64 imgDataOffset, quint32 variant)
{
// Note: This function returns false only when a device positioning error occurred
ICNSEntry entry;
entry.ostype = header.ostype;
+ entry.variant = variant;
entry.dataOffset = imgDataOffset;
entry.dataLength = header.length - ICNSBlockHeaderSize;
// Check for known magic numbers:
// We don't have a good use for these blocks... yet.
stream.skipRawData(blockDataLength);
break;
+ case ICNSBlockHeader::TypeTile:
+ case ICNSBlockHeader::TypeOver:
+ case ICNSBlockHeader::TypeOpen:
+ case ICNSBlockHeader::TypeDrop:
+ case ICNSBlockHeader::TypeOdrp:
+ // Icns container seems to have an embedded icon variant container
+ // Let's start a scan for entries
+ while (device()->pos() < nextBlockOffset) {
+ ICNSBlockHeader icon;
+ stream >> icon;
+ // Check for incorrect variant entry header and stop scan
+ if (!isBlockHeaderValid(icon, blockDataLength))
+ break;
+ if (!addEntry(icon, device()->pos(), blockHeader.ostype))
+ return false;
+ if (stream.skipRawData(icon.length - ICNSBlockHeaderSize) < 0)
+ return false;
+ }
+ if (device()->pos() != nextBlockOffset) {
+ // Scan of this container didn't end where we expected.
+ // Let's generate some output about this incident:
+ qWarning("Scan of the icon variant container (\"%s\") failed at pos %s.\n" \
+ "Reason: Scan didn't reach the end of this container's block, " \
+ "delta: %s bytes. This file may be corrupted.",
+ nameFromOSType(blockHeader.ostype).constData(),
+ QByteArray::number(device()->pos()).constData(),
+ QByteArray::number(nextBlockOffset - device()->pos()).constData());
+ if (!device()->seek(nextBlockOffset))
+ return false;
+ }
+ break;
case ICNSBlockHeader::TypeToc: {
// Quick scan, table of contents
if (blockDataOffset != ICNSBlockHeaderSize * 2) {
TypeIcnv = MAKEOSTYPE('i', 'c', 'n', 'V'), // Icon Composer version
// Legacy:
TypeClut = MAKEOSTYPE('c', 'l', 'u', 't'), // Color look-up table (pre-OS X resources)
+ TypeTile = MAKEOSTYPE('t', 'i', 'l', 'e'), // Container (icon variants)
+ TypeOver = MAKEOSTYPE('o', 'v', 'e', 'r'), // Container (icon variants)
+ TypeOpen = MAKEOSTYPE('o', 'p', 'e', 'n'), // Container (icon variants)
+ TypeDrop = MAKEOSTYPE('d', 'r', 'o', 'p'), // Container (icon variants)
+ TypeOdrp = MAKEOSTYPE('o', 'd', 'r', 'p'), // Container (icon variants)
};
quint32 ostype;
};
quint32 ostype; // Real OSType
+ quint32 variant; // Virtual OSType: a parent container, zero if parent is icns root
Group group; // ASCII character number
quint32 width; // For uncompressed icons only, zero for compressed ones for now
quint32 height; // For uncompressed icons only, zero for compressed ones fow now
qint64 dataOffset; // Offset from the initial position of the file/device
ICNSEntry() :
- ostype(0), group(GroupUnknown), width(0), height(0), depth(DepthUnknown),
+ ostype(0), variant(0), group(GroupUnknown), width(0), height(0), depth(DepthUnknown),
flags(Unknown), dataFormat(FormatUnknown), dataLength(0), dataOffset(0)
{
}
private:
bool ensureScanned() const;
bool scanDevice();
- bool addEntry(const ICNSBlockHeader &header, qint64 imgDataOffset);
+ bool addEntry(const ICNSBlockHeader &header, qint64 imgDataOffset, quint32 variant = 0);
const ICNSEntry &getIconMask(const ICNSEntry &icon) const;
private:
QTest::newRow("2") << QStringLiteral("test-jp2") << QSize(128, 128) << 7 << QByteArrayLiteral("jp2");
QTest::newRow("3") << QStringLiteral("test-32bit") << QSize(128, 128) << 4 << QByteArray();
QTest::newRow("4") << QStringLiteral("test-legacy") << QSize(48, 48) << 12 << QByteArray();
+ QTest::newRow("5") << QStringLiteral("test-variants") << QSize(128, 128) << 5 << QByteArrayLiteral("png");
}
void tst_qicns::readIcons()
<file>icns/test-jp2.icns</file>
<file>icns/test-32bit.icns</file>
<file>icns/test-legacy.icns</file>
+ <file>icns/test-variants.icns</file>
<file>icns/test-write-16.png</file>
<file>icns/test-write-32.png</file>
<file>icns/test-write-64.png</file>