server/dep/src/g3dlite/GImage_jpeg.cpp
Lynx3d ae3ad10bcf [10097] Update G3D up to v8.0b4
+ Got rid of zip lib requirement in G3D...
  Still can re-enable code by defining _HAVE_ZIP...

+ Remove silly X11 lib dependency from G3D
  Code doesn't seem to do anything yet anyway, and even if, we don't want it :p

+ Fix another weird G3D build problem...

+ Remove some __asm usage in g3d, which is not available on Win64
  My editor also decided to remove a ton of trailing white spaces...tss...

+ Reapply G3D fixes for 64bit VC

+ not use SSE specific header when SSE not enabled in *nix

+ Updated project files

+ New vmap_assembler VC90/VC80 Project

+ vmap assembler binaries updates

NOTE: Old vmap fikes expected work (as tests show) with new library version.
      But better use new generated versions. Its different in small parts to bad or good...

(based on Lynx3d's repo commit 44798d3)

Signed-off-by: VladimirMangos <vladimir@getmangos.com>
2010-06-23 06:45:25 +04:00

446 lines
11 KiB
C++

/**
@file GImage_jpeg.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2009-04-20
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include <cstring>
extern "C" {
#ifdef G3D_LINUX
# include <jconfig.h>
# include <jpeglib.h>
#else
# include "jconfig.h"
# include "jpeglib.h"
#endif
}
namespace G3D {
const int jpegQuality = 96;
/**
The IJG library needs special setup for compress/decompressing
from memory. These classes provide them.
The format of this class is defined by the IJG library; do not
change it.
*/
class memory_destination_mgr {
public:
struct jpeg_destination_mgr pub;
JOCTET* buffer;
int size;
int count;
};
typedef memory_destination_mgr* mem_dest_ptr;
/**
Signature dictated by IJG.
*/
static void init_destination (
j_compress_ptr cinfo) {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = dest->size;
dest->count=0;
}
/**
Signature dictated by IJG.
*/
static boolean empty_output_buffer (
j_compress_ptr cinfo) {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = dest->size;
return TRUE;
}
/**
Signature dictated by IJG.
*/
static void term_destination (
j_compress_ptr cinfo) {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
dest->count = dest->size - dest->pub.free_in_buffer;
}
/**
Signature dictated by IJG.
*/
static void jpeg_memory_dest (
j_compress_ptr cinfo,
JOCTET* buffer,
int size) {
mem_dest_ptr dest;
if (cinfo->dest == NULL) {
// First time for this JPEG object; call the
// IJG allocator to get space.
cinfo->dest = (struct jpeg_destination_mgr*)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
JPOOL_PERMANENT,
sizeof(memory_destination_mgr));
}
dest = (mem_dest_ptr) cinfo->dest;
dest->size = size;
dest->buffer = buffer;
dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination;
}
////////////////////////////////////////////////////////////////////////////////////////
#define INPUT_BUF_SIZE 4096
/**
Structure dictated by IJG.
*/
class memory_source_mgr {
public:
struct jpeg_source_mgr pub;
int source_size;
unsigned char* source_data;
boolean start_of_data;
JOCTET* buffer;
};
typedef memory_source_mgr* mem_src_ptr;
/**
Signature dictated by IJG.
*/
static void init_source(
j_decompress_ptr cinfo) {
mem_src_ptr src = (mem_src_ptr) cinfo->src;
src->start_of_data = TRUE;
}
/**
Signature dictated by IJG.
*/
static boolean fill_input_buffer(
j_decompress_ptr cinfo) {
mem_src_ptr src = (mem_src_ptr) cinfo->src;
size_t bytes_read = 0;
if (src->source_size > INPUT_BUF_SIZE)
bytes_read = INPUT_BUF_SIZE;
else
bytes_read = src->source_size;
memcpy (src->buffer, src->source_data, bytes_read);
src->source_data += bytes_read;
src->source_size -= bytes_read;
src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer = bytes_read;
src->start_of_data = FALSE;
return TRUE;
}
/**
Signature dictated by IJG.
*/
static void skip_input_data(
j_decompress_ptr cinfo,
long num_bytes) {
mem_src_ptr src = (mem_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;
boolean s = fill_input_buffer(cinfo);
debugAssert(s); (void)s;
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
/**
Signature dictated by IJG.
*/
static void term_source (
j_decompress_ptr cinfo) {
(void)cinfo;
// Intentionally empty
}
/**
Signature dictated by IJG.
*/
static void jpeg_memory_src (
j_decompress_ptr cinfo,
JOCTET* buffer,
int size) {
mem_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(memory_source_mgr));
src = (mem_src_ptr)cinfo->src;
src->buffer = (JOCTET*)
(*cinfo->mem->alloc_small)(
(j_common_ptr) cinfo,
JPOOL_PERMANENT,
INPUT_BUF_SIZE * sizeof(JOCTET));
}
src = (mem_src_ptr)cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
// use default method
src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.term_source = term_source;
src->source_data = buffer;
src->source_size = size;
// forces fill_input_buffer on first read
src->pub.bytes_in_buffer = 0;
// until buffer loaded
src->pub.next_input_byte = NULL;
}
void GImage::encodeJPEG(
BinaryOutput& out) const {
if (m_channels != 3) {
// Convert to three channel
GImage tmp = *this;
tmp.convertToRGB();
tmp.encodeJPEG(out);
return;
}
debugAssert(m_channels == 3);
out.setEndian(G3D_LITTLE_ENDIAN);
// Allocate and initialize a compression object
jpeg_compress_struct cinfo;
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
// Specify the destination for the compressed data.
// (Overestimate the size)
int buffer_size = m_width * m_height * 3 + 200;
JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size);
jpeg_memory_dest(&cinfo, compressed_data, buffer_size);
cinfo.image_width = m_width;
cinfo.image_height = m_height;
// # of color components per pixel
cinfo.input_components = 3;
// colorspace of input image
cinfo.in_color_space = JCS_RGB;
cinfo.input_gamma = 1.0;
// Set parameters for compression, including image size & colorspace
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, jpegQuality, false);
cinfo.smoothing_factor = 0;
cinfo.optimize_coding = TRUE;
// cinfo.dct_method = JDCT_FLOAT;
cinfo.dct_method = JDCT_ISLOW;
cinfo.jpeg_color_space = JCS_YCbCr;
// Initialize the compressor
jpeg_start_compress(&cinfo, TRUE);
// Iterate over all scanlines from top to bottom
// pointer to a single row
JSAMPROW row_pointer[1];
// JSAMPLEs per row in image_buffer
int row_stride = cinfo.image_width * 3;
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &(m_byte[cinfo.next_scanline * row_stride]);
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
// Shut down the compressor
jpeg_finish_compress(&cinfo);
// Figure out how big the result was.
int outLength = ((mem_dest_ptr)cinfo.dest)->count;
// Release the JPEG compression object
jpeg_destroy_compress(&cinfo);
// Copy into an appropriately sized output buffer.
out.writeBytes(compressed_data, outLength);
// Free the conservative buffer.
System::free(compressed_data);
compressed_data = NULL;
}
void GImage::decodeJPEG(
BinaryInput& input) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
int loc = 0;
m_channels = 3;
// We have to set up the error handler, in case initialization fails.
cinfo.err = jpeg_std_error(&jerr);
// Initialize the JPEG decompression object.
jpeg_create_decompress(&cinfo);
// Specify data source (eg, a file, for us, memory)
jpeg_memory_src(&cinfo, const_cast<uint8*>(input.getCArray()), input.size());
// Read the parameters with jpeg_read_header()
jpeg_read_header(&cinfo, TRUE);
// Set parameters for decompression
// (We do nothing here since the defaults are fine)
// Start decompressor
jpeg_start_decompress(&cinfo);
// Get and set the values of interest to this object
m_width = cinfo.output_width;
m_height = cinfo.output_height;
// Prepare the pointer object for the pixel data
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
// JSAMPLEs per row in output buffer
int bpp = cinfo.output_components;
int row_stride = cinfo.output_width * bpp;
// Make a one-row-high sample array that will go away when done with image
JSAMPARRAY temp = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
// Read data on a scanline by scanline basis
while (cinfo.output_scanline < cinfo.output_height) {
// We may need to adjust the output based on the
// number of channels it has.
switch (bpp) {
case 1:
// Grayscale; decompress to temp.
jpeg_read_scanlines(&cinfo, temp, 1);
// Expand to three channels
{
uint8* scan = &(m_byte[loc * 3]);
uint8* endScan = scan + (m_width * 3);
uint8* t = *temp;
while (scan < endScan) {
uint8 value = t[0];
// Spread the value 3x.
scan[0] = value;
scan[1] = value;
scan[2] = value;
scan += 3;
t += 1;
}
}
break;
case 3:
// Read directly into the array
{
// Need one extra level of indirection.
uint8* scan = m_byte + loc;
JSAMPARRAY ptr = &scan;
jpeg_read_scanlines(&cinfo, ptr, 1);
}
break;
case 4:
// RGBA; decompress to temp.
jpeg_read_scanlines(&cinfo, temp, 1);
// Drop the 3rd channel
{
uint8* scan = &(m_byte[loc * 3]);
uint8* endScan = scan + m_width * 3;
uint8* t = *temp;
while (scan < endScan) {
scan[0] = t[0];
scan[1] = t[1];
scan[2] = t[2];
scan += 3;
t += 4;
}
}
break;
default:
throw Error("Unexpected number of channels.", input.getFilename());
}
loc += row_stride;
}
// Finish decompression
jpeg_finish_decompress(&cinfo);
alwaysAssertM(this, "Corrupt GImage");
// Release JPEG decompression object
jpeg_destroy_decompress(&cinfo);
}
}