From 6a74639498c02701e5c0bb605022c7aa26e1081a Mon Sep 17 00:00:00 2001
From: Ryan Oldenburg <guzba8@gmail.com>
Date: Fri, 20 Nov 2020 22:34:57 -0600
Subject: [PATCH] read, encode, decode, write

---
 src/pixie.nim                 | 34 +++++++++++++++++++++++++++++++++-
 src/pixie/fileformats/bmp.nim |  7 +++++--
 src/pixie/fileformats/png.nim | 10 ++++++++--
 src/pixie/images.nim          | 16 ----------------
 4 files changed, 46 insertions(+), 21 deletions(-)

diff --git a/src/pixie.nim b/src/pixie.nim
index 9cf7e6a..4494f15 100644
--- a/src/pixie.nim
+++ b/src/pixie.nim
@@ -1,8 +1,14 @@
 ## Public interface to you library.
 
-import pixie/images, pixie/masks, pixie/paths, pixie/common
+import pixie/images, pixie/masks, pixie/paths, pixie/common,
+  pixie/fileformats/bmp, pixie/fileformats/png, flatty/binny
+
 export images, masks, paths, PixieError
 
+type
+  FileFormat* = enum
+    ffPng, ffBmp
+
 proc toMask*(image: Image): Mask =
   ## Converts an Image to a Mask.
   result = newMask(image.width, image.height)
@@ -14,3 +20,29 @@ proc toImage*(mask: Mask): Image =
   result = newImage(mask.width, mask.height)
   for i in 0 ..< mask.data.len:
     result.data[i].a = mask.data[i]
+
+proc decodeImage(data: string | seq[uint8]): Image =
+  ## Loads an image from a memory.
+  if data.len > 8 and cast[array[8, uint8]](data.readUint64(0)) == pngSignature:
+    return decodePng(data)
+
+  if data.len > 2 and data.readStr(0, 2) == "BM":
+    return decodeBmp(data)
+
+  raise newException(PixieError, "Unsupported image file format")
+
+proc readImage*(filePath: string): Image =
+  ## Loads an image from a file.
+  decodeImage(readFile(filePath))
+
+proc encodeImage(image: Image, fileFormat: FileFormat): string =
+  ## Encodes an image into a memory.
+  case fileFormat:
+  of ffPng:
+    image.encodePng()
+  of ffBmp:
+    image.encodeBmp()
+
+proc writeFile*(image: Image, filePath: string, fileFormat: FileFormat) =
+  ## Writes an image to a file.
+  writeFile(filePath, image.encodeImage(fileFormat))
diff --git a/src/pixie/fileformats/bmp.nim b/src/pixie/fileformats/bmp.nim
index 3a2c9d3..3cafb37 100644
--- a/src/pixie/fileformats/bmp.nim
+++ b/src/pixie/fileformats/bmp.nim
@@ -1,4 +1,4 @@
-import ../images, flatty/binny, chroma, pixie/common
+import flatty/binny, chroma, pixie/common, pixie/images
 
 # See: https://en.wikipedia.org/wiki/BMP_file_format
 
@@ -6,7 +6,7 @@ proc decodeBmp*(data: string): Image =
   ## Decodes bitmap data into an Image.
 
   # BMP Header
-  if data[0..1] != "BM":
+  if data[0 .. 1] != "BM":
     raise newException(PixieError, "Invalid BMP data")
 
   let
@@ -46,6 +46,9 @@ proc decodeBmp*(data: string): Image =
         offset += 3
       result[x, result.height - y - 1] = rgba
 
+proc decodeBmp*(data: seq[uint8]): Image {.inline.} =
+  decodeBmp(cast[string](data))
+
 proc encodeBmp*(image: Image): string =
   ## Encodes an image into the BMP file format.
 
diff --git a/src/pixie/fileformats/png.nim b/src/pixie/fileformats/png.nim
index 63bec8e..d0c3827 100644
--- a/src/pixie/fileformats/png.nim
+++ b/src/pixie/fileformats/png.nim
@@ -1,7 +1,10 @@
-import chroma, pixie/images, pixie/common, math, zippy, zippy/crc, flatty/binny
+import chroma, pixie/common, math, zippy, zippy/crc, flatty/binny, pixie/images
 
 # See http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html
 
+const
+  pngSignature* = [137.uint8, 80, 78, 71, 13, 10, 26, 10]
+
 type
   ChunkCounts = object
     PLTE, IDAT: uint8
@@ -286,7 +289,7 @@ proc decodePng*(data: seq[uint8]): Image =
 
   # PNG file signature
   let signature = cast[array[8, uint8]](data.readUint64(0))
-  if signature != [137.uint8, 80, 78, 71, 13, 10, 26, 10]:
+  if signature != pngSignature:
     failInvalid()
 
   var
@@ -371,6 +374,9 @@ proc decodePng*(data: seq[uint8]): Image =
   result.height = header.height
   result.data = parseImageData(header, palette, imageData)
 
+proc decodePng*(data: string): Image {.inline.} =
+  decodePng(cast[seq[uint8]](data))
+
 proc encodePng*(
   width, height, channels: int, data: pointer, len: int
 ): seq[uint8] =
diff --git a/src/pixie/images.nim b/src/pixie/images.nim
index 5492cc7..d9ec0d1 100644
--- a/src/pixie/images.nim
+++ b/src/pixie/images.nim
@@ -22,22 +22,6 @@ proc `$`*(image: Image): string =
   ## Display the image size and channels.
   "<Image " & $image.width & "x" & $image.height & ">"
 
-proc decodeImage(data: seq[uint8]): Image =
-  ## Loads an image from a memory.
-  discard
-
-proc readImage*(filePath: string): Image =
-  ## Loads an image from a file.
-  discard
-
-proc encodeImage(image: Image): seq[uint8] =
-  ## Encodes an image into a memory.
-  discard
-
-proc writeFile*(image: Image, filePath: string): Image =
-  ## Writes an image to a file.
-  discard
-
 proc inside*(image: Image, x, y: int): bool {.inline.} =
   ## Returns true if (x, y) is inside the image.
   x >= 0 and x < image.width and y >= 0 and y < image.height