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