--- a/Image.st Fri Feb 17 12:51:00 2017 +0100
+++ b/Image.st Fri Feb 17 16:04:32 2017 +0100
@@ -9966,109 +9966,51 @@
floodFillAt: aPoint withColor: aColor
"fill a area with aColor like a flood up to surrounded pixels having different colors.
- By using #atImageAndMask:put: it also works on images with mono masks."
-
- self floodFillAt:aPoint withValue:(self valueFromColor:aColor)
-
-"/ |surroundingPixelsOfDo detectedPixel processPixelToFill
-"/ allDetectedPixelCoordinates enumerateDetectedPixelsAndDo
-"/ toDo idx pixel w h|
-"/
-"/ w := self width.
-"/ h := self height.
-"/
-"/ surroundingPixelsOfDo :=
-"/ [:pX :pY :fn |
-"/ |nX nY|
-"/
-"/ nX := pX + 1.
-"/ nY := pY + 1.
-"/ (nY < h) ifTrue: [fn value:pX value:nY].
-"/ (pY > 0) ifTrue: [fn value:pX value:(pY - 1)].
-"/ (nX < w) ifTrue: [fn value:nX value:pY].
-"/ (pX > 0) ifTrue: [fn value:(pX - 1) value:pY].
-"/ ].
-"/
-"/ enumerateDetectedPixelsAndDo :=
-"/ [:detectedPixels :action |
-"/ |idx|
-"/
-"/ idx := 1.
-"/ 0 to:h-1 do:[:y |
-"/ 0 to:w-1 do:[:x |
-"/ (detectedPixels at:idx) ifTrue:[
-"/ action value:x value:y
-"/ ].
-"/ idx := idx + 1.
-"/ ].
-"/ ].
-"/ ].
-"/
-"/ processPixelToFill := [:spX :spY |
-"/ |sp idx|
-"/
-"/ ((self pixelAtX:spX y:spY) == detectedPixel and: [mask isNil or:[(mask pixelAtX:spX y:spY) == 1]])
-"/ ifTrue: [
-"/ idx := 1 + spX + (spY * w).
-"/ (allDetectedPixelCoordinates at:idx) ifFalse:[
-"/ allDetectedPixelCoordinates at:idx put:true.
-"/ toDo add:spX @ spY.
-"/ ].
-"/ ]
-"/ ].
-"/
-"/ (mask notNil and: [(mask pixelAt:aPoint) == 0]) ifTrue:[
-"/ allDetectedPixelCoordinates := mask floodFillAt: aPoint withColor: Color white.
-"/ enumerateDetectedPixelsAndDo
-"/ value:allDetectedPixelCoordinates
-"/ value:[:x :y | self atImageAndMask:(x@y) put: aColor].
-"/ ^ allDetectedPixelCoordinates
-"/ ].
-"/
-"/ detectedPixel := self pixelAt: aPoint.
-"/
-"/ allDetectedPixelCoordinates := BooleanArray new:(w * h).
-"/ toDo := OrderedCollection new:1000.
-"/ allDetectedPixelCoordinates at:(1 + aPoint x + (aPoint y * w)) put:true.
-"/ toDo add:aPoint.
-"/
-"/ [toDo notEmpty] whileTrue:[
-"/ |p|
-"/
-"/ p := toDo removeFirst.
-"/ surroundingPixelsOfDo value:p x value:p y value:processPixelToFill.
-"/ ].
-"/
-"/ idx := 1.
-"/ aColor redByte notNil ifTrue:[
-"/ pixel := self valueFromColor:aColor.
-"/ ].
-"/
-"/ pixel isNil ifTrue:[
-"/ enumerateDetectedPixelsAndDo
-"/ value:allDetectedPixelCoordinates
-"/ value:[:x :y | mask pixelAtX:x y:y put:0].
-"/ ] ifFalse:[
-"/ enumerateDetectedPixelsAndDo
-"/ value:allDetectedPixelCoordinates
-"/ value:[:x :y | self pixelAtX:x y:y put:pixel].
-"/ ].
-"/ ^ allDetectedPixelCoordinates
-
- "Modified: / 29.7.1998 / 03:09:16 / cg"
+ By using #atImageAndMask:put: it also works on images with mono masks.
+ Currently returns a collection of all pixels which have been modified (needed by imageEditor?),
+ but that should be changed to avoid huge massdata creation (instead, call a block for each)"
+
+ ^ self floodFillAt:aPoint withValue:(self valueFromColor:aColor)
+
+ "Modified: / 17-02-2017 / 15:05:29 / cg"
!
floodFillAt:aPoint withValue:aPixelValueOrNil
"fill a area with aColor like a flood up to surrounded pixels having different colors.
- By using #atImageAndMask:put: it also works on images with mono masks."
+ By using #atImageAndMask:put: it also works on images with mono masks.
+ Currently returns a collection of all pixels which have been modified (needed by imageEditor?),
+ but that should be changed to avoid huge massdata creation (instead, call a block for each)"
+
+ ^ self floodFillAt:aPoint withValue:aPixelValueOrNil maxDeviationInLight:0 maxDeviationInHue:0
+
+ "Modified (format): / 30-01-2017 / 20:57:36 / stefan"
+ "Modified (comment): / 17-02-2017 / 15:05:19 / cg"
+!
+
+floodFillAt:aPoint withValue:aPixelValueOrNil maxDeviationInLight:maxLightError maxDeviationInHue:maxHueError
+ "fill an area with aColor like a flood up to surrounded pixels having different colors.
+ If maxLightError/maxLueError are zero, the flood fills only pixels with the same value as the pixel at aPoint;
+ otherwise, a slight error (fraction) in light/hue is allowd, and pixels with a nearby color are also filled.
+ By using #atImageAndMask:put: it also works on images with mono masks.
+ Currently returns a collection of all pixels which have been modified (needed by imageEditor?),
+ but that should be changed to avoid huge massdata creation (instead, call a block for each)"
|surroundingPixelsOfDo detectedPixel detectedMask processPixelToFill
allDetectedPixelCoordinates enumerateDetectedPixelsAndDo
- toDo w h drawAction|
+ toDo w h drawAction
+ detectedColor detectedHue detectedLight minHueOK maxHueOK minLightOK maxLightOK
+ almostSamePixel|
w := self width.
h := self height.
+ detectedPixel := self pixelAt: aPoint.
+ mask isNil ifTrue:[
+ detectedMask := 1
+ ] ifFalse:[
+ detectedMask := mask pixelAt: aPoint.
+ ].
+
surroundingPixelsOfDo :=
[:pX :pY :fn |
|nX nY|
@@ -10096,29 +10038,91 @@
].
].
- processPixelToFill :=
- [:spX :spY |
- |samePixel idx|
-
- mask isNil ifTrue:[
- samePixel := (self pixelAtX:spX y:spY) == detectedPixel
- ] ifFalse:[
- detectedMask == 0 ifTrue:[
- samePixel := (mask pixelAtX:spX y:spY) == 0
+ (maxLightError = 0 and:[maxHueError = 0]) ifTrue:[
+ "/ compare pixels exactly - a faster algorithm
+ processPixelToFill :=
+ [:spX :spY |
+ |samePixel idx|
+
+ mask isNil ifTrue:[
+ samePixel := (self pixelAtX:spX y:spY) == detectedPixel
] ifFalse:[
- samePixel := ((self pixelAtX:spX y:spY) == detectedPixel)
- and:[ (mask pixelAtX:spX y:spY) == detectedMask ]
+ detectedMask == 0 ifTrue:[
+ samePixel := (mask pixelAtX:spX y:spY) == 0
+ ] ifFalse:[
+ samePixel := ((self pixelAtX:spX y:spY) == detectedPixel)
+ and:[ (mask pixelAtX:spX y:spY) == detectedMask ]
+ ].
].
- ].
- samePixel ifTrue: [
- idx := 1 + spX + (spY * w).
- (allDetectedPixelCoordinates at:idx) ifFalse:[
- allDetectedPixelCoordinates at:idx put:true.
- toDo add:spX @ spY.
+ samePixel ifTrue: [
+ idx := 1 + spX + (spY * w).
+ (allDetectedPixelCoordinates at:idx) ifFalse:[
+ allDetectedPixelCoordinates at:idx put:true.
+ toDo add:spX @ spY.
+ ].
+ ]
+ ].
+ ] ifFalse:[
+ "/ compare pixels with some error-tolerance - a slow algorithm because we have to compute
+ "/ hue and light for every pixel...
+ detectedColor := self colorFromValue:detectedPixel.
+ detectedHue := detectedColor hue.
+ detectedLight := detectedColor light.
+ detectedHue notNil ifTrue:[
+ minHueOK := detectedHue * (1.0 - maxHueError).
+ maxHueOK := detectedHue * (1.0 + maxHueError).
+ ].
+ minLightOK := detectedLight * (1.0 - maxLightError).
+ maxLightOK := detectedLight * (1.0 + maxLightError).
+
+ almostSamePixel :=
+ [:pixelValue |
+ |same pixelColor pixelHue pixelLight errHue errLight|
+
+ (same := (pixelColor == detectedPixel)) ifFalse:[
+ pixelColor := self colorFromValue:pixelValue.
+ pixelHue := pixelColor hue.
+ pixelLight := pixelColor light.
+ detectedHue isNil ifTrue:[
+ "/ detect gray - what should we do?
+ same := "(pixelHue isNil) and:["pixelLight between:minLightOK and:maxLightOK"]".
+ ] ifFalse:[
+ pixelHue isNil ifTrue:[
+ "/ pixel is gray - what should we do?
+ "/ same := false.
+ same := pixelLight between:minLightOK and:maxLightOK.
+ ] ifFalse:[
+ same := (pixelHue between:minHueOK and:maxHueOK) and:[pixelLight between:minLightOK and:maxLightOK].
+ ].
+ ].
].
- ]
- ].
-
+ same
+ ].
+
+ processPixelToFill :=
+ [:spX :spY |
+ |samePixel idx|
+
+ mask isNil ifTrue:[
+ samePixel := almostSamePixel value:(self pixelAtX:spX y:spY)
+ ] ifFalse:[
+ detectedMask == 0 ifTrue:[
+ samePixel := (mask pixelAtX:spX y:spY) == 0
+ ] ifFalse:[
+ samePixel := (almostSamePixel value:(self pixelAtX:spX y:spY))
+ and:[ (mask pixelAtX:spX y:spY) == detectedMask ]
+ ].
+ ].
+ samePixel ifTrue: [
+ idx := 1 + spX + (spY * w).
+ (allDetectedPixelCoordinates at:idx) ifFalse:[
+ allDetectedPixelCoordinates at:idx put:true.
+ toDo add:spX @ spY.
+ ].
+ ]
+ ].
+ ].
+
"/ (mask notNil and: [(mask pixelAt:aPoint) == 0]) ifTrue:[
"/ allDetectedPixelCoordinates := mask floodFillAt: aPoint withColor: Color white.
"/ enumerateDetectedPixelsAndDo
@@ -10127,13 +10131,6 @@
"/ ^ allDetectedPixelCoordinates
"/ ].
- detectedPixel := self pixelAt: aPoint.
- mask isNil ifTrue:[
- detectedMask := 1
- ] ifFalse:[
- detectedMask := mask pixelAt: aPoint.
- ].
-
allDetectedPixelCoordinates := BooleanArray new:(w * h).
toDo := OrderedCollection new:1000.
allDetectedPixelCoordinates at:((aPoint y * w) + aPoint x + 1) put:true.
@@ -10166,8 +10163,7 @@
self release. "/ device-image is no longer valid
^ allDetectedPixelCoordinates
- "Modified: / 29-07-1998 / 03:09:16 / cg"
- "Modified (format): / 30-01-2017 / 20:57:36 / stefan"
+ "Created: / 17-02-2017 / 15:03:33 / cg"
!
rectangle: aRectangle withColor:aColor