--- a/JPEGReader.st Tue Nov 29 19:12:36 2011 +0100
+++ b/JPEGReader.st Mon Dec 12 21:19:08 2011 +0100
@@ -13,7 +13,7 @@
ImageReader subclass:#JPEGReader
instanceVariableNames:'jpeg_decompress_struct jpeg_error_mgr_struct colorComponents
- forceGrayscale forceDitherMode'
+ forceGrayscale forceDitherMode app1SegmentHandler'
classVariableNames:'ErrorPrinting'
poolDictionaries:''
category:'Graphics-Images-Readers'
@@ -131,7 +131,23 @@
}
}
-#endif
+#endif /* PROGRESS_REPORT */
+
+
+/* fetch a byte from the stream */
+unsigned int
+JPG_jpeg_getc (j_decompress_ptr cinfo)
+/* Read next byte */
+{
+ struct jpeg_source_mgr *datasrc = cinfo->src;
+
+ if (datasrc->bytes_in_buffer == 0) {
+ if (! (*datasrc->fill_input_buffer) (cinfo))
+ return -1;
+ }
+ datasrc->bytes_in_buffer--;
+ return (*datasrc->next_input_byte++) & 0xFF;
+}
%}
! !
@@ -200,6 +216,12 @@
!JPEGReader methodsFor:'accessing'!
+app1SegmentHandler:aBlock
+ "set a handler block for app1 segment data (geolocation in exif format)"
+
+ app1SegmentHandler := aBlock
+!
+
forceDitherMode:something
"set the dither mode, to one of #none or #ordered"
@@ -220,8 +242,56 @@
!JPEGReader methodsFor:'private'!
+app1SegmentCallback
+ "return a callback function which invokes the app1SegmentHandlerBlock if defined.
+ This will be called to handle the exif segment, containing geolocation tags.
+ Return nil, if there is no handler blcok defined"
+
+ |cb|
+
+ app1SegmentHandler isNil ifTrue:[^ nil].
+
+ cb := ExternalFunctionCallback new.
+ cb returnType:#bool argumentTypes:#(pointer).
+ cb generateClosure.
+ cb action:[:args | self fetchApp1SegmentData. true].
+ ^ cb code. 'can be passed to C'.
+!
+
+fetchApp1SegmentData
+ |byte1 byte2 count|
+
+ byte1 := self jpeg_getc.
+ byte2 := self jpeg_getc.
+ count := (byte1 bitShift:8) + byte2. "/ msb first
+ count := count - 2. "/ count itself is included
+ data := ByteArray new:count.
+ 1 to: count do:[:i |
+ data at:i put:(self jpeg_getc).
+ ].
+ self halt.
+ app1SegmentHandler value:data.
+!
+
+jpeg_getc
+%{
+ OBJ j_d_s = __INST(jpeg_decompress_struct);
+
+ if (__isExternalBytesLike(j_d_s)) {
+ struct jpeg_decompress_struct *cinfoPtr;
+ int byte;
+
+ cinfoPtr = (struct jpeg_decompress_struct *)(__externalBytesAddress(j_d_s));
+
+ byte = JPG_jpeg_getc (cinfoPtr);
+ RETURN( __MKSMALLINT(byte) );
+ }
+%}.
+ self primitiveFailed
+!
+
create_jpeg_decompress_struct
- |errMgrStructSize decompressStructSize fp errorOccurred|
+ |errMgrStructSize decompressStructSize fp errorOccurred app1SegmentCallbackFunction|
fp := inStream filePointer.
fp isNil ifTrue:[
@@ -229,6 +299,8 @@
^ self.
].
+ app1SegmentCallbackFunction := self app1SegmentCallback.
+
%{
errMgrStructSize = __mkSmallInteger(sizeof(struct my_error_mgr));
decompressStructSize = __mkSmallInteger(sizeof(struct jpeg_decompress_struct));
@@ -279,6 +351,19 @@
*/
jpeg_set_marker_processor(cinfoPtr, JPEG_COM, COM_handler);
#endif
+ 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);
+ }
+
cinfoPtr->err->trace_level = 0;
#if 0
@@ -479,22 +564,22 @@
|dataIdx bytesPerRow returnCode pos1 ok tmpFile s|
aStream isExternalStream ifFalse:[
- "/ libJpeg can only handle real OS-streams
+ "/ 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.
- ].
+ 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
+ "/ 'JPEGReader [info]: can only read from real streams' infoPrintCR.
+ "/ ^ nil
].
inStream := aStream.
@@ -503,27 +588,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).
@@ -531,27 +616,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).
].
"
@@ -574,11 +659,11 @@
!JPEGReader class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.50 2009-10-21 16:52:38 stefan Exp $'
+ ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.51 2011-12-12 20:19:08 cg Exp $'
!
version_CVS
- ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.50 2009-10-21 16:52:38 stefan Exp $'
+ ^ '$Header: /cvs/stx/stx/libview2/JPEGReader.st,v 1.51 2011-12-12 20:19:08 cg Exp $'
! !
JPEGReader initialize!