Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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
@ 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
const Scalar scale
const SkIRect * fSubset
Definition SkCodec.h:347
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
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
SkImageInfo makeWH(int newWidth, int newHeight) const
SkImageInfo makeDimensions(SkISize newSize) const
int height() const