Add drawCorrect and drawStepper. And blends.
This commit is contained in:
parent
1c368f7118
commit
58e6950b51
11 changed files with 863 additions and 98 deletions
src/pixie
tests
|
@ -270,3 +270,404 @@ proc mix*(blendMode: BlendMode, dest, src: ColorRGBA): ColorRGBA {.inline.} =
|
|||
# result = target
|
||||
# else:
|
||||
# return blendMode.mix(target.color, blend.color).rgba
|
||||
|
||||
proc multiply(Cb, Cs: float32): float32 {.inline.} =
|
||||
Cb * Cs
|
||||
|
||||
proc screen(Cb, Cs: float32): float32 {.inline.} =
|
||||
1 - (1 - Cb) * (1 - Cs)
|
||||
|
||||
proc hardLight(Cb, Cs: float32): float32 {.inline.} =
|
||||
if Cs <= 0.5: multiply(Cb, 2 * Cs)
|
||||
else: screen(Cb, 2 * Cs - 1)
|
||||
|
||||
proc softLight(a, b: float32): float32 {.inline.} =
|
||||
## Pegtop
|
||||
(1 - 2 * b) * a ^ 2 + 2 * b * a
|
||||
|
||||
proc Lum(C: Color): float32 {.inline.} =
|
||||
0.3 * C.r + 0.59 * C.g + 0.11 * C.b
|
||||
|
||||
proc ClipColor(C: Color): Color {.inline.} =
|
||||
let
|
||||
L = Lum(C)
|
||||
n = min([C.r, C.g, C.b])
|
||||
x = max([C.r, C.g, C.b])
|
||||
var
|
||||
C = C
|
||||
if n < 0:
|
||||
C = L + (((C - L) * L) / (L - n))
|
||||
if x > 1:
|
||||
C = L + (((C - L) * (1 - L)) / (x - L))
|
||||
return C
|
||||
|
||||
proc SetLum(C: Color, l: float32): Color {.inline.} =
|
||||
let
|
||||
d = l - Lum(C)
|
||||
result.r = C.r + d
|
||||
result.g = C.g + d
|
||||
result.b = C.b + d
|
||||
return ClipColor(result)
|
||||
|
||||
proc Sat(C: Color): float32 {.inline.} =
|
||||
max([C.r, C.g, C.b]) - min([C.r, C.g, C.b])
|
||||
|
||||
proc SetSat(C: Color, s: float32): Color {.inline.} =
|
||||
var arr = [(C.r, 0), (C.g, 1), (C.b, 2)]
|
||||
# TODO: Don't rely on sort.
|
||||
arr.sort()
|
||||
var
|
||||
Cmin = arr[0][0]
|
||||
Cmid = arr[1][0]
|
||||
Cmax = arr[2][0]
|
||||
if Cmax > Cmin:
|
||||
Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin))
|
||||
Cmax = s
|
||||
else:
|
||||
Cmid = 0
|
||||
Cmax = 0
|
||||
Cmin = 0
|
||||
|
||||
if arr[0][1] == 0:
|
||||
result.r = Cmin
|
||||
if arr[1][1] == 0:
|
||||
result.r = Cmid
|
||||
if arr[2][1] == 0:
|
||||
result.r = Cmax
|
||||
|
||||
if arr[0][1] == 1:
|
||||
result.g = Cmin
|
||||
if arr[1][1] == 1:
|
||||
result.g = Cmid
|
||||
if arr[2][1] == 1:
|
||||
result.g = Cmax
|
||||
|
||||
if arr[0][1] == 2:
|
||||
result.b = Cmin
|
||||
if arr[1][1] == 2:
|
||||
result.b = Cmid
|
||||
if arr[2][1] == 2:
|
||||
result.b = Cmax
|
||||
|
||||
proc alphaFix(Cb, Cs, mixed: Color): Color {.inline.} =
|
||||
let ab = Cb.a
|
||||
let As = Cs.a
|
||||
result.r = As * (1 - ab) * Cs.r + As * ab * mixed.r + (1 - As) * ab * Cb.r
|
||||
result.g = As * (1 - ab) * Cs.g + As * ab * mixed.g + (1 - As) * ab * Cb.g
|
||||
result.b = As * (1 - ab) * Cs.b + As * ab * mixed.b + (1 - As) * ab * Cb.b
|
||||
|
||||
result.a = (Cs.a + Cb.a * (1.0 - Cs.a))
|
||||
result.r /= result.a
|
||||
result.g /= result.a
|
||||
result.b /= result.a
|
||||
|
||||
proc blendDarken(Cb, Cs: float32): float32 {.inline.} =
|
||||
min(Cb, Cs)
|
||||
|
||||
proc blendMultiply(Cb, Cs: float32): float32 {.inline.} =
|
||||
multiply(Cb, Cs)
|
||||
|
||||
proc blendLinearBurn(Cb, Cs: float32): float32 {.inline.} =
|
||||
Cb + Cs - 1
|
||||
|
||||
proc blendColorBurn(Cb, Cs: float32): float32 {.inline.} =
|
||||
if Cb == 1: 1.0
|
||||
elif Cs == 0: 0.0
|
||||
else: 1.0 - min(1, (1 - Cb) / Cs)
|
||||
|
||||
proc blendLighten(Cb, Cs: float32): float32 {.inline.} =
|
||||
max(Cb, Cs)
|
||||
|
||||
proc blendScreen(Cb, Cs: float32): float32 {.inline.} =
|
||||
screen(Cb, Cs)
|
||||
|
||||
proc blendLinearDodge(Cb, Cs: float32): float32 {.inline.} =
|
||||
Cb + Cs
|
||||
|
||||
proc blendColorDodge(Cb, Cs: float32): float32 {.inline.} =
|
||||
if Cb == 0: 0.0
|
||||
elif Cs == 1: 1.0
|
||||
else: min(1, Cb / (1 - Cs))
|
||||
|
||||
proc blendOverlay(Cb, Cs: float32): float32 {.inline.} =
|
||||
hardLight(Cs, Cb)
|
||||
|
||||
proc blendHardLight(Cb, Cs: float32): float32 {.inline.} =
|
||||
hardLight(Cb, Cs)
|
||||
|
||||
proc blendSoftLight(Cb, Cs: float32): float32 {.inline.} =
|
||||
softLight(Cb, Cs)
|
||||
|
||||
proc blendDifference(Cb, Cs: float32): float32 {.inline.} =
|
||||
abs(Cb - Cs)
|
||||
|
||||
proc blendExclusion(Cb, Cs: float32): float32 {.inline.} =
|
||||
Cb + Cs - 2 * Cb * Cs
|
||||
|
||||
proc blendNormal*(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = Cs.r
|
||||
result.g = Cs.g
|
||||
result.b = Cs.b
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendDarken(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendDarken(Cb.r, Cs.r)
|
||||
result.g = blendDarken(Cb.g, Cs.g)
|
||||
result.b = blendDarken(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendMultiply(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendMultiply(Cb.r, Cs.r)
|
||||
result.g = blendMultiply(Cb.g, Cs.g)
|
||||
result.b = blendMultiply(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendLinearBurn(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendLinearBurn(Cb.r, Cs.r)
|
||||
result.g = blendLinearBurn(Cb.g, Cs.g)
|
||||
result.b = blendLinearBurn(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendColorBurn(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendColorBurn(Cb.r, Cs.r)
|
||||
result.g = blendColorBurn(Cb.g, Cs.g)
|
||||
result.b = blendColorBurn(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendLighten(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendLighten(Cb.r, Cs.r)
|
||||
result.g = blendLighten(Cb.g, Cs.g)
|
||||
result.b = blendLighten(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendScreen(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendScreen(Cb.r, Cs.r)
|
||||
result.g = blendScreen(Cb.g, Cs.g)
|
||||
result.b = blendScreen(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendLinearDodge(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendLinearDodge(Cb.r, Cs.r)
|
||||
result.g = blendLinearDodge(Cb.g, Cs.g)
|
||||
result.b = blendLinearDodge(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendColorDodge(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendColorDodge(Cb.r, Cs.r)
|
||||
result.g = blendColorDodge(Cb.g, Cs.g)
|
||||
result.b = blendColorDodge(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendOverlay(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendOverlay(Cb.r, Cs.r)
|
||||
result.g = blendOverlay(Cb.g, Cs.g)
|
||||
result.b = blendOverlay(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendHardLight(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendHardLight(Cb.r, Cs.r)
|
||||
result.g = blendHardLight(Cb.g, Cs.g)
|
||||
result.b = blendHardLight(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendSoftLight(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendSoftLight(Cb.r, Cs.r)
|
||||
result.g = blendSoftLight(Cb.g, Cs.g)
|
||||
result.b = blendSoftLight(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendDifference(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendDifference(Cb.r, Cs.r)
|
||||
result.g = blendDifference(Cb.g, Cs.g)
|
||||
result.b = blendDifference(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendExclusion(Cb, Cs: Color): Color {.inline.} =
|
||||
result.r = blendExclusion(Cb.r, Cs.r)
|
||||
result.g = blendExclusion(Cb.g, Cs.g)
|
||||
result.b = blendExclusion(Cb.b, Cs.b)
|
||||
result = alphaFix(Cb, Cs, result)
|
||||
|
||||
proc blendColor(Cb, Cs: Color): Color {.inline.} =
|
||||
let mixed = SetLum(Cs, Lum(Cb))
|
||||
alphaFix(Cb, Cs, mixed)
|
||||
|
||||
proc blendLuminosity(Cb, Cs: Color): Color {.inline.} =
|
||||
let mixed = SetLum(Cb, Lum(Cs))
|
||||
alphaFix(Cb, Cs, mixed)
|
||||
|
||||
proc blendHue(Cb, Cs: Color): Color {.inline.} =
|
||||
let mixed = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
|
||||
alphaFix(Cb, Cs, mixed)
|
||||
|
||||
proc blendSaturation(Cb, Cs: Color): Color {.inline.} =
|
||||
let mixed = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
|
||||
alphaFix(Cb, Cs, mixed)
|
||||
|
||||
proc blendMask(target, blend: Color): Color {.inline.} =
|
||||
result.r = target.r
|
||||
result.g = target.g
|
||||
result.b = target.b
|
||||
result.a = min(target.a, blend.a)
|
||||
|
||||
proc blendSubtractMask(target, blend: Color): Color {.inline.} =
|
||||
result.r = target.r
|
||||
result.g = target.g
|
||||
result.b = target.b
|
||||
result.a = target.a * (1 - blend.a)
|
||||
|
||||
proc blendIntersectMask(target, blend: Color): Color {.inline.} =
|
||||
result.r = target.r
|
||||
result.g = target.g
|
||||
result.b = target.b
|
||||
result.a = target.a * blend.a
|
||||
|
||||
proc blendExcludeMask(target, blend: Color): Color {.inline.} =
|
||||
result.r = target.r
|
||||
result.g = target.g
|
||||
result.b = target.b
|
||||
result.a = abs(target.a - blend.a)
|
||||
|
||||
proc blendOverwrite(target, blend: Color): Color {.inline.} =
|
||||
result = blend
|
||||
|
||||
proc mix2*(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.} =
|
||||
let ab = Cb.a.int32
|
||||
let As = Cs.a.int32
|
||||
let r = As * (255 - ab) * Cs.r.int32 + As * ab * mixed.r.int32 + (255 - As) * ab * Cb.r.int32
|
||||
let g = As * (255 - ab) * Cs.g.int32 + As * ab * mixed.g.int32 + (255 - As) * ab * Cb.g.int32
|
||||
let b = As * (255 - ab) * Cs.b.int32 + As * ab * mixed.b.int32 + (255 - As) * ab * Cb.b.int32
|
||||
|
||||
let a = Cs.a.int32 + Cb.a.int32 * (255 - Cs.a.int32) div 255
|
||||
if a == 0:
|
||||
return
|
||||
else:
|
||||
result.r = (r div a div 255).uint8
|
||||
result.g = (g div a div 255).uint8
|
||||
result.b = (b div a div 255).uint8
|
||||
result.a = a.uint8
|
||||
|
||||
proc blendNormal*(a, b: ColorRGBA): ColorRGBA =
|
||||
# blendNormal(a.color, b.color).rgba
|
||||
result.r = b.r
|
||||
result.g = b.g
|
||||
result.b = b.b
|
||||
result = alphaFix(a, b, result)
|
||||
|
||||
proc blendDarken*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendDarken(a.color, b.color).rgba
|
||||
|
||||
proc blendMultiply*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendMultiply(a.color, b.color).rgba
|
||||
|
||||
proc blendLinearBurn*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendLinearBurn(a.color, b.color).rgba
|
||||
|
||||
proc blendColorBurn*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendColorBurn(a.color, b.color).rgba
|
||||
|
||||
proc blendLighten*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendLighten(a.color, b.color).rgba
|
||||
|
||||
proc blendScreen*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendScreen(a.color, b.color).rgba
|
||||
|
||||
proc blendLinearDodge*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendLinearDodge(a.color, b.color).rgba
|
||||
|
||||
proc blendColorDodge*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendColorDodge(a.color, b.color).rgba
|
||||
|
||||
proc blendOverlay*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendOverlay(a.color, b.color).rgba
|
||||
|
||||
proc blendHardLight*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendHardLight(a.color, b.color).rgba
|
||||
|
||||
proc blendSoftLight*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendSoftLight(a.color, b.color).rgba
|
||||
|
||||
proc blendDifference*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendDifference(a.color, b.color).rgba
|
||||
|
||||
proc blendExclusion*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendExclusion(a.color, b.color).rgba
|
||||
|
||||
proc blendColor*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendColor(a.color, b.color).rgba
|
||||
|
||||
proc blendLuminosity*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendLuminosity(a.color, b.color).rgba
|
||||
|
||||
proc blendHue*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendHue(a.color, b.color).rgba
|
||||
|
||||
proc blendSaturation*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendSaturation(a.color, b.color).rgba
|
||||
|
||||
proc blendMask*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendMask(a.color, b.color).rgba
|
||||
|
||||
proc blendSubtractMask*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendSubtractMask(a.color, b.color).rgba
|
||||
|
||||
proc blendIntersectMask*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendIntersectMask(a.color, b.color).rgba
|
||||
|
||||
proc blendExcludeMask*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendExcludeMask(a.color, b.color).rgba
|
||||
|
||||
proc blendOverwrite*(a, b: ColorRGBA): ColorRGBA =
|
||||
blendOverwrite(a.color, b.color).rgba
|
||||
|
||||
proc mix2*(blendMode: BlendMode, dest, src: ColorRGBA): ColorRGBA {.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)
|
||||
|
|
|
@ -62,6 +62,11 @@ proc getRgbaUnsafe*(image: Image, x, y: int): ColorRGBA {.inline.} =
|
|||
## Failure in the assumptions will case unsafe memory reads.
|
||||
result = image.data[image.width * y + x]
|
||||
|
||||
proc getAddr*(image: Image, x, y: int): pointer {.inline.} =
|
||||
## Gets a address of the color from (x, y) coordinates.
|
||||
## Unsafe make sure x, y are in bounds.
|
||||
addr image.data[image.width * y + x]
|
||||
|
||||
proc `[]`*(image: Image, x, y: int): ColorRGBA {.inline.} =
|
||||
## Gets a pixel at (x, y) or returns transparent black if outside of bounds.
|
||||
if image.inside(x, y):
|
||||
|
@ -107,12 +112,13 @@ proc invert*(image: Image): Image =
|
|||
|
||||
proc subImage*(image: Image, x, y, w, h: int): Image =
|
||||
## Gets a sub image of the main image.
|
||||
doAssert x >= 0 and y >= 0
|
||||
doAssert x + w <= image.width and y + h <= image.height
|
||||
## TODO handle images out of bounds faster
|
||||
# doAssert x >= 0 and y >= 0
|
||||
# doAssert x + w <= image.width and y + h <= image.height
|
||||
result = newImage(w, h)
|
||||
for y2 in 0 ..< h:
|
||||
for x2 in 0 ..< w:
|
||||
result.setRgbaUnsafe(x2, y2, image.getRgbaUnsafe(x2 + x, y2 + y))
|
||||
result.setRgbaUnsafe(x2, y2, image[x2 + x, y2 + y])
|
||||
|
||||
proc minifyBy2*(image: Image): Image =
|
||||
## Scales the image down by an integer scale.
|
||||
|
@ -144,13 +150,13 @@ proc magnifyBy2*(image: Image, scale2x: int): Image =
|
|||
proc magnifyBy2*(image: Image): Image =
|
||||
image.magnifyBy2(2)
|
||||
|
||||
func lerp(a, b: Color, v: float): Color {.inline.} =
|
||||
func lerp(a, b: Color, v: float32): Color {.inline.} =
|
||||
result.r = lerp(a.r, b.r, v)
|
||||
result.g = lerp(a.g, b.g, v)
|
||||
result.b = lerp(a.b, b.b, v)
|
||||
result.a = lerp(a.a, b.a, v)
|
||||
|
||||
proc getRgbaSmooth*(image: Image, x, y: float64): ColorRGBA {.inline.} =
|
||||
proc getRgbaSmooth*(image: Image, x, y: float32): ColorRGBA {.inline.} =
|
||||
## Gets a pixel as (x, y) floats.
|
||||
|
||||
proc toAlphy(c: Color): Color =
|
||||
|
@ -231,7 +237,7 @@ proc drawBlend*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
|
|||
var rgba = a.getRgbaUnsafe(x, y)
|
||||
let rgba2 = b.getRgbaUnsafe(srcPos.x.floor.int, srcPos.y.floor.int)
|
||||
if blendMode.hasEffect(rgba2):
|
||||
rgba = blendMode.mix(rgba, rgba2)
|
||||
rgba = blendMode.mix2(rgba, rgba2)
|
||||
result.setRgbaUnsafe(x, y, rgba)
|
||||
|
||||
else:
|
||||
|
@ -250,7 +256,6 @@ proc drawBlendSmooth*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Imag
|
|||
var matInv = mat.inverse()
|
||||
for y in 0 ..< a.height:
|
||||
for x in 0 ..< a.width:
|
||||
|
||||
let srcPos = matInv * vec2(x.float32, y.float32)
|
||||
if b.inside1px(srcPos.x, srcPos.y):
|
||||
var rgba = a.getRgbaUnsafe(x, y)
|
||||
|
@ -265,20 +270,218 @@ proc drawBlendSmooth*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Imag
|
|||
else:
|
||||
result.setRgbaUnsafe(x, y, rgba(0,0,0,0))
|
||||
|
||||
proc drawCorrect*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
|
||||
## Draws one image onto another using matrix with color blending.
|
||||
result = newImageNoInit(a.width, a.height)
|
||||
|
||||
var
|
||||
matInv = mat.inverse()
|
||||
# compute movement vectors
|
||||
h = 0.5.float32
|
||||
start = matInv * vec2(0 + h, 0 + h)
|
||||
stepX = matInv * vec2(1 + h, 0 + h) - start
|
||||
stepY = matInv * vec2(0 + h, 1 + h) - start
|
||||
minFilterBy2 = max(stepX.length, stepY.length)
|
||||
b = b
|
||||
|
||||
while minFilterBy2 > 2.0:
|
||||
b = b.minifyBy2()
|
||||
start /= 2
|
||||
stepX /= 2
|
||||
stepY /= 2
|
||||
minFilterBy2 /= 2
|
||||
|
||||
for y in 0 ..< a.height:
|
||||
for x in 0 ..< a.width:
|
||||
let srcPos = matInv * vec2(x.float32 + h, y.float32 + h)
|
||||
var rgba = a.getRgbaUnsafe(x, y)
|
||||
let rgba2 = b.getRgbaSmooth(srcPos.x - h, srcPos.y - h)
|
||||
rgba = blendMode.mix(rgba, rgba2)
|
||||
result.setRgbaUnsafe(x, y, rgba)
|
||||
|
||||
proc drawStepper*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
|
||||
## Draws one image onto another using matrix with color blending.
|
||||
result = newImageNoInit(a.width, a.height)
|
||||
|
||||
type Segment = object
|
||||
## A math segment from point "at" to point "to"
|
||||
at*: Vec2
|
||||
to*: Vec2
|
||||
|
||||
proc segment(at, to: Vec2): Segment =
|
||||
result.at = at
|
||||
result.to = to
|
||||
|
||||
proc intersects(a, b: Segment, at: var Vec2): bool =
|
||||
## Checks if the a segment intersects b segment.
|
||||
## If it returns true, at will have point of intersection
|
||||
var s1x, s1y, s2x, s2y: float32
|
||||
s1x = a.to.x - a.at.x
|
||||
s1y = a.to.y - a.at.y
|
||||
s2x = b.to.x - b.at.x
|
||||
s2y = b.to.y - b.at.y
|
||||
|
||||
var s, t: float32
|
||||
s = (-s1y * (a.at.x - b.at.x) + s1x * (a.at.y - b.at.y)) /
|
||||
(-s2x * s1y + s1x * s2y)
|
||||
t = (s2x * (a.at.y - b.at.y) - s2y * (a.at.x - b.at.x)) /
|
||||
(-s2x * s1y + s1x * s2y)
|
||||
|
||||
if s >= 0 and s < 1 and t >= 0 and t < 1:
|
||||
at.x = a.at.x + (t * s1x)
|
||||
at.y = a.at.y + (t * s1y)
|
||||
return true
|
||||
return false
|
||||
|
||||
var
|
||||
matInv = mat.inverse()
|
||||
# compute movement vectors
|
||||
h = 0.5.float32
|
||||
start = matInv * vec2(0 + h, 0 + h)
|
||||
stepX = matInv * vec2(1 + h, 0 + h) - start
|
||||
stepY = matInv * vec2(0 + h, 1 + h) - start
|
||||
minFilterBy2 = max(stepX.length, stepY.length)
|
||||
b = b
|
||||
|
||||
let corners = [
|
||||
mat * vec2(0, 0),
|
||||
mat * vec2(b.width.float32, 0),
|
||||
mat * vec2(b.width.float32, b.height.float32),
|
||||
mat * vec2(0, b.height.float32)
|
||||
]
|
||||
|
||||
let lines = [
|
||||
segment(corners[0], corners[1]),
|
||||
segment(corners[1], corners[2]),
|
||||
segment(corners[2], corners[3]),
|
||||
segment(corners[3], corners[0])
|
||||
]
|
||||
|
||||
while minFilterBy2 > 2.0:
|
||||
b = b.minifyBy2()
|
||||
start /= 2
|
||||
stepX /= 2
|
||||
stepY /= 2
|
||||
minFilterBy2 /= 2
|
||||
|
||||
template forBlend(
|
||||
mixer: proc(a, b: ColorRGBA): ColorRGBA,
|
||||
getRgba: proc(a: Image, x, y: float32): ColorRGBA {.inline.},
|
||||
) =
|
||||
for y in 0 ..< a.height:
|
||||
var
|
||||
xMin = 0
|
||||
xMax = 0
|
||||
hasIntersection = false
|
||||
for yOffset in [0.float32, 1]:
|
||||
var scanLine = segment(
|
||||
vec2(-100000, y.float32 + yOffset),
|
||||
vec2(10000, y.float32 + yOffset)
|
||||
)
|
||||
for l in lines:
|
||||
var at: Vec2
|
||||
if intersects(l, scanLine, at):
|
||||
if hasIntersection:
|
||||
xMin = min(xMin, at.x.floor.int)
|
||||
xMax = max(xMax, at.x.ceil.int)
|
||||
else:
|
||||
hasIntersection = true
|
||||
xMin = at.x.floor.int
|
||||
xMax = at.x.ceil.int
|
||||
|
||||
xMin = xMin.clamp(0, a.width)
|
||||
xMax = xMax.clamp(0, a.width)
|
||||
|
||||
# for x in 0 ..< xMin:
|
||||
# result.setRgbaUnsafe(x, y, a.getRgbaUnsafe(x, y))
|
||||
if xMin > 0:
|
||||
copyMem(result.getAddr(0, y), a.getAddr(0, y), 4*xMin)
|
||||
|
||||
for x in xMin ..< xMax:
|
||||
let srcV = start + stepX * float32(x) + stepY * float32(y)
|
||||
var rgba = a.getRgbaUnsafe(x, y)
|
||||
# TODO maybe remove inside check?
|
||||
if b.inside((srcV.x - h).int, (srcV.y - h).int):
|
||||
let rgba2 = b.getRgba(srcV.x - h, srcV.y - h)
|
||||
rgba = mixer(rgba, rgba2)
|
||||
result.setRgbaUnsafe(x, y, rgba)
|
||||
|
||||
#for x in xMax ..< a.width:
|
||||
# result.setRgbaUnsafe(x, y, a.getRgbaUnsafe(x, y))
|
||||
if a.width - xMax > 0:
|
||||
copyMem(result.getAddr(xMax, y), a.getAddr(xMax, y), 4*(a.width - xMax))
|
||||
|
||||
proc getRgba(a: Image, x, y: float32): ColorRGBA {.inline.} =
|
||||
a.getRgbaUnsafe(x.int, y.int)
|
||||
|
||||
# TODO check pos for fractional
|
||||
if stepX.length == 1.0 and stepY.length == 1.0:
|
||||
case blendMode
|
||||
of bmNormal: forBlend(blendNormal, getRgba)
|
||||
of bmDarken: forBlend(blendDarken, getRgba)
|
||||
of bmMultiply: forBlend(blendMultiply, getRgba)
|
||||
of bmLinearBurn: forBlend(blendLinearBurn, getRgba)
|
||||
of bmColorBurn: forBlend(blendColorBurn, getRgba)
|
||||
of bmLighten: forBlend(blendLighten, getRgba)
|
||||
of bmScreen: forBlend(blendScreen, getRgba)
|
||||
of bmLinearDodge: forBlend(blendLinearDodge, getRgba)
|
||||
of bmColorDodge: forBlend(blendColorDodge, getRgba)
|
||||
of bmOverlay: forBlend(blendOverlay, getRgba)
|
||||
of bmSoftLight: forBlend(blendSoftLight, getRgba)
|
||||
of bmHardLight: forBlend(blendHardLight, getRgba)
|
||||
of bmDifference: forBlend(blendDifference, getRgba)
|
||||
of bmExclusion: forBlend(blendExclusion, getRgba)
|
||||
of bmHue: forBlend(blendHue, getRgba)
|
||||
of bmSaturation: forBlend(blendSaturation, getRgba)
|
||||
of bmColor: forBlend(blendColor, getRgba)
|
||||
of bmLuminosity: forBlend(blendLuminosity, getRgba)
|
||||
of bmMask: forBlend(blendMask, getRgba)
|
||||
of bmOverwrite: forBlend(blendOverwrite, getRgba)
|
||||
of bmSubtractMask: forBlend(blendSubtractMask, getRgba)
|
||||
of bmIntersectMask: forBlend(blendIntersectMask, getRgba)
|
||||
of bmExcludeMask: forBlend(blendExcludeMask, getRgba)
|
||||
else:
|
||||
case blendMode
|
||||
of bmNormal: forBlend(blendNormal, getRgbaSmooth)
|
||||
of bmDarken: forBlend(blendDarken, getRgbaSmooth)
|
||||
of bmMultiply: forBlend(blendMultiply, getRgbaSmooth)
|
||||
of bmLinearBurn: forBlend(blendLinearBurn, getRgbaSmooth)
|
||||
of bmColorBurn: forBlend(blendColorBurn, getRgbaSmooth)
|
||||
of bmLighten: forBlend(blendLighten, getRgbaSmooth)
|
||||
of bmScreen: forBlend(blendScreen, getRgbaSmooth)
|
||||
of bmLinearDodge: forBlend(blendLinearDodge, getRgbaSmooth)
|
||||
of bmColorDodge: forBlend(blendColorDodge, getRgbaSmooth)
|
||||
of bmOverlay: forBlend(blendOverlay, getRgbaSmooth)
|
||||
of bmSoftLight: forBlend(blendSoftLight, getRgbaSmooth)
|
||||
of bmHardLight: forBlend(blendHardLight, getRgbaSmooth)
|
||||
of bmDifference: forBlend(blendDifference, getRgbaSmooth)
|
||||
of bmExclusion: forBlend(blendExclusion, getRgbaSmooth)
|
||||
of bmHue: forBlend(blendHue, getRgbaSmooth)
|
||||
of bmSaturation: forBlend(blendSaturation, getRgbaSmooth)
|
||||
of bmColor: forBlend(blendColor, getRgbaSmooth)
|
||||
of bmLuminosity: forBlend(blendLuminosity, getRgbaSmooth)
|
||||
of bmMask: forBlend(blendMask, getRgbaSmooth)
|
||||
of bmOverwrite: forBlend(blendOverwrite, getRgbaSmooth)
|
||||
of bmSubtractMask: forBlend(blendSubtractMask, getRgbaSmooth)
|
||||
of bmIntersectMask: forBlend(blendIntersectMask, getRgbaSmooth)
|
||||
of bmExcludeMask: forBlend(blendExcludeMask, getRgbaSmooth)
|
||||
|
||||
proc draw*(a: Image, b: Image, mat: Mat3, blendMode = bmNormal): Image =
|
||||
## Draws one image onto another using matrix with color blending.
|
||||
|
||||
# Decide which ones of the draws best fit current parameters.
|
||||
let ns = [-1.float32, 0, 1]
|
||||
if mat[0, 0] in ns and mat[0, 1] in ns and
|
||||
mat[1, 0] in ns and mat[1, 1] in ns and
|
||||
mat[2, 0].fractional == 0.0 and mat[2, 1].fractional == 0.0:
|
||||
if blendMode == bmOverwrite:
|
||||
return drawOverwrite(a, b, mat)
|
||||
else:
|
||||
return drawBlend(a, b, mat, blendMode)
|
||||
return drawBlendSmooth(a, b, mat, blendMode)
|
||||
# let ns = [-1.float32, 0, 1]
|
||||
# if mat[0, 0] in ns and mat[0, 1] in ns and
|
||||
# mat[1, 0] in ns and mat[1, 1] in ns and
|
||||
# mat[2, 0].fractional == 0.0 and mat[2, 1].fractional == 0.0:
|
||||
# if blendMode == bmOverwrite:
|
||||
# return drawOverwrite(a, b, mat)
|
||||
# else:
|
||||
# return drawBlend(a, b, mat, blendMode)
|
||||
|
||||
# return drawCorrect(a, b, mat, blendMode)
|
||||
|
||||
return drawStepper(a, b, mat, blendMode)
|
||||
|
||||
proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal): Image =
|
||||
a.draw(b, translate(pos), blendMode)
|
||||
|
|
|
@ -1,52 +1,44 @@
|
|||
import pixie, chroma, vmath, fidget/opengl/perf, pixie/fileformats/bmp
|
||||
|
||||
timeIt "drawOverwrite bmOverwrite":
|
||||
var tmp = 0
|
||||
var c: Image
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
c = a.drawOverwrite(b, translate(vec2(25, 25))) # Copy
|
||||
tmp += c.width * c.height
|
||||
c.writeFile("tests/images/bench.drawOverwrite.bmOverwrite.png")
|
||||
echo tmp
|
||||
# timeIt "drawOverwrite bmOverwrite":
|
||||
# var tmp = 0
|
||||
# var c: Image
|
||||
# for i in 0 ..< 1000:
|
||||
# var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
# var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
# c = a.drawOverwrite(b, translate(vec2(25, 25))) # Copy
|
||||
# tmp += c.width * c.height
|
||||
# c.writeFile("tests/images/bench.drawOverwrite.bmOverwrite.png")
|
||||
# echo tmp
|
||||
|
||||
timeIt "drawBlend bmOverwrite":
|
||||
var tmp = 0
|
||||
var c: Image
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
c = a.drawBlend(b, translate(vec2(25, 25)), bmOverwrite)
|
||||
tmp += c.width * c.height
|
||||
c.writeFile("tests/images/bench.drawBlend.bmOverwrite.png")
|
||||
echo tmp
|
||||
# timeIt "drawBlend bmOverwrite":
|
||||
# var tmp = 0
|
||||
# var c: Image
|
||||
# for i in 0 ..< 1000:
|
||||
# var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
# var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
# c = a.drawBlend(b, translate(vec2(25, 25)), bmOverwrite)
|
||||
# tmp += c.width * c.height
|
||||
# c.writeFile("tests/images/bench.drawBlend.bmOverwrite.png")
|
||||
# echo tmp
|
||||
|
||||
timeIt "drawBlendSmooth bmOverwrite":
|
||||
var tmp = 0
|
||||
var c: Image
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
c = a.drawBlendSmooth(b, translate(vec2(25, 25)), bmOverwrite)
|
||||
tmp += c.width * c.height
|
||||
c.writeFile("tests/images/bench.drawBlendSmooth.bmOverwrite.png")
|
||||
echo tmp
|
||||
# timeIt "drawBlendSmooth bmOverwrite":
|
||||
# var tmp = 0
|
||||
# var c: Image
|
||||
# for i in 0 ..< 1000:
|
||||
# var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
# var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
# c = a.drawBlendSmooth(b, translate(vec2(25, 25)), bmOverwrite)
|
||||
# tmp += c.width * c.height
|
||||
# c.writeFile("tests/images/bench.drawBlendSmooth.bmOverwrite.png")
|
||||
# echo tmp
|
||||
|
||||
timeIt "drawBlend bmNormal":
|
||||
var tmp = 0
|
||||
var c: Image
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
c = a.drawBlend(b, translate(vec2(25, 25)), bmNormal)
|
||||
tmp += c.width * c.height
|
||||
c.writeFile("tests/images/bench.drawBlend.bmNormal.png")
|
||||
|
@ -56,41 +48,57 @@ timeIt "drawBlendSmooth bmNormal":
|
|||
var tmp = 0
|
||||
var c: Image
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
c = a.drawBlendSmooth(b, translate(vec2(25, 25)), bmNormal)
|
||||
tmp += c.width * c.height
|
||||
c.writeFile("tests/images/bench.drawBlendSmooth.bmNormal.png")
|
||||
echo tmp
|
||||
|
||||
timeIt "drawBlend bmSaturation":
|
||||
timeIt "drawCorrect bmNormal":
|
||||
var tmp = 0
|
||||
var c: Image
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 0, 0, 255))
|
||||
c = a.drawBlend(b, translate(vec2(25, 25)), bmSaturation)
|
||||
var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
c = a.drawCorrect(b, translate(vec2(25, 25)), bmNormal)
|
||||
tmp += c.width * c.height
|
||||
c.writeFile("tests/images/bench.drawBlend.bmSaturation.png")
|
||||
c.writeFile("tests/images/bench.drawCorrect.bmNormal.png")
|
||||
echo tmp
|
||||
|
||||
timeIt "drawBlendSmooth bmSaturation":
|
||||
timeIt "drawStepper bmNormal":
|
||||
var tmp = 0
|
||||
var c: Image
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 0, 0, 255))
|
||||
c = a.drawBlendSmooth(b, translate(vec2(25, 25)), bmSaturation)
|
||||
var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
c = a.drawStepper(b, translate(vec2(25, 25)), bmNormal)
|
||||
tmp += c.width * c.height
|
||||
c.writeFile("tests/images/bench.drawBlendSmooth.bmSaturation.png")
|
||||
c.writeFile("tests/images/bench.drawStepper.bmNormal.png")
|
||||
echo tmp
|
||||
|
||||
# timeIt "drawBlend bmSaturation":
|
||||
# var tmp = 0
|
||||
# var c: Image
|
||||
# for i in 0 ..< 1000:
|
||||
# var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
# var b = newImageFill(100, 100, rgba(0, 0, 0, 255))
|
||||
# c = a.drawBlend(b, translate(vec2(25, 25)), bmSaturation)
|
||||
# tmp += c.width * c.height
|
||||
# c.writeFile("tests/images/bench.drawBlend.bmSaturation.png")
|
||||
# echo tmp
|
||||
|
||||
# timeIt "drawBlendSmooth bmSaturation":
|
||||
# var tmp = 0
|
||||
# var c: Image
|
||||
# for i in 0 ..< 1000:
|
||||
# var a = newImageFill(100, 100, rgba(255, 0, 0, 255))
|
||||
# var b = newImageFill(100, 100, rgba(0, 0, 0, 255))
|
||||
# c = a.drawBlendSmooth(b, translate(vec2(25, 25)), bmSaturation)
|
||||
# tmp += c.width * c.height
|
||||
# c.writeFile("tests/images/bench.drawBlendSmooth.bmSaturation.png")
|
||||
# echo tmp
|
||||
|
||||
# timeIt "benchDrawFast3 Rotation":
|
||||
# var tmp = 0
|
||||
# var c: Image
|
||||
|
|
|
@ -17,40 +17,193 @@ proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = bmNo
|
|||
proc inPlaceDraw*(destImage: Image, srcImage: Image, pos = vec2(0, 0), blendMode = bmNormal) =
|
||||
destImage.inPlaceDraw(srcImage, translate(-pos), blendMode)
|
||||
|
||||
block:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
a.inPlaceDraw(b, pos=vec2(25, 25))
|
||||
writeFile("tests/images/inPlaceDraw.bmp", a.encodeBmp())
|
||||
proc drawStepperInPlace*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode) =
|
||||
## Draws one image onto another using matrix with color blending.
|
||||
|
||||
block:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
var c = a.drawFast3(b, translate(vec2(25, 25)), bmNormal)
|
||||
writeFile("tests/images/copyDraw.bmp", c.encodeBmp())
|
||||
type Segment = object
|
||||
## A math segment from point "at" to point "to"
|
||||
at*: Vec2
|
||||
to*: Vec2
|
||||
|
||||
proc segment(at, to: Vec2): Segment =
|
||||
result.at = at
|
||||
result.to = to
|
||||
|
||||
proc intersects(a, b: Segment, at: var Vec2): bool =
|
||||
## Checks if the a segment intersects b segment.
|
||||
## If it returns true, at will have point of intersection
|
||||
var s1x, s1y, s2x, s2y: float32
|
||||
s1x = a.to.x - a.at.x
|
||||
s1y = a.to.y - a.at.y
|
||||
s2x = b.to.x - b.at.x
|
||||
s2y = b.to.y - b.at.y
|
||||
|
||||
var s, t: float32
|
||||
s = (-s1y * (a.at.x - b.at.x) + s1x * (a.at.y - b.at.y)) /
|
||||
(-s2x * s1y + s1x * s2y)
|
||||
t = (s2x * (a.at.y - b.at.y) - s2y * (a.at.x - b.at.x)) /
|
||||
(-s2x * s1y + s1x * s2y)
|
||||
|
||||
if s >= 0 and s < 1 and t >= 0 and t < 1:
|
||||
at.x = a.at.x + (t * s1x)
|
||||
at.y = a.at.y + (t * s1y)
|
||||
return true
|
||||
return false
|
||||
|
||||
var
|
||||
matInv = mat.inverse()
|
||||
# compute movement vectors
|
||||
h = 0.5.float32
|
||||
start = matInv * vec2(0 + h, 0 + h)
|
||||
stepX = matInv * vec2(1 + h, 0 + h) - start
|
||||
stepY = matInv * vec2(0 + h, 1 + h) - start
|
||||
minFilterBy2 = max(stepX.length, stepY.length)
|
||||
b = b
|
||||
|
||||
let corners = [
|
||||
mat * vec2(0, 0),
|
||||
mat * vec2(b.width.float32, 0),
|
||||
mat * vec2(b.width.float32, b.height.float32),
|
||||
mat * vec2(0, b.height.float32)
|
||||
]
|
||||
|
||||
let lines = [
|
||||
segment(corners[0], corners[1]),
|
||||
segment(corners[1], corners[2]),
|
||||
segment(corners[2], corners[3]),
|
||||
segment(corners[3], corners[0])
|
||||
]
|
||||
|
||||
while minFilterBy2 > 2.0:
|
||||
b = b.minifyBy2()
|
||||
start /= 2
|
||||
stepX /= 2
|
||||
stepY /= 2
|
||||
minFilterBy2 /= 2
|
||||
|
||||
template forBlend(
|
||||
mixer: proc(a, b: ColorRGBA): ColorRGBA,
|
||||
getRgba: proc(a: Image, x, y: float32): ColorRGBA {.inline.},
|
||||
) =
|
||||
for y in 0 ..< a.height:
|
||||
var
|
||||
xMin = 0
|
||||
xMax = 0
|
||||
hasIntersection = false
|
||||
for yOffset in [0.float32, 1]:
|
||||
var scanLine = segment(
|
||||
vec2(-100000, y.float32 + yOffset),
|
||||
vec2(10000, y.float32 + yOffset)
|
||||
)
|
||||
for l in lines:
|
||||
var at: Vec2
|
||||
if intersects(l, scanLine, at):
|
||||
if hasIntersection:
|
||||
xMin = min(xMin, at.x.floor.int)
|
||||
xMax = max(xMax, at.x.ceil.int)
|
||||
else:
|
||||
hasIntersection = true
|
||||
xMin = at.x.floor.int
|
||||
xMax = at.x.ceil.int
|
||||
|
||||
xMin = xMin.clamp(0, a.width)
|
||||
xMax = xMax.clamp(0, a.width)
|
||||
|
||||
# for x in 0 ..< xMin:
|
||||
# result.setRgbaUnsafe(x, y, a.getRgbaUnsafe(x, y))
|
||||
# if xMin > 0:
|
||||
# copyMem(a.getAddr(0, y), a.getAddr(0, y), 4*xMin)
|
||||
|
||||
for x in xMin ..< xMax:
|
||||
let srcV = start + stepX * float32(x) + stepY * float32(y)
|
||||
var rgba = a.getRgbaUnsafe(x, y)
|
||||
if b.inside((srcV.x - h).int, (srcV.y - h).int):
|
||||
let rgba2 = b.getRgba(srcV.x - h, srcV.y - h)
|
||||
rgba = mixer(rgba, rgba2)
|
||||
a.setRgbaUnsafe(x, y, rgba)
|
||||
|
||||
#for x in xMax ..< a.width:
|
||||
# result.setRgbaUnsafe(x, y, a.getRgbaUnsafe(x, y))
|
||||
# if a.width - xMax > 0:
|
||||
# copyMem(result.getAddr(xMax, y), a.getAddr(xMax, y), 4*(a.width - xMax))
|
||||
|
||||
proc getRgba(a: Image, x, y: float32): ColorRGBA {.inline.} =
|
||||
a.getRgbaUnsafe(x.int, y.int)
|
||||
|
||||
if stepX.length == 1.0 and stepY.length == 1.0:
|
||||
case blendMode
|
||||
of bmNormal: forBlend(blendNormal, getRgba)
|
||||
of bmDarken: forBlend(blendDarken, getRgba)
|
||||
of bmMultiply: forBlend(blendMultiply, getRgba)
|
||||
of bmLinearBurn: forBlend(blendLinearBurn, getRgba)
|
||||
of bmColorBurn: forBlend(blendColorBurn, getRgba)
|
||||
of bmLighten: forBlend(blendLighten, getRgba)
|
||||
of bmScreen: forBlend(blendScreen, getRgba)
|
||||
of bmLinearDodge: forBlend(blendLinearDodge, getRgba)
|
||||
of bmColorDodge: forBlend(blendColorDodge, getRgba)
|
||||
of bmOverlay: forBlend(blendOverlay, getRgba)
|
||||
of bmSoftLight: forBlend(blendSoftLight, getRgba)
|
||||
of bmHardLight: forBlend(blendHardLight, getRgba)
|
||||
of bmDifference: forBlend(blendDifference, getRgba)
|
||||
of bmExclusion: forBlend(blendExclusion, getRgba)
|
||||
of bmHue: forBlend(blendHue, getRgba)
|
||||
of bmSaturation: forBlend(blendSaturation, getRgba)
|
||||
of bmColor: forBlend(blendColor, getRgba)
|
||||
of bmLuminosity: forBlend(blendLuminosity, getRgba)
|
||||
of bmMask: forBlend(blendMask, getRgba)
|
||||
of bmOverwrite: forBlend(blendOverwrite, getRgba)
|
||||
of bmSubtractMask: forBlend(blendSubtractMask, getRgba)
|
||||
of bmIntersectMask: forBlend(blendIntersectMask, getRgba)
|
||||
of bmExcludeMask: forBlend(blendExcludeMask, getRgba)
|
||||
else:
|
||||
case blendMode
|
||||
of bmNormal: forBlend(blendNormal, getRgbaSmooth)
|
||||
of bmDarken: forBlend(blendDarken, getRgbaSmooth)
|
||||
of bmMultiply: forBlend(blendMultiply, getRgbaSmooth)
|
||||
of bmLinearBurn: forBlend(blendLinearBurn, getRgbaSmooth)
|
||||
of bmColorBurn: forBlend(blendColorBurn, getRgbaSmooth)
|
||||
of bmLighten: forBlend(blendLighten, getRgbaSmooth)
|
||||
of bmScreen: forBlend(blendScreen, getRgbaSmooth)
|
||||
of bmLinearDodge: forBlend(blendLinearDodge, getRgbaSmooth)
|
||||
of bmColorDodge: forBlend(blendColorDodge, getRgbaSmooth)
|
||||
of bmOverlay: forBlend(blendOverlay, getRgbaSmooth)
|
||||
of bmSoftLight: forBlend(blendSoftLight, getRgbaSmooth)
|
||||
of bmHardLight: forBlend(blendHardLight, getRgbaSmooth)
|
||||
of bmDifference: forBlend(blendDifference, getRgbaSmooth)
|
||||
of bmExclusion: forBlend(blendExclusion, getRgbaSmooth)
|
||||
of bmHue: forBlend(blendHue, getRgbaSmooth)
|
||||
of bmSaturation: forBlend(blendSaturation, getRgbaSmooth)
|
||||
of bmColor: forBlend(blendColor, getRgbaSmooth)
|
||||
of bmLuminosity: forBlend(blendLuminosity, getRgbaSmooth)
|
||||
of bmMask: forBlend(blendMask, getRgbaSmooth)
|
||||
of bmOverwrite: forBlend(blendOverwrite, getRgbaSmooth)
|
||||
of bmSubtractMask: forBlend(blendSubtractMask, getRgbaSmooth)
|
||||
of bmIntersectMask: forBlend(blendIntersectMask, getRgbaSmooth)
|
||||
of bmExcludeMask: forBlend(blendExcludeMask, getRgbaSmooth)
|
||||
|
||||
timeIt "inPlaceDraw":
|
||||
var tmp = 0
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
var a = newImageFill(1000, 1000, rgba(0, 255, 0, 255))
|
||||
var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
a.inPlaceDraw(b, pos=vec2(25, 25))
|
||||
tmp += a.width * a.height
|
||||
echo tmp
|
||||
|
||||
timeIt "copyDraw":
|
||||
timeIt "drawStepper":
|
||||
var tmp = 0
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImage(100, 100)
|
||||
a.fill(rgba(255, 0, 0, 255))
|
||||
var b = newImage(100, 100)
|
||||
b.fill(rgba(0, 255, 0, 255))
|
||||
var c = a.drawFast3(b, translate(vec2(25, 25)), bmNormal)
|
||||
var a = newImageFill(1000, 1000, rgba(255, 0, 0, 255))
|
||||
var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
var c = a.drawStepper(b, translate(vec2(25, 25)), bmNormal)
|
||||
tmp += c.width * c.height
|
||||
echo tmp
|
||||
|
||||
timeIt "drawStepperInPlace":
|
||||
var tmp = 0
|
||||
for i in 0 ..< 1000:
|
||||
var a = newImageFill(1000, 1000, rgba(0, 255, 0, 255))
|
||||
var b = newImageFill(100, 100, rgba(0, 255, 0, 255))
|
||||
drawStepperInPlace(a, b, translate(vec2(25, 25)), bmNormal)
|
||||
tmp += a.width * a.height
|
||||
echo tmp
|
||||
|
|
Binary file not shown.
Before ![]() (image error) Size: 342 B After ![]() (image error) Size: 342 B ![]() ![]() |
Binary file not shown.
Before ![]() (image error) Size: 342 B After ![]() (image error) Size: 342 B ![]() ![]() |
Binary file not shown.
Before ![]() (image error) Size: 342 B After ![]() (image error) Size: 342 B ![]() ![]() |
BIN
tests/images/bench.drawBlendSmooth2.bmNormal.png
Normal file
BIN
tests/images/bench.drawBlendSmooth2.bmNormal.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 342 B |
BIN
tests/images/bench.drawCorrect.bmNormal.png
Normal file
BIN
tests/images/bench.drawCorrect.bmNormal.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 342 B |
Binary file not shown.
Before ![]() (image error) Size: 342 B After ![]() (image error) Size: 342 B ![]() ![]() |
BIN
tests/images/bench.drawStepper.bmNormal.png
Normal file
BIN
tests/images/bench.drawStepper.bmNormal.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 342 B |
Loading…
Reference in a new issue