1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:37:44 +00:00

LibGfx: Add OpenType opcodes and helpers to parse instruction streams

This defines all the OpenType opcodes/instructions from the
specification:
https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions

Each instructions has mnemonic and a range of possible opcodes (as some
of the bits are pretty much immediate value flags).

There's a little helper Instruction struct for accessing the flags and
any associated data (in the case of PUSH instructions).

Then the InstructionStream provides a way of iterating over all the
instructions in some bytes.
This commit is contained in:
MacDue 2023-01-08 22:50:12 +00:00 committed by Andreas Kling
parent 652f87821b
commit 3b282ba8bb
3 changed files with 377 additions and 0 deletions

View file

@ -21,6 +21,7 @@ set(SOURCES
Font/OpenType/Cmap.cpp Font/OpenType/Cmap.cpp
Font/OpenType/Font.cpp Font/OpenType/Font.cpp
Font/OpenType/Glyf.cpp Font/OpenType/Glyf.cpp
Font/OpenType/Hinting/Opcodes.cpp
Font/PathRasterizer.cpp Font/PathRasterizer.cpp
Font/ScaledFont.cpp Font/ScaledFont.cpp
Font/Typeface.cpp Font/Typeface.cpp

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/IntegralMath.h>
#include <AK/ScopeGuard.h>
#include <LibGfx/Font/OpenType/Hinting/Opcodes.h>
namespace OpenType::Hinting {
StringView opcode_mnemonic(Opcode opcode)
{
switch (to_underlying(opcode)) {
#define __ENUMERATE_OPENTYPE_OPCODES(mnemonic, range_start, range_end) \
case range_start ... range_end: \
return #mnemonic##sv;
ENUMERATE_OPENTYPE_OPCODES
#undef __ENUMERATE_OPENTYPE_OPCODES
}
VERIFY_NOT_REACHED();
}
static constexpr u8 flag_bit_count(Opcode opcode)
{
switch (to_underlying(opcode)) {
#define __ENUMERATE_OPENTYPE_OPCODES(mnemonic, range_start, range_end) \
case range_start ... range_end: \
return AK::ceil_log2(range_end - range_start);
ENUMERATE_OPENTYPE_OPCODES
#undef __ENUMERATE_OPENTYPE_OPCODES
}
VERIFY_NOT_REACHED();
}
Instruction::Instruction(Opcode opcode, ReadonlyBytes values)
: m_opcode(opcode)
, m_values(values)
, m_flag_bits(flag_bit_count(opcode))
{
}
bool Instruction::a() const
{
return (to_underlying(m_opcode) >> (m_flag_bits - 1)) & 1;
}
bool Instruction::b() const
{
return (to_underlying(m_opcode) >> (m_flag_bits - 2)) & 1;
}
bool Instruction::c() const
{
return (to_underlying(m_opcode) >> (m_flag_bits - 3)) & 1;
}
bool Instruction::d() const
{
return (to_underlying(m_opcode) >> (m_flag_bits - 4)) & 1;
}
bool Instruction::e() const
{
return (to_underlying(m_opcode) >> (m_flag_bits - 5)) & 1;
}
void InstructionStream::process_next_instruction()
{
auto opcode = static_cast<Opcode>(next_byte());
auto& stream = *this;
m_handler.before_operation(stream, opcode);
ScopeGuard after = [&, this]() mutable {
m_handler.after_operation(stream, opcode);
};
// The PUSH instructions are handled specially as they take their values from the instruction stream.
switch (opcode) {
case Opcode::NPUSHB: {
auto n = next_byte();
auto values = take_n_bytes(n);
return m_handler.handle_NPUSHB({ { opcode, values }, stream });
}
case Opcode::NPUSHW: {
auto n = next_byte();
auto values = take_n_bytes(n * 2);
return m_handler.handle_NPUSHW({ { opcode, values }, stream });
}
case Opcode::PUSHB... Opcode::PUSHB_MAX: {
auto n = (to_underlying(opcode) & 0b111) + 1;
auto values = take_n_bytes(n);
return m_handler.handle_PUSHB({ { opcode, values }, stream });
}
case Opcode::PUSHW... Opcode::PUSHW_MAX: {
auto n = (to_underlying(opcode) & 0b111) + 1;
auto values = take_n_bytes(n * 2);
return m_handler.handle_PUSHB({ { opcode, values }, stream });
}
default:
break;
}
switch (to_underlying(opcode)) {
#define __ENUMERATE_OPENTYPE_OPCODES(mnemonic, range_start, range_end) \
case range_start ... range_end: \
return m_handler.handle_##mnemonic({ { opcode }, stream });
ENUMERATE_OPENTYPE_OPCODES
#undef __ENUMERATE_OPENTYPE_OPCODES
}
VERIFY_NOT_REACHED();
}
u8 InstructionStream::next_byte()
{
VERIFY(!at_end());
return m_bytes[m_byte_index++];
}
ReadonlyBytes InstructionStream::take_n_bytes(size_t n)
{
VERIFY(m_byte_index + n < m_bytes.size());
auto bytes = m_bytes.slice(m_byte_index, n);
m_byte_index += n;
return bytes;
}
bool InstructionStream::at_end() const
{
return m_byte_index >= m_bytes.size();
}
void InstructionStream::jump_to_next(Opcode)
{
TODO();
}
}

View file

@ -0,0 +1,240 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Span.h>
#include <AK/StringView.h>
namespace OpenType::Hinting {
#define ENUMERATE_OPENTYPE_OPCODES \
/* Pushing data onto the interpreter stack: */ \
__ENUMERATE_OPENTYPE_OPCODES(NPUSHB, 0x40, 0x40) \
__ENUMERATE_OPENTYPE_OPCODES(NPUSHW, 0x41, 0x41) \
__ENUMERATE_OPENTYPE_OPCODES(PUSHB, 0xB0, 0xB7) \
__ENUMERATE_OPENTYPE_OPCODES(PUSHW, 0xB8, 0xBF) \
/* Managing the Storage Area */ \
__ENUMERATE_OPENTYPE_OPCODES(RS, 0x43, 0x43) \
__ENUMERATE_OPENTYPE_OPCODES(WS, 0x42, 0x42) \
/* Managing the Control Value Table */ \
__ENUMERATE_OPENTYPE_OPCODES(WCVTP, 0x44, 0x44) \
__ENUMERATE_OPENTYPE_OPCODES(WCVTF, 0x70, 0x70) \
__ENUMERATE_OPENTYPE_OPCODES(RCVT, 0x45, 0x45) \
/* Managing the Graphics State */ \
__ENUMERATE_OPENTYPE_OPCODES(SVTCA, 0x00, 0x01) \
__ENUMERATE_OPENTYPE_OPCODES(SPVTCA, 0x02, 0x03) \
__ENUMERATE_OPENTYPE_OPCODES(SFVTCA, 0x04, 0x05) \
__ENUMERATE_OPENTYPE_OPCODES(SPVTL, 0x06, 0x07) \
__ENUMERATE_OPENTYPE_OPCODES(SFVTL, 0x08, 0x09) \
__ENUMERATE_OPENTYPE_OPCODES(SFVTPV, 0x0E, 0x0E) \
__ENUMERATE_OPENTYPE_OPCODES(SDPVTL, 0x86, 0x87) \
__ENUMERATE_OPENTYPE_OPCODES(SPVFS, 0x0A, 0x0A) \
__ENUMERATE_OPENTYPE_OPCODES(SFVFS, 0x0B, 0x0B) \
__ENUMERATE_OPENTYPE_OPCODES(GPV, 0x0C, 0x0C) \
__ENUMERATE_OPENTYPE_OPCODES(GFV, 0x0D, 0x0D) \
__ENUMERATE_OPENTYPE_OPCODES(SRP0, 0x10, 0x10) \
__ENUMERATE_OPENTYPE_OPCODES(SRP1, 0x11, 0x11) \
__ENUMERATE_OPENTYPE_OPCODES(SRP2, 0x12, 0x12) \
__ENUMERATE_OPENTYPE_OPCODES(SZP0, 0x13, 0x13) \
__ENUMERATE_OPENTYPE_OPCODES(SZP1, 0x14, 0x14) \
__ENUMERATE_OPENTYPE_OPCODES(SZP2, 0x15, 0x15) \
__ENUMERATE_OPENTYPE_OPCODES(SZPS, 0x16, 0x16) \
__ENUMERATE_OPENTYPE_OPCODES(RTHG, 0x19, 0x19) \
__ENUMERATE_OPENTYPE_OPCODES(RTG, 0x18, 0x18) \
__ENUMERATE_OPENTYPE_OPCODES(RTDG, 0x3D, 0x3D) \
__ENUMERATE_OPENTYPE_OPCODES(RDTG, 0x7D, 0x7D) \
__ENUMERATE_OPENTYPE_OPCODES(RUTG, 0x7C, 0x7C) \
__ENUMERATE_OPENTYPE_OPCODES(ROFF, 0x7A, 0x7A) \
__ENUMERATE_OPENTYPE_OPCODES(SROUND, 0x76, 0x76) \
__ENUMERATE_OPENTYPE_OPCODES(S45ROUND, 0x77, 0x77) \
__ENUMERATE_OPENTYPE_OPCODES(SLOOP, 0x17, 0x17) \
__ENUMERATE_OPENTYPE_OPCODES(SMD, 0x1A, 0x1A) \
__ENUMERATE_OPENTYPE_OPCODES(INSTCTRL, 0x8E, 0x8E) \
__ENUMERATE_OPENTYPE_OPCODES(SCANCTRL, 0x85, 0x85) \
__ENUMERATE_OPENTYPE_OPCODES(SCANTYPE, 0x8D, 0x8D) \
__ENUMERATE_OPENTYPE_OPCODES(SCVTCI, 0x1D, 0x1D) \
__ENUMERATE_OPENTYPE_OPCODES(SSWCI, 0x1E, 0x1E) \
__ENUMERATE_OPENTYPE_OPCODES(SSW, 0x1F, 0x1F) \
__ENUMERATE_OPENTYPE_OPCODES(FLIPON, 0x4D, 0x4D) \
__ENUMERATE_OPENTYPE_OPCODES(FLIPOFF, 0x4E, 0x4E) \
__ENUMERATE_OPENTYPE_OPCODES(SANGW, 0x7E, 0x7E) \
__ENUMERATE_OPENTYPE_OPCODES(SDB, 0x5E, 0x5E) \
__ENUMERATE_OPENTYPE_OPCODES(SDS, 0x5F, 0x5F) \
/* Reading and writing data */ \
__ENUMERATE_OPENTYPE_OPCODES(GC, 0x46, 0x47) \
__ENUMERATE_OPENTYPE_OPCODES(SCFS, 0x48, 0x48) \
__ENUMERATE_OPENTYPE_OPCODES(MD, 0x49, 0x4A) \
__ENUMERATE_OPENTYPE_OPCODES(MPPEM, 0x4B, 0x4B) \
__ENUMERATE_OPENTYPE_OPCODES(MPS, 0x4C, 0x4C) \
/* Managing outlines */ \
__ENUMERATE_OPENTYPE_OPCODES(FLIPPT, 0x80, 0x80) \
__ENUMERATE_OPENTYPE_OPCODES(FLIPRGON, 0x81, 0x81) \
__ENUMERATE_OPENTYPE_OPCODES(FLIPRGOFF, 0x82, 0x82) \
__ENUMERATE_OPENTYPE_OPCODES(SHP, 0x32, 0x33) \
__ENUMERATE_OPENTYPE_OPCODES(SHC, 0x34, 0x35) \
__ENUMERATE_OPENTYPE_OPCODES(SHZ, 0x36, 0x37) \
__ENUMERATE_OPENTYPE_OPCODES(SHPIX, 0x38, 0x38) \
__ENUMERATE_OPENTYPE_OPCODES(MSIRP, 0x3A, 0x3B) \
__ENUMERATE_OPENTYPE_OPCODES(MDAP, 0x2E, 0x2F) \
__ENUMERATE_OPENTYPE_OPCODES(MIAP, 0x3E, 0x3F) \
__ENUMERATE_OPENTYPE_OPCODES(MDRP, 0xC0, 0xDF) \
__ENUMERATE_OPENTYPE_OPCODES(MIRP, 0xE0, 0xFF) \
__ENUMERATE_OPENTYPE_OPCODES(ALIGNRP, 0x3C, 0x3C) \
__ENUMERATE_OPENTYPE_OPCODES(ISECT, 0x0F, 0x0F) \
__ENUMERATE_OPENTYPE_OPCODES(ALIGNPTS, 0x27, 0x27) \
__ENUMERATE_OPENTYPE_OPCODES(IP, 0x39, 0x39) \
__ENUMERATE_OPENTYPE_OPCODES(UTP, 0x29, 0x29) \
__ENUMERATE_OPENTYPE_OPCODES(IUP, 0x30, 0x31) \
/* Managing exceptions */ \
__ENUMERATE_OPENTYPE_OPCODES(DELTAP1, 0x5D, 0x5D) \
__ENUMERATE_OPENTYPE_OPCODES(DELTAP2, 0x71, 0x71) \
__ENUMERATE_OPENTYPE_OPCODES(DELTAP3, 0x72, 0x72) \
__ENUMERATE_OPENTYPE_OPCODES(DELTAC1, 0x73, 0x73) \
__ENUMERATE_OPENTYPE_OPCODES(DELTAC2, 0x74, 0x74) \
__ENUMERATE_OPENTYPE_OPCODES(DELTAC3, 0x75, 0x75) \
/* Managing the stack */ \
__ENUMERATE_OPENTYPE_OPCODES(DUP, 0x20, 0x20) \
__ENUMERATE_OPENTYPE_OPCODES(POP, 0x21, 0x21) \
__ENUMERATE_OPENTYPE_OPCODES(CLEAR, 0x22, 0x22) \
__ENUMERATE_OPENTYPE_OPCODES(SWAP, 0x23, 0x23) \
__ENUMERATE_OPENTYPE_OPCODES(DEPTH, 0x24, 0x24) \
__ENUMERATE_OPENTYPE_OPCODES(CINDEX, 0x25, 0x25) \
__ENUMERATE_OPENTYPE_OPCODES(MINDEX, 0x26, 0x26) \
__ENUMERATE_OPENTYPE_OPCODES(ROLL, 0x8a, 0x8a) \
/* Managing the flow of control */ \
__ENUMERATE_OPENTYPE_OPCODES(IF, 0x58, 0x58) \
__ENUMERATE_OPENTYPE_OPCODES(ELSE, 0x1B, 0x1B) \
__ENUMERATE_OPENTYPE_OPCODES(EIF, 0x59, 0x59) \
__ENUMERATE_OPENTYPE_OPCODES(JROT, 0x78, 0x78) \
__ENUMERATE_OPENTYPE_OPCODES(JMPR, 0x1C, 0x1C) \
__ENUMERATE_OPENTYPE_OPCODES(JROF, 0x79, 0x79) \
/* Logical functions */ \
__ENUMERATE_OPENTYPE_OPCODES(LT, 0x50, 0x50) \
__ENUMERATE_OPENTYPE_OPCODES(LTEQ, 0x51, 0x51) \
__ENUMERATE_OPENTYPE_OPCODES(GT, 0x52, 0x52) \
__ENUMERATE_OPENTYPE_OPCODES(GTEQ, 0x53, 0x53) \
__ENUMERATE_OPENTYPE_OPCODES(EQ, 0x54, 0x54) \
__ENUMERATE_OPENTYPE_OPCODES(NEQ, 0x55, 0x55) \
__ENUMERATE_OPENTYPE_OPCODES(ODD, 0x56, 0x56) \
__ENUMERATE_OPENTYPE_OPCODES(EVEN, 0x57, 0x57) \
__ENUMERATE_OPENTYPE_OPCODES(AND, 0x5A, 0x5A) \
__ENUMERATE_OPENTYPE_OPCODES(OR, 0x5B, 0x5B) \
__ENUMERATE_OPENTYPE_OPCODES(NOT, 0x5C, 0x5C) \
/* Arithmetic and math instructions */ \
__ENUMERATE_OPENTYPE_OPCODES(ADD, 0x60, 0x60) \
__ENUMERATE_OPENTYPE_OPCODES(SUB, 0x61, 0x61) \
__ENUMERATE_OPENTYPE_OPCODES(DIV, 0x62, 0x62) \
__ENUMERATE_OPENTYPE_OPCODES(MUL, 0x63, 0x63) \
__ENUMERATE_OPENTYPE_OPCODES(ABS, 0x64, 0x64) \
__ENUMERATE_OPENTYPE_OPCODES(NEG, 0x65, 0x65) \
__ENUMERATE_OPENTYPE_OPCODES(FLOOR, 0x66, 0x66) \
__ENUMERATE_OPENTYPE_OPCODES(CEILING, 0x67, 0x67) \
__ENUMERATE_OPENTYPE_OPCODES(MAX, 0x8B, 0x8B) \
__ENUMERATE_OPENTYPE_OPCODES(MIN, 0x8C, 0x8C) \
/* Compensating for the engine characteristics */ \
__ENUMERATE_OPENTYPE_OPCODES(ROUND, 0x68, 0x6B) \
__ENUMERATE_OPENTYPE_OPCODES(NROUND, 0x6C, 0x6F) \
/* Defining and using functions and instructions */ \
__ENUMERATE_OPENTYPE_OPCODES(FDEF, 0x2C, 0x2C) \
__ENUMERATE_OPENTYPE_OPCODES(ENDF, 0x2D, 0x2D) \
__ENUMERATE_OPENTYPE_OPCODES(CALL, 0x2B, 0x2B) \
__ENUMERATE_OPENTYPE_OPCODES(LOOPCALL, 0x2A, 0x2A) \
__ENUMERATE_OPENTYPE_OPCODES(IDEF, 0x89, 0x89) \
/* Debugging */ \
__ENUMERATE_OPENTYPE_OPCODES(DEBUG, 0x4F, 0x4F) \
/* Miscellaneous instructions */ \
__ENUMERATE_OPENTYPE_OPCODES(GETINFO, 0x88, 0x88) \
__ENUMERATE_OPENTYPE_OPCODES(GETVARIATION, 0x91, 0x91)
// https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions
enum class Opcode : u8 {
#define __ENUMERATE_OPENTYPE_OPCODES(mnemonic, range_start, range_end) \
mnemonic = range_start, \
mnemonic##_MAX = range_end,
ENUMERATE_OPENTYPE_OPCODES
#undef __ENUMERATE_OPENTYPE_OPCODES
};
struct Instruction {
bool a() const;
bool b() const;
bool c() const;
bool d() const;
bool e() const;
u8 flag_bits() const { return m_flag_bits; };
Opcode opcode() const { return m_opcode; }
ReadonlyBytes values() const { return m_values; };
Instruction(Opcode opcode, ReadonlyBytes values = {});
private:
Opcode m_opcode;
ReadonlyBytes m_values;
u8 m_flag_bits;
};
StringView opcode_mnemonic(Opcode);
struct InstructionHandler;
struct InstructionStream {
InstructionStream(InstructionHandler& handler, ReadonlyBytes bytes)
: m_handler { handler }
, m_bytes(bytes)
{
}
bool at_end() const;
void process_next_instruction();
void jump_to_next(Opcode);
size_t current_position() const { return m_byte_index; }
size_t length() const { return m_bytes.size(); }
struct Context {
Context(Instruction instruction, InstructionStream& stream)
: m_instruction(instruction)
, m_stream(stream)
{
}
Instruction instruction() { return m_instruction; }
InstructionStream& stream() { return m_stream; }
private:
Instruction m_instruction;
InstructionStream& m_stream;
};
private:
u8 next_byte();
ReadonlyBytes take_n_bytes(size_t n);
InstructionHandler& m_handler;
ReadonlyBytes m_bytes;
size_t m_byte_index { 0 };
};
struct InstructionHandler {
using Context = InstructionStream::Context;
virtual void default_handler(Context) = 0;
virtual void before_operation(InstructionStream&, Opcode) { }
virtual void after_operation(InstructionStream&, Opcode) { }
#define __ENUMERATE_OPENTYPE_OPCODES(mnemonic, _, __) \
virtual void handle_##mnemonic(Context context) \
{ \
default_handler(context); \
}
ENUMERATE_OPENTYPE_OPCODES
#undef __ENUMERATE_OPENTYPE_OPCODES
virtual ~InstructionHandler() = default;
};
}