blob: e7a775e264b8e9181f5ddaad6e4f57299f10afbe [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
scroggoa1193e42015-01-21 12:09:53 -080027 SkData* fData;
28 SkAutoTDelete<SkStreamRewindable> fStream;
29 const SkImageInfo fInfo;
30 const int fSampleSize;
31 const bool fDitherImage;
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +000032
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:
mtklein72c9faa2015-01-09 10:06:39 -080040 SkData* onRefEncodedData() SK_OVERRIDE;
41 bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +000042 *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;
sugoib227e372014-10-16 13:10:57 -070048 virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
49 SkYUVColorSpace* colorSpace) SK_OVERRIDE;
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +000050
51private:
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +000052 typedef SkImageGenerator INHERITED;
53};
54
halcanary@google.com29d96932013-12-09 13:45:02 +000055/**
56 * Special allocator used by getPixels(). Uses preallocated memory
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000057 * provided if possible, else fall-back on the default allocator
halcanary@google.com29d96932013-12-09 13:45:02 +000058 */
59class TargetAllocator : public SkBitmap::Allocator {
60public:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000061 TargetAllocator(const SkImageInfo& info,
62 void* target,
63 size_t rowBytes)
64 : fInfo(info)
65 , fTarget(target)
halcanary@google.com29d96932013-12-09 13:45:02 +000066 , fRowBytes(rowBytes)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000067 {}
halcanary@google.com29d96932013-12-09 13:45:02 +000068
halcanary@google.com3d50ea12014-01-02 13:15:13 +000069 bool isReady() { return (fTarget != NULL); }
70
71 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000072 if (NULL == fTarget || !equal_modulo_alpha(fInfo, bm->info())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +000073 // Call default allocator.
reed84825042014-09-02 12:50:45 -070074 return bm->tryAllocPixels(NULL, ct);
halcanary@google.com29d96932013-12-09 13:45:02 +000075 }
skia.committer@gmail.com86cbf992014-02-24 03:01:55 +000076
halcanary@google.com3d50ea12014-01-02 13:15:13 +000077 // TODO(halcanary): verify that all callers of this function
78 // will respect new RowBytes. Will be moot once rowbytes belongs
79 // to PixelRef.
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +000080 bm->installPixels(fInfo, fTarget, fRowBytes, ct, NULL, NULL);
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000081
halcanary@google.com3d50ea12014-01-02 13:15:13 +000082 fTarget = NULL; // never alloc same pixels twice!
halcanary@google.com29d96932013-12-09 13:45:02 +000083 return true;
84 }
85
86private:
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000087 const SkImageInfo fInfo;
halcanary@google.com3d50ea12014-01-02 13:15:13 +000088 void* fTarget; // Block of memory to be supplied as pixel memory
89 // in allocPixelRef. Must be large enough to hold
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +000090 // a bitmap described by fInfo and fRowBytes
91 const size_t fRowBytes; // rowbytes for the destination bitmap
92
halcanary@google.com29d96932013-12-09 13:45:02 +000093 typedef SkBitmap::Allocator INHERITED;
94};
halcanary@google.comad04eb42013-11-21 15:32:08 +000095
halcanary@google.com29d96932013-12-09 13:45:02 +000096// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
97#ifdef SK_DEBUG
98 #define SkCheckResult(expr, value) SkASSERT((value) == (expr))
99#else
100 #define SkCheckResult(expr, value) (void)(expr)
101#endif
102
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000103#ifdef SK_DEBUG
104inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) {
105 return ((reported == actual)
106 || ((reported == kPremul_SkAlphaType)
107 && (actual == kOpaque_SkAlphaType)));
108}
109#endif // SK_DEBUG
110
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000111////////////////////////////////////////////////////////////////////////////////
112
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000113DecodingImageGenerator::DecodingImageGenerator(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000114 SkData* data,
115 SkStreamRewindable* stream,
116 const SkImageInfo& info,
117 int sampleSize,
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000118 bool ditherImage)
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000119 : fData(data)
120 , fStream(stream)
121 , fInfo(info)
122 , fSampleSize(sampleSize)
123 , fDitherImage(ditherImage)
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000124{
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000125 SkASSERT(stream != NULL);
126 SkSafeRef(fData); // may be NULL.
127}
128
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000129DecodingImageGenerator::~DecodingImageGenerator() {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000130 SkSafeUnref(fData);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000131}
132
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000133SkData* DecodingImageGenerator::onRefEncodedData() {
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.
reed33a30502014-09-11 08:42:36 -0700136 if (NULL == fData) {
137 // TODO(halcanary): SkStreamRewindable needs a refData() function
138 // which returns a cheap copy of the underlying data.
139 if (!fStream->rewind()) {
140 return NULL;
141 }
142 size_t length = fStream->getLength();
reed9594da12014-09-12 12:12:27 -0700143 if (length) {
144 fData = SkData::NewFromStream(fStream, length);
reed33a30502014-09-11 08:42:36 -0700145 }
halcanary@google.com29d96932013-12-09 13:45:02 +0000146 }
reed9594da12014-09-12 12:12:27 -0700147 return SkSafeRef(fData);
halcanary@google.comad04eb42013-11-21 15:32:08 +0000148}
149
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000150bool DecodingImageGenerator::onGetPixels(const SkImageInfo& info,
151 void* pixels, size_t rowBytes,
152 SkPMColor ctableEntries[], int* ctableCount) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000153 if (fInfo != info) {
154 // The caller has specified a different info. This is an
155 // error for this kind of SkImageGenerator. Use the Options
156 // to change the settings.
halcanary@google.com29d96932013-12-09 13:45:02 +0000157 return false;
158 }
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);
reede5ea5002014-09-03 11:54:58 -0700167 decoder->setRequireUnpremultipliedColors(info.alphaType() == kUnpremul_SkAlphaType);
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000168
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000169 SkBitmap bitmap;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000170 TargetAllocator allocator(fInfo, pixels, rowBytes);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000171 decoder->setAllocator(&allocator);
reedbfefc7c2014-06-12 17:40:00 -0700172 bool success = decoder->decode(fStream, &bitmap, info.colorType(),
scroggo2a120802014-10-22 12:07:00 -0700173 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
halcanary@google.com29d96932013-12-09 13:45:02 +0000174 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;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000180 SkASSERT(bitmap.canCopyTo(info.colorType()));
181 bool copySuccess = bitmap.copyTo(&bm, info.colorType(), &allocator);
182 if (!copySuccess || allocator.isReady()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000183 SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed.");
184 // Earlier we checked canCopyto(); we expect consistency.
185 return false;
186 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000187 SkASSERT(check_alpha(info.alphaType(), bm.alphaType()));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000188 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000189 SkASSERT(check_alpha(info.alphaType(), bitmap.alphaType()));
halcanary@google.com29d96932013-12-09 13:45:02 +0000190 }
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000191
192 if (kIndex_8_SkColorType == info.colorType()) {
193 if (kIndex_8_SkColorType != bitmap.colorType()) {
194 return false; // they asked for Index8, but we didn't receive that from decoder
195 }
196 SkColorTable* ctable = bitmap.getColorTable();
197 if (NULL == ctable) {
198 return false;
199 }
200 const int count = ctable->count();
mtklein775b8192014-12-02 09:11:25 -0800201 memcpy(ctableEntries, ctable->readColors(), count * sizeof(SkPMColor));
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000202 *ctableCount = count;
203 }
halcanary@google.com29d96932013-12-09 13:45:02 +0000204 return true;
halcanary@google.comad04eb42013-11-21 15:32:08 +0000205}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000206
sugoib227e372014-10-16 13:10:57 -0700207bool DecodingImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3],
208 size_t rowBytes[3], SkYUVColorSpace* colorSpace) {
209 if (!fStream->rewind()) {
210 return false;
211 }
212
213 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
214 if (NULL == decoder.get()) {
215 return false;
216 }
217
218 return decoder->decodeYUV8Planes(fStream, sizes, planes, rowBytes, colorSpace);
219}
220
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000221// A contructor-type function that returns NULL on failure. This
222// prevents the returned SkImageGenerator from ever being in a bad
223// state. Called by both Create() functions
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000224SkImageGenerator* CreateDecodingImageGenerator(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000225 SkData* data,
226 SkStreamRewindable* stream,
227 const SkDecodingImageGenerator::Options& opts) {
228 SkASSERT(stream);
scroggoa1193e42015-01-21 12:09:53 -0800229 SkAutoTDelete<SkStreamRewindable> autoStream(stream); // always delete this
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000230 SkAssertResult(autoStream->rewind());
231 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream));
232 if (NULL == decoder.get()) {
233 return NULL;
234 }
235 SkBitmap bitmap;
236 decoder->setSampleSize(opts.fSampleSize);
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000237 decoder->setRequireUnpremultipliedColors(opts.fRequireUnpremul);
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000238 if (!decoder->decode(stream, &bitmap, SkImageDecoder::kDecodeBounds_Mode)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000239 return NULL;
240 }
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000241 if (kUnknown_SkColorType == bitmap.colorType()) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000242 return NULL;
243 }
244
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000245 SkImageInfo info = bitmap.info();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000246
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000247 if (opts.fUseRequestedColorType && (opts.fRequestedColorType != info.colorType())) {
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 }
reede5ea5002014-09-03 11:54:58 -0700252 info = info.makeColorType(opts.fRequestedColorType);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000253 }
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000254
reede5ea5002014-09-03 11:54:58 -0700255 if (opts.fRequireUnpremul && info.alphaType() != kOpaque_SkAlphaType) {
256 info = info.makeAlphaType(kUnpremul_SkAlphaType);
commit-bot@chromium.org1ae492f2014-04-07 21:37:36 +0000257 }
scroggo2fd0d142014-07-01 07:08:19 -0700258
reede5ea5002014-09-03 11:54:58 -0700259 SkAlphaType newAlphaType = info.alphaType();
260 if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAlphaType)) {
scroggo2fd0d142014-07-01 07:08:19 -0700261 return NULL;
262 }
263
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000264 return SkNEW_ARGS(DecodingImageGenerator,
reede5ea5002014-09-03 11:54:58 -0700265 (data, autoStream.detach(), info.makeAlphaType(newAlphaType),
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000266 opts.fSampleSize, opts.fDitherImage));
halcanary@google.com29d96932013-12-09 13:45:02 +0000267}
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000268
269} // namespace
270
271////////////////////////////////////////////////////////////////////////////////
272
273SkImageGenerator* SkDecodingImageGenerator::Create(
274 SkData* data,
275 const SkDecodingImageGenerator::Options& opts) {
276 SkASSERT(data != NULL);
277 if (NULL == data) {
278 return NULL;
279 }
280 SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
281 SkASSERT(stream != NULL);
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000282 return CreateDecodingImageGenerator(data, stream, opts);
283}
284
285SkImageGenerator* SkDecodingImageGenerator::Create(
286 SkStreamRewindable* stream,
287 const SkDecodingImageGenerator::Options& opts) {
288 SkASSERT(stream != NULL);
scroggoa1193e42015-01-21 12:09:53 -0800289 if (stream == NULL) {
commit-bot@chromium.org1f2d3572014-04-04 20:13:05 +0000290 return NULL;
291 }
292 return CreateDecodingImageGenerator(NULL, stream, opts);
293}