ctucx.git: nimjpg

parse jpg file header

commit 6c2b1b777bc3903930a7e28d0f5800c2e72ab2ab
parent 55c1ef17b528b7892caba6a69f8e4d79bce2dd85
Author: Milan Pässler <milan@petabyte.dev>
Date: Sat, 13 Mar 2021 15:05:55 +0100

multisync
6 files changed, 173 insertions(+), 115 deletions(-)
M
example.nim
|
2
+-
A
example_sync.nim
|
23
+++++++++++++++++++++++
D
jpg.nim
|
114
-------------------------------------------------------------------------------
A
jpgnim.nim
|
122
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
jpgnim.nimble
|
13
+++++++++++++
M
utils.nim
|
14
++++++++++++++
diff --git a/example.nim b/example.nim
@@ -1,5 +1,5 @@
 import os
-import jpg
+import jpgnim
 import asyncdispatch
 import asyncfile
 import json
diff --git a/example_sync.nim b/example_sync.nim
@@ -0,0 +1,23 @@
+import os
+import jpgnim
+import asyncdispatch
+import asyncfile
+import json
+
+var result: cint
+
+if paramCount()  == 0:
+  echo "Gimme a file pls"
+  quit(QuitFailure)
+
+let path = paramStr(1)
+
+init_jpg()
+
+if fileExists(path):
+  let file = open(path)
+  echo pretty(%* collect_jpg(file))
+else:
+  echo "Gimme an existing file pls"
+
+deinit_jpg()
diff --git a/jpg.nim b/jpg.nim
@@ -1,114 +0,0 @@
-import os
-import strutils
-import asyncfile
-import asyncdispatch
-import exifnim/libexif
-import exifnim/helpers
-import tables
-import parseutils
-import options
-import utils
-
-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 SofData* = object
-  components*: uint8
-  precision*: uint8
-  height*: uint16
-  width*: uint16
-
-type JpgInfo* = object
-  exifData*: Option[Table[string, string]]
-  sofData*: Option[SofData]
-
-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)
-  debug("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:
-    error("expected ", toHex(expected),", got ", toHex(byte))
-
-proc process_sof*(file: AsyncFile): Future[SofData] {.gcsafe,async.} =
-  discard parseHex(toHex(file.read(1).await), result.precision)
-  discard parseHex(toHex(file.read(2).await), result.height)
-  discard parseHex(toHex(file.read(2).await), result.width)
-  discard parseHex(toHex(file.read(1).await), result.components)
-
-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 "":
-      error("unexpected end of file")
-    of SOS:
-      # Beginning of compressed data
-      done = true
-    of EOI:
-      # No compressed data? Okay...
-      done = true
-    of EXIF:
-      debug "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))
-      let ed_table = ed.collect_exif_data()
-      if result.exifData.isNone:
-        result.exifData = some(ed_table)
-      debug ed_table
-    of JFIF:
-      debug "found JFIF"
-      file.skipSection().await
-    else:
-      if toHex(marker).startsWith("C"):
-        debug "found SOF"
-        let section_start = file.getFilePos() + 2
-        let section_end = section_start + int64(file.getSectionSize().await)
-        let sd = file.process_sof().await
-        if result.sofData.isNone:
-          result.sofData = some(sd)
-        debug sd
-        file.setFilePos(section_end)
-        continue
-
-      debug("unknown section: ", toHex(marker))
-      file.skipSection().await
diff --git a/jpgnim.nim b/jpgnim.nim
@@ -0,0 +1,122 @@
+import os
+import streams
+import strutils
+import asyncfile
+import asyncdispatch
+import exifnim/libexif
+import exifnim/helpers
+import tables
+import parseutils
+import options
+
+import utils
+
+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 SofData* = object
+  components*: uint8
+  precision*: uint8
+  height*: uint16
+  width*: uint16
+
+type JpgInfo* = object
+  exifData*: Option[Table[string, string]]
+  sofData*: Option[SofData]
+
+proc getSectionSize(file: Stream | AsyncFile): Future[uint16] {.multisync.} =
+  # FIXME consider endianness
+  var size: uint16
+  let val = toHex(file.read(2).await)
+  discard parseHex(val, size)
+  echo val
+  return size - uint16(2)
+
+proc skipSection(file: Stream | AsyncFile): Future[int] {.multisync.} =
+  let size = int64(file.getSectionSize().await)
+  debug("skipping ", size)
+  file.setFilePos(file.getFilePos() + size)
+
+proc expect(file: Stream | AsyncFile, expected: string): Future[int] {.multisync.} =
+  let byte = file.read(1).await
+  if byte != expected:
+    error("expected ", toHex(expected),", got ", toHex(byte))
+
+proc process_sof*(file: Stream | AsyncFile): Future[SofData] {.multisync.} =
+  discard parseHex(toHex(file.read(1).await), result.precision)
+  discard parseHex(toHex(file.read(2).await), result.height)
+  discard parseHex(toHex(file.read(2).await), result.width)
+  discard parseHex(toHex(file.read(1).await), result.components)
+
+proc collect_jpg*(file: Stream | AsyncFile): Future[JpgInfo] {.multisync,gcsafe.} =
+  var done = false
+  var byte: string
+
+  discard file.expect(MARKER_START).await
+  discard file.expect(SOI).await
+
+  while not done:
+    discard file.expect(MARKER_START).await
+
+    let marker = file.read(1).await
+
+    case marker:
+    of "":
+      error("unexpected end of file")
+    of SOS:
+      # Beginning of compressed data
+      done = true
+    of EOI:
+      # No compressed data? Okay...
+      done = true
+    of EXIF:
+      debug "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))
+      let ed_table = ed.collect_exif_data()
+      if result.exifData.isNone:
+        result.exifData = some(ed_table)
+      debug ed_table
+    of JFIF:
+      debug "found JFIF"
+      discard file.skipSection().await
+    else:
+      if toHex(marker).startsWith("C"):
+        debug "found SOF"
+        let section_start = file.getFilePos() + 2
+        let section_end = section_start + int64(file.getSectionSize().await)
+        let sd = file.process_sof().await
+        if result.sofData.isNone:
+          result.sofData = some(sd)
+        debug sd
+        file.setFilePos(section_end)
+        continue
+
+      debug("unknown section: ", toHex(marker))
+      discard file.skipSection().await
+
+proc collect_jpg*(file: File): JpgInfo =
+  let stream = newFileStream(file)
+  result = collect_jpg(stream)
+  stream.close()
diff --git a/jpgnim.nimble b/jpgnim.nimble
@@ -0,0 +1,13 @@
+# Package
+
+version       = "0.1.0"
+author        = "ctucx, Milan"
+description   = "Read jpg headers"
+license       = "GPL-3.0"
+srcDir        = "./"
+bin           = @["example", "example_sync"]
+installFiles  = @["jpgnim.nim"]
+
+# Dependencies
+requires "nim >= 1.4"
+requires "https://cgit.ctu.cx/exifnim/#main"
diff --git a/utils.nim b/utils.nim
@@ -1,3 +1,5 @@
+import streams
+
 template debug*(x: varargs[untyped]) =
   if not defined(release):
     echo(x)

@@ -5,3 +7,15 @@ template debug*(x: varargs[untyped]) =
 template error*(x: varargs[untyped]) =
   echo(x)
   quit(1)
+
+proc read*(stream: Stream, length: int): string =
+  return stream.readStr(length)
+
+proc getFilePos*(stream: Stream): int64 =
+  return int64(stream.getPosition())
+
+proc setFilePos*(stream: Stream, position: int64) =
+  stream.setPosition(int(position))
+
+proc readBuffer*(stream: Stream, buffer: pointer, length: int): int =
+  result = stream.readData(buffer, length)