--- 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$'
! !