SkIcoCodec.cpp 14 KB

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