blob: a95ab6f602d3610918b5874b10de50a76fd894de [file] [log] [blame]
krajcevski99ffe242014-06-03 13:04:35 -07001/*
2 * Copyright 2014 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
8#include "SkColorPriv.h"
9#include "SkImageDecoder.h"
krajcevskic250d2e2014-06-06 06:16:28 -070010#include "SkPixelRef.h"
krajcevski99ffe242014-06-03 13:04:35 -070011#include "SkScaledBitmapSampler.h"
12#include "SkStream.h"
halcanary67ec1f82014-06-27 11:36:20 -070013#include "SkStreamPriv.h"
krajcevski99ffe242014-06-03 13:04:35 -070014#include "SkTypes.h"
15
16#include "ktx.h"
17#include "etc1.h"
18
19/////////////////////////////////////////////////////////////////////////////////////////
20
21
22/////////////////////////////////////////////////////////////////////////////////////////
23
24// KTX Image decoder
25// ---
26// KTX is a general texture data storage file format ratified by the Khronos Group. As an
27// overview, a KTX file contains all of the appropriate values needed to fully specify a
28// texture in an OpenGL application, including the use of compressed data.
29//
30// This decoder is meant to be used with an SkDiscardablePixelRef so that GPU backends
31// can sniff the data before creating a texture. If they encounter a compressed format
32// that they understand, they can then upload the data directly to the GPU. Otherwise,
33// they will decode the data into a format that Skia supports.
34
35class SkKTXImageDecoder : public SkImageDecoder {
36public:
37 SkKTXImageDecoder() { }
38
mtklein36352bf2015-03-25 18:17:31 -070039 Format getFormat() const override {
krajcevski99ffe242014-06-03 13:04:35 -070040 return kKTX_Format;
41 }
42
43protected:
mtklein36352bf2015-03-25 18:17:31 -070044 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
krajcevski99ffe242014-06-03 13:04:35 -070045
46private:
47 typedef SkImageDecoder INHERITED;
48};
49
scroggo2a120802014-10-22 12:07:00 -070050SkImageDecoder::Result SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
krajcevski99ffe242014-06-03 13:04:35 -070051 // TODO: Implement SkStream::copyToData() that's cheap for memory and file streams
halcanary67ec1f82014-06-27 11:36:20 -070052 SkAutoDataUnref data(SkCopyStreamToData(stream));
halcanary96fcdcc2015-08-27 07:41:13 -070053 if (nullptr == data) {
scroggo2a120802014-10-22 12:07:00 -070054 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -070055 }
56
57 SkKTXFile ktxFile(data);
58 if (!ktxFile.valid()) {
scroggo2a120802014-10-22 12:07:00 -070059 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -070060 }
61
62 const unsigned short width = ktxFile.width();
63 const unsigned short height = ktxFile.height();
64
krajcevski3b570c62014-06-10 09:17:48 -070065 // Set a flag if our source is premultiplied alpha
66 const SkString premulKey("KTXPremultipliedAlpha");
67 const bool bSrcIsPremul = ktxFile.getValueForKey(premulKey) == SkString("True");
68
krajcevski99ffe242014-06-03 13:04:35 -070069 // Setup the sampler...
70 SkScaledBitmapSampler sampler(width, height, this->getSampleSize());
71
krajcevski3b570c62014-06-10 09:17:48 -070072 // Determine the alpha of the bitmap...
73 SkAlphaType alphaType = kOpaque_SkAlphaType;
74 if (ktxFile.isRGBA8()) {
75 if (this->getRequireUnpremultipliedColors()) {
76 alphaType = kUnpremul_SkAlphaType;
77 // If the client wants unpremul colors and we only have
78 // premul, then we cannot honor their wish.
79 if (bSrcIsPremul) {
scroggo2a120802014-10-22 12:07:00 -070080 return kFailure;
krajcevski3b570c62014-06-10 09:17:48 -070081 }
82 } else {
83 alphaType = kPremul_SkAlphaType;
84 }
85 }
86
krajcevski86bc1242014-08-07 10:58:43 -070087 // Search through the compressed formats to see if the KTX file is holding
88 // compressed data
89 bool ktxIsCompressed = false;
90 SkTextureCompressor::Format ktxCompressedFormat;
91 for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
92 SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
93 if (ktxFile.isCompressedFormat(fmt)) {
94 ktxIsCompressed = true;
95 ktxCompressedFormat = fmt;
96 break;
97 }
98 }
99
100 // If the compressed format is a grayscale image, then setup the bitmap properly...
101 bool isCompressedAlpha = ktxIsCompressed &&
102 ((SkTextureCompressor::kLATC_Format == ktxCompressedFormat) ||
103 (SkTextureCompressor::kR11_EAC_Format == ktxCompressedFormat));
104
105 // Set the image dimensions and underlying pixel type.
106 if (isCompressedAlpha) {
107 const int w = sampler.scaledWidth();
108 const int h = sampler.scaledHeight();
109 bm->setInfo(SkImageInfo::MakeA8(w, h));
110 } else {
111 const int w = sampler.scaledWidth();
112 const int h = sampler.scaledHeight();
113 bm->setInfo(SkImageInfo::MakeN32(w, h, alphaType));
114 }
115
krajcevski99ffe242014-06-03 13:04:35 -0700116 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700117 return kSuccess;
krajcevski99ffe242014-06-03 13:04:35 -0700118 }
krajcevski86bc1242014-08-07 10:58:43 -0700119
krajcevski99ffe242014-06-03 13:04:35 -0700120 // If we've made it this far, then we know how to grok the data.
halcanary96fcdcc2015-08-27 07:41:13 -0700121 if (!this->allocPixelRef(bm, nullptr)) {
scroggo2a120802014-10-22 12:07:00 -0700122 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -0700123 }
124
125 // Lock the pixels, since we're about to write to them...
126 SkAutoLockPixels alp(*bm);
127
krajcevski86bc1242014-08-07 10:58:43 -0700128 if (isCompressedAlpha) {
129 if (!sampler.begin(bm, SkScaledBitmapSampler::kGray, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700130 return kFailure;
krajcevski86bc1242014-08-07 10:58:43 -0700131 }
132
133 // Alpha data is only a single byte per pixel.
134 int nPixels = width * height;
135 SkAutoMalloc outRGBData(nPixels);
136 uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get());
137
138 // Decode the compressed format
139 const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
140 if (!SkTextureCompressor::DecompressBufferFromFormat(
141 outRGBDataPtr, width, buf, width, height, ktxCompressedFormat)) {
scroggo2a120802014-10-22 12:07:00 -0700142 return kFailure;
krajcevski86bc1242014-08-07 10:58:43 -0700143 }
144
145 // Set each of the pixels...
146 const int srcRowBytes = width;
147 const int dstHeight = sampler.scaledHeight();
148 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
149 srcRow += sampler.srcY0() * srcRowBytes;
150 for (int y = 0; y < dstHeight; ++y) {
151 sampler.next(srcRow);
152 srcRow += sampler.srcDY() * srcRowBytes;
153 }
154
scroggo2a120802014-10-22 12:07:00 -0700155 return kSuccess;
krajcevski86bc1242014-08-07 10:58:43 -0700156
157 } else if (ktxFile.isCompressedFormat(SkTextureCompressor::kETC1_Format)) {
krajcevski99ffe242014-06-03 13:04:35 -0700158 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700159 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -0700160 }
161
162 // ETC1 Data is encoded as RGB pixels, so we should extract it as such
163 int nPixels = width * height;
164 SkAutoMalloc outRGBData(nPixels * 3);
krajcevski40a1e112014-08-05 14:13:36 -0700165 uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get());
krajcevski99ffe242014-06-03 13:04:35 -0700166
167 // Decode ETC1
krajcevski40a1e112014-08-05 14:13:36 -0700168 const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
169 if (!SkTextureCompressor::DecompressBufferFromFormat(
170 outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) {
scroggo2a120802014-10-22 12:07:00 -0700171 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -0700172 }
173
174 // Set each of the pixels...
175 const int srcRowBytes = width * 3;
176 const int dstHeight = sampler.scaledHeight();
177 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
178 srcRow += sampler.srcY0() * srcRowBytes;
179 for (int y = 0; y < dstHeight; ++y) {
180 sampler.next(srcRow);
181 srcRow += sampler.srcDY() * srcRowBytes;
182 }
183
scroggo2a120802014-10-22 12:07:00 -0700184 return kSuccess;
krajcevski99ffe242014-06-03 13:04:35 -0700185
186 } else if (ktxFile.isRGB8()) {
187
188 // Uncompressed RGB data (without alpha)
189 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700190 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -0700191 }
192
193 // Just need to read RGB pixels
194 const int srcRowBytes = width * 3;
195 const int dstHeight = sampler.scaledHeight();
196 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
197 srcRow += sampler.srcY0() * srcRowBytes;
198 for (int y = 0; y < dstHeight; ++y) {
199 sampler.next(srcRow);
200 srcRow += sampler.srcDY() * srcRowBytes;
201 }
202
scroggo2a120802014-10-22 12:07:00 -0700203 return kSuccess;
krajcevski99ffe242014-06-03 13:04:35 -0700204
205 } else if (ktxFile.isRGBA8()) {
206
207 // Uncompressed RGBA data
krajcevski3b570c62014-06-10 09:17:48 -0700208
209 // If we know that the image contains premultiplied alpha, then
210 // we need to turn off the premultiplier
211 SkScaledBitmapSampler::Options opts (*this);
212 if (bSrcIsPremul) {
213 SkASSERT(bm->alphaType() == kPremul_SkAlphaType);
214 SkASSERT(!this->getRequireUnpremultipliedColors());
215
216 opts.fPremultiplyAlpha = false;
217 }
218
219 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, opts)) {
scroggo2a120802014-10-22 12:07:00 -0700220 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -0700221 }
222
223 // Just need to read RGBA pixels
224 const int srcRowBytes = width * 4;
225 const int dstHeight = sampler.scaledHeight();
226 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
227 srcRow += sampler.srcY0() * srcRowBytes;
228 for (int y = 0; y < dstHeight; ++y) {
229 sampler.next(srcRow);
230 srcRow += sampler.srcDY() * srcRowBytes;
231 }
232
scroggo2a120802014-10-22 12:07:00 -0700233 return kSuccess;
krajcevski99ffe242014-06-03 13:04:35 -0700234 }
235
scroggo2a120802014-10-22 12:07:00 -0700236 return kFailure;
krajcevski99ffe242014-06-03 13:04:35 -0700237}
238
krajcevskic250d2e2014-06-06 06:16:28 -0700239///////////////////////////////////////////////////////////////////////////////
240
241// KTX Image Encoder
242//
243// This encoder takes a best guess at how to encode the bitmap passed to it. If
244// there is an installed discardable pixel ref with existing PKM data, then we
245// will repurpose the existing ETC1 data into a KTX file. If the data contains
246// KTX data, then we simply return a copy of the same data. For all other files,
247// the underlying KTX library tries to do its best to encode the appropriate
248// data specified by the bitmap based on the config. (i.e. kAlpha8_Config will
249// be represented as a full resolution 8-bit image dump with the appropriate
250// OpenGL defines in the header).
251
252class SkKTXImageEncoder : public SkImageEncoder {
253protected:
mtklein36352bf2015-03-25 18:17:31 -0700254 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
krajcevskic250d2e2014-06-06 06:16:28 -0700255
256private:
257 virtual bool encodePKM(SkWStream* stream, const SkData *data);
258 typedef SkImageEncoder INHERITED;
259};
260
261bool SkKTXImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) {
scroggoc2dcf4a2014-07-02 15:00:07 -0700262 if (!bitmap.pixelRef()) {
263 return false;
264 }
reedc9e190d2015-09-28 09:58:41 -0700265 SkAutoDataUnref data(bitmap.pixelRef()->refEncodedData());
krajcevskic250d2e2014-06-06 06:16:28 -0700266
reedc9e190d2015-09-28 09:58:41 -0700267 // Is this even encoded data?
268 if (data) {
269 const uint8_t *bytes = data->bytes();
270 if (etc1_pkm_is_valid(bytes)) {
271 return this->encodePKM(stream, data);
krajcevskic250d2e2014-06-06 06:16:28 -0700272 }
reedc9e190d2015-09-28 09:58:41 -0700273
274 // Is it a KTX file??
275 if (SkKTXFile::is_ktx(bytes)) {
276 return stream->write(bytes, data->size());
277 }
278
279 // If it's neither a KTX nor a PKM, then we need to
280 // get at the actual pixels, so fall through and decompress...
krajcevskic250d2e2014-06-06 06:16:28 -0700281 }
reedc9e190d2015-09-28 09:58:41 -0700282
krajcevskic250d2e2014-06-06 06:16:28 -0700283 return SkKTXFile::WriteBitmapToKTX(stream, bitmap);
284}
285
286bool SkKTXImageEncoder::encodePKM(SkWStream* stream, const SkData *data) {
287 const uint8_t* bytes = data->bytes();
288 SkASSERT(etc1_pkm_is_valid(bytes));
289
290 etc1_uint32 width = etc1_pkm_get_width(bytes);
291 etc1_uint32 height = etc1_pkm_get_height(bytes);
292
293 // ETC1 Data is stored as compressed 4x4 pixel blocks, so we must make sure
294 // that our dimensions are valid.
295 if (width == 0 || (width & 3) != 0 || height == 0 || (height & 3) != 0) {
296 return false;
297 }
298
299 // Advance pointer to etc1 data.
300 bytes += ETC_PKM_HEADER_SIZE;
301
302 return SkKTXFile::WriteETC1ToKTX(stream, bytes, width, height);
303}
304
krajcevski99ffe242014-06-03 13:04:35 -0700305/////////////////////////////////////////////////////////////////////////////////////////
306DEFINE_DECODER_CREATOR(KTXImageDecoder);
krajcevskic250d2e2014-06-06 06:16:28 -0700307DEFINE_ENCODER_CREATOR(KTXImageEncoder);
krajcevski99ffe242014-06-03 13:04:35 -0700308/////////////////////////////////////////////////////////////////////////////////////////
309
310static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) {
311 if (SkKTXFile::is_ktx(stream)) {
halcanary385fe4d2015-08-26 13:07:48 -0700312 return new SkKTXImageDecoder;
krajcevski99ffe242014-06-03 13:04:35 -0700313 }
halcanary96fcdcc2015-08-27 07:41:13 -0700314 return nullptr;
krajcevski99ffe242014-06-03 13:04:35 -0700315}
316
krajcevski99ffe242014-06-03 13:04:35 -0700317static SkImageDecoder::Format get_format_ktx(SkStreamRewindable* stream) {
318 if (SkKTXFile::is_ktx(stream)) {
319 return SkImageDecoder::kKTX_Format;
320 }
321 return SkImageDecoder::kUnknown_Format;
322}
323
krajcevskic250d2e2014-06-06 06:16:28 -0700324SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) {
halcanary96fcdcc2015-08-27 07:41:13 -0700325 return (SkImageEncoder::kKTX_Type == t) ? new SkKTXImageEncoder : nullptr;
krajcevskic250d2e2014-06-06 06:16:28 -0700326}
327
328static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory);
krajcevski99ffe242014-06-03 13:04:35 -0700329static SkImageDecoder_FormatReg gFormatReg(get_format_ktx);
krajcevskic250d2e2014-06-06 06:16:28 -0700330static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory);