EDID (VESA Enhanced Extended Display Identification Data): Lua parsing library

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of EDID (VESA Enhanced Extended Display Identification Data) 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 EDID (VESA Enhanced Extended Display Identification Data)

edid.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 stringstream = require("string_stream")
local str_decode = require("string_decode")
local enum = require("enum")

Edid = class.class(KaitaiStruct)

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

function Edid:_read()
  self.magic = self._io:read_bytes(8)
  if not(self.magic == "\000\255\255\255\255\255\255\000") then
    error("not equal, expected " .. "\000\255\255\255\255\255\255\000" .. ", but got " .. self.magic)
  end
  self.mfg_bytes = self._io:read_u2be()
  self.product_code = self._io:read_u2le()
  self.serial = self._io:read_u4le()
  self.mfg_week = self._io:read_u1()
  self.mfg_year_mod = self._io:read_u1()
  self.edid_version_major = self._io:read_u1()
  self.edid_version_minor = self._io:read_u1()
  self.input_flags = self._io:read_u1()
  self.screen_size_h = self._io:read_u1()
  self.screen_size_v = self._io:read_u1()
  self.gamma_mod = self._io:read_u1()
  self.features_flags = self._io:read_u1()
  self.chromacity = Edid.ChromacityInfo(self._io, self, self._root)
  self.est_timings = Edid.EstTimingsInfo(self._io, self, self._root)
  self._raw_std_timings = {}
  self.std_timings = {}
  for i = 0, 8 - 1 do
    self._raw_std_timings[i + 1] = self._io:read_bytes(2)
    local _io = KaitaiStream(stringstream(self._raw_std_timings[i + 1]))
    self.std_timings[i + 1] = Edid.StdTiming(_io, self, self._root)
  end
end

Edid.property.gamma = {}
function Edid.property.gamma:get()
  if self._m_gamma ~= nil then
    return self._m_gamma
  end

  if self.gamma_mod ~= 255 then
    self._m_gamma = (self.gamma_mod + 100) / 100.0
  end
  return self._m_gamma
end

Edid.property.mfg_id_ch1 = {}
function Edid.property.mfg_id_ch1:get()
  if self._m_mfg_id_ch1 ~= nil then
    return self._m_mfg_id_ch1
  end

  self._m_mfg_id_ch1 = (self.mfg_bytes & 31744) >> 10
  return self._m_mfg_id_ch1
end

Edid.property.mfg_id_ch2 = {}
function Edid.property.mfg_id_ch2:get()
  if self._m_mfg_id_ch2 ~= nil then
    return self._m_mfg_id_ch2
  end

  self._m_mfg_id_ch2 = (self.mfg_bytes & 992) >> 5
  return self._m_mfg_id_ch2
end

Edid.property.mfg_id_ch3 = {}
function Edid.property.mfg_id_ch3:get()
  if self._m_mfg_id_ch3 ~= nil then
    return self._m_mfg_id_ch3
  end

  self._m_mfg_id_ch3 = self.mfg_bytes & 31
  return self._m_mfg_id_ch3
end

Edid.property.mfg_str = {}
function Edid.property.mfg_str:get()
  if self._m_mfg_str ~= nil then
    return self._m_mfg_str
  end

  self._m_mfg_str = str_decode.decode(string.char(self.mfg_id_ch1 + 64, self.mfg_id_ch2 + 64, self.mfg_id_ch3 + 64), "ASCII")
  return self._m_mfg_str
end

Edid.property.mfg_year = {}
function Edid.property.mfg_year:get()
  if self._m_mfg_year ~= nil then
    return self._m_mfg_year
  end

  self._m_mfg_year = self.mfg_year_mod + 1990
  return self._m_mfg_year
end

-- 
-- Manufacturer product code.
-- 
-- Serial number.
-- 
-- Week of manufacture. Week numbering is not consistent between manufacturers.
-- 
-- Year of manufacture, less 1990. (1990-2245). If week=255, it is the model year instead.
-- 
-- EDID version, usually 1 (for 1.3).
-- 
-- EDID revision, usually 3 (for 1.3).
-- 
-- Maximum horizontal image size, in centimetres (max 292 cm/115 in at 16:9 aspect ratio).
-- 
-- Maximum vertical image size, in centimetres. If either byte is 0, undefined (e.g. projector).
-- 
-- Display gamma, datavalue = (gamma*100)-100 (range 1.00-3.54).
-- 
-- Phosphor or filter chromaticity structure, which provides info on colorimetry and white point.
-- See also: Standard, section 3.7
-- 
-- Block of bit flags that indicates support of so called
-- "established timings", which is a commonly used subset of VESA
-- DMT (Discrete Monitor Timings) modes.
-- See also: Standard, section 3.8
-- 
-- Array of descriptions of so called "standard timings", which are
-- used to specify up to 8 additional timings not included in
-- "established timings".

