• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkCodec.h"
9 #include "SkCodecPriv.h"
10 #include "SkMath.h"
11 #include "SkMathPriv.h"
12 #include "SkSampledCodec.h"
13 #include "SkSampler.h"
14 #include "SkTemplates.h"
15 
SkSampledCodec(SkCodec * codec,ExifOrientationBehavior behavior)16 SkSampledCodec::SkSampledCodec(SkCodec* codec, ExifOrientationBehavior behavior)
17     : INHERITED(codec, behavior)
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     // Create an Options struct for the codec.
77     SkCodec::Options codecOptions;
78     codecOptions.fZeroInitialized = options.fZeroInitialized;
79 
80     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, &codecOptions);
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.makeWH(scaledSize.width(), scaledSize.height());
105 
106     {
107         // Although startScanlineDecode expects the bottom and top to match the
108         // SkImageInfo, startIncrementalDecode uses them to determine which rows to
109         // decode.
110         SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY,
111                                                       scaledSubsetWidth, scaledSubsetHeight);
112         codecOptions.fSubset = &incrementalSubset;
113         const SkCodec::Result startResult = this->codec()->startIncrementalDecode(
114                 scaledInfo, pixels, rowBytes, &codecOptions);
115         if (SkCodec::kSuccess == startResult) {
116             int rowsDecoded = 0;
117             const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
118             if (incResult == SkCodec::kSuccess) {
119                 return SkCodec::kSuccess;
120             }
121             SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput);
122 
123             // FIXME: Can zero initialized be read from SkCodec::fOptions?
124             this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes,
125                     options.fZeroInitialized, scaledSubsetHeight, rowsDecoded);
126             return incResult;
127         } else if (startResult != SkCodec::kUnimplemented) {
128             return startResult;
129         }
130         // Otherwise fall down to use the old scanline decoder.
131         // codecOptions.fSubset will be reset below, so it will not continue to
132         // point to the object that is no longer on the stack.
133     }
134 
135     // Start the scanline decode.
136     SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
137             scaledSize.height());
138     codecOptions.fSubset = &scanlineSubset;
139 
140     SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo,
141             &codecOptions);
142     if (SkCodec::kSuccess != result) {
143         return result;
144     }
145 
146     // At this point, we are only concerned with subsetting.  Either no scale was
147     // requested, or the this->codec() is handling the scale.
148     // Note that subsetting is only supported for kTopDown, so this code will not be
149     // reached for other orders.
150     SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder);
151     if (!this->codec()->skipScanlines(scaledSubsetY)) {
152         this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
153                 scaledSubsetHeight, 0);
154         return SkCodec::kIncompleteInput;
155     }
156 
157     int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
158     if (decodedLines != scaledSubsetHeight) {
159         return SkCodec::kIncompleteInput;
160     }
161     return SkCodec::kSuccess;
162 }
163 
164 
sampledDecode(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions & options)165 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
166         size_t rowBytes, const AndroidOptions& options) {
167     // We should only call this function when sampling.
168     SkASSERT(options.fSampleSize > 1);
169 
170     // Create options struct for the codec.
171     SkCodec::Options sampledOptions;
172     sampledOptions.fZeroInitialized = options.fZeroInitialized;
173 
174     // FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
175     int sampleSize = options.fSampleSize;
176     int nativeSampleSize;
177     SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
178 
179     // Check if there is a subset.
180     SkIRect subset;
181     int subsetY = 0;
182     int subsetWidth = nativeSize.width();
183     int subsetHeight = nativeSize.height();
184     if (options.fSubset) {
185         // We will need to know about subsetting in the y-dimension in order to use the
186         // scanline decoder.
187         // Update the subset to account for scaling done by this->codec().
188         const SkIRect* subsetPtr = options.fSubset;
189 
190         // Do the divide ourselves, instead of calling get_scaled_dimension. If
191         // X and Y are 0, they should remain 0, rather than being upgraded to 1
192         // due to being smaller than the sampleSize.
193         const int subsetX = subsetPtr->x() / nativeSampleSize;
194         subsetY = subsetPtr->y() / nativeSampleSize;
195 
196         subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
197         subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
198 
199         // The scanline decoder only needs to be aware of subsetting in the x-dimension.
200         subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
201         sampledOptions.fSubset = ⊂
202     }
203 
204     // Since we guarantee that output dimensions are always at least one (even if the sampleSize
205     // is greater than a given dimension), the input sampleSize is not always the sampleSize that
206     // we use in practice.
207     const int sampleX = subsetWidth / info.width();
208     const int sampleY = subsetHeight / info.height();
209 
210     const int samplingOffsetY = get_start_coord(sampleY);
211     const int startY = samplingOffsetY + subsetY;
212     const int dstHeight = info.height();
213 
214     const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height());
215 
216     {
217         // Although startScanlineDecode expects the bottom and top to match the
218         // SkImageInfo, startIncrementalDecode uses them to determine which rows to
219         // decode.
220         SkCodec::Options incrementalOptions = sampledOptions;
221         SkIRect incrementalSubset;
222         if (sampledOptions.fSubset) {
223             incrementalSubset.fTop = subsetY;
224             incrementalSubset.fBottom = subsetY + subsetHeight;
225             incrementalSubset.fLeft = sampledOptions.fSubset->fLeft;
226             incrementalSubset.fRight = sampledOptions.fSubset->fRight;
227             incrementalOptions.fSubset = &incrementalSubset;
228         }
229         const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo,
230                 pixels, rowBytes, &incrementalOptions);
231         if (SkCodec::kSuccess == startResult) {
232             SkSampler* sampler = this->codec()->getSampler(true);
233             if (!sampler) {
234                 return SkCodec::kUnimplemented;
235             }
236 
237             if (sampler->setSampleX(sampleX) != info.width()) {
238                 return SkCodec::kInvalidScale;
239             }
240             if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
241                 return SkCodec::kInvalidScale;
242             }
243 
244             sampler->setSampleY(sampleY);
245 
246             int rowsDecoded = 0;
247             const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
248             if (incResult == SkCodec::kSuccess) {
249                 return SkCodec::kSuccess;
250             }
251             SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput);
252 
253             SkASSERT(rowsDecoded <= info.height());
254             this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
255                                                info.height(), rowsDecoded);
256             return incResult;
257         } else if (startResult == SkCodec::kIncompleteInput
258                 || startResult == SkCodec::kErrorInInput) {
259             return SkCodec::kInvalidInput;
260         } else if (startResult != SkCodec::kUnimplemented) {
261             return startResult;
262         } // kUnimplemented means use the old method.
263     }
264 
265     // Start the scanline decode.
266     SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo,
267             &sampledOptions);
268     if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) {
269         return SkCodec::kInvalidInput;
270     } else if (SkCodec::kSuccess != result) {
271         return result;
272     }
273 
274     SkSampler* sampler = this->codec()->getSampler(true);
275     if (!sampler) {
276         return SkCodec::kUnimplemented;
277     }
278 
279     if (sampler->setSampleX(sampleX) != info.width()) {
280         return SkCodec::kInvalidScale;
281     }
282     if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
283         return SkCodec::kInvalidScale;
284     }
285 
286     switch(this->codec()->getScanlineOrder()) {
287         case SkCodec::kTopDown_SkScanlineOrder: {
288             if (!this->codec()->skipScanlines(startY)) {
289                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
290                         dstHeight, 0);
291                 return SkCodec::kIncompleteInput;
292             }
293             void* pixelPtr = pixels;
294             for (int y = 0; y < dstHeight; y++) {
295                 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
296                     this->codec()->fillIncompleteImage(info, pixels, rowBytes,
297                             options.fZeroInitialized, dstHeight, y + 1);
298                     return SkCodec::kIncompleteInput;
299                 }
300                 if (y < dstHeight - 1) {
301                     if (!this->codec()->skipScanlines(sampleY - 1)) {
302                         this->codec()->fillIncompleteImage(info, pixels, rowBytes,
303                                 options.fZeroInitialized, dstHeight, y + 1);
304                         return SkCodec::kIncompleteInput;
305                     }
306                 }
307                 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
308             }
309             return SkCodec::kSuccess;
310         }
311         case SkCodec::kBottomUp_SkScanlineOrder: {
312             // Note that these modes do not support subsetting.
313             SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight);
314             int y;
315             for (y = 0; y < nativeSize.height(); y++) {
316                 int srcY = this->codec()->nextScanline();
317                 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
318                     void* pixelPtr = SkTAddOffset<void>(pixels,
319                             rowBytes * get_dst_coord(srcY, sampleY));
320                     if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
321                         break;
322                     }
323                 } else {
324                     if (!this->codec()->skipScanlines(1)) {
325                         break;
326                     }
327                 }
328             }
329 
330             if (nativeSize.height() == y) {
331                 return SkCodec::kSuccess;
332             }
333 
334             // We handle filling uninitialized memory here instead of using this->codec().
335             // this->codec() does not know that we are sampling.
336             const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
337             for (; y < nativeSize.height(); y++) {
338                 int srcY = this->codec()->outputScanline(y);
339                 if (!is_coord_necessary(srcY, sampleY, dstHeight)) {
340                     continue;
341                 }
342 
343                 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY));
344                 SkSampler::Fill(fillInfo, rowPtr, rowBytes, options.fZeroInitialized);
345             }
346             return SkCodec::kIncompleteInput;
347         }
348         default:
349             SkASSERT(false);
350             return SkCodec::kUnimplemented;
351     }
352 }
353