Merge pull request #27 from guzba/master

faster fill, invert, draw
This commit is contained in:
treeform 2020-12-08 19:57:14 -08:00 committed by GitHub
commit 21f4560092
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 270 additions and 315 deletions

View file

@ -10,3 +10,4 @@ requires "vmath >= 0.3.3"
requires "chroma >= 0.1.5" requires "chroma >= 0.1.5"
requires "zippy >= 0.3.5" requires "zippy >= 0.3.5"
requires "flatty >= 0.1.2" requires "flatty >= 0.1.2"
requires "nimsimd >= 0.4.6"

View file

@ -1,10 +1,11 @@
## Blending modes. ## Blending modes.
import chroma, math, algorithm import chroma, math
# See https://www.w3.org/TR/compositing-1/ # See https://www.w3.org/TR/compositing-1/
# See https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_blend_equation_advanced.txt # See https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_blend_equation_advanced.txt
type BlendMode* = enum type
BlendMode* = enum
bmNormal bmNormal
bmDarken bmDarken
bmMultiply bmMultiply
@ -30,6 +31,8 @@ type BlendMode* = enum
bmIntersectMask bmIntersectMask
bmExcludeMask bmExcludeMask
Mixer* = proc(a, b: ColorRGBA): ColorRGBA
proc `+`*(a, b: Color): Color {.inline.} = proc `+`*(a, b: Color): Color {.inline.} =
result.r = a.r + b.r result.r = a.r + b.r
result.g = a.g + b.g result.g = a.g + b.g
@ -123,202 +126,176 @@ proc alphaFix(Cb, Cs, mixed: Color): Color {.inline.} =
result.g /= result.a result.g /= result.a
result.b /= result.a result.b /= result.a
proc blendDarken(Cb, Cs: float32): float32 {.inline.} = proc blendDarkenFloat(Cb, Cs: float32): float32 {.inline.} =
min(Cb, Cs) min(Cb, Cs)
proc blendMultiply(Cb, Cs: float32): float32 {.inline.} = proc blendMultiplyFloat(Cb, Cs: float32): float32 {.inline.} =
Cb * Cs Cb * Cs
proc blendLinearBurn(Cb, Cs: float32): float32 {.inline.} = proc blendLinearBurnFloat(Cb, Cs: float32): float32 {.inline.} =
Cb + Cs - 1 Cb + Cs - 1
proc blendColorBurn(Cb, Cs: float32): float32 {.inline.} = proc blendColorBurnFloat(Cb, Cs: float32): float32 {.inline.} =
if Cb == 1: 1.0 if Cb == 1: 1.0
elif Cs == 0: 0.0 elif Cs == 0: 0.0
else: 1.0 - min(1, (1 - Cb) / Cs) else: 1.0 - min(1, (1 - Cb) / Cs)
proc blendLighten(Cb, Cs: float32): float32 {.inline.} = proc blendLightenFloat(Cb, Cs: float32): float32 {.inline.} =
max(Cb, Cs) max(Cb, Cs)
proc blendScreen(Cb, Cs: float32): float32 {.inline.} = proc blendScreenFloat(Cb, Cs: float32): float32 {.inline.} =
screen(Cb, Cs) screen(Cb, Cs)
proc blendLinearDodge(Cb, Cs: float32): float32 {.inline.} = proc blendLinearDodgeFloat(Cb, Cs: float32): float32 {.inline.} =
Cb + Cs Cb + Cs
proc blendColorDodge(Cb, Cs: float32): float32 {.inline.} = proc blendColorDodgeFloat(Cb, Cs: float32): float32 {.inline.} =
if Cb == 0: 0.0 if Cb == 0: 0.0
elif Cs == 1: 1.0 elif Cs == 1: 1.0
else: min(1, Cb / (1 - Cs)) else: min(1, Cb / (1 - Cs))
proc blendOverlay(Cb, Cs: float32): float32 {.inline.} = proc blendOverlayFloat(Cb, Cs: float32): float32 {.inline.} =
hardLight(Cs, Cb) hardLight(Cs, Cb)
proc blendHardLight(Cb, Cs: float32): float32 {.inline.} = proc blendHardLightFloat(Cb, Cs: float32): float32 {.inline.} =
hardLight(Cb, Cs) hardLight(Cb, Cs)
proc blendSoftLight(Cb, Cs: float32): float32 {.inline.} = proc blendSoftLightFloat(Cb, Cs: float32): float32 {.inline.} =
softLight(Cb, Cs) softLight(Cb, Cs)
proc blendDifference(Cb, Cs: float32): float32 {.inline.} = proc blendDifferenceFloat(Cb, Cs: float32): float32 {.inline.} =
abs(Cb - Cs) abs(Cb - Cs)
proc blendExclusion(Cb, Cs: float32): float32 {.inline.} = proc blendExclusionFloat(Cb, Cs: float32): float32 {.inline.} =
Cb + Cs - 2 * Cb * Cs Cb + Cs - 2 * Cb * Cs
proc blendNormal*(Cb, Cs: Color): Color {.inline.} = proc blendNormalFloats(Cb, Cs: Color): Color {.inline.} =
result.r = Cs.r result.r = Cs.r
result.g = Cs.g result.g = Cs.g
result.b = Cs.b result.b = Cs.b
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendDarken(Cb, Cs: Color): Color {.inline.} = proc blendDarkenFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendDarken(Cb.r, Cs.r) result.r = blendDarkenFloat(Cb.r, Cs.r)
result.g = blendDarken(Cb.g, Cs.g) result.g = blendDarkenFloat(Cb.g, Cs.g)
result.b = blendDarken(Cb.b, Cs.b) result.b = blendDarkenFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendMultiply(Cb, Cs: Color): Color {.inline.} = proc blendMultiplyFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendMultiply(Cb.r, Cs.r) result.r = blendMultiplyFloat(Cb.r, Cs.r)
result.g = blendMultiply(Cb.g, Cs.g) result.g = blendMultiplyFloat(Cb.g, Cs.g)
result.b = blendMultiply(Cb.b, Cs.b) result.b = blendMultiplyFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendLinearBurn(Cb, Cs: Color): Color {.inline.} = proc blendLinearBurnFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendLinearBurn(Cb.r, Cs.r) result.r = blendLinearBurnFloat(Cb.r, Cs.r)
result.g = blendLinearBurn(Cb.g, Cs.g) result.g = blendLinearBurnFloat(Cb.g, Cs.g)
result.b = blendLinearBurn(Cb.b, Cs.b) result.b = blendLinearBurnFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendColorBurn(Cb, Cs: Color): Color {.inline.} = proc blendColorBurnFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendColorBurn(Cb.r, Cs.r) result.r = blendColorBurnFloat(Cb.r, Cs.r)
result.g = blendColorBurn(Cb.g, Cs.g) result.g = blendColorBurnFloat(Cb.g, Cs.g)
result.b = blendColorBurn(Cb.b, Cs.b) result.b = blendColorBurnFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendLighten(Cb, Cs: Color): Color {.inline.} = proc blendLightenFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendLighten(Cb.r, Cs.r) result.r = blendLightenFloat(Cb.r, Cs.r)
result.g = blendLighten(Cb.g, Cs.g) result.g = blendLightenFloat(Cb.g, Cs.g)
result.b = blendLighten(Cb.b, Cs.b) result.b = blendLightenFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendScreen(Cb, Cs: Color): Color {.inline.} = proc blendScreenFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendScreen(Cb.r, Cs.r) result.r = blendScreenFloat(Cb.r, Cs.r)
result.g = blendScreen(Cb.g, Cs.g) result.g = blendScreenFloat(Cb.g, Cs.g)
result.b = blendScreen(Cb.b, Cs.b) result.b = blendScreenFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendLinearDodge(Cb, Cs: Color): Color {.inline.} = proc blendLinearDodgeFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendLinearDodge(Cb.r, Cs.r) result.r = blendLinearDodgeFloat(Cb.r, Cs.r)
result.g = blendLinearDodge(Cb.g, Cs.g) result.g = blendLinearDodgeFloat(Cb.g, Cs.g)
result.b = blendLinearDodge(Cb.b, Cs.b) result.b = blendLinearDodgeFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendColorDodge(Cb, Cs: Color): Color {.inline.} = proc blendColorDodgeFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendColorDodge(Cb.r, Cs.r) result.r = blendColorDodgeFloat(Cb.r, Cs.r)
result.g = blendColorDodge(Cb.g, Cs.g) result.g = blendColorDodgeFloat(Cb.g, Cs.g)
result.b = blendColorDodge(Cb.b, Cs.b) result.b = blendColorDodgeFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendOverlay(Cb, Cs: Color): Color {.inline.} = proc blendOverlayFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendOverlay(Cb.r, Cs.r) result.r = blendOverlayFloat(Cb.r, Cs.r)
result.g = blendOverlay(Cb.g, Cs.g) result.g = blendOverlayFloat(Cb.g, Cs.g)
result.b = blendOverlay(Cb.b, Cs.b) result.b = blendOverlayFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendHardLight(Cb, Cs: Color): Color {.inline.} = proc blendHardLightFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendHardLight(Cb.r, Cs.r) result.r = blendHardLightFloat(Cb.r, Cs.r)
result.g = blendHardLight(Cb.g, Cs.g) result.g = blendHardLightFloat(Cb.g, Cs.g)
result.b = blendHardLight(Cb.b, Cs.b) result.b = blendHardLightFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendSoftLight(Cb, Cs: Color): Color {.inline.} = proc blendSoftLightFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendSoftLight(Cb.r, Cs.r) result.r = blendSoftLightFloat(Cb.r, Cs.r)
result.g = blendSoftLight(Cb.g, Cs.g) result.g = blendSoftLightFloat(Cb.g, Cs.g)
result.b = blendSoftLight(Cb.b, Cs.b) result.b = blendSoftLightFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendDifference(Cb, Cs: Color): Color {.inline.} = proc blendDifferenceFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendDifference(Cb.r, Cs.r) result.r = blendDifferenceFloat(Cb.r, Cs.r)
result.g = blendDifference(Cb.g, Cs.g) result.g = blendDifferenceFloat(Cb.g, Cs.g)
result.b = blendDifference(Cb.b, Cs.b) result.b = blendDifferenceFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendExclusion(Cb, Cs: Color): Color {.inline.} = proc blendExclusionFloats(Cb, Cs: Color): Color {.inline.} =
result.r = blendExclusion(Cb.r, Cs.r) result.r = blendExclusionFloat(Cb.r, Cs.r)
result.g = blendExclusion(Cb.g, Cs.g) result.g = blendExclusionFloat(Cb.g, Cs.g)
result.b = blendExclusion(Cb.b, Cs.b) result.b = blendExclusionFloat(Cb.b, Cs.b)
result = alphaFix(Cb, Cs, result) result = alphaFix(Cb, Cs, result)
proc blendColor(Cb, Cs: Color): Color {.inline.} = proc blendColorFloats(Cb, Cs: Color): Color {.inline.} =
let mixed = SetLum(Cs, Lum(Cb)) let mixed = SetLum(Cs, Lum(Cb))
alphaFix(Cb, Cs, mixed) alphaFix(Cb, Cs, mixed)
proc blendLuminosity(Cb, Cs: Color): Color {.inline.} = proc blendLuminosityFloats(Cb, Cs: Color): Color {.inline.} =
let mixed = SetLum(Cb, Lum(Cs)) let mixed = SetLum(Cb, Lum(Cs))
alphaFix(Cb, Cs, mixed) alphaFix(Cb, Cs, mixed)
proc blendHue(Cb, Cs: Color): Color {.inline.} = proc blendHueFloats(Cb, Cs: Color): Color {.inline.} =
let mixed = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb)) let mixed = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
alphaFix(Cb, Cs, mixed) alphaFix(Cb, Cs, mixed)
proc blendSaturation(Cb, Cs: Color): Color {.inline.} = proc blendSaturationFloats(Cb, Cs: Color): Color {.inline.} =
let mixed = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb)) let mixed = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
alphaFix(Cb, Cs, mixed) alphaFix(Cb, Cs, mixed)
proc blendMask(target, blend: Color): Color {.inline.} = proc blendMaskFloats(target, blend: Color): Color {.inline.} =
result.r = target.r result.r = target.r
result.g = target.g result.g = target.g
result.b = target.b result.b = target.b
result.a = min(target.a, blend.a) result.a = min(target.a, blend.a)
proc blendSubtractMask(target, blend: Color): Color {.inline.} = proc blendSubtractMaskFloats(target, blend: Color): Color {.inline.} =
result.r = target.r result.r = target.r
result.g = target.g result.g = target.g
result.b = target.b result.b = target.b
result.a = target.a * (1 - blend.a) result.a = target.a * (1 - blend.a)
proc blendIntersectMask(target, blend: Color): Color {.inline.} = proc blendIntersectMaskFloats(target, blend: Color): Color {.inline.} =
result.r = target.r result.r = target.r
result.g = target.g result.g = target.g
result.b = target.b result.b = target.b
result.a = target.a * blend.a result.a = target.a * blend.a
proc blendExcludeMask(target, blend: Color): Color {.inline.} = proc blendExcludeMaskFloats(target, blend: Color): Color {.inline.} =
result.r = target.r result.r = target.r
result.g = target.g result.g = target.g
result.b = target.b result.b = target.b
result.a = abs(target.a - blend.a) result.a = abs(target.a - blend.a)
proc blendOverwrite(target, blend: Color): Color {.inline.} = proc blendOverwriteFloats(target, blend: Color): Color {.inline.} =
result = blend result = blend
proc mix*(blendMode: BlendMode, dest, src: Color): Color {.inline.} =
case blendMode
of bmNormal: blendNormal(dest, src)
of bmDarken: blendDarken(dest, src)
of bmMultiply: blendMultiply(dest, src)
of bmLinearBurn: blendLinearBurn(dest, src)
of bmColorBurn: blendColorBurn(dest, src)
of bmLighten: blendLighten(dest, src)
of bmScreen: blendScreen(dest, src)
of bmLinearDodge: blendLinearDodge(dest, src)
of bmColorDodge: blendColorDodge(dest, src)
of bmOverlay: blendOverlay(dest, src)
of bmSoftLight: blendSoftLight(dest, src)
of bmHardLight: blendHardLight(dest, src)
of bmDifference: blendDifference(dest, src)
of bmExclusion: blendExclusion(dest, src)
of bmHue: blendHue(dest, src)
of bmSaturation: blendSaturation(dest, src)
of bmColor: blendColor(dest, src)
of bmLuminosity: blendLuminosity(dest, src)
of bmMask: blendMask(dest, src)
of bmOverwrite: blendOverwrite(dest, src)
of bmSubtractMask: blendSubtractMask(dest, src)
of bmIntersectMask: blendIntersectMask(dest, src)
of bmExcludeMask: blendExcludeMask(dest, src)
proc alphaFix(Cb, Cs, mixed: ColorRGBA): ColorRGBA {.inline.} = proc alphaFix(Cb, Cs, mixed: ColorRGBA): ColorRGBA {.inline.} =
let ab = Cb.a.int32 let ab = Cb.a.int32
let As = Cs.a.int32 let As = Cs.a.int32
@ -335,129 +312,103 @@ proc alphaFix(Cb, Cs, mixed: ColorRGBA): ColorRGBA {.inline.} =
result.b = (b div a div 255).uint8 result.b = (b div a div 255).uint8
result.a = a.uint8 result.a = a.uint8
proc blendNormal*(a, b: ColorRGBA): ColorRGBA = proc blendNormal(a, b: ColorRGBA): ColorRGBA =
# blendNormal(a.color, b.color).rgba
result.r = b.r result.r = b.r
result.g = b.g result.g = b.g
result.b = b.b result.b = b.b
result = alphaFix(a, b, result) result = alphaFix(a, b, result)
proc blendDarken*(a, b: ColorRGBA): ColorRGBA = proc blendDarken(a, b: ColorRGBA): ColorRGBA =
blendDarken(a.color, b.color).rgba blendDarkenFloats(a.color, b.color).rgba
proc blendMultiply*(a, b: ColorRGBA): ColorRGBA = proc blendMultiply(a, b: ColorRGBA): ColorRGBA =
blendMultiply(a.color, b.color).rgba blendMultiplyFloats(a.color, b.color).rgba
proc blendLinearBurn*(a, b: ColorRGBA): ColorRGBA = proc blendLinearBurn(a, b: ColorRGBA): ColorRGBA =
blendLinearBurn(a.color, b.color).rgba blendLinearBurnFloats(a.color, b.color).rgba
proc blendColorBurn*(a, b: ColorRGBA): ColorRGBA = proc blendColorBurn(a, b: ColorRGBA): ColorRGBA =
blendColorBurn(a.color, b.color).rgba blendColorBurnFloats(a.color, b.color).rgba
proc blendLighten*(a, b: ColorRGBA): ColorRGBA = proc blendLighten(a, b: ColorRGBA): ColorRGBA =
blendLighten(a.color, b.color).rgba blendLightenFloats(a.color, b.color).rgba
proc blendScreen*(a, b: ColorRGBA): ColorRGBA = proc blendScreen(a, b: ColorRGBA): ColorRGBA =
blendScreen(a.color, b.color).rgba blendScreenFloats(a.color, b.color).rgba
proc blendLinearDodge*(a, b: ColorRGBA): ColorRGBA = proc blendLinearDodge(a, b: ColorRGBA): ColorRGBA =
blendLinearDodge(a.color, b.color).rgba blendLinearDodgeFloats(a.color, b.color).rgba
proc blendColorDodge*(a, b: ColorRGBA): ColorRGBA = proc blendColorDodge(a, b: ColorRGBA): ColorRGBA =
blendColorDodge(a.color, b.color).rgba blendColorDodgeFloats(a.color, b.color).rgba
proc blendOverlay*(a, b: ColorRGBA): ColorRGBA = proc blendOverlay(a, b: ColorRGBA): ColorRGBA =
blendOverlay(a.color, b.color).rgba blendOverlayFloats(a.color, b.color).rgba
proc blendHardLight*(a, b: ColorRGBA): ColorRGBA = proc blendHardLight(a, b: ColorRGBA): ColorRGBA =
blendHardLight(a.color, b.color).rgba blendHardLightFloats(a.color, b.color).rgba
proc blendSoftLight*(a, b: ColorRGBA): ColorRGBA = proc blendSoftLight(a, b: ColorRGBA): ColorRGBA =
blendSoftLight(a.color, b.color).rgba blendSoftLightFloats(a.color, b.color).rgba
proc blendDifference*(a, b: ColorRGBA): ColorRGBA = proc blendDifference(a, b: ColorRGBA): ColorRGBA =
blendDifference(a.color, b.color).rgba blendDifferenceFloats(a.color, b.color).rgba
proc blendExclusion*(a, b: ColorRGBA): ColorRGBA = proc blendExclusion(a, b: ColorRGBA): ColorRGBA =
blendExclusion(a.color, b.color).rgba blendExclusionFloats(a.color, b.color).rgba
proc blendColor*(a, b: ColorRGBA): ColorRGBA = proc blendColor(a, b: ColorRGBA): ColorRGBA =
blendColor(a.color, b.color).rgba blendColorFloats(a.color, b.color).rgba
proc blendLuminosity*(a, b: ColorRGBA): ColorRGBA = proc blendLuminosity(a, b: ColorRGBA): ColorRGBA =
blendLuminosity(a.color, b.color).rgba blendLuminosityFloats(a.color, b.color).rgba
proc blendHue*(a, b: ColorRGBA): ColorRGBA = proc blendHue(a, b: ColorRGBA): ColorRGBA =
blendHue(a.color, b.color).rgba blendHueFloats(a.color, b.color).rgba
proc blendSaturation*(a, b: ColorRGBA): ColorRGBA = proc blendSaturation(a, b: ColorRGBA): ColorRGBA =
blendSaturation(a.color, b.color).rgba blendSaturationFloats(a.color, b.color).rgba
proc blendMask*(a, b: ColorRGBA): ColorRGBA = proc blendMask(a, b: ColorRGBA): ColorRGBA =
result.r = a.r result.r = a.r
result.g = a.g result.g = a.g
result.b = a.b result.b = a.b
result.a = min(a.a, b.a) result.a = min(a.a, b.a)
proc blendSubtractMask*(a, b: ColorRGBA): ColorRGBA = proc blendSubtractMask(a, b: ColorRGBA): ColorRGBA =
blendSubtractMask(a.color, b.color).rgba blendSubtractMaskFloats(a.color, b.color).rgba
proc blendIntersectMask*(a, b: ColorRGBA): ColorRGBA = proc blendIntersectMask(a, b: ColorRGBA): ColorRGBA =
blendIntersectMask(a.color, b.color).rgba blendIntersectMaskFloats(a.color, b.color).rgba
proc blendExcludeMask*(a, b: ColorRGBA): ColorRGBA = proc blendExcludeMask(a, b: ColorRGBA): ColorRGBA =
blendExcludeMask(a.color, b.color).rgba blendExcludeMaskFloats(a.color, b.color).rgba
proc blendOverwrite*(a, b: ColorRGBA): ColorRGBA = proc blendOverwrite(a, b: ColorRGBA): ColorRGBA =
blendOverwrite(a.color, b.color).rgba blendOverwriteFloats(a.color, b.color).rgba
proc mix*(blendMode: BlendMode, dest, src: ColorRGBA): ColorRGBA {.inline.} = proc mixer*(blendMode: BlendMode): Mixer =
case blendMode case blendMode
of bmNormal: blendNormal(dest, src) of bmNormal: blendNormal
of bmDarken: blendDarken(dest, src) of bmDarken: blendDarken
of bmMultiply: blendMultiply(dest, src) of bmMultiply: blendMultiply
of bmLinearBurn: blendLinearBurn(dest, src) of bmLinearBurn: blendLinearBurn
of bmColorBurn: blendColorBurn(dest, src) of bmColorBurn: blendColorBurn
of bmLighten: blendLighten(dest, src) of bmLighten: blendLighten
of bmScreen: blendScreen(dest, src) of bmScreen: blendScreen
of bmLinearDodge: blendLinearDodge(dest, src) of bmLinearDodge: blendLinearDodge
of bmColorDodge: blendColorDodge(dest, src) of bmColorDodge: blendColorDodge
of bmOverlay: blendOverlay(dest, src) of bmOverlay: blendOverlay
of bmSoftLight: blendSoftLight(dest, src) of bmSoftLight: blendSoftLight
of bmHardLight: blendHardLight(dest, src) of bmHardLight: blendHardLight
of bmDifference: blendDifference(dest, src) of bmDifference: blendDifference
of bmExclusion: blendExclusion(dest, src) of bmExclusion: blendExclusion
of bmHue: blendHue(dest, src) of bmHue: blendHue
of bmSaturation: blendSaturation(dest, src) of bmSaturation: blendSaturation
of bmColor: blendColor(dest, src) of bmColor: blendColor
of bmLuminosity: blendLuminosity(dest, src) of bmLuminosity: blendLuminosity
of bmMask: blendMask(dest, src) of bmMask: blendMask
of bmOverwrite: blendOverwrite(dest, src) of bmOverwrite: blendOverwrite
of bmSubtractMask: blendSubtractMask(dest, src) of bmSubtractMask: blendSubtractMask
of bmIntersectMask: blendIntersectMask(dest, src) of bmIntersectMask: blendIntersectMask
of bmExcludeMask: blendExcludeMask(dest, src) of bmExcludeMask: blendExcludeMask
proc mixStatic*(blendMode: static[BlendMode], dest, src: ColorRGBA): ColorRGBA {.inline.} =
when blendMOde == bmNormal: blendNormal(dest, src)
elif blendMOde == bmDarken: blendDarken(dest, src)
elif blendMOde == bmMultiply: blendMultiply(dest, src)
elif blendMOde == bmLinearBurn: blendLinearBurn(dest, src)
elif blendMOde == bmColorBurn: blendColorBurn(dest, src)
elif blendMOde == bmLighten: blendLighten(dest, src)
elif blendMOde == bmScreen: blendScreen(dest, src)
elif blendMOde == bmLinearDodge: blendLinearDodge(dest, src)
elif blendMOde == bmColorDodge: blendColorDodge(dest, src)
elif blendMOde == bmOverlay: blendOverlay(dest, src)
elif blendMOde == bmSoftLight: blendSoftLight(dest, src)
elif blendMOde == bmHardLight: blendHardLight(dest, src)
elif blendMOde == bmDifference: blendDifference(dest, src)
elif blendMOde == bmExclusion: blendExclusion(dest, src)
elif blendMOde == bmHue: blendHue(dest, src)
elif blendMOde == bmSaturation: blendSaturation(dest, src)
elif blendMOde == bmColor: blendColor(dest, src)
elif blendMOde == bmLuminosity: blendLuminosity(dest, src)
elif blendMOde == bmMask: blendMask(dest, src)
elif blendMOde == bmOverwrite: blendOverwrite(dest, src)
elif blendMOde == bmSubtractMask: blendSubtractMask(dest, src)
elif blendMOde == bmIntersectMask: blendIntersectMask(dest, src)
elif blendMOde == bmExcludeMask: blendExcludeMask(dest, src)

