summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-dds/ddsread.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/file-dds/ddsread.c')
-rw-r--r--plug-ins/file-dds/ddsread.c1439
1 files changed, 1439 insertions, 0 deletions
diff --git a/plug-ins/file-dds/ddsread.c b/plug-ins/file-dds/ddsread.c
new file mode 100644
index 0000000..dcb4449
--- /dev/null
+++ b/plug-ins/file-dds/ddsread.c
@@ -0,0 +1,1439 @@
+/*
+ * 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.
+ */
+
+/*
+ ** !!! COPYRIGHT NOTICE !!!
+ **
+ ** The following is based on code (C) 2003 Arne Reuter <homepage@arnereuter.de>
+ ** URL: http://www.dr-reuter.de/arne/dds.html
+ **
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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 "endian_rw.h"
+#include "misc.h"
+#include "imath.h"
+
+typedef struct
+{
+ unsigned char rshift, gshift, bshift, ashift;
+ unsigned char rbits, gbits, bbits, abits;
+ unsigned int rmask, gmask, bmask, amask;
+ unsigned int bpp, gimp_bpp;
+ unsigned int gimp_bps; /* bytes per sample */
+ int tile_height;
+ unsigned char *palette;
+} dds_load_info_t;
+
+static int read_header (dds_header_t *hdr,
+ FILE *fp);
+static int read_header_dx10 (dds_header_dx10_t *hdr,
+ FILE *fp);
+static int validate_header (dds_header_t *hdr);
+static int setup_dxgi_format (dds_header_t *hdr,
+ dds_header_dx10_t *dx10hdr);
+static int load_layer (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ unsigned int level,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf);
+static int load_mipmaps (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf);
+static int load_face (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf);
+static unsigned char color_bits (unsigned int mask);
+static unsigned char color_shift (unsigned int mask);
+static int load_dialog (void);
+
+static gboolean runme = FALSE;
+
+GimpPDBStatusType
+read_dds (gchar *filename,
+ gint32 *imageID,
+ gboolean interactive_dds)
+{
+ gint32 image = 0;
+ unsigned char *buf;
+ unsigned int l = 0;
+ guchar *pixels;
+ gchar *tmp;
+ FILE *fp;
+ gsize file_size;
+ dds_header_t hdr;
+ dds_header_dx10_t dx10hdr;
+ dds_load_info_t d;
+ gint *layers, layer_count;
+ GimpImageBaseType type;
+ GimpPrecision precision;
+ int i, j;
+
+ if (interactive_dds)
+ {
+ if (!load_dialog ())
+ return GIMP_PDB_CANCEL;
+ }
+
+ fp = g_fopen (filename, "rb");
+ if (fp == 0)
+ {
+ g_message ("Error opening file.\n");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ fseek (fp, 0L, SEEK_END);
+ file_size = ftell (fp);
+ fseek (fp, 0, SEEK_SET);
+
+ if (strrchr (filename, '/'))
+ tmp = g_strdup_printf ("Loading %s:", strrchr (filename, '/') + 1);
+ else
+ tmp = g_strdup_printf ("Loading %s:", filename);
+ gimp_progress_init (tmp);
+ g_free (tmp);
+
+ /* read header */
+ read_header (&hdr, fp);
+
+ memset (&dx10hdr, 0, sizeof (dds_header_dx10_t));
+
+ /* read DX10 header if necessary */
+ if (GETL32(hdr.pixelfmt.fourcc) == FOURCC ('D','X','1','0'))
+ {
+ read_header_dx10(&dx10hdr, fp);
+
+ if (!setup_dxgi_format (&hdr, &dx10hdr))
+ {
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (!validate_header (&hdr))
+ {
+ fclose (fp);
+ g_message ("Invalid DDS header!\n");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* a lot of DDS images out there don't have this for some reason -_- */
+ if (hdr.pitch_or_linsize == 0)
+ {
+ if (hdr.pixelfmt.flags & DDPF_FOURCC) /* assume linear size */
+ {
+ hdr.pitch_or_linsize = ((hdr.width + 3) >> 2) * ((hdr.height + 3) >> 2);
+ switch (GETL32(hdr.pixelfmt.fourcc))
+ {
+ case FOURCC ('D','X','T','1'):
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'):
+ hdr.pitch_or_linsize *= 8;
+ break;
+ default:
+ hdr.pitch_or_linsize *= 16;
+ break;
+ }
+ }
+ else /* assume pitch */
+ {
+ hdr.pitch_or_linsize = hdr.height * hdr.width * (hdr.pixelfmt.bpp >> 3);
+ }
+ }
+
+ if (hdr.pixelfmt.flags & DDPF_FOURCC)
+ {
+ /* fourcc is dXt* or rXgb */
+ if (hdr.pixelfmt.fourcc[1] == 'X')
+ hdr.pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ }
+
+ d.gimp_bps = 1; /* Most formats will be converted to 1 byte per sample */
+ if (hdr.pixelfmt.flags & DDPF_FOURCC)
+ {
+ switch (GETL32(hdr.pixelfmt.fourcc))
+ {
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'):
+ d.bpp = d.gimp_bpp = 1;
+ type = GIMP_GRAY;
+ break;
+ case FOURCC ('A','T','I','2'):
+ case FOURCC ('B','C','5','U'):
+ case FOURCC ('B','C','5','S'):
+ d.bpp = d.gimp_bpp = 3;
+ type = GIMP_RGB;
+ break;
+ default:
+ d.bpp = d.gimp_bpp = 4;
+ type = GIMP_RGB;
+ break;
+ }
+ }
+ else
+ {
+ d.bpp = hdr.pixelfmt.bpp >> 3;
+
+ if (d.bpp == 2)
+ {
+ if (hdr.pixelfmt.amask == 0xf000) // RGBA4
+ {
+ d.gimp_bpp = 4;
+ type = GIMP_RGB;
+ }
+ else if (hdr.pixelfmt.amask == 0xff00) //L8A8
+ {
+ d.gimp_bpp = 2;
+ type = GIMP_GRAY;
+ }
+ else if (hdr.pixelfmt.bmask == 0x1f) //R5G6B5 or RGB5A1
+ {
+ if (hdr.pixelfmt.amask == 0x8000) // RGB5A1
+ d.gimp_bpp = 4;
+ else
+ d.gimp_bpp = 3;
+
+ type = GIMP_RGB;
+ }
+ else if (hdr.pixelfmt.rmask == 0xffff) /* L16 */
+ {
+ d.gimp_bpp = 2;
+ d.gimp_bps = 2;
+ type = GIMP_GRAY;
+ }
+ else
+ {
+ g_message ("Unsupported uncompressed dds format: "
+ "bpp: %d, Rmask: %x, Gmask: %x, Bmask: %x, Amask: %x",
+ hdr.pixelfmt.bpp,
+ hdr.pixelfmt.rmask, hdr.pixelfmt.gmask,
+ hdr.pixelfmt.bmask, hdr.pixelfmt.amask);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ type = GIMP_INDEXED;
+ d.gimp_bpp = 1;
+ }
+ else if (hdr.pixelfmt.rmask == 0xe0) // R3G3B2
+ {
+ type = GIMP_RGB;
+ d.gimp_bpp = 3;
+ }
+ else
+ {
+ /* test alpha only image */
+ if (d.bpp == 1 && (hdr.pixelfmt.flags & DDPF_ALPHA))
+ {
+ d.gimp_bpp = 2;
+ type = GIMP_GRAY;
+ }
+ else
+ {
+ d.gimp_bpp = d.bpp;
+ type = (d.bpp == 1) ? GIMP_GRAY : GIMP_RGB;
+ }
+ }
+ }
+ }
+
+ if (d.gimp_bps == 2)
+ {
+ precision = GIMP_PRECISION_U16_GAMMA;
+ }
+ else
+ {
+ precision = GIMP_PRECISION_U8_GAMMA;
+ }
+
+ /* verify header information is accurate */
+ if (d.bpp < 1 ||
+ (hdr.pitch_or_linsize > (file_size - sizeof (hdr))))
+ {
+ fclose (fp);
+ g_message ("Invalid or corrupted DDS header\n");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ image = gimp_image_new_with_precision (hdr.width, hdr.height, type, precision);
+
+ if (image == -1)
+ {
+ g_message ("Can't allocate new image.\n");
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_image_set_filename (image, filename);
+
+ if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ d.palette = g_malloc (256 * 4);
+ if (fread (d.palette, 1, 1024, fp) != 1024)
+ {
+ g_message ("Error reading palette.\n");
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ for (i = j = 0; i < 768; i += 3, j += 4)
+ {
+ d.palette[i + 0] = d.palette[j + 0];
+ d.palette[i + 1] = d.palette[j + 1];
+ d.palette[i + 2] = d.palette[j + 2];
+ }
+ gimp_image_set_colormap (image, d.palette, 256);
+ }
+
+ d.tile_height = gimp_tile_height ();
+
+ pixels = g_new (guchar, d.tile_height * hdr.width * d.gimp_bpp);
+ buf = g_malloc (hdr.pitch_or_linsize);
+
+ d.rshift = color_shift (hdr.pixelfmt.rmask);
+ d.gshift = color_shift (hdr.pixelfmt.gmask);
+ d.bshift = color_shift (hdr.pixelfmt.bmask);
+ d.ashift = color_shift (hdr.pixelfmt.amask);
+ d.rbits = color_bits (hdr.pixelfmt.rmask);
+ d.gbits = color_bits (hdr.pixelfmt.gmask);
+ d.bbits = color_bits (hdr.pixelfmt.bmask);
+ d.abits = color_bits (hdr.pixelfmt.amask);
+ d.rmask = (hdr.pixelfmt.rmask >> d.rshift) << (8 - d.rbits);
+ d.gmask = (hdr.pixelfmt.gmask >> d.gshift) << (8 - d.gbits);
+ d.bmask = (hdr.pixelfmt.bmask >> d.bshift) << (8 - d.bbits);
+ d.amask = (hdr.pixelfmt.amask >> d.ashift) << (8 - d.abits);
+
+ if (!(hdr.caps.caps2 & DDSCAPS2_CUBEMAP) &&
+ !(hdr.caps.caps2 & DDSCAPS2_VOLUME) &&
+ dx10hdr.arraySize == 0)
+ {
+ if (!load_layer (fp, &hdr, &d, image, 0, "", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if (!load_mipmaps (fp, &hdr, &d, image, "", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (hdr.caps.caps2 & DDSCAPS2_CUBEMAP)
+ {
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEX) &&
+ !load_face (fp, &hdr, &d, image, "(positive x)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) &&
+ !load_face (fp, &hdr, &d, image, "(negative x)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEY) &&
+ !load_face (fp, &hdr, &d, image, "(positive y)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) &&
+ !load_face (fp, &hdr, &d, image, "(negative y)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) &&
+ !load_face (fp, &hdr, &d, image, "(positive z)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) &&
+ !load_face (fp, &hdr, &d, image, "(negative z)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if ((hdr.caps.caps2 & DDSCAPS2_VOLUME) &&
+ (hdr.flags & DDSD_DEPTH))
+ {
+ unsigned int i, level;
+ char *plane;
+ for (i = 0; i < hdr.depth; ++i)
+ {
+ plane = g_strdup_printf ("(z = %d)", i);
+ if (!load_layer (fp, &hdr, &d, image, 0, plane, &l, pixels, buf))
+ {
+ g_free (plane);
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_free (plane);
+ }
+
+ if ((hdr.flags & DDSD_MIPMAPCOUNT) &&
+ (hdr.caps.caps1 & DDSCAPS_MIPMAP) &&
+ (dds_read_vals.mipmaps != 0))
+ {
+ for (level = 1; level < hdr.num_mipmaps; ++level)
+ {
+ int n = hdr.depth >> level;
+ if (n < 1) n = 1;
+ for (i = 0; i < n; ++i)
+ {
+ plane = g_strdup_printf ("(z = %d)", i);
+ if (!load_layer (fp, &hdr, &d, image, level, plane, &l, pixels, buf))
+ {
+ g_free (plane);
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_free (plane);
+ }
+ }
+ }
+ }
+ else if (dx10hdr.arraySize > 0)
+ {
+ unsigned int i;
+ char *elem;
+
+ for (i = 0; i < dx10hdr.arraySize; ++i)
+ {
+ elem = g_strdup_printf ("(array element %d)", i);
+ if (!load_layer (fp, &hdr, &d, image, 0, elem, &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if (!load_mipmaps (fp, &hdr, &d, image, elem, &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_free (elem);
+ }
+ }
+
+ gimp_progress_update (1.0);
+
+ if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ g_free (d.palette);
+
+ g_free (buf);
+ g_free (pixels);
+ fclose (fp);
+
+ layers = gimp_image_get_layers (image, &layer_count);
+
+ if (layers == NULL || layer_count == 0)
+ {
+ g_message ("Oops! NULL image read! Please report this!");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_image_set_active_layer (image, layers[0]);
+ g_free (layers);
+
+ *imageID = image;
+
+ return GIMP_PDB_SUCCESS;
+}
+
+static int
+read_header (dds_header_t *hdr,
+ FILE *fp)
+{
+ unsigned char buf[DDS_HEADERSIZE];
+
+ memset (hdr, 0, sizeof (dds_header_t));
+
+ if (fread (buf, 1, DDS_HEADERSIZE, fp) != DDS_HEADERSIZE)
+ return 0;
+
+ hdr->magic = GETL32(buf);
+
+ hdr->size = GETL32(buf + 4);
+ hdr->flags = GETL32(buf + 8);
+ hdr->height = GETL32(buf + 12);
+ hdr->width = GETL32(buf + 16);
+ hdr->pitch_or_linsize = GETL32(buf + 20);
+ hdr->depth = GETL32(buf + 24);
+ hdr->num_mipmaps = GETL32(buf + 28);
+
+ hdr->pixelfmt.size = GETL32(buf + 76);
+ hdr->pixelfmt.flags = GETL32(buf + 80);
+ hdr->pixelfmt.fourcc[0] = buf[84];
+ hdr->pixelfmt.fourcc[1] = buf[85];
+ hdr->pixelfmt.fourcc[2] = buf[86];
+ hdr->pixelfmt.fourcc[3] = buf[87];
+ hdr->pixelfmt.bpp = GETL32(buf + 88);
+ hdr->pixelfmt.rmask = GETL32(buf + 92);
+ hdr->pixelfmt.gmask = GETL32(buf + 96);
+ hdr->pixelfmt.bmask = GETL32(buf + 100);
+ hdr->pixelfmt.amask = GETL32(buf + 104);
+
+ hdr->caps.caps1 = GETL32(buf + 108);
+ hdr->caps.caps2 = GETL32(buf + 112);
+
+ /* GIMP-DDS special info */
+ if (GETL32(buf + 32) == FOURCC ('G','I','M','P') &&
+ GETL32(buf + 36) == FOURCC ('-','D','D','S'))
+ {
+ hdr->reserved.gimp_dds_special.magic1 = GETL32(buf + 32);
+ hdr->reserved.gimp_dds_special.magic2 = GETL32(buf + 36);
+ hdr->reserved.gimp_dds_special.version = GETL32(buf + 40);
+ hdr->reserved.gimp_dds_special.extra_fourcc = GETL32(buf + 44);
+ }
+
+ return 1;
+}
+
+static int
+read_header_dx10 (dds_header_dx10_t *hdr,
+ FILE *fp)
+{
+ char buf[DDS_HEADERSIZE_DX10];
+
+ memset (hdr, 0, sizeof (dds_header_dx10_t));
+
+ if (fread (buf, 1, DDS_HEADERSIZE_DX10, fp) != DDS_HEADERSIZE_DX10)
+ return 0;
+
+ hdr->dxgiFormat = GETL32(buf);
+ hdr->resourceDimension = GETL32(buf + 4);
+ hdr->miscFlag = GETL32(buf + 8);
+ hdr->arraySize = GETL32(buf + 12);
+ hdr->reserved = GETL32(buf + 16);
+
+ return 1;
+}
+
+static int
+validate_header (dds_header_t *hdr)
+{
+ unsigned int fourcc;
+
+ if (hdr->magic != FOURCC ('D','D','S',' '))
+ {
+ g_message ("Invalid DDS file.\n");
+ return 0;
+ }
+
+ if (hdr->pixelfmt.flags & DDPF_FOURCC)
+ {
+ if (hdr->flags & DDSD_PITCH)
+ {
+ g_message ("Warning: DDSD_PITCH is incorrectly set for DDPF_FOURCC!");
+ hdr->flags &= DDSD_PITCH;
+ }
+ if (! (hdr->flags & DDSD_LINEARSIZE))
+ {
+ g_message ("Warning: DDSD_LINEARSIZE is incorrectly not set for DDPF_FOURCC!");
+ hdr->flags |= DDSD_LINEARSIZE;
+ }
+ }
+ else
+ {
+ if (! (hdr->flags & DDSD_PITCH))
+ {
+ g_printerr ("Warning: DDSD_PITCH is incorrectly not set for an uncompressed texture! (recovered)\n");
+ hdr->flags |= DDSD_PITCH;
+ }
+ if ((hdr->flags & DDSD_LINEARSIZE))
+ {
+ g_printerr ("Warning: DDSD_LINEARSIZE is incorrectly set for an uncompressed texture! (recovered)\n");
+ hdr->flags &= DDSD_LINEARSIZE;
+ }
+ }
+
+ /*
+ if ((hdr->pixelfmt.flags & DDPF_FOURCC) ==
+ (hdr->pixelfmt.flags & DDPF_RGB))
+ {
+ g_message ("Invalid pixel format.\n");
+ return 0;
+ }
+ */
+ fourcc = GETL32(hdr->pixelfmt.fourcc);
+
+ if ((hdr->pixelfmt.flags & DDPF_FOURCC) &&
+ fourcc != FOURCC ('D','X','T','1') &&
+ fourcc != FOURCC ('D','X','T','2') &&
+ fourcc != FOURCC ('D','X','T','3') &&
+ fourcc != FOURCC ('D','X','T','4') &&
+ fourcc != FOURCC ('D','X','T','5') &&
+ fourcc != FOURCC ('R','X','G','B') &&
+ fourcc != FOURCC ('A','T','I','1') &&
+ fourcc != FOURCC ('B','C','4','U') &&
+ fourcc != FOURCC ('B','C','4','S') &&
+ fourcc != FOURCC ('A','T','I','2') &&
+ fourcc != FOURCC ('B','C','5','U') &&
+ fourcc != FOURCC ('B','C','5','S') &&
+ fourcc != FOURCC ('D','X','1','0'))
+ {
+ g_message ("Unsupported format (FOURCC: %c%c%c%c, hex: %08x).\n",
+ hdr->pixelfmt.fourcc[0],
+ hdr->pixelfmt.fourcc[1],
+ hdr->pixelfmt.fourcc[2],
+ hdr->pixelfmt.fourcc[3],
+ GETL32(hdr->pixelfmt.fourcc));
+ return 0;
+ }
+
+ if (hdr->pixelfmt.flags & DDPF_RGB)
+ {
+ if ((hdr->pixelfmt.bpp != 8) &&
+ (hdr->pixelfmt.bpp != 16) &&
+ (hdr->pixelfmt.bpp != 24) &&
+ (hdr->pixelfmt.bpp != 32))
+ {
+ g_message ("Invalid BPP.\n");
+ return 0;
+ }
+ }
+ else if (hdr->pixelfmt.flags & DDPF_LUMINANCE)
+ {
+ if ((hdr->pixelfmt.bpp != 8) &&
+ (hdr->pixelfmt.bpp != 16))
+ {
+ g_message ("Invalid BPP.\n");
+ return 0;
+ }
+
+ hdr->pixelfmt.flags |= DDPF_RGB;
+ }
+ else if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ hdr->pixelfmt.flags |= DDPF_RGB;
+ }
+
+ if (!(hdr->pixelfmt.flags & DDPF_RGB) &&
+ !(hdr->pixelfmt.flags & DDPF_ALPHA) &&
+ !(hdr->pixelfmt.flags & DDPF_FOURCC) &&
+ !(hdr->pixelfmt.flags & DDPF_LUMINANCE))
+ {
+ g_message ("Unknown pixel format! Taking a guess, expect trouble!");
+ switch (fourcc)
+ {
+ case FOURCC ('D','X','T','1'):
+ case FOURCC ('D','X','T','2'):
+ case FOURCC ('D','X','T','3'):
+ case FOURCC ('D','X','T','4'):
+ case FOURCC ('D','X','T','5'):
+ case FOURCC ('R','X','G','B'):
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'):
+ case FOURCC ('A','T','I','2'):
+ case FOURCC ('B','C','5','U'):
+ case FOURCC ('B','C','5','S'):
+ hdr->pixelfmt.flags |= DDPF_FOURCC;
+ break;
+ default:
+ switch (hdr->pixelfmt.bpp)
+ {
+ case 8:
+ if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
+ hdr->pixelfmt.flags |= DDPF_ALPHA;
+ else
+ hdr->pixelfmt.flags |= DDPF_LUMINANCE;
+ break;
+ case 16:
+ case 24:
+ case 32:
+ hdr->pixelfmt.flags |= DDPF_RGB;
+ break;
+ default:
+ g_message ("Invalid pixel format.");
+ return 0;
+ }
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * This function will set the necessary flags and attributes in the standard
+ * dds header using the information found in the DX10 header.
+ */
+static int
+setup_dxgi_format (dds_header_t *hdr,
+ dds_header_dx10_t *dx10hdr)
+{
+ if ((dx10hdr->resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE2D) &&
+ (dx10hdr->miscFlag & D3D10_RESOURCE_MISC_TEXTURECUBE))
+ {
+ hdr->caps.caps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES;
+ }
+ else if (dx10hdr->resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D)
+ {
+ hdr->flags |= DDSD_DEPTH;
+ hdr->caps.caps2 |= DDSCAPS2_VOLUME;
+ }
+
+ if ((dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE1D) &&
+ (dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE2D) &&
+ (dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE3D))
+ return 0;
+
+ // check for a compressed DXGI format
+ if ((dx10hdr->dxgiFormat >= DXGI_FORMAT_BC1_TYPELESS) &&
+ (dx10hdr->dxgiFormat <= DXGI_FORMAT_BC5_SNORM))
+ {
+ // set flag and replace FOURCC
+ hdr->pixelfmt.flags |= DDPF_FOURCC;
+
+ switch (dx10hdr->dxgiFormat)
+ {
+ case DXGI_FORMAT_BC1_TYPELESS:
+ case DXGI_FORMAT_BC1_UNORM:
+ case DXGI_FORMAT_BC1_UNORM_SRGB:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','1'));
+ break;
+ case DXGI_FORMAT_BC2_TYPELESS:
+ case DXGI_FORMAT_BC2_UNORM:
+ case DXGI_FORMAT_BC2_UNORM_SRGB:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','3'));
+ break;
+ case DXGI_FORMAT_BC3_TYPELESS:
+ case DXGI_FORMAT_BC3_UNORM:
+ case DXGI_FORMAT_BC3_UNORM_SRGB:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','5'));
+ break;
+ case DXGI_FORMAT_BC4_TYPELESS:
+ case DXGI_FORMAT_BC4_UNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('A','T','I','1'));
+ break;
+ case DXGI_FORMAT_BC4_SNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('B','C','4','S'));
+ break;
+ case DXGI_FORMAT_BC5_TYPELESS:
+ case DXGI_FORMAT_BC5_UNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('A','T','I','2'));
+ break;
+ case DXGI_FORMAT_BC5_SNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('B','C','5','S'));
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ /* unset the FOURCC flag */
+ hdr->pixelfmt.flags &= ~DDPF_FOURCC;
+
+ switch (dx10hdr->dxgiFormat)
+ {
+ case DXGI_FORMAT_B8G8R8A8_TYPELESS:
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x00ff0000;
+ hdr->pixelfmt.gmask = 0x0000ff00;
+ hdr->pixelfmt.bmask = 0x000000ff;
+ hdr->pixelfmt.amask = 0xff000000;
+ break;
+ case DXGI_FORMAT_B8G8R8X8_TYPELESS:
+ case DXGI_FORMAT_B8G8R8X8_UNORM:
+ case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x00ff0000;
+ hdr->pixelfmt.gmask = 0x0000ff00;
+ hdr->pixelfmt.bmask = 0x000000ff;
+ hdr->pixelfmt.amask = 0x00000000;
+ break;
+ case DXGI_FORMAT_R8G8B8A8_TYPELESS:
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+ case DXGI_FORMAT_R8G8B8A8_UINT:
+ case DXGI_FORMAT_R8G8B8A8_SNORM:
+ case DXGI_FORMAT_R8G8B8A8_SINT:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x000000ff;
+ hdr->pixelfmt.gmask = 0x0000ff00;
+ hdr->pixelfmt.bmask = 0x00ff0000;
+ hdr->pixelfmt.amask = 0xff000000;
+ break;
+ case DXGI_FORMAT_B5G6R5_UNORM:
+ hdr->pixelfmt.bpp = 16;
+ hdr->pixelfmt.rmask = 0x0000f800;
+ hdr->pixelfmt.gmask = 0x000007e0;
+ hdr->pixelfmt.bmask = 0x0000001f;
+ hdr->pixelfmt.amask = 0x00000000;
+ break;
+ case DXGI_FORMAT_B5G5R5A1_UNORM:
+ hdr->pixelfmt.bpp = 16;
+ hdr->pixelfmt.rmask = 0x00007c00;
+ hdr->pixelfmt.gmask = 0x000003e0;
+ hdr->pixelfmt.bmask = 0x0000001f;
+ hdr->pixelfmt.amask = 0x00008000;
+ break;
+ case DXGI_FORMAT_R10G10B10A2_TYPELESS:
+ case DXGI_FORMAT_R10G10B10A2_UNORM:
+ case DXGI_FORMAT_R10G10B10A2_UINT:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x000003ff;
+ hdr->pixelfmt.gmask = 0x000ffc00;
+ hdr->pixelfmt.bmask = 0x3ff00000;
+ hdr->pixelfmt.amask = 0xc0000000;
+ break;
+ case DXGI_FORMAT_A8_UNORM:
+ hdr->pixelfmt.bpp = 8;
+ hdr->pixelfmt.flags |= DDPF_ALPHA | DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = hdr->pixelfmt.gmask = hdr->pixelfmt.bmask = 0;
+ hdr->pixelfmt.amask = 0x000000ff;
+ break;
+ case DXGI_FORMAT_R8_TYPELESS:
+ case DXGI_FORMAT_R8_UNORM:
+ case DXGI_FORMAT_R8_UINT:
+ case DXGI_FORMAT_R8_SNORM:
+ case DXGI_FORMAT_R8_SINT:
+ hdr->pixelfmt.bpp = 8;
+ hdr->pixelfmt.rmask = 0x000000ff;
+ hdr->pixelfmt.gmask = hdr->pixelfmt.bmask = hdr->pixelfmt.amask = 0;
+ break;
+ case DXGI_FORMAT_B4G4R4A4_UNORM:
+ hdr->pixelfmt.bpp = 16;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x00000f00;
+ hdr->pixelfmt.gmask = 0x000000f0;
+ hdr->pixelfmt.bmask = 0x0000000f;
+ hdr->pixelfmt.amask = 0x0000f000;
+ break;
+ case DXGI_FORMAT_UNKNOWN:
+ g_message ("Unknown DXGI format. Expect problems...");
+ break;
+ default: /* unsupported DXGI format */
+ g_message ("Unsupported DXGI format (%d)", dx10hdr->dxgiFormat);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static const Babl*
+premultiplied_variant (const Babl* format)
+{
+ if (format == babl_format ("R'G'B'A u8"))
+ return babl_format ("R'aG'aB'aA u8");
+ else
+ g_printerr ("Add format %s to premultiplied_variant () %s: %d\n", babl_get_name (format), __FILE__, __LINE__);
+ return format;
+}
+
+static int
+load_layer (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ unsigned int level,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf)
+{
+ GeglBuffer *buffer;
+ const Babl *bablfmt = NULL;
+ GimpImageType type = GIMP_RGBA_IMAGE;
+ gchar *layer_name;
+ gint x, y, z, n;
+ gint32 layer;
+ unsigned int width = hdr->width >> level;
+ unsigned int height = hdr->height >> level;
+ unsigned int size = hdr->pitch_or_linsize >> (2 * level);
+ unsigned int layerw;
+ int format = DDS_COMPRESS_NONE;
+ gsize file_size;
+ gsize current_position;
+
+ current_position = ftell (fp);
+ fseek (fp, 0L, SEEK_END);
+ file_size = ftell (fp);
+ fseek (fp, 0, SEEK_SET);
+ fseek (fp, current_position, SEEK_SET);
+
+ if (width < 1) width = 1;
+ if (height < 1) height = 1;
+
+ switch (d->bpp)
+ {
+ case 1:
+ if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ type = GIMP_INDEXED_IMAGE;
+ }
+ else if (hdr->pixelfmt.rmask == 0xe0)
+ {
+ type = GIMP_RGB_IMAGE;
+ bablfmt = babl_format ("R'G'B' u8");
+ }
+ else if (hdr->pixelfmt.flags & DDPF_ALPHA)
+ {
+ type = GIMP_GRAYA_IMAGE;
+ bablfmt = babl_format ("Y'A u8");
+ }
+ else
+ {
+ type = GIMP_GRAY_IMAGE;
+ bablfmt = babl_format ("Y' u8");
+ }
+ break;
+ case 2:
+ if ((hdr->pixelfmt.flags & (DDPF_PALETTEINDEXED8 + DDPF_ALPHA)) ==
+ DDPF_PALETTEINDEXED8 + DDPF_ALPHA)
+ {
+ type = GIMP_INDEXEDA_IMAGE;
+ }
+ else if (hdr->pixelfmt.amask == 0xf000) /* RGBA4 */
+ {
+ type = GIMP_RGBA_IMAGE;
+ bablfmt = babl_format ("R'G'B'A u8");
+ }
+ else if (hdr->pixelfmt.amask == 0xff00) /* L8A8 */
+ {
+ type = GIMP_GRAYA_IMAGE;
+ bablfmt = babl_format ("Y'A u8");
+ }
+ else if (hdr->pixelfmt.bmask == 0x1f) /* R5G6B5 or RGB5A1 */
+ {
+ type = (hdr->pixelfmt.amask == 0x8000) ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+ bablfmt = (hdr->pixelfmt.amask == 0x8000) ? babl_format ("R'G'B'A u8") : babl_format ("R'G'B' u8");
+ }
+ else if (hdr->pixelfmt.rmask == 0xffff) /* L16 */
+ {
+ type = GIMP_GRAY_IMAGE;
+ bablfmt = babl_format ("Y' u16");
+ }
+ break;
+ case 3: type = GIMP_RGB_IMAGE; bablfmt = babl_format ("R'G'B' u8"); break;
+ case 4: type = GIMP_RGBA_IMAGE; bablfmt = babl_format ("R'G'B'A u8"); break;
+ }
+
+ layer_name = (level) ? g_strdup_printf ("mipmap %d %s", level, prefix) :
+ g_strdup_printf ("main surface %s", prefix);
+
+ layer = gimp_layer_new (image, layer_name, width, height, type, 100,
+ GIMP_LAYER_MODE_NORMAL);
+ g_free (layer_name);
+
+ gimp_image_insert_layer (image, layer, 0, *l);
+
+ if (type == GIMP_INDEXED_IMAGE || type == GIMP_INDEXEDA_IMAGE)
+ bablfmt = gimp_drawable_get_format (layer);
+
+ if ((*l)++) gimp_item_set_visible (layer, FALSE);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ layerw = gegl_buffer_get_width (buffer);
+
+ if (hdr->pixelfmt.flags & DDPF_FOURCC)
+ {
+ unsigned int w = (width + 3) >> 2;
+ unsigned int h = (height + 3) >> 2;
+
+ switch (GETL32(hdr->pixelfmt.fourcc))
+ {
+ case FOURCC ('D','X','T','1'): format = DDS_COMPRESS_BC1; break;
+ case FOURCC ('D','X','T','2'): bablfmt = premultiplied_variant (bablfmt);
+ case FOURCC ('D','X','T','3'): format = DDS_COMPRESS_BC2; break;
+ case FOURCC ('D','X','T','4'): bablfmt = premultiplied_variant (bablfmt);
+ case FOURCC ('D','X','T','5'): format = DDS_COMPRESS_BC3; break;
+ case FOURCC ('R','X','G','B'): format = DDS_COMPRESS_BC3; break;
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'): format = DDS_COMPRESS_BC4; break;
+ case FOURCC ('A','T','I','2'):
+ case FOURCC ('B','C','5','U'):
+ case FOURCC ('B','C','5','S'): format = DDS_COMPRESS_BC5; break;
+ }
+
+ size = w * h;
+ if ((format == DDS_COMPRESS_BC1) || (format == DDS_COMPRESS_BC4))
+ size *= 8;
+ else
+ size *= 16;
+ }
+
+ if (size > (file_size - current_position) ||
+ size > hdr->pitch_or_linsize)
+ {
+ g_message ("Requested data exceeds size of file.\n");
+ return 0;
+ }
+
+ if ((hdr->flags & DDSD_LINEARSIZE) &&
+ !fread (buf, size, 1, fp))
+ {
+ g_message ("Unexpected EOF.\n");
+ return 0;
+ }
+
+ if ((hdr->pixelfmt.flags & DDPF_RGB) ||
+ (hdr->pixelfmt.flags & DDPF_ALPHA))
+ {
+ guint ired = 0;
+ guint iblue = 2;
+
+ if (hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') &&
+ hdr->reserved.gimp_dds_special.version <= 199003 &&
+ hdr->reserved.gimp_dds_special.version > 0 &&
+ d->bpp >= 3 && hdr->pixelfmt.amask == 0xc0000000)
+ {
+ /* GIMP dds plug-in versions before or equal to 199003 (3.9.91) wrote
+ * the red and green channels reversed for RGB10A2. We will fix that here.
+ */
+ g_printerr ("Switching incorrect red and green channels in RGB10A2 dds "
+ "written by an older version of GIMP's dds plug-in.\n");
+ ired = 2;
+ iblue = 0;
+ }
+
+ z = 0;
+ for (y = 0, n = 0; y < height; ++y, ++n)
+ {
+ if (n >= d->tile_height)
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+ n = 0;
+ gimp_progress_update ((double)y / (double)hdr->height);
+ }
+
+ current_position = ftell (fp);
+ if ((hdr->flags & DDSD_PITCH) &&
+ ((width * d->bpp) > (file_size - current_position) ||
+ (width * d->bpp) > hdr->pitch_or_linsize))
+ {
+ g_message ("Requested data exceeds size of file.\n");
+ return 0;
+ }
+
+ if ((hdr->flags & DDSD_PITCH) &&
+ !fread (buf, width * d->bpp, 1, fp))
+ {
+ g_message ("Unexpected EOF.\n");
+ return 0;
+ }
+
+ if (!(hdr->flags & DDSD_LINEARSIZE)) z = 0;
+
+ for (x = 0; x < layerw; ++x)
+ {
+ unsigned int pixel = buf[z];
+ unsigned int pos = (n * layerw + x) * d->gimp_bpp;
+
+ if (d->bpp > 1) pixel += ((unsigned int)buf[z + 1] << 8);
+ if (d->bpp > 2) pixel += ((unsigned int)buf[z + 2] << 16);
+ if (d->bpp > 3) pixel += ((unsigned int)buf[z + 3] << 24);
+
+ if (d->bpp >= 3)
+ {
+ if (hdr->pixelfmt.amask == 0xc0000000) // handle RGB10A2
+ {
+ pixels[pos + ired] = (pixel >> d->rshift) >> 2;
+ pixels[pos + 1] = (pixel >> d->gshift) >> 2;
+ pixels[pos + iblue] = (pixel >> d->bshift) >> 2;
+ if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
+ pixels[pos + 3] = (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ else
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
+ {
+ pixels[pos + 3] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ }
+ }
+ else if (d->bpp == 2)
+ {
+ if (hdr->pixelfmt.amask == 0xf000) //RGBA4
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ pixels[pos + 3] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ else if (hdr->pixelfmt.amask == 0xff00) //L8A8
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ else if (hdr->pixelfmt.bmask == 0x1f) //R5G6B5 or RGB5A1
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ if (hdr->pixelfmt.amask == 0x8000)
+ {
+ pixels[pos + 3] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ }
+ else if (hdr->pixelfmt.rmask == 0xffff) /* L16 */
+ {
+ guint16 *pixels16 = (guint16 *) &pixels[pos];
+
+ *pixels16 = (guint16) (pixel & 0xffff);
+ }
+ }
+ else
+ {
+ if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ pixels[pos] = pixel & 0xff;
+ }
+ else if (hdr->pixelfmt.rmask == 0xe0) // R3G3B2
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ }
+ else if (hdr->pixelfmt.flags & DDPF_ALPHA)
+ {
+ pixels[pos + 0] = 255;
+ pixels[pos + 1] = pixel & 0xff;
+ }
+ else // LUMINANCE
+ {
+ pixels[pos] = pixel & 0xff;
+ }
+ }
+
+ z += d->bpp;
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+ }
+ else if (hdr->pixelfmt.flags & DDPF_FOURCC)
+ {
+ unsigned char *dst;
+
+ dst = g_malloc (width * height * d->gimp_bpp);
+ memset (dst, 0, width * height * d->gimp_bpp);
+
+ if (d->gimp_bpp == 4)
+ {
+ for (y = 0; y < height; ++y)
+ for (x = 0; x < width; ++x)
+ dst[y * (width * 4) + (x * 4) + 3] = 255;
+ }
+
+ dxt_decompress (dst, buf, format, size, width, height, d->gimp_bpp,
+ hdr->pixelfmt.flags & DDPF_NORMAL);
+
+ if (format == DDS_COMPRESS_BC5 &&
+ hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') &&
+ hdr->reserved.gimp_dds_special.version > 0 &&
+ hdr->reserved.gimp_dds_special.version <= 199002)
+ {
+ /* GIMP dds plug-in versions before 199002 == 3.9.90 wrote
+ * the red and green channels reversed. We will fix that here.
+ */
+ g_printerr ("Switching incorrect red and green channels in BC5 dds "
+ "written by an older version of GIMP's dds plug-in.\n");
+
+ for (y = 0; y < height; ++y)
+ for (x = 0; x < width; ++x)
+ {
+ guchar tmpG;
+ guint pix_width = width * d->gimp_bpp;
+ guint x_width = x * d->gimp_bpp;
+
+ tmpG = dst[y * pix_width + x_width];
+ dst[y * pix_width + x_width] = dst[y * pix_width + x_width + 1];
+ dst[y * pix_width + x_width + 1] = tmpG;
+ }
+ }
+
+ z = 0;
+ for (y = 0, n = 0; y < height; ++y, ++n)
+ {
+ if (n >= d->tile_height)
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+ n = 0;
+ gimp_progress_update ((double)y / (double)hdr->height);
+ }
+
+ memcpy (pixels + n * layerw * d->gimp_bpp,
+ dst + y * layerw * d->gimp_bpp,
+ width * d->gimp_bpp);
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (dst);
+ }
+
+ gegl_buffer_flush (buffer);
+
+ g_object_unref (buffer);
+
+ /* gimp dds specific. decode encoded images */
+ if (dds_read_vals.decode_images &&
+ hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') &&
+ hdr->reserved.gimp_dds_special.magic2 == FOURCC ('-','D','D','S'))
+ {
+ switch (hdr->reserved.gimp_dds_special.extra_fourcc)
+ {
+ case FOURCC ('A','E','X','P'):
+ decode_alpha_exp_image (layer, FALSE);
+ break;
+ case FOURCC ('Y','C','G','1'):
+ decode_ycocg_image (layer, FALSE);
+ break;
+ case FOURCC ('Y','C','G','2'):
+ decode_ycocg_scaled_image (layer, FALSE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int
+load_mipmaps (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf)
+{
+ unsigned int level;
+
+ if ((hdr->flags & DDSD_MIPMAPCOUNT) &&
+ (hdr->caps.caps1 & DDSCAPS_MIPMAP) &&
+ (dds_read_vals.mipmaps != 0))
+ {
+ for (level = 1; level < hdr->num_mipmaps; ++level)
+ {
+ if (!load_layer (fp, hdr, d, image, level, prefix, l, pixels, buf))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+load_face (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf)
+{
+ if (!load_layer (fp, hdr, d, image, 0, prefix, l, pixels, buf))
+ return 0;
+
+ return load_mipmaps (fp, hdr, d, image, prefix, l, pixels, buf);
+}
+
+static unsigned char
+color_bits (unsigned int mask)
+{
+ unsigned char i = 0;
+
+ while (mask)
+ {
+ if (mask & 1) ++i;
+ mask >>= 1;
+ }
+
+ return i;
+}
+
+static unsigned char
+color_shift (unsigned int mask)
+{
+ guchar i = 0;
+
+ if (! mask)
+ return 0;
+
+ while (!((mask >> i) & 1))
+ ++i;
+
+ return i;
+}
+
+static void
+load_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
+toggle_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ int *flag = (int*) data;
+ (*flag) = !(*flag);
+}
+
+static int
+load_dialog (void)
+{
+ GtkWidget *dlg;
+ GtkWidget *vbox;
+ GtkWidget *check;
+
+ dlg = gimp_dialog_new (_("Load DDS"), "dds", NULL, GTK_WIN_POS_MOUSE,
+ gimp_standard_help_func, LOAD_PROC,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+ NULL);
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (load_dialog_response),
+ 0);
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ vbox, 1, 1, 0);
+ gtk_widget_show (vbox);
+
+ check = gtk_check_button_new_with_mnemonic (_("_Load mipmaps"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), dds_read_vals.mipmaps);
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked), &dds_read_vals.mipmaps);
+ gtk_box_pack_start (GTK_BOX (vbox), check, 1, 1, 0);
+ gtk_widget_show (check);
+
+ check = gtk_check_button_new_with_mnemonic (_("_Automatically decode YCoCg/AExp images when detected"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), dds_read_vals.decode_images);
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked), &dds_read_vals.decode_images);
+ gtk_box_pack_start (GTK_BOX (vbox), check, 1, 1, 0);
+ gtk_widget_show (check);
+
+ gtk_widget_show (dlg);
+
+ runme = FALSE;
+
+ gtk_main ();
+
+ return runme;
+}