2 * Copyright (C) 2011 Rudolf Polzer All Rights Reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * RUDOLF POLZER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 #define S2TC_LICENSE_IDENTIFIER s2tc_compress_license
22 #include "s2tc_license.h"
32 #ifdef ENABLE_RUNTIME_LINKING
35 typedef void (tx_compress_dxtn_t)(GLint srccomps, GLint width, GLint height,
36 const GLubyte *srcPixData, GLenum destformat,
37 GLubyte *dest, GLint dstRowStride);
38 tx_compress_dxtn_t *tx_compress_dxtn = NULL;
39 bool load_libraries(const char *n)
41 void *l = dlopen(n, RTLD_NOW);
44 fprintf(stderr, "Cannot load library: %s\n", dlerror());
47 tx_compress_dxtn = (tx_compress_dxtn_t *) dlsym(l, "tx_compress_dxtn");
50 fprintf(stderr, "The selected libtxc_dxtn.so does not contain all required symbols.");
60 /* START stuff that originates from image.c in DarkPlaces */
62 Thu Jul 14 21:58:02 CEST 2011
63 21:25:25 @divVerent | LordHavoc: http://paste.pocoo.org/show/438804/
64 21:25:31 @divVerent | can I have this code under a MIT-style license?
65 21:59:58 @LordHavoc | divVerent: yeah, have them under any license you want
66 22:00:11 @LordHavoc | divVerent: my attitude toward licenses is generally "WTFPL would be preferably if I could use it" :P
67 22:04:01 @divVerent | LordHavoc: okay, thanks
70 int image_width, image_height;
72 typedef struct _TargaHeader
74 unsigned char id_length, colormap_type, image_type;
75 unsigned short colormap_index, colormap_length;
76 unsigned char colormap_size;
77 unsigned short x_origin, y_origin, width, height;
78 unsigned char pixel_size, attributes;
82 unsigned char *LoadTGA_BGRA (const unsigned char *f, int filesize)
84 int x, y, pix_inc, row_inci, runlen, alphabits;
85 unsigned char *image_buffer;
86 unsigned int *pixbufi;
87 const unsigned char *fin, *enddata;
88 TargaHeader targa_header;
89 unsigned int palettei[256];
100 enddata = f + filesize;
102 targa_header.id_length = f[0];
103 targa_header.colormap_type = f[1];
104 targa_header.image_type = f[2];
106 targa_header.colormap_index = f[3] + f[4] * 256;
107 targa_header.colormap_length = f[5] + f[6] * 256;
108 targa_header.colormap_size = f[7];
109 targa_header.x_origin = f[8] + f[9] * 256;
110 targa_header.y_origin = f[10] + f[11] * 256;
111 targa_header.width = image_width = f[12] + f[13] * 256;
112 targa_header.height = image_height = f[14] + f[15] * 256;
113 targa_header.pixel_size = f[16];
114 targa_header.attributes = f[17];
116 if (image_width > 32768 || image_height > 32768 || image_width <= 0 || image_height <= 0)
118 printf("LoadTGA: invalid size\n");
122 /* advance to end of header */
125 /* skip TARGA image comment (usually 0 bytes) */
126 fin += targa_header.id_length;
128 /* read/skip the colormap if present (note: according to the TARGA spec it */
129 /* can be present even on 1color or greyscale images, just not used by */
130 /* the image data) */
131 if (targa_header.colormap_type)
133 if (targa_header.colormap_length > 256)
135 printf("LoadTGA: only up to 256 colormap_length supported\n");
138 if (targa_header.colormap_index)
140 printf("LoadTGA: colormap_index not supported\n");
143 if (targa_header.colormap_size == 24)
145 for (x = 0;x < targa_header.colormap_length;x++)
151 palettei[x] = bgra.i;
154 else if (targa_header.colormap_size == 32)
156 memcpy(palettei, fin, targa_header.colormap_length*4);
157 fin += targa_header.colormap_length * 4;
161 printf("LoadTGA: Only 32 and 24 bit colormap_size supported\n");
166 /* check our pixel_size restrictions according to image_type */
167 switch (targa_header.image_type & ~8)
170 if (targa_header.pixel_size != 24 && targa_header.pixel_size != 32)
172 printf("LoadTGA: only 24bit and 32bit pixel sizes supported for type 2 and type 10 images\n");
177 /* set up a palette to make the loader easier */
178 for (x = 0;x < 256;x++)
180 bgra.b[0] = bgra.b[1] = bgra.b[2] = x;
182 palettei[x] = bgra.i;
184 /* fall through to colormap case */
186 if (targa_header.pixel_size != 8)
188 printf("LoadTGA: only 8bit pixel size for type 1, 3, 9, and 11 images supported\n");
193 printf("LoadTGA: Only type 1, 2, 3, 9, 10, and 11 targa RGB images supported, image_type = %i\n", targa_header.image_type);
197 if (targa_header.attributes & 0x10)
199 printf("LoadTGA: origin must be in top left or bottom left, top right and bottom right are not supported\n");
203 /* number of attribute bits per pixel, we only support 0 or 8 */
204 alphabits = targa_header.attributes & 0x0F;
205 if (alphabits != 8 && alphabits != 0)
207 printf("LoadTGA: only 0 or 8 attribute (alpha) bits supported\n");
211 image_buffer = (unsigned char *)malloc(image_width * image_height * 4);
214 printf("LoadTGA: not enough memory for %i by %i image\n", image_width, image_height);
218 /* If bit 5 of attributes isn't set, the image has been stored from bottom to top */
219 if ((targa_header.attributes & 0x20) == 0)
221 pixbufi = (unsigned int*)image_buffer + (image_height - 1)*image_width;
222 row_inci = -image_width*2;
226 pixbufi = (unsigned int*)image_buffer;
231 if ((targa_header.image_type & ~8) == 2)
232 pix_inc = (targa_header.pixel_size + 7) / 8;
233 switch (targa_header.image_type)
235 case 1: /* colormapped, uncompressed */
236 case 3: /* greyscale, uncompressed */
237 if (fin + image_width * image_height * pix_inc > enddata)
239 for (y = 0;y < image_height;y++, pixbufi += row_inci)
240 for (x = 0;x < image_width;x++)
241 *pixbufi++ = palettei[*fin++];
244 /* BGR or BGRA, uncompressed */
245 if (fin + image_width * image_height * pix_inc > enddata)
247 if (targa_header.pixel_size == 32 && alphabits)
249 for (y = 0;y < image_height;y++)
250 memcpy(pixbufi + y * (image_width + row_inci), fin + y * image_width * pix_inc, image_width*4);
254 for (y = 0;y < image_height;y++, pixbufi += row_inci)
256 for (x = 0;x < image_width;x++, fin += pix_inc)
267 case 9: /* colormapped, RLE */
268 case 11: /* greyscale, RLE */
269 for (y = 0;y < image_height;y++, pixbufi += row_inci)
271 for (x = 0;x < image_width;)
274 break; /* error - truncated file */
278 /* RLE - all pixels the same color */
280 if (fin + pix_inc > enddata)
281 break; /* error - truncated file */
282 if (x + runlen > image_width)
283 break; /* error - line exceeds width */
284 bgra.i = palettei[*fin++];
290 /* uncompressed - all pixels different color */
292 if (fin + pix_inc * runlen > enddata)
293 break; /* error - truncated file */
294 if (x + runlen > image_width)
295 break; /* error - line exceeds width */
297 *pixbufi++ = palettei[*fin++];
301 if (x != image_width)
303 /* pixbufi is useless now */
304 printf("LoadTGA: corrupt file\n");
310 /* BGR or BGRA, RLE */
311 if (targa_header.pixel_size == 32 && alphabits)
313 for (y = 0;y < image_height;y++, pixbufi += row_inci)
315 for (x = 0;x < image_width;)
318 break; /* error - truncated file */
322 /* RLE - all pixels the same color */
324 if (fin + pix_inc > enddata)
325 break; /* error - truncated file */
326 if (x + runlen > image_width)
327 break; /* error - line exceeds width */
338 /* uncompressed - all pixels different color */
340 if (fin + pix_inc * runlen > enddata)
341 break; /* error - truncated file */
342 if (x + runlen > image_width)
343 break; /* error - line exceeds width */
356 if (x != image_width)
358 /* pixbufi is useless now */
359 printf("LoadTGA: corrupt file\n");
366 for (y = 0;y < image_height;y++, pixbufi += row_inci)
368 for (x = 0;x < image_width;)
371 break; /* error - truncated file */
375 /* RLE - all pixels the same color */
377 if (fin + pix_inc > enddata)
378 break; /* error - truncated file */
379 if (x + runlen > image_width)
380 break; /* error - line exceeds width */
391 /* uncompressed - all pixels different color */
393 if (fin + pix_inc * runlen > enddata)
394 break; /* error - truncated file */
395 if (x + runlen > image_width)
396 break; /* error - line exceeds width */
409 if (x != image_width)
411 /* pixbufi is useless now */
412 printf("LoadTGA: corrupt file\n");
419 /* unknown image_type */
426 /* in can be the same as out */
427 void Image_MipReduce32(const unsigned char *in, unsigned char *out, int *width, int *height, int destwidth, int destheight)
429 const unsigned char *inrow;
431 /* note: if given odd width/height this discards the last row/column of
432 * pixels, rather than doing a proper box-filter scale down
435 nextrow = *width * 4;
436 if (*width > destwidth)
439 if (*height > destheight)
443 for (y = 0;y < *height;y++, inrow += nextrow * 2)
445 for (in = inrow, x = 0;x < *width;x++)
447 out[0] = (unsigned char) ((in[0] + in[4] + in[nextrow ] + in[nextrow+4]) >> 2);
448 out[1] = (unsigned char) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
449 out[2] = (unsigned char) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
450 out[3] = (unsigned char) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
459 for (y = 0;y < *height;y++, inrow += nextrow)
461 for (in = inrow, x = 0;x < *width;x++)
463 out[0] = (unsigned char) ((in[0] + in[4]) >> 1);
464 out[1] = (unsigned char) ((in[1] + in[5]) >> 1);
465 out[2] = (unsigned char) ((in[2] + in[6]) >> 1);
466 out[3] = (unsigned char) ((in[3] + in[7]) >> 1);
475 if (*height > destheight)
479 for (y = 0;y < *height;y++, inrow += nextrow * 2)
481 for (in = inrow, x = 0;x < *width;x++)
483 out[0] = (unsigned char) ((in[0] + in[nextrow ]) >> 1);
484 out[1] = (unsigned char) ((in[1] + in[nextrow+1]) >> 1);
485 out[2] = (unsigned char) ((in[2] + in[nextrow+2]) >> 1);
486 out[3] = (unsigned char) ((in[3] + in[nextrow+3]) >> 1);
494 unsigned char *FS_LoadFile(const char *fn, int *len)
496 unsigned char *buf = NULL;
498 FILE *f = fn ? fopen(fn, "rb") : stdin;
504 unsigned char *newbuf = (unsigned char *) realloc(buf, *len + 65536);
517 n = fread(buf + *len, 1, 65536, f);
534 /* END of darkplaces stuff */
536 uint32_t LittleLong(uint32_t w)
551 int usage(const char *me)
553 fprintf(stderr, "usage:\n"
556 " [-o outfile.dds]\n"
557 " [-t {DXT1|DXT3|DXT5}]\n"
558 #ifdef ENABLE_RUNTIME_LINKING
559 " [-l path_to_libtxc_dxtn.so]\n"
566 int main(int argc, char **argv)
568 const char *infile = NULL, *outfile = NULL;
570 unsigned char *picdata, *pic;
571 int x, mipcount, piclen;
574 GLenum dxt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
576 #ifdef ENABLE_RUNTIME_LINKING
577 const char *library = "libtxc_dxtn.so";
581 while((opt = getopt(argc, argv, "i:o:t:"
582 #ifdef ENABLE_RUNTIME_LINKING
596 if(!strcasecmp(optarg, "DXT1"))
597 dxt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
598 else if(!strcasecmp(optarg, "DXT3"))
599 dxt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
600 else if(!strcasecmp(optarg, "DXT5"))
601 dxt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
603 return usage(argv[0]);
605 #ifdef ENABLE_RUNTIME_LINKING
611 return usage(argv[0]);
615 #ifdef ENABLE_RUNTIME_LINKING
616 if(!load_libraries(library))
620 outfh = outfile ? fopen(outfile, "wb") : stdout;
623 printf("opening output failed\n");
627 picdata = FS_LoadFile(infile, &piclen);
630 printf("FS_LoadFile failed\n");
634 pic = LoadTGA_BGRA(picdata, piclen);
635 for(x = 0; x < image_width*image_height; ++x) {
636 unsigned char h = pic[4*x];
637 pic[4*x] = pic[4*x+2];
641 while(image_width >= (1 << mipcount) || image_height >= (1 << mipcount))
643 /* now, (1 << mipcount) >= width, height */
647 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
651 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
656 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
663 uint32_t zero = LittleLong(0);
664 bool alphapixels = false;
666 uint32_t dds_picsize, dds_mipcount, dds_width, dds_height;
668 for(y = 0; y < image_height; ++y)
669 for(x = 0; x < image_width; ++x)
670 if(pic[(y*image_width+x)*4+3] != 255)
676 dds_picsize = LittleLong(((image_width+3)/4) * ((image_height+3)/4) * blocksize);
677 dds_mipcount = LittleLong(mipcount);
678 dds_width = LittleLong(image_width);
679 dds_height = LittleLong(image_height);
682 fwrite("DDS ", 4, 1, outfh);
683 fwrite("\x7c\x00\x00\x00", 4, 1, outfh);
684 fwrite("\x07\x10\x0a\x00", 4, 1, outfh);
685 fwrite(&dds_height, 4, 1, outfh);
686 fwrite(&dds_width, 4, 1, outfh);
687 fwrite(&dds_picsize, 4, 1, outfh);
688 fwrite(&zero, 4, 1, outfh);
689 fwrite(&dds_mipcount, 4, 1, outfh);
692 fwrite(&zero, 4, 1, outfh);
693 fwrite(&zero, 4, 1, outfh);
694 fwrite(&zero, 4, 1, outfh);
695 fwrite(&zero, 4, 1, outfh);
696 fwrite(&zero, 4, 1, outfh);
697 fwrite(&zero, 4, 1, outfh);
698 fwrite(&zero, 4, 1, outfh);
699 fwrite(&zero, 4, 1, outfh);
702 fwrite(&zero, 4, 1, outfh);
703 fwrite(&zero, 4, 1, outfh);
704 fwrite(&zero, 4, 1, outfh);
705 fwrite("\x20\x00\x00\x00", 4, 1, outfh);
706 fwrite(alphapixels ? "\x05\x00\x00\x00" : "\x04\x00\x00\x00", 4, 1, outfh);
707 fwrite(fourcc, 4, 1, outfh);
708 fwrite(&zero, 4, 1, outfh);
709 fwrite(&zero, 4, 1, outfh);
712 fwrite(&zero, 4, 1, outfh);
713 fwrite(&zero, 4, 1, outfh);
714 fwrite(&zero, 4, 1, outfh);
715 fwrite("\x08\x10\x40\x00", 4, 1, outfh);
716 fwrite(&zero, 4, 1, outfh);
717 fwrite(&zero, 4, 1, outfh);
718 fwrite(&zero, 4, 1, outfh);
719 fwrite(&zero, 4, 1, outfh);
724 int blocks_w = (image_width + 3) / 4;
725 int blocks_h = (image_height + 3) / 4;
726 GLubyte *obuf = (GLubyte *) malloc(blocksize * blocks_w * blocks_h);
727 tx_compress_dxtn(4, image_width, image_height, pic, dxt, obuf, blocks_w * blocksize);
728 fwrite(obuf, blocksize * blocks_w * blocks_h, 1, outfh);
730 if(image_width == 1 && image_height == 1)
732 Image_MipReduce32(pic, pic, &image_width, &image_height, 1, 1);