JPEGReader.st
author ca
Mon, 09 Mar 1998 17:06:35 +0100
changeset 861 10bbb13fcb05
parent 669 d38a796ad10b
child 895 ebac67edf08e
permissions -rw-r--r--
add more simple drag & drop functionality: support one icon for a set of drag objects

"
 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.
"

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

!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;
};

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

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

%}
! !

!JPEGReader primitiveFunctions!
%{

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

/*
 * 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) {
      fprintf(stderr, "\rPass %d/%d: %3d%% ",
              prog->pub.completed_passes + prog->completed_extra_passes + 1,
              total_passes, percent_done);
    } else {
      fprintf(stderr, "\r %3d%% ", percent_done);
    }
    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) {
    fprintf(stderr, "\r                \r");
    fflush(stderr);
  }
}

#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 asLowercase endsWith:'.jpg') ifTrue:[^ true].
    (aFileName 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 = __MKSMALLINT(sizeof(struct my_error_mgr));
    decompressStructSize = __MKSMALLINT(sizeof(struct jpeg_decompress_struct));
%}.

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

%{
    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 (__isExternalBytes(j_d_s)
     && __isExternalBytes(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);
	}

        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) = __MKSMALLINT(cinfoPtr->output_width);
        __INST(height) = __MKSMALLINT(cinfoPtr->output_height);
        __INST(colorComponents) = __MKSMALLINT(cinfoPtr->output_components);

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

    }
%}.
    ^ true
!

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

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

        rowPtr = (char *)(__ByteArrayInstPtr(aByteArray)->ba_element);
        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 (__MKSMALLINT(-1));
	    }
            num_scanlines = jpeg_read_scanlines(cinfoPtr, 
                                                rowPointers,
                                                1);
            RETURN (__MKSMALLINT(num_scanlines));
        }
        RETURN (__MKSMALLINT(0));
    }
%}.
    self halt.
!

finish_decompress
%{
    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 (__isExternalBytes(j_d_s)
     && __isExternalBytes(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 (__isExternalBytes(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
%{
    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 (__isExternalBytes(j_d_s)
     && __isExternalBytes(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 from stream'!

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

    |dataIdx bytesPerRow returnCode pos1 ok|

    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: 22.7.1997 / 11:43:18 / cg"
! !

!JPEGReader class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.32 1997-07-22 09:44:45 cg Exp $'
! !
JPEGReader initialize!