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
111 changes: 105 additions & 6 deletions experiments/benchmark_cairo_draw.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,41 @@
import benchy, cairo, pixie
import benchy, cairo, pixie, pixie/blends

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

when defined(release):
{.push checks: off.}

proc drawBasic(backdrop, source: Image) =
let sourceIsOpaque = source.isOpaque()

for y in 0 ..< min(backdrop.height, source.height):
if sourceIsOpaque:
copyMem(
backdrop.data[backdrop.dataIndex(0, y)].addr,
source.data[source.dataIndex(0, y)].addr,
min(backdrop.width, source.width) * 4
)
else:
var x: int
when defined(amd64) and not defined(pixieNoSimd):
let vec255 = mm_set1_epi32(cast[int32](uint32.high))
for _ in 0 ..< min(backdrop.width, source.width) div 4:
let sourceVec = mm_loadu_si128(source.data[source.dataIndex(x, y)].addr)
if mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, mm_setzero_si128())) != 0xffff:
if (mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) and 0x8888) == 0x8888:
mm_storeu_si128(backdrop.data[backdrop.dataIndex(x, y)].addr, sourceVec)
else:
let backdropVec = mm_loadu_si128(backdrop.data[backdrop.dataIndex(x, y)].addr)
mm_storeu_si128(
backdrop.data[backdrop.dataIndex(x, y)].addr,
blendNormalInlineSimd(backdropVec, sourceVec)
)
x += 4
# No scalar for now

when defined(release):
{.pop.}

block:
let
Expand All @@ -7,7 +44,13 @@ block:
tmp = imageSurfaceCreate(FORMAT_ARGB32, 1568, 940)
ctx = tmp.create()

timeIt "cairo draw basic":
timeIt "cairo draw normal":
# ctx.setSourceRgba(0.5, 0.5, 0.5, 1)
# let operator = ctx.getOperator()
# ctx.setOperator(OperatorSource)
# ctx.paint()
# ctx.setOperator(operator)

