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