module arsd.jpg; import std.typecons; import std.stdio; import std.conv; struct JpegSection { ubyte identifier; ubyte[] data; } // gives as a range of file sections struct LazyJpegFile { File f; JpegSection _front; bool _frontIsValid; this(File f) { this.f = f; ubyte[2] headerBuffer; auto data = f.rawRead(headerBuffer); if(data != [0xff, 0xd8]) throw new Exception("no jpeg header"); popFront(); // prime } void popFront() { ubyte[4] startingBuffer; auto read = f.rawRead(startingBuffer); if(read.length != 4) { _frontIsValid = false; return; // end of file } if(startingBuffer[0] != 0xff) throw new Exception("not lined up in file"); _front.identifier = startingBuffer[1]; ushort length = cast(ushort) (startingBuffer[2]) * 256 + startingBuffer[3]; if(length < 2) throw new Exception("wtf"); length -= 2; // the length in the file includes the block header, but we just want the data here _front.data = new ubyte[](length); read = f.rawRead(_front.data); if(read.length != length) throw new Exception("didn't read the file right, got " ~ to!string(read.length) ~ " instead of " ~ to!string(length)); _frontIsValid = true; } JpegSection front() { return _front; } bool empty() { return !_frontIsValid; } } // returns width, height Tuple!(int, int) getSizeFromFile(string filename) { import std.stdio; auto file = File(filename, "rb"); auto jpeg = LazyJpegFile(file); auto firstSection = jpeg.front(); jpeg.popFront(); // commented because exif and jfif are both readable by this so no need to be picky //if(firstSection.identifier != 0xe0) //throw new Exception("bad header"); for(; !jpeg.empty(); jpeg.popFront()) { if(jpeg.front.identifier != 0xc0) continue; auto data = jpeg.front.data[1..$]; // skip the precision byte ushort height = data[0] * 256 + data[1]; ushort width = data[2] * 256 + data[3]; return tuple(cast(int) width, cast(int) height); } throw new Exception("idk about the length"); }