blob: 7e3bb9b2fb6e61e2bc4763ca238580b6a57dfbcb [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.org8a2ad3c2014-02-23 03:59:35 +000017static bool equal_modulo_alpha(const SkImageInfo& a, const SkImageInfo& b) {
18 return a.width() == b.width() && a.height() == b.height() &&
19 a.colorType() == b.colorType();
20}
21
halcanary@google.com29d96932013-12-09 13:45:02 +000022namespace {
23/**
24 * Special allocator used by getPixels(). Uses preallocated memory
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000025 * provided if possible, else fall-back on the default allocator
halcanary@google.com29d96932013-12-09 13:45:02 +000026 */
27class TargetAllocator : public SkBitmap::Allocator {
28public:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000029 TargetAllocator(const SkImageInfo& info,
30 void* target,
31 size_t rowBytes)
32 : fInfo(info)
33 , fTarget(target)
halcanary@google.com29d96932013-12-09 13:45:02 +000034 , fRowBytes(rowBytes)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000035 {}
halcanary@google.com29d96932013-12-09 13:45:02 +000036
halcanary@google.com3d50ea12014-01-02 13:15:13 +000037 bool isReady() { return (fTarget != NULL); }
38
39 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000040 if (NULL == fTarget || !equal_modulo_alpha(fInfo, bm->info())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +000041 // Call default allocator.
42 return bm->allocPixels(NULL, ct);
halcanary@google.com29d96932013-12-09 13:45:02 +000043 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000044
halcanary@google.com3d50ea12014-01-02 13:15:13 +000045 // TODO(halcanary): verify that all callers of this function
46 // will respect new RowBytes. Will be moot once rowbytes belongs
47 // to PixelRef.
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000048 bm->installPixels(fInfo, fTarget, fRowBytes, NULL, NULL);
49
halcanary@google.com3d50ea12014-01-02 13:15:13 +000050 fTarget = NULL; // never alloc same pixels twice!
halcanary@google.com29d96932013-12-09 13:45:02 +000051 return true;
52 }
53
54private:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000055 const SkImageInfo fInfo;
halcanary@google.com3d50ea12014-01-02 13:15:13 +000056 void* fTarget; // Block of memory to be supplied as pixel memory
57 // in allocPixelRef. Must be large enough to hold
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000058 // a bitmap described by fInfo and fRowBytes
59 const size_t fRowBytes; // rowbytes for the destination bitmap
60
halcanary@google.com29d96932013-12-09 13:45:02 +000061 typedef SkBitmap::Allocator INHERITED;
62};
halcanary@google.comad04eb42013-11-21 15:32:08 +000063
halcanary@google.com29d96932013-12-09 13:45:02 +000064// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
65#ifdef SK_DEBUG
66 #define SkCheckResult(expr, value) SkASSERT((value) == (expr))
67#else
68 #define SkCheckResult(expr, value) (void)(expr)
69#endif
70
halcanary@google.com3d50ea12014-01-02 13:15:13 +000071#ifdef SK_DEBUG
72inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) {
73 return ((reported == actual)
74 || ((reported == kPremul_SkAlphaType)
75 && (actual == kOpaque_SkAlphaType)));
76}
77#endif // SK_DEBUG
78
79} // namespace
80////////////////////////////////////////////////////////////////////////////////
81
82SkDecodingImageGenerator::SkDecodingImageGenerator(
83 SkData* data,
84 SkStreamRewindable* stream,
85 const SkImageInfo& info,
86 int sampleSize,
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000087 bool ditherImage)
halcanary@google.com3d50ea12014-01-02 13:15:13 +000088 : fData(data)
89 , fStream(stream)
90 , fInfo(info)
91 , fSampleSize(sampleSize)
92 , fDitherImage(ditherImage)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000093{
halcanary@google.com3d50ea12014-01-02 13:15:13 +000094 SkASSERT(stream != NULL);
95 SkSafeRef(fData); // may be NULL.
96}
97
98SkDecodingImageGenerator::~SkDecodingImageGenerator() {
99 SkSafeUnref(fData);
100 fStream->unref();
101}
102
103bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) {
104 if (info != NULL) {
105 *info = fInfo;
106 }
107 return true;
108}
109
halcanary@google.comad04eb42013-11-21 15:32:08 +0000110SkData* SkDecodingImageGenerator::refEncodedData() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000111 // This functionality is used in `gm --serialize`
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000112 // Does not encode options.
halcanary@google.com29d96932013-12-09 13:45:02 +0000113 if (fData != NULL) {
114 return SkSafeRef(fData);
115 }
116 // TODO(halcanary): SkStreamRewindable needs a refData() function
117 // which returns a cheap copy of the underlying data.
118 if (!fStream->rewind()) {
119 return NULL;
120 }
121 size_t length = fStream->getLength();
122 if (0 == length) {
123 return NULL;
124 }
125 void* buffer = sk_malloc_flags(length, 0);
126 SkCheckResult(fStream->read(buffer, length), length);
127 fData = SkData::NewFromMalloc(buffer, length);
128 return SkSafeRef(fData);
halcanary@google.comad04eb42013-11-21 15:32:08 +0000129}
130
halcanary@google.comad04eb42013-11-21 15:32:08 +0000131bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info,
132 void* pixels,
133 size_t rowBytes) {
halcanary@google.com29d96932013-12-09 13:45:02 +0000134 if (NULL == pixels) {
135 return false;
136 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000137 if (fInfo != info) {
138 // The caller has specified a different info. This is an
139 // error for this kind of SkImageGenerator. Use the Options
140 // to change the settings.
halcanary@google.com29d96932013-12-09 13:45:02 +0000141 return false;
142 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000143 if (info.minRowBytes() > rowBytes) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000144 // The caller has specified a bad rowBytes.
145 return false;
halcanary@google.com29d96932013-12-09 13:45:02 +0000146 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000147
halcanary@google.com29d96932013-12-09 13:45:02 +0000148 SkAssertResult(fStream->rewind());
149 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
150 if (NULL == decoder.get()) {
151 return false;
152 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000153 decoder->setDitherImage(fDitherImage);
154 decoder->setSampleSize(fSampleSize);
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000155
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000156 SkBitmap bitmap;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000157 TargetAllocator allocator(fInfo, pixels, rowBytes);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000158 decoder->setAllocator(&allocator);
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000159 // TODO: need to be able to pass colortype directly to decoder
160 SkBitmap::Config legacyConfig = SkColorTypeToBitmapConfig(info.colorType());
161 bool success = decoder->decode(fStream, &bitmap, legacyConfig,
halcanary@google.com29d96932013-12-09 13:45:02 +0000162 SkImageDecoder::kDecodePixels_Mode);
163 decoder->setAllocator(NULL);
164 if (!success) {
165 return false;
166 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000167 if (allocator.isReady()) { // Did not use pixels!
168 SkBitmap bm;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000169 SkASSERT(bitmap.canCopyTo(info.colorType()));
170 bool copySuccess = bitmap.copyTo(&bm, info.colorType(), &allocator);
171 if (!copySuccess || allocator.isReady()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000172 SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed.");
173 // Earlier we checked canCopyto(); we expect consistency.
174 return false;
175 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000176 SkASSERT(check_alpha(info.alphaType(), bm.alphaType()));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000177 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000178 SkASSERT(check_alpha(info.alphaType(), bitmap.alphaType()));
halcanary@google.com29d96932013-12-09 13:45:02 +0000179 }
180 return true;
halcanary@google.comad04eb42013-11-21 15:32:08 +0000181}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000182
183SkImageGenerator* SkDecodingImageGenerator::Create(
184 SkData* data,
185 const SkDecodingImageGenerator::Options& opts) {
halcanary@google.comad04eb42013-11-21 15:32:08 +0000186 SkASSERT(data != NULL);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000187 if (NULL == data) {
188 return NULL;
189 }
190 SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
191 SkASSERT(stream != NULL);
192 SkASSERT(stream->unique());
193 return SkDecodingImageGenerator::Create(data, stream, opts);
halcanary@google.comad04eb42013-11-21 15:32:08 +0000194}
halcanary@google.com29d96932013-12-09 13:45:02 +0000195
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000196SkImageGenerator* SkDecodingImageGenerator::Create(
197 SkStreamRewindable* stream,
198 const SkDecodingImageGenerator::Options& opts) {
halcanary@google.com29d96932013-12-09 13:45:02 +0000199 SkASSERT(stream != NULL);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000200 SkASSERT(stream->unique());
halcanary@google.com29d96932013-12-09 13:45:02 +0000201 if ((stream == NULL) || !stream->unique()) {
202 SkSafeUnref(stream);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000203 return NULL;
halcanary@google.com29d96932013-12-09 13:45:02 +0000204 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000205 return SkDecodingImageGenerator::Create(NULL, stream, opts);
206}
207
208// 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
211SkImageGenerator* SkDecodingImageGenerator::Create(
212 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);
229 if (!decoder->decode(stream, &bitmap,
230 SkImageDecoder::kDecodeBounds_Mode)) {
231 return NULL;
232 }
233 if (bitmap.config() == SkBitmap::kNo_Config) {
234 return NULL;
235 }
236
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000237 SkImageInfo info = bitmap.info();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000238
239 if (!opts.fUseRequestedColorType) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000240 // Use default
241 if (kIndex_8_SkColorType == bitmap.colorType()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000242 // We don't support kIndex8 because we don't support
243 // colortables in this workflow.
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000244 info.fColorType = kPMColor_SkColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000245 }
246 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000247 if (!bitmap.canCopyTo(opts.fRequestedColorType)) {
248 SkASSERT(bitmap.colorType() != opts.fRequestedColorType);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000249 return NULL; // Can not translate to needed config.
250 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000251 info.fColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000252 }
253 return SkNEW_ARGS(SkDecodingImageGenerator,
254 (data, autoStream.detach(), info,
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000255 opts.fSampleSize, opts.fDitherImage));
halcanary@google.com29d96932013-12-09 13:45:02 +0000256}