1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkBmpCodec.h" 9 #include "SkCodecPriv.h" 10 #include "SkColorData.h" 11 #include "SkData.h" 12 #include "SkIcoCodec.h" 13 #include "SkPngCodec.h" 14 #include "SkStream.h" 15 #include "SkTDArray.h" 16 #include "SkTSort.h" 17 18 /* 19 * Checks the start of the stream to see if the image is an Ico or Cur 20 */ 21 bool SkIcoCodec::IsIco(const void* buffer, size_t bytesRead) { 22 const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' }; 23 const char curSig[] = { '\x00', '\x00', '\x02', '\x00' }; 24 return bytesRead >= sizeof(icoSig) && 25 (!memcmp(buffer, icoSig, sizeof(icoSig)) || 26 !memcmp(buffer, curSig, sizeof(curSig))); 27 } 28 29 std::unique_ptr<SkCodec> SkIcoCodec::MakeFromStream(std::unique_ptr<SkStream> stream, 30 Result* result) { 31 // Header size constants 32 constexpr uint32_t kIcoDirectoryBytes = 6; 33 constexpr uint32_t kIcoDirEntryBytes = 16; 34 35 // Read the directory header 36 std::unique_ptr<uint8_t[]> dirBuffer(new uint8_t[kIcoDirectoryBytes]); 37 if (stream->read(dirBuffer.get(), kIcoDirectoryBytes) != kIcoDirectoryBytes) { 38 SkCodecPrintf("Error: unable to read ico directory header.\n"); 39 *result = kIncompleteInput; 40 return nullptr; 41 } 42 43 // Process the directory header 44 const uint16_t numImages = get_short(dirBuffer.get(), 4); 45 if (0 == numImages) { 46 SkCodecPrintf("Error: No images embedded in ico.\n"); 47 *result = kInvalidInput; 48 return nullptr; 49 } 50 51 // This structure is used to represent the vital information about entries 52 // in the directory header. We will obtain this information for each 53 // directory entry. 54 struct Entry { 55 uint32_t offset; 56 uint32_t size; 57 }; 58 SkAutoFree dirEntryBuffer(sk_malloc_canfail(sizeof(Entry) * numImages)); 59 if (!dirEntryBuffer) { 60 SkCodecPrintf("Error: OOM allocating ICO directory for %i images.\n", 61 numImages); 62 *result = kInternalError; 63 return nullptr; 64 } 65 auto* directoryEntries = reinterpret_cast<Entry*>(dirEntryBuffer.get()); 66 67 // Iterate over directory entries 68 for (uint32_t i = 0; i < numImages; i++) { 69 uint8_t entryBuffer[kIcoDirEntryBytes]; 70 if (stream->read(entryBuffer, kIcoDirEntryBytes) != kIcoDirEntryBytes) { 71 SkCodecPrintf("Error: Dir entries truncated in ico.\n"); 72 *result = kIncompleteInput; 73 return nullptr; 74 } 75 76 // The directory entry contains information such as width, height, 77 // bits per pixel, and number of colors in the color palette. We will 78 // ignore these fields since they are repeated in the header of the 79 // embedded image. In the event of an inconsistency, we would always 80 // defer to the value in the embedded header anyway. 81 82 // Specifies the size of the embedded image, including the header 83 uint32_t size = get_int(entryBuffer, 8); 84 85 // Specifies the offset of the embedded image from the start of file. 86 // It does not indicate the start of the pixel data, but rather the 87 // start of the embedded image header. 88 uint32_t offset = get_int(entryBuffer, 12); 89 90 // Save the vital fields 91 directoryEntries[i].offset = offset; 92 directoryEntries[i].size = size; 93 } 94 95 // Default Result, if no valid embedded codecs are found. 96 *result = kInvalidInput; 97 98 // It is "customary" that the embedded images will be stored in order of 99 // increasing offset. However, the specification does not indicate that 100 // they must be stored in this order, so we will not trust that this is the 101 // case. Here we sort the embedded images by increasing offset. 102 struct EntryLessThan { 103 bool operator() (Entry a, Entry b) const { 104 return a.offset < b.offset; 105 } 106 }; 107 EntryLessThan lessThan; 108 SkTQSort(directoryEntries, &directoryEntries[numImages - 1], lessThan); 109 110 // Now will construct a candidate codec for each of the embedded images 111 uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; 112 std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> codecs( 113 new (SkTArray<std::unique_ptr<SkCodec>, true>)(numImages)); 114 for (uint32_t i = 0; i < numImages; i++) { 115 uint32_t offset = directoryEntries[i].offset; 116 uint32_t size = directoryEntries[i].size; 117 118 // Ensure that the offset is valid 119 if (offset < bytesRead) { 120 SkCodecPrintf("Warning: invalid ico offset.\n"); 121 continue; 122 } 123 124 // If we cannot skip, assume we have reached the end of the stream and 125 // stop trying to make codecs 126 if (stream->skip(offset - bytesRead) != offset - bytesRead) { 127 SkCodecPrintf("Warning: could not skip to ico offset.\n"); 128 break; 129 } 130 bytesRead = offset; 131 132 // Create a new stream for the embedded codec 133 SkAutoFree buffer(sk_malloc_canfail(size)); 134 if (!buffer) { 135 SkCodecPrintf("Warning: OOM trying to create embedded stream.\n"); 136 break; 137 } 138 139 if (stream->read(buffer.get(), size) != size) { 140 SkCodecPrintf("Warning: could not create embedded stream.\n"); 141 *result = kIncompleteInput; 142 break; 143 } 144 145 sk_sp<SkData> data(SkData::MakeFromMalloc(buffer.release(), size)); 146 auto embeddedStream = SkMemoryStream::Make(data); 147 bytesRead += size; 148 149 // Check if the embedded codec is bmp or png and create the codec 150 std::unique_ptr<SkCodec> codec; 151 Result dummyResult; 152 if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) { 153 codec = SkPngCodec::MakeFromStream(std::move(embeddedStream), &dummyResult); 154 } else { 155 codec = SkBmpCodec::MakeFromIco(std::move(embeddedStream), &dummyResult); 156 } 157 158 // Save a valid codec 159 if (nullptr != codec) { 160 codecs->push_back().reset(codec.release()); 161 } 162 } 163 164 // Recognize if there are no valid codecs 165 if (0 == codecs->count()) { 166 SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); 167 return nullptr; 168 } 169 170 // Use the largest codec as a "suggestion" for image info 171 size_t maxSize = 0; 172 int maxIndex = 0; 173 for (int i = 0; i < codecs->count(); i++) { 174 SkImageInfo info = codecs->operator[](i)->getInfo(); 175 size_t size = info.computeMinByteSize(); 176 177 if (size > maxSize) { 178 maxSize = size; 179 maxIndex = i; 180 } 181 } 182 int width = codecs->operator[](maxIndex)->getInfo().width(); 183 int height = codecs->operator[](maxIndex)->getInfo().height(); 184 SkEncodedInfo info = codecs->operator[](maxIndex)->getEncodedInfo(); 185 SkColorSpace* colorSpace = codecs->operator[](maxIndex)->getInfo().colorSpace(); 186 187 *result = kSuccess; 188 // The original stream is no longer needed, because the embedded codecs own their 189 // own streams. 190 return std::unique_ptr<SkCodec>(new SkIcoCodec(width, height, info, codecs.release(), 191 sk_ref_sp(colorSpace))); 192 } 193 194 /* 195 * Creates an instance of the decoder 196 * Called only by NewFromStream 197 */ 198 SkIcoCodec::SkIcoCodec(int width, int height, const SkEncodedInfo& info, 199 SkTArray<std::unique_ptr<SkCodec>, true>* codecs, 200 sk_sp<SkColorSpace> colorSpace) 201 // The source SkColorSpaceXform::ColorFormat will not be used. The embedded 202 // codec's will be used instead. 203 : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(), nullptr, 204 std::move(colorSpace)) 205 , fEmbeddedCodecs(codecs) 206 , fCurrCodec(nullptr) 207 {} 208 209 /* 210 * Chooses the best dimensions given the desired scale 211 */ 212 SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const { 213 // We set the dimensions to the largest candidate image by default. 214 // Regardless of the scale request, this is the largest image that we 215 // will decode. 216 int origWidth = this->getInfo().width(); 217 int origHeight = this->getInfo().height(); 218 float desiredSize = desiredScale * origWidth * origHeight; 219 // At least one image will have smaller error than this initial value 220 float minError = ((float) (origWidth * origHeight)) - desiredSize + 1.0f; 221 int32_t minIndex = -1; 222 for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) { 223 int width = fEmbeddedCodecs->operator[](i)->getInfo().width(); 224 int height = fEmbeddedCodecs->operator[](i)->getInfo().height(); 225 float error = SkTAbs(((float) (width * height)) - desiredSize); 226 if (error < minError) { 227 minError = error; 228 minIndex = i; 229 } 230 } 231 SkASSERT(minIndex >= 0); 232 233 return fEmbeddedCodecs->operator[](minIndex)->getInfo().dimensions(); 234 } 235 236 int SkIcoCodec::chooseCodec(const SkISize& requestedSize, int startIndex) { 237 SkASSERT(startIndex >= 0); 238 239 // FIXME: Cache the index from onGetScaledDimensions? 240 for (int i = startIndex; i < fEmbeddedCodecs->count(); i++) { 241 if (fEmbeddedCodecs->operator[](i)->getInfo().dimensions() == requestedSize) { 242 return i; 243 } 244 } 245 246 return -1; 247 } 248 249 bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) { 250 return this->chooseCodec(dim, 0) >= 0; 251 } 252 253 /* 254 * Initiates the Ico decode 255 */ 256 SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, 257 void* dst, size_t dstRowBytes, 258 const Options& opts, 259 int* rowsDecoded) { 260 if (opts.fSubset) { 261 // Subsets are not supported. 262 return kUnimplemented; 263 } 264 265 int index = 0; 266 SkCodec::Result result = kInvalidScale; 267 while (true) { 268 index = this->chooseCodec(dstInfo.dimensions(), index); 269 if (index < 0) { 270 break; 271 } 272 273 SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get(); 274 result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts); 275 switch (result) { 276 case kSuccess: 277 case kIncompleteInput: 278 // The embedded codec will handle filling incomplete images, so we will indicate 279 // that all of the rows are initialized. 280 *rowsDecoded = dstInfo.height(); 281 return result; 282 default: 283 // Continue trying to find a valid embedded codec on a failed decode. 284 break; 285 } 286 287 index++; 288 } 289 290 SkCodecPrintf("Error: No matching candidate image in ico.\n"); 291 return result; 292 } 293 294 SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, 295 const SkCodec::Options& options) { 296 int index = 0; 297 SkCodec::Result result = kInvalidScale; 298 while (true) { 299 index = this->chooseCodec(dstInfo.dimensions(), index); 300 if (index < 0) { 301 break; 302 } 303 304 SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get(); 305 result = embeddedCodec->startScanlineDecode(dstInfo, &options); 306 if (kSuccess == result) { 307 fCurrCodec = embeddedCodec; 308 return result; 309 } 310 311 index++; 312 } 313 314 SkCodecPrintf("Error: No matching candidate image in ico.\n"); 315 return result; 316 } 317 318 int SkIcoCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { 319 SkASSERT(fCurrCodec); 320 return fCurrCodec->getScanlines(dst, count, rowBytes); 321 } 322 323 bool SkIcoCodec::onSkipScanlines(int count) { 324 SkASSERT(fCurrCodec); 325 return fCurrCodec->skipScanlines(count); 326 } 327 328 SkCodec::Result SkIcoCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, 329 void* pixels, size_t rowBytes, const SkCodec::Options& options) { 330 int index = 0; 331 while (true) { 332 index = this->chooseCodec(dstInfo.dimensions(), index); 333 if (index < 0) { 334 break; 335 } 336 337 SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get(); 338 switch (embeddedCodec->startIncrementalDecode(dstInfo, 339 pixels, rowBytes, &options)) { 340 case kSuccess: 341 fCurrCodec = embeddedCodec; 342 return kSuccess; 343 case kUnimplemented: 344 // FIXME: embeddedCodec is a BMP. If scanline decoding would work, 345 // return kUnimplemented so that SkSampledCodec will fall through 346 // to use the scanline decoder. 347 // Note that calling startScanlineDecode will require an extra 348 // rewind. The embedded codec has an SkMemoryStream, which is 349 // cheap to rewind, though it will do extra work re-reading the 350 // header. 351 // Also note that we pass nullptr for Options. This is because 352 // Options that are valid for incremental decoding may not be 353 // valid for scanline decoding. 354 // Once BMP supports incremental decoding this workaround can go 355 // away. 356 if (embeddedCodec->startScanlineDecode(dstInfo) == kSuccess) { 357 return kUnimplemented; 358 } 359 // Move on to the next embedded codec. 360 break; 361 default: 362 break; 363 } 364 365 index++; 366 } 367 368 SkCodecPrintf("Error: No matching candidate image in ico.\n"); 369 return kInvalidScale; 370 } 371 372 SkCodec::Result SkIcoCodec::onIncrementalDecode(int* rowsDecoded) { 373 SkASSERT(fCurrCodec); 374 return fCurrCodec->incrementalDecode(rowsDecoded); 375 } 376 377 SkCodec::SkScanlineOrder SkIcoCodec::onGetScanlineOrder() const { 378 // FIXME: This function will possibly return the wrong value if it is called 379 // before startScanlineDecode()/startIncrementalDecode(). 380 if (fCurrCodec) { 381 return fCurrCodec->getScanlineOrder(); 382 } 383 384 return INHERITED::onGetScanlineOrder(); 385 } 386 387 SkSampler* SkIcoCodec::getSampler(bool createIfNecessary) { 388 if (fCurrCodec) { 389 return fCurrCodec->getSampler(createIfNecessary); 390 } 391 392 return nullptr; 393 } 394