docs, bugfix, ctx blend test, remove seq[seq[Vec2]] from SomePath
This commit is contained in:
parent
b06f653ed9
commit
10bc3100e6
4 changed files with 76 additions and 62 deletions
|
@ -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 =
|
||||||
|
|
|
@ -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,
|
||||||
|
|
BIN
tests/images/context/blendmode_1.png
Normal file
BIN
tests/images/context/blendmode_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 880 B |
|
@ -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")
|
||||||
|
|
Loading…
Reference in a new issue