diff -r 7eafbd0cad40 -r 2854769b212a ImageEditor.st --- 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'!