pixie/src/pixie.nim
2022-07-26 00:19:43 -05:00

119 lines
4.4 KiB
Nim

import bumpy, chroma, flatty/binny, os, pixie/common, pixie/contexts,
pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpeg,
pixie/fileformats/png, pixie/fileformats/ppm, pixie/fileformats/qoi,
pixie/fileformats/svg, pixie/fonts, pixie/images, pixie/internal,
pixie/paints, pixie/paths, strutils, vmath
export bumpy, chroma, common, contexts, fonts, images, paints, paths, vmath
type
FileFormat* = enum
PngFormat, BmpFormat, JpegFormat, GifFormat, QoiFormat, PpmFormat
converter autoStraightAlpha*(c: ColorRGBX): ColorRGBA {.inline, raises: [].} =
## Convert a premultiplied alpha RGBA to a straight alpha RGBA.
c.rgba()
converter autoPremultipliedAlpha*(c: ColorRGBA): ColorRGBX {.inline, raises: [].} =
## Convert a straight alpha RGBA to a premultiplied alpha RGBA.
c.rgbx()
proc decodeImageDimensions*(
data: string
): ImageDimensions {.raises: [PixieError].} =
## Decodes an image's dimensions from memory.
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
decodePngDimensions(data)
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage):
decodeJpegDimensions(data)
elif data.len > 2 and data.readStr(0, 2) == bmpSignature:
decodeBmpDimensions(data)
elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
decodeGifDimensions(data)
elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature:
decodeQoiDimensions(data)
elif data.len > 9 and data.readStr(0, 2) in ppmSignatures:
decodePpmDimensions(data)
else:
raise newException(PixieError, "Unsupported image file format")
proc decodeImage*(data: string): Image {.raises: [PixieError].} =
## Loads an image from memory.
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
decodePng(data).convertToImage()
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage):
decodeJpeg(data)
elif data.len > 2 and data.readStr(0, 2) == bmpSignature:
decodeBmp(data)
elif data.len > 5 and
(data.readStr(0, 5) == xmlSignature or data.readStr(0, 4) == svgSignature):
newImage(parseSvg(data))
elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
newImage(decodeGif(data))
elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature:
decodeQoi(data).convertToImage()
elif data.len > 9 and data.readStr(0, 2) in ppmSignatures:
decodePpm(data)
else:
raise newException(PixieError, "Unsupported image file format")
proc readImageDimensions*(
filePath: string
): ImageDimensions {.inline, raises: [PixieError].} =
## Decodes an image's dimensions from a file.
try:
decodeImageDimensions(readFile(filePath))
except IOError as e:
raise newException(PixieError, e.msg, e)
proc readImage*(filePath: string): Image {.inline, raises: [PixieError].} =
## Loads an image from a file.
try:
decodeImage(readFile(filePath))
except IOError as e:
raise newException(PixieError, e.msg, e)
proc encodeImage*(image: Image, fileFormat: FileFormat): string {.raises: [PixieError].} =
## Encodes an image into memory.
case fileFormat:
of PngFormat:
image.encodePng()
of JpegFormat:
raise newException(PixieError, "Unsupported file format")
of BmpFormat:
image.encodeBmp()
of QoiFormat:
image.encodeQoi()
of GifFormat:
raise newException(PixieError, "Unsupported file format")
of PpmFormat:
image.encodePpm()
proc writeFile*(image: Image, filePath: string) {.raises: [PixieError].} =
## Writes an image to a file.
let fileFormat = case splitFile(filePath).ext.toLowerAscii():
of ".png": PngFormat
of ".bmp": BmpFormat
of ".jpg", ".jpeg": JpegFormat
of ".qoi": QoiFormat
of ".ppm": PpmFormat
else:
raise newException(PixieError, "Unsupported file extension")
try:
writeFile(filePath, image.encodeImage(fileFormat))
except IOError as e:
raise newException(PixieError, e.msg, e)
proc fill*(image: Image, paint: Paint) {.raises: [PixieError].} =
## Fills the image with the paint.
case paint.kind:
of SolidPaint:
fillUnsafe(image.data, paint.color, 0, image.data.len)
of ImagePaint, TiledImagePaint:
fillUnsafe(image.data, rgbx(0, 0, 0, 0), 0, image.data.len)
let path = newPath()
path.rect(0, 0, image.width.float32, image.height.float32)
image.fillPath(path, paint)
of LinearGradientPaint, RadialGradientPaint, AngularGradientPaint:
image.fillGradient(paint)