/* * Copyright 2014 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkEndian.h" #include "SkColorPriv.h" #include "SkImageDecoder.h" #include "SkScaledBitmapSampler.h" #include "SkStream.h" #include "SkStreamPriv.h" #include "SkTypes.h" #include "SkTextureCompressor.h" class SkASTCImageDecoder : public SkImageDecoder { public: SkASTCImageDecoder() { } virtual Format getFormat() const SK_OVERRIDE { return kASTC_Format; } protected: virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; private: typedef SkImageDecoder INHERITED; }; ///////////////////////////////////////////////////////////////////////////////////////// static const uint32_t kASTCMagicNumber = 0x5CA1AB13; static inline int read_24bit(const uint8_t* buf) { // Assume everything is little endian... return static_cast(buf[0]) | (static_cast(buf[1]) << 8) | (static_cast(buf[2]) << 16); } SkImageDecoder::Result SkASTCImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { SkAutoMalloc autoMal; const size_t length = SkCopyStreamToStorage(&autoMal, stream); if (0 == length) { return kFailure; } unsigned char* buf = (unsigned char*)autoMal.get(); // Make sure that the magic header is there... SkASSERT(SkEndian_SwapLE32(*(reinterpret_cast(buf))) == kASTCMagicNumber); // Advance past the magic header buf += 4; const int blockDimX = buf[0]; const int blockDimY = buf[1]; const int blockDimZ = buf[2]; if (1 != blockDimZ) { // We don't support decoding 3D return kFailure; } // Choose the proper ASTC format SkTextureCompressor::Format astcFormat; if (4 == blockDimX && 4 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_4x4_Format; } else if (5 == blockDimX && 4 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_5x4_Format; } else if (5 == blockDimX && 5 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_5x5_Format; } else if (6 == blockDimX && 5 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_6x5_Format; } else if (6 == blockDimX && 6 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_6x6_Format; } else if (8 == blockDimX && 5 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_8x5_Format; } else if (8 == blockDimX && 6 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_8x6_Format; } else if (8 == blockDimX && 8 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_8x8_Format; } else if (10 == blockDimX && 5 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_10x5_Format; } else if (10 == blockDimX && 6 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_10x6_Format; } else if (10 == blockDimX && 8 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_10x8_Format; } else if (10 == blockDimX && 10 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_10x10_Format; } else if (12 == blockDimX && 10 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_12x10_Format; } else if (12 == blockDimX && 12 == blockDimY) { astcFormat = SkTextureCompressor::kASTC_12x12_Format; } else { // We don't support any other block dimensions.. return kFailure; } // Advance buf past the block dimensions buf += 3; // Read the width/height/depth from the buffer... const int width = read_24bit(buf); const int height = read_24bit(buf + 3); const int depth = read_24bit(buf + 6); if (1 != depth) { // We don't support decoding 3D. return kFailure; } // Advance the buffer past the image dimensions buf += 9; #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER // should we allow the Chooser (if present) to pick a config for us??? if (!this->chooseFromOneChoice(kN32_SkColorType, width, height)) { return kFailure; } #endif // Setup the sampler... SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); // Determine the alpha of the bitmap... SkAlphaType alphaType = kOpaque_SkAlphaType; if (this->getRequireUnpremultipliedColors()) { alphaType = kUnpremul_SkAlphaType; } else { alphaType = kPremul_SkAlphaType; } // Set the config... bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(), alphaType)); if (SkImageDecoder::kDecodeBounds_Mode == mode) { return kSuccess; } if (!this->allocPixelRef(bm, NULL)) { return kFailure; } // Lock the pixels, since we're about to write to them... SkAutoLockPixels alp(*bm); if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, *this)) { return kFailure; } // ASTC Data is encoded as RGBA pixels, so we should extract it as such int nPixels = width * height; SkAutoMalloc outRGBAData(nPixels * 4); uint8_t *outRGBADataPtr = reinterpret_cast(outRGBAData.get()); // Decode ASTC if (!SkTextureCompressor::DecompressBufferFromFormat( outRGBADataPtr, width*4, buf, width, height, astcFormat)) { return kFailure; } // Set each of the pixels... const int srcRowBytes = width * 4; const int dstHeight = sampler.scaledHeight(); const uint8_t *srcRow = reinterpret_cast(outRGBADataPtr); srcRow += sampler.srcY0() * srcRowBytes; for (int y = 0; y < dstHeight; ++y) { sampler.next(srcRow); srcRow += sampler.srcDY() * srcRowBytes; } return kSuccess; } ///////////////////////////////////////////////////////////////////////////////////////// DEFINE_DECODER_CREATOR(ASTCImageDecoder); ///////////////////////////////////////////////////////////////////////////////////////// static bool is_astc(SkStreamRewindable* stream) { // Read the ASTC header and make sure it's valid. uint32_t magic; if (stream->read((void*)&magic, 4) != 4) { return false; } return kASTCMagicNumber == SkEndian_SwapLE32(magic); } static SkImageDecoder* sk_libastc_dfactory(SkStreamRewindable* stream) { if (is_astc(stream)) { return SkNEW(SkASTCImageDecoder); } return NULL; } static SkImageDecoder_DecodeReg gReg(sk_libastc_dfactory); static SkImageDecoder::Format get_format_astc(SkStreamRewindable* stream) { if (is_astc(stream)) { return SkImageDecoder::kASTC_Format; } return SkImageDecoder::kUnknown_Format; } static SkImageDecoder_FormatReg gFormatReg(get_format_astc);