summaryrefslogtreecommitdiffstats
path: root/nimjpg.nim
diff options
context:
space:
mode:
authorLeah (ctucx) <leah@ctu.cx>2021-03-13 16:01:44 +0100
committerLeah (ctucx) <leah@ctu.cx>2021-03-13 16:01:44 +0100
commit7cfcb30c2f3ffc4e71df1445c99f2a9444d2cb86 (patch)
tree93f9ec03e06b5983f04a68a6c96baba8bad5fcdb /nimjpg.nim
parenteb52a606f9aa6d5317e2b9adc37dc92d79ca11c6 (diff)
downloadnimjpg-7cfcb30c2f3ffc4e71df1445c99f2a9444d2cb86.tar.gz
nimjpg-7cfcb30c2f3ffc4e71df1445c99f2a9444d2cb86.tar.bz2
nimjpg-7cfcb30c2f3ffc4e71df1445c99f2a9444d2cb86.zip
rename to nimjpg
Diffstat (limited to 'nimjpg.nim')
-rw-r--r--nimjpg.nim122
1 files changed, 122 insertions, 0 deletions
diff --git a/nimjpg.nim b/nimjpg.nim
new file mode 100644
index 0000000..58ff67b
--- /dev/null
+++ b/nimjpg.nim
@@ -0,0 +1,122 @@
+import os
+import streams
+import strutils
+import asyncfile
+import asyncdispatch
+import nimexif/libexif
+import nimexif/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()