Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
SkIcoCodec.cpp
Go to the documentation of this file.
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
9
11#include "include/core/SkData.h"
18#include "src/base/SkTSort.h"
23
24#include "modules/skcms/skcms.h"
25#include <cstdint>
26#include <cstring>
27#include <memory>
28#include <utility>
29
30using namespace skia_private;
31
32class SkSampler;
33
34/*
35 * Checks the start of the stream to see if the image is an Ico or Cur
36 */
37bool SkIcoCodec::IsIco(const void* buffer, size_t bytesRead) {
38 const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' };
39 const char curSig[] = { '\x00', '\x00', '\x02', '\x00' };
40 return bytesRead >= sizeof(icoSig) &&
41 (!memcmp(buffer, icoSig, sizeof(icoSig)) ||
42 !memcmp(buffer, curSig, sizeof(curSig)));
43}
44
45std::unique_ptr<SkCodec> SkIcoCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
46 Result* result) {
48 if (!stream) {
50 return nullptr;
51 }
52 // It is helpful to have the entire stream in a contiguous buffer. In some cases,
53 // this is already the case anyway, so this method is faster. In others, this is
54 // safer than the old method, which required allocating a block of memory whose
55 // byte size is stored in the stream as a uint32_t, and may result in a large or
56 // failed allocation.
57 sk_sp<SkData> data = nullptr;
58 if (stream->getMemoryBase()) {
59 // It is safe to make without copy because we'll hold onto the stream.
61 } else {
63
64 // If we are forced to copy the stream to a data, we can go ahead and delete the stream.
65 stream.reset(nullptr);
66 }
67
68 // Header size constants
69 constexpr uint32_t kIcoDirectoryBytes = 6;
70 constexpr uint32_t kIcoDirEntryBytes = 16;
71
72 // Read the directory header
73 if (data->size() < kIcoDirectoryBytes) {
74 SkCodecPrintf("Error: unable to read ico directory header.\n");
76 return nullptr;
77 }
78
79 // Process the directory header
80 const uint16_t numImages = get_short(data->bytes(), 4);
81 if (0 == numImages) {
82 SkCodecPrintf("Error: No images embedded in ico.\n");
84 return nullptr;
85 }
86
87 // This structure is used to represent the vital information about entries
88 // in the directory header. We will obtain this information for each
89 // directory entry.
90 struct Entry {
91 uint32_t offset;
92 uint32_t size;
93 };
94 UniqueVoidPtr dirEntryBuffer(sk_malloc_canfail(sizeof(Entry) * numImages));
95 if (!dirEntryBuffer) {
96 SkCodecPrintf("Error: OOM allocating ICO directory for %i images.\n",
97 numImages);
99 return nullptr;
100 }
101 auto* directoryEntries = reinterpret_cast<Entry*>(dirEntryBuffer.get());
102
103 // Iterate over directory entries
104 for (uint32_t i = 0; i < numImages; i++) {
105 const uint8_t* entryBuffer = data->bytes() + kIcoDirectoryBytes + i * kIcoDirEntryBytes;
106 if (data->size() < kIcoDirectoryBytes + (i+1) * kIcoDirEntryBytes) {
107 SkCodecPrintf("Error: Dir entries truncated in ico.\n");
109 return nullptr;
110 }
111
112 // The directory entry contains information such as width, height,
113 // bits per pixel, and number of colors in the color palette. We will
114 // ignore these fields since they are repeated in the header of the
115 // embedded image. In the event of an inconsistency, we would always
116 // defer to the value in the embedded header anyway.
117
118 // Specifies the size of the embedded image, including the header
119 uint32_t size = get_int(entryBuffer, 8);
120
121 // Specifies the offset of the embedded image from the start of file.
122 // It does not indicate the start of the pixel data, but rather the
123 // start of the embedded image header.
124 uint32_t offset = get_int(entryBuffer, 12);
125
126 // Save the vital fields
127 directoryEntries[i].offset = offset;
128 directoryEntries[i].size = size;
129 }
130
131 // Default Result, if no valid embedded codecs are found.
133
134 // It is "customary" that the embedded images will be stored in order of
135 // increasing offset. However, the specification does not indicate that
136 // they must be stored in this order, so we will not trust that this is the
137 // case. Here we sort the embedded images by increasing offset.
138 struct EntryLessThan {
139 bool operator() (Entry a, Entry b) const {
140 return a.offset < b.offset;
141 }
142 };
143 EntryLessThan lessThan;
144 SkTQSort(directoryEntries, directoryEntries + numImages, lessThan);
145
146 // Now will construct a candidate codec for each of the embedded images
147 uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes;
148 auto codecs = std::make_unique<TArray<std::unique_ptr<SkCodec>>>(numImages);
149 for (uint32_t i = 0; i < numImages; i++) {
150 uint32_t offset = directoryEntries[i].offset;
151 uint32_t size = directoryEntries[i].size;
152
153 // Ensure that the offset is valid
154 if (offset < bytesRead) {
155 SkCodecPrintf("Warning: invalid ico offset.\n");
156 continue;
157 }
158
159 // If we cannot skip, assume we have reached the end of the stream and
160 // stop trying to make codecs
161 if (offset >= data->size()) {
162 SkCodecPrintf("Warning: could not skip to ico offset.\n");
163 break;
164 }
165 bytesRead = offset;
166
167 if (offset + size > data->size()) {
168 SkCodecPrintf("Warning: could not create embedded stream.\n");
170 break;
171 }
172
173 sk_sp<SkData> embeddedData(SkData::MakeSubset(data.get(), offset, size));
174 auto embeddedStream = SkMemoryStream::Make(embeddedData);
175 bytesRead += size;
176
177 // Check if the embedded codec is bmp or png and create the codec
178 std::unique_ptr<SkCodec> codec;
179 Result ignoredResult;
180 if (SkPngCodec::IsPng(embeddedData->bytes(), embeddedData->size())) {
181 codec = SkPngCodec::MakeFromStream(std::move(embeddedStream), &ignoredResult);
182 } else {
183 codec = SkBmpCodec::MakeFromIco(std::move(embeddedStream), &ignoredResult);
184 }
185
186 if (nullptr != codec) {
187 codecs->push_back(std::move(codec));
188 }
189 }
190
191 if (codecs->empty()) {
192 SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n");
193 return nullptr;
194 }
195
196 // Use the largest codec as a "suggestion" for image info
197 size_t maxSize = 0;
198 int maxIndex = 0;
199 for (int i = 0; i < codecs->size(); i++) {
200 SkImageInfo info = codecs->at(i)->getInfo();
201 size_t size = info.computeMinByteSize();
202
203 if (size > maxSize) {
204 maxSize = size;
205 maxIndex = i;
206 }
207 }
208
209 auto maxInfo = codecs->at(maxIndex)->getEncodedInfo().copy();
210
211 *result = kSuccess;
212 return std::unique_ptr<SkCodec>(
213 new SkIcoCodec(std::move(maxInfo), std::move(stream), std::move(codecs)));
214}
215
217 std::unique_ptr<SkStream> stream,
218 std::unique_ptr<TArray<std::unique_ptr<SkCodec>>> codecs)
219 // The source skcms_PixelFormat will not be used. The embedded
220 // codec's will be used instead.
221 : INHERITED(std::move(info), skcms_PixelFormat(), std::move(stream))
222 , fEmbeddedCodecs(std::move(codecs))
223 , fCurrCodec(nullptr) {}
224
225/*
226 * Chooses the best dimensions given the desired scale
227 */
229 // We set the dimensions to the largest candidate image by default.
230 // Regardless of the scale request, this is the largest image that we
231 // will decode.
232 int origWidth = this->dimensions().width();
233 int origHeight = this->dimensions().height();
234 float desiredSize = desiredScale * origWidth * origHeight;
235 // At least one image will have smaller error than this initial value
236 float minError = ((float) (origWidth * origHeight)) - desiredSize + 1.0f;
237 int32_t minIndex = -1;
238 for (int32_t i = 0; i < fEmbeddedCodecs->size(); i++) {
239 auto dimensions = fEmbeddedCodecs->at(i)->dimensions();
240 int width = dimensions.width();
241 int height = dimensions.height();
242 float error = SkTAbs(((float) (width * height)) - desiredSize);
243 if (error < minError) {
244 minError = error;
245 minIndex = i;
246 }
247 }
248 SkASSERT(minIndex >= 0);
249
250 return fEmbeddedCodecs->at(minIndex)->dimensions();
251}
252
253int SkIcoCodec::chooseCodec(const SkISize& requestedSize, int startIndex) {
254 SkASSERT(startIndex >= 0);
255
256 // FIXME: Cache the index from onGetScaledDimensions?
257 for (int i = startIndex; i < fEmbeddedCodecs->size(); i++) {
258 if (fEmbeddedCodecs->at(i)->dimensions() == requestedSize) {
259 return i;
260 }
261 }
262
263 return -1;
264}
265
267 return this->chooseCodec(dim, 0) >= 0;
268}
269
270/*
271 * Initiates the Ico decode
272 */
274 void* dst, size_t dstRowBytes,
275 const Options& opts,
276 int* rowsDecoded) {
277 if (opts.fSubset) {
278 // Subsets are not supported.
279 return kUnimplemented;
280 }
281
282 int index = 0;
284 while (true) {
285 index = this->chooseCodec(dstInfo.dimensions(), index);
286 if (index < 0) {
287 break;
288 }
289
290 SkCodec* embeddedCodec = fEmbeddedCodecs->at(index).get();
291 result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts);
292 switch (result) {
293 case kSuccess:
294 case kIncompleteInput:
295 // The embedded codec will handle filling incomplete images, so we will indicate
296 // that all of the rows are initialized.
297 *rowsDecoded = dstInfo.height();
298 return result;
299 default:
300 // Continue trying to find a valid embedded codec on a failed decode.
301 break;
302 }
303
304 index++;
305 }
306
307 SkCodecPrintf("Error: No matching candidate image in ico.\n");
308 return result;
309}
310
311SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
312 const SkCodec::Options& options) {
313 int index = 0;
315 while (true) {
316 index = this->chooseCodec(dstInfo.dimensions(), index);
317 if (index < 0) {
318 break;
319 }
320
321 SkCodec* embeddedCodec = fEmbeddedCodecs->at(index).get();
322 result = embeddedCodec->startScanlineDecode(dstInfo, &options);
323 if (kSuccess == result) {
324 fCurrCodec = embeddedCodec;
325 return result;
326 }
327
328 index++;
329 }
330
331 SkCodecPrintf("Error: No matching candidate image in ico.\n");
332 return result;
333}
334
335int SkIcoCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
336 SkASSERT(fCurrCodec);
337 return fCurrCodec->getScanlines(dst, count, rowBytes);
338}
339
340bool SkIcoCodec::onSkipScanlines(int count) {
341 SkASSERT(fCurrCodec);
342 return fCurrCodec->skipScanlines(count);
343}
344
345SkCodec::Result SkIcoCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
346 void* pixels, size_t rowBytes, const SkCodec::Options& options) {
347 int index = 0;
348 while (true) {
349 index = this->chooseCodec(dstInfo.dimensions(), index);
350 if (index < 0) {
351 break;
352 }
353
354 SkCodec* embeddedCodec = fEmbeddedCodecs->at(index).get();
355 switch (embeddedCodec->startIncrementalDecode(dstInfo,
356 pixels, rowBytes, &options)) {
357 case kSuccess:
358 fCurrCodec = embeddedCodec;
359 return kSuccess;
360 case kUnimplemented:
361 // FIXME: embeddedCodec is a BMP. If scanline decoding would work,
362 // return kUnimplemented so that SkSampledCodec will fall through
363 // to use the scanline decoder.
364 // Note that calling startScanlineDecode will require an extra
365 // rewind. The embedded codec has an SkMemoryStream, which is
366 // cheap to rewind, though it will do extra work re-reading the
367 // header.
368 // Also note that we pass nullptr for Options. This is because
369 // Options that are valid for incremental decoding may not be
370 // valid for scanline decoding.
371 // Once BMP supports incremental decoding this workaround can go
372 // away.
373 if (embeddedCodec->startScanlineDecode(dstInfo) == kSuccess) {
374 return kUnimplemented;
375 }
376 // Move on to the next embedded codec.
377 break;
378 default:
379 break;
380 }
381
382 index++;
383 }
384
385 SkCodecPrintf("Error: No matching candidate image in ico.\n");
386 return kInvalidScale;
387}
388
389SkCodec::Result SkIcoCodec::onIncrementalDecode(int* rowsDecoded) {
390 SkASSERT(fCurrCodec);
391 return fCurrCodec->incrementalDecode(rowsDecoded);
392}
393
395 // FIXME: This function will possibly return the wrong value if it is called
396 // before startScanlineDecode()/startIncrementalDecode().
397 if (fCurrCodec) {
398 return fCurrCodec->getScanlineOrder();
399 }
400
402}
403
404SkSampler* SkIcoCodec::getSampler(bool createIfNecessary) {
405 if (fCurrCodec) {
406 return fCurrCodec->getSampler(createIfNecessary);
407 }
408
409 return nullptr;
410}
411
412namespace SkIcoDecoder {
413bool IsIco(const void* data, size_t len) {
414 return SkIcoCodec::IsIco(data, len);
415}
416
417std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
418 SkCodec::Result* outResult,
420 SkCodec::Result resultStorage;
421 if (!outResult) {
422 outResult = &resultStorage;
423 }
424 return SkIcoCodec::MakeFromStream(std::move(stream), outResult);
425}
426
427std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
428 SkCodec::Result* outResult,
430 if (!data) {
431 if (outResult) {
432 *outResult = SkCodec::kInvalidInput;
433 }
434 return nullptr;
435 }
436 return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr);
437}
438} // namespace SkIcoDecoder
439
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
static uint32_t get_int(const uint8_t *buffer, uint32_t i)
Definition: SkCodecPriv.h:168
#define SkCodecPrintf(...)
Definition: SkCodecPriv.h:23
static uint16_t get_short(const uint8_t *buffer, uint32_t i)
Definition: SkCodecPriv.h:154
static void * sk_malloc_canfail(size_t size)
Definition: SkMalloc.h:93
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
sk_sp< SkData > SkCopyStreamToData(SkStream *stream)
Definition: SkStream.cpp:937
void SkTQSort(T *begin, T *end, const C &lessThan)
Definition: SkTSort.h:194
static T SkTAbs(T value)
Definition: SkTemplates.h:43
static std::unique_ptr< SkCodec > MakeFromIco(std::unique_ptr< SkStream >, Result *)
Definition: SkBmpCodec.cpp:91
int getScanlines(void *dst, int countLines, size_t rowBytes)
Definition: SkCodec.cpp:694
SkISize dimensions() const
Definition: SkCodec.h:230
const SkImageInfo & dstInfo() const
Definition: SkCodec.h:878
SkStream * stream()
Definition: SkCodec.h:865
virtual SkScanlineOrder onGetScanlineOrder() const
Definition: SkCodec.h:876
Result startScanlineDecode(const SkImageInfo &dstInfo, const Options *options)
Definition: SkCodec.cpp:635
SkScanlineOrder
Definition: SkCodec.h:575
Result getPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, const Options *)
Definition: SkCodec.cpp:467
SkScanlineOrder getScanlineOrder() const
Definition: SkCodec.h:613
virtual SkSampler * getSampler(bool)
Definition: SkCodec.h:1035
Result startIncrementalDecode(const SkImageInfo &dstInfo, void *dst, size_t rowBytes, const Options *)
Definition: SkCodec.cpp:575
Result incrementalDecode(int *rowsDecoded=nullptr)
Definition: SkCodec.h:498
friend class SkIcoCodec
Definition: SkCodec.h:1040
Result
Definition: SkCodec.h:76
@ kInvalidScale
Definition: SkCodec.h:100
@ kIncompleteInput
Definition: SkCodec.h:84
@ kInvalidInput
Definition: SkCodec.h:109
@ kInternalError
Definition: SkCodec.h:118
@ kUnimplemented
Definition: SkCodec.h:123
@ kSuccess
Definition: SkCodec.h:80
bool skipScanlines(int countLines)
Definition: SkCodec.cpp:713
const Options & options() const
Definition: SkCodec.h:880
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition: SkData.h:116
const uint8_t * bytes() const
Definition: SkData.h:43
static sk_sp< SkData > MakeSubset(const SkData *src, size_t offset, size_t length)
Definition: SkData.cpp:173
size_t size() const
Definition: SkData.h:30
SkISize onGetScaledDimensions(float desiredScale) const override
Definition: SkIcoCodec.cpp:228
Result onGetPixels(const SkImageInfo &dstInfo, void *dst, size_t dstRowBytes, const Options &, int *) override
Definition: SkIcoCodec.cpp:273
static bool IsIco(const void *, size_t)
Definition: SkIcoCodec.cpp:37
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, Result *)
Definition: SkIcoCodec.cpp:45
SkScanlineOrder onGetScanlineOrder() const override
Definition: SkIcoCodec.cpp:394
bool onDimensionsSupported(const SkISize &) override
Definition: SkIcoCodec.cpp:266
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, Result *, SkPngChunkReader *=nullptr)
static bool IsPng(const void *, size_t)
Definition: SkPngCodec.cpp:344
virtual size_t getLength() const
Definition: SkStream.h:137
virtual const void * getMemoryBase()
Definition: SkStream.h:141
static bool b
struct MyStruct a[10]
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
void * DecodeContext
Definition: SkCodec.h:1047
SK_API bool IsIco(const void *, size_t)
Definition: SkIcoCodec.cpp:413
SK_API std::unique_ptr< SkCodec > Decode(std::unique_ptr< SkStream >, SkCodec::Result *, SkCodecs::DecodeContext=nullptr)
Definition: SkIcoCodec.cpp:417
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
std::unique_ptr< void, SkOverloadedFunctionObject< void(void *), sk_free > > UniqueVoidPtr
Definition: SkTemplates.h:431
Definition: ref_ptr.h:256
int32_t height
int32_t width
skcms_PixelFormat
Definition: skcms_public.h:273
SeparatedVector2 offset
const SkIRect * fSubset
Definition: SkCodec.h:347
Definition: SkSize.h:16
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
SkISize dimensions() const
Definition: SkImageInfo.h:421
int height() const
Definition: SkImageInfo.h:371
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63