[gimp-dds] / tags / release-2.0.5 / ddswrite.c Repository:
ViewVC logotype

View of /tags/release-2.0.5/ddswrite.c

Parent Directory Parent Directory | Revision Log Revision Log


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

ViewVC Help
Powered by ViewVC 1.0.4