1/* 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" 10#include "SkPixelRef.h" 11#include "SkScaledBitmapSampler.h" 12#include "SkStream.h" 13#include "SkStreamPriv.h" 14#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 39 Format getFormat() const override { 40 return kKTX_Format; 41 } 42 43protected: 44 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; 45 46private: 47 typedef SkImageDecoder INHERITED; 48}; 49 50SkImageDecoder::Result SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { 51 // TODO: Implement SkStream::copyToData() that's cheap for memory and file streams 52 SkAutoDataUnref data(SkCopyStreamToData(stream)); 53 if (NULL == data) { 54 return kFailure; 55 } 56 57 SkKTXFile ktxFile(data); 58 if (!ktxFile.valid()) { 59 return kFailure; 60 } 61 62 const unsigned short width = ktxFile.width(); 63 const unsigned short height = ktxFile.height(); 64 65 // Set a flag if our source is premultiplied alpha 66 const SkString premulKey("KTXPremultipliedAlpha"); 67 const bool bSrcIsPremul = ktxFile.getValueForKey(premulKey) == SkString("True"); 68 69 // Setup the sampler... 70 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); 71 72 // 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) { 80 return kFailure; 81 } 82 } else { 83 alphaType = kPremul_SkAlphaType; 84 } 85 } 86 87 // 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 116 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 117 return kSuccess; 118 } 119 120 // If we've made it this far, then we know how to grok the data. 121 if (!this->allocPixelRef(bm, NULL)) { 122 return kFailure; 123 } 124 125 // Lock the pixels, since we're about to write to them... 126 SkAutoLockPixels alp(*bm); 127 128 if (isCompressedAlpha) { 129 if (!sampler.begin(bm, SkScaledBitmapSampler::kGray, *this)) { 130 return kFailure; 131 } 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)) { 142 return kFailure; 143 } 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 155 return kSuccess; 156 157 } else if (ktxFile.isCompressedFormat(SkTextureCompressor::kETC1_Format)) { 158 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { 159 return kFailure; 160 } 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); 165 uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get()); 166 167 // Decode ETC1 168 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)) { 171 return kFailure; 172 } 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 184 return kSuccess; 185 186 } else if (ktxFile.isRGB8()) { 187 188 // Uncompressed RGB data (without alpha) 189 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { 190 return kFailure; 191 } 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 203 return kSuccess; 204 205 } else if (ktxFile.isRGBA8()) { 206 207 // Uncompressed RGBA data 208 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)) { 220 return kFailure; 221 } 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 233 return kSuccess; 234 } 235 236 return kFailure; 237} 238 239/////////////////////////////////////////////////////////////////////////////// 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: 254 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override; 255 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) { 262 if (!bitmap.pixelRef()) { 263 return false; 264 } 265 SkAutoDataUnref data(bitmap.pixelRef()->refEncodedData()); 266 267 // 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); 272 } 273 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... 281 } 282 283 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 305///////////////////////////////////////////////////////////////////////////////////////// 306DEFINE_DECODER_CREATOR(KTXImageDecoder); 307DEFINE_ENCODER_CREATOR(KTXImageEncoder); 308///////////////////////////////////////////////////////////////////////////////////////// 309 310static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) { 311 if (SkKTXFile::is_ktx(stream)) { 312 return SkNEW(SkKTXImageDecoder); 313 } 314 return NULL; 315} 316 317static 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 324SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) { 325 return (SkImageEncoder::kKTX_Type == t) ? SkNEW(SkKTXImageEncoder) : NULL; 326} 327 328static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory); 329static SkImageDecoder_FormatReg gFormatReg(get_format_ktx); 330static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory); 331