-- 
-- Chromaticity information: colorimetry and white point
-- coordinates. All coordinates are stored as fixed precision
-- 10-bit numbers, bits are shuffled for compactness.
Edid.ChromacityInfo = class.class(KaitaiStruct)

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

function Edid.ChromacityInfo:_read()
  self.red_x_1_0 = self._io:read_bits_int_be(2)
  self.red_y_1_0 = self._io:read_bits_int_be(2)
  self.green_x_1_0 = self._io:read_bits_int_be(2)
  self.green_y_1_0 = self._io:read_bits_int_be(2)
  self.blue_x_1_0 = self._io:read_bits_int_be(2)
  self.blue_y_1_0 = self._io:read_bits_int_be(2)
  self.white_x_1_0 = self._io:read_bits_int_be(2)
  self.white_y_1_0 = self._io:read_bits_int_be(2)
  self._io:align_to_byte()
  self.red_x_9_2 = self._io:read_u1()
  self.red_y_9_2 = self._io:read_u1()
  self.green_x_9_2 = self._io:read_u1()
  self.green_y_9_2 = self._io:read_u1()
  self.blue_x_9_2 = self._io:read_u1()
  self.blue_y_9_2 = self._io:read_u1()
  self.white_x_9_2 = self._io:read_u1()
  self.white_y_9_2 = self._io:read_u1()
end

-- 
-- Blue X coordinate.
Edid.ChromacityInfo.property.blue_x = {}
function Edid.ChromacityInfo.property.blue_x:get()
  if self._m_blue_x ~= nil then
    return self._m_blue_x
  end

  self._m_blue_x = self.blue_x_int / 1024.0
  return self._m_blue_x
end

Edid.ChromacityInfo.property.blue_x_int = {}
function Edid.ChromacityInfo.property.blue_x_int:get()
  if self._m_blue_x_int ~= nil then
    return self._m_blue_x_int
  end

  self._m_blue_x_int = self.blue_x_9_2 << 2 | self.blue_x_1_0
  return self._m_blue_x_int
end

-- 
-- Blue Y coordinate.
Edid.ChromacityInfo.property.blue_y = {}
function Edid.ChromacityInfo.property.blue_y:get()
  if self._m_blue_y ~= nil then
    return self._m_blue_y
  end

  self._m_blue_y = self.blue_y_int / 1024.0
  return self._m_blue_y
end

Edid.ChromacityInfo.property.blue_y_int = {}
function Edid.ChromacityInfo.property.blue_y_int:get()
  if self._m_blue_y_int ~= nil then
    return self._m_blue_y_int
  end

  self._m_blue_y_int = self.blue_y_9_2 << 2 | self.blue_y_1_0
  return self._m_blue_y_int
end

-- 
-- Green X coordinate.
Edid.ChromacityInfo.property.green_x = {}
function Edid.ChromacityInfo.property.green_x:get()
  if self._m_green_x ~= nil then
    return self._m_green_x
  end

  self._m_green_x = self.green_x_int / 1024.0
  return self._m_green_x
end

Edid.ChromacityInfo.property.green_x_int = {}
function Edid.ChromacityInfo.property.green_x_int:get()
  if self._m_green_x_int ~= nil then
    return self._m_green_x_int
  end

  self._m_green_x_int = self.green_x_9_2 << 2 | self.green_x_1_0
  return self._m_green_x_int
end

-- 
-- Green Y coordinate.
Edid.ChromacityInfo.property.green_y = {}
function Edid.ChromacityInfo.property.green_y:get()
  if self._m_green_y ~= nil then
    return self._m_green_y
  end

  self._m_green_y = self.green_y_int / 1024.0
  return self._m_green_y
end

Edid.ChromacityInfo.property.green_y_int = {}
function Edid.ChromacityInfo.property.green_y_int:get()
  if self._m_green_y_int ~= nil then
    return self._m_green_y_int
  end

  self._m_green_y_int = self.green_y_9_2 << 2 | self.green_y_1_0
  return self._m_green_y_int
