added image writing support
authorClaus Gittinger <cg@exept.de>
Tue, 16 Feb 2016 04:39:16 +0100
changeset 3557 dc0c5f9000c2
parent 3555 7c304f9f757f
child 3558 44a139dd7ac5
child 3559 7464e6c95511
added image writing support
JPEGReader.st
--- a/JPEGReader.st	Sun Feb 14 00:17:27 2016 +0100
+++ b/JPEGReader.st	Tue Feb 16 04:39:16 2016 +0100
@@ -12,8 +12,8 @@
 "{ Package: 'stx:libview2' }"
 
 ImageReader subclass:#JPEGReader
-	instanceVariableNames:'jpeg_decompress_struct jpeg_error_mgr_struct colorComponents
-		forceGrayscale forceDitherMode app1SegmentHandler'
+	instanceVariableNames:'jpeg_decompress_struct jpeg_compress_struct jpeg_error_mgr_struct colorComponents
+		forceGrayscale forceDitherMode app1SegmentHandler compressQuality bytesPerRow'
 	classVariableNames:'ErrorPrinting'
 	poolDictionaries:''
 	category:'Graphics-Images-Readers'
@@ -202,12 +202,12 @@
     "return true, if aFileName contains a JPG image.
      Only look at the file's name here ..."
 
-    ^ (MIMETypes mimeTypeForSuffix:aFilenameOrString asFilename suffix) = 'image/jpeg' 
+    ^ (MIMETypes mimeTypeForSuffix:aFilenameOrString asFilename suffix) = 'image/jpeg'
 
     "Created: 11.4.1997 / 16:26:25 / cg"
 
     "
-     self isValidImageFile:'xxx.jpg' 
+     self isValidImageFile:'xxx.jpg'
     "
 ! !
 
@@ -260,8 +260,8 @@
 
     fp := inStream filePointer.
     fp isNil ifTrue:[
-        self error:'can only read from an external stream'.
-        ^ false.
+	self error:'can only read from an external stream'.
+	^ false.
     ].
 
     app1SegmentCallbackFunction := self app1SegmentCallback.
@@ -284,121 +284,205 @@
 
     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));
+	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->pub);
+	/*
+	 * Initialize the JPEG decompression object with default error handling.
+	 */
+	cinfoPtr->err = jpeg_std_error(&jerrPtr->pub);
 
-        /*
-         * 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);
-        }
+	/*
+	 * 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;
+	/*
+	 * use my message print function
+	 */
+	jerrPtr->pub.output_message = my_output_message;
 
-        jpeg_create_decompress(cinfoPtr);
+	jpeg_create_decompress(cinfoPtr);
 #if 0
-        /*
-         * Insert custom COM marker processor.
-         */
-        jpeg_set_marker_processor(cinfoPtr, JPEG_COM, COM_handler);
+	/*
+	 * Insert custom COM marker processor.
+	 */
+	jpeg_set_marker_processor(cinfoPtr, JPEG_COM, COM_handler);
 #endif
-        if (app1SegmentCallbackFunction != nil) {
-            jpeg_marker_parser_method cb = NULL;
+#if 0
+	if (app1SegmentCallbackFunction != nil) {
+	    jpeg_marker_parser_method cb = NULL;
 
-            if (__isExternalFunction(app1SegmentCallbackFunction)) {
-                cb = (jpeg_marker_parser_method)__externalFunctionVal(app1SegmentCallbackFunction);
-            } else if (__isExternalAddress(app1SegmentCallbackFunction)) {
-                cb = (jpeg_marker_parser_method)__externalAddressVal(app1SegmentCallbackFunction);
-            } else {
-                /* ignore, but should report an error... */
-            }
-            jpeg_set_marker_processor(cinfoPtr, JPEG_APP0+1, cb);
-        }
+	    if (__isExternalFunction(app1SegmentCallbackFunction)) {
+		cb = (jpeg_marker_parser_method)__externalFunctionVal(app1SegmentCallbackFunction);
+	    } else if (__isExternalAddress(app1SegmentCallbackFunction)) {
+		cb = (jpeg_marker_parser_method)__externalAddressVal(app1SegmentCallbackFunction);
+	    } else {
+		/* ignore, but should report an error... */
+	    }
+	    jpeg_set_marker_processor(cinfoPtr, JPEG_APP0+1, cb);
+	}
+#endif
+	/* Specify data source for decompression */
+	jpeg_stdio_src(cinfoPtr, f);
 
-        /* Specify data source for decompression */
-        jpeg_stdio_src(cinfoPtr, f);
+	jpeg_save_markers (cinfoPtr, JPEG_APP0, 0xffff);
+	jpeg_save_markers (cinfoPtr, JPEG_APP0+1, 0xffff);
+	jpeg_save_markers (cinfoPtr, JPEG_APP0+13, 0xffff);
 
