|
@ -60,7 +60,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
|
||||||
|
|
||||||
when defined(pixieDebugSvg):
|
when defined(pixieDebugSvg):
|
||||||
proc maybeLogPair(k, v: string) =
|
proc maybeLogPair(k, v: string) =
|
||||||
if k notin [ # Handled, never need to log
|
if k notin [
|
||||||
"fill-rule", "fill", "stroke", "stroke-width", "stroke-linecap",
|
"fill-rule", "fill", "stroke", "stroke-width", "stroke-linecap",
|
||||||
"stroke-linejoin", "stroke-miterlimit", "stroke-dasharray",
|
"stroke-linejoin", "stroke-miterlimit", "stroke-dasharray",
|
||||||
"transform", "style", "version", "viewBox", "width", "height",
|
"transform", "style", "version", "viewBox", "width", "height",
|
||||||
|
|
|
@ -209,10 +209,55 @@ proc minifyBy2*(image: Image, power = 1): Image =
|
||||||
return image.copy()
|
return image.copy()
|
||||||
|
|
||||||
var src = image
|
var src = image
|
||||||
for i in 1 .. power:
|
for _ in 1 .. power:
|
||||||
result = newImage(src.width div 2, src.height div 2)
|
result = newImage(src.width div 2, src.height div 2)
|
||||||
for y in 0 ..< result.height:
|
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
|
let
|
||||||
a = src.getRgbaUnsafe(x * 2 + 0, y * 2 + 0)
|
a = src.getRgbaUnsafe(x * 2 + 0, y * 2 + 0)
|
||||||
b = src.getRgbaUnsafe(x * 2 + 1, 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
|
let scale = 2 ^ power
|
||||||
result = newImage(image.width * scale, image.height * scale)
|
result = newImage(image.width * scale, image.height * scale)
|
||||||
for y in 0 ..< result.height:
|
for y in 0 ..< result.height:
|
||||||
for x in 0 ..< result.width:
|
for x in 0 ..< image.width:
|
||||||
var rgba = image.getRgbaUnsafe(x div scale, y div scale)
|
let
|
||||||
result.setRgbaUnsafe(x, y, rgba)
|
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) =
|
proc applyOpacity*(target: Image | Mask, opacity: float32) =
|
||||||
## Multiplies alpha of the image by opacity.
|
## Multiplies alpha of the image by opacity.
|
||||||
|
@ -356,6 +405,8 @@ proc blur*(
|
||||||
let radius = round(radius).int
|
let radius = round(radius).int
|
||||||
if radius == 0:
|
if radius == 0:
|
||||||
return
|
return
|
||||||
|
if radius < 0:
|
||||||
|
raise newException(PixieError, "Cannot apply negative blur")
|
||||||
|
|
||||||
let
|
let
|
||||||
kernel = gaussianKernel(radius)
|
kernel = gaussianKernel(radius)
|
||||||
|
@ -500,7 +551,7 @@ proc drawCorrect(
|
||||||
dy = matInv * vec2(0 + h, 1 + h) - p
|
dy = matInv * vec2(0 + h, 1 + h) - p
|
||||||
minFilterBy2 = max(dx.length, dy.length)
|
minFilterBy2 = max(dx.length, dy.length)
|
||||||
|
|
||||||
while minFilterBy2 > 2:
|
while minFilterBy2 >= 2:
|
||||||
b = b.minifyBy2()
|
b = b.minifyBy2()
|
||||||
dx /= 2
|
dx /= 2
|
||||||
dy /= 2
|
dy /= 2
|
||||||
|
@ -557,7 +608,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
|
||||||
minFilterBy2 = max(dx.length, dy.length)
|
minFilterBy2 = max(dx.length, dy.length)
|
||||||
b = b
|
b = b
|
||||||
|
|
||||||
while minFilterBy2 > 2.0:
|
while minFilterBy2 >= 2.0:
|
||||||
b = b.minifyBy2()
|
b = b.minifyBy2()
|
||||||
p /= 2
|
p /= 2
|
||||||
dx /= 2
|
dx /= 2
|
||||||
|
@ -772,7 +823,7 @@ proc drawTiled*(dest, src: Image, mat: Mat3, blendMode = bmNormal) =
|
||||||
dest.drawCorrect(src, mat, true, blendMode)
|
dest.drawCorrect(src, mat, true, blendMode)
|
||||||
|
|
||||||
proc resize*(srcImage: Image, width, height: int): Image =
|
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:
|
if width == srcImage.width and height == srcImage.height:
|
||||||
result = srcImage.copy()
|
result = srcImage.copy()
|
||||||
else:
|
else:
|
||||||
|
@ -804,12 +855,9 @@ proc shadow*(
|
||||||
): Image =
|
): Image =
|
||||||
## Create a shadow of the image with the offset, spread and blur.
|
## Create a shadow of the image with the offset, spread and blur.
|
||||||
let mask = image.newMask()
|
let mask = image.newMask()
|
||||||
if offset != vec2(0, 0):
|
mask.shift(offset)
|
||||||
mask.shift(offset)
|
mask.spread(spread)
|
||||||
if spread > 0:
|
mask.blur(blur)
|
||||||
mask.spread(spread)
|
|
||||||
if blur > 0:
|
|
||||||
mask.blur(blur)
|
|
||||||
result = newImage(mask.width, mask.height)
|
result = newImage(mask.width, mask.height)
|
||||||
result.fill(color)
|
result.fill(color)
|
||||||
result.draw(mask, blendMode = bmMask)
|
result.draw(mask, blendMode = bmMask)
|
||||||
|
|
|
@ -35,6 +35,12 @@ timeIt "minifyBy2":
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
|
timeIt "magnifyBy2":
|
||||||
|
let minified = image.magnifyBy2()
|
||||||
|
doAssert minified[0, 0] == rgba(63, 127, 191, 191)
|
||||||
|
|
||||||
|
reset()
|
||||||
|
|
||||||
timeIt "invert":
|
timeIt "invert":
|
||||||
image.invert()
|
image.invert()
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,17 @@ block:
|
||||||
a.draw(b, translate(vec2(25.2, 25.2)), bmNormal)
|
a.draw(b, translate(vec2(25.2, 25.2)), bmNormal)
|
||||||
keep(b)
|
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:
|
block:
|
||||||
let
|
let
|
||||||
a = newImage(100, 100)
|
a = newImage(100, 100)
|
||||||
|
|
BIN
tests/images/magnifiedBy2.png
Normal file
After Width: | Height: | Size: 353 B |
BIN
tests/images/magnifiedBy4.png
Normal file
After Width: | Height: | Size: 408 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 342 B |
Before Width: | Height: | Size: 370 B |
Before Width: | Height: | Size: 342 B |
Before Width: | Height: | Size: 359 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.7 KiB |
BIN
tests/images/minifiedBaboon.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
tests/images/scaleHalf.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
|
@ -93,12 +93,30 @@ block:
|
||||||
b = a.minifyBy2()
|
b = a.minifyBy2()
|
||||||
b.writeFile("tests/images/minifiedBy2.png")
|
b.writeFile("tests/images/minifiedBy2.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let
|
||||||
|
a = readImage("tests/images/minifiedBy2.png")
|
||||||
|
b = a.magnifyBy2()
|
||||||
|
b.writeFile("tests/images/magnifiedBy2.png")
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
a = readImage("tests/images/flipped1.png")
|
a = readImage("tests/images/flipped1.png")
|
||||||
b = a.minifyBy2(2)
|
b = a.minifyBy2(2)
|
||||||
b.writeFile("tests/images/minifiedBy4.png")
|
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:
|
block:
|
||||||
let a = newImage(100, 100)
|
let a = newImage(100, 100)
|
||||||
a.fill(rgbx(50, 100, 150, 200))
|
a.fill(rgbx(50, 100, 150, 200))
|
||||||
|
|
|
@ -117,3 +117,13 @@ block:
|
||||||
ctx.image.fill(rgba(0, 255, 255, 255))
|
ctx.image.fill(rgba(0, 255, 255, 255))
|
||||||
ctx.strokePolygon(vec2(50, 50), 30, 6)
|
ctx.strokePolygon(vec2(50, 50), 30, 6)
|
||||||
ctx.image.writeFile("tests/images/strokePolygon.png")
|
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")
|
||||||
|
|