AROS-v0/workbench/classes/datatypes/jpeg/jpegclass.c

568 lines
18 KiB
C
Executable File

/*
Copyright (C) 1995-2005, The AROS Development Team. All rights reserved.
*/
/**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dostags.h>
#include <graphics/gfxbase.h>
#include <graphics/rpattr.h>
#include <cybergraphx/cybergraphics.h>
#include <intuition/imageclass.h>
#include <intuition/icclass.h>
#include <intuition/gadgetclass.h>
#include <intuition/cghooks.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/pictureclass.h>
#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/utility.h>
#include <proto/iffparse.h>
#include <proto/datatypes.h>
#include <aros/symbolsets.h>
//#include <jinclude.h>
#include <jpeglib.h>
#include <jerror.h>
#include <setjmp.h>
#include "debug.h"
#include "methods.h"
ADD2LIBS("datatypes/picture.datatype", 0, struct Library *, PictureBase);
/**************************************************************************************************/
#define QUALITY 90 /* compress quality for saving */
typedef struct {
struct IFFHandle *filehandle;
UBYTE *linebuf;
} JpegHandleType;
struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct my_error_mgr * my_error_ptr;
METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
my_error_ptr myerr = (my_error_ptr) cinfo->err;
/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
D(bug("jpeg.datatype/libjpeg: Fatal Error !\n"));
(*cinfo->err->output_message) (cinfo);
/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}
METHODDEF(void)
my_output_message (j_common_ptr cinfo)
{
D(
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
(*cinfo->err->format_message) (cinfo, buffer);
/* Display debug output, adding a newline */
bug("jpeg.datatype/libjpeg: %s\n", buffer)
);
}
typedef struct {
struct jpeg_source_mgr pub; /* public fields */
FILE * infile; /* source stream */
JOCTET * buffer; /* start of buffer */
boolean start_of_file; /* have we gotten any data yet? */
} my_source_mgr;
typedef my_source_mgr * my_src_ptr;
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
METHODDEF(boolean)
my_fill_input_buffer (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
size_t nbytes;
// D(bug("jpeg.datatype/my_fill_input_buffer\n"));
nbytes = Read(MKBADDR(src->infile), src->buffer, INPUT_BUF_SIZE);
if (nbytes <= 0) {
if (src->start_of_file) /* Treat empty input file as fatal error */
ERREXIT(cinfo, JERR_INPUT_EMPTY);
WARNMS(cinfo, JWRN_JPEG_EOF);
/* Insert a fake EOI marker */
src->buffer[0] = (JOCTET) 0xFF;
src->buffer[1] = (JOCTET) JPEG_EOI;
nbytes = 2;
}
src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer = nbytes;
src->start_of_file = FALSE;
return TRUE;
}
METHODDEF(void)
my_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
my_src_ptr src = (my_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;
(void)my_fill_input_buffer(cinfo);
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
/* Dummy functions for the linker */
void abort(void)
{
exit(1);
}
void exit(int bla)
{
D(bug("jpeg.datatype/exit\n"));
abort();
}
/**************************************************************************************************/
static void JPEG_Exit(JpegHandleType *jpeghandle, LONG errorcode)
{
D(if (errorcode) bug("jpeg.datatype/JPEG_Exit(): IoErr %ld\n", errorcode));
if( jpeghandle->linebuf )
FreeVec( jpeghandle->linebuf );
FreeMem(jpeghandle, sizeof(JpegHandleType));
SetIoErr(errorcode);
}
/**************************************************************************************************/
static BOOL LoadJPEG(struct IClass *cl, Object *o)
{
JpegHandleType *jpeghandle;
union {
struct IFFHandle *iff;
BPTR bptr;
} filehandle;
long width, height;
IPTR sourcetype;
struct BitMapHeader *bmhd;
struct ColorRegister *colormap;
ULONG *colorregs;
ULONG numcolors;
STRPTR name;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
JSAMPARRAY buffer; /* Output row buffer */
int row_stride; /* physical row width in output buffer */
my_src_ptr src;
D(bug("jpeg.datatype/LoadJPEG()\n"));
if( !(jpeghandle = AllocMem(sizeof(JpegHandleType), MEMF_ANY)) )
{
SetIoErr(ERROR_NO_FREE_STORE);
return FALSE;
}
jpeghandle->linebuf = NULL;
if( GetDTAttrs(o, DTA_SourceType , (IPTR)&sourcetype ,
DTA_Handle , (IPTR)&filehandle,
PDTA_BitMapHeader , (IPTR)&bmhd,
TAG_DONE) != 3 )
{
JPEG_Exit(jpeghandle, ERROR_OBJECT_NOT_FOUND);
return FALSE;
}
if ( sourcetype == DTST_RAM && filehandle.iff == NULL && bmhd )
{
D(bug("jpeg.datatype/LoadJPEG(): Creating an empty object\n"));
JPEG_Exit(jpeghandle, ERROR_NOT_IMPLEMENTED);
return TRUE;
}
if ( sourcetype != DTST_FILE || !filehandle.bptr || !bmhd )
{
D(bug("jpeg.datatype/LoadJPEG(): unsupported mode\n"));
JPEG_Exit(jpeghandle, ERROR_NOT_IMPLEMENTED);
return FALSE;
}
D(bug("jpeg.datatype/LoadJPEG(): Setting error handler\n"));
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
if (setjmp(jerr.setjmp_buffer)) {
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
jpeg_destroy_decompress(&cinfo);
JPEG_Exit(jpeghandle, 1);
return FALSE;
}
D(bug("jpeg.datatype/LoadJPEG(): Create decompressor\n"));
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, BADDR(filehandle.bptr));
src = (my_src_ptr) cinfo.src;
src->pub.fill_input_buffer = my_fill_input_buffer;
src->pub.skip_input_data = my_skip_input_data;
D(bug("jpeg.datatype/LoadJPEG(): Read Header\n"));
(void) jpeg_read_header(&cinfo, TRUE);
if (cinfo.output_components == 1)
{
// force loading as a 8-bit greyscale image
cinfo.out_color_space = JCS_GRAYSCALE;
}
D(bug("jpeg.datatype/LoadJPEG(): Starting decompression\n"));
(void) jpeg_start_decompress(&cinfo);
/* set BitMapHeader with image size */
bmhd->bmh_Width = bmhd->bmh_PageWidth = width = cinfo.output_width;
bmhd->bmh_Height = bmhd->bmh_PageHeight = height = cinfo.output_height;
bmhd->bmh_Depth = 24;
D(bug("jpeg.datatype/LoadJPEG(): Size %ld x %ld x %d bit\n", width, height, (int)(cinfo.output_components*8)));
if (cinfo.output_components != 1 && cinfo.output_components != 3) /* disallow images with no. components not eq to 1 or 3 */
{
D(bug("jpeg.datatype/LoadJPEG(): unsupported colormode\n"));
JPEG_Exit(jpeghandle, ERROR_NOT_IMPLEMENTED);
return FALSE;
}
int x;
int comp = 3;
numcolors = 0;
ULONG pixelfmt = PBPAFMT_RGB; /* set default to rgb colorspace & image data */
/* set colorspace & image data for 8bit */
if (cinfo.output_components == 1)
{
D(bug("jpeg.datatype/LoadJPEG(): Loading 8bit Greyscale Image.\n"));
comp = 1;
numcolors = 256;
bmhd->bmh_Depth = 8;
pixelfmt = PBPAFMT_LUT8;
D(bug("jpeg.datatype/LoadJPEG(): Colors %ld\n", numcolors));
if( !(GetDTAttrs(o, PDTA_ColorRegisters, (IPTR)&colormap,
PDTA_CRegs, (IPTR)&colorregs,
TAG_DONE ) == 2) ||
!(colormap && colorregs) )
{
JPEG_Exit(jpeghandle, ERROR_OBJECT_NOT_FOUND);
return FALSE;
}
/* Make Greyscale Palette */
for(x=0; x<numcolors; x++)
{
/* greyscale */
colormap->red = x;
colormap->green = x;
colormap->blue = x;
colormap++;
*colorregs++ = (ULONG)x * 0x01010101;
*colorregs++ = (ULONG)x * 0x01010101;
*colorregs++ = (ULONG)x * 0x01010101;
}
SetDTAttrs(o, NULL, NULL, PDTA_NumColors, numcolors, TAG_DONE);
}
/* Make a one-row-high sample array that will go away when done with image */
row_stride = width * comp; /* Set generic row_stride instead of [row_stride = width * 3] */
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
/* Here we use the library's state variable cinfo.output_scanline as the
* loop counter, so that we don't have to keep track ourselves.
*/
while (cinfo.output_scanline < height)
{
/* jpeg_read_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could ask for
* more than one scanline at a time if that's more convenient.
*/
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
// D(bug("jpeg.datatype/LoadJPEG(): Copy line %ld\n", (long)cinfo.output_scanline));
if(!DoSuperMethod(cl, o,
PDTM_WRITEPIXELARRAY, /* Method_ID */
(IPTR) buffer[0], /* PixelData */
pixelfmt, /* Use PixelFormat */
row_stride, /* Use PixelArrayMod (number of bytes per row) */
0, /* Left edge */
cinfo.output_scanline-1, /* Top edge */
width, /* Width */
1)) /* Height (here: one line) */
{
D(bug("jpeg.datatype/LoadJPEG(): WRITEPIXELARRAY failed\n"));
JPEG_Exit(jpeghandle, ERROR_OBJECT_NOT_FOUND);
return FALSE;
}
}
D(bug("jpeg.datatype/LoadJPEG(): WRITEPIXELARRAY of whole picture done\n"));
D(bug("jpeg.datatype/LoadJPEG(): Clean up\n"));
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
/* Pass picture size to picture.datatype */
GetDTAttrs( o, DTA_Name, (IPTR)&name, TAG_DONE );
SetDTAttrs(o, NULL, NULL, DTA_NominalHoriz, width,
DTA_NominalVert , height,
DTA_ObjName , (IPTR)name,
TAG_DONE);
D(bug("jpeg.datatype/LoadJPEG(): Normal Exit\n"));
JPEG_Exit(jpeghandle, 0);
return TRUE;
}
/**************************************************************************************************/
typedef struct {
struct jpeg_destination_mgr pub; /* public fields */
FILE * outfile; /* target stream */
JOCTET * buffer; /* start of buffer */
} my_destination_mgr;
typedef my_destination_mgr * my_dest_ptr;
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
METHODDEF(boolean)
my_empty_output_buffer (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
// D(bug("jpeg.datatype/my_empty_output_buffer\n"));
if (Write(MKBADDR(dest->outfile), dest->buffer, OUTPUT_BUF_SIZE) !=
(size_t) 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;
}
METHODDEF(void)
my_term_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
/* Write any data remaining in the buffer */
if (datacount > 0) {
if (Write(MKBADDR(dest->outfile), dest->buffer, datacount) != datacount)
ERREXIT(cinfo, JERR_FILE_WRITE);
}
}
/**************************************************************************************************/
static BOOL SaveJPEG(struct IClass *cl, Object *o, struct dtWrite *dtw )
{
JpegHandleType *jpeghandle;
BPTR filehandle;
unsigned int width, height, numplanes, comp;
UBYTE *linebuf;
struct BitMapHeader *bmhd;
long *colorregs;
ULONG pixelfmt;
struct jpeg_compress_struct cinfo;
struct my_error_mgr jerr;
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in output buffer */
my_dest_ptr dest;
D(bug("jpeg.datatype/SaveJPEG()\n"));
/* A NULL file handle is a NOP */
if( !dtw->dtw_FileHandle )
{
D(bug("jpeg.datatype/SaveJPEG(): empty Filehandle - just testing\n"));
return TRUE;
}
filehandle = dtw->dtw_FileHandle;
/* Get BitMapHeader and color palette */
if( GetDTAttrs( o, PDTA_BitMapHeader, (IPTR) &bmhd,
PDTA_CRegs, (IPTR) &colorregs,
TAG_DONE ) != 2UL ||
!bmhd || !colorregs )
{
D(bug("jpeg.datatype/SaveJPEG(): missing attributes\n"));
SetIoErr(ERROR_OBJECT_WRONG_TYPE);
return FALSE;
}
width = bmhd->bmh_Width;
height = bmhd->bmh_Height;
numplanes = bmhd->bmh_Depth;
if(( numplanes != 8 ) && ( numplanes != 24 )) /* disallow images with numplanes not eq to 8 or 24 */
{
D(bug("jpeg.datatype/SaveJPEG(): color depth %d, can save only depths of 24\n", numplanes));
SetIoErr(ERROR_OBJECT_WRONG_TYPE);
return FALSE;
}
D(bug("jpeg.datatype/SaveJPEG(): Picture size %d x %d (x %d bit)\n", width, height, numplanes));
if( !(jpeghandle = AllocMem(sizeof(JpegHandleType), MEMF_ANY)) )
{
SetIoErr(ERROR_NO_FREE_STORE);
return FALSE;
}
jpeghandle->linebuf = NULL;
D(bug("jpeg.datatype/SaveJPEG(): Setting error handler\n"));
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
if (setjmp(jerr.setjmp_buffer)) {
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
jpeg_destroy_compress(&cinfo);
JPEG_Exit(jpeghandle, 1);
return FALSE;
}
D(bug("jpeg.datatype/SaveJPEG(): Create compressor\n"));
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, (FILE *)filehandle);
dest = (my_dest_ptr) cinfo.dest;
dest->pub.empty_output_buffer = my_empty_output_buffer;
dest->pub.term_destination = my_term_destination;
/* determine colorspace & image data by bitdepth */
if(numplanes == 8)
{
comp = 1;
cinfo.in_color_space = JCS_GRAYSCALE; /* greyscale colorspace of input image */
cinfo.input_components = 1; /* # of color components per pixel */
pixelfmt = PBPAFMT_LUT8;
}
else if(numplanes == 24)
{
comp = 3;
cinfo.in_color_space = JCS_RGB; /* rgb colorspace of input image */
cinfo.input_components = 3; /* # of color components per pixel */
pixelfmt = PBPAFMT_RGB;
}
row_stride = width * comp;
cinfo.image_width = width; /* image width and height, in pixels */
cinfo.image_height = height;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, QUALITY, TRUE /* limit to baseline-JPEG values */);
D(bug("jpeg.datatype/SaveJPEG(): Starting compression\n"));
jpeg_start_compress(&cinfo, TRUE);
/* Now read the picture data line by line and write it to a chunky buffer */
if( !(linebuf = AllocVec(row_stride, MEMF_ANY)) ) /* allocate memory using row_stride */
{
JPEG_Exit(jpeghandle, ERROR_NO_FREE_STORE);
return FALSE;
}
jpeghandle->linebuf = linebuf;
row_pointer[0] = linebuf;
while (cinfo.next_scanline < cinfo.image_height)
{
// D(bug("jpeg.datatype/SaveJPEG(): READPIXELARRAY line %ld\n", (long)cinfo.next_scanline));
if(!DoSuperMethod(cl, o,
PDTM_READPIXELARRAY, /* Method_ID */
(IPTR)linebuf, /* PixelData */
pixelfmt, /* PixelFormat */
row_stride, /* PixelArrayMod (number of bytes per row) */
0, /* Left edge */
cinfo.next_scanline, /* Top edge */
width, /* Width */
1)) /* Height */
{
D(bug("jpeg.datatype/SaveJPEG(): READPIXELARRAY failed!\n"));
JPEG_Exit(jpeghandle, ERROR_OBJECT_WRONG_TYPE);
return FALSE;
}
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
D(bug("jpeg.datatype/SaveJPEG() --- Normal Exit\n"));
JPEG_Exit(jpeghandle, 0);
return TRUE;
}
/**************************************************************************************************/
IPTR JPEG__OM_NEW(Class *cl, Object *o, Msg msg)
{
Object *newobj;
D(bug("jpeg.datatype/DT_Dispatcher: Method OM_NEW\n"));
newobj = (Object *)DoSuperMethodA(cl, o, (Msg)msg);
if (newobj)
{
if (!LoadJPEG(cl, newobj))
{
CoerceMethod(cl, newobj, OM_DISPOSE);
newobj = NULL;
}
}
return (IPTR)newobj;
}
/**************************************************************************************************/
IPTR JPEG__DTM_WRITE(Class *cl, Object *o, struct dtWrite *dtw)
{
D(bug("jpeg.datatype/DT_Dispatcher: Method DTM_WRITE\n"));
if( (dtw -> dtw_Mode) == DTWM_RAW )
{
/* Local data format requested */
return SaveJPEG(cl, o, dtw );
}
else
{
/* Pass msg to superclass (which writes an IFF ILBM picture)... */
return DoSuperMethodA( cl, o, (Msg)dtw );
}
}
/**************************************************************************************************/