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 "src/codec/SkSampledCodec.h"
9
10 #include "include/codec/SkCodec.h"
11 #include "include/codec/SkEncodedImageFormat.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkTypes.h"
15 #include "include/private/base/SkTemplates.h"
16 #include "src/base/SkMathPriv.h"
17 #include "src/codec/SkCodecPriv.h"
18 #include "src/codec/SkSampler.h"
19
SkSampledCodec(SkCodec * codec)20 SkSampledCodec::SkSampledCodec(SkCodec* codec)
21 : INHERITED(codec)
22 {}
23
accountForNativeScaling(int * sampleSizePtr,int * nativeSampleSize) const24 SkISize 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.
34 if (this->codec()->getEncodedFormat() == SkEncodedImageFormat::kJPEG) {
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
onGetSampledDimensions(int sampleSize) const72 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
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
onGetAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions & options)78 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
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);
156 return SkCodec::kIncompleteInput;
157 }
158
159 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
160 if (decodedLines != scaledSubsetHeight) {
161 return SkCodec::kIncompleteInput;
162 }
163 return SkCodec::kSuccess;
164 }
165
166
sampledDecode(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions & options)167 SkCodec::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) {
231 return SkCodec::kUnimplemented;
232 }
233
234 if (sampler->setSampleX(sampleX) != info.width()) {
235 return SkCodec::kInvalidScale;
236 }
237 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
238 return SkCodec::kInvalidScale;
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) {
256 return SkCodec::kInvalidInput;
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 = ⊂
266 }
267 SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo,
268 &sampledOptions);
269 if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) {
270 return SkCodec::kInvalidInput;
271 } else if (SkCodec::kSuccess != result) {
272 return result;
273 }
274
275 SkSampler* sampler = this->codec()->getSampler(true);
276 if (!sampler) {
277 return SkCodec::kInternalError;
278 }
279
280 if (sampler->setSampleX(sampleX) != info.width()) {
281 return SkCodec::kInvalidScale;
282 }
283 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
284 return SkCodec::kInvalidScale;
285 }
286
287 switch(this->codec()->getScanlineOrder()) {
288 case SkCodec::kTopDown_SkScanlineOrder: {
289 if (!this->codec()->skipScanlines(startY)) {
290 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
291 dstHeight, 0);
292 return SkCodec::kIncompleteInput;
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);
299 return SkCodec::kIncompleteInput;
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);
305 return SkCodec::kIncompleteInput;
306 }
307 }
308 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
309 }
310 return SkCodec::kSuccess;
311 }
312 case SkCodec::kBottomUp_SkScanlineOrder: {
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 }
347 return SkCodec::kIncompleteInput;
348 }
349 default:
350 SkASSERT(false);
351 return SkCodec::kUnimplemented;
352 }
353 }
354