Moves most of the code relating to YCoCg and Alpha Exponent into misc.c/h, in the interest of making the rest of the codebase cleaner. Removes the decode option from the import menu, as encoded files are always decoded now (there used to be a menu button for doing this after import, but with it gone there's no reason ever to not decode). Finally, the remaining functions in color.c were only ever called once, so these were extracted and inlined, and the empty file deleted.
1168 lines
27 KiB
C
1168 lines
27 KiB
C
/*
|
|
* DDS GIMP plugin
|
|
*
|
|
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
|
|
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <libgimp/gimp.h>
|
|
|
|
#ifdef _OPENMP
|
|
#include <omp.h>
|
|
#endif
|
|
|
|
#include "dds.h"
|
|
#include "mipmap.h"
|
|
#include "imath.h"
|
|
|
|
|
|
typedef gfloat (*filterfunc_t) (gfloat);
|
|
typedef gint (*wrapfunc_t) (gint, gint);
|
|
typedef void (*mipmapfunc_t) (guchar*, gint, gint, guchar*, gint, gint, gint,
|
|
filterfunc_t, gfloat, wrapfunc_t, gint, gfloat);
|
|
typedef void (*volmipmapfunc_t) (guchar*, gint, gint, gint, guchar*, gint, gint, gint,
|
|
gint, filterfunc_t, gfloat, wrapfunc_t, gint, gfloat);
|
|
|
|
|
|
/**
|
|
* Size Functions
|
|
*/
|
|
|
|
gint
|
|
get_num_mipmaps (gint width,
|
|
gint height)
|
|
{
|
|
gint w = width << 1;
|
|
gint h = height << 1;
|
|
gint n = 0;
|
|
|
|
while (w != 1 || h != 1)
|
|
{
|
|
if (w > 1) w >>= 1;
|
|
if (h > 1) h >>= 1;
|
|
++n;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
guint
|
|
get_mipmapped_size (gint width,
|
|
gint height,
|
|
gint bpp,
|
|
gint level,
|
|
gint num,
|
|
gint format)
|
|
{
|
|
gint w, h, n = 0;
|
|
guint size = 0;
|
|
|
|
w = width >> level;
|
|
h = height >> level;
|
|
w = MAX (1, w);
|
|
h = MAX (1, h);
|
|
w <<= 1;
|
|
h <<= 1;
|
|
|
|
while (n < num && (w != 1 || h != 1))
|
|
{
|
|
if (w > 1) w >>= 1;
|
|
if (h > 1) h >>= 1;
|
|
if (format == DDS_COMPRESS_NONE)
|
|
size += (w * h);
|
|
else
|
|
size += ((w + 3) >> 2) * ((h + 3) >> 2);
|
|
++n;
|
|
}
|
|
|
|
if (format == DDS_COMPRESS_NONE)
|
|
{
|
|
size *= bpp;
|
|
}
|
|
else
|
|
{
|
|
if (format == DDS_COMPRESS_BC1 || format == DDS_COMPRESS_BC4)
|
|
size *= 8;
|
|
else
|
|
size *= 16;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
guint
|
|
get_volume_mipmapped_size (gint width,
|
|
gint height,
|
|
gint depth,
|
|
gint bpp,
|
|
gint level,
|
|
gint num,
|
|
gint format)
|
|
{
|
|
gint w, h, d, n = 0;
|
|
guint size = 0;
|
|
|
|
w = width >> level;
|
|
h = height >> level;
|
|
d = depth >> level;
|
|
w = MAX (1, w);
|
|
h = MAX (1, h);
|
|
d = MAX (1, d);
|
|
w <<= 1;
|
|
h <<= 1;
|
|
d <<= 1;
|
|
|
|
while (n < num && (w != 1 || h != 1))
|
|
{
|
|
if (w > 1) w >>= 1;
|
|
if (h > 1) h >>= 1;
|
|
if (d > 1) d >>= 1;
|
|
if (format == DDS_COMPRESS_NONE)
|
|
size += (w * h * d);
|
|
else
|
|
size += (((w + 3) >> 2) * ((h + 3) >> 2) * d);
|
|
++n;
|
|
}
|
|
|
|
if (format == DDS_COMPRESS_NONE)
|
|
{
|
|
size *= bpp;
|
|
}
|
|
else
|
|
{
|
|
if (format == DDS_COMPRESS_BC1 || format == DDS_COMPRESS_BC4)
|
|
size *= 8;
|
|
else
|
|
size *= 16;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
gint
|
|
get_next_mipmap_dimensions (gint *next_w,
|
|
gint *next_h,
|
|
gint curr_w,
|
|
gint curr_h)
|
|
{
|
|
if (curr_w == 1 || curr_h == 1)
|
|
return 0;
|
|
|
|
if (next_w) *next_w = curr_w >> 1;
|
|
if (next_h) *next_h = curr_h >> 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Wrap Modes
|
|
*/
|
|
|
|
static gint
|
|
wrap_mirror (gint x,
|
|
gint max)
|
|
{
|
|
if (max == 1)
|
|
x = 0;
|
|
|
|
x = abs (x);
|
|
while (x >= max)
|
|
x = abs (max + max - x - 2);
|
|
|
|
return x;
|
|
}
|
|
|
|
static gint
|
|
wrap_repeat (gint x,
|
|
gint max)
|
|
{
|
|
gfloat t;
|
|
t = (gfloat) x / (gfloat) max;
|
|
return (gint) ((t - floorf (t)) * (gfloat) max);
|
|
}
|
|
|
|
static gint
|
|
wrap_clamp (gint x,
|
|
gint max)
|
|
{
|
|
return MAX (0, MIN (max - 1, x));
|
|
}
|
|
|
|
|
|
/**
|
|
* Gamma-correction
|
|
*/
|
|
|
|
static gfloat
|
|
linear_to_sRGB (gfloat c)
|
|
{
|
|
gfloat v = (gfloat) c;
|
|
|
|
if (v < 0.0f)
|
|
v = 0.0f;
|
|
else if (v > 1.0f)
|
|
v = 1.0f;
|
|
else if (v <= 0.0031308f)
|
|
v = 12.92f * v;
|
|
else
|
|
v = 1.055f * powf (v, 0.41666f) - 0.055f;
|
|
|
|
return v;
|
|
}
|
|
|
|
static gfloat
|
|
linear_to_gamma (gint gc,
|
|
gfloat v,
|
|
gfloat gamma)
|
|
{
|
|
if (gc == 1)
|
|
{
|
|
v = powf (v, 1.0f / gamma);
|
|
if (v > 1.0f)
|
|
v = 1.0f;
|
|
}
|
|
else if (gc == 2)
|
|
{
|
|
v = linear_to_sRGB (v);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
static gfloat
|
|
sRGB_to_linear (gfloat c)
|
|
{
|
|
gfloat v = (gfloat) c;
|
|
|
|
if (v < 0.0f)
|
|
v = 0.0f;
|
|
else if (v > 1.0f)
|
|
v = 1.0f;
|
|
else if (v <= 0.04045f)
|
|
v /= 12.92f;
|
|
else
|
|
v = powf ((v + 0.055f) / 1.055f, 2.4f);
|
|
|
|
return v;
|
|
}
|
|
|
|
static gfloat
|
|
gamma_to_linear (gint gc,
|
|
gfloat v,
|
|
gfloat gamma)
|
|
{
|
|
if (gc == 1)
|
|
{
|
|
v = powf (v, gamma);
|
|
if (v > 1.0f)
|
|
v = 1.0f;
|
|
}
|
|
else if (gc == 2)
|
|
{
|
|
v = sRGB_to_linear (v);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
/**
|
|
* Filters
|
|
*/
|
|
|
|
static gfloat
|
|
box_filter (gfloat t)
|
|
{
|
|
if ((t >= -0.5f) && (t < 0.5f))
|
|
return 1.0f;
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
static gfloat
|
|
triangle_filter (gfloat t)
|
|
{
|
|
if (t < 0.0f) t = -t;
|
|
if (t < 1.0f) return 1.0f - t;
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
static gfloat
|
|
quadratic_filter (gfloat t)
|
|
{
|
|
if (t < 0.0f) t = -t;
|
|
if (t < 0.5f) return 0.75f - t * t;
|
|
if (t < 1.5f)
|
|
{
|
|
t -= 1.5f;
|
|
return 0.5f * t * t;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
static gfloat
|
|
mitchell (gfloat t,
|
|
const gfloat B)
|
|
{
|
|
gfloat C, tt;
|
|
|
|
C = 0.5f * (1.0f - B);
|
|
tt = t * t;
|
|
if (t < 0.0f)
|
|
t = -t;
|
|
|
|
if (t < 1.0f)
|
|
{
|
|
t = (((12.0f - 9.0f * B - 6.0f * C) * (t * tt)) +
|
|
((-18.0f + 12.0f * B + 6.0f * C) * tt) +
|
|
(6.0f - 2.0f * B));
|
|
|
|
return t / 6.0f;
|
|
}
|
|
else if (t < 2.0f)
|
|
{
|
|
t = (((-1.0f * B - 6.0f * C) * (t * tt)) +
|
|
((6.0f * B + 30.0f * C) * tt) +
|
|
((-12.0f * B - 48.0f * C) * t) +
|
|
(8.0f * B + 24.0f * C));
|
|
|
|
return t / 6.0f;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
static gfloat
|
|
bspline_filter (gfloat t)
|
|
{
|
|
return mitchell (t, 1.0f);
|
|
}
|
|
|
|
static gfloat
|
|
mitchell_filter (gfloat t)
|
|
{
|
|
return mitchell (t, 1.0f / 3.0f);
|
|
}
|
|
|
|
static gfloat
|
|
catrom_filter (gfloat t)
|
|
{
|
|
return mitchell (t, 0.0f);
|
|
}
|
|
|
|
static gfloat
|
|
sinc (gfloat x)
|
|
{
|
|
x = (x * M_PI);
|
|
if (fabsf (x) < 1e-04f)
|
|
return 1.0f + x * x * (-1.0f / 6.0f + x * x * 1.0f / 120.0f);
|
|
|
|
return sinf (x) / x;
|
|
}
|
|
|
|
static gfloat
|
|
lanczos_filter (gfloat t)
|
|
{
|
|
if (t < 0.0f) t = -t;
|
|
if (t < 3.0f) return sinc (t) * sinc (t / 3.0f);
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
static gfloat
|
|
bessel0 (gfloat x)
|
|
{
|
|
const gfloat EPSILON = 1e-6f;
|
|
gfloat xh, sum, pow, ds;
|
|
gint k;
|
|
|
|
xh = 0.5f * x;
|
|
sum = 1.0f;
|
|
pow = 1.0f;
|
|
k = 0;
|
|
ds = 1.0f;
|
|
|
|
while (ds > sum * EPSILON)
|
|
{
|
|
++k;
|
|
pow = pow * (xh / k);
|
|
ds = pow * pow;
|
|
sum += ds;
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
static gfloat
|
|
kaiser_filter (gfloat t)
|
|
{
|
|
if (t < 0.0f) t = -t;
|
|
|
|
if (t < 3.0f)
|
|
{
|
|
const gfloat alpha = 4.0f;
|
|
const gfloat rb04 = 0.0884805322f; // 1.0f / bessel0(4.0f);
|
|
const gfloat ratio = t / 3.0f;
|
|
if ((1.0f - ratio * ratio) >= 0)
|
|
return sinc (t) * bessel0 (alpha * sqrtf (1.0f - ratio * ratio)) * rb04;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
|
|
/**
|
|
* 2D Scaling
|
|
*/
|
|
|
|
static void
|
|
scale_image_nearest (guchar *dst,
|
|
gint dw,
|
|
gint dh,
|
|
guchar *src,
|
|
gint sw,
|
|
gint sh,
|
|
gint bpp,
|
|
filterfunc_t filter,
|
|
gfloat support,
|
|
wrapfunc_t wrap,
|
|
gint gc,
|
|
gfloat gamma)
|
|
{
|
|
gint n, x, y;
|
|
gint ix, iy;
|
|
gint srowbytes = sw * bpp;
|
|
gint drowbytes = dw * bpp;
|
|
|
|
for (y = 0; y < dh; ++y)
|
|
{
|
|
iy = (y * sh + sh / 2) / dh;
|
|
for (x = 0; x < dw; ++x)
|
|
{
|
|
ix = (x * sw + sw / 2) / dw;
|
|
for (n = 0; n < bpp; ++n)
|
|
{
|
|
dst[y * drowbytes + (x * bpp) + n] =
|
|
src[iy * srowbytes + (ix * bpp) + n];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
scale_image (guchar *dst,
|
|
gint dw,
|
|
gint dh,
|
|
guchar *src,
|
|
gint sw,
|
|
gint sh,
|
|
gint bpp,
|
|
filterfunc_t filter,
|
|
gfloat support,
|
|
wrapfunc_t wrap,
|
|
gint gc,
|
|
gfloat gamma)
|
|
{
|
|
const gfloat xfactor = (gfloat) dw / (gfloat) sw;
|
|
const gfloat yfactor = (gfloat) dh / (gfloat) sh;
|
|
|
|
gint x, y, start, stop, nmax, n, i;
|
|
gfloat center, contrib, density, s, r, t;
|
|
gint sstride = sw * bpp;
|
|
gfloat xscale = MIN (xfactor, 1.0f);
|
|
gfloat yscale = MIN (yfactor, 1.0f);
|
|
gfloat xsupport = support / xscale;
|
|
gfloat ysupport = support / yscale;
|
|
guchar *d, *row, *col;
|
|
guchar *tmp;
|
|
|
|
if (xsupport <= 0.5f)
|
|
{
|
|
xsupport = 0.5f + 1e-10f;
|
|
xscale = 1.0f;
|
|
}
|
|
|
|
if (ysupport <= 0.5f)
|
|
{
|
|
ysupport = 0.5f + 1e-10f;
|
|
yscale = 1.0f;
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
tmp = g_malloc (sw * bpp * omp_get_max_threads ());
|
|
#else
|
|
tmp = g_malloc (sw * bpp);
|
|
#endif
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for schedule(dynamic) \
|
|
private(x, y, d, row, col, center, start, stop, nmax, s, i, n, density, r, t, contrib)
|
|
#endif
|
|
for (y = 0; y < dh; ++y)
|
|
{
|
|
/* resample in Y direction to temp buffer */
|
|
d = tmp;
|
|
#ifdef _OPENMP
|
|
d += (sw * bpp * omp_get_thread_num ());
|
|
#endif
|
|
|
|
center = ((gfloat) y + 0.5f) / yfactor;
|
|
start = (gint) roundf ((center - ysupport) + 0.5f);
|
|
stop = (gint) roundf ((center + ysupport) + 0.5f);
|
|
nmax = stop - start;
|
|
s = (gfloat) start - center + 0.5f;
|
|
|
|
for (x = 0; x < sw; ++x)
|
|
{
|
|
col = src + (x * bpp);
|
|
|
|
for (i = 0; i < bpp; ++i)
|
|
{
|
|
density = 0.0f;
|
|
r = 0.0f;
|
|
|
|
for (n = 0; n < nmax; ++n)
|
|
{
|
|
contrib = filter((s + n) * yscale);
|
|
density += contrib;
|
|
if (i == 3)
|
|
t = (gfloat) col[(wrap (start + n, sh) * sstride) + i] / 255.0f;
|
|
else
|
|
t = gamma_to_linear (gc, (gfloat) col[(wrap (start + n, sh) * sstride) + i] / 255.0f, gamma);
|
|
r += t * contrib;
|
|
}
|
|
|
|
if (density != 0.0f && density != 1.0f)
|
|
r /= density;
|
|
|
|
r = MIN (1.0f, MAX (0.0f, r));
|
|
|
|
if (i != 3)
|
|
r = linear_to_gamma (gc, r, gamma);
|
|
|
|
d[(x * bpp) + i] = (guchar) floorf (r * 255.0f + 0.5f);
|
|
}
|
|
}
|
|
|
|
/* resample in X direction using temp buffer */
|
|
row = d;
|
|
d = dst;
|
|
|
|
for (x = 0; x < dw; ++x)
|
|
{
|
|
center = ((gfloat) x + 0.5f) / xfactor;
|
|
start = (gint) roundf ((center - xsupport) + 0.5f);
|
|
stop = (gint) roundf ((center + xsupport) + 0.5f);
|
|
nmax = stop - start;
|
|
s = (gfloat) start - center + 0.5f;
|
|
|
|
for (i = 0; i < bpp; ++i)
|
|
{
|
|
density = 0.0f;
|
|
r = 0.0f;
|
|
|
|
for (n = 0; n < nmax; ++n)
|
|
{
|
|
contrib = filter((s + n) * xscale);
|
|
density += contrib;
|
|
if (i == 3)
|
|
t = (gfloat) row[(wrap (start + n, sw) * bpp) + i] / 255.0f;
|
|
else
|
|
t = gamma_to_linear (gc, (gfloat) row[(wrap (start + n, sw) * bpp) + i] / 255.0f, gamma);
|
|
r += t * contrib;
|
|
}
|
|
|
|
if (density != 0.0f && density != 1.0f)
|
|
r /= density;
|
|
|
|
r = MIN (1.0f, MAX (0.0f, r));
|
|
|
|
if (i != 3)
|
|
r = linear_to_gamma (gc, r, gamma);
|
|
|
|
d[(y * (dw * bpp)) + (x * bpp) + i] = (guchar) floorf (r * 255.0f + 0.5f);
|
|
}
|
|
}
|
|
}
|
|
|
|
g_free (tmp);
|
|
}
|
|
|
|
|
|
/**
|
|
* 3D Scaling
|
|
*/
|
|
|
|
static void
|
|
scale_volume_image_nearest (guchar *dst,
|
|
gint dw,
|
|
gint dh,
|
|
gint dd,
|
|
guchar *src,
|
|
gint sw,
|
|
gint sh,
|
|
gint sd,
|
|
gint bpp,
|
|
filterfunc_t filter,
|
|
gfloat support,
|
|
wrapfunc_t wrap,
|
|
gint gc,
|
|
gfloat gamma)
|
|
{
|
|
gint n, x, y, z;
|
|
gint ix, iy, iz;
|
|
|
|
for (z = 0; z < dd; ++z)
|
|
{
|
|
iz = (z * sd + sd / 2) / dd;
|
|
for (y = 0; y < dh; ++y)
|
|
{
|
|
iy = (y * sh + sh / 2) / dh;
|
|
for (x = 0; x < dw; ++x)
|
|
{
|
|
ix = (x * sw + sw / 2) / dw;
|
|
for (n = 0; n < bpp; ++n)
|
|
{
|
|
dst[(z * (dw * dh)) + (y * dw) + (x * bpp) + n] =
|
|
src[(iz * (sw * sh)) + (iy * sw) + (ix * bpp) + n];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
scale_volume_image (guchar *dst,
|
|
gint dw,
|
|
gint dh,
|
|
gint dd,
|
|
guchar *src,
|
|
gint sw,
|
|
gint sh,
|
|
gint sd,
|
|
gint bpp,
|
|
filterfunc_t filter,
|
|
gfloat support,
|
|
wrapfunc_t wrap,
|
|
gint gc,
|
|
gfloat gamma)
|
|
{
|
|
const gfloat xfactor = (gfloat) dw / (gfloat) sw;
|
|
const gfloat yfactor = (gfloat) dh / (gfloat) sh;
|
|
const gfloat zfactor = (gfloat) dd / (gfloat) sd;
|
|
|
|
gint x, y, z, start, stop, nmax, n, i;
|
|
gfloat center, contrib, density, s, r, t;
|
|
gint sstride = sw * bpp;
|
|
gint zstride = sh * sw * bpp;
|
|
gfloat xscale = MIN (xfactor, 1.0f);
|
|
gfloat yscale = MIN (yfactor, 1.0f);
|
|
gfloat zscale = MIN (zfactor, 1.0f);
|
|
gfloat xsupport = support / xscale;
|
|
gfloat ysupport = support / yscale;
|
|
gfloat zsupport = support / zscale;
|
|
guchar *d, *row, *col, *slice;
|
|
guchar *tmp1, *tmp2;
|
|
|
|
/* down to a 2D image, use the faster 2D image resampler */
|
|
if (dd == 1 && sd == 1)
|
|
{
|
|
scale_image (dst, dw, dh, src, sw, sh, bpp, filter, support, wrap, gc, gamma);
|
|
return;
|
|
}
|
|
|
|
if (xsupport <= 0.5f)
|
|
{
|
|
xsupport = 0.5f + 1e-10f;
|
|
xscale = 1.0f;
|
|
}
|
|
|
|
if (ysupport <= 0.5f)
|
|
{
|
|
ysupport = 0.5f + 1e-10f;
|
|
yscale = 1.0f;
|
|
}
|
|
|
|
if (zsupport <= 0.5f)
|
|
{
|
|
zsupport = 0.5f + 1e-10f;
|
|
zscale = 1.0f;
|
|
}
|
|
|
|
tmp1 = g_malloc (sh * sw * bpp);
|
|
tmp2 = g_malloc (dh * sw * bpp);
|
|
|
|
for (z = 0; z < dd; ++z)
|
|
{
|
|
/* resample in Z direction */
|
|
d = tmp1;
|
|
|
|
center = ((gfloat) z + 0.5f) / zfactor;
|
|
start = (gint) roundf ((center - zsupport) + 0.5f);
|
|
stop = (gint) roundf ((center + zsupport) + 0.5f);
|
|
nmax = stop - start;
|
|
s = (gfloat) start - center + 0.5f;
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for schedule(dynamic) \
|
|
private(x, y, slice, i, n, density, r, t, contrib)
|
|
#endif
|
|
for (y = 0; y < sh; ++y)
|
|
{
|
|
for (x = 0; x < sw; ++x)
|
|
{
|
|
slice = src + (y * (sw * bpp)) + (x * bpp);
|
|
|
|
for (i = 0; i < bpp; ++i)
|
|
{
|
|
density = 0.0f;
|
|
r = 0.0f;
|
|
|
|
for (n = 0; n < nmax; ++n)
|
|
{
|
|
contrib = filter((s + n) * zscale);
|
|
density += contrib;
|
|
if (i == 3)
|
|
t = (gfloat) slice[(wrap (start + n, sd) * zstride) + i] / 255.0f;
|
|
else
|
|
t = gamma_to_linear (gc, (gfloat) slice[(wrap (start + n, sd) * zstride) + i] / 255.0f, gamma);
|
|
r += t * contrib;
|
|
}
|
|
|
|
if (density != 0.0f && density != 1.0f)
|
|
r /= density;
|
|
|
|
r = MIN (1.0f, MAX (0.0f, r));
|
|
|
|
if (i != 3)
|
|
r = linear_to_gamma (gc, r, gamma);
|
|
|
|
d[((y * sw) + x) * bpp + i] = (guchar) floorf (r * 255.0f + 0.5f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* resample in Y direction */
|
|
d = tmp2;
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for schedule(dynamic) \
|
|
private(x, y, col, center, start, stop, nmax, s, i, n, density, r, t, contrib)
|
|
#endif
|
|
for (y = 0; y < dh; ++y)
|
|
{
|
|
center = ((gfloat) y + 0.5f) / yfactor;
|
|
start = (gint) roundf ((center - ysupport) + 0.5f);
|
|
stop = (gint) roundf ((center + ysupport) + 0.5f);
|
|
nmax = stop - start;
|
|
s = (gfloat) start - center + 0.5f;
|
|
|
|
for (x = 0; x < sw; ++x)
|
|
{
|
|
col = tmp1 + (x * bpp);
|
|
|
|
for (i = 0; i < bpp; ++i)
|
|
{
|
|
density = 0.0f;
|
|
r = 0.0f;
|
|
|
|
for (n = 0; n < nmax; ++n)
|
|
{
|
|
contrib = filter((s + n) * yscale);
|
|
density += contrib;
|
|
if (i == 3)
|
|
t = (gfloat) col[(wrap (start + n, sh) * sstride) + i] / 255.0f;
|
|
else
|
|
t = gamma_to_linear (gc, (gfloat) col[(wrap (start + n, sh) * sstride) + i] / 255.0f, gamma);
|
|
r += t * contrib;
|
|
}
|
|
|
|
if (density != 0.0f && density != 1.0f)
|
|
r /= density;
|
|
|
|
r = MIN (1.0f, MAX (0.0f, r));
|
|
|
|
if (i != 3)
|
|
r = linear_to_gamma (gc, r, gamma);
|
|
|
|
d[((y * sw) + x) * bpp + i] = (guchar) floorf (r * 255.0f + 0.5f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* resample in X direction */
|
|
d = dst;
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for schedule(dynamic) \
|
|
private(x, y, row, center, start, stop, nmax, s, i, n, density, r, t, contrib)
|
|
#endif
|
|
for (y = 0; y < dh; ++y)
|
|
{
|
|
row = tmp2 + (y * sstride);
|
|
|
|
for (x = 0; x < dw; ++x)
|
|
{
|
|
center = ((gfloat) x + 0.5f) / xfactor;
|
|
start = (gint) roundf ((center - xsupport) + 0.5f);
|
|
stop = (gint) roundf ((center + xsupport) + 0.5f);
|
|
nmax = stop - start;
|
|
s = (gfloat) start - center + 0.5f;
|
|
|
|
for (i = 0; i < bpp; ++i)
|
|
{
|
|
density = 0.0f;
|
|
r = 0.0f;
|
|
|
|
for (n = 0; n < nmax; ++n)
|
|
{
|
|
contrib = filter((s + n) * xscale);
|
|
density += contrib;
|
|
if (i == 3)
|
|
t = (gfloat) row[(wrap (start + n, sw) * bpp) + i] / 255.0f;
|
|
else
|
|
t = gamma_to_linear (gc, (gfloat) row[(wrap (start + n, sw) * bpp) + i] / 255.0f, gamma);
|
|
r += t * contrib;
|
|
}
|
|
|
|
if (density != 0.0f && density != 1.0f)
|
|
r /= density;
|
|
|
|
r = MIN (1.0f, MAX (0.0f, r));
|
|
|
|
if (i != 3)
|
|
r = linear_to_gamma (gc, r, gamma);
|
|
|
|
d[((z * dh * dw) + (y * dw) + x) * bpp + i] = (guchar) floorf (r * 255.0f + 0.5f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
g_free (tmp1);
|
|
g_free (tmp2);
|
|
}
|
|
|
|
|
|
/**
|
|
* Filter Lookup-table
|
|
*/
|
|
|
|
static struct
|
|
{
|
|
gint filter;
|
|
filterfunc_t func;
|
|
gfloat support;
|
|
} filters[] =
|
|
{
|
|
{ DDS_MIPMAP_FILTER_BOX, box_filter, 0.5f },
|
|
{ DDS_MIPMAP_FILTER_TRIANGLE, triangle_filter, 1.0f },
|
|
{ DDS_MIPMAP_FILTER_QUADRATIC, quadratic_filter, 1.5f },
|
|
{ DDS_MIPMAP_FILTER_BSPLINE, bspline_filter, 2.0f },
|
|
{ DDS_MIPMAP_FILTER_MITCHELL, mitchell_filter, 2.0f },
|
|
{ DDS_MIPMAP_FILTER_CATROM, catrom_filter, 2.0f },
|
|
{ DDS_MIPMAP_FILTER_LANCZOS, lanczos_filter, 3.0f },
|
|
{ DDS_MIPMAP_FILTER_KAISER, kaiser_filter, 3.0f },
|
|
{ DDS_MIPMAP_FILTER_MAX, NULL, 0.0f }
|
|
};
|
|
|
|
|
|
/**
|
|
* Alpha-test Coverage - portion of visible texels after alpha test:
|
|
* if (texel_alpha < alpha_test_threshold) discard;
|
|
*/
|
|
|
|
static gfloat
|
|
calc_alpha_test_coverage (guchar *src,
|
|
guint width,
|
|
guint height,
|
|
gint bpp,
|
|
gfloat alpha_test_threshold,
|
|
gfloat alpha_scale)
|
|
{
|
|
const gint alpha_channel_idx = 3;
|
|
gint rowbytes = width * bpp;
|
|
gint coverage = 0;
|
|
guint x, y;
|
|
|
|
if (bpp <= alpha_channel_idx)
|
|
{
|
|
/* No alpha channel */
|
|
return 1.0f;
|
|
}
|
|
|
|
for (y = 0; y < height; ++y)
|
|
{
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
const gfloat alpha = src[y * rowbytes + (x * bpp) + alpha_channel_idx];
|
|
if ((alpha * alpha_scale) >= (alpha_test_threshold * 255))
|
|
{
|
|
++coverage;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (gfloat) coverage / (width * height);
|
|
}
|
|
|
|
static void
|
|
scale_alpha_to_coverage (guchar *img,
|
|
guint width,
|
|
guint height,
|
|
gint bpp,
|
|
gfloat desired_coverage,
|
|
gfloat alpha_test_threshold)
|
|
{
|
|
const gint rowbytes = width * bpp;
|
|
const gint alpha_channel_idx = 3;
|
|
gfloat min_alpha_scale = 0.0f;
|
|
gfloat max_alpha_scale = 4.0f;
|
|
gfloat alpha_scale = 1.0f;
|
|
guint x, y;
|
|
gint i;
|
|
|
|
if (bpp <= alpha_channel_idx)
|
|
{
|
|
/* No alpha channel */
|
|
return;
|
|
}
|
|
|
|
/* Binary search */
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
gfloat cur_coverage = calc_alpha_test_coverage (img, width, height, bpp, alpha_test_threshold, alpha_scale);
|
|
|
|
if (cur_coverage < desired_coverage)
|
|
{
|
|
min_alpha_scale = alpha_scale;
|
|
}
|
|
else if (cur_coverage > desired_coverage)
|
|
{
|
|
max_alpha_scale = alpha_scale;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
alpha_scale = (min_alpha_scale + max_alpha_scale) / 2;
|
|
}
|
|
|
|
/* Scale alpha channel */
|
|
for (y = 0; y < height; ++y)
|
|
{
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
gfloat new_alpha = img[y * rowbytes + (x * bpp) + alpha_channel_idx] * alpha_scale;
|
|
if (new_alpha > 255.0f)
|
|
{
|
|
new_alpha = 255.0f;
|
|
}
|
|
|
|
img[y * rowbytes + (x * bpp) + alpha_channel_idx] = (guchar) new_alpha;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Mipmap Generation
|
|
*/
|
|
|
|
gint
|
|
generate_mipmaps (guchar *dst,
|
|
guchar *src,
|
|
guint width,
|
|
guint height,
|
|
gint bpp,
|
|
gint indexed,
|
|
gint mipmaps,
|
|
gint filter,
|
|
gint wrap,
|
|
gint gc,
|
|
gfloat gamma,
|
|
gint preserve_alpha_coverage,
|
|
gfloat alpha_test_threshold)
|
|
{
|
|
const gint has_alpha = (bpp >= 3);
|
|
mipmapfunc_t mipmap_func = NULL;
|
|
filterfunc_t filter_func = NULL;
|
|
wrapfunc_t wrap_func = NULL;
|
|
gfloat coverage = 1.0f;
|
|
gfloat support = 0.0f;
|
|
guint sw, sh, dw, dh;
|
|
guchar *s, *d;
|
|
gint i;
|
|
|
|
if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST)
|
|
{
|
|
mipmap_func = scale_image_nearest;
|
|
}
|
|
else
|
|
{
|
|
if ((filter < DDS_MIPMAP_FILTER_NEAREST) ||
|
|
(filter >= DDS_MIPMAP_FILTER_MAX))
|
|
filter = DDS_MIPMAP_FILTER_BOX;
|
|
|
|
mipmap_func = scale_image;
|
|
|
|
for (i = 0; filters[i].filter != DDS_MIPMAP_FILTER_MAX; ++i)
|
|
{
|
|
if (filter == filters[i].filter)
|
|
{
|
|
filter_func = filters[i].func;
|
|
support = filters[i].support;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (wrap)
|
|
{
|
|
case DDS_MIPMAP_WRAP_MIRROR: wrap_func = wrap_mirror; break;
|
|
case DDS_MIPMAP_WRAP_REPEAT: wrap_func = wrap_repeat; break;
|
|
case DDS_MIPMAP_WRAP_CLAMP: wrap_func = wrap_clamp; break;
|
|
default: wrap_func = wrap_clamp; break;
|
|
}
|
|
|
|
if (has_alpha && preserve_alpha_coverage)
|
|
{
|
|
coverage = calc_alpha_test_coverage (src, width, height, bpp,
|
|
alpha_test_threshold,
|
|
1.0f);
|
|
}
|
|
|
|
memcpy (dst, src, width * height * bpp);
|
|
|
|
s = dst;
|
|
d = dst + (width * height * bpp);
|
|
|
|
dw = sw = width;
|
|
dh = sh = height;
|
|
|
|
for (i = 1; i < mipmaps; ++i)
|
|
{
|
|
dw = MAX (1, dw >> 1);
|
|
dh = MAX (1, dh >> 1);
|
|
|
|
mipmap_func (d, dw, dh, s, sw, sh, bpp, filter_func, support, wrap_func, gc, gamma);
|
|
|
|
if (has_alpha && preserve_alpha_coverage)
|
|
{
|
|
scale_alpha_to_coverage (d, dw, dh, bpp, coverage, alpha_test_threshold);
|
|
}
|
|
|
|
s = d;
|
|
sw = dw;
|
|
sh = dh;
|
|
d += (dw * dh * bpp);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
gint
|
|
generate_volume_mipmaps (guchar *dst,
|
|
guchar *src,
|
|
guint width,
|
|
guint height,
|
|
guint depth,
|
|
gint bpp,
|
|
gint indexed,
|
|
gint mipmaps,
|
|
gint filter,
|
|
gint wrap,
|
|
gint gc,
|
|
gfloat gamma)
|
|
{
|
|
volmipmapfunc_t mipmap_func = NULL;
|
|
filterfunc_t filter_func = NULL;
|
|
wrapfunc_t wrap_func = NULL;
|
|
gfloat support = 0.0f;
|
|
guint sw, sh, sd;
|
|
guint dw, dh, dd;
|
|
guchar *s, *d;
|
|
gint i;
|
|
|
|
if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST)
|
|
{
|
|
mipmap_func = scale_volume_image_nearest;
|
|
}
|
|
else
|
|
{
|
|
if ((filter < DDS_MIPMAP_FILTER_NEAREST) ||
|
|
(filter >= DDS_MIPMAP_FILTER_MAX))
|
|
filter = DDS_MIPMAP_FILTER_BOX;
|
|
|
|
mipmap_func = scale_volume_image;
|
|
|
|
for (i = 0; filters[i].filter != DDS_MIPMAP_FILTER_MAX; ++i)
|
|
{
|
|
if (filter == filters[i].filter)
|
|
{
|
|
filter_func = filters[i].func;
|
|
support = filters[i].support;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (wrap)
|
|
{
|
|
case DDS_MIPMAP_WRAP_MIRROR: wrap_func = wrap_mirror; break;
|
|
case DDS_MIPMAP_WRAP_REPEAT: wrap_func = wrap_repeat; break;
|
|
case DDS_MIPMAP_WRAP_CLAMP: wrap_func = wrap_clamp; break;
|
|
default: wrap_func = wrap_clamp; break;
|
|
}
|
|
|
|
memcpy (dst, src, width * height * depth * bpp);
|
|
|
|
s = dst;
|
|
d = dst + (width * height * depth * bpp);
|
|
|
|
sw = width;
|
|
sh = height;
|
|
sd = depth;
|
|
|
|
for (i = 1; i < mipmaps; ++i)
|
|
{
|
|
dw = MAX (1, sw >> 1);
|
|
dh = MAX (1, sh >> 1);
|
|
dd = MAX (1, sd >> 1);
|
|
|
|
mipmap_func (d, dw, dh, dd, s, sw, sh, sd, bpp, filter_func, support, wrap_func, gc, gamma);
|
|
|
|
s = d;
|
|
sw = dw;
|
|
sh = dh;
|
|
sd = dd;
|
|
d += (dw * dh * dd * bpp);
|
|
}
|
|
|
|
return 1;
|
|
}
|