• 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 "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 = &subset;
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