-        /* Read file header, set default decompression parameters */
-        (void) jpeg_read_header(cinfoPtr, TRUE);
+	/* Read file header, set default decompression parameters */
+	(void) jpeg_read_header(cinfoPtr, TRUE);
 
-        cinfoPtr->err->trace_level = 0;
+	cinfoPtr->err->trace_level = 0;
 
 #if 0
-        /* colors setting */
-        cinfoPtr->desired_number_of_colors = val;
-        cinfoPtr->quantize_colors = TRUE;
+	/* 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;
+	/* 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;
-            }
-        }
+	/* 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;
+	/* 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 (__INST(forceGrayscale) == true) {
+	    /* grayscale setting */
+	    cinfoPtr->out_color_space = JCS_GRAYSCALE;
+	}
 
 #if 0
-        /* maxmemory setting */
-        cinfoPtr->mem->max_memory_to_use = lval * 1000L;
+	/* maxmemory setting */
+	cinfoPtr->mem->max_memory_to_use = lval * 1000L;
 #endif
 
 #if 0
-        /* nosmooth setting */
-        cinfoPtr->do_fancy_upsampling = FALSE;
+	/* nosmooth setting */
+	cinfoPtr->do_fancy_upsampling = FALSE;
 #endif
 
 #if 0
-        /* onepass setting */
-        cinfoPtr->two_pass_quantize = FALSE;
+	/* onepass setting */
+	cinfoPtr->two_pass_quantize = FALSE;
 #endif
 
-        /* Calculate output image dimensions so we can allocate space */
-        jpeg_calc_output_dimensions(cinfoPtr);
+	/* 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);
+
+	/* could now set additional values in cinfo
+	 * (colormap)
+	 */
+
+    }
+%}.
+    ^ true
+!
+
+create_jpeg_compress_struct
+    |errMgrStructSize compressStructSize fp errorOccurred|
+
+    self assert:(photometric == #rgb).
+    self assert:(samplesPerPixel == 3).
+    self assert:(bitsPerSample asArray = #(8 8 8)).
+
+    fp := outStream filePointer.
+    fp isNil ifTrue:[
+	self error:'can only write to an external stream'.
+	^ false.
+    ].
+
+%{
+    errMgrStructSize = __mkSmallInteger(sizeof(struct my_error_mgr));
+    compressStructSize = __mkSmallInteger(sizeof(struct jpeg_compress_struct));
+%}.
+
+    jpeg_error_mgr_struct := ExternalBytes unprotectedNew:errMgrStructSize.
+    jpeg_compress_struct := ExternalBytes unprotectedNew:compressStructSize.
+    errorOccurred := false.
 
-        __INST(width) = __mkSmallInteger(cinfoPtr->output_width);
-        __INST(height) = __mkSmallInteger(cinfoPtr->output_height);
-        __INST(colorComponents) = __mkSmallInteger(cinfoPtr->output_components);
+%{  /* STACK: 400000 */
+    struct jpeg_compress_struct *cinfoPtr;
+    struct my_error_mgr *jerrPtr;
+    OBJ j_e_m = __INST(jpeg_error_mgr_struct);
+    OBJ j_c_s = __INST(jpeg_compress_struct);
+    FILE *f = __FILEVal(fp);
+    char *outBuffer;
+
+    if (__isExternalBytesLike(j_c_s)
+     && __isExternalBytesLike(j_e_m)) {
+	cinfoPtr = (struct jpeg_compress_struct *)(__externalBytesAddress(j_c_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->pub);
 
-        /* could now set additional values in cinfo
-         * (colormap)
-         */
+	/*
+	 * prepare to handle errors smoothly ...
+	 */
+	jerrPtr->pub.error_exit = my_error_exit;
+	if (setjmp(jerrPtr->setjmp_buffer)) {
+	    /*
+	     * error occurred ...
+	     */
+	    jpeg_destroy_compress(cinfoPtr);
+	    RETURN (false);
+	}
+
+	/*
+	 * use my message print function
+	 */
+	jerrPtr->pub.output_message = my_output_message;
 
+	jpeg_create_compress(cinfoPtr);
+
+	/* Specify data destination for compression */
+	jpeg_stdio_dest(cinfoPtr, f);
+
+	cinfoPtr->image_width      = __intVal(__INST(width));
+	cinfoPtr->image_height     = __intVal(__INST(height));
+	cinfoPtr->input_components = __intVal(__INST(samplesPerPixel));
+	cinfoPtr->in_color_space   = JCS_RGB;
+
+	jpeg_set_defaults(cinfoPtr);
+	/*set the quality [0..100]  */
+	jpeg_set_quality (cinfoPtr, __intVal(__INST(compressQuality)), 1);
+	jpeg_start_compress(cinfoPtr, 1);
+
+
+	cinfoPtr->err->trace_level = 0;
     }
 %}.
     ^ true
@@ -414,40 +498,37 @@
     int num_scanlines;
     JSAMPROW rowPointers[4];
 
-
     if (__isByteArray(aByteArray)) {
-        rowPtr = (char *)(__ByteArrayInstPtr(aByteArray)->ba_element);
+	rowPtr = (char *)(__ByteArrayInstPtr(aByteArray)->ba_element);
     } else if (__isExternalBytesLike(aByteArray)) {
-        rowPtr = __externalBytesAddress(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));
+	cinfoPtr = (struct jpeg_decompress_struct *)(__externalBytesAddress(j_d_s));
+	jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m));
 
-        rowPtr += __intVal(index) - 1;
+	rowPtr += __intVal(index) - 1;
 
-        rowPointers[0] = rowPtr;
-        rowPointers[1] = NULL;
-        rowPointers[2] = NULL;
-        rowPointers[3] = NULL;
+	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));
+	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'.
@@ -455,7 +536,7 @@
 
 extractApp1DataFrom:data
     (data startsWith:#[16r45 16r78 16r69 16r66 16r0] " = 'Exif' + 0-byte" ) ifTrue:[
-        self extractExifDataFrom:data.
+	self extractExifDataFrom:data.
     ].
 
     "Created: / 12-12-2011 / 21:22:23 / cg"
