mirror of https://github.com/GNOME/gimp.git
362 lines
11 KiB
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, ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_bit, 1);
|
|
|
|
for (gint i = 1; i < 16; i++)
|
|
alpha_indexes[i] = get_bits (s, ¤t_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, ¤t_bit, precision);
|
|
}
|
|
|
|
if (mode == 5 || (mode == 4 && ! selector))
|
|
{
|
|
alpha_indexes[0] = get_bits (s, ¤t_bit, no_sel_precision - 1);
|
|
|
|
for (gint i = 1; i < 16; i++)
|
|
alpha_indexes[i] = get_bits (s, ¤t_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;
|
|
}
|