/* * DDS PIKA plugin * * Copyright (C) 2004-2012 Shawn Kirst , * with parts (C) 2003 Arne Reuter 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 #include #include #include #include #include #ifdef _OPENMP #include #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; }