View file

@ -1,4 +1,4 @@
import chroma, blends, vmath, common, system/memory import chroma, blends, vmath, common, nimsimd/sse2
type type
Image* = ref object Image* = ref object
@ -70,19 +70,31 @@ proc `[]=`*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} =
proc fill*(image: Image, rgba: ColorRgba) = proc fill*(image: Image, rgba: ColorRgba) =
## Fills the image with a solid color. ## Fills the image with a solid color.
if rgba.r == rgba.g and rgba.r == rgba.b and rgba.r == rgba.a: # SIMD fill until we run out of room.
nimSetMem(image.data[0].addr, rgba.r.cint, image.data.len * 4) let m = mm_set1_epi32(cast[int32](rgba))
else: var i: int
for c in image.data.mitems: while i < image.data.len - 4:
c = rgba mm_store_si128(image.data[i].addr, m)
i += 4
for j in i ..< image.data.len:
image.data[j] = rgba
proc invert*(image: Image) = proc invert*(image: Image) =
## Inverts all of the colors and alpha. ## Inverts all of the colors and alpha.
for rgba in image.data.mitems: let vec255 = mm_set1_epi8(255)
var i: int
while i < image.data.len - 4:
var m = mm_loadu_si128(image.data[i].addr)
m = mm_sub_epi8(vec255, m)
mm_store_si128(image.data[i].addr, m)
i += 4
for j in i ..< image.data.len:
var rgba = image.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
image.data[j] = rgba
proc subImage*(image: Image, x, y, w, h: int): Image = proc subImage*(image: Image, x, y, w, h: int): Image =
## Gets a sub image of the main image. ## Gets a sub image of the main image.
@ -394,13 +406,14 @@ proc drawCorrect*(a, b: Image, mat: Mat3, blendMode: BlendMode) =
minFilterBy2 /= 2 minFilterBy2 /= 2
matInv = matInv * scale(vec2(0.5, 0.5)) matInv = matInv * scale(vec2(0.5, 0.5))
let mixer = blendMode.mixer()
for y in 0 ..< a.height: for y in 0 ..< a.height:
for x in 0 ..< a.width: for x in 0 ..< a.width:
let srcPos = matInv * vec2(x.float32 + h, y.float32 + h) let
var rgba = a.getRgbaUnsafe(x, y) srcPos = matInv * vec2(x.float32 + h, y.float32 + h)
let rgba2 = b.getRgbaSmooth(srcPos.x - h, srcPos.y - h) rgba = a.getRgbaUnsafe(x, y)
rgba = blendMode.mix(rgba, rgba2) rgba2 = b.getRgbaSmooth(srcPos.x - h, srcPos.y - h)
a.setRgbaUnsafe(x, y, rgba) a.setRgbaUnsafe(x, y, mixer(rgba, rgba2))
const h = 0.5.float32 const h = 0.5.float32
@ -408,9 +421,10 @@ proc drawUber(
a, b: Image, a, b: Image,
p, dx, dy: Vec2, p, dx, dy: Vec2,
lines: array[0..3, Segment], lines: array[0..3, Segment],
blendMode: static[BlendMode], blendMode: BlendMode,
smooth: static[bool] smooth: bool
) = ) =
let mixer = blendMode.mixer()
for y in 0 ..< a.height: for y in 0 ..< a.height:
var var
xMin = 0 xMin = 0
@ -435,25 +449,24 @@ proc drawUber(
xMin = xMin.clamp(0, a.width) xMin = xMin.clamp(0, a.width)
xMax = xMax.clamp(0, a.width) xMax = xMax.clamp(0, a.width)
when blendMode == bmIntersectMask: if blendMode == bmIntersectMask:
if xMin > 0: if xMin > 0:
zeroMem(a.getAddr(0, y), 4 * xMin) zeroMem(a.getAddr(0, y), 4 * xMin)
for x in xMin ..< xMax: for x in xMin ..< xMax:
let srcPos = p + dx * float32(x) + dy * float32(y)
let let
srcPos = p + dx * float32(x) + dy * float32(y)
xFloat = srcPos.x - h xFloat = srcPos.x - h
yFloat = srcPos.y - h yFloat = srcPos.y - h
var rgba = a.getRgbaUnsafe(x, y) rgba = a.getRgbaUnsafe(x, y)
var rgba2 = rgba2 =
when smooth: if smooth:
b.getRgbaSmooth(xFloat, yFloat) b.getRgbaSmooth(xFloat, yFloat)
else: else:
b.getRgbaUnsafe(xFloat.round.int, yFloat.round.int) b.getRgbaUnsafe(xFloat.round.int, yFloat.round.int)
rgba = blendMode.mixStatic(rgba, rgba2) a.setRgbaUnsafe(x, y, mixer(rgba, rgba2))
a.setRgbaUnsafe(x, y, rgba)
when blendMode == bmIntersectMask: if blendMode == bmIntersectMask:
if a.width - xMax > 0: if a.width - xMax > 0:
zeroMem(a.getAddr(xMax, y), 4 * (a.width - xMax)) zeroMem(a.getAddr(xMax, y), 4 * (a.width - xMax))
@ -494,56 +507,7 @@ proc draw*(a, b: Image, mat: Mat3, blendMode: BlendMode) =
let smooth = not(dx.length == 1.0 and dy.length == 1.0 and let smooth = not(dx.length == 1.0 and dy.length == 1.0 and
mat[2, 0].fractional == 0.0 and mat[2, 1].fractional == 0.0) mat[2, 0].fractional == 0.0 and mat[2, 1].fractional == 0.0)
if not smooth: a.drawUber(b, p, dx, dy, lines, blendMode, smooth)
case blendMode
of bmNormal: a.drawUber(b, p, dx, dy, lines, bmNormal, false)
of bmDarken: a.drawUber(b, p, dx, dy, lines, bmDarken, false)
of bmMultiply: a.drawUber(b, p, dx, dy, lines, bmMultiply, false)
of bmLinearBurn: a.drawUber(b, p, dx, dy, lines, bmLinearBurn, false)
of bmColorBurn: a.drawUber(b, p, dx, dy, lines, bmColorBurn, false)
of bmLighten: a.drawUber(b, p, dx, dy, lines, bmLighten, false)
of bmScreen: a.drawUber(b, p, dx, dy, lines, bmScreen, false)
of bmLinearDodge: a.drawUber(b, p, dx, dy, lines, bmLinearDodge, false)
of bmColorDodge: a.drawUber(b, p, dx, dy, lines, bmColorDodge, false)
of bmOverlay: a.drawUber(b, p, dx, dy, lines, bmOverlay, false)
of bmSoftLight: a.drawUber(b, p, dx, dy, lines, bmSoftLight, false)
of bmHardLight: a.drawUber(b, p, dx, dy, lines, bmHardLight, false)
of bmDifference: a.drawUber(b, p, dx, dy, lines, bmDifference, false)
of bmExclusion: a.drawUber(b, p, dx, dy, lines, bmExclusion, false)
of bmHue: a.drawUber(b, p, dx, dy, lines, bmHue, false)
of bmSaturation: a.drawUber(b, p, dx, dy, lines, bmSaturation, false)
of bmColor: a.drawUber(b, p, dx, dy, lines, bmColor, false)
of bmLuminosity: a.drawUber(b, p, dx, dy, lines, bmLuminosity, false)
of bmMask: a.drawUber(b, p, dx, dy, lines, bmMask, false)
of bmOverwrite: a.drawUber(b, p, dx, dy, lines, bmOverwrite, false)
of bmSubtractMask: a.drawUber(b, p, dx, dy, lines, bmSubtractMask, false)
of bmIntersectMask: a.drawUber(b, p, dx, dy, lines, bmIntersectMask, false)
of bmExcludeMask: a.drawUber(b, p, dx, dy, lines, bmExcludeMask, false)
else:
case blendMode
of bmNormal: a.drawUber(b, p, dx, dy, lines, bmNormal, true)
of bmDarken: a.drawUber(b, p, dx, dy, lines, bmDarken, true)
of bmMultiply: a.drawUber(b, p, dx, dy, lines, bmMultiply, true)
of bmLinearBurn: a.drawUber(b, p, dx, dy, lines, bmLinearBurn, true)
of bmColorBurn: a.drawUber(b, p, dx, dy, lines, bmColorBurn, true)
of bmLighten: a.drawUber(b, p, dx, dy, lines, bmLighten, true)
of bmScreen: a.drawUber(b, p, dx, dy, lines, bmScreen, true)
of bmLinearDodge: a.drawUber(b, p, dx, dy, lines, bmLinearDodge, true)
of bmColorDodge: a.drawUber(b, p, dx, dy, lines, bmColorDodge, true)
of bmOverlay: a.drawUber(b, p, dx, dy, lines, bmOverlay, true)
of bmSoftLight: a.drawUber(b, p, dx, dy, lines, bmSoftLight, true)
of bmHardLight: a.drawUber(b, p, dx, dy, lines, bmHardLight, true)
of bmDifference: a.drawUber(b, p, dx, dy, lines, bmDifference, true)
of bmExclusion: a.drawUber(b, p, dx, dy, lines, bmExclusion, true)
of bmHue: a.drawUber(b, p, dx, dy, lines, bmHue, true)
of bmSaturation: a.drawUber(b, p, dx, dy, lines, bmSaturation, true)
of bmColor: a.drawUber(b, p, dx, dy, lines, bmColor, true)
of bmLuminosity: a.drawUber(b, p, dx, dy, lines, bmLuminosity, true)
of bmMask: a.drawUber(b, p, dx, dy, lines, bmMask, true)
of bmOverwrite: a.drawUber(b, p, dx, dy, lines, bmOverwrite, true)
of bmSubtractMask: a.drawUber(b, p, dx, dy, lines, bmSubtractMask, true)
of bmIntersectMask: a.drawUber(b, p, dx, dy, lines, bmIntersectMask, true)
of bmExcludeMask: a.drawUber(b, p, dx, dy, lines, bmExcludeMask, true)
proc draw*(a, b: Image, pos = vec2(0, 0), blendMode = bmNormal) {.inline.} = proc draw*(a, b: Image, pos = vec2(0, 0), blendMode = bmNormal) {.inline.} =
a.draw(b, translate(pos), blendMode) a.draw(b, translate(pos), blendMode)

View file

@ -1,11 +1,19 @@
import chroma, pixie, benchy import chroma, pixie, benchy, system/memory
proc fillOriginal(a: Image, rgba: ColorRGBA) = proc fill1(a: Image, rgba: ColorRGBA) =
for y in 0 ..< a.height: for y in 0 ..< a.height:
for x in 0 ..< a.width: for x in 0 ..< a.width:
a.setRgbaUnsafe(x, y, rgba) a.setRgbaUnsafe(x, y, rgba)
proc invertOriginal(a: Image) = proc fill2*(image: Image, rgba: ColorRgba) =
## Fills the image with a solid color.
if rgba.r == rgba.g and rgba.r == rgba.b and rgba.r == rgba.a:
nimSetMem(image.data[0].addr, rgba.r.cint, image.data.len * 4)
else:
for c in image.data.mitems:
c = rgba
proc invert1(a: Image) =
for y in 0 ..< a.height: for y in 0 ..< a.height:
for x in 0 ..< a.width: for x in 0 ..< a.width:
var rgba = a.getRgbaUnsafe(x, y) var rgba = a.getRgbaUnsafe(x, y)
@ -15,7 +23,14 @@ proc invertOriginal(a: Image) =
rgba.a = 255 - rgba.a rgba.a = 255 - rgba.a
a.setRgbaUnsafe(x, y, rgba) a.setRgbaUnsafe(x, y, rgba)
proc applyOpacityOriginal(a: Image, opacity: float32): Image = proc invert2*(image: Image) =
for rgba in image.data.mitems:
rgba.r = 255 - rgba.r
rgba.g = 255 - rgba.g
rgba.b = 255 - rgba.b
rgba.a = 255 - rgba.a
proc applyOpacity1(a: Image, opacity: float32): Image =
result = newImage(a.width, a.height) result = newImage(a.width, a.height)
let op = (255 * opacity).uint32 let op = (255 * opacity).uint32
for y in 0 ..< a.height: for y in 0 ..< a.height:
@ -24,7 +39,7 @@ proc applyOpacityOriginal(a: Image, opacity: float32): Image =
rgba.a = ((rgba.a.uint32 * op) div 255).clamp(0, 255).uint8 rgba.a = ((rgba.a.uint32 * op) div 255).clamp(0, 255).uint8
result.setRgbaUnsafe(x, y, rgba) result.setRgbaUnsafe(x, y, rgba)
proc sharpOpacityOriginal(a: Image): Image = proc sharpOpacity1(a: Image): Image =
result = newImage(a.width, a.height) result = newImage(a.width, a.height)
for y in 0 ..< a.height: for y in 0 ..< a.height:
for x in 0 ..< a.width: for x in 0 ..< a.width:
@ -36,8 +51,13 @@ proc sharpOpacityOriginal(a: Image): Image =
var a = newImage(2560, 1440) var a = newImage(2560, 1440)
timeIt "fillOriginal": timeIt "fill1":
a.fillOriginal(rgba(255, 255, 255, 255)) a.fill1(rgba(255, 255, 255, 255))
doAssert a[0, 0] == rgba(255, 255, 255, 255)
keep(a)
timeIt "fill2":
a.fill2(rgba(255, 255, 255, 255))
doAssert a[0, 0] == rgba(255, 255, 255, 255) doAssert a[0, 0] == rgba(255, 255, 255, 255)
keep(a) keep(a)
@ -46,24 +66,43 @@ timeIt "fill":
doAssert a[0, 0] == rgba(255, 255, 255, 255) doAssert a[0, 0] == rgba(255, 255, 255, 255)
keep(a) keep(a)
timeIt "invertOriginal": timeIt "fill1_rgba":
a.invertOriginal() a.fill1(rgba(63, 127, 191, 255))
doAssert a[0, 0] == rgba(63, 127, 191, 255)
keep(a)
timeIt "fill2_rgba":
a.fill2(rgba(63, 127, 191, 255))
doAssert a[0, 0] == rgba(63, 127, 191, 255)
keep(a)
timeIt "fill_rgba":
a.fill(rgba(63, 127, 191, 255))
doAssert a[0, 0] == rgba(63, 127, 191, 255)
keep(a)
timeIt "invert1":
a.invert1()
keep(a)
timeIt "invert2":
a.invert2()
keep(a) keep(a)
timeIt "invert": timeIt "invert":
a.invert() a.invert()
keep(a) keep(a)
timeIt "applyOpacityOriginal": timeIt "applyOpacity1":
a = a.applyOpacityOriginal(0.5) a = a.applyOpacity1(0.5)
keep(a) keep(a)
timeIt "applyOpacity": timeIt "applyOpacity":
a.applyOpacity(0.5) a.applyOpacity(0.5)
keep(a) keep(a)
timeIt "sharpOpacityOriginal": timeIt "sharpOpacity1":
a = a.sharpOpacityOriginal() a = a.sharpOpacity1()
keep(a) keep(a)
timeIt "sharpOpacity": timeIt "sharpOpacity":

View file

@ -4,7 +4,7 @@ block:
var c: Image var c: Image
var a = newImage(1000, 1000) var a = newImage(1000, 1000)
a.fill(rgba(255, 0, 0, 255)) a.fill(rgba(255, 0, 0, 255))
var b = newImage(1000, 1000) var b = newImage(500, 500)
b.fill(rgba(0, 255, 0, 255)) b.fill(rgba(0, 255, 0, 255))
timeIt "drawCorrect bmNormal": timeIt "drawCorrect bmNormal":
@ -14,7 +14,7 @@ block:
block: block:
var a = newImage(1000, 1000) var a = newImage(1000, 1000)
a.fill(rgba(255, 0, 0, 255)) a.fill(rgba(255, 0, 0, 255))
var b = newImage(1000, 1000) var b = newImage(500, 500)
b.fill(rgba(0, 255, 0, 255)) b.fill(rgba(0, 255, 0, 255))
timeIt "draw bmNormal": timeIt "draw bmNormal":
@ -24,7 +24,7 @@ block:
block: block:
var a = newImage(1000, 1000) var a = newImage(1000, 1000)
a.fill(rgba(255, 0, 0, 255)) a.fill(rgba(255, 0, 0, 255))
var b = newImage(1000, 1000) var b = newImage(500, 500)
b.fill(rgba(0, 255, 0, 255)) b.fill(rgba(0, 255, 0, 255))
timeIt "draw Smooth bmNormal": timeIt "draw Smooth bmNormal":