about summary refs log tree commit diff stats
path: root/adapter/img/stbi.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-06-21 18:37:33 +0200
committerbptato <nincsnevem662@gmail.com>2024-06-21 18:37:33 +0200
commit51ae4cc4ec4402f78bbda6de5207c60b6a1aaf86 (patch)
tree687e668736dd5c8638df45cc8342773069bcebf0 /adapter/img/stbi.nim
parentb8a21c2407a9898a538852098eca5658de5afd37 (diff)
downloadchawan-51ae4cc4ec4402f78bbda6de5207c60b6a1aaf86.tar.gz
stbi: add encoders
Diffstat (limited to 'adapter/img/stbi.nim')
-rw-r--r--adapter/img/stbi.nim97
1 files changed, 80 insertions, 17 deletions
diff --git a/adapter/img/stbi.nim b/adapter/img/stbi.nim
index 35ef8f64..c0f44c86 100644
--- a/adapter/img/stbi.nim
+++ b/adapter/img/stbi.nim
@@ -1,8 +1,13 @@
+import std/options
 import std/os
+import std/strutils
 
 import utils/sandbox
 import utils/twtstr
 
+{.passc: "-fno-strict-aliasing".}
+{.passl: "-fno-strict-aliasing".}
+
 {.compile: "stb_image.c".}
 
 type stbi_io_callbacks = object
@@ -16,6 +21,8 @@ proc stbi_load_from_callbacks(clbk: ptr stbi_io_callbacks; user: pointer;
 
 proc stbi_failure_reason(): cstring {.importc.}
 
+proc stbi_image_free(retval_from_stbi_load: pointer) {.importc.}
+
 proc myRead(user: pointer; data: ptr uint8; size: cint): cint {.cdecl.} =
   return cint(stdin.readBuffer(data, size))
 
@@ -32,26 +39,82 @@ proc mySkip(user: pointer; n: cint) {.cdecl.} =
 proc myEof(user: pointer): cint {.cdecl.} =
   return cint(stdin.endOfFile())
 
+type stbi_write_func = proc(context, data: pointer; size: cint) {.cdecl.}
+
+proc stbi_write_png_to_func(fun: stbi_write_func; context: pointer;
+  w, h, comp: cint; data: pointer; stride_in_bytes: cint) {.importc.}
+proc stbi_write_bmp_to_func(fun: stbi_write_func; context: pointer;
+  w, h, comp: cint; data: pointer) {.importc.}
+proc stbi_write_jpg_to_func(fun: stbi_write_func; context: pointer;
+  w, h, comp: cint; data: pointer; quality: cint) {.importc.}
+
+proc myWriteFunc(context, data: pointer; size: cint) {.cdecl.} =
+  discard stdout.writeBuffer(data, size)
+
 proc main() =
   enterNetworkSandbox()
   let scheme = getEnv("MAPPED_URI_SCHEME")
   let f = scheme.after('+')
-  if f notin ["jpeg", "gif", "bmp", "png"]:
-    stdout.write("Cha-Control: ConnectionError 1 wrong format " & f)
-  var x: cint
-  var y: cint
-  var channels_in_file: cint
-  var clbk = stbi_io_callbacks(
-    read: myRead,
-    skip: mySkip,
-    eof: myEof
-  )
-  let p = stbi_load_from_callbacks(addr clbk, nil, x, y, channels_in_file, 4)
-  if p == nil:
-    stdout.write("Cha-Control: ConnectionError 1 stbi error " &
-      $stbi_failure_reason())
-    return
-  stdout.write("Cha-Image-Dimensions: " & $x & "x" & $y & "\n\n")
-  discard stdout.writeBuffer(p, x * y * 4)
+  case getEnv("MAPPED_URI_PATH")
+  of "decode":
+    if f notin ["jpeg", "gif", "bmp", "png"]:
+      stdout.write("Cha-Control: ConnectionError 1 unknown format " & f)
+    var x: cint
+    var y: cint
+    var channels_in_file: cint
+    var clbk = stbi_io_callbacks(
+      read: myRead,
+      skip: mySkip,
+      eof: myEof
+    )
+    let p = stbi_load_from_callbacks(addr clbk, nil, x, y, channels_in_file, 4)
+    if p == nil:
+      stdout.write("Cha-Control: ConnectionError 1 stbi error " &
+        $stbi_failure_reason())
+      return
+    stdout.write("Cha-Image-Dimensions: " & $x & "x" & $y & "\n\n")
+    discard stdout.writeBuffer(p, x * y * 4)
+    stbi_image_free(p)
+  of "encode":
+    let headers = getEnv("REQUEST_HEADERS")
+    var quality = cint(50)
+    var width = cint(0)
+    var height = cint(0)
+    for hdr in headers.split('\n'):
+      case hdr.until(':')
+      of "Cha-Image-Dimensions":
+        let s = hdr.after(':').strip().split('x')
+        #TODO error handling
+        let w = parseUInt32(s[0], allowSign = false)
+        let h = parseUInt32(s[1], allowSign = false)
+        if w.isNone or w.isNone:
+          stdout.write("Cha-Control: ConnectionError 1 wrong dimensions")
+          return
+        width = cint(w.get)
+        height = cint(h.get)
+      of "Cha-Image-Quality":
+        let s = hdr.after(':').strip()
+        let q = parseUInt32(s, allowSign = false).get(101)
+        if q < 1 or 100 < q:
+          stdout.write("Cha-Control: ConnectionError 1 wrong quality")
+          return
+        quality = cint(q)
+    let s = stdin.readAll()
+    if s.len != width * height * 4:
+      stdout.write("Cha-Control: ConnectionError 1 wrong size")
+      return
+    stdout.write("Cha-Image-Dimensions: " & $width & 'x' & $height & "\n\n")
+    let p = unsafeAddr s[0]
+    case f
+    of "png":
+      stbi_write_png_to_func(myWriteFunc, nil, cint(width), cint(height), 4, p,
+        0)
+    of "bmp":
+      stbi_write_bmp_to_func(myWriteFunc, nil, cint(width), cint(height), 4, p)
+    of "jpeg":
+      stbi_write_jpg_to_func(myWriteFunc, nil, cint(width), cint(height), 4, p,
+        quality)
+    else:
+      stdout.write("Cha-Control: ConnectionError 1 unknown format " & f)
 
 main()