blob: ed4eb7f02de94cab2b411291805e06d4fdf703b4 [file] [log] [blame]
emmaleer8f4ba762015-08-14 07:44:46 -07001/*
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
msarett3d9d7a72015-10-21 10:27:10 -07008#include "SkCodec.h"
emmaleer8f4ba762015-08-14 07:44:46 -07009#include "SkCodecPriv.h"
scroggo501b7342015-11-03 07:55:11 -080010#include "SkMath.h"
msarett3d9d7a72015-10-21 10:27:10 -070011#include "SkSampledCodec.h"
emmaleer8f4ba762015-08-14 07:44:46 -070012
msarett3d9d7a72015-10-21 10:27:10 -070013SkSampledCodec::SkSampledCodec(SkCodec* codec)
14 : INHERITED(codec->getInfo())
scroggo46c57472015-09-30 08:57:13 -070015 , fCodec(codec)
emmaleer8f4ba762015-08-14 07:44:46 -070016{}
17
scroggo501b7342015-11-03 07:55:11 -080018SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const {
19 SkISize preSampledSize = fCodec->getInfo().dimensions();
20 int sampleSize = *sampleSizePtr;
21 SkASSERT(sampleSize > 1);
22
23 if (nativeSampleSize) {
24 *nativeSampleSize = 1;
25 }
26
27 // Only JPEG supports native downsampling.
28 if (fCodec->getEncodedFormat() == kJPEG_SkEncodedFormat) {
29 // See if libjpeg supports this scale directly
30 switch (sampleSize) {
31 case 2:
32 case 4:
33 case 8:
34 // This class does not need to do any sampling.
35 *sampleSizePtr = 1;
36 return fCodec->getScaledDimensions(get_scale_from_sample_size(sampleSize));
37 default:
38 break;
39 }
40
41 // Check if sampleSize is a multiple of something libjpeg can support.
42 int remainder;
43 const int sampleSizes[] = { 8, 4, 2 };
44 for (int supportedSampleSize : sampleSizes) {
45 int actualSampleSize;
46 SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder);
47 if (0 == remainder) {
48 float scale = get_scale_from_sample_size(supportedSampleSize);
49
50 // fCodec will scale to this size.
51 preSampledSize = fCodec->getScaledDimensions(scale);
52
53 // And then this class will sample it.
54 *sampleSizePtr = actualSampleSize;
55 if (nativeSampleSize) {
56 *nativeSampleSize = supportedSampleSize;
57 }
58 break;
59 }
60 }
61 }
62
63 return preSampledSize;
64}
65
msarett3d9d7a72015-10-21 10:27:10 -070066SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
scroggo501b7342015-11-03 07:55:11 -080067 const SkISize size = this->accountForNativeScaling(&sampleSize);
68 return SkISize::Make(get_scaled_dimension(size.width(), sampleSize),
69 get_scaled_dimension(size.height(), sampleSize));
msaretta83593b2015-08-18 08:03:58 -070070}
emmaleer8f4ba762015-08-14 07:44:46 -070071
msarett3d9d7a72015-10-21 10:27:10 -070072SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
73 size_t rowBytes, AndroidOptions& options) {
74 // Create an Options struct for the codec.
75 SkCodec::Options codecOptions;
76 codecOptions.fZeroInitialized = options.fZeroInitialized;
emmaleer8f4ba762015-08-14 07:44:46 -070077
msarett3d9d7a72015-10-21 10:27:10 -070078 SkIRect* subset = options.fSubset;
79 if (!subset || subset->size() == fCodec->getInfo().dimensions()) {
80 if (fCodec->dimensionsSupported(info.dimensions())) {
81 return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr,
82 options.fColorCount);
emmaleer8f4ba762015-08-14 07:44:46 -070083 }
emmaleer8f4ba762015-08-14 07:44:46 -070084
msarett3d9d7a72015-10-21 10:27:10 -070085 // If the native codec does not support the requested scale, scale by sampling.
86 return this->sampledDecode(info, pixels, rowBytes, options);
scroggoe7fc14b2015-10-02 13:14:46 -070087 }
88
msarett3d9d7a72015-10-21 10:27:10 -070089 // We are performing a subset decode.
90 int sampleSize = options.fSampleSize;
scroggo501b7342015-11-03 07:55:11 -080091 SkISize scaledSize = this->getSampledDimensions(sampleSize);
msarett3d9d7a72015-10-21 10:27:10 -070092 if (!fCodec->dimensionsSupported(scaledSize)) {
93 // If the native codec does not support the requested scale, scale by sampling.
94 return this->sampledDecode(info, pixels, rowBytes, options);
95 }
scroggoe7fc14b2015-10-02 13:14:46 -070096
msarett3d9d7a72015-10-21 10:27:10 -070097 // Calculate the scaled subset bounds.
98 int scaledSubsetX = subset->x() / sampleSize;
99 int scaledSubsetY = subset->y() / sampleSize;
100 int scaledSubsetWidth = info.width();
101 int scaledSubsetHeight = info.height();
emmaleer8f4ba762015-08-14 07:44:46 -0700102
msarett3d9d7a72015-10-21 10:27:10 -0700103 // Start the scanline decode.
104 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
105 scaledSize.height());
106 codecOptions.fSubset = &scanlineSubset;
107 SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize.width(),
108 scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount);
109 if (SkCodec::kSuccess != result) {
110 return result;
111 }
emmaleer8f4ba762015-08-14 07:44:46 -0700112
msarett3d9d7a72015-10-21 10:27:10 -0700113 // At this point, we are only concerned with subsetting. Either no scale was
114 // requested, or the fCodec is handling the scale.
115 switch (fCodec->getScanlineOrder()) {
116 case SkCodec::kTopDown_SkScanlineOrder:
117 case SkCodec::kNone_SkScanlineOrder: {
118 if (!fCodec->skipScanlines(scaledSubsetY)) {
119 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
120 scaledSubsetHeight, 0);
121 return SkCodec::kIncompleteInput;
emmaleer8f4ba762015-08-14 07:44:46 -0700122 }
emmaleer8f4ba762015-08-14 07:44:46 -0700123
msarett3d9d7a72015-10-21 10:27:10 -0700124 int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight, rowBytes);
125 if (decodedLines != scaledSubsetHeight) {
126 return SkCodec::kIncompleteInput;
127 }
128 return SkCodec::kSuccess;
129 }
130 default:
131 SkASSERT(false);
132 return SkCodec::kUnimplemented;
emmaleer8f4ba762015-08-14 07:44:46 -0700133 }
134}
135
emmaleer8f4ba762015-08-14 07:44:46 -0700136
msarett3d9d7a72015-10-21 10:27:10 -0700137SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
138 size_t rowBytes, AndroidOptions& options) {
scroggo501b7342015-11-03 07:55:11 -0800139 // We should only call this function when sampling.
140 SkASSERT(options.fSampleSize > 1);
141
msarett3d9d7a72015-10-21 10:27:10 -0700142 // Create options struct for the codec.
143 SkCodec::Options sampledOptions;
144 sampledOptions.fZeroInitialized = options.fZeroInitialized;
emmaleer8f4ba762015-08-14 07:44:46 -0700145
scroggo501b7342015-11-03 07:55:11 -0800146 // FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
147 int sampleSize = options.fSampleSize;
148 int nativeSampleSize;
149 SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
150
msarett3d9d7a72015-10-21 10:27:10 -0700151 // Check if there is a subset.
152 SkIRect subset;
153 int subsetY = 0;
scroggo501b7342015-11-03 07:55:11 -0800154 int subsetWidth = nativeSize.width();
155 int subsetHeight = nativeSize.height();
emmaleer8f4ba762015-08-14 07:44:46 -0700156 if (options.fSubset) {
msarett3d9d7a72015-10-21 10:27:10 -0700157 // We will need to know about subsetting in the y-dimension in order to use the
158 // scanline decoder.
scroggo501b7342015-11-03 07:55:11 -0800159 // Update the subset to account for scaling done by fCodec.
msarett3d9d7a72015-10-21 10:27:10 -0700160 SkIRect* subsetPtr = options.fSubset;
scroggo501b7342015-11-03 07:55:11 -0800161
162 // Do the divide ourselves, instead of calling get_scaled_dimension. If
163 // X and Y are 0, they should remain 0, rather than being upgraded to 1
164 // due to being smaller than the sampleSize.
165 const int subsetX = subsetPtr->x() / nativeSampleSize;
166 subsetY = subsetPtr->y() / nativeSampleSize;
167
168 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
169 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
msarett3d9d7a72015-10-21 10:27:10 -0700170
171 // The scanline decoder only needs to be aware of subsetting in the x-dimension.
scroggo501b7342015-11-03 07:55:11 -0800172 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
msarett3d9d7a72015-10-21 10:27:10 -0700173 sampledOptions.fSubset = ⊂
emmaleer8f4ba762015-08-14 07:44:46 -0700174 }
175
msarett3d9d7a72015-10-21 10:27:10 -0700176 // Start the scanline decode.
177 SkCodec::Result result = fCodec->startScanlineDecode(
scroggo501b7342015-11-03 07:55:11 -0800178 info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions,
msarett3d9d7a72015-10-21 10:27:10 -0700179 options.fColorPtr, options.fColorCount);
180 if (SkCodec::kSuccess != result) {
emmaleer8f4ba762015-08-14 07:44:46 -0700181 return result;
182 }
emmaleer8f4ba762015-08-14 07:44:46 -0700183
msarette6dd0042015-10-09 11:07:34 -0700184 SkSampler* sampler = fCodec->getSampler(true);
scroggoe7fc14b2015-10-02 13:14:46 -0700185 if (!sampler) {
msarett3d9d7a72015-10-21 10:27:10 -0700186 return SkCodec::kUnimplemented;
scroggoe7fc14b2015-10-02 13:14:46 -0700187 }
188
msarett3d9d7a72015-10-21 10:27:10 -0700189 // Since we guarantee that output dimensions are always at least one (even if the sampleSize
190 // is greater than a given dimension), the input sampleSize is not always the sampleSize that
191 // we use in practice.
192 const int sampleX = subsetWidth / info.width();
193 const int sampleY = subsetHeight / info.height();
194 if (sampler->setSampleX(sampleX) != info.width()) {
195 return SkCodec::kInvalidScale;
196 }
197 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
198 return SkCodec::kInvalidScale;
scroggoe7fc14b2015-10-02 13:14:46 -0700199 }
200
msarett3d9d7a72015-10-21 10:27:10 -0700201 const int samplingOffsetY = get_start_coord(sampleY);
202 const int startY = samplingOffsetY + subsetY;
203 int dstHeight = info.height();
scroggo46c57472015-09-30 08:57:13 -0700204 switch(fCodec->getScanlineOrder()) {
205 case SkCodec::kTopDown_SkScanlineOrder: {
msarett3d9d7a72015-10-21 10:27:10 -0700206 if (!fCodec->skipScanlines(startY)) {
207 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
208 dstHeight, 0);
209 return SkCodec::kIncompleteInput;
msarett5406d6f2015-08-31 06:55:13 -0700210 }
msarett3d9d7a72015-10-21 10:27:10 -0700211 void* pixelPtr = pixels;
msarett5406d6f2015-08-31 06:55:13 -0700212 for (int y = 0; y < dstHeight; y++) {
msarett3d9d7a72015-10-21 10:27:10 -0700213 if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) {
214 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
215 dstHeight, y + 1);
216 return SkCodec::kIncompleteInput;
msarett5406d6f2015-08-31 06:55:13 -0700217 }
msarett3d9d7a72015-10-21 10:27:10 -0700218 int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1);
219 if (!fCodec->skipScanlines(linesToSkip)) {
220 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
221 dstHeight, y + 1);
222 return SkCodec::kIncompleteInput;
msarett5406d6f2015-08-31 06:55:13 -0700223 }
msarett3d9d7a72015-10-21 10:27:10 -0700224 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
msarett5406d6f2015-08-31 06:55:13 -0700225 }
msarett3d9d7a72015-10-21 10:27:10 -0700226 return SkCodec::kSuccess;
emmaleer8f4ba762015-08-14 07:44:46 -0700227 }
scroggo46c57472015-09-30 08:57:13 -0700228 case SkCodec::kNone_SkScanlineOrder: {
msarett3d9d7a72015-10-21 10:27:10 -0700229 const int linesNeeded = subsetHeight - samplingOffsetY;
230 SkAutoMalloc storage(linesNeeded * rowBytes);
msarett5406d6f2015-08-31 06:55:13 -0700231 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
msarett3d9d7a72015-10-21 10:27:10 -0700232
233 if (!fCodec->skipScanlines(startY)) {
234 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
235 dstHeight, 0);
236 return SkCodec::kIncompleteInput;
237 }
238 int scanlines = fCodec->getScanlines(storagePtr, linesNeeded, rowBytes);
239
240 for (int y = 0; y < dstHeight; y++) {
241 memcpy(pixels, storagePtr, info.minRowBytes());
msarett5406d6f2015-08-31 06:55:13 -0700242 storagePtr += sampleY * rowBytes;
msarett3d9d7a72015-10-21 10:27:10 -0700243 pixels = SkTAddOffset<void>(pixels, rowBytes);
emmaleer8f4ba762015-08-14 07:44:46 -0700244 }
msarett3d9d7a72015-10-21 10:27:10 -0700245
246 if (scanlines < dstHeight) {
msarette6dd0042015-10-09 11:07:34 -0700247 // fCodec has already handled filling uninitialized memory.
msarett3d9d7a72015-10-21 10:27:10 -0700248 return SkCodec::kIncompleteInput;
msarette6dd0042015-10-09 11:07:34 -0700249 }
msarett3d9d7a72015-10-21 10:27:10 -0700250 return SkCodec::kSuccess;
emmaleer8f4ba762015-08-14 07:44:46 -0700251 }
msarett5406d6f2015-08-31 06:55:13 -0700252 default:
253 SkASSERT(false);
msarett3d9d7a72015-10-21 10:27:10 -0700254 return SkCodec::kUnimplemented;
emmaleer8f4ba762015-08-14 07:44:46 -0700255 }
emmaleer8f4ba762015-08-14 07:44:46 -0700256}