Type TSImage = Class
	Function Create(stream); overload;
	Begin
		content_ := stream;
		imageDef := array(('Png', 0, (0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a)),
			('Jfif',6,'JFIF'),
			('Exif',6,'Exif'),
			('Gif',0,'GIF87a'),
			('Gif',0,'GIF89a'),
			('Tiff',0,('M','M',0x00,'*')),
			('Tiff',0,('I', 'I', '*', 0x00)),
			('Bmp',0,'BM'));
		ind := getImageDef(stream, imageDef);
		if ind >= 0 then Begin
			case imageDef[ind][0] of
			'Gif':
				[px_width, px_height, horz_dpi, vert_dpi] := gif_dimensions(stream);
			'Bmp':
				[px_width, px_height, horz_dpi, vert_dpi] := bmp_dimensions(stream);
			'Tiff':
				[px_width, px_height, horz_dpi, vert_dpi] := tiff_dimensions(stream, imageDef[ind][2]);
			End;
		End;
		println('width={},height={}',px_width, px_height);
	end;
	
	Function Width();
	Begin
		return integer((px_width / horz_dpi) * 914400);
	End;
	
	Function Height();
	Begin
		return integer((px_height / vert_dpi) * 914400);
	End;
	(*
	Function exif_dimensions(stream, r);
	Begin
		start := 0;
		code := nil;
		while code <> 0xd9 do Begin
			[code, off] := _exif_next(start);
			case code of 
				0xe0:
				Begin
					segment_length := _read_int(stream, off, '', 2);
					density_units := _read_int(stream, off + 9, '', 1);
					horz_dpi := read_int(stream, off + 10, '', 2);
					vert_dpi := read_int(stream, off + 12, '', 2);
				End;
			End;
			start := off + segment_length;
		End;
	End;
	
	Function _exif_next(start);
	Begin
        while 1 do Begin
			position := _offset_of_next_ff_byte(start);
            [position, byte_] = _next_non_ff_byte(position+1);
			start := position + 1;
            if byte_ = 0x00 then continue;
			marker_code := byte_;
			segment_offset := position + 1;
			break;
		End;
        return array(marker_code, segment_offset);
	End;
	
    Function _offset_of_next_ff_byte(start);
	Begin
		for i:=start to length(content_)-1 do Begin
			byte_ := ord(content_[i]);
			if byte_ <> 0xff then return array(start - 1, byte_);
		return array(start, 0);
	End;
	
    Function _next_non_ff_byte(start);
	Begin
		for i:=start to length(content_)-1 do Begin
			byte_ := ord(content_[i]);
			if byte_ = 0xff then return start - 1;
		return start;
	End;
	*)
	Function tiff_dimensions(stream, r);
	Begin
		off := _read_int(stream, 4, r[0], 4);
		count := _read_int(stream, off, r[0], 2);
		m := array();
		for i:=0 to count-1 do Begin
            dir_entry_offset := off + 2 + i*12;
			tag_code := _read_int(stream, dir_entry_offset, r[0], 2);
			field_type := _read_int(stream, dir_entry_offset + 2, r[0], 2);
			value_count := _read_int(stream, dir_entry_offset + 4, r[0], 4);
			value_offset := _read_int(stream, dir_entry_offset + 8, r[0], 4);				
			case field_type of 
			2:
				val := stream[value_offset:value_offset+value_count-1];
			3:
				if value_count=1 then
					val := _read_int(stream, dir_entry_offset + 8, r[0], 2);
			4:
				if value_count=1 then
					val := _read_int(stream, dir_entry_offset + 8, r[0], 4);
			5:
				if value_count=1 then Begin
					numerator := _read_int(stream, value_offset, r[0], 4);
					denominator := _read_int(stream, value_offset + 4, r[0], 4);
					val := numerator / denominator;
				End;
			End;
println('off={}, tag={},type={},value_count={},value_off={},val={}',off, tag_code,field_type,value_count,value_offset,val);	
			m[tag_code] := val;
		End;
		px_width := m[256];
		px_height := m[257];
		horz_dpi := _tiff_dpi(282, m);
		vert_dpi := _tiff_dpi(283, m);
		return array(px_width, px_height, horz_dpi, vert_dpi);
	End;
	
	Function _read_int(stream, off, t, size);
	Begin
		stm := new TMemoryStream();
		if t = 'M' then Begin
			s := '';
			setlength(s, 8);
			for i:=0 to size-1 do Begin
				s[i] := stream[off + size - i - 1];
			End;
			stm.Write(s, size);
		End
		else Begin
			stm.Write(stream[off:off + 7], size);
		End;
		stm.Seek(0);
		v := 0;
		stm.Read(v, size);
		return v;
	End;
	
	Function bmp_dimensions(stream);
	Begin
		s := new TMemoryStream();
		s.Write(stream[0:63], 64);
		s.Seek(0x12);
        px_width := 0;
		s.Read(px_width, 4);
		px_height := 0;
		s.Read(px_height, 4);
		s.Seek(0x26);
		horz_px_per_meter := 0;
		s.Read(horz_px_per_meter, 4);
		vert_px_per_meter := 0;
		s.Read(vert_px_per_meter, 4);
        horz_dpi := _bmp_dpi(horz_px_per_meter);
        vert_dpi := _bmp_dpi(vert_px_per_meter);
		return array(px_width, px_height, horz_dpi, vert_dpi);
	End;
	
	Function gif_dimensions(stream);
	Begin
		s := new TMemoryStream();
		s.Write(stream[6:9], 4);
		s.Seek(0);
		px_width := 0;
		s.Read(px_width, 2);
		px_height := 0;
		s.Read(px_height, 2);
		return array(px_width, px_height, 72, 72);
	End;
		
	Function getImageDef(stream, imageDef);
	Begin
		for i:=0 to length(imageDef)-1 do Begin
			off := imageDef[i][1];
			len := length(imageDef[i][2]);
			buf := stream[off:off+len-1];
			if eq(buf, imageDef[i][2]) then Begin
				return i;
			End;
		End;
		return -1;
	End;
	
	Function eq(buf, r);
	Begin
		for i:=0 to length(buf)-1 do Begin
			c := ifstring(r) ? r[i + 1] : r[i];
			if ifint(c) then c := chr(c);
			if c <> buf[i+1] then return 0;
		End;
		return 1;
	End;
	
	Function _tiff_dpi(tag, m);
	Begin
		if not ifint(m[tag]) then return 72;
		v := ifint(m[296]) ? m[296] : 2;
		if v = 1 then return 72;
        units_per_inch := (v=2 ? 2.54 : 1);
        dots_per_unit := m[tag];
        return integer(round(dots_per_unit * units_per_inch));
	End;
	
	Function _bmp_dpi(px_per_meter);
	Begin
        if px_per_meter = 0 then return 96;
        return integer(round(px_per_meter * 0.0254));
	End;
	
	Name;
	content_;
	px_width:integer;
	px_height:integer;
	horz_dpi:integer;
	vert_dpi:integer;
End;