blob: b3924a7daa4d2b342cb6ea29f333ce93aa4b7390 [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();
26 virtual SkData* refEncodedData() SK_OVERRIDE;
27 // This implementaion of getInfo() always returns true.
28 virtual bool getInfo(SkImageInfo* info) SK_OVERRIDE;
29 virtual bool getPixels(const SkImageInfo& info,
30 void* pixels,
31 size_t rowBytes) SK_OVERRIDE;
32
33 SkData* fData;
34 SkStreamRewindable* fStream;
35 const SkImageInfo fInfo;
36 const int fSampleSize;
37 const bool fDitherImage;
38
39 DecodingImageGenerator(SkData* data,
40 SkStreamRewindable* stream,
41 const SkImageInfo& info,
42 int sampleSize,
43 bool ditherImage);
44 typedef SkImageGenerator INHERITED;
45};
46
halcanary@google.com29d96932013-12-09 13:45:02 +000047/**
48 * Special allocator used by getPixels(). Uses preallocated memory
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000049 * provided if possible, else fall-back on the default allocator
halcanary@google.com29d96932013-12-09 13:45:02 +000050 */
51class TargetAllocator : public SkBitmap::Allocator {
52public:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000053 TargetAllocator(const SkImageInfo& info,
54 void* target,
55 size_t rowBytes)
56 : fInfo(info)
57 , fTarget(target)
halcanary@google.com29d96932013-12-09 13:45:02 +000058 , fRowBytes(rowBytes)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000059 {}
halcanary@google.com29d96932013-12-09 13:45:02 +000060
halcanary@google.com3d50ea12014-01-02 13:15:13 +000061 bool isReady() { return (fTarget != NULL); }
62
63 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000064 if (NULL == fTarget || !equal_modulo_alpha(fInfo, bm->info())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +000065 // Call default allocator.
66 return bm->allocPixels(NULL, ct);
halcanary@google.com29d96932013-12-09 13:45:02 +000067 }
skia.committer@gmail.com86cbf992014-02-24 03:01:55 +000068
halcanary@google.com3d50ea12014-01-02 13:15:13 +000069 // TODO(halcanary): verify that all callers of this function
70 // will respect new RowBytes. Will be moot once rowbytes belongs
71 // to PixelRef.
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000072 bm->installPixels(fInfo, fTarget, fRowBytes, NULL, NULL);
73
halcanary@google.com3d50ea12014-01-02 13:15:13 +000074 fTarget = NULL; // never alloc same pixels twice!
halcanary@google.com29d96932013-12-09 13:45:02 +000075 return true;
76 }
77
78private:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000079 const SkImageInfo fInfo;
halcanary@google.com3d50ea12014-01-02 13:15:13 +000080 void* fTarget; // Block of memory to be supplied as pixel memory
81 // in allocPixelRef. Must be large enough to hold
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000082 // a bitmap described by fInfo and fRowBytes
83 const size_t fRowBytes; // rowbytes for the destination bitmap
84
halcanary@google.com29d96932013-12-09 13:45:02 +000085 typedef SkBitmap::Allocator INHERITED;
86};
halcanary@google.comad04eb42013-11-21 15:32:08 +000087
halcanary@google.com29d96932013-12-09 13:45:02 +000088// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
89#ifdef SK_DEBUG
90 #define SkCheckResult(expr, value) SkASSERT((value) == (expr))
91#else
92 #define SkCheckResult(expr, value) (void)(expr)
93#endif
94
halcanary@google.com3d50ea12014-01-02 13:15:13 +000095#ifdef SK_DEBUG
96inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) {
97 return ((reported == actual)
98 || ((reported == kPremul_SkAlphaType)
99 && (actual == kOpaque_SkAlphaType)));
100}
101#endif // SK_DEBUG
102
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000103////////////////////////////////////////////////////////////////////////////////
104
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000105DecodingImageGenerator::DecodingImageGenerator(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000106 SkData* data,
107 SkStreamRewindable* stream,
108 const SkImageInfo& info,
109 int sampleSize,
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000110 bool ditherImage)
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000111 : fData(data)
112 , fStream(stream)
113 , fInfo(info)
114 , fSampleSize(sampleSize)
115 , fDitherImage(ditherImage)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000116{
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000117 SkASSERT(stream != NULL);
118 SkSafeRef(fData); // may be NULL.
119}
120
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000121DecodingImageGenerator::~DecodingImageGenerator() {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000122 SkSafeUnref(fData);
123 fStream->unref();
124}
125
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000126bool DecodingImageGenerator::getInfo(SkImageInfo* info) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000127 if (info != NULL) {
128 *info = fInfo;
129 }
130 return true;
131}
132
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000133SkData* DecodingImageGenerator::refEncodedData() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000134 // This functionality is used in `gm --serialize`
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000135 // Does not encode options.
halcanary@google.com29d96932013-12-09 13:45:02 +0000136 if (fData != NULL) {
137 return SkSafeRef(fData);
138 }
139 // TODO(halcanary): SkStreamRewindable needs a refData() function
140 // which returns a cheap copy of the underlying data.
141 if (!fStream->rewind()) {
142 return NULL;
143 }
144 size_t length = fStream->getLength();
145 if (0 == length) {
146 return NULL;
147 }
148 void* buffer = sk_malloc_flags(length, 0);
149 SkCheckResult(fStream->read(buffer, length), length);
150 fData = SkData::NewFromMalloc(buffer, length);
151 return SkSafeRef(fData);
halcanary@google.comad04eb42013-11-21 15:32:08 +0000152}
153
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000154bool DecodingImageGenerator::getPixels(const SkImageInfo& info,
halcanary@google.comad04eb42013-11-21 15:32:08 +0000155 void* pixels,
156 size_t rowBytes) {
halcanary@google.com29d96932013-12-09 13:45:02 +0000157 if (NULL == pixels) {
158 return false;
159 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000160 if (fInfo != info) {
161 // The caller has specified a different info. This is an
162 // error for this kind of SkImageGenerator. Use the Options
163 // to change the settings.
halcanary@google.com29d96932013-12-09 13:45:02 +0000164 return false;
165 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000166 if (info.minRowBytes() > rowBytes) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000167 // The caller has specified a bad rowBytes.
168 return false;
halcanary@google.com29d96932013-12-09 13:45:02 +0000169 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000170
halcanary@google.com29d96932013-12-09 13:45:02 +0000171 SkAssertResult(fStream->rewind());
172 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
173 if (NULL == decoder.get()) {
174 return false;
175 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000176 decoder->setDitherImage(fDitherImage);
177 decoder->setSampleSize(fSampleSize);
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000178 decoder->setRequireUnpremultipliedColors(
179 info.fAlphaType == kUnpremul_SkAlphaType);
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000180
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000181 SkBitmap bitmap;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000182 TargetAllocator allocator(fInfo, pixels, rowBytes);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000183 decoder->setAllocator(&allocator);
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000184 // TODO: need to be able to pass colortype directly to decoder
185 SkBitmap::Config legacyConfig = SkColorTypeToBitmapConfig(info.colorType());
186 bool success = decoder->decode(fStream, &bitmap, legacyConfig,
halcanary@google.com29d96932013-12-09 13:45:02 +0000187 SkImageDecoder::kDecodePixels_Mode);
188 decoder->setAllocator(NULL);
189 if (!success) {
190 return false;
191 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000192 if (allocator.isReady()) { // Did not use pixels!
193 SkBitmap bm;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000194 SkASSERT(bitmap.canCopyTo(info.colorType()));
195 bool copySuccess = bitmap.copyTo(&bm, info.colorType(), &allocator);
196 if (!copySuccess || allocator.isReady()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000197 SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed.");
198 // Earlier we checked canCopyto(); we expect consistency.
199 return false;
200 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000201 SkASSERT(check_alpha(info.alphaType(), bm.alphaType()));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000202 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000203 SkASSERT(check_alpha(info.alphaType(), bitmap.alphaType()));
halcanary@google.com29d96932013-12-09 13:45:02 +0000204 }
205 return true;
halcanary@google.comad04eb42013-11-21 15:32:08 +0000206}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000207
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000208// A contructor-type function that returns NULL on failure. This
209// prevents the returned SkImageGenerator from ever being in a bad
210// state. Called by both Create() functions
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000211SkImageGenerator* CreateDecodingImageGenerator(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000212 SkData* data,
213 SkStreamRewindable* stream,
214 const SkDecodingImageGenerator::Options& opts) {
215 SkASSERT(stream);
216 SkAutoTUnref<SkStreamRewindable> autoStream(stream); // always unref this.
217 if (opts.fUseRequestedColorType &&
218 (kIndex_8_SkColorType == opts.fRequestedColorType)) {
219 // We do not support indexed color with SkImageGenerators,
220 return NULL;
221 }
222 SkAssertResult(autoStream->rewind());
223 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream));
224 if (NULL == decoder.get()) {
225 return NULL;
226 }
227 SkBitmap bitmap;
228 decoder->setSampleSize(opts.fSampleSize);
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000229 decoder->setRequireUnpremultipliedColors(opts.fRequireUnpremul);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000230 if (!decoder->decode(stream, &bitmap,
231 SkImageDecoder::kDecodeBounds_Mode)) {
232 return NULL;
233 }
234 if (bitmap.config() == SkBitmap::kNo_Config) {
235 return NULL;
236 }
237
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000238 SkImageInfo info = bitmap.info();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000239
240 if (!opts.fUseRequestedColorType) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000241 // Use default
242 if (kIndex_8_SkColorType == bitmap.colorType()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000243 // We don't support kIndex8 because we don't support
244 // colortables in this workflow.
commit-bot@chromium.org149e9a12014-04-09 20:45:29 +0000245 info.fColorType = kN32_SkColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000246 }
247 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000248 if (!bitmap.canCopyTo(opts.fRequestedColorType)) {
249 SkASSERT(bitmap.colorType() != opts.fRequestedColorType);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000250 return NULL; // Can not translate to needed config.
251 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000252 info.fColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000253 }
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000254
255 if (opts.fRequireUnpremul && info.fAlphaType != kOpaque_SkAlphaType) {
256 info.fAlphaType = kUnpremul_SkAlphaType;
257 }
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000258 return SkNEW_ARGS(DecodingImageGenerator,
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000259 (data, autoStream.detach(), info,
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000260 opts.fSampleSize, opts.fDitherImage));
halcanary@google.com29d96932013-12-09 13:45:02 +0000261}
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000262
263} // namespace
264
265////////////////////////////////////////////////////////////////////////////////
266
267SkImageGenerator* SkDecodingImageGenerator::Create(
268 SkData* data,
269 const SkDecodingImageGenerator::Options& opts) {
270 SkASSERT(data != NULL);
271 if (NULL == data) {
272 return NULL;
273 }
274 SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
275 SkASSERT(stream != NULL);
276 SkASSERT(stream->unique());
277 return CreateDecodingImageGenerator(data, stream, opts);
278}
279
280SkImageGenerator* SkDecodingImageGenerator::Create(
281 SkStreamRewindable* stream,
282 const SkDecodingImageGenerator::Options& opts) {
283 SkASSERT(stream != NULL);
284 SkASSERT(stream->unique());
285 if ((stream == NULL) || !stream->unique()) {
286 SkSafeUnref(stream);
287 return NULL;
288 }
289 return CreateDecodingImageGenerator(NULL, stream, opts);
290}