///|
/// Header and stream-level format helpers for integrations.
pub(all) enum GifVersion {
Gif87a
Gif89a
} derive(Eq, Debug)
///|
pub(all) struct GifHeader {
version : GifVersion
width : Int
height : Int
global_palette : Bool
color_resolution : Int
sorted_palette : Bool
background_index : Int
aspect_ratio : Int
} derive(Debug)
///|
pub fn GifHeader::version(self : GifHeader) -> GifVersion {
self.version
}
///|
pub fn GifHeader::width(self : GifHeader) -> Int {
self.width
}
///|
pub fn GifHeader::height(self : GifHeader) -> Int {
self.height
}
///|
pub fn GifHeader::global_palette(self : GifHeader) -> Bool {
self.global_palette
}
///|
pub fn GifHeader::color_resolution(self : GifHeader) -> Int {
self.color_resolution
}
///|
pub fn GifHeader::sorted_palette(self : GifHeader) -> Bool {
self.sorted_palette
}
///|
pub fn GifHeader::background_index(self : GifHeader) -> Int {
self.background_index
}
///|
pub fn GifHeader::aspect_ratio(self : GifHeader) -> Int {
self.aspect_ratio
}
///|
/// Read the six-byte signature and logical screen descriptor.
pub fn parse_header(data : Bytes) -> GifHeader raise GifError {
if data.length() < 13 {
raise UnexpectedEnd(context="GIF format header")
}
let version = if data[3] == b'8' && data[4] == b'7' && data[5] == b'a' {
Gif87a
} else if data[3] == b'8' && data[4] == b'9' && data[5] == b'a' {
Gif89a
} else {
raise InvalidSignature
}
if data[0] != b'G' || data[1] != b'I' || data[2] != b'F' {
raise InvalidSignature
}
let packed = data[10].to_int()
GifHeader::{
version,
width: data[6].to_int() | (data[7].to_int() << 8),
height: data[8].to_int() | (data[9].to_int() << 8),
global_palette: (packed & 0x80) != 0,
color_resolution: ((packed >> 4) & 7) + 1,
sorted_palette: (packed & 8) != 0,
background_index: data[11].to_int(),
aspect_ratio: data[12].to_int(),
}
}
///|
/// Return a user-facing version string.
pub fn GifVersion::name(self : GifVersion) -> String {
match self {
Gif87a => "GIF87a"
Gif89a => "GIF89a"
}
}
///|
/// Check only the signature, without requiring complete frame data.
pub fn has_gif_signature(data : Bytes) -> Bool {
data.length() >= 6 &&
data[0] == b'G' &&
data[1] == b'I' &&
data[2] == b'F' &&
data[3] == b'8' &&
(data[4] == b'7' || data[4] == b'9') &&
data[5] == b'a'
}
///|
/// Return the logical screen area, clamped to a safe integer range.
pub fn GifHeader::area(self : GifHeader) -> Int {
self.width * self.height
}