--- a/ImageEditor.st Thu Aug 31 21:39:41 2017 +0200
+++ b/ImageEditor.st Fri Sep 01 10:17:35 2017 +0200
@@ -2850,6 +2850,7 @@
"Do not manually edit this!! If it is corrupted,
the MenuEditor may not be able to read the specification."
+
"
MenuEditor new openOnClass:ImageEditor andSelector:#menuColors
(Menu new fromLiteralArrayEncoding:(ImageEditor menuColors)) startUp
@@ -2861,160 +2862,145 @@
#(Menu
(
(MenuItem
- "/ enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth
enabled: imageIsLoaded
label: 'Depth'
- translateLabel: true
submenu:
(Menu
(
(MenuItem
activeHelpKey: colorMap1
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '1-Plane'
itemValue: colorMapMode:
- translateLabel: true
argument: depth1
choice: colorMapMode
choiceValue: depth1
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
activeHelpKey: colorMap1M
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '1-Plane + Mask'
itemValue: colorMapMode:
- translateLabel: true
argument: masked1
choice: colorMapMode
choiceValue: masked1
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
label: '-'
)
(MenuItem
activeHelpKey: colorMap2
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '2-Plane'
itemValue: colorMapMode:
- translateLabel: true
argument: depth2
choice: colorMapMode
choiceValue: depth2
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
activeHelpKey: colorMap2M
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '2-Plane + Mask'
itemValue: colorMapMode:
- translateLabel: true
argument: masked2
choice: colorMapMode
choiceValue: masked2
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
label: '-'
)
(MenuItem
activeHelpKey: colorMap4
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '4-Plane'
itemValue: colorMapMode:
- translateLabel: true
argument: depth4
choice: colorMapMode
choiceValue: depth4
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
activeHelpKey: colorMap4M
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '4-Plane + Mask'
itemValue: colorMapMode:
- translateLabel: true
argument: masked4
choice: colorMapMode
choiceValue: masked4
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
label: '-'
)
(MenuItem
activeHelpKey: colorMap8
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '8-Plane'
itemValue: colorMapMode:
- translateLabel: true
argument: depth8
choice: colorMapMode
choiceValue: depth8
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
activeHelpKey: colorMap8M
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '8-Plane + Mask'
itemValue: colorMapMode:
- translateLabel: true
argument: masked8
choice: colorMapMode
choiceValue: masked8
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
label: '-'
)
(MenuItem
activeHelpKey: colorMap16
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '16-Plane'
itemValue: colorMapMode:
- translateLabel: true
argument: depth16
choice: colorMapMode
choiceValue: depth16
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
activeHelpKey: colorMap16M
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '16-Plane + Mask'
itemValue: colorMapMode:
- translateLabel: true
argument: masked16
choice: colorMapMode
choiceValue: masked16
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
label: '-'
)
(MenuItem
activeHelpKey: colorMap24
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '24-Plane'
itemValue: colorMapMode:
- translateLabel: true
argument: depth24
choice: colorMapMode
choiceValue: depth24
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
activeHelpKey: colorMap24M
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '24-Plane + Mask'
itemValue: colorMapMode:
- translateLabel: true
argument: masked24
choice: colorMapMode
choiceValue: masked24
- enabled: imageIsLoadedAndNotReadonlyHolder
)
(MenuItem
label: '-'
)
(MenuItem
activeHelpKey: colorMap32
+ enabled: imageIsLoadedAndNotReadonlyHolder
label: '32-Plane (rgba)'
itemValue: colorMapMode:
- translateLabel: true
argument: depth32
choice: colorMapMode
choiceValue: depth32
- enabled: imageIsLoadedAndNotReadonlyHolder
)
)
nil
@@ -3024,7 +3010,6 @@
(MenuItem
enabled: imageIsLoadedAndNotReadonlyHolder
label: 'ColorMap'
- translateLabel: true
submenu:
(Menu
(
@@ -3033,44 +3018,19 @@
enabled: hasColormapHolder
label: 'Compress Colormap'
itemValue: #'menu_compressColorMap'
- translateLabel: true
)
(MenuItem
enabled: hasColormapHolder
label: 'Sort Colormap'
itemValue: #'menu_sortColorMap'
- translateLabel: true
)
(MenuItem
label: 'Reduce Number of Colors by Rounding...'
itemValue: reduceNumberOfColors2
- translateLabel: true
)
(MenuItem
label: 'Reduce Number of Colors by Masking Bits...'
itemValue: reduceNumberOfColors
- translateLabel: true
- )
- (MenuItem
- label: '-'
- )
- (MenuItem
- enabled: imageIsLoadedHolder
- label: 'Brighten'
- itemValue: doBrightenImage
- translateLabel: true
- )
- (MenuItem
- enabled: imageIsLoadedHolder
- label: 'Darken'
- itemValue: doDarkenImage
- translateLabel: true
- )
- (MenuItem
- enabled: imageIsLoadedHolder
- label: 'Invert'
- itemValue: doNegativeImage
- translateLabel: true
)
)
nil
@@ -3080,24 +3040,17 @@
(MenuItem
enabled: imageIsLoadedAndNotReadonlyHolder
label: 'Process'
- translateLabel: true
submenu:
(Menu
(
(MenuItem
- label: 'Make GrayScale'
- itemValue: makeGrayScaleImage
- translateLabel: true
+ label: 'Negative'
+ itemValue: makeNegative
)
(MenuItem
- label: 'Make Monochrome...'
- itemValue: makeMonochromeImage
- translateLabel: true
- )
- (MenuItem
- label: 'Make Inverse'
- itemValue: makeInverse
- translateLabel: true
+ label: 'Invert Pixel Bits'
+ itemValue: makeInvertedBits
+ isVisible: hasColormap
)
(MenuItem
label: '-'
@@ -3106,28 +3059,34 @@
enabled: allowedToChangeImageDimensionAndDepth
label: 'Make dithered 8Bit Palette'
itemValue: makeDitheredPaletteImage
- translateLabel: true
isVisible: false
)
(MenuItem
label: 'Dither to Depth...'
itemValue: ditherToDepth
- translateLabel: true
)
(MenuItem
label: 'Threshold to Depth...'
itemValue: thresholdToDepth
- translateLabel: true
+ )
+ (MenuItem
+ label: '-'
+ )
+ (MenuItem
+ label: 'Make Monochrome...'
+ itemValue: makeMonochromeImage
+ )
+ (MenuItem
+ label: 'Make GrayScale (same Depth)'
+ itemValue: makeGrayScaleImage
)
(MenuItem
label: 'Make GrayScale with Depth (Dither)...'
itemValue: ditherGrayToDepth
- translateLabel: true
)
(MenuItem
label: 'Make GrayScale with Depth (Threshold)...'
itemValue: thresholdGrayToDepth
- translateLabel: true
)
(MenuItem
label: '-'
@@ -3135,25 +3094,18 @@
(MenuItem
label: 'Make Slightly Brighter'
itemValue: makeSlightlyBrighter
- translateLabel: true
+ )
+ (MenuItem
+ label: 'Make Brighter'
+ itemValue: makeBrighter
)
(MenuItem
label: 'Make Slightly Darker'
itemValue: makeSlightlyDarker
- translateLabel: true
- )
- (MenuItem
- label: '-'
- )
- (MenuItem
- label: 'Make Brighter'
- itemValue: makeBrighter
- translateLabel: true
)
(MenuItem
label: 'Make Darker'
itemValue: makeDarker
- translateLabel: true
)
(MenuItem
label: '-'
@@ -3161,12 +3113,20 @@
(MenuItem
label: 'Change HLS...'
itemValue: changeHLS
- translateLabel: true
)
(MenuItem
label: 'Colorize...'
itemValue: colorize
- translateLabel: true
+ )
+ (MenuItem
+ enabled: imageIsLoadedHolder
+ label: 'Brighten'
+ itemValue: doBrightenImage
+ )
+ (MenuItem
+ enabled: imageIsLoadedHolder
+ label: 'Darken'
+ itemValue: doDarkenImage
)
)
nil
@@ -3176,7 +3136,6 @@
(MenuItem
enabled: imageIsLoadedAndNotReadonlyHolder
label: 'Mask'
- translateLabel: true
submenu:
(Menu
(
@@ -3185,26 +3144,22 @@
enabled: hasMask
label: 'Copy Mask'
itemValue: #'menu_copyMask'
- translateLabel: true
)
(MenuItem
activeHelpKey: pasteMask
enabled: hasMask
label: 'Paste Mask'
itemValue: #'menu_pasteMask'
- translateLabel: true
)
(MenuItem
enabled: hasMask
label: 'Clear Masked Pixels'
itemValue: #'menu_clearMaskedPixels'
- translateLabel: true
)
(MenuItem
enabled: hasMask
label: 'Clear Colormap Entry for Masked Pixels'
itemValue: #'menu_clearColormapEntry0AndMaskedPixels'
- translateLabel: true
)
)
nil
@@ -3216,7 +3171,7 @@
nil
)
- "Modified: / 30-08-2017 / 00:35:31 / cg"
+ "Modified: / 31-08-2017 / 14:43:51 / cg"
!
menuEdit
@@ -4588,9 +4543,9 @@
!
hasColormapAndColorSelected
- ^ [ self hasColormapHolder value and:[self hasColorSelectedHolder value]]
-
- "Modified: / 04-07-2010 / 10:13:13 / cg"
+ ^ [ self hasColormap and:[self hasColorSelectedHolder value]]
+
+ "Modified: / 31-08-2017 / 14:08:20 / cg"
!
hasColormapAndSingleColorSelected
@@ -5129,7 +5084,7 @@
updateLabelsAndHistory
"updates labels and history, if something has changed"
- |image|
+ |image rsrcClass rsrcSelector imgFile|
image := self image.
@@ -5138,17 +5093,16 @@
self updateInfoLabel.
- imageEditView resourceClass notNil ifTrue:[
- imageEditView resourceSelector notNil ifTrue:[
- self addHistoryEntryForClass:imageEditView resourceClass selector:imageEditView resourceSelector.
- ]
- ].
-
- image fileName notNil ifTrue: [
- self addHistoryEntryForFile:image fileName.
- ].
-
- "Modified: / 04-07-2010 / 10:16:02 / cg"
+ ((rsrcClass := imageEditView resourceClass) notNil
+ and:[ (rsrcSelector := imageEditView resourceSelector) notNil ]) ifTrue:[
+ self addHistoryEntryForClass:rsrcClass selector:rsrcSelector.
+ ] ifFalse:[
+ (imgFile := image fileName) notNil ifTrue: [
+ self addHistoryEntryForFile:imgFile.
+ ].
+ ].
+
+ "Modified: / 01-09-2017 / 10:10:12 / cg"
!
updateListOfColorsAndColormapMode
@@ -5984,13 +5938,18 @@
!ImageEditor methodsFor:'user actions-colormap'!
addColorToColormap
- self addColorToColormap:(Color black)
+ "undoable: add black (a new color) to the map"
+
+ self addColorToColormap:(Color black) undoable:true
+
+ "Modified: / 31-08-2017 / 14:30:32 / cg"
!
addColorToColormap:newColor
- "when editing a palette image, the new color is added to the images colorMap
+ "undoable
+ when editing a palette image, the new color is added to the image's colorMap
(unless it is full).
- when editing a true-color image, it is added to my own list-of-colors,
+ When editing a true-color image, it is added to my own list-of-colors,
which only holds drawing colors, but is not the colormap's image"
|depth img cMap newColorMap newImage oldCListSize newMode listOfColors|
@@ -6063,6 +6022,84 @@
"Created: / 12-03-1999 / 00:20:28 / cg"
"Modified: / 16-02-2017 / 10:17:25 / cg"
+ "Modified (comment): / 31-08-2017 / 14:28:11 / cg"
+!
+
+addColorToColormap:newColor undoable:undoable
+ "when editing a palette image, the new color is added to the image's colorMap
+ (unless it is full).
+ When editing a true-color image, it is added to my own list-of-colors,
+ which only holds drawing colors, but is not the colormap's image"
+
+ |depth img cMap newColorMap newImage oldCListSize newMode listOfColors|
+
+ img := self image.
+ img isNil ifTrue:[
+ self warn:'No Image.'.
+ ^ self
+ ].
+
+ depth := img depth.
+ cMap := img colorMap.
+ (cMap isNil or:[cMap isMappedPalette or:[cMap isFixedPalette]]) ifTrue:[
+ drawingColormap isNil ifTrue:[
+ self information:(resources stringWithCRs:'Image has no colormap.\The shown colorMap is for drawing only.').
+ drawingColormap := OrderedCollection new.
+ ].
+ drawingColormap add:newColor.
+ self listOfColors contents:drawingColormap.
+ self selectedColors value:{drawingColormap size}.
+ "/ self warn:'Image has no colormap.\Change colorMap mode first.' withCRs.
+ ^ self
+ ].
+
+ (cMap size == (1 bitShift:depth)) ifTrue:[
+ depth >= 8 ifTrue:[
+ self warn:'No space for more colors in colormap.'.
+ ^ self
+ ].
+ (self confirm:(resources stringWithCRs:'No space for more colors in colormap.\Change depth ?'))
+ ifFalse:[
+ ^ self
+ ].
+
+ undoable ifTrue:[ imageEditView makeUndo ].
+ img mask notNil ifTrue:[
+ newMode := 'masked' , (depth*2) printString.
+ ] ifFalse:[
+ newMode := 'depth' , (depth*2) printString.
+ ].
+ self colorMapMode:newMode.
+ ] ifFalse:[
+ undoable ifTrue:[ imageEditView makeUndo ].
+ ].
+
+ cMap := cMap asArray.
+ listOfColors := self listOfColors.
+ oldCListSize := listOfColors size.
+
+ newColorMap := cMap copyWith:newColor.
+
+ newImage := img species new
+ width:(img width) height:(img height) depth:depth
+ fromArray:img bits.
+
+ newImage colorMap:newColorMap.
+ newImage fileName:img fileName.
+ newImage mask:(img mask copy).
+
+ (imageEditView image:newImage) notNil ifTrue:[
+ listOfColors contents: newImage colorMap.
+ self findColorMapMode.
+ "/ mhmh - somehow, we get two colors added ... (sigh findColorMapMode adds another one ...)
+ listOfColors size > (oldCListSize + 1) ifTrue:[
+ listOfColors removeLast
+ ].
+ self selectedColors value:{listOfColors size}.
+ self updateLabelsAndHistory.
+ ]
+
+ "Created: / 31-08-2017 / 14:30:01 / cg"
!
changeHLS
@@ -6334,18 +6371,21 @@
|index colorMap|
- self compressColorMap.
+ self nonUndoableCompressColorMap.
+
colorMap := self image colorMap.
(colorMap includes:(Color black)) ifFalse:[
- self addColorToColormap:(Color black).
+ self addColorToColormap:(Color black) undoable:false.
colorMap := self image colorMap.
].
index := colorMap indexOf:(Color black).
index == 1 ifFalse:[
- self sortColorMap.
+ self nonUndoableSortColorMapWith:self sortBlockForColorsByRGB.
colorMap := self image colorMap.
].
- self clearMaskedPixels
+ self nonUndoableClearMaskedPixels
+
+ "Modified: / 31-08-2017 / 14:30:28 / cg"
!
clearMaskedPixels
@@ -6471,7 +6511,9 @@
] ifFalse:[
colorMapMode value:prevMode.
self findColorMapMode.
- self warn:('Too many used colors in image (', oldImage usedColors size printString , ').').
+ self warn:(resources
+ stringWithCRs:'Too many used colors (%1) in image.\\You should choose one of:\\- convert the image to grey\- reduce the number of colors\- dither to depth\- choose another depth'
+ with: oldImage realUsedColors size ).
^ self
"/ (self confirm:('Too many used colors in image (', oldImage usedColors size printString , ').\\Dither ?' withCRs))
"/ ifFalse:[.
@@ -6636,7 +6678,7 @@
]
]
- "Modified: / 24-08-2017 / 18:16:02 / cg"
+ "Modified: / 31-08-2017 / 14:41:06 / cg"
!
colorize
@@ -6808,55 +6850,21 @@
!
makeBrighter
- | anyChange|
-
- self withExecuteCursorDo:[
- anyChange := imageEditView makeBrighter.
- anyChange ifFalse:[
- Dialog warn:'Image unchanged'.
- ] ifTrue:[
- self updateImage.
- ]
- ].
+ self updateImageAfterDoing:#makeBrighter.
+
+ "Modified: / 31-08-2017 / 12:16:30 / cg"
!
makeDarker
- | anyChange|
-
- self withExecuteCursorDo:[
- anyChange := imageEditView makeDarker.
- anyChange ifFalse:[
- Dialog warn:'Image unchanged'.
- ] ifTrue:[
- self updateImage.
- ]
- ].
+ self updateImageAfterDoing:#makeDarker.
+
+ "Modified: / 31-08-2017 / 12:16:24 / cg"
!
makeGrayScaleImage
- |anyChange|
-
- self withExecuteCursorDo:[
- anyChange := imageEditView makeGrayScaleImage.
- anyChange ifFalse:[
- Dialog warn:'Image unchanged'.
- ] ifTrue:[
- self updateImage.
- ]
- ].
-!
-
-makeInverse
- | anyChange|
-
- self withExecuteCursorDo:[
- anyChange := imageEditView makeInverse.
- anyChange ifFalse:[
- Dialog warn:'Image unchanged'.
- ] ifTrue:[
- self updateImage.
- ]
- ].
+ self updateImageAfterDoing:#makeGrayScaleImage.
+
+ "Modified: / 31-08-2017 / 12:16:15 / cg"
!
makeSelectedColorBrighter
@@ -6872,45 +6880,33 @@
!
makeSelectedColorShifted
- |cMap colors|
+ "shift the selected color (in the colormap)
+ using the hls/rgb shifting slider dialog"
+
+ |cMap colors cmapOffset|
cMap := self image colorMap.
- self hasMask ifTrue:[
- colors := self selectedColors value collect:[:idx | cMap at:idx-1].
- ] ifFalse:[
- colors := self selectedColors value collect:[:idx | cMap at:idx].
- ].
+
+ "/ if there is a mask, it is at position 1 in the table
+ cmapOffset := self hasMask ifTrue:[1] ifFalse:[0].
+ colors := self selectedColors value collect:[:idx | cMap at:idx-cmapOffset].
self changeHLSOfColors:colors.
+
+ "Modified: / 31-08-2017 / 14:15:03 / cg"
!
makeSlightlyBrighter
- | anyChange|
-
- self withExecuteCursorDo:[
- anyChange := imageEditView makeSlightlyBrighter.
- anyChange ifFalse:[
- Dialog warn:'Image unchanged'.
- ] ifTrue:[
- self updateImage.
- ]
- ].
+ self updateImageAfterDoing:#makeSlightlyBrighter.
"Created: / 24-11-2010 / 11:06:11 / cg"
+ "Modified: / 31-08-2017 / 12:16:01 / cg"
!
makeSlightlyDarker
- | anyChange|
-
- self withExecuteCursorDo:[
- anyChange := imageEditView makeSlightlyDarker.
- anyChange ifFalse:[
- Dialog warn:'Image unchanged'.
- ] ifTrue:[
- self updateImage.
- ]
- ].
+ self updateImageAfterDoing:#makeSlightlyDarker.
"Created: / 24-11-2010 / 11:06:23 / cg"
+ "Modified: / 31-08-2017 / 12:15:55 / cg"
!
menu_clearColormapEntry0AndMaskedPixels
@@ -6919,18 +6915,21 @@
imageEditView makeUndo.
self withExecuteCursorDo:[
- self clearColormapEntry0AndMaskedPixels
+ self nonUndoableClearColormapEntry0AndMaskedPixels
]
+
+ "Modified: / 31-08-2017 / 14:31:21 / cg"
!
menu_clearMaskedPixels
"clear all masked pixels (to pixelValue 0)"
imageEditView makeUndo.
-
self withExecuteCursorDo:[
- self clearMaskedPixels
+ self nonUndoableClearMaskedPixels
]
+
+ "Modified: / 31-08-2017 / 14:23:20 / cg"
!
menu_compressColorMap
@@ -6953,10 +6952,11 @@
].
imageEditView makeUndo.
-
self withExecuteCursorDo:[
- self compressColorMap
+ self nonUndoableCompressColorMap
]
+
+ "Modified: / 31-08-2017 / 14:22:19 / cg"
!
menu_copyMask
@@ -7001,13 +7001,12 @@
].
imageEditView makeUndo.
-
self withExecuteCursorDo:[
- self sortColorMapWith:sortBlock
+ self nonUndoableSortColorMapWith:sortBlock
]
- "Modified: / 15.9.1998 / 17:53:32 / cg"
- "Created: / 30.9.1998 / 23:51:23 / cg"
+ "Created: / 30-09-1998 / 23:51:23 / cg"
+ "Modified: / 31-08-2017 / 14:20:14 / cg"
!
pasteColorIntoColormap
@@ -7039,7 +7038,9 @@
!
pickAndAddColorToColormap
- self addColorToColormap:(Color fromUser)
+ self addColorToColormap:(Color fromUser) undoable:true
+
+ "Modified: / 31-08-2017 / 14:30:22 / cg"
!
pickAndPasteColor
@@ -7301,11 +7302,161 @@
sortColorMap
"calculates a new color map for the image, sorting colors"
- self sortColorMapWith:self sortBlockForColorsByRGB
+ self nonUndoableSortColorMapWith:self sortBlockForColorsByRGB
+
+ "Modified: / 31-08-2017 / 14:20:07 / cg"
!
sortColorMapWith:sortBlock
- "calculates a new color map for the image, sorting colors"
+ "warning: not undoable
+ calculates a new color map for the image, sorting colors"
+
+ |depth newColorMap newImage oldImage usedColors oldToNew oldBits newBits tmpBits
+ expectedSize w h|
+
+ oldImage := self image.
+ depth := oldImage depth.
+ w := oldImage width.
+ h := oldImage height.
+
+ usedColors := oldImage realColorMap.
+
+ "/ translation table
+ oldToNew := ByteArray new:(1 bitShift:depth).
+ newColorMap := usedColors asArray.
+ newColorMap sort:sortBlock.
+
+ oldImage colorMap asArray keysAndValuesDo:[:oldIdx :clr |
+ |newPixel|
+
+ (usedColors includes:clr) ifTrue:[
+ newPixel := newColorMap indexOf:clr.
+ oldToNew at:oldIdx put:newPixel-1.
+ ]
+ ].
+
+ oldBits := oldImage bits.
+ "/ sanity check...
+ expectedSize := ((w * h * depth + 7) // 8).
+ (oldBits size < expectedSize) ifTrue:[
+ self halt:'incorrect pixeldata size'.
+ oldBits := (ByteArray new:expectedSize) replaceFrom:1 with:oldBits; yourself.
+ ].
+ newBits := ByteArray new:(oldBits size).
+ depth ~~ 8 ifTrue:[
+
+ "/ expand/compress can only handle 8bits
+ tmpBits := ByteArray uninitializedNew:(w*h).
+ oldBits
+ expandPixels:depth
+ width:w height:h
+ into:tmpBits
+ mapping:oldToNew.
+ tmpBits
+ compressPixels:depth
+ width:w height:h
+ into:newBits
+ mapping:nil
+ ] ifFalse:[
+ oldBits
+ expandPixels:depth
+ width:w height:h
+ into:newBits
+ mapping:oldToNew.
+ ].
+
+ newImage := oldImage species new
+ width:w height:h depth:depth
+ fromArray:newBits.
+
+ newImage colorMap:newColorMap.
+ newImage fileName:oldImage fileName.
+ newImage mask:(oldImage mask copy).
+
+ (imageEditView image:newImage) notNil ifTrue:[
+ self fetchImageData.
+ ]
+
+ "Modified: / 15-09-1998 / 17:53:32 / cg"
+ "Created: / 30-09-1998 / 23:51:23 / cg"
+ "Modified (comment): / 31-08-2017 / 14:19:21 / cg"
+!
+
+updateImageAfterDoing:aBlockOrSelector
+ self withExecuteCursorDo:[
+ aBlockOrSelector value:imageEditView.
+ self updateImage.
+ ].
+
+ "Created: / 31-08-2017 / 12:14:39 / cg"
+! !
+
+!ImageEditor methodsFor:'user actions-colormap-basic'!
+
+nonUndoableClearColormapEntry0AndMaskedPixels
+ "ensure that there is a colorMap entry with 0/0/0 at position
+ 0 and then clear all masked pixels (to pixelValue 0).
+ This is required for windows icons to be really transparent"
+
+ |index colorMap|
+
+ self nonUndoableCompressColorMap.
+
+ colorMap := self image colorMap.
+ (colorMap includes:(Color black)) ifFalse:[
+ self addColorToColormap:(Color black) undoable:false.
+ colorMap := self image colorMap.
+ ].
+ index := colorMap indexOf:(Color black).
+ index == 1 ifFalse:[
+ self nonUndoableSortColorMapWith:self sortBlockForColorsByRGB.
+ colorMap := self image colorMap.
+ ].
+ self nonUndoableClearMaskedPixels
+
+ "Created: / 31-08-2017 / 14:31:09 / cg"
+!
+
+nonUndoableClearMaskedPixels
+ "clear all masked pixels (to pixelValue 0)"
+
+ |newImage|
+
+ newImage := self image clearMaskedPixels.
+ 0 to:newImage height - 1 do:[:y |
+ 0 to:newImage width - 1 do:[:x |
+ (newImage maskAtX:x y:y) == 0 ifTrue:[
+ newImage pixelAtX:x y:y put:0
+ ]
+ ]
+ ].
+
+ (imageEditView image:newImage) notNil ifTrue:[
+ self fetchImageData.
+ ]
+
+ "Created: / 31-08-2017 / 14:22:58 / cg"
+!
+
+nonUndoableCompressColorMap
+ "not undoable
+ calculates a new color map for the image, using only used colors"
+
+ |newImage|
+
+ newImage := self image.
+ newImage compressColorMap.
+
+ (imageEditView image:newImage) notNil ifTrue:[
+ self fetchImageData.
+ ]
+
+ "Created: / 31-08-2017 / 14:21:51 / cg"
+!
+
+nonUndoableSortColorMapWith:sortBlock
+ "not undoable
+ calculates a new color map for the image, sorting colors"
|depth newColorMap newImage oldImage usedColors oldToNew oldBits newBits tmpBits
expectedSize w h|
@@ -7373,8 +7524,7 @@
self fetchImageData.
]
- "Modified: / 15.9.1998 / 17:53:32 / cg"
- "Created: / 30.9.1998 / 23:51:23 / cg"
+ "Created: / 31-08-2017 / 14:19:42 / cg"
! !
!ImageEditor methodsFor:'user actions-editing'!
@@ -7424,6 +7574,623 @@
"Created: / 20-02-2017 / 18:06:03 / cg"
!
+ditherGrayToDepth
+ self askForDepthThenDo:[:depth |
+ self ditherGrayToDepth:depth
+ ].
+
+ "Created: / 24-08-2017 / 17:49:42 / cg"
+!
+
+ditherGrayToDepth:depth
+ self withExecuteCursorDo:[
+ |newImage|
+
+ depth == 1 ifTrue:[
+ newImage := self image asErrorDitheredMonochromeImage
+ ] ifFalse:[
+ newImage := self image asGrayImageDepth:depth dither:#floydSteinberg.
+ ].
+ imageEditView newImageWithUndo:newImage.
+ ].
+
+ "Created: / 24-08-2017 / 17:51:07 / cg"
+ "Modified: / 30-08-2017 / 01:18:43 / cg"
+!
+
+ditherToDepth
+ self askForDepthThenDo:[:depth |
+ self convertToDepth:depth dither:true
+ ].
+
+ "Created: / 07-07-2006 / 13:22:10 / cg"
+ "Modified: / 30-08-2017 / 00:34:42 / cg"
+!
+
+do3DProjection
+ "make a naive 3D projection;
+ can be used to create those typical marketing images as seen in web pages"
+
+ |box dx1 dx2 image|
+
+ image := imageEditView image.
+
+ box := EnterBox new.
+ box title:(resources string:'dX1 (0 < dx < 0.5):').
+ box okText:(resources string:'OK').
+ box abortText:(resources string:'Cancel').
+ box initialText:'0.1'.
+ box showAtPointer.
+
+ (box accepted
+ and: [(dx1 := Number readFrom:(box contents) onError:nil) notNil])
+ ifTrue:[
+ box title:(resources string:'dX2 (0 < dx < 0.5):').
+ box initialText:(dx1 printString).
+ box showAtPointer.
+ (box accepted
+ and: [(dx2 := Number readFrom:(box contents) onError:nil) notNil])
+ ifTrue:[
+ imageEditView threeDProjection:dx1 and:dx2.
+ ]
+ ].
+
+ self updateInfoLabel
+
+ "Modified (comment): / 31-08-2017 / 13:58:03 / cg"
+!
+
+doBrightenImage
+ imageEditView brightenImage.
+ self listOfColors removeAll.
+ self findColorMapMode.
+ "/ imageEditView removelastUndo
+!
+
+doBrowseClass
+ "opens a System Browser on the resourceClass and the resourceSelector"
+
+ |cls|
+
+ cls := imageEditView resourceClass.
+ cls isNil ifTrue:[^ self warn:'No Class specified'].
+
+ cls browserClass
+ openInClass:cls class
+ selector:(imageEditView resourceSelector)
+
+ "Modified: / 31.7.1998 / 02:01:15 / cg"
+!
+
+doCopyImageToClipboard
+ imageEditView copyImageToClipboard.
+!
+
+doCropManual
+ "let user specify borders and cut them off"
+
+ |bindings left top right bottom img firstChange cropAction acceptChannel|
+
+ acceptChannel := TriggerValue new.
+
+ firstChange := true.
+
+ cropAction :=
+ [:lV :rV :tV :bV | |l r t b|
+ acceptChannel value:true.
+
+ l := lV value.
+ r := rV value.
+ t := tV value.
+ b := bV value.
+ (l + r + t + b) == 0 ifTrue:[
+ UserPreferences current beepInEditor ifTrue:[
+ self window beep
+ ]
+ ] ifFalse:[
+ img := imageEditView image.
+ firstChange ifTrue:[
+ imageEditView makeUndo.
+ firstChange := false.
+ ].
+ imageEditView
+ makeSubImageX:l y:t
+ width:(img width - l - r)
+ height:(img height - t - b).
+
+ self updateImagePreView.
+ self updateInfoLabel
+ ].
+ ].
+
+ bindings := IdentityDictionary new.
+ bindings at:#cropLeftAmount put:(left := 1 asValue).
+ bindings at:#cropRightAmount put:(right := 1 asValue).
+ bindings at:#cropTopAmount put:(top := 1 asValue).
+ bindings at:#cropBottomAmount put:(bottom := 1 asValue).
+ bindings at:#acceptChannel put:acceptChannel.
+
+ bindings at:#cropLeftNow put:[ cropAction value:left value:0 value:0 value:0 ].
+ bindings at:#cropRightNow put:[ cropAction value:0 value:right value:0 value:0 ].
+ bindings at:#cropTopNow put:[ cropAction value:0 value:0 value:top value:0 ].
+ bindings at:#cropBottomNow put:[ cropAction value:0 value:0 value:0 value:bottom ].
+
+ bindings at:#applyCropAction put:[ cropAction value:left value:right value:top value:bottom ].
+ bindings at:#cropBoxIsDialog put:true.
+
+ (self openDialogInterface:#cropSpec withBindings:bindings)
+ ifFalse:[
+ firstChange ~~ true ifTrue:[
+ imageEditView undo.
+ self updateImagePreView.
+ ]
+ ].
+
+ "Created: / 07-09-1998 / 18:16:07 / cg"
+ "Modified: / 19-02-2017 / 15:43:50 / cg"
+!
+
+doDarkenImage
+ imageEditView darkenImage.
+ self listOfColors removeAll.
+ self findColorMapMode.
+ "/ imageEditView removelastUndo
+!
+
+doEditMask
+ |mask|
+
+ (mask := self image mask) notNil ifTrue:[
+ mask edit
+ ].
+
+ "Modified: / 18-02-2017 / 00:38:51 / cg"
+!
+
+doFlipHorizontal
+ "flips horizontally current image"
+
+ imageEditView flipHorizontal
+!
+
+doFlipVertical
+ "flips vertically current image"
+
+ imageEditView flipVertical
+!
+
+doInsertTextFromUser
+ |text tempForm tempImage maskImage font w h paintColor|
+
+ text := Dialog request:'Text to be inserted (placed as bitmap into clipboard for paste):'.
+ text isEmptyOrNil ifTrue:[^ self ].
+
+ font := Font family:'arial' size:20.
+ font := font onDevice:Screen current.
+ w := font widthOf:text.
+ h := font heightOf:text.
+
+ tempForm := Form extent:(w@h) depth:1 onDevice:(Screen current).
+ tempForm clear.
+ tempForm font:font.
+ tempForm paint:(Color colorId:1).
+ tempForm displayString:text at:(0@font ascent).
+
+ tempImage := tempForm asImage.
+ maskImage := tempForm asImage.
+
+ paintColor := imageEditView selectedColor.
+ paintColor colorId == 0 ifTrue:[
+ paintColor := Color black
+ ].
+ tempImage
+ photometric:#palette;
+ colorMap:(Array
+ with:Color white
+ with:paintColor);
+ mask:maskImage.
+
+ ImageEditView copyImageToClipboard:tempImage.
+ self editMode value:#paste.
+
+ "Modified: / 11-11-2007 / 12:32:55 / cg"
+!
+
+doInspectImage
+ "opens a System Browser on the resourceClass and the resourceSelector"
+
+ self image inspect
+!
+
+doInvertedBitsImage
+ "inverts the pixels"
+
+ self withExecuteCursorDo:[
+ imageEditView makeInvertedBits.
+ self updateImage.
+ ].
+"/
+"/ imageEditView negativeImage.
+"/ self listOfColors removeAll.
+"/ self findColorMapMode.
+"/ "/ imageEditView removelastUndo
+"/
+"/
+
+ "Created: / 31-08-2017 / 12:49:13 / cg"
+!
+
+doMagnifyDown
+ "magnifies the current image one step down"
+
+ |magHolder mag|
+
+ magHolder := self magnificationHolder.
+ (mag := magHolder value) > 1 ifTrue: [
+ magHolder value: mag - 1
+ ]
+
+ "Modified: / 26.7.1998 / 20:24:08 / cg"
+!
+
+doMagnifyImage
+ "magnifies the current image to a new size"
+
+ |box newSize image antiAliased|
+
+ antiAliased := false asValue.
+ image := imageEditView image.
+
+ box := EnterBox new.
+ box title:(resources string:'Images new size:').
+ box okText:(resources string:'OK').
+ box abortText:(resources string:'Cancel').
+ box initialText:image extent printString.
+ box verticalPanel add:(CheckBox label:(resources string:'Antialias/Smooth') model:antiAliased).
+ box showAtPointer.
+
+ (box accepted
+ and: [(newSize := self pointFromString:(box contents)) notNil])
+ ifTrue:[
+ newSize isPoint ifFalse:[
+ self warn:'Please enter the new size as ''x @ y''.'.
+ ^ self.
+ ].
+ antiAliased value ifTrue:[
+ ((newSize x < image width) or:[(newSize y < image height)]) ifTrue:[
+ imageEditView magnifySmoothingTo:newSize.
+ ] ifFalse:[
+ imageEditView magnifyAntiAliasedImageTo:newSize.
+ ].
+ ] ifFalse:[
+ imageEditView magnifyImageTo:newSize.
+ ].
+ ].
+
+ self updateInfoLabel
+
+ "Modified: / 30-08-2017 / 15:46:02 / cg"
+!
+
+doMagnifyImageBy
+ "magnifies the current image (by a scale)"
+
+ |oldSize newSize scaleString scale image antiAliased smoothing|
+
+ image := imageEditView image.
+ oldSize := image extent.
+
+ antiAliased := false asValue.
+ smoothing := false asValue.
+
+ Dialog modifyingBoxWith:[:box |
+ box verticalPanel add:(CheckBox label:(resources string:'Antialias/Smooth') model:antiAliased).
+ "/ box verticalPanel add:(CheckBox label:(resources string:'Smoothing') model:smoothing).
+ ] do:[
+ scaleString := Dialog
+ request:(resources string:'Scale factor (<1 to shrink; >1 to magnify):')
+ initialAnswer:'1'
+ list:#('0.1' '0.25' '0.3' '0.5' '1.5' '2' '3' '4').
+ ].
+ scaleString isNil ifTrue:[^ self].
+
+ scale := Object readFromString:scaleString onError:nil.
+
+ scale notNil ifTrue:[
+ scale isNumber ifFalse:[
+ self warn:'please enter a scale factor (<1 to shrink; >1 to magnify).'.
+ ^ self.
+ ].
+ newSize := oldSize * scale.
+ antiAliased value ifTrue:[
+ scale < 1 ifTrue:[
+ imageEditView magnifySmoothingBy:scale.
+ ] ifFalse:[
+ imageEditView magnifyAntiAliasedImageTo:newSize.
+ ].
+ ] ifFalse:[
+ imageEditView magnifyImageTo:newSize.
+ ].
+ ].
+
+ self updateInfoLabel
+
+ "Modified: / 30-08-2017 / 15:34:56 / cg"
+!
+
+doMagnifyUp
+ "magnifies the current image one step up"
+
+ |magHolder mag|
+
+ magHolder := self magnificationHolder.
+ (mag := magHolder value) < 63 ifTrue: [
+ magHolder value: mag + 1
+ ]
+
+ "Modified: / 26.7.1998 / 20:23:52 / cg"
+!
+
+doNegativeImage
+ "negates current image by negating the color map"
+
+ self withExecuteCursorDo:[
+ imageEditView negativeImage.
+ self updateImage.
+ ].
+"/
+"/ imageEditView negativeImage.
+"/ self listOfColors removeAll.
+"/ self findColorMapMode.
+"/ "/ imageEditView removelastUndo
+"/
+"/
+
+ "Modified: / 31-08-2017 / 12:44:25 / cg"
+!
+
+doResizeImage
+ "resizes the current image"
+
+ |box newSize image|
+
+ image := imageEditView image.
+
+ box := EnterBox new.
+ box title:(resources string:'Images new size:').
+ box okText:(resources string:'OK').
+ box abortText:(resources string:'Cancel').
+ box initialText:image extent printString.
+ box showAtPointer.
+ (box accepted
+ and: [(newSize := self pointFromString:(box contents)) notNil])
+ ifTrue:[
+ imageEditView resizeImageTo:newSize.
+ ].
+!
+
+doRotateImage
+ "rotates current image"
+
+ |rotationString box rotation|
+
+ rotationString := Dialog
+ request:(resources string:'Rotate by (degrees, clockwise):')
+ list:#( '-90' '90' '180' '45' '-45' '135' '-135' )
+ initialAnswer:90.
+ rotationString isEmptyOrNil ifTrue:[^ self]. "/ canceled
+ rotation := Number readFrom:rotationString onError:[nil].
+ rotation isNil ifTrue:[^ self].
+
+"/ box := EnterBox new.
+"/ box title:(resources string:'Rotate by (degrees, clockwise):').
+"/ box okText:(resources string:'OK').
+"/ box abortText:(resources string:'Cancel').
+"/ box initialText: '0'.
+"/ box showAtPointer.
+"/ (box accepted and: [(rotation := Number readFromString: box contents onError:nil) notNil])
+"/ ifFalse:[ ^ self ].
+
+ imageEditView rotateImageBy:rotation.
+ self updateInfoLabel.
+
+ "Modified: / 18-03-2012 / 14:41:14 / cg"
+ "Modified (comment): / 24-08-2017 / 15:02:57 / cg"
+!
+
+doShiftManual
+ "let user specify amount and shift"
+
+ |bindings amount img firstChange shiftAction acceptChannel wrapHolder|
+
+ acceptChannel := TriggerValue new.
+ wrapHolder := (lastShiftUsedWrap ? true) asValue.
+
+ firstChange := true.
+
+ shiftAction :=
+ [:shiftH :shiftV |
+ acceptChannel value:true.
+
+ img := imageEditView image.
+ firstChange ifTrue:[
+ imageEditView makeUndo.
+ firstChange := false.
+ ].
+ imageEditView shiftImageHorizontal:(shiftH value) vertical:(shiftV value) wrap:(wrapHolder value).
+ self updateInfoLabel
+ ].
+
+ bindings := IdentityDictionary new.
+ bindings at:#shiftAmount put:(amount := 1 asValue).
+ bindings at:#wrap put:wrapHolder.
+ bindings at:#acceptChannel put:acceptChannel.
+
+ bindings at:#shiftLeftNow put:[ shiftAction value:(-1*amount value) value:0 ].
+ bindings at:#shiftRightNow put:[ shiftAction value:amount value value:0 ].
+ bindings at:#shiftUpNow put:[ shiftAction value:0 value:(-1*amount value) ].
+ bindings at:#shiftDownNow put:[ shiftAction value:0 value:amount value ].
+
+ (self openDialogInterface:#shiftDialogSpec withBindings:bindings)
+ ifFalse:[
+ firstChange ~~ true ifTrue:[
+ imageEditView undo
+ ]
+ ].
+ lastShiftUsedWrap := wrapHolder value.
+
+ "Created: / 7.9.1998 / 18:16:07 / cg"
+ "Modified: / 7.9.1998 / 18:20:42 / cg"
+!
+
+doUnCropManual
+ "let user specify borders and add them"
+
+ |bindings leftAmount topAmount rightAmount bottomAmount img|
+
+ bindings := IdentityDictionary new.
+ bindings at:#cropLeftAmount put:(leftAmount := 1 asValue).
+ bindings at:#cropRightAmount put:(rightAmount := 1 asValue).
+ bindings at:#cropTopAmount put:(topAmount := 1 asValue).
+ bindings at:#cropBottomAmount put:(bottomAmount := 1 asValue).
+ bindings at:#cropBoxIsDialog put:true.
+
+ (self openDialogInterface:#uncropSpec withBindings:bindings)
+ ifTrue:[
+ leftAmount := leftAmount value.
+ rightAmount := rightAmount value.
+ topAmount := topAmount value.
+ bottomAmount := bottomAmount value.
+ img := imageEditView image.
+
+ imageEditView
+ makeBorderedImageX:leftAmount y:topAmount
+ width:(img width + leftAmount + rightAmount)
+ height:(img height + topAmount + bottomAmount).
+ self updateInfoLabel
+ ].
+
+ "Created: / 07-09-1998 / 18:16:07 / cg"
+ "Modified: / 19-02-2017 / 15:31:00 / cg"
+!
+
+doUndo
+ "reverses last edit action"
+
+ imageEditView undo.
+ self updateImagePreView
+!
+
+makeInverse
+ "inverts the pixels - for palettes, this leads to funny results"
+
+ self updateImageAfterDoing:#makeInverse.
+
+ "Modified: / 31-08-2017 / 12:16:07 / cg"
+ "Modified (comment): / 31-08-2017 / 13:51:28 / cg"
+!
+
+makeMonochromeImage
+ "let user choose a threshold, then convert to monochrome"
+
+ |image userInput thresholdBrighness|
+
+ image := imageEditView image.
+
+ Dialog modifyingBoxWith:[:box |
+ |preview slider update thresholdValue|
+
+ thresholdValue := 0.5 asValue.
+
+ box enterField
+ converter:(PrintConverter new initForNumber);
+ model:thresholdValue.
+
+ box verticalPanel extent:1.0 @ 300.
+
+ box verticalPanel add:(slider := HorizontalSlider new start:0 stop:1 step:0.05).
+ slider model:thresholdValue.
+ slider width:1.0; leftInset:4; rightInset:4.
+
+ box verticalPanel add:(preview := ImageView new).
+ preview extent:300 @300.
+ preview level:-1.
+ box verticalPanel horizontalLayout:#fitSpace.
+
+ update :=
+ [
+ |s t|
+
+ t := thresholdValue value clampBetween:0 and:1.
+ preview image:((image asThresholdMonochromeImage:t)
+ magnifiedPreservingRatioTo:preview extent).
+ ].
+ update value.
+ box enterField acceptOnLostFocus:true.
+ box enterField acceptOnLeave:true.
+ thresholdValue onChangeEvaluate:update.
+
+ ] do:[
+ userInput := Dialog request:'Threshold (0=black; 1=white) ?' initialAnswer:0.5.
+ ].
+ userInput isEmptyOrNil ifTrue:[^ self].
+ thresholdBrighness := Number readFrom:userInput onError:nil.
+ thresholdBrighness isNil ifTrue:[^ self].
+
+ thresholdBrighness := thresholdBrighness clampBetween:0 and:1.
+ imageEditView newImageWithUndo:(image asThresholdMonochromeImage:thresholdBrighness)
+
+ "Created: / 24-08-2017 / 15:26:44 / cg"
+ "Modified: / 24-08-2017 / 17:54:21 / cg"
+!
+
+makeNegative
+ "negates current image by negating the color map"
+
+ self withExecuteCursorDo:[
+ imageEditView negativeImage.
+ self updateImage.
+ ].
+"/
+"/ imageEditView negativeImage.
+"/ self listOfColors removeAll.
+"/ self findColorMapMode.
+"/ "/ imageEditView removelastUndo
+"/
+"/
+
+ "Created: / 31-08-2017 / 13:49:47 / cg"
+!
+
+thresholdGrayToDepth
+ self askForDepthThenDo:[:depth |
+ self thresholdGrayToDepth:depth
+ ].
+
+ "Created: / 24-08-2017 / 17:49:23 / cg"
+!
+
+thresholdGrayToDepth:depth
+ self withExecuteCursorDo:[
+ |newImage|
+
+ newImage := self image asThresholdGrayImageDepth:depth.
+ imageEditView newImageWithUndo:newImage.
+ ].
+
+ "Created: / 24-08-2017 / 17:49:30 / cg"
+!
+
+thresholdToDepth
+ self askForDepthThenDo:[:depth |
+ self convertToDepth:depth dither:false
+ ].
+
+ "Created: / 30-08-2017 / 00:31:33 / cg"
+! !
+
+!ImageEditor methodsFor:'user actions-editing-colors'!
+
convertToDepth:depth dither:doDither
|answer labels values
ditherColors fixColors
@@ -7588,564 +8355,13 @@
"Modified: / 30-08-2017 / 02:13:03 / cg"
!
-ditherGrayToDepth
- self askForDepthThenDo:[:depth |
- self ditherGrayToDepth:depth
- ].
-
- "Created: / 24-08-2017 / 17:49:42 / cg"
-!
-
-ditherGrayToDepth:depth
- self withExecuteCursorDo:[
- |newImage|
-
- depth == 1 ifTrue:[
- newImage := self image asErrorDitheredMonochromeImage
- ] ifFalse:[
- newImage := self image asGrayImageDepth:depth dither:#floydSteinberg.
- ].
- imageEditView newImageWithUndo:newImage.
- ].
-
- "Created: / 24-08-2017 / 17:51:07 / cg"
- "Modified: / 30-08-2017 / 01:18:43 / cg"
-!
-
-ditherToDepth
- self askForDepthThenDo:[:depth |
- self convertToDepth:depth dither:true
- ].
-
- "Created: / 07-07-2006 / 13:22:10 / cg"
- "Modified: / 30-08-2017 / 00:34:42 / cg"
-!
-
-do3DProjection
- |box dx1 dx2 image|
-
- image := imageEditView image.
-
- box := EnterBox new.
- box title:(resources string:'dX1 (0 < dx < 0.5):').
- box okText:(resources string:'OK').
- box abortText:(resources string:'Cancel').
- box initialText:'0.1'.
- box showAtPointer.
-
- (box accepted
- and: [(dx1 := Number readFrom:(box contents) onError:nil) notNil])
- ifTrue:[
- box title:(resources string:'dX2 (0 < dx < 0.5):').
- box initialText:(dx1 printString).
- box showAtPointer.
- (box accepted
- and: [(dx2 := Number readFrom:(box contents) onError:nil) notNil])
- ifTrue:[
- imageEditView threeDProjection:dx1 and:dx2.
- ]
- ].
-
- self updateInfoLabel
-!
-
-doBrightenImage
- imageEditView brightenImage.
- self listOfColors removeAll.
- self findColorMapMode.
- "/ imageEditView removelastUndo
-!
-
-doBrowseClass
- "opens a System Browser on the resourceClass and the resourceSelector"
-
- |cls|
-
- cls := imageEditView resourceClass.
- cls isNil ifTrue:[^ self warn:'No Class specified'].
-
- cls browserClass
- openInClass:cls class
- selector:(imageEditView resourceSelector)
-
- "Modified: / 31.7.1998 / 02:01:15 / cg"
-!
-
-doCopyImageToClipboard
- imageEditView copyImageToClipboard.
-!
-
-doCropManual
- "let user specify borders and cut them off"
-
- |bindings left top right bottom img firstChange cropAction acceptChannel|
-
- acceptChannel := TriggerValue new.
-
- firstChange := true.
-
- cropAction :=
- [:lV :rV :tV :bV | |l r t b|
- acceptChannel value:true.
-
- l := lV value.
- r := rV value.
- t := tV value.
- b := bV value.
- (l + r + t + b) == 0 ifTrue:[
- UserPreferences current beepInEditor ifTrue:[
- self window beep
- ]
- ] ifFalse:[
- img := imageEditView image.
- firstChange ifTrue:[
- imageEditView makeUndo.
- firstChange := false.
- ].
- imageEditView
- makeSubImageX:l y:t
- width:(img width - l - r)
- height:(img height - t - b).
-
- self updateImagePreView.
- self updateInfoLabel
- ].
- ].
-
- bindings := IdentityDictionary new.
- bindings at:#cropLeftAmount put:(left := 1 asValue).
- bindings at:#cropRightAmount put:(right := 1 asValue).
- bindings at:#cropTopAmount put:(top := 1 asValue).
- bindings at:#cropBottomAmount put:(bottom := 1 asValue).
- bindings at:#acceptChannel put:acceptChannel.
-
- bindings at:#cropLeftNow put:[ cropAction value:left value:0 value:0 value:0 ].
- bindings at:#cropRightNow put:[ cropAction value:0 value:right value:0 value:0 ].
- bindings at:#cropTopNow put:[ cropAction value:0 value:0 value:top value:0 ].
- bindings at:#cropBottomNow put:[ cropAction value:0 value:0 value:0 value:bottom ].
-
- bindings at:#applyCropAction put:[ cropAction value:left value:right value:top value:bottom ].
- bindings at:#cropBoxIsDialog put:true.
-
- (self openDialogInterface:#cropSpec withBindings:bindings)
- ifFalse:[
- firstChange ~~ true ifTrue:[
- imageEditView undo.
- self updateImagePreView.
- ]
- ].
-
- "Created: / 07-09-1998 / 18:16:07 / cg"
- "Modified: / 19-02-2017 / 15:43:50 / cg"
-!
-
-doDarkenImage
- imageEditView darkenImage.
- self listOfColors removeAll.
- self findColorMapMode.
- "/ imageEditView removelastUndo
-!
-
-doEditMask
- |mask|
-
- (mask := self image mask) notNil ifTrue:[
- mask edit
- ].
-
- "Modified: / 18-02-2017 / 00:38:51 / cg"
-!
-
-doFlipHorizontal
- "flips horizontally current image"
-
- imageEditView flipHorizontal
-!
-
-doFlipVertical
- "flips vertically current image"
-
- imageEditView flipVertical
-!
-
-doInsertTextFromUser
- |text tempForm tempImage maskImage font w h paintColor|
-
- text := Dialog request:'Text to be inserted (placed as bitmap into clipboard for paste):'.
- text isEmptyOrNil ifTrue:[^ self ].
-
- font := Font family:'arial' size:20.
- font := font onDevice:Screen current.
- w := font widthOf:text.
- h := font heightOf:text.
-
- tempForm := Form extent:(w@h) depth:1 onDevice:(Screen current).
- tempForm clear.
- tempForm font:font.
- tempForm paint:(Color colorId:1).
- tempForm displayString:text at:(0@font ascent).
-
- tempImage := tempForm asImage.
- maskImage := tempForm asImage.
-
- paintColor := imageEditView selectedColor.
- paintColor colorId == 0 ifTrue:[
- paintColor := Color black
- ].
- tempImage
- photometric:#palette;
- colorMap:(Array
- with:Color white
- with:paintColor);
- mask:maskImage.
-
- ImageEditView copyImageToClipboard:tempImage.
- self editMode value:#paste.
-
- "Modified: / 11-11-2007 / 12:32:55 / cg"
-!
-
-doInspectImage
- "opens a System Browser on the resourceClass and the resourceSelector"
-
- self image inspect
-!
-
-doMagnifyDown
- "magnifies the current image one step down"
-
- |magHolder mag|
-
- magHolder := self magnificationHolder.
- (mag := magHolder value) > 1 ifTrue: [
- magHolder value: mag - 1
- ]
-
- "Modified: / 26.7.1998 / 20:24:08 / cg"
-!
-
-doMagnifyImage
- "magnifies the current image to a new size"
-
- |box newSize image antiAliased|
-
- antiAliased := false asValue.
- image := imageEditView image.
-
- box := EnterBox new.
- box title:(resources string:'Images new size:').
- box okText:(resources string:'OK').
- box abortText:(resources string:'Cancel').
- box initialText:image extent printString.
- box verticalPanel add:(CheckBox label:(resources string:'Antialias/Smooth') model:antiAliased).
- box showAtPointer.
-
- (box accepted
- and: [(newSize := self pointFromString:(box contents)) notNil])
- ifTrue:[
- newSize isPoint ifFalse:[
- self warn:'Please enter the new size as ''x @ y''.'.
- ^ self.
- ].
- antiAliased value ifTrue:[
- ((newSize x < image width) or:[(newSize y < image height)]) ifTrue:[
- imageEditView magnifySmoothingTo:newSize.
- ] ifFalse:[
- imageEditView magnifyAntiAliasedImageTo:newSize.
- ].
- ] ifFalse:[
- imageEditView magnifyImageTo:newSize.
- ].
- ].
-
- self updateInfoLabel
-
- "Modified: / 30-08-2017 / 15:46:02 / cg"
-!
-
-doMagnifyImageBy
- "magnifies the current image (by a scale)"
-
- |oldSize newSize scaleString scale image antiAliased smoothing|
-
- image := imageEditView image.
- oldSize := image extent.
-
- antiAliased := false asValue.
- smoothing := false asValue.
-
- Dialog modifyingBoxWith:[:box |
- box verticalPanel add:(CheckBox label:(resources string:'Antialias/Smooth') model:antiAliased).
- "/ box verticalPanel add:(CheckBox label:(resources string:'Smoothing') model:smoothing).
- ] do:[
- scaleString := Dialog
- request:(resources string:'Scale factor (<1 to shrink; >1 to magnify):')
- initialAnswer:'1'
- list:#('0.1' '0.25' '0.3' '0.5' '1.5' '2' '3' '4').
- ].
- scaleString isNil ifTrue:[^ self].
-
- scale := Object readFromString:scaleString onError:nil.
-
- scale notNil ifTrue:[
- scale isNumber ifFalse:[
- self warn:'please enter a scale factor (<1 to shrink; >1 to magnify).'.
- ^ self.
- ].
- newSize := oldSize * scale.
- antiAliased value ifTrue:[
- scale < 1 ifTrue:[
- imageEditView magnifySmoothingBy:scale.
- ] ifFalse:[
- imageEditView magnifyAntiAliasedImageTo:newSize.
- ].
- ] ifFalse:[
- imageEditView magnifyImageTo:newSize.
- ].
- ].
-
- self updateInfoLabel
-
- "Modified: / 30-08-2017 / 15:34:56 / cg"
-!
-
-doMagnifyUp
- "magnifies the current image one step up"
-
- |magHolder mag|
-
- magHolder := self magnificationHolder.
- (mag := magHolder value) < 63 ifTrue: [
- magHolder value: mag + 1
- ]
-
- "Modified: / 26.7.1998 / 20:23:52 / cg"
-!
-
-doNegativeImage
- "negates current image by negating the color map"
-
- self image depth ~~ 1 ifTrue:[
- Dialog warn:'Only useful for depth 1 images'.
- ^ self
- ].
- imageEditView negativeImage.
- self listOfColors removeAll.
- self findColorMapMode.
- "/ imageEditView removelastUndo
-!
-
-doResizeImage
- "resizes the current image"
-
- |box newSize image|
-
- image := imageEditView image.
-
- box := EnterBox new.
- box title:(resources string:'Images new size:').
- box okText:(resources string:'OK').
- box abortText:(resources string:'Cancel').
- box initialText:image extent printString.
- box showAtPointer.
- (box accepted
- and: [(newSize := self pointFromString:(box contents)) notNil])
- ifTrue:[
- imageEditView resizeImageTo:newSize.
- ].
-!
-
-doRotateImage
- "rotates current image"
-
- |rotationString box rotation|
-
- rotationString := Dialog
- request:(resources string:'Rotate by (degrees, clockwise):')
- list:#( '-90' '90' '180' '45' '-45' '135' '-135' )
- initialAnswer:90.
- rotationString isEmptyOrNil ifTrue:[^ self]. "/ canceled
- rotation := Number readFrom:rotationString onError:[nil].
- rotation isNil ifTrue:[^ self].
-
-"/ box := EnterBox new.
-"/ box title:(resources string:'Rotate by (degrees, clockwise):').
-"/ box okText:(resources string:'OK').
-"/ box abortText:(resources string:'Cancel').
-"/ box initialText: '0'.
-"/ box showAtPointer.
-"/ (box accepted and: [(rotation := Number readFromString: box contents onError:nil) notNil])
-"/ ifFalse:[ ^ self ].
-
- imageEditView rotateImageBy:rotation.
- self updateInfoLabel.
-
- "Modified: / 18-03-2012 / 14:41:14 / cg"
- "Modified (comment): / 24-08-2017 / 15:02:57 / cg"
-!
-
-doShiftManual
- "let user specify amount and shift"
-
- |bindings amount img firstChange shiftAction acceptChannel wrapHolder|
-
- acceptChannel := TriggerValue new.
- wrapHolder := (lastShiftUsedWrap ? true) asValue.
-
- firstChange := true.
-
- shiftAction :=
- [:shiftH :shiftV |
- acceptChannel value:true.
-
- img := imageEditView image.
- firstChange ifTrue:[
- imageEditView makeUndo.
- firstChange := false.
- ].
- imageEditView shiftImageHorizontal:(shiftH value) vertical:(shiftV value) wrap:(wrapHolder value).
- self updateInfoLabel
- ].
-
- bindings := IdentityDictionary new.
- bindings at:#shiftAmount put:(amount := 1 asValue).
- bindings at:#wrap put:wrapHolder.
- bindings at:#acceptChannel put:acceptChannel.
-
- bindings at:#shiftLeftNow put:[ shiftAction value:(-1*amount value) value:0 ].
- bindings at:#shiftRightNow put:[ shiftAction value:amount value value:0 ].
- bindings at:#shiftUpNow put:[ shiftAction value:0 value:(-1*amount value) ].
- bindings at:#shiftDownNow put:[ shiftAction value:0 value:amount value ].
-
- (self openDialogInterface:#shiftDialogSpec withBindings:bindings)
- ifFalse:[
- firstChange ~~ true ifTrue:[
- imageEditView undo
- ]
- ].
- lastShiftUsedWrap := wrapHolder value.
-
- "Created: / 7.9.1998 / 18:16:07 / cg"
- "Modified: / 7.9.1998 / 18:20:42 / cg"
-!
-
-doUnCropManual
- "let user specify borders and add them"
-
- |bindings leftAmount topAmount rightAmount bottomAmount img|
-
- bindings := IdentityDictionary new.
- bindings at:#cropLeftAmount put:(leftAmount := 1 asValue).
- bindings at:#cropRightAmount put:(rightAmount := 1 asValue).
- bindings at:#cropTopAmount put:(topAmount := 1 asValue).
- bindings at:#cropBottomAmount put:(bottomAmount := 1 asValue).
- bindings at:#cropBoxIsDialog put:true.
-
- (self openDialogInterface:#uncropSpec withBindings:bindings)
- ifTrue:[
- leftAmount := leftAmount value.
- rightAmount := rightAmount value.
- topAmount := topAmount value.
- bottomAmount := bottomAmount value.
- img := imageEditView image.
-
- imageEditView
- makeBorderedImageX:leftAmount y:topAmount
- width:(img width + leftAmount + rightAmount)
- height:(img height + topAmount + bottomAmount).
- self updateInfoLabel
- ].
-
- "Created: / 07-09-1998 / 18:16:07 / cg"
- "Modified: / 19-02-2017 / 15:31:00 / cg"
-!
-
-doUndo
- "reverses last edit action"
-
- imageEditView undo.
- self updateImagePreView
-!
-
-makeMonochromeImage
- "let user choose a threshold, then convert to monochrome"
-
- |image userInput thresholdBrighness|
-
- image := imageEditView image.
-
- Dialog modifyingBoxWith:[:box |
- |preview slider update thresholdValue|
-
- thresholdValue := 0.5 asValue.
-
- box enterField
- converter:(PrintConverter new initForNumber);
- model:thresholdValue.
-
- box verticalPanel extent:1.0 @ 300.
-
- box verticalPanel add:(slider := HorizontalSlider new start:0 stop:1 step:0.05).
- slider model:thresholdValue.
- slider width:1.0; leftInset:4; rightInset:4.
-
- box verticalPanel add:(preview := ImageView new).
- preview extent:300 @300.
- preview level:-1.
- box verticalPanel horizontalLayout:#fitSpace.
-
- update :=
- [
- |s t|
-
- t := thresholdValue value clampBetween:0 and:1.
- preview image:((image asThresholdMonochromeImage:t)
- magnifiedPreservingRatioTo:preview extent).
- ].
- update value.
- box enterField acceptOnLostFocus:true.
- box enterField acceptOnLeave:true.
- thresholdValue onChangeEvaluate:update.
-
- ] do:[
- userInput := Dialog request:'Threshold (0=black; 1=white) ?' initialAnswer:0.5.
- ].
- userInput isEmptyOrNil ifTrue:[^ self].
- thresholdBrighness := Number readFrom:userInput onError:nil.
- thresholdBrighness isNil ifTrue:[^ self].
-
- thresholdBrighness := thresholdBrighness clampBetween:0 and:1.
- imageEditView newImageWithUndo:(image asThresholdMonochromeImage:thresholdBrighness)
-
- "Created: / 24-08-2017 / 15:26:44 / cg"
- "Modified: / 24-08-2017 / 17:54:21 / cg"
-!
-
-thresholdGrayToDepth
- self askForDepthThenDo:[:depth |
- self thresholdGrayToDepth:depth
- ].
-
- "Created: / 24-08-2017 / 17:49:23 / cg"
-!
-
-thresholdGrayToDepth:depth
- self withExecuteCursorDo:[
- |newImage|
-
- newImage := self image asThresholdGrayImageDepth:depth.
- imageEditView newImageWithUndo:newImage.
- ].
-
- "Created: / 24-08-2017 / 17:49:30 / cg"
-!
-
-thresholdToDepth
- self askForDepthThenDo:[:depth |
- self convertToDepth:depth dither:false
- ].
-
- "Created: / 30-08-2017 / 00:31:33 / cg"
+makeInvertedBits
+ "inverts the pixels - for palettes, this leads to funny results.
+ For others, this is the same as negating"
+
+ self updateImageAfterDoing:#makeInvertedBits.
+
+ "Created: / 31-08-2017 / 13:51:10 / cg"
! !
!ImageEditor methodsFor:'user actions-image sequences'!