| /* | |
| * 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 "SkTextureCompressor.h" | |
| #include "SkTextureCompressor_ASTC.h" | |
| #include "SkTextureCompressor_LATC.h" | |
| #include "SkTextureCompressor_R11EAC.h" | |
| #include "SkBitmap.h" | |
| #include "SkBitmapProcShader.h" | |
| #include "SkData.h" | |
| #include "SkEndian.h" | |
| #include "SkTextureCompression_opts.h" | |
| #ifndef SK_IGNORE_ETC1_SUPPORT | |
| # include "etc1.h" | |
| #endif | |
| // Convert ETC1 functions to our function signatures | |
| static bool compress_etc1_565(uint8_t* dst, const uint8_t* src, | |
| int width, int height, int rowBytes) { | |
| #ifndef SK_IGNORE_ETC1_SUPPORT | |
| return 0 == etc1_encode_image(src, width, height, 2, rowBytes, dst); | |
| #else | |
| return false; | |
| #endif | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| namespace SkTextureCompressor { | |
| void GetBlockDimensions(Format format, int* dimX, int* dimY, bool matchSpec) { | |
| if (NULL == dimX || NULL == dimY) { | |
| return; | |
| } | |
| if (!matchSpec && SkTextureCompressorGetPlatformDims(format, dimX, dimY)) { | |
| return; | |
| } | |
| // No specialized arguments, return the dimensions as they are in the spec. | |
| static const struct FormatDimensions { | |
| const int fBlockSizeX; | |
| const int fBlockSizeY; | |
| } kFormatDimensions[kFormatCnt] = { | |
| { 4, 4 }, // kLATC_Format | |
| { 4, 4 }, // kR11_EAC_Format | |
| { 4, 4 }, // kETC1_Format | |
| { 4, 4 }, // kASTC_4x4_Format | |
| { 5, 4 }, // kASTC_5x4_Format | |
| { 5, 5 }, // kASTC_5x5_Format | |
| { 6, 5 }, // kASTC_6x5_Format | |
| { 6, 6 }, // kASTC_6x6_Format | |
| { 8, 5 }, // kASTC_8x5_Format | |
| { 8, 6 }, // kASTC_8x6_Format | |
| { 8, 8 }, // kASTC_8x8_Format | |
| { 10, 5 }, // kASTC_10x5_Format | |
| { 10, 6 }, // kASTC_10x6_Format | |
| { 10, 8 }, // kASTC_10x8_Format | |
| { 10, 10 }, // kASTC_10x10_Format | |
| { 12, 10 }, // kASTC_12x10_Format | |
| { 12, 12 }, // kASTC_12x12_Format | |
| }; | |
| *dimX = kFormatDimensions[format].fBlockSizeX; | |
| *dimY = kFormatDimensions[format].fBlockSizeY; | |
| } | |
| int GetCompressedDataSize(Format fmt, int width, int height) { | |
| int dimX, dimY; | |
| GetBlockDimensions(fmt, &dimX, &dimY, true); | |
| int encodedBlockSize = 0; | |
| switch (fmt) { | |
| // These formats are 64 bits per 4x4 block. | |
| case kLATC_Format: | |
| case kR11_EAC_Format: | |
| case kETC1_Format: | |
| encodedBlockSize = 8; | |
| break; | |
| // This format is 128 bits. | |
| case kASTC_4x4_Format: | |
| case kASTC_5x4_Format: | |
| case kASTC_5x5_Format: | |
| case kASTC_6x5_Format: | |
| case kASTC_6x6_Format: | |
| case kASTC_8x5_Format: | |
| case kASTC_8x6_Format: | |
| case kASTC_8x8_Format: | |
| case kASTC_10x5_Format: | |
| case kASTC_10x6_Format: | |
| case kASTC_10x8_Format: | |
| case kASTC_10x10_Format: | |
| case kASTC_12x10_Format: | |
| case kASTC_12x12_Format: | |
| encodedBlockSize = 16; | |
| break; | |
| default: | |
| SkFAIL("Unknown compressed format!"); | |
| return -1; | |
| } | |
| if(((width % dimX) == 0) && ((height % dimY) == 0)) { | |
| const int blocksX = width / dimX; | |
| const int blocksY = height / dimY; | |
| return blocksX * blocksY * encodedBlockSize; | |
| } | |
| return -1; | |
| } | |
| bool CompressBufferToFormat(uint8_t* dst, const uint8_t* src, SkColorType srcColorType, | |
| int width, int height, int rowBytes, Format format, bool opt) { | |
| CompressionProc proc = NULL; | |
| if (opt) { | |
| proc = SkTextureCompressorGetPlatformProc(srcColorType, format); | |
| } | |
| if (NULL == proc) { | |
| switch (srcColorType) { | |
| case kAlpha_8_SkColorType: | |
| { | |
| switch (format) { | |
| case kLATC_Format: | |
| proc = CompressA8ToLATC; | |
| break; | |
| case kR11_EAC_Format: | |
| proc = CompressA8ToR11EAC; | |
| break; | |
| case kASTC_12x12_Format: | |
| proc = CompressA8To12x12ASTC; | |
| break; | |
| default: | |
| // Do nothing... | |
| break; | |
| } | |
| } | |
| break; | |
| case kRGB_565_SkColorType: | |
| { | |
| switch (format) { | |
| case kETC1_Format: | |
| proc = compress_etc1_565; | |
| break; | |
| default: | |
| // Do nothing... | |
| break; | |
| } | |
| } | |
| break; | |
| default: | |
| // Do nothing... | |
| break; | |
| } | |
| } | |
| if (NULL != proc) { | |
| return proc(dst, src, width, height, rowBytes); | |
| } | |
| return false; | |
| } | |
| SkData *CompressBitmapToFormat(const SkBitmap &bitmap, Format format) { | |
| SkAutoLockPixels alp(bitmap); | |
| int compressedDataSize = GetCompressedDataSize(format, bitmap.width(), bitmap.height()); | |
| if (compressedDataSize < 0) { | |
| return NULL; | |
| } | |
| const uint8_t* src = reinterpret_cast<const uint8_t*>(bitmap.getPixels()); | |
| uint8_t* dst = reinterpret_cast<uint8_t*>(sk_malloc_throw(compressedDataSize)); | |
| if (CompressBufferToFormat(dst, src, bitmap.colorType(), bitmap.width(), bitmap.height(), | |
| bitmap.rowBytes(), format)) { | |
| return SkData::NewFromMalloc(dst, compressedDataSize); | |
| } | |
| sk_free(dst); | |
| return NULL; | |
| } | |
| SkBlitter* CreateBlitterForFormat(int width, int height, void* compressedBuffer, | |
| SkTBlitterAllocator *allocator, Format format) { | |
| switch(format) { | |
| case kLATC_Format: | |
| return CreateLATCBlitter(width, height, compressedBuffer, allocator); | |
| case kR11_EAC_Format: | |
| return CreateR11EACBlitter(width, height, compressedBuffer, allocator); | |
| case kASTC_12x12_Format: | |
| return CreateASTCBlitter(width, height, compressedBuffer, allocator); | |
| default: | |
| return NULL; | |
| } | |
| return NULL; | |
| } | |
| bool DecompressBufferFromFormat(uint8_t* dst, int dstRowBytes, const uint8_t* src, | |
| int width, int height, Format format) { | |
| int dimX, dimY; | |
| GetBlockDimensions(format, &dimX, &dimY, true); | |
| if (width < 0 || ((width % dimX) != 0) || height < 0 || ((height % dimY) != 0)) { | |
| return false; | |
| } | |
| switch(format) { | |
| case kLATC_Format: | |
| DecompressLATC(dst, dstRowBytes, src, width, height); | |
| return true; | |
| case kR11_EAC_Format: | |
| DecompressR11EAC(dst, dstRowBytes, src, width, height); | |
| return true; | |
| #ifndef SK_IGNORE_ETC1_SUPPORT | |
| case kETC1_Format: | |
| return 0 == etc1_decode_image(src, dst, width, height, 3, dstRowBytes); | |
| #endif | |
| case kASTC_4x4_Format: | |
| case kASTC_5x4_Format: | |
| case kASTC_5x5_Format: | |
| case kASTC_6x5_Format: | |
| case kASTC_6x6_Format: | |
| case kASTC_8x5_Format: | |
| case kASTC_8x6_Format: | |
| case kASTC_8x8_Format: | |
| case kASTC_10x5_Format: | |
| case kASTC_10x6_Format: | |
| case kASTC_10x8_Format: | |
| case kASTC_10x10_Format: | |
| case kASTC_12x10_Format: | |
| case kASTC_12x12_Format: | |
| DecompressASTC(dst, dstRowBytes, src, width, height, dimX, dimY); | |
| return true; | |
| default: | |
| // Do nothing... | |
| break; | |
| } | |
| return false; | |
| } | |
| } // namespace SkTextureCompressor |