9 other person. No title to or ownership of the software is |
11 other person. No title to or ownership of the software is |
10 hereby transferred. |
12 hereby transferred. |
11 " |
13 " |
12 "{ Package: 'stx:libview2' }" |
14 "{ Package: 'stx:libview2' }" |
13 |
15 |
|
16 "{ NameSpace: Smalltalk }" |
|
17 |
14 ImageReader subclass:#JPEGReader |
18 ImageReader subclass:#JPEGReader |
15 instanceVariableNames:'jpeg_decompress_struct jpeg_compress_struct jpeg_error_mgr_struct colorComponents |
19 instanceVariableNames:'jpeg_decompress_struct jpeg_compress_struct jpeg_error_mgr_struct |
16 forceGrayscale forceDitherMode app1SegmentHandler compressQuality bytesPerRow' |
20 colorComponents forceGrayscale forceDitherMode app1SegmentHandler |
|
21 compressQuality bytesPerRow' |
17 classVariableNames:'ErrorPrinting' |
22 classVariableNames:'ErrorPrinting' |
18 poolDictionaries:'' |
23 poolDictionaries:'' |
19 category:'Graphics-Images-Readers' |
24 category:'Graphics-Images-Readers' |
20 ! |
25 ! |
21 |
26 |
172 " |
177 " |
173 Reader for JPEG images. |
178 Reader for JPEG images. |
174 |
179 |
175 This uses the libpeg library to read the image |
180 This uses the libpeg library to read the image |
176 |
181 |
177 Only reading of files is supported. |
182 Only writing of depth24 images is currently supported. |
178 |
183 |
179 [See also:] |
184 [See also:] |
180 Image Form Icon |
185 Image Form Icon |
181 BlitImageReader FaceReader GIFReader PBMReader PCXReader |
186 BlitImageReader FaceReader GIFReader PBMReader PCXReader |
182 ST80FormReader SunRasterReader TargaReader TIFFReader WindowsIconReader |
187 ST80FormReader SunRasterReader TargaReader TIFFReader WindowsIconReader |
183 XBMReader XPMReader XWDReader |
188 XBMReader XPMReader XWDReader |
|
189 " |
|
190 ! |
|
191 |
|
192 examples |
|
193 " |
|
194 good quality: |
|
195 [exBegin] |
|
196 |i i24 i2| |
|
197 |
|
198 Transcript printf:'gif original %d\n' with:( '../../goodies/bitmaps/gifImages/garfield.gif' asFilename fileSize ). |
|
199 i := Image fromFile:'../../goodies/bitmaps/gifImages/garfield.gif'. |
|
200 i24 := Depth24Image fromImage:i. |
|
201 JPEGReader save:i24 onFile:'test100.jpg'. |
|
202 i2 := Image fromFile:'test100.jpg'. |
|
203 Transcript printf:'jpg 100%% %d\n' with:( 'test100.jpg' asFilename fileSize ). |
|
204 i2 inspect |
|
205 [exEnd] |
|
206 |
|
207 |
|
208 normal quality: |
|
209 [exBegin] |
|
210 |i i24 i2| |
|
211 |
|
212 Transcript printf:'gif original %d\n' with:( '../../goodies/bitmaps/gifImages/garfield.gif' asFilename fileSize ). |
|
213 i := Image fromFile:'../../goodies/bitmaps/gifImages/garfield.gif'. |
|
214 i24 := Depth24Image fromImage:i. |
|
215 JPEGReader save:i24 onFile:'test80.jpg'. |
|
216 i2 := Image fromFile:'test80.jpg'. |
|
217 Transcript printf:'jpg 80%% %d\n' with:( 'test80.jpg' asFilename fileSize ). |
|
218 i2 inspect |
|
219 [exEnd] |
|
220 |
|
221 |
|
222 low quality: |
|
223 [exBegin] |
|
224 |i i24 i2| |
|
225 |
|
226 i := Image fromFile:'../../goodies/bitmaps/gifImages/garfield.gif'. |
|
227 i24 := Depth24Image fromImage:i. |
|
228 JPEGReader new save:i24 onStream:('test50.jpg' asFilename writeStream) compressQuality:50. |
|
229 i2 := Image fromFile:'test50.jpg'. |
|
230 Transcript printf:'jpg 50%% %d\n' with:( 'test50.jpg' asFilename fileSize ). |
|
231 i2 inspect |
|
232 [exEnd] |
|
233 |
|
234 bad quality: |
|
235 [exBegin] |
|
236 |i i24 i2| |
|
237 |
|
238 i := Image fromFile:'../../goodies/bitmaps/gifImages/garfield.gif'. |
|
239 i24 := Depth24Image fromImage:i. |
|
240 JPEGReader new save:i24 onStream:('test20.jpg' asFilename writeStream) compressQuality:20. |
|
241 i2 := Image fromFile:'test20.jpg'. |
|
242 Transcript printf:'jpg 20%% %d\n' with:( 'test20.jpg' asFilename fileSize ). |
|
243 i2 inspect |
|
244 [exEnd] |
|
245 |
|
246 very bad quality: |
|
247 [exBegin] |
|
248 |i i24 i2| |
|
249 |
|
250 i := Image fromFile:'../../goodies/bitmaps/gifImages/garfield.gif'. |
|
251 i24 := Depth24Image fromImage:i. |
|
252 JPEGReader new save:i24 onStream:('test10.jpg' asFilename writeStream) compressQuality:10. |
|
253 i2 := Image fromFile:'test10.jpg'. |
|
254 Transcript printf:'jpg 10%% %d\n' with:( 'test10.jpg' asFilename fileSize ). |
|
255 i2 inspect |
|
256 [exEnd] |
184 " |
257 " |
185 ! ! |
258 ! ! |
186 |
259 |
187 !JPEGReader class methodsFor:'initialization'! |
260 !JPEGReader class methodsFor:'initialization'! |
188 |
261 |
192 |
265 |
193 MIMETypes defineImageType:'image/jpeg' suffix:'jpg' reader:self. |
266 MIMETypes defineImageType:'image/jpeg' suffix:'jpg' reader:self. |
194 MIMETypes defineImageType:nil suffix:'jpeg' reader:self. |
267 MIMETypes defineImageType:nil suffix:'jpeg' reader:self. |
195 |
268 |
196 "Modified: 1.2.1997 / 15:01:55 / cg" |
269 "Modified: 1.2.1997 / 15:01:55 / cg" |
|
270 ! ! |
|
271 |
|
272 !JPEGReader class methodsFor:'defaults'! |
|
273 |
|
274 defaultCompressQuality |
|
275 ^ 85 |
197 ! ! |
276 ! ! |
198 |
277 |
199 !JPEGReader class methodsFor:'testing'! |
278 !JPEGReader class methodsFor:'testing'! |
200 |
279 |
201 isValidImageFile:aFilenameOrString |
280 isValidImageFile:aFilenameOrString |
251 cb := ExternalFunctionCallback new. |
330 cb := ExternalFunctionCallback new. |
252 cb returnType:#bool argumentTypes:#(pointer). |
331 cb returnType:#bool argumentTypes:#(pointer). |
253 cb generateClosure. |
332 cb generateClosure. |
254 cb action:[:args | self fetchApp1SegmentData. true]. |
333 cb action:[:args | self fetchApp1SegmentData. true]. |
255 ^ cb code. "can be passed to C." |
334 ^ cb code. "can be passed to C." |
|
335 ! |
|
336 |
|
337 create_jpeg_compress_struct |
|
338 |errMgrStructSize compressStructSize fp errorOccurred| |
|
339 |
|
340 self assert:(photometric == #rgb). |
|
341 self assert:(samplesPerPixel == 3). |
|
342 self assert:(bitsPerSample asArray = #(8 8 8)). |
|
343 |
|
344 fp := outStream filePointer. |
|
345 fp isNil ifTrue:[ |
|
346 self error:'can only write to an external stream'. |
|
347 ^ false. |
|
348 ]. |
|
349 |
|
350 %{ |
|
351 errMgrStructSize = __mkSmallInteger(sizeof(struct my_error_mgr)); |
|
352 compressStructSize = __mkSmallInteger(sizeof(struct jpeg_compress_struct)); |
|
353 %}. |
|
354 |
|
355 jpeg_error_mgr_struct := ExternalBytes unprotectedNew:errMgrStructSize. |
|
356 jpeg_compress_struct := ExternalBytes unprotectedNew:compressStructSize. |
|
357 errorOccurred := false. |
|
358 |
|
359 %{ /* STACK: 400000 */ |
|
360 struct jpeg_compress_struct *cinfoPtr; |
|
361 struct my_error_mgr *jerrPtr; |
|
362 OBJ j_e_m = __INST(jpeg_error_mgr_struct); |
|
363 OBJ j_c_s = __INST(jpeg_compress_struct); |
|
364 FILE *f = __FILEVal(fp); |
|
365 char *outBuffer; |
|
366 |
|
367 if (__isExternalBytesLike(j_c_s) |
|
368 && __isExternalBytesLike(j_e_m)) { |
|
369 cinfoPtr = (struct jpeg_compress_struct *)(__externalBytesAddress(j_c_s)); |
|
370 jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m)); |
|
371 |
|
372 /* |
|
373 * Initialize the JPEG decompression object with default error handling. |
|
374 */ |
|
375 cinfoPtr->err = jpeg_std_error(&jerrPtr->pub); |
|
376 |
|
377 /* |
|
378 * prepare to handle errors smoothly ... |
|
379 */ |
|
380 jerrPtr->pub.error_exit = my_error_exit; |
|
381 if (setjmp(jerrPtr->setjmp_buffer)) { |
|
382 /* |
|
383 * error occurred ... |
|
384 */ |
|
385 jpeg_destroy_compress(cinfoPtr); |
|
386 RETURN (false); |
|
387 } |
|
388 |
|
389 /* |
|
390 * use my message print function |
|
391 */ |
|
392 jerrPtr->pub.output_message = my_output_message; |
|
393 |
|
394 jpeg_create_compress(cinfoPtr); |
|
395 |
|
396 /* Specify data destination for compression */ |
|
397 jpeg_stdio_dest(cinfoPtr, f); |
|
398 |
|
399 cinfoPtr->image_width = __intVal(__INST(width)); |
|
400 cinfoPtr->image_height = __intVal(__INST(height)); |
|
401 cinfoPtr->input_components = __intVal(__INST(samplesPerPixel)); |
|
402 cinfoPtr->in_color_space = JCS_RGB; |
|
403 |
|
404 jpeg_set_defaults(cinfoPtr); |
|
405 /*set the quality [0..100] */ |
|
406 jpeg_set_quality (cinfoPtr, __intVal(__INST(compressQuality)), 1); |
|
407 jpeg_start_compress(cinfoPtr, 1); |
|
408 |
|
409 |
|
410 cinfoPtr->err->trace_level = 0; |
|
411 } |
|
412 %}. |
|
413 ^ true |
256 ! |
414 ! |
257 |
415 |
258 create_jpeg_decompress_struct |
416 create_jpeg_decompress_struct |
259 |errMgrStructSize decompressStructSize fp errorOccurred app1SegmentCallbackFunction| |
417 |errMgrStructSize decompressStructSize fp errorOccurred app1SegmentCallbackFunction| |
260 |
418 |
407 } |
565 } |
408 %}. |
566 %}. |
409 ^ true |
567 ^ true |
410 ! |
568 ! |
411 |
569 |
412 create_jpeg_compress_struct |
|
413 |errMgrStructSize compressStructSize fp errorOccurred| |
|
414 |
|
415 self assert:(photometric == #rgb). |
|
416 self assert:(samplesPerPixel == 3). |
|
417 self assert:(bitsPerSample asArray = #(8 8 8)). |
|
418 |
|
419 fp := outStream filePointer. |
|
420 fp isNil ifTrue:[ |
|
421 self error:'can only write to an external stream'. |
|
422 ^ false. |
|
423 ]. |
|
424 |
|
425 %{ |
|
426 errMgrStructSize = __mkSmallInteger(sizeof(struct my_error_mgr)); |
|
427 compressStructSize = __mkSmallInteger(sizeof(struct jpeg_compress_struct)); |
|
428 %}. |
|
429 |
|
430 jpeg_error_mgr_struct := ExternalBytes unprotectedNew:errMgrStructSize. |
|
431 jpeg_compress_struct := ExternalBytes unprotectedNew:compressStructSize. |
|
432 errorOccurred := false. |
|
433 |
|
434 %{ /* STACK: 400000 */ |
|
435 struct jpeg_compress_struct *cinfoPtr; |
|
436 struct my_error_mgr *jerrPtr; |
|
437 OBJ j_e_m = __INST(jpeg_error_mgr_struct); |
|
438 OBJ j_c_s = __INST(jpeg_compress_struct); |
|
439 FILE *f = __FILEVal(fp); |
|
440 char *outBuffer; |
|
441 |
|
442 if (__isExternalBytesLike(j_c_s) |
|
443 && __isExternalBytesLike(j_e_m)) { |
|
444 cinfoPtr = (struct jpeg_compress_struct *)(__externalBytesAddress(j_c_s)); |
|
445 jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m)); |
|
446 |
|
447 /* |
|
448 * Initialize the JPEG decompression object with default error handling. |
|
449 */ |
|
450 cinfoPtr->err = jpeg_std_error(&jerrPtr->pub); |
|
451 |
|
452 /* |
|
453 * prepare to handle errors smoothly ... |
|
454 */ |
|
455 jerrPtr->pub.error_exit = my_error_exit; |
|
456 if (setjmp(jerrPtr->setjmp_buffer)) { |
|
457 /* |
|
458 * error occurred ... |
|
459 */ |
|
460 jpeg_destroy_compress(cinfoPtr); |
|
461 RETURN (false); |
|
462 } |
|
463 |
|
464 /* |
|
465 * use my message print function |
|
466 */ |
|
467 jerrPtr->pub.output_message = my_output_message; |
|
468 |
|
469 jpeg_create_compress(cinfoPtr); |
|
470 |
|
471 /* Specify data destination for compression */ |
|
472 jpeg_stdio_dest(cinfoPtr, f); |
|
473 |
|
474 cinfoPtr->image_width = __intVal(__INST(width)); |
|
475 cinfoPtr->image_height = __intVal(__INST(height)); |
|
476 cinfoPtr->input_components = __intVal(__INST(samplesPerPixel)); |
|
477 cinfoPtr->in_color_space = JCS_RGB; |
|
478 |
|
479 jpeg_set_defaults(cinfoPtr); |
|
480 /*set the quality [0..100] */ |
|
481 jpeg_set_quality (cinfoPtr, __intVal(__INST(compressQuality)), 1); |
|
482 jpeg_start_compress(cinfoPtr, 1); |
|
483 |
|
484 |
|
485 cinfoPtr->err->trace_level = 0; |
|
486 } |
|
487 %}. |
|
488 ^ true |
|
489 ! |
|
490 |
|
491 decompressChunkInto:aByteArray startingAt:index |
570 decompressChunkInto:aByteArray startingAt:index |
492 %{ /* STACK: 400000 */ |
571 %{ /* STACK: 400000 */ |
493 struct jpeg_decompress_struct *cinfoPtr; |
572 struct jpeg_decompress_struct *cinfoPtr; |
494 struct my_error_mgr *jerrPtr; |
573 struct my_error_mgr *jerrPtr; |
495 char *rowPtr = NULL; |
574 char *rowPtr = NULL; |
561 ]. |
640 ]. |
562 self breakPoint:#cg. |
641 self breakPoint:#cg. |
563 app1SegmentHandler value:data. |
642 app1SegmentHandler value:data. |
564 ! |
643 ! |
565 |
644 |
|
645 finish_compress |
|
646 %{ /* STACK: 400000 */ |
|
647 struct jpeg_compress_struct *cinfoPtr; |
|
648 struct my_error_mgr *jerrPtr; |
|
649 OBJ j_c_s = __INST(jpeg_compress_struct); |
|
650 OBJ j_e_m = __INST(jpeg_error_mgr_struct); |
|
651 |
|
652 if (__isExternalBytesLike(j_c_s) |
|
653 && __isExternalBytesLike(j_e_m)) { |
|
654 cinfoPtr = (struct jpeg_compress_struct *)(__externalBytesAddress(j_c_s)); |
|
655 jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m)); |
|
656 |
|
657 if (setjmp(jerrPtr->setjmp_buffer)) { |
|
658 jpeg_destroy_compress(cinfoPtr); |
|
659 RETURN (false); |
|
660 } |
|
661 |
|
662 /* finish decompressor */ |
|
663 (void) jpeg_finish_compress(cinfoPtr); |
|
664 (void) jpeg_destroy_compress(cinfoPtr); |
|
665 RETURN (true); |
|
666 } |
|
667 %} |
|
668 ! |
|
669 |
566 finish_decompress |
670 finish_decompress |
567 %{ /* STACK: 400000 */ |
671 %{ /* STACK: 400000 */ |
568 struct jpeg_decompress_struct *cinfoPtr; |
672 struct jpeg_decompress_struct *cinfoPtr; |
569 struct my_error_mgr *jerrPtr; |
673 struct my_error_mgr *jerrPtr; |
570 OBJ j_d_s = __INST(jpeg_decompress_struct); |
674 OBJ j_d_s = __INST(jpeg_decompress_struct); |
581 } |
685 } |
582 |
686 |
583 /* finish decompressor */ |
687 /* finish decompressor */ |
584 (void) jpeg_finish_decompress(cinfoPtr); |
688 (void) jpeg_finish_decompress(cinfoPtr); |
585 (void) jpeg_destroy_decompress(cinfoPtr); |
689 (void) jpeg_destroy_decompress(cinfoPtr); |
586 RETURN (true); |
|
587 } |
|
588 %} |
|
589 ! |
|
590 |
|
591 finish_compress |
|
592 %{ /* STACK: 400000 */ |
|
593 struct jpeg_compress_struct *cinfoPtr; |
|
594 struct my_error_mgr *jerrPtr; |
|
595 OBJ j_c_s = __INST(jpeg_compress_struct); |
|
596 OBJ j_e_m = __INST(jpeg_error_mgr_struct); |
|
597 |
|
598 if (__isExternalBytesLike(j_c_s) |
|
599 && __isExternalBytesLike(j_e_m)) { |
|
600 cinfoPtr = (struct jpeg_compress_struct *)(__externalBytesAddress(j_c_s)); |
|
601 jerrPtr = (struct my_error_mgr *)(__externalBytesAddress(j_e_m)); |
|
602 |
|
603 if (setjmp(jerrPtr->setjmp_buffer)) { |
|
604 jpeg_destroy_compress(cinfoPtr); |
|
605 RETURN (false); |
|
606 } |
|
607 |
|
608 /* finish decompressor */ |
|
609 (void) jpeg_finish_compress(cinfoPtr); |
|
610 (void) jpeg_destroy_compress(cinfoPtr); |
|
611 RETURN (true); |
690 RETURN (true); |
612 } |
691 } |
613 %} |
692 %} |
614 ! |
693 ! |
615 |
694 |
779 " |
858 " |
780 |
859 |
781 "Modified: / 12-12-2011 / 21:34:48 / cg" |
860 "Modified: / 12-12-2011 / 21:34:48 / cg" |
782 ! ! |
861 ! ! |
783 |
862 |
784 !JPEGReader class methodsFor:'defaults'! |
|
785 defaultCompressQuality |
|
786 ^ 85 |
|
787 ! ! |
|
788 |
|
789 !JPEGReader methodsFor:'writing'! |
863 !JPEGReader methodsFor:'writing'! |
790 |
|
791 save:image onStream:aStream |
|
792 "save image in JPG-file-format onto aStream" |
|
793 |
|
794 self save:image onStream:aStream compressQuality:(self class defaultCompressQuality) |
|
795 ! |
|
796 |
|
797 save:image onStream:aStream compressQuality:qualityInPercent |
|
798 "save image in JPG-file-format onto aStream" |
|
799 |
|
800 image depth ~~ 24 ifTrue:[ |
|
801 ^ Image cannotRepresentImageSignal |
|
802 raiseWith:image |
|
803 errorString:('JPG (currently) only supports depth24 images'). |
|
804 ]. |
|
805 |
|
806 outStream := aStream. |
|
807 outStream binary. |
|
808 |
|
809 "/ mask := image mask. |
|
810 "/ mask notNil ifTrue:[ |
|
811 "/ self assignTransparentPixelIn:image |
|
812 "/ ]. |
|
813 |
|
814 width := image width. |
|
815 height := image height. |
|
816 photometric := image photometric. |
|
817 samplesPerPixel := image samplesPerPixel. |
|
818 bitsPerSample := image bitsPerSample. |
|
819 colorMap := image colorMap. |
|
820 bytesPerRow := image bytesPerRow. |
|
821 data := image bits. |
|
822 compressQuality := qualityInPercent. |
|
823 |
|
824 (self create_jpeg_compress_struct not) ifTrue:[ |
|
825 self errir:'JPG: setup error'. |
|
826 ]. |
|
827 |
|
828 self compressScanlines. |
|
829 self finish_compress. |
|
830 ! |
|
831 |
864 |
832 compressScanlines |
865 compressScanlines |
833 %{ |
866 %{ |
834 unsigned char *rowPtr; |
867 unsigned char *rowPtr; |
835 int bpr = __intVal(__INST(bytesPerRow)); |
868 int bpr = __intVal(__INST(bytesPerRow)); |
861 jpeg_write_scanlines(cinfoPtr, &rowPtr, 1); |
894 jpeg_write_scanlines(cinfoPtr, &rowPtr, 1); |
862 rowPtr += bpr; |
895 rowPtr += bpr; |
863 } |
896 } |
864 } |
897 } |
865 %} |
898 %} |
|
899 ! |
|
900 |
|
901 save:image onStream:aStream |
|
902 "save image in JPG-file-format onto aStream" |
|
903 |
|
904 self save:image onStream:aStream compressQuality:(self class defaultCompressQuality) |
|
905 |
|
906 " |
|
907 |i i24 i2| |
|
908 |
|
909 i := Image fromFile:'../../goodies/bitmaps/gifImages/garfield.gif'. |
|
910 i24 := Depth24Image fromImage:i. |
|
911 JPEGReader save:i24 onFile:'test.jpg'. |
|
912 i2 := Image fromFile:'test.jpg'. |
|
913 i2 inspect |
|
914 " |
|
915 |
|
916 "Modified (comment): / 18-02-2016 / 02:36:44 / cg" |
|
917 ! |
|
918 |
|
919 save:image onStream:aStream compressQuality:qualityInPercent |
|
920 "save image in JPG-file-format onto aStream" |
|
921 |
|
922 image depth ~~ 24 ifTrue:[ |
|
923 ^ Image cannotRepresentImageSignal |
|
924 raiseWith:image |
|
925 errorString:('JPG (currently) only supports depth24 images'). |
|
926 ]. |
|
927 |
|
928 outStream := aStream. |
|
929 outStream binary. |
|
930 |
|
931 "/ mask := image mask. |
|
932 "/ mask notNil ifTrue:[ |
|
933 "/ self assignTransparentPixelIn:image |
|
934 "/ ]. |
|
935 |
|
936 width := image width. |
|
937 height := image height. |
|
938 photometric := image photometric. |
|
939 samplesPerPixel := image samplesPerPixel. |
|
940 bitsPerSample := image bitsPerSample. |
|
941 colorMap := image colorMap. |
|
942 bytesPerRow := image bytesPerRow. |
|
943 data := image bits. |
|
944 compressQuality := qualityInPercent. |
|
945 |
|
946 (self create_jpeg_compress_struct not) ifTrue:[ |
|
947 self errir:'JPG: setup error'. |
|
948 ]. |
|
949 |
|
950 self compressScanlines. |
|
951 self finish_compress. |
866 ! ! |
952 ! ! |
867 |
953 |
868 !JPEGReader class methodsFor:'documentation'! |
954 !JPEGReader class methodsFor:'documentation'! |
869 |
955 |
870 version |
956 version |