JPEGReader.st
author Claus Gittinger <cg@exept.de>
Wed, 11 Feb 2009 12:43:05 +0100
changeset 2594 b59b02924f4f
parent 2284 cebd50e9da1c
child 2768 38e9b42f2af8
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1993 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

"{ Package: 'stx:libview2' }"

ImageReader subclass:#JPEGReader
	instanceVariableNames:'jpeg_decompress_struct jpeg_error_mgr_struct colorComponents
		forceGrayscale forceDitherMode'
	classVariableNames:'ErrorPrinting'
	poolDictionaries:''
	category:'Graphics-Images-Readers'
!

!JPEGReader primitiveDefinitions!
%{

/*
 * includes, defines, structure definitions
 * and typedefs come here.
 */

#include <stdio.h>

#ifdef sunos
# include <sys/types.h>
#endif

#include <jpeglib.h>
#include <jerror.h>

struct my_error_mgr {
	struct jpeg_error_mgr pub;
	jmp_buf setjmp_buffer;
};

%}
! !

!JPEGReader primitiveFunctions!
%{

/*
 * any local C (helper) functions
 * come here (please, define as static)
 */

static
my_error_exit(cinfo)
    j_common_ptr cinfo;
{
    struct my_error_mgr *myerrPtr = (struct my_error_mgr *) cinfo->err;

    if (@global(ErrorPrinting) == true) {
	console_fprintf(stderr, "JPEGReader [warning]: jpeg error\n");
    }
    longjmp(myerrPtr->setjmp_buffer, 1);
}

static
my_output_message(cinfo)
    j_common_ptr cinfo;
{
    char buffer[JMSG_LENGTH_MAX];

    if (@global(ErrorPrinting) == true) {
	console_fprintf(stderr, "libJPEG [error]: ");

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

       /* Send it to stderr, adding a newline */
       console_fprintf(stderr, "%s\n", buffer);
    }
}

/*
 * Optional progress monitor: display a percent-done figure on stderr.
 */

#ifdef PROGRESS_REPORT

void
JPG_progress_monitor (j_common_ptr cinfo)
{
  cd_progress_ptr prog = (cd_progress_ptr) cinfo->progress;
  int total_passes = prog->pub.total_passes + prog->total_extra_passes;
  int percent_done = (int) (prog->pub.pass_counter*100L/prog->pub.pass_limit);

  if (percent_done != prog->percent_done) {
    prog->percent_done = percent_done;
    if (total_passes > 1) {
      console_fprintf("\rPass %d/%d: %3d%% ",
	      prog->pub.completed_passes + prog->completed_extra_passes + 1,
	      total_passes, percent_done);
    } else {
      console_fprintf("\r %3d%% ", percent_done);
    }
    console_fflush(stderr);
  }
}


void
JPG_start_progress_monitor (j_common_ptr cinfo, cd_progress_ptr progress)
{
  /* Enable progress display, unless trace output is on */
  if (cinfo->err->trace_level == 0) {
    progress->pub.progress_monitor = progress_monitor;
    progress->completed_extra_passes = 0;
    progress->total_extra_passes = 0;
    progress->percent_done = -1;
    cinfo->progress = &progress->pub;
  }
}


void
JPG_end_progress_monitor (j_common_ptr cinfo)
{
  /* Clear away progress display */
  if (cinfo->err->trace_level == 0) {
    console_fprintf(stderr, "\r                \r");
  }
}

#endif

%}
! !

!JPEGReader class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1993 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    Reader for JPEG images.

    This uses the libpeg library to read the image

    Only reading of files is supported.

    [See also:]
	Image Form Icon
	BlitImageReader FaceReader GIFReader PBMReader PCXReader
	ST80FormReader SunRasterReader TargaReader TIFFReader WindowsIconReader
	XBMReader XPMReader XWDReader
"
! !

!JPEGReader class methodsFor:'initialization'!

