ImageEditor.st
changeset 919 4e50c3164625
parent 917 43e80afe8222
child 920 bde5b9369ef8
--- a/ImageEditor.st	Mon Jul 27 18:20:47 1998 +0200
+++ b/ImageEditor.st	Tue Jul 28 21:48:48 1998 +0200
@@ -788,7 +788,7 @@
                 )
             )
              #(#MenuItem
-                #label: 'Color Map'
+                #label: 'ColorMap'
                 #translateLabel: true
                 #activeHelpKey: #colorMap
                 #enabled: #imageIsLoaded
@@ -855,6 +855,14 @@
                           #argument: '1-plane + mask'
                           #indication: #colorMapMode:value:
                       )
+                       #(#MenuItem
+                          #label: '-'
+                      )
+                       #(#MenuItem
+                          #label: 'Compress colormap'
+                          #translateLabel: true
+                          #value: #compressColorMap
+                      )
                     ) nil
                     nil
                 )
@@ -900,6 +908,8 @@
           ) nil
           nil
       )
+
+    "Modified: / 28.7.1998 / 20:03:52 / cg"
 !
 
 menuMouseButtonColors
@@ -1348,80 +1358,6 @@
     ^colorMapMode = aMode
 !
 
-colorMapMode: aMode value: aValue
-    "calculates a new color map for the image from aMode"
-
-    |depth newColorMap newImage image newColors realColorMap oldFileName| 
-
-    imageEditView makeUndo.
-
-    newColorMap := self class listOfColorMaps at: aMode.
-    depth       := (newColorMap size log: 2) asInteger. 
-    newImage    := (Image implementorForDepth: depth) new.  
-    oldFileName := self image fileName.
-
-    Object errorSignal handle:
-    [:ex|
-        Object errorSignal handle:
-        [:ex|
-            imageEditView undo.
-            ^self warn: 'Convertation failed!!'
-        ]
-        do:
-        [
-            self image colorsFromX:0 y:0 toX:(self image width-1) y:(self image height-1) do:
-            [:x :y :clr |
-                (newColorMap includes: clr)
-                    ifTrue: [self image colorAtX:x y:y put:clr]
-                    ifFalse: [self image colorAtX:x y:y put: self image colorMap first]
-            ].
-            image := newImage fromImage: self image.
-        ].
-    ] 
-    do:
-    [ 
-        image := newImage fromImage: self image
-    ].
-
-    (String fromString: aMode) reverse readStream nextWord reverse = 'mask'
-    ifTrue:
-    [
-        image mask isNil
-        ifTrue:
-        [
-            image mask: (Depth1Image fromImage: (image asThresholdMonochromeImage: 0.1)). 
-        ].
-    ]
-    ifFalse:
-    [ 
-        image mask: nil.
-    ]. 
-    realColorMap := OrderedCollection new.
-    image realColorMap do:
-    [:clr|
-        (realColorMap includes: clr) ifFalse: [realColorMap add: clr]
-    ].
-    newColors := realColorMap copyFrom: 1 to: (newColorMap size min: realColorMap size).
-    newColorMap do:
-    [:clr|
-        ((newColors size < newColorMap size) and: [(newColors includes: clr) not]) 
-        ifTrue:
-        [      
-            newColors add: clr
-        ]
-    ].                  
-    image colorMap: newColors.
-    colorMapMode := aMode.            
-    (imageEditView image: image) notNil
-    ifTrue:
-    [
-        self image fileName: oldFileName.
-        self listOfColors contents: image colorMap.
-        self findColorMapMode.
-        self updateLabelsAndHistory.
-    ]
-!
-
 editMode: aMode
     "returns whether editMode is equal to aMode"
 
@@ -1520,6 +1456,254 @@
     "Modified: / 26.7.1998 / 12:32:55 / cg"
 ! !
 
