Merge pull request #229 from guzba/master

performance improvements
This commit is contained in:
treeform 2021-06-17 23:18:09 -07:00 committed by GitHub
commit 818d7522c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 108 additions and 15 deletions

View file

@ -60,7 +60,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
when defined(pixieDebugSvg):
proc maybeLogPair(k, v: string) =
if k notin [ # Handled, never need to log
if k notin [
"fill-rule", "fill", "stroke", "stroke-width", "stroke-linecap",
"stroke-linejoin", "stroke-miterlimit", "stroke-dasharray",
"transform", "style", "version", "viewBox", "width", "height",

View file

@ -209,10 +209,55 @@ proc minifyBy2*(image: Image, power = 1): Image =
return image.copy()
var src = image
for i in 1 .. power:
for _ in 1 .. power:
result = newImage(src.width div 2, src.height div 2)
for y in 0 ..< result.height:
for x in 0 ..< result.width:
var x: int
when defined(amd64) and not defined(pixieNoSimd):
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
first32 = cast[M128i]([uint32.high, 0, 0, 0])
for _ in countup(0, result.width - 4, 2):
let
top = mm_loadu_si128(src.data[src.dataIndex(x * 2, y * 2 + 0)].addr)
btm = mm_loadu_si128(src.data[src.dataIndex(x * 2, y * 2 + 1)].addr)
topShifted = mm_srli_si128(top, 4)
btmShifted = mm_srli_si128(btm, 4)
topEven = mm_andnot_si128(oddMask, top)
topOdd = mm_srli_epi16(mm_and_si128(top, oddMask), 8)
btmEven = mm_andnot_si128(oddMask, btm)
btmOdd = mm_srli_epi16(mm_and_si128(btm, oddMask), 8)
topShiftedEven = mm_andnot_si128(oddMask, topShifted)
topShiftedOdd = mm_srli_epi16(mm_and_si128(topShifted, oddMask), 8)
btmShiftedEven = mm_andnot_si128(oddMask, btmShifted)
btmShiftedOdd = mm_srli_epi16(mm_and_si128(btmShifted, oddMask), 8)
topAddedEven = mm_add_epi16(topEven, topShiftedEven)
btmAddedEven = mm_add_epi16(btmEven, btmShiftedEven)
topAddedOdd = mm_add_epi16(topOdd, topShiftedOdd)
bottomAddedOdd = mm_add_epi16(btmOdd, btmShiftedOdd)
addedEven = mm_add_epi16(topAddedEven, btmAddedEven)
addedOdd = mm_add_epi16(topAddedOdd, bottomAddedOdd)
addedEvenDiv4 = mm_srli_epi16(addedEven, 2)
addedOddDiv4 = mm_srli_epi16(addedOdd, 2)
merged = mm_or_si128(addedEvenDiv4, mm_slli_epi16(addedOddDiv4, 8))
# merged [0, 1, 2, 3] has the correct values for the next two pixels
# at index 0 and 2 so shift those into position and store
zero = mm_and_si128(merged, first32)
two = mm_and_si128(mm_srli_si128(merged, 8), first32)
zeroTwo = mm_or_si128(zero, mm_slli_si128(two, 4))
mm_storeu_si128(result.data[result.dataIndex(x, y)].addr, zeroTwo)
x += 2
for x in x ..< result.width:
let
a = src.getRgbaUnsafe(x * 2 + 0, y * 2 + 0)
b = src.getRgbaUnsafe(x * 2 + 1, y * 2 + 0)
@ -239,9 +284,13 @@ proc magnifyBy2*(image: Image, power = 1): Image =
let scale = 2 ^ power
result = newImage(image.width * scale, image.height * scale)
for y in 0 ..< result.height:
for x in 0 ..< result.width:
var rgba = image.getRgbaUnsafe(x div scale, y div scale)
result.setRgbaUnsafe(x, y, rgba)
for x in 0 ..< image.width:
let
rgba = image.getRgbaUnsafe(x, y div scale)
scaledX = x * scale
idx = result.dataIndex(scaledX, y)
for i in 0 ..< scale:
result.data[idx + i] = rgba
proc applyOpacity*(target: Image | Mask, opacity: float32) =
## Multiplies alpha of the image by opacity.
@ -356,6 +405,8 @@ proc blur*(
let radius = round(radius).int
if radius == 0:
return
if radius < 0:
raise newException(PixieError, "Cannot apply negative blur")
let
kernel = gaussianKernel(radius)
@ -500,7 +551,7 @@ proc drawCorrect(
dy = matInv * vec2(0 + h, 1 + h) - p
minFilterBy2 = max(dx.length, dy.length)
while minFilterBy2 > 2:
while minFilterBy2 >= 2:
b = b.minifyBy2()
dx /= 2
dy /= 2
@ -557,7 +608,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
minFilterBy2 = max(dx.length, dy.length)
b = b
while minFilterBy2 > 2.0:
while minFilterBy2 >= 2.0:
b = b.minifyBy2()
p /= 2
dx /= 2
@ -772,7 +823,7 @@ proc drawTiled*(dest, src: Image, mat: Mat3, blendMode = bmNormal) =
dest.drawCorrect(src, mat, true, blendMode)
proc resize*(srcImage: Image, width, height: int): Image =
## Resize an image to a given hight and width.
## Resize an image to a given height and width.
if width == srcImage.width and height == srcImage.height:
result = srcImage.copy()
else:
@ -804,12 +855,9 @@ proc shadow*(
): Image =
## Create a shadow of the image with the offset, spread and blur.
let mask = image.newMask()
if offset != vec2(0, 0):
mask.shift(offset)
if spread > 0:
mask.spread(spread)
if blur > 0:
mask.blur(blur)
mask.shift(offset)
mask.spread(spread)
mask.blur(blur)
result = newImage(mask.width, mask.height)
result.fill(color)
result.draw(mask, blendMode = bmMask)

View file

@ -35,6 +35,12 @@ timeIt "minifyBy2":
reset()
timeIt "magnifyBy2":
let minified = image.magnifyBy2()
doAssert minified[0, 0] == rgba(63, 127, 191, 191)
reset()
timeIt "invert":
image.invert()

View file

@ -44,6 +44,17 @@ block:
a.draw(b, translate(vec2(25.2, 25.2)), bmNormal)
keep(b)
block:
let
a = newImage(1000, 1000)
b = newImage(500, 500)
a.fill(rgba(255, 0, 0, 255))
b.fill(rgba(0, 255, 0, 255))
timeIt "draw big-on-bigger bmNormal scale(0.5)":
a.draw(b, translate(vec2(25, 25)) * scale(vec2(0.5, 0.5)), bmNormal)
keep(b)
block:
let
a = newImage(100, 100)

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
tests/images/scaleHalf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

@ -93,12 +93,30 @@ block:
b = a.minifyBy2()
b.writeFile("tests/images/minifiedBy2.png")
block:
let
a = readImage("tests/images/minifiedBy2.png")
b = a.magnifyBy2()
b.writeFile("tests/images/magnifiedBy2.png")
block:
let
a = readImage("tests/images/flipped1.png")
b = a.minifyBy2(2)
b.writeFile("tests/images/minifiedBy4.png")
block:
let
a = readImage("tests/images/minifiedBy4.png")
b = a.magnifyBy2(2)
b.writeFile("tests/images/magnifiedBy4.png")
block:
let
a = readImage("tests/images/png/baboon.png")
b = a.minifyBy2()
b.writeFile("tests/images/minifiedBaboon.png")
block:
let a = newImage(100, 100)
a.fill(rgbx(50, 100, 150, 200))

View file

@ -117,3 +117,13 @@ block:
ctx.image.fill(rgba(0, 255, 255, 255))
ctx.strokePolygon(vec2(50, 50), 30, 6)
ctx.image.writeFile("tests/images/strokePolygon.png")
block:
let
a = newImage(1000, 1000)
b = newImage(500, 500)
a.fill(rgba(255, 0, 0, 255))
b.fill(rgba(0, 255, 0, 255))
a.draw(b, translate(vec2(250, 250)) * scale(vec2(0.5, 0.5)))
a.writeFile("tests/images/scaleHalf.png")