|
@ -434,45 +434,31 @@ proc applyOpacity*(target: Image | Mask, opacity: float32) {.raises: [].} =
|
||||||
for j in i ..< target.data.len:
|
for j in i ..< target.data.len:
|
||||||
target.data[j] = ((target.data[j] * opacity) div 255).uint8
|
target.data[j] = ((target.data[j] * opacity) div 255).uint8
|
||||||
|
|
||||||
proc invert*(target: Image | Mask) {.raises: [].} =
|
proc invert*(target: Image) {.raises: [].} =
|
||||||
## Inverts all of the colors and alpha.
|
## Inverts all of the colors and alpha.
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
let vec255 = mm_set1_epi8(cast[int8](255))
|
let vec255 = mm_set1_epi8(cast[int8](255))
|
||||||
|
let byteLen = target.data.len * 4
|
||||||
when type(target) is Image:
|
|
||||||
let byteLen = target.data.len * 4
|
|
||||||
else:
|
|
||||||
let byteLen = target.data.len
|
|
||||||
|
|
||||||
for _ in 0 ..< byteLen div 16:
|
for _ in 0 ..< byteLen div 16:
|
||||||
when type(target) is Image:
|
let index = i div 4
|
||||||
let index = i div 4
|
|
||||||
else:
|
|
||||||
let index = i
|
|
||||||
|
|
||||||
var values = mm_loadu_si128(target.data[index].addr)
|
var values = mm_loadu_si128(target.data[index].addr)
|
||||||
values = mm_sub_epi8(vec255, values)
|
values = mm_sub_epi8(vec255, values)
|
||||||
mm_storeu_si128(target.data[index].addr, values)
|
mm_storeu_si128(target.data[index].addr, values)
|
||||||
|
|
||||||
i += 16
|
i += 16
|
||||||
|
|
||||||
when type(target) is Image:
|
for j in i div 4 ..< target.data.len:
|
||||||
for j in i div 4 ..< target.data.len:
|
var rgba = target.data[j]
|
||||||
var rgba = target.data[j]
|
rgba.r = 255 - rgba.r
|
||||||
rgba.r = 255 - rgba.r
|
rgba.g = 255 - rgba.g
|
||||||
rgba.g = 255 - rgba.g
|
rgba.b = 255 - rgba.b
|
||||||
rgba.b = 255 - rgba.b
|
rgba.a = 255 - rgba.a
|
||||||
rgba.a = 255 - rgba.a
|
target.data[j] = rgba
|
||||||
target.data[j] = rgba
|
|
||||||
|
|
||||||
# Inverting rgbx(50, 100, 150, 200) becomes rgbx(205, 155, 105, 55). This
|
# Inverting rgbx(50, 100, 150, 200) becomes rgbx(205, 155, 105, 55). This
|
||||||
# is not a valid premultiplied alpha color.
|
# is not a valid premultiplied alpha color.
|
||||||
# We need to convert back to premultiplied alpha after inverting.
|
# We need to convert back to premultiplied alpha after inverting.
|
||||||
target.data.toPremultipliedAlpha()
|
target.data.toPremultipliedAlpha()
|
||||||
else:
|
|
||||||
for j in i ..< target.data.len:
|
|
||||||
target.data[j] = (255 - target.data[j]).uint8
|
|
||||||
|
|
||||||
proc blur*(
|
proc blur*(
|
||||||
image: Image, radius: float32, outOfBounds: SomeColor = color(0, 0, 0, 0)
|
image: Image, radius: float32, outOfBounds: SomeColor = color(0, 0, 0, 0)
|
||||||
|
|
|
@ -234,38 +234,81 @@ proc getValueSmooth*(mask: Mask, x, y: float32): uint8 {.raises: [].} =
|
||||||
else:
|
else:
|
||||||
topMix
|
topMix
|
||||||
|
|
||||||
|
proc invert*(mask: Mask) {.raises: [].} =
|
||||||
|
## Inverts all of the values - creates a negative of the mask.
|
||||||
|
var i: int
|
||||||
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
|
let vec255 = mm_set1_epi8(cast[int8](255))
|
||||||
|
let byteLen = mask.data.len
|
||||||
|
for _ in 0 ..< byteLen div 16:
|
||||||
|
let index = i
|
||||||
|
var values = mm_loadu_si128(mask.data[index].addr)
|
||||||
|
values = mm_sub_epi8(vec255, values)
|
||||||
|
mm_storeu_si128(mask.data[index].addr, values)
|
||||||
|
i += 16
|
||||||
|
|
||||||
|
for j in i ..< mask.data.len:
|
||||||
|
mask.data[j] = (255 - mask.data[j]).uint8
|
||||||
|
|
||||||
proc spread*(mask: Mask, spread: float32) {.raises: [PixieError].} =
|
proc spread*(mask: Mask, spread: float32) {.raises: [PixieError].} =
|
||||||
## Grows the mask by spread.
|
## Grows the mask by spread.
|
||||||
let spread = round(spread).int
|
let spread = round(spread).int
|
||||||
if spread == 0:
|
if spread == 0:
|
||||||
return
|
return
|
||||||
if spread < 0:
|
elif spread > 0:
|
||||||
raise newException(PixieError, "Cannot apply negative spread")
|
|
||||||
|
|
||||||
# Spread in the X direction. Store with dimensions swapped for reading later.
|
# Spread in the X direction. Store with dimensions swapped for reading later.
|
||||||
let spreadX = newMask(mask.height, mask.width)
|
let spreadX = newMask(mask.height, mask.width)
|
||||||
for y in 0 ..< mask.height:
|
for y in 0 ..< mask.height:
|
||||||
for x in 0 ..< mask.width:
|
for x in 0 ..< mask.width:
|
||||||
var maxValue: uint8
|
var maxValue: uint8
|
||||||
for xx in max(x - spread, 0) .. min(x + spread, mask.width - 1):
|
for xx in max(x - spread, 0) .. min(x + spread, mask.width - 1):
|
||||||
let value = mask.unsafe[xx, y]
|
let value = mask.unsafe[xx, y]
|
||||||
if value > maxValue:
|
if value > maxValue:
|
||||||
maxValue = value
|
maxValue = value
|
||||||
if maxValue == 255:
|
if maxValue == 255:
|
||||||
break
|
break
|
||||||
spreadX.unsafe[y, x] = maxValue
|
spreadX.unsafe[y, x] = maxValue
|
||||||
|
|
||||||
# Spread in the Y direction and modify mask.
|
# Spread in the Y direction and modify mask.
|
||||||
for y in 0 ..< mask.height:
|
for y in 0 ..< mask.height:
|
||||||
for x in 0 ..< mask.width:
|
for x in 0 ..< mask.width:
|
||||||
var maxValue: uint8
|
var maxValue: uint8
|
||||||
for yy in max(y - spread, 0) .. min(y + spread, mask.height - 1):
|
for yy in max(y - spread, 0) .. min(y + spread, mask.height - 1):
|
||||||
let value = spreadX.unsafe[yy, x]
|
let value = spreadX.unsafe[yy, x]
|
||||||
if value > maxValue:
|
if value > maxValue:
|
||||||
maxValue = value
|
maxValue = value
|
||||||
if maxValue == 255:
|
if maxValue == 255:
|
||||||
break
|
break
|
||||||
mask.unsafe[x, y] = maxValue
|
mask.unsafe[x, y] = maxValue
|
||||||
|
|
||||||
|
elif spread < 0:
|
||||||
|
|
||||||
|
# Spread in the X direction. Store with dimensions swapped for reading later.
|
||||||
|
let spread = -spread
|
||||||
|
let spreadX = newMask(mask.height, mask.width)
|
||||||
|
for y in 0 ..< mask.height:
|
||||||
|
for x in 0 ..< mask.width:
|
||||||
|
var maxValue: uint8 = 255
|
||||||
|
for xx in max(x - spread, 0) .. min(x + spread, mask.width - 1):
|
||||||
|
let value = mask.unsafe[xx, y]
|
||||||
|
if value < maxValue:
|
||||||
|
maxValue = value
|
||||||
|
if maxValue == 0:
|
||||||
|
break
|
||||||
|
spreadX.unsafe[y, x] = maxValue
|
||||||
|
|
||||||
|
# Spread in the Y direction and modify mask.
|
||||||
|
for y in 0 ..< mask.height:
|
||||||
|
for x in 0 ..< mask.width:
|
||||||
|
var maxValue: uint8 = 255
|
||||||
|
for yy in max(y - spread, 0) .. min(y + spread, mask.height - 1):
|
||||||
|
let value = spreadX.unsafe[yy, x]
|
||||||
|
if value < maxValue:
|
||||||
|
maxValue = value
|
||||||
|
if maxValue == 0:
|
||||||
|
break
|
||||||
|
mask.unsafe[x, y] = maxValue
|
||||||
|
|
||||||
proc ceil*(mask: Mask) {.raises: [].} =
|
proc ceil*(mask: Mask) {.raises: [].} =
|
||||||
## A value of 0 stays 0. Anything else turns into 255.
|
## A value of 0 stays 0. Anything else turns into 255.
|
||||||
|
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 737 B |
BIN
tests/masks/negativeSpread.png
Normal file
After Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -92,6 +92,17 @@ block:
|
||||||
|
|
||||||
a.writeFile("tests/masks/spread.png")
|
a.writeFile("tests/masks/spread.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let path = newPath()
|
||||||
|
path.rect(40, 40, 20, 20)
|
||||||
|
|
||||||
|
let a = newMask(100, 100)
|
||||||
|
a.fillPath(path)
|
||||||
|
|
||||||
|
a.spread(-5)
|
||||||
|
|
||||||
|
a.writeFile("tests/masks/negativeSpread.png")
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let mask = newMask(100, 100)
|
let mask = newMask(100, 100)
|
||||||
|
|
||||||
|
|