]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - image.c
use strcasecmp for file name sorting
[xonotic/darkplaces.git] / image.c
diff --git a/image.c b/image.c
index be151e7892bbc8d4e018e0753bcca08ecf935a11..dd2d159194fff656553ae800f4d0d5e3d1aa9058 100644 (file)
--- a/image.c
+++ b/image.c
@@ -836,7 +836,8 @@ imageformat_t imageformats_other[] =
        {NULL, NULL}
 };
 
-unsigned char *loadimagepixels (const char *filename, qboolean complain, int matchwidth, int matchheight)
+int fixtransparentpixels(unsigned char *data, int w, int h);
+unsigned char *loadimagepixels (const char *filename, qboolean complain, int matchwidth, int matchheight, qboolean allowFixtrans)
 {
        fs_offset_t filesize;
        imageformat_t *firstformat, *format;
@@ -884,6 +885,22 @@ unsigned char *loadimagepixels (const char *filename, qboolean complain, int mat
                                        Con_Printf("loaded image %s (%dx%d)\n", name, image_width, image_height);
                                if (developer_memorydebug.integer)
                                        Mem_CheckSentinelsGlobal();
+                               if(allowFixtrans && r_fixtrans_auto.integer)
+                               {
+                                       int n = fixtransparentpixels(data, image_width, image_height);
+                                       if(n)
+                                       {
+                                               Con_Printf("- had to fix %s (%d pixels changed)\n", name, n);
+                                               if(r_fixtrans_auto.integer >= 2)
+                                               {
+                                                       char outfilename[MAX_QPATH], buf[MAX_QPATH];
+                                                       Image_StripImageExtension(name, buf, sizeof(buf));
+                                                       dpsnprintf(outfilename, sizeof(outfilename), "fixtrans/%s.tga", buf);
+                                                       Image_WriteTGARGBA(outfilename, image_width, image_height, data);
+                                                       Con_Printf("- %s written.\n", outfilename);
+                                               }
+                                       }
+                               }
                                return data;
                        }
                        else
@@ -907,17 +924,171 @@ unsigned char *loadimagepixels (const char *filename, qboolean complain, int mat
        return NULL;
 }
 
-rtexture_t *loadtextureimage (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags)
+rtexture_t *loadtextureimage (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags, qboolean allowFixtrans)
 {
        unsigned char *data;
        rtexture_t *rt;
-       if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
+       if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight, allowFixtrans)))
                return 0;
        rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
        Mem_Free(data);
        return rt;
 }
 