@@ -476,7 +557,7 @@
     count := count - 2. "/ count itself is included
     data := ByteArray new:count.
     1 to: count do:[:i |
-        data at:i put:(self jpeg_getc).
+	data at:i put:(self jpeg_getc).
     ].
     self breakPoint:#cg.
     app1SegmentHandler value:data.
@@ -507,6 +588,31 @@
 %}
 !
 
+finish_compress
+%{  /* STACK: 400000 */
+    struct jpeg_compress_struct *cinfoPtr;
+    struct my_error_mgr *jerrPtr;
+    OBJ j_c_s = __INST(jpeg_compress_struct);
+    OBJ j_e_m = __INST(jpeg_error_mgr_struct);
+
+    if (__isExternalBytesLike(j_c_s)
+     && __isExternalBytesLike(j_e_m)) {
+	cinfoPtr = (struct jpeg_compress_struct *)(__externalBytesAddress(j_c_s));
+	jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m));
+
+	if (setjmp(jerrPtr->setjmp_buffer)) {
+	    jpeg_destroy_compress(cinfoPtr);
+	    RETURN (false);
+	}
+
+	/* finish decompressor */
+	(void) jpeg_finish_compress(cinfoPtr);
+	(void) jpeg_destroy_compress(cinfoPtr);
+	RETURN (true);
+    }
+%}
+!
+
 get_error_message
 %{
     j_common_ptr cinfoPtr;
@@ -514,11 +620,11 @@
     char buffer[JMSG_LENGTH_MAX+1];
 
     if (__isExternalBytesLike(j_d_s)) {
-        cinfoPtr = (j_common_ptr)(__externalBytesAddress(j_d_s));
+	cinfoPtr = (j_common_ptr)(__externalBytesAddress(j_d_s));
 
-        (*cinfoPtr->err->format_message) (cinfoPtr, buffer);
-        buffer[JMSG_LENGTH_MAX] = '\0';
-        RETURN ( __MKSTRING(buffer));
+	(*cinfoPtr->err->format_message) (cinfoPtr, buffer);
+	buffer[JMSG_LENGTH_MAX] = '\0';
+	RETURN ( __MKSTRING(buffer));
     }
 %}.
     ^ nil
@@ -573,20 +679,20 @@
     |dataIdx bytesPerRow returnCode pos1 ok s|
 
     aStream isExternalStream ifFalse:[
-        "/ libJpeg can only handle real OS-streams
+	"/ libJpeg can only handle real OS-streams
 
-        s := FileStream newTemporary binary.
-        [
-            s nextPutAll:aStream contents.
-            s reset.
-            ^ self fromStream:s.
-        ] ensure:[
-            s close.
-            s fileName delete.
-        ].
+	s := FileStream newTemporary binary.
+	[
+	    s nextPutAll:aStream contents.
+	    s reset.
+	    ^ self fromStream:s.
+	] ensure:[
+	    s close.
+	    s fileName delete.
+	].
 
-        "/ 'JPEGReader [info]: can only read from real streams' infoPrintCR.
-        "/ ^ nil
+	"/ 'JPEGReader [info]: can only read from real streams' infoPrintCR.
+	"/ ^ nil
     ].
 
     inStream := aStream.
