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 "SkColorPriv.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 /* 30 * Assumes IsIco was called and returned true 31 * Creates an Ico decoder 32 * Reads enough of the stream to determine the image format 33 */ 34 SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) { 35 // Ensure that we do not leak the input stream 36 SkAutoTDelete<SkStream> inputStream(stream); 37 38 // Header size constants 39 static const uint32_t kIcoDirectoryBytes = 6; 40 static const uint32_t kIcoDirEntryBytes = 16; 41 42 // Read the directory header 43 SkAutoTDeleteArray<uint8_t> dirBuffer(new uint8_t[kIcoDirectoryBytes]); 44 if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) != 45 kIcoDirectoryBytes) { 46 SkCodecPrintf("Error: unable to read ico directory header.\n"); 47 return nullptr; 48 } 49 50 // Process the directory header 51 const uint16_t numImages = get_short(dirBuffer.get(), 4); 52 if (0 == numImages) { 53 SkCodecPrintf("Error: No images embedded in ico.\n"); 54 return nullptr; 55 } 56 57 // Ensure that we can read all of indicated directory entries 58 SkAutoTDeleteArray<uint8_t> entryBuffer(new uint8_t[numImages * kIcoDirEntryBytes]); 59 if (inputStream.get()->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) != 60 numImages*kIcoDirEntryBytes) { 61 SkCodecPrintf("Error: unable to read ico directory entries.\n"); 62 return nullptr; 63 } 64 65 // This structure is used to represent the vital information about entries 66 // in the directory header. We will obtain this information for each 67 // directory entry. 68 struct Entry { 69 uint32_t offset; 70 uint32_t size; 71 }; 72 SkAutoTDeleteArray<Entry> directoryEntries(new Entry[numImages]); 73 74 // Iterate over directory entries 75 for (uint32_t i = 0; i < numImages; i++) { 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.get(), 8 + i*kIcoDirEntryBytes); 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.get(), 12 + i*kIcoDirEntryBytes); 89 90 // Save the vital fields 91 directoryEntries.get()[i].offset = offset; 92 directoryEntries.get()[i].size = size; 93 } 94 95 // It is "customary" that the embedded images will be stored in order of 96 // increasing offset. However, the specification does not indicate that 97 // they must be stored in this order, so we will not trust that this is the 98 // case. Here we sort the embedded images by increasing offset. 99 struct EntryLessThan { 100 bool operator() (Entry a, Entry b) const { 101 return a.offset < b.offset; 102 } 103 }; 104 EntryLessThan lessThan; 105 SkTQSort(directoryEntries.get(), directoryEntries.get() + numImages - 1, 106 lessThan); 107 108 // Now will construct a candidate codec for each of the embedded images 109 uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; 110 SkAutoTDelete<SkTArray<SkAutoTDelete<SkCodec>, true>> codecs( 111 new (SkTArray<SkAutoTDelete<SkCodec>, true>)(numImages)); 112 for (uint32_t i = 0; i < numImages; i++) { 113 uint32_t offset = directoryEntries.get()[i].offset; 114 uint32_t size = directoryEntries.get()[i].size; 115 116 // Ensure that the offset is valid 117 if (offset < bytesRead) { 118 SkCodecPrintf("Warning: invalid ico offset.\n"); 119 continue; 120 } 121 122 // If we cannot skip, assume we have reached the end of the stream and 123 // stop trying to make codecs 124 if (inputStream.get()->skip(offset - bytesRead) != offset - bytesRead) { 125 SkCodecPrintf("Warning: could not skip to ico offset.\n"); 126 break; 127 } 128 bytesRead = offset; 129 130 // Create a new stream for the embedded codec 131 SkAutoTUnref<SkData> data( 132 SkData::NewFromStream(inputStream.get(), size)); 133 if (nullptr == data.get()) { 134 SkCodecPrintf("Warning: could not create embedded stream.\n"); 135 break; 136 } 137 SkAutoTDelete<SkMemoryStream> embeddedStream(new SkMemoryStream(data.get())); 138 bytesRead += size; 139 140 // Check if the embedded codec is bmp or png and create the codec 141 SkCodec* codec = nullptr; 142 if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) { 143 codec = SkPngCodec::NewFromStream(embeddedStream.detach()); 144 } else { 145 codec = SkBmpCodec::NewFromIco(embeddedStream.detach()); 146 } 147 148 // Save a valid codec 149 if (nullptr != codec) { 150 codecs->push_back().reset(codec); 151 } 152 } 153 154 // Recognize if there are no valid codecs 155 if (0 == codecs->count()) { 156 SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); 157 return nullptr; 158 } 159 160 // Use the largest codec as a "suggestion" for image info 161 uint32_t maxSize = 0; 162 uint32_t maxIndex = 0; 163 for (int32_t i = 0; i < codecs->count(); i++) { 164 SkImageInfo info = codecs->operator[](i)->getInfo(); 165 uint32_t size = info.width() * info.height(); 166 if (size > maxSize) { 167 maxSize = size; 168 maxIndex = i; 169 } 170 } 171 SkImageInfo info = codecs->operator[](maxIndex)->getInfo(); 172 173 // Note that stream is owned by the embedded codec, the ico does not need 174 // direct access to the stream. 175 return new SkIcoCodec(info, codecs.detach()); 176 } 177 178 /* 179 * Creates an instance of the decoder 180 * Called only by NewFromStream 181 */ 182 SkIcoCodec::SkIcoCodec(const SkImageInfo& info, 183 SkTArray<SkAutoTDelete<SkCodec>, true>* codecs) 184 : INHERITED(info, nullptr) 185 , fEmbeddedCodecs(codecs) 186 , fCurrScanlineCodec(nullptr) 187 {} 188 189 /* 190 * Chooses the best dimensions given the desired scale 191 */ 192 SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const { 193 // We set the dimensions to the largest candidate image by default. 194 // Regardless of the scale request, this is the largest image that we 195 // will decode. 196 int origWidth = this->getInfo().width(); 197 int origHeight = this->getInfo().height(); 198 float desiredSize = desiredScale * origWidth * origHeight; 199 // At least one image will have smaller error than this initial value 200 float minError = ((float) (origWidth * origHeight)) - desiredSize + 1.0f; 201 int32_t minIndex = -1; 202 for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) { 203 int width = fEmbeddedCodecs->operator[](i)->getInfo().width(); 204 int height = fEmbeddedCodecs->operator[](i)->getInfo().height(); 205 float error = SkTAbs(((float) (width * height)) - desiredSize); 206 if (error < minError) { 207 minError = error; 208 minIndex = i; 209 } 210 } 211 SkASSERT(minIndex >= 0); 212 213 return fEmbeddedCodecs->operator[](minIndex)->getInfo().dimensions(); 214 } 215 216 int SkIcoCodec::chooseCodec(const SkISize& requestedSize, int startIndex) { 217 SkASSERT(startIndex >= 0); 218 219 // FIXME: Cache the index from onGetScaledDimensions? 220 for (int i = startIndex; i < fEmbeddedCodecs->count(); i++) { 221 if (fEmbeddedCodecs->operator[](i)->getInfo().dimensions() == requestedSize) { 222 return i; 223 } 224 } 225 226 return -1; 227 } 228 229 bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) { 230 return this->chooseCodec(dim, 0) >= 0; 231 } 232 233 /* 234 * Initiates the Ico decode 235 */ 236 SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, 237 void* dst, size_t dstRowBytes, 238 const Options& opts, SkPMColor* colorTable, 239 int* colorCount, int* rowsDecoded) { 240 if (opts.fSubset) { 241 // Subsets are not supported. 242 return kUnimplemented; 243 } 244 245 int index = 0; 246 SkCodec::Result result = kInvalidScale; 247 while (true) { 248 index = this->chooseCodec(dstInfo.dimensions(), index); 249 if (index < 0) { 250 break; 251 } 252 253 SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index); 254 result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts, colorTable, 255 colorCount); 256 257 switch (result) { 258 case kSuccess: 259 case kIncompleteInput: 260 // The embedded codec will handle filling incomplete images, so we will indicate 261 // that all of the rows are initialized. 262 *rowsDecoded = dstInfo.height(); 263 return result; 264 default: 265 // Continue trying to find a valid embedded codec on a failed decode. 266 break; 267 } 268 269 index++; 270 } 271 272 SkCodecPrintf("Error: No matching candidate image in ico.\n"); 273 return result; 274 } 275 276 SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, 277 const SkCodec::Options& options, SkPMColor colorTable[], int* colorCount) { 278 int index = 0; 279 SkCodec::Result result = kInvalidScale; 280 while (true) { 281 index = this->chooseCodec(dstInfo.dimensions(), index); 282 if (index < 0) { 283 break; 284 } 285 286 SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index); 287 result = embeddedCodec->startScanlineDecode(dstInfo, &options, colorTable, colorCount); 288 if (kSuccess == result) { 289 fCurrScanlineCodec = embeddedCodec; 290 return result; 291 } 292 293 index++; 294 } 295 296 SkCodecPrintf("Error: No matching candidate image in ico.\n"); 297 return result; 298 } 299 300 int SkIcoCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { 301 SkASSERT(fCurrScanlineCodec); 302 return fCurrScanlineCodec->getScanlines(dst, count, rowBytes); 303 } 304 305 bool SkIcoCodec::onSkipScanlines(int count) { 306 SkASSERT(fCurrScanlineCodec); 307 return fCurrScanlineCodec->skipScanlines(count); 308 } 309 310 SkCodec::SkScanlineOrder SkIcoCodec::onGetScanlineOrder() const { 311 // FIXME: This function will possibly return the wrong value if it is called 312 // before startScanlineDecode(). 313 return fCurrScanlineCodec ? fCurrScanlineCodec->getScanlineOrder() : 314 INHERITED::onGetScanlineOrder(); 315 } 316 317 SkSampler* SkIcoCodec::getSampler(bool createIfNecessary) { 318 return fCurrScanlineCodec ? fCurrScanlineCodec->getSampler(createIfNecessary) : nullptr; 319 } 320