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