+!ImageEditor methodsFor:'user actions - colormap'!
+
+colorMapMode:aMode value: aValue
+    "calculates a new color map for the image from aMode"
+
+    |depth numColors newColorMap newImage 
+     oldImage image newColors realColorMap oldFileName
+     usedColors useNearest usageCounts tmpBits tmpMap| 
+
+    self withExecuteCursorDo:[
+        oldImage := self image.
+
+        newColorMap := self class listOfColorMaps at:aMode.
+        depth       := (newColorMap size log: 2) asInteger. 
+
+        useNearest := false.
+        (self confirm:'Keep colormap (or use standard)') ifTrue:[
+            "/ keep the colormap
+            depth > oldImage depth ifTrue:[
+                "/ easy - simply copy the part
+                numColors := 1 bitShift:oldImage depth.
+                0 to:numColors-1 do:[:pixel |
+                    newColorMap at:(pixel+1) put:(oldImage colorFromValue:pixel)
+                ].
+            ] ifFalse:[
+                "/ see if all used color fit the new colormap
+                usedColors := oldImage usedColors.
+                usedColors := usedColors asArray.
+                usedColors size <= (1 bitShift:depth) ifTrue:[
+                    "/ yea - just install them
+                    usedColors keysAndValuesDo:[:idx :clr |
+                        newColorMap at:idx put:clr
+                    ]
+                ] ifFalse:[
+                    "/ copy over those that are most often used.
+                    oldImage depth ~~ 8 ifTrue:[
+                        tmpBits := ByteArray uninitializedNew:(oldImage width*oldImage height).
+                        oldImage bits
+                            expandPixels:(oldImage depth)
+                            width:oldImage width
+                            height:oldImage height 
+                            into:tmpBits
+                            mapping:nil.
+                    ] ifFalse:[
+                        tmpBits := oldImage bits
+                    ].
+                    usageCounts := tmpBits usageCounts.
+                    tmpMap := Array new:usageCounts size.
+                    oldImage colorMap asArray keysAndValuesDo:[:i :clr |
+                        tmpMap at:i put:clr
+                    ].
+                    usageCounts sort:[:a :b | a > b] with:tmpMap.
+
+                    1 to:(1 bitShift:depth) do:[:idx |
+                        newColorMap at:idx put:(tmpMap at:idx)
+                    ].
+
+                    useNearest := Dialog 
+                                        confirmWithCancel:('Image requires %1 colors.\ColorMap has only space for %2\\Use nearest (or map to black) ?'
+                                                           bindWith:usedColors size
+                                                           with:(1 bitShift:depth)) withCRs
+                                        labels:#('cancel' 'black' 'nearest').
+                    useNearest isNil ifTrue:[
+                        ^ self   "/ cancel
+                    ].
+                ]
+            ]
+        ] ifFalse:[
+            "/ standard colormap
+        ].
+
+        imageEditView makeUndo.
+
+        newImage    := (Image implementorForDepth: depth) new.  
+        oldFileName := oldImage fileName.
+
+        Object errorSignal handle:
+        [:ex|
+            Object errorSignal handle:
+            [:ex|
+                imageEditView undo.
+                ^self warn: 'Conversion failed!!'
+            ]
+            do:
+            [
+                newImage := (Image implementorForDepth: depth) new.
+                newImage width:oldImage width height:oldImage height depth:depth.
+                newImage colorMap:newColorMap.
+                newImage photometric:#palette.
+                newImage bits:(ByteArray new:(newImage bytesPerRow * newImage height)).
+            
+                oldImage colorsFromX:0 y:0 toX:(oldImage width-1) y:(oldImage height-1) do:
+                [:x :y :clr |
+                    |newColor|
+
+                    (newColorMap includes:clr)
+                        ifTrue: [newColor := clr]
+                        ifFalse: [
+                            useNearest ifTrue:[
+                                newColor := clr nearestIn:newColorMap
+                            ] ifFalse:[
+                                newColor := oldImage colorMap first
+                            ]
+                        ].
+                    newImage colorAtX:x y:y put:newColor.
+                ].
+                image := newImage
+            ].
+        ] 
+        do:
+        [ 
+            image := newImage fromImage: oldImage
+        ].
+
+        (String fromString: aMode) reverse readStream nextWord reverse = 'mask'
+        ifTrue:
+        [
+            image mask isNil
+            ifTrue:
+            [
+                image mask: (Depth1Image fromImage: (image asThresholdMonochromeImage: 0.1)). 
+            ].
+        ]
+        ifFalse:
+        [ 
+            image mask: nil.
+        ]. 
+        realColorMap := OrderedCollection new.
+        image realColorMap do:
+        [:clr|
+            (realColorMap includes: clr) ifFalse: [realColorMap add: clr]
+        ].
+        newColors := realColorMap copyFrom: 1 to: (newColorMap size min: realColorMap size).
+        newColorMap do:
+        [:clr|
+            ((newColors size < newColorMap size) and: [(newColors includes: clr) not]) 
+            ifTrue:
+            [      
+                newColors add: clr
+            ]
+        ].                  
+        image colorMap: newColors.
+        colorMapMode := aMode.            
+
+        (imageEditView image: image) notNil ifTrue:
+        [
+            image fileName: oldFileName.
+            self listOfColors contents: image colorMap.
+            self findColorMapMode.
+            self updateLabelsAndHistory.
+        ]
+    ]
+
+    "Modified: / 28.7.1998 / 21:42:14 / cg"
+!
+
+compressColorMap
+    "calculates a new color map for the image, using only used colors"
+
+    |depth numColors newColorMap newImage 
+     oldImage  newColors realColorMap oldFileName
+     usedColors useNearest oldToNew oldBits newBits tmpBits| 
+
+    oldImage := self image.
+    depth := oldImage depth.
+
+    oldImage photometric ~~ #palette ifTrue:[
+        self information:'Only palette images have colormaps.'.
+        ^ self
+    ].
+
+    usedColors := oldImage usedColors.
+    usedColors size == (1 bitShift:depth) ifTrue:[
+        self information:'All colors are used - no compression.'.
+        ^ self
+    ].
+    usedColors size == oldImage colorMap size ifTrue:[
+        self information:'Colormap already compressed - no compression.'.
+        ^ self
+    ].
+
+    imageEditView makeUndo.
+
+    self information:('%1 colors used.' bindWith:usedColors size).
+
+    self withExecuteCursorDo:[
+        newColorMap := Array new:usedColors size.
+
+        "/ translation table
+        oldToNew := ByteArray new:(1 bitShift:depth).
+        newColorMap := usedColors asArray.
+        oldImage colorMap asArray keysAndValuesDo:[:oldIdx :clr |
+            |newPixel|
+
+            (usedColors includes:clr) ifTrue:[
+                newPixel := newColorMap indexOf:clr.
+                oldToNew at:oldIdx put:newPixel-1.
+            ]
+        ].
+
+        oldBits := oldImage bits.
+        newBits := ByteArray new:(oldBits size).
+        depth ~~ 8 ifTrue:[
+            "/ expand/compress can only handle 8bits
+            tmpBits := ByteArray uninitializedNew:(oldImage width*oldImage height).
+            oldBits
+                expandPixels:depth
+                width:oldImage width
+                height:oldImage height 
+                into:tmpBits
+                mapping:oldToNew.
+            tmpBits
+                compressPixels:depth 
+                width:oldImage width 
+                height:oldImage height 
+                into:newBits 
+                mapping:nil
+        ] ifFalse:[
+            oldBits
+                expandPixels:depth
+                width:oldImage width
+                height:oldImage height 
+                into:newBits
+                mapping:oldToNew.
+        ].
+
+        newImage := oldImage species new
+                        width:oldImage width
+                        height:oldImage height
+                        depth:depth
+                        fromArray:newBits.
+
+        newImage colorMap:newColorMap.  
+        newImage fileName:oldImage fileName.
+        newImage mask:(oldImage mask copy).
+
+        (imageEditView image:newImage) notNil ifTrue:
+        [
+            self listOfColors contents: newImage colorMap.
+            self findColorMapMode.
+            self updateLabelsAndHistory.
+        ]
+    ]
+
+    "Created: / 28.7.1998 / 20:03:11 / cg"
+    "Modified: / 28.7.1998 / 21:36:17 / cg"
+! !
+
 !ImageEditor methodsFor:'user actions - editing'!
 
 doBrowseClass