Both draw in place and not in place done.
This commit is contained in:
parent
58e6950b51
commit
57af191774
10 changed files with 332 additions and 66 deletions
BIN
cairotest.png
Normal file
BIN
cairotest.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 800 B |
BIN
screen.png
Normal file
BIN
screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
|
@ -4,7 +4,7 @@ import pixie/images, pixie/masks, pixie/paths, pixie/common, pixie/blends,
|
||||||
pixie/fileformats/bmp, pixie/fileformats/png, pixie/fileformats/jpg,
|
pixie/fileformats/bmp, pixie/fileformats/png, pixie/fileformats/jpg,
|
||||||
flatty/binny, os
|
flatty/binny, os
|
||||||
|
|
||||||
export images, masks, paths, PixieError, blends
|
export images, masks, paths, common, blends
|
||||||
|
|
||||||
type
|
type
|
||||||
FileFormat* = enum
|
FileFormat* = enum
|
||||||
|
|
|
@ -632,7 +632,10 @@ proc blendSaturation*(a, b: ColorRGBA): ColorRGBA =
|
||||||
blendSaturation(a.color, b.color).rgba
|
blendSaturation(a.color, b.color).rgba
|
||||||
|
|
||||||
proc blendMask*(a, b: ColorRGBA): ColorRGBA =
|
proc blendMask*(a, b: ColorRGBA): ColorRGBA =
|
||||||
blendMask(a.color, b.color).rgba
|
result.r = a.r
|
||||||
|
result.g = a.g
|
||||||
|
result.b = a.b
|
||||||
|
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
|
blendSubtractMask(a.color, b.color).rgba
|
||||||
|
|
28
src/pixie/cairotest.nim
Normal file
28
src/pixie/cairotest.nim
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import cairo, math, times
|
||||||
|
|
||||||
|
var
|
||||||
|
surface = imageSurfaceCreate(FORMAT_ARGB32, 256, 256)
|
||||||
|
ctx = surface.create()
|
||||||
|
|
||||||
|
let start = epochTime()
|
||||||
|
|
||||||
|
ctx.setSourceRGB(0, 0, 1)
|
||||||
|
ctx.newPath() # current path is not consumed by ctx.clip()
|
||||||
|
ctx.rectangle(96, 96, 128, 128)
|
||||||
|
ctx.fill()
|
||||||
|
|
||||||
|
ctx.setSourceRGB(0, 1, 0)
|
||||||
|
ctx.newPath() # current path is not consumed by ctx.clip()
|
||||||
|
ctx.rectangle(64, 64, 128, 128)
|
||||||
|
ctx.fill()
|
||||||
|
|
||||||
|
for i in 0 .. 10000:
|
||||||
|
|
||||||
|
ctx.setSourceRGB(1, 0, 0)
|
||||||
|
ctx.newPath() # current path is not consumed by ctx.clip()
|
||||||
|
ctx.rectangle(32, 32, 128, 128)
|
||||||
|
ctx.fill()
|
||||||
|
|
||||||
|
echo epochTime() - start
|
||||||
|
|
||||||
|
discard surface.writeToPng("cairotest.png")
|
|
@ -1,2 +1,34 @@
|
||||||
|
import vmath
|
||||||
|
|
||||||
type
|
type
|
||||||
PixieError* = object of ValueError ## Raised if an operation fails.
|
PixieError* = object of ValueError ## Raised if an operation fails.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
57
src/pixie/gputest.nim
Normal file
57
src/pixie/gputest.nim
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import staticglfw, opengl, pixie, times
|
||||||
|
|
||||||
|
if init() == 0:
|
||||||
|
raise newException(Exception, "Failed to Initialize GLFW")
|
||||||
|
windowHint(VISIBLE, false.cint)
|
||||||
|
var window = createWindow(512, 512, "GLFW3 WINDOW", nil, nil)
|
||||||
|
window.makeContextCurrent()
|
||||||
|
# This must be called to make any GL function work
|
||||||
|
loadExtensions()
|
||||||
|
|
||||||
|
|
||||||
|
let start = epochTime()
|
||||||
|
|
||||||
|
# Draw red color screen.
|
||||||
|
glClearColor(1, 1, 1, 1)
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT)
|
||||||
|
|
||||||
|
glLoadIdentity()
|
||||||
|
glTranslatef(-0.25, -0.25, 0)
|
||||||
|
glBegin(GL_QUADS)
|
||||||
|
glColor3f(1.0, 0.0, 0.0)
|
||||||
|
glVertex2f(0.0, 0.0)
|
||||||
|
glVertex2f(1.0, 0.0)
|
||||||
|
glVertex2f(1.0, 1.0)
|
||||||
|
glVertex2f(0.0, 1.0)
|
||||||
|
glEnd()
|
||||||
|
|
||||||
|
glTranslatef(-0.25, -0.25, 0)
|
||||||
|
glBegin(GL_QUADS)
|
||||||
|
glColor3f(0.0, 0.0, 1.0)
|
||||||
|
glVertex2f(0.0, 0.0)
|
||||||
|
glVertex2f(1.0, 0.0)
|
||||||
|
glVertex2f(1.0, 1.0)
|
||||||
|
glVertex2f(0.0, 1.0)
|
||||||
|
glEnd()
|
||||||
|
|
||||||
|
glTranslatef(-0.25, -0.25, 0)
|
||||||
|
glBegin(GL_QUADS)
|
||||||
|
glColor3f(0.0, 1.0, 0.0)
|
||||||
|
glVertex2f(0.0, 0.0)
|
||||||
|
glVertex2f(1.0, 0.0)
|
||||||
|
glVertex2f(1.0, 1.0)
|
||||||
|
glVertex2f(0.0, 1.0)
|
||||||
|
glEnd()
|
||||||
|
|
||||||
|
var screen = newImage(512, 512)
|
||||||
|
glReadPixels(
|
||||||
|
0, 0,
|
||||||
|
512, 512,
|
||||||
|
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||||
|
screen.data[0].addr
|
||||||
|
)
|
||||||
|
|
||||||
|
echo epochTime() - start
|
||||||
|
|
||||||
|
|
||||||
|
screen.writeFile("screen.png")
|
|
@ -290,6 +290,7 @@ proc drawCorrect*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
|
||||||
stepX /= 2
|
stepX /= 2
|
||||||
stepY /= 2
|
stepY /= 2
|
||||||
minFilterBy2 /= 2
|
minFilterBy2 /= 2
|
||||||
|
matInv = matInv * scale(vec2(0.5, 0.5))
|
||||||
|
|
||||||
for y in 0 ..< a.height:
|
for y in 0 ..< a.height:
|
||||||
for x in 0 ..< a.width:
|
for x in 0 ..< a.width:
|
||||||
|
@ -363,10 +364,11 @@ proc drawStepper*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
|
||||||
stepX /= 2
|
stepX /= 2
|
||||||
stepY /= 2
|
stepY /= 2
|
||||||
minFilterBy2 /= 2
|
minFilterBy2 /= 2
|
||||||
|
matInv = matInv * scale(vec2(0.5, 0.5))
|
||||||
|
|
||||||
template forBlend(
|
template forBlend(
|
||||||
mixer: proc(a, b: ColorRGBA): ColorRGBA,
|
mixer: proc(a, b: ColorRGBA): ColorRGBA,
|
||||||
getRgba: proc(a: Image, x, y: float32): ColorRGBA {.inline.},
|
getRgbaFn: proc(a: Image, x, y: float32): ColorRGBA {.inline.},
|
||||||
) =
|
) =
|
||||||
for y in 0 ..< a.height:
|
for y in 0 ..< a.height:
|
||||||
var
|
var
|
||||||
|
@ -398,11 +400,10 @@ proc drawStepper*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
|
||||||
copyMem(result.getAddr(0, y), a.getAddr(0, y), 4*xMin)
|
copyMem(result.getAddr(0, y), a.getAddr(0, y), 4*xMin)
|
||||||
|
|
||||||
for x in xMin ..< xMax:
|
for x in xMin ..< xMax:
|
||||||
let srcV = start + stepX * float32(x) + stepY * float32(y)
|
let srcPos = start + stepX * float32(x) + stepY * float32(y)
|
||||||
|
#let srcPos = matInv * vec2(x.float32 + h, y.float32 + h)
|
||||||
var rgba = a.getRgbaUnsafe(x, y)
|
var rgba = a.getRgbaUnsafe(x, y)
|
||||||
# TODO maybe remove inside check?
|
let rgba2 = b.getRgbaFn(srcPos.x - h, srcPos.y - h)
|
||||||
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)
|
rgba = mixer(rgba, rgba2)
|
||||||
result.setRgbaUnsafe(x, y, rgba)
|
result.setRgbaUnsafe(x, y, rgba)
|
||||||
|
|
||||||
|
@ -411,36 +412,38 @@ proc drawStepper*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
|
||||||
if a.width - xMax > 0:
|
if a.width - xMax > 0:
|
||||||
copyMem(result.getAddr(xMax, y), a.getAddr(xMax, y), 4*(a.width - xMax))
|
copyMem(result.getAddr(xMax, y), a.getAddr(xMax, y), 4*(a.width - xMax))
|
||||||
|
|
||||||
proc getRgba(a: Image, x, y: float32): ColorRGBA {.inline.} =
|
proc getRgbaUnsafe(a: Image, x, y: float32): ColorRGBA {.inline.} =
|
||||||
a.getRgbaUnsafe(x.int, y.int)
|
a.getRgbaUnsafe(x.round.int, y.round.int)
|
||||||
|
|
||||||
# TODO check pos for fractional
|
if stepX.length == 1.0 and stepY.length == 1.0 and
|
||||||
if stepX.length == 1.0 and stepY.length == 1.0:
|
mat[2, 0].fractional == 0.0 and mat[2, 1].fractional == 0.0:
|
||||||
|
#echo "copy non-smooth"
|
||||||
case blendMode
|
case blendMode
|
||||||
of bmNormal: forBlend(blendNormal, getRgba)
|
of bmNormal: forBlend(blendNormal, getRgbaUnsafe)
|
||||||
of bmDarken: forBlend(blendDarken, getRgba)
|
of bmDarken: forBlend(blendDarken, getRgbaUnsafe)
|
||||||
of bmMultiply: forBlend(blendMultiply, getRgba)
|
of bmMultiply: forBlend(blendMultiply, getRgbaUnsafe)
|
||||||
of bmLinearBurn: forBlend(blendLinearBurn, getRgba)
|
of bmLinearBurn: forBlend(blendLinearBurn, getRgbaUnsafe)
|
||||||
of bmColorBurn: forBlend(blendColorBurn, getRgba)
|
of bmColorBurn: forBlend(blendColorBurn, getRgbaUnsafe)
|
||||||
of bmLighten: forBlend(blendLighten, getRgba)
|
of bmLighten: forBlend(blendLighten, getRgbaUnsafe)
|
||||||
of bmScreen: forBlend(blendScreen, getRgba)
|
of bmScreen: forBlend(blendScreen, getRgbaUnsafe)
|
||||||
of bmLinearDodge: forBlend(blendLinearDodge, getRgba)
|
of bmLinearDodge: forBlend(blendLinearDodge, getRgbaUnsafe)
|
||||||
of bmColorDodge: forBlend(blendColorDodge, getRgba)
|
of bmColorDodge: forBlend(blendColorDodge, getRgbaUnsafe)
|
||||||
of bmOverlay: forBlend(blendOverlay, getRgba)
|
of bmOverlay: forBlend(blendOverlay, getRgbaUnsafe)
|
||||||
of bmSoftLight: forBlend(blendSoftLight, getRgba)
|
of bmSoftLight: forBlend(blendSoftLight, getRgbaUnsafe)
|
||||||
of bmHardLight: forBlend(blendHardLight, getRgba)
|
of bmHardLight: forBlend(blendHardLight, getRgbaUnsafe)
|
||||||
of bmDifference: forBlend(blendDifference, getRgba)
|
of bmDifference: forBlend(blendDifference, getRgbaUnsafe)
|
||||||
of bmExclusion: forBlend(blendExclusion, getRgba)
|
of bmExclusion: forBlend(blendExclusion, getRgbaUnsafe)
|
||||||
of bmHue: forBlend(blendHue, getRgba)
|
of bmHue: forBlend(blendHue, getRgbaUnsafe)
|
||||||
of bmSaturation: forBlend(blendSaturation, getRgba)
|
of bmSaturation: forBlend(blendSaturation, getRgbaUnsafe)
|
||||||
of bmColor: forBlend(blendColor, getRgba)
|
of bmColor: forBlend(blendColor, getRgbaUnsafe)
|
||||||
of bmLuminosity: forBlend(blendLuminosity, getRgba)
|
of bmLuminosity: forBlend(blendLuminosity, getRgbaUnsafe)
|
||||||
of bmMask: forBlend(blendMask, getRgba)
|
of bmMask: forBlend(blendMask, getRgbaUnsafe)
|
||||||
of bmOverwrite: forBlend(blendOverwrite, getRgba)
|
of bmOverwrite: forBlend(blendOverwrite, getRgbaUnsafe)
|
||||||
of bmSubtractMask: forBlend(blendSubtractMask, getRgba)
|
of bmSubtractMask: forBlend(blendSubtractMask, getRgbaUnsafe)
|
||||||
of bmIntersectMask: forBlend(blendIntersectMask, getRgba)
|
of bmIntersectMask: forBlend(blendIntersectMask, getRgbaUnsafe)
|
||||||
of bmExcludeMask: forBlend(blendExcludeMask, getRgba)
|
of bmExcludeMask: forBlend(blendExcludeMask, getRgbaUnsafe)
|
||||||
else:
|
else:
|
||||||
|
#echo "copy smooth"
|
||||||
case blendMode
|
case blendMode
|
||||||
of bmNormal: forBlend(blendNormal, getRgbaSmooth)
|
of bmNormal: forBlend(blendNormal, getRgbaSmooth)
|
||||||
of bmDarken: forBlend(blendDarken, getRgbaSmooth)
|
of bmDarken: forBlend(blendDarken, getRgbaSmooth)
|
||||||
|
@ -486,6 +489,168 @@ proc draw*(a: Image, b: Image, mat: Mat3, blendMode = bmNormal): Image =
|
||||||
proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal): Image =
|
proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal): Image =
|
||||||
a.draw(b, translate(pos), blendMode)
|
a.draw(b, translate(pos), blendMode)
|
||||||
|
|
||||||
|
proc drawInPlace*(a: Image, b: Image, mat: Mat3, blendMode = bmNormal) =
|
||||||
|
## Draws one image onto another using matrix with color blending.
|
||||||
|
|
||||||
|
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
|
||||||
|
matInv = matInv * scale(vec2(0.5, 0.5))
|
||||||
|
|
||||||
|
template forBlend(
|
||||||
|
mixer: proc(a, b: ColorRGBA): ColorRGBA,
|
||||||
|
getRgbaFn: 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 xMin ..< xMax:
|
||||||
|
let srcPos = start + stepX * float32(x) + stepY * float32(y)
|
||||||
|
#let srcPos = matInv * vec2(x.float32 + h, y.float32 + h)
|
||||||
|
var rgba = a.getRgbaUnsafe(x, y)
|
||||||
|
let rgba2 = b.getRgbaFn(srcPos.x - h, srcPos.y - h)
|
||||||
|
rgba = mixer(rgba, rgba2)
|
||||||
|
a.setRgbaUnsafe(x, y, rgba)
|
||||||
|
|
||||||
|
proc getRgbaUnsafe(a: Image, x, y: float32): ColorRGBA {.inline.} =
|
||||||
|
a.getRgbaUnsafe(x.round.int, y.round.int)
|
||||||
|
|
||||||
|
if stepX.length == 1.0 and stepY.length == 1.0 and
|
||||||
|
mat[2, 0].fractional == 0.0 and mat[2, 1].fractional == 0.0:
|
||||||
|
#echo "inplace non-smooth"
|
||||||
|
case blendMode
|
||||||
|
of bmNormal: forBlend(blendNormal, getRgbaUnsafe)
|
||||||
|
of bmDarken: forBlend(blendDarken, getRgbaUnsafe)
|
||||||
|
of bmMultiply: forBlend(blendMultiply, getRgbaUnsafe)
|
||||||
|
of bmLinearBurn: forBlend(blendLinearBurn, getRgbaUnsafe)
|
||||||
|
of bmColorBurn: forBlend(blendColorBurn, getRgbaUnsafe)
|
||||||
|
of bmLighten: forBlend(blendLighten, getRgbaUnsafe)
|
||||||
|
of bmScreen: forBlend(blendScreen, getRgbaUnsafe)
|
||||||
|
of bmLinearDodge: forBlend(blendLinearDodge, getRgbaUnsafe)
|
||||||
|
of bmColorDodge: forBlend(blendColorDodge, getRgbaUnsafe)
|
||||||
|
of bmOverlay: forBlend(blendOverlay, getRgbaUnsafe)
|
||||||
|
of bmSoftLight: forBlend(blendSoftLight, getRgbaUnsafe)
|
||||||
|
of bmHardLight: forBlend(blendHardLight, getRgbaUnsafe)
|
||||||
|
of bmDifference: forBlend(blendDifference, getRgbaUnsafe)
|
||||||
|
of bmExclusion: forBlend(blendExclusion, getRgbaUnsafe)
|
||||||
|
of bmHue: forBlend(blendHue, getRgbaUnsafe)
|
||||||
|
of bmSaturation: forBlend(blendSaturation, getRgbaUnsafe)
|
||||||
|
of bmColor: forBlend(blendColor, getRgbaUnsafe)
|
||||||
|
of bmLuminosity: forBlend(blendLuminosity, getRgbaUnsafe)
|
||||||
|
of bmMask: forBlend(blendMask, getRgbaUnsafe)
|
||||||
|
of bmOverwrite: forBlend(blendOverwrite, getRgbaUnsafe)
|
||||||
|
of bmSubtractMask: forBlend(blendSubtractMask, getRgbaUnsafe)
|
||||||
|
of bmIntersectMask: forBlend(blendIntersectMask, getRgbaUnsafe)
|
||||||
|
of bmExcludeMask: forBlend(blendExcludeMask, getRgbaUnsafe)
|
||||||
|
else:
|
||||||
|
#echo "inplace smooth"
|
||||||
|
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 drawInPlace*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal) =
|
||||||
|
a.drawInPlace(b, translate(pos), blendMode)
|
||||||
|
|
||||||
proc blur*(image: Image, radius: float32): Image =
|
proc blur*(image: Image, radius: float32): Image =
|
||||||
## Applies Gaussian blur to the image given a radius.
|
## Applies Gaussian blur to the image given a radius.
|
||||||
let radius = (radius).int
|
let radius = (radius).int
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import vmath, images, chroma, strutils, algorithm
|
import vmath, images, chroma, strutils, algorithm, common
|
||||||
|
|
||||||
type
|
type
|
||||||
Segment* = object
|
|
||||||
## A math segment from point "at" to point "to"
|
|
||||||
at*: Vec2
|
|
||||||
to*: Vec2
|
|
||||||
|
|
||||||
PathCommandKind* = enum
|
PathCommandKind* = enum
|
||||||
## Type of path commands
|
## Type of path commands
|
||||||
|
@ -21,10 +17,6 @@ type
|
||||||
at*: Vec2
|
at*: Vec2
|
||||||
commands*: seq[PathCommand]
|
commands*: seq[PathCommand]
|
||||||
|
|
||||||
proc segment(at, to: Vec2): Segment =
|
|
||||||
result.at = at
|
|
||||||
result.to = to
|
|
||||||
|
|
||||||
proc newPath*(): Path =
|
proc newPath*(): Path =
|
||||||
result = Path()
|
result = Path()
|
||||||
|
|
||||||
|
@ -503,27 +495,6 @@ iterator zipwise*[T](s: seq[T]): (T, T) =
|
||||||
if s.len > 0:
|
if s.len > 0:
|
||||||
yield(s[^1], s[0])
|
yield(s[^1], s[0])
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
proc strokePolygons*(ps: seq[seq[Vec2]], strokeWidthR, strokeWidthL: float32): seq[seq[Vec2]] =
|
proc strokePolygons*(ps: seq[seq[Vec2]], strokeWidthR, strokeWidthL: float32): seq[seq[Vec2]] =
|
||||||
## Converts simple polygons into stroked versions:
|
## Converts simple polygons into stroked versions:
|
||||||
# TODO: Stroke location, add caps and joins.
|
# TODO: Stroke location, add caps and joins.
|
||||||
|
|
|
@ -77,6 +77,16 @@ timeIt "drawStepper bmNormal":
|
||||||
c.writeFile("tests/images/bench.drawStepper.bmNormal.png")
|
c.writeFile("tests/images/bench.drawStepper.bmNormal.png")
|
||||||
echo tmp
|
echo tmp
|
||||||
|
|
||||||
|
timeIt "drawInPlace bmNormal":
|
||||||
|
var tmp = 0
|
||||||
|
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))
|
||||||
|
a.drawInPlace(b, translate(vec2(25, 25)), bmNormal)
|
||||||
|
tmp += a.width * a.height
|
||||||
|
#a.writeFile("tests/images/bench.drawStepper.bmNormal.png")
|
||||||
|
echo tmp
|
||||||
|
|
||||||
# timeIt "drawBlend bmSaturation":
|
# timeIt "drawBlend bmSaturation":
|
||||||
# var tmp = 0
|
# var tmp = 0
|
||||||
# var c: Image
|
# var c: Image
|
||||||
|
|
Loading…
Reference in a new issue