File: jpgutils.cpp

package info (click to toggle)
freespace2 3.7.0%2Brepack-2
  • links: PTS, VCS
  • area: non-free
  • in suites: jessie, jessie-kfreebsd
  • size: 22,848 kB
  • ctags: 41,897
  • sloc: cpp: 369,931; makefile: 1,060; xml: 129; sh: 112
file content (340 lines) | stat: -rw-r--r-- 9,392 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/



#include <stdio.h>
#include <string.h>
#include <setjmp.h>

#include "jpeglib.h"

#undef LOCAL // fix from a jpeg header, pstypes.h will define it again

#include "globalincs/pstypes.h"
#include "jpgutils/jpgutils.h"
#include "cfile/cfile.h"
#include "bmpman/bmpman.h"
#include "palman/palman.h"
#include "graphics/2d.h"


// forward declarations
void jpeg_cfile_src(j_decompress_ptr cinfo, CFILE *cfp);

// structs
typedef struct {
  struct jpeg_source_mgr pub;	// public fields

  CFILE *infile;		// source stream
  JOCTET *buffer;		// start of buffer
  boolean start_of_file;	// have we gotten any data yet?
} cfile_source_mgr;

typedef cfile_source_mgr *cfile_src_ptr;
struct jpeg_decompress_struct jpeg_info;
struct jpeg_error_mgr jpeg_err;

#define INPUT_BUF_SIZE  4096	// choose an efficiently read'able size

static int jpeg_error_code;

// set current error
#define Jpeg_Set_Error(x)	{ jpeg_error_code = x; }

// error handler stuff, rather than the default, which will screw us
//
jmp_buf FSJpegError;

// error (exit) handler
void jpg_error_exit(j_common_ptr cinfo)
{
	// give an error message that isn't just generic
	(*cinfo->err->output_message) (cinfo);

	// do cleanup since we won't get the chance otherwise
	jpeg_destroy(cinfo);

	// set the error code, pretty much always going to be a read error
	Jpeg_Set_Error(JPEG_ERROR_READING);

	// bail
	longjmp(FSJpegError, 1);
}

// message handler for errors
void jpg_output_message(j_common_ptr cinfo)
{
	// JMSG_LENGTH_MAX should be 200, DO NOT change from this define so
	// that we don't risk overruns from the jpeg lib
	char buffer[JMSG_LENGTH_MAX];

	// Create the message 
	(*cinfo->err->format_message) (cinfo, buffer);

	// don't actually output anything unless we are a debug build, let bmpman
	// give any errors instead for release builds
#ifndef NDEBUG
	Warning(LOCATION, "%s %s", "JPEG Error:", buffer);
#endif
}



// Reads header information from the JPEG file into the bitmap pointer
// 
// filename - name of the JPEG bitmap file
// w - (output) width of the bitmap
// h - (output) height of the bitmap
// bpp - (output) bits per pixel of the bitmap
//
// returns - JPEG_ERROR_NONE if successful, otherwise error code
//
int jpeg_read_header(char *real_filename, CFILE *img_cfp, int *w, int *h, int *bpp, ubyte *palette)
{
	CFILE *jpeg_file = NULL;
	char filename[MAX_FILENAME_LEN];

	if (img_cfp == NULL) {
		strcpy_s( filename, real_filename );

		char *p = strchr( filename, '.' );

		if ( p )
			*p = 0;

		strcat_s( filename, ".jpg" );

		jpeg_file = cfopen( filename , "rb" );

		if ( !jpeg_file ) {
			return JPEG_ERROR_READING;
		}
	} else {
		jpeg_file = img_cfp;
	}

	Assert( jpeg_file != NULL );

	if (jpeg_file == NULL)
		return JPEG_ERROR_READING;

	// set the basic/default error code
	Jpeg_Set_Error(JPEG_ERROR_NONE);

	// initialize error message handler and decompression struct
	jpeg_info.err = jpeg_std_error(&jpeg_err);
	jpeg_err.error_exit = jpg_error_exit;
	jpeg_err.output_message = jpg_output_message;

	jpeg_create_decompress(&jpeg_info);

	// setup to read data via CFILE
	jpeg_cfile_src(&jpeg_info, jpeg_file);

	jpeg_read_header(&jpeg_info, TRUE);

	// send the info back out
	if (w) *w = jpeg_info.image_width;
	if (h) *h = jpeg_info.image_height;
	if (bpp) *bpp = (jpeg_info.num_components * 8);

	// cleanup
	jpeg_destroy_decompress(&jpeg_info);

	if (img_cfp == NULL) {
		cfclose(jpeg_file);
		jpeg_file = NULL;
	}

	return jpeg_error_code;
}


