[gimp-dds] / trunk / ddswrite.c Repository:
ViewVC logotype

View of /trunk/ddswrite.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 125 - (download) (as text) (annotate)
Fri Dec 12 18:58:19 2008 UTC (11 months, 1 week ago) by cocidius
File size: 51887 byte(s)
Tableized pixel format information
    1 /*
    2    DDS GIMP plugin
    3 
    4    Copyright (C) 2004-2008 Shawn Kirst <skirst@insightbb.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, Inc., 59 Temple Place - Suite 330,
   20    Boston, MA 02111-1307, USA.
   21 */
   22 
   23 #include <stdio.h>
   24 #include <stdlib.h>
   25 #include <string.h>
   26 
   27 #include <gtk/gtk.h>
   28 
   29 #include <libgimp/gimp.h>
   30 #include <libgimp/gimpui.h>
   31 
   32 #include "ddsplugin.h"
   33 #include "dds.h"
   34 #include "dxt.h"
   35 #include "mipmap.h"
   36 #include "endian.h"
   37 #include "imath.h"
   38 
   39 static gint save_dialog(gint32 image_id, gint32 drawable);
   40 static void save_dialog_response(GtkWidget *widget, gint response_id, gpointer data);
   41 static int write_image(FILE *fp, gint32 image_id, gint32 drawable_id);
   42 
   43 static int runme = 0;
   44 
   45 enum
   46 {
   47    COMBO_VALUE, COMBO_STRING, COMBO_SENSITIVE
   48 };
   49 
   50 static const char *cubemap_face_names[4][6] =
   51 {
   52    {
   53       "positive x", "negative x",
   54       "positive y", "negative y",
   55       "positive z", "negative z"
   56    },
   57    {
   58       "pos x", "neg x",
   59       "pos y", "neg y",
   60       "pos z", "neg z",
   61    },
   62    {
   63       "+x", "-x",
   64       "+y", "-y",
   65       "+z", "-z"
   66    },
   67    {
   68       "right", "left",
   69       "top", "bottom",
   70       "back", "front"
   71    }
   72 };
   73 
   74 static gint cubemap_faces[6];
   75 static gint is_cubemap = 0;
   76 static gint is_volume = 0;
   77 
   78 static GtkWidget *mipmap_check;
   79 static GtkWidget *compress_opt;
   80 static GtkWidget *format_opt;
   81 static GtkWidget *color_type_opt;
   82 static GtkWidget *dither_chk;
   83 static GtkWidget *mipmap_filter_opt;
   84 
   85 typedef struct string_value_s
   86 {
   87    int value;
   88    char *string;
   89 } string_value_t;
   90 
   91 static string_value_t compression_strings[] =
   92 {
   93    {DDS_COMPRESS_NONE,   "None"},
   94    {DDS_COMPRESS_BC1,    "BC1 / DXT1"},
   95    {DDS_COMPRESS_BC2,    "BC2 / DXT3"},
   96    {DDS_COMPRESS_BC3,    "BC3 / DXT5"},
   97    {DDS_COMPRESS_BC3N,   "BC3n / DXT5n"},
   98    {DDS_COMPRESS_BC4,    "BC4 / ATI1"},
   99    {DDS_COMPRESS_BC5,    "BC5 / ATI2"},
  100    {DDS_COMPRESS_AEXP,   "Alpha Exponent (DXT5)"},
  101    {DDS_COMPRESS_YCOCG,  "YCoCg (DXT5)"},
  102    {DDS_COMPRESS_YCOCGS, "YCoCg scaled (DXT5)"},
  103    {-1, 0}
  104 };
  105 
  106 static string_value_t format_strings[] =
  107 {
  108    {DDS_FORMAT_DEFAULT, "Default"},
  109    {DDS_FORMAT_RGB8, "RGB8"},
  110    {DDS_FORMAT_RGBA8, "RGBA8"},
  111    {DDS_FORMAT_BGR8, "BGR8"},
  112    {DDS_FORMAT_ABGR8, "ABGR8"},
  113    {DDS_FORMAT_R5G6B5, "R5G6B5"},
  114    {DDS_FORMAT_RGBA4, "RGBA4"},
  115    {DDS_FORMAT_RGB5A1, "RGB5A1"},
  116    {DDS_FORMAT_RGB10A2, "RGB10A2"},
  117    {DDS_FORMAT_R3G3B2, "R3G3B2"},
  118    {DDS_FORMAT_A8, "A8"},
  119    {DDS_FORMAT_L8, "L8"},
  120    {DDS_FORMAT_L8A8, "L8A8"},
  121    {DDS_FORMAT_AEXP, "AExp"},
  122    {DDS_FORMAT_YCOCG, "YCoCg"},
  123    {-1, 0}
  124 };
  125 
  126 static string_value_t color_type_strings[] =
  127 {
  128    {DDS_COLOR_DEFAULT,    "Default"},
  129    {DDS_COLOR_DISTANCE,   "Distance"},
  130    {DDS_COLOR_LUMINANCE,  "Luminance"},
  131    {DDS_COLOR_INSET_BBOX, "Inset bounding box"},
  132    {-1, 0}
  133 };
  134 
  135 static string_value_t mipmap_filter_strings[] =
  136 {
  137    {DDS_MIPMAP_DEFAULT,  "Default"},
  138    {DDS_MIPMAP_NEAREST,  "Nearest"},
  139    {DDS_MIPMAP_BOX,      "Box"},
  140    {DDS_MIPMAP_BILINEAR, "Bilinear"},
  141    {DDS_MIPMAP_BICUBIC,  "Bicubic"},
  142    {DDS_MIPMAP_LANCZOS,  "Lanczos"},
  143    {-1, 0}
  144 };
  145 
  146 static string_value_t save_type_strings[] =
  147 {
  148    {DDS_SAVE_SELECTED_LAYER, "Selected layer"},
  149    {DDS_SAVE_CUBEMAP,        "As cube map"},
  150    {DDS_SAVE_VOLUMEMAP,      "As volume map"},
  151    {-1, 0}
  152 };
  153 
  154 static struct
  155 {
  156    int format;
  157    int bpp;
  158    int alpha;
  159    unsigned int rmask;
  160    unsigned int gmask;
  161    unsigned int bmask;
  162    unsigned int amask;
  163 } format_info[] =
  164 {
  165    {DDS_FORMAT_RGB8,    3, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
  166    {DDS_FORMAT_RGBA8,   4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
  167    {DDS_FORMAT_BGR8,    3, 0, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000},
  168    {DDS_FORMAT_ABGR8,   4, 1, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},
  169    {DDS_FORMAT_R5G6B5,  2, 0, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000},
  170    {DDS_FORMAT_RGBA4,   2, 1, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000},
  171    {DDS_FORMAT_RGB5A1,  2, 1, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000},
  172    {DDS_FORMAT_RGB10A2, 4, 1, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000},
  173    {DDS_FORMAT_R3G3B2,  1, 0, 0x000000e0, 0x0000001c, 0x00000003, 0x00000000},
  174    {DDS_FORMAT_A8,      1, 0, 0x00000000, 0x00000000, 0x00000000, 0x000000ff},
  175    {DDS_FORMAT_L8,      1, 0, 0x000000ff, 0x000000ff, 0x000000ff, 0x00000000},
  176    {DDS_FORMAT_L8A8,    2, 1, 0x000000ff, 0x000000ff, 0x000000ff, 0x0000ff00},
  177    {DDS_FORMAT_AEXP,    4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
  178    {DDS_FORMAT_YCOCG,   4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}
  179 };
  180 
  181 static int check_cubemap(gint32 image_id)
  182 {
  183    gint *layers, num_layers;
  184    int cubemap = 0, i, j, k, w, h;
  185    char *layer_name;
  186    GimpDrawable *drawable;
  187    GimpImageType type;
  188 
  189    layers = gimp_image_get_layers(image_id, &num_layers);
  190 
  191    if(num_layers == 6)
  192    {
  193       for(i = 0; i < 6; ++i)
  194          cubemap_faces[i] = -1;
  195 
  196       for(i = 0; i < 6; ++i)
  197       {
  198          layer_name = (char*)gimp_drawable_get_name(layers[i]);
  199          for(j = 0; j < 6; ++j)
  200          {
  201             for(k = 0; k < 4; ++k)
  202             {
  203                if(strstr(layer_name, cubemap_face_names[k][j]))
  204                {
  205                   if(cubemap_faces[j] == -1)
  206                   {
  207                      cubemap_faces[j] = layers[i];
  208                      break;
  209                   }
  210                }
  211             }
  212          }
  213       }
  214 
  215       cubemap = 1;
  216 
  217       /* check for 6 valid faces */
  218       for(i = 0; i < 6; ++i)
  219       {
  220          if(cubemap_faces[i] == -1)
  221          {
  222             cubemap = 0;
  223             break;
  224          }
  225       }
  226 
  227       /* make sure they are all the same size */
  228       if(cubemap)
  229       {
  230          drawable = gimp_drawable_get(cubemap_faces[0]);
  231          w = drawable->width;
  232          h = drawable->height;
  233          gimp_drawable_detach(drawable);
  234          for(i = 1; i < 6 && cubemap; ++i)
  235          {
  236             drawable = gimp_drawable_get(cubemap_faces[i]);
  237             if(drawable->width  != w ||
  238                drawable->height != h)
  239             {
  240                cubemap = 0;
  241             }
  242             gimp_drawable_detach(drawable);
  243          }
  244 
  245          if(cubemap == 0)
  246          {
  247             g_message("DDS: It appears that your image is a cube map,\n"
  248                       "but not all layers are the same size, thus a cube\n"
  249                       "map cannot be written.");
  250          }
  251       }
  252 
  253       /* make sure they are all the same type */
  254       if(cubemap)
  255       {
  256          type = gimp_drawable_type(cubemap_faces[0]);
  257          for(i = 1; i < 6; ++i)
  258          {
  259             if(gimp_drawable_type(cubemap_faces[i]) != type)
  260             {
  261                cubemap = 0;
  262                break;
  263             }
  264          }
  265 
  266          if(cubemap == 0)
  267          {
  268             g_message("DDS: It appears that your image is a cube map,\n"
  269                       "but not all layers are the same type, thus a cube\n"
  270                       "map cannot be written (Perhaps some layers have\n"
  271                       "transparency and others do not?).");
  272          }
  273       }
  274    }
  275 
  276    return(cubemap);
  277 }
  278 
  279 static int check_volume(gint32 image_id)
  280 {
  281    gint *layers, num_layers;
  282    int volume = 0, i, w, h;
  283    GimpDrawable *drawable;
  284    GimpImageType type;
  285 
  286    layers = gimp_image_get_layers(image_id, &num_layers);
  287 
  288    if(num_layers > 1)
  289    {
  290       volume = 1;
  291 
  292       drawable = gimp_drawable_get(layers[0]);
  293       w = drawable->width;
  294       h = drawable->height;
  295       gimp_drawable_detach(drawable);
  296       for(i = 1; i < num_layers && volume; ++i)
  297       {
  298          drawable = gimp_drawable_get(layers[i]);
  299          if(drawable->width  != w ||
  300             drawable->height != h)
  301          {
  302             volume = 0;
  303          }
  304          gimp_drawable_detach(drawable);
  305       }
  306 
  307       if(!volume)
  308       {
  309          g_message("DDS: It appears your image may be a volume map,\n"
  310                    "but not all layers are the same size, thus a volume\n"
  311                    "map cannot be written.");
  312       }
  313 
  314       if(volume)
  315       {
  316          type = gimp_drawable_type(layers[0]);
  317          for(i = 1; i < num_layers; ++i)
  318          {
  319             if(gimp_drawable_type(layers[i]) != type)
  320             {
  321                volume = 0;
  322                break;
  323             }
  324          }
  325 
  326          if(!volume)
  327          {
  328             g_message("DDS: It appears your image may be a volume map,\n"
  329                       "but not all layers are the same type, thus a volume\n"
  330                       "map cannot be written (Perhaps some layers have\n"
  331                       "transparency and others do not?).");
  332          }
  333       }
  334    }
  335 
  336    return(volume);
  337 }
  338 
  339 GimpPDBStatusType write_dds(gchar *filename, gint32 image_id, gint32 drawable_id)
  340 {
  341    FILE *fp;
  342    gchar *tmp;
  343    int rc = 0;
  344 
  345    is_cubemap = check_cubemap(image_id);
  346    is_volume = check_volume(image_id);
  347 
  348    if(interactive_dds)
  349    {
  350       if(!save_dialog(image_id, drawable_id))
  351          return(GIMP_PDB_CANCEL);
  352    }
  353    else
  354    {
  355       if(dds_write_vals.savetype == DDS_SAVE_CUBEMAP && !is_cubemap)
  356       {
  357          g_message("DDS: Cannot save image as cube map");
  358          return(GIMP_PDB_EXECUTION_ERROR);
  359       }
  360 
  361       if(dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP && !is_volume)
  362       {
  363          g_message("DDS: Cannot save image as volume map");
  364          return(GIMP_PDB_EXECUTION_ERROR);
  365       }
  366 
  367       if(dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP &&
  368          dds_write_vals.compression != DDS_COMPRESS_NONE)
  369       {
  370          g_message("DDS: Cannot save volume map with compression");
  371          return(GIMP_PDB_EXECUTION_ERROR);
  372       }
  373    }
  374 
  375    fp = fopen(filename, "wb");
  376    if(fp == 0)
  377    {
  378       g_message("Error opening %s", filename);
  379       return(GIMP_PDB_EXECUTION_ERROR);
  380    }
  381 
  382    if(interactive_dds)
  383    {
  384       if(strrchr(filename, '/'))
  385          tmp = g_strdup_printf("Saving %s:", strrchr(filename, '/') + 1);
  386       else
  387          tmp = g_strdup_printf("Saving %s:", filename);
  388       gimp_progress_init(tmp);
  389       g_free(tmp);
  390    }
  391 
  392    rc = write_image(fp, image_id, drawable_id);
  393 
  394    fclose(fp);
  395 
  396    return(rc ? GIMP_PDB_SUCCESS : GIMP_PDB_EXECUTION_ERROR);
  397 }
  398 
  399 #define TO_R5G6B5(r, g, b) \
  400    (unsigned short)((unsigned short)((((r) >> 3) & 0x1f) << 11) |\
  401                     (unsigned short)((((g) >> 2) & 0x3f) <<  5) |\
  402                     (unsigned short)((((b) >> 3) & 0x1f)      ))
  403 #define TO_RGBA4(r, g, b, a) \
  404    (unsigned short)((unsigned short)((((a) >> 4) & 0x0f) << 12) |\
  405                     (unsigned short)((((r) >> 4) & 0x0f) <<  8) |\
  406                     (unsigned short)((((g) >> 4) & 0x0f) <<  4) |\
  407                     (unsigned short)((((b) >> 4) & 0x0f)      ))
  408 #define TO_RGB5A1(r, g, b, a) \
  409    (unsigned short)((unsigned short)((((a) >> 7) & 0x01) << 15) |\
  410                     (unsigned short)((((r) >> 3) & 0x1f) << 10) |\
  411                     (unsigned short)((((g) >> 3) & 0x1f) <<  5) |\
  412                     (unsigned short)((((b) >> 3) & 0x1f)      ))
  413 #define TO_RGB10A2(r, g, b, a) \
  414    (unsigned int)((unsigned int)((((a) >> 6) & 0x003) << 30) | \
  415                   (unsigned int)((((r) << 2) & 0x3ff) << 20) | \
  416                   (unsigned int)((((g) << 2) & 0x3ff) << 10) | \
  417                   (unsigned int)((((b) << 2) & 0x3ff)      ))
  418 #define TO_R3G3B2(r, g, b) \
  419    (unsigned char)(((((r) >> 5) & 0x07) << 5) |\
  420                    ((((g) >> 5) & 0x07) << 2) |\
  421                    ((((b) >> 6) & 0x03)     ))
  422 
  423 #define TO_YCOCG_Y(r, g, b)  (((  (r) +      ((g) << 1) +  (b)      ) + 2) >> 2)
  424 #define TO_YCOCG_CO(r, g, b) ((( ((r) << 1)             - ((b) << 1)) + 2) >> 2)
  425 #define TO_YCOCG_CG(r, g, b) ((( -(r) +      ((g) << 1) -  (b)      ) + 2) >> 2)
  426 
  427 #define TO_LUMINANCE(r, g, b) (((r) * 54 + (g) * 182 + (b) * 20) >> 8)
  428 
  429 static void swap_rb(unsigned char *pixels, unsigned int n, int bpp)
  430 {
  431    unsigned int i;
  432    unsigned char t;
  433 
  434    for(i = 0; i < n; ++i)
  435    {
  436       t = pixels[bpp * i + 0];
  437       pixels[bpp * i + 0] = pixels[bpp * i + 2];
  438       pixels[bpp * i + 2] = t;
  439    }
  440 }
  441 
  442 static void alpha_exp(unsigned char *dst, int r, int g, int b, int a)
  443 {
  444    float ar, ag, ab, aa;
  445 
  446    ar = (float)r / 255.0f;
  447    ag = (float)g / 255.0f;
  448    ab = (float)b / 255.0f;
  449 
  450    aa = MAX(ar, MAX(ag, ab));
  451 
  452    if(aa < 1e-04f)
  453    {
  454       dst[0] = b;
  455       dst[1] = g;
  456       dst[2] = r;
  457       dst[3] = a;
  458       return;
  459    }
  460 
  461    ar /= aa;
  462    ag /= aa;
  463    ab /= aa;
  464 
  465    dst[0] = (int)(ab * 255);
  466    dst[1] = (int)(ag * 255);
  467    dst[2] = (int)(ar * 255);
  468    dst[3] = (int)(aa * a);
  469 }
  470 
  471 static void convert_pixels(unsigned char *dst, unsigned char *src,
  472                            int format, int w, int h, int bpp,
  473                            unsigned char *palette, int mipmaps)
  474 {
  475    unsigned int i, num_pixels;
  476    unsigned char r, g, b, a;
  477 
  478    num_pixels = get_mipmapped_size(w, h, 1, 0, mipmaps, DDS_COMPRESS_NONE);
  479 
  480    for(i = 0; i < num_pixels; ++i)
  481    {
  482       if(bpp == 1)
  483       {
  484          if(palette)
  485          {
  486             r = palette[3 * src[i] + 0];
  487             g = palette[3 * src[i] + 1];
  488             b = palette[3 * src[i] + 2];
  489          }
  490          else
  491             r = g = b = src[i];
  492 
  493          a = 255;
  494       }
  495       else if(bpp == 2)
  496       {
  497          r = g = b = src[2 * i];
  498          a = src[2 * i + 1];
  499       }
  500       else if(bpp == 3)
  501       {
  502          b = src[3 * i + 0];
  503          g = src[3 * i + 1];
  504          r = src[3 * i + 2];
  505          a = 255;
  506       }
  507       else
  508       {
  509          b = src[4 * i + 0];
  510          g = src[4 * i + 1];
  511          r = src[4 * i + 2];
  512          a = src[4 * i + 3];
  513       }
  514 
  515       switch(format)
  516       {
  517          case DDS_FORMAT_RGB8:
  518             dst[3 * i + 0] = b;
  519             dst[3 * i + 1] = g;
  520             dst[3 * i + 2] = r;
  521             break;
  522          case DDS_FORMAT_RGBA8:
  523             dst[4 * i + 0] = b;
  524             dst[4 * i + 1] = g;
  525             dst[4 * i + 2] = r;
  526             dst[4 * i + 3] = a;
  527             break;
  528          case DDS_FORMAT_BGR8:
  529             dst[4 * i + 0] = r;
  530             dst[4 * i + 1] = g;
  531             dst[4 * i + 2] = b;
  532             dst[4 * i + 3] = 255;
  533             break;
  534          case DDS_FORMAT_ABGR8:
  535             dst[4 * i + 0] = r;
  536             dst[4 * i + 1] = g;
  537             dst[4 * i + 2] = b;
  538             dst[4 * i + 3] = a;
  539             break;
  540          case DDS_FORMAT_R5G6B5:
  541             PUTL16(&dst[2 * i], TO_R5G6B5(r, g, b));
  542             break;
  543          case DDS_FORMAT_RGBA4:
  544             PUTL16(&dst[2 * i], TO_RGBA4(r, g, b, a));
  545             break;
  546          case DDS_FORMAT_RGB5A1:
  547             PUTL16(&dst[2 * i], TO_RGB5A1(r, g, b, a));
  548             break;
  549          case DDS_FORMAT_RGB10A2:
  550             PUTL32(&dst[4 * i], TO_RGB10A2(r, g, b, a));
  551             break;
  552          case DDS_FORMAT_R3G3B2:
  553             dst[i] = TO_R3G3B2(r, g, b);
  554             break;
  555          case DDS_FORMAT_A8:
  556             dst[i] = a;
  557             break;
  558          case DDS_FORMAT_L8:
  559             dst[i] = TO_LUMINANCE(r, g, b);
  560             break;
  561          case DDS_FORMAT_L8A8:
  562             dst[2 * i + 0] = TO_LUMINANCE(r, g, b);
  563             dst[2 * i + 1] = a;
  564             break;
  565          case DDS_FORMAT_YCOCG:
  566          {
  567             int co = TO_YCOCG_CO(r, g, b) + 128;
  568             int cg = TO_YCOCG_CG(r, g, b) + 128;
  569             dst[4 * i + 0] = a;
  570             dst[4 * i + 1] = MAX(0, MIN(255, cg));
  571             dst[4 * i + 2] = MAX(0, MIN(255, co));
  572             dst[4 * i + 3] = TO_YCOCG_Y(r, g, b);
  573             break;
  574          }
  575          case DDS_FORMAT_AEXP:
  576             alpha_exp(&dst[4 * i], r, g, b, a);
  577             break;
  578          default:
  579             break;
  580       }
  581    }
  582 }
  583 
  584 static void convert_volume_pixels(unsigned char *dst, unsigned char *src,
  585                                   int format, int w, int h, int d, int bpp,
  586                                   unsigned char *palette, int mipmaps)
  587 {
  588    unsigned int i, num_pixels;
  589    unsigned char r, g, b, a;
  590 
  591    num_pixels = get_volume_mipmapped_size(w, h, d, 1, 0, mipmaps,
  592                                           DDS_COMPRESS_NONE);
  593 
  594    for(i = 0; i < num_pixels; ++i)
  595    {
  596       if(bpp == 1)
  597       {
  598          if(palette)
  599          {
  600             r = palette[3 * src[i] + 0];
  601             g = palette[3 * src[i] + 1];
  602             b = palette[3 * src[i] + 2];
  603          }
  604          else
  605             r = g = b = src[i];
  606 
  607          a = 255;
  608       }
  609       else if(bpp == 2)
  610       {
  611          r = g = b = src[2 * i];
  612          a = src[2 * i + 1];
  613       }
  614       else if(bpp == 3)
  615       {
  616          b = src[3 * i + 0];
  617          g = src[3 * i + 1];
  618          r = src[3 * i + 2];
  619          a = 255;
  620       }
  621       else
  622       {
  623          b = src[4 * i + 0];
  624          g = src[4 * i + 1];
  625          r = src[4 * i + 2];
  626          a = src[4 * i + 3];
  627       }
  628 
  629       switch(format)
  630       {
  631          case DDS_FORMAT_RGB8:
  632             dst[3 * i + 0] = b;
  633             dst[3 * i + 1] = g;
  634             dst[3 * i + 2] = r;
  635             break;
  636          case DDS_FORMAT_RGBA8:
  637             dst[4 * i + 0] = b;
  638             dst[4 * i + 1] = g;
  639             dst[4 * i + 2] = r;
  640             dst[4 * i + 3] = a;
  641             break;
  642          case DDS_FORMAT_BGR8:
  643             dst[4 * i + 0] = r;
  644             dst[4 * i + 1] = g;
  645             dst[4 * i + 2] = b;
  646             dst[4 * i + 3] = 255;
  647             break;
  648          case DDS_FORMAT_ABGR8:
  649             dst[4 * i + 0] = r;
  650             dst[4 * i + 1] = g;
  651             dst[4 * i + 2] = b;
  652             dst[4 * i + 3] = a;
  653             break;
  654          case DDS_FORMAT_R5G6B5:
  655             PUTL16(&dst[2 * i], TO_R5G6B5(r, g, b));
  656             break;
  657          case DDS_FORMAT_RGBA4:
  658             PUTL16(&dst[2 * i], TO_RGBA4(r, g, b, a));
  659             break;
  660          case DDS_FORMAT_RGB5A1:
  661             PUTL16(&dst[2 * i], TO_RGB5A1(r, g, b, a));
  662             break;
  663          case DDS_FORMAT_RGB10A2:
  664             PUTL32(&dst[4 * i], TO_RGB10A2(r, g, b, a));
  665             break;
  666          case DDS_FORMAT_R3G3B2:
  667             dst[i] = TO_R3G3B2(r, g, b);
  668             break;
  669          case DDS_FORMAT_A8:
  670             dst[i] = a;
  671             break;
  672          case DDS_FORMAT_L8:
  673             dst[i] = TO_LUMINANCE(r, g, b);
  674             break;
  675          case DDS_FORMAT_L8A8:
  676             dst[2 * i + 0] = TO_LUMINANCE(r, g, b);
  677             dst[2 * i + 1] = a;
  678             break;
  679          case DDS_FORMAT_YCOCG:
  680          {
  681             int co = TO_YCOCG_CO(r, g, b) + 128;
  682             int cg = TO_YCOCG_CG(r, g, b) + 128;
  683             dst[4 * i + 0] = a;
  684             dst[4 * i + 1] = MAX(0, MIN(255, cg));
  685             dst[4 * i + 2] = MAX(0, MIN(255, co));
  686             dst[4 * i + 3] = TO_YCOCG_Y(r, g, b);
  687             break;
  688          }
  689          case DDS_FORMAT_AEXP:
  690             alpha_exp(&dst[4 * i], r, g, b, a);
  691             break;
  692          default:
  693             break;
  694       }
  695    }
  696 }
  697 
  698 static void write_layer(FILE *fp, gint32 image_id, gint32 drawable_id,
  699                         int w, int h, int bpp, int fmtbpp, int mipmaps)
  700 {
  701    GimpDrawable *drawable;
  702    GimpPixelRgn rgn;
  703    GimpImageType basetype, type;
  704    unsigned char *src, *dst, *fmtdst, *tmp, c;
  705    unsigned char *palette = NULL;
  706    int i, x, y, size, fmtsize, offset, colors;
  707    int compression = dds_write_vals.compression;
  708 
  709    basetype = gimp_image_base_type(image_id);
  710    type = gimp_drawable_type(drawable_id);
  711 
  712    drawable = gimp_drawable_get(drawable_id);
  713    src = g_malloc(w * h * bpp);
  714    gimp_pixel_rgn_init(&rgn, drawable, 0, 0, w, h, 0, 0);
  715    gimp_pixel_rgn_get_rect(&rgn, src, 0, 0, w, h);
  716 
  717    if(basetype == GIMP_INDEXED)
  718    {
  719       palette = gimp_image_get_colormap(image_id, &colors);
  720 
  721       if(type == GIMP_INDEXEDA_IMAGE)
  722       {
  723          tmp = g_malloc(w * h);
  724          for(i = 0; i < w * h; ++i)
  725             tmp[i] = src[2 * i];
  726          g_free(src);
  727          src = tmp;
  728          bpp = 1;
  729       }
  730    }
  731 
  732    if(bpp >= 3)
  733       swap_rb(src, w * h, bpp);
  734 
  735    if(compression == DDS_COMPRESS_BC3N)
  736    {
  737       if(bpp != 4)
  738       {
  739          fmtsize = w * h * 4;
  740          fmtdst = g_malloc(fmtsize);
  741          convert_pixels(fmtdst, src, DDS_FORMAT_RGBA8, w, h, bpp,
  742                         palette, 1);
  743          g_free(src);
  744          src = fmtdst;
  745          bpp = 4;
  746       }
  747 
  748       for(y = 0; y < drawable->height; ++y)
  749       {
  750          for(x = 0; x < drawable->width; ++x)
  751          {
  752             c = src[y * (drawable->width * 4) + (x * 4) + 2];
  753             src[y * (drawable->width * 4) + (x * 4) + 2] =
  754                src[y * (drawable->width * 4) + (x * 4) + 3];
  755             src[y * (drawable->width * 4) + (x * 4) + 3] = c;
  756          }
  757       }
  758 
  759       compression = DDS_COMPRESS_BC3;
  760    }
  761 
  762    if(compression == DDS_COMPRESS_YCOCG ||
  763       compression == DDS_COMPRESS_YCOCGS) /* convert to YCoCG */
  764    {
  765       fmtsize = w * h * 4;
  766       fmtdst = g_malloc(fmtsize);
  767       convert_pixels(fmtdst, src, DDS_FORMAT_YCOCG, w, h, bpp,
  768                      palette, 1);
  769       g_free(src);
  770       src = fmtdst;
  771       bpp = 4;
  772 
  773       if(compression == DDS_COMPRESS_YCOCG)
  774          compression = DDS_COMPRESS_BC3;
  775    }
  776 
  777    if(compression == DDS_COMPRESS_AEXP)
  778    {
  779       fmtsize = w * h * 4;
  780       fmtdst = g_malloc(fmtsize);
  781       convert_pixels(fmtdst, src, DDS_FORMAT_AEXP, w, h, bpp,
  782                      palette, 1);
  783       g_free(src);
  784       src = fmtdst;
  785       bpp = 4;
  786 
  787       compression = DDS_COMPRESS_BC3;
  788    }
  789 
  790    if(compression == DDS_COMPRESS_NONE)
  791    {
  792       if(mipmaps > 1)
  793       {
  794          /* pre-convert indexed images to RGB for better quality mipmaps
  795             if a pixel format conversion is requested */
  796          if(dds_write_vals.format > DDS_FORMAT_DEFAULT && basetype == GIMP_INDEXED)
  797          {
  798             fmtsize = get_mipmapped_size(w, h, 3, 0, mipmaps, DDS_COMPRESS_NONE);
  799             fmtdst = g_malloc(fmtsize);
  800             convert_pixels(fmtdst, src, DDS_FORMAT_RGB8, w, h, bpp,
  801                            palette, 1);
  802             g_free(src);
  803             src = fmtdst;
  804             bpp = 3;
  805             palette = NULL;
  806          }
  807 
  808          size = get_mipmapped_size(w, h, bpp, 0, mipmaps, DDS_COMPRESS_NONE);
  809          dst = g_malloc(size);
  810          generate_mipmaps(dst, src, w, h, bpp, palette != NULL, mipmaps,
  811                           dds_write_vals.mipmap_filter);
  812 
  813          offset = 0;
  814 
  815          if(dds_write_vals.format > DDS_FORMAT_DEFAULT)
  816          {
  817             fmtsize = get_mipmapped_size(w, h, fmtbpp, 0, mipmaps,
  818                                          DDS_COMPRESS_NONE);
  819             fmtdst = g_malloc(fmtsize);
  820 
  821             convert_pixels(fmtdst, dst, dds_write_vals.format, w, h, bpp,
  822                            palette, mipmaps);
  823 
  824             g_free(dst);
  825             dst = fmtdst;
  826             bpp = fmtbpp;
  827          }
  828 
  829          for(i = 0; i < mipmaps; ++i)
  830          {
  831             size = get_mipmapped_size(w, h, bpp, i, 1, DDS_COMPRESS_NONE);
  832             fwrite(dst + offset, 1, size, fp);
  833             offset += size;
  834          }
  835 
  836          g_free(dst);
  837       }
  838       else
  839       {
  840          if(dds_write_vals.format > DDS_FORMAT_DEFAULT)
  841          {
  842             fmtdst = g_malloc(h * w * fmtbpp);
  843             convert_pixels(fmtdst, src, dds_write_vals.format, w, h, bpp,
  844                            palette, 1);
  845             g_free(src);
  846             src = fmtdst;
  847             bpp = fmtbpp;
  848          }
  849 
  850          fwrite(src, 1, h * w * bpp, fp);
  851       }
  852    }
  853    else
  854    {
  855       size = get_mipmapped_size(w, h, bpp, 0, mipmaps, compression);
  856 
  857       dst = g_malloc(size);
  858 
  859       if(basetype == GIMP_INDEXED)
  860       {
  861          fmtsize = get_mipmapped_size(w, h, 3, 0, mipmaps,
  862                                       DDS_COMPRESS_NONE);
  863          fmtdst = g_malloc(fmtsize);
  864          convert_pixels(fmtdst, src, DDS_FORMAT_RGB8, w, h, bpp,
  865                         palette, mipmaps);
  866          g_free(src);
  867          src = fmtdst;
  868          bpp = 3;
  869       }
  870 
  871       dxt_compress(dst, src, compression, w, h, bpp, mipmaps,
  872                    dds_write_vals.color_type, dds_write_vals.dither,
  873                    dds_write_vals.mipmap_filter);
  874 
  875       fwrite(dst, 1, size, fp);
  876 
  877       g_free(dst);
  878    }
  879 
  880    g_free(src);
  881 
  882    gimp_drawable_detach(drawable);
  883 }
  884 
  885 static void write_volume_mipmaps(FILE *fp, gint32 image_id, gint32 *layers,
  886                                  int w, int h, int d, int bpp, int fmtbpp,
  887                                  int mipmaps)
  888 {
  889    int i, size, offset, colors;
  890    unsigned char *src, *dst, *tmp, *fmtdst;
  891    unsigned char *palette = 0;
  892    GimpDrawable *drawable;
  893    GimpPixelRgn rgn;
  894    GimpImageType type;
  895 
  896    type = gimp_image_base_type(image_id);
  897 
  898    if(dds_write_vals.compression != DDS_COMPRESS_NONE) return;
  899 
  900    src = g_malloc(w * h * bpp * d);
  901 
  902    if(gimp_image_base_type(image_id) == GIMP_INDEXED)
  903       palette = gimp_image_get_colormap(image_id, &colors);
  904 
  905    offset = 0;
  906    for(i = 0; i < d; ++i)
  907    {
  908       drawable = gimp_drawable_get(layers[i]);
  909       gimp_pixel_rgn_init(&rgn, drawable, 0, 0, w, h, 0, 0);
  910       gimp_pixel_rgn_get_rect(&rgn, src + offset, 0, 0, w, h);
  911       offset += (w * h * bpp);
  912       gimp_drawable_detach(drawable);
  913    }
  914 
  915    if(gimp_drawable_type(layers[0]) == GIMP_INDEXEDA_IMAGE)
  916    {
  917       tmp = g_malloc(w * h * d);
  918       for(i = 0; i < w * h * d; ++i)
  919          tmp[i] = src[2 * i];
  920       g_free(src);
  921       src = tmp;
  922       bpp = 1;
  923    }
  924 
  925    if(bpp >= 3)
  926       swap_rb(src, w * h * d, bpp);
  927 
  928    /* pre-convert indexed images to RGB for better mipmaps if a
  929       pixel format conversion is requested */
  930    if(dds_write_vals.format > DDS_FORMAT_DEFAULT && type == GIMP_INDEXED)
  931    {
  932       size = get_volume_mipmapped_size(w, h, d, 3, 0, mipmaps,
  933                                        DDS_COMPRESS_NONE);
  934       dst = g_malloc(size);
  935       convert_volume_pixels(dst, src, DDS_FORMAT_RGB8, w, h, d, bpp,
  936                             palette, 1);
  937       g_free(src);
  938       src = dst;
  939       bpp = 3;
  940       palette = NULL;
  941    }
  942 
  943    size = get_volume_mipmapped_size(w, h, d, bpp, 0, mipmaps,
  944                                     dds_write_vals.compression);
  945 
  946    dst = g_malloc(size);
  947 
  948    offset = get_volume_mipmapped_size(w, h, d, bpp, 0, 1,
  949                                       dds_write_vals.compression);
  950 
  951    generate_volume_mipmaps(dst, src, w, h, d, bpp,
  952                            palette != NULL, mipmaps,
  953                            dds_write_vals.mipmap_filter);
  954 
  955    if(dds_write_vals.format > DDS_FORMAT_DEFAULT)
  956    {
  957       size = get_volume_mipmapped_size(w, h, d, fmtbpp, 0, mipmaps,
  958                                        dds_write_vals.compression);
  959       offset = get_volume_mipmapped_size(w, h, d, fmtbpp, 0, 1,
  960                                          dds_write_vals.compression);
  961       fmtdst = g_malloc(size);
  962 
  963       convert_volume_pixels(fmtdst, dst, dds_write_vals.format, w, h, d, bpp,
  964                             palette, mipmaps);
  965       g_free(dst);
  966       dst = fmtdst;
  967    }
  968 
  969    fwrite(dst + offset, 1, size, fp);
  970 
  971    g_free(src);
  972    g_free(dst);
  973 }
  974 
  975 static int write_image(FILE *fp, gint32 image_id, gint32 drawable_id)
  976 {
  977    GimpDrawable *drawable;
  978    GimpImageType drawable_type, basetype;
  979    GimpPixelRgn rgn;
  980    int i, w, h, bpp = 0, fmtbpp = 0, has_alpha = 0;
  981    int num_mipmaps;
  982    unsigned char hdr[DDS_HEADERSIZE];
  983    unsigned int flags = 0, pflags = 0, caps = 0, caps2 = 0, size = 0;
  984    unsigned int rmask = 0, gmask = 0, bmask = 0, amask = 0;
  985    unsigned int fourcc = 0;
  986    gint32 num_layers, *layers;
  987    guchar *cmap;
  988    gint colors;
  989    unsigned char zero[4] = {0, 0, 0, 0};
  990 
  991    layers = gimp_image_get_layers(image_id, &num_layers);
  992 
  993    drawable = gimp_drawable_get(drawable_id);
  994 
  995    w = drawable->width;
  996    h = drawable->height;
  997 
  998    basetype = gimp_image_base_type(image_id);
  999    drawable_type = gimp_drawable_type(drawable_id);
 1000    gimp_pixel_rgn_init(&rgn, drawable, 0, 0, w, h, 0, 0);
 1001 
 1002    if((dds_write_vals.compression != DDS_COMPRESS_NONE) &&
 1003       !(IS_MUL4(w) && IS_MUL4(h)))
 1004    {
 1005       dds_write_vals.compression = DDS_COMPRESS_NONE;
 1006       g_message("DDS: Cannot compress images whose dimensions are not multiples of 4.\n"
 1007                 "Saved image will not be compressed.");
 1008    }
 1009 
 1010    if((dds_write_vals.compression != DDS_COMPRESS_NONE) &&
 1011       dds_write_vals.mipmaps &&
 1012       !(IS_POW2(w) && IS_POW2(h)))
 1013    {
 1014       dds_write_vals.mipmaps = 0;
 1015       g_message("DDS: Cannot generate mipmaps for compressed images whose dimensions are not powers of 2.\n"
 1016                 "Saved image will not have mipmaps generated.");
 1017    }
 1018 
 1019    switch(drawable_type)
 1020    {
 1021       case GIMP_RGB_IMAGE:      bpp = 3; break;
 1022       case GIMP_RGBA_IMAGE:     bpp = 4; break;
 1023       case GIMP_GRAY_IMAGE:     bpp = 1; break;
 1024       case GIMP_GRAYA_IMAGE:    bpp = 2; break;
 1025       case GIMP_INDEXED_IMAGE:  bpp = 1; break;
 1026       case GIMP_INDEXEDA_IMAGE: bpp = 2; break;
 1027       default:
 1028          break;
 1029    }
 1030 
 1031    if(dds_write_vals.format > DDS_FORMAT_DEFAULT)
 1032    {
 1033       for(i = 0; ; ++i)
 1034       {
 1035          if(format_info[i].format == dds_write_vals.format)
 1036          {
 1037             fmtbpp = format_info[i].bpp;
 1038             has_alpha = format_info[i].alpha;
 1039             rmask = format_info[i].rmask;
 1040             gmask = format_info[i].gmask;
 1041             bmask = format_info[i].bmask;
 1042             amask = format_info[i].amask;
 1043             break;
 1044          }
 1045       }
 1046    }
 1047    else if(bpp == 1)
 1048    {
 1049       if(basetype == GIMP_INDEXED)
 1050       {
 1051          fmtbpp = 1;
 1052          has_alpha = 0;
 1053          rmask = bmask = gmask = amask = 0;
 1054       }
 1055       else
 1056       {
 1057          fmtbpp = 1;
 1058          has_alpha = 0;
 1059          rmask = 0x000000ff;
 1060          gmask = bmask = amask = 0;
 1061       }
 1062    }
 1063    else if(bpp == 2)
 1064    {
 1065       if(basetype == GIMP_INDEXED)
 1066       {
 1067          fmtbpp = 1;
 1068          has_alpha = 0;
 1069          rmask = gmask = bmask = amask = 0;
 1070       }
 1071       else
 1072       {
 1073          fmtbpp = 2;
 1074          has_alpha = 1;
 1075          rmask = 0x000000ff;
 1076          gmask = 0x000000ff;
 1077          bmask = 0x000000ff;
 1078          amask = 0x0000ff00;
 1079       }
 1080    }
 1081    else if(bpp == 3)
 1082    {
 1083       fmtbpp = 3;
 1084       rmask = 0x00ff0000;
 1085       gmask = 0x0000ff00;
 1086       bmask = 0x000000ff;
 1087       amask = 0x00000000;
 1088    }
 1089    else
 1090    {
 1091       fmtbpp = 4;
 1092       has_alpha = 1;
 1093       rmask = 0x00ff0000;
 1094       gmask = 0x0000ff00;
 1095       bmask = 0x000000ff;
 1096       amask = 0xff000000;
 1097    }
 1098 
 1099    memset(hdr, 0, DDS_HEADERSIZE);
 1100 
 1101    memcpy(hdr, "DDS ", 4);
 1102    PUTL32(hdr + 4, 124);
 1103    PUTL32(hdr + 12, h);
 1104    PUTL32(hdr + 16, w);
 1105    PUTL32(hdr + 76, 32);
 1106    PUTL32(hdr + 88, fmtbpp << 3);
 1107    PUTL32(hdr + 92,  rmask);
 1108    PUTL32(hdr + 96,  gmask);
 1109    PUTL32(hdr + 100, bmask);
 1110    PUTL32(hdr + 104, amask);
 1111 
 1112    flags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
 1113 
 1114    caps = DDSCAPS_TEXTURE;
 1115    if(dds_write_vals.mipmaps)
 1116    {
 1117       flags |= DDSD_MIPMAPCOUNT;
 1118       caps |= (DDSCAPS_COMPLEX | DDSCAPS_MIPMAP);
 1119       num_mipmaps = get_num_mipmaps(w, h);
 1120    }
 1121    else
 1122       num_mipmaps = 1;
 1123 
 1124    if(dds_write_vals.savetype == DDS_SAVE_CUBEMAP && is_cubemap)
 1125    {
 1126       caps |= DDSCAPS_COMPLEX;
 1127       caps2 |= (DDSCAPS2_CUBEMAP |
 1128                 DDSCAPS2_CUBEMAP_POSITIVEX |
 1129                 DDSCAPS2_CUBEMAP_NEGATIVEX |
 1130                 DDSCAPS2_CUBEMAP_POSITIVEY |
 1131                 DDSCAPS2_CUBEMAP_NEGATIVEY |
 1132                 DDSCAPS2_CUBEMAP_POSITIVEZ |
 1133                 DDSCAPS2_CUBEMAP_NEGATIVEZ);
 1134    }
 1135    else if(dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP && is_volume)
 1136    {
 1137       PUTL32(hdr + 24, num_layers);
 1138       flags |= DDSD_DEPTH;
 1139       caps |= DDSCAPS_COMPLEX;
 1140       caps2 |= DDSCAPS2_VOLUME;
 1141    }
 1142 
 1143    PUTL32(hdr + 28, num_mipmaps);
 1144    PUTL32(hdr + 108, caps);
 1145    PUTL32(hdr + 112, caps2);
 1146 
 1147    if(dds_write_vals.compression == DDS_COMPRESS_NONE)
 1148    {
 1149       flags |= DDSD_PITCH;
 1150 
 1151       if(dds_write_vals.format > DDS_FORMAT_DEFAULT)
 1152       {
 1153          if(dds_write_vals.format == DDS_FORMAT_A8)
 1154             pflags |= DDPF_ALPHA;
 1155          else
 1156          {
 1157             if((fmtbpp == 1 || dds_write_vals.format == DDS_FORMAT_L8A8) &&
 1158                (dds_write_vals.format != DDS_FORMAT_R3G3B2))
 1159                pflags |= DDPF_LUMINANCE;
 1160             else
 1161                pflags |= DDPF_RGB;
 1162          }
 1163       }
 1164       else
 1165       {
 1166          if(bpp == 1)
 1167          {
 1168             if(basetype == GIMP_INDEXED)
 1169                pflags |= DDPF_PALETTEINDEXED8;
 1170             else
 1171                pflags |= DDPF_LUMINANCE;
 1172          }
 1173          else if(bpp == 2 && basetype == GIMP_INDEXED)
 1174             pflags |= DDPF_PALETTEINDEXED8;
 1175          else
 1176             pflags |= DDPF_RGB;
 1177       }
 1178 
 1179       if(has_alpha) pflags |= DDPF_ALPHAPIXELS;
 1180 
 1181       PUTL32(hdr + 8, flags);
 1182       PUTL32(hdr + 20, w * fmtbpp);
 1183       PUTL32(hdr + 80, pflags);
 1184    }
 1185    else
 1186    {
 1187       flags |= DDSD_LINEARSIZE;
 1188       PUTL32(hdr + 8, flags);
 1189       PUTL32(hdr + 80, DDPF_FOURCC);
 1190       switch(dds_write_vals.compression)
 1191       {
 1192          case DDS_COMPRESS_BC1:
 1193             fourcc = FOURCC('D','X','T','1'); break;
 1194          case DDS_COMPRESS_BC2:
 1195             fourcc = FOURCC('D','X','T','3'); break;
 1196          case DDS_COMPRESS_BC3:
 1197          case DDS_COMPRESS_BC3N:
 1198          case DDS_COMPRESS_YCOCG:
 1199          case DDS_COMPRESS_YCOCGS:
 1200          case DDS_COMPRESS_AEXP:
 1201             fourcc = FOURCC('D','X','T','5'); break;
 1202          case DDS_COMPRESS_BC4:
 1203             fourcc = FOURCC('A','T','I','1'); break;
 1204          case DDS_COMPRESS_BC5:
 1205             fourcc = FOURCC('A','T','I','2'); break;
 1206       }
 1207       PUTL32(hdr + 84, fourcc);
 1208 
 1209       size = ((w + 3) >> 2) * ((h + 3) >> 2);
 1210       if(dds_write_vals.compression == DDS_COMPRESS_BC1 ||
 1211          dds_write_vals.compression == DDS_COMPRESS_BC4)
 1212          size *= 8;
 1213       else
 1214          size *= 16;
 1215 
 1216       PUTL32(hdr + 20, size);
 1217    }
 1218 
 1219    fwrite(hdr, DDS_HEADERSIZE, 1, fp);
 1220 
 1221    if(basetype == GIMP_INDEXED && dds_write_vals.format == DDS_FORMAT_DEFAULT &&
 1222       dds_write_vals.compression == DDS_COMPRESS_NONE)
 1223    {
 1224       cmap = gimp_image_get_colormap(image_id, &colors);
 1225       for(i = 0; i < colors; ++i)
 1226       {
 1227          fwrite(&cmap[3 * i], 1, 3, fp);
 1228          if(i == dds_write_vals.transindex)
 1229             fputc(0, fp);
 1230          else
 1231             fputc(255, fp);
 1232       }
 1233       for(; i < 256; ++i)
 1234          fwrite(zero, 1, 4, fp);
 1235    }
 1236 
 1237    if(dds_write_vals.savetype == DDS_SAVE_CUBEMAP)
 1238    {
 1239       for(i = 0; i < 6; ++i)
 1240       {
 1241          write_layer(fp, image_id, cubemap_faces[i], w, h, bpp, fmtbpp,
 1242                      num_mipmaps);
 1243          if(interactive_dds)
 1244             gimp_progress_update((float)(i + 1) / 6.0);
 1245       }
 1246    }
 1247    else if(dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
 1248    {
 1249       for(i = 0; i < num_layers; ++i)
 1250       {
 1251          write_layer(fp, image_id, layers[i], w, h, bpp, fmtbpp, 1);
 1252          if(interactive_dds)
 1253             gimp_progress_update((float)i / (float)num_layers);
 1254       }
 1255 
 1256       if(num_mipmaps > 1)
 1257          write_volume_mipmaps(fp, image_id, layers, w, h, num_layers,
 1258                               bpp, fmtbpp, num_mipmaps);
 1259    }
 1260    else
 1261    {
 1262       write_layer(fp, image_id, drawable_id, w, h, bpp, fmtbpp,
 1263                   num_mipmaps);
 1264    }
 1265 
 1266    if(interactive_dds)
 1267       gimp_progress_update(1.0);
 1268 
 1269    gimp_drawable_detach(drawable);
 1270 
 1271    return(1);
 1272 }
 1273 
 1274 static GtkWidget *string_value_combo_new(string_value_t *strings,
 1275                                          int active_value)
 1276 {
 1277    GtkWidget *opt;
 1278    GtkCellRenderer *renderer;
 1279    GtkListStore *store;
 1280    GtkTreeIter iter;
 1281    int i, active = 0;
 1282 
 1283    store = gtk_list_store_new(3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN);
 1284    for(i = 0; strings[i].string; ++i)
 1285    {
 1286       if(strings[i].value == active_value) active = i;
 1287       gtk_list_store_append(store, &iter);
 1288       gtk_list_store_set(store, &iter,
 1289                          0, strings[i].value,
 1290                          1, strings[i].string,
 1291                          2, 1,
 1292                          -1);
 1293    }
 1294 
 1295    renderer = gtk_cell_renderer_text_new();
 1296 
 1297    opt = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
 1298    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(opt), renderer, 1);
 1299    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(opt), renderer,
 1300                                   "text", COMBO_STRING,
 1301                                   "sensitive", COMBO_SENSITIVE,
 1302                                   NULL);
 1303 
 1304    gtk_combo_box_set_active(GTK_COMBO_BOX(opt), active);
 1305 
 1306    g_object_unref(store);
 1307 
 1308    return(opt);
 1309 }
 1310 
 1311 static void string_value_combo_selected(GtkWidget *widget, gpointer data)
 1312 {
 1313    int value;
 1314    GtkTreeIter iter;
 1315    GtkTreeModel *model;
 1316 
 1317    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
 1318    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter);
 1319    gtk_tree_model_get(model, &iter, COMBO_VALUE, &value, -1);
 1320 
 1321    *((int *)data) = value;
 1322 }
 1323 
 1324 static void string_value_combo_set_item_sensitive(GtkWidget *widget,
 1325                                                   int value, int sensitive)
 1326 {
 1327    GtkTreeIter iter;
 1328    GtkTreeModel *model;
 1329    int val;
 1330 
 1331    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
 1332    gtk_tree_model_get_iter_first(model, &iter);
 1333    do
 1334    {
 1335       gtk_tree_model_get(model, &iter, COMBO_VALUE, &val, -1);
 1336       if(val == value)
 1337       {
 1338          gtk_list_store_set(GTK_LIST_STORE(model), &iter,
 1339                             COMBO_SENSITIVE, sensitive, -1);
 1340          break;
 1341       }
 1342    } while(gtk_tree_model_iter_next(model, &iter));
 1343 }
 1344 
 1345 static void string_value_combo_set_active(GtkWidget *widget,
 1346                                           int value)
 1347 {
 1348    GtkTreeIter iter;
 1349    GtkTreeModel *model;
 1350    int val;
 1351 
 1352    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
 1353    gtk_tree_model_get_iter_first(model, &iter);
 1354    do
 1355    {
 1356       gtk_tree_model_get(model, &iter, COMBO_VALUE, &val, -1);
 1357       if(val == value)
 1358       {
 1359          gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &iter);
 1360          break;
 1361       }
 1362    } while(gtk_tree_model_iter_next(model, &iter));
 1363 }
 1364 
 1365 static void save_dialog_response(GtkWidget *widget, gint response_id,
 1366                                  gpointer data)
 1367 {
 1368    switch(response_id)
 1369    {
 1370       case GTK_RESPONSE_OK:
 1371          runme = 1;
 1372       default:
 1373          gtk_widget_destroy(widget);
 1374          break;
 1375    }
 1376 }
 1377 
 1378 static void compression_selected(GtkWidget *widget, gpointer data)
 1379 {
 1380    GtkTreeIter iter;
 1381    GtkTreeModel *model;
 1382    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
 1383    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter);
 1384    gtk_tree_model_get(model, &iter, COMBO_VALUE,
 1385                       &dds_write_vals.compression, -1);
 1386 
 1387    gtk_widget_set_sensitive(format_opt, dds_write_vals.compression == DDS_COMPRESS_NONE);
 1388    gtk_widget_set_sensitive(color_type_opt,
 1389                             dds_write_vals.compression != DDS_COMPRESS_NONE &&
 1390                             dds_write_vals.compression != DDS_COMPRESS_BC4 &&
 1391                             dds_write_vals.compression != DDS_COMPRESS_BC5 &&
 1392                             dds_write_vals.compression != DDS_COMPRESS_YCOCGS);
 1393    gtk_widget_set_sensitive(dither_chk,
 1394                             dds_write_vals.compression != DDS_COMPRESS_NONE &&
 1395                             dds_write_vals.compression != DDS_COMPRESS_BC4 &&
 1396                             dds_write_vals.compression != DDS_COMPRESS_BC5 &&
 1397                             dds_write_vals.compression != DDS_COMPRESS_YCOCGS);
 1398 }
 1399 
 1400 static void savetype_selected(GtkWidget *widget, gpointer data)
 1401 {
 1402    dds_write_vals.savetype = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
 1403 
 1404    switch(dds_write_vals.savetype)
 1405    {
 1406       case DDS_SAVE_SELECTED_LAYER:
 1407       case DDS_SAVE_CUBEMAP:
 1408          gtk_widget_set_sensitive(compress_opt, 1);
 1409          string_value_combo_set_item_sensitive(mipmap_filter_opt,
 1410                                                DDS_MIPMAP_LANCZOS, 1);
 1411          break;
 1412       case DDS_SAVE_VOLUMEMAP:
 1413          dds_write_vals.compression = DDS_COMPRESS_NONE;
 1414          gtk_combo_box_set_active(GTK_COMBO_BOX(compress_opt), DDS_COMPRESS_NONE);
 1415          gtk_widget_set_sensitive(compress_opt, 0);
 1416          if(dds_write_vals.mipmap_filter == DDS_MIPMAP_LANCZOS)
 1417             dds_write_vals.mipmap_filter = DDS_MIPMAP_DEFAULT;
 1418          string_value_combo_set_active(mipmap_filter_opt,
 1419                                        dds_write_vals.mipmap_filter);
 1420          string_value_combo_set_item_sensitive(mipmap_filter_opt,
 1421                                                DDS_MIPMAP_LANCZOS, 0);
 1422          break;
 1423    }
 1424 }
 1425 
 1426 static void toggle_clicked(GtkWidget *widget, gpointer data)
 1427 {
 1428    int *flag = (int*)data;
 1429    (*flag) = !(*flag);
 1430 }
 1431 
 1432 static void mipmaps_clicked(GtkWidget *widget, gpointer data)
 1433 {
 1434    dds_write_vals.mipmaps = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
 1435    gtk_widget_set_sensitive(mipmap_filter_opt, dds_write_vals.mipmaps);
 1436 }
 1437 
 1438 static void transindex_clicked(GtkWidget *widget, gpointer data)
 1439 {
 1440    GtkWidget *spin = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), "spin"));
 1441 
 1442    if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
 1443    {
 1444       dds_write_vals.transindex = 0;
 1445       gtk_widget_set_sensitive(spin, 1);
 1446       gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0);
 1447    }
 1448    else
 1449    {
 1450       gtk_widget_set_sensitive(spin, 0);
 1451       dds_write_vals.transindex = -1;
 1452    }
 1453 }
 1454 
 1455 static void transindex_changed(GtkWidget *widget, gpointer data)
 1456 {
 1457    dds_write_vals.transindex = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
 1458 }
 1459 
 1460 static void adv_opt_expanded(GtkWidget *widget, gpointer data)
 1461 {
 1462    dds_write_vals.show_adv_opt = !gtk_expander_get_expanded(GTK_EXPANDER(widget));
 1463 }
 1464 
 1465 static gint save_dialog(gint32 image_id, gint32 drawable_id)
 1466 {
 1467    GtkWidget *dlg;
 1468    GtkWidget *vbox, *hbox;
 1469    GtkWidget *table;
 1470    GtkWidget *label;
 1471    GtkWidget *opt;
 1472    GtkWidget *check;
 1473    GtkWidget *spin;
 1474    GtkWidget *expander;
 1475    GimpImageType type, basetype;
 1476    int w, h;
 1477 
 1478    if(is_cubemap)
 1479       dds_write_vals.savetype = DDS_SAVE_CUBEMAP;
 1480    else if(is_volume)
 1481       dds_write_vals.savetype = DDS_SAVE_VOLUMEMAP;
 1482    else
 1483       dds_write_vals.savetype = DDS_SAVE_SELECTED_LAYER;
 1484 
 1485    basetype = gimp_image_base_type(image_id);
 1486    type = gimp_drawable_type(drawable_id);
 1487 
 1488    w = gimp_image_width(image_id);
 1489    h = gimp_image_height(image_id);
 1490 
 1491    dlg = gimp_dialog_new("Save as DDS", "dds", NULL, GTK_WIN_POS_MOUSE,
 1492                          gimp_standard_help_func, SAVE_PROC,
 1493                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 1494                          GTK_STOCK_OK, GTK_RESPONSE_OK,
 1495                          NULL);
 1496 
 1497    gtk_signal_connect(GTK_OBJECT(dlg), "response",
 1498                       GTK_SIGNAL_FUNC(save_dialog_response),
 1499                       0);
 1500    gtk_signal_connect(GTK_OBJECT(dlg), "destroy",
 1501                       GTK_SIGNAL_FUNC(gtk_main_quit),
 1502                       0);
 1503 
 1504    vbox = gtk_vbox_new(0, 8);
 1505    gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
 1506    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), vbox, 1, 1, 0);
 1507    gtk_widget_show(vbox);
 1508 
 1509    table = gtk_table_new(3, 2, 0);
 1510    gtk_widget_show(table);
 1511    gtk_box_pack_start(GTK_BOX(vbox), table, 1, 1, 0);
 1512    gtk_table_set_row_spacings(GTK_TABLE(table), 8);
 1513    gtk_table_set_col_spacings(GTK_TABLE(table), 8);
 1514 
 1515    label = gtk_label_new("Compression:");
 1516    gtk_widget_show(label);
 1517    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
 1518                     (GtkAttachOptions)(GTK_FILL),
 1519                     (GtkAttachOptions)(0), 0, 0);
 1520    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 1521 
 1522 
 1523    opt = string_value_combo_new(compression_strings,
 1524                                 dds_write_vals.compression);
 1525    gtk_widget_show(opt);
 1526    gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 0, 1,
 1527                     (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
 1528                     (GtkAttachOptions)(GTK_EXPAND), 0, 0);
 1529 
 1530    gtk_signal_connect(GTK_OBJECT(opt), "changed",
 1531                       GTK_SIGNAL_FUNC(compression_selected), 0);
 1532 
 1533    compress_opt = opt;
 1534 
 1535    label = gtk_label_new("Format:");
 1536    gtk_widget_show(label);
 1537    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
 1538                     (GtkAttachOptions)(GTK_FILL),
 1539                     (GtkAttachOptions)(0), 0, 0);
 1540    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 1541 
 1542    opt = string_value_combo_new(format_strings, dds_write_vals.format);
 1543    gtk_widget_show(opt);
 1544    gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 1, 2,
 1545                     (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
 1546                     (GtkAttachOptions)(GTK_EXPAND), 0, 0);
 1547 
 1548    gtk_signal_connect(GTK_OBJECT(opt), "changed",
 1549                       GTK_SIGNAL_FUNC(string_value_combo_selected),
 1550                       &dds_write_vals.format);
 1551 
 1552    gtk_widget_set_sensitive(opt, dds_write_vals.compression == DDS_COMPRESS_NONE);
 1553 
 1554    format_opt = opt;
 1555 
 1556    label = gtk_label_new("Save:");
 1557    gtk_widget_show(label);
 1558    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
 1559                     (GtkAttachOptions)(GTK_FILL),
 1560                     (GtkAttachOptions)(0), 0, 0);
 1561    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 1562 
 1563    opt = string_value_combo_new(save_type_strings, dds_write_vals.savetype);
 1564    gtk_widget_show(opt);
 1565    gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 2, 3,
 1566                     (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
 1567                     (GtkAttachOptions)(GTK_EXPAND), 0, 0);
 1568 
 1569    gtk_signal_connect(GTK_OBJECT(opt), "changed",
 1570                       GTK_SIGNAL_FUNC(savetype_selected), 0);
 1571 
 1572    string_value_combo_set_item_sensitive(opt, DDS_SAVE_CUBEMAP, is_cubemap);
 1573    string_value_combo_set_item_sensitive(opt, DDS_SAVE_VOLUMEMAP, is_volume);
 1574 
 1575    gtk_widget_set_sensitive(opt, is_cubemap || is_volume);
 1576 
 1577    check = gtk_check_button_new_with_label("Generate mipmaps");
 1578    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), dds_write_vals.mipmaps);
 1579    gtk_box_pack_start(GTK_BOX(vbox), check, 0, 0, 0);
 1580    gtk_signal_connect(GTK_OBJECT(check), "clicked",
 1581                       GTK_SIGNAL_FUNC(mipmaps_clicked), 0);
 1582    gtk_widget_show(check);
 1583    mipmap_check = check;
 1584 
 1585    if(is_volume && dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
 1586    {
 1587       dds_write_vals.compression = DDS_COMPRESS_NONE;
 1588       string_value_combo_set_active(compress_opt, DDS_COMPRESS_NONE);
 1589       gtk_widget_set_sensitive(compress_opt, 0);
 1590    }
 1591 
 1592    hbox = gtk_hbox_new(0, 8);
 1593    gtk_box_pack_start(GTK_BOX(vbox), hbox, 1, 1, 0);
 1594    gtk_widget_show(hbox);
 1595 
 1596    check = gtk_check_button_new_with_label("Transparent index:");
 1597    gtk_box_pack_start(GTK_BOX(hbox), check, 0, 0, 0);
 1598    gtk_signal_connect(GTK_OBJECT(check), "clicked",
 1599                       GTK_SIGNAL_FUNC(transindex_clicked), 0);
 1600    gtk_widget_show(check);
 1601 
 1602    spin = gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 255, 1, 1, 0)), 1, 0);
 1603    gtk_box_pack_start(GTK_BOX(hbox), spin, 1, 1, 0);
 1604    gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin),
 1605                                      GTK_UPDATE_IF_VALID);
 1606    gtk_signal_connect(GTK_OBJECT(spin), "value_changed",
 1607                       GTK_SIGNAL_FUNC(transindex_changed), 0);
 1608    gtk_widget_show(spin);
 1609 
 1610    g_object_set_data(G_OBJECT(check), "spin", spin);
 1611 
 1612    if(basetype != GIMP_INDEXED)
 1613    {
 1614       gtk_widget_set_sensitive(check, 0);
 1615       gtk_widget_set_sensitive(spin, 0);
 1616    }
 1617    else if(dds_write_vals.transindex < 0)
 1618    {
 1619       gtk_widget_set_sensitive(spin, 0);
 1620    }
 1621    else if(dds_write_vals.transindex >= 0)
 1622    {
 1623       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), 1);
 1624       gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), dds_write_vals.transindex);
 1625    }
 1626 
 1627    expander = gtk_expander_new("<b>Advanced Options</b>");
 1628    gtk_expander_set_use_markup(GTK_EXPANDER(expander), 1);
 1629    gtk_expander_set_expanded(GTK_EXPANDER(expander), dds_write_vals.show_adv_opt);
 1630    gtk_expander_set_spacing(GTK_EXPANDER(expander), 8);
 1631    gtk_signal_connect(GTK_OBJECT(expander), "activate",
 1632                       GTK_SIGNAL_FUNC(adv_opt_expanded), 0);
 1633    gtk_box_pack_start(GTK_BOX(vbox), expander, 1, 1, 0);
 1634    gtk_widget_show(expander);
 1635 
 1636    table = gtk_table_new(3, 2, 0);
 1637    gtk_table_set_row_spacings(GTK_TABLE(table), 8);
 1638    gtk_table_set_col_spacings(GTK_TABLE(table), 8);
 1639    gtk_container_add(GTK_CONTAINER(expander), table);
 1640    gtk_widget_show(table);
 1641 
 1642    label = gtk_label_new("Color selection:");
 1643    gtk_widget_show(label);
 1644    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
 1645                     (GtkAttachOptions)(GTK_FILL),
 1646                     (GtkAttachOptions)(0), 0, 0);
 1647    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 1648 
 1649    opt = string_value_combo_new(color_type_strings, dds_write_vals.color_type);
 1650    gtk_widget_show(opt);
 1651    gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 0, 1,
 1652                     (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
 1653                     (GtkAttachOptions)(GTK_EXPAND), 0, 0);
 1654 
 1655    gtk_signal_connect(GTK_OBJECT(opt), "changed",
 1656                       GTK_SIGNAL_FUNC(string_value_combo_selected),
 1657                       &dds_write_vals.color_type);
 1658 
 1659    color_type_opt = opt;
 1660 
 1661    check = gtk_check_button_new_with_label("Use dithering");
 1662    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), dds_write_vals.dither);
 1663    gtk_table_attach(GTK_TABLE(table), check, 0, 2, 1, 2,
 1664                     (GtkAttachOptions)(GTK_FILL),
 1665                     (GtkAttachOptions)(0), 0, 0);
 1666    gtk_signal_connect(GTK_OBJECT(check), "clicked",
 1667                       GTK_SIGNAL_FUNC(toggle_clicked), &dds_write_vals.dither);
 1668    gtk_widget_show(check);
 1669 
 1670    dither_chk = check;
 1671 
 1672    label = gtk_label_new("Mipmap filter:");
 1673    gtk_widget_show(label);
 1674    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
 1675                     (GtkAttachOptions)(GTK_FILL),
 1676                     (GtkAttachOptions)(0), 0, 0);
 1677    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 1678 
 1679    opt = string_value_combo_new(mipmap_filter_strings,
 1680                                 dds_write_vals.mipmap_filter);
 1681    gtk_widget_show(opt);
 1682    gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 2, 3,
 1683                     (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
 1684                     (GtkAttachOptions)(GTK_EXPAND), 0, 0);
 1685 
 1686    if(is_volume && dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
 1687       string_value_combo_set_item_sensitive(opt, DDS_MIPMAP_LANCZOS, 0);
 1688 
 1689    gtk_signal_connect(GTK_OBJECT(opt), "changed",
 1690                       GTK_SIGNAL_FUNC(string_value_combo_selected),
 1691                       &dds_write_vals.mipmap_filter);
 1692 
 1693    mipmap_filter_opt = opt;
 1694 
 1695    gtk_widget_set_sensitive(color_type_opt,
 1696                             dds_write_vals.compression != DDS_COMPRESS_NONE &&
 1697                             dds_write_vals.compression != DDS_COMPRESS_BC4 &&
 1698                             dds_write_vals.compression != DDS_COMPRESS_BC5 &&
 1699                             dds_write_vals.compression != DDS_COMPRESS_YCOCGS);
 1700    gtk_widget_set_sensitive(dither_chk,
 1701                             dds_write_vals.compression != DDS_COMPRESS_NONE &&
 1702                             dds_write_vals.compression != DDS_COMPRESS_BC4 &&
 1703                             dds_write_vals.compression != DDS_COMPRESS_BC5 &&
 1704                             dds_write_vals.compression != DDS_COMPRESS_YCOCGS);
 1705    gtk_widget_set_sensitive(mipmap_filter_opt, dds_write_vals.mipmaps);
 1706 
 1707    gtk_widget_show(dlg);
 1708 
 1709    runme = 0;
 1710 
 1711    gtk_main();
 1712 
 1713    return(runme);
 1714 }

ViewVC Help
Powered by ViewVC 1.0.4