+int fixtransparentpixels(unsigned char *data, int w, int h)
+{
+       int const FIXTRANS_NEEDED = 1;
+       int const FIXTRANS_HAS_L = 2;
+       int const FIXTRANS_HAS_R = 4;
+       int const FIXTRANS_HAS_U = 8;
+       int const FIXTRANS_HAS_D = 16;
+       int const FIXTRANS_FIXED = 32;
+       unsigned char *fixMask = Mem_Alloc(tempmempool, w * h);
+       int fixPixels = 0;
+       int changedPixels = 0;
+       int x, y;
+
+#define FIXTRANS_PIXEL (y*w+x)
+#define FIXTRANS_PIXEL_U (((y+h-1)%h)*w+x)
+#define FIXTRANS_PIXEL_D (((y+1)%h)*w+x)
+#define FIXTRANS_PIXEL_L (y*w+((x+w-1)%w))
+#define FIXTRANS_PIXEL_R (y*w+((x+1)%w))
+
+       memset(fixMask, 0, w * h);
+       for(y = 0; y < h; ++y)
+               for(x = 0; x < w; ++x)
+               {
+                       if(data[FIXTRANS_PIXEL * 4 + 3] == 0)
+                       {
+                               fixMask[FIXTRANS_PIXEL] |= FIXTRANS_NEEDED;
+                               ++fixPixels;
+                       }
+                       else
+                       {
+                               fixMask[FIXTRANS_PIXEL_D] |= FIXTRANS_HAS_U;
+                               fixMask[FIXTRANS_PIXEL_U] |= FIXTRANS_HAS_D;
+                               fixMask[FIXTRANS_PIXEL_R] |= FIXTRANS_HAS_L;
+                               fixMask[FIXTRANS_PIXEL_L] |= FIXTRANS_HAS_R;
+                       }
+               }
+       if(fixPixels == w * h)
+               return 0; // sorry, can't do anything about this
+       while(fixPixels)
+       {
+               for(y = 0; y < h; ++y)
+                       for(x = 0; x < w; ++x)
+                               if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_NEEDED)
+                               {
+                                       unsigned int sumR = 0, sumG = 0, sumB = 0, sumA = 0, sumRA = 0, sumGA = 0, sumBA = 0, cnt = 0;
+                                       unsigned char r, g, b, a, r0, g0, b0;
+                                       if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_U)
+                                       {
+                                               r = data[FIXTRANS_PIXEL_U * 4 + 0];
+                                               g = data[FIXTRANS_PIXEL_U * 4 + 1];
+                                               b = data[FIXTRANS_PIXEL_U * 4 + 2];
+                                               a = data[FIXTRANS_PIXEL_U * 4 + 3];
+                                               sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt;
+                                       }
+                                       if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_D)
+                                       {
+                                               r = data[FIXTRANS_PIXEL_D * 4 + 0];
+                                               g = data[FIXTRANS_PIXEL_D * 4 + 1];
+                                               b = data[FIXTRANS_PIXEL_D * 4 + 2];
+                                               a = data[FIXTRANS_PIXEL_D * 4 + 3];
+                                               sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt;
+                                       }
+                                       if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_L)
+                                       {
+                                               r = data[FIXTRANS_PIXEL_L * 4 + 0];
+                                               g = data[FIXTRANS_PIXEL_L * 4 + 1];
+                                               b = data[FIXTRANS_PIXEL_L * 4 + 2];
+                                               a = data[FIXTRANS_PIXEL_L * 4 + 3];
+                                               sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt;
+                                       }
+                                       if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_R)
+                                       {
+                                               r = data[FIXTRANS_PIXEL_R * 4 + 0];
+                                               g = data[FIXTRANS_PIXEL_R * 4 + 1];
+                                               b = data[FIXTRANS_PIXEL_R * 4 + 2];
+                                               a = data[FIXTRANS_PIXEL_R * 4 + 3];
+                                               sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt;
+                                       }
+                                       if(!cnt)
+                                               continue;
+                                       r0 = data[FIXTRANS_PIXEL * 4 + 0];
+                                       g0 = data[FIXTRANS_PIXEL * 4 + 1];
+                                       b0 = data[FIXTRANS_PIXEL * 4 + 2];
+                                       if(sumA)
+                                       {
+                                               // there is a surrounding non-alpha pixel
+                                               r = (sumRA + sumA / 2) / sumA;
+                                               g = (sumGA + sumA / 2) / sumA;
+                                               b = (sumBA + sumA / 2) / sumA;
+                                       }
+                                       else
+                                       {
+                                               // need to use a "regular" average
+                                               r = (sumR + cnt / 2) / cnt;
+                                               g = (sumG + cnt / 2) / cnt;
+                                               b = (sumB + cnt / 2) / cnt;
+                                       }
+                                       if(r != r0 || g != g0 || b != b0)
+                                               ++changedPixels;
+                                       data[FIXTRANS_PIXEL * 4 + 0] = r;
+                                       data[FIXTRANS_PIXEL * 4 + 1] = g;
+                                       data[FIXTRANS_PIXEL * 4 + 2] = b;
+                                       fixMask[FIXTRANS_PIXEL] |= FIXTRANS_FIXED;
+                               }
+               for(y = 0; y < h; ++y)
+                       for(x = 0; x < w; ++x)
+                               if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_FIXED)
+                               {
+                                       fixMask[FIXTRANS_PIXEL] &= ~(FIXTRANS_NEEDED | FIXTRANS_FIXED);
+                                       fixMask[FIXTRANS_PIXEL_D] |= FIXTRANS_HAS_U;
+                                       fixMask[FIXTRANS_PIXEL_U] |= FIXTRANS_HAS_D;
+                                       fixMask[FIXTRANS_PIXEL_R] |= FIXTRANS_HAS_L;
+                                       fixMask[FIXTRANS_PIXEL_L] |= FIXTRANS_HAS_R;
+                                       --fixPixels;
+                               }
+       }
+       return changedPixels;
+}
+
+void Image_FixTransparentPixels_f(void)
+{
+       const char *filename, *filename_pattern;
+       fssearch_t *search;
+       int i, n;
+       char outfilename[MAX_QPATH], buf[MAX_QPATH];
+       unsigned char *data;
+       if(Cmd_Argc() != 2)
+       {
+               Con_Printf("Usage: %s imagefile\n", Cmd_Argv(0));
+               return;
+       }
+       filename_pattern = Cmd_Argv(1);
+       search = FS_Search(filename_pattern, true, true);
+       if(!search)
+               return;
+       for(i = 0; i < search->numfilenames; ++i)
+       {
+               filename = search->filenames[i];
+               Con_Printf("Processing %s... ", filename);
+               Image_StripImageExtension(filename, buf, sizeof(buf));
+               dpsnprintf(outfilename, sizeof(outfilename), "fixtrans/%s.tga", buf);
+               if(!(data = loadimagepixels(filename, true, 0, 0, false)))
+                       return;
+               if((n = fixtransparentpixels(data, image_width, image_height)))
+               {
+                       Image_WriteTGARGBA(outfilename, image_width, image_height, data);
+                       Con_Printf("%s written (%d pixels changed).\n", outfilename, n);
+               }
+               else
+                       Con_Printf("unchanged.\n");
+               Mem_Free(data);
+       }
+}
+
 qboolean Image_WriteTGARGB_preflipped (const char *filename, int width, int height, const unsigned char *data, unsigned char *buffer)
 {
        qboolean ret;
@@ -961,24 +1132,54 @@ void Image_WriteTGARGBA (const char *filename, int width, int height, const unsi
        buffer[13] = (width >> 8) & 0xFF;
        buffer[14] = (height >> 0) & 0xFF;
        buffer[15] = (height >> 8) & 0xFF;
-       buffer[16] = 32;        // pixel size
-       buffer[17] = 8; // 8 bits of alpha
 
-       // swap rgba to bgra and flip upside down
-       out = buffer + 18;
-       for (y = height - 1;y >= 0;y--)
+       for (y = 3;y < width*height*4;y += 4)
+               if (data[y] < 255)
+                       break;
+
+       if (y < width*height*4)
        {
-               in = data + y * width * 4;
-               end = in + width * 4;
-               for (;in < end;in += 4)
+               // save the alpha channel
+               buffer[16] = 32;        // pixel size
+               buffer[17] = 8; // 8 bits of alpha
+
+               // swap rgba to bgra and flip upside down
+               out = buffer + 18;
+               for (y = height - 1;y >= 0;y--)
                {
-                       *out++ = in[2];
-                       *out++ = in[1];
-                       *out++ = in[0];
-                       *out++ = in[3];
+                       in = data + y * width * 4;
+                       end = in + width * 4;
+                       for (;in < end;in += 4)
+                       {
+                               *out++ = in[2];
+                               *out++ = in[1];
+                               *out++ = in[0];
+                               *out++ = in[3];
+                       }
+               }
+               FS_WriteFile (filename, buffer, width*height*4 + 18 );
+       }
+       else
+       {
+               // save only the color channels
+               buffer[16] = 24;        // pixel size
+               buffer[17] = 0; // 8 bits of alpha
+
+               // swap rgba to bgr and flip upside down
+               out = buffer + 18;
+               for (y = height - 1;y >= 0;y--)
+               {
+                       in = data + y * width * 4;
+                       end = in + width * 4;
+                       for (;in < end;in += 4)
+                       {
+                               *out++ = in[2];
+                               *out++ = in[1];
+                               *out++ = in[0];
+                       }
                }
+               FS_WriteFile (filename, buffer, width*height*3 + 18 );
        }
-       FS_WriteFile (filename, buffer, width*height*4 + 18 );
 
        Mem_Free(buffer);
 }