Skip to content

Commit 0fc4ef0

Browse files
authored
Merge pull request #575 from treeform/add-image-base64
Add image base64 helpers
2 parents c140e23 + 47cddb1 commit 0fc4ef0

5 files changed

Lines changed: 100 additions & 7 deletions

File tree

bindings/bindings.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ exportRefObject Image:
112112
newImage(int, int)
113113
procs:
114114
writeFile(Image, string)
115+
encodeBase64
115116
copy(Image)
116117
getColor
117118
setColor
@@ -291,6 +292,7 @@ exportRefObject Context:
291292
isPointInStroke(Context, Path, float32, float32)
292293

293294
exportProcs:
295+
decodeBase64
294296
decodeImage
295297
decodeImageDimensions
296298
readImage

src/pixie.nim

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import bumpy, chroma, flatty/binny, os, pixie/common, pixie/contexts,
2-
pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpeg,
3-
pixie/fileformats/png, pixie/fileformats/ppm, pixie/fileformats/qoi,
4-
pixie/fileformats/svg, pixie/fonts, pixie/images, pixie/internal,
5-
pixie/paints, pixie/paths, strutils, vmath
1+
import
2+
std/[os, strutils],
3+
bumpy, chroma, flatty/binny, vmath,
4+
pixie/[common, contexts, fonts, imagebase64, images, internal, paints, paths],
5+
pixie/fileformats/[bmp, gif, jpeg, png, ppm, qoi, svg]
66

7-
export bumpy, chroma, common, contexts, fonts, images, paints, paths, vmath
7+
export bumpy, chroma, common, contexts, fonts, imagebase64, images, paints,
8+
paths, vmath
89

910
type
1011
FileFormat* = enum
@@ -85,7 +86,9 @@ proc readImage*(filePath: string): Image {.inline, raises: [PixieError].} =
8586
except IOError as e:
8687
raise newException(PixieError, e.msg, e)
8788

88-
proc encodeImage*(image: Image, fileFormat: FileFormat): string {.raises: [PixieError].} =
89+
proc encodeImage*(
90+
image: Image, fileFormat: FileFormat
91+
): string {.raises: [PixieError].} =
8992
## Encodes an image into memory.
9093
case fileFormat:
9194
of PngFormat:

src/pixie/imagebase64.nim

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import
2+
std/base64 as stdbase64,
3+
std/strutils,
4+
flatty/binny,
5+
common,
6+
fileformats/[bmp, gif, jpeg, png, ppm, qoi, svg]
7+
8+
proc decodeImageData(data: string): Image {.raises: [PixieError].} =
9+
## Decodes supported image bytes into an image.
10+
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
11+
decodePng(data).convertToImage()
12+
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage):
13+
decodeJpeg(data)
14+
elif data.len > 2 and data.readStr(0, 2) == bmpSignature:
15+
decodeBmp(data)
16+
elif data.len > 5 and (
17+
data.readStr(0, 5) == xmlSignature or
18+
data.readStr(0, 4) == svgSignature
19+
):
20+
newImage(parseSvg(data))
21+
elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
22+
newImage(decodeGif(data))
23+
elif data.len > (14 + 8) and data.readStr(0, 4) == qoiSignature:
24+
decodeQoi(data).convertToImage()
25+
elif data.len > 9 and data.readStr(0, 2) in ppmSignatures:
26+
decodePpm(data)
27+
else:
28+
raise newException(PixieError, "Unsupported image file format")
29+
30+
proc getPayload(data: string): string {.raises: [PixieError].} =
31+
## Gets the base64 payload from plain base64 data or a data URL.
32+
if data.startsWith("data:"):
33+
let comma = data.find(',')
34+
if comma == -1:
35+
raise newException(PixieError, "Invalid data URL")
36+
if comma + 1 < data.len:
37+
data[comma + 1 .. ^1]
38+
else:
39+
""
40+
else:
41+
data
42+
43+
proc encodeBase64*(image: Image): string {.raises: [PixieError].} =
44+
## Encodes an image into PNG format and returns it as base64.
45+
stdbase64.encode(image.encodePng())
46+
47+
proc decodeBase64*(data: string): Image {.raises: [PixieError].} =
48+
## Decodes a base64 image string into an image.
49+
try:
50+
decodeImageData(stdbase64.decode(data.getPayload()))
51+
except ValueError as e:
52+
raise newException(PixieError, e.msg, e)

tests/test_base64.nim

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import
2+
std/base64,
3+
chroma, pixie, pixie/fileformats/png
4+
5+
block:
6+
let image = newImage(2, 1)
7+
image[0, 0] = rgba(255, 0, 0, 255)
8+
image[1, 0] = rgba(0, 0, 255, 128)
9+
10+
let encoded = image.encodeBase64()
11+
doAssert encoded == base64.encode(image.encodePng())
12+
13+
let decoded = decodeBase64(encoded)
14+
doAssert decoded.width == image.width
15+
doAssert decoded.height == image.height
16+
doAssert decoded.data == image.data
17+
18+
block:
19+
let image = newImage(1, 1)
20+
image[0, 0] = rgba(0, 255, 0, 255)
21+
22+
let
23+
encoded = image.encodeBase64()
24+
decoded = decodeBase64("data:image/png;base64," & encoded)
25+
26+
doAssert decoded.width == image.width
27+
doAssert decoded.height == image.height
28+
doAssert decoded.data == image.data
29+
30+
block:
31+
try:
32+
discard decodeBase64("nope")
33+
doAssert false
34+
except PixieError:
35+
discard

tests/tests.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import
2+
test_base64,
23
test_bmp,
34
test_contexts,
45
test_fonts,

0 commit comments

Comments
 (0)