-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
1,271 additions
and
1,159 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module FastImageParsing | ||
class Avif < ImageBase # :nodoc: | ||
def dimensions | ||
bmff = IsoBmff.new(@stream) | ||
[bmff.width, bmff.height] | ||
end | ||
|
||
def animated? | ||
@stream.peek(12)[4..-1] == "ftypavis" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module FastImageParsing | ||
class Bmp < ImageBase # :nodoc: | ||
def dimensions | ||
d = @stream.read(32)[14..28] | ||
header = d.unpack("C")[0] | ||
|
||
result = if header == 12 | ||
d[4..8].unpack('SS') | ||
else | ||
d[4..-1].unpack('l<l<') | ||
end | ||
|
||
# ImageHeight is expressed in pixels. The absolute value is necessary because ImageHeight can be negative | ||
[result.first, result.last.abs] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
module FastImageParsing | ||
class Exif # :nodoc: | ||
attr_reader :width, :height, :orientation | ||
|
||
def initialize(stream) | ||
@stream = stream | ||
@width, @height, @orientation = nil | ||
parse_exif | ||
end | ||
|
||
def rotated? | ||
@orientation >= 5 | ||
end | ||
|
||
private | ||
|
||
def get_exif_byte_order | ||
byte_order = @stream.read(2) | ||
case byte_order | ||
when 'II' | ||
@short, @long = 'v', 'V' | ||
when 'MM' | ||
@short, @long = 'n', 'N' | ||
else | ||
raise FastImage::CannotParseImage | ||
end | ||
end | ||
|
||
def parse_exif_ifd | ||
tag_count = @stream.read(2).unpack(@short)[0] | ||
tag_count.downto(1) do | ||
type = @stream.read(2).unpack(@short)[0] | ||
@stream.read(6) | ||
data = @stream.read(2).unpack(@short)[0] | ||
case type | ||
when 0x0100 # image width | ||
@width = data | ||
when 0x0101 # image height | ||
@height = data | ||
when 0x0112 # orientation | ||
@orientation = data | ||
end | ||
if @width && @height && @orientation | ||
return # no need to parse more | ||
end | ||
@stream.read(2) | ||
end | ||
end | ||
|
||
def parse_exif | ||
@start_byte = @stream.pos | ||
|
||
get_exif_byte_order | ||
|
||
@stream.read(2) # 42 | ||
|
||
offset = @stream.read(4).unpack(@long)[0] | ||
if @stream.respond_to?(:skip) | ||
@stream.skip(offset - 8) | ||
else | ||
@stream.read(offset - 8) | ||
end | ||
|
||
parse_exif_ifd | ||
|
||
@orientation ||= 1 | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
module FastImageParsing | ||
class FiberStream # :nodoc: | ||
include StreamUtil | ||
attr_reader :pos | ||
|
||
# read_fiber should return nil if it no longer has anything to return when resumed | ||
# so the result of the whole Fiber block should be set to be nil in case yield is no | ||
# longer called | ||
def initialize(read_fiber) | ||
@read_fiber = read_fiber | ||
@pos = 0 | ||
@strpos = 0 | ||
@str = '' | ||
end | ||
|
||
# Peeking beyond the end of the input will raise | ||
def peek(n) | ||
while @strpos + n > @str.size | ||
unused_str = @str[@strpos..-1] | ||
|
||
new_string = @read_fiber.resume | ||
raise FastImage::CannotParseImage if !new_string | ||
# we are dealing with bytes here, so force the encoding | ||
new_string.force_encoding("ASCII-8BIT") if new_string.respond_to? :force_encoding | ||
|
||
@str = unused_str + new_string | ||
@strpos = 0 | ||
end | ||
|
||
@str[@strpos, n] | ||
end | ||
|
||
def read(n) | ||
result = peek(n) | ||
@strpos += n | ||
@pos += n | ||
result | ||
end | ||
|
||
def skip(n) | ||
discarded = 0 | ||
fetched = @str[@strpos..-1].size | ||
while n > fetched | ||
discarded += @str[@strpos..-1].size | ||
new_string = @read_fiber.resume | ||
raise FastImage::CannotParseImage if !new_string | ||
|
||
new_string.force_encoding("ASCII-8BIT") if new_string.respond_to? :force_encoding | ||
|
||
fetched += new_string.size | ||
@str = new_string | ||
@strpos = 0 | ||
end | ||
@strpos = @strpos + n - discarded | ||
@pos += n | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
module FastImageParsing | ||
class Gif < ImageBase # :nodoc: | ||
def dimensions | ||
@stream.read(11)[6..10].unpack('SS') | ||
end | ||
|
||
# Checks for multiple frames | ||
def animated? | ||
frames = 0 | ||
|
||
# "GIF" + version (3) + width (2) + height (2) | ||
@stream.skip(10) | ||
|
||
# fields (1) + bg color (1) + pixel ratio (1) | ||
fields = @stream.read(3).unpack("CCC")[0] | ||
if fields & 0x80 != 0 # Global Color Table | ||
# 2 * (depth + 1) colors, each occupying 3 bytes (RGB) | ||
@stream.skip(3 * 2 ** ((fields & 0x7) + 1)) | ||
end | ||
|
||
loop do | ||
block_type = @stream.read(1).unpack("C")[0] | ||
|
||
if block_type == 0x21 # Graphic Control Extension | ||
# extension type (1) + size (1) | ||
size = @stream.read(2).unpack("CC")[1] | ||
@stream.skip(size) | ||
skip_sub_blocks | ||
elsif block_type == 0x2C # Image Descriptor | ||
frames += 1 | ||
return true if frames > 1 | ||
|
||
# left position (2) + top position (2) + width (2) + height (2) + fields (1) | ||
fields = @stream.read(9).unpack("SSSSC")[4] | ||
if fields & 0x80 != 0 # Local Color Table | ||
# 2 * (depth + 1) colors, each occupying 3 bytes (RGB) | ||
@stream.skip(3 * 2 ** ((fields & 0x7) + 1)) | ||
end | ||
|
||
@stream.skip(1) # LZW min code size (1) | ||
skip_sub_blocks | ||
else | ||
break # unrecognized block | ||
end | ||
end | ||
|
||
false | ||
end | ||
|
||
private | ||
|
||
def skip_sub_blocks | ||
loop do | ||
size = @stream.read(1).unpack("C")[0] | ||
if size == 0 | ||
break | ||
else | ||
@stream.skip(size) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module FastImageParsing | ||
class Heic < ImageBase # :nodoc: | ||
def dimensions | ||
bmff = IsoBmff.new(@stream) | ||
[bmff.width, bmff.height] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module FastImageParsing | ||
class Ico < ImageBase | ||
def dimensions | ||
icons = @stream.read(6)[4..5].unpack('v').first | ||
sizes = icons.times.map { @stream.read(16).unpack('C2').map { |x| x == 0 ? 256 : x } }.sort_by { |w,h| w * h } | ||
sizes.last | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module FastImageParsing | ||
class ImageBase # :nodoc: | ||
def initialize(stream) | ||
@stream = stream | ||
end | ||
|
||
# Implement in subclasses | ||
def dimensions | ||
raise NotImplementedError | ||
end | ||
|
||
# Implement in subclasses if appropriate | ||
def animated? | ||
nil | ||
end | ||
end | ||
end |
Oops, something went wrong.