| 1 | // Copyright (C) 2016 The Qt Company Ltd. | 
| 2 | // Copyright (C) 2016 Alex Char. | 
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 4 |  | 
| 5 | #include "qicnshandler_p.h" | 
| 6 |  | 
| 7 | #include <QtCore/qmath.h> | 
| 8 | #include <QtCore/qendian.h> | 
| 9 | #include <QtCore/qregularexpression.h> | 
| 10 | #include <QtCore/qbuffer.h> | 
| 11 | #include <QtGui/qimage.h> | 
| 12 |  | 
| 13 | #ifndef QT_NO_DATASTREAM | 
| 14 |  | 
| 15 | QT_BEGIN_NAMESPACE | 
| 16 |  | 
| 17 | static const quint8  = 8; | 
| 18 |  | 
| 19 | static const QRgb ICNSColorTableMono[] = { | 
| 20 |     qRgb(r: 0xFF, g: 0xFF, b: 0xFF), | 
| 21 |     qRgb(r: 0x00, g: 0x00, b: 0x00) | 
| 22 | }; | 
| 23 | Q_STATIC_ASSERT(sizeof(ICNSColorTableMono) / sizeof(ICNSColorTableMono[0]) == (1 << ICNSEntry::DepthMono)); | 
| 24 |  | 
| 25 | static const QRgb ICNSColorTable4bit[] = { | 
| 26 |     qRgb(r: 0xFF, g: 0xFF, b: 0xFF), | 
| 27 |     qRgb(r: 0xFC, g: 0xF3, b: 0x05), | 
| 28 |     qRgb(r: 0xFF, g: 0x64, b: 0x02), | 
| 29 |     qRgb(r: 0xDD, g: 0x08, b: 0x06), | 
| 30 |     qRgb(r: 0xF2, g: 0x08, b: 0x84), | 
| 31 |     qRgb(r: 0x46, g: 0x00, b: 0xA5), | 
| 32 |     qRgb(r: 0x00, g: 0x00, b: 0xD4), | 
| 33 |     qRgb(r: 0x02, g: 0xAB, b: 0xEA), | 
| 34 |     qRgb(r: 0x1F, g: 0xB7, b: 0x14), | 
| 35 |     qRgb(r: 0x00, g: 0x64, b: 0x11), | 
| 36 |     qRgb(r: 0x56, g: 0x2C, b: 0x05), | 
| 37 |     qRgb(r: 0x90, g: 0x71, b: 0x3A), | 
| 38 |     qRgb(r: 0xC0, g: 0xC0, b: 0xC0), | 
| 39 |     qRgb(r: 0x80, g: 0x80, b: 0x80), | 
| 40 |     qRgb(r: 0x40, g: 0x40, b: 0x40), | 
| 41 |     qRgb(r: 0x00, g: 0x00, b: 0x00) | 
| 42 | }; | 
| 43 | Q_STATIC_ASSERT(sizeof(ICNSColorTable4bit) / sizeof(ICNSColorTable4bit[0]) == (1 << ICNSEntry::Depth4bit)); | 
| 44 |  | 
| 45 | static const QRgb ICNSColorTable8bit[] = { | 
| 46 |     qRgb(r: 0xFF, g: 0xFF, b: 0xFF), | 
| 47 |     qRgb(r: 0xFF, g: 0xFF, b: 0xCC), | 
| 48 |     qRgb(r: 0xFF, g: 0xFF, b: 0x99), | 
| 49 |     qRgb(r: 0xFF, g: 0xFF, b: 0x66), | 
| 50 |     qRgb(r: 0xFF, g: 0xFF, b: 0x33), | 
| 51 |     qRgb(r: 0xFF, g: 0xFF, b: 0x00), | 
| 52 |     qRgb(r: 0xFF, g: 0xCC, b: 0xFF), | 
| 53 |     qRgb(r: 0xFF, g: 0xCC, b: 0xCC), | 
| 54 |     qRgb(r: 0xFF, g: 0xCC, b: 0x99), | 
| 55 |     qRgb(r: 0xFF, g: 0xCC, b: 0x66), | 
| 56 |     qRgb(r: 0xFF, g: 0xCC, b: 0x33), | 
| 57 |     qRgb(r: 0xFF, g: 0xCC, b: 0x00), | 
| 58 |     qRgb(r: 0xFF, g: 0x99, b: 0xFF), | 
| 59 |     qRgb(r: 0xFF, g: 0x99, b: 0xCC), | 
| 60 |     qRgb(r: 0xFF, g: 0x99, b: 0x99), | 
| 61 |     qRgb(r: 0xFF, g: 0x99, b: 0x66), | 
| 62 |     qRgb(r: 0xFF, g: 0x99, b: 0x33), | 
| 63 |     qRgb(r: 0xFF, g: 0x99, b: 0x00), | 
| 64 |     qRgb(r: 0xFF, g: 0x66, b: 0xFF), | 
| 65 |     qRgb(r: 0xFF, g: 0x66, b: 0xCC), | 
| 66 |     qRgb(r: 0xFF, g: 0x66, b: 0x99), | 
| 67 |     qRgb(r: 0xFF, g: 0x66, b: 0x66), | 
| 68 |     qRgb(r: 0xFF, g: 0x66, b: 0x33), | 
| 69 |     qRgb(r: 0xFF, g: 0x66, b: 0x00), | 
| 70 |     qRgb(r: 0xFF, g: 0x33, b: 0xFF), | 
| 71 |     qRgb(r: 0xFF, g: 0x33, b: 0xCC), | 
| 72 |     qRgb(r: 0xFF, g: 0x33, b: 0x99), | 
| 73 |     qRgb(r: 0xFF, g: 0x33, b: 0x66), | 
| 74 |     qRgb(r: 0xFF, g: 0x33, b: 0x33), | 
| 75 |     qRgb(r: 0xFF, g: 0x33, b: 0x00), | 
| 76 |     qRgb(r: 0xFF, g: 0x00, b: 0xFF), | 
| 77 |     qRgb(r: 0xFF, g: 0x00, b: 0xCC), | 
| 78 |     qRgb(r: 0xFF, g: 0x00, b: 0x99), | 
| 79 |     qRgb(r: 0xFF, g: 0x00, b: 0x66), | 
| 80 |     qRgb(r: 0xFF, g: 0x00, b: 0x33), | 
| 81 |     qRgb(r: 0xFF, g: 0x00, b: 0x00), | 
| 82 |     qRgb(r: 0xCC, g: 0xFF, b: 0xFF), | 
| 83 |     qRgb(r: 0xCC, g: 0xFF, b: 0xCC), | 
| 84 |     qRgb(r: 0xCC, g: 0xFF, b: 0x99), | 
| 85 |     qRgb(r: 0xCC, g: 0xFF, b: 0x66), | 
| 86 |     qRgb(r: 0xCC, g: 0xFF, b: 0x33), | 
| 87 |     qRgb(r: 0xCC, g: 0xFF, b: 0x00), | 
| 88 |     qRgb(r: 0xCC, g: 0xCC, b: 0xFF), | 
| 89 |     qRgb(r: 0xCC, g: 0xCC, b: 0xCC), | 
| 90 |     qRgb(r: 0xCC, g: 0xCC, b: 0x99), | 
| 91 |     qRgb(r: 0xCC, g: 0xCC, b: 0x66), | 
| 92 |     qRgb(r: 0xCC, g: 0xCC, b: 0x33), | 
| 93 |     qRgb(r: 0xCC, g: 0xCC, b: 0x00), | 
| 94 |     qRgb(r: 0xCC, g: 0x99, b: 0xFF), | 
| 95 |     qRgb(r: 0xCC, g: 0x99, b: 0xCC), | 
| 96 |     qRgb(r: 0xCC, g: 0x99, b: 0x99), | 
| 97 |     qRgb(r: 0xCC, g: 0x99, b: 0x66), | 
| 98 |     qRgb(r: 0xCC, g: 0x99, b: 0x33), | 
| 99 |     qRgb(r: 0xCC, g: 0x99, b: 0x00), | 
| 100 |     qRgb(r: 0xCC, g: 0x66, b: 0xFF), | 
| 101 |     qRgb(r: 0xCC, g: 0x66, b: 0xCC), | 
| 102 |     qRgb(r: 0xCC, g: 0x66, b: 0x99), | 
| 103 |     qRgb(r: 0xCC, g: 0x66, b: 0x66), | 
| 104 |     qRgb(r: 0xCC, g: 0x66, b: 0x33), | 
| 105 |     qRgb(r: 0xCC, g: 0x66, b: 0x00), | 
| 106 |     qRgb(r: 0xCC, g: 0x33, b: 0xFF), | 
| 107 |     qRgb(r: 0xCC, g: 0x33, b: 0xCC), | 
| 108 |     qRgb(r: 0xCC, g: 0x33, b: 0x99), | 
| 109 |     qRgb(r: 0xCC, g: 0x33, b: 0x66), | 
| 110 |     qRgb(r: 0xCC, g: 0x33, b: 0x33), | 
| 111 |     qRgb(r: 0xCC, g: 0x33, b: 0x00), | 
| 112 |     qRgb(r: 0xCC, g: 0x00, b: 0xFF), | 
| 113 |     qRgb(r: 0xCC, g: 0x00, b: 0xCC), | 
| 114 |     qRgb(r: 0xCC, g: 0x00, b: 0x99), | 
| 115 |     qRgb(r: 0xCC, g: 0x00, b: 0x66), | 
| 116 |     qRgb(r: 0xCC, g: 0x00, b: 0x33), | 
| 117 |     qRgb(r: 0xCC, g: 0x00, b: 0x00), | 
| 118 |     qRgb(r: 0x99, g: 0xFF, b: 0xFF), | 
| 119 |     qRgb(r: 0x99, g: 0xFF, b: 0xCC), | 
| 120 |     qRgb(r: 0x99, g: 0xFF, b: 0x99), | 
| 121 |     qRgb(r: 0x99, g: 0xFF, b: 0x66), | 
| 122 |     qRgb(r: 0x99, g: 0xFF, b: 0x33), | 
| 123 |     qRgb(r: 0x99, g: 0xFF, b: 0x00), | 
| 124 |     qRgb(r: 0x99, g: 0xCC, b: 0xFF), | 
| 125 |     qRgb(r: 0x99, g: 0xCC, b: 0xCC), | 
| 126 |     qRgb(r: 0x99, g: 0xCC, b: 0x99), | 
| 127 |     qRgb(r: 0x99, g: 0xCC, b: 0x66), | 
| 128 |     qRgb(r: 0x99, g: 0xCC, b: 0x33), | 
| 129 |     qRgb(r: 0x99, g: 0xCC, b: 0x00), | 
| 130 |     qRgb(r: 0x99, g: 0x99, b: 0xFF), | 
| 131 |     qRgb(r: 0x99, g: 0x99, b: 0xCC), | 
| 132 |     qRgb(r: 0x99, g: 0x99, b: 0x99), | 
| 133 |     qRgb(r: 0x99, g: 0x99, b: 0x66), | 
| 134 |     qRgb(r: 0x99, g: 0x99, b: 0x33), | 
| 135 |     qRgb(r: 0x99, g: 0x99, b: 0x00), | 
| 136 |     qRgb(r: 0x99, g: 0x66, b: 0xFF), | 
| 137 |     qRgb(r: 0x99, g: 0x66, b: 0xCC), | 
| 138 |     qRgb(r: 0x99, g: 0x66, b: 0x99), | 
| 139 |     qRgb(r: 0x99, g: 0x66, b: 0x66), | 
| 140 |     qRgb(r: 0x99, g: 0x66, b: 0x33), | 
| 141 |     qRgb(r: 0x99, g: 0x66, b: 0x00), | 
| 142 |     qRgb(r: 0x99, g: 0x33, b: 0xFF), | 
| 143 |     qRgb(r: 0x99, g: 0x33, b: 0xCC), | 
| 144 |     qRgb(r: 0x99, g: 0x33, b: 0x99), | 
| 145 |     qRgb(r: 0x99, g: 0x33, b: 0x66), | 
| 146 |     qRgb(r: 0x99, g: 0x33, b: 0x33), | 
| 147 |     qRgb(r: 0x99, g: 0x33, b: 0x00), | 
| 148 |     qRgb(r: 0x99, g: 0x00, b: 0xFF), | 
| 149 |     qRgb(r: 0x99, g: 0x00, b: 0xCC), | 
| 150 |     qRgb(r: 0x99, g: 0x00, b: 0x99), | 
| 151 |     qRgb(r: 0x99, g: 0x00, b: 0x66), | 
| 152 |     qRgb(r: 0x99, g: 0x00, b: 0x33), | 
| 153 |     qRgb(r: 0x99, g: 0x00, b: 0x00), | 
| 154 |     qRgb(r: 0x66, g: 0xFF, b: 0xFF), | 
| 155 |     qRgb(r: 0x66, g: 0xFF, b: 0xCC), | 
| 156 |     qRgb(r: 0x66, g: 0xFF, b: 0x99), | 
| 157 |     qRgb(r: 0x66, g: 0xFF, b: 0x66), | 
| 158 |     qRgb(r: 0x66, g: 0xFF, b: 0x33), | 
| 159 |     qRgb(r: 0x66, g: 0xFF, b: 0x00), | 
| 160 |     qRgb(r: 0x66, g: 0xCC, b: 0xFF), | 
| 161 |     qRgb(r: 0x66, g: 0xCC, b: 0xCC), | 
| 162 |     qRgb(r: 0x66, g: 0xCC, b: 0x99), | 
| 163 |     qRgb(r: 0x66, g: 0xCC, b: 0x66), | 
| 164 |     qRgb(r: 0x66, g: 0xCC, b: 0x33), | 
| 165 |     qRgb(r: 0x66, g: 0xCC, b: 0x00), | 
| 166 |     qRgb(r: 0x66, g: 0x99, b: 0xFF), | 
| 167 |     qRgb(r: 0x66, g: 0x99, b: 0xCC), | 
| 168 |     qRgb(r: 0x66, g: 0x99, b: 0x99), | 
| 169 |     qRgb(r: 0x66, g: 0x99, b: 0x66), | 
| 170 |     qRgb(r: 0x66, g: 0x99, b: 0x33), | 
| 171 |     qRgb(r: 0x66, g: 0x99, b: 0x00), | 
| 172 |     qRgb(r: 0x66, g: 0x66, b: 0xFF), | 
| 173 |     qRgb(r: 0x66, g: 0x66, b: 0xCC), | 
| 174 |     qRgb(r: 0x66, g: 0x66, b: 0x99), | 
| 175 |     qRgb(r: 0x66, g: 0x66, b: 0x66), | 
| 176 |     qRgb(r: 0x66, g: 0x66, b: 0x33), | 
| 177 |     qRgb(r: 0x66, g: 0x66, b: 0x00), | 
| 178 |     qRgb(r: 0x66, g: 0x33, b: 0xFF), | 
| 179 |     qRgb(r: 0x66, g: 0x33, b: 0xCC), | 
| 180 |     qRgb(r: 0x66, g: 0x33, b: 0x99), | 
| 181 |     qRgb(r: 0x66, g: 0x33, b: 0x66), | 
| 182 |     qRgb(r: 0x66, g: 0x33, b: 0x33), | 
| 183 |     qRgb(r: 0x66, g: 0x33, b: 0x00), | 
| 184 |     qRgb(r: 0x66, g: 0x00, b: 0xFF), | 
| 185 |     qRgb(r: 0x66, g: 0x00, b: 0xCC), | 
| 186 |     qRgb(r: 0x66, g: 0x00, b: 0x99), | 
| 187 |     qRgb(r: 0x66, g: 0x00, b: 0x66), | 
| 188 |     qRgb(r: 0x66, g: 0x00, b: 0x33), | 
| 189 |     qRgb(r: 0x66, g: 0x00, b: 0x00), | 
| 190 |     qRgb(r: 0x33, g: 0xFF, b: 0xFF), | 
| 191 |     qRgb(r: 0x33, g: 0xFF, b: 0xCC), | 
| 192 |     qRgb(r: 0x33, g: 0xFF, b: 0x99), | 
| 193 |     qRgb(r: 0x33, g: 0xFF, b: 0x66), | 
| 194 |     qRgb(r: 0x33, g: 0xFF, b: 0x33), | 
| 195 |     qRgb(r: 0x33, g: 0xFF, b: 0x00), | 
| 196 |     qRgb(r: 0x33, g: 0xCC, b: 0xFF), | 
| 197 |     qRgb(r: 0x33, g: 0xCC, b: 0xCC), | 
| 198 |     qRgb(r: 0x33, g: 0xCC, b: 0x99), | 
| 199 |     qRgb(r: 0x33, g: 0xCC, b: 0x66), | 
| 200 |     qRgb(r: 0x33, g: 0xCC, b: 0x33), | 
| 201 |     qRgb(r: 0x33, g: 0xCC, b: 0x00), | 
| 202 |     qRgb(r: 0x33, g: 0x99, b: 0xFF), | 
| 203 |     qRgb(r: 0x33, g: 0x99, b: 0xCC), | 
| 204 |     qRgb(r: 0x33, g: 0x99, b: 0x99), | 
| 205 |     qRgb(r: 0x33, g: 0x99, b: 0x66), | 
| 206 |     qRgb(r: 0x33, g: 0x99, b: 0x33), | 
| 207 |     qRgb(r: 0x33, g: 0x99, b: 0x00), | 
| 208 |     qRgb(r: 0x33, g: 0x66, b: 0xFF), | 
| 209 |     qRgb(r: 0x33, g: 0x66, b: 0xCC), | 
| 210 |     qRgb(r: 0x33, g: 0x66, b: 0x99), | 
| 211 |     qRgb(r: 0x33, g: 0x66, b: 0x66), | 
| 212 |     qRgb(r: 0x33, g: 0x66, b: 0x33), | 
| 213 |     qRgb(r: 0x33, g: 0x66, b: 0x00), | 
| 214 |     qRgb(r: 0x33, g: 0x33, b: 0xFF), | 
| 215 |     qRgb(r: 0x33, g: 0x33, b: 0xCC), | 
| 216 |     qRgb(r: 0x33, g: 0x33, b: 0x99), | 
| 217 |     qRgb(r: 0x33, g: 0x33, b: 0x66), | 
| 218 |     qRgb(r: 0x33, g: 0x33, b: 0x33), | 
| 219 |     qRgb(r: 0x33, g: 0x33, b: 0x00), | 
| 220 |     qRgb(r: 0x33, g: 0x00, b: 0xFF), | 
| 221 |     qRgb(r: 0x33, g: 0x00, b: 0xCC), | 
| 222 |     qRgb(r: 0x33, g: 0x00, b: 0x99), | 
| 223 |     qRgb(r: 0x33, g: 0x00, b: 0x66), | 
| 224 |     qRgb(r: 0x33, g: 0x00, b: 0x33), | 
| 225 |     qRgb(r: 0x33, g: 0x00, b: 0x00), | 
| 226 |     qRgb(r: 0x00, g: 0xFF, b: 0xFF), | 
| 227 |     qRgb(r: 0x00, g: 0xFF, b: 0xCC), | 
| 228 |     qRgb(r: 0x00, g: 0xFF, b: 0x99), | 
| 229 |     qRgb(r: 0x00, g: 0xFF, b: 0x66), | 
| 230 |     qRgb(r: 0x00, g: 0xFF, b: 0x33), | 
| 231 |     qRgb(r: 0x00, g: 0xFF, b: 0x00), | 
| 232 |     qRgb(r: 0x00, g: 0xCC, b: 0xFF), | 
| 233 |     qRgb(r: 0x00, g: 0xCC, b: 0xCC), | 
| 234 |     qRgb(r: 0x00, g: 0xCC, b: 0x99), | 
| 235 |     qRgb(r: 0x00, g: 0xCC, b: 0x66), | 
| 236 |     qRgb(r: 0x00, g: 0xCC, b: 0x33), | 
| 237 |     qRgb(r: 0x00, g: 0xCC, b: 0x00), | 
| 238 |     qRgb(r: 0x00, g: 0x99, b: 0xFF), | 
| 239 |     qRgb(r: 0x00, g: 0x99, b: 0xCC), | 
| 240 |     qRgb(r: 0x00, g: 0x99, b: 0x99), | 
| 241 |     qRgb(r: 0x00, g: 0x99, b: 0x66), | 
| 242 |     qRgb(r: 0x00, g: 0x99, b: 0x33), | 
| 243 |     qRgb(r: 0x00, g: 0x99, b: 0x00), | 
| 244 |     qRgb(r: 0x00, g: 0x66, b: 0xFF), | 
| 245 |     qRgb(r: 0x00, g: 0x66, b: 0xCC), | 
| 246 |     qRgb(r: 0x00, g: 0x66, b: 0x99), | 
| 247 |     qRgb(r: 0x00, g: 0x66, b: 0x66), | 
| 248 |     qRgb(r: 0x00, g: 0x66, b: 0x33), | 
| 249 |     qRgb(r: 0x00, g: 0x66, b: 0x00), | 
| 250 |     qRgb(r: 0x00, g: 0x33, b: 0xFF), | 
| 251 |     qRgb(r: 0x00, g: 0x33, b: 0xCC), | 
| 252 |     qRgb(r: 0x00, g: 0x33, b: 0x99), | 
| 253 |     qRgb(r: 0x00, g: 0x33, b: 0x66), | 
| 254 |     qRgb(r: 0x00, g: 0x33, b: 0x33), | 
| 255 |     qRgb(r: 0x00, g: 0x33, b: 0x00), | 
| 256 |     qRgb(r: 0x00, g: 0x00, b: 0xFF), | 
| 257 |     qRgb(r: 0x00, g: 0x00, b: 0xCC), | 
| 258 |     qRgb(r: 0x00, g: 0x00, b: 0x99), | 
| 259 |     qRgb(r: 0x00, g: 0x00, b: 0x66), | 
| 260 |     qRgb(r: 0x00, g: 0x00, b: 0x33), | 
| 261 |     qRgb(r: 0xEE, g: 0x00, b: 0x00), | 
| 262 |     qRgb(r: 0xDD, g: 0x00, b: 0x00), | 
| 263 |     qRgb(r: 0xBB, g: 0x00, b: 0x00), | 
| 264 |     qRgb(r: 0xAA, g: 0x00, b: 0x00), | 
| 265 |     qRgb(r: 0x88, g: 0x00, b: 0x00), | 
| 266 |     qRgb(r: 0x77, g: 0x00, b: 0x00), | 
| 267 |     qRgb(r: 0x55, g: 0x00, b: 0x00), | 
| 268 |     qRgb(r: 0x44, g: 0x00, b: 0x00), | 
| 269 |     qRgb(r: 0x22, g: 0x00, b: 0x00), | 
| 270 |     qRgb(r: 0x11, g: 0x00, b: 0x00), | 
| 271 |     qRgb(r: 0x00, g: 0xEE, b: 0x00), | 
| 272 |     qRgb(r: 0x00, g: 0xDD, b: 0x00), | 
| 273 |     qRgb(r: 0x00, g: 0xBB, b: 0x00), | 
| 274 |     qRgb(r: 0x00, g: 0xAA, b: 0x00), | 
| 275 |     qRgb(r: 0x00, g: 0x88, b: 0x00), | 
| 276 |     qRgb(r: 0x00, g: 0x77, b: 0x00), | 
| 277 |     qRgb(r: 0x00, g: 0x55, b: 0x00), | 
| 278 |     qRgb(r: 0x00, g: 0x44, b: 0x00), | 
| 279 |     qRgb(r: 0x00, g: 0x22, b: 0x00), | 
| 280 |     qRgb(r: 0x00, g: 0x11, b: 0x00), | 
| 281 |     qRgb(r: 0x00, g: 0x00, b: 0xEE), | 
| 282 |     qRgb(r: 0x00, g: 0x00, b: 0xDD), | 
| 283 |     qRgb(r: 0x00, g: 0x00, b: 0xBB), | 
| 284 |     qRgb(r: 0x00, g: 0x00, b: 0xAA), | 
| 285 |     qRgb(r: 0x00, g: 0x00, b: 0x88), | 
| 286 |     qRgb(r: 0x00, g: 0x00, b: 0x77), | 
| 287 |     qRgb(r: 0x00, g: 0x00, b: 0x55), | 
| 288 |     qRgb(r: 0x00, g: 0x00, b: 0x44), | 
| 289 |     qRgb(r: 0x00, g: 0x00, b: 0x22), | 
| 290 |     qRgb(r: 0x00, g: 0x00, b: 0x11), | 
| 291 |     qRgb(r: 0xEE, g: 0xEE, b: 0xEE), | 
| 292 |     qRgb(r: 0xDD, g: 0xDD, b: 0xDD), | 
| 293 |     qRgb(r: 0xBB, g: 0xBB, b: 0xBB), | 
| 294 |     qRgb(r: 0xAA, g: 0xAA, b: 0xAA), | 
| 295 |     qRgb(r: 0x88, g: 0x88, b: 0x88), | 
| 296 |     qRgb(r: 0x77, g: 0x77, b: 0x77), | 
| 297 |     qRgb(r: 0x55, g: 0x55, b: 0x55), | 
| 298 |     qRgb(r: 0x44, g: 0x44, b: 0x44), | 
| 299 |     qRgb(r: 0x22, g: 0x22, b: 0x22), | 
| 300 |     qRgb(r: 0x11, g: 0x11, b: 0x11), | 
| 301 |     qRgb(r: 0x00, g: 0x00, b: 0x00) | 
| 302 | }; | 
| 303 | Q_STATIC_ASSERT(sizeof(ICNSColorTable8bit) / sizeof(ICNSColorTable8bit[0]) == (1 << ICNSEntry::Depth8bit)); | 
| 304 |  | 
| 305 | static inline QDataStream &(QDataStream &in, ICNSBlockHeader &p) | 
| 306 | { | 
| 307 |     in >> p.ostype; | 
| 308 |     in >> p.length; | 
| 309 |     return in; | 
| 310 | } | 
| 311 |  | 
| 312 | static inline QDataStream &(QDataStream &out, const ICNSBlockHeader &p) | 
| 313 | { | 
| 314 |     out << p.ostype; | 
| 315 |     out << p.length; | 
| 316 |     return out; | 
| 317 | } | 
| 318 |  | 
| 319 | static inline bool isPowOf2OrDividesBy16(quint32 u, qreal r) | 
| 320 | { | 
| 321 |     return u == r && ((u % 16 == 0) || (r >= 16 && (u & (u - 1)) == 0)); | 
| 322 | } | 
| 323 |  | 
| 324 | static inline bool (const ICNSBlockHeader &, quint64 bound = 0) | 
| 325 | { | 
| 326 |     return header.ostype != 0 && (bound == 0 | 
| 327 |                 || qBound(min: quint64(ICNSBlockHeaderSize), val: quint64(header.length), max: bound) == header.length); | 
| 328 | } | 
| 329 |  | 
| 330 | static inline bool isIconCompressed(const ICNSEntry &icon) | 
| 331 | { | 
| 332 |     return icon.dataFormat == ICNSEntry::PNG || icon.dataFormat == ICNSEntry::JP2; | 
| 333 | } | 
| 334 |  | 
| 335 | static inline bool isMaskSuitable(const ICNSEntry &mask, const ICNSEntry &icon, ICNSEntry::Depth target) | 
| 336 | { | 
| 337 |     return mask.variant == icon.variant && mask.depth == target | 
| 338 |             && mask.height == icon.height && mask.width == icon.width; | 
| 339 | } | 
| 340 |  | 
| 341 | static inline QByteArray nameFromOSType(quint32 ostype) | 
| 342 | { | 
| 343 |     const quint32 bytes = qToBigEndian(source: ostype); | 
| 344 |     return QByteArray((const char*)&bytes, 4); | 
| 345 | } | 
| 346 |  | 
| 347 | static inline quint32 nameToOSType(const QByteArray &ostype) | 
| 348 | { | 
| 349 |     if (ostype.size() != 4) | 
| 350 |         return 0; | 
| 351 |     return qFromBigEndian(source: *reinterpret_cast<const quint32*>(ostype.constData())); | 
| 352 | } | 
| 353 |  | 
| 354 | static inline QByteArray nameForCompressedIcon(quint8 iconNumber) | 
| 355 | { | 
| 356 |     const bool portable = iconNumber < 7; | 
| 357 |     const QByteArray base =  portable ? QByteArrayLiteral("icp" ) : QByteArrayLiteral("ic" ); | 
| 358 |     if (!portable && iconNumber < 10) | 
| 359 |         return base + "0"  + QByteArray::number(iconNumber); | 
| 360 |     return base + QByteArray::number(iconNumber); | 
| 361 | } | 
| 362 |  | 
| 363 | static inline QList<QRgb> getColorTable(ICNSEntry::Depth depth) | 
| 364 | { | 
| 365 |     QList<QRgb> table; | 
| 366 |     uint n = 1 << depth; | 
| 367 |     const QRgb *data; | 
| 368 |     switch (depth) { | 
| 369 |     case ICNSEntry::DepthMono: | 
| 370 |         data = ICNSColorTableMono; | 
| 371 |         break; | 
| 372 |     case ICNSEntry::Depth4bit: | 
| 373 |         data = ICNSColorTable4bit; | 
| 374 |         break; | 
| 375 |     case ICNSEntry::Depth8bit: | 
| 376 |         data = ICNSColorTable8bit; | 
| 377 |         break; | 
| 378 |     default: | 
| 379 |         Q_UNREACHABLE(); | 
| 380 |         break; | 
| 381 |     } | 
| 382 |     table.resize(size: n); | 
| 383 |     memcpy(dest: table.data(), src: data, n: sizeof(QRgb) * n); | 
| 384 |     return table; | 
| 385 | } | 
| 386 |  | 
| 387 | static bool parseIconEntryData(ICNSEntry &icon, QIODevice *device) | 
| 388 | { | 
| 389 |     const qint64 oldPos = device->pos(); | 
| 390 |     if (oldPos != icon.dataOffset && !device->seek(pos: icon.dataOffset)) | 
| 391 |         return false; | 
| 392 |  | 
| 393 |     const QByteArray magic = device->peek(maxlen: 12); | 
| 394 |     const bool isPNG = magic.startsWith(QByteArrayLiteral("\211PNG\r\n\032\n\000\000\000\r" )); | 
| 395 |     const bool isJP2 = !isPNG && magic == QByteArrayLiteral("\000\000\000\014jP  \r\n\207\n" ); | 
| 396 |     if (isPNG || isJP2) { | 
| 397 |         // TODO: Add parsing of png/jp2 headers to enable feature reporting by plugin? | 
| 398 |         icon.flags = ICNSEntry::IsIcon; | 
| 399 |         icon.dataFormat = isPNG? ICNSEntry::PNG : ICNSEntry::JP2; | 
| 400 |     } | 
| 401 |     if (oldPos != icon.dataOffset && !device->seek(pos: oldPos)) | 
| 402 |         return false; | 
| 403 |     return true; | 
| 404 | } | 
| 405 |  | 
| 406 | static bool parseIconEntryInfo(ICNSEntry &icon) | 
| 407 | { | 
| 408 |     const QString ostype = QString::fromLatin1(ba: nameFromOSType(ostype: icon.ostype)); | 
| 409 |     // Typical OSType naming: <junk><group><depth><mask>; | 
| 410 |     // For icons OSType should be strictly alphanumeric + '#' character for masks/mono. | 
| 411 |     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})$" ); | 
| 412 |     QRegularExpression regexp(ptrn); | 
| 413 |     QRegularExpressionMatch match = regexp.match(subject: ostype); | 
| 414 |     if (!match.hasMatch()) { | 
| 415 |         qWarning(msg: "parseIconEntryInfo(): Failed, OSType doesn't match: \"%s\"" , qPrintable(ostype)); | 
| 416 |         return false; | 
| 417 |     } | 
| 418 |     const QString group = match.captured(QStringLiteral("group" )); | 
| 419 |     const QString depth = match.captured(QStringLiteral("depth" )); | 
| 420 |     const QString mask = match.captured(QStringLiteral("mask" )); | 
| 421 |     // Icon group: | 
| 422 |     if (!group.isEmpty()) | 
| 423 |         icon.group = ICNSEntry::Group(group.at(i: 0).toLatin1()); | 
| 424 |  | 
| 425 |     // That's enough for compressed ones | 
| 426 |     if (isIconCompressed(icon)) | 
| 427 |         return true; | 
| 428 |     // Icon depth: | 
| 429 |     if (!depth.isEmpty()) { | 
| 430 |         const uint depthUInt = depth.toUInt(); | 
| 431 |         if (depthUInt > 32) | 
| 432 |             return false; | 
| 433 |         icon.depth = ICNSEntry::Depth(depthUInt); | 
| 434 |     } | 
| 435 |     // Try mono if depth not found | 
| 436 |     if (icon.depth == ICNSEntry::DepthUnknown) | 
| 437 |         icon.depth = ICNSEntry::DepthMono; | 
| 438 |     // Detect size: | 
| 439 |     const qreal bytespp = (qreal)icon.depth / 8; | 
| 440 |     const qreal r1 = qSqrt(v: icon.dataLength / bytespp); | 
| 441 |     const qreal r2 = qSqrt(v: (icon.dataLength / bytespp) / 2); | 
| 442 |     const quint32 r1u = qRound(d: r1); | 
| 443 |     const quint32 r2u = qRound(d: r2); | 
| 444 |     const bool singleEntry = isPowOf2OrDividesBy16(u: r1u, r: r1); | 
| 445 |     const bool doubleSize = isPowOf2OrDividesBy16(u: r2u, r: r2); | 
| 446 |     if (singleEntry) { | 
| 447 |         icon.flags = mask.isEmpty() ? ICNSEntry::IsIcon : ICNSEntry::IsMask; | 
| 448 |         icon.dataFormat = ICNSEntry::RawIcon; | 
| 449 |         icon.width = r1u; | 
| 450 |         icon.height = r1u; | 
| 451 |     } else if (doubleSize) { | 
| 452 |         icon.flags = ICNSEntry::IconPlusMask; | 
| 453 |         icon.dataFormat = ICNSEntry::RawIcon; | 
| 454 |         icon.width = r2u; | 
| 455 |         icon.height = r2u; | 
| 456 |     } else if (icon.group == ICNSEntry::GroupMini) { | 
| 457 |         // Legacy 16x12 icons are an exception from the generic square formula | 
| 458 |         const bool doubleSize = icon.dataLength == 192 * bytespp * 2; | 
| 459 |         icon.flags = doubleSize ? ICNSEntry::IconPlusMask : ICNSEntry::IsIcon; | 
| 460 |         icon.dataFormat = ICNSEntry::RawIcon; | 
| 461 |         icon.width = 16; | 
| 462 |         icon.height = 12; | 
| 463 |     } else if (icon.depth == ICNSEntry::Depth32bit) { | 
| 464 |         // We have a formula mismatch in a 32bit icon there, probably RLE24 | 
| 465 |         icon.dataFormat = ICNSEntry::RLE24; | 
| 466 |         icon.flags = mask.isEmpty() ? ICNSEntry::IsIcon : ICNSEntry::IsMask; | 
| 467 |         switch (icon.group) { | 
| 468 |         case ICNSEntry::GroupSmall: | 
| 469 |             icon.width = 16; | 
| 470 |             break; | 
| 471 |         case ICNSEntry::GroupLarge: | 
| 472 |             icon.width = 32; | 
| 473 |             break; | 
| 474 |         case ICNSEntry::GroupHuge: | 
| 475 |             icon.width = 48; | 
| 476 |             break; | 
| 477 |         case ICNSEntry::GroupThumbnail: | 
| 478 |             icon.width = 128; | 
| 479 |             break; | 
| 480 |         default: | 
| 481 |             qWarning(msg: "parseIconEntryInfo(): Failed, 32bit icon from an unknown group. OSType: \"%s\"" , | 
| 482 |                      qPrintable(ostype)); | 
| 483 |         } | 
| 484 |         icon.height = icon.width; | 
| 485 |     } | 
| 486 |     // Sanity check | 
| 487 |     if (icon.width == 0 || icon.width > 4096) | 
| 488 |         return false; | 
| 489 |     return true; | 
| 490 | } | 
| 491 |  | 
| 492 | static QImage readMask(const ICNSEntry &mask, QDataStream &stream) | 
| 493 | { | 
| 494 |     if ((mask.flags & ICNSEntry::IsMask) == 0) | 
| 495 |         return QImage(); | 
| 496 |     if (mask.depth != ICNSEntry::DepthMono && mask.depth != ICNSEntry::Depth8bit) { | 
| 497 |         qWarning(msg: "readMask(): Failed, unusual bit depth: %u OSType: \"%s\"" , | 
| 498 |                  mask.depth, nameFromOSType(ostype: mask.ostype).constData()); | 
| 499 |         return QImage(); | 
| 500 |     } | 
| 501 |     const bool isMono = mask.depth == ICNSEntry::DepthMono; | 
| 502 |     const bool doubleSize = mask.flags == ICNSEntry::IconPlusMask; | 
| 503 |     const quint32 imageDataSize = (mask.width * mask.height * mask.depth) / 8; | 
| 504 |     const qint64 pos = doubleSize ? (mask.dataOffset + imageDataSize) : mask.dataOffset; | 
| 505 |     const qint64 oldPos = stream.device()->pos(); | 
| 506 |     if (!stream.device()->seek(pos)) | 
| 507 |         return QImage(); | 
| 508 |     QImage img; | 
| 509 |     if (!QImageIOHandler::allocateImage(size: QSize(mask.width, mask.height), format: QImage::Format_RGB32, image: &img)) | 
| 510 |         return QImage(); | 
| 511 |     quint8 byte = 0; | 
| 512 |     quint32 pixel = 0; | 
| 513 |     for (quint32 y = 0; y < mask.height; y++) { | 
| 514 |         QRgb *line = reinterpret_cast<QRgb *>(img.scanLine(y)); | 
| 515 |         for (quint32 x = 0; x < mask.width; x++) { | 
| 516 |             if (pixel % (8 / mask.depth) == 0) | 
| 517 |                 stream >> byte; | 
| 518 |             else if (isMono) | 
| 519 |                 byte <<= 1; | 
| 520 |             const quint8 alpha = isMono ? (((byte >> 7) & 0x01) * 255) : byte; | 
| 521 |             line[x] = qRgb(r: alpha, g: alpha, b: alpha); | 
| 522 |             pixel++; | 
| 523 |         } | 
| 524 |     } | 
| 525 |     stream.device()->seek(pos: oldPos); | 
| 526 |     return img; | 
| 527 | } | 
| 528 |  | 
| 529 | template <ICNSEntry::Depth depth> | 
| 530 | static QImage readLowDepthIcon(const ICNSEntry &icon, QDataStream &stream) | 
| 531 | { | 
| 532 |     Q_STATIC_ASSERT(depth == ICNSEntry::DepthMono || depth == ICNSEntry::Depth4bit | 
| 533 |                     || depth == ICNSEntry::Depth8bit); | 
| 534 |  | 
| 535 |     const bool isMono = depth == ICNSEntry::DepthMono; | 
| 536 |     const QImage::Format format = isMono ? QImage::Format_Mono : QImage::Format_Indexed8; | 
| 537 |     const QList<QRgb> colortable = getColorTable(depth); | 
| 538 |     if (colortable.isEmpty()) | 
| 539 |         return QImage(); | 
| 540 |     QImage img; | 
| 541 |     if (!QImageIOHandler::allocateImage(size: QSize(icon.width, icon.height), format, image: &img)) | 
| 542 |         return QImage(); | 
| 543 |     img.setColorTable(colortable); | 
| 544 |     quint32 pixel = 0; | 
| 545 |     quint8 byte = 0; | 
| 546 |     for (quint32 y = 0; y < icon.height; y++) { | 
| 547 |         for (quint32 x = 0; x < icon.width; x++) { | 
| 548 |             if (pixel % (8 / depth) == 0) | 
| 549 |                 stream >> byte; | 
| 550 |             quint8 cindex; | 
| 551 |             switch (depth) { | 
| 552 |             case ICNSEntry::DepthMono: | 
| 553 |                 cindex = (byte >> 7) & 0x01; // left 1 bit | 
| 554 |                 byte <<= 1; | 
| 555 |                 break; | 
| 556 |             case ICNSEntry::Depth4bit: | 
| 557 |                 cindex = (byte >> 4) & 0x0F; // left 4 bits | 
| 558 |                 byte <<= 4; | 
| 559 |                 break; | 
| 560 |             default: | 
| 561 |                 cindex = byte; // 8 bits | 
| 562 |                 break; | 
| 563 |             } | 
| 564 |             img.setPixel(x, y, index_or_rgb: cindex); | 
| 565 |             pixel++; | 
| 566 |         } | 
| 567 |     } | 
| 568 |     return img; | 
| 569 | } | 
| 570 |  | 
| 571 | static QImage read32bitIcon(const ICNSEntry &icon, QDataStream &stream) | 
| 572 | { | 
| 573 |     QImage img; | 
| 574 |     if (!QImageIOHandler::allocateImage(size: QSize(icon.width, icon.height), format: QImage::Format_RGB32, image: &img)) | 
| 575 |         return QImage(); | 
| 576 |     if (icon.dataFormat != ICNSEntry::RLE24) { | 
| 577 |         for (quint32 y = 0; y < icon.height; y++) { | 
| 578 |             QRgb *line = reinterpret_cast<QRgb *>(img.scanLine(y)); | 
| 579 |             for (quint32 x = 0; x < icon.width; x++) { | 
| 580 |                 quint8 r, g, b, a; | 
| 581 |                 stream >> r >> g >> b >> a; | 
| 582 |                 line[x] = qRgb(r, g, b); | 
| 583 |             } | 
| 584 |         } | 
| 585 |     } else { | 
| 586 |         const quint32 estPxsNum = icon.width * icon.height; | 
| 587 |         const QByteArray &bytes = stream.device()->peek(maxlen: 4); | 
| 588 |         if (bytes.isEmpty()) | 
| 589 |             return QImage(); | 
| 590 |         // Zero-padding may be present: | 
| 591 |         if (qFromBigEndian<quint32>(source: *bytes.constData()) == 0) | 
| 592 |             stream.skipRawData(len: 4); | 
| 593 |         for (quint8 colorNRun = 0; colorNRun < 3; colorNRun++) { | 
| 594 |             quint32 pixel = 0; | 
| 595 |             QRgb *line = 0; | 
| 596 |             while (pixel < estPxsNum && !stream.atEnd()) { | 
| 597 |                 quint8 byte, value; | 
| 598 |                 stream >> byte; | 
| 599 |                 const bool bitIsClear = (byte & 0x80) == 0; | 
| 600 |                 // If high bit is clear: run of different values; else: same value | 
| 601 |                 quint8 runLength = bitIsClear ? ((0xFF & byte) + 1) : ((0xFF & byte) - 125); | 
| 602 |                 // Length of the run for for different values: 1 <= len <= 128 | 
| 603 |                 // Length of the run for same values: 3 <= len <= 130 | 
| 604 |                 if (!bitIsClear) | 
| 605 |                     stream >> value; | 
| 606 |                 for (quint8 i = 0; i < runLength && pixel < estPxsNum; i++) { | 
| 607 |                     if (bitIsClear) | 
| 608 |                         stream >> value; | 
| 609 |                     const quint32 y = pixel / icon.height; | 
| 610 |                     const quint32 x = pixel - (icon.width * y); | 
| 611 |                     if (pixel % icon.height == 0) | 
| 612 |                         line = reinterpret_cast<QRgb *>(img.scanLine(y)); | 
| 613 |                     QRgb rgb = line[x]; | 
| 614 |                     const int r = (colorNRun == 0) ? value : qRed(rgb); | 
| 615 |                     const int g = (colorNRun == 1) ? value : qGreen(rgb); | 
| 616 |                     const int b = (colorNRun == 2) ? value : qBlue(rgb); | 
| 617 |                     line[x] = qRgb(r, g, b); | 
| 618 |                     pixel++; | 
| 619 |                 } | 
| 620 |             } | 
| 621 |         } | 
| 622 |     } | 
| 623 |     return img; | 
| 624 | } | 
| 625 |  | 
| 626 | QICNSHandler::QICNSHandler() : | 
| 627 |     m_currentIconIndex(0), m_state(ScanNotScanned) | 
| 628 | { | 
| 629 | } | 
| 630 |  | 
| 631 | bool QICNSHandler::canRead(QIODevice *device) | 
| 632 | { | 
| 633 |     if (!device || !device->isReadable()) { | 
| 634 |         qWarning(msg: "QICNSHandler::canRead() called without a readable device" ); | 
| 635 |         return false; | 
| 636 |     } | 
| 637 |  | 
| 638 |     if (device->peek(maxlen: 4) == QByteArrayLiteral("icns" )) { | 
| 639 |         if (device->isSequential()) { | 
| 640 |             qWarning(msg: "QICNSHandler::canRead() called on a sequential device" ); | 
| 641 |             return false; | 
| 642 |         } | 
| 643 |         return true; | 
| 644 |     } | 
| 645 |  | 
| 646 |     return false; | 
| 647 | } | 
| 648 |  | 
| 649 | bool QICNSHandler::canRead() const | 
| 650 | { | 
| 651 |     if (m_state == ScanNotScanned && !canRead(device: device())) | 
| 652 |         return false; | 
| 653 |  | 
| 654 |     if (m_state != ScanError) { | 
| 655 |         setFormat(QByteArrayLiteral("icns" )); | 
| 656 |         return true; | 
| 657 |     } | 
| 658 |  | 
| 659 |     return false; | 
| 660 | } | 
| 661 |  | 
| 662 | bool QICNSHandler::read(QImage *outImage) | 
| 663 | { | 
| 664 |     QImage img; | 
| 665 |     if (!ensureScanned() || m_currentIconIndex >= m_icons.size()) { | 
| 666 |         qWarning(msg: "QICNSHandler::read(): The device wasn't parsed properly!" ); | 
| 667 |         return false; | 
| 668 |     } | 
| 669 |  | 
| 670 |     const ICNSEntry &icon = m_icons.at(i: m_currentIconIndex); | 
| 671 |     QDataStream stream(device()); | 
| 672 |     stream.setByteOrder(QDataStream::BigEndian); | 
| 673 |     if (!device()->seek(pos: icon.dataOffset)) | 
| 674 |         return false; | 
| 675 |  | 
| 676 |     switch (icon.dataFormat) { | 
| 677 |     case ICNSEntry::RawIcon: | 
| 678 |     case ICNSEntry::RLE24: | 
| 679 |         if (qMin(a: icon.width, b: icon.height) == 0) | 
| 680 |             break; | 
| 681 |         switch (icon.depth) { | 
| 682 |         case ICNSEntry::DepthMono: | 
| 683 |             img = readLowDepthIcon<ICNSEntry::DepthMono>(icon, stream); | 
| 684 |             break; | 
| 685 |         case ICNSEntry::Depth4bit: | 
| 686 |             img = readLowDepthIcon<ICNSEntry::Depth4bit>(icon, stream); | 
| 687 |             break; | 
| 688 |         case ICNSEntry::Depth8bit: | 
| 689 |             img = readLowDepthIcon<ICNSEntry::Depth8bit>(icon, stream); | 
| 690 |             break; | 
| 691 |         case ICNSEntry::Depth32bit: | 
| 692 |             img = read32bitIcon(icon, stream); | 
| 693 |             break; | 
| 694 |         default: | 
| 695 |             qWarning(msg: "QICNSHandler::read(): Failed, unsupported icon bit depth: %u, OSType: \"%s\"" , | 
| 696 |                      icon.depth, nameFromOSType(ostype: icon.ostype).constData()); | 
| 697 |         } | 
| 698 |         if (!img.isNull()) { | 
| 699 |             QImage alpha = readMask(mask: getIconMask(icon), stream); | 
| 700 |             if (!alpha.isNull()) | 
| 701 |                 img.setAlphaChannel(alpha); | 
| 702 |         } | 
| 703 |         break; | 
| 704 |     default: | 
| 705 |         const char *format = 0; | 
| 706 |         if (icon.dataFormat == ICNSEntry::PNG) | 
| 707 |             format = "png" ; | 
| 708 |         else if (icon.dataFormat == ICNSEntry::JP2) | 
| 709 |             format = "jp2" ; | 
| 710 |         // Even if JP2 or PNG magic is not detected, try anyway for unknown formats | 
| 711 |         img = QImage::fromData(data: device()->read(maxlen: icon.dataLength), format); | 
| 712 |         if (img.isNull()) { | 
| 713 |             if (format == 0) | 
| 714 |                 format = "unknown" ; | 
| 715 |             qWarning(msg: "QICNSHandler::read(): Failed, compressed format \"%s\" is not supported "  \ | 
| 716 |                      "by your Qt library or this file is corrupt. OSType: \"%s\"" , | 
| 717 |                      format, nameFromOSType(ostype: icon.ostype).constData()); | 
| 718 |         } | 
| 719 |     } | 
| 720 |     *outImage = img; | 
| 721 |     return !img.isNull(); | 
| 722 | } | 
| 723 |  | 
| 724 | bool QICNSHandler::write(const QImage &image) | 
| 725 | { | 
| 726 |     /* | 
| 727 |     Notes: | 
| 728 |     * Experimental implementation. Just for simple converting tasks / testing purposes. | 
| 729 |     * Min. size is 16x16, Max. size is 1024x1024. | 
| 730 |     * Performs downscale to a square image if width != height. | 
| 731 |     * Performs upscale to 16x16, if the image is smaller. | 
| 732 |     * Performs downscale to a nearest power of two if size is not a power of two. | 
| 733 |     * Currently uses non-hardcoded OSTypes. | 
| 734 |     */ | 
| 735 |     QIODevice *device = this->device(); | 
| 736 |     if (!device->isWritable() || image.isNull() || qMin(a: image.width(), b: image.height()) == 0) | 
| 737 |         return false; | 
| 738 |     const int minSize = qMin(a: image.width(), b: image.height()); | 
| 739 |     const int oldSize = (minSize < 16) ? 16 : minSize; | 
| 740 |     // Calc power of two: | 
| 741 |     int size = oldSize; | 
| 742 |     uint pow = 0; | 
| 743 |     // Note: Values over 10 are reserved for retina icons. | 
| 744 |     while (pow < 10 && (size >>= 1)) | 
| 745 |         pow++; | 
| 746 |     const int newSize = 1 << pow; | 
| 747 |     QImage img = image; | 
| 748 |     // Let's enforce resizing if size differs: | 
| 749 |     if (newSize != oldSize || qMax(a: image.width(), b: image.height()) != minSize) | 
| 750 |         img = img.scaled(w: newSize, h: newSize, aspectMode: Qt::IgnoreAspectRatio, mode: Qt::SmoothTransformation); | 
| 751 |     // Construct OSType and headers: | 
| 752 |     const quint32 ostype = nameToOSType(ostype: nameForCompressedIcon(iconNumber: pow)); | 
| 753 |     ICNSBlockHeader ; | 
| 754 |     fileHeader.ostype = ICNSBlockHeader::TypeIcns; | 
| 755 |     ICNSBlockHeader ; | 
| 756 |     tocHeader.ostype = ICNSBlockHeader::TypeToc; | 
| 757 |     ICNSBlockHeader iconEntry; | 
| 758 |     iconEntry.ostype = ostype; | 
| 759 |     QByteArray imageData; | 
| 760 |     QBuffer buffer(&imageData); | 
| 761 |     if (!buffer.open(openMode: QIODevice::WriteOnly) || !img.save(device: &buffer, format: "png" )) | 
| 762 |         return false; | 
| 763 |     buffer.close(); | 
| 764 |     iconEntry.length = ICNSBlockHeaderSize + imageData.size(); | 
| 765 |     tocHeader.length = ICNSBlockHeaderSize * 2; | 
| 766 |     fileHeader.length = ICNSBlockHeaderSize + tocHeader.length + iconEntry.length; | 
| 767 |     if (!isBlockHeaderValid(header: iconEntry)) | 
| 768 |         return false; | 
| 769 |  | 
| 770 |     QDataStream stream(device); | 
| 771 |     // iconEntry is also a TOC entry | 
| 772 |     stream << fileHeader << tocHeader << iconEntry << iconEntry; | 
| 773 |     stream.writeRawData(imageData.constData(), len: imageData.size()); | 
| 774 |     return stream.status() == QDataStream::Ok; | 
| 775 | } | 
| 776 |  | 
| 777 | bool QICNSHandler::supportsOption(ImageOption option) const | 
| 778 | { | 
| 779 |     return option == SubType; | 
| 780 | } | 
| 781 |  | 
| 782 | QVariant QICNSHandler::option(ImageOption option) const | 
| 783 | { | 
| 784 |     if (!supportsOption(option) || !ensureScanned()) | 
| 785 |         return QVariant(); | 
| 786 |  | 
| 787 |     if (option == SubType) { | 
| 788 |         if (imageCount() > 0 && m_currentIconIndex <= imageCount()) { | 
| 789 |             const ICNSEntry &icon = m_icons.at(i: m_currentIconIndex); | 
| 790 |             if (icon.variant != 0) | 
| 791 |                 return QByteArray(nameFromOSType(ostype: icon.variant) + '-' + nameFromOSType(ostype: icon.ostype)); | 
| 792 |             return nameFromOSType(ostype: icon.ostype); | 
| 793 |         } | 
| 794 |     } | 
| 795 |  | 
| 796 |     return QVariant(); | 
| 797 | } | 
| 798 |  | 
| 799 | int QICNSHandler::imageCount() const | 
| 800 | { | 
| 801 |     if (!ensureScanned()) | 
| 802 |         return 0; | 
| 803 |  | 
| 804 |     return m_icons.size(); | 
| 805 | } | 
| 806 |  | 
| 807 | bool QICNSHandler::jumpToImage(int imageNumber) | 
| 808 | { | 
| 809 |     if (imageNumber >= imageCount()) | 
| 810 |         return false; | 
| 811 |  | 
| 812 |     m_currentIconIndex = imageNumber; | 
| 813 |     return true; | 
| 814 | } | 
| 815 |  | 
| 816 | bool QICNSHandler::jumpToNextImage() | 
| 817 | { | 
| 818 |     return jumpToImage(imageNumber: m_currentIconIndex + 1); | 
| 819 | } | 
| 820 |  | 
| 821 | bool QICNSHandler::ensureScanned() const | 
| 822 | { | 
| 823 |     if (m_state == ScanNotScanned) { | 
| 824 |         QICNSHandler *that = const_cast<QICNSHandler *>(this); | 
| 825 |         that->m_state = that->scanDevice() ? ScanSuccess : ScanError; | 
| 826 |     } | 
| 827 |  | 
| 828 |     return m_state == ScanSuccess; | 
| 829 | } | 
| 830 |  | 
| 831 | bool QICNSHandler::addEntry(const ICNSBlockHeader &, qint64 imgDataOffset, quint32 variant) | 
| 832 | { | 
| 833 |     // Note: This function returns false only when a device positioning error occurred | 
| 834 |     ICNSEntry entry; | 
| 835 |     entry.ostype = header.ostype; | 
| 836 |     entry.variant = variant; | 
| 837 |     entry.dataOffset = imgDataOffset; | 
| 838 |     entry.dataLength = header.length - ICNSBlockHeaderSize; | 
| 839 |     // Check for known magic numbers: | 
| 840 |     if (!parseIconEntryData(icon&: entry, device: device())) | 
| 841 |         return false; | 
| 842 |     // Parse everything else and index this entry: | 
| 843 |     if (parseIconEntryInfo(icon&: entry)) { | 
| 844 |         if ((entry.flags & ICNSEntry::IsMask) != 0) | 
| 845 |             m_masks << entry; | 
| 846 |         if ((entry.flags & ICNSEntry::IsIcon) != 0) | 
| 847 |             m_icons << entry; | 
| 848 |     } | 
| 849 |     return true; | 
| 850 | } | 
| 851 |  | 
| 852 | bool QICNSHandler::scanDevice() | 
| 853 | { | 
| 854 |     if (m_state == ScanSuccess) | 
| 855 |         return true; | 
| 856 |  | 
| 857 |     if (!device()->seek(pos: 0)) | 
| 858 |         return false; | 
| 859 |  | 
| 860 |     QDataStream stream(device()); | 
| 861 |     stream.setByteOrder(QDataStream::BigEndian); | 
| 862 |  | 
| 863 |     bool scanIsIncomplete = false; | 
| 864 |     qint64 filelength = device()->size(); | 
| 865 |     ICNSBlockHeader ; | 
| 866 |     while (!stream.atEnd() || device()->pos() < filelength) { | 
| 867 |         stream >> blockHeader; | 
| 868 |         if (stream.status() != QDataStream::Ok) | 
| 869 |             return false; | 
| 870 |  | 
| 871 |         const qint64 blockDataOffset = device()->pos(); | 
| 872 |         if (!isBlockHeaderValid(header: blockHeader, bound: ICNSBlockHeaderSize + filelength - blockDataOffset)) { | 
| 873 |             qWarning(msg: "QICNSHandler::scanDevice(): Failed, bad header at pos %s. OSType \"%s\", length %u" , | 
| 874 |                      QByteArray::number(blockDataOffset).constData(), | 
| 875 |                      nameFromOSType(ostype: blockHeader.ostype).constData(), blockHeader.length); | 
| 876 |             return false; | 
| 877 |         } | 
| 878 |         const quint64 blockDataLength = blockHeader.length - ICNSBlockHeaderSize; | 
| 879 |         const qint64 nextBlockOffset = blockDataOffset + blockDataLength; | 
| 880 |  | 
| 881 |         switch (blockHeader.ostype) { | 
| 882 |         case ICNSBlockHeader::TypeIcns: | 
| 883 |             if (blockDataOffset != ICNSBlockHeaderSize) { | 
| 884 |                 // Icns container definition should be in the beginning of the device. | 
| 885 |                 // If we meet this block somewhere else, then just ignore it. | 
| 886 |                 stream.skipRawData(len: blockDataLength); | 
| 887 |                 break; | 
| 888 |             } | 
| 889 |             filelength = blockHeader.length; | 
| 890 |             if (device()->size() < blockHeader.length) { | 
| 891 |                 qWarning(msg: "QICNSHandler::scanDevice(): Failed, file is incomplete." ); | 
| 892 |                 return false; | 
| 893 |             } | 
| 894 |             break; | 
| 895 |         case ICNSBlockHeader::TypeIcnv: | 
| 896 |         case ICNSBlockHeader::TypeClut: | 
| 897 |             // We don't have a good use for these blocks... yet. | 
| 898 |             stream.skipRawData(len: blockDataLength); | 
| 899 |             break; | 
| 900 |         case ICNSBlockHeader::TypeTile: | 
| 901 |         case ICNSBlockHeader::TypeOver: | 
| 902 |         case ICNSBlockHeader::TypeOpen: | 
| 903 |         case ICNSBlockHeader::TypeDrop: | 
| 904 |         case ICNSBlockHeader::TypeOdrp: | 
| 905 |             // Icns container seems to have an embedded icon variant container | 
| 906 |             // Let's start a scan for entries | 
| 907 |             while (!stream.atEnd() && device()->pos() < nextBlockOffset) { | 
| 908 |                 ICNSBlockHeader icon; | 
| 909 |                 stream >> icon; | 
| 910 |                 if (stream.status() != QDataStream::Ok) | 
| 911 |                     return false; | 
| 912 |                 // Check for incorrect variant entry header and stop scan | 
| 913 |                 quint64 remaining = blockDataLength - (device()->pos() - blockDataOffset); | 
| 914 |                 if (!isBlockHeaderValid(header: icon, bound: ICNSBlockHeaderSize + remaining)) | 
| 915 |                     break; | 
| 916 |                 if (!addEntry(header: icon, imgDataOffset: device()->pos(), variant: blockHeader.ostype)) | 
| 917 |                     return false; | 
| 918 |                 if (stream.skipRawData(len: icon.length - ICNSBlockHeaderSize) < 0) | 
| 919 |                     return false; | 
| 920 |             } | 
| 921 |             if (device()->pos() != nextBlockOffset) { | 
| 922 |                 // Scan of this container didn't end where we expected. | 
| 923 |                 // Let's generate some output about this incident: | 
| 924 |                 qWarning(msg: "Scan of the icon variant container (\"%s\") failed at pos %s.\n"  \ | 
| 925 |                          "Reason: Scan didn't reach the end of this container's block, "  \ | 
| 926 |                          "delta: %s bytes. This file may be corrupted." , | 
| 927 |                          nameFromOSType(ostype: blockHeader.ostype).constData(), | 
| 928 |                          QByteArray::number(device()->pos()).constData(), | 
| 929 |                          QByteArray::number(nextBlockOffset - device()->pos()).constData()); | 
| 930 |                 if (!device()->seek(pos: nextBlockOffset)) | 
| 931 |                     return false; | 
| 932 |             } | 
| 933 |             break; | 
| 934 |         case ICNSBlockHeader::TypeToc: { | 
| 935 |             // Quick scan, table of contents | 
| 936 |             if (blockDataOffset != ICNSBlockHeaderSize * 2) { | 
| 937 |                 // TOC should be the first block in the file after container definition. | 
| 938 |                 // Ignore and go on with a deep scan. | 
| 939 |                 stream.skipRawData(len: blockDataLength); | 
| 940 |                 break; | 
| 941 |             } | 
| 942 |             // First image data offset: | 
| 943 |             qint64 imgDataOffset = blockDataOffset + blockHeader.length; | 
| 944 |             for (uint i = 0, count = blockDataLength / ICNSBlockHeaderSize; i < count; i++) { | 
| 945 |                 ICNSBlockHeader tocEntry; | 
| 946 |                 stream >> tocEntry; | 
| 947 |                 if (!isBlockHeaderValid(header: tocEntry)) { | 
| 948 |                     // TOC contains incorrect header, we should skip TOC since we can't trust it | 
| 949 |                     qWarning(msg: "QICNSHandler::scanDevice(): Warning! Table of contents contains a bad "  \ | 
| 950 |                              "entry! Stop at device pos: %s bytes. This file may be corrupted." , | 
| 951 |                              QByteArray::number(device()->pos()).constData()); | 
| 952 |                     if (!device()->seek(pos: nextBlockOffset)) | 
| 953 |                         return false; | 
| 954 |                     break; | 
| 955 |                 } | 
| 956 |                 if (!addEntry(header: tocEntry, imgDataOffset)) | 
| 957 |                     return false; | 
| 958 |                 imgDataOffset += tocEntry.length; | 
| 959 |                 // If TOC covers all the blocks in the file, then quick scan is complete | 
| 960 |                 if (imgDataOffset == filelength) | 
| 961 |                     return true; | 
| 962 |             } | 
| 963 |             // Else just start a deep scan to salvage anything left after TOC's end | 
| 964 |             scanIsIncomplete = true; | 
| 965 |             break; | 
| 966 |         } | 
| 967 |         default: | 
| 968 |             // Deep scan, block by block | 
| 969 |             if (scanIsIncomplete) { | 
| 970 |                 // Check if entry with this offset is added somewhere | 
| 971 |                 // But only if we have incomplete TOC, otherwise just try to add | 
| 972 |                 bool exists = false; | 
| 973 |                 for (int i = 0; i < m_icons.size() && !exists; i++) | 
| 974 |                     exists = m_icons.at(i).dataOffset == blockDataOffset; | 
| 975 |                 for (int i = 0; i < m_masks.size() && !exists; i++) | 
| 976 |                     exists = m_masks.at(i).dataOffset == blockDataOffset; | 
| 977 |                 if (!exists && !addEntry(header: blockHeader, imgDataOffset: blockDataOffset)) | 
| 978 |                     return false; | 
| 979 |             } else if (!addEntry(header: blockHeader, imgDataOffset: blockDataOffset)) { | 
| 980 |                 return false; | 
| 981 |             } | 
| 982 |             stream.skipRawData(len: blockDataLength); | 
| 983 |             break; | 
| 984 |         } | 
| 985 |     } | 
| 986 |     return (m_icons.size() > 0); | 
| 987 | } | 
| 988 |  | 
| 989 | const ICNSEntry &QICNSHandler::getIconMask(const ICNSEntry &icon) const | 
| 990 | { | 
| 991 |     const bool is32bit = icon.depth == ICNSEntry::Depth32bit; | 
| 992 |     ICNSEntry::Depth targetDepth = is32bit ? ICNSEntry::Depth8bit : ICNSEntry::DepthMono; | 
| 993 |     for (int i = 0; i < m_masks.size(); i++) { | 
| 994 |         const ICNSEntry &mask = m_masks.at(i); | 
| 995 |         if (isMaskSuitable(mask, icon, target: targetDepth)) | 
| 996 |             return mask; | 
| 997 |     } | 
| 998 |     return icon; | 
| 999 | } | 
| 1000 |  | 
| 1001 | QT_END_NAMESPACE | 
| 1002 |  | 
| 1003 | #endif // QT_NO_DATASTREAM | 
| 1004 |  |