mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 17:48:46 -10:00
FSO doesn't appear to use this any longer. And even when it was enabled the requirements made it largely useless anyway. Removing to clean up cfile a bit and so we don't have to keep maintaining it.
348 lines
9.5 KiB
C++
348 lines
9.5 KiB
C++
/*
|
|
* 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 <cstdio>
|
|
#include <cstring>
|
|
|
|
#include "globalincs/pstypes.h"
|
|
#include "jpgutils/jpgutils.h"
|
|
#include "cfile/cfile.h"
|
|
#include "bmpman/bmpman.h"
|
|
#include "graphics/2d.h"
|
|
|
|
#undef LOCAL // fix for the jpeg header, pstypes.h has defined these macros
|
|
|
|
// fix a warning where jpeg and SDL headers collide
|
|
#ifdef HAVE_STDDEF_H
|
|
#undef HAVE_STDDEF_H
|
|
#endif
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
#undef HAVE_STDLIB_H
|
|
#endif
|
|
|
|
#include "jpeglib.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
|
|
//
|
|
|
|
// 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
|
|
throw std::runtime_error("JPEG_ERROR_READING");
|
|
}
|
|
|
|
// 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(const 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;
|
|
}
|
|
|
|
// 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(const 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", 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.
|
|
|
|
try {
|
|
// 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
|
|
|
|
// Actually check the precondition specified in the comment above
|
|
Assertion(dest_size == 3, "JPEG decompression currently only support 24-bit bitmap locking!");
|
|
|
|
// 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);
|
|
}
|
|
catch (const std::exception &e) {
|
|
mprintf(("jpgutils: error code %d (%s) while reading %s\n", jpeg_error_code, e.what(), real_filename));
|
|
}
|
|
|
|
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
|
|
}
|