/*
* 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, size_t rowBytes) {
#ifndef SK_IGNORE_ETC1_SUPPORT
return 0 == etc1_encode_image(src, width, height, 2, SkToInt(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, size_t 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 (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());
SkData* dst = SkData::NewUninitialized(compressedDataSize);
if (!CompressBufferToFormat((uint8_t*)dst->writable_data(), src, bitmap.colorType(),
bitmap.width(), bitmap.height(), bitmap.rowBytes(), format)) {
dst->unref();
dst = NULL;
}
return dst;
}
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