#include "CImageWriterJPG.h"
#ifdef _IRR_COMPILE_WITH_JPG_WRITER_
#include "CColorConverter.h"
#include "IWriteFile.h"
#include "CImage.h"
#include "irrString.h"
#ifdef _IRR_COMPILE_WITH_LIBJPEG_
#include <stdio.h>
extern "C"
{
#ifndef _IRR_USE_NON_SYSTEM_JPEG_LIB_
#include <jpeglib.h>
#include <jerror.h>
#else
#include "jpeglib/jpeglib.h"
#include "jpeglib/jerror.h"
#endif
}
namespace irr
{
namespace video
{
#define OUTPUT_BUF_SIZE 4096
typedef struct
{
struct jpeg_destination_mgr pub;
io::IWriteFile* file;
JOCTET buffer[OUTPUT_BUF_SIZE];
} mem_destination_mgr;
typedef mem_destination_mgr * mem_dest_ptr;
static void jpeg_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 = OUTPUT_BUF_SIZE;
}
static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
{
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
if (dest->file->write(dest->buffer, OUTPUT_BUF_SIZE) != OUTPUT_BUF_SIZE)
ERREXIT (cinfo, JERR_FILE_WRITE);
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
return TRUE;
}
static void jpeg_term_destination(j_compress_ptr cinfo)
{
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
const s32 datacount = (s32)(OUTPUT_BUF_SIZE - dest->pub.free_in_buffer);
if (dest->file->write(dest->buffer, datacount) != datacount)
ERREXIT (cinfo, JERR_FILE_WRITE);
}
static void jpeg_file_dest(j_compress_ptr cinfo, io::IWriteFile* file)
{
if (cinfo->dest == NULL)
{
cinfo->dest = (struct jpeg_destination_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
JPOOL_PERMANENT,
sizeof(mem_destination_mgr));
}
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
dest->pub.init_destination = jpeg_init_destination;
dest->pub.empty_output_buffer = jpeg_empty_output_buffer;
dest->pub.term_destination = jpeg_term_destination;
dest->file = file;
}
static bool writeJPEGFile(io::IWriteFile* file, IImage* image, u32 quality)
{
void (*format)(const void*, s32, void*) = 0;
switch( image->getColorFormat () )
{
case ECF_R8G8B8:
format = CColorConverter::convert_R8G8B8toR8G8B8;
break;
case ECF_A8R8G8B8:
format = CColorConverter::convert_A8R8G8B8toR8G8B8;
break;
case ECF_A1R5G5B5:
format = CColorConverter::convert_A1R5G5B5toB8G8R8;
break;
case ECF_R5G6B5:
format = CColorConverter::convert_R5G6B5toR8G8B8;
break;
}
if ( 0 == format )
return false;
const core::dimension2du dim = image->getDimension();
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_file_dest(&cinfo, file);
cinfo.image_width = dim.Width;
cinfo.image_height = dim.Height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
if ( 0 == quality )
quality = 75;
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
u8 * dest = new u8[dim.Width*3];
if (dest)
{
const u32 pitch = image->getPitch();
JSAMPROW row_pointer[1];
row_pointer[0] = dest;
u8* src = (u8*)image->lock();
while (cinfo.next_scanline < cinfo.image_height)
{
format( src, dim.Width, dest );
src += pitch;
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
image->unlock();
delete [] dest;
jpeg_finish_compress(&cinfo);
}
jpeg_destroy_compress(&cinfo);
return (dest != 0);
}
}
}
#endif
namespace irr
{
namespace video
{
IImageWriter* createImageWriterJPG()
{
return new CImageWriterJPG;
}
CImageWriterJPG::CImageWriterJPG()
{
#ifdef _DEBUG
setDebugName("CImageWriterJPG");
#endif
}
bool CImageWriterJPG::isAWriteableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "jpg", "jpeg" );
}
bool CImageWriterJPG::writeImage(io::IWriteFile *file, IImage *image, u32 quality) const
{
#ifndef _IRR_COMPILE_WITH_LIBJPEG_
return false;
#else
return writeJPEGFile(file, image, quality);
#endif
}
}
}
#endif