Flutter Engine
The Flutter Engine
SkJpegSourceMgr.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 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
10#include "include/core/SkData.h"
15
16#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
19#endif // SK_CODEC_DECODES_JPEG_GAINMAPS
20
21////////////////////////////////////////////////////////////////////////////////////////////////////
22// SkStream helpers.
23
24/*
25 * Class that will will rewind an SkStream, and then restore it to its original position when it
26 * goes out of scope. If the SkStream is not seekable, then the stream will not be altered at all,
27 * and will return false from canRestore.
28 */
29
31public:
32 ScopedSkStreamRestorer(SkStream* stream) : fStream(stream), fPosition(stream->getPosition()) {
33 if (!fStream->rewind()) {
34 SkCodecPrintf("Failed to rewind decoder stream.\n");
35 }
36 }
38 if (!fStream->seek(fPosition)) {
39 SkCodecPrintf("Failed to restore decoder stream.\n");
40 }
41 }
42
43private:
44 SkStream* const fStream;
45 const size_t fPosition;
46};
47
48////////////////////////////////////////////////////////////////////////////////////////////////////
49// SkJpegMemorySourceMgr
50
52public:
55
56 void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
57 nextInputByte = reinterpret_cast<const uint8_t*>(fStream->getMemoryBase());
58 bytesInBuffer = static_cast<size_t>(fStream->getLength());
59 }
60 bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
61 // The whole JPEG data is expected to reside in the supplied memory buffer, so any request
62 // for more data beyond the given buffer size is treated as an error.
63 SkCodecPrintf("Asked to re-fill a memory-mapped stream.\n");
64 return false;
65 }
66 bool skipInputBytes(size_t bytesToSkip,
67 const uint8_t*& nextInputByte,
68 size_t& bytesInBuffer) override {
69 if (bytesToSkip > bytesInBuffer) {
70 SkCodecPrintf("Asked to read past end of a memory-mapped stream.\n");
71 return false;
72 }
73 nextInputByte += bytesToSkip;
74 bytesInBuffer -= bytesToSkip;
75 return true;
76 }
77#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
78 const std::vector<SkJpegSegment>& getAllSegments() override {
79 if (fScanner) {
80 return fScanner->getSegments();
81 }
82 fScanner = std::make_unique<SkJpegSegmentScanner>(kJpegMarkerEndOfImage);
83 fScanner->onBytes(fStream->getMemoryBase(), fStream->getLength());
84 return fScanner->getSegments();
85 }
86 sk_sp<SkData> getSubsetData(size_t offset, size_t size, bool* wasCopied) override {
87 if (offset > fStream->getLength() || size > fStream->getLength() - offset) {
88 return nullptr;
89 }
90 if (wasCopied) {
91 *wasCopied = false;
92 }
94 reinterpret_cast<const uint8_t*>(fStream->getMemoryBase()) + offset, size);
95 }
96 sk_sp<SkData> getSegmentParameters(const SkJpegSegment& segment) override {
97 const uint8_t* base =
98 reinterpret_cast<const uint8_t*>(fStream->getMemoryBase()) + segment.offset;
99 SkASSERT(segment.offset < fStream->getLength());
101 fStream->getLength() - segment.offset);
102
103 // Read the marker and verify it matches `segment`.
104 SkASSERT(base[0] == 0xFF);
105 SkASSERT(base[1] == segment.marker);
106
107 // Read the parameter length and verify it matches `segment`.
108 SkASSERT(256 * base[2] + base[3] == segment.parameterLength);
110 return nullptr;
111 }
112
113 // Read the remainder of the segment.
116 }
117#endif // SK_CODEC_DECODES_JPEG_GAINMAPS
118};
119
120////////////////////////////////////////////////////////////////////////////////////////////////////
121// SkJpegBufferedSourceMgr
122
124public:
126 fBuffer = SkData::MakeUninitialized(bufferSize);
127 }
129
130 void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
131 nextInputByte = fBuffer->bytes();
132 bytesInBuffer = 0;
133 }
134 bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
135 size_t bytesRead = fStream->read(fBuffer->writable_data(), fBuffer->size());
136 if (bytesRead == 0) {
137 // Fail if we read zero bytes (libjpeg will accept any non-zero number of bytes).
138 SkCodecPrintf("Hit end of file reading a buffered stream.\n");
139 return false;
140 }
141 nextInputByte = fBuffer->bytes();
142 bytesInBuffer = bytesRead;
143 return true;
144 }
145 bool skipInputBytes(size_t bytesToSkip,
146 const uint8_t*& nextInputByte,
147 size_t& bytesInBuffer) override {
148 // Skip through the already-read (or already in memory) buffer.
149 if (bytesToSkip <= bytesInBuffer) {
150 nextInputByte += bytesToSkip;
151 bytesInBuffer -= bytesToSkip;
152 return true;
153 }
154 bytesToSkip -= bytesInBuffer;
155
156 // Fail if we skip past the end of the stream.
157 if (fStream->skip(bytesToSkip) != bytesToSkip) {
158 SkCodecPrintf("Failed to skip through buffered stream.\n");
159 return false;
160 }
161
162 bytesInBuffer = 0;
163 nextInputByte = fBuffer->bytes();
164 return true;
165 }
166#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
167 const std::vector<SkJpegSegment>& getAllSegments() override {
168 if (fScanner) {
169 return fScanner->getSegments();
170 }
171 ScopedSkStreamRestorer streamRestorer(fStream);
172 fScanner = std::make_unique<SkJpegSegmentScanner>(kJpegMarkerEndOfImage);
173 while (!fScanner->isDone() && !fScanner->hadError()) {
174 constexpr size_t kBufferSize = 1024;
175 uint8_t buffer[kBufferSize];
176 size_t bytesRead = fStream->read(buffer, kBufferSize);
177 if (bytesRead == 0) {
178 SkCodecPrintf("Unexpected EOF.\n");
179 break;
180 }
181 fScanner->onBytes(buffer, bytesRead);
182 }
183 return fScanner->getSegments();
184 }
185 sk_sp<SkData> getSubsetData(size_t offset, size_t size, bool* wasCopied) override {
186 ScopedSkStreamRestorer streamRestorer(fStream);
187 if (!fStream->seek(offset)) {
188 SkCodecPrintf("Failed to seek to subset stream position.\n");
189 return nullptr;
190 }
192 if (fStream->read(data->writable_data(), size) != size) {
193 SkCodecPrintf("Failed to read subset stream data.\n");
194 return nullptr;
195 }
196 if (wasCopied) {
197 *wasCopied = true;
198 }
199 return data;
200 }
201 sk_sp<SkData> getSegmentParameters(const SkJpegSegment& segment) override {
202 // If the segment's parameter length isn't longer than the two bytes for the length,
203 // early-out early-out.
205 return nullptr;
206 }
207
208 // Seek to the start of the segment.
209 ScopedSkStreamRestorer streamRestorer(fStream);
210 if (!fStream->seek(segment.offset)) {
211 SkCodecPrintf("Failed to seek to segment\n");
212 return nullptr;
213 }
214
215 // Read the marker and verify it matches `segment`.
216 uint8_t markerCode[kJpegMarkerCodeSize] = {0};
218 SkCodecPrintf("Failed to read segment marker code\n");
219 return nullptr;
220 }
221 SkASSERT(markerCode[0] == 0xFF);
222 SkASSERT(markerCode[1] == segment.marker);
223
224 // Read the parameter length and verify it matches `segment`.
225 uint8_t parameterLength[kJpegSegmentParameterLengthSize] = {0};
226 if (fStream->read(parameterLength, kJpegSegmentParameterLengthSize) !=
228 SkCodecPrintf("Failed to read parameter length\n");
229 return nullptr;
230 }
231 SkASSERT(256 * parameterLength[0] + parameterLength[1] == segment.parameterLength);
232
233 // Read the remainder of the segment.
234 size_t sizeToRead = segment.parameterLength - kJpegSegmentParameterLengthSize;
235 auto result = SkData::MakeUninitialized(sizeToRead);
236 if (fStream->read(result->writable_data(), sizeToRead) != sizeToRead) {
237 return nullptr;
238 }
239
240 return result;
241 }
242#endif // SK_CODEC_DECODES_JPEG_GAINMAPS
243
244private:
245 sk_sp<SkData> fBuffer;
246};
247
248////////////////////////////////////////////////////////////////////////////////////////////////////
249// SkJpegUnseekableSourceMgr
250
251#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
252/*
253 * This class implements SkJpegSourceMgr for a stream that cannot seek or rewind. It scans the data
254 * as it is presented to the decoder. This allows it to track the position of segments, so that it
255 * can extract subsets at a specific offset (e.g, relative to the EndOfImage segment for JpegR or
256 * relative to an MPF segment for MPF).
257 */
258class SkJpegUnseekableSourceMgr : public SkJpegSourceMgr {
259public:
260 SkJpegUnseekableSourceMgr(SkStream* stream, size_t bufferSize) : SkJpegSourceMgr(stream) {
261 fBuffer = SkData::MakeUninitialized(bufferSize);
262 fScanner = std::make_unique<SkJpegSegmentScanner>(kJpegMarkerEndOfImage);
263 }
264 ~SkJpegUnseekableSourceMgr() override {}
265
266 void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
267 nextInputByte = fBuffer->bytes();
268 bytesInBuffer = 0;
269 }
270 bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
271 if (!readToBufferAndScan(fBuffer->size())) {
272 SkCodecPrintf("Failure filling unseekable input buffer.\n");
273 return false;
274 }
275 nextInputByte = fBuffer->bytes();
276 bytesInBuffer = fLastReadSize;
277 return true;
278 }
279 bool skipInputBytes(size_t bytesToSkip,
280 const uint8_t*& nextInputByte,
281 size_t& bytesInBuffer) override {
282 // Skip through the already-read (or already in memory) buffer.
283 if (bytesToSkip <= bytesInBuffer) {
284 nextInputByte += bytesToSkip;
285 bytesInBuffer -= bytesToSkip;
286 return true;
287 }
288 bytesToSkip -= bytesInBuffer;
289
290 // Read the remaining bytes to skip into fBuffer and feed them into fScanner.
291 while (bytesToSkip > 0) {
292 if (!readToBufferAndScan(std::min(bytesToSkip, fBuffer->size()))) {
293 SkCodecPrintf("Failure filling unseekable input buffer.\n");
294 return false;
295 }
296 bytesToSkip -= fLastReadSize;
297 }
298
299 // Indicate to libjpeg that it it needs to call fillInputBuffer.
300 bytesInBuffer = 0;
301 nextInputByte = fBuffer->bytes();
302 return true;
303 }
304 const std::vector<SkJpegSegment>& getAllSegments() override {
305 while (!fScanner->isDone() && !fScanner->hadError()) {
306 if (!readToBufferAndScan(fBuffer->size())) {
307 SkCodecPrintf("Failure finishing unseekable input buffer.\n");
308 break;
309 }
310 }
311 return fScanner->getSegments();
312 }
313 sk_sp<SkData> getSubsetData(size_t offset, size_t size, bool* wasCopied) override {
314 // If we haven't reached the EndOfImage, then we are throwing away the base image before
315 // decoding it. This is only reasonable for tests.
316 if (!fScanner->isDone()) {
317 SkCodecPrintf("getSubsetData is prematurely terminating scan.\n");
318 }
319
320 // If we have read past offset, we can never get that data back again.
321 if (offset < fLastReadOffset) {
322 SkCodecPrintf("Requested that is gone.\n");
323 return nullptr;
324 }
325
326 // Allocate the memory to return, and indicate that the result is a copy.
328 uint8_t* subsetDataCurrent = reinterpret_cast<uint8_t*>(subsetData->writable_data());
329
330 // Determine the relationship between the offset we're reading from and |fBuffer|.
331 size_t offsetIntoBuffer = offset - fLastReadOffset;
332 if (offsetIntoBuffer >= fLastReadSize) {
333 // We have to skip past |fBuffer| to get to |offset|.
334 fLastReadOffset += fLastReadSize;
335 fLastReadSize = 0;
336
337 // Skip any additional bytes needed to get to |offset|.
338 size_t bytesToSkip = offset - fLastReadOffset;
339 while (bytesToSkip > 0) {
340 size_t bytesSkipped = fStream->skip(bytesToSkip);
341 if (bytesSkipped == 0) {
342 SkCodecPrintf("Failed to skip bytes before subset.\n");
343 return nullptr;
344 }
345 bytesToSkip -= bytesSkipped;
346 fLastReadOffset += bytesSkipped;
347 }
348 } else {
349 // This assert is to emphatically document the side of the branch we're on.
350 SkASSERT(offsetIntoBuffer < fLastReadSize);
351
352 // Some of the data we want to copy has already been read into |fBuffer|. Copy that data
353 // to |subsetData|
354 size_t bytesToReadFromBuffer = std::min(fLastReadSize - offsetIntoBuffer, size);
355 memcpy(subsetDataCurrent, fBuffer->bytes() + offsetIntoBuffer, bytesToReadFromBuffer);
356 size -= bytesToReadFromBuffer;
357 subsetDataCurrent += bytesToReadFromBuffer;
358
359 // If all of the data that we needed was in |fBuffer|, then return early.
360 if (size == 0) {
361 if (wasCopied) {
362 *wasCopied = true;
363 }
364 return subsetData;
365 }
366 // We will now have to read beyond |fBuffer|, so reset it.
367 fLastReadOffset += fLastReadSize;
368 fLastReadSize = 0;
369 }
370
371 // Read the remaining data from |fStream|.
372 while (size > 0) {
373 size_t bytesRead = fStream->read(subsetDataCurrent, size);
374 if (bytesRead == 0) {
375 SkCodecPrintf("Failed to read subset stream data.\n");
376 return nullptr;
377 }
378 size -= bytesRead;
379 subsetDataCurrent += bytesRead;
380 fLastReadOffset += bytesRead;
381 }
382
383 if (wasCopied) {
384 *wasCopied = true;
385 }
386 return subsetData;
387 }
388 sk_sp<SkData> getSegmentParameters(const SkJpegSegment& segment) override {
389 // The only way to implement this for an unseekable stream is to record the parameters as
390 // they are scanned.
391 return nullptr;
392 }
393
394private:
395 // Read the specified number of bytes into fBuffer and feed them to fScanner. The number of
396 // bytes must not be larger than fBuffer's size.
397 bool readToBufferAndScan(size_t bytesToRead) {
398 SkASSERT(bytesToRead <= fBuffer->size());
399 fLastReadOffset += fLastReadSize;
400 fLastReadSize = fStream->read(fBuffer->writable_data(), bytesToRead);
401 if (fLastReadSize == 0) {
402 SkCodecPrintf("Hit end of file reading an unseekable stream.\n");
403 return false;
404 }
405 fScanner->onBytes(fBuffer->bytes(), fLastReadSize);
406 return true;
407 }
408
409 sk_sp<SkData> fBuffer;
410
411 // The number of bytes that were most recently read into fBuffer (this can be less than the size
412 // of fBuffer).
413 size_t fLastReadSize = 0;
414
415 // The offset into the stream (total number of bytes read) at the time of our most recent read
416 // into fBuffer.
417 size_t fLastReadOffset = 0;
418};
419#endif // SK_CODEC_DECODES_JPEG_GAINMAPS
420
421////////////////////////////////////////////////////////////////////////////////////////////////////
422// SkJpegSourceMgr
423
424// static
425std::unique_ptr<SkJpegSourceMgr> SkJpegSourceMgr::Make(SkStream* stream, size_t bufferSize) {
426#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
427 if (!stream->hasPosition()) {
428 return std::make_unique<SkJpegUnseekableSourceMgr>(stream, bufferSize);
429 }
430#endif
431 if (stream->hasLength() && stream->getMemoryBase()) {
432 return std::make_unique<SkJpegMemorySourceMgr>(stream);
433 }
434 return std::make_unique<SkJpegBufferedSourceMgr>(stream, bufferSize);
435}
436
438
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SkCodecPrintf(...)
Definition: SkCodecPriv.h:23
static constexpr size_t kJpegSegmentParameterLengthSize
static constexpr uint8_t kJpegMarkerEndOfImage
static constexpr size_t kJpegMarkerCodeSize
static const size_t kBufferSize
Definition: SkString.cpp:27
ScopedSkStreamRestorer(SkStream *stream)
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 > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
void * writable_data()
Definition: SkData.h:52
size_t size() const
Definition: SkData.h:30
void initSource(const uint8_t *&nextInputByte, size_t &bytesInBuffer) override
bool fillInputBuffer(const uint8_t *&nextInputByte, size_t &bytesInBuffer) override
SkJpegBufferedSourceMgr(SkStream *stream, size_t bufferSize)
bool skipInputBytes(size_t bytesToSkip, const uint8_t *&nextInputByte, size_t &bytesInBuffer) override
void initSource(const uint8_t *&nextInputByte, size_t &bytesInBuffer) override
SkJpegMemorySourceMgr(SkStream *stream)
bool skipInputBytes(size_t bytesToSkip, const uint8_t *&nextInputByte, size_t &bytesInBuffer) override
~SkJpegMemorySourceMgr() override
bool fillInputBuffer(const uint8_t *&nextInputByte, size_t &bytesInBuffer) override
virtual ~SkJpegSourceMgr()
virtual bool skipInputBytes(size_t bytes, const uint8_t *&nextInputByte, size_t &bytesInBuffer)=0
SkStream *const fStream
virtual bool fillInputBuffer(const uint8_t *&nextInputByte, size_t &bytesInBuffer)=0
virtual void initSource(const uint8_t *&nextInputByte, size_t &bytesInBuffer)=0
SkJpegSourceMgr(SkStream *stream)
static std::unique_ptr< SkJpegSourceMgr > Make(SkStream *stream, size_t bufferSize=1024)
virtual bool rewind()
Definition: SkStream.h:100
size_t skip(size_t size)
Definition: SkStream.h:51
virtual bool seek(size_t)
Definition: SkStream.h:125
virtual size_t getLength() const
Definition: SkStream.h:137
virtual const void * getMemoryBase()
Definition: SkStream.h:141
virtual size_t read(void *buffer, size_t size)=0
GAsyncResult * result
static float min(float r, float g, float b)
Definition: hsl.cpp:48
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
SeparatedVector2 offset
uint16_t parameterLength
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63