Take account of the exif orientation flag for tiff and jpegs that have it

This commit is contained in:
Stephen Sykes 2013-07-02 23:58:25 +03:00
parent 05a3443164
commit 5ecaac4179
3 changed files with 68 additions and 22 deletions

View File

@ -274,6 +274,7 @@ class FastImage
@str.force_encoding("ASCII-8BIT") if has_encoding?
@strpos = 0
@bytes_read = 0
@bytes_delivered = 0
begin
result = send("parse_#{@property}")
@ -290,6 +291,7 @@ class FastImage
def parse_size
@type = parse_type unless @type
@strpos = 0
@bytes_delivered = 0
send("parse_size_for_#{@type}")
end
@ -320,6 +322,7 @@ class FastImage
result = @str[@strpos..(@strpos + n - 1)]
@strpos += n
@bytes_delivered += n
result
end
@ -369,6 +372,15 @@ class FastImage
get_byte == 0xFF ? :sof : :started
when :sof
case get_byte
when 0xe1 # APP1
skip_chars = read_int(get_chars(2)) - 2
skip_from = @bytes_delivered
if get_chars(4) == "Exif"
get_chars(2)
parse_exif
end
get_chars(skip_chars - (@bytes_delivered - skip_from))
:started
when 0xe0..0xef
:skipframe
when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF
@ -379,14 +391,16 @@ class FastImage
:skipframe
end
when :skipframe
@skip_chars = read_int(get_chars(2)) - 2
:do_skip
when :do_skip
get_chars(@skip_chars)
skip_chars = read_int(get_chars(2)) - 2
get_chars(skip_chars)
:started
when :readsize
s = get_chars(7)
return [read_int(s[5..6]), read_int(s[3..4])]
if @exif_orientation && @exif_orientation >= 5
return [read_int(s[3..4]), read_int(s[5..6])]
else
return [read_int(s[5..6]), read_int(s[3..4])]
end
end
end
end
@ -396,36 +410,67 @@ class FastImage
d.unpack("C")[0] == 40 ? d[4..-1].unpack('LL') : d[4..8].unpack('SS')
end
def parse_size_for_tiff
def get_exif_byte_order
byte_order = get_chars(2)
case byte_order
when 'II'; short, long = 'v', 'V'
when 'MM'; short, long = 'n', 'N'
when 'II'
@short, @long = 'v', 'V'
when 'MM'
@short, @long = 'n', 'N'
else
raise CannotParseImage
end
get_chars(2) # 42
end
offset = get_chars(4).unpack(long)[0]
get_chars(offset - 8)
width = height = nil
tag_count = get_chars(2).unpack(short)[0]
def parse_exif_ifd
tag_count = get_chars(2).unpack(@short)[0]
tag_count.downto(1) do
type = get_chars(2).unpack(short)[0]
type = get_chars(2).unpack(@short)[0]
get_chars(6)
data = get_chars(2).unpack(short)[0]
data = get_chars(2).unpack(@short)[0]
case type
when 0x0100 # image width
width = data
@exif_width = data
when 0x0101 # image height
height = data
@exif_height = data
when 0x0112 # orientation
@exif_orientation = data
end
if width && height
return [width, height]
if @type == :tiff && @exif_width && @exif_height && @exif_orientation
return # no need to parse more
end
get_chars(2)
end
next_offset = get_chars(4).unpack(@long)[0]
if next_offset > 0
get_chars(next_offset - (@bytes_delivered - @exif_start_byte))
parse_exif_ifd
end
end
def parse_exif
@exif_start_byte = @bytes_delivered
get_exif_byte_order
get_chars(2) # 42
offset = get_chars(4).unpack(@long)[0]
get_chars(offset - 8)
parse_exif_ifd
end
def parse_size_for_tiff
parse_exif
if @exif_orientation && @exif_orientation >= 5
return [@exif_height, @exif_width]
else
return [@exif_width, @exif_height]
end
raise CannotParseImage
end
end

BIN
test/fixtures/exif_orientation.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@ -18,7 +18,8 @@ GoodFixtures = {
"test2.jpg"=>[:jpeg, [250, 188]],
"test3.jpg"=>[:jpeg, [630, 367]],
"test.tiff"=>[:tiff, [85, 67]],
"test2.tiff"=>[:tiff, [333, 225]]
"test2.tiff"=>[:tiff, [333, 225]],
"exif_orientation.jpg"=>[:jpeg, [2448, 3264]]
}
BadFixtures = [