end

-- 
-- Red X coordinate.
Edid.ChromacityInfo.property.red_x = {}
function Edid.ChromacityInfo.property.red_x:get()
  if self._m_red_x ~= nil then
    return self._m_red_x
  end

  self._m_red_x = self.red_x_int / 1024.0
  return self._m_red_x
end

Edid.ChromacityInfo.property.red_x_int = {}
function Edid.ChromacityInfo.property.red_x_int:get()
  if self._m_red_x_int ~= nil then
    return self._m_red_x_int
  end

  self._m_red_x_int = self.red_x_9_2 << 2 | self.red_x_1_0
  return self._m_red_x_int
end

-- 
-- Red Y coordinate.
Edid.ChromacityInfo.property.red_y = {}
function Edid.ChromacityInfo.property.red_y:get()
  if self._m_red_y ~= nil then
    return self._m_red_y
  end

  self._m_red_y = self.red_y_int / 1024.0
  return self._m_red_y
end

Edid.ChromacityInfo.property.red_y_int = {}
function Edid.ChromacityInfo.property.red_y_int:get()
  if self._m_red_y_int ~= nil then
    return self._m_red_y_int
  end

  self._m_red_y_int = self.red_y_9_2 << 2 | self.red_y_1_0
  return self._m_red_y_int
end

-- 
-- White X coordinate.
Edid.ChromacityInfo.property.white_x = {}
function Edid.ChromacityInfo.property.white_x:get()
  if self._m_white_x ~= nil then
    return self._m_white_x
  end

  self._m_white_x = self.white_x_int / 1024.0
  return self._m_white_x
end

Edid.ChromacityInfo.property.white_x_int = {}
function Edid.ChromacityInfo.property.white_x_int:get()
  if self._m_white_x_int ~= nil then
    return self._m_white_x_int
  end

  self._m_white_x_int = self.white_x_9_2 << 2 | self.white_x_1_0
  return self._m_white_x_int
end

-- 
-- White Y coordinate.
Edid.ChromacityInfo.property.white_y = {}
function Edid.ChromacityInfo.property.white_y:get()
  if self._m_white_y ~= nil then
    return self._m_white_y
  end

  self._m_white_y = self.white_y_int / 1024.0
  return self._m_white_y
end

Edid.ChromacityInfo.property.white_y_int = {}
function Edid.ChromacityInfo.property.white_y_int:get()
  if self._m_white_y_int ~= nil then
    return self._m_white_y_int
  end

  self._m_white_y_int = self.white_y_9_2 << 2 | self.white_y_1_0
  return self._m_white_y_int
end

-- 
-- Red X, bits 1..0.
-- 
-- Red Y, bits 1..0.
-- 
-- Green X, bits 1..0.
-- 
-- Green Y, bits 1..0.
-- 
-- Blue X, bits 1..0.
-- 
-- Blue Y, bits 1..0.
-- 
-- White X, bits 1..0.
-- 
-- White Y, bits 1..0.
-- 
-- Red X, bits 9..2.
-- 
-- Red Y, bits 9..2.
-- 
-- Green X, bits 9..2.
-- 
-- Green Y, bits 9..2.
-- 
-- Blue X, bits 9..2.
-- 
-- Blue Y, bits 9..2.
-- 
-- White X, bits 9..2.
-- 
-- White Y, bits 9..2.

Edid.EstTimingsInfo = class.class(KaitaiStruct)

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

function Edid.EstTimingsInfo:_read()
  self.can_720x400px_70hz = self._io:read_bits_int_be(1) ~= 0
  self.can_720x400px_88hz = self._io:read_bits_int_be(1) ~= 0
  self.can_640x480px_60hz = self._io:read_bits_int_be(1) ~= 0
  self.can_640x480px_67hz = self._io:read_bits_int_be(1) ~= 0
  self.can_640x480px_72hz = self._io:read_bits_int_be(1) ~= 0
  self.can_640x480px_75hz = self._io:read_bits_int_be(1) ~= 0
  self.can_800x600px_56hz = self._io:read_bits_int_be(1) ~= 0
  self.can_800x600px_60hz = self._io:read_bits_int_be(1) ~= 0
  self.can_800x600px_72hz = self._io:read_bits_int_be(1) ~= 0
  self.can_800x600px_75hz = self._io:read_bits_int_be(1) ~= 0
  self.can_832x624px_75hz = self._io:read_bits_int_be(1) ~= 0
  self.can_1024x768px_87hz_i = self._io:read_bits_int_be(1) ~= 0
  self.can_1024x768px_60hz = self._io:read_bits_int_be(1) ~= 0
  self.can_1024x768px_70hz = self._io:read_bits_int_be(1) ~= 0
  self.can_1024x768px_75hz = self._io:read_bits_int_be(1) ~= 0
  self.can_1280x1024px_75hz = self._io:read_bits_int_be(1) ~= 0
  self.can_1152x870px_75hz = self._io:read_bits_int_be(1) ~= 0
  self.reserved = self._io:read_bits_int_be(7)
