--- a/WindowsIconReader.st Tue Dec 02 16:32:21 2008 +0100
+++ b/WindowsIconReader.st Tue Dec 02 22:02:23 2008 +0100
@@ -12,7 +12,7 @@
"{ Package: 'stx:libview2' }"
ImageReader subclass:#WindowsIconReader
- instanceVariableNames:''
+ instanceVariableNames:'compression inDepth'
classVariableNames:''
poolDictionaries:''
category:'Graphics-Images-Readers'
@@ -235,6 +235,36 @@
"Modified: 1.2.1997 / 15:03:59 / cg"
! !
+!WindowsIconReader class methodsFor:'image support'!
+
+loadBMPWidth:w height:h bytesPerPixel:bpp compression:c from:aStream into:data
+ |buff idx fileBytesPerRow imgBytesPerRow align|
+
+ align := 4.
+ c == 0 ifTrue:[
+ imgBytesPerRow := w * bpp.
+ fileBytesPerRow := imgBytesPerRow.
+ (fileBytesPerRow bitAnd:(align-1)) ~~ 0 ifTrue:[
+ fileBytesPerRow := (fileBytesPerRow bitAnd:((align-1) bitInvert)) + align.
+ ].
+ "/
+ "/ stupid - last row comes first
+ "/
+ idx := imgBytesPerRow * (h - 1) + 1.
+ buff := ByteArray uninitializedNew:fileBytesPerRow.
+
+ 1 to:h do:[:row |
+ (aStream nextBytes:fileBytesPerRow into:buff) ~~ fileBytesPerRow ifTrue:[
+ ^ false
+ ].
+ data replaceFrom:idx to:idx+imgBytesPerRow-1 with:buff.
+ idx := idx - imgBytesPerRow.
+ ].
+ ^ true
+ ].
+ ^ false.
+! !
+
!WindowsIconReader class methodsFor:'testing'!
canRepresent:anImage
@@ -293,54 +323,393 @@
!WindowsIconReader methodsFor:'private'!
-loadBMPWidth:w height:h depth:d compression:c from:aStream into:data
- "helper: load a BMP image"
+loadBMP1From:aStream into:aByteArray
+ "load bmp-1 bit per pixel imagedata."
+
+ compression == 0 ifFalse:[
+ ^ false
+ ].
+
+ ^ self loadUncompressedFrom:aStream into:aByteArray
+!
+
+loadBMP2From:aStream into:aByteArray
+ "load bmp-2 bit per pixel imagedata."
+
+ compression == 0 ifFalse:[
+ ^ false
+ ].
+
+ ^ self loadUncompressedFrom:aStream into:aByteArray
+!
+
+loadBMP4From:aStream into:aByteArray
+ "load bmp-4 bit per pixel imagedata."
- |buff idx fileBytesPerRow imgBytesPerRow|
+ compression == 0 ifTrue:[
+ ^ self loadUncompressedFrom:aStream into:aByteArray
+ ].
+ compression == 2 ifTrue:[
+ ^ self loadRLECompressedBMP4From:aStream into:aByteArray
+ ].
+
+ ^ false
+!
- d == 8 ifTrue:[
- (self class loadBMP8Width:w height:h compression:c from:aStream into:data) ifFalse:[
- self fileFormatError:'read/decompression failed'.
+loadBMP4to8Width:width height:height from:aStream into:aByteArray
+ "load bmp-4 bit per pixel imagedata. A helper for BMP image reader.
+ Primitive c function for speed"
+
+ |f bytes offset|
+
+ aStream isExternalStream ifTrue:[
+ f := aStream filePointer.
+ f isNil ifTrue:[^ false].
+ ] ifFalse:[
+ aStream isInternalByteStream ifTrue:[
+ bytes := aStream collection.
+ bytes isNil ifTrue:[^ false].
+ offset := aStream position.
+ ] ifFalse:[
^ false
- ].
- ^ true
- ].
- d == 4 ifTrue:[
- (self class loadBMP4to8Width:w height:h compression:c from:aStream into:data) ifFalse:[
- self fileFormatError:'read/decompression failed'.
- ^ false
- ].
- ^ true
+ ]
].
- d == 2 ifTrue:[
- (self class loadBMP2to8Width:w height:h from:aStream into:data) ifFalse:[
- self fileFormatError:'read failed'.
+%{
+#define GETC(fp, cp) (fp ? getc(fp) : *cp++)
+#define FERROR(fp) (fp && ferror(fp))
+
+ FILE *__fp = NULL;
+ char *__cp = NULL;
+
+ if (f != nil) {
+ __fp = __FILEVal(f);
+ } else {
+ __cp = __stringVal(bytes);
+ __cp += __intVal(offset);
+ }
+
+ if (! (__bothSmallInteger(width, height)
+ && __isByteArray(aByteArray))) {
+ RETURN (false);
+ }
+
+{
+ int w = __intVal(width);
+ int h = __intVal(height);
+ int comp = __intVal(__INST(compression));
+ unsigned char *dest = __ByteArrayInstPtr(aByteArray)->ba_element;
+ int szDest = __byteArraySize(aByteArray);
+ int i, j, c, c1, nibnum, padw, x, y;
+ unsigned char *pp;
+
+ c = c1 = 0;
+ if (comp == 0) { /* read uncompressed data */
+ padw = ((w + 7)/8) * 8; /* 'w', padded to be a multiple of 8pix (32bits) */
+
+ for (i=h-1; i>=0; i--) {
+ pp = dest + (i * w);
+ for (j=nibnum=0; j<padw; j++,nibnum++) {
+ if ((nibnum&1) == 0) { /* read the next byte */
+ c = GETC(__fp, __cp);
+ console_printf("\nch: %02x ", c);
+ if (c == EOF) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: premature EOF\n");
+ }
+ RETURN (false);
+ }
+ nibnum = 0;
+ }
+ if (j<w) {
+ if (pp >= (dest+szDest)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
+ }
+ { RETURN (false) };
+ }
+ *pp++ = (c & 0xF0) >> 4;
+console_printf("%d ", pp[-1]);
+ c <<= 4;
+ }
+ }
+ if (FERROR(__fp)) { RETURN (false) };
+ }
+ RETURN (true);
+ }
+ if (comp == 2) { /* read RLE4 compressed data */
+ x = y = 0;
+ pp = dest + x + (h-y-1)*w;
+
+ while (y<h) {
+ c = GETC(__fp, __cp); if (c == EOF) { RETURN (false); }
+ if (c) { /* encoded mode */
+ c &= 0xFF;
+ c1 = GETC(__fp, __cp); if (c1 == EOF) { RETURN (false); }
+ for (i=0; i<c; i++,x++,pp++) {
+ if (pp >= (dest+szDest)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
+ }
+ return 0;
+ }
+ *pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
+ }
+ } else {
+ /* c==0x00 : escape codes */
+ c = GETC(__fp, __cp); if (c == EOF) { RETURN (false); }
+
+ if (c == 0x00) { /* end of line */
+ x=0; y++; pp = dest + x + (h-y-1)*w;
+ } else
+ if (c == 0x01) break; /* end of pic8 */
+
+ else if (c == 0x02) { /* delta */
+ c = GETC(__fp, __cp); if (c == EOF) { RETURN (false); }
+ x += (c & 0xFF);
+ c = GETC(__fp, __cp); if (c == EOF) { RETURN (false); }
+ y += (c & 0xFF);
+ pp = dest + x + (h-y-1)*w;
+ } else { /* absolute mode */
+ c &= 0xFF;
+ for (i=0; i<c; i++, x++, pp++) {
+ if ((i&1) == 0) {
+ c1 = GETC(__fp, __cp); if (c1 == EOF) { RETURN (false); }
+ }
+ if (pp >= (dest+szDest)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
+ }
+ return 0;
+ }
+ *pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
+ }
+
+ if (((c&3)==1) || ((c&3)==2)) {
+ GETC(__fp, __cp); /* read pad byte */
+ }
+ }
+ } /* escape processing */
+ if (FERROR(__fp)) RETURN (false);
+ } /* while */
+ RETURN (true);
+ }
+}
+%}.
+ ^ false
+
+ "Modified: 22.4.1996 / 19:14:32 / cg"
+!
+
+loadBMP8Width:width height:height from:aStream into:aByteArray
+ "load bmp-4 bit per pixel imagedata. A helper for BMP image reader.
+ Primitive c function for speed"
+
+ |f bytes offset|
+
+ aStream isExternalStream ifTrue:[
+ f := aStream filePointer.
+ f isNil ifTrue:[^ false].
+ ] ifFalse:[
+ aStream isInternalByteStream ifTrue:[
+ bytes := aStream collection.
+ bytes isNil ifTrue:[^ false].
+ offset := aStream position.
+ ] ifFalse:[
^ false
- ].
- ^ true
+ ]
].
- d == 1 ifTrue:[
- (self class loadBMP1to8Width:w height:h from:aStream into:data) ifFalse:[
- self fileFormatError:'read failed'.
- ^ false
- ].
- ^ true
- ].
- d == 24 ifTrue:[
- imgBytesPerRow := w * 3.
+%{
+#define GETC(fp, cp) (fp ? getc(fp) : *cp++)
+#define FERROR(fp) (fp && ferror(fp))
+
+ FILE *__fp = NULL;
+ char *__cp = NULL;
+
+ if (f != nil) {
+ __fp = __FILEVal(f);
+ } else {
+ __cp = __stringVal(bytes);
+ __cp += __intVal(offset);
+ }
+
+ if (! (__bothSmallInteger(width, height)
+ && __isByteArray(aByteArray))) {
+ RETURN (false);
+ }
+
+{
+ int w = __intVal(width);
+ int h = __intVal(height);
+ int comp = __intVal(__INST(compression));
+ unsigned char *dest = __ByteArrayInstPtr(aByteArray)->ba_element;
+ int szDest = __byteArraySize(aByteArray);
+ int i,j,c,c1,padw,x,y;
+ unsigned char *pp;
+
+ if (comp == 0) { /* uncompressed data */
+ padw = ((w + 3)/4) * 4;
+
+ for (i=h-1; i>=0; i--) {
+ pp = dest + (i * w);
+
+ for (j=0; j<padw; j++) {
+ c = GETC(__fp, __cp);
+ if (c==EOF) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP premature EOF [%d]\n", __LINE__);
+ console_fprintf(stderr, "h=%d i=%d w=%d j=%d padw=%d\n", h, i, w, j, padw);
+ }
+ RETURN(false);
+ }
+ if (j<w) {
+ if (pp >= (dest+szDest)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
+ }
+ RETURN(false);
+ }
+ *pp++ = c;
+ }
+ }
+ if (FERROR(__fp)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP ferror set\n");
+ }
+ RETURN(false);
+ }
+ }
+ if (FERROR(__fp)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP ferror set at end\n");
+ }
+ RETURN(false);
+ }
+ RETURN(true);
+ }
+ if (comp == 1) { /* RLE8 compressed */
+ x = y = 0;
+ pp = dest + x + (h-y-1)*w;
+
+ while (y<h) {
+ c = GETC(__fp, __cp);
+ if (c == EOF) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
+ }
+ RETURN(false);
+ }
+ if (c) { /* encoded mode */
+ c1 = GETC(__fp, __cp);
+ if (c1 == EOF) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
+ }
+ RETURN(false);
+ }
+ c &= 0xFF;
+
+ for (i=0; i<c; i++,x++,pp++) {
+ if (pp >= (dest+szDest)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 outBuffer overrun\n");
+ }
+ RETURN(false);
+ }
+ *pp = c1;
+ }
+ } else {
+ /* c==0x00 : escape codes */
+ c = GETC(__fp, __cp);
+ if (c == EOF) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
+ }
+ RETURN(false);
+ }
+ if (c == 0x00) { /* end of line */
+ x=0; y++; pp = dest + x + (h-y-1)*w;
+ } else
+ if (c == 0x01) break; /* end of pic8 */
+ else if (c == 0x02) { /* delta */
+ c = GETC(__fp, __cp);
+ if (c == EOF) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
+ }
+ RETURN(false);
+ }
+ x += (c & 0xFF);
+ c = GETC(__fp, __cp);
+ if (c == EOF) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
+ }
+ RETURN(false);
+ }
+ y += (c & 0xFF);
+ pp = dest + x + (h-y-1)*w;
+ } else { /* absolute mode */
+ c &= 0xFF;
+ for (i=0; i<c; i++, x++, pp++) {
+ c1 = GETC(__fp, __cp);
+ if (c1 == EOF) { RETURN(false); }
+ if (pp >= (dest+szDest)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 outBuffer overrun\n");
+ }
+ RETURN(false);
+ }
+ *pp = c1;
+ }
+
+ if (c & 1) GETC(__fp, __cp); /* odd length run: read an extra pad byte */
+ }
+ }
+ if (FERROR(__fp)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 ferror set\n");
+ }
+ RETURN(false);
+ }
+ }
+ if (FERROR(__fp)) {
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP/RLE8 ferror set at end\n");
+ }
+ RETURN(false);
+ }
+ RETURN(true);
+ }
+ if (@global(InfoPrinting) == true) {
+ console_fprintf(stderr, "ImageReader [warning]: BMP unhandled compression: %d\n", comp);
+ }
+ RETURN(false);
+}
+%}.
+ ^ true
+
+ "Modified: 22.4.1996 / 19:14:32 / cg"
+!
+
+loadBMPWidth:w height:h bytesPerPixel:bpp from:aStream into:data
+ |buff idx fileBytesPerRow imgBytesPerRow align|
+
+ align := 4.
+
+ compression == 0 ifTrue:[
+ imgBytesPerRow := w * bpp.
fileBytesPerRow := imgBytesPerRow.
- (fileBytesPerRow bitAnd:3) ~~ 0 ifTrue:[
- fileBytesPerRow := (fileBytesPerRow bitAnd:(3 bitInvert)) + 4.
+ (fileBytesPerRow bitAnd:(align-1)) ~~ 0 ifTrue:[
+ fileBytesPerRow := (fileBytesPerRow bitAnd:((align-1) bitInvert)) + align.
].
"/
"/ stupid - last row comes first
"/
- idx := imgBytesPerRow * (height - 1) + 1.
+ idx := imgBytesPerRow * (h - 1) + 1.
buff := ByteArray uninitializedNew:fileBytesPerRow.
- 1 to:height do:[:row |
+ 1 to:h do:[:row |
(aStream nextBytes:fileBytesPerRow into:buff) ~~ fileBytesPerRow ifTrue:[
- self fileFormatError:'read failed'.
^ false
].
data replaceFrom:idx to:idx+imgBytesPerRow-1 with:buff.
@@ -348,11 +717,148 @@
].
^ true
].
- self fileFormatError:('unsupported depth:' , d printString).
+ ^ false.
+!
+
+loadBMPWidth:w height:h depth:d from:aStream into:data
+ "helper: load a BMP image"
+
+ d == 8 ifTrue:[
+ compression == 0 ifTrue:[
+ ^ self loadUncompressedFrom:aStream into:data.
+ ].
+ compression == 1 ifTrue:[
+ ^ self loadRLECompressedBMP8From:aStream into:data.
+ ].
+self halt.
+ ^ false
+ ].
+ d == 4 ifTrue:[
+ ^ self loadBMP4From:aStream into:data
+ ].
+ d == 2 ifTrue:[
+ ^ self loadBMP2From:aStream into:data
+ ].
+ d == 1 ifTrue:[
+ ^ self loadBMP1From:aStream into:data
+ ].
+ ((d == 16)
+ or:[ (d == 24)
+ or:[ (d == 32) ]]) ifTrue:[
+ (self loadBMPWidth:w height:h bytesPerPixel:(d // 8) from:aStream into:data) ifFalse:[
+ ^ false
+ ].
+ inDepth == 16 ifTrue:[
+ "/ Depth16Image keeps its data MSB (sigh); here they come LSB.
+ data swapBytes.
+ ].
+ inDepth == 24 ifTrue:[
+ "/ Depth24Image keeps its data r/g/b; BMP has it b/g/r (sigh)
+ self swapBytesFromRGB_to_BGR.
+ ].
+
+ ^ true
+ ].
^ false
"Created: / 17.9.1995 / 18:48:11 / claus"
"Modified: / 3.2.1998 / 20:21:16 / cg"
+!
+
+loadRLECompressedBMP8From:aStream into:aByteArray
+ "load bmp-8 bit per pixel imagedata"
+
+ |bytesPerRowInData x y dstIndex lineStartIndex cnt clr code n|
+
+ bytesPerRowInData := self bytesPerRow.
+ x := 0.
+ y := height - 1.
+ lineStartIndex := (y * bytesPerRowInData) + 1.
+ dstIndex := lineStartIndex.
+
+ [ y < height ] whileTrue:[
+ cnt := aStream nextByte.
+ clr := aStream nextByte.
+ cnt ~~ 0 ifTrue:[
+ aByteArray from:dstIndex to:dstIndex+cnt-1 put:clr.
+ x := x + cnt.
+ dstIndex := dstIndex + cnt.
+ ] ifFalse:[
+ "/ cnt == 0: escape codes */
+ code := clr.
+ code == 0 ifTrue:[
+ "/ end of line
+ x := 0.
+ y := y - 1.
+ lineStartIndex := lineStartIndex - bytesPerRowInData.
+ dstIndex := lineStartIndex.
+ ] ifFalse:[
+ code == 1 ifTrue:[
+ "/ end of pic
+ ^ true
+ ].
+ code == 2 ifTrue:[
+ "/ delta
+ x := x + aStream nextSignedByte.
+ y := y - aStream nextSignedByte.
+ lineStartIndex := (y * bytesPerRowInData) + 1.
+ dstIndex := lineStartIndex + x.
+ ] ifFalse:[
+ "/ absolute; cnt pixels coming
+ cnt := code.
+ n := aStream nextBytes:cnt into:aByteArray startingAt:dstIndex.
+ n ~~ cnt ifTrue:[self halt. ^ false].
+ x := x + cnt.
+ dstIndex := dstIndex + cnt.
+ "/ odd count - padd
+ cnt odd ifTrue:[
+ aStream skip:1.
+ ].
+ ].
+ ].
+ ].
+ ].
+ ^ true.
+!
+
+loadUncompressedFrom:aStream into:aByteArray
+ "load bmp-1,2,4 and 8 bit per pixel imagedata."
+
+ |bytesPerRowInStream bytesPerRowInData skip dstIndex n|
+
+ compression == 0 ifFalse:[
+ ^ false
+ ].
+
+ bytesPerRowInStream := Image bytesPerRowForWidth:width depth:inDepth padding:32.
+ bytesPerRowInData := self bytesPerRow.
+ skip := bytesPerRowInStream - bytesPerRowInData.
+
+ "/ bottom row first...
+ dstIndex := (height - 1) * bytesPerRowInData + 1.
+ height to:1 by:-1 do:[:y |
+ n := aStream nextBytes:bytesPerRowInData into:aByteArray startingAt:dstIndex.
+ n ~~ bytesPerRowInData ifTrue:[
+ ^ false.
+ ].
+ skip ~~ 0 ifTrue:[
+ aStream skip:skip.
+ ].
+ dstIndex := dstIndex - bytesPerRowInData.
+ ].
+ ^ true.
+!
+
+swapBytesFromRGB_to_BGR
+ |idx bytesPerRow|
+
+ "/ Depth24Image keeps its data r/g/b; BMP has it b/g/r (sigh)
+ idx := 1.
+ bytesPerRow := self bytesPerRow.
+ 1 to:height do:[:y |
+ self class swap:bytesPerRow bytesFromRGB_to_BGR_in:data startingAt:idx.
+ idx := idx + bytesPerRow.
+ ].
! !
!WindowsIconReader methodsFor:'private-reading'!
@@ -411,7 +917,13 @@
fromOS2Stream:aStream
"read an image from an OS/2 BMP stream"
- |header inDepth inBytesPerRow mask bytesPerRow nColors nByte|
+ ^ self fromOS2Stream:aStream alreadyRead:nil
+!
+
+fromOS2Stream:aStream alreadyRead:bytesAlreadyRead
+ "read an image from an OS/2 BMP stream"
+
+ |header inBytesPerRow mask bytesPerRow nColors nByte|
inStream := aStream.
aStream binary.
@@ -420,7 +932,10 @@
"read the header"
header := ByteArray uninitializedNew:8r110.
- aStream nextBytes:16 into:header.
+ bytesAlreadyRead size > 0 ifTrue:[
+ header replaceFrom:1 with:bytesAlreadyRead
+ ].
+ aStream nextBytes:(16-bytesAlreadyRead size) into:header startingAt:(1+bytesAlreadyRead size).
(header startsWith:#(73 67)) ifTrue:[ "IC"
"IC format"
@@ -448,7 +963,7 @@
"read the mask"
- nByte := width * height + 7 // 8.
+ nByte := ((width * height) + 7) // 8.
mask := ByteArray uninitializedNew:nByte.
aStream nextBytes:nByte into:mask.
@@ -484,12 +999,13 @@
"/ into:data mapping:nil.
"/
- bytesPerRow := width * inDepth + 7 // 8.
+ bytesPerRow := ((width * inDepth) + 7) // 8.
"/ bmp data is always 32bit aligned; if required,
inBytesPerRow := ((bytesPerRow + 3) // 4) * 4.
data := ByteArray uninitializedNew:(height * width "bytesPerRow").
- (self loadBMPWidth:width height:height depth:inDepth compression:0 from:aStream into:data) ifFalse:[
+ compression := 0.
+ (self loadBMPWidth:width height:height depth:inDepth from:aStream into:data) ifFalse:[
^ nil
].
photometric := #palette.
@@ -519,23 +1035,23 @@
byteOrder := #lsb.
aStream binary.
- fileSize := aStream fileSize.
-
- fileSize < 16 ifTrue:[
- ^ self fileFormatError:'short file'.
+ aStream isFileStream ifTrue:[
+ fileSize := aStream fileSize.
+ fileSize < 16 ifTrue:[
+ ^ self fileFormatError:'short file'.
+ ].
].
header := ByteArray uninitializedNew:4.
aStream nextBytes:4 into:header.
- aStream position:0.
(header startsWith:#(66 77)) ifTrue:[ "BM"
"/ 'WinIconReader [info]: Win3.x or OS/2 vsn 2 BM format' infoPrintNL.
- ^ self fromWindowsBMPStream:aStream
+ ^ self fromWindowsBMPStream:aStream alreadyRead:header
].
(header startsWith:#(66 65)) ifTrue:[ "BA"
"/ 'WinIconReader [info]: OS/2 vsn 2 BA format' infoPrintNL.
- ^ self fromOS2Stream:aStream
+ ^ self fromOS2Stream:aStream alreadyRead:header
].
(header startsWith:#(67 73)) ifTrue:[ "CI"
"/ 'WinIconReader [info]: OS/2 vsn 2 BA format' infoPrintNL.
@@ -544,11 +1060,11 @@
].
(header startsWith:#(73 67)) ifTrue:[ "IC"
"/ 'WinIconReader [info]: OS/2 IC format' infoPrintNL.
- ^ self fromOS2Stream:aStream
+ ^ self fromOS2Stream:aStream alreadyRead:header
].
(header startsWith:#(80 84)) ifTrue:[ "PT"
"/ 'WinIconReader [info]: OS/2 PT format' infoPrintNL.
- ^ self fromOS2Stream:aStream
+ ^ self fromOS2Stream:aStream alreadyRead:header
].
(header startsWith:#(16r53 16r5A)) ifTrue:[ "SZ"
"/ 'WinIconReader [info]: OS/2 SZ format' infoPrintNL.
@@ -557,7 +1073,7 @@
].
(header startsWith:#(0 0 1 0)) ifTrue:[
"/ 'WinIconReader [info]: Win3.x ICO format' infoPrintNL.
- ^ self fromWindowsICOStream:aStream
+ ^ self fromWindowsICOStream:aStream alreadyRead:header
].
^ self fileFormatError:('format not supported:'
, ((header at:1) printStringRadix:16)
@@ -590,9 +1106,15 @@
fromWindowsBMPStream:aStream
"read an image from a windows BMP stream"
- | header iSize inDepth inPlanes compression
+ ^ self fromWindowsBMPStream:aStream alreadyRead:nil
+!
+
+fromWindowsBMPStream:aStream alreadyRead:bytesAlreadyRead
+ "read an image from a windows BMP stream"
+
+ | header iSize inPlanes
imgSize resH resV numColor numImportantColor
- dataStart t
+ dataStart redMask greenMask blueMask alphaMask
bytesPerRow numBytesPerColorInColormap|
inStream := aStream.
@@ -602,20 +1124,22 @@
"read the header"
header := ByteArray uninitializedNew:16r54.
- aStream nextBytes:18 into:header.
+ bytesAlreadyRead size > 0 ifTrue:[
+ header replaceFrom:1 with:bytesAlreadyRead
+ ].
+ aStream nextBytes:(18-bytesAlreadyRead size) into:header startingAt:(1+bytesAlreadyRead size).
iSize := header at:(16r0E + 1).
(iSize == 40) ifTrue:[ "header-size"
- "
- its an Windows3.x BMP file
- or OS/2 vsn 2 BMP file
- "
- "/ 'WinIconReader [info]: Win3.x or OS/2 vsn 2 format' infoPrintCR.
+ "/
+ "/ a Windows3.x BMP file
+ "/
+ "/ 'WinIconReader [info]: Win3.x format' infoPrintCR.
aStream nextBytes:(40-4) into:header startingAt:19.
- width := header wordAt:(16r12 + 1) MSB:false. "(header at:19) + ((header at:20) * 256). "
- height := header wordAt:(16r16 + 1) MSB:false. "(header at:23) + ((header at:24) * 256). "
+ width := header wordAt:(16r12 + 1) MSB:false.
+ height := header wordAt:(16r16 + 1) MSB:false.
inPlanes := header wordAt:(16r1A + 1) MSB:false.
inDepth := header wordAt:(16r1C + 1) MSB:false.
compression := header wordAt:(16r1E + 1) MSB:false.
@@ -624,6 +1148,10 @@
resV := header doubleWordAt:(16r2A + 1) MSB:false.
numColor := header doubleWordAt:(16r2E + 1) MSB:false.
numImportantColor := header doubleWordAt:(16r32 + 1) MSB:false.
+ redMask := header doubleWordAt:(16r36 + 1) MSB:false.
+ greenMask := header doubleWordAt:(16r3A + 1) MSB:false.
+ blueMask := header doubleWordAt:(16r3E + 1) MSB:false.
+ alphaMask := header doubleWordAt:(16r42 + 1) MSB:false.
numColor == 0 ifTrue:[
"
@@ -638,23 +1166,38 @@
numBytesPerColorInColormap := 4.
dataStart := header wordAt:(16r0A + 1) MSB:false
] ifFalse:[
- (iSize == 12) ifTrue:[ "core-info header size"
- "
- its an OS/2 (vsn1.2) BMP file
- "
- "/ 'WinIconReader [info]: OS/2 vsn 1.2 format' infoPrintCR.
- aStream nextBytes:(12-4) into:header startingAt:19.
-
- width := header wordAt:(16r12 + 1) MSB:false. "(header at:19) + ((header at:20) * 256). "
- height := header wordAt:(16r14 + 1) MSB:false. "(header at:21) + ((header at:22) * 256). "
- inPlanes := header wordAt:(16r16 + 1) MSB:false.
- inDepth := header wordAt:(16r18 + 1) MSB:false.
- numColor := 1 bitShift:inDepth.
+ ((iSize == 12) or:[iSize >= 64]) ifTrue:[
+ "/
+ "/ its an OS/2 BMP file
+ "/
+ "/ 'WinIconReader [info]: OS/2 format' infoPrintCR.
+ aStream nextBytes:(iSize-4) into:header startingAt:19.
numBytesPerColorInColormap := 3.
- compression := 0.
- "/ dataStart := header wordAt:(16r0A + 1) MSB:false.
dataStart := nil.
+
+ iSize == 12 ifTrue:[
+ width := header wordAt:(16r12 + 1) MSB:false.
+ height := header wordAt:(16r14 + 1) MSB:false.
+ inPlanes := header wordAt:(16r16 + 1) MSB:false.
+ inDepth := header wordAt:(16r18 + 1) MSB:false.
+ "/ dataStart := header wordAt:(16r0A + 1) MSB:false.
+ compression := 0.
+ ].
+ iSize >= 64 ifTrue:[
+ "/
+ "/ its an OS/2 (vsn2) BMP file
+ "/
+ width := header doubleWordAt:(16r12 + 1) MSB:false.
+ height := header doubleWordAt:(16r16 + 1) MSB:false.
+ inPlanes := header wordAt:(16r1A + 1) MSB:false.
+ inDepth := header wordAt:(16r1c + 1) MSB:false.
+ compression := header doubleWordAt:(16r1e + 1) MSB:false.
+ numColor := header doubleWordAt:(16r2E + 1) MSB:false.
+ numImportantColor := header doubleWordAt:(16r32 + 1) MSB:false.
+ dataStart := header wordAt:(16r0A + 1) MSB:false.
+ ].
+ numColor := 1 bitShift:inDepth.
] ifFalse:[
^ self fileFormatError:'unknown format'.
].
@@ -678,7 +1221,6 @@
].
"/ check for valid compression
-
compression ~~ 0 ifTrue:[
"/ some compression
compression == 1 ifTrue:[
@@ -693,6 +1235,15 @@
^ self fileFormatError:'RLE4 compression only supported with depth4 images'.
].
].
+ compression == 3 ifTrue:[
+ "/ BITFIELDS - must be depth-16 or 32
+ ((inDepth ~~ 16) and:[inDepth ~~ 32]) ifTrue:[
+ ^ self fileFormatError:'BITFIELDS compression only supported with depth16/32 images'.
+ ].
+ ].
+ compression >= 4 ifTrue:[
+ ^ self fileFormatError:'unsupported compression'.
+ ].
].
inPlanes ~~ 1 ifTrue:[
@@ -700,43 +1251,62 @@
].
dataStart notNil ifTrue:[
- aStream position1Based:(dataStart + 1).
+ aStream position:dataStart.
+ ].
+
+ inDepth <= 8 ifTrue:[
+ samplesPerPixel := 1.
+ bitsPerSample := Array with:inDepth.
+ photometric := #palette.
+ ] ifFalse:[
+ inDepth == 16 ifTrue:[
+ photometric := #palette.
+ samplesPerPixel := 3.
+ bitsPerSample := #(5 5 5).
+ colorMap := FixedPalette
+ redShift:10 redMask:16r1f
+ greenShift:5 greenMask:16r1f
+ blueShift:0 blueMask:16r1F.
+
+ ] ifFalse:[
+ inDepth == 24 ifTrue:[
+ photometric := #rgb.
+ samplesPerPixel := 3.
+ bitsPerSample := #(8 8 8).
+ ] ifFalse:[
+ inDepth == 32 ifTrue:[
+ photometric := #rgb.
+ samplesPerPixel := 4.
+ bitsPerSample := #(8 8 8 8).
+ ] ifFalse:[
+ ^ self fileFormatError:'unsupported depth'.
+ ]
+ ]
+ ]
].
inDepth == 24 ifTrue:[
bytesPerRow := width * 3
] ifFalse:[
- bytesPerRow := width
+ inDepth == 16 ifTrue:[
+ bytesPerRow := width * 2
+ ] ifFalse:[
+ inDepth == 32 ifTrue:[
+ bytesPerRow := width * 4
+ ] ifFalse:[
+ bytesPerRow := self bytesPerRow
+ ].
+ ].
].
data := ByteArray uninitializedNew:(height * bytesPerRow).
"/ read & possibly decompress
- (self loadBMPWidth:width height:height depth:inDepth compression:compression from:aStream into:data) ifFalse:[
+ (self loadBMPWidth:width height:height depth:inDepth from:aStream into:data) ifFalse:[
+ self fileFormatError:('read/decompression error').
^ nil
].
- inDepth == 24 ifTrue:[
- photometric := #rgb.
- samplesPerPixel := 3.
- bitsPerSample := #(8 8 8).
- "/ stupid must swap red & blue bytes
-
- 1 to:data size by:3 do:[:i |
- t := data at:i.
- data at:i put:(data at:i+2).
- data at:i+2 put:t
- ].
- ] ifFalse:[
- samplesPerPixel := 1.
- bitsPerSample := Array with:inDepth.
-
- inDepth == 1 ifTrue:[
- photometric := #blackIs0.
- ] ifFalse:[
- photometric := #palette.
- ].
- ].
^ self image
"Modified: / 17-09-1995 / 18:48:46 / claus"
@@ -765,7 +1335,13 @@
fromWindowsICOStream:aStream
"read an image from a windows ICO stream"
- |header inDepth
+ ^ self fromWindowsICOStream:aStream alreadyRead:nil
+!
+
+fromWindowsICOStream:aStream alreadyRead:bytesAlreadyRead
+ "read an image from a windows ICO stream"
+
+ |header
srcIndex dstIndex
rawData tmp bytesPerRow nColor cmapSize|
@@ -775,7 +1351,11 @@
"read the header"
header := ByteArray uninitializedNew:(6 + 16 + 40).
- aStream nextBytes:(6 + 16 + 40) into:header.
+ bytesAlreadyRead size > 0 ifTrue:[
+ header replaceFrom:1 with:bytesAlreadyRead
+ ].
+ aStream nextBytes:((6 + 16 + 40)-bytesAlreadyRead size) into:header startingAt:(1+bytesAlreadyRead size).
+
width := header at:(6+1).
height := header at:(7+1).
nColor := header at:(8+1).
@@ -809,7 +1389,7 @@
"read the data bits"
- bytesPerRow := width * inDepth + 7 // 8.
+ bytesPerRow := ((width * inDepth) + 7) // 8.
rawData := ByteArray uninitializedNew:(height * bytesPerRow).
aStream nextBytes:(height * bytesPerRow) into:rawData.
@@ -1102,7 +1682,7 @@
!WindowsIconReader class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libview2/WindowsIconReader.st,v 1.61 2007-05-30 15:41:12 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libview2/WindowsIconReader.st,v 1.62 2008-12-02 21:02:23 cg Exp $'
! !
WindowsIconReader initialize!