WindowsIconReader.st
changeset 713 548898fdd1dc
parent 692 e6af4c70e066
child 819 e358c08e45ea
--- a/WindowsIconReader.st	Wed Oct 15 19:58:09 1997 +0200
+++ b/WindowsIconReader.st	Tue Oct 21 20:27:06 1997 +0200
@@ -10,6 +10,8 @@
  hereby transferred.
 "
 
+'From Smalltalk/X, Version:3.2.1 on 21-oct-1997 at 5:07:02 pm'                  !
+
 ImageReader subclass:#WindowsIconReader
 	instanceVariableNames:''
 	classVariableNames:''
@@ -36,7 +38,7 @@
 documentation
 "
     this class provides methods for loading Windows and OS2 icon files.
-    Image writing is not supported.
+    Image writing is only supported for BMP format with depth 1,4,8 and 24 bit images.
 
     The reader tries to figure out which version of BMP/ICO is used.
     It seems to be able to load most formats, but who knows ...
@@ -63,6 +65,15 @@
 
 !WindowsIconReader class methodsFor:'testing'!
 
+canRepresent:anImage
+    "return true, if anImage can be represented in my file format.
+     BMP supports depth 1,4,8 and 24."
+
+    ^ (#(1 4 8 24) includes:anImage depth)
+
+    "Created: 17.10.1997 / 20:18:23 / cg"
+!
+
 isValidImageFile:aFileName
     "return true, if aFileName contains a valid windows bitmap-file image"
 
@@ -113,7 +124,7 @@
 loadBMPWidth:w height:h depth:d compression:c from:aStream into:data
     "helper: load a BMP image"
 
-    |buff idx1 idx2 bytesPerRow|
+    |buff idx fileBytesPerRow imgBytesPerRow|
 
     d == 8 ifTrue:[
         (self class loadBMP8Width:w height:h compression:c from:aStream into:data) ifFalse:[
@@ -144,29 +155,31 @@
         ^ true
     ].
     d == 24 ifTrue:[
-        bytesPerRow := w * 3.
-        ((aStream nextBytes:(h * bytesPerRow) into:data) ~~ (h * bytesPerRow)) ifTrue:[
-            'WinIconReader [warning]: read failed' infoPrintNL.
-            ^ false
+        imgBytesPerRow := w * 3.
+        fileBytesPerRow := imgBytesPerRow.
+        (fileBytesPerRow bitAnd:3) ~~ 0 ifTrue:[
+            fileBytesPerRow := (fileBytesPerRow bitAnd:(3 bitInvert)) + 4.
         ].
+        "/
         "/ stupid - last row comes first
+        "/
+        idx := imgBytesPerRow * (height - 1) + 1.
+        buff := ByteArray uninitializedNew:fileBytesPerRow.
 
-        buff := ByteArray uninitializedNew:bytesPerRow.
-        idx1 := 1.
-        idx2 := 1 + (h-1 * bytesPerRow).
-        [idx1 < idx2] whileTrue:[
-            buff replaceFrom:1 to:bytesPerRow with:data startingAt:idx1.
-            data replaceFrom:idx1 to:(idx1 + bytesPerRow - 1) with:data startingAt:idx2.
-            data replaceFrom:idx2 to:(idx2 + bytesPerRow - 1) with:buff startingAt:1.
-            idx1 := idx1 + bytesPerRow.
-            idx2 := idx2 - bytesPerRow.
+        1 to:height do:[:row |
+            (aStream nextBytes:fileBytesPerRow into:buff) ~~ fileBytesPerRow ifTrue:[
+                'WinIconReader [warning]: read failed' infoPrintNL.
+                ^ false
+            ].
+            data replaceFrom:idx to:idx+imgBytesPerRow-1 with:buff.
+            idx := idx - imgBytesPerRow.
         ].
         ^ true
     ].
     'WinIconReader [warning]: unsupported depth:' infoPrint. d infoPrintNL.
 
     "Created: 17.9.1995 / 18:48:11 / claus"
-    "Modified: 28.1.1997 / 01:46:07 / cg"
+    "Modified: 19.10.1997 / 00:01:56 / cg"
 ! !
 
 !WindowsIconReader methodsFor:'reading from file'!
@@ -665,9 +678,126 @@
     "Modified: 24.4.1997 / 22:03:48 / cg"
 ! !
 
+!WindowsIconReader methodsFor:'writing to file'!
+
+save:image onFile:aFileName
+    "save image as BMP file on aFileName.
+     Only depth 1,4,8 and 24 images can be represented in this format."
+
+    self saveBMP:image onFile:aFileName.
+
+    "Modified: 17.10.1997 / 20:16:53 / cg"
+!
+
+saveBMP:image onFile:fileName 
+    "save image as BMP file on aFileName.
+     Only depth 1,4,8 and 24 images can be represented in this format."
+
+    |depth bhSize biSize biClrUsed biSizeImage bfOffBits rowBytes imgBytesPerRow data srcIndex row|
+
+    depth := image depth.
+    width := image width.
+    height := image height.
+
+    (#(1 4 8 24) includes:depth) ifFalse:[
+        ^ Image cannotRepresentImageSignal 
+            raiseWith:image
+            errorString:('BMP format only supports depths 1,4,8 and 24').
+    ].
+    image mask notNil ifTrue:[
+        Image informationLostQuerySignal
+            raiseWith:image
+            errorString:('BMP format does not support an imageMask').
+    ].
+
+    bhSize := 14.  "# bytes in file header"
+    biSize := 40.  "info header size in bytes" 
+    biClrUsed := (depth >= 24) ifTrue:[0] ifFalse:[1 << depth].  "No. color table entries"
+    bfOffBits := biSize + bhSize + (4*biClrUsed).
+    "/ bmp aligns rows on a longword boundary
+    rowBytes := ((depth min:24) * width + 31 // 32) * 4.
+    biSizeImage := height * rowBytes.
+
+    outStream := fileName asFilename writeStream.
+    outStream binary.
+    byteOrder := #lsb.
+
+    "Write the file header"
+    self writeShort:19778.  "bfType = BM" 
+    self writeLong:(bfOffBits + biSizeImage).  "Entire file size in bytes"
+    self writeLong:0.  "bfReserved" 
+    self writeLong:bfOffBits.  "Offset of bitmap data from start of hdr (and file)"
+
+    "Write the bitmap info header"
+    outStream position: bhSize+1.
+    self writeLong:biSize.  "info header size in bytes" 
+    self writeLong:width.  "biWidth" 
+    self writeLong:height.  "biHeight" 
+    self writeShort:1.  "biPlanes" 
+    self writeShort:(depth min:24).  "biBitCount" 
+    self writeLong:0.  "biCompression" 
+    self writeLong:biSizeImage.  "size of image section in bytes"
+    self writeLong:2800.  "biXPelsPerMeter" 
+    self writeLong:2800.  "biYPelsPerMeter" 
+    self writeLong:biClrUsed.
+    self writeLong:0.  "biClrImportant" 
+    1 to:biClrUsed do:[:i |  "Color map"
+        |clr r g b|
+
+        clr := image colorFromValue:i-1.
+        clr isNil ifTrue:[
+            r := g := b := 0.
+        ] ifFalse:[
+            r := clr redByte.
+            g := clr greenByte.
+            b := clr blueByte.
+        ].
+
+        "/ put B,G,R
+        outStream nextPut:b.
+        outStream nextPut:g.
+        outStream nextPut:r.
+        outStream nextPut:0.
+    ].
+
+    imgBytesPerRow := image bytesPerRow.
+    data := image data.
+
+
+    "/ sorry, must extract rows individually
+    "/ (even if alignment is correct),
+    "/ since BMP saves rows bottom-to-top
+
+    row := ByteArray new:rowBytes.
+
+    srcIndex := 1 + (height * imgBytesPerRow).
+    1 to:height do:[:i |
+        srcIndex := srcIndex - imgBytesPerRow.
+        row replaceFrom:1 to:imgBytesPerRow with:data startingAt:srcIndex.
+        outStream nextPutAll:row.
+    ].
+
+    outStream close.
+
+    "
+     |i|
+
+     i := Image fromFile:'bitmaps/SBrowser.xbm'.
+     WindowsIconReader save:i onFile:'test.bmp'.
+    "
+    "
+     |i|
+
+     i := Image fromFile:'bitmaps/gifImages/garfield.gif'.
+     WindowsIconReader save:i onFile:'test.bmp'.
+    "
+
+    "Modified: 21.10.1997 / 05:02:02 / cg"
+! !
+
 !WindowsIconReader class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview2/WindowsIconReader.st,v 1.36 1997-09-10 21:28:18 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview2/WindowsIconReader.st,v 1.37 1997-10-21 18:27:06 cg Exp $'
 ! !
 WindowsIconReader initialize!