Evas: Implement full ETC2 encoder
Supports T, H, and Planar mode as well as Alpha. Merge branch 'devs/jpeg/etc2' @feature
This commit is contained in:
commit
bb3d5190b8
|
@ -1816,6 +1816,7 @@ modules/evas/savers/tgv/evas_image_save_tgv.c \
|
|||
static_libs/rg_etc/rg_etc1.c \
|
||||
static_libs/rg_etc/rg_etc2.c \
|
||||
static_libs/rg_etc/rg_etc1.h \
|
||||
static_libs/rg_etc/etc2_encoder.c \
|
||||
static_libs/lz4/lz4.c \
|
||||
static_libs/lz4/lz4.h \
|
||||
static_libs/lz4/lz4hc.c \
|
||||
|
@ -1871,6 +1872,7 @@ modules_evas_savers_tgv_module_la_SOURCES = \
|
|||
modules/evas/savers/tgv/evas_image_save_tgv.c \
|
||||
static_libs/rg_etc/rg_etc1.c \
|
||||
static_libs/rg_etc/rg_etc1.h \
|
||||
static_libs/rg_etc/etc2_encoder.c \
|
||||
static_libs/lz4/lz4.c \
|
||||
static_libs/lz4/lz4.h \
|
||||
static_libs/lz4/lz4hc.c \
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
#include "lz4hc.h"
|
||||
#include "rg_etc1.h"
|
||||
|
||||
// FIXME: Remove DEBUG
|
||||
#define DEBUG
|
||||
#if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME) && defined(_POSIX_MONOTONIC_CLOCK)
|
||||
# include <time.h>
|
||||
# define DEBUG_STATS
|
||||
#endif
|
||||
|
||||
static int
|
||||
_block_size_get(int size)
|
||||
{
|
||||
|
@ -147,13 +154,26 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
{
|
||||
rg_etc1_pack_params param;
|
||||
FILE *f;
|
||||
char *comp;
|
||||
char *buffer;
|
||||
uint8_t *comp = NULL;
|
||||
uint8_t *buffer;
|
||||
uint32_t *data;
|
||||
uint32_t width, height;
|
||||
uint8_t header[8] = "TGV1";
|
||||
int block_width, block_height, macro_block_width, macro_block_height;
|
||||
int block_count, image_stride, image_height;
|
||||
int block_count, image_stride, image_height, etc_block_size;
|
||||
Evas_Colorspace cspace;
|
||||
Eina_Bool alpha;
|
||||
|
||||
#ifdef DEBUG_STATS
|
||||
struct timespec ts1, ts2;
|
||||
long long tsdiff, mse = 0, mse_div = 0, mse_alpha = 0;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts1);
|
||||
#endif
|
||||
|
||||
/* FIXME: How to tell the encoder to encode as ETC1 or ETC2?
|
||||
* The save API is weak. For now, assume ETC2 iif there's alpha.
|
||||
* As long as we don't have a full ETC2 encoder, this is fine.
|
||||
*/
|
||||
|
||||
if (!im || !im->image.data || !file)
|
||||
return 0;
|
||||
|
@ -161,8 +181,7 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
switch (im->cache_entry.space)
|
||||
{
|
||||
case EVAS_COLORSPACE_ARGB8888:
|
||||
if (im->cache_entry.flags.alpha)
|
||||
return 0;
|
||||
alpha = im->cache_entry.flags.alpha;
|
||||
break;
|
||||
case EVAS_COLORSPACE_ETC1:
|
||||
case EVAS_COLORSPACE_RGB8_ETC2:
|
||||
|
@ -177,6 +196,7 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
data = im->image.data;
|
||||
width = htonl(image_stride);
|
||||
height = htonl(image_height);
|
||||
compress = !!compress;
|
||||
|
||||
// Disable dithering, as it will deteriorate the quality of flat surfaces
|
||||
param.m_dithering = 0;
|
||||
|
@ -193,11 +213,22 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
block_height = _block_size_get(image_height + 2);
|
||||
header[4] = (block_height << 4) | block_width;
|
||||
|
||||
// header[5]: 0 for ETC1
|
||||
// header[5]: 0 for ETC1, 1 for RGB8_ETC2, 2 for RGBA8_ETC2_EAC
|
||||
if (alpha)
|
||||
{
|
||||
cspace = EVAS_COLORSPACE_RGBA8_ETC2_EAC;
|
||||
etc_block_size = 16;
|
||||
header[5] = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
cspace = EVAS_COLORSPACE_ETC1;
|
||||
etc_block_size = 8;
|
||||
header[5] = 0;
|
||||
}
|
||||
|
||||
// header[6]: 0 for raw, 1, for LZ4 compressed
|
||||
header[6] = (!!compress & 0x1);
|
||||
header[6] = compress;
|
||||
|
||||
// header[7]: options (unused)
|
||||
header[7] = 0;
|
||||
|
@ -216,16 +247,10 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
|
||||
// Number of ETC1 blocks in a compressed block
|
||||
block_count = (macro_block_width * macro_block_height) / (4 * 4);
|
||||
buffer = alloca(block_count * 8);
|
||||
buffer = alloca(block_count * etc_block_size);
|
||||
|
||||
if (compress)
|
||||
{
|
||||
comp = alloca(LZ4_compressBound(block_count * 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
comp = NULL;
|
||||
}
|
||||
comp = alloca(LZ4_compressBound(block_count * etc_block_size));
|
||||
|
||||
// Write macro block
|
||||
for (int y = 0; y < image_height + 2; y += macro_block_height)
|
||||
|
@ -240,7 +265,7 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
|
||||
for (int x = 0; x < image_stride + 2; x += macro_block_width)
|
||||
{
|
||||
char *offset = buffer;
|
||||
uint8_t *offset = buffer;
|
||||
int real_x = x;
|
||||
|
||||
if (x == 0) real_x = 0;
|
||||
|
@ -315,19 +340,56 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
}
|
||||
}
|
||||
|
||||
rg_etc1_pack_block(offset, (unsigned int*) todo, ¶m);
|
||||
offset += 8;
|
||||
switch (cspace)
|
||||
{
|
||||
case EVAS_COLORSPACE_ETC1:
|
||||
rg_etc1_pack_block(offset, (uint32_t *) todo, ¶m);
|
||||
break;
|
||||
case EVAS_COLORSPACE_RGB8_ETC2:
|
||||
etc2_rgb8_block_pack(offset, (uint32_t *) todo, ¶m);
|
||||
break;
|
||||
case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
|
||||
etc2_rgba8_block_pack(offset, (uint32_t *) todo, ¶m);
|
||||
break;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_STATS
|
||||
{
|
||||
// Decode to compute PSNR, this is slow.
|
||||
uint32_t done[16];
|
||||
|
||||
if (alpha)
|
||||
rg_etc2_rgba8_decode_block(offset, done);
|
||||
else
|
||||
rg_etc2_rgb8_decode_block(offset, done);
|
||||
|
||||
for (int k = 0; k < 16; k++)
|
||||
{
|
||||
const int r = (R_VAL(&(todo[k])) - R_VAL(&(done[k])));
|
||||
const int g = (G_VAL(&(todo[k])) - G_VAL(&(done[k])));
|
||||
const int b = (B_VAL(&(todo[k])) - B_VAL(&(done[k])));
|
||||
const int a = (A_VAL(&(todo[k])) - A_VAL(&(done[k])));
|
||||
mse += r*r + g*g + b*b;
|
||||
if (alpha) mse_alpha += a*a;
|
||||
mse_div++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
offset += etc_block_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (compress)
|
||||
{
|
||||
wlen = LZ4_compressHC(buffer, comp, block_count * 8);
|
||||
wlen = LZ4_compressHC((char *) buffer, (char *) comp,
|
||||
block_count * etc_block_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
comp = buffer;
|
||||
wlen = block_count * 8;
|
||||
wlen = block_count * etc_block_size;
|
||||
}
|
||||
|
||||
if (wlen > 0)
|
||||
|
@ -350,6 +412,29 @@ evas_image_save_file_tgv(RGBA_Image *im,
|
|||
}
|
||||
fclose(f);
|
||||
|
||||
#ifdef DEBUG_STATS
|
||||
if (mse_div && mse)
|
||||
{
|
||||
// TODO: Add DSSIM too
|
||||
double dmse = (double) mse / (double) (mse_div * 3.0);
|
||||
double psnr = 20 * log10(255.0) - 10 * log10(dmse);
|
||||
double dmse_alpha = (double) mse_alpha / (double) mse_div;
|
||||
double psnr_alpha = (dmse_alpha > 0.0) ? (20 * log10(255.0) - 10 * log10(dmse_alpha)) : 0;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts2);
|
||||
tsdiff = ((ts2.tv_sec - ts1.tv_sec) * 1000LL) + ((ts2.tv_nsec - ts1.tv_nsec) / 1000000LL);
|
||||
if (!alpha)
|
||||
{
|
||||
INF("ETC%d encoding stats: %dx%d, Time: %lldms, RGB PSNR: %.02fdB",
|
||||
alpha ? 2 : 1, image_stride, image_height, tsdiff, psnr);
|
||||
}
|
||||
else
|
||||
{
|
||||
INF("ETC%d encoding stats: %dx%d, Time: %lldms, RGB PSNR: %.02fdB, Alpha PSNR: %.02fdB",
|
||||
alpha ? 2 : 1, image_stride, image_height, tsdiff, psnr, psnr_alpha);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
|
||||
on_error:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,6 +37,12 @@ void rg_etc1_pack_block_init();
|
|||
// pack_etc1_block() does not currently support "perceptual" colorspace metrics - it primarily optimizes for RGB RMSE.
|
||||
unsigned int rg_etc1_pack_block(void* pETC1_block, const unsigned int* pSrc_pixels_BGRA, rg_etc1_pack_params *pack_params);
|
||||
|
||||
// Pack a 4x4 block of 32bpp BGRA pixels to a 16-byte RGBA8_ETC2_EAC block (supports alpha).
|
||||
unsigned int etc2_rgba8_block_pack(unsigned char *etc2, const unsigned int *bgra, rg_etc1_pack_params *params);
|
||||
|
||||
// Pack a 4x4 block of 32bpp BGRA pixels to a 8-byte RGB8_ETC2 block (opaque).
|
||||
unsigned int etc2_rgb8_block_pack(unsigned char *etc2, const unsigned int *bgra, rg_etc1_pack_params *params);
|
||||
|
||||
// ETC2 support: RGB8_ETC2
|
||||
void rg_etc2_rgb8_decode_block(const unsigned char *etc_block, unsigned int *bgra);
|
||||
|
||||
|
|
Loading…
Reference in New Issue