Home | History | Annotate | Download | only in images
      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 
     36 class SkKTXImageDecoder : public SkImageDecoder {
     37 public:
     38     SkKTXImageDecoder() { }
     39 
     40     Format getFormat() const override {
     41         return kKTX_Format;
     42     }
     43 
     44 protected:
     45     Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
     46 
     47 private:
     48     typedef SkImageDecoder INHERITED;
     49 };
     50 
     51 SkImageDecoder::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 
    253 class SkKTXImageEncoder : public SkImageEncoder {
    254 protected:
    255     bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
    256 
    257 private:
    258     virtual bool encodePKM(SkWStream* stream, const SkData *data);
    259     typedef SkImageEncoder INHERITED;
    260 };
    261 
    262 bool 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 
    287 bool 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 /////////////////////////////////////////////////////////////////////////////////////////
    307 DEFINE_DECODER_CREATOR(KTXImageDecoder);
    308 DEFINE_ENCODER_CREATOR(KTXImageEncoder);
    309 /////////////////////////////////////////////////////////////////////////////////////////
    310 
    311 static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) {
    312     if (SkKTXFile::is_ktx(stream)) {
    313         return new SkKTXImageDecoder;
    314     }
    315     return nullptr;
    316 }
    317 
    318 static 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 
    325 SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) {
    326     return (SkImageEncoder::kKTX_Type == t) ? new SkKTXImageEncoder : nullptr;
    327 }
    328 
    329 static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory);
    330 static SkImageDecoder_FormatReg gFormatReg(get_format_ktx);
    331 static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory);
    332