diff options
Diffstat (limited to '')
-rw-r--r-- | plug-ins/file-dds/mipmap.c | 1132 |
1 files changed, 1132 insertions, 0 deletions
diff --git a/plug-ins/file-dds/mipmap.c b/plug-ins/file-dds/mipmap.c new file mode 100644 index 0000000..a1ad0c1 --- /dev/null +++ b/plug-ins/file-dds/mipmap.c @@ -0,0 +1,1132 @@ +/* + * 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> + +#ifdef _OPENMP +#include <omp.h> +#endif + +#include "dds.h" +#include "mipmap.h" +#include "imath.h" +#include "color.h" + +typedef float (*filterfunc_t)(float); +typedef int (*wrapfunc_t)(int, int); +typedef void (*mipmapfunc_t)(unsigned char *, int, int, unsigned char *, int, int, int, filterfunc_t, float, wrapfunc_t, int, float); +typedef void (*volmipmapfunc_t)(unsigned char *, int, int, int, unsigned char *, int, int, int, int, filterfunc_t, float, wrapfunc_t, int, float); + +/****************************************************************************** + * size functions * + ******************************************************************************/ + +int +get_num_mipmaps (int width, + int height) +{ + int w = width << 1; + int h = height << 1; + int n = 0; + + while (w != 1 || h != 1) + { + if (w > 1) w >>= 1; + if (h > 1) h >>= 1; + ++n; + } + + return n; +} + +unsigned int +get_mipmapped_size (int width, + int height, + int bpp, + int level, + int num, + int format) +{ + int w, h, n = 0; + unsigned int 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; +} + +unsigned int +get_volume_mipmapped_size (int width, + int height, + int depth, + int bpp, + int level, + int num, + int format) +{ + int w, h, d, n = 0; + unsigned int 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; +} + +int +get_next_mipmap_dimensions (int *next_w, + int *next_h, + int curr_w, + int 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 int +wrap_mirror (int x, + int max) +{ + if (max == 1) x = 0; + x = abs(x); + while (x >= max) + x = abs(max + max - x - 2); + + return x; +} + +static int +wrap_repeat (int x, + int max) +{ + if (x >= 0) + return x % max; + + return (x + 1) % max + max - 1; +} + +static int +wrap_clamp (int x, + int max) +{ + return MAX(0, MIN(max - 1, x)); +} + +/****************************************************************************** + * gamma-correction * + ******************************************************************************/ + +static int +linear_to_gamma (int gc, + int v, + float gamma) +{ + if (gc == 1) + { + v = (int)(powf((float)v / 255.0f, gamma) * 255); + if (v > 255) v = 255; + } + else if (gc == 2) + { + v = linear_to_sRGB(v); + } + + return v; +} + +static int +gamma_to_linear (int gc, + int v, + float gamma) +{ + if (gc == 1) + { + v = (int)(powf((float)v / 255.0f, 1.0f / gamma) * 255); + if(v > 255) v = 255; + } + else if (gc == 2) + { + v = sRGB_to_linear(v); + } + + return v; +} + +/****************************************************************************** + * filters * + ******************************************************************************/ + +static float +box_filter (float t) +{ + if ((t >= -0.5f) && (t < 0.5f)) + return 1.0f; + + return 0.0f; +} + +static float +triangle_filter (float t) +{ + if (t < 0.0f) t = -t; + if (t < 1.0f) return 1.0f - t; + + return 0.0f; +} + +static float +quadratic_filter (float 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 float +bspline_filter (float t) +{ + float tt; + + if (t < 0.0f) + t = -t; + + if (t < 1.0f) + { + tt = t * t; + return ((0.5f * tt * t) - tt + (2.0f / 3.0f)); + } + else if (t < 2.0f) + { + t = 2.0f - t; + return (1.0f / 6.0f) * (t * t * t); + } + + return 0.0f; +} + +static float +mitchell (float t, + const float B, + const float C) +{ + float tt; + + 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 float +mitchell_filter (float t) +{ + return mitchell(t, 1.0f / 3.0f, 1.0f / 3.0f); +} + +static float +sinc (float 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 float +lanczos_filter (float t) +{ + if (t < 0.0f) t = -t; + if (t < 3.0f) return sinc(t) * sinc(t / 3.0f); + + return 0.0f; +} + +static float +bessel0 (float x) +{ + const float EPSILON = 1e-6f; + float xh, sum, pow, ds; + int 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 float +kaiser_filter (float t) +{ + if (t < 0.0f) t = -t; + + if (t < 3.0f) + { + const float alpha = 4.0f; + const float rb04 = 0.0884805322f; // 1.0f / bessel0(4.0f); + const float 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 image scaling * + ******************************************************************************/ + +static void +scale_image_nearest (unsigned char *dst, + int dw, + int dh, + unsigned char *src, + int sw, + int sh, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + int n, x, y; + int ix, iy; + int srowbytes = sw * bpp; + int 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 (unsigned char *dst, + int dw, + int dh, + unsigned char *src, + int sw, + int sh, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + const float blur = 1.0f; + const float xfactor = (float)dw / (float)sw; + const float yfactor = (float)dh / (float)sh; + + int x, y, start, stop, nmax, n, i; + int sstride = sw * bpp; + float center, contrib, density, s, r, t; + + unsigned char *d, *row, *col; + + float xscale = MIN(xfactor, 1.0f) / blur; + float yscale = MIN(yfactor, 1.0f) / blur; + float xsupport = support / xscale; + float ysupport = support / yscale; + unsigned char *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 = ((float)y + 0.5f) / yfactor; + start = (int)(center - ysupport + 0.5f); + stop = (int)(center + ysupport + 0.5f); + nmax = stop - start; + s = (float)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 = col[(wrap(start + n, sh) * sstride) + i]; + else + t = linear_to_gamma(gc, col[(wrap(start + n, sh) * sstride) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[(x * bpp) + i] = (unsigned char)r; + } + } + + /* resample in X direction using temp buffer */ + row = d; + d = dst; + + for (x = 0; x < dw; ++x) + { + center = ((float)x + 0.5f) / xfactor; + start = (int)(center - xsupport + 0.5f); + stop = (int)(center + xsupport + 0.5f); + nmax = stop - start; + s = (float)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 = row[(wrap(start + n, sw) * bpp) + i]; + else + t = linear_to_gamma(gc, row[(wrap(start + n, sw) * bpp) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[(y * (dw * bpp)) + (x * bpp) + i] = (unsigned char)r; + } + } + } + + g_free (tmp); +} + +/****************************************************************************** + * 3D image scaling * + ******************************************************************************/ + +static void +scale_volume_image_nearest (unsigned char *dst, + int dw, + int dh, + int dd, + unsigned char *src, + int sw, + int sh, + int sd, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + int n, x, y, z; + int 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 (unsigned char *dst, + int dw, + int dh, + int dd, + unsigned char *src, + int sw, + int sh, + int sd, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + const float blur = 1.0f; + const float xfactor = (float)dw / (float)sw; + const float yfactor = (float)dh / (float)sh; + const float zfactor = (float)dd / (float)sd; + + int x, y, z, start, stop, nmax, n, i; + int sstride = sw * bpp; + int zstride = sh * sw * bpp; + float center, contrib, density, s, r, t; + + unsigned char *d, *row, *col, *slice; + + float xscale = MIN(xfactor, 1.0f) / blur; + float yscale = MIN(yfactor, 1.0f) / blur; + float zscale = MIN(zfactor, 1.0f) / blur; + float xsupport = support / xscale; + float ysupport = support / yscale; + float zsupport = support / zscale; + unsigned char *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 = ((float)z + 0.5f) / zfactor; + start = (int)(center - zsupport + 0.5f); + stop = (int)(center + zsupport + 0.5f); + nmax = stop - start; + s = (float)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 = slice[(wrap(start + n, sd) * zstride) + i]; + else + t = linear_to_gamma(gc, slice[(wrap(start + n, sd) * zstride) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[((y * sw) + x) * bpp + i] = (unsigned char)r; + } + } + } + + /* 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 = ((float)y + 0.5f) / yfactor; + start = (int)(center - ysupport + 0.5f); + stop = (int)(center + ysupport + 0.5f); + nmax = stop - start; + s = (float)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 = col[(wrap(start + n, sh) * sstride) + i]; + else + t = linear_to_gamma(gc, col[(wrap(start + n, sh) * sstride) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[((y * sw) + x) * bpp + i] = (unsigned char)r; + } + } + } + + /* 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 = ((float)x + 0.5f) / xfactor; + start = (int)(center - xsupport + 0.5f); + stop = (int)(center + xsupport + 0.5f); + nmax = stop - start; + s = (float)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 = row[(wrap(start + n, sw) * bpp) + i]; + else + t = linear_to_gamma(gc, row[(wrap(start + n, sw) * bpp) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[((z * dh * dw) + (y * dw) + x) * bpp + i] = (unsigned char)r; + } + } + } + } + + g_free (tmp1); + g_free (tmp2); +} + +/****************************************************************************** + * filter lookup table * + ******************************************************************************/ + +static struct +{ + int filter; + filterfunc_t func; + float 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_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 float +calc_alpha_test_coverage (unsigned char *src, + unsigned int width, + unsigned int height, + int bpp, + float alpha_test_threshold, + float alpha_scale) +{ + unsigned int x, y; + int rowbytes = width * bpp; + int coverage = 0; + const int alpha_channel_idx = 3; + + if (bpp <= alpha_channel_idx) + { + /* No alpha channel */ + return 1.f; + } + + for (y = 0; y < height; ++y) + { + for (x = 0; x < width; ++x) + { + const float alpha = src[y * rowbytes + (x * bpp) + alpha_channel_idx]; + if ((alpha * alpha_scale) >= (alpha_test_threshold * 255)) + { + ++coverage; + } + } + } + + return (float)coverage / (width * height); +} + +static void +scale_alpha_to_coverage (unsigned char *img, + unsigned int width, + unsigned int height, + int bpp, + float desired_coverage, + float alpha_test_threshold) +{ + int i; + unsigned int x, y; + const int rowbytes = width * bpp; + const int alpha_channel_idx = 3; + float min_alpha_scale = 0.0f; + float max_alpha_scale = 4.0f; + float alpha_scale = 1.0f; + + if (bpp <= alpha_channel_idx) + { + /* No alpha channel */ + return; + } + + /* Binary search */ + for (i = 0; i < 10; i++) + { + float 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) + { + float 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] = (unsigned char)new_alpha; + } + } +} + +/****************************************************************************** + * mipmap generation * + ******************************************************************************/ + +int +generate_mipmaps (unsigned char *dst, + unsigned char *src, + unsigned int width, + unsigned int height, + int bpp, + int indexed, + int mipmaps, + int filter, + int wrap, + int gc, + float gamma, + int preserve_alpha_coverage, + float alpha_test_threshold) +{ + int i; + unsigned int sw, sh, dw, dh; + unsigned char *s, *d; + mipmapfunc_t mipmap_func = NULL; + filterfunc_t filter_func = NULL; + wrapfunc_t wrap_func = NULL; + float support = 0.0f; + const int has_alpha = (bpp >= 3); + float alpha_test_coverage = 1; + + if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST) + { + mipmap_func = scale_image_nearest; + } + else + { + if ((filter <= DDS_MIPMAP_FILTER_DEFAULT) || + (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) + { + alpha_test_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); + + sw = width; + sh = height; + + for (i = 1; i < mipmaps; ++i) + { + dw = MAX(1, sw >> 1); + dh = MAX(1, sh >> 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, alpha_test_coverage, alpha_test_threshold); + } + + s = d; + sw = dw; + sh = dh; + d += (dw * dh * bpp); + } + + return 1; +} + +int +generate_volume_mipmaps (unsigned char *dst, + unsigned char *src, + unsigned int width, + unsigned int height, + unsigned int depth, + int bpp, + int indexed, + int mipmaps, + int filter, + int wrap, + int gc, + float gamma) +{ + int i; + unsigned int sw, sh, sd; + unsigned int dw, dh, dd; + unsigned char *s, *d; + volmipmapfunc_t mipmap_func = NULL; + filterfunc_t filter_func = NULL; + wrapfunc_t wrap_func = NULL; + float support = 0.0f; + + if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST) + { + mipmap_func = scale_volume_image_nearest; + } + else + { + if ((filter <= DDS_MIPMAP_FILTER_DEFAULT) || + (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; +} |