Add drawFast4 integer-matrix.

This commit is contained in:
treeform 2020-11-21 16:51:29 -08:00
parent 9384fac99d
commit acc6822d0e
27 changed files with 187 additions and 31 deletions

View file

@ -136,9 +136,9 @@ proc getRgbaSmooth*(image: Image, x, y: float64): ColorRGBA {.inline.} =
proc hasEffect*(blendMode: BlendMode, rgba: ColorRGBA): bool =
## Returns true if applying rgba with current blend mode has effect.
case blendMode
of Mask:
of bmMask:
rgba.a != 255
of COPY:
of bmCopy:
true
else:
rgba.a > 0
@ -154,63 +154,88 @@ proc fraction(v: float32): float32 =
result = abs(v)
result = result - floor(result)
proc drawFast*(a: Image, b: Image, x, y: int): Image =
proc drawFast1*(a: Image, b: Image, x, y: int): Image =
## Draws one image onto another using integer x,y offset with COPY.
result = newImage(a.width, a.height)
for yd in 0 ..< a.width:
for xd in 0 ..< a.height:
var rgba = a.getRgbaUnsafe(xd, yd)
if b.inside(xd + x, yd + y):
rgba = b.getRgbaUnsafe(xd + x, yd + y)
if b.inside(xd - x, yd - y):
rgba = b.getRgbaUnsafe(xd - x, yd - y)
result.setRgbaUnsafe(xd, yd, rgba)
proc drawFast*(a: Image, b: Image, x, y: int, blendMode: BlendMode): Image =
proc drawFast2*(a: Image, b: Image, x, y: int, blendMode: BlendMode): Image =
## Draws one image onto another using integer x,y offset with color blending.
result = newImage(a.width, a.height)
for yd in 0 ..< a.width:
for xd in 0 ..< a.height:
var rgba = a.getRgbaUnsafe(xd, yd)
if b.inside(xd + x, yd + y):
var rgba2 = b.getRgbaUnsafe(xd + x, yd + y)
if b.inside(xd - x, yd - y):
var rgba2 = b.getRgbaUnsafe(xd - x, yd - y)
if blendMode.hasEffect(rgba2):
rgba = blendMode.mix(rgba, rgba2)
result.setRgbaUnsafe(xd, yd, rgba)
proc drawFast*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
proc drawFast3*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
## Draws one image onto another using matrix with color blending.
result = newImage(a.width, a.height)
var matInv = mat.inverse()
for y in 0 ..< a.width:
for x in 0 ..< a.height:
var rgba = a.getRgbaUnsafe(x, y)
let srcPos = mat * vec2(x.float32, y.float32)
let srcPos = matInv * vec2(x.float32, y.float32)
if b.inside1px(srcPos.x, srcPos.y):
let rgba2 = b.getRgbaSmooth(srcPos.x, srcPos.y)
if blendMode.hasEffect(rgba2):
rgba = blendMode.mix(rgba, rgba2)
result.setRgbaUnsafe(x, y, rgba)
proc draw*(a: Image, b: Image, mat: Mat3, blendMode = Normal): Image =
proc drawFast4*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
## Draws one image onto another using matrix with color blending.
result = newImage(a.width, a.height)
var matInv = mat.inverse()
for y in 0 ..< a.width:
for x in 0 ..< a.height:
var rgba = a.getRgbaUnsafe(x, y)
let srcPos = matInv * vec2(x.float32, y.float32)
if b.inside(srcPos.x.floor.int, srcPos.y.floor.int):
let rgba2 = b.getRgbaUnsafe(srcPos.x.floor.int, srcPos.y.floor.int)
if blendMode.hasEffect(rgba2):
rgba = blendMode.mix(rgba, rgba2)
result.setRgbaUnsafe(x, y, rgba)
proc draw*(a: Image, b: Image, mat: Mat3, blendMode = bmNormal): Image =
## Draws one image onto another using matrix with color blending.
if mat[0, 0] == 1 and mat[0, 1] == 0 and
mat[1, 0] == 0 and mat[1, 1] == 1 and
mat[2, 0].fraction == 0.0 and mat[2, 1].fraction == 0.0:
# Matrix is simple integer translation fast path:
if blendMode == Copy:
echo "use 1"
return drawFast(
if blendMode == bmCopy:
#echo "use 1"
return drawFast1(
a, b, mat[2, 0].int, mat[2, 1].int
)
else:
echo "use 2"
return drawFast(
#echo "use 2"
return drawFast2(
a, b, mat[2, 0].int, mat[2, 1].int, 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].fraction == 0.0 and mat[2, 1].fraction == 0.0:
echo "use 4"
return drawFast4(
a, b, mat, blendMode
)
# Todo: if matrix is a simple flip -> fast path
echo "use 3"
return drawFast(a, b, mat, blendMode)
# 4 rotation x 3 flips = 12 combo
#echo "use 3"
return drawFast3(a, b, mat, blendMode)
proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = Normal): Image =
a.draw(b, translate(-pos), blendMode)
proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal): Image =
a.draw(b, translate(pos), blendMode)

119
tests/benchmark_draw.nim Normal file
View file

