Skip to content

Latest commit

 

History

History
198 lines (148 loc) · 8.26 KB

Specification.md

File metadata and controls

198 lines (148 loc) · 8.26 KB

FIOQ is a lossless image format designed for fast encoding and decoding of RGBA image data used by Game Maker Studio. It uses a combination of techniques inspired by the QOI (Quite OK Image) format, including an index of previously seen colors, run-length encoding, and differential color encoding.

Header

An FIOQ file starts with a 12-byte header:

Bytes Description Type
0-3 Magic Bytes char[4]
4-5 Image Width u16
6-7 Image Height u16
8-11 Payload Size u32

The Payload Size indicates the number of bytes that follow the header, containing the encoded pixel data (operation chunks).

Encoding/Decoding

Images are encoded row by row, left to right, top to bottom. The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. Pixels are encoded as:

  • A run of the current color
  • An index into an array of previously seen pixels
  • A difference to the current color value in r,g,b
  • Full r,g,b or r,g,b,a values

After a pixel color is determined by an operation (except for run operations), that color is stored in the color_array at an index calculated by:

index_position = (r ^ g ^ b ^ a) % 64

Operations

The operation payload is a sequence of "chunks." Each chunk starts with a 1-byte operation code. The top two bits or the top four bits of the op-byte determine the operation type.

FIOQ_OP_INDEX (Color from Index Array)

  • Op-Byte Pattern: 00xxxxxx

  • Total Bytes: 1

    • op_code = 2-bit tag b00
    • index = 6-bit index into the color index array: 0..63
  • Action:

    • The current_color is set to the color stored at color_array[index].
    • Thecurrent_color is output as the next pixel.

FIOQ_OP_RUN (Short Run)

  • Op-Byte Pattern: 010xxxxx (binary)
  • Total Bytes: 1
  • Payload Breakdown:
    • op_code_tag = 3-bit tag b010.
    • value = 5-bit value (op_byte & 0x1F).
    • run_length = value + 1. (This results in a run_length from 1 to 32 pixels.)
  • Action:
    • The current_color (the color of the previous pixel) is output run_length times.

FIOQ_OP_RUN_EX (Extended Run)

  • Op-Byte Pattern: 011yxxxx (binary)

    • This corresponds to (op_byte & 0xE0) == 0x60. The y bit (bit 4) is part of the 011 prefix. The lower 4 bits xxxx are used for the run length calculation.
  • Total Bytes: 2

  • Payload Breakdown:

    Byte Part Description
    0 Op-Byte Tag b011 (bits 7-5). Lower 4 bits (op_byte & 0x0F) are run_length_high_bits.
    1 Data run_length_low_byte (8 bits).
    • run_length = (run_length_high_bits << 8) + run_length_low_byte + 33.
  • Action:

    • The current_color (the color of the previous pixel) is output run_length times.
    • The run_length ranges from (0 << 8) + 0 + 33 = 33 to (15 << 8) + 255 + 33 = 3840 + 255 + 33 = 4128 pixels.

Differential Color Operations

FIOQ_OP_DIFF_S (Small Diffs)

  • Op-Byte Pattern: 10xxyyzz (binary)

    • This corresponds to (op_byte & 0xC0) == 0x80.
  • Total Bytes: 1

  • Payload Breakdown:

    Bits Description
    7-6 Tag b10
    5-4 dr_2bit (2-bit signed difference for Red, bias -2)
    3-2 dg_2bit (2-bit signed difference for Green, bias -2)
    1-0 db_2bit (2-bit signed difference for Blue, bias -2)
    • The signed differences dr, dg, db range from -2 to 1.
    • (00 -> 0, 01 -> 1, 10 -> -2, 11 -> -1).
  • Action:

    • new_r = (current_color.r + dr_2bit) % 256
    • new_g = (current_color.g + dg_2bit) % 256
    • new_b = (current_color.b + db_2bit) % 256
    • new_a = current_color.a (alpha remains unchanged).
    • current_color is updated to {new_r, new_g, new_b, new_a}.
    • The new current_color is output as the next pixel.
    • The new current_color is stored in color_array. (Using the hash function)
    • The difference to the current channel values are using a wraparound operation, so 1 - 2 will result in 255, while 255 + 1 will result in 0.

FIOQ_OP_ADD_DIFF / FIOQ_OP_SUB_DIFF (Medium Diffs)

