|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
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.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))
|
||||
|
|
|
@ -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")
|
||||
|
|