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