UFO: Alien Invasion
Loading...
Searching...
No Matches
images.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2009 Quake2World.
8Copyright (C) 2002-2025 UFO: Alien Invasion.
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
19See the GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24*/
25
26#include "images.h"
27#include "shared.h"
28
29/* Workaround for a warning about redeclaring the macro. jpeglib sets this macro
30 * and SDL, too. */
31#undef HAVE_STDLIB_H
32
33#include <jpeglib.h>
34#include <png.h>
35#ifdef HAVE_LIBWEBP_DECODE_H
36# include <webp/decode.h>
37#endif
38#include <zlib.h>
39
40#if JPEG_LIB_VERSION < 80
41# include <jerror.h>
42#endif
43
45static char const* const IMAGE_TYPES[] = {
46#ifdef HAVE_LIBWEBP_DECODE_H
47 "webp",
48#endif
49 "png",
50 "jpg",
51 nullptr
52};
53
54#define TGA_UNMAP_COMP 10
55
56char const* const* Img_GetImageTypes (void)
57{
58 return IMAGE_TYPES;
59}
60
66void R_WritePNG (qFILE* f, byte* buffer, int width, int height)
67{
68 png_structp png_ptr;
69 png_infop info_ptr;
70 png_bytep* row_pointers;
71
72 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
73 if (!png_ptr) {
74 return;
75 }
76
77 info_ptr = png_create_info_struct(png_ptr);
78 if (!info_ptr) {
79 png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
80 return;
81 }
82
83 png_init_io(png_ptr, f->f);
84
85 png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
86 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
87
88 png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
89 png_set_compression_mem_level(png_ptr, 9);
90
91 png_write_info(png_ptr, info_ptr);
92
93 row_pointers = (png_bytep*)malloc(height * sizeof(png_bytep));
94 for (int i = 0; i < height; i++)
95 row_pointers[i] = buffer + (height - 1 - i) * 3 * width;
96
97 png_write_image(png_ptr, row_pointers);
98 png_write_end(png_ptr, info_ptr);
99
100 png_destroy_write_struct(&png_ptr, &info_ptr);
101
102 free(row_pointers);
103}
104
105#define TGA_CHANNELS 3
106
111void R_WriteCompressedTGA (qFILE* f, const byte* buffer, int width, int height)
112{
113 byte pixel_data[TGA_CHANNELS];
114 byte block_data[TGA_CHANNELS * 128];
115 byte rle_packet;
116 int compress = 0;
117 size_t block_length = 0;
118 byte header[18];
119 char footer[26];
120
121 OBJZERO(header);
122 OBJZERO(footer);
123
124 /* Fill in header */
125 /* byte 0: image ID field length */
126 /* byte 1: color map type */
127 header[2] = TGA_UNMAP_COMP; /* image type: Truecolor RLE encoded */
128 /* byte 3 - 11: palette data */
129 /* image width */
130 header[12] = width & 255;
131 header[13] = (width >> 8) & 255;
132 /* image height */
133 header[14] = height & 255;
134 header[15] = (height >> 8) & 255;
135 header[16] = 8 * TGA_CHANNELS; /* Pixel size 24 (RGB) or 32 (RGBA) */
136 header[17] = 0x20; /* Origin at bottom left */
137
138 /* write header */
139 FS_Write(header, sizeof(header), f);
140
141 for (int y = height - 1; y >= 0; y--) {
142 for (size_t x = 0; x < width; x++) {
143 const size_t index = y * width * TGA_CHANNELS + x * TGA_CHANNELS;
144 pixel_data[0] = buffer[index + 2];
145 pixel_data[1] = buffer[index + 1];
146 pixel_data[2] = buffer[index];
147
148 if (block_length == 0) {
149 memcpy(block_data, pixel_data, TGA_CHANNELS);
150 block_length++;
151 compress = 0;
152 } else {
153 if (!compress) {
154 /* uncompressed block and pixel_data differs from the last pixel */
155 if (memcmp(&block_data[(block_length - 1) * TGA_CHANNELS], pixel_data, TGA_CHANNELS) != 0) {
156 /* append pixel */
157 memcpy(&block_data[block_length * TGA_CHANNELS], pixel_data, TGA_CHANNELS);
158
159 block_length++;
160 } else {
161 /* uncompressed block and pixel data is identical */
162 if (block_length > 1) {
163 /* write the uncompressed block */
164 rle_packet = block_length - 2;
165 FS_Write(&rle_packet, 1, f);
166 FS_Write(block_data, (block_length - 1) * TGA_CHANNELS, f);
167 block_length = 1;
168 }
169 memcpy(block_data, pixel_data, TGA_CHANNELS);
170 block_length++;
171 compress = 1;
172 }
173 } else {
174 /* compressed block and pixel data is identical */
175 if (memcmp(block_data, pixel_data, TGA_CHANNELS) == 0) {
176 block_length++;
177 } else {
178 /* compressed block and pixel data differs */
179 if (block_length > 1) {
180 /* write the compressed block */
181 rle_packet = block_length + 127;
182 FS_Write(&rle_packet, 1, f);
183 FS_Write(block_data, TGA_CHANNELS, f);
184 block_length = 0;
185 }
186 memcpy(&block_data[block_length * TGA_CHANNELS], pixel_data, TGA_CHANNELS);
187 block_length++;
188 compress = 0;
189 }
190 }
191 }
192
193 if (block_length == 128) {
194 rle_packet = block_length - 1;
195 if (!compress) {
196 FS_Write(&rle_packet, 1, f);
197 FS_Write(block_data, 128 * TGA_CHANNELS, f);
198 } else {
199 rle_packet += 128;
200 FS_Write(&rle_packet, 1, f);
201 FS_Write(block_data, TGA_CHANNELS, f);
202 }
203
204 block_length = 0;
205 compress = 0;
206 }
207 }
208 }
209
210 /* write remaining bytes */
211 if (block_length) {
212 rle_packet = block_length - 1;
213 if (!compress) {
214 FS_Write(&rle_packet, 1, f);
215 FS_Write(block_data, block_length * TGA_CHANNELS, f);
216 } else {
217 rle_packet += 128;
218 FS_Write(&rle_packet, 1, f);
219 FS_Write(block_data, TGA_CHANNELS, f);
220 }
221 }
222
223 /* write footer (optional, but the specification recommends it) */
224 strncpy(&footer[8] , "TRUEVISION-XFILE", 16);
225 footer[24] = '.';
226 footer[25] = 0;
227 FS_Write(footer, sizeof(footer), f);
228}
229
234void R_WriteJPG (qFILE* f, byte* buffer, int width, int height, int quality)
235{
236 int offset, w3;
237 struct jpeg_compress_struct cinfo;
238 struct jpeg_error_mgr jerr;
239 byte* s;
240
241 /* Initialise the jpeg compression object */
242 cinfo.err = jpeg_std_error(&jerr);
243 jpeg_create_compress(&cinfo);
244 jpeg_stdio_dest(&cinfo, f->f);
245
246 /* Setup jpeg parameters */
247 cinfo.image_width = width;
248 cinfo.image_height = height;
249 cinfo.in_color_space = JCS_RGB;
250 cinfo.input_components = 3;
251 cinfo.progressive_mode = TRUE;
252
253 jpeg_set_defaults(&cinfo);
254 jpeg_set_quality(&cinfo, quality, TRUE);
255 jpeg_start_compress(&cinfo, TRUE); /* start compression */
256 jpeg_write_marker(&cinfo, JPEG_COM, (const byte*) "UFOAI", (uint32_t) 5);
257
258 /* Feed scanline data */
259 w3 = cinfo.image_width * 3;
260 offset = w3 * cinfo.image_height - w3;
261 while (cinfo.next_scanline < cinfo.image_height) {
262 s = &buffer[offset - (cinfo.next_scanline * w3)];
263 jpeg_write_scanlines(&cinfo, &s, 1);
264 }
265
266 /* Finish compression */
267 jpeg_finish_compress(&cinfo);
268 jpeg_destroy_compress(&cinfo);
269}
270
271static byte* readFile(char const* const name, char const* const suffix, size_t& len)
272{
273 char path[MAX_QPATH];
274 snprintf(path, sizeof(path), "%s.%s", name, suffix);
275 byte* buf = 0;
276 len = FS_LoadFile(path, &buf);
277 return buf;
278}
279
280static SDL_Surface* createSurface(int const height, int const width)
281{
282 return SDL_CreateRGBSurface(0, width, height, 32,
283#if SDL_BYTEORDER == SDL_BIG_ENDIAN
284 0xFF000000U, 0x00FF0000U, 0x0000FF00U, 0x000000FFU
285#else
286 0x000000FFU, 0x0000FF00U, 0x00FF0000U, 0xFF000000U
287#endif
288 );
289}
290
292{
293 byte* ptr;
294 byte const* end;
295};
296
297static void readMem(png_struct* const png, png_byte* const dst, png_size_t const size)
298{
299 bufState_t& state = *static_cast<bufState_t*>(png_get_io_ptr(png));
300 if (state.end - state.ptr < size) {
301 png_error(png, "premature end of input");
302 } else {
303 memcpy(dst, state.ptr, size);
304 state.ptr += size;
305 }
306}
307
313static SDL_Surface* Img_LoadPNG(char const* const name)
314{
315 SDL_Surface* res = 0;
316 size_t len;
317 if (byte* const buf = readFile(name, "png", len)) {
318 if (png_struct* png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)) {
319 png_info* info = png_create_info_struct(png);
320 if (info) {
321 bufState_t state = { buf, buf + len };
322 png_set_read_fn(png, &state, &readMem);
323
324 png_read_info(png, info);
325
326 png_uint_32 height;
327 png_uint_32 width;
328 int bit_depth;
329 int color_type;
330 png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type, 0, 0, 0);
331
332 /* Ensure that we always get a RGBA image with 8 bits per channel. */
333 png_set_gray_to_rgb(png);
334 png_set_strip_16(png);
335 png_set_packing(png);
336 png_set_expand(png);
337 png_set_add_alpha(png, 255, PNG_FILLER_AFTER);
338
339 if (SDL_Surface* const s = createSurface(height, width)) {
340 png_start_read_image(png);
341
342 png_byte* dst = static_cast<png_byte*>(s->pixels);
343 size_t const pitch = s->pitch;
344 for (size_t n = height; n != 0; dst += pitch, --n) {
345 png_read_row(png, dst, 0);
346 }
347
348 png_read_end(png, info);
349 res = s;
350 }
351 }
352
353 png_destroy_read_struct(&png, &info, 0);
354 }
355
357 }
358
359 return res;
360}
361
362#if JPEG_LIB_VERSION < 80
363static void djpeg_nop(jpeg_decompress_struct*) {}
364
365static boolean djpeg_fill_input_buffer(jpeg_decompress_struct* const cinfo)
366{
367 ERREXIT(cinfo, JERR_INPUT_EOF);
368 return false;
369}
370
371static void djpeg_skip_input_data(jpeg_decompress_struct* const cinfo, long const num_bytes)
372{
373 if (num_bytes < 0)
374 return;
375
376 jpeg_source_mgr& src = *cinfo->src;
377 if (src.bytes_in_buffer < (size_t)num_bytes)
378 ERREXIT(cinfo, JERR_INPUT_EOF);
379
380 src.next_input_byte += num_bytes;
381 src.bytes_in_buffer -= num_bytes;
382}
383#endif
384
390static SDL_Surface* Img_LoadJPG(char const* const name)
391{
392 SDL_Surface* res = 0;
393 size_t len;
394 if (byte* const buf = readFile(name, "jpg", len)) {
395 jpeg_decompress_struct cinfo;
396 jpeg_error_mgr jerr;
397
398 cinfo.err = jpeg_std_error(&jerr);
399 jpeg_create_decompress(&cinfo);
400
401#if JPEG_LIB_VERSION < 80
402 jpeg_source_mgr src;
403 src.next_input_byte = buf;
404 src.bytes_in_buffer = len;
405 src.init_source = &djpeg_nop;
406 src.fill_input_buffer = &djpeg_fill_input_buffer;
407 src.skip_input_data = &djpeg_skip_input_data;
408 src.resync_to_restart = &jpeg_resync_to_restart;
409 src.term_source = &djpeg_nop;
410 cinfo.src = &src;
411#else
412 jpeg_mem_src(&cinfo, buf, len);
413#endif
414
415 jpeg_read_header(&cinfo, TRUE);
416
417 cinfo.out_color_space = JCS_RGB;
418
419 if (SDL_Surface* const s = createSurface(cinfo.image_height, cinfo.image_width)) {
420 jpeg_start_decompress(&cinfo);
421
422 byte* dst = static_cast<byte*>(s->pixels);
423 size_t const pitch = s->pitch;
424 for (size_t n = cinfo.image_height; n != 0; dst += pitch, --n) {
425 JSAMPLE* lines[1] = { dst };
426 jpeg_read_scanlines(&cinfo, lines, 1);
427
428 /* Convert RGB to RGBA. */
429 for (size_t x = cinfo.image_width; x-- != 0;) {
430 dst[4 * x + 0] = dst[3 * x + 0];
431 dst[4 * x + 1] = dst[3 * x + 1];
432 dst[4 * x + 2] = dst[3 * x + 2];
433 dst[4 * x + 3] = 255;
434 }
435 }
436
437 jpeg_finish_decompress(&cinfo);
438 res = s;
439 }
440
441 jpeg_destroy_decompress(&cinfo);
443 }
444
445 return res;
446}
447
448
449#ifdef HAVE_LIBWEBP_DECODE_H
455static SDL_Surface* Img_LoadWebP(char const* const name)
456{
457 SDL_Surface* res = 0;
458 size_t len;
459
460 if (byte* const buf = readFile(name, "webp", len)) {
461 int height = 0;
462 int width = 0;
463 if (!WebPGetInfo(buf, len, &width, &height)) {
465 return nullptr;
466 }
467
468 if (SDL_Surface* const s = createSurface(height, width)) {
469 size_t imageSize = width * height * s->format->BytesPerPixel;
470 void* pixels = WebPDecodeRGBAInto(buf, len, (uint8_t*)s->pixels, imageSize,
471 width * s->format->BytesPerPixel);
472 res = pixels ? s : nullptr;
473 }
474
476 }
477
478 return res;
479}
480# endif
481
488SDL_Surface* Img_LoadImage (char const* name)
489{
490#ifdef HAVE_LIBWEBP_DECODE_H
491 if (SDL_Surface* const s = Img_LoadWebP(name))
492 return s;
493#endif
494 if (SDL_Surface* const s = Img_LoadPNG(name))
495 return s;
496 if (SDL_Surface* const s = Img_LoadJPG(name))
497 return s;
498 return 0;
499}
int FS_Write(const void *buffer, int len, qFILE *f)
Properly handles partial writes.
Definition files.cpp:1513
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
Definition files.cpp:384
void FS_FreeFile(void *buffer)
Definition files.cpp:411
#define MAX_QPATH
Definition filesys.h:40
char const *const * Img_GetImageTypes(void)
Definition images.cpp:56
static SDL_Surface * createSurface(int const height, int const width)
Definition images.cpp:280
void R_WriteJPG(qFILE *f, byte *buffer, int width, int height, int quality)
Definition images.cpp:234
static void djpeg_nop(jpeg_decompress_struct *)
Definition images.cpp:363
static boolean djpeg_fill_input_buffer(jpeg_decompress_struct *const cinfo)
Definition images.cpp:365
#define TGA_CHANNELS
Definition images.cpp:105
static SDL_Surface * Img_LoadJPG(char const *const name)
Loads a Jpeg image into an SDL_Surface.
Definition images.cpp:390
#define TGA_UNMAP_COMP
Definition images.cpp:54
void R_WriteCompressedTGA(qFILE *f, const byte *buffer, int width, int height)
Definition images.cpp:111
static void djpeg_skip_input_data(jpeg_decompress_struct *const cinfo, long const num_bytes)
Definition images.cpp:371
SDL_Surface * Img_LoadImage(char const *name)
Loads the specified image from the game filesystem into an SDL_Surface.
Definition images.cpp:488
static byte * readFile(char const *const name, char const *const suffix, size_t &len)
Definition images.cpp:271
static SDL_Surface * Img_LoadPNG(char const *const name)
Loads a PNG image into an SDL_Surface.
Definition images.cpp:313
static char const *const IMAGE_TYPES[]
Definition images.cpp:45
void R_WritePNG(qFILE *f, byte *buffer, int width, int height)
Definition images.cpp:66
static void readMem(png_struct *const png, png_byte *const dst, png_size_t const size)
Definition images.cpp:297
Image loading and saving functions.
voidpf void uLong size
Definition ioapi.h:42
voidpf void * buf
Definition ioapi.h:42
voidpf uLong offset
Definition ioapi.h:45
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN GLuint index
Definition r_gl.h:110
QGL_EXTERN GLfloat f
Definition r_gl.h:114
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
#define OBJZERO(obj)
Definition shared.h:178
byte const * end
Definition images.cpp:294
byte * ptr
Definition images.cpp:293