--- a/WindowsIconReader.st Wed Feb 22 18:18:58 2017 +0100
+++ b/WindowsIconReader.st Wed Feb 22 18:19:29 2017 +0100
@@ -41,7 +41,7 @@
"
this class provides methods for loading Windows and OS2 icon and bmp files.
- The class name *IconReader is a bad, historic choice - it ws originally
+ The class name *IconReader is a bad, historic choice - it was originally
written to read icons only, but evolved over time and is now also
capable of reading/writing bmp and cursor files.
@@ -53,11 +53,20 @@
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 ...
+ [notice:]
+ when reading an ICO/CUR file with multiple icons in it,
+ the first image is returned as such, holding on the other images via its
+ imageFrames instvar.
+ Thus, the imageEditor will usually present the first of the images,
+ and offer a next-in-sequence button to step through them.
+ To get a collection of all images, collect the images from the sequence, as in:
+ someIcoImage imageFrames collect:#image
+
[See also:]
Image Form Icon
BlitImageReader FaceReader GIFReader JPEGReader PBMReader PCXReader
ST80FormReader SunRasterReader TargaReader TIFFReader
- XBMReader XPMReader XWDReader
+ XBMReader XPMReader XWDReader MacOSXIconReader
"
!
@@ -633,7 +642,10 @@
] ifFalse:[
skip := fileBytesPerRow - imgBytesPerRow.
1 to:h do:[:row |
- (aStream nextBytes:imgBytesPerRow into:aByteArray startingAt:dstIdx) == imgBytesPerRow ifFalse:[
+ |nRead|
+
+ nRead := aStream nextBytes:imgBytesPerRow into:aByteArray startingAt:dstIdx.
+ nRead == imgBytesPerRow ifFalse:[
^ false
].
skip ~~ 0 ifTrue:[ aStream skip:skip ].
@@ -641,6 +653,8 @@
].
].
^ true
+
+ "Modified: / 22-02-2017 / 16:20:50 / cg"
!
loadBMPWidth:w height:h depth:d from:aStream into:aByteArray
@@ -666,9 +680,8 @@
d == 1 ifTrue:[
^ self loadBMP1From:aStream into:aByteArray
].
- ((d == 16)
- or:[ (d == 24)
- or:[ (d == 32) ]]) ifTrue:[
+
+ ((d == 16) or:[ (d == 24) or:[ (d == 32) ]]) ifTrue:[
(self loadBMPWidth:w height:h bytesPerPixel:(d // 8) from:aStream into:aByteArray) ifFalse:[
^ false
].
@@ -677,8 +690,9 @@
self fileFormatError:('unsupported depth:', d printString).
^ false
- "Created: / 17.9.1995 / 18:48:11 / claus"
- "Modified: / 3.2.1998 / 20:21:16 / cg"
+ "Created: / 17-09-1995 / 18:48:11 / claus"
+ "Modified: / 03-02-1998 / 20:21:16 / cg"
+ "Modified (format): / 22-02-2017 / 17:04:56 / cg"
!
loadRLECompressedBMP4From:aStream into:aByteArray
@@ -1161,6 +1175,10 @@
"/ 'WinIconReader [info]: Win3.x ICO format' infoPrintNL.
^ self fromWindowsICOStream:aStream alreadyRead:header
].
+ (header startsWith:#(0 0 2 0)) ifTrue:[
+ "/ 'WinIconReader [info]: Win3.x CUR format' infoPrintNL.
+ ^ self fromWindowsICOStream:aStream alreadyRead:header
+ ].
].
^ self fileFormatError:('format not supported: %02x %02x' printfWith:(header at:1) with:(header at:2))
@@ -1169,8 +1187,8 @@
Image fromFile:'/phys/clam//LocalLibrary/Images/OS2_icons/dos.ico'
"
- "Modified: / 17.9.1995 / 18:59:07 / claus"
- "Modified: / 3.2.1998 / 20:18:14 / cg"
+ "Modified: / 17-09-1995 / 18:59:07 / claus"
+ "Modified: / 22-02-2017 / 18:12:21 / cg"
!
fromWindowsBMPFile: aFilename
@@ -1194,7 +1212,7 @@
^ self fromWindowsBMPStream:aStream alreadyRead:nil
!
-fromWindowsBMPStream:aStream alreadyRead:bytesAlreadyRead
+fromWindowsBMPStream:aStream alreadyRead:fileHeaerBytesAlreadyRead
"read an image from a windows BMP stream"
| fileHeader bitmapHeader iSize inPlanes
@@ -1209,12 +1227,18 @@
"read the header"
- fileHeader := ByteArray new:14.
- bytesAlreadyRead size > 0 ifTrue:[
- fileHeader replaceFrom:1 with:bytesAlreadyRead
+ fileHeaerBytesAlreadyRead size >= 16 ifTrue:[
+ "/ used when coming from an ICO file.
+ fileHeader := nil.
+ ] ifFalse:[
+ fileHeader := ByteArray new:14.
+ fileHeaerBytesAlreadyRead size > 0 ifTrue:[
+ fileHeader replaceFrom:1 with:fileHeaerBytesAlreadyRead
+ ].
+ aStream nextBytes:(14-fileHeaerBytesAlreadyRead size) into:fileHeader startingAt:(1+fileHeaerBytesAlreadyRead size).
+ dataStart := fileHeader unsignedInt32At:(10 + 1) MSB:false.
].
- aStream nextBytes:(14-bytesAlreadyRead size) into:fileHeader startingAt:(1+bytesAlreadyRead size).
-
+
iSize := aStream nextUnsignedInt32MSB:false.
iSize > 124 ifTrue:[
^ self fileFormatError:'unknown format'.
@@ -1223,8 +1247,6 @@
bitmapHeader := ByteArray new:iSize+16. "/ reserve to allow masks to be read with iSize=40
aStream nextBytes:(iSize-4) into:bitmapHeader startingAt:(1+4).
- dataStart := fileHeader unsignedInt32At:(10 + 1) MSB:false.
-
alphaMask := 0.
((iSize == 40) or:[iSize == 52 or:[iSize == 56 or:[iSize == 108 or:[iSize == 124]]]]) ifTrue:[ "header-size"
@@ -1234,9 +1256,13 @@
"/ a Windows5.x BMP file (124)
"/
"/ 'WinIconReader [info]: Win3.x/Win4.x/Win5.x format' infoPrintCR.
-
- width := bitmapHeader unsignedInt32At:(4 + 1) MSB:false.
- height := bitmapHeader signedInt32At:(8 + 1) MSB:false.
+ fileHeader isNil ifTrue:[
+ self assert:(bitmapHeader unsignedInt32At:(4 + 1) MSB:false) == width.
+ self assert:(bitmapHeader signedInt32At:(8 + 1) MSB:false) == (height*2).
+ ] ifFalse:[
+ width := bitmapHeader unsignedInt32At:(4 + 1) MSB:false.
+ height := bitmapHeader signedInt32At:(8 + 1) MSB:false.
+ ].
inPlanes := bitmapHeader unsignedInt16At:(12 + 1) MSB:false.
inDepth := bitmapHeader unsignedInt16At:(14 + 1) MSB:false.
compression := bitmapHeader unsignedInt32At:(16 + 1) MSB:false.
@@ -1463,7 +1489,7 @@
^ self image
"Modified: / 17-09-1995 / 18:48:46 / claus"
- "Modified: / 30-05-2007 / 16:57:39 / cg"
+ "Modified: / 22-02-2017 / 18:03:10 / cg"
!
fromWindowsICOFile:aFilename
@@ -1496,6 +1522,51 @@
fromWindowsICOStream:aStream alreadyRead:bytesAlreadyRead
"read an image from a windows ICO stream"
+ |header nImages images|
+
+ inStream := aStream.
+ aStream binary.
+
+ "read the ICO/CUR header"
+ header := ByteArray uninitializedNew:6.
+ bytesAlreadyRead notEmptyOrNil ifTrue:[
+ header replaceFrom:1 with:bytesAlreadyRead
+ ].
+ aStream nextBytes:((6)-bytesAlreadyRead size) into:header startingAt:(1+bytesAlreadyRead size).
+ (header startsWith:#[0 0 1 0]) ifFalse:[
+ (header startsWith:#[0 0 2 0]) ifFalse:[
+ self fileFormatError:'not an ICO/CUR file'.
+ ].
+ ].
+
+ nImages := header unsignedInt16At:4+1 MSB:false.
+ images := ImageSequence new.
+ 1 to:nImages do:[:each |
+ |img|
+
+ img := self readSingleImageFromWindowsICOStream:aStream.
+ img imageSequence:images.
+ images add:(ImageFrame new image:img).
+ ].
+ imageSequence := images.
+ ^ images first image.
+
+ "
+ WindowsIconReader new fromWindowsICOFile:'~/work/exept/expecco/application/expecco.ico'
+ "
+ "
+ |icons|
+
+ icons := WindowsIconReader new fromWindowsICOFile:'~/work/exept/expecco/application/expecco.ico'
+ MacOSXIconReader save:icons onFile:'~/work/exept/expecco/application/expecco.icns'.
+ "
+
+ "Modified (comment): / 22-02-2017 / 18:14:12 / cg"
+!
+
+old_fromWindowsICOStream:aStream alreadyRead:bytesAlreadyRead
+ "read an image from a windows ICO stream"
+
|header
srcIndex dstIndex
rawData tmp bytesPerRow nColor cmapSize|
@@ -1606,7 +1677,52 @@
WindowsIconReader new fromWindowsICOFile:'/phys/clam2//LocalLibrary/Images/WIN_icons/ibm.ico'.
"
- "Modified: / 30-05-2007 / 16:58:11 / cg"
+ "Created: / 22-02-2017 / 15:19:49 / cg"
+!
+
+readSingleImageFromWindowsICOStream:aStream
+ "read one image from a windows ICO stream"
+
+ |icondirEntryHeader nBytes fileOffset savedPosition nColor nPlanes nBitsPerPixel img|
+
+ inStream := aStream.
+ aStream binary.
+
+ "read the icondirEntryHeader"
+ icondirEntryHeader := aStream nextBytes:16.
+
+ width := icondirEntryHeader at:0+1.
+ width == 0 ifTrue:[ width := 256].
+ height := icondirEntryHeader at:1+1.
+ height == 0 ifTrue:[ height := 256].
+ nColor := icondirEntryHeader at:2+1. "/ 0 if not a palette image
+ "/ reserved := icondirEntryHeader at:3+1.
+ nPlanes := icondirEntryHeader unsignedInt16At:4+1 MSB:false.
+ nBitsPerPixel := icondirEntryHeader unsignedInt16At:6+1 MSB:false.
+ nBytes := icondirEntryHeader unsignedInt32At:(8+1) MSB:false.
+ fileOffset := icondirEntryHeader unsignedInt32At:(12+1) MSB:false.
+
+ savedPosition := aStream position.
+ aStream position:fileOffset.
+ (aStream nextBytes:(PNGReader pngHeader size)) = PNGReader pngHeader ifTrue:[
+ "/ PNG incl bitmapFileHeader
+ aStream position:fileOffset.
+ img := PNGReader fromStream:aStream.
+ ] ifFalse:[
+ "/ BMP without bitmapFileHeader
+ aStream position:fileOffset.
+ img := self fromWindowsBMPStream:aStream alreadyRead:icondirEntryHeader.
+ ].
+ aStream position:savedPosition.
+
+ ^ img
+
+ "
+ WindowsIconReader new fromWindowsICOFile:'/phys/clam2//LocalLibrary/Images/WIN_icons/ibm.ico'.
+ "
+
+ "Created: / 22-02-2017 / 15:22:09 / cg"
+ "Modified (comment): / 22-02-2017 / 17:42:48 / cg"
! !
!WindowsIconReader methodsFor:'writing'!