commit
d2a090514d
16 changed files with 224 additions and 102 deletions
|
@ -156,6 +156,7 @@ exportRefObject Mask:
|
||||||
applyOpacity(Mask, float32)
|
applyOpacity(Mask, float32)
|
||||||
invert(Mask)
|
invert(Mask)
|
||||||
blur(Mask, float32, uint8)
|
blur(Mask, float32, uint8)
|
||||||
|
resize(Mask, int, int)
|
||||||
draw(Mask, Mask, Mat3, BlendMode)
|
draw(Mask, Mask, Mat3, BlendMode)
|
||||||
draw(Mask, Image, Mat3, BlendMode)
|
draw(Mask, Image, Mat3, BlendMode)
|
||||||
fillText(Mask, Font, string, Mat3, Vec2, HorizontalAlignment, VerticalAlignment)
|
fillText(Mask, Font, string, Mat3, Vec2, HorizontalAlignment, VerticalAlignment)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
## Blending modes.
|
## Blending modes.
|
||||||
|
|
||||||
import chroma, common, math
|
import chroma, common, internal, std/math
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
# See https://www.w3.org/TR/compositing-1/
|
# See https://www.w3.org/TR/compositing-1/
|
||||||
|
@ -274,7 +274,7 @@ proc blendSoftLight(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
source = source.rgba()
|
source = source.rgba()
|
||||||
|
|
||||||
var rgba: ColorRGBA
|
var rgba: ColorRGBA
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let
|
let
|
||||||
vb = mm_setr_ps(
|
vb = mm_setr_ps(
|
||||||
backdrop.r.float32,
|
backdrop.r.float32,
|
||||||
|
@ -479,7 +479,7 @@ proc masker*(blendMode: BlendMode): Masker {.raises: [PixieError].} =
|
||||||
else:
|
else:
|
||||||
raise newException(PixieError, "No masker for " & $blendMode)
|
raise newException(PixieError, "No masker for " & $blendMode)
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
type
|
type
|
||||||
BlenderSimd* = proc(blackdrop, source: M128i): M128i {.gcsafe, raises: [].}
|
BlenderSimd* = proc(blackdrop, source: M128i): M128i {.gcsafe, raises: [].}
|
||||||
## Function signature returned by blenderSimd.
|
## Function signature returned by blenderSimd.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import pixie/common, pixie/images, pixie/masks, sequtils, strutils, chroma,
|
import chroma, flatty/binny, pixie/common, pixie/images, pixie/internal,
|
||||||
std/decls, flatty/binny
|
pixie/masks, sequtils, std/decls, strutils
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
# This JPEG decoder is loosely based on stb_image which is public domain.
|
# This JPEG decoder is loosely based on stb_image which is public domain.
|
||||||
|
@ -649,7 +649,7 @@ proc decodeProgressiveContinuationBlock(
|
||||||
data[zig] = cast[int16](state.receiveExtend(s.int) * (1 shl shift))
|
data[zig] = cast[int16](state.receiveExtend(s.int) * (1 shl shift))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
var bit = 1 shl state.successiveApproxLow
|
let bit = 1 shl state.successiveApproxLow
|
||||||
|
|
||||||
if state.eobRun != 0:
|
if state.eobRun != 0:
|
||||||
dec state.eobRun
|
dec state.eobRun
|
||||||
|
@ -681,9 +681,9 @@ proc decodeProgressiveContinuationBlock(
|
||||||
if s != 1:
|
if s != 1:
|
||||||
failInvalid("bad huffman code")
|
failInvalid("bad huffman code")
|
||||||
if state.readBit() != 0:
|
if state.readBit() != 0:
|
||||||
s = bit.int
|
s = bit
|
||||||
else:
|
else:
|
||||||
s = -bit.int
|
s = -bit
|
||||||
|
|
||||||
while k <= state.spectralEnd:
|
while k <= state.spectralEnd:
|
||||||
let zig = deZigZag[k]
|
let zig = deZigZag[k]
|
||||||
|
@ -881,7 +881,7 @@ proc quantizationAndIDCTPass(state: var DecoderState) =
|
||||||
for row in 0 ..< w:
|
for row in 0 ..< w:
|
||||||
var data {.byaddr.} = state.components[comp].blocks[row][column]
|
var data {.byaddr.} = state.components[comp].blocks[row][column]
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
for i in 0 ..< 8: # 8 per pass
|
for i in 0 ..< 8: # 8 per pass
|
||||||
var q = mm_loadu_si128(state.quantizationTables[qTableId][i * 8].addr)
|
var q = mm_loadu_si128(state.quantizationTables[qTableId][i * 8].addr)
|
||||||
q = mm_unpacklo_epi8(q, mm_setzero_si128())
|
q = mm_unpacklo_epi8(q, mm_setzero_si128())
|
||||||
|
@ -906,13 +906,17 @@ proc magnifyXBy2(mask: Mask): Mask =
|
||||||
let n = 3 * mask.unsafe[x, y].uint16
|
let n = 3 * mask.unsafe[x, y].uint16
|
||||||
if x == 0:
|
if x == 0:
|
||||||
result.unsafe[x * 2 + 0, y] = mask.unsafe[x, y]
|
result.unsafe[x * 2 + 0, y] = mask.unsafe[x, y]
|
||||||
result.unsafe[x * 2 + 1, y] = ((n + mask.unsafe[x + 1, y].uint16 + 2) div 4).uint8
|
result.unsafe[x * 2 + 1, y] =
|
||||||
|
((n + mask.unsafe[x + 1, y].uint16 + 2) div 4).uint8
|
||||||
elif x == mask.width - 1:
|
elif x == mask.width - 1:
|
||||||
result.unsafe[x * 2 + 0, y] = ((n + mask.unsafe[x - 1, y].uint16 + 2) div 4).uint8
|
result.unsafe[x * 2 + 0, y] =
|
||||||
|
((n + mask.unsafe[x - 1, y].uint16 + 2) div 4).uint8
|
||||||
result.unsafe[x * 2 + 1, y] = mask.unsafe[x, y]
|
result.unsafe[x * 2 + 1, y] = mask.unsafe[x, y]
|
||||||
else:
|
else:
|
||||||
result.unsafe[x * 2 + 0, y] = ((n + mask.unsafe[x - 1, y].uint16) div 4).uint8
|
result.unsafe[x * 2 + 0, y] =
|
||||||
result.unsafe[x * 2 + 1, y] = ((n + mask.unsafe[x + 1, y].uint16) div 4).uint8
|
((n + mask.unsafe[x - 1, y].uint16) div 4).uint8
|
||||||
|
result.unsafe[x * 2 + 1, y] =
|
||||||
|
((n + mask.unsafe[x + 1, y].uint16) div 4).uint8
|
||||||
|
|
||||||
proc magnifyYBy2(mask: Mask): Mask =
|
proc magnifyYBy2(mask: Mask): Mask =
|
||||||
## Smooth magnify by power of 2 only in the Y direction.
|
## Smooth magnify by power of 2 only in the Y direction.
|
||||||
|
@ -922,13 +926,17 @@ proc magnifyYBy2(mask: Mask): Mask =
|
||||||
let n = 3 * mask.unsafe[x, y].uint16
|
let n = 3 * mask.unsafe[x, y].uint16
|
||||||
if y == 0:
|
if y == 0:
|
||||||
result.unsafe[x, y * 2 + 0] = mask.unsafe[x, y]
|
result.unsafe[x, y * 2 + 0] = mask.unsafe[x, y]
|
||||||
result.unsafe[x, y * 2 + 1] = ((n + mask.unsafe[x, y + 1].uint16 + 2) div 4).uint8
|
result.unsafe[x, y * 2 + 1] =
|
||||||
|
((n + mask.unsafe[x, y + 1].uint16 + 2) div 4).uint8
|
||||||
elif y == mask.height - 1:
|
elif y == mask.height - 1:
|
||||||
result.unsafe[x, y * 2 + 0] = ((n + mask.unsafe[x, y - 1].uint16 + 2) div 4).uint8
|
result.unsafe[x, y * 2 + 0] =
|
||||||
|
((n + mask.unsafe[x, y - 1].uint16 + 2) div 4).uint8
|
||||||
result.unsafe[x, y * 2 + 1] = mask.unsafe[x, y]
|
result.unsafe[x, y * 2 + 1] = mask.unsafe[x, y]
|
||||||
else:
|
else:
|
||||||
result.unsafe[x, y * 2 + 0] = ((n + mask.unsafe[x, y - 1].uint16) div 4).uint8
|
result.unsafe[x, y * 2 + 0] =
|
||||||
result.unsafe[x, y * 2 + 1] = ((n + mask.unsafe[x, y + 1].uint16) div 4).uint8
|
((n + mask.unsafe[x, y - 1].uint16) div 4).uint8
|
||||||
|
result.unsafe[x, y * 2 + 1] =
|
||||||
|
((n + mask.unsafe[x, y + 1].uint16) div 4).uint8
|
||||||
|
|
||||||
proc yCbCrToRgbx(py, pcb, pcr: uint8): ColorRGBX =
|
proc yCbCrToRgbx(py, pcb, pcr: uint8): ColorRGBX =
|
||||||
## Takes a 3 component yCbCr outputs and populates image.
|
## Takes a 3 component yCbCr outputs and populates image.
|
||||||
|
|
|
@ -26,10 +26,10 @@ type
|
||||||
|
|
||||||
Index = array[indexLen, ColorRGBA]
|
Index = array[indexLen, ColorRGBA]
|
||||||
|
|
||||||
func hash(p: ColorRGBA): int =
|
proc hash(p: ColorRGBA): int =
|
||||||
(p.r.int * 3 + p.g.int * 5 + p.b.int * 7 + p.a.int * 11) mod indexLen
|
(p.r.int * 3 + p.g.int * 5 + p.b.int * 7 + p.a.int * 11) mod indexLen
|
||||||
|
|
||||||
func newImage*(qoi: Qoi): Image =
|
proc newImage*(qoi: Qoi): Image =
|
||||||
## Converts raw QOI data to `Image`.
|
## Converts raw QOI data to `Image`.
|
||||||
result = newImage(qoi.width, qoi.height)
|
result = newImage(qoi.width, qoi.height)
|
||||||
copyMem(result.data[0].addr, qoi.data[0].addr, qoi.data.len * 4)
|
copyMem(result.data[0].addr, qoi.data[0].addr, qoi.data.len * 4)
|
||||||
|
|
|
@ -519,7 +519,6 @@ proc parseSvg*(
|
||||||
var rootProps = initSvgProperties()
|
var rootProps = initSvgProperties()
|
||||||
rootProps = root.parseSvgProperties(rootProps)
|
rootProps = root.parseSvgProperties(rootProps)
|
||||||
|
|
||||||
|
|
||||||
if viewBoxMinX != 0 or viewBoxMinY != 0:
|
if viewBoxMinX != 0 or viewBoxMinY != 0:
|
||||||
let viewBoxMin = vec2(-viewBoxMinX.float32, -viewBoxMinY.float32)
|
let viewBoxMin = vec2(-viewBoxMinX.float32, -viewBoxMinY.float32)
|
||||||
rootprops.transform = rootprops.transform * translate(viewBoxMin)
|
rootprops.transform = rootprops.transform * translate(viewBoxMin)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import blends, bumpy, chroma, common, masks, pixie/internal, vmath
|
import blends, bumpy, chroma, common, masks, pixie/internal, vmath
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
const h = 0.5.float32
|
const h = 0.5.float32
|
||||||
|
@ -29,7 +29,7 @@ proc newImage*(width, height: int): Image {.raises: [PixieError].} =
|
||||||
proc newImage*(mask: Mask): Image {.raises: [PixieError].} =
|
proc newImage*(mask: Mask): Image {.raises: [PixieError].} =
|
||||||
result = newImage(mask.width, mask.height)
|
result = newImage(mask.width, mask.height)
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
for _ in 0 ..< mask.data.len div 16:
|
for _ in 0 ..< mask.data.len div 16:
|
||||||
var alphas = mm_loadu_si128(mask.data[i].addr)
|
var alphas = mm_loadu_si128(mask.data[i].addr)
|
||||||
for j in 0 ..< 4:
|
for j in 0 ..< 4:
|
||||||
|
@ -63,7 +63,7 @@ proc dataIndex*(image: Image, x, y: int): int {.inline, raises: [].} =
|
||||||
template unsafe*(src: Image): UnsafeImage =
|
template unsafe*(src: Image): UnsafeImage =
|
||||||
cast[UnsafeImage](src)
|
cast[UnsafeImage](src)
|
||||||
|
|
||||||
template `[]`*(view: UnsafeImage, x, y: int): ColorRGBX =
|
template `[]`*(view: UnsafeImage, x, y: int): var ColorRGBX =
|
||||||
## Gets a color from (x, y) coordinates.
|
## Gets a color from (x, y) coordinates.
|
||||||
## * No bounds checking *
|
## * No bounds checking *
|
||||||
## Make sure that x, y are in bounds.
|
## Make sure that x, y are in bounds.
|
||||||
|
@ -106,7 +106,7 @@ proc isOneColor*(image: Image): bool {.raises: [].} =
|
||||||
let color = image.data[0]
|
let color = image.data[0]
|
||||||
|
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let colorVec = mm_set1_epi32(cast[int32](color))
|
let colorVec = mm_set1_epi32(cast[int32](color))
|
||||||
for _ in 0 ..< image.data.len div 8:
|
for _ in 0 ..< image.data.len div 8:
|
||||||
let
|
let
|
||||||
|
@ -127,7 +127,7 @@ proc isTransparent*(image: Image): bool {.raises: [].} =
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let vecZero = mm_setzero_si128()
|
let vecZero = mm_setzero_si128()
|
||||||
for _ in 0 ..< image.data.len div 16:
|
for _ in 0 ..< image.data.len div 16:
|
||||||
let
|
let
|
||||||
|
@ -254,7 +254,7 @@ proc minifyBy2*(image: Image, power = 1): Image {.raises: [PixieError].} =
|
||||||
)
|
)
|
||||||
for y in 0 ..< resultEvenHeight:
|
for y in 0 ..< resultEvenHeight:
|
||||||
var x: int
|
var x: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let
|
let
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
first32 = cast[M128i]([uint32.high, 0, 0, 0])
|
first32 = cast[M128i]([uint32.high, 0, 0, 0])
|
||||||
|
@ -348,7 +348,7 @@ proc magnifyBy2*(image: Image, power = 1): Image {.raises: [PixieError].} =
|
||||||
for y in 0 ..< image.height:
|
for y in 0 ..< image.height:
|
||||||
# Write one row of pixels duplicated by scale
|
# Write one row of pixels duplicated by scale
|
||||||
var x: int
|
var x: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
if scale == 2:
|
if scale == 2:
|
||||||
while x <= image.width - 4:
|
while x <= image.width - 4:
|
||||||
let
|
let
|
||||||
|
@ -391,7 +391,7 @@ proc applyOpacity*(target: Image | Mask, opacity: float32) {.raises: [].} =
|
||||||
return
|
return
|
||||||
|
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
when type(target) is Image:
|
when type(target) is Image:
|
||||||
let byteLen = target.data.len * 4
|
let byteLen = target.data.len * 4
|
||||||
else:
|
else:
|
||||||
|
@ -447,7 +447,7 @@ proc applyOpacity*(target: Image | Mask, opacity: float32) {.raises: [].} =
|
||||||
proc invert*(target: Image) {.raises: [].} =
|
proc invert*(target: Image) {.raises: [].} =
|
||||||
## Inverts all of the colors and alpha.
|
## Inverts all of the colors and alpha.
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let vec255 = mm_set1_epi8(cast[int8](255))
|
let vec255 = mm_set1_epi8(cast[int8](255))
|
||||||
let byteLen = target.data.len * 4
|
let byteLen = target.data.len * 4
|
||||||
for _ in 0 ..< byteLen div 16:
|
for _ in 0 ..< byteLen div 16:
|
||||||
|
@ -536,7 +536,7 @@ proc newMask*(image: Image): Mask {.raises: [PixieError].} =
|
||||||
result = newMask(image.width, image.height)
|
result = newMask(image.width, image.height)
|
||||||
|
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
for _ in 0 ..< image.data.len div 16:
|
for _ in 0 ..< image.data.len div 16:
|
||||||
let
|
let
|
||||||
a = mm_loadu_si128(image.data[i + 0].addr)
|
a = mm_loadu_si128(image.data[i + 0].addr)
|
||||||
|
@ -798,7 +798,7 @@ proc drawUber(
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
case blendMode:
|
case blendMode:
|
||||||
of OverwriteBlend:
|
of OverwriteBlend:
|
||||||
for _ in 0 ..< (xStop - xStart) div 16:
|
for _ in 0 ..< (xStop - xStart) div 16:
|
||||||
|
@ -1155,6 +1155,21 @@ proc resize*(srcImage: Image, width, height: int): Image {.raises: [PixieError].
|
||||||
OverwriteBlend
|
OverwriteBlend
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proc resize*(srcMask: Mask, width, height: int): Mask {.raises: [PixieError].} =
|
||||||
|
## Resize a mask to a given height and width.
|
||||||
|
if width == srcMask.width and height == srcMask.height:
|
||||||
|
result = srcMask.copy()
|
||||||
|
else:
|
||||||
|
result = newMask(width, height)
|
||||||
|
result.draw(
|
||||||
|
srcMask,
|
||||||
|
scale(vec2(
|
||||||
|
width.float32 / srcMask.width.float32,
|
||||||
|
height.float32 / srcMask.height.float32
|
||||||
|
)),
|
||||||
|
OverwriteBlend
|
||||||
|
)
|
||||||
|
|
||||||
proc shadow*(
|
proc shadow*(
|
||||||
image: Image, offset: Vec2, spread, blur: float32, color: SomeColor
|
image: Image, offset: Vec2, spread, blur: float32, color: SomeColor
|
||||||
): Image {.raises: [PixieError].} =
|
): Image {.raises: [PixieError].} =
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import chroma, system/memory, vmath
|
import chroma, system/memory, vmath
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
const allowSimd* = not defined(pixieNoSimd) and not defined(tcc)
|
||||||
|
|
||||||
|
when defined(amd64) and allowSimd:
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
template currentExceptionAsPixieError*(): untyped =
|
template currentExceptionAsPixieError*(): untyped =
|
||||||
|
@ -59,7 +61,7 @@ proc fillUnsafe*(
|
||||||
nimSetMem(data[start].addr, rgbx.r.cint, len * 4)
|
nimSetMem(data[start].addr, rgbx.r.cint, len * 4)
|
||||||
else:
|
else:
|
||||||
var i = start
|
var i = start
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
# When supported, SIMD fill until we run out of room
|
# When supported, SIMD fill until we run out of room
|
||||||
let colorVec = mm_set1_epi32(cast[int32](rgbx))
|
let colorVec = mm_set1_epi32(cast[int32](rgbx))
|
||||||
for _ in 0 ..< len div 8:
|
for _ in 0 ..< len div 8:
|
||||||
|
@ -93,7 +95,7 @@ proc toStraightAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} =
|
||||||
proc toPremultipliedAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} =
|
proc toPremultipliedAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} =
|
||||||
## Converts an image to premultiplied alpha from straight alpha.
|
## Converts an image to premultiplied alpha from straight alpha.
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
# When supported, SIMD convert as much as possible
|
# When supported, SIMD convert as much as possible
|
||||||
let
|
let
|
||||||
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
|
@ -140,7 +142,7 @@ proc isOpaque*(data: var seq[ColorRGBX], start, len: int): bool =
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
var i = start
|
var i = start
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let
|
let
|
||||||
vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
||||||
colorMask = mm_set1_epi32(cast[int32]([255.uint8, 255, 255, 0]))
|
colorMask = mm_set1_epi32(cast[int32]([255.uint8, 255, 255, 0]))
|
||||||
|
@ -161,7 +163,7 @@ proc isOpaque*(data: var seq[ColorRGBX], start, len: int): bool =
|
||||||
if data[j].a != 255:
|
if data[j].a != 255:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
proc packAlphaValues*(v: M128i): M128i {.inline, raises: [].} =
|
proc packAlphaValues*(v: M128i): M128i {.inline, raises: [].} =
|
||||||
## Shuffle the alpha values for these 4 colors to the first 4 bytes
|
## Shuffle the alpha values for these 4 colors to the first 4 bytes
|
||||||
let mask = mm_set1_epi32(cast[int32](0xff000000))
|
let mask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import common, internal, vmath
|
import common, internal, vmath
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -87,7 +87,7 @@ proc minifyBy2*(mask: Mask, power = 1): Mask {.raises: [PixieError].} =
|
||||||
result = newMask(src.width div 2, src.height div 2)
|
result = newMask(src.width div 2, src.height div 2)
|
||||||
for y in 0 ..< result.height:
|
for y in 0 ..< result.height:
|
||||||
var x: int
|
var x: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let
|
let
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
firstByte = cast[M128i](
|
firstByte = cast[M128i](
|
||||||
|
@ -169,7 +169,7 @@ proc magnifyBy2*(mask: Mask, power = 1): Mask {.raises: [PixieError].} =
|
||||||
for y in 0 ..< mask.height:
|
for y in 0 ..< mask.height:
|
||||||
# Write one row of values duplicated by scale
|
# Write one row of values duplicated by scale
|
||||||
var x: int
|
var x: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
if scale == 2:
|
if scale == 2:
|
||||||
while x <= mask.width - 16:
|
while x <= mask.width - 16:
|
||||||
let
|
let
|
||||||
|
@ -236,7 +236,7 @@ proc getValueSmooth*(mask: Mask, x, y: float32): uint8 {.raises: [].} =
|
||||||
proc invert*(mask: Mask) {.raises: [].} =
|
proc invert*(mask: Mask) {.raises: [].} =
|
||||||
## Inverts all of the values - creates a negative of the mask.
|
## Inverts all of the values - creates a negative of the mask.
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let vec255 = mm_set1_epi8(cast[int8](255))
|
let vec255 = mm_set1_epi8(cast[int8](255))
|
||||||
let byteLen = mask.data.len
|
let byteLen = mask.data.len
|
||||||
for _ in 0 ..< byteLen div 16:
|
for _ in 0 ..< byteLen div 16:
|
||||||
|
@ -312,7 +312,7 @@ proc spread*(mask: Mask, spread: float32) {.raises: [PixieError].} =
|
||||||
proc ceil*(mask: Mask) {.raises: [].} =
|
proc ceil*(mask: Mask) {.raises: [].} =
|
||||||
## A value of 0 stays 0. Anything else turns into 255.
|
## A value of 0 stays 0. Anything else turns into 255.
|
||||||
var i: int
|
var i: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let
|
let
|
||||||
zeroVec = mm_setzero_si128()
|
zeroVec = mm_setzero_si128()
|
||||||
vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import chroma, common, images, vmath
|
import chroma, common, images, internal, vmath
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -122,7 +122,7 @@ proc fillGradientLinear(image: Image, paint: Paint) =
|
||||||
if at.y == to.y: # Horizontal gradient
|
if at.y == to.y: # Horizontal gradient
|
||||||
var x: int
|
var x: int
|
||||||
while x < image.width:
|
while x < image.width:
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
if x + 4 <= image.width:
|
if x + 4 <= image.width:
|
||||||
var colors: array[4, ColorRGBX]
|
var colors: array[4, ColorRGBX]
|
||||||
for i in 0 ..< 4:
|
for i in 0 ..< 4:
|
||||||
|
@ -153,7 +153,7 @@ proc fillGradientLinear(image: Image, paint: Paint) =
|
||||||
t = toLineSpace(at, to, xy)
|
t = toLineSpace(at, to, xy)
|
||||||
rgbx = paint.gradientColor(t)
|
rgbx = paint.gradientColor(t)
|
||||||
var x: int
|
var x: int
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let colorVec = mm_set1_epi32(cast[int32](rgbx))
|
let colorVec = mm_set1_epi32(cast[int32](rgbx))
|
||||||
for _ in 0 ..< image.width div 4:
|
for _ in 0 ..< image.width div 4:
|
||||||
mm_storeu_si128(image.data[image.dataIndex(x, y)].addr, colorVec)
|
mm_storeu_si128(image.data[image.dataIndex(x, y)].addr, colorVec)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import blends, bumpy, chroma, common, fenv, images, internal, masks, paints,
|
import blends, bumpy, chroma, common, fenv, images, internal, masks, paints,
|
||||||
strutils, vmath
|
strutils, vmath
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -1296,7 +1296,7 @@ proc computeCoverage(
|
||||||
let fillLen = at.int - fillStart
|
let fillLen = at.int - fillStart
|
||||||
if fillLen > 0:
|
if fillLen > 0:
|
||||||
var i = fillStart
|
var i = fillStart
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
let sampleCoverageVec = mm_set1_epi8(cast[int8](sampleCoverage))
|
let sampleCoverageVec = mm_set1_epi8(cast[int8](sampleCoverage))
|
||||||
for _ in 0 ..< fillLen div 16:
|
for _ in 0 ..< fillLen div 16:
|
||||||
var coverageVec = mm_loadu_si128(coverages[i - startX].addr)
|
var coverageVec = mm_loadu_si128(coverages[i - startX].addr)
|
||||||
|
@ -1326,7 +1326,7 @@ proc fillCoverage(
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) =
|
) =
|
||||||
var x = startX
|
var x = startX
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
if blendMode.hasSimdBlender():
|
if blendMode.hasSimdBlender():
|
||||||
# When supported, SIMD blend as much as possible
|
# When supported, SIMD blend as much as possible
|
||||||
let
|
let
|
||||||
|
@ -1445,7 +1445,7 @@ proc fillCoverage(
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) =
|
) =
|
||||||
var x = startX
|
var x = startX
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
if blendMode.hasSimdMasker():
|
if blendMode.hasSimdMasker():
|
||||||
let
|
let
|
||||||
maskerSimd = blendMode.maskerSimd()
|
maskerSimd = blendMode.maskerSimd()
|
||||||
|
@ -1511,7 +1511,7 @@ proc fillHits(
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var x = fillStart
|
var x = fillStart
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
if blendMode.hasSimdBlender():
|
if blendMode.hasSimdBlender():
|
||||||
# When supported, SIMD blend as much as possible
|
# When supported, SIMD blend as much as possible
|
||||||
let colorVec = mm_set1_epi32(cast[int32](rgbx))
|
let colorVec = mm_set1_epi32(cast[int32](rgbx))
|
||||||
|
@ -1573,7 +1573,7 @@ proc fillHits(
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var x = fillStart
|
var x = fillStart
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and allowSimd:
|
||||||
if blendMode.hasSimdMasker():
|
if blendMode.hasSimdMasker():
|
||||||
let
|
let
|
||||||
maskerSimd = blendMode.maskerSimd()
|
maskerSimd = blendMode.maskerSimd()
|
||||||
|
|
|
@ -16,12 +16,101 @@ block:
|
||||||
timeIt "fillOverlaps":
|
timeIt "fillOverlaps":
|
||||||
doAssert path.fillOverlaps(vec2(1, 1)) == false
|
doAssert path.fillOverlaps(vec2(1, 1)) == false
|
||||||
|
|
||||||
timeIt "roundedRect":
|
const
|
||||||
const radius = 20
|
width = 500
|
||||||
|
height = 300
|
||||||
|
radius = 20
|
||||||
|
|
||||||
let path = newPath()
|
let paint = newPaint(SolidPaint)
|
||||||
path.roundedRect(0.5, 0.5, 499, 299, radius, radius, radius, radius)
|
paint.color = color(0, 0, 0, 0.5)
|
||||||
# path.roundedRect(0, 0, 500, 300, radius, radius, radius, radius)
|
|
||||||
|
|
||||||
let image = newImage(500, 300)
|
let rect = newPath()
|
||||||
image.fillPath(path, rgba(0, 0, 0, 255))
|
rect.rect(10, 10, 480, 280)
|
||||||
|
|
||||||
|
let roundedRect = newPath()
|
||||||
|
roundedRect.roundedRect(10.5, 10.5, 479, 279, radius, radius, radius, radius)
|
||||||
|
# roundedRect.roundedRect(10, 10, 480, 280, radius, radius, radius, radius)
|
||||||
|
|
||||||
|
timeIt "rect Image OverwriteBlend":
|
||||||
|
paint.blendMode = OverwriteBlend
|
||||||
|
|
||||||
|
let image = newImage(width, height)
|
||||||
|
image.fillPath(rect, paint)
|
||||||
|
|
||||||
|
timeIt "rect Image NormalBlend":
|
||||||
|
paint.blendMode = NormalBlend
|
||||||
|
|
||||||
|
let image = newImage(width, height)
|
||||||
|
image.fillPath(rect, paint)
|
||||||
|
|
||||||
|
timeIt "rect Image MaskBlend":
|
||||||
|
paint.blendMode = MaskBlend
|
||||||
|
|
||||||
|
let image = newImage(width, height)
|
||||||
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
|
image.fillPath(rect, paint)
|
||||||
|
|
||||||
|
timeIt "roundedRect Image OverwriteBlend":
|
||||||
|
paint.blendMode = OverwriteBlend
|
||||||
|
|
||||||
|
let image = newImage(width, height)
|
||||||
|
image.fillPath(roundedRect, paint)
|
||||||
|
|
||||||
|
timeIt "roundedRect Image NormalBlend":
|
||||||
|
paint.blendMode = NormalBlend
|
||||||
|
|
||||||
|
let image = newImage(width, height)
|
||||||
|
image.fillPath(roundedRect, paint)
|
||||||
|
|
||||||
|
timeIt "roundedRect Image MaskBlend":
|
||||||
|
paint.blendMode = MaskBlend
|
||||||
|
|
||||||
|
let image = newImage(width, height)
|
||||||
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
|
image.fillPath(roundedRect, paint)
|
||||||
|
|
||||||
|
timeIt "rect Mask OverwriteBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fillPath(roundedRect, blendMode = OverwriteBlend)
|
||||||
|
|
||||||
|
timeIt "rect Mask NormalBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fillPath(rect, blendMode = NormalBlend)
|
||||||
|
|
||||||
|
timeIt "rect Mask MaskBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fill(255)
|
||||||
|
mask.fillPath(rect, blendMode = MaskBlend)
|
||||||
|
|
||||||
|
timeIt "rect Mask SubtractMaskBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fill(255)
|
||||||
|
mask.fillPath(rect, blendMode = SubtractMaskBlend)
|
||||||
|
|
||||||
|
timeIt "rect Mask ExcludeMaskBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fill(255)
|
||||||
|
mask.fillPath(rect, blendMode = ExcludeMaskBlend)
|
||||||
|
|
||||||
|
timeIt "roundedRect Mask OverwriteBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fillPath(roundedRect, blendMode = OverwriteBlend)
|
||||||
|
|
||||||
|
timeIt "roundedRect Mask NormalBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fillPath(roundedRect, blendMode = NormalBlend)
|
||||||
|
|
||||||
|
timeIt "roundedRect Mask MaskBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fill(255)
|
||||||
|
mask.fillPath(roundedRect, blendMode = MaskBlend)
|
||||||
|
|
||||||
|
timeIt "roundedRect Mask SubtractMaskBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fill(255)
|
||||||
|
mask.fillPath(roundedRect, blendMode = SubtractMaskBlend)
|
||||||
|
|
||||||
|
timeIt "roundedRect Mask ExcludeMaskBlend":
|
||||||
|
let mask = newMask(width, height)
|
||||||
|
mask.fill(255)
|
||||||
|
mask.fillPath(roundedRect, blendMode = ExcludeMaskBlend)
|
||||||
|
|
|
@ -5,30 +5,35 @@ let
|
||||||
filePath = "tests/fileformats/png/lenna.png"
|
filePath = "tests/fileformats/png/lenna.png"
|
||||||
data = readFile(filePath)
|
data = readFile(filePath)
|
||||||
|
|
||||||
timeIt "pixie decode":
|
block:
|
||||||
keep decodePngRaw(data)
|
let
|
||||||
|
decodedPng = decodePng(data)
|
||||||
|
decodedImage = newImage(decodedPng)
|
||||||
|
|
||||||
timeIt "pixie encode":
|
timeIt "pixie decode":
|
||||||
let decoded = decodePngRaw(data)
|
discard decodePng(data)
|
||||||
keep encodePng(decoded).len
|
|
||||||
|
|
||||||
timeIt "pixie decode + alpha":
|
timeIt "pixie encode":
|
||||||
keep decodePng(data)
|
discard encodePng(decodedPng)
|
||||||
|
|
||||||
timeIt "pixie encode + alpha":
|
timeIt "pixie decode + alpha":
|
||||||
let decoded = decodePng(data)
|
discard newImage(decodePng(data))
|
||||||
keep encodePng(decoded).len
|
|
||||||
|
|
||||||
timeIt "nimPNG decode":
|
timeIt "pixie encode + alpha":
|
||||||
keep decodePNG32(data)
|
discard encodePng(decodedImage)
|
||||||
|
|
||||||
|
block:
|
||||||
|
timeIt "nimPNG decode":
|
||||||
|
discard decodePNG32(data)
|
||||||
|
|
||||||
timeIt "nimPNG encode":
|
|
||||||
let decoded = decodePNG32(data)
|
let decoded = decodePNG32(data)
|
||||||
keep encodePNG32(decoded.data, decoded.width, decoded.height).pixels.len
|
timeIt "nimPNG encode":
|
||||||
|
discard encodePNG32(decoded.data, decoded.width, decoded.height)
|
||||||
|
|
||||||
timeIt "stb_image decode":
|
block:
|
||||||
|
timeIt "stb_image decode":
|
||||||
var width, height, channels: int
|
var width, height, channels: int
|
||||||
keep loadFromMemory(
|
discard loadFromMemory(
|
||||||
cast[seq[byte]](data),
|
cast[seq[byte]](data),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -36,7 +41,6 @@ timeIt "stb_image decode":
|
||||||
stbi.RGBA
|
stbi.RGBA
|
||||||
)
|
)
|
||||||
|
|
||||||
timeIt "stb_image encode":
|
|
||||||
var width, height, channels: int
|
var width, height, channels: int
|
||||||
let decoded = loadFromMemory(
|
let decoded = loadFromMemory(
|
||||||
cast[seq[byte]](data),
|
cast[seq[byte]](data),
|
||||||
|
@ -45,16 +49,17 @@ timeIt "stb_image encode":
|
||||||
channels,
|
channels,
|
||||||
stbi.RGBA
|
stbi.RGBA
|
||||||
)
|
)
|
||||||
keep writePNG(width, height, channels, decoded).len
|
|
||||||
|
|
||||||
timeIt "cairo decode":
|
timeIt "stb_image encode":
|
||||||
keep imageSurfaceCreateFromPng(filePath)
|
discard writePNG(width, height, channels, decoded).len
|
||||||
|
|
||||||
|
block:
|
||||||
|
timeIt "cairo decode":
|
||||||
|
discard imageSurfaceCreateFromPng(filePath)
|
||||||
|
|
||||||
timeIt "cairo encode":
|
|
||||||
let decoded = imageSurfaceCreateFromPng(filePath)
|
let decoded = imageSurfaceCreateFromPng(filePath)
|
||||||
|
timeIt "cairo encode":
|
||||||
var write: WriteFunc =
|
var write: WriteFunc =
|
||||||
proc(closure: pointer, data: cstring, len: int32): Status {.cdecl.} =
|
proc(closure: pointer, data: cstring, len: int32): Status {.cdecl.} =
|
||||||
StatusSuccess
|
StatusSuccess
|
||||||
|
|
||||||
discard decoded.writeToPng(write, nil)
|
discard decoded.writeToPng(write, nil)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import pixie, pixie/fileformats/png, strformat, unicode, os
|
import os, pixie, pixie/fileformats/png, strformat, unicode
|
||||||
|
|
||||||
proc wh(image: Image): Vec2 =
|
proc wh(image: Image): Vec2 =
|
||||||
## Return with and height as a size vector.
|
## Return with and height as a size vector.
|
||||||
|
|
|
@ -23,10 +23,10 @@ block:
|
||||||
doAssert image[9, 9] == rgba(128, 0, 0, 128)
|
doAssert image[9, 9] == rgba(128, 0, 0, 128)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let image = newImage(10, 10)
|
var data = newSeq[ColorRGBX](100)
|
||||||
image.fill(rgba(128, 0, 0, 128))
|
fillUnsafe(data, rgbx(100, 0, 0, 128), 0, data.len)
|
||||||
image.data.toStraightAlpha()
|
data.toStraightAlpha()
|
||||||
doAssert image[9, 9] == rgba(254, 0, 0, 128)
|
doAssert data[10] == rgbx(199, 0, 0, 128)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let image = newImage(100, 100)
|
let image = newImage(100, 100)
|
||||||
|
|
|
@ -29,5 +29,8 @@ for file in files:
|
||||||
doDiff(readImage(&"tests/fileformats/svg/{file}.svg"), file)
|
doDiff(readImage(&"tests/fileformats/svg/{file}.svg"), file)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let svg = parseSvg(readFile("tests/fileformats/svg/accessibility-outline.svg"), 512, 512)
|
let svg = parseSvg(
|
||||||
|
readFile("tests/fileformats/svg/accessibility-outline.svg"),
|
||||||
|
512, 512
|
||||||
|
)
|
||||||
doDiff(newImage(svg), "accessibility-outline")
|
doDiff(newImage(svg), "accessibility-outline")
|
||||||
|
|
Loading…
Reference in a new issue