Browse Source

added partial tiff support

Nicolas Cannasse 5 years ago
parent
commit
95dd3d1152
3 changed files with 198 additions and 2 deletions
  1. 13 2
      hxd/Pixels.hx
  2. 75 0
      hxd/fmt/tiff/Data.hx
  3. 110 0
      hxd/fmt/tiff/Reader.hx

+ 13 - 2
hxd/Pixels.hx

@@ -70,11 +70,11 @@ abstract PixelsFloat(Pixels) to Pixels {
 		this.willChange();
 		var bytes : hxd.impl.UncheckedBytes = this.bytes;
 		switch( [this.format, target] ) {
-	
+
 		case [RGBA32F, R32F]:
 			var nbytes = haxe.io.Bytes.alloc(this.height * this.width * 4);
 			var out : hxd.impl.UncheckedBytes = nbytes;
-			for( i in 0 ... this.width * this.height ) 
+			for( i in 0 ... this.width * this.height )
 				nbytes.setFloat(i << 2, this.bytes.getFloat(i << 4));
 			this.bytes = nbytes;
 
@@ -379,6 +379,17 @@ class Pixels {
 				out[i] = bytes[i << 2];
 			this.bytes = nbytes;
 
+		case [R32F, RGBA|BGRA]:
+			var fbytes = this.bytes;
+			var p = 0;
+			for( i in 0...width*height ) {
+				var v = Std.int(fbytes.getFloat(p)*255);
+				if( v < 0 ) v = 0 else if( v > 255 ) v = 255;
+				bytes[p++] = v;
+				bytes[p++] = v;
+				bytes[p++] = v;
+				bytes[p++] = 0xFF;
+			}
 		case [S3TC(a),S3TC(b)] if( a == b ):
 			// nothing
 

+ 75 - 0
hxd/fmt/tiff/Data.hx

@@ -0,0 +1,75 @@
+package hxd.fmt.tiff;
+
+enum abstract TifTag(Int) {
+	public var ImageWidth = 256;
+	public var ImageHeight = 257;
+	public var BitsPerSample = 258;
+	public var Compression = 259;
+	public var PhotometricInterpretation = 262;
+	public var StripOffsets = 273;
+	public var Orientation = 274;
+	public var SamplesPerPixel = 277;
+	public var RowsPerStrip = 278;
+	public var StripByteCounts = 279;
+	public var PlanarConfiguration = 284;
+	public var SampleFormat = 339;
+	public var ModelPixelScale = 33550;
+	public var ModelTiepoint = 33922;
+	public var GeoKeyDirectory = 34735;
+	public var GeoDoubleParams = 34736;
+	public var GeoAsciiParams = 34737;
+	public inline function new(v) {
+		this = v;
+	}
+}
+
+enum abstract TifType(Int) {
+	public var Byte = 1;
+	public var Ascii = 2;
+	public var Short = 3;
+	public var Long = 4;
+	public var Rational = 5;
+	public var SByte = 6;
+	public var UndefByte = 7;
+	public var SShort = 8;
+	public var SLong = 9;
+	public var SRational = 10;
+	public var Float = 11;
+	public var Double = 12;
+	public inline function new(v) {
+		this = v;
+	}
+}
+
+enum TifValue {
+	VInt( v : Int );
+	VFloat( v : Float );
+	VString( s : String );
+	VArray( a : Array<TifValue> );
+}
+
+typedef TifFile = {
+	var tags : Array<{ tag : TifTag, value : TifValue }>;
+	var data : Array<haxe.io.Bytes>;
+}
+
+class Utils {
+
+	public static function get( f : TifFile, tag : TifTag ) {
+		for( t in f.tags )
+			if( t.tag == tag )
+				return t.value;
+		return null;
+	}
+
+	public static function getInt( f : TifFile, tag : TifTag ) : Null<Int> {
+		var v = get(f, tag);
+		if( v == null ) return null;
+		return switch( v ) {
+		case VInt(v): v;
+		case VFloat(f): Std.int(f);
+		default: throw "assert";
+		}
+	}
+
+}

+ 110 - 0
hxd/fmt/tiff/Reader.hx

@@ -0,0 +1,110 @@
+package hxd.fmt.tiff;
+import hxd.fmt.tiff.Data;
+
+class Reader {
+
+	var f : sys.io.FileInput;
+
+	public function new(f:sys.io.FileInput) {
+		this.f = f;
+	}
+
+	static function getTypeSize(t : TifType) {
+		return switch( t ) {
+		case Byte, Ascii, SByte, UndefByte: 1;
+		case Short, SShort: 2;
+		case Long, SLong, Float: 4;
+		case Rational, SRational, Double: 8;
+		default: 0;
+		}
+	}
+
+	public function read() {
+		var order = f.readString(2);
+		f.bigEndian = order == "MM";
+		var id = f.readUInt16();
+		if( (order != "II" && order != "MM") || id != 42 ) throw "Invalid TIF file";
+		var offset = f.readInt32();
+
+		var tags = [];
+		while( offset != 0 ) {
+			f.seek(offset, SeekBegin);
+			for( i in 0...f.readUInt16() ) {
+				var tag = new TifTag(f.readUInt16());
+				var type = new TifType(f.readUInt16());
+				var count = f.readInt32();
+				var next = f.tell();
+				if( count * getTypeSize(type) > 4 ) {
+					var offset = f.readInt32();
+					f.seek(offset, SeekBegin);
+				}
+				var value = if( count == 1 || type == Ascii ) readValue(type) else VArray([for( i in 0...count ) readValue(type)]);
+				f.seek(next + 4, SeekBegin);
+				tags.push({ tag : tag, value : value });
+			}
+			offset = f.readInt32();
+		}
+
+		var tif : TifFile = {
+			tags : tags,
+			data : [],
+		};
+
+		var offsets = Utils.get(tif, StripOffsets);
+		var bytes = Utils.get(tif, StripByteCounts);
+		if( offsets == null || bytes == null ) throw "Missing image data";
+		switch( [offsets,bytes] ) {
+		case [VArray(offsets), VArray(bytes)] if( offsets.length == bytes.length ):
+			for( i in 0...offsets.length )
+				switch( [offsets[i],bytes[i]] ) {
+				case [VInt(offset), VInt(len)]:
+					f.seek(offset, SeekBegin);
+					tif.data.push(f.read(len));
+				default: throw "assert";
+				}
+		default: throw "Invalid image data";
+		}
+
+		return tif;
+	}
+
+	function readValue( t : TifType ) : TifValue {
+		return switch( t ) {
+		case Byte: VInt(f.readByte());
+		case Ascii:
+			var b = new StringBuf();
+			while( true ) {
+				var c = f.readByte();
+				if( c == 0 ) break;
+				b.addChar(c);
+			}
+			VString(b.toString());
+		case Short: VInt(f.readUInt16());
+		case Long: VInt(f.readInt32());
+		case Float: VFloat(f.readFloat());
+		case Double: VFloat(f.readDouble());
+		default: throw "Unsupported type "+t;
+		}
+	}
+
+	public static function decode( f : TifFile ) : hxd.Pixels {
+		var bpp = Utils.getInt(f, BitsPerSample);
+		var channels = Utils.getInt(f, SamplesPerPixel);
+
+		if( Utils.getInt(f, Compression) != 1 ) throw "Unsupported compression";
+
+		var width = Utils.getInt(f, ImageWidth);
+		var height = Utils.getInt(f, ImageHeight);
+
+		switch( [bpp, channels] ) {
+		case [32, 1]:
+			var b = new haxe.io.BytesBuffer();
+			for( i in 0...width ) {
+				b.add(f.data[i]);
+			}
+			return new hxd.Pixels(width, height, b.getBytes(), R32F);
+		default: throw "Unsupported tif format";
+		}
+	}
+
+}