diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim index 3295158..b83783c 100644 --- a/src/pixie/fileformats/svg.nim +++ b/src/pixie/fileformats/svg.nim @@ -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", diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 91de331..f5614a7 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -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) diff --git a/tests/benchmark_images.nim b/tests/benchmark_images.nim index e74ce70..fc53bf6 100644 --- a/tests/benchmark_images.nim +++ b/tests/benchmark_images.nim @@ -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() diff --git a/tests/benchmark_images_draw.nim b/tests/benchmark_images_draw.nim index 3d34544..bf4a925 100644 --- a/tests/benchmark_images_draw.nim +++ b/tests/benchmark_images_draw.nim @@ -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) diff --git a/tests/images/magnifiedBy2.png b/tests/images/magnifiedBy2.png new file mode 100644 index 0000000..24655dc Binary files /dev/null and b/tests/images/magnifiedBy2.png differ diff --git a/tests/images/magnifiedBy4.png b/tests/images/magnifiedBy4.png new file mode 100644 index 0000000..cefd926 Binary files /dev/null and b/tests/images/magnifiedBy4.png differ diff --git a/tests/images/masters/centerRotation.png b/tests/images/masters/centerRotation.png deleted file mode 100644 index 9942445..0000000 Binary files a/tests/images/masters/centerRotation.png and /dev/null differ diff --git a/tests/images/masters/centerRotationWhite.png b/tests/images/masters/centerRotationWhite.png deleted file mode 100644 index 31e4c17..0000000 Binary files a/tests/images/masters/centerRotationWhite.png and /dev/null differ diff --git a/tests/images/masters/drawBlend.png b/tests/images/masters/drawBlend.png deleted file mode 100644 index 43b43aa..0000000 Binary files a/tests/images/masters/drawBlend.png and /dev/null differ diff --git a/tests/images/masters/drawBlendSmooth.png b/tests/images/masters/drawBlendSmooth.png deleted file mode 100644 index 4dd286a..0000000 Binary files a/tests/images/masters/drawBlendSmooth.png and /dev/null differ diff --git a/tests/images/masters/drawOverwrite.png b/tests/images/masters/drawOverwrite.png deleted file mode 100644 index 43b43aa..0000000 Binary files a/tests/images/masters/drawOverwrite.png and /dev/null differ diff --git a/tests/images/masters/drawOverwriteRot.png b/tests/images/masters/drawOverwriteRot.png deleted file mode 100644 index 007f69e..0000000 Binary files a/tests/images/masters/drawOverwriteRot.png and /dev/null differ diff --git a/tests/images/masters/transCompose.c.png b/tests/images/masters/transCompose.c.png deleted file mode 100644 index 9942445..0000000 Binary files a/tests/images/masters/transCompose.c.png and /dev/null differ diff --git a/tests/images/masters/transCompose.png b/tests/images/masters/transCompose.png deleted file mode 100644 index 5e8175b..0000000 Binary files a/tests/images/masters/transCompose.png and /dev/null differ diff --git a/tests/images/minifiedBaboon.png b/tests/images/minifiedBaboon.png new file mode 100644 index 0000000..4478e04 Binary files /dev/null and b/tests/images/minifiedBaboon.png differ diff --git a/tests/images/scaleHalf.png b/tests/images/scaleHalf.png new file mode 100644 index 0000000..99b52b2 Binary files /dev/null and b/tests/images/scaleHalf.png differ diff --git a/tests/test_images.nim b/tests/test_images.nim index 517d4d7..5f237f0 100644 --- a/tests/test_images.nim +++ b/tests/test_images.nim @@ -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)) diff --git a/tests/test_images_draw.nim b/tests/test_images_draw.nim index 3435278..4f619a6 100644 --- a/tests/test_images_draw.nim +++ b/tests/test_images_draw.nim @@ -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")