diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 008319c..8493a77 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -317,7 +317,7 @@ proc invert*(target: Image | Mask) = for j in i ..< target.data.len: target.data[j] = (255 - target.data[j]).uint8 -proc blur*(image: Image, radius: float32) = +proc blur*(image: Image, radius: float32, outOfBounds = ColorRGBX()) = ## Applies Gaussian blur to the image given a radius. let radius = round(radius).int if radius == 0: @@ -354,18 +354,23 @@ proc blur*(image: Image, radius: float32) = for y in 0 ..< image.height: for x in 0 ..< image.width: var values: array[4, uint32] - if image.inside(x - radius, y) and image.inside(x + radius, y): - for step in -radius .. radius: - let - sample = image.getRgbaUnsafe(x + step, y) - a = lookup[step + radius].uint32 - values += sample * a - else: - for step in -radius .. radius: - let - sample = image[x + step, y] - a = lookup[step + radius].uint32 - values += sample * a + for xx in x - radius ..< min(x + radius, 0): + let + sample = outOfBounds + a = lookup[xx - x + radius].uint32 + values += sample * a + + for xx in max(x - radius, 0) ..< min(x + radius, image.width): + let + sample = image.getRgbaUnsafe(xx, y) + a = lookup[xx - x + radius].uint32 + values += sample * a + + for xx in max(x - radius, image.width) ..< x + radius: + let + sample = outOfBounds + a = lookup[xx - x + radius].uint32 + values += sample * a blurX.setRgbaUnsafe(x, y, values.rgbx()) @@ -373,18 +378,23 @@ proc blur*(image: Image, radius: float32) = for y in 0 ..< image.height: for x in 0 ..< image.width: var values: array[4, uint32] - if image.inside(x, y - radius) and image.inside(x, y + radius): - for step in -radius .. radius: - let - sample = blurX.getRgbaUnsafe(x, y + step) - a = lookup[step + radius].uint32 - values += sample * a - else: - for step in -radius .. radius: - let - sample = blurX[x, y + step] - a = lookup[step + radius].uint32 - values += sample * a + for yy in y - radius ..< min(y + radius, 0): + let + sample = outOfBounds + a = lookup[yy - y + radius].uint32 + values += sample * a + + for yy in max(y - radius, 0) ..< min(y + radius, image.height): + let + sample = blurX.getRgbaUnsafe(x, yy) + a = lookup[yy - y + radius].uint32 + values += sample * a + + for yy in max(y - radius, image.height) ..< y + radius: + let + sample = outOfBounds + a = lookup[yy - y + radius].uint32 + values += sample * a image.setRgbaUnsafe(x, y, values.rgbx()) diff --git a/src/pixie/masks.nim b/src/pixie/masks.nim index 57f14ee..57ca1ef 100644 --- a/src/pixie/masks.nim +++ b/src/pixie/masks.nim @@ -167,18 +167,17 @@ proc blur*(mask: Mask, radius: float32, outOfBounds: uint8 = 0) = for y in 0 ..< mask.height: for x in 0 ..< mask.width: var value: uint32 - if mask.inside(x - radius, y) and mask.inside(x + radius, y): - for step in -radius .. radius: - let sample = mask.getValueUnsafe(x + step, y) - value += sample * lookup[step + radius].uint32 - else: - for step in -radius .. radius: - var sample: uint32 - if mask.inside(x + step, y): - sample = mask.getValueUnsafe(x + step, y) - else: - sample = outOfBounds - value += sample * lookup[step + radius].uint32 + for xx in x - radius ..< min(x + radius, 0): + let sample = outOfBounds + value += sample * lookup[xx - x + radius].uint32 + + for xx in max(x - radius, 0) ..< min(x + radius, mask.width): + let sample = mask.getValueUnsafe(xx, y) + value += sample * lookup[xx - x + radius].uint32 + + for xx in max(x - radius, mask.width) ..< x + radius: + let sample = outOfBounds + value += sample * lookup[xx - x + radius].uint32 blurX.setValueUnsafe(x, y, (value div 1024 div 255).uint8) @@ -186,19 +185,17 @@ proc blur*(mask: Mask, radius: float32, outOfBounds: uint8 = 0) = for y in 0 ..< mask.height: for x in 0 ..< mask.width: var value: uint32 - if mask.inside(x, y - radius) and mask.inside(x, y + radius): - for step in -radius .. radius: - let sample = blurX.getValueUnsafe(x, y + step) - value += sample * lookup[step + radius].uint32 - else: - for step in -radius .. radius: - var sample: uint32 - if blurX.inside(x, y + step): - sample = blurX.getValueUnsafe(x, y + step) - else: - sample = outOfBounds - let a = lookup[step + radius].uint32 - value += sample * a + for yy in y - radius ..< min(y + radius, 0): + let sample = outOfBounds + value += sample * lookup[yy - y + radius].uint32 + + for yy in max(y - radius, 0) ..< min(y + radius, mask.height): + let sample = blurX.getValueUnsafe(x, yy) + value += sample * lookup[yy - y + radius].uint32 + + for yy in max(y - radius, mask.height) ..< y + radius: + let sample = outOfBounds + value += sample * lookup[yy - y + radius].uint32 mask.setValueUnsafe(x, y, (value div 1024 div 255).uint8) diff --git a/tests/images/imageblur20.png b/tests/images/imageblur20.png index 9704d36..828c71e 100644 Binary files a/tests/images/imageblur20.png and b/tests/images/imageblur20.png differ diff --git a/tests/images/imageblur20oob.png b/tests/images/imageblur20oob.png new file mode 100644 index 0000000..f964ef0 Binary files /dev/null and b/tests/images/imageblur20oob.png differ diff --git a/tests/images/maskblur20.png b/tests/images/maskblur20.png index bab2143..e255e76 100644 Binary files a/tests/images/maskblur20.png and b/tests/images/maskblur20.png differ diff --git a/tests/test_images.nim b/tests/test_images.nim index 08fa617..07b83a7 100644 --- a/tests/test_images.nim +++ b/tests/test_images.nim @@ -111,3 +111,10 @@ block: image.fillRect(rect(25, 25, 50, 50), rgba(255, 255, 255, 255)) image.blur(20) image.writeFile("tests/images/imageblur20.png") + +block: + let image = newImage(100, 100) + image.fill(rgba(0, 0, 0, 255)) + image.fillRect(rect(25, 25, 50, 50), rgba(255, 255, 255, 255)) + image.blur(20, rgba(0, 0, 0, 255)) + image.writeFile("tests/images/imageblur20oob.png")