Quake II RTX doxygen  1.0 dev
images.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2003-2008 Andrey Nazarov
4 Copyright (C) 2019, NVIDIA CORPORATION. All rights reserved.
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 //
22 // images.c -- image reading and writing functions
23 //
24 
25 #include "shared/shared.h"
26 #include "common/common.h"
27 #include "common/cvar.h"
28 #include "common/files.h"
29 #include "refresh/images.h"
30 #include "format/pcx.h"
31 #include "format/wal.h"
32 #include "stb_image.h"
33 #include "stb_image_write.h"
34 
35 #define R_COLORMAP_PCX "pics/colormap.pcx"
36 
37 #define IMG_LOAD(x) \
38  static qerror_t IMG_Load##x(byte *rawdata, size_t rawlen, \
39  image_t *image, byte **pic)
40 
41 #define IMG_SAVE(x) \
42  static qerror_t IMG_Save##x(qhandle_t f, const char *filename, \
43  byte *pic, int width, int height, int row_stride, int param)
44 
45 void stbi_write(void *context, void *data, int size)
46 {
47  FS_Write(data, size, (qhandle_t)(size_t)context);
48 }
49 
50 extern cvar_t* vid_rtx;
51 
52 /*
53 ====================================================================
54 
55 IMAGE FLOOD FILLING
56 
57 ====================================================================
58 */
59 
60 typedef struct {
61  short x, y;
62 } floodfill_t;
63 
64 // must be a power of 2
65 #define FLOODFILL_FIFO_SIZE 0x1000
66 #define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
67 
68 #define FLOODFILL_STEP(off, dx, dy) \
69  do { \
70  if (pos[off] == fillcolor) { \
71  pos[off] = 255; \
72  fifo[inpt].x = x + (dx); \
73  fifo[inpt].y = y + (dy); \
74  inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
75  } else if (pos[off] != 255) { \
76  fdc = pos[off]; \
77  } \
78  } while(0)
79 
80 /*
81 =================
82 IMG_FloodFill
83 
84 Fill background pixels so mipmapping doesn't have haloes
85 =================
86 */
87 static void IMG_FloodFill(byte *skin, int skinwidth, int skinheight)
88 {
89  byte fillcolor = *skin; // assume this is the pixel to fill
91  int inpt = 0, outpt = 0;
92  int filledcolor = 0; // FIXME: fixed black
93 
94  // can't fill to filled color or to transparent color
95  // (used as visited marker)
96  if (fillcolor == filledcolor || fillcolor == 255) {
97  return;
98  }
99 
100  fifo[inpt].x = 0, fifo[inpt].y = 0;
101  inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
102 
103  while (outpt != inpt) {
104  int x = fifo[outpt].x, y = fifo[outpt].y;
105  int fdc = filledcolor;
106  byte *pos = &skin[x + skinwidth * y];
107 
108  outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
109 
110  if (x > 0) FLOODFILL_STEP(-1, -1, 0);
111  if (x < skinwidth - 1) FLOODFILL_STEP(1, 1, 0);
112  if (y > 0) FLOODFILL_STEP(-skinwidth, 0, -1);
113  if (y < skinheight - 1) FLOODFILL_STEP(skinwidth, 0, 1);
114 
115  skin[x + skinwidth * y] = fdc;
116  }
117 }
118 
119 /*
120 =================================================================
121 
122 PCX LOADING
123 
124 =================================================================
125 */
126 
127 static qerror_t IMG_DecodePCX(byte *rawdata, size_t rawlen, byte *pixels,
128  byte *palette, int *width, int *height)
129 {
130  byte *raw, *end;
131  dpcx_t *pcx;
132  int x, y, w, h, scan;
133  int dataByte, runLength;
134 
135  //
136  // parse the PCX file
137  //
138  if (rawlen < sizeof(dpcx_t)) {
139  return Q_ERR_FILE_TOO_SMALL;
140  }
141 
142  pcx = (dpcx_t *)rawdata;
143 
144  if (pcx->manufacturer != 10 || pcx->version != 5) {
145  return Q_ERR_UNKNOWN_FORMAT;
146  }
147 
148  if (pcx->encoding != 1 || pcx->bits_per_pixel != 8) {
149  return Q_ERR_INVALID_FORMAT;
150  }
151 
152  w = (LittleShort(pcx->xmax) - LittleShort(pcx->xmin)) + 1;
153  h = (LittleShort(pcx->ymax) - LittleShort(pcx->ymin)) + 1;
154  if (w < 1 || h < 1 || w > 640 || h > 480) {
155  return Q_ERR_INVALID_FORMAT;
156  }
157 
158  if (pcx->color_planes != 1) {
159  return Q_ERR_INVALID_FORMAT;
160  }
161 
162  scan = LittleShort(pcx->bytes_per_line);
163  if (scan < w) {
164  return Q_ERR_INVALID_FORMAT;
165  }
166 
167  //
168  // get palette
169  //
170  if (palette) {
171  if (rawlen < 768) {
172  return Q_ERR_FILE_TOO_SMALL;
173  }
174  memcpy(palette, (byte *)pcx + rawlen - 768, 768);
175  }
176 
177  //
178  // get pixels
179  //
180  if (pixels) {
181  raw = pcx->data;
182  end = (byte *)pcx + rawlen;
183  for (y = 0; y < h; y++, pixels += w) {
184  for (x = 0; x < scan;) {
185  if (raw >= end)
186  return Q_ERR_BAD_RLE_PACKET;
187  dataByte = *raw++;
188 
189  if ((dataByte & 0xC0) == 0xC0) {
190  runLength = dataByte & 0x3F;
191  if (x + runLength > scan)
192  return Q_ERR_BAD_RLE_PACKET;
193  if (raw >= end)
194  return Q_ERR_BAD_RLE_PACKET;
195  dataByte = *raw++;
196  } else {
197  runLength = 1;
198  }
199 
200  while (runLength--) {
201  if (x < w)
202  pixels[x] = dataByte;
203  x++;
204  }
205  }
206  }
207  }
208 
209  if (width)
210  *width = w;
211  if (height)
212  *height = h;
213 
214  return Q_ERR_SUCCESS;
215 }
216 
217 /*
218 ===============
219 IMG_Unpack8
220 ===============
221 */
222 static int IMG_Unpack8(uint32_t *out, const uint8_t *in, int width, int height)
223 {
224  int x, y, p;
225  qboolean has_alpha = qfalse;
226 
227  for (y = 0; y < height; y++) {
228  for (x = 0; x < width; x++) {
229  p = *in;
230  if (p == 255) {
231  has_alpha = qtrue;
232  // transparent, so scan around for another color
233  // to avoid alpha fringes
234  if (y > 0 && *(in - width) != 255)
235  p = *(in - width);
236  else if (y < height - 1 && *(in + width) != 255)
237  p = *(in + width);
238  else if (x > 0 && *(in - 1) != 255)
239  p = *(in - 1);
240  else if (x < width - 1 && *(in + 1) != 255)
241  p = *(in + 1);
242  else if (y > 0 && x > 0 && *(in - width - 1) != 255)
243  p = *(in - width - 1);
244  else if (y > 0 && x < width - 1 && *(in - width + 1) != 255)
245  p = *(in - width + 1);
246  else if (y < height - 1 && x > 0 && *(in + width - 1) != 255)
247  p = *(in + width - 1);
248  else if (y < height - 1 && x < width - 1 && *(in + width + 1) != 255)
249  p = *(in + width + 1);
250  else
251  p = 0;
252  // copy rgb components
253  *out = d_8to24table[p] & U32_RGB;
254  } else {
255  *out = d_8to24table[p];
256  }
257  in++;
258  out++;
259  }
260  }
261 
262  if (has_alpha)
263  return IF_PALETTED | IF_TRANSPARENT;
264 
265  return IF_PALETTED | IF_OPAQUE;
266 }
267 
269 {
270  byte buffer[640 * 480];
271  int w, h;
272  qerror_t ret;
273 
274  ret = IMG_DecodePCX(rawdata, rawlen, buffer, NULL, &w, &h);
275  if (ret < 0)
276  return ret;
277 
278  if (image->type == IT_SKIN)
279  IMG_FloodFill(buffer, w, h);
280 
281  *pic = IMG_AllocPixels(w * h * 4);
282 
283  image->upload_width = image->width = w;
284  image->upload_height = image->height = h;
285  image->flags |= IMG_Unpack8((uint32_t *)*pic, buffer, w, h);
286 
287  return Q_ERR_SUCCESS;
288 }
289 
290 
291 /*
292 =================================================================
293 
294 WAL LOADING
295 
296 =================================================================
297 */
298 
300 {
301  miptex_t *mt;
302  size_t w, h, offset, size, endpos;
303 
304  if (rawlen < sizeof(miptex_t)) {
305  return Q_ERR_FILE_TOO_SMALL;
306  }
307 
308  mt = (miptex_t *)rawdata;
309 
310  w = LittleLong(mt->width);
311  h = LittleLong(mt->height);
312  if (w < 1 || h < 1 || w > 512 || h > 512) {
313  return Q_ERR_INVALID_FORMAT;
314  }
315 
316  size = w * h;
317 
318  offset = LittleLong(mt->offsets[0]);
319  endpos = offset + size;
320  if (endpos < offset || endpos > rawlen) {
321  return Q_ERR_BAD_EXTENT;
322  }
323 
324  *pic = IMG_AllocPixels(size * 4);
325 
326  image->upload_width = image->width = w;
327  image->upload_height = image->height = h;
328  image->flags |= IMG_Unpack8((uint32_t *)*pic, (uint8_t *)mt + offset, w, h);
329 
330  return Q_ERR_SUCCESS;
331 }
332 
333 /*
334 =================================================================
335 
336 STB_IMAGE LOADING
337 
338 =================================================================
339 */
340 
342 {
343  int w, h, channels;
344  byte* data = stbi_load_from_memory(rawdata, rawlen, &w, &h, &channels, 4);
345 
346  if (!data)
347  return Q_ERR_LIBRARY_ERROR;
348 
349  *pic = data;
350 
351  image->upload_width = image->width = w;
352  image->upload_height = image->height = h;
353 
354  if (channels == 3)
355  image->flags |= IF_OPAQUE;
356 
357  return Q_ERR_SUCCESS;
358 }
359 
360 
361 /*
362 =================================================================
363 
364 STB_IMAGE SAVING
365 
366 =================================================================
367 */
368 
370 {
371  stbi_flip_vertically_on_write(1);
372  int ret = stbi_write_tga_to_func(stbi_write, (void*)(size_t)f, width, height, 3, pic);
373 
374  if (ret)
375  return Q_ERR_SUCCESS;
376 
377  return Q_ERR_LIBRARY_ERROR;
378 }
379 
381 {
382  stbi_flip_vertically_on_write(1);
383  int ret = stbi_write_jpg_to_func(stbi_write, (void*)(size_t)f, width, height, 3, pic, param);
384 
385  if (ret)
386  return Q_ERR_SUCCESS;
387 
388  return Q_ERR_LIBRARY_ERROR;
389 }
390 
391 
393 {
394  stbi_flip_vertically_on_write(1);
395  int ret = stbi_write_png_to_func(stbi_write, (void*)(size_t)f, width, height, 3, pic, row_stride);
396 
397  if (ret)
398  return Q_ERR_SUCCESS;
399 
400  return Q_ERR_LIBRARY_ERROR;
401 }
402 
403 /*
404 =========================================================
405 
406 SCREEN SHOTS
407 
408 =========================================================
409 */
410 
411 static cvar_t *r_screenshot_format;
412 static cvar_t *r_screenshot_quality;
414 
415 static qhandle_t create_screenshot(char *buffer, size_t size,
416  const char *name, const char *ext)
417 {
418  qhandle_t f;
419  qerror_t ret;
420  int i;
421 
422  if (name && *name) {
423  // save to user supplied name
424  return FS_EasyOpenFile(buffer, size, FS_MODE_WRITE,
425  "screenshots/", name, ext);
426  }
427 
428  // find a file name to save it to
429  for (i = 0; i < 1000; i++) {
430  Q_snprintf(buffer, size, "screenshots/quake%03d%s", i, ext);
431  ret = FS_FOpenFile(buffer, &f, FS_MODE_WRITE | FS_FLAG_EXCL);
432  if (f) {
433  return f;
434  }
435  if (ret != Q_ERR_EXIST) {
436  Com_EPrintf("Couldn't exclusively open %s for writing: %s\n",
437  buffer, Q_ErrorString(ret));
438  return 0;
439  }
440  }
441 
442  Com_EPrintf("All screenshot slots are full.\n");
443  return 0;
444 }
445 
446 static void make_screenshot(const char *name, const char *ext,
447  qerror_t (*save)(qhandle_t, const char *, byte *, int, int, int, int),
448  int param)
449 {
450  char buffer[MAX_OSPATH];
451  byte *pixels;
452  qerror_t ret;
453  qhandle_t f;
454  int w, h, rowbytes;
455 
456  f = create_screenshot(buffer, sizeof(buffer), name, ext);
457  if (!f) {
458  return;
459  }
460 
461  pixels = IMG_ReadPixels(&w, &h, &rowbytes);
462  ret = save(f, buffer, pixels, w, h, rowbytes, param);
463  FS_FreeTempMem(pixels);
464 
465  FS_FCloseFile(f);
466 
467  if (ret < 0) {
468  Com_EPrintf("Couldn't write %s: %s\n", buffer, Q_ErrorString(ret));
469  } else {
470  Com_Printf("Wrote %s\n", buffer);
471  }
472 }
473 
474 /*
475 ==================
476 IMG_ScreenShot_f
477 
478 Standard function to take a screenshot. Saves in default format unless user
479 overrides format with a second argument. Screenshot name can't be
480 specified. This function is always compiled in to give a meaningful warning
481 if no formats are available.
482 ==================
483 */
484 static void IMG_ScreenShot_f(void)
485 {
486  const char *s;
487 
488  if (Cmd_Argc() > 2) {
489  Com_Printf("Usage: %s [format]\n", Cmd_Argv(0));
490  return;
491  }
492 
493  if (Cmd_Argc() > 1) {
494  s = Cmd_Argv(1);
495  } else {
496  s = r_screenshot_format->string;
497  }
498 
499  if (*s == 'j') {
500  make_screenshot(NULL, ".jpg", IMG_SaveJPG,
501  r_screenshot_quality->integer);
502  return;
503  }
504 
505  if (*s == 'p') {
506  make_screenshot(NULL, ".png", IMG_SavePNG,
507  r_screenshot_compression->integer);
508  return;
509  }
510 
511  make_screenshot(NULL, ".tga", IMG_SaveTGA, 0);
512 }
513 
514 /*
515 ==================
516 IMG_ScreenShotXXX_f
517 
518 Specialized function to take a screenshot in specified format. Screenshot name
519 can be also specified, as well as quality and compression options.
520 ==================
521 */
522 
523 static void IMG_ScreenShotTGA_f(void)
524 {
525  if (Cmd_Argc() > 2) {
526  Com_Printf("Usage: %s [name]\n", Cmd_Argv(0));
527  return;
528  }
529 
530  make_screenshot(Cmd_Argv(1), ".tga", IMG_SaveTGA, 0);
531 }
532 
533 static void IMG_ScreenShotJPG_f(void)
534 {
535  int quality;
536 
537  if (Cmd_Argc() > 3) {
538  Com_Printf("Usage: %s [name] [quality]\n", Cmd_Argv(0));
539  return;
540  }
541 
542  if (Cmd_Argc() > 2) {
543  quality = atoi(Cmd_Argv(2));
544  } else {
545  quality = r_screenshot_quality->integer;
546  }
547 
548  make_screenshot(Cmd_Argv(1), ".jpg", IMG_SaveJPG, quality);
549 }
550 
551 static void IMG_ScreenShotPNG_f(void)
552 {
553  int compression;
554 
555  if (Cmd_Argc() > 3) {
556  Com_Printf("Usage: %s [name] [compression]\n", Cmd_Argv(0));
557  return;
558  }
559 
560  if (Cmd_Argc() > 2) {
561  compression = atoi(Cmd_Argv(2));
562  } else {
563  compression = r_screenshot_compression->integer;
564  }
565 
566  make_screenshot(Cmd_Argv(1), ".png", IMG_SavePNG, compression);
567 }
568 
569 /*
570 =========================================================
571 
572 IMAGE PROCESSING
573 
574 =========================================================
575 */
576 
577 void IMG_ResampleTexture(const byte *in, int inwidth, int inheight,
578  byte *out, int outwidth, int outheight)
579 {
580  int i, j;
581  const byte *inrow1, *inrow2;
582  unsigned frac, fracstep;
583  unsigned p1[MAX_TEXTURE_SIZE], p2[MAX_TEXTURE_SIZE];
584  const byte *pix1, *pix2, *pix3, *pix4;
585  float heightScale;
586 
587  if (outwidth > MAX_TEXTURE_SIZE) {
588  Com_Error(ERR_FATAL, "%s: outwidth > %d", __func__, MAX_TEXTURE_SIZE);
589  }
590 
591  fracstep = inwidth * 0x10000 / outwidth;
592 
593  frac = fracstep >> 2;
594  for (i = 0; i < outwidth; i++) {
595  p1[i] = 4 * (frac >> 16);
596  frac += fracstep;
597  }
598  frac = 3 * (fracstep >> 2);
599  for (i = 0; i < outwidth; i++) {
600  p2[i] = 4 * (frac >> 16);
601  frac += fracstep;
602  }
603 
604  heightScale = (float)inheight / outheight;
605  inwidth <<= 2;
606  for (i = 0; i < outheight; i++) {
607  inrow1 = in + inwidth * (int)((i + 0.25f) * heightScale);
608  inrow2 = in + inwidth * (int)((i + 0.75f) * heightScale);
609  for (j = 0; j < outwidth; j++) {
610  pix1 = inrow1 + p1[j];
611  pix2 = inrow1 + p2[j];
612  pix3 = inrow2 + p1[j];
613  pix4 = inrow2 + p2[j];
614  out[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2;
615  out[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2;
616  out[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2;
617  out[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2;
618  out += 4;
619  }
620  }
621 }
622 
623 void IMG_MipMap(byte *out, byte *in, int width, int height)
624 {
625  int i, j;
626 
627  width <<= 2;
628  height >>= 1;
629  for (i = 0; i < height; i++, in += width) {
630  for (j = 0; j < width; j += 8, out += 4, in += 8) {
631  out[0] = (in[0] + in[4] + in[width + 0] + in[width + 4]) >> 2;
632  out[1] = (in[1] + in[5] + in[width + 1] + in[width + 5]) >> 2;
633  out[2] = (in[2] + in[6] + in[width + 2] + in[width + 6]) >> 2;
634  out[3] = (in[3] + in[7] + in[width + 3] + in[width + 7]) >> 2;
635  }
636  }
637 }
638 
639 /*
640 =========================================================
641 
642 IMAGE MANAGER
643 
644 =========================================================
645 */
646 
647 #define RIMAGES_HASH 256
648 
649 static list_t r_imageHash[RIMAGES_HASH];
650 
651 image_t r_images[MAX_RIMAGES];
653 
654 uint32_t d_8to24table[256];
655 
656 static const struct {
657  char ext[4];
658  qerror_t (*load)(byte *, size_t, image_t *, byte **);
659 } img_loaders[IM_MAX] = {
660  { "pcx", IMG_LoadPCX },
661  { "wal", IMG_LoadWAL },
662  { "tga", IMG_LoadSTB },
663  { "jpg", IMG_LoadSTB },
664  { "png", IMG_LoadSTB }
665 };
666 
667 static imageformat_t img_search[IM_MAX];
668 static int img_total;
669 
670 static cvar_t *r_override_textures;
671 static cvar_t *r_texture_formats;
672 
673 /*
674 ===============
675 IMG_List_f
676 ===============
677 */
678 static void IMG_List_f(void)
679 {
680  int i;
681  image_t *image;
682  int texels, count;
683 
684  if (Cmd_Argc() > 1) {
685 
686  // save to file
687  char path[MAX_OSPATH];
688 
689  qhandle_t f = FS_EasyOpenFile(path, sizeof(path), FS_MODE_WRITE | FS_FLAG_TEXT, "", Cmd_Argv(1), ".csv");
690  if (!f) {
691  Com_EPrintf("Error opening '%s'\n", path);
692  return;
693  }
694 
695  for (i = 1, count = 0, image = r_images + 1; i < r_numImages; i++, image++) {
696 
697  if (!image->registration_sequence)
698  continue;
699 
700  char fmt[MAX_QPATH];
701  sprintf(fmt, "%%-%ds, %%-%ds, (%% 5d %% 5d), sRGB:%%d\n", MAX_QPATH, MAX_QPATH);
702 
703  FS_FPrintf(f, fmt,
704  image->name,
705  image->filepath,
706  image->width,
707  image->height,
708  image->is_srgb);
709  }
710  FS_FCloseFile(f);
711 
712  Com_Printf("Saved '%s'\n", path);
713 
714  } else {
715 
716  // dump to console
717  static const char types[8] = "PFMSWY??";
718 
719  Com_Printf("------------------\n");
720  texels = count = 0;
721 
722  for (i = 1, image = r_images + 1; i < r_numImages; i++, image++) {
723  if (!image->registration_sequence)
724  continue;
725 
726  Com_Printf("%c%c%c%c %4i %4i %s: %s\n",
727  types[image->type > IT_MAX ? IT_MAX : image->type],
728  (image->flags & IF_TRANSPARENT) ? 'T' : ' ',
729  (image->flags & IF_SCRAP) ? 'S' : ' ',
730  (image->flags & IF_PERMANENT) ? '*' : ' ',
731  image->upload_width,
732  image->upload_height,
733  (image->flags & IF_PALETTED) ? "PAL" : "RGB",
734  image->name);
735 
736  texels += image->upload_width * image->upload_height;
737  count++;
738  }
739  Com_Printf("Total images: %d (out of %d slots)\n", count, r_numImages);
740  Com_Printf("Total texels: %d (not counting mipmaps)\n", texels);
741  }
742 }
743 
744 static image_t *alloc_image(void)
745 {
746  int i;
747  image_t *image;
748 
749  // find a free image_t slot
750  for (i = 1, image = r_images + 1; i < r_numImages; i++, image++) {
751  if (!image->registration_sequence)
752  break;
753  }
754 
755  if (i == r_numImages) {
756  if (r_numImages == MAX_RIMAGES)
757  return NULL;
758  r_numImages++;
759  }
760 
761  return image;
762 }
763 
764 // finds the given image of the given type.
765 // case and extension insensitive.
766 static image_t *lookup_image(const char *name,
767  imagetype_t type, unsigned hash, size_t baselen)
768 {
769  image_t *image;
770 
771  // look for it
772  LIST_FOR_EACH(image_t, image, &r_imageHash[hash], entry) {
773  if (image->type != type) {
774  continue;
775  }
776  if (image->baselen != baselen) {
777  continue;
778  }
779  if (!FS_pathcmpn(image->name, name, baselen)) {
780  return image;
781  }
782  }
783 
784  return NULL;
785 }
786 
787 static int _try_image_format(imageformat_t fmt, image_t *image, byte **pic)
788 {
789  byte *data;
790  ssize_t len;
791  qerror_t ret;
792 
793  // load the file
794  len = FS_LoadFile(image->name, (void **)&data);
795  if (!data) {
796  return len;
797  }
798 
799  // decompress the image
800  ret = img_loaders[fmt].load(data, len, image, pic);
801 
802  FS_FreeFile(data);
803 
804  image->filepath[0] = 0;
805  if (ret >= 0) {
806  strcpy(image->filepath, image->name);
807  // record last modified time (skips reload when invoking IMG_ReloadAll)
808  image->last_modified = 0;
809  FS_LastModified(image->filepath, &image->last_modified);
810  }
811  return ret < 0 ? ret : fmt;
812 }
813 
814 static int try_image_format(imageformat_t fmt, image_t *image, byte **pic)
815 {
816  // replace the extension
817  memcpy(image->name + image->baselen + 1, img_loaders[fmt].ext, 4);
818  return _try_image_format(fmt, image, pic);
819 }
820 
821 
822 // tries to load the image with a different extension
823 static int try_other_formats(imageformat_t orig, image_t *image, byte **pic)
824 {
825  imageformat_t fmt;
826  qerror_t ret;
827  int i;
828 
829  // search through all the 32-bit formats
830  for (i = 0; i < img_total; i++) {
831  fmt = img_search[i];
832  if (fmt == orig) {
833  continue; // don't retry twice
834  }
835 
836  ret = try_image_format(fmt, image, pic);
837  if (ret != Q_ERR_NOENT) {
838  return ret; // found something
839  }
840  }
841 
842  // fall back to 8-bit formats
843  fmt = (image->type == IT_WALL) ? IM_WAL : IM_PCX;
844  if (fmt == orig) {
845  return Q_ERR_NOENT; // don't retry twice
846  }
847 
848  return try_image_format(fmt, image, pic);
849 }
850 
851 static void get_image_dimensions(imageformat_t fmt, image_t *image)
852 {
853  char buffer[MAX_QPATH];
854  ssize_t len;
855  miptex_t mt;
856  dpcx_t pcx;
857  qhandle_t f;
858  unsigned w, h;
859 
860  memcpy(buffer, image->name, image->baselen + 1);
861 
862  w = h = 0;
863  if (fmt == IM_WAL) {
864  memcpy(buffer + image->baselen + 1, "wal", 4);
865  FS_FOpenFile(buffer, &f, FS_MODE_READ);
866  if (f) {
867  len = FS_Read(&mt, sizeof(mt), f);
868  if (len == sizeof(mt)) {
869  w = LittleLong(mt.width);
870  h = LittleLong(mt.height);
871  }
872  FS_FCloseFile(f);
873  }
874  } else {
875  memcpy(buffer + image->baselen + 1, "pcx", 4);
876  FS_FOpenFile(buffer, &f, FS_MODE_READ);
877  if (f) {
878  len = FS_Read(&pcx, sizeof(pcx), f);
879  if (len == sizeof(pcx)) {
880  w = LittleShort(pcx.xmax) + 1;
881  h = LittleShort(pcx.ymax) + 1;
882  }
883  FS_FCloseFile(f);
884  }
885  }
886 
887  if (w < 1 || h < 1 || w > 512 || h > 512) {
888  return;
889  }
890 
891  image->width = w;
892  image->height = h;
893 }
894 
895 static void r_texture_formats_changed(cvar_t *self)
896 {
897  char *s;
898  int i, j;
899 
900  // reset the search order
901  img_total = 0;
902 
903  // parse the string
904  for (s = self->string; *s; s++) {
905  switch (*s) {
906  case 't': case 'T': i = IM_TGA; break;
907  case 'j': case 'J': i = IM_JPG; break;
908  case 'p': case 'P': i = IM_PNG; break;
909  default: continue;
910  }
911 
912  // don't let format to be specified more than once
913  for (j = 0; j < img_total; j++)
914  if (img_search[j] == i)
915  break;
916  if (j != img_total)
917  continue;
918 
919  img_search[img_total++] = i;
920  if (img_total == IM_MAX) {
921  break;
922  }
923  }
924 }
925 
926 qerror_t
927 load_img(const char *name, image_t *image)
928 {
929  byte *pic;
930  imageformat_t fmt;
931  qerror_t ret;
932 
933  size_t len = strlen(name);
934 
935  // must have an extension and at least 1 char of base name
936  if (len <= 4) {
937  return Q_ERR_NAMETOOSHORT;
938  }
939  if (name[len - 4] != '.') {
940  return Q_ERR_INVALID_PATH;
941  }
942 
943  memcpy(image->name, name, len + 1);
944  image->baselen = len - 4;
945  image->type = 0;
946  image->flags = 0;
947  image->registration_sequence = 1;
948 
949  // find out original extension
950  for (fmt = 0; fmt < IM_MAX; fmt++) {
951  if (!Q_stricmp(image->name + image->baselen + 1, img_loaders[fmt].ext)) {
952  break;
953  }
954  }
955 
956  // load the pic from disk
957  pic = NULL;
958 
959  // first try with original extension
960  ret = _try_image_format(fmt, image, &pic);
961  if (ret == Q_ERR_NOENT) {
962  // retry with remaining extensions
963  ret = try_other_formats(fmt, image, &pic);
964  }
965 
966  // if we are replacing 8-bit texture with a higher resolution 32-bit
967  // texture, we need to recover original image dimensions
968  if (fmt <= IM_WAL && ret > IM_WAL) {
969  get_image_dimensions(fmt, image);
970  }
971 
972  // if we are replacing 8-bit texture with a higher resolution 32-bit
973  // texture, we need to recover original image dimensions
974  if (fmt <= IM_WAL && ret > IM_WAL) {
975  get_image_dimensions(fmt, image);
976  }
977 
978  if (ret < 0) {
979  memset(image, 0, sizeof(*image));
980  return ret;
981  }
982 
983 #if USE_REF == REF_VKPT
984  image->pix_data = pic;
985 #endif
986 
987  return Q_ERR_SUCCESS;
988 }
989 
990 // finds or loads the given image, adding it to the hash table.
991 static qerror_t find_or_load_image(const char *name, size_t len,
992  imagetype_t type, imageflags_t flags,
993  image_t **image_p)
994 {
995  image_t *image;
996  byte *pic;
997  unsigned hash;
998  imageformat_t fmt;
999  qerror_t ret;
1000 
1001  *image_p = NULL;
1002 
1003  // must have an extension and at least 1 char of base name
1004  if (len <= 4) {
1005  return Q_ERR_NAMETOOSHORT;
1006  }
1007  if (name[len - 4] != '.') {
1008  return Q_ERR_INVALID_PATH;
1009  }
1010 
1011  hash = FS_HashPathLen(name, len - 4, RIMAGES_HASH);
1012 
1013  // look for it
1014  if ((image = lookup_image(name, type, hash, len - 4)) != NULL) {
1015  image->flags |= flags & IF_PERMANENT;
1016  image->registration_sequence = registration_sequence;
1017  *image_p = image;
1018  return Q_ERR_SUCCESS;
1019  }
1020 
1021  // allocate image slot
1022  image = alloc_image();
1023  if (!image) {
1024  return Q_ERR_OUT_OF_SLOTS;
1025  }
1026 
1027  int override_textures = !!r_override_textures->integer;
1028  if (!vid_rtx->integer && (type != IT_PIC))
1029  override_textures = 0;
1030 
1031  for (int use_override = override_textures; use_override >= 0; use_override--)
1032  {
1033  // fill in some basic info
1034  if (use_override)
1035  {
1036  const char* last_slash = strrchr(name, '/');
1037  if (!last_slash)
1038  last_slash = name;
1039  else
1040  last_slash += 1;
1041 
1042  strcpy(image->name, "overrides/");
1043  strcat(image->name, last_slash);
1044  image->baselen = strlen(image->name) - 4;
1045  }
1046  else
1047  {
1048  memcpy(image->name, name, len + 1);
1049  image->baselen = len - 4;
1050  }
1051  image->type = type;
1052  image->flags = flags;
1053  image->registration_sequence = registration_sequence;
1054 
1055  // find out original extension
1056  for (fmt = 0; fmt < IM_MAX; fmt++) {
1057  if (!Q_stricmp(image->name + image->baselen + 1, img_loaders[fmt].ext)) {
1058  break;
1059  }
1060  }
1061 
1062  // load the pic from disk
1063  pic = NULL;
1064 
1065  if (fmt == IM_MAX) {
1066  // unknown extension, but give it a chance to load anyway
1067  ret = try_other_formats(IM_MAX, image, &pic);
1068  if (ret == Q_ERR_NOENT) {
1069  // not found, change error to invalid path
1070  ret = Q_ERR_INVALID_PATH;
1071  }
1072  }
1073  else if (override_textures) {
1074  // forcibly replace the extension
1075  ret = try_other_formats(IM_MAX, image, &pic);
1076  }
1077  else {
1078  // first try with original extension
1079  ret = _try_image_format(fmt, image, &pic);
1080  if (ret == Q_ERR_NOENT) {
1081  // retry with remaining extensions
1082  ret = try_other_formats(fmt, image, &pic);
1083  }
1084  }
1085 
1086  // record last modified time (skips reload when invoking IMG_ReloadAll)
1087  image->last_modified = 0;
1088  FS_LastModified(image->name, &image->last_modified);
1089 
1090  if (use_override)
1091  {
1092  memcpy(image->name, name, len + 1);
1093  image->baselen = len - 4;
1094  }
1095 
1096  // if we are replacing 8-bit texture with a higher resolution 32-bit
1097  // texture, we need to recover original image dimensions
1098  if (fmt <= IM_WAL && ret > IM_WAL) {
1099  get_image_dimensions(fmt, image);
1100  }
1101 
1102  if(ret >= 0)
1103  break;
1104  }
1105 
1106  if (ret < 0) {
1107  memset(image, 0, sizeof(*image));
1108  return ret;
1109  }
1110 
1111  List_Append(&r_imageHash[hash], &image->entry);
1112 
1113  image->is_srgb = !!(flags & IF_SRGB);
1114 
1115  // upload the image
1116  IMG_Load(image, pic);
1117 
1118  *image_p = image;
1119  return Q_ERR_SUCCESS;
1120 }
1121 
1122 image_t *IMG_Find(const char *name, imagetype_t type, imageflags_t flags)
1123 {
1124  image_t *image;
1125  size_t len;
1126  qerror_t ret;
1127 
1128  if (!name) {
1129  Com_Error(ERR_FATAL, "%s: NULL", __func__);
1130  }
1131 
1132  // this should never happen
1133  len = strlen(name);
1134  if (len >= MAX_QPATH) {
1135  Com_Error(ERR_FATAL, "%s: oversize name", __func__);
1136  }
1137 
1138  ret = find_or_load_image(name, len, type, flags, &image);
1139  if (image) {
1140  return image;
1141  }
1142 
1143  // don't spam about missing images
1144  if (ret != Q_ERR_NOENT) {
1145  Com_EPrintf("Couldn't load %s: %s\n", name, Q_ErrorString(ret));
1146  }
1147 
1148  return R_NOTEXTURE;
1149 }
1150 
1151 /*
1152 ===============
1153 IMG_ForHandle
1154 ===============
1155 */
1156 image_t *IMG_ForHandle(qhandle_t h)
1157 {
1158  if (h < 0 || h >= r_numImages) {
1159  Com_Error(ERR_FATAL, "%s: %d out of range", __func__, h);
1160  }
1161 
1162  return &r_images[h];
1163 }
1164 
1165 /*
1166 ===============
1167 R_RegisterImage
1168 ===============
1169 */
1170 qhandle_t R_RegisterImage(const char *name, imagetype_t type,
1171  imageflags_t flags, qerror_t *err_p)
1172 {
1173  image_t *image;
1174  char fullname[MAX_QPATH];
1175  size_t len;
1176  qerror_t err;
1177 
1178  // empty names are legal, silently ignore them
1179  if (!*name) {
1180  if (err_p)
1181  *err_p = Q_ERR_NAMETOOSHORT;
1182  return 0;
1183  }
1184 
1185  // no images = not initialized
1186  if (!r_numImages) {
1187  if (err_p)
1188  *err_p = Q_ERR_AGAIN;
1189  return 0;
1190  }
1191 
1192  if (type == IT_SKIN) {
1193  len = FS_NormalizePathBuffer(fullname, name, sizeof(fullname));
1194  } else if (*name == '/' || *name == '\\') {
1195  len = FS_NormalizePathBuffer(fullname, name + 1, sizeof(fullname));
1196  } else {
1197  len = Q_concat(fullname, sizeof(fullname), "pics/", name, NULL);
1198  if (len >= sizeof(fullname)) {
1199  err = Q_ERR_NAMETOOLONG;
1200  goto fail;
1201  }
1202  FS_NormalizePath(fullname, fullname);
1203  len = COM_DefaultExtension(fullname, ".pcx", sizeof(fullname));
1204  }
1205 
1206  if (len >= sizeof(fullname)) {
1207  err = Q_ERR_NAMETOOLONG;
1208  goto fail;
1209  }
1210 
1211  err = find_or_load_image(fullname, len, type, flags, &image);
1212  if (image) {
1213  if (err_p)
1214  *err_p = Q_ERR_SUCCESS;
1215  return image - r_images;
1216  }
1217 
1218 fail:
1219  // don't spam about missing images
1220  if (err_p)
1221  *err_p = err;
1222  else if (err != Q_ERR_NOENT)
1223  Com_EPrintf("Couldn't load %s: %s\n", fullname, Q_ErrorString(err));
1224 
1225  return 0;
1226 }
1227 
1228 qhandle_t R_RegisterRawImage(const char *name, int width, int height, byte* pic, imagetype_t type, imageflags_t flags)
1229 {
1230  image_t *image;
1231  unsigned hash;
1232 
1233  int len = strlen(name);
1234  hash = FS_HashPathLen(name, len, RIMAGES_HASH);
1235 
1236  // look for it
1237  if ((image = lookup_image(name, type, hash, len)) != NULL) {
1238  image->flags |= flags & IF_PERMANENT;
1239  image->registration_sequence = registration_sequence;
1240  return image - r_images;
1241  }
1242 
1243  // allocate image slot
1244  image = alloc_image();
1245  if (!image) {
1246  return 0;
1247  }
1248 
1249  memcpy(image->name, name, len + 1);
1250  image->baselen = len;
1251  image->type = type;
1252  image->flags = flags;
1253  image->registration_sequence = registration_sequence;
1254  image->last_modified = 0;
1255  image->width = width;
1256  image->height = height;
1257  image->upload_width = width;
1258  image->upload_height = height;
1259 
1260  List_Append(&r_imageHash[hash], &image->entry);
1261 
1262  image->is_srgb = !!(flags & IF_SRGB);
1263 
1264  // upload the image
1265  IMG_Load(image, pic);
1266 
1267  return image - r_images;
1268 }
1269 
1270 void R_UnregisterImage(qhandle_t handle)
1271 {
1272  if (!handle)
1273  return;
1274 
1275  image_t* image = r_images + handle;
1276 
1277  if (image->registration_sequence)
1278  {
1279  image->registration_sequence = -1;
1280  IMG_FreeUnused();
1281  }
1282 }
1283 
1284 /*
1285 =============
1286 R_GetPicSize
1287 =============
1288 */
1289 qboolean R_GetPicSize(int *w, int *h, qhandle_t pic)
1290 {
1291  image_t *image = IMG_ForHandle(pic);
1292 
1293  if (w) {
1294  *w = image->width;
1295  }
1296  if (h) {
1297  *h = image->height;
1298  }
1299  return !!(image->flags & IF_TRANSPARENT);
1300 }
1301 
1302 /*
1303 ================
1304 IMG_FreeUnused
1305 
1306 Any image that was not touched on this registration sequence
1307 will be freed.
1308 ================
1309 */
1310 void IMG_FreeUnused(void)
1311 {
1312  image_t *image;
1313  int i, count = 0;
1314 
1315  for (i = 1, image = r_images + 1; i < r_numImages; i++, image++) {
1316  if (image->registration_sequence == registration_sequence) {
1317 #if USE_REF == REF_SOFT
1318  // TODO: account for MIPSIZE, TEX_BYTES
1319  Com_PageInMemory(image->pixels[0], image->upload_width * image->upload_height * 4);
1320 #endif
1321  continue; // used this sequence
1322  }
1323  if (!image->registration_sequence)
1324  continue; // free image_t slot
1325  if (image->flags & (IF_PERMANENT | IF_SCRAP))
1326  continue; // don't free pics
1327 
1328  // delete it from hash table
1329  List_Remove(&image->entry);
1330 
1331  // free it
1332  IMG_Unload(image);
1333 
1334  memset(image, 0, sizeof(*image));
1335  count++;
1336  }
1337 
1338  if (count) {
1339  Com_DPrintf("%s: %i images freed\n", __func__, count);
1340  }
1341 }
1342 
1343 void IMG_FreeAll(void)
1344 {
1345  image_t *image;
1346  int i, count = 0;
1347 
1348  for (i = 1, image = r_images + 1; i < r_numImages; i++, image++) {
1349  if (!image->registration_sequence)
1350  continue; // free image_t slot
1351  // free it
1352  IMG_Unload(image);
1353 
1354  memset(image, 0, sizeof(*image));
1355  count++;
1356  }
1357 
1358  if (count) {
1359  Com_DPrintf("%s: %i images freed\n", __func__, count);
1360  }
1361 
1362  for (i = 0; i < RIMAGES_HASH; i++) {
1363  List_Init(&r_imageHash[i]);
1364  }
1365 
1366  // &r_images[0] == R_NOTEXTURE
1367  r_numImages = 1;
1368 }
1369 
1370 /*
1371 ===============
1372 R_GetPalette
1373 
1374 ===============
1375 */
1376 void IMG_GetPalette(void)
1377 {
1378  byte pal[768], *src, *data;
1379  qerror_t ret;
1380  ssize_t len;
1381  int i;
1382 
1383  // get the palette
1384  len = FS_LoadFile(R_COLORMAP_PCX, (void **)&data);
1385  if (!data) {
1386  ret = len;
1387  goto fail;
1388  }
1389 
1390  ret = IMG_DecodePCX(data, len, NULL, pal, NULL, NULL);
1391 
1392  FS_FreeFile(data);
1393 
1394  if (ret < 0) {
1395  goto fail;
1396  }
1397 
1398  for (i = 0, src = pal; i < 255; i++, src += 3) {
1399  d_8to24table[i] = MakeColor(src[0], src[1], src[2], 255);
1400  }
1401 
1402  // 255 is transparent
1403  d_8to24table[i] = MakeColor(src[0], src[1], src[2], 0);
1404  return;
1405 
1406 fail:
1407  Com_Error(ERR_FATAL, "Couldn't load %s: %s", R_COLORMAP_PCX, Q_ErrorString(ret));
1408 }
1409 
1410 static const cmdreg_t img_cmd[] = {
1411  { "imagelist", IMG_List_f },
1412  { "screenshot", IMG_ScreenShot_f },
1413  { "screenshottga", IMG_ScreenShotTGA_f },
1414  { "screenshotjpg", IMG_ScreenShotJPG_f },
1415  { "screenshotpng", IMG_ScreenShotPNG_f },
1416  { NULL }
1417 };
1418 
1419 void IMG_Init(void)
1420 {
1421  int i;
1422 
1423  if (r_numImages) {
1424  Com_Error(ERR_FATAL, "%s: %d images not freed", __func__, r_numImages);
1425  }
1426 
1427 
1428  r_override_textures = Cvar_Get("r_override_textures", "1", CVAR_FILES);
1429  r_texture_formats = Cvar_Get("r_texture_formats", "pjt", 0);
1432 
1433  r_screenshot_format = Cvar_Get("gl_screenshot_format", "jpg", 0);
1434  r_screenshot_format = Cvar_Get("gl_screenshot_format", "png", 0);
1435  r_screenshot_quality = Cvar_Get("gl_screenshot_quality", "100", 0);
1436  r_screenshot_compression = Cvar_Get("gl_screenshot_compression", "6", 0);
1437 
1439 
1440  for (i = 0; i < RIMAGES_HASH; i++) {
1441  List_Init(&r_imageHash[i]);
1442  }
1443 
1444  // &r_images[0] == R_NOTEXTURE
1445  r_numImages = 1;
1446 }
1447 
1448 void IMG_Shutdown(void)
1449 {
1451  r_numImages = 0;
1452 }
R_RegisterImage
qhandle_t R_RegisterImage(const char *name, imagetype_t type, imageflags_t flags, qerror_t *err_p)
Definition: images.c:1170
handle
static void * handle
Definition: dynamic.c:52
r_texture_formats_changed
static void r_texture_formats_changed(cvar_t *self)
Definition: images.c:895
R_RegisterRawImage
qhandle_t R_RegisterRawImage(const char *name, int width, int height, byte *pic, imagetype_t type, imageflags_t flags)
Definition: images.c:1228
FS_EasyOpenFile
qhandle_t FS_EasyOpenFile(char *buf, size_t size, unsigned mode, const char *dir, const char *name, const char *ext)
Definition: files.c:1846
IMG_GetPalette
void IMG_GetPalette(void)
Definition: images.c:1376
r_screenshot_compression
static cvar_t * r_screenshot_compression
Definition: images.c:413
IMG_FreeAll
void IMG_FreeAll(void)
Definition: images.c:1343
height
static int height
Definition: physical_sky.c:39
Q_snprintf
size_t Q_snprintf(char *dest, size_t size, const char *fmt,...)
Definition: shared.c:846
image_t
struct image_s image_t
Definition: material.h:27
FS_Read
ssize_t FS_Read(void *buf, size_t len, qhandle_t f)
Definition: files.c:1547
FS_LastModified
qerror_t FS_LastModified(char const *file, uint64_t *last_modified)
Definition: files.c:1310
IMG_Load
void(* IMG_Load)(image_t *image, byte *pic)
Definition: refresh.c:431
channels
channel_t channels[MAX_CHANNELS]
Definition: main.c:29
r_screenshot_quality
static cvar_t * r_screenshot_quality
Definition: images.c:412
Cvar_Get
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
Definition: cvar.c:257
Q_ErrorString
const char * Q_ErrorString(qerror_t error)
Definition: error.c:51
r_texture_formats
static cvar_t * r_texture_formats
Definition: images.c:671
IMG_MipMap
void IMG_MipMap(byte *out, byte *in, int width, int height)
Definition: images.c:623
FS_FPrintf
ssize_t FS_FPrintf(qhandle_t f, const char *format,...)
Definition: files.c:2039
make_screenshot
static void make_screenshot(const char *name, const char *ext, qerror_t(*save)(qhandle_t, const char *, byte *, int, int, int, int), int param)
Definition: images.c:446
IMG_List_f
static void IMG_List_f(void)
Definition: images.c:678
IMG_FloodFill
static void IMG_FloodFill(byte *skin, int skinwidth, int skinheight)
Definition: images.c:87
IMG_SAVE
#define IMG_SAVE(x)
Definition: images.c:41
floodfill_t::x
short x
Definition: images.c:61
alloc_image
static image_t * alloc_image(void)
Definition: images.c:744
img_cmd
static const cmdreg_t img_cmd[]
Definition: images.c:1410
lookup_image
static image_t * lookup_image(const char *name, imagetype_t type, unsigned hash, size_t baselen)
Definition: images.c:766
try_image_format
static int try_image_format(imageformat_t fmt, image_t *image, byte **pic)
Definition: images.c:814
ext
char ext[4]
Definition: images.c:657
IMG_ResampleTexture
void IMG_ResampleTexture(const byte *in, int inwidth, int inheight, byte *out, int outwidth, int outheight)
Definition: images.c:577
IMG_Unpack8
static int IMG_Unpack8(uint32_t *out, const uint8_t *in, int width, int height)
Definition: images.c:222
IMG_ScreenShot_f
static void IMG_ScreenShot_f(void)
Definition: images.c:484
Cmd_Deregister
void Cmd_Deregister(const cmdreg_t *reg)
Definition: cmd.c:1580
Cmd_Argv
char * Cmd_Argv(int arg)
Definition: cmd.c:899
_try_image_format
static int _try_image_format(imageformat_t fmt, image_t *image, byte **pic)
Definition: images.c:787
Cmd_Argc
int Cmd_Argc(void)
Definition: cmd.c:889
RIMAGES_HASH
#define RIMAGES_HASH
Definition: images.c:647
width
static int width
Definition: physical_sky.c:38
FS_FOpenFile
ssize_t FS_FOpenFile(const char *name, qhandle_t *f, unsigned mode)
Definition: files.c:1692
FLOODFILL_FIFO_MASK
#define FLOODFILL_FIFO_MASK
Definition: images.c:66
FS_NormalizePathBuffer
size_t FS_NormalizePathBuffer(char *out, const char *in, size_t size)
Definition: files.c:400
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
img_search
static imageformat_t img_search[IM_MAX]
Definition: images.c:667
load
qerror_t(* load)(byte *, size_t, image_t *, byte **)
Definition: images.c:658
r_override_textures
static cvar_t * r_override_textures
Definition: images.c:670
r_numImages
int r_numImages
Definition: images.c:652
stbi_write
void stbi_write(void *context, void *data, int size)
Definition: images.c:45
r_imageHash
static list_t r_imageHash[RIMAGES_HASH]
Definition: images.c:649
IMG_ReadPixels
byte *(* IMG_ReadPixels)(int *width, int *height, int *rowbytes)
Definition: refresh.c:432
IMG_ScreenShotJPG_f
static void IMG_ScreenShotJPG_f(void)
Definition: images.c:533
IMG_Shutdown
void IMG_Shutdown(void)
Definition: images.c:1448
FLOODFILL_FIFO_SIZE
#define FLOODFILL_FIFO_SIZE
Definition: images.c:65
Cmd_Register
void Cmd_Register(const cmdreg_t *reg)
Definition: cmd.c:1572
try_other_formats
static int try_other_formats(imageformat_t orig, image_t *image, byte **pic)
Definition: images.c:823
floodfill_t
Definition: images.c:60
r_images
image_t r_images[MAX_RIMAGES]
Definition: images.c:651
IMG_LOAD
#define IMG_LOAD(x)
Definition: images.c:37
vid_rtx
cvar_t * vid_rtx
Definition: refresh.c:30
IMG_Unload
void(* IMG_Unload)(image_t *image)
Definition: refresh.c:430
IMG_Find
image_t * IMG_Find(const char *name, imagetype_t type, imageflags_t flags)
Definition: images.c:1122
FS_Write
ssize_t FS_Write(const void *buf, size_t len, qhandle_t f)
Definition: files.c:1643
find_or_load_image
static qerror_t find_or_load_image(const char *name, size_t len, imagetype_t type, imageflags_t flags, image_t **image_p)
Definition: images.c:991
img_loaders
static const struct @12 img_loaders[IM_MAX]
img_total
static int img_total
Definition: images.c:668
context
static ALCcontext * context
Definition: dynamic.c:54
IMG_ScreenShotPNG_f
static void IMG_ScreenShotPNG_f(void)
Definition: images.c:551
R_GetPicSize
qboolean R_GetPicSize(int *w, int *h, qhandle_t pic)
Definition: images.c:1289
registration_sequence
int registration_sequence
Definition: main.c:34
err
int err
Definition: win.h:24
R_UnregisterImage
void R_UnregisterImage(qhandle_t handle)
Definition: images.c:1270
FLOODFILL_STEP
#define FLOODFILL_STEP(off, dx, dy)
Definition: images.c:68
create_screenshot
static qhandle_t create_screenshot(char *buffer, size_t size, const char *name, const char *ext)
Definition: images.c:415
COM_DefaultExtension
size_t COM_DefaultExtension(char *path, const char *ext, size_t size)
Definition: shared.c:279
IMG_ForHandle
image_t * IMG_ForHandle(qhandle_t h)
Definition: images.c:1156
IMG_FreeUnused
void IMG_FreeUnused(void)
Definition: images.c:1310
FS_FCloseFile
void FS_FCloseFile(qhandle_t f)
Definition: files.c:759
Q_concat
size_t Q_concat(char *dest, size_t size,...)
Definition: shared.c:758
IMG_ScreenShotTGA_f
static void IMG_ScreenShotTGA_f(void)
Definition: images.c:523
Com_PageInMemory
void Com_PageInMemory(void *buffer, size_t size)
Definition: utils.c:383
int
CONST PIXELFORMATDESCRIPTOR int
Definition: wgl.c:26
get_image_dimensions
static void get_image_dimensions(imageformat_t fmt, image_t *image)
Definition: images.c:851
load_img
qerror_t load_img(const char *name, image_t *image)
Definition: images.c:927
r_screenshot_format
static cvar_t * r_screenshot_format
Definition: images.c:411
floodfill_t::y
short y
Definition: images.c:61
IMG_Init
void IMG_Init(void)
Definition: images.c:1419
FS_NormalizePath
size_t FS_NormalizePath(char *out, const char *in)
Definition: files.c:331
R_COLORMAP_PCX
#define R_COLORMAP_PCX
Definition: images.c:35
IMG_DecodePCX
static qerror_t IMG_DecodePCX(byte *rawdata, size_t rawlen, byte *pixels, byte *palette, int *width, int *height)
Definition: images.c:127
d_8to24table
uint32_t d_8to24table[256]
Definition: images.c:654