WebP: Lua parsing library

File extension

webp

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.11

References

This page hosts a formal specification of WebP using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Lua source code to parse WebP

webp.lua

-- This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
--
-- This file is compatible with Lua 5.3

local class = require("class")
require("kaitaistruct")
local enum = require("enum")
local stringstream = require("string_stream")
local str_decode = require("string_decode")

-- 
-- See also: Source (https://developers.google.com/speed/webp/docs/riff_container)
Webp = class.class(KaitaiStruct)

Webp.ChunkNames = enum.Enum {
  xmp_var = 5262680,
  vp8 = 540561494,
  xmp = 542133592,
  exif = 1179211845,
  anmf = 1179471425,
  alph = 1213221953,
  vp8l = 1278758998,
  frgm = 1296519750,
  anim = 1296649793,
  iccp = 1346585417,
  vp8x = 1480085590,
}

Webp.CompressionMethod = enum.Enum {
  none = 0,
  webp_lossless = 1,
}

Webp.FilteringMethod = enum.Enum {
  none = 0,
  horizontal = 1,
  vertical = 2,
  gradient = 3,
}

Webp.Preprocessing = enum.Enum {
  none = 0,
  level_reduction = 1,
}

function Webp:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root or self
  self:_read()
end

function Webp:_read()
  self.magic = self._io:read_bytes(4)
  if not(self.magic == "\082\073\070\070") then
    error("not equal, expected " .. "\082\073\070\070" .. ", but got " .. self.magic)
  end
  self.len_data = self._io:read_u4le()
  self.webp = self._io:read_bytes(4)
  if not(self.webp == "\087\069\066\080") then
    error("not equal, expected " .. "\087\069\066\080" .. ", but got " .. self.webp)
  end
  self._raw_payload = self._io:read_bytes(self.len_data - 4)
  local _io = KaitaiStream(stringstream(self._raw_payload))
  self.payload = Webp.Chunks(_io, self, self._root)
end


Webp.Alph = class.class(KaitaiStruct)

function Webp.Alph:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Alph:_read()
  self.reserved = self._io:read_bits_int_be(2)
  if not(self.reserved == 0) then
    error("not equal, expected " .. 0 .. ", but got " .. self.reserved)
  end
  self.preprocessing = Webp.Preprocessing(self._io:read_bits_int_be(2))
  if self.preprocessing == nil then
    error("ValidationNotInEnumError")
  end
  self.filtering = Webp.FilteringMethod(self._io:read_bits_int_be(2))
  self.compression = Webp.CompressionMethod(self._io:read_bits_int_be(2))
  if self.compression == nil then
    error("ValidationNotInEnumError")
  end
  self._io:align_to_byte()
  self.data = self._io:read_bytes_full()
end


-- 
-- See also: Source (https://developers.google.com/speed/webp/docs/riff_container#animation)
Webp.Anim = class.class(KaitaiStruct)

function Webp.Anim:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Anim:_read()
  self.background_color = Webp.Anim.BgColor(self._io, self, self._root)
  self.loop_count = self._io:read_u2le()
end


Webp.Anim.BgColor = class.class(KaitaiStruct)

function Webp.Anim.BgColor:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Anim.BgColor:_read()
  self.blue = self._io:read_u1()
  self.green = self._io:read_u1()
  self.red = self._io:read_u1()
  self.alpha = self._io:read_u1()
end


Webp.Anmf = class.class(KaitaiStruct)

function Webp.Anmf:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Anmf:_read()
  self.frame_x_div_2 = self._io:read_bits_int_le(24)
  self.frame_y_div_2 = self._io:read_bits_int_le(24)
  self.frame_width_minus_1 = self._io:read_bits_int_le(24)
  self.frame_height_minus_1 = self._io:read_bits_int_le(24)
  self.duration = self._io:read_bits_int_le(24)
  self.reserved = self._io:read_bits_int_be(6)
  if not(self.reserved == 0) then
    error("not equal, expected " .. 0 .. ", but got " .. self.reserved)
  end
  self.blending_method = self._io:read_bits_int_be(1) ~= 0
  self.disposal_method = self._io:read_bits_int_be(1) ~= 0
  self._io:align_to_byte()
  self.data = self._io:read_bytes_full()
end

Webp.Anmf.property.frame_height = {}
function Webp.Anmf.property.frame_height:get()
  if self._m_frame_height ~= nil then
    return self._m_frame_height
  end

  self._m_frame_height = self.frame_height_minus_1 + 1
  return self._m_frame_height
end

Webp.Anmf.property.frame_width = {}
function Webp.Anmf.property.frame_width:get()
  if self._m_frame_width ~= nil then
    return self._m_frame_width
  end

  self._m_frame_width = self.frame_width_minus_1 + 1
  return self._m_frame_width
end

Webp.Anmf.property.frame_x = {}
function Webp.Anmf.property.frame_x:get()
  if self._m_frame_x ~= nil then
    return self._m_frame_x
  end

  self._m_frame_x = self.frame_x_div_2 * 2
  return self._m_frame_x
end

Webp.Anmf.property.frame_y = {}
function Webp.Anmf.property.frame_y:get()
  if self._m_frame_y ~= nil then
    return self._m_frame_y
  end

  self._m_frame_y = self.frame_y_div_2 * 2
  return self._m_frame_y
end


Webp.Chunk = class.class(KaitaiStruct)

function Webp.Chunk:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Chunk:_read()
  self.name = Webp.ChunkNames(self._io:read_u4le())
  if self.name == nil then
    error("ValidationNotInEnumError")
  end
  self.len_data = self._io:read_u4le()
  local _on = self.name
  if _on == Webp.ChunkNames.alph then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Alph(_io, self, self._root)
  elseif _on == Webp.ChunkNames.anim then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Anim(_io, self, self._root)
  elseif _on == Webp.ChunkNames.anmf then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Anmf(_io, self, self._root)
  elseif _on == Webp.ChunkNames.vp8 then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Vp8(_io, self, self._root)
  elseif _on == Webp.ChunkNames.vp8l then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Vp8l(_io, self, self._root)
  elseif _on == Webp.ChunkNames.vp8x then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Vp8x(_io, self, self._root)
  elseif _on == Webp.ChunkNames.xmp then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Xmp(_io, self, self._root)
  elseif _on == Webp.ChunkNames.xmp_var then
    self._raw_data = self._io:read_bytes(self.len_data)
    local _io = KaitaiStream(stringstream(self._raw_data))
    self.data = Webp.Xmp(_io, self, self._root)
  else
    self.data = self._io:read_bytes(self.len_data)
  end
  if self.len_data % 2 ~= 0 then
    self.padding = self._io:read_bytes(1)
    if not(self.padding == "\000") then
      error("not equal, expected " .. "\000" .. ", but got " .. self.padding)
    end
  end
end


Webp.Chunks = class.class(KaitaiStruct)

function Webp.Chunks:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Chunks:_read()
  self.chunks = {}
  local i = 0
  while not self._io:is_eof() do
    self.chunks[i + 1] = Webp.Chunk(self._io, self, self._root)
    i = i + 1
  end
end


-- 
-- See also: Source (https://www.rfc-editor.org/rfc/rfc6386#section-9.1)
Webp.Vp8 = class.class(KaitaiStruct)

function Webp.Vp8:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Vp8:_read()
  self.frame_type = self._io:read_bits_int_le(1) ~= 0
  if not(self.frame_type == false) then
    error("not equal, expected " .. false .. ", but got " .. self.frame_type)
  end
  self.version = self._io:read_bits_int_le(3)
  if not(self.version <= 3) then
    error("ValidationGreaterThanError")
  end
  self.show_frame = self._io:read_bits_int_le(1) ~= 0
  self.len_first_partition = self._io:read_bits_int_le(19)
  self._io:align_to_byte()
  self.start_code = self._io:read_bytes(3)
  if not(self.start_code == "\157\001\042") then
    error("not equal, expected " .. "\157\001\042" .. ", but got " .. self.start_code)
  end
  self.width = self._io:read_bits_int_le(14)
  self.horizontal_scale = self._io:read_bits_int_le(2)
  self.height = self._io:read_bits_int_le(14)
  self.vertical_scale = self._io:read_bits_int_le(2)
  self._io:align_to_byte()
  self.data = self._io:read_bytes_full()
end


-- 
-- See also: Source (https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification)
Webp.Vp8l = class.class(KaitaiStruct)

function Webp.Vp8l:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Vp8l:_read()
  self.signature = self._io:read_u1()
  if not(self.signature == 47) then
    error("not equal, expected " .. 47 .. ", but got " .. self.signature)
  end
  self.image_width_minus_1 = self._io:read_bits_int_le(14)
  self.image_height_minus_1 = self._io:read_bits_int_le(14)
  self.alpha_is_used = self._io:read_bits_int_le(1) ~= 0
  self.version_number = self._io:read_bits_int_le(3)
  if not(self.version_number == 0) then
    error("not equal, expected " .. 0 .. ", but got " .. self.version_number)
  end
  self._io:align_to_byte()
  self.data = self._io:read_bytes_full()
end

Webp.Vp8l.property.image_height = {}
function Webp.Vp8l.property.image_height:get()
  if self._m_image_height ~= nil then
    return self._m_image_height
  end

  self._m_image_height = self.image_height_minus_1 + 1
  return self._m_image_height
end

Webp.Vp8l.property.image_width = {}
function Webp.Vp8l.property.image_width:get()
  if self._m_image_width ~= nil then
    return self._m_image_width
  end

  self._m_image_width = self.image_width_minus_1 + 1
  return self._m_image_width
end

-- 
-- A hint only - it should not impact decoding. It should be `false` when
-- all alpha values are 255 in the picture, and `true` otherwise.

Webp.Vp8x = class.class(KaitaiStruct)

function Webp.Vp8x:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Vp8x:_read()
  self.reserved1 = self._io:read_bits_int_be(2)
  if not(self.reserved1 == 0) then
    error("not equal, expected " .. 0 .. ", but got " .. self.reserved1)
  end
  self.icc_profile = self._io:read_bits_int_be(1) ~= 0
  self.alpha = self._io:read_bits_int_be(1) ~= 0
  self.exif = self._io:read_bits_int_be(1) ~= 0
  self.xmp = self._io:read_bits_int_be(1) ~= 0
  self.animation = self._io:read_bits_int_be(1) ~= 0
  self.reserved2 = self._io:read_bits_int_be(1) ~= 0
  if not(self.reserved2 == false) then
    error("not equal, expected " .. false .. ", but got " .. self.reserved2)
  end
  self.reserved3 = self._io:read_bits_int_be(24)
  if not(self.reserved3 == 0) then
    error("not equal, expected " .. 0 .. ", but got " .. self.reserved3)
  end
  self.canvas_width_minus_1 = self._io:read_bits_int_le(24)
  self.canvas_height_minus_1 = self._io:read_bits_int_le(24)
  if not(self.canvas_height_minus_1 <= math.floor(4294967295 / self.canvas_width) - 1) then
    error("ValidationGreaterThanError")
  end
end

Webp.Vp8x.property.canvas_height = {}
function Webp.Vp8x.property.canvas_height:get()
  if self._m_canvas_height ~= nil then
    return self._m_canvas_height
  end

  self._m_canvas_height = self.canvas_height_minus_1 + 1
  return self._m_canvas_height
end

Webp.Vp8x.property.canvas_width = {}
function Webp.Vp8x.property.canvas_width:get()
  if self._m_canvas_width ~= nil then
    return self._m_canvas_width
  end

  self._m_canvas_width = self.canvas_width_minus_1 + 1
  return self._m_canvas_width
end


Webp.Xmp = class.class(KaitaiStruct)

function Webp.Xmp:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root
  self:_read()
end

function Webp.Xmp:_read()
  self.data = str_decode.decode(self._io:read_bytes_full(), "UTF-8")
end