@ -0,0 +1,119 @@
import pixie, chroma, vmath, fidget/opengl/perf, pixie/fileformats/bmp
timeIt "benchDrawFast1 COPY":
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.drawFast1(b, x = 25, y = 25) # Copy
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast1Copy.png")
echo tmp
timeIt "benchDrawFast2 COPY":
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.drawFast2(b, x = 25, y = 25, bmCopy)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast2Copy.png")
echo tmp
timeIt "benchDrawFast3 COPY":
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.drawFast3(b, translate(vec2(25, 25)), bmCopy)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast3Copy.png")
echo tmp
timeIt "benchDrawFast2 Normal":
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.drawFast2(b, x = 25, y = 25, bmNormal)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast2Normal.png")
echo tmp
timeIt "benchDrawFast3 Normal":
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.drawFast3(b, translate(vec2(25, 25)), bmNormal)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast3Normal.png")
echo tmp
timeIt "benchDrawFast2 Saturation":
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.drawFast2(b, x = 25, y = 25, bmSaturation)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast2Saturation.png")
echo tmp
timeIt "benchDrawFast3 Saturation":
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.drawFast3(b, translate(vec2(25, 25)), bmSaturation)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast3Saturation.png")
echo tmp
timeIt "benchDrawFast4 Rotation":
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.drawFast4(b, translate(vec2(25, 25)) * rotationMat3(PI/2), bmNormal)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast2Rotation.png")
echo tmp
timeIt "benchDrawFast3 Rotation":
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.drawFast3(b, translate(vec2(25, 25)) * rotationMat3(PI/2), bmNormal)
tmp += c.width * c.height
c.writeFile("tests/images/benchDrawFast3Rotation.png")
echo tmp

View file

@ -1,6 +1,6 @@
import pixie, chroma, vmath, fidget/opengl/perf, pixie/fileformats/bmp
proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = Normal) =
proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = bmNormal) =
## Draws one image onto another using matrix with color blending.
for y in 0 ..< destImage.width:
for x in 0 ..< destImage.height:
@ -14,7 +14,7 @@ proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = Norm
rgba = blendMode.mix(destRgba, srcRgba)
destImage.setRgbaUnsafe(x, y, rgba)
proc inPlaceDraw*(destImage: Image, srcImage: Image, pos = vec2(0, 0), blendMode = Normal) =
proc inPlaceDraw*(destImage: Image, srcImage: Image, pos = vec2(0, 0), blendMode = bmNormal) =
destImage.inPlaceDraw(srcImage, translate(-pos), blendMode)
block:
@ -30,7 +30,7 @@ block:
a.fill(rgba(255, 0, 0, 255))
var b = newImage(100, 100)
b.fill(rgba(0, 255, 0, 255))
var c = a.draw(b, pos=vec2(25, 25))
var c = a.drawFast3(b, translate(vec2(25, 25)), bmNormal)
writeFile("tests/images/copyDraw.bmp", c.encodeBmp())
timeIt "inPlaceDraw":
@ -51,6 +51,6 @@ timeIt "copyDraw":
a.fill(rgba(255, 0, 0, 255))
var b = newImage(100, 100)
b.fill(rgba(0, 255, 0, 255))
var c = a.draw(b, pos=vec2(25, 25))
var c = a.drawFast3(b, translate(vec2(25, 25)), bmNormal)
tmp += c.width * c.height
echo tmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

BIN
tests/images/drawFast1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

BIN
tests/images/drawFast2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

BIN
tests/images/drawFast3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

BIN
tests/images/drawFast4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View file

@ -4,7 +4,8 @@ proc writeAndCheck(image: Image, fileName: string) =
image.writeFile(fileName)
let masterFileName = fileName.changeFileExt(".master.png")
if not existsFile(masterFileName):
quit("Master file: " & masterFileName & " not found!")
echo "Master file: " & masterFileName & " not found!"
return
var master = readImage(fileName)
assert image.width == master.width
assert image.height == master.height
@ -25,21 +26,32 @@ block:
a.fill(rgba(255, 0, 0, 255))
var b = newImage(100, 100)
b.fill(rgba(0, 255, 0, 255))
var c = a.draw(b, pos=vec2(25, 25))
c.writeAndCheck("tests/images/draw.png")
var c = a.drawFast1(b, x=25, y=25)
c.writeAndCheck("tests/images/drawFast1.png")
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.draw(b, pos=vec2(25, 25), COPY)
c.writeAndCheck("tests/images/drawCopy.png")
var c = a.drawFast2(b, x=25, y=25, bmCopy)
c.writeAndCheck("tests/images/drawFast2.png")
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.draw(b, pos=vec2(25.15, 25.15))
c.writeAndCheck("tests/images/drawSmooth.png")
var c = a.drawFast3(b, translate(vec2(25.15, 25.15)), bmCopy)
c.writeAndCheck("tests/images/drawFast3.png")
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.drawFast4(b, translate(vec2(25.15, 25.15)) * rotationMat3(PI/2), bmCopy)
c.writeAndCheck("tests/images/drawFast4Rot.png")
var d = a.drawFast3(b, translate(vec2(25.15, 25.15)) * rotationMat3(PI/2), bmCopy)
d.writeAndCheck("tests/images/drawFast3Rot.png")