@@ -599,27 +705,27 @@
 
     (self create_jpeg_decompress_struct not
     or:[self start_decompress not]) ifTrue:[
-        ok := false.
+	ok := false.
 
-        "/ if there was no SOI marker,
-        "/ try again, skipping first 128 bytes
-        "/ (seems to be generated by some jpg writers)
+	"/ 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
-        ]
+	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).
@@ -627,27 +733,27 @@
     bytesPerRow := colorComponents * width.
 
     [(returnCode := self decompressChunkInto:data startingAt:dataIdx) > 0] whileTrue:[
-        "/ got a row in the buffer ...
-        dataIdx := dataIdx + bytesPerRow
+	"/ got a row in the buffer ...
+	dataIdx := dataIdx + bytesPerRow
     ].
     returnCode < 0 ifTrue:[
-        'JPEGReader [info]: ' infoPrint. self get_error_message infoPrintCR.
-        ^ nil
+	'JPEGReader [info]: ' infoPrint. self get_error_message infoPrintCR.
+	^ nil
     ].
 
     (self finish_decompress) ifFalse:[
-        'JPEGReader [info]: ' infoPrint. self get_error_message infoPrintCR.
-        ^ nil
+	'JPEGReader [info]: ' infoPrint. self get_error_message infoPrintCR.
+	^ nil
     ].
 
     colorComponents == 3 ifTrue:[
-        photometric := #rgb.
-        samplesPerPixel := 3.
-        bitsPerSample := #(8 8 8).
+	photometric := #rgb.
+	samplesPerPixel := 3.
+	bitsPerSample := #(8 8 8).
     ] ifFalse:[
-        photometric := #blackIs0.
-        samplesPerPixel := 1.
-        bitsPerSample := #(8).
+	photometric := #blackIs0.
+	samplesPerPixel := 1.
+	bitsPerSample := #(8).
     ].
 
     "
@@ -675,14 +781,87 @@
     "Modified: / 12-12-2011 / 21:34:48 / cg"
 ! !
 
+!JPEGReader methodsFor:'writing'!
+
+save:image onStream:aStream
+    "save image in JPG-file-format onto aStream"
+
+    image depth ~~ 24 ifTrue:[
+	^ Image cannotRepresentImageSignal
+	    raiseWith:image
+	    errorString:('JPG (currently) only supports depth24 images').
+    ].
+
+    outStream := aStream.
+    outStream binary.
+
+"/    mask := image mask.
+"/    mask notNil ifTrue:[
+"/        self assignTransparentPixelIn:image
+"/    ].
+
+    width := image width.
+    height := image height.
+    photometric := image photometric.
+    samplesPerPixel := image samplesPerPixel.
+    bitsPerSample := image bitsPerSample.
+    colorMap := image colorMap.
+    bytesPerRow := image bytesPerRow.
+    data := image bits.
+    compressQuality := 80.
+
+    (self create_jpeg_compress_struct not) ifTrue:[
+	self errir:'JPG: setup error'.
+    ].
+
+    self compressScanlines.
+    self finish_compress.
+!
+
+compressScanlines
+%{
+    unsigned char *rowPtr;
+    int bpr = __intVal(__INST(bytesPerRow));
+    int row;
+    int __height = __intVal(__INST(height));
+    struct jpeg_compress_struct *cinfoPtr;
+    struct my_error_mgr *jerrPtr;
+    OBJ j_c_s = __INST(jpeg_compress_struct);
+    OBJ j_e_m = __INST(jpeg_error_mgr_struct);
+    OBJ __data = __INST(data);
+
+    if (__isByteArray(__data)) {
+	rowPtr = (char *)(__ByteArrayInstPtr(__data)->ba_element);
+    } else if (__isExternalBytesLike(__data)) {
+	rowPtr = __externalBytesAddress(__data);
+    }
+
+    if (__isExternalBytesLike(j_c_s)
+     && __isExternalBytesLike(j_e_m)) {
+	cinfoPtr = (struct jpeg_compress_struct *)(__externalBytesAddress(j_c_s));
+	jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m));
+
+	if (setjmp(jerrPtr->setjmp_buffer)) {
+	    jpeg_destroy_compress(cinfoPtr);
+	    RETURN (false);
+	}
+
+	for (row=0; row<__height; row++) {
+	    jpeg_write_scanlines(cinfoPtr, &rowPtr, 1);
+	    rowPtr += bpr;
+	}
+    }
+%}
+! !
+
 !JPEGReader class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.63 2014-06-14 09:14:50 cg Exp $'
+    ^ '$Header$'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.63 2014-06-14 09:14:50 cg Exp $'
+    ^ '$Header$'
 ! !