end

-- 
-- Supports 720 x 400 @ 70Hz.
-- 
-- Supports 720 x 400 @ 88Hz.
-- 
-- Supports 640 x 480 @ 60Hz.
-- 
-- Supports 640 x 480 @ 67Hz.
-- 
-- Supports 640 x 480 @ 72Hz.
-- 
-- Supports 640 x 480 @ 75Hz.
-- 
-- Supports 800 x 600 @ 56Hz.
-- 
-- Supports 800 x 600 @ 60Hz.
-- 
-- Supports 800 x 600 @ 72Hz.
-- 
-- Supports 800 x 600 @ 75Hz.
-- 
-- Supports 832 x 624 @ 75Hz.
-- 
-- Supports 1024 x 768 @ 87Hz(I).
-- 
-- Supports 1024 x 768 @ 60Hz.
-- 
-- Supports 1024 x 768 @ 70Hz.
-- 
-- Supports 1024 x 768 @ 75Hz.
-- 
-- Supports 1280 x 1024 @ 75Hz.
-- 
-- Supports 1152 x 870 @ 75Hz.

Edid.StdTiming = class.class(KaitaiStruct)

Edid.StdTiming.AspectRatios = enum.Enum {
  ratio_16_10 = 0,
  ratio_4_3 = 1,
  ratio_5_4 = 2,
  ratio_16_9 = 3,
}

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

function Edid.StdTiming:_read()
  self.horiz_active_pixels_mod = self._io:read_u1()
  self.aspect_ratio = Edid.StdTiming.AspectRatios(self._io:read_bits_int_be(2))
  self.refresh_rate_mod = self._io:read_bits_int_be(6)
end

Edid.StdTiming.property.bytes_lookahead = {}
function Edid.StdTiming.property.bytes_lookahead:get()
  if self._m_bytes_lookahead ~= nil then
    return self._m_bytes_lookahead
  end

  local _pos = self._io:pos()
  self._io:seek(0)
  self._m_bytes_lookahead = self._io:read_bytes(2)
  self._io:seek(_pos)
  return self._m_bytes_lookahead
end

-- 
-- Range of horizontal active pixels.
Edid.StdTiming.property.horiz_active_pixels = {}
function Edid.StdTiming.property.horiz_active_pixels:get()
  if self._m_horiz_active_pixels ~= nil then
    return self._m_horiz_active_pixels
  end

  if self.is_used then
    self._m_horiz_active_pixels = (self.horiz_active_pixels_mod + 31) * 8
  end
  return self._m_horiz_active_pixels
end

Edid.StdTiming.property.is_used = {}
function Edid.StdTiming.property.is_used:get()
  if self._m_is_used ~= nil then
    return self._m_is_used
  end

  self._m_is_used = self.bytes_lookahead ~= "\001\001"
  return self._m_is_used
end

-- 
-- Vertical refresh rate, Hz.
Edid.StdTiming.property.refresh_rate = {}
function Edid.StdTiming.property.refresh_rate:get()
  if self._m_refresh_rate ~= nil then
    return self._m_refresh_rate
  end

  if self.is_used then
    self._m_refresh_rate = self.refresh_rate_mod + 60
  end
  return self._m_refresh_rate
end

-- 
-- Range of horizontal active pixels, written in modified form:
-- `(horiz_active_pixels / 8) - 31`. This yields an effective
-- range of 256..2288, with steps of 8 pixels.
-- 
-- Aspect ratio of the image. Can be used to calculate number
-- of vertical pixels.
-- 
-- Refresh rate in Hz, written in modified form: `refresh_rate
-- - 60`. This yields an effective range of 60..123 Hz.