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