These operations use 2 bytes. The first byte determines the operation type and the red channel difference. The second byte encodes green and blue differences.

  • Op-Byte Patterns:

    • FIOQ_OP_ADD_DIFF: 1100rrrr (binary) ((op_byte & 0xF0) == 0xC0)
    • FIOQ_OP_SUB_DIFF: 1101rrrr (binary) ((op_byte & 0xF0) == 0xD0)
  • Total Bytes: 2

  • Payload Breakdown:

    Byte Bits Description
    0 7-4 Tag b1100 (ADD_DIFF) or b1101 (SUB_DIFF)
    0 3-0 val_r (4 bits used for dr calculation)
    1 7-4 val_g (4-bit signed difference for Green, bias -8)
    1 3-0 val_b (4-bit signed difference for Blue, bias -8)
  • Action:

    • dg is derived from val_g (a 4-bit signed value, range -8 to 7).
    • db is derived from val_b (a 4-bit signed value, range -8 to 7).
    • For FIOQ_OP_ADD_DIFF (1100rrrr):
      • dr = val_r (unsigned, range 0 to 15).
    • For FIOQ_OP_SUB_DIFF (1101rrrr):
      • dr = val_r - 16 (range -16 to -1, assuming val_r is treated as 0-15 before subtraction).
    • new_r = (current_color.r + dr) % 256
    • new_g = (current_color.g + dg) % 256
    • new_b = (current_color.b + db) % 256
    • new_a = current_color.a (alpha remains unchanged).
    • current_color is updated to {new_r, new_g, new_b, new_a}.
    • The new current_color is output as the next pixel.
    • The new current_color is stored in color_array. (Using the hash function)

FIOQ_OP_DIFF_L (Large Diffs with Alpha)

  • Op-Byte Pattern: 1110xxxx (binary)
    • This corresponds to (op_byte & 0xF0) == 0xE0.
  • Total Bytes: 3
  • Payload Breakdown:
    The 3 bytes (op-byte, data-byte1, data-byte2) form a 20-bit data payload after the 4-bit tag b1110. These 20 bits are split into four 5-bit signed differences for dr, dg, db, da.
    • Op-Byte: 1110 dR4 dR3 dR2 dR1
    • Data-Byte 1: dR0 dG4 dG3 dG2 dG1 dG0 dB4 dB3
    • Data-Byte 2: dB2 dB1 dB0 dA4 dA3 dA2 dA1 dA0
      Where:
    • dR4 dR3 dR2 dR1 dR0 form a 5-bit signed dr (bias -16, range -16 to 15).
    • dG4 dG3 dG2 dG1 dG0 form a 5-bit signed dg (bias -16, range -16 to 15).
    • dB4 dB3 dB2 dB1 dB0 form a 5-bit signed db (bias -16, range -16 to 15).
    • dA4 dA3 dA2 dA1 dA0 form a 5-bit signed da (bias -16, range -16 to 15).
  • Action:
    • new_r = (current_color.r + dr) % 256
    • new_g = (current_color.g + dg) % 256
    • new_b = (current_color.b + db) % 256
    • new_a = (current_color.a + da) % 256
    • current_color is updated to {new_r, new_g, new_b, new_a}.
    • The new current_color is output as the next pixel.
    • The new current_color is stored in color_array. (Using the hash function)

FIOQ_OP_CHANNEL (Set Absolute Channel Values)

  • Op-Byte Pattern: 1111rgba (binary)
    • This corresponds to (op_byte & 0xF0) == 0xF0.
  • Total Bytes: 1 to 5
    • 1 byte for the op-code.
    • +1 byte for each channel flag set in the lower 4 bits of the op-code.
  • Payload Breakdown:
    • Op-Byte:
      • Bits 7-4: Tag b1111.
      • Bit 3 (R_flag): If set (1), the next byte in the stream is the new Red value.
      • Bit 2 (G_flag): If set (1), the next byte (after R if R_flag was set) is the new Green value.
      • Bit 1 (B_flag): If set (1), the next byte (after G if G_flag was set) is the new Blue value.
      • Bit 0 (A_flag): If set (1), the next byte (after B if B_flag was set) is the new Alpha value.
  • Action:
    • If R_flag is set, read 1 byte and set current_color.r to this value.
    • If G_flag is set, read 1 byte and set current_color.g to this value.
    • If B_flag is set, read 1 byte and set current_color.b to this value.
    • If A_flag is set, read 1 byte and set current_color.a to this value.
    • (Channels not flagged remain unchanged from the current_color before this operation).
    • The new current_color is output as the next pixel.
    • The new current_color is stored in color_array. (Using the hash function)