Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bindings/bindings.nim
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ exportRefObject Mask:
applyOpacity(Mask, float32)
invert(Mask)
blur(Mask, float32, uint8)
resize(Mask, int, int)
draw(Mask, Mask, Mat3, BlendMode)
draw(Mask, Image, Mat3, BlendMode)
fillText(Mask, Font, string, Mat3, Vec2, HorizontalAlignment, VerticalAlignment)
Expand Down
8 changes: 4 additions & 4 deletions src/pixie/blends.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## 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

# See https://www.w3.org/TR/compositing-1/
Expand Down Expand Up @@ -274,7 +274,7 @@ proc blendSoftLight(backdrop, source: ColorRGBX): ColorRGBX =
source = source.rgba()

var rgba: ColorRGBA
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let
vb = mm_setr_ps(
backdrop.r.float32,
Expand Down Expand Up @@ -479,7 +479,7 @@ proc masker*(blendMode: BlendMode): Masker {.raises: [PixieError].} =
else:
raise newException(PixieError, "No masker for " & $blendMode)

when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
type
BlenderSimd* = proc(blackdrop, source: M128i): M128i {.gcsafe, raises: [].}
## Function signature returned by blenderSimd.
Expand Down
38 changes: 23 additions & 15 deletions src/pixie/fileformats/jpeg.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pixie/common, pixie/images, pixie/masks, sequtils, strutils, chroma,
std/decls, flatty/binny
import chroma, flatty/binny, pixie/common, pixie/images, pixie/internal,
pixie/masks, sequtils, std/decls, strutils

when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
import nimsimd/sse2

# This JPEG decoder is loosely based on stb_image which is public domain.
Expand Down Expand Up @@ -649,7 +649,7 @@ proc decodeProgressiveContinuationBlock(
data[zig] = cast[int16](state.receiveExtend(s.int) * (1 shl shift))

else:
var bit = 1 shl state.successiveApproxLow
let bit = 1 shl state.successiveApproxLow

if state.eobRun != 0:
dec state.eobRun
Expand Down Expand Up @@ -681,9 +681,9 @@ proc decodeProgressiveContinuationBlock(
if s != 1:
failInvalid("bad huffman code")
if state.readBit() != 0:
s = bit.int
s = bit
else:
s = -bit.int
s = -bit

while k <= state.spectralEnd:
let zig = deZigZag[k]
Expand Down Expand Up @@ -881,7 +881,7 @@ proc quantizationAndIDCTPass(state: var DecoderState) =
for row in 0 ..< w:
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
var q = mm_loadu_si128(state.quantizationTables[qTableId][i * 8].addr)
q = mm_unpacklo_epi8(q, mm_setzero_si128())
Expand All @@ -906,13 +906,17 @@ proc magnifyXBy2(mask: Mask): Mask =
let n = 3 * mask.unsafe[x, y].uint16
if x == 0:
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:
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]
else:
result.unsafe[x * 2 + 0, y] = ((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
result.unsafe[x * 2 + 0, y] =
((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 =
## Smooth magnify by power of 2 only in the Y direction.
Expand All @@ -922,13 +926,17 @@ proc magnifyYBy2(mask: Mask): Mask =
let n = 3 * mask.unsafe[x, y].uint16
if y == 0:
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:
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]
else:
result.unsafe[x, y * 2 + 0] = ((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
result.unsafe[x, y * 2 + 0] =
((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 =
## Takes a 3 component yCbCr outputs and populates image.
Expand Down
4 changes: 2 additions & 2 deletions src/pixie/fileformats/qoi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ type

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

func newImage*(qoi: Qoi): Image =
proc newImage*(qoi: Qoi): Image =
## Converts raw QOI data to `Image`.
result = newImage(qoi.width, qoi.height)
copyMem(result.data[0].addr, qoi.data[0].addr, qoi.data.len * 4)
Expand Down
1 change: 0 additions & 1 deletion src/pixie/fileformats/svg.nim
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,6 @@ proc parseSvg*(
var rootProps = initSvgProperties()
rootProps = root.parseSvgProperties(rootProps)


if viewBoxMinX != 0 or viewBoxMinY != 0:
let viewBoxMin = vec2(-viewBoxMinX.float32, -viewBoxMinY.float32)
rootprops.transform = rootprops.transform * translate(viewBoxMin)
Expand Down
4 changes: 2 additions & 2 deletions src/pixie/fontformats/opentype.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import flatty/binny, flatty/encode, math, pixie/common, pixie/paths, sets,
strutils, tables, unicode, vmath
strutils, tables, unicode, vmath

## See https://docs.microsoft.com/en-us/typography/opentype/spec/

Expand Down Expand Up @@ -663,7 +663,7 @@ proc parseNameTable(buf: string, offset: int): NameTable =
if record.platformID == 3 and
record.encodingID == 1 and
record.languageID == 1033:
record.text = fromUTF16BE(record.text)
record.text = fromUTF16BE(record.text)

record.text = record.text
result.nameRecords.add(record)
Expand Down
37 changes: 26 additions & 11 deletions src/pixie/images.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import blends, bumpy, chroma, common, masks, pixie/internal, vmath

when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
import nimsimd/sse2

const h = 0.5.float32
Expand Down Expand Up @@ -29,7 +29,7 @@ proc newImage*(width, height: int): Image {.raises: [PixieError].} =
proc newImage*(mask: Mask): Image {.raises: [PixieError].} =
result = newImage(mask.width, mask.height)
var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
for _ in 0 ..< mask.data.len div 16:
var alphas = mm_loadu_si128(mask.data[i].addr)
for j in 0 ..< 4:
Expand Down Expand Up @@ -63,7 +63,7 @@ proc dataIndex*(image: Image, x, y: int): int {.inline, raises: [].} =
template unsafe*(src: Image): UnsafeImage =
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.
## * No bounds checking *
## Make sure that x, y are in bounds.
Expand Down Expand Up @@ -106,7 +106,7 @@ proc isOneColor*(image: Image): bool {.raises: [].} =
let color = image.data[0]

var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let colorVec = mm_set1_epi32(cast[int32](color))
for _ in 0 ..< image.data.len div 8:
let
Expand All @@ -127,7 +127,7 @@ proc isTransparent*(image: Image): bool {.raises: [].} =
result = true

var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let vecZero = mm_setzero_si128()
for _ in 0 ..< image.data.len div 16:
let
Expand Down Expand Up @@ -254,7 +254,7 @@ proc minifyBy2*(image: Image, power = 1): Image {.raises: [PixieError].} =
)
for y in 0 ..< resultEvenHeight:
var x: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
first32 = cast[M128i]([uint32.high, 0, 0, 0])
Expand Down Expand Up @@ -348,7 +348,7 @@ proc magnifyBy2*(image: Image, power = 1): Image {.raises: [PixieError].} =
for y in 0 ..< image.height:
# Write one row of pixels duplicated by scale
var x: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
if scale == 2:
while x <= image.width - 4:
let
Expand Down Expand Up @@ -391,7 +391,7 @@ proc applyOpacity*(target: Image | Mask, opacity: float32) {.raises: [].} =
return

var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
when type(target) is Image:
let byteLen = target.data.len * 4
else:
Expand Down Expand Up @@ -447,7 +447,7 @@ proc applyOpacity*(target: Image | Mask, opacity: float32) {.raises: [].} =
proc invert*(target: Image) {.raises: [].} =
## Inverts all of the colors and alpha.
var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let vec255 = mm_set1_epi8(cast[int8](255))
let byteLen = target.data.len * 4
for _ in 0 ..< byteLen div 16:
Expand Down Expand Up @@ -536,7 +536,7 @@ proc newMask*(image: Image): Mask {.raises: [PixieError].} =
result = newMask(image.width, image.height)

var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
for _ in 0 ..< image.data.len div 16:
let
a = mm_loadu_si128(image.data[i + 0].addr)
Expand Down Expand Up @@ -798,7 +798,7 @@ proc drawUber(
)
continue

when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
case blendMode:
of OverwriteBlend:
for _ in 0 ..< (xStop - xStart) div 16:
Expand Down Expand Up @@ -1155,6 +1155,21 @@ proc resize*(srcImage: Image, width, height: int): Image {.raises: [PixieError].
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*(
image: Image, offset: Vec2, spread, blur: float32, color: SomeColor
): Image {.raises: [PixieError].} =
Expand Down
12 changes: 7 additions & 5 deletions src/pixie/internal.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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

template currentExceptionAsPixieError*(): untyped =
Expand Down Expand Up @@ -59,7 +61,7 @@ proc fillUnsafe*(
nimSetMem(data[start].addr, rgbx.r.cint, len * 4)
else:
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
let colorVec = mm_set1_epi32(cast[int32](rgbx))
for _ in 0 ..< len div 8:
Expand Down Expand Up @@ -93,7 +95,7 @@ proc toStraightAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} =
proc toPremultipliedAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} =
## Converts an image to premultiplied alpha from straight alpha.
var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
# When supported, SIMD convert as much as possible
let
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
Expand Down Expand Up @@ -140,7 +142,7 @@ proc isOpaque*(data: var seq[ColorRGBX], start, len: int): bool =
result = true

var i = start
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let
vec255 = mm_set1_epi32(cast[int32](uint32.high))
colorMask = mm_set1_epi32(cast[int32]([255.uint8, 255, 255, 0]))
Expand All @@ -161,7 +163,7 @@ proc isOpaque*(data: var seq[ColorRGBX], start, len: int): bool =
if data[j].a != 255:
return false

when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
proc packAlphaValues*(v: M128i): M128i {.inline, raises: [].} =
## Shuffle the alpha values for these 4 colors to the first 4 bytes
let mask = mm_set1_epi32(cast[int32](0xff000000))
Expand Down
10 changes: 5 additions & 5 deletions src/pixie/masks.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import common, internal, vmath

when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
import nimsimd/sse2

type
Expand Down Expand Up @@ -87,7 +87,7 @@ proc minifyBy2*(mask: Mask, power = 1): Mask {.raises: [PixieError].} =
result = newMask(src.width div 2, src.height div 2)
for y in 0 ..< result.height:
var x: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
firstByte = cast[M128i](
Expand Down Expand Up @@ -169,7 +169,7 @@ proc magnifyBy2*(mask: Mask, power = 1): Mask {.raises: [PixieError].} =
for y in 0 ..< mask.height:
# Write one row of values duplicated by scale
var x: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
if scale == 2:
while x <= mask.width - 16:
let
Expand Down Expand Up @@ -236,7 +236,7 @@ proc getValueSmooth*(mask: Mask, x, y: float32): uint8 {.raises: [].} =
proc invert*(mask: Mask) {.raises: [].} =
## Inverts all of the values - creates a negative of the mask.
var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let vec255 = mm_set1_epi8(cast[int8](255))
let byteLen = mask.data.len
for _ in 0 ..< byteLen div 16:
Expand Down Expand Up @@ -312,7 +312,7 @@ proc spread*(mask: Mask, spread: float32) {.raises: [PixieError].} =
proc ceil*(mask: Mask) {.raises: [].} =
## A value of 0 stays 0. Anything else turns into 255.
var i: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let
zeroVec = mm_setzero_si128()
vec255 = mm_set1_epi32(cast[int32](uint32.high))
Expand Down
8 changes: 4 additions & 4 deletions src/pixie/paints.nim
Original file line number Diff line number Diff line change
@@ -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

type
Expand Down Expand Up @@ -122,7 +122,7 @@ proc fillGradientLinear(image: Image, paint: Paint) =
if at.y == to.y: # Horizontal gradient
var x: int
while x < image.width:
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
if x + 4 <= image.width:
var colors: array[4, ColorRGBX]
for i in 0 ..< 4:
Expand Down Expand Up @@ -153,7 +153,7 @@ proc fillGradientLinear(image: Image, paint: Paint) =
t = toLineSpace(at, to, xy)
rgbx = paint.gradientColor(t)
var x: int
when defined(amd64) and not defined(pixieNoSimd):
when defined(amd64) and allowSimd:
let colorVec = mm_set1_epi32(cast[int32](rgbx))
for _ in 0 ..< image.width div 4:
mm_storeu_si128(image.data[image.dataIndex(x, y)].addr, colorVec)
Expand Down
Loading