ctx.setSource(backdrop, 0, 0)
ctx.paint()
ctx.setSource(source, 0, 0)
Expand All @@ -22,15 +65,71 @@ block:
source = readImage("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
tmp = newImage(1568, 940)

timeIt "isOneColor":
doAssert not backdrop.isOneColor()

timeIt "pixie draw basic":
timeIt "pixie draw normal":
# tmp.fill(rgbx(127, 127, 127, 255))
tmp.draw(backdrop)
tmp.draw(source)

# tmp.writeFile("tmp2.png")

block:
let
backdrop = readImage("tests/fileformats/svg/masters/dragon2.png")
source = readImage("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
tmp = newImage(1568, 940)

timeIt "pixie draw overwrite":
# tmp.fill(rgbx(127, 127, 127, 255))
tmp.draw(backdrop, blendMode = bmOverwrite)
tmp.draw(source)

# tmp.writeFile("tmp2.png")

block:
let
backdrop = readImage("tests/fileformats/svg/masters/dragon2.png")
source = readImage("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
tmp = newImage(1568, 940)

timeIt "pixie draw basic":
# tmp.fill(rgbx(127, 127, 127, 255))
tmp.drawBasic(backdrop)
tmp.drawBasic(source)

# tmp.writeFile("tmp2.png")

block:
let
backdrop = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/dragon2.png")
source = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
tmp = imageSurfaceCreate(FORMAT_ARGB32, 1568, 940)
ctx = tmp.create()

timeIt "cairo draw mask":
ctx.setSourceRgba(1, 1, 1, 1)
let operator = ctx.getOperator()
ctx.setOperator(OperatorSource)
ctx.paint()
ctx.setOperator(operator)

ctx.setSource(backdrop, 0, 0)
ctx.mask(source, 0, 0)
tmp.flush()

# echo tmp.writeToPng("tmp_masked.png")

block:
let
backdrop = readImage("tests/fileformats/svg/masters/dragon2.png")
source = readImage("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
tmp = newImage(1568, 940)

timeIt "pixie draw mask":
tmp.draw(backdrop)
tmp.draw(source, blendMode = bmMask)

# tmp.writeFile("tmp_masked2.png")

block:
let
backdrop = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/dragon2.png")
Expand Down
11 changes: 5 additions & 6 deletions src/pixie.nim
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import bumpy, chroma, flatty/binny, os, pixie/blends, pixie/common,
pixie/contexts, pixie/fileformats/bmp, pixie/fileformats/gif,
pixie/fileformats/jpg, pixie/fileformats/png, pixie/fileformats/svg,
pixie/fonts, pixie/images, pixie/masks, pixie/paints, pixie/paths, strutils, vmath
import bumpy, chroma, flatty/binny, os, pixie/common, pixie/contexts,
pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpg,
pixie/fileformats/png, pixie/fileformats/svg, pixie/fonts, pixie/images,
pixie/masks, pixie/paints, pixie/paths, strutils, vmath

export blends, bumpy, chroma, common, contexts, fonts, images, masks, paints,
paths, vmath
export bumpy, chroma, common, contexts, fonts, images, masks, paints, paths, vmath

type
FileFormat* = enum
Expand Down
34 changes: 6 additions & 28 deletions src/pixie/blends.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,6 @@ when defined(amd64) and not defined(pixieNoSimd):
# See https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_blend_equation_advanced.txt

type
BlendMode* = enum
bmNormal
bmDarken
bmMultiply
# bmLinearBurn
bmColorBurn
bmLighten
bmScreen
# bmLinearDodge
bmColorDodge
bmOverlay
bmSoftLight
bmHardLight
bmDifference
bmExclusion
bmHue
bmSaturation
bmColor
bmLuminosity

bmMask ## Special blend mode that is used for masking
bmOverwrite ## Special blend mode that just copies pixels
bmSubtractMask ## Inverse mask
bmExcludeMask

Blender* = proc(backdrop, source: ColorRGBX): ColorRGBX {.gcsafe, raises: [].}
## Function signature returned by blender.
Masker* = proc(backdrop, source: uint8): uint8 {.gcsafe, raises: [].}
Expand Down Expand Up @@ -165,7 +140,7 @@ proc SetSat(C: Color, s: float32): Color {.inline.} =
if satC > 0:
result = (C - min([C.r, C.g, C.b])) * s / satC

proc blendNormal(backdrop, source: ColorRGBX): ColorRGBX =
proc blendNormal*(backdrop, source: ColorRGBX): ColorRGBX =
if backdrop.a == 0 or source.a == 255:
return source
if source.a == 0:
Expand Down Expand Up @@ -419,7 +394,7 @@ proc blendSaturation(backdrop, source: ColorRGBX): ColorRGBX =
blended = SetLum(SetSat(backdrop, Sat(source)), Lum(backdrop))
result = alphaFix(backdrop, source, blended).rgba.rgbx()

proc blendMask(backdrop, source: ColorRGBX): ColorRGBX =
proc blendMask*(backdrop, source: ColorRGBX): ColorRGBX =
let k = source.a.uint32
result.r = ((backdrop.r * k) div 255).uint8
result.g = ((backdrop.g * k) div 255).uint8
Expand Down Expand Up @@ -477,10 +452,13 @@ proc maskNormal(backdrop, source: uint8): uint8 =
## Blending masks
blendAlpha(backdrop, source)

proc maskMask(backdrop, source: uint8): uint8 =
proc maskMaskInline*(backdrop, source: uint8): uint8 {.inline.} =
## Masking masks
((backdrop.uint32 * source) div 255).uint8

proc maskMask(backdrop, source: uint8): uint8 =
maskMaskInline(backdrop, source)

proc maskSubtract(backdrop, source: uint8): uint8 =
((backdrop.uint32 * (255 - source)) div 255).uint8

Expand Down
25 changes: 25 additions & 0 deletions src/pixie/common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@ import bumpy, chroma, vmath
type
PixieError* = object of ValueError ## Raised if an operation fails.

BlendMode* = enum
bmNormal
bmDarken
bmMultiply
# bmLinearBurn
bmColorBurn
bmLighten
bmScreen
# bmLinearDodge
bmColorDodge
bmOverlay
bmSoftLight
bmHardLight
bmDifference
bmExclusion
bmHue
bmSaturation
bmColor
bmLuminosity

bmMask ## Special blend mode that is used for masking
bmOverwrite ## Special blend mode that just copies pixels
bmSubtractMask ## Inverse mask
bmExcludeMask

proc mix*(a, b: uint8, t: float32): uint8 {.inline, raises: [].} =
## Linearly interpolate between a and b using t.
let t = round(t * 255).uint32
Expand Down
4 changes: 2 additions & 2 deletions src/pixie/contexts.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import bumpy, chroma, pixie/blends, pixie/common, pixie/fonts, pixie/images,
pixie/masks, pixie/paints, pixie/paths, tables, vmath
import bumpy, chroma, pixie/common, pixie/fonts, pixie/images, pixie/masks,
pixie/paints, pixie/paths, tables, vmath

## This file provides a Nim version of the Canvas 2D API commonly used on the
## web. The goal is to make picking up Pixie easy for developers familiar with
Expand Down
Loading