plug-ins: Initial DDS BC7 export support
We use Richard Geldreich's bc7enc_rdo library to minimize the code changes required in the existing DDS plug-in, and so it can more easily be swapped out in the future.
This commit is contained in:
parent
d35893890d
commit
009aa52cbb
6 changed files with 2815 additions and 16 deletions
2636
plug-ins/file-dds/bc7enc_rdo/bc7enc.c
Normal file
2636
plug-ins/file-dds/bc7enc_rdo/bc7enc.c
Normal file
File diff suppressed because it is too large
Load diff
113
plug-ins/file-dds/bc7enc_rdo/bc7enc.h
Normal file
113
plug-ins/file-dds/bc7enc_rdo/bc7enc.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/* File: bc7enc.h - Richard Geldreich, Jr. - MIT license or public domain (see end of bc7enc.c)
|
||||
* If you use this software in a product, attribution / credits is requested but not required. */
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#define BC7ENC_BLOCK_SIZE (16)
|
||||
#define BC7ENC_MAX_PARTITIONS (64)
|
||||
#define BC7ENC_MAX_UBER_LEVEL (4)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t m_c[4];
|
||||
} color_rgba;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t m_mode_mask;
|
||||
|
||||
/* m_max_partitions may range from 0 (disables mode 1) to BC7ENC_MAX_PARTITIONS.
|
||||
* The higher this value, the slower the compressor, but the higher the quality. */
|
||||
uint32_t m_max_partitions;
|
||||
|
||||
/* Relative RGBA or YCbCrA weights. */
|
||||
uint32_t m_weights[4];
|
||||
|
||||
/* m_uber_level may range from 0 to BC7ENC_MAX_UBER_LEVEL.
|
||||
* The higher this value, the slower the compressor, but the higher the quality. */
|
||||
uint32_t m_uber_level;
|
||||
|
||||
/* If m_perceptual is TRUE, colorspace error is computed in YCbCr space, otherwise RGB. */
|
||||
gboolean m_perceptual;
|
||||
|
||||
/* Set m_try_least_squares to FALSE for slightly faster/lower quality compression. */
|
||||
gboolean m_try_least_squares;
|
||||
|
||||
/* When m_mode17_partition_estimation_filterbank, the mode1 partition estimator skips
|
||||
* lesser used partition patterns unless they are strongly predicted to be potentially useful.
|
||||
* There's a slight loss in quality with this enabled (around .08 dB RGB PSNR or .05 dB Y PSNR),
|
||||
* but up to a 11% gain in speed depending on the other settings. */
|
||||
gboolean m_mode17_partition_estimation_filterbank;
|
||||
|
||||
gboolean m_force_alpha;
|
||||
|
||||
gboolean m_force_selectors;
|
||||
uint8_t m_selectors[16];
|
||||
|
||||
gboolean m_quant_mode6_endpoints;
|
||||
gboolean m_bias_mode1_pbits;
|
||||
|
||||
float m_pbit1_weight;
|
||||
|
||||
float m_mode1_error_weight;
|
||||
float m_mode5_error_weight;
|
||||
float m_mode6_error_weight;
|
||||
float m_mode7_error_weight;
|
||||
|
||||
float m_low_frequency_partition_weight;
|
||||
} bc7enc_compress_block_params;
|
||||
|
||||
inline void bc7enc_compress_block_params_init_linear_weights(bc7enc_compress_block_params *p)
|
||||
{
|
||||
p->m_perceptual = FALSE;
|
||||
p->m_weights[0] = 1;
|
||||
p->m_weights[1] = 1;
|
||||
p->m_weights[2] = 1;
|
||||
p->m_weights[3] = 1;
|
||||
}
|
||||
|
||||
inline void bc7enc_compress_block_params_init_perceptual_weights(bc7enc_compress_block_params *p)
|
||||
{
|
||||
p->m_perceptual = TRUE;
|
||||
p->m_weights[0] = 128;
|
||||
p->m_weights[1] = 64;
|
||||
p->m_weights[2] = 16;
|
||||
p->m_weights[3] = 32;
|
||||
}
|
||||
|
||||
inline void bc7enc_compress_block_params_init(bc7enc_compress_block_params *p)
|
||||
{
|
||||
p->m_mode_mask = UINT32_MAX;
|
||||
p->m_max_partitions = BC7ENC_MAX_PARTITIONS;
|
||||
p->m_try_least_squares = TRUE;
|
||||
p->m_mode17_partition_estimation_filterbank = FALSE;
|
||||
p->m_uber_level = 4;
|
||||
p->m_force_selectors = FALSE;
|
||||
p->m_force_alpha = FALSE;
|
||||
p->m_quant_mode6_endpoints = TRUE;
|
||||
p->m_bias_mode1_pbits = TRUE;
|
||||
p->m_pbit1_weight = 1.0f;
|
||||
p->m_mode1_error_weight = 1.0f;
|
||||
p->m_mode5_error_weight = 1.0f;
|
||||
p->m_mode6_error_weight = 1.0f;
|
||||
p->m_mode7_error_weight = 1.0f;
|
||||
p->m_low_frequency_partition_weight = 1.0f;
|
||||
|
||||
if (p->m_perceptual)
|
||||
bc7enc_compress_block_params_init_perceptual_weights (p);
|
||||
else
|
||||
bc7enc_compress_block_params_init_linear_weights (p);
|
||||
}
|
||||
|
||||
/* bc7enc_compress_block_init() MUST be called before calling
|
||||
* bc7enc_compress_block() (or you'll get artifacts). */
|
||||
void bc7enc_compress_block_init (void);
|
||||
|
||||
/* Packs a single block of 16x16 RGBA pixels (R first in memory) to 128-bit BC7 block pBlock, using either mode 1 and/or 6.
|
||||
* Alpha blocks will always use mode 6, and by default opaque blocks will use either modes 1 or 6.
|
||||
* Returns TRUE if the block had any pixels with alpha < 255, otherwise it return FALSE. (This is not an error code - a block is always encoded.) */
|
||||
gboolean bc7enc_compress_block(void *pBlock, const void *pPixelsRGBA, const bc7enc_compress_block_params *pComp_params);
|
||||
|
|
@ -195,16 +195,17 @@ dds_create_procedure (GimpPlugIn *plug_in,
|
|||
gimp_procedure_add_choice_argument (procedure, "compression-format",
|
||||
_("Compressio_n"),
|
||||
_("Compression format"),
|
||||
gimp_choice_new_with_values ("none", DDS_COMPRESS_NONE, _("None"), NULL,
|
||||
gimp_choice_new_with_values ("none", DDS_COMPRESS_NONE, _("None"), NULL,
|
||||
"bc1", DDS_COMPRESS_BC1, _("BC1 / DXT1"), NULL,
|
||||
"bc2", DDS_COMPRESS_BC2, _("BC2 / DXT3"), NULL,
|
||||
"bc3, ", DDS_COMPRESS_BC3, _("BC3 / DXT5"), NULL,
|
||||
"bc3n", DDS_COMPRESS_BC3N, _("BC3nm / DXT5nm"), NULL,
|
||||
"bc4", DDS_COMPRESS_BC4, _("BC4 / ATI1 (3Dc+)"), NULL,
|
||||
"bc5", DDS_COMPRESS_BC5, _("BC5 / ATI2 (3Dc)"), NULL,
|
||||
"bc7", DDS_COMPRESS_BC7, "BC7", NULL,
|
||||
"rxgb", DDS_COMPRESS_RXGB, _("RXGB (DXT5)"), NULL,
|
||||
"aexp", DDS_COMPRESS_AEXP, _("Alpha Exponent (DXT5)"), NULL,
|
||||
"ycocg", DDS_COMPRESS_YCOCG, _("YCoCg (DXT5)"), NULL,
|
||||
"ycocg", DDS_COMPRESS_YCOCG, _("YCoCg (DXT5)"), NULL,
|
||||
"ycocgs", DDS_COMPRESS_YCOCGS, _("YCoCg scaled (DXT5)"), NULL,
|
||||
NULL),
|
||||
"none",
|
||||
|
|
|
|||
|
|
@ -891,8 +891,8 @@ write_layer (FILE *fp,
|
|||
}
|
||||
}
|
||||
|
||||
/* We want and assume BGRA ordered pixels for bpp >= 3 from here on */
|
||||
if (bpp >= 3)
|
||||
/* We want and assume BGRA ordered pixels for bpp >= 3 from here on */
|
||||
if (bpp >= 3 && compression != DDS_COMPRESS_BC7)
|
||||
swap_rb (src, w * h, bpp);
|
||||
|
||||
if (compression == DDS_COMPRESS_BC3N)
|
||||
|
|
@ -1232,7 +1232,7 @@ write_volume_mipmaps (FILE *fp,
|
|||
}
|
||||
|
||||
/* We want and assume BGRA ordered pixels for bpp >= 3 from here on */
|
||||
if (bpp >= 3)
|
||||
if (bpp >= 3 && compression != DDS_COMPRESS_BC7)
|
||||
swap_rb (src, w * h * d, bpp);
|
||||
|
||||
/* Pre-convert indexed images to RGB if not exporting as indexed */
|
||||
|
|
@ -1575,6 +1575,10 @@ write_image (FILE *fp,
|
|||
dxgi_format = DXGI_FORMAT_BC5_UNORM;
|
||||
/*is_dx10 = TRUE;*/
|
||||
break;
|
||||
|
||||
case DDS_COMPRESS_BC7:
|
||||
dxgi_format = DXGI_FORMAT_BC7_UNORM;
|
||||
is_dx10 = TRUE;
|
||||
}
|
||||
|
||||
if ((compression == DDS_COMPRESS_BC3N) ||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@
|
|||
#include "misc.h"
|
||||
#include "vec.h"
|
||||
|
||||
#include "bc7enc_rdo/bc7enc.h"
|
||||
|
||||
#include "dxt_tables.h"
|
||||
|
||||
#define SWAP(a, b) do { typeof(a) t; t = a; a = b; b = t; } while(0)
|
||||
|
|
@ -869,7 +871,7 @@ compress_BC1 (unsigned char *dst,
|
|||
x = (i % ((w + 3) >> 2)) << 2;
|
||||
y = (i / ((w + 3) >> 2)) << 2;
|
||||
p = dst + BLOCK_OFFSET(x, y, w, 8);
|
||||
extract_block(src, x, y, w, h, block);
|
||||
extract_block (src, x, y, w, h, block);
|
||||
encode_color_block(p, block, DXT_BC1 | flags);
|
||||
}
|
||||
}
|
||||
|
|
@ -894,7 +896,7 @@ compress_BC2 (unsigned char *dst,
|
|||
x = (i % ((w + 3) >> 2)) << 2;
|
||||
y = (i / ((w + 3) >> 2)) << 2;
|
||||
p = dst + BLOCK_OFFSET(x, y, w, 16);
|
||||
extract_block(src, x, y, w, h, block);
|
||||
extract_block (src, x, y, w, h, block);
|
||||
encode_alpha_block_BC2(p, block);
|
||||
encode_color_block(p + 8, block, DXT_BC2 | flags);
|
||||
}
|
||||
|
|
@ -920,7 +922,7 @@ compress_BC3 (unsigned char *dst,
|
|||
x = (i % ((w + 3) >> 2)) << 2;
|
||||
y = (i / ((w + 3) >> 2)) << 2;
|
||||
p = dst + BLOCK_OFFSET(x, y, w, 16);
|
||||
extract_block(src, x, y, w, h, block);
|
||||
extract_block (src, x, y, w, h, block);
|
||||
encode_alpha_block_BC3(p, block, 0);
|
||||
encode_color_block(p + 8, block, DXT_BC3 | flags);
|
||||
}
|
||||
|
|
@ -945,7 +947,7 @@ compress_BC4 (unsigned char *dst,
|
|||
x = (i % ((w + 3) >> 2)) << 2;
|
||||
y = (i / ((w + 3) >> 2)) << 2;
|
||||
p = dst + BLOCK_OFFSET(x, y, w, 8);
|
||||
extract_block(src, x, y, w, h, block);
|
||||
extract_block (src, x, y, w, h, block);
|
||||
encode_alpha_block_BC3(p, block, -1);
|
||||
}
|
||||
}
|
||||
|
|
@ -969,7 +971,7 @@ compress_BC5 (unsigned char *dst,
|
|||
x = (i % ((w + 3) >> 2)) << 2;
|
||||
y = (i / ((w + 3) >> 2)) << 2;
|
||||
p = dst + BLOCK_OFFSET(x, y, w, 16);
|
||||
extract_block(src, x, y, w, h, block);
|
||||
extract_block (src, x, y, w, h, block);
|
||||
/* Pixels are ordered as BGRA (see write_layer)
|
||||
* First we encode red -1+3: channel 2;
|
||||
* then we encode green -2+3: channel 1.
|
||||
|
|
@ -979,6 +981,34 @@ compress_BC5 (unsigned char *dst,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
compress_BC7 (guchar *dst,
|
||||
const guchar *src,
|
||||
gint w,
|
||||
gint h,
|
||||
bc7enc_compress_block_params *params)
|
||||
{
|
||||
const guint block_count = BLOCK_COUNT (w, h);
|
||||
guchar block[64] = { 0 };
|
||||
guint i;
|
||||
guchar *p;
|
||||
gint x;
|
||||
gint y;
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
|
||||
#endif
|
||||
for (i = 0; i < block_count; ++i)
|
||||
{
|
||||
x = (i % ((w + 3) >> 2)) << 2;
|
||||
y = (i / ((w + 3) >> 2)) << 2;
|
||||
p = dst + BLOCK_OFFSET (x, y, w, 16);
|
||||
|
||||
extract_block (src, x, y, w, h, block);
|
||||
bc7enc_compress_block (p, block, params);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
compress_YCoCg (unsigned char *dst,
|
||||
const unsigned char *src,
|
||||
|
|
@ -998,7 +1028,7 @@ compress_YCoCg (unsigned char *dst,
|
|||
x = (i % ((w + 3) >> 2)) << 2;
|
||||
y = (i / ((w + 3) >> 2)) << 2;
|
||||
p = dst + BLOCK_OFFSET(x, y, w, 16);
|
||||
extract_block(src, x, y, w, h, block);
|
||||
extract_block (src, x, y, w, h, block);
|
||||
encode_alpha_block_BC3(p, block, 0);
|
||||
encode_YCoCg_block(p + 8, block);
|
||||
}
|
||||
|
|
@ -1014,11 +1044,15 @@ dxt_compress (unsigned char *dst,
|
|||
int mipmaps,
|
||||
int flags)
|
||||
{
|
||||
int i, size, w, h;
|
||||
unsigned int offset;
|
||||
unsigned char *tmp = NULL;
|
||||
int j;
|
||||
unsigned char *s;
|
||||
gint i;
|
||||
gint size;
|
||||
gint w;
|
||||
gint h;
|
||||
guint offset;
|
||||
guchar *tmp = NULL;
|
||||
gint j;
|
||||
guchar *s;
|
||||
bc7enc_compress_block_params bc7_params;
|
||||
|
||||
if (bpp == 1)
|
||||
{
|
||||
|
|
@ -1078,6 +1112,13 @@ dxt_compress (unsigned char *dst,
|
|||
h = height;
|
||||
s = tmp ? tmp : src;
|
||||
|
||||
bc7_params.m_perceptual = (flags & DXT_PERCEPTUAL);
|
||||
if (format == DDS_COMPRESS_BC7)
|
||||
{
|
||||
bc7enc_compress_block_init ();
|
||||
bc7enc_compress_block_params_init (&bc7_params);
|
||||
}
|
||||
|
||||
for (i = 0; i < mipmaps; ++i)
|
||||
{
|
||||
switch (format)
|
||||
|
|
@ -1101,6 +1142,9 @@ dxt_compress (unsigned char *dst,
|
|||
case DDS_COMPRESS_BC5:
|
||||
compress_BC5(dst + offset, s, w, h);
|
||||
break;
|
||||
case DDS_COMPRESS_BC7:
|
||||
compress_BC7 (dst + offset, s, w, h, &bc7_params);
|
||||
break;
|
||||
case DDS_COMPRESS_YCOCGS:
|
||||
compress_YCoCg(dst + offset, s, w, h);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
plugin_name = 'file-dds'
|
||||
|
||||
plugin_sourcecode = [
|
||||
'bc7enc_rdo/bc7enc.c',
|
||||
'bc7.c',
|
||||
'dds.c',
|
||||
'ddsread.c',
|
||||
|
|
|
|||
Loading…
Reference in a new issue