6
.github/workflows/build.yml
vendored
|
@ -6,12 +6,18 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
nim-version: ['1.4.x', 'stable']
|
||||||
|
include:
|
||||||
|
- nim-version: '1.4.x'
|
||||||
|
- nim-version: 'stable'
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: jiro4989/setup-nim-action@v1
|
- uses: jiro4989/setup-nim-action@v1
|
||||||
|
with:
|
||||||
|
nim-version: ${{ matrix.nim-version }}
|
||||||
- run: nimble test -d:release -y
|
- run: nimble test -d:release -y
|
||||||
- run: nimble test -d:release -d:pixieNoSimd -y
|
- run: nimble test -d:release -d:pixieNoSimd -y
|
||||||
- run: nimble test --gc:orc -d:release -y
|
- run: nimble test --gc:orc -d:release -y
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
version = "3.1.2"
|
version = "3.1.3"
|
||||||
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"
|
||||||
|
|
||||||
srcDir = "src"
|
srcDir = "src"
|
||||||
|
|
||||||
requires "nim >= 1.4.0"
|
requires "nim >= 1.4.8"
|
||||||
requires "vmath >= 1.1.0"
|
requires "vmath >= 1.1.0"
|
||||||
requires "chroma >= 0.2.5"
|
requires "chroma >= 0.2.5"
|
||||||
requires "zippy >= 0.7.4"
|
requires "zippy >= 0.8.1"
|
||||||
requires "flatty >= 0.2.2"
|
requires "flatty >= 0.2.2"
|
||||||
requires "nimsimd >= 1.0.0"
|
requires "nimsimd >= 1.0.0"
|
||||||
requires "bumpy >= 1.0.3"
|
requires "bumpy >= 1.0.3"
|
||||||
|
|
|
@ -101,7 +101,10 @@ proc decodeGif*(data: string): Image {.raises: [PixieError].} =
|
||||||
|
|
||||||
# Turn full lzw data into bit stream.
|
# Turn full lzw data into bit stream.
|
||||||
var
|
var
|
||||||
bs = initBitStream(lzwData)
|
bs = BitStreamReader(
|
||||||
|
src: cast[ptr UncheckedArray[uint8]](lzwData[0].addr),
|
||||||
|
len: lzwData.len
|
||||||
|
)
|
||||||
bitSize = lzwMinBitSize + 1
|
bitSize = lzwMinBitSize + 1
|
||||||
currentCodeTableMax = (1 shl (bitSize)) - 1
|
currentCodeTableMax = (1 shl (bitSize)) - 1
|
||||||
codeLast: int = -1
|
codeLast: int = -1
|
||||||
|
@ -110,10 +113,10 @@ proc decodeGif*(data: string): Image {.raises: [PixieError].} =
|
||||||
|
|
||||||
# Main decode loop.
|
# Main decode loop.
|
||||||
while codeLast != endCode:
|
while codeLast != endCode:
|
||||||
if bs.pos + bitSize.int > bs.data.len * 8: failInvalid()
|
if bs.pos + bitSize.int > bs.len * 8: failInvalid()
|
||||||
var
|
var
|
||||||
# Read variable bits out of the table.
|
# Read variable bits out of the table.
|
||||||
codeId = bs.readBits(bitSize).int
|
codeId = bs.readBits(bitSize.int).int
|
||||||
# Some time we need to carry over table information.
|
# Some time we need to carry over table information.
|
||||||
carryOver: seq[int]
|
carryOver: seq[int]
|
||||||
|
|
||||||
|
|
|
@ -165,14 +165,31 @@ proc unfilter(
|
||||||
discard # Not possible, parseHeader validates
|
discard # Not possible, parseHeader validates
|
||||||
|
|
||||||
proc decodeImageData(
|
proc decodeImageData(
|
||||||
|
data: string,
|
||||||
header: PngHeader,
|
header: PngHeader,
|
||||||
palette: seq[ColorRGB],
|
palette: seq[ColorRGB],
|
||||||
transparency, data: string
|
transparency: string,
|
||||||
|
idats: seq[(int, int)]
|
||||||
): seq[ColorRGBA] =
|
): seq[ColorRGBA] =
|
||||||
|
if idats.len == 0:
|
||||||
|
failInvalid()
|
||||||
|
|
||||||
result.setLen(header.width * header.height)
|
result.setLen(header.width * header.height)
|
||||||
|
|
||||||
let
|
let
|
||||||
uncompressed = try: uncompress(data) except ZippyError: failInvalid()
|
uncompressed =
|
||||||
|
if idats.len > 1:
|
||||||
|
var imageData: string
|
||||||
|
for (start, len) in idats:
|
||||||
|
let op = imageData.len
|
||||||
|
imageData.setLen(imageData.len + len)
|
||||||
|
copyMem(imageData[op].addr, data[start].unsafeAddr, len)
|
||||||
|
try: uncompress(imageData) except ZippyError: failInvalid()
|
||||||
|
else:
|
||||||
|
let
|
||||||
|
(start, len) = idats[0]
|
||||||
|
p = data[start].unsafeAddr
|
||||||
|
try: uncompress(p, len) except ZippyError: failInvalid()
|
||||||
valuesPerPixel =
|
valuesPerPixel =
|
||||||
case header.colorType:
|
case header.colorType:
|
||||||
of 0: 1
|
of 0: 1
|
||||||
|
@ -340,7 +357,8 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} =
|
||||||
counts = ChunkCounts()
|
counts = ChunkCounts()
|
||||||
header: PngHeader
|
header: PngHeader
|
||||||
palette: seq[ColorRGB]
|
palette: seq[ColorRGB]
|
||||||
transparency, imageData: string
|
transparency: string
|
||||||
|
idats: seq[(int, int)]
|
||||||
prevChunkType: string
|
prevChunkType: string
|
||||||
|
|
||||||
# First chunk must be IHDR
|
# First chunk must be IHDR
|
||||||
|
@ -402,9 +420,7 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} =
|
||||||
failInvalid()
|
failInvalid()
|
||||||
if header.colorType == 3 and counts.PLTE == 0:
|
if header.colorType == 3 and counts.PLTE == 0:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
let op = imageData.len
|
idats.add((pos, chunkLen))
|
||||||
imageData.setLen(imageData.len + chunkLen)
|
|
||||||
copyMem(imageData[op].addr, data[pos].unsafeAddr, chunkLen)
|
|
||||||
of "IEND":
|
of "IEND":
|
||||||
if chunkLen != 0:
|
if chunkLen != 0:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
@ -432,7 +448,7 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} =
|
||||||
result.width = header.width
|
result.width = header.width
|
||||||
result.height = header.height
|
result.height = header.height
|
||||||
result.channels = 4
|
result.channels = 4
|
||||||
result.data = decodeImageData(header, palette, transparency, imageData)
|
result.data = decodeImageData(data, header, palette, transparency, idats)
|
||||||
|
|
||||||
proc decodePng*(data: string): Image {.raises: [PixieError].} =
|
proc decodePng*(data: string): Image {.raises: [PixieError].} =
|
||||||
## Decodes the PNG data into an Image.
|
## Decodes the PNG data into an Image.
|
||||||
|
|
|
@ -560,6 +560,48 @@ proc draw(img: Image, node: XmlNode, ctxStack: var seq[Ctx]) =
|
||||||
except:
|
except:
|
||||||
raise currentExceptionAsPixieError()
|
raise currentExceptionAsPixieError()
|
||||||
|
|
||||||
|
proc decodeSvg*(
|
||||||
|
data: string, width = 0, height = 0
|
||||||
|
): Image {.raises: [PixieError].} =
|
||||||
|
## Render SVG XML and return the image. Defaults to the SVG's view box size.
|
||||||
|
try:
|
||||||
|
let root = parseXml(data)
|
||||||
|
if root.tag != "svg":
|
||||||
|
failInvalid()
|
||||||
|
|
||||||
|
let
|
||||||
|
viewBox = root.attr("viewBox")
|
||||||
|
box = viewBox.split(" ")
|
||||||
|
viewBoxMinX = parseInt(box[0])
|
||||||
|
viewBoxMinY = parseInt(box[1])
|
||||||
|
viewBoxWidth = parseInt(box[2])
|
||||||
|
viewBoxHeight = parseInt(box[3])
|
||||||
|
|
||||||
|
var rootCtx = initCtx()
|
||||||
|
rootCtx = decodeCtx(rootCtx, root)
|
||||||
|
|
||||||
|
if viewBoxMinX != 0 or viewBoxMinY != 0:
|
||||||
|
let viewBoxMin = vec2(-viewBoxMinX.float32, -viewBoxMinY.float32)
|
||||||
|
rootCtx.transform = rootCtx.transform * translate(viewBoxMin)
|
||||||
|
|
||||||
|
if width == 0 and height == 0: # Default to the view box size
|
||||||
|
result = newImage(viewBoxWidth, viewBoxHeight)
|
||||||
|
else:
|
||||||
|
result = newImage(width, height)
|
||||||
|
|
||||||
|
let
|
||||||
|
scaleX = width.float32 / viewBoxWidth.float32
|
||||||
|
scaleY = height.float32 / viewBoxHeight.float32
|
||||||
|
rootCtx.transform = rootCtx.transform * scale(vec2(scaleX, scaleY))
|
||||||
|
|
||||||
|
var ctxStack = @[rootCtx]
|
||||||
|
for node in root:
|
||||||
|
result.draw(node, ctxStack)
|
||||||
|
except PixieError as e:
|
||||||
|
raise e
|
||||||
|
except:
|
||||||
|
raise newException(PixieError, "Unable to load SVG")
|
||||||
|
|
||||||
proc decodeSvg*(
|
proc decodeSvg*(
|
||||||
root: XmlNode, width = 0, height = 0
|
root: XmlNode, width = 0, height = 0
|
||||||
): Image {.raises: [PixieError].} =
|
): Image {.raises: [PixieError].} =
|
||||||
|
@ -600,14 +642,3 @@ proc decodeSvg*(
|
||||||
raise e
|
raise e
|
||||||
except:
|
except:
|
||||||
raise newException(PixieError, "Unable to load SVG")
|
raise newException(PixieError, "Unable to load SVG")
|
||||||
|
|
||||||
proc decodeSvg*(
|
|
||||||
data: string, width = 0, height = 0
|
|
||||||
): Image {.raises: [PixieError].} =
|
|
||||||
## Render SVG data and return the image. Defaults to the SVG's view box size.
|
|
||||||
let root =
|
|
||||||
try:
|
|
||||||
parseXml(data)
|
|
||||||
except:
|
|
||||||
raise currentExceptionAsPixieError()
|
|
||||||
decodeSvg(root)
|
|
||||||
|
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
Before Width: | Height: | Size: 644 KiB After Width: | Height: | Size: 674 KiB |
Before Width: | Height: | Size: 783 KiB After Width: | Height: | Size: 782 KiB |
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 3.4 MiB |
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 610 KiB After Width: | Height: | Size: 629 KiB |
Before Width: | Height: | Size: 519 KiB After Width: | Height: | Size: 519 KiB |
Before Width: | Height: | Size: 3.9 MiB After Width: | Height: | Size: 3.9 MiB |