blob: 5c66c94a534e257d01d09d2f0a7bbe595de96bfa [file] [log] [blame]
halcanary@google.comad04eb42013-11-21 15:32:08 +00001/*
2 * Copyright 2013 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
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +00008#include "SkData.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +00009#include "SkDecodingImageGenerator.h"
halcanary@google.comad04eb42013-11-21 15:32:08 +000010#include "SkImageDecoder.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000011#include "SkImageInfo.h"
halcanary@google.comedd370f2013-12-10 21:11:12 +000012#include "SkImageGenerator.h"
halcanary@google.com29d96932013-12-09 13:45:02 +000013#include "SkImagePriv.h"
14#include "SkStream.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000015#include "SkUtils.h"
halcanary@google.com29d96932013-12-09 13:45:02 +000016
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +000017namespace {
18bool equal_modulo_alpha(const SkImageInfo& a, const SkImageInfo& b) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000019 return a.width() == b.width() && a.height() == b.height() &&
20 a.colorType() == b.colorType();
21}
22
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +000023class DecodingImageGenerator : public SkImageGenerator {
24public:
25 virtual ~DecodingImageGenerator();
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +000026
27 SkData* fData;
28 SkStreamRewindable* fStream;
29 const SkImageInfo fInfo;
30 const int fSampleSize;
31 const bool fDitherImage;
32
33 DecodingImageGenerator(SkData* data,
34 SkStreamRewindable* stream,
35 const SkImageInfo& info,
36 int sampleSize,
37 bool ditherImage);
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +000038
39protected:
40 virtual SkData* onRefEncodedData() SK_OVERRIDE;
41 virtual bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
42 *info = fInfo;
43 return true;
44 }
45 virtual bool onGetPixels(const SkImageInfo& info,
46 void* pixels, size_t rowBytes,
47 SkPMColor ctable[], int* ctableCount) SK_OVERRIDE;
48
49private:
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +000050 typedef SkImageGenerator INHERITED;
51};
52
halcanary@google.com29d96932013-12-09 13:45:02 +000053/**
54 * Special allocator used by getPixels(). Uses preallocated memory
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000055 * provided if possible, else fall-back on the default allocator
halcanary@google.com29d96932013-12-09 13:45:02 +000056 */
57class TargetAllocator : public SkBitmap::Allocator {
58public:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000059 TargetAllocator(const SkImageInfo& info,
60 void* target,
61 size_t rowBytes)
62 : fInfo(info)
63 , fTarget(target)
halcanary@google.com29d96932013-12-09 13:45:02 +000064 , fRowBytes(rowBytes)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000065 {}
halcanary@google.com29d96932013-12-09 13:45:02 +000066
halcanary@google.com3d50ea12014-01-02 13:15:13 +000067 bool isReady() { return (fTarget != NULL); }
68
69 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000070 if (NULL == fTarget || !equal_modulo_alpha(fInfo, bm->info())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +000071 // Call default allocator.
reed84825042014-09-02 12:50:45 -070072 return bm->tryAllocPixels(NULL, ct);
halcanary@google.com29d96932013-12-09 13:45:02 +000073 }
skia.committer@gmail.com86cbf992014-02-24 03:01:55 +000074
halcanary@google.com3d50ea12014-01-02 13:15:13 +000075 // TODO(halcanary): verify that all callers of this function
76 // will respect new RowBytes. Will be moot once rowbytes belongs
77 // to PixelRef.
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +000078 bm->installPixels(fInfo, fTarget, fRowBytes, ct, NULL, NULL);
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000079
halcanary@google.com3d50ea12014-01-02 13:15:13 +000080 fTarget = NULL; // never alloc same pixels twice!
halcanary@google.com29d96932013-12-09 13:45:02 +000081 return true;
82 }
83
84private:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000085 const SkImageInfo fInfo;
halcanary@google.com3d50ea12014-01-02 13:15:13 +000086 void* fTarget; // Block of memory to be supplied as pixel memory
87 // in allocPixelRef. Must be large enough to hold
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000088 // a bitmap described by fInfo and fRowBytes
89 const size_t fRowBytes; // rowbytes for the destination bitmap
90
halcanary@google.com29d96932013-12-09 13:45:02 +000091 typedef SkBitmap::Allocator INHERITED;
92};
halcanary@google.comad04eb42013-11-21 15:32:08 +000093
halcanary@google.com29d96932013-12-09 13:45:02 +000094// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
95#ifdef SK_DEBUG
96 #define SkCheckResult(expr, value) SkASSERT((value) == (expr))
97#else
98 #define SkCheckResult(expr, value) (void)(expr)
99#endif
100
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000101#ifdef SK_DEBUG
102inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) {
103 return ((reported == actual)
104 || ((reported == kPremul_SkAlphaType)
105 && (actual == kOpaque_SkAlphaType)));
106}
107#endif // SK_DEBUG
108
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000109////////////////////////////////////////////////////////////////////////////////
110
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000111DecodingImageGenerator::DecodingImageGenerator(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000112 SkData* data,
113 SkStreamRewindable* stream,
114 const SkImageInfo& info,
115 int sampleSize,
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000116 bool ditherImage)
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000117 : fData(data)
118 , fStream(stream)
119 , fInfo(info)
120 , fSampleSize(sampleSize)
121 , fDitherImage(ditherImage)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000122{
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000123 SkASSERT(stream != NULL);
124 SkSafeRef(fData); // may be NULL.
125}
126
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000127DecodingImageGenerator::~DecodingImageGenerator() {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000128 SkSafeUnref(fData);
129 fStream->unref();
130}
131
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000132SkData* DecodingImageGenerator::onRefEncodedData() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000133 // This functionality is used in `gm --serialize`
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000134 // Does not encode options.
halcanary@google.com29d96932013-12-09 13:45:02 +0000135 if (fData != NULL) {
136 return SkSafeRef(fData);
137 }
138 // TODO(halcanary): SkStreamRewindable needs a refData() function
139 // which returns a cheap copy of the underlying data.
140 if (!fStream->rewind()) {
141 return NULL;
142 }
143 size_t length = fStream->getLength();
144 if (0 == length) {
145 return NULL;
146 }
147 void* buffer = sk_malloc_flags(length, 0);
148 SkCheckResult(fStream->read(buffer, length), length);
149 fData = SkData::NewFromMalloc(buffer, length);
150 return SkSafeRef(fData);
halcanary@google.comad04eb42013-11-21 15:32:08 +0000151}
152
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000153bool DecodingImageGenerator::onGetPixels(const SkImageInfo& info,
154 void* pixels, size_t rowBytes,
155 SkPMColor ctableEntries[], int* ctableCount) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000156 if (fInfo != info) {
157 // The caller has specified a different info. This is an
158 // error for this kind of SkImageGenerator. Use the Options
159 // to change the settings.
halcanary@google.com29d96932013-12-09 13:45:02 +0000160 return false;
161 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000162
halcanary@google.com29d96932013-12-09 13:45:02 +0000163 SkAssertResult(fStream->rewind());
164 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
165 if (NULL == decoder.get()) {
166 return false;
167 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000168 decoder->setDitherImage(fDitherImage);
169 decoder->setSampleSize(fSampleSize);
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000170 decoder->setRequireUnpremultipliedColors(
171 info.fAlphaType == kUnpremul_SkAlphaType);
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000172
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000173 SkBitmap bitmap;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000174 TargetAllocator allocator(fInfo, pixels, rowBytes);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000175 decoder->setAllocator(&allocator);
reedbfefc7c2014-06-12 17:40:00 -0700176 bool success = decoder->decode(fStream, &bitmap, info.colorType(),
halcanary@google.com29d96932013-12-09 13:45:02 +0000177 SkImageDecoder::kDecodePixels_Mode);
178 decoder->setAllocator(NULL);
179 if (!success) {
180 return false;
181 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000182 if (allocator.isReady()) { // Did not use pixels!
183 SkBitmap bm;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000184 SkASSERT(bitmap.canCopyTo(info.colorType()));
185 bool copySuccess = bitmap.copyTo(&bm, info.colorType(), &allocator);
186 if (!copySuccess || allocator.isReady()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000187 SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed.");
188 // Earlier we checked canCopyto(); we expect consistency.
189 return false;
190 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000191 SkASSERT(check_alpha(info.alphaType(), bm.alphaType()));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000192 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000193 SkASSERT(check_alpha(info.alphaType(), bitmap.alphaType()));
halcanary@google.com29d96932013-12-09 13:45:02 +0000194 }
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000195
196 if (kIndex_8_SkColorType == info.colorType()) {
197 if (kIndex_8_SkColorType != bitmap.colorType()) {
198 return false; // they asked for Index8, but we didn't receive that from decoder
199 }
200 SkColorTable* ctable = bitmap.getColorTable();
201 if (NULL == ctable) {
202 return false;
203 }
204 const int count = ctable->count();
205 memcpy(ctableEntries, ctable->lockColors(), count * sizeof(SkPMColor));
206 ctable->unlockColors();
207 *ctableCount = count;
208 }
halcanary@google.com29d96932013-12-09 13:45:02 +0000209 return true;
halcanary@google.comad04eb42013-11-21 15:32:08 +0000210}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000211
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000212// A contructor-type function that returns NULL on failure. This
213// prevents the returned SkImageGenerator from ever being in a bad
214// state. Called by both Create() functions
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000215SkImageGenerator* CreateDecodingImageGenerator(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000216 SkData* data,
217 SkStreamRewindable* stream,
218 const SkDecodingImageGenerator::Options& opts) {
219 SkASSERT(stream);
220 SkAutoTUnref<SkStreamRewindable> autoStream(stream); // always unref this.
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000221 SkAssertResult(autoStream->rewind());
222 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream));
223 if (NULL == decoder.get()) {
224 return NULL;
225 }
226 SkBitmap bitmap;
227 decoder->setSampleSize(opts.fSampleSize);
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000228 decoder->setRequireUnpremultipliedColors(opts.fRequireUnpremul);
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000229 if (!decoder->decode(stream, &bitmap, SkImageDecoder::kDecodeBounds_Mode)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000230 return NULL;
231 }
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000232 if (kUnknown_SkColorType == bitmap.colorType()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000233 return NULL;
234 }
235
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000236 SkImageInfo info = bitmap.info();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000237
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000238 if (opts.fUseRequestedColorType && (opts.fRequestedColorType != info.colorType())) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000239 if (!bitmap.canCopyTo(opts.fRequestedColorType)) {
240 SkASSERT(bitmap.colorType() != opts.fRequestedColorType);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000241 return NULL; // Can not translate to needed config.
242 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000243 info.fColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000244 }
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000245
246 if (opts.fRequireUnpremul && info.fAlphaType != kOpaque_SkAlphaType) {
247 info.fAlphaType = kUnpremul_SkAlphaType;
248 }
scroggo2fd0d142014-07-01 07:08:19 -0700249
250 if (!SkColorTypeValidateAlphaType(info.fColorType, info.fAlphaType, &info.fAlphaType)) {
251 return NULL;
252 }
253
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000254 return SkNEW_ARGS(DecodingImageGenerator,
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000255 (data, autoStream.detach(), info,
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000256 opts.fSampleSize, opts.fDitherImage));
halcanary@google.com29d96932013-12-09 13:45:02 +0000257}
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000258
259} // namespace
260
261////////////////////////////////////////////////////////////////////////////////
262
263SkImageGenerator* SkDecodingImageGenerator::Create(
264 SkData* data,
265 const SkDecodingImageGenerator::Options& opts) {
266 SkASSERT(data != NULL);
267 if (NULL == data) {
268 return NULL;
269 }
270 SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
271 SkASSERT(stream != NULL);
272 SkASSERT(stream->unique());
273 return CreateDecodingImageGenerator(data, stream, opts);
274}
275
276SkImageGenerator* SkDecodingImageGenerator::Create(
277 SkStreamRewindable* stream,
278 const SkDecodingImageGenerator::Options& opts) {
279 SkASSERT(stream != NULL);
280 SkASSERT(stream->unique());
281 if ((stream == NULL) || !stream->unique()) {
282 SkSafeUnref(stream);
283 return NULL;
284 }
285 return CreateDecodingImageGenerator(NULL, stream, opts);
286}