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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

ViewVC Help
Powered by ViewVC 1.0.4