summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-dds/ddswrite.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
commit3c57dd931145d43f2b0aef96c4d178135956bf91 (patch)
tree3de698981e9f0cc2c4f9569b19a5f3595e741f6b /plug-ins/file-dds/ddswrite.c
parentInitial commit. (diff)
downloadgimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz
gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--plug-ins/file-dds/ddswrite.c2278
1 files changed, 2278 insertions, 0 deletions
diff --git a/plug-ins/file-dds/ddswrite.c b/plug-ins/file-dds/ddswrite.c
new file mode 100644
index 0000000..5fb658a
--- /dev/null
+++ b/plug-ins/file-dds/ddswrite.c
@@ -0,0 +1,2278 @@
+/*
+ * 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 "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <libgimp/stdplugins-intl.h>
+
+#include "ddsplugin.h"
+#include "dds.h"
+#include "dxt.h"
+#include "mipmap.h"
+#include "endian_rw.h"
+#include "imath.h"
+#include "color.h"
+
+
+enum
+{
+ COMBO_VALUE,
+ COMBO_STRING,
+ COMBO_SENSITIVE
+};
+
+
+static gint save_dialog (gint32 image_id,
+ gint32 drawable);
+static void save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static gboolean write_image (FILE *fp,
+ gint32 image_id,
+ gint32 drawable_id);
+
+
+static gboolean runme = FALSE;
+
+static const char *cubemap_face_names[4][6] =
+{
+ {
+ "positive x", "negative x",
+ "positive y", "negative y",
+ "positive z", "negative z"
+ },
+ {
+ "pos x", "neg x",
+ "pos y", "neg y",
+ "pos z", "neg z",
+ },
+ {
+ "+x", "-x",
+ "+y", "-y",
+ "+z", "-z"
+ },
+ {
+ "right", "left",
+ "top", "bottom",
+ "back", "front"
+ }
+};
+
+static gint cubemap_faces[6];
+static gboolean is_cubemap = FALSE;
+static gboolean is_volume = FALSE;
+static gboolean is_array = FALSE;
+static gboolean is_mipmap_chain_valid = FALSE;
+
+static GtkWidget *compress_opt;
+static GtkWidget *format_opt;
+static GtkWidget *mipmap_opt;
+static GtkWidget *mipmap_filter_opt;
+static GtkWidget *mipmap_wrap_opt;
+static GtkWidget *srgb_chk;
+static GtkWidget *gamma_chk;
+static GtkWidget *gamma_spin;
+static GtkWidget *pm_chk;
+static GtkWidget *alpha_coverage_chk;
+static GtkWidget *alpha_test_threshold_spin;
+
+typedef struct string_value_s
+{
+ gint value;
+ gchar *string;
+} string_value_t;
+
+static string_value_t compression_strings[] =
+{
+ { DDS_COMPRESS_NONE, "None" },
+ { DDS_COMPRESS_BC1, "BC1 / DXT1" },
+ { DDS_COMPRESS_BC2, "BC2 / DXT3" },
+ { DDS_COMPRESS_BC3, "BC3 / DXT5" },
+ { DDS_COMPRESS_BC3N, "BC3nm / DXT5nm" },
+ { DDS_COMPRESS_BC4, "BC4 / ATI1 (3Dc+)" },
+ { DDS_COMPRESS_BC5, "BC5 / ATI2 (3Dc)" },
+ { DDS_COMPRESS_RXGB, "RXGB (DXT5)" },
+ { DDS_COMPRESS_AEXP, "Alpha Exponent (DXT5)" },
+ { DDS_COMPRESS_YCOCG, "YCoCg (DXT5)" },
+ { DDS_COMPRESS_YCOCGS, "YCoCg scaled (DXT5)" },
+ { -1, 0}
+};
+
+static string_value_t format_strings[] =
+{
+ { DDS_FORMAT_DEFAULT, "Default" },
+ { DDS_FORMAT_RGB8, "RGB8" },
+ { DDS_FORMAT_RGBA8, "RGBA8" },
+ { DDS_FORMAT_BGR8, "BGR8" },
+ { DDS_FORMAT_ABGR8, "ABGR8" },
+ { DDS_FORMAT_R5G6B5, "R5G6B5" },
+ { DDS_FORMAT_RGBA4, "RGBA4" },
+ { DDS_FORMAT_RGB5A1, "RGB5A1" },
+ { DDS_FORMAT_RGB10A2, "RGB10A2" },
+ { DDS_FORMAT_R3G3B2, "R3G3B2" },
+ { DDS_FORMAT_A8, "A8" },
+ { DDS_FORMAT_L8, "L8" },
+ { DDS_FORMAT_L8A8, "L8A8" },
+ { DDS_FORMAT_AEXP, "AExp" },
+ { DDS_FORMAT_YCOCG, "YCoCg" },
+ { -1, 0}
+};
+
+static string_value_t mipmap_strings[] =
+{
+ { DDS_MIPMAP_NONE, "No mipmaps" },
+ { DDS_MIPMAP_GENERATE, "Generate mipmaps" },
+ { DDS_MIPMAP_EXISTING, "Use existing mipmaps" },
+ { -1, 0}
+};
+
+static string_value_t mipmap_filter_strings[] =
+{
+ { DDS_MIPMAP_FILTER_DEFAULT, "Default" },
+ { DDS_MIPMAP_FILTER_NEAREST, "Nearest" },
+ { DDS_MIPMAP_FILTER_BOX, "Box" },
+ { DDS_MIPMAP_FILTER_TRIANGLE, "Triangle" },
+ { DDS_MIPMAP_FILTER_QUADRATIC, "Quadratic" },
+ { DDS_MIPMAP_FILTER_BSPLINE, "B-Spline" },
+ { DDS_MIPMAP_FILTER_MITCHELL, "Mitchell" },
+ { DDS_MIPMAP_FILTER_LANCZOS, "Lanczos" },
+ { DDS_MIPMAP_FILTER_KAISER, "Kaiser" },
+ { -1, 0}
+};
+
+static string_value_t mipmap_wrap_strings[] =
+{
+ { DDS_MIPMAP_WRAP_DEFAULT, "Default" },
+ { DDS_MIPMAP_WRAP_MIRROR, "Mirror" },
+ { DDS_MIPMAP_WRAP_REPEAT, "Repeat" },
+ { DDS_MIPMAP_WRAP_CLAMP, "Clamp" },
+ { -1, 0}
+};
+
+static string_value_t save_type_strings[] =
+{
+ { DDS_SAVE_SELECTED_LAYER, "Selected layer" },
+ { DDS_SAVE_CUBEMAP, "As cube map" },
+ { DDS_SAVE_VOLUMEMAP, "As volume map" },
+ { DDS_SAVE_ARRAY, "As texture array" },
+ { DDS_SAVE_VISIBLE_LAYERS, "All visible layers" },
+ { -1, 0}
+};
+
+static struct
+{
+ int format;
+ DXGI_FORMAT dxgi_format;
+ int bpp;
+ int alpha;
+ unsigned int rmask;
+ unsigned int gmask;
+ unsigned int bmask;
+ unsigned int amask;
+} format_info[] =
+{
+ { DDS_FORMAT_RGB8, DXGI_FORMAT_UNKNOWN, 3, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
+ { DDS_FORMAT_RGBA8, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
+ { DDS_FORMAT_BGR8, DXGI_FORMAT_UNKNOWN, 3, 0, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000},
+ { DDS_FORMAT_ABGR8, DXGI_FORMAT_R8G8B8A8_UNORM, 4, 1, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},
+ { DDS_FORMAT_R5G6B5, DXGI_FORMAT_B5G6R5_UNORM, 2, 0, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000},
+ { DDS_FORMAT_RGBA4, DXGI_FORMAT_B4G4R4A4_UNORM, 2, 1, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000},
+ { DDS_FORMAT_RGB5A1, DXGI_FORMAT_B5G5R5A1_UNORM, 2, 1, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000},
+ { DDS_FORMAT_RGB10A2, DXGI_FORMAT_R10G10B10A2_UNORM, 4, 1, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000},
+ { DDS_FORMAT_R3G3B2, DXGI_FORMAT_UNKNOWN, 1, 0, 0x000000e0, 0x0000001c, 0x00000003, 0x00000000},
+ { DDS_FORMAT_A8, DXGI_FORMAT_A8_UNORM, 1, 0, 0x00000000, 0x00000000, 0x00000000, 0x000000ff},
+ { DDS_FORMAT_L8, DXGI_FORMAT_R8_UNORM, 1, 0, 0x000000ff, 0x000000ff, 0x000000ff, 0x00000000},
+ { DDS_FORMAT_L8A8, DXGI_FORMAT_UNKNOWN, 2, 1, 0x000000ff, 0x000000ff, 0x000000ff, 0x0000ff00},
+ { DDS_FORMAT_AEXP, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
+ { DDS_FORMAT_YCOCG, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}
+};
+
+
+static gboolean
+check_mipmaps (gint32 image_id,
+ gint savetype)
+{
+ gint *layers;
+ gint num_layers;
+ gint i, j, w, h, mipw, miph;
+ gint num_mipmaps;
+ gint num_surfaces = 0;
+ gint min_surfaces = 1;
+ gint max_surfaces = 1;
+ gboolean valid = TRUE;
+ GimpImageType type;
+
+ /* not handling volume maps for the moment... */
+ if (savetype == DDS_SAVE_VOLUMEMAP)
+ return 0;
+
+ if (savetype == DDS_SAVE_CUBEMAP)
+ {
+ min_surfaces = 6;
+ max_surfaces = 6;
+ }
+ else if (savetype == DDS_SAVE_ARRAY)
+ {
+ min_surfaces = 2;
+ max_surfaces = INT_MAX;
+ }
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+
+ num_mipmaps = get_num_mipmaps (w, h);
+
+ type = gimp_drawable_type (layers[0]);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if (type != gimp_drawable_type (layers[i]))
+ return 0;
+
+ if ((gimp_drawable_width (layers[i]) == w) &&
+ (gimp_drawable_height (layers[i]) == h))
+ ++num_surfaces;
+ }
+
+ if ((num_surfaces < min_surfaces) ||
+ (num_surfaces > max_surfaces) ||
+ (num_layers != (num_surfaces * num_mipmaps)))
+ return 0;
+
+ for (i = 0; valid && i < num_layers; i += num_mipmaps)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ {
+ valid = FALSE;
+ break;
+ }
+
+ for (j = 1; j < num_mipmaps; ++j)
+ {
+ mipw = w >> j;
+ miph = h >> j;
+ if (mipw < 1) mipw = 1;
+ if (miph < 1) miph = 1;
+ if ((gimp_drawable_width (layers[i + j]) != mipw) ||
+ (gimp_drawable_height (layers[i + j]) != miph))
+ {
+ valid = FALSE;
+ break;
+ }
+ }
+ }
+
+ return valid;
+}
+
+static gboolean
+check_cubemap (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gboolean cubemap = TRUE;
+ gint i, j, k, w, h;
+ gchar *layer_name;
+ GimpImageType type;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers < 6)
+ return FALSE;
+
+ /* check for a valid cubemap with mipmap layers */
+ if (num_layers > 6)
+ {
+ /* check that mipmap layers are in order for a cubemap */
+ if (! check_mipmaps (image_id, DDS_SAVE_CUBEMAP))
+ return FALSE;
+
+ /* invalidate cubemap faces */
+ for (i = 0; i < 6; ++i)
+ cubemap_faces[i] = -1;
+
+ /* find the mipmap level 0 layers */
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ continue;
+
+ layer_name = (char*)gimp_item_get_name (layers[i]);
+ for (j = 0; j < 6; ++j)
+ {
+ for (k = 0; k < 4; ++k)
+ {
+ if (strstr (layer_name, cubemap_face_names[k][j]))
+ {
+ if (cubemap_faces[j] == -1)
+ {
+ cubemap_faces[j] = layers[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* check for 6 valid faces */
+ for (i = 0; i < 6; ++i)
+ {
+ if (cubemap_faces[i] == -1)
+ {
+ cubemap = FALSE;
+ break;
+ }
+ }
+
+ /* make sure they are all the same type */
+ if (cubemap)
+ {
+ type = gimp_drawable_type (cubemap_faces[0]);
+ for (i = 1; i < 6 && cubemap; ++i)
+ {
+ if (gimp_drawable_type (cubemap_faces[i]) != type)
+ cubemap = FALSE;
+ }
+ }
+ }
+
+ if (num_layers == 6)
+ {
+ /* invalidate cubemap faces */
+ for (i = 0; i < 6; ++i)
+ cubemap_faces[i] = -1;
+
+ for (i = 0; i < 6; ++i)
+ {
+ layer_name = (char*)gimp_item_get_name (layers[i]);
+ for (j = 0; j < 6; ++j)
+ {
+ for (k = 0; k < 4; ++k)
+ {
+ if (strstr (layer_name, cubemap_face_names[k][j]))
+ {
+ if (cubemap_faces[j] == -1)
+ {
+ cubemap_faces[j] = layers[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* check for 6 valid faces */
+ for (i = 0; i < 6; ++i)
+ {
+ if (cubemap_faces[i] == -1)
+ {
+ cubemap = FALSE;
+ break;
+ }
+ }
+
+ /* make sure they are all the same size */
+ if (cubemap)
+ {
+ w = gimp_drawable_width (cubemap_faces[0]);
+ h = gimp_drawable_height (cubemap_faces[0]);
+
+ for (i = 1; i < 6 && cubemap; ++i)
+ {
+ if ((gimp_drawable_width (cubemap_faces[i]) != w) ||
+ (gimp_drawable_height (cubemap_faces[i]) != h))
+ cubemap = FALSE;
+ }
+ }
+
+ /* make sure they are all the same type */
+ if (cubemap)
+ {
+ type = gimp_drawable_type (cubemap_faces[0]);
+ for (i = 1; i < 6 && cubemap; ++i)
+ {
+ if (gimp_drawable_type (cubemap_faces[i]) != type)
+ cubemap = FALSE;
+ }
+ }
+ }
+
+ return cubemap;
+}
+
+static gboolean
+check_volume (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gboolean volume = FALSE;
+ gint i, w, h;
+ GimpImageType type;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers > 1)
+ {
+ volume = TRUE;
+
+ /* make sure all layers are the same size */
+ w = gimp_drawable_width (layers[0]);
+ h = gimp_drawable_height (layers[0]);
+
+ for (i = 1; i < num_layers && volume; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ volume = FALSE;
+ }
+
+ if (volume)
+ {
+ /* make sure all layers are the same type */
+ type = gimp_drawable_type (layers[0]);
+ for (i = 1; i < num_layers && volume; ++i)
+ {
+ if (gimp_drawable_type (layers[i]) != type)
+ volume = FALSE;
+ }
+ }
+ }
+
+ return volume;
+}
+
+static gboolean
+check_array (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gboolean array = FALSE;
+ gint i, w, h;
+ GimpImageType type;
+
+ if (check_mipmaps (image_id, DDS_SAVE_ARRAY))
+ return 1;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers > 1)
+ {
+ array = TRUE;
+
+ /* make sure all layers are the same size */
+ w = gimp_drawable_width (layers[0]);
+ h = gimp_drawable_height (layers[0]);
+
+ for (i = 1; i < num_layers && array; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ array = FALSE;
+ }
+
+ if (array)
+ {
+ /* make sure all layers are the same type */
+ type = gimp_drawable_type (layers[0]);
+ for (i = 1; i < num_layers; ++i)
+ {
+ if (gimp_drawable_type (layers[i]) != type)
+ {
+ array = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ return array;
+}
+
+static int
+get_array_size (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gint i, w, h;
+ gint elements = 0;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) == w) &&
+ (gimp_drawable_height (layers[i]) == h))
+ {
+ elements++;
+ }
+ }
+
+ return elements;
+}
+
+GimpPDBStatusType
+write_dds (gchar *filename,
+ gint32 image_id,
+ gint32 drawable_id,
+ gboolean interactive_dds,
+ gboolean is_duplicate_image)
+{
+ FILE *fp;
+ gchar *tmp;
+ int rc = 0;
+
+ is_mipmap_chain_valid = check_mipmaps (image_id, dds_write_vals.savetype);
+
+ is_cubemap = check_cubemap (image_id);
+ is_volume = check_volume (image_id);
+ is_array = check_array (image_id);
+
+ if (interactive_dds)
+ {
+ if (! is_mipmap_chain_valid &&
+ dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING)
+ dds_write_vals.mipmaps = DDS_MIPMAP_NONE;
+
+ if (! save_dialog (image_id, drawable_id))
+ return GIMP_PDB_CANCEL;
+ }
+ else
+ {
+ if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP && ! is_cubemap)
+ {
+ g_message ("DDS: Cannot save image as cube map");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP && ! is_volume)
+ {
+ g_message ("DDS: Cannot save image as volume map");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP &&
+ dds_write_vals.compression != DDS_COMPRESS_NONE)
+ {
+ g_message ("DDS: Cannot save volume map with compression");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING &&
+ ! is_mipmap_chain_valid)
+ {
+ g_message ("DDS: Cannot save with existing mipmaps as the mipmap chain is incomplete");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ fp = g_fopen (filename, "wb");
+ if (fp == 0)
+ {
+ g_message ("Error opening %s", filename);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (strrchr (filename, '/'))
+ tmp = g_strdup_printf ("Saving %s:", strrchr (filename, '/') + 1);
+ else
+ tmp = g_strdup_printf ("Saving %s:", filename);
+ gimp_progress_init (tmp);
+ g_free (tmp);
+
+ /* If destructive changes are going to happen to the image,
+ * make sure we send a duplicate of it to write_image
+ */
+ if (! is_duplicate_image)
+ {
+ gint32 duplicate_image = gimp_image_duplicate (image_id);
+ rc = write_image (fp, duplicate_image, drawable_id);
+ gimp_image_delete (duplicate_image);
+ }
+ else
+ {
+ rc = write_image (fp, image_id, drawable_id);
+ }
+
+
+ fclose (fp);
+
+ return rc ? GIMP_PDB_SUCCESS : GIMP_PDB_EXECUTION_ERROR;
+}
+
+static void
+swap_rb (unsigned char *pixels,
+ unsigned int n,
+ int bpp)
+{
+ unsigned int i;
+ unsigned char t;
+
+ for (i = 0; i < n; ++i)
+ {
+ t = pixels[bpp * i + 0];
+ pixels[bpp * i + 0] = pixels[bpp * i + 2];
+ pixels[bpp * i + 2] = t;
+ }
+}
+
+static void
+alpha_exp (unsigned char *dst,
+ int r,
+ int g,
+ int b,
+ int a)
+{
+ float ar, ag, ab, aa;
+
+ ar = (float)r / 255.0f;
+ ag = (float)g / 255.0f;
+ ab = (float)b / 255.0f;
+
+ aa = MAX (ar, MAX (ag, ab));
+
+ if (aa < 1e-04f)
+ {
+ dst[0] = b;
+ dst[1] = g;
+ dst[2] = r;
+ dst[3] = 255;
+ return;
+ }
+
+ ar /= aa;
+ ag /= aa;
+ ab /= aa;
+
+ r = (int)floorf (255.0f * ar + 0.5f);
+ g = (int)floorf (255.0f * ag + 0.5f);
+ b = (int)floorf (255.0f * ab + 0.5f);
+ a = (int)floorf (255.0f * aa + 0.5f);
+
+ dst[0] = MAX (0, MIN (255, b));
+ dst[1] = MAX (0, MIN (255, g));
+ dst[2] = MAX (0, MIN (255, r));
+ dst[3] = MAX (0, MIN (255, a));
+}
+
+static void
+convert_pixels (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ int w,
+ int h,
+ int d,
+ int bpp,
+ unsigned char *palette,
+ int mipmaps)
+{
+ unsigned int i, num_pixels;
+ unsigned char r, g, b, a;
+
+ if (d > 0)
+ num_pixels = get_volume_mipmapped_size (w, h, d, 1, 0, mipmaps, DDS_COMPRESS_NONE);
+ else
+ num_pixels = get_mipmapped_size (w, h, 1, 0, mipmaps, DDS_COMPRESS_NONE);
+
+ for (i = 0; i < num_pixels; ++i)
+ {
+ if (bpp == 1)
+ {
+ if (palette)
+ {
+ r = palette[3 * src[i] + 0];
+ g = palette[3 * src[i] + 1];
+ b = palette[3 * src[i] + 2];
+ }
+ else
+ r = g = b = src[i];
+
+ if (format == DDS_FORMAT_A8)
+ a = src[i];
+ else
+ a = 255;
+ }
+ else if (bpp == 2)
+ {
+ r = g = b = src[2 * i];
+ a = src[2 * i + 1];
+ }
+ else if (bpp == 3)
+ {
+ b = src[3 * i + 0];
+ g = src[3 * i + 1];
+ r = src[3 * i + 2];
+ a = 255;
+ }
+ else
+ {
+ b = src[4 * i + 0];
+ g = src[4 * i + 1];
+ r = src[4 * i + 2];
+ a = src[4 * i + 3];
+ }
+
+ switch (format)
+ {
+ case DDS_FORMAT_RGB8:
+ dst[3 * i + 0] = b;
+ dst[3 * i + 1] = g;
+ dst[3 * i + 2] = r;
+ break;
+ case DDS_FORMAT_RGBA8:
+ dst[4 * i + 0] = b;
+ dst[4 * i + 1] = g;
+ dst[4 * i + 2] = r;
+ dst[4 * i + 3] = a;
+ break;
+ case DDS_FORMAT_BGR8:
+ dst[3 * i + 0] = r;
+ dst[3 * i + 1] = g;
+ dst[3 * i + 2] = b;
+ break;
+ case DDS_FORMAT_ABGR8:
+ dst[4 * i + 0] = r;
+ dst[4 * i + 1] = g;
+ dst[4 * i + 2] = b;
+ dst[4 * i + 3] = a;
+ break;
+ case DDS_FORMAT_R5G6B5:
+ PUTL16(&dst[2 * i], pack_r5g6b5(r, g, b));
+ break;
+ case DDS_FORMAT_RGBA4:
+ PUTL16(&dst[2 * i], pack_rgba4(r, g, b, a));
+ break;
+ case DDS_FORMAT_RGB5A1:
+ PUTL16(&dst[2 * i], pack_rgb5a1(r, g, b, a));
+ break;
+ case DDS_FORMAT_RGB10A2:
+ PUTL32(&dst[4 * i], pack_rgb10a2(r, g, b, a));
+ break;
+ case DDS_FORMAT_R3G3B2:
+ dst[i] = pack_r3g3b2(r, g, b);
+ break;
+ case DDS_FORMAT_A8:
+ dst[i] = a;
+ break;
+ case DDS_FORMAT_L8:
+ dst[i] = rgb_to_luminance (r, g, b);
+ break;
+ case DDS_FORMAT_L8A8:
+ dst[2 * i + 0] = rgb_to_luminance (r, g, b);
+ dst[2 * i + 1] = a;
+ break;
+ case DDS_FORMAT_YCOCG:
+ dst[4 * i] = a;
+ RGB_to_YCoCg (&dst[4 * i], r, g, b);
+ break;
+ case DDS_FORMAT_AEXP:
+ alpha_exp (&dst[4 * i], r, g, b, a);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+get_mipmap_chain (unsigned char *dst,
+ int w,
+ int h,
+ int bpp,
+ gint32 image_id,
+ gint drawable_id)
+{
+ gint *layers, num_layers;
+ GeglBuffer *buffer;
+ const Babl *format = 0;
+ int i, idx = 0, offset, mipw, miph;
+
+ if (bpp == 1)
+ format = babl_format ("Y' u8");
+ else if (bpp == 2)
+ format = babl_format ("Y'A u8");
+ else if (bpp == 3)
+ format = babl_format ("R'G'B' u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if (layers[i] == drawable_id)
+ {
+ idx = i;
+ break;
+ }
+ }
+
+ if (i == num_layers) return;
+
+ offset = 0;
+
+ while (get_next_mipmap_dimensions (&mipw, &miph, w, h))
+ {
+ buffer = gimp_drawable_get_buffer (layers[++idx]);
+
+ if ((gegl_buffer_get_width (buffer) != mipw) ||
+ (gegl_buffer_get_height (buffer) != miph))
+ {
+ g_object_unref (buffer);
+ return;
+ }
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, mipw, miph), 1.0, format,
+ dst + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ g_object_unref (buffer);
+
+ /* we need BGRX or BGRA */
+ if (bpp >= 3)
+ swap_rb (dst + offset, mipw * miph, bpp);
+
+ offset += (mipw * miph * bpp);
+ w = mipw;
+ h = miph;
+ }
+}
+
+static void
+write_layer (FILE *fp,
+ gint32 image_id,
+ gint32 drawable_id,
+ int w,
+ int h,
+ int bpp,
+ int fmtbpp,
+ int mipmaps)
+{
+ GeglBuffer *buffer;
+ const Babl *format = 0;
+ GimpImageBaseType basetype;
+ GimpImageType type;
+ unsigned char *src, *dst, *fmtdst, *tmp;
+ unsigned char *palette = NULL;
+ int i, c, x, y, size, fmtsize, offset, colors;
+ int compression = dds_write_vals.compression;
+ int flags = 0;
+
+ basetype = gimp_image_base_type (image_id);
+ type = gimp_drawable_type (drawable_id);
+
+ buffer = gimp_drawable_get_buffer (drawable_id);
+
+ src = g_malloc (w * h * bpp);
+
+ if (basetype == GIMP_INDEXED)
+ format = gimp_drawable_get_format (drawable_id);
+ else if (bpp == 1)
+ format = babl_format ("Y' u8");
+ else if (bpp == 2)
+ format = babl_format ("Y'A u8");
+ else if (bpp == 3)
+ format = babl_format ("R'G'B' u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (basetype == GIMP_INDEXED)
+ {
+ palette = gimp_image_get_colormap (image_id, &colors);
+
+ if (type == GIMP_INDEXEDA_IMAGE)
+ {
+ tmp = g_malloc (w * h);
+ for (i = 0; i < w * h; ++i)
+ tmp[i] = src[2 * i];
+ g_free (src);
+ src = tmp;
+ bpp = 1;
+ }
+ }
+
+ /* we want and assume BGRA ordered pixels for bpp >= 3 from here and
+ onwards */
+ if (bpp >= 3)
+ swap_rb (src, w * h, bpp);
+
+ if (compression == DDS_COMPRESS_BC3N)
+ {
+ if (bpp != 4)
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ for (y = 0; y < h; ++y)
+ {
+ for (x = 0; x < w; ++x)
+ {
+ /* set alpha to red (x) */
+ src[y * (w * 4) + (x * 4) + 3] =
+ src[y * (w * 4) + (x * 4) + 2];
+ /* set red to 1 */
+ src[y * (w * 4) + (x * 4) + 2] = 255;
+ }
+ }
+ }
+
+ /* RXGB (Doom3) */
+ if (compression == DDS_COMPRESS_RXGB)
+ {
+ if (bpp != 4)
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ for (y = 0; y < h; ++y)
+ {
+ for (x = 0; x < w; ++x)
+ {
+ /* swap red and alpha */
+ c = src[y * (w * 4) + (x * 4) + 3];
+ src[y * (w * 4) + (x * 4) + 3] =
+ src[y * (w * 4) + (x * 4) + 2];
+ src[y * (w * 4) + (x * 4) + 2] = c;
+ }
+ }
+ }
+
+ if (compression == DDS_COMPRESS_YCOCG ||
+ compression == DDS_COMPRESS_YCOCGS) /* convert to YCoCG */
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_YCOCG, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ if (compression == DDS_COMPRESS_AEXP)
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_AEXP, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ if (compression == DDS_COMPRESS_NONE)
+ {
+ if (mipmaps > 1)
+ {
+ /* pre-convert indexed images to RGB for better quality mipmaps
+ if a pixel format conversion is requested */
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT && basetype == GIMP_INDEXED)
+ {
+ fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps, DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 3;
+ palette = NULL;
+ }
+
+ size = get_mipmapped_size (w, h, bpp, 0, mipmaps, DDS_COMPRESS_NONE);
+ dst = g_malloc (size);
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE)
+ {
+ generate_mipmaps (dst, src, w, h, bpp, palette != NULL,
+ mipmaps,
+ dds_write_vals.mipmap_filter,
+ dds_write_vals.mipmap_wrap,
+ dds_write_vals.gamma_correct + dds_write_vals.srgb,
+ dds_write_vals.gamma,
+ dds_write_vals.preserve_alpha_coverage,
+ dds_write_vals.alpha_test_threshold);
+ }
+ else
+ {
+ memcpy (dst, src, w * h * bpp);
+ get_mipmap_chain (dst + (w * h * bpp), w, h, bpp, image_id, drawable_id);
+ }
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ fmtsize = get_mipmapped_size (w, h, fmtbpp, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+
+ convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, 0, bpp,
+ palette, mipmaps);
+
+ g_free (dst);
+ dst = fmtdst;
+ bpp = fmtbpp;
+ }
+
+ offset = 0;
+
+ for (i = 0; i < mipmaps; ++i)
+ {
+ size = get_mipmapped_size (w, h, bpp, i, 1, DDS_COMPRESS_NONE);
+ fwrite (dst + offset, 1, size, fp);
+ offset += size;
+ }
+
+ g_free (dst);
+ }
+ else
+ {
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ fmtdst = g_malloc (h * w * fmtbpp);
+ convert_pixels (fmtdst, src, dds_write_vals.format, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = fmtbpp;
+ }
+
+ fwrite (src, 1, h * w * bpp, fp);
+ }
+ }
+ else
+ {
+ size = get_mipmapped_size (w, h, bpp, 0, mipmaps, compression);
+
+ dst = g_malloc (size);
+
+ if (basetype == GIMP_INDEXED)
+ {
+ fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp,
+ palette, mipmaps);
+ g_free (src);
+ src = fmtdst;
+ bpp = 3;
+ }
+
+ if (mipmaps > 1)
+ {
+ fmtsize = get_mipmapped_size (w, h, bpp, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE)
+ {
+ generate_mipmaps (fmtdst, src, w, h, bpp, 0, mipmaps,
+ dds_write_vals.mipmap_filter,
+ dds_write_vals.mipmap_wrap,
+ dds_write_vals.gamma_correct + dds_write_vals.srgb,
+ dds_write_vals.gamma,
+ dds_write_vals.preserve_alpha_coverage,
+ dds_write_vals.alpha_test_threshold);
+ }
+ else
+ {
+ memcpy (fmtdst, src, w * h * bpp);
+ get_mipmap_chain (fmtdst + (w * h * bpp), w, h, bpp, image_id, drawable_id);
+ }
+
+ g_free (src);
+ src = fmtdst;
+ }
+
+ flags = 0;
+ if (dds_write_vals.perceptual_metric) flags |= DXT_PERCEPTUAL;
+
+ dxt_compress (dst, src, compression, w, h, bpp, mipmaps, flags);
+
+ fwrite (dst, 1, size, fp);
+
+ g_free (dst);
+ }
+
+ g_free (src);
+
+ g_object_unref (buffer);
+}
+
+static void
+write_volume_mipmaps (FILE *fp,
+ gint32 image_id,
+ gint32 *layers,
+ int w,
+ int h,
+ int d,
+ int bpp,
+ int fmtbpp,
+ int mipmaps)
+{
+ int i, size, offset, colors;
+ unsigned char *src, *dst, *tmp, *fmtdst;
+ unsigned char *palette = 0;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GimpImageBaseType type;
+
+ type = gimp_image_base_type (image_id);
+
+ if (dds_write_vals.compression != DDS_COMPRESS_NONE) return;
+
+ src = g_malloc (w * h * bpp * d);
+
+ if (bpp == 1)
+ format = babl_format ("Y' u8");
+ else if (bpp == 2)
+ format = babl_format ("Y'A u8");
+ else if (bpp == 3)
+ format = babl_format ("R'G'B' u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+
+ if (gimp_image_base_type (image_id) == GIMP_INDEXED)
+ palette = gimp_image_get_colormap (image_id, &colors);
+
+ offset = 0;
+ for (i = 0; i < d; ++i)
+ {
+ buffer = gimp_drawable_get_buffer (layers[i]);
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format,
+ src + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ offset += (w * h * bpp);
+ g_object_unref (buffer);
+ }
+
+ if (gimp_drawable_type (layers[0]) == GIMP_INDEXEDA_IMAGE)
+ {
+ tmp = g_malloc (w * h * d);
+ for (i = 0; i < w * h * d; ++i)
+ tmp[i] = src[2 * i];
+ g_free (src);
+ src = tmp;
+ bpp = 1;
+ }
+
+ /* we want and assume BGRA ordered pixels for bpp >= 3 from here and
+ onwards */
+ if (bpp >= 3)
+ swap_rb (src, w * h * d, bpp);
+
+ /* pre-convert indexed images to RGB for better mipmaps if a
+ pixel format conversion is requested */
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT && type == GIMP_INDEXED)
+ {
+ size = get_volume_mipmapped_size (w, h, d, 3, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ dst = g_malloc (size);
+ convert_pixels (dst, src, DDS_FORMAT_RGB8, w, h, d, bpp, palette, 1);
+ g_free (src);
+ src = dst;
+ bpp = 3;
+ palette = NULL;
+ }
+
+ size = get_volume_mipmapped_size (w, h, d, bpp, 0, mipmaps,
+ dds_write_vals.compression);
+
+ dst = g_malloc (size);
+
+ offset = get_volume_mipmapped_size (w, h, d, bpp, 0, 1,
+ dds_write_vals.compression);
+
+ generate_volume_mipmaps (dst, src, w, h, d, bpp,
+ palette != NULL, mipmaps,
+ dds_write_vals.mipmap_filter,
+ dds_write_vals.mipmap_wrap,
+ dds_write_vals.gamma_correct + dds_write_vals.srgb,
+ dds_write_vals.gamma);
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ size = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, mipmaps,
+ dds_write_vals.compression);
+ offset = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, 1,
+ dds_write_vals.compression);
+ fmtdst = g_malloc (size);
+
+ convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, d, bpp,
+ palette, mipmaps);
+ g_free (dst);
+ dst = fmtdst;
+ }
+
+ fwrite (dst + offset, 1, size, fp);
+
+ g_free (src);
+ g_free (dst);
+}
+
+static gboolean
+write_image (FILE *fp,
+ gint32 image_id,
+ gint32 drawable_id)
+{
+ GimpImageType drawable_type;
+ GimpImageBaseType basetype;
+ gint i, w, h;
+ gint bpp = 0;
+ gint fmtbpp = 0;
+ gint has_alpha = 0;
+ gint num_mipmaps;
+ guchar hdr[DDS_HEADERSIZE];
+ guchar hdr10[DDS_HEADERSIZE_DX10];
+ guint flags = 0, pflags = 0, caps = 0, caps2 = 0, size = 0;
+ guint rmask = 0, gmask = 0, bmask = 0, amask = 0;
+ guint fourcc = 0;
+ DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
+ gint32 num_layers;
+ gint32 *layers;
+ guchar *cmap;
+ gint colors;
+ guchar zero[4] = {0, 0, 0, 0};
+ gint is_dx10 = 0;
+ gint array_size = 1;
+
+ if (dds_write_vals.flip_image)
+ {
+ gimp_image_flip (image_id, GIMP_ORIENTATION_VERTICAL);
+ drawable_id = gimp_image_get_active_drawable (image_id);
+ }
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING)
+ drawable_id = layers[0];
+
+ if (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER)
+ {
+ w = gimp_drawable_width (drawable_id);
+ h = gimp_drawable_height (drawable_id);
+ }
+ else
+ {
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+ }
+
+ basetype = gimp_image_base_type (image_id);
+ drawable_type = gimp_drawable_type (drawable_id);
+
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE: bpp = 3; break;
+ case GIMP_RGBA_IMAGE: bpp = 4; break;
+ case GIMP_GRAY_IMAGE: bpp = 1; break;
+ case GIMP_GRAYA_IMAGE: bpp = 2; break;
+ case GIMP_INDEXED_IMAGE: bpp = 1; break;
+ case GIMP_INDEXEDA_IMAGE: bpp = 2; break;
+ default:
+ break;
+ }
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ for (i = 0; ; ++i)
+ {
+ if (format_info[i].format == dds_write_vals.format)
+ {
+ fmtbpp = format_info[i].bpp;
+ has_alpha = format_info[i].alpha;
+ rmask = format_info[i].rmask;
+ gmask = format_info[i].gmask;
+ bmask = format_info[i].bmask;
+ amask = format_info[i].amask;
+ dxgi_format = format_info[i].dxgi_format;
+ break;
+ }
+ }
+ }
+ else if (bpp == 1)
+ {
+ if (basetype == GIMP_INDEXED)
+ {
+ fmtbpp = 1;
+ has_alpha = 0;
+ rmask = bmask = gmask = amask = 0;
+ }
+ else
+ {
+ fmtbpp = 1;
+ has_alpha = 0;
+ rmask = 0x000000ff;
+ gmask = bmask = amask = 0;
+ dxgi_format = DXGI_FORMAT_R8_UNORM;
+ }
+ }
+ else if (bpp == 2)
+ {
+ if (basetype == GIMP_INDEXED)
+ {
+ fmtbpp = 1;
+ has_alpha = 0;
+ rmask = gmask = bmask = amask = 0;
+ }
+ else
+ {
+ fmtbpp = 2;
+ has_alpha = 1;
+ rmask = 0x000000ff;
+ gmask = 0x000000ff;
+ bmask = 0x000000ff;
+ amask = 0x0000ff00;
+ }
+ }
+ else if (bpp == 3)
+ {
+ fmtbpp = 3;
+ rmask = 0x00ff0000;
+ gmask = 0x0000ff00;
+ bmask = 0x000000ff;
+ amask = 0x00000000;
+ }
+ else
+ {
+ fmtbpp = 4;
+ has_alpha = 1;
+ rmask = 0x00ff0000;
+ gmask = 0x0000ff00;
+ bmask = 0x000000ff;
+ amask = 0xff000000;
+ dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ }
+
+ memset (hdr, 0, DDS_HEADERSIZE);
+
+ PUTL32(hdr, FOURCC ('D','D','S',' '));
+ PUTL32(hdr + 4, 124);
+ PUTL32(hdr + 12, h);
+ PUTL32(hdr + 16, w);
+ PUTL32(hdr + 76, 32);
+
+ if (dds_write_vals.compression == DDS_COMPRESS_NONE)
+ {
+ PUTL32(hdr + 88, fmtbpp << 3);
+ PUTL32(hdr + 92, rmask);
+ PUTL32(hdr + 96, gmask);
+ PUTL32(hdr + 100, bmask);
+ PUTL32(hdr + 104, amask);
+ }
+
+ /*
+ put some information in the reserved area to identify the origin
+ of the image
+ */
+ PUTL32(hdr + 32, FOURCC ('G','I','M','P'));
+ PUTL32(hdr + 36, FOURCC ('-','D','D','S'));
+ PUTL32(hdr + 40, DDS_PLUGIN_VERSION);
+
+ flags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
+
+ caps = DDSCAPS_TEXTURE;
+ if (dds_write_vals.mipmaps)
+ {
+ flags |= DDSD_MIPMAPCOUNT;
+ caps |= (DDSCAPS_COMPLEX | DDSCAPS_MIPMAP);
+ num_mipmaps = get_num_mipmaps (w, h);
+ }
+ else
+ {
+ num_mipmaps = 1;
+ }
+
+ if ((dds_write_vals.savetype == DDS_SAVE_CUBEMAP) && is_cubemap)
+ {
+ caps |= DDSCAPS_COMPLEX;
+ caps2 |= (DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES);
+ }
+ else if ((dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP) && is_volume)
+ {
+ PUTL32(hdr + 24, num_layers); /* depth */
+ flags |= DDSD_DEPTH;
+ caps |= DDSCAPS_COMPLEX;
+ caps2 |= DDSCAPS2_VOLUME;
+ }
+
+ PUTL32(hdr + 28, num_mipmaps);
+ PUTL32(hdr + 108, caps);
+ PUTL32(hdr + 112, caps2);
+
+ if (dds_write_vals.compression == DDS_COMPRESS_NONE)
+ {
+ flags |= DDSD_PITCH;
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ if (dds_write_vals.format == DDS_FORMAT_A8)
+ pflags |= DDPF_ALPHA;
+ else
+ {
+ if (((fmtbpp == 1) || (dds_write_vals.format == DDS_FORMAT_L8A8)) &&
+ (dds_write_vals.format != DDS_FORMAT_R3G3B2))
+ pflags |= DDPF_LUMINANCE;
+ else
+ pflags |= DDPF_RGB;
+ }
+ }
+ else
+ {
+ if (bpp == 1)
+ {
+ if (basetype == GIMP_INDEXED)
+ pflags |= DDPF_PALETTEINDEXED8;
+ else
+ pflags |= DDPF_LUMINANCE;
+ }
+ else if ((bpp == 2) && (basetype == GIMP_INDEXED))
+ {
+ pflags |= DDPF_PALETTEINDEXED8;
+ }
+ else
+ {
+ pflags |= DDPF_RGB;
+ }
+ }
+
+ if (has_alpha)
+ pflags |= DDPF_ALPHAPIXELS;
+
+ PUTL32 (hdr + 8, flags);
+ PUTL32 (hdr + 20, w * fmtbpp); /* pitch */
+ PUTL32 (hdr + 80, pflags);
+
+ /*
+ * write extra fourcc info - this is special to GIMP DDS. When the image
+ * is read by the plugin, we can detect the added information to decode
+ * the pixels
+ */
+ if (dds_write_vals.format == DDS_FORMAT_AEXP)
+ {
+ PUTL32 (hdr + 44, FOURCC ('A','E','X','P'));
+ }
+ else if (dds_write_vals.format == DDS_FORMAT_YCOCG)
+ {
+ PUTL32 (hdr + 44, FOURCC ('Y','C','G','1'));
+ }
+ }
+ else
+ {
+ flags |= DDSD_LINEARSIZE;
+ pflags = DDPF_FOURCC;
+
+ switch (dds_write_vals.compression)
+ {
+ case DDS_COMPRESS_BC1:
+ fourcc = FOURCC ('D','X','T','1');
+ dxgi_format = DXGI_FORMAT_BC1_UNORM;
+ break;
+
+ case DDS_COMPRESS_BC2:
+ fourcc = FOURCC ('D','X','T','3');
+ dxgi_format = DXGI_FORMAT_BC2_UNORM;
+ break;
+
+ case DDS_COMPRESS_BC3:
+ case DDS_COMPRESS_BC3N:
+ case DDS_COMPRESS_YCOCG:
+ case DDS_COMPRESS_YCOCGS:
+ case DDS_COMPRESS_AEXP:
+ fourcc = FOURCC ('D','X','T','5');
+ dxgi_format = DXGI_FORMAT_BC3_UNORM;
+ break;
+
+ case DDS_COMPRESS_RXGB:
+ fourcc = FOURCC ('R','X','G','B');
+ dxgi_format = DXGI_FORMAT_BC3_UNORM;
+ break;
+
+ case DDS_COMPRESS_BC4:
+ fourcc = FOURCC ('A','T','I','1');
+ dxgi_format = DXGI_FORMAT_BC4_UNORM;
+ //is_dx10 = 1;
+ break;
+
+ case DDS_COMPRESS_BC5:
+ fourcc = FOURCC ('A','T','I','2');
+ dxgi_format = DXGI_FORMAT_BC5_UNORM;
+ //is_dx10 = 1;
+ break;
+ }
+
+ if ((dds_write_vals.compression == DDS_COMPRESS_BC3N) ||
+ (dds_write_vals.compression == DDS_COMPRESS_RXGB))
+ {
+ pflags |= DDPF_NORMAL;
+ }
+
+ PUTL32 (hdr + 8, flags);
+ PUTL32 (hdr + 80, pflags);
+ PUTL32 (hdr + 84, fourcc);
+
+ size = ((w + 3) >> 2) * ((h + 3) >> 2);
+ if ((dds_write_vals.compression == DDS_COMPRESS_BC1) ||
+ (dds_write_vals.compression == DDS_COMPRESS_BC4))
+ size *= 8;
+ else
+ size *= 16;
+
+ PUTL32 (hdr + 20, size); /* linear size */
+
+ /*
+ * write extra fourcc info - this is special to GIMP DDS. When the image
+ * is read by the plugin, we can detect the added information to decode
+ * the pixels
+ */
+ if (dds_write_vals.compression == DDS_COMPRESS_AEXP)
+ {
+ PUTL32 (hdr + 44, FOURCC ('A','E','X','P'));
+ }
+ else if (dds_write_vals.compression == DDS_COMPRESS_YCOCG)
+ {
+ PUTL32 (hdr + 44, FOURCC ('Y','C','G','1'));
+ }
+ else if (dds_write_vals.compression == DDS_COMPRESS_YCOCGS)
+ {
+ PUTL32 (hdr + 44, FOURCC ('Y','C','G','2'));
+ }
+ }
+
+ /* texture arrays require a DX10 header */
+ if (dds_write_vals.savetype == DDS_SAVE_ARRAY)
+ is_dx10 = 1;
+
+ if (is_dx10)
+ {
+ array_size = (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER ||
+ dds_write_vals.savetype == DDS_SAVE_VISIBLE_LAYERS) ? 1 : get_array_size (image_id);
+
+ PUTL32 (hdr10 + 0, dxgi_format);
+ PUTL32 (hdr10 + 4, D3D10_RESOURCE_DIMENSION_TEXTURE2D);
+ PUTL32 (hdr10 + 8, 0);
+ PUTL32 (hdr10 + 12, array_size);
+ PUTL32 (hdr10 + 16, 0);
+
+ /* update main header accordingly */
+ PUTL32 (hdr + 80, pflags | DDPF_FOURCC);
+ PUTL32 (hdr + 84, FOURCC ('D','X','1','0'));
+ }
+
+ fwrite (hdr, DDS_HEADERSIZE, 1, fp);
+
+ if (is_dx10)
+ fwrite (hdr10, DDS_HEADERSIZE_DX10, 1, fp);
+
+ /* write palette for indexed images */
+ if ((basetype == GIMP_INDEXED) &&
+ (dds_write_vals.format == DDS_FORMAT_DEFAULT) &&
+ (dds_write_vals.compression == DDS_COMPRESS_NONE))
+ {
+ cmap = gimp_image_get_colormap (image_id, &colors);
+
+ for (i = 0; i < colors; ++i)
+ {
+ fwrite (&cmap[3 * i], 1, 3, fp);
+ if (i == dds_write_vals.transindex)
+ fputc (0, fp);
+ else
+ fputc (255, fp);
+ }
+
+ for (; i < 256; ++i)
+ fwrite (zero, 1, 4, fp);
+ }
+
+ if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP)
+ {
+ for (i = 0; i < 6; ++i)
+ {
+ write_layer (fp, image_id, cubemap_faces[i], w, h, bpp, fmtbpp,
+ num_mipmaps);
+ gimp_progress_update ((float)(i + 1) / 6.0);
+ }
+ }
+ else if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
+ {
+ for (i = 0; i < num_layers; ++i)
+ {
+ write_layer (fp, image_id, layers[i], w, h, bpp, fmtbpp, 1);
+ gimp_progress_update ((float)i / (float)num_layers);
+ }
+
+ if (num_mipmaps > 1)
+ write_volume_mipmaps (fp, image_id, layers, w, h, num_layers,
+ bpp, fmtbpp, num_mipmaps);
+ }
+ else if (dds_write_vals.savetype == DDS_SAVE_ARRAY)
+ {
+ for (i = 0; i < num_layers; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) == w) &&
+ (gimp_drawable_height (layers[i]) == h))
+ {
+ write_layer (fp, image_id, layers[i],
+ w, h, bpp, fmtbpp, num_mipmaps);
+ }
+
+ gimp_progress_update ((float)i / (float)num_layers);
+ }
+ }
+ else
+ {
+ if (dds_write_vals.savetype == DDS_SAVE_VISIBLE_LAYERS)
+ drawable_id = gimp_image_merge_visible_layers (image_id, 1);
+ write_layer (fp, image_id, drawable_id, w, h, bpp, fmtbpp, num_mipmaps);
+ }
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+}
+
+static GtkWidget *
+string_value_combo_new (string_value_t *strings,
+ int active_value)
+{
+ GtkWidget *opt;
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gint i;
+ gint active = 0;
+
+ store = gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN);
+ for (i = 0; strings[i].string; ++i)
+ {
+ if (strings[i].value == active_value) active = i;
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ 0, strings[i].value,
+ 1, strings[i].string,
+ 2, 1,
+ -1);
+ }
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ opt = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (opt), renderer, 1);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (opt), renderer,
+ "text", COMBO_STRING,
+ "sensitive", COMBO_SENSITIVE,
+ NULL);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (opt), active);
+
+ g_object_unref (store);
+
+ return opt;
+}
+
+static void
+string_value_combo_selected (GtkWidget *widget,
+ gpointer data)
+{
+ gint value;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+ gtk_tree_model_get (model, &iter, COMBO_VALUE, &value, -1);
+
+ *((int *)data) = value;
+}
+
+static void
+string_value_combo_set_item_sensitive (GtkWidget *widget,
+ gint value,
+ gboolean sensitive)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gint val;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_tree_model_get_iter_first (model, &iter);
+ do
+ {
+ gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1);
+ if (val == value)
+ {
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ COMBO_SENSITIVE, sensitive, -1);
+ break;
+ }
+ } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+string_value_combo_set_active (GtkWidget *widget,
+ gint value)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ int val;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_tree_model_get_iter_first (model, &iter);
+ do
+ {
+ gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1);
+ if (val == value)
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
+ break;
+ }
+ } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ runme = TRUE;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static void
+compression_selected (GtkWidget *widget,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+ gtk_tree_model_get (model, &iter, COMBO_VALUE,
+ &dds_write_vals.compression,
+ -1);
+
+ gtk_widget_set_sensitive (format_opt,
+ dds_write_vals.compression == DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (pm_chk,
+ dds_write_vals.compression != DDS_COMPRESS_NONE);
+}
+
+static void
+savetype_selected (GtkWidget *widget,
+ gpointer data)
+{
+ gint32 image_id = *((gint32 *)data);
+
+ dds_write_vals.savetype = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
+
+ switch (dds_write_vals.savetype)
+ {
+ case DDS_SAVE_SELECTED_LAYER:
+ case DDS_SAVE_VISIBLE_LAYERS:
+ case DDS_SAVE_CUBEMAP:
+ case DDS_SAVE_ARRAY:
+ gtk_widget_set_sensitive (compress_opt, TRUE);
+ break;
+
+ case DDS_SAVE_VOLUMEMAP:
+ dds_write_vals.compression = DDS_COMPRESS_NONE;
+ gtk_combo_box_set_active (GTK_COMBO_BOX (compress_opt),
+ DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (compress_opt, FALSE);
+ break;
+ }
+
+ string_value_combo_set_item_sensitive (mipmap_opt, DDS_MIPMAP_EXISTING,
+ check_mipmaps (image_id, dds_write_vals.savetype));
+}
+
+static void
+mipmaps_selected (GtkWidget *widget,
+ gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+ gtk_tree_model_get (model, &iter, COMBO_VALUE,
+ &dds_write_vals.mipmaps, -1);
+
+ gtk_widget_set_sensitive (mipmap_filter_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (mipmap_wrap_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (gamma_chk,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (srgb_chk,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct);
+ gtk_widget_set_sensitive (gamma_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct &&
+ !dds_write_vals.srgb);
+ gtk_widget_set_sensitive (alpha_coverage_chk,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (alpha_test_threshold_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.preserve_alpha_coverage);
+}
+
+static void
+toggle_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ gint *flag = (int *)data;
+
+ (*flag) = !(*flag);
+}
+
+static void
+transindex_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *spin = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "spin"));
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ dds_write_vals.transindex = 0;
+ gtk_widget_set_sensitive (spin, TRUE);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 0);
+ }
+ else
+ {
+ gtk_widget_set_sensitive (spin, FALSE);
+ dds_write_vals.transindex = -1;
+ }
+}
+
+static void
+transindex_changed (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.transindex =
+ gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+}
+
+static void
+gamma_correct_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.gamma_correct =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ gtk_widget_set_sensitive (srgb_chk,
+ dds_write_vals.gamma_correct);
+ gtk_widget_set_sensitive (gamma_spin,
+ dds_write_vals.gamma_correct &&
+ ! dds_write_vals.srgb);
+}
+
+static void
+srgb_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.srgb =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ gtk_widget_set_sensitive (gamma_spin, !dds_write_vals.srgb);
+}
+
+static void
+gamma_changed (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.gamma =
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
+}
+
+static void
+alpha_coverage_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.preserve_alpha_coverage =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ gtk_widget_set_sensitive (alpha_test_threshold_spin,
+ dds_write_vals.preserve_alpha_coverage);
+}
+
+static void
+alpha_test_threshold_changed (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.alpha_test_threshold =
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
+}
+
+static gint
+save_dialog (gint32 image_id,
+ gint32 drawable_id)
+{
+ GtkWidget *dlg;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *opt;
+ GtkWidget *check;
+ GtkWidget *spin;
+ GtkWidget *frame;
+ GimpImageBaseType basetype;
+
+ if (is_cubemap || is_volume || is_array)
+ dds_write_vals.savetype = DDS_SAVE_SELECTED_LAYER;
+
+ basetype = gimp_image_base_type (image_id);
+
+ dlg = gimp_dialog_new (_("Export as DDS"), "dds", NULL, GTK_WIN_POS_MOUSE,
+ gimp_standard_help_func, SAVE_PROC,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Export"), GTK_RESPONSE_OK,
+ NULL);
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (save_dialog_response),
+ NULL);
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (6, 2, 0);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 8);
+
+ opt = string_value_combo_new (compression_strings,
+ dds_write_vals.compression);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Compression:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (compression_selected),
+ NULL);
+
+ compress_opt = opt;
+
+ check = gtk_check_button_new_with_mnemonic (_("Use _perceptual error metric"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.perceptual_metric);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 1, 2,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked),
+ &dds_write_vals.perceptual_metric);
+
+ pm_chk = check;
+
+ opt = string_value_combo_new (format_strings, dds_write_vals.format);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Format:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (string_value_combo_selected),
+ &dds_write_vals.format);
+
+ gtk_widget_set_sensitive (opt, dds_write_vals.compression == DDS_COMPRESS_NONE);
+
+ format_opt = opt;
+
+ opt = string_value_combo_new (save_type_strings, dds_write_vals.savetype);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Save:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (savetype_selected),
+ &image_id);
+
+ string_value_combo_set_item_sensitive (opt, DDS_SAVE_CUBEMAP, is_cubemap);
+ string_value_combo_set_item_sensitive (opt, DDS_SAVE_VOLUMEMAP, is_volume);
+ string_value_combo_set_item_sensitive (opt, DDS_SAVE_ARRAY, is_array);
+
+ check = gtk_check_button_new_with_mnemonic (_("Flip the image _vertically on export"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.flip_image);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 4, 5,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked),
+ &dds_write_vals.flip_image);
+
+ opt = string_value_combo_new (mipmap_strings, dds_write_vals.mipmaps);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 5,
+ _("_Mipmaps:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (mipmaps_selected),
+ &image_id);
+
+ string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING,
+ check_mipmaps (image_id,
+ dds_write_vals.savetype));
+
+ mipmap_opt = opt;
+
+ string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING,
+ ! (is_volume || is_cubemap) &&
+ is_mipmap_chain_valid);
+
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_label (_("Transparent index:"));
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (transindex_clicked),
+ NULL);
+
+ spin = gimp_spin_button_new
+ (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 255, 1, 1, 0)), 1, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin),
+ GTK_UPDATE_IF_VALID);
+ gtk_widget_show (spin);
+
+ g_signal_connect (spin, "value-changed",
+ G_CALLBACK (transindex_changed),
+ NULL);
+
+ g_object_set_data (G_OBJECT (check), "spin", spin);
+
+ if (basetype != GIMP_INDEXED)
+ {
+ gtk_widget_set_sensitive (check, FALSE);
+ gtk_widget_set_sensitive (spin, FALSE);
+ }
+ else if (dds_write_vals.transindex < 0)
+ {
+ gtk_widget_set_sensitive (spin, FALSE);
+ }
+ else if (dds_write_vals.transindex >= 0)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin),
+ dds_write_vals.transindex);
+ }
+
+ if (is_volume && dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
+ {
+ dds_write_vals.compression = DDS_COMPRESS_NONE;
+ string_value_combo_set_active (compress_opt, DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (compress_opt, FALSE);
+ }
+
+ frame = gimp_frame_new (_("Mipmap Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (7, 2, 0);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 8);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ opt = string_value_combo_new (mipmap_filter_strings,
+ dds_write_vals.mipmap_filter);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("F_ilter:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (string_value_combo_selected),
+ &dds_write_vals.mipmap_filter);
+
+ mipmap_filter_opt = opt;
+
+ opt = string_value_combo_new (mipmap_wrap_strings,
+ dds_write_vals.mipmap_wrap);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Wrap mode:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (string_value_combo_selected),
+ &dds_write_vals.mipmap_wrap);
+
+ mipmap_wrap_opt = opt;
+
+ check = gtk_check_button_new_with_mnemonic (_("Appl_y gamma correction"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.gamma_correct &&
+ dds_write_vals.mipmaps);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 2, 3,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (gamma_correct_clicked),
+ NULL);
+
+ gamma_chk = check;
+
+ check = gtk_check_button_new_with_mnemonic (_("Use s_RGB colorspace"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.gamma_correct &&
+ dds_write_vals.srgb);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 3, 4,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (srgb_clicked),
+ NULL);
+
+ srgb_chk = check;
+
+ spin = gimp_spin_button_new
+ (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.gamma,
+ 1e-05, 100, 0.1, 0.5, 0)), 1, 1);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("_Gamma:"),
+ 0.0, 0.5,
+ spin, 1, FALSE);
+
+ g_signal_connect (spin, "value_changed",
+ G_CALLBACK (gamma_changed),
+ NULL);
+
+ gamma_spin = spin;
+
+ check = gtk_check_button_new_with_mnemonic (_("Preserve alpha _test coverage"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.preserve_alpha_coverage &&
+ dds_write_vals.mipmaps);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 5, 6,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (alpha_coverage_clicked),
+ NULL);
+
+ alpha_coverage_chk = check;
+
+ spin = gimp_spin_button_new
+ (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.alpha_test_threshold,
+ 0, 1, 0.01, 0.1, 0)), 1, 2);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
+ _("_Alpha test threshold:"),
+ 0.0, 0.5,
+ spin, 1, FALSE);
+
+ g_signal_connect (spin, "value_changed",
+ G_CALLBACK (alpha_test_threshold_changed),
+ NULL);
+
+ alpha_test_threshold_spin = spin;
+
+ gtk_widget_set_sensitive (mipmap_filter_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (mipmap_wrap_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (gamma_chk
+ , dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (srgb_chk,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct);
+ gtk_widget_set_sensitive (gamma_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct &&
+ !dds_write_vals.srgb);
+ gtk_widget_set_sensitive (pm_chk,
+ dds_write_vals.compression != DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (alpha_coverage_chk,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (alpha_test_threshold_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.preserve_alpha_coverage);
+
+ gtk_widget_show (dlg);
+
+ runme = FALSE;
+
+ gtk_main ();
+
+ return runme;
+}