Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(220)

Side by Side Diff: src/codec/SkScaledCodec.cpp

Issue 1406223002: Create an SkAndroidCodec API separate from SkCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Win bot fix Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/codec/SkSampledCodec.h ('k') | src/codec/SkSwizzler.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkCodec.h"
8 #include "SkCodecPriv.h" 9 #include "SkCodecPriv.h"
9 #include "SkScaledCodec.h" 10 #include "SkSampledCodec.h"
10 #include "SkStream.h" 11
11 #include "SkWebpCodec.h" 12 // FIXME: Rename this file to SkSampledCodec.cpp
12 13
13 14 SkSampledCodec::SkSampledCodec(SkCodec* codec)
14 SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { 15 : INHERITED(codec->getInfo())
15 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream));
16 if (nullptr == codec) {
17 return nullptr;
18 }
19
20 switch (codec->getEncodedFormat()) {
21 case kWEBP_SkEncodedFormat:
22 // Webp codec supports scaling and subsetting natively
23 return codec.detach();
24 case kPNG_SkEncodedFormat:
25 case kJPEG_SkEncodedFormat:
26 // wrap in new SkScaledCodec
27 return new SkScaledCodec(codec.detach());
28 default:
29 // FIXME: SkScaledCodec is temporarily disabled for other formats
30 // while focusing on the formats that are supported by
31 // BitmapRegionDecoder.
32 return nullptr;
33 }
34 }
35
36 SkCodec* SkScaledCodec::NewFromData(SkData* data) {
37 if (!data) {
38 return nullptr;
39 }
40 return NewFromStream(new SkMemoryStream(data));
41 }
42
43 SkScaledCodec::SkScaledCodec(SkCodec* codec)
44 : INHERITED(codec->getInfo(), nullptr)
45 , fCodec(codec) 16 , fCodec(codec)
46 {} 17 {}
47 18
48 SkScaledCodec::~SkScaledCodec() {} 19 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
49 20 // Fast path for when we are not scaling.
50 bool SkScaledCodec::onRewind() { 21 if (1 == sampleSize) {
51 return fCodec->onRewind(); 22 return fCodec->getInfo().dimensions();
52 } 23 }
53 24
54 static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& na tiveDims, 25 const int width = fCodec->getInfo().width();
55 const SkISize& scaledCodecDims, float desi redScale) { 26 const int height = fCodec->getInfo().height();
56 if (nativeDims == scaledCodecDims) { 27
57 // does not matter which to return if equal. Return here to skip below c alculations 28 // Check if the codec can provide the scaling natively.
58 return nativeDims; 29 float scale = get_scale_from_sample_size(sampleSize);
59 } 30 SkSize idealSize = SkSize::Make(scale * (float) width, scale * (float) heigh t);
60 float idealWidth = origDims.width() * desiredScale; 31 SkISize nativeSize = fCodec->getScaledDimensions(scale);
61 float idealHeight = origDims.height() * desiredScale; 32 float widthDiff = SkTAbs(((float) nativeSize.width()) - idealSize.width());
62 33 float heightDiff = SkTAbs(((float) nativeSize.height()) - idealSize.height() );
63 // calculate difference between native dimensions and ideal dimensions
64 float nativeWDiff = SkTAbs(idealWidth - nativeDims.width());
65 float nativeHDiff = SkTAbs(idealHeight - nativeDims.height());
66 float nativeDiff = nativeWDiff + nativeHDiff;
67
68 // Native scaling is preferred to sampling. If we can scale natively to 34 // Native scaling is preferred to sampling. If we can scale natively to
69 // within one of the ideal value, we should choose to scale natively. 35 // within one of the ideal value, we should choose to scale natively.
70 if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) { 36 if (widthDiff < 1.0f && heightDiff < 1.0f) {
71 return nativeDims; 37 return nativeSize;
72 } 38 }
73 39
74 // calculate difference between scaledCodec dimensions and ideal dimensions 40 // Provide the scaling by sampling.
75 float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); 41 return SkISize::Make(get_scaled_dimension(width, sampleSize),
76 float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); 42 get_scaled_dimension(height, sampleSize));
77 float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff;
78
79 // return dimensions closest to ideal dimensions.
80 // If the differences are equal, return nativeDims, as native scaling is mor e efficient.
81 return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims;
82 } 43 }
83 44
84 /* 45 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void * pixels,
85 * Return a valid set of output dimensions for this decoder, given an input scal e 46 size_t rowBytes, AndroidOptions& options) {
86 */ 47 // Create an Options struct for the codec.
87 SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { 48 SkCodec::Options codecOptions;
88 SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale); 49 codecOptions.fZeroInitialized = options.fZeroInitialized;
89 // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ... 50
90 SkISize scaledCodecDimensions; 51 SkIRect* subset = options.fSubset;
91 if (desiredScale > 0.5f) { 52 if (!subset || subset->size() == fCodec->getInfo().dimensions()) {
92 // sampleSize = 1 53 if (fCodec->dimensionsSupported(info.dimensions())) {
93 scaledCodecDimensions = fCodec->getInfo().dimensions(); 54 return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, opti ons.fColorPtr,
94 } 55 options.fColorCount);
95 // sampleSize determines the step size between samples 56 }
96 // Ex: sampleSize = 2, sample every second pixel in x and y directions 57
97 int sampleSize = int ((1.0f / desiredScale) + 0.5f); 58 // If the native codec does not support the requested scale, scale by sa mpling.
98 59 return this->sampledDecode(info, pixels, rowBytes, options);
99 int scaledWidth = get_scaled_dimension(this->getInfo().width(), sampleSize); 60 }
100 int scaledHeight = get_scaled_dimension(this->getInfo().height(), sampleSize ); 61
101 62 // We are performing a subset decode.
102 // Return the calculated output dimensions for the given scale 63 int sampleSize = options.fSampleSize;
103 scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight); 64 SkISize scaledSize = this->onGetSampledDimensions(sampleSize);
104 65 if (!fCodec->dimensionsSupported(scaledSize)) {
105 return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions , 66 // If the native codec does not support the requested scale, scale by sa mpling.
106 scaledCodecDimensions, desiredScale); 67 return this->sampledDecode(info, pixels, rowBytes, options);
68 }
69
70 // Calculate the scaled subset bounds.
71 int scaledSubsetX = subset->x() / sampleSize;
72 int scaledSubsetY = subset->y() / sampleSize;
73 int scaledSubsetWidth = info.width();
74 int scaledSubsetHeight = info.height();
75
76 // Start the scanline decode.
77 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWid th,
78 scaledSize.height());
79 codecOptions.fSubset = &scanlineSubset;
80 SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize. width(),
81 scaledSize.height()), &codecOptions, options.fColorPtr, options.fCol orCount);
82 if (SkCodec::kSuccess != result) {
83 return result;
84 }
85
86 // At this point, we are only concerned with subsetting. Either no scale wa s
87 // requested, or the fCodec is handling the scale.
88 switch (fCodec->getScanlineOrder()) {
89 case SkCodec::kTopDown_SkScanlineOrder:
90 case SkCodec::kNone_SkScanlineOrder: {
91 if (!fCodec->skipScanlines(scaledSubsetY)) {
92 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer oInitialized,
93 scaledSubsetHeight, 0);
94 return SkCodec::kIncompleteInput;
95 }
96
97 int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight, rowBytes);
98 if (decodedLines != scaledSubsetHeight) {
99 return SkCodec::kIncompleteInput;
100 }
101 return SkCodec::kSuccess;
102 }
103 default:
104 SkASSERT(false);
105 return SkCodec::kUnimplemented;
106 }
107 } 107 }
108 108
109 // check if scaling to dstInfo size from srcInfo size using sampleSize is possib le 109
110 static bool scaling_supported(const SkISize& dstDim, const SkISize& srcDim, 110 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix els,
111 int* sampleX, int* sampleY) { 111 size_t rowBytes, AndroidOptions& options) {
112 SkScaledCodec::ComputeSampleSize(dstDim, srcDim, sampleX, sampleY); 112 // Create options struct for the codec.
113 const int dstWidth = dstDim.width(); 113 SkCodec::Options sampledOptions;
114 const int dstHeight = dstDim.height(); 114 sampledOptions.fZeroInitialized = options.fZeroInitialized;
115 const int srcWidth = srcDim.width(); 115
116 const int srcHeight = srcDim.height(); 116 // Check if there is a subset.
117 // only support down sampling, not up sampling 117 SkIRect subset;
118 if (dstWidth > srcWidth || dstHeight > srcHeight) { 118 int subsetY = 0;
119 return false; 119 int subsetWidth = fCodec->getInfo().width();
120 } 120 int subsetHeight = fCodec->getInfo().height();
121 // check that srcWidth is scaled down by an integer value
122 if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) {
123 return false;
124 }
125 // check that src height is scaled down by an integer value
126 if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) {
127 return false;
128 }
129 // sampleX and sampleY should be equal unless the original sampleSize reques ted was larger
130 // than srcWidth or srcHeight. If so, the result of this is dstWidth or dstH eight = 1.
131 // This functionality allows for tall thin images to still be scaled down by scaling factors.
132 if (*sampleX != *sampleY){
133 if (1 != dstWidth && 1 != dstHeight) {
134 return false;
135 }
136 }
137 return true;
138 }
139
140 bool SkScaledCodec::onDimensionsSupported(const SkISize& dim) {
141 // Check with fCodec first. No need to call the non-virtual version, which
142 // just checks if it matches the original, since a match means this method
143 // will not be called.
144 if (fCodec->onDimensionsSupported(dim)) {
145 return true;
146 }
147
148 // FIXME: These variables are unused, but are needed by scaling_supported.
149 // This class could also cache these values, and avoid calling this in
150 // onGetPixels (since getPixels already calls it).
151 int sampleX;
152 int sampleY;
153 return scaling_supported(dim, this->getInfo().dimensions(), &sampleX, &sampl eY);
154 }
155
156 // calculates sampleSize in x and y direction
157 void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcD im,
158 int* sampleXPtr, int* sampleYPtr) {
159 int srcWidth = srcDim.width();
160 int dstWidth = dstDim.width();
161 int srcHeight = srcDim.height();
162 int dstHeight = dstDim.height();
163
164 int sampleX = srcWidth / dstWidth;
165 int sampleY = srcHeight / dstHeight;
166
167 // only support down sampling, not up sampling
168 SkASSERT(dstWidth <= srcWidth);
169 SkASSERT(dstHeight <= srcHeight);
170
171 // sampleX and sampleY should be equal unless the original sampleSize reques ted was
172 // larger than srcWidth or srcHeight.
173 // If so, the result of this is dstWidth or dstHeight = 1. This functionalit y
174 // allows for tall thin images to still be scaled down by scaling factors.
175
176 if (sampleX != sampleY){
177 if (1 != dstWidth && 1 != dstHeight) {
178
179 // rounding during onGetScaledDimensions can cause different sampleS izes
180 // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10
181 // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10
182 // correct for this rounding by comparing width to sampleY and heigh t to sampleX
183
184 if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) {
185 sampleX = sampleY;
186 } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) {
187 sampleY = sampleX;
188 }
189 }
190 }
191
192 if (sampleXPtr) {
193 *sampleXPtr = sampleX;
194 }
195 if (sampleYPtr) {
196 *sampleYPtr = sampleY;
197 }
198 }
199
200 // TODO: Implement subsetting in onGetPixels which works when and when not sampl ing
201
202 SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi d* dst,
203 size_t rowBytes, const Options& optio ns,
204 SkPMColor ctable[], int* ctableCount,
205 int* rowsDecoded) {
206
207 if (options.fSubset) { 121 if (options.fSubset) {
208 // Subsets are not supported. 122 // We will need to know about subsetting in the y-dimension in order to use the
209 return kUnimplemented; 123 // scanline decoder.
210 } 124 SkIRect* subsetPtr = options.fSubset;
211 125 subsetY = subsetPtr->y();
212 if (fCodec->dimensionsSupported(requestedInfo.dimensions())) { 126 subsetWidth = subsetPtr->width();
213 // Make sure that the parent class does not fill on an incomplete decode , since 127 subsetHeight = subsetPtr->height();
214 // fCodec will take care of filling the uninitialized lines. 128
215 *rowsDecoded = requestedInfo.height(); 129 // The scanline decoder only needs to be aware of subsetting in the x-di mension.
216 return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount); 130 subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height( ));
217 } 131 sampledOptions.fSubset = &subset;
218 132 }
219 // scaling requested 133
220 int sampleX; 134 // Start the scanline decode.
221 int sampleY; 135 SkCodec::Result result = fCodec->startScanlineDecode(
222 if (!scaling_supported(requestedInfo.dimensions(), fCodec->getInfo().dimensi ons(), 136 info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()), &sampledOptions,
223 &sampleX, &sampleY)) { 137 options.fColorPtr, options.fColorCount);
224 // onDimensionsSupported would have returned false, meaning we should ne ver reach here. 138 if (SkCodec::kSuccess != result) {
225 SkASSERT(false);
226 return kInvalidScale;
227 }
228
229 // set first sample pixel in y direction
230 const int Y0 = get_start_coord(sampleY);
231
232 const int dstHeight = requestedInfo.height();
233 const int srcWidth = fCodec->getInfo().width();
234 const int srcHeight = fCodec->getInfo().height();
235
236 const SkImageInfo info = requestedInfo.makeWH(srcWidth, srcHeight);
237
238 Result result = fCodec->startScanlineDecode(info, &options, ctable, ctableCo unt);
239
240 if (kSuccess != result) {
241 return result; 139 return result;
242 } 140 }
243 141
244 SkSampler* sampler = fCodec->getSampler(true); 142 SkSampler* sampler = fCodec->getSampler(true);
245 if (!sampler) { 143 if (!sampler) {
246 return kUnimplemented; 144 return SkCodec::kUnimplemented;
247 } 145 }
248 146
249 if (sampler->setSampleX(sampleX) != requestedInfo.width()) { 147 // Since we guarantee that output dimensions are always at least one (even i f the sampleSize
250 return kInvalidScale; 148 // is greater than a given dimension), the input sampleSize is not always th e sampleSize that
251 } 149 // we use in practice.
252 150 const int sampleX = subsetWidth / info.width();
151 const int sampleY = subsetHeight / info.height();
152 if (sampler->setSampleX(sampleX) != info.width()) {
153 return SkCodec::kInvalidScale;
154 }
155 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
156 return SkCodec::kInvalidScale;
157 }
158
159 const int samplingOffsetY = get_start_coord(sampleY);
160 const int startY = samplingOffsetY + subsetY;
161 int dstHeight = info.height();
253 switch(fCodec->getScanlineOrder()) { 162 switch(fCodec->getScanlineOrder()) {
254 case SkCodec::kTopDown_SkScanlineOrder: { 163 case SkCodec::kTopDown_SkScanlineOrder: {
255 if (!fCodec->skipScanlines(Y0)) { 164 if (!fCodec->skipScanlines(startY)) {
256 *rowsDecoded = 0; 165 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer oInitialized,
257 return kIncompleteInput; 166 dstHeight, 0);
258 } 167 return SkCodec::kIncompleteInput;
168 }
169 void* pixelPtr = pixels;
259 for (int y = 0; y < dstHeight; y++) { 170 for (int y = 0; y < dstHeight; y++) {
260 if (1 != fCodec->getScanlines(dst, 1, rowBytes)) { 171 if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) {
261 // The failed call to getScanlines() will take care of 172 fCodec->fillIncompleteImage(info, pixels, rowBytes, options. fZeroInitialized,
262 // filling the failed row, so we indicate that we have 173 dstHeight, y + 1);
263 // decoded (y + 1) rows. 174 return SkCodec::kIncompleteInput;
264 *rowsDecoded = y + 1;
265 return kIncompleteInput;
266 } 175 }
267 if (y < dstHeight - 1) { 176 int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1);
268 if (!fCodec->skipScanlines(sampleY - 1)) { 177 if (!fCodec->skipScanlines(linesToSkip)) {
269 *rowsDecoded = y + 1; 178 fCodec->fillIncompleteImage(info, pixels, rowBytes, options. fZeroInitialized,
270 return kIncompleteInput; 179 dstHeight, y + 1);
271 } 180 return SkCodec::kIncompleteInput;
272 } 181 }
273 dst = SkTAddOffset<void>(dst, rowBytes); 182 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
274 } 183 }
275 return kSuccess; 184 return SkCodec::kSuccess;
276 }
277 case SkCodec::kBottomUp_SkScanlineOrder:
278 case SkCodec::kOutOfOrder_SkScanlineOrder: {
279 Result result = kSuccess;
280 int y;
281 for (y = 0; y < srcHeight; y++) {
282 int srcY = fCodec->nextScanline();
283 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
284 void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_co ord(srcY, sampleY));
285 if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) {
286 result = kIncompleteInput;
287 break;
288 }
289 } else {
290 if (!fCodec->skipScanlines(1)) {
291 result = kIncompleteInput;
292 break;
293 }
294 }
295 }
296
297 // We handle filling uninitialized memory here instead of in the par ent class.
298 // The parent class does not know that we are sampling.
299 if (kIncompleteInput == result) {
300 const uint32_t fillValue = fCodec->getFillValue(requestedInfo.co lorType(),
301 requestedInfo.alphaType());
302 for (; y < srcHeight; y++) {
303 int srcY = fCodec->outputScanline(y);
304 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
305 void* dstRow = SkTAddOffset<void>(dst,
306 rowBytes * get_dst_coord(srcY, sampleY));
307 SkSampler::Fill(requestedInfo.makeWH(requestedInfo.width (), 1), dstRow,
308 rowBytes, fillValue, options.fZeroInitialized);
309 }
310 }
311 *rowsDecoded = dstHeight;
312 }
313 return result;
314 } 185 }
315 case SkCodec::kNone_SkScanlineOrder: { 186 case SkCodec::kNone_SkScanlineOrder: {
316 SkAutoMalloc storage(srcHeight * rowBytes); 187 const int linesNeeded = subsetHeight - samplingOffsetY;
188 SkAutoMalloc storage(linesNeeded * rowBytes);
317 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); 189 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
318 int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes ); 190
319 storagePtr += Y0 * rowBytes; 191 if (!fCodec->skipScanlines(startY)) {
320 scanlines -= Y0; 192 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer oInitialized,
321 int y = 0; 193 dstHeight, 0);
322 while (y < dstHeight && scanlines > 0) { 194 return SkCodec::kIncompleteInput;
323 memcpy(dst, storagePtr, rowBytes); 195 }
196 int scanlines = fCodec->getScanlines(storagePtr, linesNeeded, rowByt es);
197
198 for (int y = 0; y < dstHeight; y++) {
199 memcpy(pixels, storagePtr, info.minRowBytes());
324 storagePtr += sampleY * rowBytes; 200 storagePtr += sampleY * rowBytes;
325 dst = SkTAddOffset<void>(dst, rowBytes); 201 pixels = SkTAddOffset<void>(pixels, rowBytes);
326 scanlines -= sampleY; 202 }
327 y++; 203
328 } 204 if (scanlines < dstHeight) {
329 if (y < dstHeight) {
330 // fCodec has already handled filling uninitialized memory. 205 // fCodec has already handled filling uninitialized memory.
331 *rowsDecoded = dstHeight; 206 return SkCodec::kIncompleteInput;
332 return kIncompleteInput; 207 }
333 } 208 return SkCodec::kSuccess;
334 return kSuccess;
335 } 209 }
336 default: 210 default:
337 SkASSERT(false); 211 SkASSERT(false);
338 return kUnimplemented; 212 return SkCodec::kUnimplemented;
339 } 213 }
340 } 214 }
341
342 uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaT ype) const {
343 return fCodec->onGetFillValue(colorType, alphaType);
344 }
345
346 SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const {
347 return fCodec->onGetScanlineOrder();
348 }
OLDNEW
« no previous file with comments | « src/codec/SkSampledCodec.h ('k') | src/codec/SkSwizzler.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698