ctucx.git: nimjpg

parse jpg file header

commit 5637b7d5aaccda626b7b274bfb72a69324bb4720
parent 0d87f77c176d9b73346c865972704a1addfe74ed
Author: Milan Pässler <milan@petabyte.dev>
Date: Sat, 13 Mar 2021 11:35:46 +0100

jpg: pass exif data to libexif
2 files changed, 97 insertions(+), 9 deletions(-)
M
exif.nim
|
11
++---------
A
jpg.nim
|
95
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/exif.nim b/exif.nim
@@ -2,7 +2,6 @@ import asyncdispatch
 import libexif
 import tables
 
-var ed {.threadvar.}: ptr ExifData
 var buf {.threadvar.}: cstring
 
 # MUST be called once per thread

@@ -12,14 +11,8 @@ proc init_exif*() =
 proc deinit_exif*() =
   dealloc(buf)
 
-proc collect_exif*(path: string): Future[Table[string, string]] {.gcsafe,async.} =
-  result = initTable[string, string]()
-
-  ed = exif_data_new_from_file(path)
-
-  if ed == nil:
-    # ERROR
-    quit(0)
+proc collect_exif*(ed: ptr ExifData): Table[string, string] {.gcsafe.} =
+  #result = initTable[string, string]()
 
   proc process_entries(entry: ptr ExifEntry , callback_data: pointer) {.cdecl.} =
     let name = exif_tag_get_name(entry.tag)
diff --git a/jpg.nim b/jpg.nim
@@ -0,0 +1,95 @@
+import os
+import strutils
+import asyncfile
+import asyncdispatch
+import libexif
+import exif
+import tables
+import parseutils
+
+var buf {.threadvar.}: pointer
+
+# MUST be called once per thread
+proc init_jpg*() =
+  init_exif()
+  buf = alloc(65536)
+
+proc deinit_jpg*() =
+  deinit_exif()
+  dealloc(buf)
+
+const
+  MARKER_START = parseHexStr("FF")
+
+  # 0xCn SOFn
+
+  SOS   = parseHexStr("DA") # Start Of Scan (begins compressed data)
+  SOI   = parseHexStr("D8") # Start Of Image (beginning of datastream)
+  EOI   = parseHexStr("D9") # End Of Image (end of datastream)
+
+  EXIF  = parseHexStr("E1")
+  JFIF  = parseHexStr("E0")
+
+type JpgInfo* = object
+  exifInfo*: Table[string, string]
+  resolution*: tuple[x: int, y: int]
+
+proc getSectionSize(file: AsyncFile): Future[uint16] {.async.} =
+  # FIXME consider endianness
+  var size: uint16
+  let val = toHex(file.read(2).await)
+  discard parseHex(val, size)
+  return size - uint16(2)
+
+proc skipSection(file: AsyncFile): Future[void] {.async.} =
+  let size = int64(file.getSectionSize().await)
+  echo("skipping ", size)
+  file.setFilePos(file.getFilePos() + size)
+
+proc expect(file: AsyncFile, expected: string): Future[void] {.async.} =
+  let byte = file.read(1).await
+  if byte != expected:
+    echo("expected ", toHex(expected),", got ", toHex(byte))
+    quit(1)
+
+proc collect_jpg*(file: AsyncFile): Future[JpgInfo] {.gcsafe,async.} =
+  var done = false
+  var byte: string
+
+  file.expect(MARKER_START).await
+  file.expect(SOI).await
+
+  while not done:
+    file.expect(MARKER_START).await
+
+    let marker = file.read(1).await
+
+    case marker:
+    of "":
+      echo("unexpected end of file")
+      quit(1)
+    of SOS:
+      # Beginning of compressed data
+      done = true
+    of EOI:
+      # No compressed data? Okay...
+      done = true
+    of EXIF:
+      echo "found EXIF"
+      let size = int(file.getSectionSize().await)
+      discard file.readBuffer(buf, size).await
+      let ed = exif_data_new_from_data(cast[ptr[cuchar]](buf), cuint(size))
+      echo ed.collect_exif()
+    of JFIF:
+      echo "found JFIF"
+      file.skipSection().await
+    else:
+      if toHex(marker).startsWith("C"):
+        echo "found SOF"
+        file.skipSection().await
+        continue
+
+      echo("unknown section: ", toHex(marker))
+      file.skipSection().await
+
+  return JpgInfo(exifInfo: initTable[string, string](), resolution: (0, 0))