OLD | NEW |
| (Empty) |
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 "SkSampledCodec.h" | |
11 | |
12 // FIXME: Rename this file to SkSampledCodec.cpp | |
13 | |
14 SkSampledCodec::SkSampledCodec(SkCodec* codec) | |
15 : INHERITED(codec->getInfo()) | |
16 , fCodec(codec) | |
17 {} | |
18 | |
19 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { | |
20 // Fast path for when we are not scaling. | |
21 if (1 == sampleSize) { | |
22 return fCodec->getInfo().dimensions(); | |
23 } | |
24 | |
25 const int width = fCodec->getInfo().width(); | |
26 const int height = fCodec->getInfo().height(); | |
27 | |
28 // Check if the codec can provide the scaling natively. | |
29 float scale = get_scale_from_sample_size(sampleSize); | |
30 SkSize idealSize = SkSize::Make(scale * (float) width, scale * (float) heigh
t); | |
31 SkISize nativeSize = fCodec->getScaledDimensions(scale); | |
32 float widthDiff = SkTAbs(((float) nativeSize.width()) - idealSize.width()); | |
33 float heightDiff = SkTAbs(((float) nativeSize.height()) - idealSize.height()
); | |
34 // Native scaling is preferred to sampling. If we can scale natively to | |
35 // within one of the ideal value, we should choose to scale natively. | |
36 if (widthDiff < 1.0f && heightDiff < 1.0f) { | |
37 return nativeSize; | |
38 } | |
39 | |
40 // Provide the scaling by sampling. | |
41 return SkISize::Make(get_scaled_dimension(width, sampleSize), | |
42 get_scaled_dimension(height, sampleSize)); | |
43 } | |
44 | |
45 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void
* pixels, | |
46 size_t rowBytes, AndroidOptions& options) { | |
47 // Create an Options struct for the codec. | |
48 SkCodec::Options codecOptions; | |
49 codecOptions.fZeroInitialized = options.fZeroInitialized; | |
50 | |
51 SkIRect* subset = options.fSubset; | |
52 if (!subset || subset->size() == fCodec->getInfo().dimensions()) { | |
53 if (fCodec->dimensionsSupported(info.dimensions())) { | |
54 return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, opti
ons.fColorPtr, | |
55 options.fColorCount); | |
56 } | |
57 | |
58 // If the native codec does not support the requested scale, scale by sa
mpling. | |
59 return this->sampledDecode(info, pixels, rowBytes, options); | |
60 } | |
61 | |
62 // We are performing a subset decode. | |
63 int sampleSize = options.fSampleSize; | |
64 SkISize scaledSize = this->onGetSampledDimensions(sampleSize); | |
65 if (!fCodec->dimensionsSupported(scaledSize)) { | |
66 // If the native codec does not support the requested scale, scale by sa
mpling. | |
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 } | |
108 | |
109 | |
110 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
els, | |
111 size_t rowBytes, AndroidOptions& options) { | |
112 // Create options struct for the codec. | |
113 SkCodec::Options sampledOptions; | |
114 sampledOptions.fZeroInitialized = options.fZeroInitialized; | |
115 | |
116 // Check if there is a subset. | |
117 SkIRect subset; | |
118 int subsetY = 0; | |
119 int subsetWidth = fCodec->getInfo().width(); | |
120 int subsetHeight = fCodec->getInfo().height(); | |
121 if (options.fSubset) { | |
122 // We will need to know about subsetting in the y-dimension in order to
use the | |
123 // scanline decoder. | |
124 SkIRect* subsetPtr = options.fSubset; | |
125 subsetY = subsetPtr->y(); | |
126 subsetWidth = subsetPtr->width(); | |
127 subsetHeight = subsetPtr->height(); | |
128 | |
129 // The scanline decoder only needs to be aware of subsetting in the x-di
mension. | |
130 subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height(
)); | |
131 sampledOptions.fSubset = ⊂ | |
132 } | |
133 | |
134 // Start the scanline decode. | |
135 SkCodec::Result result = fCodec->startScanlineDecode( | |
136 info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()),
&sampledOptions, | |
137 options.fColorPtr, options.fColorCount); | |
138 if (SkCodec::kSuccess != result) { | |
139 return result; | |
140 } | |
141 | |
142 SkSampler* sampler = fCodec->getSampler(true); | |
143 if (!sampler) { | |
144 return SkCodec::kUnimplemented; | |
145 } | |
146 | |
147 // Since we guarantee that output dimensions are always at least one (even i
f the sampleSize | |
148 // is greater than a given dimension), the input sampleSize is not always th
e sampleSize that | |
149 // we use in practice. | |
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(); | |
162 switch(fCodec->getScanlineOrder()) { | |
163 case SkCodec::kTopDown_SkScanlineOrder: { | |
164 if (!fCodec->skipScanlines(startY)) { | |
165 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, | |
166 dstHeight, 0); | |
167 return SkCodec::kIncompleteInput; | |
168 } | |
169 void* pixelPtr = pixels; | |
170 for (int y = 0; y < dstHeight; y++) { | |
171 if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) { | |
172 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.
fZeroInitialized, | |
173 dstHeight, y + 1); | |
174 return SkCodec::kIncompleteInput; | |
175 } | |
176 int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1); | |
177 if (!fCodec->skipScanlines(linesToSkip)) { | |
178 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.
fZeroInitialized, | |
179 dstHeight, y + 1); | |
180 return SkCodec::kIncompleteInput; | |
181 } | |
182 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); | |
183 } | |
184 return SkCodec::kSuccess; | |
185 } | |
186 case SkCodec::kNone_SkScanlineOrder: { | |
187 const int linesNeeded = subsetHeight - samplingOffsetY; | |
188 SkAutoMalloc storage(linesNeeded * rowBytes); | |
189 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); | |
190 | |
191 if (!fCodec->skipScanlines(startY)) { | |
192 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, | |
193 dstHeight, 0); | |
194 return SkCodec::kIncompleteInput; | |
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()); | |
200 storagePtr += sampleY * rowBytes; | |
201 pixels = SkTAddOffset<void>(pixels, rowBytes); | |
202 } | |
203 | |
204 if (scanlines < dstHeight) { | |
205 // fCodec has already handled filling uninitialized memory. | |
206 return SkCodec::kIncompleteInput; | |
207 } | |
208 return SkCodec::kSuccess; | |
209 } | |
210 default: | |
211 SkASSERT(false); | |
212 return SkCodec::kUnimplemented; | |
213 } | |
214 } | |
OLD | NEW |