mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-30 00:31:14 +00:00
LibGfx/JPEG: Add a JPEG encoder :^)
This encoder is very naive as it only output SOF0 images and uses pre-defined Huffman tables. There is also a small bug with quantization which make using it over-degrade the quality.
This commit is contained in:
parent
503720b574
commit
226b214142
Notes:
sideshowbarker
2024-07-17 08:55:54 +09:00
Author: https://github.com/LucasChollet Commit: https://github.com/SerenityOS/serenity/commit/226b214142 Pull-request: https://github.com/SerenityOS/serenity/pull/19519 Reviewed-by: https://github.com/gmta ✅ Reviewed-by: https://github.com/nico
|
@ -39,6 +39,7 @@ set(SOURCES
|
|||
ImageFormats/ICOLoader.cpp
|
||||
ImageFormats/ImageDecoder.cpp
|
||||
ImageFormats/JPEGLoader.cpp
|
||||
ImageFormats/JPEGWriter.cpp
|
||||
ImageFormats/PBMLoader.cpp
|
||||
ImageFormats/PGMLoader.cpp
|
||||
ImageFormats/PNGLoader.cpp
|
||||
|
|
501
Userland/Libraries/LibGfx/ImageFormats/JPEGWriter.cpp
Normal file
501
Userland/Libraries/LibGfx/ImageFormats/JPEGWriter.cpp
Normal file
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "JPEGWriter.h"
|
||||
#include "JPEGShared.h"
|
||||
#include "JPEGWriterTables.h"
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Function.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
namespace {
|
||||
|
||||
// This is basically a BigEndianOutputBitStream, the only difference
|
||||
// is that it appends 0x00 after each 0xFF when it writes bits.
|
||||
class JPEGBigEndianOutputBitStream : public Stream {
|
||||
public:
|
||||
explicit JPEGBigEndianOutputBitStream(Stream& stream)
|
||||
: m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ErrorOr<Bytes> read_some(Bytes) override
|
||||
{
|
||||
return Error::from_errno(EBADF);
|
||||
}
|
||||
|
||||
virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override
|
||||
{
|
||||
VERIFY(m_bit_offset == 0);
|
||||
return m_stream.write_some(bytes);
|
||||
}
|
||||
|
||||
template<Unsigned T>
|
||||
ErrorOr<void> write_bits(T value, size_t bit_count)
|
||||
{
|
||||
VERIFY(m_bit_offset <= 7);
|
||||
|
||||
while (bit_count > 0) {
|
||||
u8 const next_bit = (value >> (bit_count - 1)) & 1;
|
||||
bit_count--;
|
||||
|
||||
m_current_byte <<= 1;
|
||||
m_current_byte |= next_bit;
|
||||
m_bit_offset++;
|
||||
|
||||
if (m_bit_offset > 7) {
|
||||
TRY(m_stream.write_value(m_current_byte));
|
||||
if (m_current_byte == 0xFF)
|
||||
TRY(m_stream.write_value<u8>(0));
|
||||
|
||||
m_bit_offset = 0;
|
||||
m_current_byte = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool is_eof() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool is_open() const override
|
||||
{
|
||||
return m_stream.is_open();
|
||||
}
|
||||
|
||||
virtual void close() override
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> align_to_byte_boundary(u8 filler = 0x0)
|
||||
{
|
||||
if (m_bit_offset == 0)
|
||||
return {};
|
||||
|
||||
TRY(write_bits(filler, 8 - m_bit_offset));
|
||||
VERIFY(m_bit_offset == 0);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
Stream& m_stream;
|
||||
u8 m_current_byte { 0 };
|
||||
size_t m_bit_offset { 0 };
|
||||
};
|
||||
|
||||
class JPEGEncodingContext {
|
||||
public:
|
||||
JPEGEncodingContext(JPEGBigEndianOutputBitStream output_stream)
|
||||
: m_bit_stream(move(output_stream))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> initialize_mcu(Bitmap const& bitmap)
|
||||
{
|
||||
u64 const horizontal_macroblocks = bitmap.width() / 8 + (bitmap.width() % 8 == 0 ? 0 : 1);
|
||||
m_vertical_macroblocks = bitmap.height() / 8 + (bitmap.height() % 8 == 0 ? 0 : 1);
|
||||
|
||||
TRY(m_macroblocks.try_resize(horizontal_macroblocks * m_vertical_macroblocks));
|
||||
|
||||
for (u16 y {}; y < bitmap.height(); ++y) {
|
||||
u16 const vertical_macroblock_index = y / 8;
|
||||
u16 const vertical_pixel_offset = y - vertical_macroblock_index * 8;
|
||||
|
||||
for (u16 x {}; x < bitmap.width(); ++x) {
|
||||
u16 const horizontal_macroblock_index = x / 8;
|
||||
u16 const horizontal_pixel_offset = x - horizontal_macroblock_index * 8;
|
||||
|
||||
auto& macroblock = m_macroblocks[vertical_macroblock_index * horizontal_macroblocks + horizontal_macroblock_index];
|
||||
auto const pixel_offset = vertical_pixel_offset * 8 + horizontal_pixel_offset;
|
||||
|
||||
auto const original_pixel = bitmap.get_pixel(x, y);
|
||||
|
||||
// Conversion from YCbCr to RGB isn't specified in the first JPEG specification but in the JFIF extension:
|
||||
// See: https://www.itu.int/rec/dologin_pub.asp?lang=f&id=T-REC-T.871-201105-I!!PDF-E&type=items
|
||||
// 7 - Conversion to and from RGB
|
||||
auto const y_ = clamp(0.299 * original_pixel.red() + 0.587 * original_pixel.green() + 0.114 * original_pixel.blue(), 0, 255);
|
||||
auto const cb = clamp(-0.1687 * original_pixel.red() - 0.3313 * original_pixel.green() + 0.5 * original_pixel.blue() + 128, 0, 255);
|
||||
auto const cr = clamp(0.5 * original_pixel.red() - 0.4187 * original_pixel.green() - 0.0813 * original_pixel.blue() + 128, 0, 255);
|
||||
|
||||
// A.3.1 - Level shift
|
||||
macroblock.r[pixel_offset] = y_ - 128;
|
||||
macroblock.g[pixel_offset] = cb - 128;
|
||||
macroblock.b[pixel_offset] = cr - 128;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void fdct_and_quantization()
|
||||
{
|
||||
for (auto& macroblock : m_macroblocks) {
|
||||
constexpr double pi_over_16 = AK::Pi<double> / 16;
|
||||
constexpr double inverse_sqrt_2 = M_SQRT1_2;
|
||||
|
||||
auto const convert_one_component = [](i16 component[], QuantizationTable const& table) {
|
||||
Array<i16, 64> result {};
|
||||
|
||||
auto const sum_xy = [&component](u8 u, u8 v) {
|
||||
double sum {};
|
||||
for (u8 x {}; x < 8; ++x) {
|
||||
for (u8 y {}; y < 8; ++y)
|
||||
sum += component[x * 8 + y] * cos((2 * x + 1) * u * pi_over_16) * cos((2 * y + 1) * v * pi_over_16);
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
|
||||
for (u8 u {}; u < 7; ++u) {
|
||||
double const cu = u == 0 ? inverse_sqrt_2 : 1;
|
||||
for (u8 v {}; v < 7; ++v) {
|
||||
auto const table_index = u * 8 + v;
|
||||
|
||||
double const cv = v == 0 ? inverse_sqrt_2 : 1;
|
||||
|
||||
// A.3.3 - FDCT and IDCT
|
||||
double const fdct = cu * cv * sum_xy(u, v) / 4;
|
||||
|
||||
// A.3.4 - DCT coefficient quantization
|
||||
i16 const quantized = round(fdct / table.table[table_index]);
|
||||
|
||||
result[table_index] = quantized;
|
||||
}
|
||||
}
|
||||
|
||||
for (u8 i {}; i < result.size(); ++i)
|
||||
component[i] = result[i];
|
||||
};
|
||||
|
||||
convert_one_component(macroblock.y, m_luminance_quantization_table);
|
||||
convert_one_component(macroblock.cb, m_chrominance_quantization_table);
|
||||
convert_one_component(macroblock.cr, m_chrominance_quantization_table);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<void> write_huffman_stream()
|
||||
{
|
||||
for (auto& macroblock : m_macroblocks) {
|
||||
TRY(encode_dc(dc_luminance_huffman_table, macroblock.y, 0));
|
||||
TRY(encode_ac(ac_luminance_huffman_table, macroblock.y));
|
||||
|
||||
TRY(encode_dc(dc_chrominance_huffman_table, macroblock.cb, 1));
|
||||
TRY(encode_ac(ac_chrominance_huffman_table, macroblock.cb));
|
||||
|
||||
TRY(encode_dc(dc_chrominance_huffman_table, macroblock.cr, 2));
|
||||
TRY(encode_ac(ac_chrominance_huffman_table, macroblock.cr));
|
||||
}
|
||||
|
||||
TRY(m_bit_stream.align_to_byte_boundary(0xFF));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void set_luminance_quantization_table(QuantizationTable const& table, int quality)
|
||||
{
|
||||
set_quantization_table(m_luminance_quantization_table, table, quality);
|
||||
}
|
||||
|
||||
void set_chrominance_quantization_table(QuantizationTable const& table, int quality)
|
||||
{
|
||||
set_quantization_table(m_chrominance_quantization_table, table, quality);
|
||||
}
|
||||
|
||||
QuantizationTable const& luminance_quantization_table() const
|
||||
{
|
||||
return m_luminance_quantization_table;
|
||||
}
|
||||
|
||||
QuantizationTable const& chrominance_quantization_table() const
|
||||
{
|
||||
return m_chrominance_quantization_table;
|
||||
}
|
||||
|
||||
OutputHuffmanTable dc_luminance_huffman_table;
|
||||
OutputHuffmanTable dc_chrominance_huffman_table;
|
||||
|
||||
OutputHuffmanTable ac_luminance_huffman_table;
|
||||
OutputHuffmanTable ac_chrominance_huffman_table;
|
||||
|
||||
private:
|
||||
static void set_quantization_table(QuantizationTable& destination, QuantizationTable const& source, int quality)
|
||||
{
|
||||
// In order to be compatible with libjpeg-turbo, we use the same coefficients as them.
|
||||
|
||||
quality = clamp(quality, 1, 100);
|
||||
|
||||
if (quality < 50)
|
||||
quality = 5000 / quality;
|
||||
else
|
||||
quality = 200 - quality * 2;
|
||||
|
||||
destination = source;
|
||||
for (u8 i {}; i < 64; ++i) {
|
||||
auto const shifted_value = (destination.table[i] * quality + 50) / 100;
|
||||
destination.table[i] = clamp(shifted_value, 1, 255);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<void> write_symbol(OutputHuffmanTable::Symbol symbol)
|
||||
{
|
||||
return m_bit_stream.write_bits(symbol.word, symbol.code_length);
|
||||
};
|
||||
|
||||
ErrorOr<void> encode_dc(OutputHuffmanTable const& dc_table, i16 const component[], u8 component_id)
|
||||
{
|
||||
// F.1.2.1.3 - Huffman encoding procedures for DC coefficients
|
||||
auto diff = component[0] - m_last_dc_values[component_id];
|
||||
m_last_dc_values[component_id] = component[0];
|
||||
|
||||
auto const size = csize(diff);
|
||||
TRY(write_symbol(dc_table.from_input_byte(size)));
|
||||
|
||||
if (diff < 0)
|
||||
diff -= 1;
|
||||
|
||||
TRY(m_bit_stream.write_bits<u16>(diff, size));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> encode_ac(OutputHuffmanTable const& ac_table, i16 const component[])
|
||||
{
|
||||
{
|
||||
// F.2 - Procedure for sequential encoding of AC coefficients with Huffman coding
|
||||
u32 k {};
|
||||
u32 r {};
|
||||
|
||||
while (k < 63) {
|
||||
k++;
|
||||
|
||||
auto coefficient = component[zigzag_map[k]];
|
||||
if (coefficient == 0) {
|
||||
if (k == 63) {
|
||||
TRY(write_symbol(ac_table.from_input_byte(0x00)));
|
||||
break;
|
||||
}
|
||||
r += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (r > 15) {
|
||||
TRY(write_symbol(ac_table.from_input_byte(0xF0)));
|
||||
r -= 16;
|
||||
}
|
||||
|
||||
{
|
||||
// F.3 - Sequential encoding of a non-zero AC coefficient
|
||||
auto const ssss = csize(coefficient);
|
||||
auto const rs = (r << 4) + ssss;
|
||||
TRY(write_symbol(ac_table.from_input_byte(rs)));
|
||||
|
||||
if (coefficient < 0)
|
||||
coefficient -= 1;
|
||||
|
||||
TRY(m_bit_stream.write_bits<u16>(coefficient, ssss));
|
||||
}
|
||||
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static u8 csize(i16 coefficient)
|
||||
{
|
||||
VERIFY(coefficient >= -2047 && coefficient <= 2047);
|
||||
return floor(log2(abs(coefficient))) + 1;
|
||||
};
|
||||
|
||||
QuantizationTable m_luminance_quantization_table {};
|
||||
QuantizationTable m_chrominance_quantization_table {};
|
||||
|
||||
Vector<Macroblock> m_macroblocks {};
|
||||
Array<i16, 3> m_last_dc_values {};
|
||||
|
||||
u64 m_vertical_macroblocks {};
|
||||
|
||||
JPEGBigEndianOutputBitStream m_bit_stream;
|
||||
};
|
||||
|
||||
ErrorOr<void> add_start_of_image(Stream& stream)
|
||||
{
|
||||
TRY(stream.write_value<BigEndian<Marker>>(JPEG_SOI));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> add_end_of_image(Stream& stream)
|
||||
{
|
||||
TRY(stream.write_value<BigEndian<Marker>>(JPEG_EOI));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> add_frame_header(Stream& stream, JPEGEncodingContext const& context, Bitmap const& bitmap)
|
||||
{
|
||||
// B.2.2 - Frame header syntax
|
||||
TRY(stream.write_value<BigEndian<Marker>>(JPEG_SOF0));
|
||||
|
||||
// Lf = 8 + 3 × Nf, we only support a single image per frame so Nf = 3
|
||||
TRY(stream.write_value<BigEndian<u16>>(17));
|
||||
|
||||
// P
|
||||
TRY(stream.write_value<u8>(8));
|
||||
|
||||
// Y
|
||||
TRY(stream.write_value<BigEndian<u16>>(bitmap.height()));
|
||||
|
||||
// X
|
||||
TRY(stream.write_value<BigEndian<u16>>(bitmap.width()));
|
||||
|
||||
// Nf, as mentioned earlier, we only support Nf = 3
|
||||
TRY(stream.write_value<u8>(3));
|
||||
|
||||
// Encode 3 components
|
||||
for (u8 i {}; i < 3; ++i) {
|
||||
// Ci
|
||||
TRY(stream.write_value<u8>(i + 1));
|
||||
|
||||
// Hi and Vi
|
||||
TRY(stream.write_value<u8>((1 << 4) | 1));
|
||||
|
||||
// Tqi
|
||||
TRY(stream.write_value<u8>((i == 0 ? context.luminance_quantization_table() : context.chrominance_quantization_table()).id));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> add_quantization_table(Stream& stream, QuantizationTable const& table)
|
||||
{
|
||||
// B.2.4.1 - Quantization table-specification syntax
|
||||
TRY(stream.write_value<BigEndian<Marker>>(JPEG_DQT));
|
||||
|
||||
// Lq = 2 + 1 * 65
|
||||
TRY(stream.write_value<BigEndian<u16>>(2 + 65));
|
||||
|
||||
// Pq and Tq
|
||||
TRY(stream.write_value<u8>((0 << 4) | table.id));
|
||||
|
||||
for (auto coefficient : table.table)
|
||||
TRY(stream.write_value<u8>(coefficient));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<Vector<Vector<u8>, 16>> sort_symbols_per_size(OutputHuffmanTable const& table)
|
||||
{
|
||||
// JPEG only allows symbol with a size less than or equal to 16.
|
||||
Vector<Vector<u8>, 16> output {};
|
||||
TRY(output.try_resize(16));
|
||||
|
||||
for (auto const& symbol : table.table)
|
||||
TRY(output[symbol.code_length - 1].try_append(symbol.input_byte));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
ErrorOr<void> add_huffman_table(Stream& stream, OutputHuffmanTable const& table)
|
||||
{
|
||||
// B.2.4.2 - Huffman table-specification syntax
|
||||
TRY(stream.write_value<BigEndian<Marker>>(JPEG_DHT));
|
||||
|
||||
// Lh
|
||||
TRY(stream.write_value<BigEndian<u16>>(2 + 17 + table.table.size()));
|
||||
|
||||
// Tc and Th
|
||||
TRY(stream.write_value<u8>(table.id));
|
||||
|
||||
auto const vectorized_table = TRY(sort_symbols_per_size(table));
|
||||
for (auto const& symbol_vector : vectorized_table)
|
||||
TRY(stream.write_value<u8>(symbol_vector.size()));
|
||||
|
||||
for (auto const& symbol_vector : vectorized_table) {
|
||||
for (auto symbol : symbol_vector)
|
||||
TRY(stream.write_value<u8>(symbol));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> add_scan_header(Stream& stream)
|
||||
{
|
||||
// B.2.3 - Scan header syntax
|
||||
TRY(stream.write_value<BigEndian<Marker>>(JPEG_SOS));
|
||||
|
||||
// Ls - 6 + 2 × Ns
|
||||
TRY(stream.write_value<BigEndian<u16>>(6 + 2 * 3));
|
||||
|
||||
// Ns
|
||||
TRY(stream.write_value<u8>(3));
|
||||
|
||||
// Encode 3 components
|
||||
for (u8 i {}; i < 3; ++i) {
|
||||
// Csj
|
||||
TRY(stream.write_value<u8>(i + 1));
|
||||
|
||||
// Tdj and Taj
|
||||
// We're using 0 for luminance and 1 for chrominance
|
||||
u8 const huffman_identifier = i > 0 ? 1 : 0;
|
||||
TRY(stream.write_value<u8>((huffman_identifier << 4) | huffman_identifier));
|
||||
}
|
||||
|
||||
// Ss
|
||||
TRY(stream.write_value<u8>(0));
|
||||
|
||||
// Se
|
||||
TRY(stream.write_value<u8>(63));
|
||||
|
||||
// Ah and Al
|
||||
TRY(stream.write_value<u8>((0 << 4) | 0));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ErrorOr<void> JPEGWriter::encode(Stream& stream, Bitmap const& bitmap)
|
||||
{
|
||||
JPEGEncodingContext context { JPEGBigEndianOutputBitStream { stream } };
|
||||
|
||||
// FIXME: Let's take the quality as an option instead of hardcoding it
|
||||
// (there might also be a bug with quantization tables :^)).
|
||||
context.set_luminance_quantization_table(s_default_luminance_quantization_table, 100);
|
||||
context.set_chrominance_quantization_table(s_default_chrominance_quantization_table, 100);
|
||||
|
||||
context.dc_luminance_huffman_table = s_default_dc_luminance_huffman_table;
|
||||
context.dc_chrominance_huffman_table = s_default_dc_chrominance_huffman_table;
|
||||
|
||||
context.ac_luminance_huffman_table = s_default_ac_luminance_huffman_table;
|
||||
context.ac_chrominance_huffman_table = s_default_ac_chrominance_huffman_table;
|
||||
|
||||
TRY(add_start_of_image(stream));
|
||||
|
||||
TRY(add_frame_header(stream, context, bitmap));
|
||||
|
||||
TRY(add_quantization_table(stream, context.luminance_quantization_table()));
|
||||
TRY(add_quantization_table(stream, context.chrominance_quantization_table()));
|
||||
|
||||
TRY(add_huffman_table(stream, context.dc_luminance_huffman_table));
|
||||
TRY(add_huffman_table(stream, context.dc_chrominance_huffman_table));
|
||||
TRY(add_huffman_table(stream, context.ac_luminance_huffman_table));
|
||||
TRY(add_huffman_table(stream, context.ac_chrominance_huffman_table));
|
||||
|
||||
TRY(add_scan_header(stream));
|
||||
|
||||
TRY(context.initialize_mcu(bitmap));
|
||||
context.fdct_and_quantization();
|
||||
|
||||
TRY(context.write_huffman_stream());
|
||||
|
||||
TRY(add_end_of_image(stream));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
22
Userland/Libraries/LibGfx/ImageFormats/JPEGWriter.h
Normal file
22
Userland/Libraries/LibGfx/ImageFormats/JPEGWriter.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
class JPEGWriter {
|
||||
public:
|
||||
static ErrorOr<void> encode(Stream&, Bitmap const&);
|
||||
|
||||
private:
|
||||
JPEGWriter() = delete;
|
||||
};
|
||||
|
||||
}
|
457
Userland/Libraries/LibGfx/ImageFormats/JPEGWriterTables.h
Normal file
457
Userland/Libraries/LibGfx/ImageFormats/JPEGWriterTables.h
Normal file
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
struct QuantizationTable {
|
||||
Array<u8, 64> table {};
|
||||
u8 id {};
|
||||
};
|
||||
|
||||
// K.1 - Quantization tables for luminance and chrominance components
|
||||
|
||||
// clang-format off
|
||||
constexpr static QuantizationTable s_default_luminance_quantization_table {
|
||||
.table = {
|
||||
16, 11, 10, 16, 124, 140, 151, 161,
|
||||
12, 12, 14, 19, 126, 158, 160, 155,
|
||||
14, 13, 16, 24, 140, 157, 169, 156,
|
||||
14, 17, 22, 29, 151, 187, 180, 162,
|
||||
18, 22, 37, 56, 168, 109, 103, 177,
|
||||
24, 35, 55, 64, 181, 104, 113, 192,
|
||||
49, 64, 78, 87, 103, 121, 120, 101,
|
||||
72, 92, 95, 98, 112, 100, 103, 199,
|
||||
},
|
||||
.id = 0,
|
||||
};
|
||||
|
||||
constexpr static QuantizationTable s_default_chrominance_quantization_table {
|
||||
.table = {
|
||||
17, 18, 24, 47, 99, 99, 99, 99,
|
||||
18, 21, 26, 66, 99, 99, 99, 99,
|
||||
24, 26, 56, 99, 99, 99, 99, 99,
|
||||
47, 66, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
},
|
||||
.id = 1,
|
||||
};
|
||||
|
||||
constexpr static QuantizationTable s_dummy_quantization_table {
|
||||
.table = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
},
|
||||
.id = 1,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
struct OutputHuffmanTable {
|
||||
struct Symbol {
|
||||
u8 input_byte {};
|
||||
u8 code_length {};
|
||||
u16 word {};
|
||||
};
|
||||
|
||||
Symbol from_input_byte(u8 input_byte) const
|
||||
{
|
||||
for (auto symbol : table) {
|
||||
if (symbol.input_byte == input_byte)
|
||||
return symbol;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
Vector<Symbol, 16> table {};
|
||||
u8 id {};
|
||||
};
|
||||
|
||||
static OutputHuffmanTable s_default_dc_luminance_huffman_table {
|
||||
.table = {
|
||||
{ 0, 2, 0b00 },
|
||||
{ 1, 3, 0b010 },
|
||||
{ 2, 3, 0b011 },
|
||||
{ 3, 3, 0b100 },
|
||||
{ 4, 3, 0b101 },
|
||||
{ 5, 3, 0b110 },
|
||||
{ 6, 4, 0b1110 },
|
||||
{ 7, 5, 0b11110 },
|
||||
{ 8, 6, 0b111110 },
|
||||
{ 9, 7, 0b1111110 },
|
||||
{ 10, 8, 0b11111110 },
|
||||
{ 11, 9, 0b111111110 },
|
||||
},
|
||||
.id = (0 << 4) | 0,
|
||||
};
|
||||
|
||||
static OutputHuffmanTable s_default_dc_chrominance_huffman_table {
|
||||
.table = {
|
||||
{ 0, 2, 0b00 },
|
||||
{ 1, 2, 0b01 },
|
||||
{ 2, 2, 0b10 },
|
||||
{ 3, 3, 0b110 },
|
||||
{ 4, 4, 0b1110 },
|
||||
{ 5, 5, 0b11110 },
|
||||
{ 6, 6, 0b111110 },
|
||||
{ 7, 7, 0b1111110 },
|
||||
{ 8, 8, 0b11111110 },
|
||||
{ 9, 9, 0b111111110 },
|
||||
{ 10, 10, 0b1111111110 },
|
||||
{ 11, 11, 0b11111111110 },
|
||||
},
|
||||
.id = (0 << 4) | 1,
|
||||
};
|
||||
|
||||
static OutputHuffmanTable s_default_ac_luminance_huffman_table {
|
||||
.table = {
|
||||
{ 0x01, 2, 0b00 },
|
||||
{ 0x02, 2, 0b01 },
|
||||
{ 0x03, 3, 0b100 },
|
||||
{ 0x00, 4, 0b1010 },
|
||||
{ 0x04, 4, 0b1011 },
|
||||
{ 0x11, 4, 0b1100 },
|
||||
{ 0x05, 5, 0b11010 },
|
||||
{ 0x12, 5, 0b11011 },
|
||||
{ 0x21, 5, 0b11100 },
|
||||
{ 0x31, 6, 0b111010 },
|
||||
{ 0x41, 6, 0b111011 },
|
||||
{ 0x06, 7, 0b1111000 },
|
||||
{ 0x13, 7, 0b1111001 },
|
||||
{ 0x51, 7, 0b1111010 },
|
||||
{ 0x61, 7, 0b1111011 },
|
||||
{ 0x07, 8, 0b11111000 },
|
||||
{ 0x22, 8, 0b11111001 },
|
||||
{ 0x71, 8, 0b11111010 },
|
||||
{ 0x14, 9, 0b111110110 },
|
||||
{ 0x32, 9, 0b111110111 },
|
||||
{ 0x81, 9, 0b111111000 },
|
||||
{ 0x91, 9, 0b111111001 },
|
||||
{ 0xA1, 9, 0b111111010 },
|
||||
{ 0x08, 10, 0b1111110110 },
|
||||
{ 0x23, 10, 0b1111110111 },
|
||||
{ 0x42, 10, 0b1111111000 },
|
||||
{ 0xB1, 10, 0b1111111001 },
|
||||
{ 0xC1, 10, 0b1111111010 },
|
||||
{ 0x15, 11, 0b11111110110 },
|
||||
{ 0x52, 11, 0b11111110111 },
|
||||
{ 0xD1, 11, 0b11111111000 },
|
||||
{ 0xF0, 11, 0b11111111001 },
|
||||
{ 0x24, 12, 0b111111110100 },
|
||||
{ 0x33, 12, 0b111111110101 },
|
||||
{ 0x62, 12, 0b111111110110 },
|
||||
{ 0x72, 12, 0b111111110111 },
|
||||
{ 0x82, 15, 0b111111111000000 },
|
||||
{ 0x09, 16, 0b1111111110000010 },
|
||||
{ 0x0A, 16, 0b1111111110000011 },
|
||||
{ 0x16, 16, 0b1111111110000100 },
|
||||
{ 0x17, 16, 0b1111111110000101 },
|
||||
{ 0x18, 16, 0b1111111110000110 },
|
||||
{ 0x19, 16, 0b1111111110000111 },
|
||||
{ 0x1A, 16, 0b1111111110001000 },
|
||||
{ 0x25, 16, 0b1111111110001001 },
|
||||
{ 0x26, 16, 0b1111111110001010 },
|
||||
{ 0x27, 16, 0b1111111110001011 },
|
||||
{ 0x28, 16, 0b1111111110001100 },
|
||||
{ 0x29, 16, 0b1111111110001101 },
|
||||
{ 0x2A, 16, 0b1111111110001110 },
|
||||
{ 0x34, 16, 0b1111111110001111 },
|
||||
{ 0x35, 16, 0b1111111110010000 },
|
||||
{ 0x36, 16, 0b1111111110010001 },
|
||||
{ 0x37, 16, 0b1111111110010010 },
|
||||
{ 0x38, 16, 0b1111111110010011 },
|
||||
{ 0x39, 16, 0b1111111110010100 },
|
||||
{ 0x3A, 16, 0b1111111110010101 },
|
||||
{ 0x43, 16, 0b1111111110010110 },
|
||||
{ 0x44, 16, 0b1111111110010111 },
|
||||
{ 0x45, 16, 0b1111111110011000 },
|
||||
{ 0x46, 16, 0b1111111110011001 },
|
||||
{ 0x47, 16, 0b1111111110011010 },
|
||||
{ 0x48, 16, 0b1111111110011011 },
|
||||
{ 0x49, 16, 0b1111111110011100 },
|
||||
{ 0x4A, 16, 0b1111111110011101 },
|
||||
{ 0x53, 16, 0b1111111110011110 },
|
||||
{ 0x54, 16, 0b1111111110011111 },
|
||||
{ 0x55, 16, 0b1111111110100000 },
|
||||
{ 0x56, 16, 0b1111111110100001 },
|
||||
{ 0x57, 16, 0b1111111110100010 },
|
||||
{ 0x58, 16, 0b1111111110100011 },
|
||||
{ 0x59, 16, 0b1111111110100100 },
|
||||
{ 0x5A, 16, 0b1111111110100101 },
|
||||
{ 0x63, 16, 0b1111111110100110 },
|
||||
{ 0x64, 16, 0b1111111110100111 },
|
||||
{ 0x65, 16, 0b1111111110101000 },
|
||||
{ 0x66, 16, 0b1111111110101001 },
|
||||
{ 0x67, 16, 0b1111111110101010 },
|
||||
{ 0x68, 16, 0b1111111110101011 },
|
||||
{ 0x69, 16, 0b1111111110101100 },
|
||||
{ 0x6A, 16, 0b1111111110101101 },
|
||||
{ 0x73, 16, 0b1111111110101110 },
|
||||
{ 0x74, 16, 0b1111111110101111 },
|
||||
{ 0x75, 16, 0b1111111110110000 },
|
||||
{ 0x76, 16, 0b1111111110110001 },
|
||||
{ 0x77, 16, 0b1111111110110010 },
|
||||
{ 0x78, 16, 0b1111111110110011 },
|
||||
{ 0x79, 16, 0b1111111110110100 },
|
||||
{ 0x7A, 16, 0b1111111110110101 },
|
||||
{ 0x83, 16, 0b1111111110110110 },
|
||||
{ 0x84, 16, 0b1111111110110111 },
|
||||
{ 0x85, 16, 0b1111111110111000 },
|
||||
{ 0x86, 16, 0b1111111110111001 },
|
||||
{ 0x87, 16, 0b1111111110111010 },
|
||||
{ 0x88, 16, 0b1111111110111011 },
|
||||
{ 0x89, 16, 0b1111111110111100 },
|
||||
{ 0x8A, 16, 0b1111111110111101 },
|
||||
{ 0x92, 16, 0b1111111110111110 },
|
||||
{ 0x93, 16, 0b1111111110111111 },
|
||||
{ 0x94, 16, 0b1111111111000000 },
|
||||
{ 0x95, 16, 0b1111111111000001 },
|
||||
{ 0x96, 16, 0b1111111111000010 },
|
||||
{ 0x97, 16, 0b1111111111000011 },
|
||||
{ 0x98, 16, 0b1111111111000100 },
|
||||
{ 0x99, 16, 0b1111111111000101 },
|
||||
{ 0x9A, 16, 0b1111111111000110 },
|
||||
{ 0xA2, 16, 0b1111111111000111 },
|
||||
{ 0xA3, 16, 0b1111111111001000 },
|
||||
{ 0xA4, 16, 0b1111111111001001 },
|
||||
{ 0xA5, 16, 0b1111111111001010 },
|
||||
{ 0xA6, 16, 0b1111111111001011 },
|
||||
{ 0xA7, 16, 0b1111111111001100 },
|
||||
{ 0xA8, 16, 0b1111111111001101 },
|
||||
{ 0xA9, 16, 0b1111111111001110 },
|
||||
{ 0xAA, 16, 0b1111111111001111 },
|
||||
{ 0xB2, 16, 0b1111111111010000 },
|
||||
{ 0xB3, 16, 0b1111111111010001 },
|
||||
{ 0xB4, 16, 0b1111111111010010 },
|
||||
{ 0xB5, 16, 0b1111111111010011 },
|
||||
{ 0xB6, 16, 0b1111111111010100 },
|
||||
{ 0xB7, 16, 0b1111111111010101 },
|
||||
{ 0xB8, 16, 0b1111111111010110 },
|
||||
{ 0xB9, 16, 0b1111111111010111 },
|
||||
{ 0xBA, 16, 0b1111111111011000 },
|
||||
{ 0xC2, 16, 0b1111111111011001 },
|
||||
{ 0xC3, 16, 0b1111111111011010 },
|
||||
{ 0xC4, 16, 0b1111111111011011 },
|
||||
{ 0xC5, 16, 0b1111111111011100 },
|
||||
{ 0xC6, 16, 0b1111111111011101 },
|
||||
{ 0xC7, 16, 0b1111111111011110 },
|
||||
{ 0xC8, 16, 0b1111111111011111 },
|
||||
{ 0xC9, 16, 0b1111111111100000 },
|
||||
{ 0xCA, 16, 0b1111111111100001 },
|
||||
{ 0xD2, 16, 0b1111111111100010 },
|
||||
{ 0xD3, 16, 0b1111111111100011 },
|
||||
{ 0xD4, 16, 0b1111111111100100 },
|
||||
{ 0xD5, 16, 0b1111111111100101 },
|
||||
{ 0xD6, 16, 0b1111111111100110 },
|
||||
{ 0xD7, 16, 0b1111111111100111 },
|
||||
{ 0xD8, 16, 0b1111111111101000 },
|
||||
{ 0xD9, 16, 0b1111111111101001 },
|
||||
{ 0xDA, 16, 0b1111111111101010 },
|
||||
{ 0xE1, 16, 0b1111111111101011 },
|
||||
{ 0xE2, 16, 0b1111111111101100 },
|
||||
{ 0xE3, 16, 0b1111111111101101 },
|
||||
{ 0xE4, 16, 0b1111111111101110 },
|
||||
{ 0xE5, 16, 0b1111111111101111 },
|
||||
{ 0xE6, 16, 0b1111111111110000 },
|
||||
{ 0xE7, 16, 0b1111111111110001 },
|
||||
{ 0xE8, 16, 0b1111111111110010 },
|
||||
{ 0xE9, 16, 0b1111111111110011 },
|
||||
{ 0xEA, 16, 0b1111111111110100 },
|
||||
{ 0xF1, 16, 0b1111111111110101 },
|
||||
{ 0xF2, 16, 0b1111111111110110 },
|
||||
{ 0xF3, 16, 0b1111111111110111 },
|
||||
{ 0xF4, 16, 0b1111111111111000 },
|
||||
{ 0xF5, 16, 0b1111111111111001 },
|
||||
{ 0xF6, 16, 0b1111111111111010 },
|
||||
{ 0xF7, 16, 0b1111111111111011 },
|
||||
{ 0xF8, 16, 0b1111111111111100 },
|
||||
{ 0xF9, 16, 0b1111111111111101 },
|
||||
{ 0xFA, 16, 0b1111111111111110 },
|
||||
},
|
||||
.id = (1 << 4) | 0,
|
||||
};
|
||||
|
||||
static OutputHuffmanTable s_default_ac_chrominance_huffman_table {
|
||||
.table = {
|
||||
{ 0x00, 2, 0b00 },
|
||||
{ 0x01, 2, 0b01 },
|
||||
{ 0x02, 3, 0b100 },
|
||||
{ 0x03, 4, 0b1010 },
|
||||
{ 0x11, 4, 0b1011 },
|
||||
{ 0x04, 5, 0b11000 },
|
||||
{ 0x05, 5, 0b11001 },
|
||||
{ 0x21, 5, 0b11010 },
|
||||
{ 0x31, 5, 0b11011 },
|
||||
{ 0x06, 6, 0b111000 },
|
||||
{ 0x12, 6, 0b111001 },
|
||||
{ 0x41, 6, 0b111010 },
|
||||
{ 0x51, 6, 0b111011 },
|
||||
{ 0x07, 7, 0b1111000 },
|
||||
{ 0x61, 7, 0b1111001 },
|
||||
{ 0x71, 7, 0b1111010 },
|
||||
{ 0x13, 8, 0b11110110 },
|
||||
{ 0x22, 8, 0b11110111 },
|
||||
{ 0x32, 8, 0b11111000 },
|
||||
{ 0x81, 8, 0b11111001 },
|
||||
{ 0x08, 9, 0b111110100 },
|
||||
{ 0x14, 9, 0b111110101 },
|
||||
{ 0x42, 9, 0b111110110 },
|
||||
{ 0x91, 9, 0b111110111 },
|
||||
{ 0xA1, 9, 0b111111000 },
|
||||
{ 0xB1, 9, 0b111111001 },
|
||||
{ 0xC1, 9, 0b111111010 },
|
||||
{ 0x09, 10, 0b1111110110 },
|
||||
{ 0x23, 10, 0b1111110111 },
|
||||
{ 0x33, 10, 0b1111111000 },
|
||||
{ 0x52, 10, 0b1111111001 },
|
||||
{ 0xF0, 10, 0b1111111010 },
|
||||
{ 0x15, 11, 0b11111110110 },
|
||||
{ 0x62, 11, 0b11111110111 },
|
||||
{ 0x72, 11, 0b11111111000 },
|
||||
{ 0xD1, 11, 0b11111111001 },
|
||||
{ 0x0A, 12, 0b111111110100 },
|
||||
{ 0x16, 12, 0b111111110101 },
|
||||
{ 0x24, 12, 0b111111110110 },
|
||||
{ 0x34, 12, 0b111111110111 },
|
||||
{ 0xE1, 14, 0b11111111100000 },
|
||||
{ 0x25, 15, 0b111111111000010 },
|
||||
{ 0xF1, 15, 0b111111111000011 },
|
||||
{ 0x17, 16, 0b1111111110001000 },
|
||||
{ 0x18, 16, 0b1111111110001001 },
|
||||
{ 0x19, 16, 0b1111111110001010 },
|
||||
{ 0x1A, 16, 0b1111111110001011 },
|
||||
{ 0x26, 16, 0b1111111110001100 },
|
||||
{ 0x27, 16, 0b1111111110001101 },
|
||||
{ 0x28, 16, 0b1111111110001110 },
|
||||
{ 0x29, 16, 0b1111111110001111 },
|
||||
{ 0x2A, 16, 0b1111111110010000 },
|
||||
{ 0x35, 16, 0b1111111110010001 },
|
||||
{ 0x36, 16, 0b1111111110010010 },
|
||||
{ 0x37, 16, 0b1111111110010011 },
|
||||
{ 0x38, 16, 0b1111111110010100 },
|
||||
{ 0x39, 16, 0b1111111110010101 },
|
||||
{ 0x3A, 16, 0b1111111110010110 },
|
||||
{ 0x43, 16, 0b1111111110010111 },
|
||||
{ 0x44, 16, 0b1111111110011000 },
|
||||
{ 0x45, 16, 0b1111111110011001 },
|
||||
{ 0x46, 16, 0b1111111110011010 },
|
||||
{ 0x47, 16, 0b1111111110011011 },
|
||||
{ 0x48, 16, 0b1111111110011100 },
|
||||
{ 0x49, 16, 0b1111111110011101 },
|
||||
{ 0x4A, 16, 0b1111111110011110 },
|
||||
{ 0x53, 16, 0b1111111110011111 },
|
||||
{ 0x54, 16, 0b1111111110100000 },
|
||||
{ 0x55, 16, 0b1111111110100001 },
|
||||
{ 0x56, 16, 0b1111111110100010 },
|
||||
{ 0x57, 16, 0b1111111110100011 },
|
||||
{ 0x58, 16, 0b1111111110100100 },
|
||||
{ 0x59, 16, 0b1111111110100101 },
|
||||
{ 0x5A, 16, 0b1111111110100110 },
|
||||
{ 0x63, 16, 0b1111111110100111 },
|
||||
{ 0x64, 16, 0b1111111110101000 },
|
||||
{ 0x65, 16, 0b1111111110101001 },
|
||||
{ 0x66, 16, 0b1111111110101010 },
|
||||
{ 0x67, 16, 0b1111111110101011 },
|
||||
{ 0x68, 16, 0b1111111110101100 },
|
||||
{ 0x69, 16, 0b1111111110101101 },
|
||||
{ 0x6A, 16, 0b1111111110101110 },
|
||||
{ 0x73, 16, 0b1111111110101111 },
|
||||
{ 0x74, 16, 0b1111111110110000 },
|
||||
{ 0x75, 16, 0b1111111110110001 },
|
||||
{ 0x76, 16, 0b1111111110110010 },
|
||||
{ 0x77, 16, 0b1111111110110011 },
|
||||
{ 0x78, 16, 0b1111111110110100 },
|
||||
{ 0x79, 16, 0b1111111110110101 },
|
||||
{ 0x7A, 16, 0b1111111110110110 },
|
||||
{ 0x82, 16, 0b1111111110110111 },
|
||||
{ 0x83, 16, 0b1111111110111000 },
|
||||
{ 0x84, 16, 0b1111111110111001 },
|
||||
{ 0x85, 16, 0b1111111110111010 },
|
||||
{ 0x86, 16, 0b1111111110111011 },
|
||||
{ 0x87, 16, 0b1111111110111100 },
|
||||
{ 0x88, 16, 0b1111111110111101 },
|
||||
{ 0x89, 16, 0b1111111110111110 },
|
||||
{ 0x8A, 16, 0b1111111110111111 },
|
||||
{ 0x92, 16, 0b1111111111000000 },
|
||||
{ 0x93, 16, 0b1111111111000001 },
|
||||
{ 0x94, 16, 0b1111111111000010 },
|
||||
{ 0x95, 16, 0b1111111111000011 },
|
||||
{ 0x96, 16, 0b1111111111000100 },
|
||||
{ 0x97, 16, 0b1111111111000101 },
|
||||
{ 0x98, 16, 0b1111111111000110 },
|
||||
{ 0x99, 16, 0b1111111111000111 },
|
||||
{ 0x9A, 16, 0b1111111111001000 },
|
||||
{ 0xA2, 16, 0b1111111111001001 },
|
||||
{ 0xA3, 16, 0b1111111111001010 },
|
||||
{ 0xA4, 16, 0b1111111111001011 },
|
||||
{ 0xA5, 16, 0b1111111111001100 },
|
||||
{ 0xA6, 16, 0b1111111111001101 },
|
||||
{ 0xA7, 16, 0b1111111111001110 },
|
||||
{ 0xA8, 16, 0b1111111111001111 },
|
||||
{ 0xA9, 16, 0b1111111111010000 },
|
||||
{ 0xAA, 16, 0b1111111111010001 },
|
||||
{ 0xB2, 16, 0b1111111111010010 },
|
||||
{ 0xB3, 16, 0b1111111111010011 },
|
||||
{ 0xB4, 16, 0b1111111111010100 },
|
||||
{ 0xB5, 16, 0b1111111111010101 },
|
||||
{ 0xB6, 16, 0b1111111111010110 },
|
||||
{ 0xB7, 16, 0b1111111111010111 },
|
||||
{ 0xB8, 16, 0b1111111111011000 },
|
||||
{ 0xB9, 16, 0b1111111111011001 },
|
||||
{ 0xBA, 16, 0b1111111111011010 },
|
||||
{ 0xC2, 16, 0b1111111111011011 },
|
||||
{ 0xC3, 16, 0b1111111111011100 },
|
||||
{ 0xC4, 16, 0b1111111111011101 },
|
||||
{ 0xC5, 16, 0b1111111111011110 },
|
||||
{ 0xC6, 16, 0b1111111111011111 },
|
||||
{ 0xC7, 16, 0b1111111111100000 },
|
||||
{ 0xC8, 16, 0b1111111111100001 },
|
||||
{ 0xC9, 16, 0b1111111111100010 },
|
||||
{ 0xCA, 16, 0b1111111111100011 },
|
||||
{ 0xD2, 16, 0b1111111111100100 },
|
||||
{ 0xD3, 16, 0b1111111111100101 },
|
||||
{ 0xD4, 16, 0b1111111111100110 },
|
||||
{ 0xD5, 16, 0b1111111111100111 },
|
||||
{ 0xD6, 16, 0b1111111111101000 },
|
||||
{ 0xD7, 16, 0b1111111111101001 },
|
||||
{ 0xD8, 16, 0b1111111111101010 },
|
||||
{ 0xD9, 16, 0b1111111111101011 },
|
||||
{ 0xDA, 16, 0b1111111111101100 },
|
||||
{ 0xE2, 16, 0b1111111111101101 },
|
||||
{ 0xE3, 16, 0b1111111111101110 },
|
||||
{ 0xE4, 16, 0b1111111111101111 },
|
||||
{ 0xE5, 16, 0b1111111111110000 },
|
||||
{ 0xE6, 16, 0b1111111111110001 },
|
||||
{ 0xE7, 16, 0b1111111111110010 },
|
||||
{ 0xE8, 16, 0b1111111111110011 },
|
||||
{ 0xE9, 16, 0b1111111111110100 },
|
||||
{ 0xEA, 16, 0b1111111111110101 },
|
||||
{ 0xF2, 16, 0b1111111111110110 },
|
||||
{ 0xF3, 16, 0b1111111111110111 },
|
||||
{ 0xF4, 16, 0b1111111111111000 },
|
||||
{ 0xF5, 16, 0b1111111111111001 },
|
||||
{ 0xF6, 16, 0b1111111111111010 },
|
||||
{ 0xF7, 16, 0b1111111111111011 },
|
||||
{ 0xF8, 16, 0b1111111111111100 },
|
||||
{ 0xF9, 16, 0b1111111111111101 },
|
||||
{ 0xFA, 16, 0b1111111111111110 },
|
||||
},
|
||||
.id = (1 << 4) | 1,
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue