8#include <QtCore/qmath.h>
9#include <QtCore/qendian.h>
10#include <QtCore/qregularexpression.h>
11#include <QtCore/qbuffer.h>
12#include <QtGui/qimage.h>
14#ifndef QT_NO_DATASTREAM
21 qRgb(0xFF, 0xFF, 0xFF),
22 qRgb(0x00, 0x00, 0x00)
27 qRgb(0xFF, 0xFF, 0xFF),
28 qRgb(0xFC, 0xF3, 0x05),
29 qRgb(0xFF, 0x64, 0x02),
30 qRgb(0xDD, 0x08, 0x06),
31 qRgb(0xF2, 0x08, 0x84),
32 qRgb(0x46, 0x00, 0xA5),
33 qRgb(0x00, 0x00, 0xD4),
34 qRgb(0x02, 0xAB, 0xEA),
35 qRgb(0x1F, 0xB7, 0x14),
36 qRgb(0x00, 0x64, 0x11),
37 qRgb(0x56, 0x2C, 0x05),
38 qRgb(0x90, 0x71, 0x3A),
39 qRgb(0xC0, 0xC0, 0xC0),
40 qRgb(0x80, 0x80, 0x80),
41 qRgb(0x40, 0x40, 0x40),
42 qRgb(0x00, 0x00, 0x00)
47 qRgb(0xFF, 0xFF, 0xFF),
48 qRgb(0xFF, 0xFF, 0xCC),
49 qRgb(0xFF, 0xFF, 0x99),
50 qRgb(0xFF, 0xFF, 0x66),
51 qRgb(0xFF, 0xFF, 0x33),
52 qRgb(0xFF, 0xFF, 0x00),
53 qRgb(0xFF, 0xCC, 0xFF),
54 qRgb(0xFF, 0xCC, 0xCC),
55 qRgb(0xFF, 0xCC, 0x99),
56 qRgb(0xFF, 0xCC, 0x66),
57 qRgb(0xFF, 0xCC, 0x33),
58 qRgb(0xFF, 0xCC, 0x00),
59 qRgb(0xFF, 0x99, 0xFF),
60 qRgb(0xFF, 0x99, 0xCC),
61 qRgb(0xFF, 0x99, 0x99),
62 qRgb(0xFF, 0x99, 0x66),
63 qRgb(0xFF, 0x99, 0x33),
64 qRgb(0xFF, 0x99, 0x00),
65 qRgb(0xFF, 0x66, 0xFF),
66 qRgb(0xFF, 0x66, 0xCC),
67 qRgb(0xFF, 0x66, 0x99),
68 qRgb(0xFF, 0x66, 0x66),
69 qRgb(0xFF, 0x66, 0x33),
70 qRgb(0xFF, 0x66, 0x00),
71 qRgb(0xFF, 0x33, 0xFF),
72 qRgb(0xFF, 0x33, 0xCC),
73 qRgb(0xFF, 0x33, 0x99),
74 qRgb(0xFF, 0x33, 0x66),
75 qRgb(0xFF, 0x33, 0x33),
76 qRgb(0xFF, 0x33, 0x00),
77 qRgb(0xFF, 0x00, 0xFF),
78 qRgb(0xFF, 0x00, 0xCC),
79 qRgb(0xFF, 0x00, 0x99),
80 qRgb(0xFF, 0x00, 0x66),
81 qRgb(0xFF, 0x00, 0x33),
82 qRgb(0xFF, 0x00, 0x00),
83 qRgb(0xCC, 0xFF, 0xFF),
84 qRgb(0xCC, 0xFF, 0xCC),
85 qRgb(0xCC, 0xFF, 0x99),
86 qRgb(0xCC, 0xFF, 0x66),
87 qRgb(0xCC, 0xFF, 0x33),
88 qRgb(0xCC, 0xFF, 0x00),
89 qRgb(0xCC, 0xCC, 0xFF),
90 qRgb(0xCC, 0xCC, 0xCC),
91 qRgb(0xCC, 0xCC, 0x99),
92 qRgb(0xCC, 0xCC, 0x66),
93 qRgb(0xCC, 0xCC, 0x33),
94 qRgb(0xCC, 0xCC, 0x00),
95 qRgb(0xCC, 0x99, 0xFF),
96 qRgb(0xCC, 0x99, 0xCC),
97 qRgb(0xCC, 0x99, 0x99),
98 qRgb(0xCC, 0x99, 0x66),
99 qRgb(0xCC, 0x99, 0x33),
100 qRgb(0xCC, 0x99, 0x00),
101 qRgb(0xCC, 0x66, 0xFF),
102 qRgb(0xCC, 0x66, 0xCC),
103 qRgb(0xCC, 0x66, 0x99),
104 qRgb(0xCC, 0x66, 0x66),
105 qRgb(0xCC, 0x66, 0x33),
106 qRgb(0xCC, 0x66, 0x00),
107 qRgb(0xCC, 0x33, 0xFF),
108 qRgb(0xCC, 0x33, 0xCC),
109 qRgb(0xCC, 0x33, 0x99),
110 qRgb(0xCC, 0x33, 0x66),
111 qRgb(0xCC, 0x33, 0x33),
112 qRgb(0xCC, 0x33, 0x00),
113 qRgb(0xCC, 0x00, 0xFF),
114 qRgb(0xCC, 0x00, 0xCC),
115 qRgb(0xCC, 0x00, 0x99),
116 qRgb(0xCC, 0x00, 0x66),
117 qRgb(0xCC, 0x00, 0x33),
118 qRgb(0xCC, 0x00, 0x00),
119 qRgb(0x99, 0xFF, 0xFF),
120 qRgb(0x99, 0xFF, 0xCC),
121 qRgb(0x99, 0xFF, 0x99),
122 qRgb(0x99, 0xFF, 0x66),
123 qRgb(0x99, 0xFF, 0x33),
124 qRgb(0x99, 0xFF, 0x00),
125 qRgb(0x99, 0xCC, 0xFF),
126 qRgb(0x99, 0xCC, 0xCC),
127 qRgb(0x99, 0xCC, 0x99),
128 qRgb(0x99, 0xCC, 0x66),
129 qRgb(0x99, 0xCC, 0x33),
130 qRgb(0x99, 0xCC, 0x00),
131 qRgb(0x99, 0x99, 0xFF),
132 qRgb(0x99, 0x99, 0xCC),
133 qRgb(0x99, 0x99, 0x99),
134 qRgb(0x99, 0x99, 0x66),
135 qRgb(0x99, 0x99, 0x33),
136 qRgb(0x99, 0x99, 0x00),
137 qRgb(0x99, 0x66, 0xFF),
138 qRgb(0x99, 0x66, 0xCC),
139 qRgb(0x99, 0x66, 0x99),
140 qRgb(0x99, 0x66, 0x66),
141 qRgb(0x99, 0x66, 0x33),
142 qRgb(0x99, 0x66, 0x00),
143 qRgb(0x99, 0x33, 0xFF),
144 qRgb(0x99, 0x33, 0xCC),
145 qRgb(0x99, 0x33, 0x99),
146 qRgb(0x99, 0x33, 0x66),
147 qRgb(0x99, 0x33, 0x33),
148 qRgb(0x99, 0x33, 0x00),
149 qRgb(0x99, 0x00, 0xFF),
150 qRgb(0x99, 0x00, 0xCC),
151 qRgb(0x99, 0x00, 0x99),
152 qRgb(0x99, 0x00, 0x66),
153 qRgb(0x99, 0x00, 0x33),
154 qRgb(0x99, 0x00, 0x00),
155 qRgb(0x66, 0xFF, 0xFF),
156 qRgb(0x66, 0xFF, 0xCC),
157 qRgb(0x66, 0xFF, 0x99),
158 qRgb(0x66, 0xFF, 0x66),
159 qRgb(0x66, 0xFF, 0x33),
160 qRgb(0x66, 0xFF, 0x00),
161 qRgb(0x66, 0xCC, 0xFF),
162 qRgb(0x66, 0xCC, 0xCC),
163 qRgb(0x66, 0xCC, 0x99),
164 qRgb(0x66, 0xCC, 0x66),
165 qRgb(0x66, 0xCC, 0x33),
166 qRgb(0x66, 0xCC, 0x00),
167 qRgb(0x66, 0x99, 0xFF),
168 qRgb(0x66, 0x99, 0xCC),
169 qRgb(0x66, 0x99, 0x99),
170 qRgb(0x66, 0x99, 0x66),
171 qRgb(0x66, 0x99, 0x33),
172 qRgb(0x66, 0x99, 0x00),
173 qRgb(0x66, 0x66, 0xFF),
174 qRgb(0x66, 0x66, 0xCC),
175 qRgb(0x66, 0x66, 0x99),
176 qRgb(0x66, 0x66, 0x66),
177 qRgb(0x66, 0x66, 0x33),
178 qRgb(0x66, 0x66, 0x00),
179 qRgb(0x66, 0x33, 0xFF),
180 qRgb(0x66, 0x33, 0xCC),
181 qRgb(0x66, 0x33, 0x99),
182 qRgb(0x66, 0x33, 0x66),
183 qRgb(0x66, 0x33, 0x33),
184 qRgb(0x66, 0x33, 0x00),
185 qRgb(0x66, 0x00, 0xFF),
186 qRgb(0x66, 0x00, 0xCC),
187 qRgb(0x66, 0x00, 0x99),
188 qRgb(0x66, 0x00, 0x66),
189 qRgb(0x66, 0x00, 0x33),
190 qRgb(0x66, 0x00, 0x00),
191 qRgb(0x33, 0xFF, 0xFF),
192 qRgb(0x33, 0xFF, 0xCC),
193 qRgb(0x33, 0xFF, 0x99),
194 qRgb(0x33, 0xFF, 0x66),
195 qRgb(0x33, 0xFF, 0x33),
196 qRgb(0x33, 0xFF, 0x00),
197 qRgb(0x33, 0xCC, 0xFF),
198 qRgb(0x33, 0xCC, 0xCC),
199 qRgb(0x33, 0xCC, 0x99),
200 qRgb(0x33, 0xCC, 0x66),
201 qRgb(0x33, 0xCC, 0x33),
202 qRgb(0x33, 0xCC, 0x00),
203 qRgb(0x33, 0x99, 0xFF),
204 qRgb(0x33, 0x99, 0xCC),
205 qRgb(0x33, 0x99, 0x99),
206 qRgb(0x33, 0x99, 0x66),
207 qRgb(0x33, 0x99, 0x33),
208 qRgb(0x33, 0x99, 0x00),
209 qRgb(0x33, 0x66, 0xFF),
210 qRgb(0x33, 0x66, 0xCC),
211 qRgb(0x33, 0x66, 0x99),
212 qRgb(0x33, 0x66, 0x66),
213 qRgb(0x33, 0x66, 0x33),
214 qRgb(0x33, 0x66, 0x00),
215 qRgb(0x33, 0x33, 0xFF),
216 qRgb(0x33, 0x33, 0xCC),
217 qRgb(0x33, 0x33, 0x99),
218 qRgb(0x33, 0x33, 0x66),
219 qRgb(0x33, 0x33, 0x33),
220 qRgb(0x33, 0x33, 0x00),
221 qRgb(0x33, 0x00, 0xFF),
222 qRgb(0x33, 0x00, 0xCC),
223 qRgb(0x33, 0x00, 0x99),
224 qRgb(0x33, 0x00, 0x66),
225 qRgb(0x33, 0x00, 0x33),
226 qRgb(0x33, 0x00, 0x00),
227 qRgb(0x00, 0xFF, 0xFF),
228 qRgb(0x00, 0xFF, 0xCC),
229 qRgb(0x00, 0xFF, 0x99),
230 qRgb(0x00, 0xFF, 0x66),
231 qRgb(0x00, 0xFF, 0x33),
232 qRgb(0x00, 0xFF, 0x00),
233 qRgb(0x00, 0xCC, 0xFF),
234 qRgb(0x00, 0xCC, 0xCC),
235 qRgb(0x00, 0xCC, 0x99),
236 qRgb(0x00, 0xCC, 0x66),
237 qRgb(0x00, 0xCC, 0x33),
238 qRgb(0x00, 0xCC, 0x00),
239 qRgb(0x00, 0x99, 0xFF),
240 qRgb(0x00, 0x99, 0xCC),
241 qRgb(0x00, 0x99, 0x99),
242 qRgb(0x00, 0x99, 0x66),
243 qRgb(0x00, 0x99, 0x33),
244 qRgb(0x00, 0x99, 0x00),
245 qRgb(0x00, 0x66, 0xFF),
246 qRgb(0x00, 0x66, 0xCC),
247 qRgb(0x00, 0x66, 0x99),
248 qRgb(0x00, 0x66, 0x66),
249 qRgb(0x00, 0x66, 0x33),
250 qRgb(0x00, 0x66, 0x00),
251 qRgb(0x00, 0x33, 0xFF),
252 qRgb(0x00, 0x33, 0xCC),
253 qRgb(0x00, 0x33, 0x99),
254 qRgb(0x00, 0x33, 0x66),
255 qRgb(0x00, 0x33, 0x33),
256 qRgb(0x00, 0x33, 0x00),
257 qRgb(0x00, 0x00, 0xFF),
258 qRgb(0x00, 0x00, 0xCC),
259 qRgb(0x00, 0x00, 0x99),
260 qRgb(0x00, 0x00, 0x66),
261 qRgb(0x00, 0x00, 0x33),
262 qRgb(0xEE, 0x00, 0x00),
263 qRgb(0xDD, 0x00, 0x00),
264 qRgb(0xBB, 0x00, 0x00),
265 qRgb(0xAA, 0x00, 0x00),
266 qRgb(0x88, 0x00, 0x00),
267 qRgb(0x77, 0x00, 0x00),
268 qRgb(0x55, 0x00, 0x00),
269 qRgb(0x44, 0x00, 0x00),
270 qRgb(0x22, 0x00, 0x00),
271 qRgb(0x11, 0x00, 0x00),
272 qRgb(0x00, 0xEE, 0x00),
273 qRgb(0x00, 0xDD, 0x00),
274 qRgb(0x00, 0xBB, 0x00),
275 qRgb(0x00, 0xAA, 0x00),
276 qRgb(0x00, 0x88, 0x00),
277 qRgb(0x00, 0x77, 0x00),
278 qRgb(0x00, 0x55, 0x00),
279 qRgb(0x00, 0x44, 0x00),
280 qRgb(0x00, 0x22, 0x00),
281 qRgb(0x00, 0x11, 0x00),
282 qRgb(0x00, 0x00, 0xEE),
283 qRgb(0x00, 0x00, 0xDD),
284 qRgb(0x00, 0x00, 0xBB),
285 qRgb(0x00, 0x00, 0xAA),
286 qRgb(0x00, 0x00, 0x88),
287 qRgb(0x00, 0x00, 0x77),
288 qRgb(0x00, 0x00, 0x55),
289 qRgb(0x00, 0x00, 0x44),
290 qRgb(0x00, 0x00, 0x22),
291 qRgb(0x00, 0x00, 0x11),
292 qRgb(0xEE, 0xEE, 0xEE),
293 qRgb(0xDD, 0xDD, 0xDD),
294 qRgb(0xBB, 0xBB, 0xBB),
295 qRgb(0xAA, 0xAA, 0xAA),
296 qRgb(0x88, 0x88, 0x88),
297 qRgb(0x77, 0x77, 0x77),
298 qRgb(0x55, 0x55, 0x55),
299 qRgb(0x44, 0x44, 0x44),
300 qRgb(0x22, 0x22, 0x22),
301 qRgb(0x11, 0x11, 0x11),
302 qRgb(0x00, 0x00, 0x00)
322 return u == r && ((u % 16 == 0) || (r >= 16 && (u & (u - 1)) == 0));
327 return header.ostype != 0 &&
341 return mask.variant == icon.variant && mask
.depth == target
342 && mask.height == icon.height && mask.width == icon.width;
347 const quint32 bytes = qToBigEndian(ostype);
348 return QByteArray((
const char*)&bytes, 4);
353 if (ostype.size() != 4)
355 return qFromBigEndian(*
reinterpret_cast<
const quint32*>(ostype.constData()));
360 const bool portable = iconNumber < 7;
361 const QByteArray base = portable ? QByteArrayLiteral(
"icp") : QByteArrayLiteral(
"ic");
362 if (!portable && iconNumber < 10)
363 return base +
"0" + QByteArray::number(iconNumber);
364 return base + QByteArray::number(iconNumber);
374 data = ICNSColorTableMono;
377 data = ICNSColorTable4bit;
380 data = ICNSColorTable8bit;
387 memcpy(table.data(), data,
sizeof(QRgb) * n);
393 const qint64 oldPos = device->pos();
394 if (oldPos != icon.dataOffset && !device->seek(icon.dataOffset))
397 const QByteArray magic = device->peek(12);
398 const bool isPNG = magic.startsWith(QByteArrayLiteral(
"\211PNG\r\n\032\n\000\000\000\r"));
399 const bool isJP2 = !isPNG && magic == QByteArrayLiteral(
"\000\000\000\014jP \r\n\207\n");
400 if (isPNG || isJP2) {
405 if (oldPos != icon.dataOffset && !device->seek(oldPos))
412 const QString ostype = QString::fromLatin1(nameFromOSType(icon.ostype));
415 const QString ptrn = QStringLiteral(
"^(?<junk>[a-z|A-Z]{0,4})(?<group>[a-z|A-Z]{1})(?<depth>[\\d]{0,2})(?<mask>[#mk]{0,2})$");
416 QRegularExpression regexp(ptrn);
417 QRegularExpressionMatch match = regexp.match(ostype);
418 if (!match.hasMatch()) {
419 qWarning(
"parseIconEntryInfo(): Failed, OSType doesn't match: \"%s\"", qPrintable(ostype));
422 const QString group = match.captured(QStringLiteral(
"group"));
423 const QString depth = match.captured(QStringLiteral(
"depth"));
424 const QString mask = match.captured(QStringLiteral(
"mask"));
426 if (!group.isEmpty())
433 if (!depth.isEmpty()) {
434 const uint depthUInt = depth.toUInt();
443 const qreal bytespp = (qreal)icon.depth / 8;
444 const qreal r1 = qSqrt(icon.dataLength / bytespp);
445 const qreal r2 = qSqrt((icon.dataLength / bytespp) / 2);
446 const quint32 r1u = qRound(r1);
447 const quint32 r2u = qRound(r2);
448 const bool singleEntry = isPowOf2OrDividesBy16(r1u, r1);
449 const bool doubleSize = isPowOf2OrDividesBy16(r2u, r2);
455 }
else if (doubleSize) {
462 const bool doubleSize = icon.dataLength == 192 * bytespp * 2;
485 qWarning(
"parseIconEntryInfo(): Failed, 32bit icon from an unknown group. OSType: \"%s\"",
488 icon.height = icon.width;
491 if (icon.width == 0 || icon.width > 4096)
501 qWarning(
"readMask(): Failed, unusual bit depth: %u OSType: \"%s\"",
502 mask.depth, nameFromOSType(mask.ostype).constData());
507 const quint32 imageDataSize = (mask.width * mask.height * mask
.depth) / 8;
508 const qint64 pos = doubleSize ? (mask.dataOffset + imageDataSize) : mask.dataOffset;
509 const qint64 oldPos = stream.device()->pos();
510 if (!stream.device()->seek(pos))
513 if (!QImageIOHandler::allocateImage(QSize(mask.width, mask.height), QImage::Format_RGB32, &img))
517 for (quint32 y = 0; y < mask.height; y++) {
518 QRgb *line =
reinterpret_cast<QRgb *>(img.scanLine(y));
519 for (quint32 x = 0; x < mask.width; x++) {
520 if (pixel % (8 / mask
.depth) == 0)
524 const quint8 alpha = isMono ? (((byte >> 7) & 0x01) * 255) : byte;
525 line[x] = qRgb(alpha, alpha, alpha);
529 stream.device()->seek(oldPos);
540 const QImage::Format format = isMono ? QImage::Format_Mono : QImage::Format_Indexed8;
541 const QList<QRgb> colortable = getColorTable(depth);
542 if (colortable.isEmpty())
545 if (!QImageIOHandler::allocateImage(QSize(icon.width, icon.height), format, &img))
547 img.setColorTable(colortable);
550 for (quint32 y = 0; y < icon.height; y++) {
551 for (quint32 x = 0; x < icon.width; x++) {
552 if (pixel % (8 / depth) == 0)
557 cindex = (byte >> 7) & 0x01;
561 cindex = (byte >> 4) & 0x0F;
568 img.setPixel(x, y, cindex);
578 if (!QImageIOHandler::allocateImage(QSize(icon.width, icon.height), QImage::Format_RGB32, &img))
581 for (quint32 y = 0; y < icon.height; y++) {
582 QRgb *line =
reinterpret_cast<QRgb *>(img.scanLine(y));
583 for (quint32 x = 0; x < icon.width; x++) {
585 stream >> r >> g >> b >> a;
586 line[x] = qRgb(r, g, b);
590 const quint32 estPxsNum = icon.width * icon.height;
591 const QByteArray &bytes = stream.device()->peek(4);
595 if (qFromBigEndian<quint32>(*bytes.constData()) == 0)
596 stream.skipRawData(4);
597 for (quint8 colorNRun = 0; colorNRun < 3; colorNRun++) {
600 while (pixel < estPxsNum && !stream.atEnd()) {
603 const bool bitIsClear = (byte & 0x80) == 0;
605 quint8 runLength = bitIsClear ? ((0xFF & byte) + 1) : ((0xFF & byte) - 125);
610 for (quint8 i = 0; i < runLength && pixel < estPxsNum; i++) {
613 const quint32 y = pixel / icon.height;
614 const quint32 x = pixel - (icon.width * y);
615 if (pixel % icon.height == 0)
616 line =
reinterpret_cast<QRgb *>(img.scanLine(y));
618 const int r = (colorNRun == 0) ? value : qRed(rgb);
619 const int g = (colorNRun == 1) ? value : qGreen(rgb);
620 const int b = (colorNRun == 2) ? value : qBlue(rgb);
621 line[x] = qRgb(r, g, b);
631 m_currentIconIndex(0), m_state(ScanNotScanned)
637 if (!device || !device->isReadable()) {
638 qWarning(
"QICNSHandler::canRead() called without a readable device");
642 if (device->peek(4) == QByteArrayLiteral(
"icns")) {
643 if (device->isSequential()) {
644 qWarning(
"QICNSHandler::canRead() called on a sequential device");
655 if (m_state == ScanNotScanned && !
canRead(device()
))
658 if (m_state != ScanError) {
659 setFormat(QByteArrayLiteral(
"icns"));
669 if (!ensureScanned() || m_currentIconIndex >= m_icons.size()) {
670 qWarning(
"QICNSHandler::read(): The device wasn't parsed properly!");
674 const ICNSEntry &icon = m_icons.at(m_currentIconIndex);
675 QDataStream stream(device());
676 stream.setByteOrder(QDataStream::BigEndian);
677 if (!device()->seek(icon.dataOffset))
683 if (qMin(icon.width, icon.height) == 0)
695 case ICNSEntry::Depth32bit:
696 img = read32bitIcon(icon, stream);
699 qWarning(
"QICNSHandler::read(): Failed, unsupported icon bit depth: %u, OSType: \"%s\"",
700 icon.depth, nameFromOSType(icon.ostype).constData());
703 QImage alpha = readMask(getIconMask(icon), stream);
705 img.setAlphaChannel(alpha);
709 const char *format = 0;
715 img = QImage::fromData(device()->read(icon.dataLength), format);
719 qWarning(
"QICNSHandler::read(): Failed, compressed format \"%s\" is not supported "
720 "by your Qt library or this file is corrupt. OSType: \"%s\"",
721 format, nameFromOSType(icon.ostype).constData());
725 return !img.isNull();
731
732
733
734
735
736
737
738
739 QIODevice *device =
this->device();
740 if (!device->isWritable() || image.isNull() || qMin(image.width(), image.height()) == 0)
742 const int minSize = qMin(image.width(), image.height());
743 const int oldSize = (minSize < 16) ? 16 : minSize;
748 while (pow < 10 && (size >>= 1))
750 const int newSize = 1 << pow;
753 if (newSize != oldSize || qMax(image.width(), image.height()) != minSize)
754 img = img.scaled(newSize, newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
756 const quint32 ostype = nameToOSType(nameForCompressedIcon(pow));
757 ICNSBlockHeader fileHeader;
758 fileHeader.ostype = ICNSBlockHeader::TypeIcns;
759 ICNSBlockHeader tocHeader;
760 tocHeader.ostype = ICNSBlockHeader::TypeToc;
761 ICNSBlockHeader iconEntry;
762 iconEntry.ostype = ostype;
763 QByteArray imageData;
764 QBuffer buffer(&imageData);
765 if (!buffer.open(QIODevice::WriteOnly) || !img.save(&buffer,
"png"))
768 iconEntry.length = ICNSBlockHeaderSize + imageData.size();
769 tocHeader.length = ICNSBlockHeaderSize * 2;
770 fileHeader.length = ICNSBlockHeaderSize + tocHeader.length + iconEntry.length;
771 if (!isBlockHeaderValid(iconEntry))
774 QDataStream stream(device);
776 stream << fileHeader << tocHeader << iconEntry << iconEntry;
777 stream.writeRawData(imageData.constData(), imageData.size());
778 return stream.status() == QDataStream::Ok;
783 return option == SubType;
788 if (!supportsOption(option) || !ensureScanned())
791 if (option == SubType) {
793 const ICNSEntry &icon = m_icons.at(m_currentIconIndex);
794 if (icon.variant != 0)
795 return QByteArray(nameFromOSType(icon.variant) +
'-' + nameFromOSType(icon.ostype));
796 return nameFromOSType(icon.ostype);
805 if (!ensureScanned())
808 return m_icons.size();
816 m_currentIconIndex = imageNumber;
827 if (m_state == ScanNotScanned) {
829 that->m_state = that->scanDevice() ? ScanSuccess : ScanError;
832 return m_state == ScanSuccess;
835bool QICNSHandler::addEntry(
const ICNSBlockHeader &header, qint64 imgDataOffset, quint32 variant)
839 entry.ostype = header.ostype;
840 entry.variant = variant;
841 entry.dataOffset = imgDataOffset;
842 entry.dataLength = header.length - ICNSBlockHeaderSize;
844 if (!parseIconEntryData(entry, device()))
848 if ((entry.flags & ICNSEntry::IsMask) != 0)
850 if ((entry.flags & ICNSEntry::IsIcon) != 0)
858 if (m_state == ScanSuccess)
861 if (!device()->seek(0))
864 QDataStream stream(device());
865 stream.setByteOrder(QDataStream::BigEndian);
867 bool scanIsIncomplete =
false;
868 qint64 filelength = device()->size();
869 ICNSBlockHeader blockHeader;
870 while (!stream.atEnd() || device()->pos() < filelength) {
871 stream >> blockHeader;
872 if (stream.status() != QDataStream::Ok)
875 const qint64 blockDataOffset = device()->pos();
876 if (!isBlockHeaderValid(blockHeader, ICNSBlockHeaderSize - blockDataOffset + filelength)) {
877 qWarning(
"QICNSHandler::scanDevice(): Failed, bad header at pos %s. OSType \"%s\", length %u",
878 QByteArray::number(blockDataOffset).constData(),
879 nameFromOSType(blockHeader.ostype).constData(), blockHeader.length);
882 const quint64 blockDataLength = blockHeader.length - ICNSBlockHeaderSize;
883 const qint64 nextBlockOffset = blockDataOffset + blockDataLength;
885 switch (blockHeader.ostype) {
886 case ICNSBlockHeader::TypeIcns:
887 if (blockDataOffset != ICNSBlockHeaderSize) {
890 stream.skipRawData(blockDataLength);
893 filelength = blockHeader.length;
894 if (device()->size() < blockHeader.length) {
895 qWarning(
"QICNSHandler::scanDevice(): Failed, file is incomplete.");
899 case ICNSBlockHeader::TypeIcnv:
900 case ICNSBlockHeader::TypeClut:
902 stream.skipRawData(blockDataLength);
904 case ICNSBlockHeader::TypeTile:
905 case ICNSBlockHeader::TypeOver:
906 case ICNSBlockHeader::TypeOpen:
907 case ICNSBlockHeader::TypeDrop:
908 case ICNSBlockHeader::TypeOdrp:
911 while (!stream.atEnd() && device()->pos() < nextBlockOffset) {
912 ICNSBlockHeader icon;
914 if (stream.status() != QDataStream::Ok)
917 quint64 remaining = blockDataLength - (device()->pos() - blockDataOffset);
918 if (!isBlockHeaderValid(icon, ICNSBlockHeaderSize + remaining))
920 if (!addEntry(icon, device()->pos(), blockHeader.ostype))
922 if (stream.skipRawData(icon.length - ICNSBlockHeaderSize) < 0)
925 if (device()->pos() != nextBlockOffset) {
928 qWarning(
"Scan of the icon variant container (\"%s\") failed at pos %s.\n"
929 "Reason: Scan didn't reach the end of this container's block, "
930 "delta: %s bytes. This file may be corrupted.",
931 nameFromOSType(blockHeader.ostype).constData(),
932 QByteArray::number(device()->pos()).constData(),
933 QByteArray::number(nextBlockOffset - device()->pos()).constData());
934 if (!device()->seek(nextBlockOffset))
938 case ICNSBlockHeader::TypeToc: {
940 if (blockDataOffset != ICNSBlockHeaderSize * 2) {
943 stream.skipRawData(blockDataLength);
947 qint64 imgDataOffset = blockDataOffset + blockHeader.length;
948 for (uint i = 0, count = blockDataLength / ICNSBlockHeaderSize; i < count; i++) {
949 ICNSBlockHeader tocEntry;
951 if (!isBlockHeaderValid(tocEntry)) {
953 qWarning(
"QICNSHandler::scanDevice(): Warning! Table of contents contains a bad "
954 "entry! Stop at device pos: %s bytes. This file may be corrupted.",
955 QByteArray::number(device()->pos()).constData());
956 if (!device()->seek(nextBlockOffset))
960 if (!addEntry(tocEntry, imgDataOffset))
962 imgDataOffset += tocEntry.length;
964 if (imgDataOffset == filelength)
968 scanIsIncomplete =
true;
973 if (scanIsIncomplete) {
977 for (
int i = 0; i < m_icons.size() && !exists; i++)
978 exists = m_icons.at(i).dataOffset == blockDataOffset;
979 for (
int i = 0; i < m_masks.size() && !exists; i++)
980 exists = m_masks.at(i).dataOffset == blockDataOffset;
981 if (!exists && !addEntry(blockHeader, blockDataOffset))
983 }
else if (!addEntry(blockHeader, blockDataOffset)) {
986 stream.skipRawData(blockDataLength);
990 return (m_icons.size() > 0);
997 for (
int i = 0; i < m_masks.size(); i++) {
int imageCount() const override
For image formats that support animation, this function returns the number of images in the animation...
bool write(const QImage &image) override
Writes the image image to the assigned device.
bool read(QImage *image) override
Read an image from the device, and stores it in image.
bool canRead() const override
Returns true if an image can be read from the device (i.e., the image format is supported,...
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
bool jumpToImage(int imageNumber) override
For image formats that support animation, this function jumps to the image whose sequence number is i...
bool jumpToNextImage() override
For image formats that support animation, this function jumps to the next image.
static bool isPowOf2OrDividesBy16(quint32 u, qreal r)
static bool isBlockHeaderValid(const ICNSBlockHeader &header, quint64 bound=0)
static const QRgb ICNSColorTableMono[]
static QImage readLowDepthIcon(const ICNSEntry &icon, QDataStream &stream)
static bool isMaskSuitable(const ICNSEntry &mask, const ICNSEntry &icon, ICNSEntry::Depth target)
static bool isIconCompressed(const ICNSEntry &icon)
static const QRgb ICNSColorTable4bit[]
static QList< QRgb > getColorTable(ICNSEntry::Depth depth)
static QByteArray nameForCompressedIcon(quint8 iconNumber)
static bool parseIconEntryData(ICNSEntry &icon, QIODevice *device)
static bool parseIconEntryInfo(ICNSEntry &icon)
static QImage read32bitIcon(const ICNSEntry &icon, QDataStream &stream)
static quint32 nameToOSType(const QByteArray &ostype)
static QByteArray nameFromOSType(quint32 ostype)
static const QRgb ICNSColorTable8bit[]
static QImage readMask(const ICNSEntry &mask, QDataStream &stream)
static QT_BEGIN_NAMESPACE const quint8 ICNSBlockHeaderSize
static QDataStream & operator>>(QDataStream &in, ICNSBlockHeader &p)
static QDataStream & operator<<(QDataStream &out, const ICNSBlockHeader &p)
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)