• 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 "SkSampledCodec.h"
12 #include "SkSampler.h"
13 #include "SkTemplates.h"
14 
SkSampledCodec(SkCodec * codec)15 SkSampledCodec::SkSampledCodec(SkCodec* codec)
16     : INHERITED(codec)
17 {}
18 
accountForNativeScaling(int * sampleSizePtr,int * nativeSampleSize) const19 SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const {
20     SkISize preSampledSize = this->codec()->getInfo().dimensions();
21     int sampleSize = *sampleSizePtr;
22     SkASSERT(sampleSize > 1);
23 
24     if (nativeSampleSize) {
25         *nativeSampleSize = 1;
26     }
27 
28     // Only JPEG supports native downsampling.
29     if (this->codec()->getEncodedFormat() == kJPEG_SkEncodedFormat) {
30         // See if libjpeg supports this scale directly
31         switch (sampleSize) {
32             case 2:
33             case 4:
34             case 8:
35                 // This class does not need to do any sampling.
36                 *sampleSizePtr = 1;
37                 return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize));
38             default:
39                 break;
40         }
41 
42         // Check if sampleSize is a multiple of something libjpeg can support.
43         int remainder;
44         const int sampleSizes[] = { 8, 4, 2 };
45         for (int supportedSampleSize : sampleSizes) {
46             int actualSampleSize;
47             SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder);
48             if (0 == remainder) {
49                 float scale = get_scale_from_sample_size(supportedSampleSize);
50 
51                 // this->codec() will scale to this size.
52                 preSampledSize = this->codec()->getScaledDimensions(scale);
53 
54                 // And then this class will sample it.
55                 *sampleSizePtr = actualSampleSize;
56                 if (nativeSampleSize) {
57                     *nativeSampleSize = supportedSampleSize;
58                 }
59                 break;
60             }
61         }
62     }
63 
64     return preSampledSize;
65 }
66 
onGetSampledDimensions(int sampleSize) const67 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
68     const SkISize size = this->accountForNativeScaling(&sampleSize);
69     return SkISize::Make(get_scaled_dimension(size.width(), sampleSize),
70                          get_scaled_dimension(size.height(), sampleSize));
71 }
72 
onGetAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions & options)73 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
74         size_t rowBytes, const AndroidOptions& options) {
75     // Create an Options struct for the codec.
76     SkCodec::Options codecOptions;
77     codecOptions.fZeroInitialized = options.fZeroInitialized;
78 
79     SkIRect* subset = options.fSubset;
80     if (!subset || subset->size() == this->codec()->getInfo().dimensions()) {
81         if (this->codec()->dimensionsSupported(info.dimensions())) {
82             return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions,
83                     options.fColorPtr, options.fColorCount);
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     // Start the scanline decode.
105     SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
106             scaledSize.height());
107     codecOptions.fSubset = &scanlineSubset;
108     SkCodec::Result result = this->codec()->startScanlineDecode(info.makeWH(scaledSize.width(),
109             scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount);
110     if (SkCodec::kSuccess != result) {
111         return result;
112     }
113 
114     // At this point, we are only concerned with subsetting.  Either no scale was
115     // requested, or the this->codec() is handling the scale.
116     switch (this->codec()->getScanlineOrder()) {
117         case SkCodec::kTopDown_SkScanlineOrder:
118         case SkCodec::kNone_SkScanlineOrder: {
119             if (!this->codec()->skipScanlines(scaledSubsetY)) {
120                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
121                         scaledSubsetHeight, 0);
122                 return SkCodec::kIncompleteInput;
123             }
124 
125             int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
126             if (decodedLines != scaledSubsetHeight) {
127                 return SkCodec::kIncompleteInput;
128             }
129             return SkCodec::kSuccess;
130         }
131         default:
132             SkASSERT(false);
133             return SkCodec::kUnimplemented;
134     }
135 }
136 
137 
sampledDecode(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions & options)138 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
139         size_t rowBytes, const AndroidOptions& options) {
140     // We should only call this function when sampling.
141     SkASSERT(options.fSampleSize > 1);
142 
143     // Create options struct for the codec.
144     SkCodec::Options sampledOptions;
145     sampledOptions.fZeroInitialized = options.fZeroInitialized;
146 
147     // FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
148     int sampleSize = options.fSampleSize;
149     int nativeSampleSize;
150     SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
151 
152     // Check if there is a subset.
153     SkIRect subset;
154     int subsetY = 0;
155     int subsetWidth = nativeSize.width();
156     int subsetHeight = nativeSize.height();
157     if (options.fSubset) {
158         // We will need to know about subsetting in the y-dimension in order to use the
159         // scanline decoder.
160         // Update the subset to account for scaling done by this->codec().
161         SkIRect* subsetPtr = options.fSubset;
162 
163         // Do the divide ourselves, instead of calling get_scaled_dimension. If
164         // X and Y are 0, they should remain 0, rather than being upgraded to 1
165         // due to being smaller than the sampleSize.
166         const int subsetX = subsetPtr->x() / nativeSampleSize;
167         subsetY = subsetPtr->y() / nativeSampleSize;
168 
169         subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
170         subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
171 
172         // The scanline decoder only needs to be aware of subsetting in the x-dimension.
173         subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
174         sampledOptions.fSubset = ⊂
175     }
176 
177     // Start the scanline decode.
178     SkCodec::Result result = this->codec()->startScanlineDecode(
179             info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions,
180             options.fColorPtr, options.fColorCount);
181     if (SkCodec::kSuccess != result) {
182         return result;
183     }
184 
185     SkSampler* sampler = this->codec()->getSampler(true);
186     if (!sampler) {
187         return SkCodec::kUnimplemented;
188     }
189 
190     // Since we guarantee that output dimensions are always at least one (even if the sampleSize
191     // is greater than a given dimension), the input sampleSize is not always the sampleSize that
192     // we use in practice.
193     const int sampleX = subsetWidth / info.width();
194     const int sampleY = subsetHeight / info.height();
195     if (sampler->setSampleX(sampleX) != info.width()) {
196         return SkCodec::kInvalidScale;
197     }
198     if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
199         return SkCodec::kInvalidScale;
200     }
201 
202     const int samplingOffsetY = get_start_coord(sampleY);
203     const int startY = samplingOffsetY + subsetY;
204     int dstHeight = info.height();
205     switch(this->codec()->getScanlineOrder()) {
206         case SkCodec::kTopDown_SkScanlineOrder: {
207             if (!this->codec()->skipScanlines(startY)) {
208                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
209                         dstHeight, 0);
210                 return SkCodec::kIncompleteInput;
211             }
212             void* pixelPtr = pixels;
213             for (int y = 0; y < dstHeight; y++) {
214                 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
215                     this->codec()->fillIncompleteImage(info, pixels, rowBytes,
216                             options.fZeroInitialized, dstHeight, y + 1);
217                     return SkCodec::kIncompleteInput;
218                 }
219                 if (y < dstHeight - 1) {
220                     if (!this->codec()->skipScanlines(sampleY - 1)) {
221                         this->codec()->fillIncompleteImage(info, pixels, rowBytes,
222                                 options.fZeroInitialized, dstHeight, y + 1);
223                         return SkCodec::kIncompleteInput;
224                     }
225                 }
226                 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
227             }
228             return SkCodec::kSuccess;
229         }
230         case SkCodec::kOutOfOrder_SkScanlineOrder:
231         case SkCodec::kBottomUp_SkScanlineOrder: {
232             // Note that these modes do not support subsetting.
233             SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight);
234             int y;
235             for (y = 0; y < nativeSize.height(); y++) {
236                 int srcY = this->codec()->nextScanline();
237                 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
238                     void* pixelPtr = SkTAddOffset<void>(pixels,
239                             rowBytes * get_dst_coord(srcY, sampleY));
240                     if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
241                         break;
242                     }
243                 } else {
244                     if (!this->codec()->skipScanlines(1)) {
245                         break;
246                     }
247                 }
248             }
249 
250             if (nativeSize.height() == y) {
251                 return SkCodec::kSuccess;
252             }
253 
254             // We handle filling uninitialized memory here instead of using this->codec().
255             // this->codec() does not know that we are sampling.
256             const uint32_t fillValue = this->codec()->getFillValue(info.colorType());
257             const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
258             for (; y < nativeSize.height(); y++) {
259                 int srcY = this->codec()->outputScanline(y);
260                 if (!is_coord_necessary(srcY, sampleY, dstHeight)) {
261                     continue;
262                 }
263 
264                 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY));
265                 SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.fZeroInitialized);
266             }
267             return SkCodec::kIncompleteInput;
268         }
269         case SkCodec::kNone_SkScanlineOrder: {
270             const int linesNeeded = subsetHeight - samplingOffsetY;
271             SkAutoTMalloc<uint8_t> storage(linesNeeded * rowBytes);
272             uint8_t* storagePtr = storage.get();
273 
274             if (!this->codec()->skipScanlines(startY)) {
275                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
276                         dstHeight, 0);
277                 return SkCodec::kIncompleteInput;
278             }
279             int scanlines = this->codec()->getScanlines(storagePtr, linesNeeded, rowBytes);
280 
281             for (int y = 0; y < dstHeight; y++) {
282                 memcpy(pixels, storagePtr, info.minRowBytes());
283                 storagePtr += sampleY * rowBytes;
284                 pixels = SkTAddOffset<void>(pixels, rowBytes);
285             }
286 
287             if (scanlines < dstHeight) {
288                 // this->codec() has already handled filling uninitialized memory.
289                 return SkCodec::kIncompleteInput;
290             }
291             return SkCodec::kSuccess;
292         }
293         default:
294             SkASSERT(false);
295             return SkCodec::kUnimplemented;
296     }
297 }
298