// Loads a JPEG image
// 
// filename - name of the targa file to load
// image_data - allocated storage for the bitmap
//
// returns - true if succesful, false otherwise
//
int jpeg_read_bitmap(char *real_filename, ubyte *image_data, ubyte *palette, int dest_size, int cf_type)
{
	char filename[MAX_FILENAME_LEN];
	CFILE *img_cfp = NULL;
	JSAMPARRAY buffer = NULL;

	strcpy_s( filename, real_filename );
	char *p = strchr( filename, '.' );
	if ( p ) *p = 0;
	strcat_s( filename, ".jpg" );


	img_cfp = cfopen(filename, "rb", CFILE_NORMAL, cf_type);

	if (img_cfp == NULL)
		return JPEG_ERROR_READING;

	// set the basic error code
	Jpeg_Set_Error(JPEG_ERROR_NONE);

	// initialize error message handler
	jpeg_info.err = jpeg_std_error(&jpeg_err);
	jpeg_err.error_exit = jpg_error_exit;
	jpeg_err.output_message = jpg_output_message;

	// SPECIAL NOTE: we've already allocated memory on the basis of original height, width and bpp
	// of the image from the header.  DO NOT change any settings that affect output variables here
	// or we risk needed more memory than we have available in "image_data".  The only exception is
	// bpp since 'dest_size' should already indicate what we want to end up with.

	if ( setjmp( FSJpegError ) == 0) {
		// initialize decompression struct
		jpeg_create_decompress(&jpeg_info);

		// setup to read data via CFILE
		jpeg_cfile_src(&jpeg_info, img_cfp);

		jpeg_read_header(&jpeg_info, TRUE);

		// memory for the storage of each scanline
		jpeg_calc_output_dimensions(&jpeg_info);

		// set the output components to be 'dest_size' (so we can support 16/24/32-bit images with this one function)
		// NOTE: only 24-bit is actually supported right now, we don't currently up/down sample at all
		jpeg_info.output_components = dest_size;
		jpeg_info.out_color_components = dest_size;	// may need/have to match above

		// multiplying by rec_outbuf_height isn't required but is more efficient
		int size = jpeg_info.output_width * jpeg_info.output_components * jpeg_info.rec_outbuf_height;
		// a standard malloc doesn't appear to work properly here (debug vm_malloc??), crashes in lib
		buffer = (*jpeg_info.mem->alloc_sarray)((j_common_ptr) &jpeg_info, JPOOL_IMAGE, size, 1); // DON'T free() THIS! jpeg lib does it

		// begin decompression process --
		jpeg_start_decompress(&jpeg_info);

		// read each scanline and output to previously allocated 'image_data'
		while (jpeg_info.output_scanline < jpeg_info.output_height) {
			jpeg_read_scanlines(&jpeg_info, buffer, 1);

			// niffiwan: swap some bytes (FSO uses BGR, not RGB) so that libjpeg
			// code is used in its original state
			// NOTE: assumes only one scanline is being read at a time. If
			// multiple lines are read this needs updating
			// also note that this doesn't deal with jpegs that are greyscale
			JSAMPLE tmp;
			for (int k = 2; k < size; k += 3) {
				tmp = buffer[0][k - 2];
				buffer[0][k - 2] = buffer[0][k];
				buffer[0][k] = tmp;
			}

			memcpy(image_data, *buffer, size);
			image_data += size;
		}

		// -- done with decompression process
		jpeg_finish_decompress(&jpeg_info);

		// cleanup
		jpeg_destroy_decompress(&jpeg_info);
	}

	cfclose(img_cfp);


	return jpeg_error_code;
}




// --------------------- handlers for reading of files -------------------------
// ---- basic copy of source from jdatasrc.c, originally:
//		Copyright (C) 1994-1996, Thomas G. Lane.

void jpeg_cf_init_source(j_decompress_ptr cinfo)
{
	cfile_src_ptr src = (cfile_src_ptr) cinfo->src;

	// We reset the empty-input-file flag for each image,
	// but we don't clear the input buffer.
	// This is correct behavior for reading a series of images from one source.
	src->start_of_file = TRUE;
}

boolean jpeg_cf_fill_input_buffer(j_decompress_ptr cinfo)
{
	cfile_src_ptr src = (cfile_src_ptr) cinfo->src;
	size_t nbytes;

	nbytes = cfread(src->buffer, 1, INPUT_BUF_SIZE, src->infile);

	if (nbytes <= 0) {
		if (src->start_of_file) {	// Treat empty input file as fatal error
			Jpeg_Set_Error(JPEG_ERROR_READING);
		}

		// Insert a fake EOI marker
		src->buffer[0] = (JOCTET) 0xFF;
		src->buffer[1] = (JOCTET) JPEG_EOI;
		nbytes = 2;

		return FALSE;
	}

	src->pub.next_input_byte = src->buffer;
	src->pub.bytes_in_buffer = nbytes;
	src->start_of_file = FALSE;

	return TRUE;
}

void jpeg_cf_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
	cfile_src_ptr src = (cfile_src_ptr) cinfo->src;

	if (num_bytes > 0) {
		while (num_bytes > (long) src->pub.bytes_in_buffer) {
			num_bytes -= (long) src->pub.bytes_in_buffer;

			if (!jpeg_cf_fill_input_buffer(cinfo))
				return;
		}

		src->pub.next_input_byte += (size_t) num_bytes;
		src->pub.bytes_in_buffer -= (size_t) num_bytes;
	}
}

void jpeg_cf_term_source(j_decompress_ptr cinfo)
{
	// no work necessary here
}

void jpeg_cfile_src(j_decompress_ptr cinfo, CFILE *cfp)
{
	cfile_src_ptr src;

	if (cinfo->src == NULL) {	// first time for this JPEG object?
		cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(cfile_source_mgr));
		src = (cfile_src_ptr) cinfo->src;
		src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET));
	}

	src = (cfile_src_ptr) cinfo->src;
	src->pub.init_source = jpeg_cf_init_source;
	src->pub.fill_input_buffer = jpeg_cf_fill_input_buffer;
	src->pub.skip_input_data = jpeg_cf_skip_input_data;
	src->pub.resync_to_restart = jpeg_resync_to_restart; // use default method
	src->pub.term_source = jpeg_cf_term_source;
	src->infile = cfp;
	src->pub.bytes_in_buffer = 0; // forces fill_input_buffer on first read
	src->pub.next_input_byte = NULL; // until buffer loaded
}