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