gimp/plug-ins/file-dds/bc7.c

362 lines
11 KiB
C

/*
* DDS GIMP plugin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301, USA.
*/
/* ImageMagick's implementation of BC7 was referenced for our implementation.
* The relevant commit: https://github.com/ImageMagick/ImageMagick/pull/4126/files
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <glib.h>
#include <libgimp/gimp.h>
#include "bc7.h"
#define SWAP(a, b) do { typeof(a) t; t = a; a = b; b = t; } while(0)
static guchar get_bits (const guchar *block,
guchar *start_bit,
guchar length);
static void decode_rgba_channels (BC7_colors *colors,
guchar *src,
guint mode,
guchar *start);
static gboolean is_pixel_anchor (guchar subset_index,
guint precision,
guchar pixel_index,
guint partition_id);
gint
bc7_decompress (guchar *src,
guint size,
guchar *block)
{
/* BC7 blocks are always 16 bytes */
guchar s[16];
BC7_colors colors;
guchar rgb_indexes[16];
guchar alpha_indexes[16];
guchar subset_indexes[16];
guchar rgba[4];
guint mode = 0;
guint subset_count = 0;
guint partition_id = 0;
guint swap = 0;
guint selector = 0;
guchar current_bit = 0;
guint i_precision = 0;
guint no_sel_precision = 0;
for (gint i = 0; i < 16; i++)
s[i] = src[i];
/* BC7 blocks are read from the last bit of the last byte, backwards.
* The mode is determined by the first 1 bit encountered. For instance,
* 1 is mode 0, 10 is mode 1, 100 is mode 2, and so on. */
while (current_bit <= 8 && ! get_bits (s, &current_bit, 1))
{
continue;
}
mode = current_bit - 1;
if (mode > 7)
return 0;
/* For modes that support partitions, we start counting
* from left of the mode bit, and get the partition ID.
* The size of the partition ID is defined by the mode. */
subset_count = mode_info[mode].subset_count;
if (subset_count > 1)
{
partition_id = get_bits (s, &current_bit,
mode_info[mode].partition_bits);
if (partition_id > 63)
return 0;
}
/* Mode 4 and 5 might swap channels for better compression. 2 bits after the mode bit
* indicate which were swapped (if any) */
if (mode == 4 || mode == 5)
swap = get_bits (s, &current_bit, 2);
/* Mode 4 also has a single selector bit, to the left of its swap bits.
* This bit determines where the alpha channel indexes are located. */
if (mode == 4)
selector = get_bits (s, &current_bit, 1);
/* Channel values are stored in RnGnBn(An) format, where the mode determines
* how many bits of precision and how many values are stored. */
decode_rgba_channels (&colors, s, mode, &current_bit);
/* Next, we get the indexes to assemble pixels from the channel values. */
i_precision = mode_info[mode].index_precision;
no_sel_precision = mode_info[mode].no_sel_precision;
if (mode == 4 && selector)
{
i_precision = 3;
alpha_indexes[0] = get_bits (s, &current_bit, 1);
for (gint i = 1; i < 16; i++)
alpha_indexes[i] = get_bits (s, &current_bit, 2);
}
/* First, calculate the RGB channel indexes based on the partition ID
* and the number of subsets. */
for (gint i = 0; i < 16; i++)
{
guint precision = i_precision;
if (subset_count == 2)
subset_indexes[i] = partition_table[0][partition_id][i];
else if (subset_count == 3)
subset_indexes[i] = partition_table[1][partition_id][i];
else
subset_indexes[i] = 0;
if (is_pixel_anchor (subset_indexes[i], subset_count, i, partition_id))
precision--;
rgb_indexes[i]= get_bits (s, &current_bit, precision);
}
if (mode == 5 || (mode == 4 && ! selector))
{
alpha_indexes[0] = get_bits (s, &current_bit, no_sel_precision - 1);
for (gint i = 1; i < 16; i++)
alpha_indexes[i] = get_bits (s, &current_bit, no_sel_precision);
}
/* Create pixels from subset indexes */
for (gint i = 0; i < 16; i++)
{
guint weight;
guint c0 = 2 * subset_indexes[i];
guint c1 = (2 * subset_indexes[i]) + 1;
if (i_precision == 2)
weight = weight_2[rgb_indexes[i]];
else if (i_precision == 3)
weight = weight_3[rgb_indexes[i]];
else
weight = weight_4[rgb_indexes[i]];
rgba[0] = ((64 - weight) * colors.r[c0] + weight * colors.r[c1] + 32) >> 6;
rgba[1] = ((64 - weight) * colors.g[c0] + weight * colors.g[c1] + 32) >> 6;
rgba[2] = ((64 - weight) * colors.b[c0] + weight * colors.b[c1] + 32) >> 6;
if (mode == 4 || mode == 5)
{
weight = weight_2[alpha_indexes[i]];
if (mode == 4 && ! selector)
weight = weight_3[alpha_indexes[i]];
}
rgba[3] = ((64 - weight) * colors.a[c0] + weight * colors.a[c1] + 32) >> 6;
switch (swap)
{
case 1:
SWAP (rgba[3], rgba[0]);
break;
case 2:
SWAP (rgba[3], rgba[1]);
break;
case 3:
SWAP (rgba[3], rgba[2]);
break;
default:
break;
}
for (gint j = 0; j < 4; j++)
block[(i * 4) + j] = rgba[j];
}
return 1;
}
/* Private Functions */
static guchar
get_bits (const guchar *block,
guchar *start_bit,
guchar length)
{
guchar bits;
guint index;
guint base;
guint first_bits;
guint next_bits;
index = (*start_bit) >> 3;
base = (*start_bit) - (index << 3);
if (index > 15)
return 0;
if (base + length > 8)
{
first_bits = 8 - base;
next_bits = length - first_bits;
bits = block[index] >> base;
bits |= (block[index + 1] & ((1 << next_bits) - 1)) << first_bits;
}
else
{
bits = (block[index] >> base) & ((1 << length) - 1);
}
(*start_bit) += length;
return bits;
}
static void
decode_rgba_channels (BC7_colors *colors,
guchar *src,
guint mode,
guchar *start)
{
guint channel_count = mode_info[mode].subset_count * 2;
guint rgb_precision = mode_info[mode].rgb_precision;
guint alpha_precision = mode_info[mode].alpha_precision;
/* Get RGB channel values */
for (gint i = 0; i < channel_count; i++)
colors->r[i] = get_bits (src, start, rgb_precision);
for (gint i = 0; i < channel_count; i++)
colors->g[i] = get_bits (src, start, rgb_precision);
for (gint i = 0; i < channel_count; i++)
colors->b[i] = get_bits (src, start, rgb_precision);
/* Modes 4 - 7 also have alpha values */
if (alpha_precision)
{
for (gint i = 0; i < channel_count; i++)
colors->a[i] = get_bits (src, start, alpha_precision);
}
else
{
for (gint i = 0; i < channel_count; i++)
colors->a[i] = 255;
}
/* Modes 0, 1, 3, 6, and 7 have P-bits, which increase the precision
* by 1. */
if (mode_info[mode].p_bit_count)
{
rgb_precision++;
if (alpha_precision)
alpha_precision++;
/* P-bits are added at the least significant bit, so
* we have to shift existing channel values over by 1 */
for (gint i = 0; i < channel_count; i++)
{
colors->r[i] <<= 1;
colors->g[i] <<= 1;
colors->b[i] <<= 1;
if (alpha_precision)
colors->a[i] <<= 1;
}
if (mode == 1)
{
guint p_bit_1 = get_bits (src, start, 1);
guint p_bit_2 = get_bits (src, start, 1);
for (gint i = 0; i < 2; i++)
{
colors->r[i] |= p_bit_1;
colors->g[i] |= p_bit_1;
colors->b[i] |= p_bit_1;
colors->r[i + 2] |= p_bit_2;
colors->g[i + 2] |= p_bit_2;
colors->b[i + 2] |= p_bit_2;
}
}
else
{
for (gint i = 0; i < channel_count; i++)
{
guint p_bit = get_bits (src, start, 1);
colors->r[i] |= p_bit;
colors->g[i] |= p_bit;
colors->b[i] |= p_bit;
if (alpha_precision)
colors->a[i] |= p_bit;
}
}
}
/* Normalize all channel values to 8 bits */
for (gint i = 0; i < channel_count; i++)
{
colors->r[i] <<= (8 - rgb_precision);
colors->g[i] <<= (8 - rgb_precision);
colors->b[i] <<= (8 - rgb_precision);
colors->r[i] = colors->r[i] | (colors->r[i] >> rgb_precision);
colors->g[i] = colors->g[i] | (colors->g[i] >> rgb_precision);
colors->b[i] = colors->b[i] | (colors->b[i] >> rgb_precision);
if (alpha_precision)
{
colors->a[i] <<= (8 - alpha_precision);
colors->a[i] = colors->a[i] | (colors->a[i] >> alpha_precision);
}
}
}
static gboolean
is_pixel_anchor (guchar subset_index,
guint precision,
guchar pixel_index,
guint partition_id)
{
guint table_index;
if (subset_index == 0)
table_index = 0;
else if (subset_index == 1 && precision == 2)
table_index = 1;
else if (subset_index == 1 && precision == 3)
table_index = 2;
else
table_index = 3;
if (anchor_index_table[table_index][partition_id] == pixel_index)
return TRUE;
return FALSE;
}