diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9977fff..2fa1bfa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,12 +6,18 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] + nim-version: ['1.4.x', 'stable'] + include: + - nim-version: '1.4.x' + - nim-version: 'stable' runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: ${{ matrix.nim-version }} - run: nimble test -d:release -y - run: nimble test -d:release -d:pixieNoSimd -y - run: nimble test --gc:orc -d:release -y diff --git a/pixie.nimble b/pixie.nimble index d8cf7b3..2336455 100644 --- a/pixie.nimble +++ b/pixie.nimble @@ -1,14 +1,14 @@ -version = "3.1.2" +version = "3.1.3" author = "Andre von Houck and Ryan Oldenburg" description = "Full-featured 2d graphics library for Nim." license = "MIT" srcDir = "src" -requires "nim >= 1.4.0" +requires "nim >= 1.4.8" requires "vmath >= 1.1.0" requires "chroma >= 0.2.5" -requires "zippy >= 0.7.4" +requires "zippy >= 0.8.1" requires "flatty >= 0.2.2" requires "nimsimd >= 1.0.0" requires "bumpy >= 1.0.3" diff --git a/src/pixie/fileformats/gif.nim b/src/pixie/fileformats/gif.nim index b2ee795..1b89302 100644 --- a/src/pixie/fileformats/gif.nim +++ b/src/pixie/fileformats/gif.nim @@ -101,7 +101,10 @@ proc decodeGif*(data: string): Image {.raises: [PixieError].} = # Turn full lzw data into bit stream. var - bs = initBitStream(lzwData) + bs = BitStreamReader( + src: cast[ptr UncheckedArray[uint8]](lzwData[0].addr), + len: lzwData.len + ) bitSize = lzwMinBitSize + 1 currentCodeTableMax = (1 shl (bitSize)) - 1 codeLast: int = -1 @@ -110,10 +113,10 @@ proc decodeGif*(data: string): Image {.raises: [PixieError].} = # Main decode loop. while codeLast != endCode: - if bs.pos + bitSize.int > bs.data.len * 8: failInvalid() + if bs.pos + bitSize.int > bs.len * 8: failInvalid() var # 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. carryOver: seq[int] diff --git a/src/pixie/fileformats/png.nim b/src/pixie/fileformats/png.nim index cfd65a2..fb96177 100644 --- a/src/pixie/fileformats/png.nim +++ b/src/pixie/fileformats/png.nim @@ -165,14 +165,31 @@ proc unfilter( discard # Not possible, parseHeader validates proc decodeImageData( + data: string, header: PngHeader, palette: seq[ColorRGB], - transparency, data: string + transparency: string, + idats: seq[(int, int)] ): seq[ColorRGBA] = + if idats.len == 0: + failInvalid() + result.setLen(header.width * header.height) 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 = case header.colorType: of 0: 1 @@ -340,7 +357,8 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} = counts = ChunkCounts() header: PngHeader palette: seq[ColorRGB] - transparency, imageData: string + transparency: string + idats: seq[(int, int)] prevChunkType: string # First chunk must be IHDR @@ -402,9 +420,7 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} = failInvalid() if header.colorType == 3 and counts.PLTE == 0: failInvalid() - let op = imageData.len - imageData.setLen(imageData.len + chunkLen) - copyMem(imageData[op].addr, data[pos].unsafeAddr, chunkLen) + idats.add((pos, chunkLen)) of "IEND": if chunkLen != 0: failInvalid() @@ -432,7 +448,7 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} = result.width = header.width result.height = header.height 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].} = ## Decodes the PNG data into an Image. diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim index 015276f..d45f0dc 100644 --- a/src/pixie/fileformats/svg.nim +++ b/src/pixie/fileformats/svg.nim @@ -560,6 +560,48 @@ proc draw(img: Image, node: XmlNode, ctxStack: var seq[Ctx]) = except: 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*( root: XmlNode, width = 0, height = 0 ): Image {.raises: [PixieError].} = @@ -600,14 +642,3 @@ proc decodeSvg*( raise e except: 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) diff --git a/tests/fileformats/svg/diffs/dragon2.png b/tests/fileformats/svg/diffs/dragon2.png index ac80807..04163e3 100644 Binary files a/tests/fileformats/svg/diffs/dragon2.png and b/tests/fileformats/svg/diffs/dragon2.png differ diff --git a/tests/fileformats/svg/emojitwo.png b/tests/fileformats/svg/emojitwo.png index 06e65a5..9ff9e7b 100644 Binary files a/tests/fileformats/svg/emojitwo.png and b/tests/fileformats/svg/emojitwo.png differ diff --git a/tests/fileformats/svg/flat-color-icons.png b/tests/fileformats/svg/flat-color-icons.png index a20e17f..4adbef3 100644 Binary files a/tests/fileformats/svg/flat-color-icons.png and b/tests/fileformats/svg/flat-color-icons.png differ diff --git a/tests/fileformats/svg/ionicons.png b/tests/fileformats/svg/ionicons.png index f757375..c0565c0 100644 Binary files a/tests/fileformats/svg/ionicons.png and b/tests/fileformats/svg/ionicons.png differ diff --git a/tests/fileformats/svg/noto-emoji.png b/tests/fileformats/svg/noto-emoji.png index 832c484..2960dcd 100644 Binary files a/tests/fileformats/svg/noto-emoji.png and b/tests/fileformats/svg/noto-emoji.png differ diff --git a/tests/fileformats/svg/openmoji.png b/tests/fileformats/svg/openmoji.png index c2f3695..1669e25 100644 Binary files a/tests/fileformats/svg/openmoji.png and b/tests/fileformats/svg/openmoji.png differ diff --git a/tests/fileformats/svg/rendered/dragon2.png b/tests/fileformats/svg/rendered/dragon2.png index 8f67649..a62ddc3 100644 Binary files a/tests/fileformats/svg/rendered/dragon2.png and b/tests/fileformats/svg/rendered/dragon2.png differ diff --git a/tests/fileformats/svg/simple-icons.png b/tests/fileformats/svg/simple-icons.png index 713afac..c240a57 100644 Binary files a/tests/fileformats/svg/simple-icons.png and b/tests/fileformats/svg/simple-icons.png differ diff --git a/tests/fileformats/svg/tabler-icons.png b/tests/fileformats/svg/tabler-icons.png index 01f016e..afb16a6 100644 Binary files a/tests/fileformats/svg/tabler-icons.png and b/tests/fileformats/svg/tabler-icons.png differ diff --git a/tests/fileformats/svg/twbs-icons.png b/tests/fileformats/svg/twbs-icons.png index abef7c7..cc31391 100644 Binary files a/tests/fileformats/svg/twbs-icons.png and b/tests/fileformats/svg/twbs-icons.png differ diff --git a/tests/fileformats/svg/twemoji.png b/tests/fileformats/svg/twemoji.png index 78ad6ff..d4483dc 100644 Binary files a/tests/fileformats/svg/twemoji.png and b/tests/fileformats/svg/twemoji.png differ