1 /*
2  * DDS GIMP plugin
3  *
4  * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
5  * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; see the file COPYING.  If not, write to
19  * the Free Software Foundation, 51 Franklin Street, Fifth Floor
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "config.h"
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29 
30 #include <gtk/gtk.h>
31 #include <glib/gstdio.h>
32 
33 #include <libgimp/gimp.h>
34 #include <libgimp/gimpui.h>
35 
36 #include <libgimp/stdplugins-intl.h>
37 
38 #include "ddsplugin.h"
39 #include "dds.h"
40 #include "dxt.h"
41 #include "mipmap.h"
42 #include "endian_rw.h"
43 #include "imath.h"
44 #include "color.h"
45 
46 
47 enum
48 {
49   COMBO_VALUE,
50   COMBO_STRING,
51   COMBO_SENSITIVE
52 };
53 
54 
55 static gint       save_dialog          (gint32     image_id,
56                                         gint32     drawable);
57 static void       save_dialog_response (GtkWidget *widget,
58                                         gint       response_id,
59                                         gpointer   data);
60 static gboolean   write_image          (FILE      *fp,
61                                         gint32     image_id,
62                                         gint32     drawable_id);
63 
64 
65 static gboolean runme = FALSE;
66 
67 static const char *cubemap_face_names[4][6] =
68 {
69   {
70     "positive x", "negative x",
71     "positive y", "negative y",
72     "positive z", "negative z"
73   },
74   {
75     "pos x", "neg x",
76     "pos y", "neg y",
77     "pos z", "neg z",
78   },
79   {
80     "+x", "-x",
81     "+y", "-y",
82     "+z", "-z"
83   },
84   {
85     "right", "left",
86     "top", "bottom",
87     "back", "front"
88   }
89 };
90 
91 static gint     cubemap_faces[6];
92 static gboolean is_cubemap            = FALSE;
93 static gboolean is_volume             = FALSE;
94 static gboolean is_array              = FALSE;
95 static gboolean is_mipmap_chain_valid = FALSE;
96 
97 static GtkWidget *compress_opt;
98 static GtkWidget *format_opt;
99 static GtkWidget *mipmap_opt;
100 static GtkWidget *mipmap_filter_opt;
101 static GtkWidget *mipmap_wrap_opt;
102 static GtkWidget *srgb_chk;
103 static GtkWidget *gamma_chk;
104 static GtkWidget *gamma_spin;
105 static GtkWidget *pm_chk;
106 static GtkWidget *alpha_coverage_chk;
107 static GtkWidget *alpha_test_threshold_spin;
108 
109 typedef struct string_value_s
110 {
111   gint   value;
112   gchar *string;
113 } string_value_t;
114 
115 static string_value_t compression_strings[] =
116 {
117   { DDS_COMPRESS_NONE,   "None" },
118   { DDS_COMPRESS_BC1,    "BC1 / DXT1" },
119   { DDS_COMPRESS_BC2,    "BC2 / DXT3" },
120   { DDS_COMPRESS_BC3,    "BC3 / DXT5" },
121   { DDS_COMPRESS_BC3N,   "BC3nm / DXT5nm" },
122   { DDS_COMPRESS_BC4,    "BC4 / ATI1 (3Dc+)" },
123   { DDS_COMPRESS_BC5,    "BC5 / ATI2 (3Dc)" },
124   { DDS_COMPRESS_RXGB,   "RXGB (DXT5)" },
125   { DDS_COMPRESS_AEXP,   "Alpha Exponent (DXT5)" },
126   { DDS_COMPRESS_YCOCG,  "YCoCg (DXT5)" },
127   { DDS_COMPRESS_YCOCGS, "YCoCg scaled (DXT5)" },
128   { -1, 0}
129 };
130 
131 static string_value_t format_strings[] =
132 {
133   { DDS_FORMAT_DEFAULT, "Default" },
134   { DDS_FORMAT_RGB8,    "RGB8" },
135   { DDS_FORMAT_RGBA8,   "RGBA8" },
136   { DDS_FORMAT_BGR8,    "BGR8" },
137   { DDS_FORMAT_ABGR8,   "ABGR8" },
138   { DDS_FORMAT_R5G6B5,  "R5G6B5" },
139   { DDS_FORMAT_RGBA4,   "RGBA4" },
140   { DDS_FORMAT_RGB5A1,  "RGB5A1" },
141   { DDS_FORMAT_RGB10A2, "RGB10A2" },
142   { DDS_FORMAT_R3G3B2,  "R3G3B2" },
143   { DDS_FORMAT_A8,      "A8" },
144   { DDS_FORMAT_L8,      "L8" },
145   { DDS_FORMAT_L8A8,    "L8A8" },
146   { DDS_FORMAT_AEXP,    "AExp" },
147   { DDS_FORMAT_YCOCG,   "YCoCg" },
148   { -1, 0}
149 };
150 
151 static string_value_t mipmap_strings[] =
152 {
153   { DDS_MIPMAP_NONE,     "No mipmaps" },
154   { DDS_MIPMAP_GENERATE, "Generate mipmaps" },
155   { DDS_MIPMAP_EXISTING, "Use existing mipmaps" },
156   { -1, 0}
157 };
158 
159 static string_value_t mipmap_filter_strings[] =
160 {
161   { DDS_MIPMAP_FILTER_DEFAULT,   "Default" },
162   { DDS_MIPMAP_FILTER_NEAREST,   "Nearest" },
163   { DDS_MIPMAP_FILTER_BOX,       "Box" },
164   { DDS_MIPMAP_FILTER_TRIANGLE,  "Triangle" },
165   { DDS_MIPMAP_FILTER_QUADRATIC, "Quadratic" },
166   { DDS_MIPMAP_FILTER_BSPLINE,   "B-Spline" },
167   { DDS_MIPMAP_FILTER_MITCHELL,  "Mitchell" },
168   { DDS_MIPMAP_FILTER_LANCZOS,   "Lanczos" },
169   { DDS_MIPMAP_FILTER_KAISER,    "Kaiser" },
170   { -1, 0}
171 };
172 
173 static string_value_t mipmap_wrap_strings[] =
174 {
175   { DDS_MIPMAP_WRAP_DEFAULT, "Default" },
176   { DDS_MIPMAP_WRAP_MIRROR,  "Mirror" },
177   { DDS_MIPMAP_WRAP_REPEAT,  "Repeat" },
178   { DDS_MIPMAP_WRAP_CLAMP,   "Clamp" },
179   { -1, 0}
180 };
181 
182 static string_value_t save_type_strings[] =
183 {
184   { DDS_SAVE_SELECTED_LAYER, "Image / Selected layer" },
185   { DDS_SAVE_CUBEMAP,        "As cube map" },
186   { DDS_SAVE_VOLUMEMAP,      "As volume map" },
187   { DDS_SAVE_ARRAY,          "As texture array" },
188   { -1, 0}
189 };
190 
191 static struct
192 {
193   int          format;
194   DXGI_FORMAT  dxgi_format;
195   int          bpp;
196   int          alpha;
197   unsigned int rmask;
198   unsigned int gmask;
199   unsigned int bmask;
200   unsigned int amask;
201 } format_info[] =
202 {
203   { DDS_FORMAT_RGB8,    DXGI_FORMAT_UNKNOWN,           3, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
204   { DDS_FORMAT_RGBA8,   DXGI_FORMAT_B8G8R8A8_UNORM,    4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
205   { DDS_FORMAT_BGR8,    DXGI_FORMAT_UNKNOWN,           3, 0, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000},
206   { DDS_FORMAT_ABGR8,   DXGI_FORMAT_R8G8B8A8_UNORM,    4, 1, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},
207   { DDS_FORMAT_R5G6B5,  DXGI_FORMAT_B5G6R5_UNORM,      2, 0, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000},
208   { DDS_FORMAT_RGBA4,   DXGI_FORMAT_B4G4R4A4_UNORM,    2, 1, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000},
209   { DDS_FORMAT_RGB5A1,  DXGI_FORMAT_B5G5R5A1_UNORM,    2, 1, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000},
210   { DDS_FORMAT_RGB10A2, DXGI_FORMAT_R10G10B10A2_UNORM, 4, 1, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000},
211   { DDS_FORMAT_R3G3B2,  DXGI_FORMAT_UNKNOWN,           1, 0, 0x000000e0, 0x0000001c, 0x00000003, 0x00000000},
212   { DDS_FORMAT_A8,      DXGI_FORMAT_A8_UNORM,          1, 0, 0x00000000, 0x00000000, 0x00000000, 0x000000ff},
213   { DDS_FORMAT_L8,      DXGI_FORMAT_R8_UNORM,          1, 0, 0x000000ff, 0x000000ff, 0x000000ff, 0x00000000},
214   { DDS_FORMAT_L8A8,    DXGI_FORMAT_UNKNOWN,           2, 1, 0x000000ff, 0x000000ff, 0x000000ff, 0x0000ff00},
215   { DDS_FORMAT_AEXP,    DXGI_FORMAT_B8G8R8A8_UNORM,    4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
216   { DDS_FORMAT_YCOCG,   DXGI_FORMAT_B8G8R8A8_UNORM,    4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}
217 };
218 
219 
220 static gboolean
check_mipmaps(gint32 image_id,gint savetype)221 check_mipmaps (gint32 image_id,
222                gint   savetype)
223 {
224   gint *layers;
225   gint  num_layers;
226   gint  i, j, w, h, mipw, miph;
227   gint  num_mipmaps;
228   gint  num_surfaces = 0;
229   gint  min_surfaces = 1;
230   gint  max_surfaces = 1;
231   gboolean valid = TRUE;
232   GimpImageType type;
233 
234   /* not handling volume maps for the moment... */
235   if (savetype == DDS_SAVE_VOLUMEMAP)
236     return 0;
237 
238   if (savetype == DDS_SAVE_CUBEMAP)
239     {
240       min_surfaces = 6;
241       max_surfaces = 6;
242     }
243   else if (savetype == DDS_SAVE_ARRAY)
244     {
245       min_surfaces = 2;
246       max_surfaces = INT_MAX;
247     }
248 
249   layers = gimp_image_get_layers (image_id, &num_layers);
250 
251   w = gimp_image_width (image_id);
252   h = gimp_image_height (image_id);
253 
254   num_mipmaps = get_num_mipmaps (w, h);
255 
256   type = gimp_drawable_type (layers[0]);
257 
258   for (i = 0; i < num_layers; ++i)
259     {
260       if (type != gimp_drawable_type (layers[i]))
261         return 0;
262 
263       if ((gimp_drawable_width (layers[i])  == w) &&
264           (gimp_drawable_height (layers[i]) == h))
265         ++num_surfaces;
266     }
267 
268   if ((num_surfaces < min_surfaces) ||
269       (num_surfaces > max_surfaces) ||
270       (num_layers != (num_surfaces * num_mipmaps)))
271     return 0;
272 
273   for (i = 0; valid && i < num_layers; i += num_mipmaps)
274     {
275       if ((gimp_drawable_width (layers[i])  != w) ||
276           (gimp_drawable_height (layers[i]) != h))
277         {
278           valid = FALSE;
279           break;
280         }
281 
282       for (j = 1; j < num_mipmaps; ++j)
283         {
284           mipw = w >> j;
285           miph = h >> j;
286           if (mipw < 1) mipw = 1;
287           if (miph < 1) miph = 1;
288           if ((gimp_drawable_width (layers[i + j])  != mipw) ||
289               (gimp_drawable_height (layers[i + j]) != miph))
290             {
291               valid = FALSE;
292               break;
293             }
294         }
295     }
296 
297   return valid;
298 }
299 
300 static gboolean
check_cubemap(gint32 image_id)301 check_cubemap (gint32 image_id)
302 {
303   gint *layers;
304   gint  num_layers;
305   gboolean cubemap = TRUE;
306   gint i, j, k, w, h;
307   gchar *layer_name;
308   GimpImageType type;
309 
310   layers = gimp_image_get_layers (image_id, &num_layers);
311 
312   if (num_layers < 6)
313     return FALSE;
314 
315   /* check for a valid cubemap with mipmap layers */
316   if (num_layers > 6)
317     {
318       /* check that mipmap layers are in order for a cubemap */
319       if (! check_mipmaps (image_id, DDS_SAVE_CUBEMAP))
320         return FALSE;
321 
322       /* invalidate cubemap faces */
323       for (i = 0; i < 6; ++i)
324         cubemap_faces[i] = -1;
325 
326       /* find the mipmap level 0 layers */
327       w = gimp_image_width (image_id);
328       h = gimp_image_height (image_id);
329 
330       for (i = 0; i < num_layers; ++i)
331         {
332           if ((gimp_drawable_width (layers[i])  != w) ||
333               (gimp_drawable_height (layers[i]) != h))
334             continue;
335 
336           layer_name = (char*)gimp_item_get_name (layers[i]);
337           for (j = 0; j < 6; ++j)
338             {
339               for (k = 0; k < 4; ++k)
340                 {
341                   if (strstr (layer_name, cubemap_face_names[k][j]))
342                     {
343                       if (cubemap_faces[j] == -1)
344                         {
345                           cubemap_faces[j] = layers[i];
346                           break;
347                         }
348                     }
349                 }
350             }
351         }
352 
353       /* check for 6 valid faces */
354       for (i = 0; i < 6; ++i)
355         {
356           if (cubemap_faces[i] == -1)
357             {
358               cubemap = FALSE;
359               break;
360             }
361         }
362 
363       /* make sure they are all the same type */
364       if (cubemap)
365         {
366           type = gimp_drawable_type (cubemap_faces[0]);
367           for (i = 1; i < 6 && cubemap; ++i)
368             {
369               if (gimp_drawable_type (cubemap_faces[i]) != type)
370                 cubemap = FALSE;
371             }
372         }
373     }
374 
375   if (num_layers == 6)
376     {
377       /* invalidate cubemap faces */
378       for (i = 0; i < 6; ++i)
379         cubemap_faces[i] = -1;
380 
381       for (i = 0; i < 6; ++i)
382         {
383           layer_name = (char*)gimp_item_get_name (layers[i]);
384           for (j = 0; j < 6; ++j)
385             {
386               for (k = 0; k < 4; ++k)
387                 {
388                   if (strstr (layer_name, cubemap_face_names[k][j]))
389                     {
390                       if (cubemap_faces[j] == -1)
391                         {
392                           cubemap_faces[j] = layers[i];
393                           break;
394                         }
395                     }
396                 }
397             }
398         }
399 
400       /* check for 6 valid faces */
401       for (i = 0; i < 6; ++i)
402         {
403           if (cubemap_faces[i] == -1)
404             {
405               cubemap = FALSE;
406               break;
407             }
408         }
409 
410       /* make sure they are all the same size */
411       if (cubemap)
412         {
413           w = gimp_drawable_width (cubemap_faces[0]);
414           h = gimp_drawable_height (cubemap_faces[0]);
415 
416           for (i = 1; i < 6 && cubemap; ++i)
417             {
418               if ((gimp_drawable_width (cubemap_faces[i])  != w) ||
419                   (gimp_drawable_height (cubemap_faces[i]) != h))
420                 cubemap = FALSE;
421             }
422         }
423 
424       /* make sure they are all the same type */
425       if (cubemap)
426         {
427           type = gimp_drawable_type (cubemap_faces[0]);
428           for (i = 1; i < 6 && cubemap; ++i)
429             {
430               if (gimp_drawable_type (cubemap_faces[i]) != type)
431                 cubemap = FALSE;
432             }
433         }
434     }
435 
436   return cubemap;
437 }
438 
439 static gboolean
check_volume(gint32 image_id)440 check_volume (gint32 image_id)
441 {
442   gint *layers;
443   gint  num_layers;
444   gboolean volume = FALSE;
445   gint i, w, h;
446   GimpImageType type;
447 
448   layers = gimp_image_get_layers (image_id, &num_layers);
449 
450   if (num_layers > 1)
451     {
452       volume = TRUE;
453 
454       /* make sure all layers are the same size */
455       w = gimp_drawable_width (layers[0]);
456       h = gimp_drawable_height (layers[0]);
457 
458       for (i = 1; i < num_layers && volume; ++i)
459         {
460           if ((gimp_drawable_width (layers[i])  != w) ||
461               (gimp_drawable_height (layers[i]) != h))
462             volume = FALSE;
463         }
464 
465       if (volume)
466         {
467           /* make sure all layers are the same type */
468           type = gimp_drawable_type (layers[0]);
469           for (i = 1; i < num_layers && volume; ++i)
470             {
471               if (gimp_drawable_type (layers[i]) != type)
472                 volume = FALSE;
473             }
474         }
475     }
476 
477   return volume;
478 }
479 
480 static gboolean
check_array(gint32 image_id)481 check_array (gint32 image_id)
482 {
483   gint *layers;
484   gint  num_layers;
485   gboolean array = FALSE;
486   gint i, w, h;
487   GimpImageType type;
488 
489   if (check_mipmaps (image_id, DDS_SAVE_ARRAY))
490     return 1;
491 
492   layers = gimp_image_get_layers (image_id, &num_layers);
493 
494   if (num_layers > 1)
495     {
496       array = TRUE;
497 
498       /* make sure all layers are the same size */
499       w = gimp_drawable_width (layers[0]);
500       h = gimp_drawable_height (layers[0]);
501 
502       for (i = 1; i < num_layers && array; ++i)
503         {
504           if ((gimp_drawable_width (layers[i])  != w) ||
505               (gimp_drawable_height (layers[i]) != h))
506             array = FALSE;
507         }
508 
509       if (array)
510         {
511           /* make sure all layers are the same type */
512           type = gimp_drawable_type (layers[0]);
513           for (i = 1; i < num_layers; ++i)
514             {
515               if (gimp_drawable_type (layers[i]) != type)
516                 {
517                   array = FALSE;
518                   break;
519                 }
520             }
521         }
522     }
523 
524   return array;
525 }
526 
527 static int
get_array_size(gint32 image_id)528 get_array_size (gint32 image_id)
529 {
530   gint *layers;
531   gint  num_layers;
532   gint i, w, h;
533   gint elements = 0;
534 
535   layers = gimp_image_get_layers (image_id, &num_layers);
536 
537   w = gimp_image_width (image_id);
538   h = gimp_image_height (image_id);
539 
540   for (i = 0; i < num_layers; ++i)
541     {
542       if ((gimp_drawable_width  (layers[i]) == w) &&
543           (gimp_drawable_height (layers[i]) == h))
544         {
545           elements++;
546         }
547     }
548 
549   return elements;
550 }
551 
552 GimpPDBStatusType
write_dds(gchar * filename,gint32 image_id,gint32 drawable_id,gboolean interactive_dds)553 write_dds (gchar    *filename,
554            gint32    image_id,
555            gint32    drawable_id,
556            gboolean  interactive_dds)
557 {
558   FILE  *fp;
559   gchar *tmp;
560   int    rc = 0;
561 
562   is_mipmap_chain_valid = check_mipmaps (image_id, dds_write_vals.savetype);
563 
564   is_cubemap = check_cubemap (image_id);
565   is_volume  = check_volume (image_id);
566   is_array   = check_array (image_id);
567 
568   if (interactive_dds)
569     {
570       if (! is_mipmap_chain_valid &&
571           dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING)
572         dds_write_vals.mipmaps = DDS_MIPMAP_NONE;
573 
574       if (! save_dialog (image_id, drawable_id))
575         return GIMP_PDB_CANCEL;
576     }
577   else
578     {
579       if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP && ! is_cubemap)
580         {
581           g_message ("DDS: Cannot save image as cube map");
582           return GIMP_PDB_EXECUTION_ERROR;
583         }
584 
585       if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP && ! is_volume)
586         {
587           g_message ("DDS: Cannot save image as volume map");
588           return GIMP_PDB_EXECUTION_ERROR;
589         }
590 
591       if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP &&
592           dds_write_vals.compression != DDS_COMPRESS_NONE)
593         {
594           g_message ("DDS: Cannot save volume map with compression");
595           return GIMP_PDB_EXECUTION_ERROR;
596         }
597 
598       if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING &&
599           ! is_mipmap_chain_valid)
600         {
601           g_message ("DDS: Cannot save with existing mipmaps as the mipmap chain is incomplete");
602           return GIMP_PDB_EXECUTION_ERROR;
603         }
604     }
605 
606   fp = g_fopen (filename, "wb");
607   if (fp == 0)
608     {
609       g_message ("Error opening %s", filename);
610       return GIMP_PDB_EXECUTION_ERROR;
611     }
612 
613   if (strrchr (filename, '/'))
614     tmp = g_strdup_printf ("Saving %s:", strrchr (filename, '/') + 1);
615   else
616     tmp = g_strdup_printf ("Saving %s:", filename);
617   gimp_progress_init (tmp);
618   g_free (tmp);
619 
620   rc = write_image (fp, image_id, drawable_id);
621 
622   fclose (fp);
623 
624   return rc ? GIMP_PDB_SUCCESS : GIMP_PDB_EXECUTION_ERROR;
625 }
626 
627 static void
swap_rb(unsigned char * pixels,unsigned int n,int bpp)628 swap_rb (unsigned char *pixels,
629          unsigned int   n,
630          int            bpp)
631 {
632   unsigned int i;
633   unsigned char t;
634 
635   for (i = 0; i < n; ++i)
636     {
637       t = pixels[bpp * i + 0];
638       pixels[bpp * i + 0] = pixels[bpp * i + 2];
639       pixels[bpp * i + 2] = t;
640     }
641 }
642 
643 static void
alpha_exp(unsigned char * dst,int r,int g,int b,int a)644 alpha_exp (unsigned char *dst,
645            int            r,
646            int            g,
647            int            b,
648            int            a)
649 {
650   float ar, ag, ab, aa;
651 
652   ar = (float)r / 255.0f;
653   ag = (float)g / 255.0f;
654   ab = (float)b / 255.0f;
655 
656   aa = MAX (ar, MAX (ag, ab));
657 
658   if (aa < 1e-04f)
659     {
660       dst[0] = b;
661       dst[1] = g;
662       dst[2] = r;
663       dst[3] = 255;
664       return;
665     }
666 
667   ar /= aa;
668   ag /= aa;
669   ab /= aa;
670 
671   r = (int)floorf (255.0f * ar + 0.5f);
672   g = (int)floorf (255.0f * ag + 0.5f);
673   b = (int)floorf (255.0f * ab + 0.5f);
674   a = (int)floorf (255.0f * aa + 0.5f);
675 
676   dst[0] = MAX (0, MIN (255, b));
677   dst[1] = MAX (0, MIN (255, g));
678   dst[2] = MAX (0, MIN (255, r));
679   dst[3] = MAX (0, MIN (255, a));
680 }
681 
682 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)683 convert_pixels (unsigned char *dst,
684                 unsigned char *src,
685                 int            format,
686                 int            w,
687                 int            h,
688                 int            d,
689                 int            bpp,
690                 unsigned char *palette,
691                 int            mipmaps)
692 {
693   unsigned int i, num_pixels;
694   unsigned char r, g, b, a;
695 
696   if (d > 0)
697     num_pixels = get_volume_mipmapped_size (w, h, d, 1, 0, mipmaps, DDS_COMPRESS_NONE);
698   else
699     num_pixels = get_mipmapped_size (w, h, 1, 0, mipmaps, DDS_COMPRESS_NONE);
700 
701   for (i = 0; i < num_pixels; ++i)
702     {
703       if (bpp == 1)
704         {
705           if (palette)
706             {
707               r = palette[3 * src[i] + 0];
708               g = palette[3 * src[i] + 1];
709               b = palette[3 * src[i] + 2];
710             }
711           else
712             r = g = b = src[i];
713 
714           if (format == DDS_FORMAT_A8)
715             a = src[i];
716           else
717             a = 255;
718         }
719       else if (bpp == 2)
720         {
721           r = g = b = src[2 * i];
722           a = src[2 * i + 1];
723         }
724       else if (bpp == 3)
725         {
726           b = src[3 * i + 0];
727           g = src[3 * i + 1];
728           r = src[3 * i + 2];
729           a = 255;
730         }
731       else
732         {
733           b = src[4 * i + 0];
734           g = src[4 * i + 1];
735           r = src[4 * i + 2];
736           a = src[4 * i + 3];
737         }
738 
739       switch (format)
740         {
741         case DDS_FORMAT_RGB8:
742           dst[3 * i + 0] = b;
743           dst[3 * i + 1] = g;
744           dst[3 * i + 2] = r;
745           break;
746         case DDS_FORMAT_RGBA8:
747           dst[4 * i + 0] = b;
748           dst[4 * i + 1] = g;
749           dst[4 * i + 2] = r;
750           dst[4 * i + 3] = a;
751           break;
752         case DDS_FORMAT_BGR8:
753           dst[3 * i + 0] = r;
754           dst[3 * i + 1] = g;
755           dst[3 * i + 2] = b;
756           break;
757         case DDS_FORMAT_ABGR8:
758           dst[4 * i + 0] = r;
759           dst[4 * i + 1] = g;
760           dst[4 * i + 2] = b;
761           dst[4 * i + 3] = a;
762           break;
763         case DDS_FORMAT_R5G6B5:
764           PUTL16(&dst[2 * i], pack_r5g6b5(r, g, b));
765           break;
766         case DDS_FORMAT_RGBA4:
767           PUTL16(&dst[2 * i], pack_rgba4(r, g, b, a));
768           break;
769         case DDS_FORMAT_RGB5A1:
770           PUTL16(&dst[2 * i], pack_rgb5a1(r, g, b, a));
771           break;
772         case DDS_FORMAT_RGB10A2:
773           PUTL32(&dst[4 * i], pack_rgb10a2(r, g, b, a));
774           break;
775         case DDS_FORMAT_R3G3B2:
776           dst[i] = pack_r3g3b2(r, g, b);
777           break;
778         case DDS_FORMAT_A8:
779           dst[i] = a;
780           break;
781         case DDS_FORMAT_L8:
782           dst[i] = rgb_to_luminance (r, g, b);
783           break;
784         case DDS_FORMAT_L8A8:
785           dst[2 * i + 0] = rgb_to_luminance (r, g, b);
786           dst[2 * i + 1] = a;
787           break;
788         case DDS_FORMAT_YCOCG:
789           dst[4 * i] = a;
790           RGB_to_YCoCg (&dst[4 * i], r, g, b);
791           break;
792         case DDS_FORMAT_AEXP:
793           alpha_exp (&dst[4 * i], r, g, b, a);
794           break;
795         default:
796           break;
797         }
798     }
799 }
800 
801 static void
get_mipmap_chain(unsigned char * dst,int w,int h,int bpp,gint32 image_id,gint drawable_id)802 get_mipmap_chain (unsigned char *dst,
803                   int            w,
804                   int            h,
805                   int            bpp,
806                   gint32         image_id,
807                   gint           drawable_id)
808 {
809   gint *layers, num_layers;
810   GeglBuffer *buffer;
811   const Babl *format = 0;
812   int i, idx = 0, offset, mipw, miph;
813 
814   if (bpp == 1)
815     format = babl_format ("Y' u8");
816   else if (bpp == 2)
817     format = babl_format ("Y'A u8");
818   else if (bpp == 3)
819     format = babl_format ("R'G'B' u8");
820   else
821     format = babl_format ("R'G'B'A u8");
822 
823   layers = gimp_image_get_layers (image_id, &num_layers);
824 
825   for (i = 0; i < num_layers; ++i)
826     {
827       if (layers[i] == drawable_id)
828         {
829           idx = i;
830           break;
831         }
832     }
833 
834   if (i == num_layers) return;
835 
836   offset = 0;
837 
838   while (get_next_mipmap_dimensions (&mipw, &miph, w, h))
839     {
840       buffer = gimp_drawable_get_buffer (layers[++idx]);
841 
842       if ((gegl_buffer_get_width (buffer)  != mipw) ||
843           (gegl_buffer_get_height (buffer) != miph))
844         {
845           g_object_unref (buffer);
846           return;
847         }
848 
849       gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, mipw, miph), 1.0, format,
850                        dst + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
851       g_object_unref (buffer);
852 
853       /* we need BGRX or BGRA */
854       if (bpp >= 3)
855         swap_rb (dst + offset, mipw * miph, bpp);
856 
857       offset += (mipw * miph * bpp);
858       w = mipw;
859       h = miph;
860     }
861 }
862 
863 static void
write_layer(FILE * fp,gint32 image_id,gint32 drawable_id,int w,int h,int bpp,int fmtbpp,int mipmaps)864 write_layer (FILE   *fp,
865              gint32  image_id,
866              gint32  drawable_id,
867              int     w,
868              int     h,
869              int     bpp,
870              int     fmtbpp,
871              int     mipmaps)
872 {
873   GeglBuffer *buffer;
874   const Babl *format = 0;
875   GimpImageBaseType basetype;
876   GimpImageType type;
877   unsigned char *src, *dst, *fmtdst, *tmp;
878   unsigned char *palette = NULL;
879   int i, c, x, y, size, fmtsize, offset, colors;
880   int compression = dds_write_vals.compression;
881   int flags = 0;
882 
883   basetype = gimp_image_base_type (image_id);
884   type = gimp_drawable_type (drawable_id);
885 
886   buffer = gimp_drawable_get_buffer (drawable_id);
887 
888   src = g_malloc (w * h * bpp);
889 
890   if (basetype == GIMP_INDEXED)
891     format = gimp_drawable_get_format (drawable_id);
892   else if (bpp == 1)
893     format = babl_format ("Y' u8");
894   else if (bpp == 2)
895     format = babl_format ("Y'A u8");
896   else if (bpp == 3)
897     format = babl_format ("R'G'B' u8");
898   else
899     format = babl_format ("R'G'B'A u8");
900 
901   gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, src,
902                    GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
903 
904   if (basetype == GIMP_INDEXED)
905     {
906       palette = gimp_image_get_colormap (image_id, &colors);
907 
908       if (type == GIMP_INDEXEDA_IMAGE)
909         {
910           tmp = g_malloc (w * h);
911           for (i = 0; i < w * h; ++i)
912             tmp[i] = src[2 * i];
913           g_free (src);
914           src = tmp;
915           bpp = 1;
916         }
917     }
918 
919   /* we want and assume BGRA ordered pixels for bpp >= 3 from here and
920      onwards */
921   if (bpp >= 3)
922     swap_rb (src, w * h, bpp);
923 
924   if (compression == DDS_COMPRESS_BC3N)
925     {
926       if (bpp != 4)
927         {
928           fmtsize = w * h * 4;
929           fmtdst = g_malloc (fmtsize);
930           convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp,
931                           palette, 1);
932           g_free (src);
933           src = fmtdst;
934           bpp = 4;
935         }
936 
937       for (y = 0; y < h; ++y)
938         {
939           for (x = 0; x < w; ++x)
940             {
941               /* set alpha to red (x) */
942               src[y * (w * 4) + (x * 4) + 3] =
943                 src[y * (w * 4) + (x * 4) + 2];
944               /* set red to 1 */
945               src[y * (w * 4) + (x * 4) + 2] = 255;
946             }
947         }
948     }
949 
950   /* RXGB (Doom3) */
951   if (compression == DDS_COMPRESS_RXGB)
952     {
953       if (bpp != 4)
954         {
955           fmtsize = w * h * 4;
956           fmtdst = g_malloc (fmtsize);
957           convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp,
958                           palette, 1);
959           g_free (src);
960           src = fmtdst;
961           bpp = 4;
962         }
963 
964       for (y = 0; y < h; ++y)
965         {
966           for (x = 0; x < w; ++x)
967             {
968               /* swap red and alpha */
969               c = src[y * (w * 4) + (x * 4) + 3];
970               src[y * (w * 4) + (x * 4) + 3] =
971                 src[y * (w * 4) + (x * 4) + 2];
972               src[y * (w * 4) + (x * 4) + 2] = c;
973             }
974         }
975     }
976 
977   if (compression == DDS_COMPRESS_YCOCG ||
978       compression == DDS_COMPRESS_YCOCGS) /* convert to YCoCG */
979     {
980       fmtsize = w * h * 4;
981       fmtdst = g_malloc (fmtsize);
982       convert_pixels (fmtdst, src, DDS_FORMAT_YCOCG, w, h, 0, bpp,
983                       palette, 1);
984       g_free (src);
985       src = fmtdst;
986       bpp = 4;
987     }
988 
989   if (compression == DDS_COMPRESS_AEXP)
990     {
991       fmtsize = w * h * 4;
992       fmtdst = g_malloc (fmtsize);
993       convert_pixels (fmtdst, src, DDS_FORMAT_AEXP, w, h, 0, bpp,
994                       palette, 1);
995       g_free (src);
996       src = fmtdst;
997       bpp = 4;
998     }
999 
1000   if (compression == DDS_COMPRESS_NONE)
1001     {
1002       if (mipmaps > 1)
1003         {
1004           /* pre-convert indexed images to RGB for better quality mipmaps
1005              if a pixel format conversion is requested */
1006           if (dds_write_vals.format > DDS_FORMAT_DEFAULT && basetype == GIMP_INDEXED)
1007             {
1008               fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps, DDS_COMPRESS_NONE);
1009               fmtdst = g_malloc (fmtsize);
1010               convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp,
1011                               palette, 1);
1012               g_free (src);
1013               src = fmtdst;
1014               bpp = 3;
1015               palette = NULL;
1016             }
1017 
1018           size = get_mipmapped_size (w, h, bpp, 0, mipmaps, DDS_COMPRESS_NONE);
1019           dst = g_malloc (size);
1020           if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE)
1021             {
1022               generate_mipmaps (dst, src, w, h, bpp, palette != NULL,
1023                                 mipmaps,
1024                                 dds_write_vals.mipmap_filter,
1025                                 dds_write_vals.mipmap_wrap,
1026                                 dds_write_vals.gamma_correct + dds_write_vals.srgb,
1027                                 dds_write_vals.gamma,
1028                                 dds_write_vals.preserve_alpha_coverage,
1029                                 dds_write_vals.alpha_test_threshold);
1030             }
1031           else
1032             {
1033               memcpy (dst, src, w * h * bpp);
1034               get_mipmap_chain (dst + (w * h * bpp), w, h, bpp, image_id, drawable_id);
1035             }
1036 
1037           if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
1038             {
1039               fmtsize = get_mipmapped_size (w, h, fmtbpp, 0, mipmaps,
1040                                             DDS_COMPRESS_NONE);
1041               fmtdst = g_malloc (fmtsize);
1042 
1043               convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, 0, bpp,
1044                               palette, mipmaps);
1045 
1046               g_free (dst);
1047               dst = fmtdst;
1048               bpp = fmtbpp;
1049             }
1050 
1051           offset = 0;
1052 
1053           for (i = 0; i < mipmaps; ++i)
1054             {
1055               size = get_mipmapped_size (w, h, bpp, i, 1, DDS_COMPRESS_NONE);
1056               fwrite (dst + offset, 1, size, fp);
1057               offset += size;
1058             }
1059 
1060           g_free (dst);
1061         }
1062       else
1063         {
1064           if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
1065             {
1066               fmtdst = g_malloc (h * w * fmtbpp);
1067               convert_pixels (fmtdst, src, dds_write_vals.format, w, h, 0, bpp,
1068                               palette, 1);
1069               g_free (src);
1070               src = fmtdst;
1071               bpp = fmtbpp;
1072             }
1073 
1074           fwrite (src, 1, h * w * bpp, fp);
1075         }
1076     }
1077   else
1078     {
1079       size = get_mipmapped_size (w, h, bpp, 0, mipmaps, compression);
1080 
1081       dst = g_malloc (size);
1082 
1083       if (basetype == GIMP_INDEXED)
1084         {
1085           fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps,
1086                                         DDS_COMPRESS_NONE);
1087           fmtdst = g_malloc (fmtsize);
1088           convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp,
1089                           palette, mipmaps);
1090           g_free (src);
1091           src = fmtdst;
1092           bpp = 3;
1093         }
1094 
1095       if (mipmaps > 1)
1096         {
1097           fmtsize = get_mipmapped_size (w, h, bpp, 0, mipmaps,
1098                                         DDS_COMPRESS_NONE);
1099           fmtdst = g_malloc (fmtsize);
1100           if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE)
1101             {
1102               generate_mipmaps (fmtdst, src, w, h, bpp, 0, mipmaps,
1103                                 dds_write_vals.mipmap_filter,
1104                                 dds_write_vals.mipmap_wrap,
1105                                 dds_write_vals.gamma_correct + dds_write_vals.srgb,
1106                                 dds_write_vals.gamma,
1107                                 dds_write_vals.preserve_alpha_coverage,
1108                                 dds_write_vals.alpha_test_threshold);
1109             }
1110           else
1111             {
1112               memcpy (fmtdst, src, w * h * bpp);
1113               get_mipmap_chain (fmtdst + (w * h * bpp), w, h, bpp, image_id, drawable_id);
1114             }
1115 
1116           g_free (src);
1117           src = fmtdst;
1118         }
1119 
1120       flags = 0;
1121       if (dds_write_vals.perceptual_metric) flags |= DXT_PERCEPTUAL;
1122 
1123       dxt_compress (dst, src, compression, w, h, bpp, mipmaps, flags);
1124 
1125       fwrite (dst, 1, size, fp);
1126 
1127       g_free (dst);
1128     }
1129 
1130   g_free (src);
1131 
1132   g_object_unref (buffer);
1133 }
1134 
1135 static void
write_volume_mipmaps(FILE * fp,gint32 image_id,gint32 * layers,int w,int h,int d,int bpp,int fmtbpp,int mipmaps)1136 write_volume_mipmaps (FILE   *fp,
1137                       gint32  image_id,
1138                       gint32 *layers,
1139                       int     w,
1140                       int     h,
1141                       int     d,
1142                       int     bpp,
1143                       int     fmtbpp,
1144                       int     mipmaps)
1145 {
1146   int i, size, offset, colors;
1147   unsigned char *src, *dst, *tmp, *fmtdst;
1148   unsigned char *palette = 0;
1149   GeglBuffer *buffer;
1150   const Babl *format;
1151   GimpImageBaseType type;
1152 
1153   type = gimp_image_base_type (image_id);
1154 
1155   if (dds_write_vals.compression != DDS_COMPRESS_NONE) return;
1156 
1157   src = g_malloc (w * h * bpp * d);
1158 
1159   if (bpp == 1)
1160     format = babl_format ("Y' u8");
1161   else if (bpp == 2)
1162     format = babl_format ("Y'A u8");
1163   else if (bpp == 3)
1164     format = babl_format ("R'G'B' u8");
1165   else
1166     format = babl_format ("R'G'B'A u8");
1167 
1168   if (gimp_image_base_type (image_id) == GIMP_INDEXED)
1169     palette = gimp_image_get_colormap (image_id, &colors);
1170 
1171   offset = 0;
1172   for (i = 0; i < d; ++i)
1173     {
1174       buffer = gimp_drawable_get_buffer (layers[i]);
1175       gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format,
1176                        src + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
1177       offset += (w * h * bpp);
1178       g_object_unref (buffer);
1179     }
1180 
1181   if (gimp_drawable_type (layers[0]) == GIMP_INDEXEDA_IMAGE)
1182     {
1183       tmp = g_malloc (w * h * d);
1184       for (i = 0; i < w * h * d; ++i)
1185         tmp[i] = src[2 * i];
1186       g_free (src);
1187       src = tmp;
1188       bpp = 1;
1189     }
1190 
1191   /* we want and assume BGRA ordered pixels for bpp >= 3 from here and
1192      onwards */
1193   if (bpp >= 3)
1194     swap_rb (src, w * h * d, bpp);
1195 
1196   /* pre-convert indexed images to RGB for better mipmaps if a
1197      pixel format conversion is requested */
1198   if (dds_write_vals.format > DDS_FORMAT_DEFAULT && type == GIMP_INDEXED)
1199     {
1200       size = get_volume_mipmapped_size (w, h, d, 3, 0, mipmaps,
1201                                         DDS_COMPRESS_NONE);
1202       dst = g_malloc (size);
1203       convert_pixels (dst, src, DDS_FORMAT_RGB8, w, h, d, bpp, palette, 1);
1204       g_free (src);
1205       src = dst;
1206       bpp = 3;
1207       palette = NULL;
1208     }
1209 
1210   size = get_volume_mipmapped_size (w, h, d, bpp, 0, mipmaps,
1211                                     dds_write_vals.compression);
1212 
1213   dst = g_malloc (size);
1214 
1215   offset = get_volume_mipmapped_size (w, h, d, bpp, 0, 1,
1216                                       dds_write_vals.compression);
1217 
1218   generate_volume_mipmaps (dst, src, w, h, d, bpp,
1219                            palette != NULL, mipmaps,
1220                            dds_write_vals.mipmap_filter,
1221                            dds_write_vals.mipmap_wrap,
1222                            dds_write_vals.gamma_correct + dds_write_vals.srgb,
1223                            dds_write_vals.gamma);
1224 
1225   if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
1226     {
1227       size = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, mipmaps,
1228                                         dds_write_vals.compression);
1229       offset = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, 1,
1230                                           dds_write_vals.compression);
1231       fmtdst = g_malloc (size);
1232 
1233       convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, d, bpp,
1234                       palette, mipmaps);
1235       g_free (dst);
1236       dst = fmtdst;
1237     }
1238 
1239   fwrite (dst + offset, 1, size, fp);
1240 
1241   g_free (src);
1242   g_free (dst);
1243 }
1244 
1245 static gboolean
write_image(FILE * fp,gint32 image_id,gint32 drawable_id)1246 write_image (FILE   *fp,
1247              gint32  image_id,
1248              gint32  drawable_id)
1249 {
1250   GimpImageType drawable_type;
1251   GimpImageBaseType basetype;
1252   gint i, w, h;
1253   gint bpp = 0;
1254   gint fmtbpp = 0;
1255   gint has_alpha = 0;
1256   gint num_mipmaps;
1257   guchar hdr[DDS_HEADERSIZE];
1258   guchar hdr10[DDS_HEADERSIZE_DX10];
1259   guint flags = 0, pflags = 0, caps = 0, caps2 = 0, size = 0;
1260   guint rmask = 0, gmask = 0, bmask = 0, amask = 0;
1261   guint fourcc = 0;
1262   DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
1263   gint32  num_layers;
1264   gint32 *layers;
1265   guchar *cmap;
1266   gint    colors;
1267   guchar zero[4] = {0, 0, 0, 0};
1268   gint is_dx10 = 0;
1269   gint array_size = 1;
1270 
1271   layers = gimp_image_get_layers (image_id, &num_layers);
1272 
1273   if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING)
1274     drawable_id = layers[0];
1275 
1276   if (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER)
1277     {
1278       w = gimp_drawable_width (drawable_id);
1279       h = gimp_drawable_height (drawable_id);
1280     }
1281   else
1282     {
1283       w = gimp_image_width (image_id);
1284       h = gimp_image_height (image_id);
1285     }
1286 
1287   basetype = gimp_image_base_type (image_id);
1288   drawable_type = gimp_drawable_type (drawable_id);
1289 
1290   switch (drawable_type)
1291     {
1292     case GIMP_RGB_IMAGE:      bpp = 3; break;
1293     case GIMP_RGBA_IMAGE:     bpp = 4; break;
1294     case GIMP_GRAY_IMAGE:     bpp = 1; break;
1295     case GIMP_GRAYA_IMAGE:    bpp = 2; break;
1296     case GIMP_INDEXED_IMAGE:  bpp = 1; break;
1297     case GIMP_INDEXEDA_IMAGE: bpp = 2; break;
1298     default:
1299                               break;
1300     }
1301 
1302   if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
1303     {
1304       for (i = 0; ; ++i)
1305         {
1306           if (format_info[i].format == dds_write_vals.format)
1307             {
1308               fmtbpp = format_info[i].bpp;
1309               has_alpha = format_info[i].alpha;
1310               rmask = format_info[i].rmask;
1311               gmask = format_info[i].gmask;
1312               bmask = format_info[i].bmask;
1313               amask = format_info[i].amask;
1314               dxgi_format = format_info[i].dxgi_format;
1315               break;
1316             }
1317         }
1318     }
1319   else if (bpp == 1)
1320     {
1321       if (basetype == GIMP_INDEXED)
1322         {
1323           fmtbpp = 1;
1324           has_alpha = 0;
1325           rmask = bmask = gmask = amask = 0;
1326         }
1327       else
1328         {
1329           fmtbpp = 1;
1330           has_alpha = 0;
1331           rmask = 0x000000ff;
1332           gmask = bmask = amask = 0;
1333           dxgi_format = DXGI_FORMAT_R8_UNORM;
1334         }
1335     }
1336   else if (bpp == 2)
1337     {
1338       if (basetype == GIMP_INDEXED)
1339         {
1340           fmtbpp = 1;
1341           has_alpha = 0;
1342           rmask = gmask = bmask = amask = 0;
1343         }
1344       else
1345         {
1346           fmtbpp = 2;
1347           has_alpha = 1;
1348           rmask = 0x000000ff;
1349           gmask = 0x000000ff;
1350           bmask = 0x000000ff;
1351           amask = 0x0000ff00;
1352         }
1353     }
1354   else if (bpp == 3)
1355     {
1356       fmtbpp = 3;
1357       rmask = 0x00ff0000;
1358       gmask = 0x0000ff00;
1359       bmask = 0x000000ff;
1360       amask = 0x00000000;
1361     }
1362   else
1363     {
1364       fmtbpp = 4;
1365       has_alpha = 1;
1366       rmask = 0x00ff0000;
1367       gmask = 0x0000ff00;
1368       bmask = 0x000000ff;
1369       amask = 0xff000000;
1370       dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
1371     }
1372 
1373   memset (hdr, 0, DDS_HEADERSIZE);
1374 
1375   PUTL32(hdr,       FOURCC ('D','D','S',' '));
1376   PUTL32(hdr + 4,   124);
1377   PUTL32(hdr + 12,  h);
1378   PUTL32(hdr + 16,  w);
1379   PUTL32(hdr + 76,  32);
1380 
1381   if (dds_write_vals.compression == DDS_COMPRESS_NONE)
1382     {
1383       PUTL32(hdr + 88,  fmtbpp << 3);
1384       PUTL32(hdr + 92,  rmask);
1385       PUTL32(hdr + 96,  gmask);
1386       PUTL32(hdr + 100, bmask);
1387       PUTL32(hdr + 104, amask);
1388     }
1389 
1390   /*
1391      put some information in the reserved area to identify the origin
1392      of the image
1393      */
1394   PUTL32(hdr + 32, FOURCC ('G','I','M','P'));
1395   PUTL32(hdr + 36, FOURCC ('-','D','D','S'));
1396   PUTL32(hdr + 40, DDS_PLUGIN_VERSION);
1397 
1398   flags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
1399 
1400   caps = DDSCAPS_TEXTURE;
1401   if (dds_write_vals.mipmaps)
1402     {
1403       flags |= DDSD_MIPMAPCOUNT;
1404       caps |= (DDSCAPS_COMPLEX | DDSCAPS_MIPMAP);
1405       num_mipmaps = get_num_mipmaps (w, h);
1406     }
1407   else
1408     {
1409       num_mipmaps = 1;
1410     }
1411 
1412   if ((dds_write_vals.savetype == DDS_SAVE_CUBEMAP) && is_cubemap)
1413     {
1414       caps |= DDSCAPS_COMPLEX;
1415       caps2 |= (DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES);
1416     }
1417   else if ((dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP) && is_volume)
1418     {
1419       PUTL32(hdr + 24, num_layers); /* depth */
1420       flags |= DDSD_DEPTH;
1421       caps |= DDSCAPS_COMPLEX;
1422       caps2 |= DDSCAPS2_VOLUME;
1423     }
1424 
1425   PUTL32(hdr + 28,  num_mipmaps);
1426   PUTL32(hdr + 108, caps);
1427   PUTL32(hdr + 112, caps2);
1428 
1429   if (dds_write_vals.compression == DDS_COMPRESS_NONE)
1430     {
1431       flags |= DDSD_PITCH;
1432 
1433       if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
1434         {
1435           if (dds_write_vals.format == DDS_FORMAT_A8)
1436             pflags |= DDPF_ALPHA;
1437           else
1438             {
1439               if (((fmtbpp == 1) || (dds_write_vals.format == DDS_FORMAT_L8A8)) &&
1440                   (dds_write_vals.format != DDS_FORMAT_R3G3B2))
1441                 pflags |= DDPF_LUMINANCE;
1442               else
1443                 pflags |= DDPF_RGB;
1444             }
1445         }
1446       else
1447         {
1448           if (bpp == 1)
1449             {
1450               if (basetype == GIMP_INDEXED)
1451                 pflags |= DDPF_PALETTEINDEXED8;
1452               else
1453                 pflags |= DDPF_LUMINANCE;
1454             }
1455           else if ((bpp == 2) && (basetype == GIMP_INDEXED))
1456             {
1457               pflags |= DDPF_PALETTEINDEXED8;
1458             }
1459           else
1460             {
1461               pflags |= DDPF_RGB;
1462             }
1463         }
1464 
1465       if (has_alpha)
1466         pflags |= DDPF_ALPHAPIXELS;
1467 
1468       PUTL32 (hdr + 8,  flags);
1469       PUTL32 (hdr + 20, w * fmtbpp); /* pitch */
1470       PUTL32 (hdr + 80, pflags);
1471 
1472       /*
1473        * write extra fourcc info - this is special to GIMP DDS. When the image
1474        * is read by the plugin, we can detect the added information to decode
1475        * the pixels
1476        */
1477       if (dds_write_vals.format == DDS_FORMAT_AEXP)
1478         {
1479           PUTL32 (hdr + 44, FOURCC ('A','E','X','P'));
1480         }
1481       else if (dds_write_vals.format == DDS_FORMAT_YCOCG)
1482         {
1483           PUTL32 (hdr + 44, FOURCC ('Y','C','G','1'));
1484         }
1485     }
1486   else
1487     {
1488       flags |= DDSD_LINEARSIZE;
1489       pflags = DDPF_FOURCC;
1490 
1491       switch (dds_write_vals.compression)
1492         {
1493         case DDS_COMPRESS_BC1:
1494           fourcc = FOURCC ('D','X','T','1');
1495           dxgi_format = DXGI_FORMAT_BC1_UNORM;
1496           break;
1497 
1498         case DDS_COMPRESS_BC2:
1499           fourcc = FOURCC ('D','X','T','3');
1500           dxgi_format = DXGI_FORMAT_BC2_UNORM;
1501           break;
1502 
1503         case DDS_COMPRESS_BC3:
1504         case DDS_COMPRESS_BC3N:
1505         case DDS_COMPRESS_YCOCG:
1506         case DDS_COMPRESS_YCOCGS:
1507         case DDS_COMPRESS_AEXP:
1508           fourcc = FOURCC ('D','X','T','5');
1509           dxgi_format = DXGI_FORMAT_BC3_UNORM;
1510           break;
1511 
1512         case DDS_COMPRESS_RXGB:
1513           fourcc = FOURCC ('R','X','G','B');
1514           dxgi_format = DXGI_FORMAT_BC3_UNORM;
1515           break;
1516 
1517         case DDS_COMPRESS_BC4:
1518           fourcc = FOURCC ('A','T','I','1');
1519           dxgi_format = DXGI_FORMAT_BC4_UNORM;
1520           //is_dx10 = 1;
1521           break;
1522 
1523         case DDS_COMPRESS_BC5:
1524           fourcc = FOURCC ('A','T','I','2');
1525           dxgi_format = DXGI_FORMAT_BC5_UNORM;
1526           //is_dx10 = 1;
1527           break;
1528         }
1529 
1530       if ((dds_write_vals.compression == DDS_COMPRESS_BC3N) ||
1531           (dds_write_vals.compression == DDS_COMPRESS_RXGB))
1532         {
1533           pflags |= DDPF_NORMAL;
1534         }
1535 
1536       PUTL32 (hdr + 8,  flags);
1537       PUTL32 (hdr + 80, pflags);
1538       PUTL32 (hdr + 84, fourcc);
1539 
1540       size = ((w + 3) >> 2) * ((h + 3) >> 2);
1541       if ((dds_write_vals.compression == DDS_COMPRESS_BC1) ||
1542           (dds_write_vals.compression == DDS_COMPRESS_BC4))
1543         size *= 8;
1544       else
1545         size *= 16;
1546 
1547       PUTL32 (hdr + 20, size); /* linear size */
1548 
1549       /*
1550        * write extra fourcc info - this is special to GIMP DDS. When the image
1551        * is read by the plugin, we can detect the added information to decode
1552        * the pixels
1553        */
1554       if (dds_write_vals.compression == DDS_COMPRESS_AEXP)
1555         {
1556           PUTL32 (hdr + 44, FOURCC ('A','E','X','P'));
1557         }
1558       else if (dds_write_vals.compression == DDS_COMPRESS_YCOCG)
1559         {
1560           PUTL32 (hdr + 44, FOURCC ('Y','C','G','1'));
1561         }
1562       else if (dds_write_vals.compression == DDS_COMPRESS_YCOCGS)
1563         {
1564           PUTL32 (hdr + 44, FOURCC ('Y','C','G','2'));
1565         }
1566     }
1567 
1568   /* texture arrays require a DX10 header */
1569   if (dds_write_vals.savetype == DDS_SAVE_ARRAY)
1570     is_dx10 = 1;
1571 
1572   if (is_dx10)
1573     {
1574       array_size = (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER) ? 1 : get_array_size (image_id);
1575 
1576       PUTL32 (hdr10 +  0, dxgi_format);
1577       PUTL32 (hdr10 +  4, D3D10_RESOURCE_DIMENSION_TEXTURE2D);
1578       PUTL32 (hdr10 +  8, 0);
1579       PUTL32 (hdr10 + 12, array_size);
1580       PUTL32 (hdr10 + 16, 0);
1581 
1582       /* update main header accordingly */
1583       PUTL32 (hdr + 80, pflags | DDPF_FOURCC);
1584       PUTL32 (hdr + 84, FOURCC ('D','X','1','0'));
1585     }
1586 
1587   fwrite (hdr, DDS_HEADERSIZE, 1, fp);
1588 
1589   if (is_dx10)
1590     fwrite (hdr10, DDS_HEADERSIZE_DX10, 1, fp);
1591 
1592   /* write palette for indexed images */
1593   if ((basetype == GIMP_INDEXED) &&
1594       (dds_write_vals.format == DDS_FORMAT_DEFAULT) &&
1595       (dds_write_vals.compression == DDS_COMPRESS_NONE))
1596     {
1597       cmap = gimp_image_get_colormap (image_id, &colors);
1598 
1599       for (i = 0; i < colors; ++i)
1600         {
1601           fwrite (&cmap[3 * i], 1, 3, fp);
1602           if (i == dds_write_vals.transindex)
1603             fputc (0, fp);
1604           else
1605             fputc (255, fp);
1606         }
1607 
1608       for (; i < 256; ++i)
1609         fwrite (zero, 1, 4, fp);
1610     }
1611 
1612   if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP)
1613     {
1614       for (i = 0; i < 6; ++i)
1615         {
1616           write_layer (fp, image_id, cubemap_faces[i], w, h, bpp, fmtbpp,
1617                        num_mipmaps);
1618           gimp_progress_update ((float)(i + 1) / 6.0);
1619         }
1620     }
1621   else if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
1622     {
1623       for (i = 0; i < num_layers; ++i)
1624         {
1625           write_layer (fp, image_id, layers[i], w, h, bpp, fmtbpp, 1);
1626           gimp_progress_update ((float)i / (float)num_layers);
1627         }
1628 
1629       if (num_mipmaps > 1)
1630         write_volume_mipmaps (fp, image_id, layers, w, h, num_layers,
1631                               bpp, fmtbpp, num_mipmaps);
1632     }
1633   else if (dds_write_vals.savetype == DDS_SAVE_ARRAY)
1634     {
1635       for (i = 0; i < num_layers; ++i)
1636         {
1637           if ((gimp_drawable_width  (layers[i]) == w) &&
1638               (gimp_drawable_height (layers[i]) == h))
1639             {
1640               write_layer (fp, image_id, layers[i],
1641                            w, h, bpp, fmtbpp, num_mipmaps);
1642             }
1643 
1644           gimp_progress_update ((float)i / (float)num_layers);
1645         }
1646     }
1647   else
1648     {
1649       write_layer (fp, image_id, drawable_id, w, h, bpp, fmtbpp, num_mipmaps);
1650     }
1651 
1652   gimp_progress_update (1.0);
1653 
1654   return TRUE;
1655 }
1656 
1657 static GtkWidget *
string_value_combo_new(string_value_t * strings,int active_value)1658 string_value_combo_new (string_value_t *strings,
1659                         int             active_value)
1660 {
1661   GtkWidget *opt;
1662   GtkCellRenderer *renderer;
1663   GtkListStore *store;
1664   GtkTreeIter iter;
1665   gint i;
1666   gint active = 0;
1667 
1668   store = gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN);
1669   for (i = 0; strings[i].string; ++i)
1670     {
1671       if (strings[i].value == active_value) active = i;
1672       gtk_list_store_append (store, &iter);
1673       gtk_list_store_set (store, &iter,
1674                           0, strings[i].value,
1675                           1, strings[i].string,
1676                           2, 1,
1677                           -1);
1678     }
1679 
1680   renderer = gtk_cell_renderer_text_new ();
1681 
1682   opt = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
1683   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (opt), renderer, 1);
1684   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (opt), renderer,
1685                                   "text", COMBO_STRING,
1686                                   "sensitive", COMBO_SENSITIVE,
1687                                   NULL);
1688 
1689   gtk_combo_box_set_active (GTK_COMBO_BOX (opt), active);
1690 
1691   g_object_unref (store);
1692 
1693   return opt;
1694 }
1695 
1696 static void
string_value_combo_selected(GtkWidget * widget,gpointer data)1697 string_value_combo_selected (GtkWidget *widget,
1698                              gpointer   data)
1699 {
1700   gint value;
1701   GtkTreeIter iter;
1702   GtkTreeModel *model;
1703 
1704   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
1705   gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
1706   gtk_tree_model_get (model, &iter, COMBO_VALUE, &value, -1);
1707 
1708   *((int *)data) = value;
1709 }
1710 
1711 static void
string_value_combo_set_item_sensitive(GtkWidget * widget,gint value,gboolean sensitive)1712 string_value_combo_set_item_sensitive (GtkWidget *widget,
1713                                        gint       value,
1714                                        gboolean   sensitive)
1715 {
1716   GtkTreeIter iter;
1717   GtkTreeModel *model;
1718   gint val;
1719 
1720   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
1721   gtk_tree_model_get_iter_first (model, &iter);
1722   do
1723     {
1724       gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1);
1725       if (val == value)
1726         {
1727           gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1728                               COMBO_SENSITIVE, sensitive, -1);
1729           break;
1730         }
1731     } while (gtk_tree_model_iter_next (model, &iter));
1732 }
1733 
1734 static void
string_value_combo_set_active(GtkWidget * widget,gint value)1735 string_value_combo_set_active (GtkWidget *widget,
1736                                gint       value)
1737 {
1738   GtkTreeIter iter;
1739   GtkTreeModel *model;
1740   int val;
1741 
1742   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
1743   gtk_tree_model_get_iter_first (model, &iter);
1744   do
1745     {
1746       gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1);
1747       if (val == value)
1748         {
1749           gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
1750           break;
1751         }
1752     } while (gtk_tree_model_iter_next (model, &iter));
1753 }
1754 
1755 static void
save_dialog_response(GtkWidget * widget,gint response_id,gpointer data)1756 save_dialog_response (GtkWidget *widget,
1757                       gint       response_id,
1758                       gpointer   data)
1759 {
1760   switch (response_id)
1761     {
1762     case GTK_RESPONSE_OK:
1763       runme = TRUE;
1764 
1765     default:
1766       gtk_widget_destroy (widget);
1767       break;
1768     }
1769 }
1770 
1771 static void
compression_selected(GtkWidget * widget,gpointer data)1772 compression_selected (GtkWidget *widget,
1773                       gpointer   data)
1774 {
1775   GtkTreeIter iter;
1776   GtkTreeModel *model;
1777 
1778   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
1779   gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
1780   gtk_tree_model_get (model, &iter, COMBO_VALUE,
1781                       &dds_write_vals.compression,
1782                       -1);
1783 
1784   gtk_widget_set_sensitive (format_opt,
1785                             dds_write_vals.compression == DDS_COMPRESS_NONE);
1786   gtk_widget_set_sensitive (pm_chk,
1787                             dds_write_vals.compression != DDS_COMPRESS_NONE);
1788 }
1789 
1790 static void
savetype_selected(GtkWidget * widget,gpointer data)1791 savetype_selected (GtkWidget *widget,
1792                    gpointer   data)
1793 {
1794   gint32 image_id = *((gint32 *)data);
1795 
1796   dds_write_vals.savetype = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
1797 
1798   switch (dds_write_vals.savetype)
1799     {
1800     case DDS_SAVE_SELECTED_LAYER:
1801     case DDS_SAVE_CUBEMAP:
1802     case DDS_SAVE_ARRAY:
1803       gtk_widget_set_sensitive (compress_opt, TRUE);
1804       break;
1805 
1806     case DDS_SAVE_VOLUMEMAP:
1807       dds_write_vals.compression = DDS_COMPRESS_NONE;
1808       gtk_combo_box_set_active (GTK_COMBO_BOX (compress_opt),
1809                                 DDS_COMPRESS_NONE);
1810       gtk_widget_set_sensitive (compress_opt, FALSE);
1811       break;
1812     }
1813 
1814   string_value_combo_set_item_sensitive (mipmap_opt, DDS_MIPMAP_EXISTING,
1815                                          check_mipmaps (image_id, dds_write_vals.savetype));
1816 }
1817 
1818 static void
mipmaps_selected(GtkWidget * widget,gpointer data)1819 mipmaps_selected (GtkWidget *widget,
1820                   gpointer   data)
1821 {
1822   GtkTreeModel *model;
1823   GtkTreeIter   iter;
1824 
1825   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
1826   gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
1827   gtk_tree_model_get (model, &iter, COMBO_VALUE,
1828                       &dds_write_vals.mipmaps, -1);
1829 
1830   gtk_widget_set_sensitive (mipmap_filter_opt,
1831                             dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
1832   gtk_widget_set_sensitive (mipmap_wrap_opt,
1833                             dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
1834   gtk_widget_set_sensitive (gamma_chk,
1835                             dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
1836   gtk_widget_set_sensitive (srgb_chk,
1837                             (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
1838                             dds_write_vals.gamma_correct);
1839   gtk_widget_set_sensitive (gamma_spin,
1840                             (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
1841                             dds_write_vals.gamma_correct &&
1842                             !dds_write_vals.srgb);
1843   gtk_widget_set_sensitive (alpha_coverage_chk,
1844                             dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
1845   gtk_widget_set_sensitive (alpha_test_threshold_spin,
1846                             (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
1847                             dds_write_vals.preserve_alpha_coverage);
1848 }
1849 
1850 static void
toggle_clicked(GtkWidget * widget,gpointer data)1851 toggle_clicked (GtkWidget *widget,
1852                 gpointer   data)
1853 {
1854   gint *flag = (int *)data;
1855 
1856   (*flag) = !(*flag);
1857 }
1858 
1859 static void
transindex_clicked(GtkWidget * widget,gpointer data)1860 transindex_clicked (GtkWidget *widget,
1861                     gpointer   data)
1862 {
1863   GtkWidget *spin = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "spin"));
1864 
1865   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1866     {
1867       dds_write_vals.transindex = 0;
1868       gtk_widget_set_sensitive (spin, TRUE);
1869       gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 0);
1870     }
1871   else
1872     {
1873       gtk_widget_set_sensitive (spin, FALSE);
1874       dds_write_vals.transindex = -1;
1875     }
1876 }
1877 
1878 static void
transindex_changed(GtkWidget * widget,gpointer data)1879 transindex_changed (GtkWidget *widget,
1880                     gpointer   data)
1881 {
1882   dds_write_vals.transindex =
1883     gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
1884 }
1885 
1886 static void
gamma_correct_clicked(GtkWidget * widget,gpointer data)1887 gamma_correct_clicked (GtkWidget *widget,
1888                        gpointer   data)
1889 {
1890   dds_write_vals.gamma_correct =
1891     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1892 
1893   gtk_widget_set_sensitive (srgb_chk,
1894                             dds_write_vals.gamma_correct);
1895   gtk_widget_set_sensitive (gamma_spin,
1896                             dds_write_vals.gamma_correct &&
1897                             ! dds_write_vals.srgb);
1898 }
1899 
1900 static void
srgb_clicked(GtkWidget * widget,gpointer data)1901 srgb_clicked (GtkWidget *widget,
1902               gpointer   data)
1903 {
1904   dds_write_vals.srgb =
1905     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1906 
1907   gtk_widget_set_sensitive (gamma_spin, !dds_write_vals.srgb);
1908 }
1909 
1910 static void
gamma_changed(GtkWidget * widget,gpointer data)1911 gamma_changed (GtkWidget *widget,
1912                gpointer   data)
1913 {
1914   dds_write_vals.gamma =
1915     gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
1916 }
1917 
1918 static void
alpha_coverage_clicked(GtkWidget * widget,gpointer data)1919 alpha_coverage_clicked (GtkWidget *widget,
1920                         gpointer   data)
1921 {
1922   dds_write_vals.preserve_alpha_coverage =
1923     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1924 
1925   gtk_widget_set_sensitive (alpha_test_threshold_spin,
1926                             dds_write_vals.preserve_alpha_coverage);
1927 }
1928 
1929 static void
alpha_test_threshold_changed(GtkWidget * widget,gpointer data)1930 alpha_test_threshold_changed (GtkWidget *widget,
1931                               gpointer   data)
1932 {
1933   dds_write_vals.alpha_test_threshold =
1934     gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
1935 }
1936 
1937 static gint
save_dialog(gint32 image_id,gint32 drawable_id)1938 save_dialog (gint32 image_id,
1939              gint32 drawable_id)
1940 {
1941   GtkWidget *dlg;
1942   GtkWidget *vbox;
1943   GtkWidget *hbox;
1944   GtkWidget *table;
1945   GtkWidget *opt;
1946   GtkWidget *check;
1947   GtkWidget *spin;
1948   GtkWidget *frame;
1949   GimpImageBaseType basetype;
1950 
1951   if (is_cubemap || is_volume || is_array)
1952     dds_write_vals.savetype = DDS_SAVE_SELECTED_LAYER;
1953 
1954   basetype = gimp_image_base_type (image_id);
1955 
1956   dlg = gimp_dialog_new (_("Export as DDS"), "dds", NULL, GTK_WIN_POS_MOUSE,
1957                          gimp_standard_help_func, SAVE_PROC,
1958                          _("_Cancel"), GTK_RESPONSE_CANCEL,
1959                          _("_Export"), GTK_RESPONSE_OK,
1960                          NULL);
1961 
1962   g_signal_connect (dlg, "response",
1963                     G_CALLBACK (save_dialog_response),
1964                     NULL);
1965   g_signal_connect (dlg, "destroy",
1966                     G_CALLBACK (gtk_main_quit),
1967                     NULL);
1968 
1969   gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1970 
1971   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1972   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
1973   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
1974                       vbox, TRUE, TRUE, 0);
1975   gtk_widget_show (vbox);
1976 
1977   table = gtk_table_new (6, 2, 0);
1978   gtk_widget_show (table);
1979   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1980   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1981   gtk_table_set_col_spacings (GTK_TABLE (table), 8);
1982 
1983   opt = string_value_combo_new (compression_strings,
1984                                 dds_write_vals.compression);
1985   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
1986                              _("_Compression:"),
1987                              0.0, 0.5,
1988                              opt, 1, FALSE);
1989 
1990   g_signal_connect (opt, "changed",
1991                     G_CALLBACK (compression_selected),
1992                     NULL);
1993 
1994   compress_opt = opt;
1995 
1996   check = gtk_check_button_new_with_mnemonic (_("Use _perceptual error metric"));
1997   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
1998                                 dds_write_vals.perceptual_metric);
1999   gtk_table_attach (GTK_TABLE (table), check, 1, 2, 1, 2,
2000                     GTK_FILL, 0, 0, 0);
2001   gtk_widget_show (check);
2002 
2003   g_signal_connect (check, "clicked",
2004                     G_CALLBACK (toggle_clicked),
2005                     &dds_write_vals.perceptual_metric);
2006 
2007   pm_chk = check;
2008 
2009   opt = string_value_combo_new (format_strings, dds_write_vals.format);
2010   gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
2011                              _("_Format:"),
2012                              0.0, 0.5,
2013                              opt, 1, FALSE);
2014 
2015   g_signal_connect (opt, "changed",
2016                     G_CALLBACK (string_value_combo_selected),
2017                     &dds_write_vals.format);
2018 
2019   gtk_widget_set_sensitive (opt, dds_write_vals.compression == DDS_COMPRESS_NONE);
2020 
2021   format_opt = opt;
2022 
2023   opt = string_value_combo_new (save_type_strings, dds_write_vals.savetype);
2024   gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
2025                              _("_Save:"),
2026                              0.0, 0.5,
2027                              opt, 1, FALSE);
2028 
2029   g_signal_connect (opt, "changed",
2030                     G_CALLBACK (savetype_selected),
2031                     &image_id);
2032 
2033   string_value_combo_set_item_sensitive (opt, DDS_SAVE_CUBEMAP, is_cubemap);
2034   string_value_combo_set_item_sensitive (opt, DDS_SAVE_VOLUMEMAP, is_volume);
2035   string_value_combo_set_item_sensitive (opt, DDS_SAVE_ARRAY, is_array);
2036 
2037   opt = string_value_combo_new (mipmap_strings, dds_write_vals.mipmaps);
2038   gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
2039                              _("_Mipmaps:"),
2040                              0.0, 0.5,
2041                              opt, 1, FALSE);
2042 
2043   g_signal_connect (opt, "changed",
2044                     G_CALLBACK (mipmaps_selected),
2045                     &image_id);
2046 
2047   string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING,
2048                                          check_mipmaps (image_id,
2049                                                         dds_write_vals.savetype));
2050 
2051   mipmap_opt = opt;
2052 
2053   string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING,
2054                                          ! (is_volume || is_cubemap) &&
2055                                          is_mipmap_chain_valid);
2056 
2057 
2058   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
2059   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2060   gtk_widget_show (hbox);
2061 
2062   check = gtk_check_button_new_with_label (_("Transparent index:"));
2063   gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
2064   gtk_widget_show (check);
2065 
2066   g_signal_connect (check, "clicked",
2067                     G_CALLBACK (transindex_clicked),
2068                     NULL);
2069 
2070   spin = gimp_spin_button_new
2071     (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 255, 1, 1, 0)), 1, 0);
2072   gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
2073   gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin),
2074                                      GTK_UPDATE_IF_VALID);
2075   gtk_widget_show (spin);
2076 
2077   g_signal_connect (spin, "value-changed",
2078                     G_CALLBACK (transindex_changed),
2079                     NULL);
2080 
2081   g_object_set_data (G_OBJECT (check), "spin", spin);
2082 
2083   if (basetype != GIMP_INDEXED)
2084     {
2085       gtk_widget_set_sensitive (check, FALSE);
2086       gtk_widget_set_sensitive (spin, FALSE);
2087     }
2088   else if (dds_write_vals.transindex < 0)
2089     {
2090       gtk_widget_set_sensitive (spin, FALSE);
2091     }
2092   else if (dds_write_vals.transindex >= 0)
2093     {
2094       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE);
2095       gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin),
2096                                  dds_write_vals.transindex);
2097     }
2098 
2099   if (is_volume && dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
2100     {
2101       dds_write_vals.compression = DDS_COMPRESS_NONE;
2102       string_value_combo_set_active (compress_opt, DDS_COMPRESS_NONE);
2103       gtk_widget_set_sensitive (compress_opt, FALSE);
2104     }
2105 
2106   frame = gimp_frame_new (_("Mipmap Options"));
2107   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
2108   gtk_widget_show (frame);
2109 
2110   table = gtk_table_new (7, 2, 0);
2111   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
2112   gtk_table_set_col_spacings (GTK_TABLE (table), 8);
2113   gtk_container_add (GTK_CONTAINER (frame), table);
2114   gtk_widget_show (table);
2115 
2116   opt = string_value_combo_new (mipmap_filter_strings,
2117                                 dds_write_vals.mipmap_filter);
2118   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
2119                              _("F_ilter:"),
2120                              0.0, 0.5,
2121                              opt, 1, FALSE);
2122 
2123   g_signal_connect (opt, "changed",
2124                     G_CALLBACK (string_value_combo_selected),
2125                     &dds_write_vals.mipmap_filter);
2126 
2127   mipmap_filter_opt = opt;
2128 
2129   opt = string_value_combo_new (mipmap_wrap_strings,
2130                                 dds_write_vals.mipmap_wrap);
2131   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
2132                              _("_Wrap mode:"),
2133                              0.0, 0.5,
2134                              opt, 1, FALSE);
2135 
2136   g_signal_connect (opt, "changed",
2137                     G_CALLBACK (string_value_combo_selected),
2138                     &dds_write_vals.mipmap_wrap);
2139 
2140   mipmap_wrap_opt = opt;
2141 
2142   check = gtk_check_button_new_with_mnemonic (_("Appl_y gamma correction"));
2143   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
2144                                 dds_write_vals.gamma_correct &&
2145                                 dds_write_vals.mipmaps);
2146   gtk_table_attach (GTK_TABLE (table), check, 1, 2, 2, 3,
2147                     GTK_FILL, 0, 0, 0);
2148   gtk_widget_show (check);
2149 
2150   g_signal_connect (check, "clicked",
2151                     G_CALLBACK (gamma_correct_clicked),
2152                     NULL);
2153 
2154   gamma_chk = check;
2155 
2156   check = gtk_check_button_new_with_mnemonic (_("Use s_RGB colorspace"));
2157   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
2158                                 dds_write_vals.gamma_correct &&
2159                                 dds_write_vals.srgb);
2160   gtk_table_attach (GTK_TABLE (table), check, 1, 2, 3, 4,
2161                     GTK_FILL, 0, 0, 0);
2162   gtk_widget_show (check);
2163 
2164   g_signal_connect (check, "clicked",
2165                     G_CALLBACK (srgb_clicked),
2166                     NULL);
2167 
2168   srgb_chk = check;
2169 
2170   spin = gimp_spin_button_new
2171     (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.gamma,
2172                                          1e-05, 100, 0.1, 0.5, 0)), 1, 1);
2173   gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID);
2174   gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
2175                              _("_Gamma:"),
2176                              0.0, 0.5,
2177                              spin, 1, FALSE);
2178 
2179   g_signal_connect (spin, "value_changed",
2180                     G_CALLBACK (gamma_changed),
2181                     NULL);
2182 
2183   gamma_spin = spin;
2184 
2185   check = gtk_check_button_new_with_mnemonic (_("Preserve alpha _test coverage"));
2186   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
2187                                 dds_write_vals.preserve_alpha_coverage &&
2188                                 dds_write_vals.mipmaps);
2189   gtk_table_attach (GTK_TABLE (table), check, 1, 2, 5, 6,
2190                     GTK_FILL, 0, 0, 0);
2191   gtk_widget_show (check);
2192 
2193   g_signal_connect (check, "clicked",
2194                     G_CALLBACK (alpha_coverage_clicked),
2195                     NULL);
2196 
2197   alpha_coverage_chk = check;
2198 
2199   spin = gimp_spin_button_new
2200     (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.alpha_test_threshold,
2201                                          0, 1, 0.01, 0.1, 0)), 1, 2);
2202   gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID);
2203   gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
2204                              _("_Alpha test threshold:"),
2205                              0.0, 0.5,
2206                              spin, 1, FALSE);
2207 
2208   g_signal_connect (spin, "value_changed",
2209                     G_CALLBACK (alpha_test_threshold_changed),
2210                     NULL);
2211 
2212   alpha_test_threshold_spin = spin;
2213 
2214   gtk_widget_set_sensitive (mipmap_filter_opt,
2215                             dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
2216   gtk_widget_set_sensitive (mipmap_wrap_opt,
2217                             dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
2218   gtk_widget_set_sensitive (gamma_chk
2219                             , dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
2220   gtk_widget_set_sensitive (srgb_chk,
2221                             (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
2222                             dds_write_vals.gamma_correct);
2223   gtk_widget_set_sensitive (gamma_spin,
2224                             (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
2225                             dds_write_vals.gamma_correct &&
2226                             !dds_write_vals.srgb);
2227   gtk_widget_set_sensitive (pm_chk,
2228                             dds_write_vals.compression != DDS_COMPRESS_NONE);
2229   gtk_widget_set_sensitive (alpha_coverage_chk,
2230                             dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
2231   gtk_widget_set_sensitive (alpha_test_threshold_spin,
2232                             (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
2233                             dds_write_vals.preserve_alpha_coverage);
2234 
2235   gtk_widget_show (dlg);
2236 
2237   runme = FALSE;
2238 
2239   gtk_main ();
2240 
2241   return runme;
2242 }
2243