about summary refs log tree commit diff stats
path: root/src/img/png.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/img/png.nim')
-rw-r--r--src/img/png.nim67
1 files changed, 49 insertions, 18 deletions
diff --git a/src/img/png.nim b/src/img/png.nim
index 7f4d8493..bd7e6206 100644
--- a/src/img/png.nim
+++ b/src/img/png.nim
@@ -132,8 +132,7 @@ func spp(reader: PNGReader): int =
   of pcTrueColorWithAlpha: return 4
 
 func scanlen(reader: PNGReader): int {.inline.} =
-  let w = reader.width + 1
-  return (w * reader.spp * int(reader.bitDepth) + 7) div 8
+  return 1 + (reader.width * reader.spp * int(reader.bitDepth) + 7) div 8
 
 proc handleError(reader: var PNGReader; msg: string) =
   #TODO proper error handling?
@@ -258,28 +257,55 @@ proc readtRNS(reader: var PNGReader) =
     for i in 0 ..< reader.palette.len:
       reader.palette[i].a = reader.readU8()
 
+func paethPredictor(a, b, c: uint16): uint16 =
+  let pa0 = int(b) - int(c)
+  let pb0 = int(a) - int(c)
+  let pa = abs(pa0)
+  let pb = abs(pb0)
+  let pc = abs(pa0 + pb0)
+  return if pa <= pb and pa <= pc: a elif pb <= pc: b else: c
+
 proc unfilter(reader: var PNGReader; irow: openArray[uint8]; bpp: int) =
   # none, sub, up -> replace uprow directly
   # average, paeth -> copy to temp array, then replace uprow
-  let fil = irow[0]
-  let w = reader.width
-  case fil
+  case irow[0]
   of 0u8: # none
-    copyMem(addr reader.uprow[0], unsafeAddr irow[1], w)
+    copyMem(addr reader.uprow[0], unsafeAddr irow[1], irow.len)
   of 1u8: # sub
     for i in 1 ..< irow.len:
       let j = i - 1 # skip filter byte
-      reader.uprow[j] = irow[i]
-      if j - bpp >= 0:
-        reader.uprow[j] += irow[j - bpp]
+      let aidx = j - bpp
+      let x = uint16(irow[i])
+      let a = if aidx >= 0: uint16(reader.uprow[aidx]) else: 0u16
+      reader.uprow[j] = uint8((x + a) and 0xFF)
   of 2u8: # up
     for i in 1 ..< irow.len:
       let j = i - 1 # skip filter byte
-      reader.uprow[j] += irow[i]
+      let x = uint16(irow[i])
+      let b = uint16(reader.uprow[j])
+      reader.uprow[j] = uint8((x + b) and 0xFF)
   of 3u8: # average
-    reader.err "average not implemented yet"
+    for i in 1 ..< irow.len:
+      let j = i - 1 # skip filter byte
+      let aidx = j - bpp
+      let x = uint16(irow[i])
+      let a = if aidx >= 0: uint16(reader.uprow[aidx]) else: 0u16
+      let b = uint16(reader.uprow[j])
+      reader.uprow[j] = uint8((x + (a + b) div 2) and 0xFF)
   of 4u8: # paeth
-    reader.err "paeth not implemented yet"
+    var cmap: array[8, uint16] # max bpp is 16 bit with true color = 4 * 2
+    var k = 0
+    for i in 1 ..< irow.len:
+      let j = i - 1 # skip filter byte
+      let aidx = j - bpp
+      let x = uint16(irow[i])
+      let a = if aidx >= 0: uint16(reader.uprow[aidx]) else: 0u16
+      let b = uint16(reader.uprow[j])
+      let kk = k mod bpp
+      let c = cmap[kk]
+      cmap[kk] = b
+      reader.uprow[j] = uint8((x + paethPredictor(a, b, c)) and 0xFF)
+      inc k
   else:
     reader.err "got invalid filter"
 
@@ -340,11 +366,16 @@ proc writepxs(reader: var PNGReader; crow: var openArray[RGBAColor]) =
       crow[x] = rgba(n, n, n, a)
   of pcTrueColorWithAlpha:
     let step = int(reader.bitDepth) div 8
+    var i = 0
     for x in 0 ..< crow.len:
-      let r = reader.uprow[x * step]
-      let g = reader.uprow[(x + 1) * step]
-      let b = reader.uprow[(x + 2) * step]
-      let a = reader.uprow[(x + 3) * step]
+      let r = reader.uprow[i]
+      i += step
+      let g = reader.uprow[i]
+      i += step
+      let b = reader.uprow[i]
+      i += step
+      let a = reader.uprow[i]
+      i += step
       crow[x] = rgba(r, g, b, a)
 
 proc readPLTE(reader: var PNGReader) =
@@ -404,10 +435,10 @@ proc readIDAT(reader: var PNGReader) =
   for y in reader.atline ..< maxline:
     let yi = y * sl
     assert yi + sl - 1 < reader.idatAt
-    reader.unfilter(toOpenArray(reader.idatBuf, yi, yi + sl - 1), bpp)
+    reader.unfilter(reader.idatBuf.toOpenArray(yi, yi + sl - 1), bpp)
     if unlikely(reader.bmp == nil): return
     let yj = y * reader.width
-    reader.writepxs(toOpenArray(bmp.px, yj, yj + reader.width - 1))
+    reader.writepxs(bmp.px.toOpenArray(yj, yj + reader.width - 1))
 
 proc readIEND(reader: var PNGReader) =
   if reader.i < reader.limit: