Compare commits
No commits in common. "84589810078f5b9ca85b510393e0cf1364481e65" and "523b364fcaa288d23ecb3f34c795da97d3637117" have entirely different histories.
8458981007
...
523b364fca
13 changed files with 36 additions and 143 deletions
|
@ -5,7 +5,6 @@ image.fill(rgba(255, 255, 255, 255))
|
||||||
|
|
||||||
var font = readFont("examples/data/Roboto-Regular_1.ttf")
|
var font = readFont("examples/data/Roboto-Regular_1.ttf")
|
||||||
font.size = 20
|
font.size = 20
|
||||||
font.paint.color = color(1, 0, 0)
|
|
||||||
|
|
||||||
let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."
|
let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
version = "5.0.7"
|
version = "5.0.6"
|
||||||
author = "Andre von Houck and Ryan Oldenburg"
|
author = "Andre von Houck and Ryan Oldenburg"
|
||||||
description = "Full-featured 2d graphics library for Nim."
|
description = "Full-featured 2d graphics library for Nim."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -49,39 +49,8 @@ proc decodeImageDimensions*(
|
||||||
## Decodes an image's dimensions from memory.
|
## Decodes an image's dimensions from memory.
|
||||||
decodeImageDimensions(data.cstring, data.len)
|
decodeImageDimensions(data.cstring, data.len)
|
||||||
|
|
||||||
template compare_as(T: typedesc, p,q: pointer): bool =
|
|
||||||
cast[ptr T](p)[] == cast[ptr T](q)[]
|
|
||||||
|
|
||||||
proc decodeImage*(data: pointer, len: int): Image {.raises: [PixieError].} =
|
|
||||||
## Loads an image from memory, from a pointer and a length.
|
|
||||||
if len > 8 and compare_as(uint64, data, pngSignature.addr):
|
|
||||||
decodePng(data, len).convertToImage()
|
|
||||||
elif len > 2 and compare_as(uint16, data, jpegStartOfImage.addr):
|
|
||||||
decodeJpeg(data, len)
|
|
||||||
elif len > 2 and compare_as(array[2,char], data, bmpSignature.cstring):
|
|
||||||
decodeBmp(data, len)
|
|
||||||
elif len > 5 and
|
|
||||||
compare_as(array[5,char], data, xmlSignature.cstring) or
|
|
||||||
compare_as(array[4,char], data, svgSignature.cstring):
|
|
||||||
# TODO: avoid allocating/initializing string
|
|
||||||
var s = newStringOfCap(len)
|
|
||||||
s.setLen len
|
|
||||||
copyMem(s.cstring, data, len)
|
|
||||||
newImage(parseSvg(s))
|
|
||||||
elif len > 6 and compare_as(array[6,char], data, gifSignatures[0].cstring) or
|
|
||||||
compare_as(array[6,char], data, gifSignatures[1].cstring):
|
|
||||||
newImage(decodeGif(data, len))
|
|
||||||
# TODO
|
|
||||||
# elif len > (14+8) and compare_as(array[4,char], data, qoiSignature.cstring):
|
|
||||||
# decodeQoi(data, len).convertToImage()
|
|
||||||
# elif len > 9 and compare_as(array[2,char], data, ppmSignatures[0].cstring) or
|
|
||||||
# compare_as(array[2,char], data, ppmSignatures[1].cstring):
|
|
||||||
# decodePpm(data, len)
|
|
||||||
else:
|
|
||||||
raise newException(PixieError, "Unsupported image file format")
|
|
||||||
|
|
||||||
proc decodeImage*(data: string): Image {.raises: [PixieError].} =
|
proc decodeImage*(data: string): Image {.raises: [PixieError].} =
|
||||||
## Loads an image from memory as a string.
|
## Loads an image from memory.
|
||||||
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
|
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
|
||||||
decodePng(data).convertToImage()
|
decodePng(data).convertToImage()
|
||||||
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage):
|
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage):
|
||||||
|
|
|
@ -481,8 +481,8 @@ proc fillText*(ctx: Context, text: string, at: Vec2) {.raises: [PixieError].} =
|
||||||
proc fillText*(
|
proc fillText*(
|
||||||
ctx: Context, text: string, x, y: float32
|
ctx: Context, text: string, x, y: float32
|
||||||
) {.inline, raises: [PixieError].} =
|
) {.inline, raises: [PixieError].} =
|
||||||
## Draws a text string at the specified coordinates, filling the string's
|
## Draws the outlines of the characters of a text string at the specified
|
||||||
## characters with the current fillStyle
|
## coordinates.
|
||||||
ctx.fillText(text, vec2(x, y))
|
ctx.fillText(text, vec2(x, y))
|
||||||
|
|
||||||
proc strokeText*(ctx: Context, text: string, at: Vec2) {.raises: [PixieError].} =
|
proc strokeText*(ctx: Context, text: string, at: Vec2) {.raises: [PixieError].} =
|
||||||
|
|
|
@ -227,17 +227,6 @@ proc decodeBmp*(data: string): Image {.raises: [PixieError].} =
|
||||||
|
|
||||||
decodeDib(data[14].unsafeAddr, data.len - 14)
|
decodeDib(data[14].unsafeAddr, data.len - 14)
|
||||||
|
|
||||||
proc decodeBmp*(data: pointer, len: int): Image {.raises: [PixieError].} =
|
|
||||||
## Decodes bitmap data into an image.
|
|
||||||
if len < 14:
|
|
||||||
failInvalid()
|
|
||||||
|
|
||||||
# BMP Header
|
|
||||||
if cast[ptr int16](data)[] != cast[ptr int16]("BM".cstring)[]:
|
|
||||||
failInvalid()
|
|
||||||
|
|
||||||
decodeDib(cast[ptr UncheckedArray[char]](data)[14].addr, len - 14)
|
|
||||||
|
|
||||||
proc decodeBmpDimensions*(
|
proc decodeBmpDimensions*(
|
||||||
data: pointer, len: int
|
data: pointer, len: int
|
||||||
): ImageDimensions {.raises: [PixieError].} =
|
): ImageDimensions {.raises: [PixieError].} =
|
||||||
|
|
|
@ -22,18 +22,12 @@ template failInvalid() =
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.push checks: off.}
|
{.push checks: off.}
|
||||||
|
|
||||||
template compare_as(T: typedesc, p,q: pointer): bool =
|
proc decodeGif*(data: string): Gif {.raises: [PixieError].} =
|
||||||
cast[ptr T](p)[] == cast[ptr T](q)[]
|
|
||||||
|
|
||||||
proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
|
||||||
let data = cast[ptr UncheckedArray[uint8]](data)
|
|
||||||
## Decodes GIF data.
|
## Decodes GIF data.
|
||||||
if len < 13:
|
if data.len < 13:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
if not (len > 6 and
|
if data[0 .. 5] notin gifSignatures:
|
||||||
compare_as(array[6,char], data, gifSignatures[0].cstring) or
|
|
||||||
compare_as(array[6,char], data, gifSignatures[1].cstring)):
|
|
||||||
raise newException(PixieError, "Invalid GIF file signature")
|
raise newException(PixieError, "Invalid GIF file signature")
|
||||||
|
|
||||||
result = Gif()
|
result = Gif()
|
||||||
|
@ -55,7 +49,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
|
|
||||||
var pos = 13
|
var pos = 13
|
||||||
|
|
||||||
if pos + globalColorTableSize * 3 > len:
|
if pos + globalColorTableSize * 3 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
var
|
var
|
||||||
|
@ -75,7 +69,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
|
|
||||||
proc skipSubBlocks() =
|
proc skipSubBlocks() =
|
||||||
while true: # Skip data sub-blocks
|
while true: # Skip data sub-blocks
|
||||||
if pos + 1 > len:
|
if pos + 1 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let subBlockSize = data.readUint8(pos).int
|
let subBlockSize = data.readUint8(pos).int
|
||||||
|
@ -88,7 +82,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
|
|
||||||
var controlExtension: ControlExtension
|
var controlExtension: ControlExtension
|
||||||
while true:
|
while true:
|
||||||
if pos + 1 > len:
|
if pos + 1 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let blockType = data.readUint8(pos)
|
let blockType = data.readUint8(pos)
|
||||||
|
@ -96,7 +90,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
|
|
||||||
case blockType:
|
case blockType:
|
||||||
of 0x2c: # Image
|
of 0x2c: # Image
|
||||||
if pos + 9 > len:
|
if pos + 9 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -114,7 +108,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
if imageWidth > screenWidth or imageHeight > screenHeight:
|
if imageWidth > screenWidth or imageHeight > screenHeight:
|
||||||
raise newException(PixieError, "Invalid GIF frame dimensions")
|
raise newException(PixieError, "Invalid GIF frame dimensions")
|
||||||
|
|
||||||
if pos + localColorTableSize * 3 > len:
|
if pos + localColorTableSize * 3 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
var localColorTable: seq[ColorRGBX]
|
var localColorTable: seq[ColorRGBX]
|
||||||
|
@ -129,7 +123,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
)
|
)
|
||||||
pos += 3
|
pos += 3
|
||||||
|
|
||||||
if pos + 1 > len:
|
if pos + 1 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let minCodeSize = data.readUint8(pos).int
|
let minCodeSize = data.readUint8(pos).int
|
||||||
|
@ -141,7 +135,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
# The image data is contained in a sequence of sub-blocks
|
# The image data is contained in a sequence of sub-blocks
|
||||||
var lzwDataBlocks: seq[(int, int)] # (offset, len)
|
var lzwDataBlocks: seq[(int, int)] # (offset, len)
|
||||||
while true:
|
while true:
|
||||||
if pos + 1 > len:
|
if pos + 1 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let subBlockSize = data.readUint8(pos).int
|
let subBlockSize = data.readUint8(pos).int
|
||||||
|
@ -150,7 +144,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
if subBlockSize == 0:
|
if subBlockSize == 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
if pos + subBlockSize > len:
|
if pos + subBlockSize > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
lzwDataBlocks.add((pos, subBlockSize))
|
lzwDataBlocks.add((pos, subBlockSize))
|
||||||
|
@ -313,7 +307,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
controlExtension = ControlExtension()
|
controlExtension = ControlExtension()
|
||||||
|
|
||||||
of 0x21: # Extension
|
of 0x21: # Extension
|
||||||
if pos + 1 > len:
|
if pos + 1 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let extensionType = data.readUint8(pos + 0)
|
let extensionType = data.readUint8(pos + 0)
|
||||||
|
@ -322,7 +316,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
case extensionType:
|
case extensionType:
|
||||||
of 0xf9:
|
of 0xf9:
|
||||||
# Graphic Control Extension
|
# Graphic Control Extension
|
||||||
if pos + 1 > len:
|
if pos + 1 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let blockSize = data.readUint8(pos).int
|
let blockSize = data.readUint8(pos).int
|
||||||
|
@ -331,7 +325,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
if blockSize != 4:
|
if blockSize != 4:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
if pos + blockSize > len:
|
if pos + blockSize > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
controlExtension.fields = data.readUint8(pos + 0)
|
controlExtension.fields = data.readUint8(pos + 0)
|
||||||
|
@ -350,7 +344,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
|
|
||||||
of 0xff:
|
of 0xff:
|
||||||
# Application Specific
|
# Application Specific
|
||||||
if pos + 1 > len:
|
if pos + 1 > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
let blockSize = data.readUint8(pos).int
|
let blockSize = data.readUint8(pos).int
|
||||||
|
@ -359,7 +353,7 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
if blockSize != 11:
|
if blockSize != 11:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
if pos + blockSize > len:
|
if pos + blockSize > data.len:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
pos += blockSize
|
pos += blockSize
|
||||||
|
@ -384,9 +378,6 @@ proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} =
|
||||||
for interval in result.intervals:
|
for interval in result.intervals:
|
||||||
result.duration += interval
|
result.duration += interval
|
||||||
|
|
||||||
proc decodeGif*(data: string): Gif {.raises: [PixieError].} =
|
|
||||||
return decodeGif(data.cstring, data.len)
|
|
||||||
|
|
||||||
proc decodeGifDimensions*(
|
proc decodeGifDimensions*(
|
||||||
data: pointer, len: int
|
data: pointer, len: int
|
||||||
): ImageDimensions {.raises: [PixieError].} =
|
): ImageDimensions {.raises: [PixieError].} =
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import chroma, flatty/binny, ../common, ../images, ../internal,
|
import chroma, flatty/binny, ../common, ../images, ../internal,
|
||||||
../simd, std/sequtils, std/strutils
|
../simd, std/decls, std/sequtils, std/strutils
|
||||||
|
|
||||||
# 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.
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ proc fillBitBuffer(state: var DecoderState) =
|
||||||
|
|
||||||
proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 =
|
proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 =
|
||||||
## Decode a uint8 from the huffman table.
|
## Decode a uint8 from the huffman table.
|
||||||
var huffman = state.huffmanTables[tableCurrent][table].addr
|
var huffman {.byaddr.} = state.huffmanTables[tableCurrent][table]
|
||||||
|
|
||||||
state.fillBitBuffer()
|
state.fillBitBuffer()
|
||||||
|
|
||||||
|
@ -884,15 +884,14 @@ proc idctBlock(component: var Component, offset: int, data: array[64, int16]) =
|
||||||
|
|
||||||
proc decodeBlock(state: var DecoderState, comp, row, column: int) =
|
proc decodeBlock(state: var DecoderState, comp, row, column: int) =
|
||||||
## Decodes a block.
|
## Decodes a block.
|
||||||
var data = state.components[comp].blocks[row][column].addr
|
var data {.byaddr.} = state.components[comp].blocks[row][column]
|
||||||
|
|
||||||
if state.progressive:
|
if state.progressive:
|
||||||
if state.spectralStart == 0:
|
if state.spectralStart == 0:
|
||||||
state.decodeProgressiveBlock(comp, data[])
|
state.decodeProgressiveBlock(comp, data)
|
||||||
else:
|
else:
|
||||||
state.decodeProgressiveContinuationBlock(comp, data[])
|
state.decodeProgressiveContinuationBlock(comp, data)
|
||||||
else:
|
else:
|
||||||
state.decodeRegularBlock(comp, data[])
|
state.decodeRegularBlock(comp, data)
|
||||||
|
|
||||||
proc checkRestart(state: var DecoderState) =
|
proc checkRestart(state: var DecoderState) =
|
||||||
## Check if we might have run into a restart marker, then deal with it.
|
## Check if we might have run into a restart marker, then deal with it.
|
||||||
|
@ -942,7 +941,7 @@ proc quantizationAndIDCTPass(state: var DecoderState) =
|
||||||
failInvalid()
|
failInvalid()
|
||||||
for column in 0 ..< h:
|
for column in 0 ..< h:
|
||||||
for row in 0 ..< w:
|
for row in 0 ..< w:
|
||||||
var data = state.components[comp].blocks[row][column].addr
|
var data {.byaddr.} = state.components[comp].blocks[row][column]
|
||||||
|
|
||||||
when defined(amd64) and allowSimd:
|
when defined(amd64) and allowSimd:
|
||||||
for i in 0 ..< 8: # 8 per pass
|
for i in 0 ..< 8: # 8 per pass
|
||||||
|
@ -958,7 +957,7 @@ proc quantizationAndIDCTPass(state: var DecoderState) =
|
||||||
|
|
||||||
state.components[comp].idctBlock(
|
state.components[comp].idctBlock(
|
||||||
state.components[comp].widthStride * column * 8 + row * 8,
|
state.components[comp].widthStride * column * 8 + row * 8,
|
||||||
data[]
|
data
|
||||||
)
|
)
|
||||||
|
|
||||||
proc magnifyXBy2(mask: Mask): Mask =
|
proc magnifyXBy2(mask: Mask): Mask =
|
||||||
|
@ -1092,12 +1091,12 @@ proc buildImage(state: var DecoderState): Image =
|
||||||
else:
|
else:
|
||||||
failInvalid("invalid orientation")
|
failInvalid("invalid orientation")
|
||||||
|
|
||||||
proc decodeJpeg*(data: pointer, len: int): Image {.raises: [PixieError].} =
|
proc decodeJpeg*(data: string): Image {.raises: [PixieError].} =
|
||||||
## Decodes the JPEG into an Image.
|
## Decodes the JPEG into an Image.
|
||||||
|
|
||||||
var state = DecoderState()
|
var state = DecoderState()
|
||||||
state.buffer = cast[ptr UncheckedArray[uint8]](data)
|
state.buffer = cast[ptr UncheckedArray[uint8]](data.cstring)
|
||||||
state.len = len
|
state.len = data.len
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
if state.readUint8() != 0xFF:
|
if state.readUint8() != 0xFF:
|
||||||
|
@ -1157,10 +1156,6 @@ proc decodeJpeg*(data: pointer, len: int): Image {.raises: [PixieError].} =
|
||||||
|
|
||||||
state.buildImage()
|
state.buildImage()
|
||||||
|
|
||||||
proc decodeJpeg*(data: string): Image {.raises: [PixieError].} =
|
|
||||||
## Decodes the JPEG into an Image.
|
|
||||||
decodeJpeg(data.cstring, data.len)
|
|
||||||
|
|
||||||
proc decodeJpegDimensions*(
|
proc decodeJpegDimensions*(
|
||||||
data: pointer, len: int
|
data: pointer, len: int
|
||||||
): ImageDimensions {.raises: [PixieError].} =
|
): ImageDimensions {.raises: [PixieError].} =
|
||||||
|
|
|
@ -455,7 +455,6 @@ proc parseCmapTable(buf: string, offset: int): CmapTable =
|
||||||
|
|
||||||
let format = buf.readUint16(i + 0).swap()
|
let format = buf.readUint16(i + 0).swap()
|
||||||
if format == 4:
|
if format == 4:
|
||||||
# https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values
|
|
||||||
type Format4 = object
|
type Format4 = object
|
||||||
format: uint16
|
format: uint16
|
||||||
length: uint16
|
length: uint16
|
||||||
|
@ -519,46 +518,8 @@ proc parseCmapTable(buf: string, offset: int): CmapTable =
|
||||||
if c != 65535:
|
if c != 65535:
|
||||||
result.runeToGlyphId[Rune(c)] = glyphId.uint16
|
result.runeToGlyphId[Rune(c)] = glyphId.uint16
|
||||||
result.glyphIdToRune[glyphId.uint16] = Rune(c)
|
result.glyphIdToRune[glyphId.uint16] = Rune(c)
|
||||||
|
|
||||||
elif format == 12:
|
|
||||||
# https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-12-segmented-coverage
|
|
||||||
type Format12 = object
|
|
||||||
format: uint16
|
|
||||||
reserved: uint16
|
|
||||||
length: uint32
|
|
||||||
language: uint32
|
|
||||||
numGroups: uint32
|
|
||||||
|
|
||||||
buf.eofCheck(i + 16)
|
|
||||||
|
|
||||||
var subTable: Format12
|
|
||||||
subTable.format = format
|
|
||||||
subTable.reserved = buf.readUint16(i + 2).swap()
|
|
||||||
subTable.length = buf.readUint32(i + 4).swap()
|
|
||||||
subTable.language = buf.readUint32(i + 8).swap()
|
|
||||||
subTable.numGroups = buf.readUint32(i + 12).swap()
|
|
||||||
i += 16
|
|
||||||
|
|
||||||
buf.eofCheck(i + subTable.numGroups.int * 12)
|
|
||||||
|
|
||||||
for k in 0 ..< subTable.numGroups:
|
|
||||||
let startCharCode = buf.readUint32(i + 0).swap()
|
|
||||||
let endCharCode = buf.readUint32(i + 4).swap()
|
|
||||||
let startGlyphId = buf.readUint32(i + 8).swap()
|
|
||||||
|
|
||||||
for c in startCharCode .. endCharCode:
|
|
||||||
let glyphId = startGlyphId + (c - startCharCode)
|
|
||||||
if glyphId > uint16.high:
|
|
||||||
# TODO: currently only 16 bit glyph ids are supported
|
|
||||||
raise newException(PixieError, "Found glyph outside of uint16 range: " & $glyphId)
|
|
||||||
|
|
||||||
result.runeToGlyphId[Rune(c)] = uint16(glyphId)
|
|
||||||
result.glyphIdToRune[uint16(glyphId)] = Rune(c)
|
|
||||||
|
|
||||||
i += 12
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# TODO implement other windows formats
|
# TODO implement other Windows encodingIDs
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
# TODO implement other cmap platformIDs
|
# TODO implement other cmap platformIDs
|
||||||
|
|
|
@ -497,6 +497,7 @@ proc blendRect(a, b: Image, pos: Ivec2, blendMode: BlendMode) =
|
||||||
xEnd - xStart
|
xEnd - xStart
|
||||||
)
|
)
|
||||||
of MaskBlend:
|
of MaskBlend:
|
||||||
|
{.linearScanEnd.}
|
||||||
if yStart + py > 0:
|
if yStart + py > 0:
|
||||||
zeroMem(a.data[0].addr, (yStart + py) * a.width * 4)
|
zeroMem(a.data[0].addr, (yStart + py) * a.width * 4)
|
||||||
for y in yStart ..< yEnd:
|
for y in yStart ..< yEnd:
|
||||||
|
@ -606,6 +607,7 @@ proc drawSmooth(a, b: Image, transform: Mat3, blendMode: BlendMode) =
|
||||||
)
|
)
|
||||||
|
|
||||||
of MaskBlend:
|
of MaskBlend:
|
||||||
|
{.linearScanEnd.}
|
||||||
if blendMode == MaskBlend and xStart > 0:
|
if blendMode == MaskBlend and xStart > 0:
|
||||||
zeroMem(a.data[a.dataIndex(0, y)].addr, xStart * 4)
|
zeroMem(a.data[a.dataIndex(0, y)].addr, xStart * 4)
|
||||||
|
|
||||||
|
|
|
@ -1505,6 +1505,7 @@ proc fillCoverage(
|
||||||
)
|
)
|
||||||
|
|
||||||
of MaskBlend:
|
of MaskBlend:
|
||||||
|
{.linearScanEnd.}
|
||||||
blendLineCoverageMask(
|
blendLineCoverageMask(
|
||||||
image.getUncheckedArray(startX, y),
|
image.getUncheckedArray(startX, y),
|
||||||
cast[ptr UncheckedArray[uint8]](coverages[0].unsafeAddr),
|
cast[ptr UncheckedArray[uint8]](coverages[0].unsafeAddr),
|
||||||
|
@ -1559,6 +1560,7 @@ proc fillHits(
|
||||||
blendLineNormal(image.getUncheckedArray(start, y), rgbx, len)
|
blendLineNormal(image.getUncheckedArray(start, y), rgbx, len)
|
||||||
|
|
||||||
of MaskBlend:
|
of MaskBlend:
|
||||||
|
{.linearScanEnd.}
|
||||||
var filledTo = startX
|
var filledTo = startX
|
||||||
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
||||||
if maskClears: # Clear any gap between this fill and the previous fill
|
if maskClears: # Clear any gap between this fill and the previous fill
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 101 KiB |
|
@ -4,21 +4,6 @@ proc wh(image: Image): Vec2 =
|
||||||
## Return with and height as a size vector.
|
## Return with and height as a size vector.
|
||||||
vec2(image.width.float32, image.height.float32)
|
vec2(image.width.float32, image.height.float32)
|
||||||
|
|
||||||
block:
|
|
||||||
var font = readFont("tests/fonts/NotoEmoji.otf")
|
|
||||||
font.size = 26
|
|
||||||
let image = newImage(800, 300)
|
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
|
||||||
image.fillText(font, """
|
|
||||||
🚑🐑👭🔉🚷🦣💆🔁💺🚵🕦🔦🗓🦟😶🦄⌛🍙😄🇽
|
|
||||||
🐠💓🦗🎭🏛🔴🫕🧶🍖🦁🏋🌗🛬🕐💡👉🎯🕔🚏🚲
|
|
||||||
🐵🎍💳🥬🟦🪘📠📊🎧🎦🎁🌌🪲🦩🤢☎🚺🚾👺🚃
|
|
||||||
🐨🌆🥉💭🗳🦵🟪📆🥮⏯🩴💷🦲➗🌶🧜🖖⏰🛗🔻
|
|
||||||
📁🧞😃🌴🚶🦙🔎⏲🔵🖐☦😪🌯🙆🇺😂🍅🇿🚟🤜
|
|
||||||
📼👰🍁📽☪🔄🤝🔧🦸🏰🏳🔜🎥🚋🇫🦨🏜🆖🏤🪖⏏""")
|
|
||||||
|
|
||||||
image.xray("tests/fonts/masters/emoji.png")
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var font = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
var font = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||||
font.size = 24
|
font.size = 24
|
||||||
|
|
Loading…
Reference in a new issue