many fixes
authorClaus Gittinger <cg@exept.de>
Tue, 02 Dec 2008 22:02:23 +0100
changeset 2571 ae60c0d0452d
parent 2570 a089fc8372f1
child 2572 d7cb5bf6fe77
many fixes
WindowsIconReader.st
--- 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!