Flutter Engine
The Flutter Engine
SkSampledCodec.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
13#include "include/core/SkRect.h"
16#include "src/base/SkMathPriv.h"
18#include "src/codec/SkSampler.h"
19
21 : INHERITED(codec)
22{}
23
24SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const {
25 SkISize preSampledSize = this->codec()->dimensions();
26 int sampleSize = *sampleSizePtr;
27 SkASSERT(sampleSize > 1);
28
29 if (nativeSampleSize) {
30 *nativeSampleSize = 1;
31 }
32
33 // Only JPEG supports native downsampling.
35 // See if libjpeg supports this scale directly
36 switch (sampleSize) {
37 case 2:
38 case 4:
39 case 8:
40 // This class does not need to do any sampling.
41 *sampleSizePtr = 1;
42 return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize));
43 default:
44 break;
45 }
46
47 // Check if sampleSize is a multiple of something libjpeg can support.
48 int remainder;
49 const int sampleSizes[] = { 8, 4, 2 };
50 for (int supportedSampleSize : sampleSizes) {
51 int actualSampleSize;
52 SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder);
53 if (0 == remainder) {
54 float scale = get_scale_from_sample_size(supportedSampleSize);
55
56 // this->codec() will scale to this size.
57 preSampledSize = this->codec()->getScaledDimensions(scale);
58
59 // And then this class will sample it.
60 *sampleSizePtr = actualSampleSize;
61 if (nativeSampleSize) {
62 *nativeSampleSize = supportedSampleSize;
63 }
64 break;
65 }
66 }
67 }
68
69 return preSampledSize;
70}
71
73 const SkISize size = this->accountForNativeScaling(&sampleSize);
74 return SkISize::Make(get_scaled_dimension(size.width(), sampleSize),
75 get_scaled_dimension(size.height(), sampleSize));
76}
77
79 size_t rowBytes, const AndroidOptions& options) {
80 const SkIRect* subset = options.fSubset;
81 if (!subset || subset->size() == this->codec()->dimensions()) {
82 if (this->codec()->dimensionsSupported(info.dimensions())) {
83 return this->codec()->getPixels(info, pixels, rowBytes, &options);
84 }
85
86 // If the native codec does not support the requested scale, scale by sampling.
87 return this->sampledDecode(info, pixels, rowBytes, options);
88 }
89
90 // We are performing a subset decode.
91 int sampleSize = options.fSampleSize;
92 SkISize scaledSize = this->getSampledDimensions(sampleSize);
93 if (!this->codec()->dimensionsSupported(scaledSize)) {
94 // If the native codec does not support the requested scale, scale by sampling.
95 return this->sampledDecode(info, pixels, rowBytes, options);
96 }
97
98 // Calculate the scaled subset bounds.
99 int scaledSubsetX = subset->x() / sampleSize;
100 int scaledSubsetY = subset->y() / sampleSize;
101 int scaledSubsetWidth = info.width();
102 int scaledSubsetHeight = info.height();
103
104 const SkImageInfo scaledInfo = info.makeDimensions(scaledSize);
105
106 // Copy so we can use a different fSubset.
107 AndroidOptions subsetOptions = options;
108 {
109 // Although startScanlineDecode expects the bottom and top to match the
110 // SkImageInfo, startIncrementalDecode uses them to determine which rows to
111 // decode.
112 SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY,
113 scaledSubsetWidth, scaledSubsetHeight);
114 subsetOptions.fSubset = &incrementalSubset;
115 const SkCodec::Result startResult = this->codec()->startIncrementalDecode(
116 scaledInfo, pixels, rowBytes, &subsetOptions);
117 if (SkCodec::kSuccess == startResult) {
118 int rowsDecoded = 0;
119 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
120 if (incResult == SkCodec::kSuccess) {
121 return SkCodec::kSuccess;
122 }
123 SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput);
124
125 // FIXME: Can zero initialized be read from SkCodec::fOptions?
126 this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes,
127 options.fZeroInitialized, scaledSubsetHeight, rowsDecoded);
128 return incResult;
129 } else if (startResult != SkCodec::kUnimplemented) {
130 return startResult;
131 }
132 // Otherwise fall down to use the old scanline decoder.
133 // subsetOptions.fSubset will be reset below, so it will not continue to
134 // point to the object that is no longer on the stack.
135 }
136
137 // Start the scanline decode.
138 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
139 scaledSize.height());
140 subsetOptions.fSubset = &scanlineSubset;
141
142 SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo,
143 &subsetOptions);
144 if (SkCodec::kSuccess != result) {
145 return result;
146 }
147
148 // At this point, we are only concerned with subsetting. Either no scale was
149 // requested, or the this->codec() is handling the scale.
150 // Note that subsetting is only supported for kTopDown, so this code will not be
151 // reached for other orders.
152 SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder);
153 if (!this->codec()->skipScanlines(scaledSubsetY)) {
154 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
155 scaledSubsetHeight, 0);
157 }
158
159 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
160 if (decodedLines != scaledSubsetHeight) {
162 }
163 return SkCodec::kSuccess;
164}
165
166
167SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
168 size_t rowBytes, const AndroidOptions& options) {
169 // We should only call this function when sampling.
170 SkASSERT(options.fSampleSize > 1);
171
172 // FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
173 int sampleSize = options.fSampleSize;
174 int nativeSampleSize;
175 SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
176
177 // Check if there is a subset.
178 SkIRect subset;
179 int subsetY = 0;
180 int subsetWidth = nativeSize.width();
181 int subsetHeight = nativeSize.height();
182 if (options.fSubset) {
183 // We will need to know about subsetting in the y-dimension in order to use the
184 // scanline decoder.
185 // Update the subset to account for scaling done by this->codec().
186 const SkIRect* subsetPtr = options.fSubset;
187
188 // Do the divide ourselves, instead of calling get_scaled_dimension. If
189 // X and Y are 0, they should remain 0, rather than being upgraded to 1
190 // due to being smaller than the sampleSize.
191 const int subsetX = subsetPtr->x() / nativeSampleSize;
192 subsetY = subsetPtr->y() / nativeSampleSize;
193
194 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
195 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
196
197 // The scanline decoder only needs to be aware of subsetting in the x-dimension.
198 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
199 }
200
201 // Since we guarantee that output dimensions are always at least one (even if the sampleSize
202 // is greater than a given dimension), the input sampleSize is not always the sampleSize that
203 // we use in practice.
204 const int sampleX = subsetWidth / info.width();
205 const int sampleY = subsetHeight / info.height();
206
207 const int samplingOffsetY = get_start_coord(sampleY);
208 const int startY = samplingOffsetY + subsetY;
209 const int dstHeight = info.height();
210
211 const SkImageInfo nativeInfo = info.makeDimensions(nativeSize);
212
213 {
214 // Although startScanlineDecode expects the bottom and top to match the
215 // SkImageInfo, startIncrementalDecode uses them to determine which rows to
216 // decode.
217 AndroidOptions incrementalOptions = options;
218 SkIRect incrementalSubset;
219 if (options.fSubset) {
220 incrementalSubset.fTop = subsetY;
221 incrementalSubset.fBottom = subsetY + subsetHeight;
222 incrementalSubset.fLeft = subset.fLeft;
223 incrementalSubset.fRight = subset.fRight;
224 incrementalOptions.fSubset = &incrementalSubset;
225 }
226 const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo,
227 pixels, rowBytes, &incrementalOptions);
228 if (SkCodec::kSuccess == startResult) {
229 SkSampler* sampler = this->codec()->getSampler(true);
230 if (!sampler) {
232 }
233
234 if (sampler->setSampleX(sampleX) != info.width()) {
236 }
237 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
239 }
240
241 sampler->setSampleY(sampleY);
242
243 int rowsDecoded = 0;
244 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
245 if (incResult == SkCodec::kSuccess) {
246 return SkCodec::kSuccess;
247 }
248 SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput);
249
250 SkASSERT(rowsDecoded <= info.height());
251 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
252 info.height(), rowsDecoded);
253 return incResult;
254 } else if (startResult == SkCodec::kIncompleteInput
255 || startResult == SkCodec::kErrorInInput) {
257 } else if (startResult != SkCodec::kUnimplemented) {
258 return startResult;
259 } // kUnimplemented means use the old method.
260 }
261
262 // Start the scanline decode.
263 AndroidOptions sampledOptions = options;
264 if (options.fSubset) {
265 sampledOptions.fSubset = &subset;
266 }
267 SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo,
268 &sampledOptions);
271 } else if (SkCodec::kSuccess != result) {
272 return result;
273 }
274
275 SkSampler* sampler = this->codec()->getSampler(true);
276 if (!sampler) {
278 }
279
280 if (sampler->setSampleX(sampleX) != info.width()) {
282 }
283 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
285 }
286
287 switch(this->codec()->getScanlineOrder()) {
289 if (!this->codec()->skipScanlines(startY)) {
290 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
291 dstHeight, 0);
293 }
294 void* pixelPtr = pixels;
295 for (int y = 0; y < dstHeight; y++) {
296 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
297 this->codec()->fillIncompleteImage(info, pixels, rowBytes,
298 options.fZeroInitialized, dstHeight, y + 1);
300 }
301 if (y < dstHeight - 1) {
302 if (!this->codec()->skipScanlines(sampleY - 1)) {
303 this->codec()->fillIncompleteImage(info, pixels, rowBytes,
304 options.fZeroInitialized, dstHeight, y + 1);
306 }
307 }
308 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
309 }
310 return SkCodec::kSuccess;
311 }
313 // Note that these modes do not support subsetting.
314 SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight);
315 int y;
316 for (y = 0; y < nativeSize.height(); y++) {
317 int srcY = this->codec()->nextScanline();
318 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
319 void* pixelPtr = SkTAddOffset<void>(pixels,
320 rowBytes * get_dst_coord(srcY, sampleY));
321 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
322 break;
323 }
324 } else {
325 if (!this->codec()->skipScanlines(1)) {
326 break;
327 }
328 }
329 }
330
331 if (nativeSize.height() == y) {
332 return SkCodec::kSuccess;
333 }
334
335 // We handle filling uninitialized memory here instead of using this->codec().
336 // this->codec() does not know that we are sampling.
337 const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
338 for (; y < nativeSize.height(); y++) {
339 int srcY = this->codec()->outputScanline(y);
340 if (!is_coord_necessary(srcY, sampleY, dstHeight)) {
341 continue;
342 }
343
344 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY));
345 SkSampler::Fill(fillInfo, rowPtr, rowBytes, options.fZeroInitialized);
346 }
348 }
349 default:
350 SkASSERT(false);
352 }
353}
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim)
Definition: SkCodecPriv.h:77
static int get_dst_coord(int srcCoord, int sampleFactor)
Definition: SkCodecPriv.h:67
static float get_scale_from_sample_size(int sampleSize)
Definition: SkCodecPriv.h:31
static int get_scaled_dimension(int srcDimension, int sampleSize)
Definition: SkCodecPriv.h:44
static int get_start_coord(int sampleFactor)
Definition: SkCodecPriv.h:57
void SkTDivMod(In numer, In denom, Out *div, Out *mod)
Definition: SkMathPriv.h:38
SkEncodedImageFormat getEncodedFormat() const
SkISize getSampledDimensions(int sampleSize) const
SkCodec * codec() const
int getScanlines(void *dst, int countLines, size_t rowBytes)
Definition: SkCodec.cpp:694
SkISize dimensions() const
Definition: SkCodec.h:230
Result startScanlineDecode(const SkImageInfo &dstInfo, const Options *options)
Definition: SkCodec.cpp:635
@ kBottomUp_SkScanlineOrder
Definition: SkCodec.h:604
@ kTopDown_SkScanlineOrder
Definition: SkCodec.h:581
Result getPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, const Options *)
Definition: SkCodec.cpp:467
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
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
@ kErrorInInput
Definition: SkCodec.h:91
SkISize getScaledDimensions(float desiredScale) const
Definition: SkCodec.h:255
int outputScanline(int inputScanline) const
Definition: SkCodec.cpp:731
int nextScanline() const
Definition: SkCodec.h:624
SkSampledCodec(SkCodec *)
SkISize onGetSampledDimensions(int sampleSize) const override
SkCodec::Result onGetAndroidPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, const AndroidOptions &options) override
void setSampleY(int sampleY)
Definition: SkSampler.h:32
int setSampleX(int sampleX)
Definition: SkSampler.h:25
static void Fill(const SkImageInfo &info, void *dst, size_t rowBytes, SkCodec::ZeroInitialized zeroInit)
Definition: SkSampler.cpp:20
GAsyncResult * result
double y
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
const Scalar scale
const SkIRect * fSubset
Definition: SkCodec.h:347
Definition: SkRect.h:32
constexpr int32_t x() const
Definition: SkRect.h:141
constexpr int32_t y() const
Definition: SkRect.h:148
int32_t fBottom
larger y-axis bounds
Definition: SkRect.h:36
constexpr SkISize size() const
Definition: SkRect.h:172
constexpr int32_t height() const
Definition: SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
constexpr int32_t width() const
Definition: SkRect.h:158
void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height)
Definition: SkRect.h:268
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
int32_t fLeft
smaller x-axis bounds
Definition: SkRect.h:33
int32_t fRight
larger x-axis bounds
Definition: SkRect.h:35
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37