docs, bugfix, ctx blend test, remove seq[seq[Vec2]] from SomePath

This commit is contained in:
Ryan Oldenburg 2021-05-27 17:27:19 -05:00
parent b06f653ed9
commit 10bc3100e6
4 changed files with 76 additions and 62 deletions

View file

@ -25,11 +25,11 @@ type
font*: Font font*: Font
Arrangement* = ref object Arrangement* = ref object
spans*: seq[(int, int)] spans*: seq[(int, int)] ## The (start, stop) of the spans in the text.
fonts*: seq[Font] fonts*: seq[Font] ## The font for each span.
runes*: seq[Rune] runes*: seq[Rune] ## The runes of the text.
positions*: seq[Vec2] positions*: seq[Vec2] ## The positions of the glyphs for each rune.
selectionRects*: seq[Rect] selectionRects*: seq[Rect] ## The selection rects for each glyph.
HAlignMode* = enum HAlignMode* = enum
haLeft haLeft
@ -110,6 +110,7 @@ proc defaultLineHeight*(font: Font): float32 {.inline.} =
round(fontUnits * font.scale) round(fontUnits * font.scale)
proc newSpan*(text: string, font: Font): Span = proc newSpan*(text: string, font: Font): Span =
## Creates a span, associating a font with the text.
result = Span() result = Span()
result.text = text result.text = text
result.font = font result.font = font
@ -369,6 +370,7 @@ proc typeset*(
typeset(@[newSpan(text, font)], bounds, hAlign, vAlign, wrap) typeset(@[newSpan(text, font)], bounds, hAlign, vAlign, wrap)
proc computeBounds*(arrangement: Arrangement): Vec2 = proc computeBounds*(arrangement: Arrangement): Vec2 =
## Computes the width and height of the arrangement in pixels.
if arrangement.runes.len > 0: if arrangement.runes.len > 0:
for i in 0 ..< arrangement.runes.len: for i in 0 ..< arrangement.runes.len:
if arrangement.runes[i] != LF: if arrangement.runes[i] != LF:
@ -382,6 +384,7 @@ proc computeBounds*(font: Font, text: string): Vec2 {.inline.} =
font.typeset(text).computeBounds() font.typeset(text).computeBounds()
proc computeBounds*(spans: seq[Span]): Vec2 {.inline.} = proc computeBounds*(spans: seq[Span]): Vec2 {.inline.} =
## Computes the width and height of the spans in pixels.
typeset(spans).computeBounds() typeset(spans).computeBounds()
proc parseOtf*(buf: string): Font = proc parseOtf*(buf: string): Font =

View file

@ -34,7 +34,7 @@ type
commands*: seq[PathCommand] commands*: seq[PathCommand]
start, at: Vec2 # Maintained by moveTo, lineTo, etc. Used by arcTo. start, at: Vec2 # Maintained by moveTo, lineTo, etc. Used by arcTo.
SomePath* = Path | string | seq[seq[Vec2]] SomePath* = Path | string
const const
epsilon = 0.0001 * PI ## Tiny value used for some computations. epsilon = 0.0001 * PI ## Tiny value used for some computations.
@ -604,7 +604,7 @@ proc polygon*(path: var Path, pos: Vec2, size: float32, sides: int) {.inline.} =
## Adds a n-sided regular polygon at (x, y) with the parameter size. ## Adds a n-sided regular polygon at (x, y) with the parameter size.
path.polygon(pos.x, pos.y, size, sides) path.polygon(pos.x, pos.y, size, sides)
proc commandsToShapes*(path: Path, pixelScale: float32 = 1.0): seq[seq[Vec2]] = proc commandsToShapes(path: Path, pixelScale: float32 = 1.0): seq[seq[Vec2]] =
## Converts SVG-like commands to line segments. ## Converts SVG-like commands to line segments.
var var
start, at: Vec2 start, at: Vec2
@ -1164,14 +1164,6 @@ proc fillShapes(
stopY = min(image.height, (bounds.y + bounds.h).int) stopY = min(image.height, (bounds.y + bounds.h).int)
blender = blendMode.blender() blender = blendMode.blender()
when defined(amd64) and not defined(pixieNoSimd):
let
blenderSimd = blendMode.blenderSimd()
first32 = cast[M128i]([uint32.high, 0, 0, 0]) # First 32 bits
oddMask = mm_set1_epi16(cast[int16](0xff00))
div255 = mm_set1_epi16(cast[int16](0x8081))
vColor = mm_set1_epi32(cast[int32](rgbx))
var var
coverages = newSeq[uint8](image.width) coverages = newSeq[uint8](image.width)
hits = newSeq[(float32, int16)](4) hits = newSeq[(float32, int16)](4)
@ -1190,51 +1182,58 @@ proc fillShapes(
# Apply the coverage and blend # Apply the coverage and blend
var x = startX var x = startX
when defined(amd64) and not defined(pixieNoSimd): when defined(amd64) and not defined(pixieNoSimd):
# When supported, SIMD blend as much as possible if blendMode.hasSimdBlender():
for _ in countup(x, image.width - 16, 4): # When supported, SIMD blend as much as possible
var coverage = mm_loadu_si128(coverages[x].addr)
coverage = mm_and_si128(coverage, first32)
let let
index = image.dataIndex(x, y) blenderSimd = blendMode.blenderSimd()
eqZero = mm_cmpeq_epi16(coverage, mm_setzero_si128()) first32 = cast[M128i]([uint32.high, 0, 0, 0]) # First 32 bits
if mm_movemask_epi8(eqZero) != 0xffff: oddMask = mm_set1_epi16(cast[int16](0xff00))
# If the coverages are not all zero div255 = mm_set1_epi16(cast[int16](0x8081))
if mm_movemask_epi8(mm_cmpeq_epi32(coverage, first32)) == 0xffff: vColor = mm_set1_epi32(cast[int32](rgbx))
# Coverages are all 255 for _ in countup(x, image.width - 16, 4):
if rgbx.a == 255 and blendMode == bmNormal: var coverage = mm_loadu_si128(coverages[x].addr)
mm_storeu_si128(image.data[index].addr, vColor) coverage = mm_and_si128(coverage, first32)
let
index = image.dataIndex(x, y)
eqZero = mm_cmpeq_epi16(coverage, mm_setzero_si128())
if mm_movemask_epi8(eqZero) != 0xffff:
# If the coverages are not all zero
if mm_movemask_epi8(mm_cmpeq_epi32(coverage, first32)) == 0xffff:
# Coverages are all 255
if rgbx.a == 255 and blendMode == bmNormal:
mm_storeu_si128(image.data[index].addr, vColor)
else:
let backdrop = mm_loadu_si128(image.data[index].addr)
mm_storeu_si128(
image.data[index].addr,
blenderSimd(backdrop, vColor)
)
else: else:
# Coverages are not all 255
coverage = unpackAlphaValues(coverage)
# Shift the coverages from `a` to `g` and `a` for multiplying
coverage = mm_or_si128(coverage, mm_srli_epi32(coverage, 16))
var
source = vColor
sourceEven = mm_slli_epi16(source, 8)
sourceOdd = mm_and_si128(source, oddMask)
sourceEven = mm_mulhi_epu16(sourceEven, coverage)
sourceOdd = mm_mulhi_epu16(sourceOdd, coverage)
sourceEven = mm_srli_epi16(mm_mulhi_epu16(sourceEven, div255), 7)
sourceOdd = mm_srli_epi16(mm_mulhi_epu16(sourceOdd, div255), 7)
source = mm_or_si128(sourceEven, mm_slli_epi16(sourceOdd, 8))
let backdrop = mm_loadu_si128(image.data[index].addr) let backdrop = mm_loadu_si128(image.data[index].addr)
mm_storeu_si128( mm_storeu_si128(
image.data[index].addr, image.data[index].addr,
blenderSimd(backdrop, vColor) blenderSimd(backdrop, source)
) )
else: x += 4
# Coverages are not all 255
coverage = unpackAlphaValues(coverage)
# Shift the coverages from `a` to `g` and `a` for multiplying
coverage = mm_or_si128(coverage, mm_srli_epi32(coverage, 16))
var
source = vColor
sourceEven = mm_slli_epi16(source, 8)
sourceOdd = mm_and_si128(source, oddMask)
sourceEven = mm_mulhi_epu16(sourceEven, coverage)
sourceOdd = mm_mulhi_epu16(sourceOdd, coverage)
sourceEven = mm_srli_epi16(mm_mulhi_epu16(sourceEven, div255), 7)
sourceOdd = mm_srli_epi16(mm_mulhi_epu16(sourceOdd, div255), 7)
source = mm_or_si128(sourceEven, mm_slli_epi16(sourceOdd, 8))
let backdrop = mm_loadu_si128(image.data[index].addr)
mm_storeu_si128(
image.data[index].addr,
blenderSimd(backdrop, source)
)
x += 4
while x < image.width: while x < image.width:
if x + 8 <= coverages.len: if x + 8 <= coverages.len:
@ -1328,11 +1327,11 @@ proc fillShapes(
inc x inc x
proc miterLimitToAngle*(limit: float32): float32 = proc miterLimitToAngle*(limit: float32): float32 =
## Converts milter-limit-ratio to miter-limit-angle. ## Converts miter-limit-ratio to miter-limit-angle.
arcsin(1 / limit) * 2 arcsin(1 / limit) * 2
proc angleToMiterLimit*(angle: float32): float32 = proc angleToMiterLimit*(angle: float32): float32 =
## Converts miter-limit-angle to milter-limit-ratio. ## Converts miter-limit-angle to miter-limit-ratio.
1 / sin(angle / 2) 1 / sin(angle / 2)
proc strokeShapes( proc strokeShapes(
@ -1528,11 +1527,7 @@ proc fillPath*(
mask = newMask(image.width, image.height) mask = newMask(image.width, image.height)
fill = newImage(image.width, image.height) fill = newImage(image.width, image.height)
mask.fillPath( mask.fillPath(path, transform, windingRule)
parseSomePath(path, transform.pixelScale()),
transform,
windingRule
)
case paint.kind: case paint.kind:
of pkSolid: of pkSolid:
@ -1604,7 +1599,7 @@ proc strokePath*(
fill = newImage(image.width, image.height) fill = newImage(image.width, image.height)
mask.strokePath( mask.strokePath(
parseSomePath(path, transform.pixelScale()), path,
transform, transform,
strokeWidth, strokeWidth,
lineCap, lineCap,

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

View file

@ -515,3 +515,19 @@ block:
drawDashedLine(@[12.float32, 3, 3]); drawDashedLine(@[12.float32, 3, 3]);
image.writeFile("tests/images/context/setLineDash_1.png") image.writeFile("tests/images/context/setLineDash_1.png")
block:
let
image = newImage(300, 150)
ctx = newContext(image)
image.fill(rgba(255, 255, 255, 255))
var paint = Paint(kind: pkSolid, color: rgba(0, 0, 255, 255))
paint.blendMode = bmExclusion
ctx.fillStyle = paint
ctx.fillRect(10, 10, 100, 100)
image.writeFile("tests/images/context/blendmode_1.png")