diff --git a/pixie.nimble b/pixie.nimble
index d3d2114..94fa28e 100644
--- a/pixie.nimble
+++ b/pixie.nimble
@@ -8,7 +8,7 @@ srcDir = "src"
 requires "nim >= 1.4.8"
 requires "vmath >= 1.1.4"
 requires "chroma >= 0.2.5"
-requires "zippy >= 0.9.11"
+requires "zippy >= 0.10.0"
 requires "flatty >= 0.3.4"
 requires "nimsimd >= 1.0.0"
 requires "bumpy >= 1.1.1"
diff --git a/src/pixie.nim b/src/pixie.nim
index 3b8c5dd..c98d950 100644
--- a/src/pixie.nim
+++ b/src/pixie.nim
@@ -40,7 +40,7 @@ proc decodeImageDimensions*(
 proc decodeImage*(data: string): Image {.raises: [PixieError].} =
   ## Loads an image from memory.
   if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
-    newImage(decodePng(data))
+    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:
@@ -51,7 +51,7 @@ proc decodeImage*(data: string): Image {.raises: [PixieError].} =
   elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
     decodeGif(data)
   elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature:
-    newImage(decodeQoi(data))
+    decodeQoi(data).convertToImage()
   elif data.len > 9 and data.readStr(0, 2) in ppmSignatures:
     decodePpm(data)
   else:
@@ -60,7 +60,7 @@ proc decodeImage*(data: string): Image {.raises: [PixieError].} =
 proc decodeMask*(data: string): Mask {.raises: [PixieError].} =
   ## Loads a mask from memory.
   if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
-    newMask(newImage(decodePng(data)))
+    newMask(decodePng(data).convertToImage())
   else:
     raise newException(PixieError, "Unsupported mask file format")
 
diff --git a/src/pixie/fileformats/png.nim b/src/pixie/fileformats/png.nim
index 79e3a14..7cc98e3 100644
--- a/src/pixie/fileformats/png.nim
+++ b/src/pixie/fileformats/png.nim
@@ -21,8 +21,8 @@ type
 template failInvalid() =
   raise newException(PixieError, "Invalid PNG buffer, unable to load")
 
-# template failCRC() =
-#   raise newException(PixieError, "CRC check failed")
+template failCRC() =
+  raise newException(PixieError, "CRC check failed")
 
 when defined(release):
   {.push checks: off.}
@@ -338,10 +338,24 @@ proc decodeImageData(
     discard # Not possible, parseHeader validates
 
 proc newImage*(png: Png): Image {.raises: [PixieError].} =
+  ## Creates a new Image from the PNG.
   result = newImage(png.width, png.height)
   copyMem(result.data[0].addr, png.data[0].addr, png.data.len * 4)
   result.data.toPremultipliedAlpha()
 
+proc convertToImage*(png: Png): Image {.raises: [].} =
+  ## Converts a PNG into an Image by moving the data. This is faster but can
+  ## only be done once.
+  type Movable = ref object
+    width, height, channels: int
+    data: seq[ColorRGBX]
+
+  result = Image()
+  result.width = png.width
+  result.height = png.height
+  result.data = move cast[Movable](png).data
+  result.data.toPremultipliedAlpha()
+
 proc decodePngDimensions*(
   data: pointer, len: int
 ): ImageDimensions {.raises: [PixieError].} =
@@ -400,8 +414,9 @@ proc decodePng*(data: pointer, len: int): Png {.raises: [PixieError].} =
   prevChunkType = "IHDR"
   inc(pos, 13)
 
-  # if crc32(data[pos - 17 ..< pos]) != read32be(data, pos):
-  #   failCRC()
+  let headerCrc = crc32(data[pos - 17].addr, 17)
+  if headerCrc != data.readUint32(pos).swap():
+    failCRC()
   inc(pos, 4) # CRC
 
   while true:
@@ -462,8 +477,9 @@ proc decodePng*(data: pointer, len: int): Png {.raises: [PixieError].} =
 
     inc(pos, chunkLen)
 
-    # if crc32(data[pos - chunkLen - 4 ..< pos]) != read32be(data, pos):
-    #   failCRC()
+    let chunkCrc = crc32(data[pos - chunkLen - 4].addr, chunkLen + 4)
+    if chunkCrc != data.readUint32(pos).swap():
+      failCRC()
     inc(pos, 4) # CRC
 
     prevChunkType = chunkType
@@ -523,7 +539,7 @@ proc encodePng*(
   result.add(0.char)
   result.add(0.char)
   result.add(0.char)
-  result.addUint32(crc32(result[result.len - 17 ..< result.len]).swap())
+  result.addUint32(crc32(result[result.len - 17].addr, 17).swap())
 
   # Add IDAT
   # Add room for 1 byte before each row for the filter type.
@@ -556,14 +572,15 @@ proc encodePng*(
   result.addUint32(compressed.len.uint32.swap())
   result.add("IDAT")
   result.add(compressed)
-  result.addUint32(
-    crc32(result[result.len - compressed.len - 4 ..< result.len]).swap()
-  )
+  result.addUint32(crc32(
+    result[result.len - compressed.len - 4].addr,
+    compressed.len + 4
+  ).swap())
 
   # Add IEND
   result.addUint32(0)
   result.add("IEND")
-  result.addUint32(crc32(result[result.len - 4 ..< result.len]).swap())
+  result.addUint32(crc32(result[result.len - 4].addr, 4).swap())
 
 proc encodePng*(png: Png): string {.raises: [PixieError].} =
   encodePng(png.width, png.height, 4, png.data[0].addr, png.data.len * 4)
diff --git a/src/pixie/fileformats/qoi.nim b/src/pixie/fileformats/qoi.nim
index 1d6fa4e..5395f6b 100644
--- a/src/pixie/fileformats/qoi.nim
+++ b/src/pixie/fileformats/qoi.nim
@@ -30,11 +30,25 @@ proc hash(p: ColorRGBA): int =
   (p.r.int * 3 + p.g.int * 5 + p.b.int * 7 + p.a.int * 11) mod indexLen
 
 proc newImage*(qoi: Qoi): Image =
-  ## Converts raw QOI data to `Image`.
+  ## Creates a new Image from the QOI.
   result = newImage(qoi.width, qoi.height)
   copyMem(result.data[0].addr, qoi.data[0].addr, qoi.data.len * 4)
   result.data.toPremultipliedAlpha()
 
+proc convertToImage*(qoi: Qoi): Image {.raises: [].} =
+  ## Converts a QOI into an Image by moving the data. This is faster but can
+  ## only be done once.
+  type Movable = ref object
+    width, height, channels: int
+    colorspace: Colorspace
+    data: seq[ColorRGBX]
+
+  result = Image()
+  result.width = qoi.width
+  result.height = qoi.height
+  result.data = move cast[Movable](qoi).data
+  result.data.toPremultipliedAlpha()
+
 proc decodeQoi*(data: string): Qoi {.raises: [PixieError].} =
   ## Decompress QOI file format data.
   if data.len <= 14 or data[0 .. 3] != qoiSignature:
diff --git a/src/pixie/masks.nim b/src/pixie/masks.nim
index 3af7cf7..aa42526 100644
--- a/src/pixie/masks.nim
+++ b/src/pixie/masks.nim
@@ -254,7 +254,8 @@ proc spread*(mask: Mask, spread: float32) {.raises: [PixieError].} =
   let spread = round(spread).int
   if spread == 0:
     return
-  elif spread > 0:
+
+  if spread > 0:
 
     # Spread in the X direction. Store with dimensions swapped for reading later.
     let spreadX = newMask(mask.height, mask.width)
diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim
index 6d0eba7..e098980 100644
--- a/src/pixie/paths.nim
+++ b/src/pixie/paths.nim
@@ -1320,9 +1320,9 @@ proc clearUnsafe(target: Image | Mask, startX, startY, toX, toY: int) =
     start = target.dataIndex(startX, startY)
     len = target.dataIndex(toX, toY) - start
   when type(target) is Image:
-    target.data.fillUnsafe(rgbx(0, 0, 0, 0), start, len)
+    fillUnsafe(target.data, rgbx(0, 0, 0, 0), start, len)
   else: # target is Mask
-    target.data.fillUnsafe(0, start, len)
+    fillUnsafe(target.data, 0, start, len)
 
 proc fillCoverage(
   image: Image,
diff --git a/tests/benchmark_png.nim b/tests/benchmark_png.nim
index 81fcf76..39f2bbc 100644
--- a/tests/benchmark_png.nim
+++ b/tests/benchmark_png.nim
@@ -13,12 +13,12 @@ block:
   timeIt "pixie decode":
     discard decodePng(data)
 
+  timeIt "pixie decode + alpha":
+    discard decodePng(data).convertToImage()
+
   timeIt "pixie encode":
     discard encodePng(decodedPng)
 
-  timeIt "pixie decode + alpha":
-    discard newImage(decodePng(data))
-
   timeIt "pixie encode + alpha":
     discard encodePng(decodedImage)
 
@@ -55,9 +55,9 @@ block:
 
 block:
   timeIt "cairo decode":
-    discard imageSurfaceCreateFromPng(filePath)
+    discard imageSurfaceCreateFromPng(filePath.cstring)
 
-  let decoded = imageSurfaceCreateFromPng(filePath)
+  let decoded = imageSurfaceCreateFromPng(filePath.cstring)
   timeIt "cairo encode":
     var write: WriteFunc =
       proc(closure: pointer, data: cstring, len: int32): Status {.cdecl.} =