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 "SkImageGenerator.h" 11#include "SkPixelRef.h" 12#include "SkScaledBitmapSampler.h" 13#include "SkStream.h" 14#include "SkStreamPriv.h" 15#include "SkTypes.h" 16 17#include "ktx.h" 18#include "etc1.h" 19 20///////////////////////////////////////////////////////////////////////////////////////// 21 22 23///////////////////////////////////////////////////////////////////////////////////////// 24 25// KTX Image decoder 26// --- 27// KTX is a general texture data storage file format ratified by the Khronos Group. As an 28// overview, a KTX file contains all of the appropriate values needed to fully specify a 29// texture in an OpenGL application, including the use of compressed data. 30// 31// This decoder is meant to be used with an SkDiscardablePixelRef so that GPU backends 32// can sniff the data before creating a texture. If they encounter a compressed format 33// that they understand, they can then upload the data directly to the GPU. Otherwise, 34// they will decode the data into a format that Skia supports. 35 36class SkKTXImageDecoder : public SkImageDecoder { 37public: 38 SkKTXImageDecoder() { } 39 40 Format getFormat() const override { 41 return kKTX_Format; 42 } 43 44protected: 45 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; 46 47private: 48 typedef SkImageDecoder INHERITED; 49}; 50 51SkImageDecoder::Result SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { 52 // TODO: Implement SkStream::copyToData() that's cheap for memory and file streams 53 SkAutoDataUnref data(SkCopyStreamToData(stream)); 54 if (nullptr == data) { 55 return kFailure; 56 } 57 58 SkKTXFile ktxFile(data); 59 if (!ktxFile.valid()) { 60 return kFailure; 61 } 62 63 const unsigned short width = ktxFile.width(); 64 const unsigned short height = ktxFile.height(); 65 66 // Set a flag if our source is premultiplied alpha 67 const SkString premulKey("KTXPremultipliedAlpha"); 68 const bool bSrcIsPremul = ktxFile.getValueForKey(premulKey) == SkString("True"); 69 70 // Setup the sampler... 71 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); 72 73 // Determine the alpha of the bitmap... 74 SkAlphaType alphaType = kOpaque_SkAlphaType; 75 if (ktxFile.isRGBA8()) { 76 if (this->getRequireUnpremultipliedColors()) { 77 alphaType = kUnpremul_SkAlphaType; 78 // If the client wants unpremul colors and we only have 79 // premul, then we cannot honor their wish. 80 if (bSrcIsPremul) { 81 return kFailure; 82 } 83 } else { 84 alphaType = kPremul_SkAlphaType; 85 } 86 } 87 88 // Search through the compressed formats to see if the KTX file is holding 89 // compressed data 90 bool ktxIsCompressed = false; 91 SkTextureCompressor::Format ktxCompressedFormat; 92 for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { 93 SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); 94 if (ktxFile.isCompressedFormat(fmt)) { 95 ktxIsCompressed = true; 96 ktxCompressedFormat = fmt; 97 break; 98 } 99 } 100 101 // If the compressed format is a grayscale image, then setup the bitmap properly... 102 bool isCompressedAlpha = ktxIsCompressed && 103 ((SkTextureCompressor::kLATC_Format == ktxCompressedFormat) || 104 (SkTextureCompressor::kR11_EAC_Format == ktxCompressedFormat)); 105 106 // Set the image dimensions and underlying pixel type. 107 if (isCompressedAlpha) { 108 const int w = sampler.scaledWidth(); 109 const int h = sampler.scaledHeight(); 110 bm->setInfo(SkImageInfo::MakeA8(w, h)); 111 } else { 112 const int w = sampler.scaledWidth(); 113 const int h = sampler.scaledHeight(); 114 bm->setInfo(SkImageInfo::MakeN32(w, h, alphaType)); 115 } 116 117 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 118 return kSuccess; 119 } 120 121 // If we've made it this far, then we know how to grok the data. 122 if (!this->allocPixelRef(bm, nullptr)) { 123 return kFailure; 124 } 125 126 // Lock the pixels, since we're about to write to them... 127 SkAutoLockPixels alp(*bm); 128 129 if (isCompressedAlpha) { 130 if (!sampler.begin(bm, SkScaledBitmapSampler::kGray, *this)) { 131 return kFailure; 132 } 133 134 // Alpha data is only a single byte per pixel. 135 int nPixels = width * height; 136 SkAutoMalloc outRGBData(nPixels); 137 uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get()); 138 139 // Decode the compressed format 140 const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); 141 if (!SkTextureCompressor::DecompressBufferFromFormat( 142 outRGBDataPtr, width, buf, width, height, ktxCompressedFormat)) { 143 return kFailure; 144 } 145 146 // Set each of the pixels... 147 const int srcRowBytes = width; 148 const int dstHeight = sampler.scaledHeight(); 149 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr); 150 srcRow += sampler.srcY0() * srcRowBytes; 151 for (int y = 0; y < dstHeight; ++y) { 152 sampler.next(srcRow); 153 srcRow += sampler.srcDY() * srcRowBytes; 154 } 155 156 return kSuccess; 157 158 } else if (ktxFile.isCompressedFormat(SkTextureCompressor::kETC1_Format)) { 159 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { 160 return kFailure; 161 } 162 163 // ETC1 Data is encoded as RGB pixels, so we should extract it as such 164 int nPixels = width * height; 165 SkAutoMalloc outRGBData(nPixels * 3); 166 uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get()); 167 168 // Decode ETC1 169 const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); 170 if (!SkTextureCompressor::DecompressBufferFromFormat( 171 outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) { 172 return kFailure; 173 } 174 175 // Set each of the pixels... 176 const int srcRowBytes = width * 3; 177 const int dstHeight = sampler.scaledHeight(); 178 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr); 179 srcRow += sampler.srcY0() * srcRowBytes; 180 for (int y = 0; y < dstHeight; ++y) { 181 sampler.next(srcRow); 182 srcRow += sampler.srcDY() * srcRowBytes; 183 } 184 185 return kSuccess; 186 187 } else if (ktxFile.isRGB8()) { 188 189 // Uncompressed RGB data (without alpha) 190 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { 191 return kFailure; 192 } 193 194 // Just need to read RGB pixels 195 const int srcRowBytes = width * 3; 196 const int dstHeight = sampler.scaledHeight(); 197 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); 198 srcRow += sampler.srcY0() * srcRowBytes; 199 for (int y = 0; y < dstHeight; ++y) { 200 sampler.next(srcRow); 201 srcRow += sampler.srcDY() * srcRowBytes; 202 } 203 204 return kSuccess; 205 206 } else if (ktxFile.isRGBA8()) { 207 208 // Uncompressed RGBA data 209 210 // If we know that the image contains premultiplied alpha, then 211 // we need to turn off the premultiplier 212 SkScaledBitmapSampler::Options opts (*this); 213 if (bSrcIsPremul) { 214 SkASSERT(bm->alphaType() == kPremul_SkAlphaType); 215 SkASSERT(!this->getRequireUnpremultipliedColors()); 216 217 opts.fPremultiplyAlpha = false; 218 } 219 220 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, opts)) { 221 return kFailure; 222 } 223 224 // Just need to read RGBA pixels 225 const int srcRowBytes = width * 4; 226 const int dstHeight = sampler.scaledHeight(); 227 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); 228 srcRow += sampler.srcY0() * srcRowBytes; 229 for (int y = 0; y < dstHeight; ++y) { 230 sampler.next(srcRow); 231 srcRow += sampler.srcDY() * srcRowBytes; 232 } 233 234 return kSuccess; 235 } 236 237 return kFailure; 238} 239 240/////////////////////////////////////////////////////////////////////////////// 241 242// KTX Image Encoder 243// 244// This encoder takes a best guess at how to encode the bitmap passed to it. If 245// there is an installed discardable pixel ref with existing PKM data, then we 246// will repurpose the existing ETC1 data into a KTX file. If the data contains 247// KTX data, then we simply return a copy of the same data. For all other files, 248// the underlying KTX library tries to do its best to encode the appropriate 249// data specified by the bitmap based on the config. (i.e. kAlpha8_Config will 250// be represented as a full resolution 8-bit image dump with the appropriate 251// OpenGL defines in the header). 252 253class SkKTXImageEncoder : public SkImageEncoder { 254protected: 255 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override; 256 257private: 258 virtual bool encodePKM(SkWStream* stream, const SkData *data); 259 typedef SkImageEncoder INHERITED; 260}; 261 262bool SkKTXImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) { 263 if (!bitmap.pixelRef()) { 264 return false; 265 } 266 SkAutoDataUnref data(bitmap.pixelRef()->refEncodedData()); 267 268 // Is this even encoded data? 269 if (data) { 270 const uint8_t *bytes = data->bytes(); 271 if (etc1_pkm_is_valid(bytes)) { 272 return this->encodePKM(stream, data); 273 } 274 275 // Is it a KTX file?? 276 if (SkKTXFile::is_ktx(bytes)) { 277 return stream->write(bytes, data->size()); 278 } 279 280 // If it's neither a KTX nor a PKM, then we need to 281 // get at the actual pixels, so fall through and decompress... 282 } 283 284 return SkKTXFile::WriteBitmapToKTX(stream, bitmap); 285} 286 287bool SkKTXImageEncoder::encodePKM(SkWStream* stream, const SkData *data) { 288 const uint8_t* bytes = data->bytes(); 289 SkASSERT(etc1_pkm_is_valid(bytes)); 290 291 etc1_uint32 width = etc1_pkm_get_width(bytes); 292 etc1_uint32 height = etc1_pkm_get_height(bytes); 293 294 // ETC1 Data is stored as compressed 4x4 pixel blocks, so we must make sure 295 // that our dimensions are valid. 296 if (width == 0 || (width & 3) != 0 || height == 0 || (height & 3) != 0) { 297 return false; 298 } 299 300 // Advance pointer to etc1 data. 301 bytes += ETC_PKM_HEADER_SIZE; 302 303 return SkKTXFile::WriteETC1ToKTX(stream, bytes, width, height); 304} 305 306///////////////////////////////////////////////////////////////////////////////////////// 307DEFINE_DECODER_CREATOR(KTXImageDecoder); 308DEFINE_ENCODER_CREATOR(KTXImageEncoder); 309///////////////////////////////////////////////////////////////////////////////////////// 310 311static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) { 312 if (SkKTXFile::is_ktx(stream)) { 313 return new SkKTXImageDecoder; 314 } 315 return nullptr; 316} 317 318static SkImageDecoder::Format get_format_ktx(SkStreamRewindable* stream) { 319 if (SkKTXFile::is_ktx(stream)) { 320 return SkImageDecoder::kKTX_Format; 321 } 322 return SkImageDecoder::kUnknown_Format; 323} 324 325SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) { 326 return (SkImageEncoder::kKTX_Type == t) ? new SkKTXImageEncoder : nullptr; 327} 328 329static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory); 330static SkImageDecoder_FormatReg gFormatReg(get_format_ktx); 331static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory); 332