diff -r da26c2e9a037 -r 95c973cafdc1 ImageEditor.st --- a/ImageEditor.st Tue Aug 29 21:09:29 2017 +0200 +++ b/ImageEditor.st Wed Aug 30 15:42:00 2017 +0200 @@ -19,7 +19,7 @@ lastShiftUsedWrap lastGrabbedScreenArea allowedToChangeImageDimensionAndDepth savedImage savedFile' classVariableNames:'DefaultRelativeSizes LastColormapMode LastDirectory - LastSizeString LastURL MaskClipboard' + LastSizeString LastURL MaskClipboard LastDepth' poolDictionaries:'' category:'Interface-UIPainter' ! @@ -3110,6 +3110,11 @@ translateLabel: true ) (MenuItem + label: 'Threshold to Depth...' + itemValue: thresholdToDepth + translateLabel: true + ) + (MenuItem label: 'Make GrayScale with Depth (Dither)...' itemValue: ditherGrayToDepth translateLabel: true @@ -3206,214 +3211,26 @@ nil ) - "Modified: / 24-08-2017 / 17:49:04 / cg" + "Modified: / 30-08-2017 / 00:35:31 / cg" ! menuEdit + "This resource specification was automatically generated by the MenuEditor of ST/X." - "Do not manually edit this!! If it is corrupted, the MenuEditor may not be able to read the specification." - - " MenuEditor new openOnClass:ImageEditor andSelector:#menuEdit - (Menu new fromLiteralArrayEncoding:(ImageEditor menuEdit)) startUp - " - - - - ^ - #(Menu - ( - (MenuItem - activeHelpKey: editUndo - enabled: canUndoHolder - label: 'Undo' - itemValue: doUndo - ) - (MenuItem - label: '-' - ) - (MenuItem - enabled: imageIsLoadedHolder - label: 'Copy to Clipboard' - itemValue: doCopyImageToClipboard - ) - (MenuItem - label: '-' - ) - (MenuItem - activeHelpKey: editResize - enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth - label: 'Resize...' - itemValue: doResizeImage - ) - (MenuItem - activeHelpKey: editMagnifyImage - enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth - label: 'Magnify...' - itemValue: doMagnifyImage - ) - (MenuItem - activeHelpKey: editMagnifyImage - enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth - label: 'Magnify By...' - itemValue: doMagnifyImageBy - ) - (MenuItem - activeHelpKey: editRotate - enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth - label: 'Rotate...' - itemValue: doRotateImage - ) - (MenuItem - activeHelpKey: edit3DProjection - enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth - label: '3D Projection...' - itemValue: do3DProjection - ) - (MenuItem - enabled: imageIsLoadedAndAllowedToFlipHolder - label: 'Flip' - submenu: - (Menu - ( - (MenuItem - activeHelpKey: editFlipVertical - enabled: imageIsLoadedAndNotReadonlyHolder - label: 'Flip - Vertical' - itemValue: doFlipVertical - labelImage: (ResourceRetriever ImageEditor flipVerticalIcon 'Flip - Vertical') - ) - (MenuItem - activeHelpKey: editFlipHorizontal - enabled: imageIsLoadedAndNotReadonlyHolder - label: 'Flip - Horizontal' - itemValue: doFlipHorizontal - labelImage: (ResourceRetriever ImageEditor flipHorizontalIcon 'Flip - Horizontal') - ) - ) - nil - nil - ) - ) - (MenuItem - label: '-' - ) - (MenuItem - enabled: imageIsLoadedAndAllowedToChangeImageDimension - label: 'Crop' - submenu: - (Menu - ( - (MenuItem - activeHelpKey: cropManual - label: 'Manual...' - itemValue: doCropManual - ) - (MenuItem - label: '-' - isVisible: false - ) - (MenuItem - activeHelpKey: autoCropAll - label: 'All' - itemValue: autoCropAll - ) - (MenuItem - label: '-' - ) - (MenuItem - activeHelpKey: autoCropLeft - label: 'Left' - itemValue: autoCropLeft - ) - (MenuItem - activeHelpKey: autoCropRight - label: 'Right' - itemValue: autoCropRight - ) - (MenuItem - activeHelpKey: autoCropTop - label: 'Top' - itemValue: autoCropTop - ) - (MenuItem - activeHelpKey: autoCropBottom - label: 'Bottom' - itemValue: autoCropBottom - ) - ) - nil - nil - ) - ) - (MenuItem - activeHelpKey: uncropManual - enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth - label: 'Uncrop (Add Border)...' - itemValue: doUnCropManual - ) - (MenuItem - activeHelpKey: shiftManual - enabled: imageIsLoadedAndAllowedToChangeImageDimension - label: 'Shift...' - itemValue: doShiftManual - ) - (MenuItem - label: '-' - ) - (MenuItem - activeHelpKey: fileEditMask - enabled: hasMaskHolder - label: 'Edit Mask' - itemValue: doEditMask - ) - (MenuItem - enabled: imageIsLoadedAndNotReadonlyHolder - label: 'Text...' - itemValue: doInsertTextFromUser - ) - (MenuItem - label: '-' - ) - (MenuItem - enabled: imageIsLoadedHolder - label: 'Animation Sequence' - submenu: - (Menu - ( - (MenuItem - enabled: imageHasNextImageHolder - label: 'Next in Sequence' - itemValue: nextImageInSequence - ) - (MenuItem - enabled: imageHasPreviousImageHolder - label: 'Previous in Sequence' - itemValue: previousImageInSequence - ) - (MenuItem - label: '-' - ) - (MenuItem - enabled: imageHasImageSequenceHolder - label: 'Edit each from Sequence' - itemValue: editEachImageFromSequence - ) - ) - nil - nil - ) - ) - ) - nil - nil - ) + (Menu new fromLiteralArrayEncoding:(ImageEditor menuEdit)) startUp" + + ^ #( #Menu + #((MenuItem activeHelpKey: editUndo enabled: canUndoHolder label: 'Undo' itemValue: doUndo) (MenuItem label: '-') (MenuItem enabled: imageIsLoadedHolder label: 'Copy to Clipboard' itemValue: doCopyImageToClipboard) (MenuItem label: '-') (MenuItem activeHelpKey: editResize enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth label: 'Resize...' itemValue: doResizeImage) (MenuItem activeHelpKey: editMagnifyImage enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth label: 'Magnify...' itemValue: doMagnifyImage) (MenuItem activeHelpKey: editMagnifyImage enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth label: 'Magnify By...' itemValue: doMagnifyImageBy) (MenuItem activeHelpKey: editRotate enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth label: 'Rotate...' itemValue: doRotateImage) (MenuItem activeHelpKey: edit3DProjection enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth label: '3D Projection...' itemValue: do3DProjection) (MenuItem enabled: imageIsLoadedAndAllowedToFlipHolder label: 'Flip' submenu: (Menu ((MenuItem activeHelpKey: editFlipVertical enabled: imageIsLoadedAndNotReadonlyHolder label: 'Flip - Vertical' itemValue: doFlipVertical labelImage: (ResourceRetriever ImageEditor flipVerticalIcon 'Flip - Vertical')) (MenuItem activeHelpKey: editFlipHorizontal enabled: imageIsLoadedAndNotReadonlyHolder label: 'Flip - Horizontal' itemValue: doFlipHorizontal labelImage: (ResourceRetriever ImageEditor flipHorizontalIcon 'Flip - Horizontal'))) nil nil)) (MenuItem label: '-') (MenuItem enabled: imageIsLoadedAndAllowedToChangeImageDimension label: 'Crop' submenu: (Menu ((MenuItem activeHelpKey: cropManual label: 'Manual...' itemValue: doCropManual) (MenuItem label: '-' isVisible: false) (MenuItem activeHelpKey: autoCropAll label: 'All' itemValue: autoCropAll) (MenuItem label: '-') (MenuItem activeHelpKey: autoCropLeft label: 'Left' itemValue: autoCropLeft) (MenuItem activeHelpKey: autoCropRight label: 'Right' itemValue: autoCropRight) (MenuItem activeHelpKey: autoCropTop label: 'Top' itemValue: autoCropTop) (MenuItem activeHelpKey: autoCropBottom label: 'Bottom' itemValue: autoCropBottom)) nil nil)) (MenuItem activeHelpKey: uncropManual enabled: imageIsLoadedAndAllowedToChangeImageDimensionAndDepth label: 'Uncrop (Add Border)...' itemValue: doUnCropManual) (MenuItem activeHelpKey: shiftManual enabled: imageIsLoadedAndAllowedToChangeImageDimension label: 'Shift...' itemValue: doShiftManual) (MenuItem label: '-') (MenuItem activeHelpKey: fileEditMask enabled: hasMaskHolder label: 'Edit Mask' itemValue: doEditMask) (MenuItem enabled: imageIsLoadedAndNotReadonlyHolder label: 'Text...' itemValue: doInsertTextFromUser) (MenuItem label: '-') (MenuItem enabled: imageIsLoadedHolder label: 'Animation Sequence' submenu: (Menu ((MenuItem enabled: imageHasNextImageHolder label: 'Next in Sequence' itemValue: nextImageInSequence) (MenuItem enabled: imageHasPreviousImageHolder label: 'Previous in Sequence' itemValue: previousImageInSequence) (MenuItem label: '-') (MenuItem enabled: imageHasImageSequenceHolder label: 'Edit each from Sequence' itemValue: editEachImageFromSequence)) nil nil))) + nil + nil ) "Modified: / 12-04-2017 / 09:25:18 / cg" + "Modified (comment): / 30-08-2017 / 00:30:38 / cg" ! menuFile @@ -5773,7 +5590,14 @@ |oldDepth suggestion depth| oldDepth := self image depth. - suggestion := oldDepth > 8 ifTrue:[8] ifFalse:[(oldDepth // 2 - 1) nextPowerOf2]. + + suggestion := LastDepth notNil ifTrue:[ + LastDepth + ] ifFalse:[ + oldDepth > 8 + ifTrue:[8] + ifFalse:[(oldDepth // 2 - 1) nextPowerOf2] + ]. depth := Dialog request:'New depth ?' initialAnswer:suggestion asString. depth isEmptyOrNil ifTrue:[^ self]. @@ -5781,9 +5605,11 @@ depth := Number readFrom:depth onError:nil. depth isNil ifTrue:[^ self]. + LastDepth := depth. aBlock value:depth "Created: / 24-08-2017 / 17:05:39 / cg" + "Modified: / 30-08-2017 / 01:22:36 / cg" ! checkModified @@ -7508,6 +7334,170 @@ "Created: / 20-02-2017 / 18:06:03 / cg" ! +convertToDepth:depth dither:doDither + |answer labels values + ditherColors fixColors + nGrey greyColorsAlready moreColors d| + + "/ no colormap above 12 bits!! + depth > 12 ifTrue:[ + answer := #TrueColor. + ] ifFalse:[ + doDither ifTrue:[ + (depth >= 8) ifTrue:[ + labels := #('Cancel' 'Use Browser Palette' 'Use Standard' 'Compute' 'TrueColor'). + values := #(nil UseBrowserPalette UseStandard Compute TrueColor). + ] ifFalse:[ + depth == 1 ifTrue:[ + answer := #UseStandard + ] ifFalse:[ + labels := #('Cancel' 'Use Standard' 'Compute' 'TrueColor'). + values := #(nil UseStandard Compute TrueColor). + ]. + ]. + ] ifFalse:[ + (depth >= 8) ifTrue:[ + labels := #('Cancel' 'Use Browser Palette' 'Use Standard' 'TrueColor'). + values := #(nil UseBrowserPalette UseStandard TrueColor). + ] ifFalse:[ + answer := #UseStandard + ]. + ]. + ]. + + answer isNil ifTrue:[ + answer := OptionBox + request:'Compute a new (optimized) colormap\(Warning: this may take some time)\\or else use a standard colormap?' withCRs + label:(resources string:'Dither how') + image:(YesNoBox iconBitmap) + buttonLabels:(resources array:labels) + values:values + default:#UseStandard + onCancel:nil. + answer isNil ifTrue:[^ self]. + ]. + + (answer == #Compute) ifTrue:[ + ditherColors := + Color + best:(1 bitShift:depth) + ditherColorsForImage:self image + ] ifFalse:[ + (answer == #UseBrowserPalette) ifTrue:[ + ditherColors := Color colorCubeWithRed:6 green:6 blue:6. + ] ifFalse:[ + answer == #TrueColor ifTrue:[ + depth == 6 ifTrue:[ + fixColors := FixedPalette + redShift:4 redMask:3 + greenShift:2 greenMask:3 + blueShift:0 blueMask:3 + ]. + depth == 9 ifTrue:[ + fixColors := FixedPalette + redShift:6 redMask:7 + greenShift:3 greenMask:7 + blueShift:0 blueMask:7 + ]. + depth == 12 ifTrue:[ + fixColors := FixedPalette + redShift:8 redMask:16r0F + greenShift:4 greenMask:16r0F + blueShift:0 blueMask:16r0F + ]. + depth == 15 ifTrue:[ + fixColors := FixedPalette + redShift:10 redMask:16r1F + greenShift:5 greenMask:16r1F + blueShift:0 blueMask:16r1F + ]. + depth == 16 ifTrue:[ + fixColors := FixedPalette + redShift:11 redMask:16r1F + greenShift:5 greenMask:16r3F + blueShift:0 blueMask:16r1F + ]. + depth == 18 ifTrue:[ + fixColors := FixedPalette + redShift:12 redMask:16r3F + greenShift:6 greenMask:16r3F + blueShift:0 blueMask:16r3F + ]. + ] ifFalse:[ + depth == 1 ifTrue:[ + ditherColors := { Color black . Color white }. + ]. + depth == 2 ifTrue:[ + ditherColors := { Color black . Color darkGray .Color lightGray . Color white }. + ]. + depth = 3 ifTrue:[ + ditherColors := Color colorCubeWithRed:2 green:2 blue:2. + ]. + depth = 4 ifTrue:[ + ditherColors := Color vgaColors. + ]. + depth = 5 ifTrue:[ + ditherColors := Color colorCubeWithRed:3 green:4 blue:2. + ]. + depth = 6 ifTrue:[ + ditherColors := Color colorCubeWithRed:4 green:4 blue:3. + ]. + depth = 7 ifTrue:[ + ditherColors := Color colorCubeWithRed:5 green:5 blue:4. + ]. + depth == 8 ifTrue:[ + ditherColors := Color standardDitherColorsForDepth8. + ]. + depth == 9 ifTrue:[ + ditherColors := Color colorCubeWithRed:8 green:8 blue:8. + ]. + ditherColors isNil ifTrue:[ + self error:'unsupported depth'. + ]. + + "/ add as many gray colors as possible. + nGrey := (2 raisedTo:depth) - ditherColors size. + nGrey > 0 ifTrue:[ + nGrey := nGrey min:100. + greyColorsAlready := ditherColors select:[:clr | clr isGreyColor]. + d := 1 / nGrey. + moreColors := (1 to:nGrey-1) + collect:[:i | Color brightness:(d * i)] + thenReject:[:clr | greyColorsAlready includes:clr ]. + + ditherColors := ditherColors , moreColors. + ]. + ]. + ]. + ]. + + self withExecuteCursorDo:[ + |newImage| + + doDither ifTrue:[ + depth == 1 ifTrue:[ + newImage := self image asErrorDitheredMonochromeImage + ] ifFalse:[ + answer == #TrueColor ifTrue:[ + newImage := self image asDitheredImageUsing:fixColors depth:depth. + ] ifFalse:[ + newImage := self image asDitheredImageUsing:ditherColors depth:depth. + ] + ] + ] ifFalse:[ + answer == #TrueColor ifTrue:[ + self halt. + ] ifFalse:[ + newImage := self image asNearestPaintImageDepth:depth colors:ditherColors + ]. + ]. + imageEditView newImageWithUndo:newImage. + ]. + + "Created: / 30-08-2017 / 00:33:55 / cg" + "Modified: / 30-08-2017 / 02:13:03 / cg" +! + ditherGrayToDepth self askForDepthThenDo:[:depth | self ditherGrayToDepth:depth @@ -7519,102 +7509,26 @@ ditherGrayToDepth:depth self withExecuteCursorDo:[ |newImage| - - newImage := self image asGrayImageDepth:depth dither:true. + + 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 ditherToDepth:depth + self convertToDepth:depth dither:true ]. "Created: / 07-07-2006 / 13:22:10 / cg" - "Modified: / 24-08-2017 / 17:06:00 / cg" -! - -ditherToDepth:depth - |answer labels values ditherColors nGrey greyColorsAlready moreColors d| - - (depth >= 8) ifTrue:[ - labels := #('Cancel' 'Use Browser Palette' 'Use Standard' 'Compute' ). - values := #(nil UseBrowserPalette UseStandard Compute). - ] ifFalse:[ - labels := #('Cancel' 'Use Standard' 'Compute' ). - values := #(nil UseStandard Compute). - ]. - - answer := OptionBox - request:'Compute a new (optimized) colormap\(Warning: this may take some time)\\or else use a standard colormap?' withCRs - label:(resources string:'Dither how') - image:(YesNoBox iconBitmap) - buttonLabels:(resources array:labels) - values:values - default:#UseStandard - onCancel:nil. - answer isNil ifTrue:[^ self]. - - (answer == #Compute) ifTrue:[ - ditherColors := - Color - best:(1 bitShift:depth) - ditherColorsForImage:self image - ] ifFalse:[ - (answer == #UseBrowserPalette) ifTrue:[ - ditherColors := Color colorCubeWithRed:6 green:6 blue:6. - ] ifFalse:[ - depth = 1 ifTrue:[ - ditherColors := Array with:(Color black) with:(Color white). - ] ifFalse:[ depth = 2 ifTrue:[ - ditherColors := Array - with:(Color black) - with:(Color darkGray) - with:(Color lightGray) - with:(Color white). - ] ifFalse:[ depth = 3 ifTrue:[ - ditherColors := Color colorCubeWithRed:2 green:2 blue:2. - ] ifFalse:[ depth = 4 ifTrue:[ - ditherColors := Color vgaColors. - ] ifFalse:[ depth = 5 ifTrue:[ - ditherColors := Color colorCubeWithRed:3 green:4 blue:2. - ] ifFalse:[ depth = 6 ifTrue:[ - ditherColors := Color colorCubeWithRed:4 green:4 blue:3. - ] ifFalse:[ depth = 7 ifTrue:[ - ditherColors := Color colorCubeWithRed:5 green:5 blue:4. - ] ifFalse:[ depth == 8 ifTrue:[ - ditherColors := Color standardDitherColorsForDepth8. - ] ifFalse:[ - self error:'unsupported depth'. - ]]]]]]]]. - - - "/ add as many gray colors as possible. - nGrey := (2 raisedTo:depth) - ditherColors size. - nGrey > 0 ifTrue:[ - nGrey := nGrey min:128. - greyColorsAlready := ditherColors select:[:clr | clr isGreyColor]. - d := 1 / nGrey. - moreColors := (1 to:nGrey-1) - collect:[:i | Color brightness:(d * i)] - thenReject:[:clr | greyColorsAlready includes:clr ]. - - ditherColors := ditherColors , moreColors. - ]. - ]. - ]. - - self withExecuteCursorDo:[ - |newImage| - - newImage := self image asDitheredImageUsing:ditherColors depth:depth. - imageEditView newImageWithUndo:newImage. - ]. - - "Created: / 07-07-2006 / 13:20:56 / cg" - "Modified: / 29-08-2017 / 21:08:34 / cg" + "Modified: / 30-08-2017 / 00:34:42 / cg" ! do3DProjection @@ -7856,33 +7770,25 @@ doMagnifyImageBy "magnifies the current image (by a scale)" - |oldSize newSize scaleString scale image antiAliased| + |oldSize newSize scaleString scale image antiAliased smoothing| image := imageEditView image. oldSize := image extent. antiAliased := false asValue. - + smoothing := false asValue. + Dialog modifyingBoxWith:[:box | - box addCheckBox:(resources string:'Antialiased') on:antiAliased. + 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.25' '0.5' '2' '4'). + list:#('0.1' '0.25' '0.3' '0.5' '1.5' '2' '3' '4'). ]. scaleString isNil ifTrue:[^ self]. -"/ box := EnterBox new. -"/ box title:(resources string:'Scale factor (<1 to shrink; >1 to magnify):'). -"/ box okText:(resources string:'OK'). -"/ box abortText:(resources string:'Cancel'). -"/ box initialText:1 printString. -"/ box showAtPointer. -"/ box accepted ifTrue:[ -"/ scaleString := box contents. -"/ ]. - scale := Object readFromString:scaleString onError:nil. scale notNil ifTrue:[ @@ -7892,13 +7798,19 @@ ]. newSize := oldSize * scale. antiAliased value ifTrue:[ - imageEditView magnifyAntiAliasedImageTo:newSize. + 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 @@ -8130,6 +8042,14 @@ ]. "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-image sequences'!