initialize
    "install myself in the Image classes fileFormat table
     for the `.jpg' and '.jpeg' extensions."

    MIMETypes defineImageType:'image/jpeg' suffix:'jpg'  reader:self.
    MIMETypes defineImageType:nil          suffix:'jpeg' reader:self.

    "Modified: 1.2.1997 / 15:01:55 / cg"
! !

!JPEGReader class methodsFor:'testing'!

isValidImageFile:aFileName
    "return true, if aFileName contains a JPG image.
     Only look at the files name here ..."

    (aFileName asString asLowercase endsWith:'.jpg') ifTrue:[^ true].
    (aFileName asString asLowercase endsWith:'.jpeg') ifTrue:[^ true].
    ^ false

    "Created: 11.4.1997 / 16:26:25 / cg"
! !

!JPEGReader methodsFor:'accessing'!

forceDitherMode:something
    "set the dither mode, to one of #none or #ordered"

    forceDitherMode := something.

    "Modified: 16.4.1997 / 20:34:59 / cg"
!

forceGrayscale:something
    "set the forceGrayscale mode; if true, grayScale images are
     returned, even if the input contains a color image."

    forceGrayscale := something.

    "Created: 16.4.1997 / 20:33:44 / cg"
    "Modified: 16.4.1997 / 20:34:29 / cg"
! !

!JPEGReader methodsFor:'private'!

create_jpeg_decompress_struct
    |errMgrStructSize decompressStructSize fp errorOccurred|

    fp := inStream filePointer.
    fp isNil ifTrue:[
	self error:'can only read from an external stream'.
	^ self.
    ].

%{
    errMgrStructSize = __mkSmallInteger(sizeof(struct my_error_mgr));
    decompressStructSize = __mkSmallInteger(sizeof(struct jpeg_decompress_struct));
%}.

    jpeg_error_mgr_struct := ExternalBytes unprotectedNew:errMgrStructSize.
    jpeg_decompress_struct := ExternalBytes unprotectedNew:decompressStructSize.
    errorOccurred := false.

%{  /* STACK: 400000 */
    struct jpeg_decompress_struct *cinfoPtr;
    struct my_error_mgr *jerrPtr;
    OBJ j_e_m = __INST(jpeg_error_mgr_struct);
    OBJ j_d_s = __INST(jpeg_decompress_struct);
    FILE *f = __FILEVal(fp);

    if (__isExternalBytesLike(j_d_s)
     && __isExternalBytesLike(j_e_m)) {
	cinfoPtr = (struct jpeg_decompress_struct *)(__externalBytesAddress(j_d_s));
	jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m));

	/*
	 * Initialize the JPEG decompression object with default error handling.
	 */
	cinfoPtr->err = jpeg_std_error(jerrPtr);

	/*
	 * prepare to handle errors smoothly ...
	 */
	jerrPtr->pub.error_exit = my_error_exit;
	if (setjmp(jerrPtr->setjmp_buffer)) {
	    /*
	     * error occurred ...
	     */
	    jpeg_destroy_decompress(cinfoPtr);
	    RETURN (false);
	}

       /*
	* use my message print function
	*/
       jerrPtr->pub.output_message = my_output_message;

	jpeg_create_decompress(cinfoPtr);
#if 0
	/*
	 * Insert custom COM marker processor.
	 */
	jpeg_set_marker_processor(cinfoPtr, JPEG_COM, COM_handler);
#endif
	cinfoPtr->err->trace_level = 0;

#if 0
	/* colors setting */
	cinfoPtr->desired_number_of_colors = val;
	cinfoPtr->quantize_colors = TRUE;
#endif
#if 0
	/* dct setting */
	cinfoPtr->dct_method = JDCT_ISLOW;
	or: cinfoPtr->dct_method = JDCT_IFAST;
	or: cinfoPtr->dct_method = JDCT_FLOAT;
#endif

	/* dither setting */
	cinfoPtr->dither_mode = JDITHER_FS;
	if (__INST(forceDitherMode) == @symbol(none)) {
	    cinfoPtr->dither_mode = JDITHER_NONE;
	} else {
	    if (__INST(forceDitherMode) == @symbol(ordered)) {
		cinfoPtr->dither_mode = JDITHER_ORDERED;
	    }
	}

#if 0
	/* fast setting */
	cinfoPtr->two_pass_quantize = FALSE;
	cinfoPtr->dither_mode = JDITHER_ORDERED;
	cinfoPtr->desired_number_of_colors = 216;
	cinfoPtr->dct_method = JDCT_FASTEST;
	cinfoPtr->do_fancy_upsampling = FALSE;
#endif

	if (__INST(forceGrayscale) == true) {
	    /* grayscale setting */
	    cinfoPtr->out_color_space = JCS_GRAYSCALE;
	}

#if 0
	/* maxmemory setting */
	cinfoPtr->mem->max_memory_to_use = lval * 1000L;
#endif

#if 0
	/* nosmooth setting */
	cinfoPtr->do_fancy_upsampling = FALSE;
#endif

#if 0
	/* onepass setting */
	cinfoPtr->two_pass_quantize = FALSE;
#endif

	/* Specify data source for decompression */
	jpeg_stdio_src(cinfoPtr, f);

	/* Read file header, set default decompression parameters */
	(void) jpeg_read_header(cinfoPtr, TRUE);

	/* Calculate output image dimensions so we can allocate space */
	jpeg_calc_output_dimensions(cinfoPtr);

	__INST(width) = __mkSmallInteger(cinfoPtr->output_width);
	__INST(height) = __mkSmallInteger(cinfoPtr->output_height);
	__INST(colorComponents) = __mkSmallInteger(cinfoPtr->output_components);

#if 0
	/* could now set additional values in cinfo
	 * (colormap)
	 */
#endif

    }
%}.
    ^ true
!

decompressChunkInto:aByteArray startingAt:index
%{  /* STACK: 400000 */
    struct jpeg_decompress_struct *cinfoPtr;
    struct my_error_mgr *jerrPtr;
    char *rowPtr = NULL;
    OBJ j_d_s = __INST(jpeg_decompress_struct);
    OBJ j_e_m = __INST(jpeg_error_mgr_struct);
    int num_scanlines;
    char *rowPointers[4];


    if (__isByteArray(aByteArray)) {
	rowPtr = (char *)(__ByteArrayInstPtr(aByteArray)->ba_element);
    } else if (__isExternalBytesLike(aByteArray)) {
	rowPtr = __externalBytesAddress(aByteArray);
    }

    if (__isExternalBytesLike(j_d_s)
     && (rowPtr != NULL)
     && __isExternalBytesLike(j_e_m)) {
	cinfoPtr = (struct jpeg_decompress_struct *)(__externalBytesAddress(j_d_s));
	jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m));

	rowPtr += __intVal(index) - 1;

	rowPointers[0] = rowPtr;
	rowPointers[1] = NULL;
	rowPointers[2] = NULL;
	rowPointers[3] = NULL;

	if (cinfoPtr->output_scanline < cinfoPtr->output_height) {
	    if (setjmp(jerrPtr->setjmp_buffer)) {
		/*
		 * error occurred ...
		 */
		jpeg_destroy_decompress(cinfoPtr);
		RETURN (__mkSmallInteger(-1));
	    }
	    num_scanlines = jpeg_read_scanlines(cinfoPtr,
						rowPointers,
						1);
	    RETURN (__mkSmallInteger(num_scanlines));
	}
	RETURN (__mkSmallInteger(0));
    }
%}.
    self halt:'bad arguments'.
!

finish_decompress
%{  /* STACK: 400000 */
    struct jpeg_decompress_struct *cinfoPtr;
    struct my_error_mgr *jerrPtr;
    OBJ j_d_s = __INST(jpeg_decompress_struct);
    OBJ j_e_m = __INST(jpeg_error_mgr_struct);

    if (__isExternalBytesLike(j_d_s)
     && __isExternalBytesLike(j_e_m)) {
	cinfoPtr = (struct jpeg_decompress_struct *)(__externalBytesAddress(j_d_s));
	jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m));

	if (setjmp(jerrPtr->setjmp_buffer)) {
	    jpeg_destroy_decompress(cinfoPtr);
	    RETURN (false);
	}

	/* finish decompressor */
	(void) jpeg_finish_decompress(cinfoPtr);
	(void) jpeg_destroy_decompress(cinfoPtr);
	RETURN (true);
    }
%}
!

get_error_message
%{
    struct jpeg_decompress_struct *cinfoPtr;
    OBJ j_d_s = __INST(jpeg_decompress_struct);
    char buffer[JMSG_LENGTH_MAX+1];

    if (__isExternalBytesLike(j_d_s)) {
	cinfoPtr = (struct jpeg_decompress_struct *)(__externalBytesAddress(j_d_s));

	(*cinfoPtr->err->format_message) (cinfoPtr, buffer);
	buffer[JMSG_LENGTH_MAX] = '\0';
	RETURN ( __MKSTRING(buffer));
    }
%}.
    ^ nil
!

start_decompress
%{  /* STACK: 400000 */
    struct jpeg_decompress_struct *cinfoPtr;
    struct my_error_mgr *jerrPtr;
    OBJ j_d_s = __INST(jpeg_decompress_struct);
    OBJ j_e_m = __INST(jpeg_error_mgr_struct);

    if (__isExternalBytesLike(j_d_s)
     && __isExternalBytesLike(j_e_m)) {
	cinfoPtr = (struct jpeg_decompress_struct *)(__externalBytesAddress(j_d_s));
	jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m));

	if (setjmp(jerrPtr->setjmp_buffer)) {
	    jpeg_destroy_decompress(cinfoPtr);
	    RETURN (false);
	}

	/* Start decompressor */
	(void) jpeg_start_decompress(cinfoPtr);
	RETURN (true);
    }
%}
! !

!JPEGReader methodsFor:'reading'!

fromStream:aStream
    "read a JPG image from a stream"

    |dataIdx bytesPerRow returnCode pos1 ok tmpFile s|

    aStream isExternalStream ifFalse:[
	"/ libJpeg can only handle real OS-streams

	tmpFile := Filename newTemporary.
	[
	    s := tmpFile writeStream binary.
	    s nextPutAll:aStream contents.
	    s close.
	    s := tmpFile readStream binary.
	    ^ self fromStream:s.
	] ensure:[
	    s notNil ifTrue:[s close].
	    tmpFile delete.
	].

	"/ 'JPEGReader [info]: can only read from real streams' infoPrintCR.
	"/ ^ nil
    ].

    inStream := aStream.
    pos1 := inStream position.


    (self create_jpeg_decompress_struct not
    or:[self start_decompress not]) ifTrue:[
	ok := false.

	"/ if there was no SOI marker,
	"/ try again, skipping first 128 bytes
	"/ (seems to be generated by some jpg writers)

	inStream position:pos1.
	((inStream nextByte ~~ 16rFF)
	or:[inStream nextByte ~~ 16rD8]) ifTrue:[
	    inStream position:pos1 + 128.
	    ((inStream nextByte == 16rFF)
	    and:[inStream nextByte == 16rD8]) ifTrue:[
		inStream position:pos1 + 128.
		ok := self create_jpeg_decompress_struct
		      and:[self start_decompress]
	    ].
	].
	ok ifFalse:[
	    'JPEGReader [info]: ' infoPrint. self get_error_message infoPrintCR.
	    ^ nil
	]
    ].

    data := ByteArray uninitializedNew:(width * height * colorComponents).
    dataIdx := 1.
    bytesPerRow := colorComponents * width.

    [(returnCode := self decompressChunkInto:data startingAt:dataIdx) > 0] whileTrue:[
	"/ got a row in the buffer ...
	dataIdx := dataIdx + bytesPerRow
    ].
    returnCode < 0 ifTrue:[
	'JPEGReader [info]: ' infoPrint. self get_error_message infoPrintCR.
	^ nil
    ].

    (self finish_decompress) ifFalse:[
	'JPEGReader [info]: ' infoPrint. self get_error_message infoPrintCR.
	^ nil
    ].

    colorComponents == 3 ifTrue:[
	photometric := #rgb.
	samplesPerPixel := 3.
	bitsPerSample := #(8 8 8).
    ] ifFalse:[
	photometric := #blackIs0.
	samplesPerPixel := 1.
	bitsPerSample := #(8).
    ].

    "
     JPEGReader fromFile:'../../support/libjpeg-6a/testimg.jpg'
    "
    "
     |stream reader|

     stream := '../../support/libjpeg-6a/testimg.jpg' asFilename readStream.
     reader := JPEGReader new.
     reader forceGrayscale:true.
     reader forceDitherMode:#ordered.
     reader fromStream:stream.
     ^ reader image
    "

    "Modified: / 4.4.1998 / 18:48:46 / cg"
! !

!JPEGReader class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.49 2007-01-24 15:02:21 cg Exp $'
! !

JPEGReader initialize!