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);
591 const quint32 estPxsNum = icon.width * icon.height;
592 const QByteArray &bytes = stream.device()->peek(4);
596 if (qFromBigEndian<quint32>(*bytes.constData()) == 0)
597 stream.skipRawData(4);
598 for (quint8 colorNRun = 0; colorNRun < 3; colorNRun++) {
601 while (pixel < estPxsNum && !stream.atEnd()) {
604 const bool bitIsClear = (byte & 0x80) == 0;
606 quint8 runLength = bitIsClear ? ((0xFF & byte) + 1) : ((0xFF & byte) - 125);
611 for (quint8 i = 0; i < runLength && pixel < estPxsNum; i++) {
614 const quint32 y = pixel / icon.height;
615 const quint32 x = pixel - (icon.width * y);
616 if (pixel % icon.height == 0)
617 line =
reinterpret_cast<QRgb *>(img.scanLine(y));
619 const int r = (colorNRun == 0) ? value : qRed(rgb);
620 const int g = (colorNRun == 1) ? value : qGreen(rgb);
621 const int b = (colorNRun == 2) ? value : qBlue(rgb);
622 line[x] = qRgb(r, g, b);
632 m_currentIconIndex(0), m_state(ScanNotScanned)
638 if (!device || !device->isReadable()) {
639 qWarning(
"QICNSHandler::canRead() called without a readable device");
643 if (device->peek(4) == QByteArrayLiteral(
"icns")) {
644 if (device->isSequential()) {
645 qWarning(
"QICNSHandler::canRead() called on a sequential device");
656 if (m_state == ScanNotScanned && !
canRead(device()
))
659 if (m_state != ScanError) {
660 setFormat(QByteArrayLiteral(
"icns"));
670 if (!ensureScanned() || m_currentIconIndex >= m_icons.size()) {
671 qWarning(
"QICNSHandler::read(): The device wasn't parsed properly!");
675 const ICNSEntry &icon = m_icons.at(m_currentIconIndex);
676 QDataStream stream(device());
677 stream.setByteOrder(QDataStream::BigEndian);
678 if (!device()->seek(icon.dataOffset))
684 if (qMin(icon.width, icon.height) == 0)
696 case ICNSEntry::Depth32bit:
697 img = read32bitIcon(icon, stream);
700 qWarning(
"QICNSHandler::read(): Failed, unsupported icon bit depth: %u, OSType: \"%s\"",
701 icon
.depth, nameFromOSType(icon.ostype).constData());
704 QImage alpha = readMask(getIconMask(icon), stream);
706 img.setAlphaChannel(alpha);
710 const char *format = 0;
716 if (qint64(icon.dataLength) <= device()->bytesAvailable())
717 img = QImage::fromData(device()->read(icon.dataLength), format);
721 qWarning(
"QICNSHandler::read(): Failed, compressed format \"%s\" is not supported "
722 "by your Qt library or this file is corrupt. OSType: \"%s\"",
723 format, nameFromOSType(icon.ostype).constData());
727 return !img.isNull();
733
734
735
736
737
738
739
740
741 QIODevice *device =
this->device();
742 if (!device->isWritable() || image.isNull() || qMin(image.width(), image.height()) == 0)
744 const int minSize = qMin(image.width(), image.height());
745 const int oldSize = (minSize < 16) ? 16 : minSize;
750 while (pow < 10 && (size >>= 1))
752 const int newSize = 1 << pow;
755 if (newSize != oldSize || qMax(image.width(), image.height()) != minSize)
756 img = img.scaled(newSize, newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
758 const quint32 ostype = nameToOSType(nameForCompressedIcon(pow));
759 ICNSBlockHeader fileHeader;
760 fileHeader.ostype = ICNSBlockHeader::TypeIcns;
761 ICNSBlockHeader tocHeader;
762 tocHeader.ostype = ICNSBlockHeader::TypeToc;
763 ICNSBlockHeader iconEntry;
764 iconEntry.ostype = ostype;
765 QByteArray imageData;
766 QBuffer buffer(&imageData);
767 if (!buffer.open(QIODevice::WriteOnly) || !img.save(&buffer,
"png"))
770 iconEntry.length = ICNSBlockHeaderSize + imageData.size();
771 tocHeader.length = ICNSBlockHeaderSize * 2;
772 fileHeader.length = ICNSBlockHeaderSize + tocHeader.length + iconEntry.length;
773 if (!isBlockHeaderValid(iconEntry))
776 QDataStream stream(device);
778 stream << fileHeader << tocHeader << iconEntry << iconEntry;
779 stream.writeRawData(imageData.constData(), imageData.size());
780 return stream.status() == QDataStream::Ok;
785 return option == SubType;
790 if (!supportsOption(option) || !ensureScanned())
793 if (option == SubType) {
795 const ICNSEntry &icon = m_icons.at(m_currentIconIndex);
796 if (icon.variant != 0)
797 return QByteArray(nameFromOSType(icon.variant) +
'-' + nameFromOSType(icon.ostype));
798 return nameFromOSType(icon.ostype);
807 if (!ensureScanned())
810 return m_icons.size();
818 m_currentIconIndex = imageNumber;
829 if (m_state == ScanNotScanned) {
831 that->m_state = that->scanDevice() ? ScanSuccess : ScanError;
834 return m_state == ScanSuccess;
837bool QICNSHandler::addEntry(
const ICNSBlockHeader &header, qint64 imgDataOffset, quint32 variant)
841 entry.ostype = header.ostype;
842 entry.variant = variant;
843 entry.dataOffset = imgDataOffset;
844 entry.dataLength = header.length - ICNSBlockHeaderSize;
846 if (!parseIconEntryData(entry, device()))
850 if ((entry.flags & ICNSEntry::IsMask) != 0)
852 if ((entry.flags & ICNSEntry::IsIcon) != 0)
860 if (m_state == ScanSuccess)
863 if (!device()->seek(0))
866 QDataStream stream(device());
867 stream.setByteOrder(QDataStream::BigEndian);
869 bool scanIsIncomplete =
false;
870 qint64 filelength = device()->size();
871 ICNSBlockHeader blockHeader;
872 while (!stream.atEnd() || device()->pos() < filelength) {
873 stream >> blockHeader;
874 if (stream.status() != QDataStream::Ok)
877 const qint64 blockDataOffset = device()->pos();
878 if (!isBlockHeaderValid(blockHeader, ICNSBlockHeaderSize - blockDataOffset + filelength)) {
879 qWarning(
"QICNSHandler::scanDevice(): Failed, bad header at pos %s. OSType \"%s\", length %u",
880 QByteArray::number(blockDataOffset).constData(),
881 nameFromOSType(blockHeader.ostype).constData(), blockHeader.length);
884 const quint64 blockDataLength = blockHeader.length - ICNSBlockHeaderSize;
885 const qint64 nextBlockOffset = blockDataOffset + blockDataLength;
887 switch (blockHeader.ostype) {
888 case ICNSBlockHeader::TypeIcns:
889 if (blockDataOffset != ICNSBlockHeaderSize) {
892 stream.skipRawData(blockDataLength);
895 filelength = blockHeader.length;
896 if (device()->size() < blockHeader.length) {
897 qWarning(
"QICNSHandler::scanDevice(): Failed, file is incomplete.");
901 case ICNSBlockHeader::TypeIcnv:
902 case ICNSBlockHeader::TypeClut:
904 stream.skipRawData(blockDataLength);
906 case ICNSBlockHeader::TypeTile:
907 case ICNSBlockHeader::TypeOver:
908 case ICNSBlockHeader::TypeOpen:
909 case ICNSBlockHeader::TypeDrop:
910 case ICNSBlockHeader::TypeOdrp:
913 while (!stream.atEnd() && device()->pos() < nextBlockOffset) {
914 ICNSBlockHeader icon;
916 if (stream.status() != QDataStream::Ok)
919 quint64 remaining = blockDataLength - (device()->pos() - blockDataOffset);
920 if (!isBlockHeaderValid(icon, ICNSBlockHeaderSize + remaining))
922 if (!addEntry(icon, device()->pos(), blockHeader.ostype))
924 if (stream.skipRawData(icon.length - ICNSBlockHeaderSize) < 0)
927 if (device()->pos() != nextBlockOffset) {
930 qWarning(
"Scan of the icon variant container (\"%s\") failed at pos %s.\n"
931 "Reason: Scan didn't reach the end of this container's block, "
932 "delta: %s bytes. This file may be corrupted.",
933 nameFromOSType(blockHeader.ostype).constData(),
934 QByteArray::number(device()->pos()).constData(),
935 QByteArray::number(nextBlockOffset - device()->pos()).constData());
936 if (!device()->seek(nextBlockOffset))
940 case ICNSBlockHeader::TypeToc: {
942 if (blockDataOffset != ICNSBlockHeaderSize * 2) {
945 stream.skipRawData(blockDataLength);
949 qint64 imgDataOffset = blockDataOffset + blockHeader.length;
950 for (uint i = 0, count = blockDataLength / ICNSBlockHeaderSize; i < count; i++) {
951 ICNSBlockHeader tocEntry;
953 if (imgDataOffset > filelength
954 || !isBlockHeaderValid(tocEntry, quint64(filelength - imgDataOffset))) {
956 qWarning(
"QICNSHandler::scanDevice(): Warning! Table of contents contains a bad "
957 "entry! Stop at device pos: %s bytes. This file may be corrupted.",
958 QByteArray::number(device()->pos()).constData());
959 if (!device()->seek(nextBlockOffset))
963 if (!addEntry(tocEntry, imgDataOffset))
965 imgDataOffset += tocEntry.length;
967 if (imgDataOffset == filelength)
971 scanIsIncomplete =
true;
976 if (scanIsIncomplete) {
980 for (
int i = 0; i < m_icons.size() && !exists; i++)
981 exists = m_icons.at(i).dataOffset == blockDataOffset;
982 for (
int i = 0; i < m_masks.size() && !exists; i++)
983 exists = m_masks.at(i).dataOffset == blockDataOffset;
984 if (!exists && !addEntry(blockHeader, blockDataOffset))
986 }
else if (!addEntry(blockHeader, blockDataOffset)) {
989 stream.skipRawData(blockDataLength);
993 return (m_icons.size() > 0);
1000 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.
Combined button and popup list for selecting options.
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)