+
+ rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
+
+ if (flags & TEXF_ALPHA && image_makemask(data, data, image_width * image_height))
+ image_masktex = R_LoadTexture2D(pool, va("%s_mask", filename), image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
+
+ Mem_Free(data);
+ return rt;
+}
+
+rtexture_t *loadtextureimagewithmaskandnmap (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags, float bumpscale)
+{
+ qbyte *data, *data2;
+ rtexture_t *rt;
+ image_masktex = NULL;
+ image_nmaptex = NULL;
+ if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
+ return 0;
+
+ data2 = Mem_Alloc(tempmempool, image_width * image_height * 4);
+
+ rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
+
+ Image_HeightmapToNormalmap(data, data2, image_width, image_height, (flags & TEXF_CLAMP) != 0, bumpscale);
+ image_nmaptex = R_LoadTexture2D(pool, va("%s_nmap", filename), image_width, image_height, data2, TEXTYPE_RGBA, flags, NULL);
+
+ if (flags & TEXF_ALPHA && image_makemask(data, data2, image_width * image_height))
+ image_masktex = R_LoadTexture2D(pool, va("%s_mask", filename), image_width, image_height, data2, TEXTYPE_RGBA, flags, NULL);
+
+ Mem_Free(data2);
+
+ Mem_Free(data);
+ return rt;
+}
+
+qboolean Image_WriteTGARGB_preflipped (const char *filename, int width, int height, const qbyte *data)
+{
+ qboolean ret;
+ qbyte *buffer, *out;
+ const qbyte *in, *end;
+
+ buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
+
+ memset (buffer, 0, 18);
+ buffer[2] = 2; // uncompressed type
+ buffer[12] = (width >> 0) & 0xFF;
+ buffer[13] = (width >> 8) & 0xFF;
+ buffer[14] = (height >> 0) & 0xFF;
+ buffer[15] = (height >> 8) & 0xFF;
+ buffer[16] = 24; // pixel size
+
+ // swap rgb to bgr
+ in = data;
+ out = buffer + 18;
+ end = in + width*height*3;
+ for (;in < end;in += 3)
+ {
+ *out++ = in[2];
+ *out++ = in[1];
+ *out++ = in[0];
+ }
+ ret = COM_WriteFile (filename, buffer, width*height*3 + 18 );
+
+ Mem_Free(buffer);
+ return ret;
+}
+
+void Image_WriteTGARGB (const char *filename, int width, int height, const qbyte *data)
+{
+ int y;
+ qbyte *buffer, *out;
+ const qbyte *in, *end;
+
+ buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
+
+ memset (buffer, 0, 18);
+ buffer[2] = 2; // uncompressed type
+ buffer[12] = (width >> 0) & 0xFF;
+ buffer[13] = (width >> 8) & 0xFF;
+ buffer[14] = (height >> 0) & 0xFF;
+ buffer[15] = (height >> 8) & 0xFF;
+ buffer[16] = 24; // pixel size
+
+ // swap rgb to bgr and flip upside down
+ out = buffer + 18;
+ for (y = height - 1;y >= 0;y--)
+ {
+ in = data + y * width * 3;
+ end = in + width * 3;
+ for (;in < end;in += 3)
+ {
+ *out++ = in[2];
+ *out++ = in[1];
+ *out++ = in[0];
+ }
+ }
+ COM_WriteFile (filename, buffer, width*height*3 + 18 );
+
+ Mem_Free(buffer);
+}
+
+void Image_WriteTGARGBA (const char *filename, int width, int height, const qbyte *data)
+{
+ int y;
+ qbyte *buffer, *out;
+ const qbyte *in, *end;
+
+ buffer = Mem_Alloc(tempmempool, width*height*4 + 18);
+
+ memset (buffer, 0, 18);
+ buffer[2] = 2; // uncompressed type
+ buffer[12] = (width >> 0) & 0xFF;
+ buffer[13] = (width >> 8) & 0xFF;
+ buffer[14] = (height >> 0) & 0xFF;
+ buffer[15] = (height >> 8) & 0xFF;
+ buffer[16] = 32; // pixel size
+
+ // swap rgba to bgra 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];
+ *out++ = in[3];
+ }
+ }
+ COM_WriteFile (filename, buffer, width*height*4 + 18 );
+
+ Mem_Free(buffer);
+}
+
+qboolean Image_CheckAlpha(const qbyte *data, int size, qboolean rgba)
+{
+ const qbyte *end;
+ if (rgba)
+ {
+ // check alpha bytes
+ for (end = data + size * 4, data += 3;data < end;data += 4)
+ if (*data < 255)
+ return 1;
+ }
+ else
+ {
+ // color 255 is transparent
+ for (end = data + size;data < end;data++)
+ if (*data == 255)
+ return 1;
+ }
+ return 0;
+}
+
+static void Image_Resample32LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth)
+{
+ int j, xi, oldx = 0, f, fstep, endx, lerp;
+ fstep = (int) (inwidth*65536.0f/outwidth);
+ endx = (inwidth-1);
+ for (j = 0,f = 0;j < outwidth;j++, f += fstep)
+ {
+ xi = f >> 16;
+ if (xi != oldx)
+ {
+ in += (xi - oldx) * 4;
+ oldx = xi;
+ }
+ if (xi < endx)
+ {
+ lerp = f & 0xFFFF;
+ *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
+ *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
+ *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
+ *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
+ }
+ else // last pixel of the line has no pixel to lerp to
+ {
+ *out++ = in[0];
+ *out++ = in[1];
+ *out++ = in[2];
+ *out++ = in[3];
+ }
+ }
+}
+
+static void Image_Resample24LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth)
+{
+ int j, xi, oldx = 0, f, fstep, endx, lerp;
+ fstep = (int) (inwidth*65536.0f/outwidth);
+ endx = (inwidth-1);
+ for (j = 0,f = 0;j < outwidth;j++, f += fstep)
+ {
+ xi = f >> 16;
+ if (xi != oldx)
+ {
+ in += (xi - oldx) * 3;
+ oldx = xi;
+ }
+ if (xi < endx)
+ {
+ lerp = f & 0xFFFF;
+ *out++ = (qbyte) ((((in[3] - in[0]) * lerp) >> 16) + in[0]);
+ *out++ = (qbyte) ((((in[4] - in[1]) * lerp) >> 16) + in[1]);
+ *out++ = (qbyte) ((((in[5] - in[2]) * lerp) >> 16) + in[2]);
+ }
+ else // last pixel of the line has no pixel to lerp to
+ {
+ *out++ = in[0];
+ *out++ = in[1];
+ *out++ = in[2];
+ }
+ }
+}
+
+int resamplerowsize = 0;
+qbyte *resamplerow1 = NULL;
+qbyte *resamplerow2 = NULL;
+mempool_t *resamplemempool = NULL;
+
+#define LERPBYTE(i) r = resamplerow1[i];out[i] = (qbyte) ((((resamplerow2[i] - r) * lerp) >> 16) + r)
+void Image_Resample32Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
+{
+ int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4;
+ qbyte *out;
+ const qbyte *inrow;
+ out = outdata;
+ fstep = (int) (inheight*65536.0f/outheight);
+
+ inrow = indata;
+ oldy = 0;
+ Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
+ Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
+ for (i = 0, f = 0;i < outheight;i++,f += fstep)
+ {
+ yi = f >> 16;
+ if (yi < endy)
+ {
+ lerp = f & 0xFFFF;
+ if (yi != oldy)
+ {
+ inrow = (qbyte *)indata + inwidth4*yi;
+ if (yi == oldy+1)
+ memcpy(resamplerow1, resamplerow2, outwidth4);
+ else
+ Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
+ Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
+ oldy = yi;
+ }
+ j = outwidth - 4;
+ while(j >= 0)
+ {
+ LERPBYTE( 0);
+ LERPBYTE( 1);
+ LERPBYTE( 2);
+ LERPBYTE( 3);
+ LERPBYTE( 4);
+ LERPBYTE( 5);
+ LERPBYTE( 6);
+ LERPBYTE( 7);
+ LERPBYTE( 8);
+ LERPBYTE( 9);
+ LERPBYTE(10);
+ LERPBYTE(11);
+ LERPBYTE(12);
+ LERPBYTE(13);
+ LERPBYTE(14);
+ LERPBYTE(15);
+ out += 16;
+ resamplerow1 += 16;
+ resamplerow2 += 16;
+ j -= 4;
+ }
+ if (j & 2)
+ {
+ LERPBYTE( 0);
+ LERPBYTE( 1);
+ LERPBYTE( 2);
+ LERPBYTE( 3);
+ LERPBYTE( 4);
+ LERPBYTE( 5);
+ LERPBYTE( 6);
+ LERPBYTE( 7);
+ out += 8;
+ resamplerow1 += 8;
+ resamplerow2 += 8;
+ }
+ if (j & 1)
+ {
+ LERPBYTE( 0);
+ LERPBYTE( 1);
+ LERPBYTE( 2);
+ LERPBYTE( 3);
+ out += 4;
+ resamplerow1 += 4;
+ resamplerow2 += 4;
+ }
+ resamplerow1 -= outwidth4;
+ resamplerow2 -= outwidth4;
+ }
+ else
+ {
+ if (yi != oldy)
+ {
+ inrow = (qbyte *)indata + inwidth4*yi;
+ if (yi == oldy+1)
+ memcpy(resamplerow1, resamplerow2, outwidth4);
+ else
+ Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
+ oldy = yi;
+ }
+ memcpy(out, resamplerow1, outwidth4);
+ }
+ }
+}
+
+void Image_Resample32Nearest(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
+{
+ int i, j;
+ unsigned frac, fracstep;
+ // relies on int being 4 bytes
+ int *inrow, *out;
+ out = outdata;
+
+ fracstep = inwidth*0x10000/outwidth;
+ for (i = 0;i < outheight;i++)
+ {
+ inrow = (int *)indata + inwidth*(i*inheight/outheight);
+ frac = fracstep >> 1;
+ j = outwidth - 4;
+ while (j >= 0)
+ {
+ out[0] = inrow[frac >> 16];frac += fracstep;
+ out[1] = inrow[frac >> 16];frac += fracstep;
+ out[2] = inrow[frac >> 16];frac += fracstep;
+ out[3] = inrow[frac >> 16];frac += fracstep;
+ out += 4;
+ j -= 4;
+ }
+ if (j & 2)
+ {
+ out[0] = inrow[frac >> 16];frac += fracstep;
+ out[1] = inrow[frac >> 16];frac += fracstep;
+ out += 2;
+ }
+ if (j & 1)
+ {
+ out[0] = inrow[frac >> 16];frac += fracstep;
+ out += 1;
+ }
+ }
+}
+
+void Image_Resample24Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
+{
+ int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth3 = inwidth * 3, outwidth3 = outwidth * 3;
+ qbyte *out;
+ const qbyte *inrow;
+ out = outdata;
+ fstep = (int) (inheight*65536.0f/outheight);
+
+ inrow = indata;
+ oldy = 0;
+ Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
+ Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
+ for (i = 0, f = 0;i < outheight;i++,f += fstep)
+ {
+ yi = f >> 16;
+ if (yi < endy)
+ {
+ lerp = f & 0xFFFF;
+ if (yi != oldy)
+ {
+ inrow = (qbyte *)indata + inwidth3*yi;
+ if (yi == oldy+1)
+ memcpy(resamplerow1, resamplerow2, outwidth3);
+ else
+ Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
+ Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
+ oldy = yi;
+ }
+ j = outwidth - 4;
+ while(j >= 0)
+ {
+ LERPBYTE( 0);
+ LERPBYTE( 1);
+ LERPBYTE( 2);
+ LERPBYTE( 3);
+ LERPBYTE( 4);
+ LERPBYTE( 5);
+ LERPBYTE( 6);
+ LERPBYTE( 7);
+ LERPBYTE( 8);
+ LERPBYTE( 9);
+ LERPBYTE(10);
+ LERPBYTE(11);
+ out += 12;
+ resamplerow1 += 12;
+ resamplerow2 += 12;
+ j -= 4;
+ }
+ if (j & 2)
+ {
+ LERPBYTE( 0);
+ LERPBYTE( 1);
+ LERPBYTE( 2);
+ LERPBYTE( 3);
+ LERPBYTE( 4);
+ LERPBYTE( 5);
+ out += 6;
+ resamplerow1 += 6;
+ resamplerow2 += 6;
+ }
+ if (j & 1)
+ {
+ LERPBYTE( 0);
+ LERPBYTE( 1);
+ LERPBYTE( 2);
+ out += 3;
+ resamplerow1 += 3;
+ resamplerow2 += 3;
+ }
+ resamplerow1 -= outwidth3;
+ resamplerow2 -= outwidth3;
+ }
+ else
+ {
+ if (yi != oldy)
+ {
+ inrow = (qbyte *)indata + inwidth3*yi;
+ if (yi == oldy+1)
+ memcpy(resamplerow1, resamplerow2, outwidth3);
+ else
+ Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
+ oldy = yi;
+ }
+ memcpy(out, resamplerow1, outwidth3);
+ }
+ }
+}
+
+void Image_Resample24Nolerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
+{
+ int i, j, f, inwidth3 = inwidth * 3;
+ unsigned frac, fracstep;
+ qbyte *inrow, *out;
+ out = outdata;
+
+ fracstep = inwidth*0x10000/outwidth;
+ for (i = 0;i < outheight;i++)
+ {
+ inrow = (qbyte *)indata + inwidth3*(i*inheight/outheight);
+ frac = fracstep >> 1;
+ j = outwidth - 4;
+ while (j >= 0)
+ {
+ f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
+ f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
+ f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
+ f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
+ j -= 4;
+ }
+ if (j & 2)
+ {
+ f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
+ f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
+ out += 2;
+ }
+ if (j & 1)
+ {
+ f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
+ out += 1;
+ }
+ }
+}
+
+/*
+================
+Image_Resample
+================
+*/
+void Image_Resample (const void *indata, int inwidth, int inheight, int indepth, void *outdata, int outwidth, int outheight, int outdepth, int bytesperpixel, int quality)
+{
+ if (indepth != 1 || outdepth != 1)
+ Sys_Error("Image_Resample: 3D resampling not supported\n");
+ if (resamplerowsize < outwidth*4)
+ {
+ if (resamplerow1)
+ Mem_Free(resamplerow1);
+ resamplerowsize = outwidth*4;
+ if (!resamplemempool)
+ resamplemempool = Mem_AllocPool("Image Scaling Buffer");
+ resamplerow1 = Mem_Alloc(resamplemempool, resamplerowsize*2);
+ resamplerow2 = resamplerow1 + resamplerowsize;
+ }
+ if (bytesperpixel == 4)
+ {
+ if (quality)
+ Image_Resample32Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
+ else
+ Image_Resample32Nearest(indata, inwidth, inheight, outdata, outwidth, outheight);
+ }
+ else if (bytesperpixel == 3)
+ {
+ if (quality)
+ Image_Resample24Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
+ else
+ Image_Resample24Nolerp(indata, inwidth, inheight, outdata, outwidth, outheight);
+ }
+ else
+ Sys_Error("Image_Resample: unsupported bytesperpixel %i\n", bytesperpixel);
+}
+
+// in can be the same as out
+void Image_MipReduce(const qbyte *in, qbyte *out, int *width, int *height, int *depth, int destwidth, int destheight, int destdepth, int bytesperpixel)
+{
+ int x, y, nextrow;
+ if (*depth != 1 || destdepth != 1)
+ Sys_Error("Image_Resample: 3D resampling not supported\n");
+ nextrow = *width * bytesperpixel;
+ if (*width > destwidth)
+ {
+ *width >>= 1;
+ if (*height > destheight)
+ {
+ // reduce both
+ *height >>= 1;
+ if (bytesperpixel == 4)
+ {
+ for (y = 0;y < *height;y++)
+ {
+ for (x = 0;x < *width;x++)
+ {
+ out[0] = (qbyte) ((in[0] + in[4] + in[nextrow ] + in[nextrow+4]) >> 2);
+ out[1] = (qbyte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
+ out[2] = (qbyte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
+ out[3] = (qbyte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
+ out += 4;
+ in += 8;
+ }
+ in += nextrow; // skip a line
+ }
+ }
+ else if (bytesperpixel == 3)
+ {
+ for (y = 0;y < *height;y++)
+ {
+ for (x = 0;x < *width;x++)
+ {
+ out[0] = (qbyte) ((in[0] + in[3] + in[nextrow ] + in[nextrow+3]) >> 2);
+ out[1] = (qbyte) ((in[1] + in[4] + in[nextrow+1] + in[nextrow+4]) >> 2);
+ out[2] = (qbyte) ((in[2] + in[5] + in[nextrow+2] + in[nextrow+5]) >> 2);
+ out += 3;
+ in += 6;
+ }
+ in += nextrow; // skip a line
+ }
+ }
+ else
+ Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
+ }
+ else
+ {
+ // reduce width
+ if (bytesperpixel == 4)
+ {
+ for (y = 0;y < *height;y++)
+ {
+ for (x = 0;x < *width;x++)
+ {
+ out[0] = (qbyte) ((in[0] + in[4]) >> 1);
+ out[1] = (qbyte) ((in[1] + in[5]) >> 1);
+ out[2] = (qbyte) ((in[2] + in[6]) >> 1);
+ out[3] = (qbyte) ((in[3] + in[7]) >> 1);
+ out += 4;
+ in += 8;
+ }
+ }
+ }
+ else if (bytesperpixel == 3)
+ {
+ for (y = 0;y < *height;y++)
+ {
+ for (x = 0;x < *width;x++)
+ {
+ out[0] = (qbyte) ((in[0] + in[3]) >> 1);
+ out[1] = (qbyte) ((in[1] + in[4]) >> 1);
+ out[2] = (qbyte) ((in[2] + in[5]) >> 1);
+ out += 3;
+ in += 6;
+ }
+ }
+ }
+ else
+ Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
+ }
+ }
+ else