blob: 153d1e220bc6be643dea492e8c4d74192872268c [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
17namespace {
18/**
19 * Special allocator used by getPixels(). Uses preallocated memory
20 * provided.
21 */
22class TargetAllocator : public SkBitmap::Allocator {
23public:
halcanary@google.com3d50ea12014-01-02 13:15:13 +000024 TargetAllocator(void* target,
25 size_t rowBytes,
26 int width,
27 int height,
28 SkBitmap::Config config)
halcanary@google.com29d96932013-12-09 13:45:02 +000029 : fTarget(target)
30 , fRowBytes(rowBytes)
halcanary@google.com3d50ea12014-01-02 13:15:13 +000031 , fWidth(width)
32 , fHeight(height)
33 , fConfig(config) { }
halcanary@google.com29d96932013-12-09 13:45:02 +000034
halcanary@google.com3d50ea12014-01-02 13:15:13 +000035 bool isReady() { return (fTarget != NULL); }
36
37 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
38 if ((NULL == fTarget)
39 || (fConfig != bm->config())
40 || (fWidth != bm->width())
41 || (fHeight != bm->height())
42 || (ct != NULL)) {
43 // Call default allocator.
44 return bm->allocPixels(NULL, ct);
halcanary@google.com29d96932013-12-09 13:45:02 +000045 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +000046 // make sure fRowBytes is correct.
47 bm->setConfig(fConfig, fWidth, fHeight, fRowBytes, bm->alphaType());
48 // TODO(halcanary): verify that all callers of this function
49 // will respect new RowBytes. Will be moot once rowbytes belongs
50 // to PixelRef.
51 bm->setPixels(fTarget, NULL);
52 fTarget = NULL; // never alloc same pixels twice!
halcanary@google.com29d96932013-12-09 13:45:02 +000053 return true;
54 }
55
56private:
halcanary@google.com3d50ea12014-01-02 13:15:13 +000057 void* fTarget; // Block of memory to be supplied as pixel memory
58 // in allocPixelRef. Must be large enough to hold
59 // a bitmap described by fWidth, fHeight, and
60 // fRowBytes.
61 size_t fRowBytes; // rowbytes for the destination bitmap
62 int fWidth; // Along with fHeight and fConfig, the information
63 int fHeight; // about the bitmap whose pixels this allocator is
64 // expected to allocate. If they do not match the
65 // bitmap passed to allocPixelRef, it is assumed
66 // that the bitmap will be copied to a bitmap with
67 // the correct info using this allocator, so the
68 // default allocator will be used instead of
69 // fTarget.
70 SkBitmap::Config fConfig;
halcanary@google.com29d96932013-12-09 13:45:02 +000071 typedef SkBitmap::Allocator INHERITED;
72};
halcanary@google.comad04eb42013-11-21 15:32:08 +000073
halcanary@google.com29d96932013-12-09 13:45:02 +000074// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
75#ifdef SK_DEBUG
76 #define SkCheckResult(expr, value) SkASSERT((value) == (expr))
77#else
78 #define SkCheckResult(expr, value) (void)(expr)
79#endif
80
halcanary@google.com3d50ea12014-01-02 13:15:13 +000081#ifdef SK_DEBUG
82inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) {
83 return ((reported == actual)
84 || ((reported == kPremul_SkAlphaType)
85 && (actual == kOpaque_SkAlphaType)));
86}
87#endif // SK_DEBUG
88
89} // namespace
90////////////////////////////////////////////////////////////////////////////////
91
92SkDecodingImageGenerator::SkDecodingImageGenerator(
93 SkData* data,
94 SkStreamRewindable* stream,
95 const SkImageInfo& info,
96 int sampleSize,
97 bool ditherImage,
98 SkBitmap::Config requestedConfig)
99 : fData(data)
100 , fStream(stream)
101 , fInfo(info)
102 , fSampleSize(sampleSize)
103 , fDitherImage(ditherImage)
104 , fRequestedConfig(requestedConfig) {
105 SkASSERT(stream != NULL);
106 SkSafeRef(fData); // may be NULL.
107}
108
109SkDecodingImageGenerator::~SkDecodingImageGenerator() {
110 SkSafeUnref(fData);
111 fStream->unref();
112}
113
114bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) {
115 if (info != NULL) {
116 *info = fInfo;
117 }
118 return true;
119}
120
halcanary@google.comad04eb42013-11-21 15:32:08 +0000121SkData* SkDecodingImageGenerator::refEncodedData() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000122 // This functionality is used in `gm --serialize`
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000123 // Does not encode options.
halcanary@google.com29d96932013-12-09 13:45:02 +0000124 if (fData != NULL) {
125 return SkSafeRef(fData);
126 }
127 // TODO(halcanary): SkStreamRewindable needs a refData() function
128 // which returns a cheap copy of the underlying data.
129 if (!fStream->rewind()) {
130 return NULL;
131 }
132 size_t length = fStream->getLength();
133 if (0 == length) {
134 return NULL;
135 }
136 void* buffer = sk_malloc_flags(length, 0);
137 SkCheckResult(fStream->read(buffer, length), length);
138 fData = SkData::NewFromMalloc(buffer, length);
139 return SkSafeRef(fData);
halcanary@google.comad04eb42013-11-21 15:32:08 +0000140}
141
halcanary@google.comad04eb42013-11-21 15:32:08 +0000142bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info,
143 void* pixels,
144 size_t rowBytes) {
halcanary@google.com29d96932013-12-09 13:45:02 +0000145 if (NULL == pixels) {
146 return false;
147 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000148 if (fInfo != info) {
149 // The caller has specified a different info. This is an
150 // error for this kind of SkImageGenerator. Use the Options
151 // to change the settings.
halcanary@google.com29d96932013-12-09 13:45:02 +0000152 return false;
153 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000154 int bpp = SkBitmap::ComputeBytesPerPixel(fRequestedConfig);
155 if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) {
156 // The caller has specified a bad rowBytes.
157 return false;
halcanary@google.com29d96932013-12-09 13:45:02 +0000158 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000159
halcanary@google.com29d96932013-12-09 13:45:02 +0000160 SkAssertResult(fStream->rewind());
161 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
162 if (NULL == decoder.get()) {
163 return false;
164 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000165 decoder->setDitherImage(fDitherImage);
166 decoder->setSampleSize(fSampleSize);
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000167
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000168 SkBitmap bitmap;
169 TargetAllocator allocator(pixels, rowBytes, info.fWidth,
170 info.fHeight, fRequestedConfig);
171 decoder->setAllocator(&allocator);
172 bool success = decoder->decode(fStream, &bitmap, fRequestedConfig,
halcanary@google.com29d96932013-12-09 13:45:02 +0000173 SkImageDecoder::kDecodePixels_Mode);
174 decoder->setAllocator(NULL);
175 if (!success) {
176 return false;
177 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000178 if (allocator.isReady()) { // Did not use pixels!
179 SkBitmap bm;
180 SkASSERT(bitmap.canCopyTo(fRequestedConfig));
181 if (!bitmap.copyTo(&bm, fRequestedConfig, &allocator)
182 || allocator.isReady()) {
183 SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed.");
184 // Earlier we checked canCopyto(); we expect consistency.
185 return false;
186 }
187 SkASSERT(check_alpha(fInfo.fAlphaType, bm.alphaType()));
188 } else {
189 SkASSERT(check_alpha(fInfo.fAlphaType, bitmap.alphaType()));
halcanary@google.com29d96932013-12-09 13:45:02 +0000190 }
191 return true;
halcanary@google.comad04eb42013-11-21 15:32:08 +0000192}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000193
194SkImageGenerator* SkDecodingImageGenerator::Create(
195 SkData* data,
196 const SkDecodingImageGenerator::Options& opts) {
halcanary@google.comad04eb42013-11-21 15:32:08 +0000197 SkASSERT(data != NULL);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000198 if (NULL == data) {
199 return NULL;
200 }
201 SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
202 SkASSERT(stream != NULL);
203 SkASSERT(stream->unique());
204 return SkDecodingImageGenerator::Create(data, stream, opts);
halcanary@google.comad04eb42013-11-21 15:32:08 +0000205}
halcanary@google.com29d96932013-12-09 13:45:02 +0000206
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000207SkImageGenerator* SkDecodingImageGenerator::Create(
208 SkStreamRewindable* stream,
209 const SkDecodingImageGenerator::Options& opts) {
halcanary@google.com29d96932013-12-09 13:45:02 +0000210 SkASSERT(stream != NULL);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000211 SkASSERT(stream->unique());
halcanary@google.com29d96932013-12-09 13:45:02 +0000212 if ((stream == NULL) || !stream->unique()) {
213 SkSafeUnref(stream);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000214 return NULL;
halcanary@google.com29d96932013-12-09 13:45:02 +0000215 }
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000216 return SkDecodingImageGenerator::Create(NULL, stream, opts);
217}
218
219// A contructor-type function that returns NULL on failure. This
220// prevents the returned SkImageGenerator from ever being in a bad
221// state. Called by both Create() functions
222SkImageGenerator* SkDecodingImageGenerator::Create(
223 SkData* data,
224 SkStreamRewindable* stream,
225 const SkDecodingImageGenerator::Options& opts) {
226 SkASSERT(stream);
227 SkAutoTUnref<SkStreamRewindable> autoStream(stream); // always unref this.
228 if (opts.fUseRequestedColorType &&
229 (kIndex_8_SkColorType == opts.fRequestedColorType)) {
230 // We do not support indexed color with SkImageGenerators,
231 return NULL;
232 }
233 SkAssertResult(autoStream->rewind());
234 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream));
235 if (NULL == decoder.get()) {
236 return NULL;
237 }
238 SkBitmap bitmap;
239 decoder->setSampleSize(opts.fSampleSize);
240 if (!decoder->decode(stream, &bitmap,
241 SkImageDecoder::kDecodeBounds_Mode)) {
242 return NULL;
243 }
244 if (bitmap.config() == SkBitmap::kNo_Config) {
245 return NULL;
246 }
247
248 SkImageInfo info;
249 SkBitmap::Config config;
250
251 if (!opts.fUseRequestedColorType) {
252 // Use default config.
253 if (SkBitmap::kIndex8_Config == bitmap.config()) {
254 // We don't support kIndex8 because we don't support
255 // colortables in this workflow.
256 config = SkBitmap::kARGB_8888_Config;
257 info.fWidth = bitmap.width();
258 info.fHeight = bitmap.height();
259 info.fColorType = kPMColor_SkColorType;
260 info.fAlphaType = bitmap.alphaType();
261 } else {
262 config = bitmap.config(); // Save for later!
263 if (!bitmap.asImageInfo(&info)) {
264 SkDEBUGFAIL("Getting SkImageInfo from bitmap failed.");
265 return NULL;
266 }
267 }
268 } else {
269 config = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
270 if (!bitmap.canCopyTo(config)) {
271 SkASSERT(bitmap.config() != config);
272 return NULL; // Can not translate to needed config.
273 }
274 info.fWidth = bitmap.width();
275 info.fHeight = bitmap.height();
276 info.fColorType = opts.fRequestedColorType;
277 info.fAlphaType = bitmap.alphaType();
278
279 // Sanity check.
280 SkDEBUGCODE(SkColorType tmp;)
281 SkASSERT(SkBitmapConfigToColorType(config, &tmp));
282 SkASSERT(tmp == opts.fRequestedColorType);
283 }
284 return SkNEW_ARGS(SkDecodingImageGenerator,
285 (data, autoStream.detach(), info,
286 opts.fSampleSize, opts.fDitherImage, config));
halcanary@google.com29d96932013-12-09 13:45:02 +0000287}