commit 6c2b1b777bc3903930a7e28d0f5800c2e72ab2ab
parent 55c1ef17b528b7892caba6a69f8e4d79bce2dd85
Author: Milan Pässler <milan@petabyte.dev>
Date: Sat, 13 Mar 2021 15:05:55 +0100
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(-)
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)