code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __RASM_H
#define __RASM_H

#include "compat.h"
#include "legacy_rasm.h"

#ifdef CONFIG_DEBYTECODE

#include "data.h"

// I really didn't want to have all this stuff in the header file, but exposing
// tokens to the outside world dragged it all in.

// The argument type both specifies what it is lexically and what it is
// functionally. The functionality is a finer grained specification than
// the lexical type. For instance, any "name" token will match any of the
// arg types ending in "NAME", but the type of name could affect what kind
// of further compile time checking is done (can that name be available,
// for instance) and it can affect what it is compiled to.

// A command's parameters can accept any one of the following parameter
// types, but in practice it'll use a few common groupings (basically,
// anything that will resolve to the same type, IE immediate and
// counter_load_name). The exceptions to this are for "ignore" and
// "fragment", which use the lower bits to determine a specific type
// that has to be matched.

// These are listed in order of priority. What that means is that if a
// token can ambiguously be multiple kinds of arguments then it will
// match in the order here from top to bottom. In practice I don't
// think that much ambiguity will be possible, but for instance something
// like "c00" will always be a color before it is a counter load name.

enum arg_type
{
  ARG_TYPE_IMMEDIATE          = (1 << 0),
  ARG_TYPE_STRING             = (1 << 1),
  ARG_TYPE_CHARACTER          = (1 << 2),
  ARG_TYPE_COLOR              = (1 << 3),
  ARG_TYPE_PARAM              = (1 << 4),
  ARG_TYPE_COUNTER_LOAD_NAME  = (1 << 5),
  ARG_TYPE_COUNTER_STORE_NAME = (1 << 6),
  ARG_TYPE_LABEL_NAME         = (1 << 7),
  ARG_TYPE_EXT_LABEL_NAME     = (1 << 8),
  ARG_TYPE_BOARD_NAME         = (1 << 9),
  ARG_TYPE_ROBOT_NAME         = (1 << 10),
  ARG_TYPE_DIRECTION          = (1 << 11),
  ARG_TYPE_THING              = (1 << 12),
  ARG_TYPE_EQUALITY           = (1 << 13),
  ARG_TYPE_CONDITION          = (1 << 14),
  ARG_TYPE_ITEM               = (1 << 15),
  ARG_TYPE_UNKNOWN            = (1 << 16),

  ARG_TYPE_IGNORE             = (1 << 30),
  ARG_TYPE_FRAGMENT           = (1 << 31),
};

enum arg_type_indexed
{
  ARG_TYPE_INDEXED_IMMEDIATE,
  ARG_TYPE_INDEXED_STRING,
  ARG_TYPE_INDEXED_CHARACTER,
  ARG_TYPE_INDEXED_COLOR,
  ARG_TYPE_INDEXED_PARAM,
  ARG_TYPE_INDEXED_NAME,
  ARG_TYPE_INDEXED_DIRECTION,
  ARG_TYPE_INDEXED_THING,
  ARG_TYPE_INDEXED_EQUALITY,
  ARG_TYPE_INDEXED_CONDITION,
  ARG_TYPE_INDEXED_ITEM,
  ARG_TYPE_INDEXED_IGNORE,
  ARG_TYPE_INDEXED_FRAGMENT,
  ARG_TYPE_INDEXED_COMMAND,
  ARG_TYPE_INDEXED_EXPRESSION,
  ARG_TYPE_INDEXED_LABEL,
  ARG_TYPE_INDEXED_COMMENT,
};

enum token_type
{
  TOKEN_TYPE_NUMERIC_LITERAL_BASE10,
  TOKEN_TYPE_NUMERIC_LITERAL_BASE16,
  TOKEN_TYPE_STRING_LITERAL,
  TOKEN_TYPE_CHARACTER_LITERAL,
  TOKEN_TYPE_NAME,
  TOKEN_TYPE_EXPRESSION,
  TOKEN_TYPE_EQUALITY,
  TOKEN_TYPE_BASIC_IDENTIFIER,
  TOKEN_TYPE_COMMENT,
  TOKEN_TYPE_INVALID              = 0x4000000,
  TOKEN_TYPE_CONTAINS_EXPRESSIONS = 0x8000000,
};

enum token_basic_string_type
{
  TOKEN_BASIC_STRING_DIRECTION,
  TOKEN_BASIC_STRING_THING,
  TOKEN_BASIC_STRING_CONDITION,
  TOKEN_BASIC_STRING_ITEM,
  TOKEN_BASIC_STRING_IGNORE,
  TOKEN_BASIC_STRING_FRAGMENT
};

struct special_word
{
  const char *const name;
  int instance_type;
  enum arg_type arg_type;
};

// A token is exactly one of these.

// All tokens are separated by spaces. Tokens may contain spaces if they're
// delimitted by (double) quotes, backticks, or are expressions. If
// delimitted by a particular character but can contain expressions, then
// that character does not act as a delimitter if it shows up within the
// expressions themselves. If a token contains expressions then its type
// will have TOKEN_TYPE_CONTAINS_EXPRESSIONS logically ORed into it.

// Numeric literal base10: a base 10 number.
//  Can range from -2147483648 to 2147483647.
//  Eg: 123456789

// Numeric literal base16: a base 16 number starting with 0x (not $ like
//  the old style).
//  Can range from 0x00000000 to 0xFFFFFFFF.
//  Eg: 0xDEADF00D

// String literal: a character string enclosed in double quote characters.
//  Refers to text that is processed directly as such in-game.
//  May contain expressions.
//  Eg: "Hello world!"

// Character literal: a single character enclosed in single quotes.
//  Eg: 'q'

// Name: a character string enclosed in backticks. May contain expressions.
//  Refers to some named entity like a counter, robot, board, label, etc.
//  Eg: `x marks the counter`

// Equality symbol: symbol used for comparison
//  One of: <, >, =, ==, <>, ><, and !=

// Expression: A mathematical expression separated by parentheses. The
//  grammar for expressions is as follows (\| is literal | character)
//  expr           := (expr_terms)
//  expr_terms     := expr_value | expr_value expr_binary_op expr_terms |
//                    expr_unary_op expr_terms | expr
//  expr_value     := numeric_literal | name
//  expr_binary_op := + | - | * | / | % | ** | >> | >>> | << | > | < | >= |
//                    <= | = | != | & | \| | ^
//  expr_unary_op  := - | ~

// Where the following binary operations correspond to the following things:
// +    Addition
// -    Subtraction
// *    Multiplication
// /    Division
// %    Modulo
// **   Exponent (a ** b is a to the b order/power)
// >>   Logical bitshift right
// >>>  Arithmetic bitshift right
// <<   Bitshift left
// >    Greater than; evaluates to 1 if true, 0 if false
// <    Less than; evaluates to 1 if true, 0 if false
// >=   Greater than or equal to; evaluates to 1 if true, 0 if false
// <=   Less than or equal to; evaluates to 1 if true, 0 if false
// =    Equal to; evaluates to 1 if true, 0 if false
// !=   Not equal to; evaluates to 1 if true, 0 if false
// &    Bitwise AND (not logical AND)
// |    Bitwise OR (not logical OR)
// ^    Bitwise XOR (not logical XOR)

// And the following unary operations correspond to the follow things:
// -    Two's complement negation
// ~    One's complement negation (bitwise NOT)

// Basic identifier: A string not delimitted by a special character that
//  could be a name or could be part of a command, aliased argument, param,
//  color, etc.
//  Must not contain spaces. Note that this does not include anything that
//  falls under "ignored" strings.
//  Eg: this_counter
//  Eg: NORTH
//  Eg: p??

// Ignore: Connecting words that are thrown out by the tokenizer and are
//  only allowed to make code more expressive/readible.
//  Includes:
//  , (comma)
//  ; (semicolon)
//  a
//  an
//  and
//  as
//  at
//  by
//  else
//  for
//  from
//  into
//  is
//  of
//  the
//  then
//  there
//  through
//  thru
//  to
//  with

// Invalid means that it won't let it pass. This includes malformed expressions
// (anything that begins with an ( and goes wrong from there)

union token_value
{
  int command_number;
  int numeric_literal;
  char char_literal;
  enum equality equality_type;
  const struct special_word *special_word;
};

struct token
{
  enum token_type type;
  union token_value arg_value;
  enum arg_type_indexed arg_type_indexed;
  bool value_is_cached;
  char *value;
  int length;
};

__M_BEGIN_DECLS

char *legacy_disassemble_program(char *program_bytecode, int bytecode_length,
 int *_disasm_length, bool print_ignores, int base);
char *legacy_convert_file(char *file_name, int *_disasm_length,
 bool print_ignores, int base);

char *find_non_identifier_char(char *str);
CORE_LIBSPEC char *assemble_program(char *src, int *size);

#ifdef CONFIG_EDITOR

CORE_LIBSPEC struct token *parse_command(char *src, char **_next,
 int *num_parse_tokens);
CORE_LIBSPEC void print_color(int color, char *color_buffer);
CORE_LIBSPEC int unescape_char(char *dest, char c);
CORE_LIBSPEC int get_thing(char *name, int name_length);

#endif // CONFIG_EDITOR

#endif // CONFIG_DEBYTECODE

__M_END_DECLS

#endif // __RASM_H

repo_name: pgimeno/megazeux

license: mit


code:
/* MegaZeux
 *
 * Copyright (C) 2004-2006 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007 Alistair John Strachan <alistair@devzero.co.uk>
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>

#include "platform.h"
#include "graphics.h"
#include "render.h"

static void set_colors8_mzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
  char_colors[0] = (bg << 24) | (bg << 16) | (bg << 8) | bg;
  char_colors[1] = (bg << 24) | (bg << 16) | (bg << 8) | fg;
  char_colors[2] = (bg << 24) | (bg << 16) | (fg << 8) | bg;
  char_colors[3] = (bg << 24) | (bg << 16) | (fg << 8) | fg;
  char_colors[4] = (bg << 24) | (fg << 16) | (bg << 8) | bg;
  char_colors[5] = (bg << 24) | (fg << 16) | (bg << 8) | fg;
  char_colors[6] = (bg << 24) | (fg << 16) | (fg << 8) | bg;
  char_colors[7] = (bg << 24) | (fg << 16) | (fg << 8) | fg;
  char_colors[8] = (fg << 24) | (bg << 16) | (bg << 8) | bg;
  char_colors[9] = (fg << 24) | (bg << 16) | (bg << 8) | fg;
  char_colors[10] = (fg << 24) | (bg << 16) | (fg << 8) | bg;
  char_colors[11] = (fg << 24) | (bg << 16) | (fg << 8) | fg;
  char_colors[12] = (fg << 24) | (fg << 16) | (bg << 8) | bg;
  char_colors[13] = (fg << 24) | (fg << 16) | (bg << 8) | fg;
  char_colors[14] = (fg << 24) | (fg << 16) | (fg << 8) | bg;
  char_colors[15] = (fg << 24) | (fg << 16) | (fg << 8) | fg;
#else
  char_colors[0] = (bg << 24) | (bg << 16) | (bg << 8) | bg;
  char_colors[1] = (fg << 24) | (bg << 16) | (bg << 8) | bg;
  char_colors[2] = (bg << 24) | (fg << 16) | (bg << 8) | bg;
  char_colors[3] = (fg << 24) | (fg << 16) | (bg << 8) | bg;
  char_colors[4] = (bg << 24) | (bg << 16) | (fg << 8) | bg;
  char_colors[5] = (fg << 24) | (bg << 16) | (fg << 8) | bg;
  char_colors[6] = (bg << 24) | (fg << 16) | (fg << 8) | bg;
  char_colors[7] = (fg << 24) | (fg << 16) | (fg << 8) | bg;
  char_colors[8] = (bg << 24) | (bg << 16) | (bg << 8) | fg;
  char_colors[9] = (fg << 24) | (bg << 16) | (bg << 8) | fg;
  char_colors[10] = (bg << 24) | (fg << 16) | (bg << 8) | fg;
  char_colors[11] = (fg << 24) | (fg << 16) | (bg << 8) | fg;
  char_colors[12] = (bg << 24) | (bg << 16) | (fg << 8) | fg;
  char_colors[13] = (fg << 24) | (bg << 16) | (fg << 8) | fg;
  char_colors[14] = (bg << 24) | (fg << 16) | (fg << 8) | fg;
  char_colors[15] = (fg << 24) | (fg << 16) | (fg << 8) | fg;
#endif
}

static void set_colors8_smzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  Uint32 bb, bf, fb, ff;
  bg &= 0x0F;
  fg &= 0x0F;
  bb = (bg << 4) | bg;
  bf = (bg << 4) | fg;
  fb = (fg << 4) | bg;
  ff = (fg << 4) | fg;
  bb |= bb << 8;
  bf |= bf << 8;
  fb |= fb << 8;
  ff |= ff << 8;

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
  char_colors[0] = (bb << 16) | bb;
  char_colors[1] = (bb << 16) | bf;
  char_colors[2] = (bb << 16) | fb;
  char_colors[3] = (bb << 16) | ff;
  char_colors[4] = (bf << 16) | bb;
  char_colors[5] = (bf << 16) | bf;
  char_colors[6] = (bf << 16) | fb;
  char_colors[7] = (bf << 16) | ff;
  char_colors[8] = (fb << 16) | bb;
  char_colors[9] = (fb << 16) | bf;
  char_colors[10] = (fb << 16) | fb;
  char_colors[11] = (fb << 16) | ff;
  char_colors[12] = (ff << 16) | bb;
  char_colors[13] = (ff << 16) | bf;
  char_colors[14] = (ff << 16) | fb;
  char_colors[15] = (ff << 16) | ff;
#else
  char_colors[0] = (bb << 16) | bb;
  char_colors[1] = (bf << 16) | bb;
  char_colors[2] = (fb << 16) | bb;
  char_colors[3] = (ff << 16) | bb;
  char_colors[4] = (bb << 16) | bf;
  char_colors[5] = (bf << 16) | bf;
  char_colors[6] = (fb << 16) | bf;
  char_colors[7] = (ff << 16) | bf;
  char_colors[8] = (bb << 16) | fb;
  char_colors[9] = (bf << 16) | fb;
  char_colors[10] = (fb << 16) | fb;
  char_colors[11] = (ff << 16) | fb;
  char_colors[12] = (bb << 16) | ff;
  char_colors[13] = (bf << 16) | ff;
  char_colors[14] = (fb << 16) | ff;
  char_colors[15] = (ff << 16) | ff;
#endif
}

static void set_colors8_smzx3 (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  Uint32 c0, c1, c2, c3;
  c0 = ((bg << 4) | (fg & 0x0F)) & 0xFF;
  c1 = (c0 + 1) & 0xFF;
  c2 = (c0 + 2) & 0xFF;
  c3 = (c0 + 3) & 0xFF;

  c0 |= c0 << 8;
  c1 |= c1 << 8;
  c2 |= c2 << 8;
  c3 |= c3 << 8;

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
  char_colors[0] = (c0 << 16) | c0;
  char_colors[1] = (c0 << 16) | c2;
  char_colors[2] = (c0 << 16) | c1;
  char_colors[3] = (c0 << 16) | c3;
  char_colors[4] = (c2 << 16) | c0;
  char_colors[5] = (c2 << 16) | c2;
  char_colors[6] = (c2 << 16) | c1;
  char_colors[7] = (c2 << 16) | c3;
  char_colors[8] = (c1 << 16) | c0;
  char_colors[9] = (c1 << 16) | c2;
  char_colors[10] = (c1 << 16) | c1;
  char_colors[11] = (c1 << 16) | c3;
  char_colors[12] = (c3 << 16) | c0;
  char_colors[13] = (c3 << 16) | c2;
  char_colors[14] = (c3 << 16) | c1;
  char_colors[15] = (c3 << 16) | c3;
#else
  char_colors[0] = (c0 << 16) | c0;
  char_colors[1] = (c2 << 16) | c0;
  char_colors[2] = (c1 << 16) | c0;
  char_colors[3] = (c3 << 16) | c0;
  char_colors[4] = (c0 << 16) | c2;
  char_colors[5] = (c2 << 16) | c2;
  char_colors[6] = (c1 << 16) | c2;
  char_colors[7] = (c3 << 16) | c2;
  char_colors[8] = (c0 << 16) | c1;
  char_colors[9] = (c2 << 16) | c1;
  char_colors[10] = (c1 << 16) | c1;
  char_colors[11] = (c3 << 16) | c1;
  char_colors[12] = (c0 << 16) | c3;
  char_colors[13] = (c2 << 16) | c3;
  char_colors[14] = (c1 << 16) | c3;
  char_colors[15] = (c3 << 16) | c3;
#endif
}

static void set_colors16_mzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  Uint32 cb_bg, cb_fg;

  cb_bg = graphics->flat_intensity_palette[bg];
  cb_fg = graphics->flat_intensity_palette[fg];

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
        char_colors[0] = (cb_bg << 16) | cb_bg;
        char_colors[1] = (cb_bg << 16) | cb_fg;
        char_colors[2] = (cb_fg << 16) | cb_bg;
        char_colors[3] = (cb_fg << 16) | cb_fg;
#else
        char_colors[0] = (cb_bg << 16) | cb_bg;
        char_colors[1] = (cb_fg << 16) | cb_bg;
        char_colors[2] = (cb_bg << 16) | cb_fg;
        char_colors[3] = (cb_fg << 16) | cb_fg;
#endif
}

static void set_colors16_smzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  bg &= 0x0F;
  fg &= 0x0F;

  char_colors[0] = graphics->flat_intensity_palette[(bg << 4) | bg];
  char_colors[1] = graphics->flat_intensity_palette[(bg << 4) | fg];
  char_colors[2] = graphics->flat_intensity_palette[(fg << 4) | bg];
  char_colors[3] = graphics->flat_intensity_palette[(fg << 4) | fg];

  char_colors[0] = (char_colors[0] << 16) | char_colors[0];
  char_colors[1] = (char_colors[1] << 16) | char_colors[1];
  char_colors[2] = (char_colors[2] << 16) | char_colors[2];
  char_colors[3] = (char_colors[3] << 16) | char_colors[3];
}

static void set_colors16_smzx3 (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  Uint8 base;

  base = (bg << 4) | (fg & 0x0F);

  char_colors[0] = graphics->flat_intensity_palette[base];
  char_colors[1] = graphics->flat_intensity_palette[(base + 2) & 0xFF];
  char_colors[2] = graphics->flat_intensity_palette[(base + 1) & 0xFF];
  char_colors[3] = graphics->flat_intensity_palette[(base + 3) & 0xFF];

  char_colors[0] = (char_colors[0] << 16) | char_colors[0];
  char_colors[1] = (char_colors[1] << 16) | char_colors[1];
  char_colors[2] = (char_colors[2] << 16) | char_colors[2];
  char_colors[3] = (char_colors[3] << 16) | char_colors[3];
}

static void set_colors32_mzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  char_colors[0] = graphics->flat_intensity_palette[bg];
  char_colors[1] = graphics->flat_intensity_palette[fg];
}

static void set_colors32_smzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  bg &= 0x0F;
  fg &= 0x0F;

  char_colors[0] = graphics->flat_intensity_palette[(bg << 4) | bg];
  char_colors[1] = graphics->flat_intensity_palette[(bg << 4) | fg];
  char_colors[2] = graphics->flat_intensity_palette[(fg << 4) | bg];
  char_colors[3] = graphics->flat_intensity_palette[(fg << 4) | fg];
}

static void set_colors32_smzx3 (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  Uint8 base;

  base = (bg << 4) | (fg & 0x0F);

  char_colors[0] = graphics->flat_intensity_palette[base];
  char_colors[1] = graphics->flat_intensity_palette[(base + 2) & 0xFF];
  char_colors[2] = graphics->flat_intensity_palette[(base + 1) & 0xFF];
  char_colors[3] = graphics->flat_intensity_palette[(base + 3) & 0xFF];
}

void (*const set_colors8[4])
 (struct graphics_data *, Uint32 *, Uint8, Uint8) =
{
  set_colors8_mzx,
  set_colors8_smzx,
  set_colors8_smzx,
  set_colors8_smzx3
};

void (*const set_colors16[4])
 (struct graphics_data *, Uint32 *, Uint8, Uint8) =
{
  set_colors16_mzx,
  set_colors16_smzx,
  set_colors16_smzx,
  set_colors16_smzx3
};

void (*const set_colors32[4])
 (struct graphics_data *, Uint32 *, Uint8, Uint8) =
{
  set_colors32_mzx,
  set_colors32_smzx,
  set_colors32_smzx,
  set_colors32_smzx3
};

#ifdef CONFIG_RENDER_YUV

#include "render_yuv.h"

static void yuv2_set_colors_mzx(struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  struct yuv_render_data *render_data = graphics->render_data;
  Uint32 y0mask = render_data->y0mask;
  Uint32 y1mask = render_data->y1mask;
  Uint32 uvmask = render_data->uvmask;
  Uint32 cb_bg, cb_fg, cb_mix;

  cb_bg = graphics->flat_intensity_palette[bg];
  cb_fg = graphics->flat_intensity_palette[fg];
  cb_mix = (((cb_bg & uvmask) >> 1) + ((cb_fg & uvmask) >> 1)) & uvmask;

  char_colors[0] = cb_bg;
  char_colors[1] = cb_mix | (cb_bg & y0mask) | (cb_fg & y1mask);
  char_colors[2] = cb_mix | (cb_fg & y0mask) | (cb_bg & y1mask);
  char_colors[3] = cb_fg;
}

void (*const yuv2_set_colors[4])
 (struct graphics_data *, Uint32 *, Uint8, Uint8) =
{
  yuv2_set_colors_mzx,
  set_colors32_smzx,
  set_colors32_smzx,
  set_colors32_smzx3
};

#endif

// Nominally 8-bit (Character graphics 8 bytes wide)
void render_graph8(Uint8 *pixels, Uint32 pitch, struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8))
{
  Uint32 *dest;
  Uint32 *ldest, *ldest2;
  struct char_element *src = graphics->text_video;
  Uint8 old_bg = 255;
  Uint8 old_fg = 255;
  Uint8 *char_ptr;
  Uint32 char_colors[16];
  Uint32 current_char_byte;
  Uint32 i, i2, i3;
  Uint32 line_advance = pitch / 4;
  Uint32 row_advance = line_advance * 14;

  dest = (Uint32 *)pixels;

  for(i = 0; i < 25; i++)
  {
    ldest2 = dest;
    for(i2 = 0; i2 < 80; i2++)
    {
      ldest = dest;
      if((src->bg_color != old_bg) || (src->fg_color != old_fg))
      {
        set_colors(graphics, char_colors, src->bg_color, src->fg_color);
        old_bg = src->bg_color;
        old_fg = src->fg_color;
      }

      char_ptr = graphics->charset + (src->char_value * 14);
      src++;
      for(i3 = 0; i3 < 14; i3++)
      {
        current_char_byte = *char_ptr;
        char_ptr++;
        *dest = char_colors[current_char_byte >> 4];
        *(dest + 1) = char_colors[current_char_byte & 0x0F];
        dest += line_advance;
      }

      dest = ldest + 2;
    }
    dest = ldest2 + row_advance;
  }
}

// Nominally 16-bit (Character graphics 16 bytes wide)
void render_graph16(Uint16 *pixels, Uint32 pitch,
 struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8))
{
  Uint32 *dest;
  Uint32 *ldest, *ldest2;
  struct char_element *src = graphics->text_video;
  Uint8 old_bg = 255;
  Uint8 old_fg = 255;
  Uint8 *char_ptr;
  Uint32 char_colors[4];
  Uint32 current_char_byte;
  Uint32 i, i2, i3;
  Uint32 line_advance = pitch / 4;
  Uint32 row_advance = line_advance * 14;

  dest = (Uint32 *)pixels;

  for(i = 0; i < 25; i++)
  {
    ldest2 = dest;
    for(i2 = 0; i2 < 80; i2++)
    {
      ldest = dest;
      if((src->bg_color != old_bg) || (src->fg_color != old_fg))
      {
        set_colors(graphics, char_colors, src->bg_color, src->fg_color);
        old_bg = src->bg_color;
        old_fg = src->fg_color;
      }

      char_ptr = graphics->charset + (src->char_value * 14);
      src++;
      for(i3 = 0; i3 < 14; i3++)
      {
        current_char_byte = *char_ptr;
        char_ptr++;
        *dest = char_colors[current_char_byte >> 6];
        *(dest + 1) = char_colors[(current_char_byte >> 4) & 0x03];
        *(dest + 2) = char_colors[(current_char_byte >> 2) & 0x03];
        *(dest + 3) = char_colors[current_char_byte & 0x03];
        dest += line_advance;
      }

      dest = ldest + 4;
    }
    dest = ldest2 + row_advance;
  }
}

// Nominally 32-bit (Character graphics 32 bytes wide)
void render_graph32(Uint32 *pixels, Uint32 pitch,
 struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8))
{
  Uint32 *dest;
  Uint32 *ldest, *ldest2;
  struct char_element *src = graphics->text_video;
  Uint8 old_bg = 255;
  Uint8 old_fg = 255;
  Uint8 *char_ptr;
  Uint32 char_colors[2];
  Uint32 current_char_byte;
  Uint32 i, i2, i3;
  Sint32 i4;
  Uint32 line_advance = pitch / 4;
  Uint32 line_advance_sub = line_advance - 8;
  Uint32 row_advance = line_advance * 14;

  dest = pixels;

  for(i = 0; i < 25; i++)
  {
    ldest2 = dest;
    for(i2 = 0; i2 < 80; i2++)
    {
      ldest = dest;
      if((src->bg_color != old_bg) || (src->fg_color != old_fg))
      {
        set_colors(graphics, char_colors, src->bg_color, src->fg_color);
        old_bg = src->bg_color;
        old_fg = src->fg_color;
      }

      char_ptr = graphics->charset + (src->char_value * 14);
      src++;
      for(i3 = 0; i3 < 14; i3++)
      {
        current_char_byte = *char_ptr;
        char_ptr++;
        for(i4 = 7; i4 >= 0; i4--, dest++)
        {
          *dest = char_colors[(current_char_byte >> i4) & 0x01];
        }
        dest += line_advance_sub;
      }

      dest = ldest + 8;
    }
    dest = ldest2 + row_advance;
  }
}

void render_graph32s(Uint32 *pixels, Uint32 pitch,
 struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8))
{
  Uint32 *dest;
  Uint32 *ldest, *ldest2;
  struct char_element *src = graphics->text_video;
  Uint8 old_bg = 255;
  Uint8 old_fg = 255;
  Uint8 *char_ptr;
  Uint32 char_colors[4];
  Uint32 current_char_byte;
  Uint32 current_color;
  Uint32 i, i2, i3;
  Sint32 i4;
  Uint32 line_advance = pitch / 4;
  Uint32 line_advance_sub = line_advance - 8;
  Uint32 row_advance = line_advance * 14;

  dest = pixels;

  for(i = 0; i < 25; i++)
  {
    ldest2 = dest;
    for(i2 = 0; i2 < 80; i2++)
    {
      ldest = dest;
      if((src->bg_color != old_bg) || (src->fg_color != old_fg))
      {
        set_colors(graphics, char_colors, src->bg_color, src->fg_color);
        old_bg = src->bg_color;
        old_fg = src->fg_color;
      }

      char_ptr = graphics->charset + (src->char_value * 14);
      src++;
      for(i3 = 0; i3 < 14; i3++)
      {
        current_char_byte = *char_ptr;
        char_ptr++;
        for(i4 = 6; i4 >= 0; i4 -= 2, dest += 2)
        {
          current_color = char_colors[(current_char_byte >> i4) & 0x03];
          *dest = current_color;
          *(dest + 1) = current_color;
        }
        dest += line_advance_sub;
      }

      dest = ldest + 8;
    }
    dest = ldest2 + row_advance;
  }
}

void render_cursor(Uint32 *pixels, Uint32 pitch, Uint8 bpp, Uint32 x, Uint32 y,
 Uint32 color, Uint8 lines, Uint8 offset)
{
  int i,j;
  Uint8 size = bpp / 4;
  Uint32 line_advance = pitch / 4;
  Uint32 line_advance_sub = line_advance - size;
  Uint32 row_advance = line_advance * 14;
  Uint32 *dest = pixels + (x * size) + (y * row_advance) +
   (offset * line_advance);
  for(i = 0; i < lines; i++)
  {
    for(j = 0; j < size; j++)
      *(dest++) = color;
    dest += line_advance_sub;
  }
}

void render_mouse(Uint32 *pixels, Uint32 pitch, Uint8 bpp, Uint32 x, Uint32 y,
 Uint32 mask, Uint8 w, Uint8 h)
{
  int i,j;
  Uint8 size = w * bpp / 32;
  Uint32 line_advance = pitch / 4;
  Uint32 line_advance_sub = line_advance - size;
  Uint32 *dest = pixels + (x * bpp / 32) + (y * line_advance);
  for(i = 0; i < h; i++)
  {
    for(j = 0; j < size; j++)
      *(dest++) ^= mask;
    dest += line_advance_sub;
  }
}

void get_screen_coords_centered(struct graphics_data *graphics, int screen_x,
 int screen_y, int *x, int *y, int *min_x, int *min_y, int *max_x, int *max_y)
{
  int w_offset, h_offset;
  int target_width = graphics->window_width;
  int target_height = graphics->window_height;

  if(graphics->fullscreen)
  {
    target_width = graphics->resolution_width;
    target_height = graphics->resolution_height;
  }

  w_offset = (target_width - 640) / 2;
  h_offset = (target_height - 350) / 2;

  *x = screen_x - w_offset;
  *y = screen_y - h_offset;

  *min_x = w_offset;
  *min_y = h_offset;
  *max_x = 639 + w_offset;
  *max_y = 349 + h_offset;
}

void set_screen_coords_centered(struct graphics_data *graphics, int x, int y,
 int *screen_x, int *screen_y)
{
  int target_width = graphics->window_width;
  int target_height = graphics->window_height;

  if(graphics->fullscreen)
  {
    target_width = graphics->resolution_width;
    target_height = graphics->resolution_height;
  }

  *screen_x = x + (target_width - 640) / 2;
  *screen_y = y + (target_height - 350) / 2;
}

#if defined(CONFIG_RENDER_GL_FIXED) || defined(CONFIG_RENDER_GL_PROGRAM) \
 || defined(CONFIG_RENDER_YUV)

void get_screen_coords_scaled(struct graphics_data *graphics, int screen_x,
 int screen_y, int *x, int *y, int *min_x, int *min_y, int *max_x, int *max_y)
{
  int target_width = graphics->window_width;
  int target_height = graphics->window_height;

  if(graphics->fullscreen)
  {
    target_width = graphics->resolution_width;
    target_height = graphics->resolution_height;
  }

  *x = screen_x * 640 / target_width;
  *y = screen_y * 350 / target_height;
  *min_x = 0;
  *min_y = 0;
  *max_x = target_width - 1;
  *max_y = target_height - 1;
}

void set_screen_coords_scaled(struct graphics_data *graphics, int x, int y,
 int *screen_x, int *screen_y)
{
  int target_width = graphics->window_width;
  int target_height = graphics->window_height;

  if(graphics->fullscreen)
  {
    target_width = graphics->resolution_width;
    target_height = graphics->resolution_height;
  }

  *screen_x = x * target_width / 640;
  *screen_y = y * target_height / 350;
}

#endif // CONFIG_RENDER_GL_FIXED || CONFIG_RENDER_GL_PROGRAM || CONFIG_RENDER_YUV

// FIXME: Integerize

#if defined(CONFIG_RENDER_GL_FIXED) || defined(CONFIG_RENDER_GL_PROGRAM) \
 || defined(CONFIG_RENDER_YUV) || defined(CONFIG_RENDER_GX)

void fix_viewport_ratio(int width, int height, int *v_width, int *v_height,
 enum ratio_type ratio)
{
  int numerator = 0, denominator = 0;

  *v_width = width;
  *v_height = height;

  if((ratio == RATIO_STRETCH))
    return;

  if(ratio == RATIO_CLASSIC_4_3)
  {
    numerator = 4;
    denominator = 3;
  }
  else if(ratio == RATIO_MODERN_64_35)
  {
    numerator = 64;
    denominator = 35;
  }

  if(((float)width / (float)height) < ((float)numerator / (float)denominator))
    *v_height = (width * denominator) / numerator;
  else
    *v_width = (height * numerator) / denominator;
}

#endif // CONFIG_RENDER_GL_FIXED || CONFIG_RENDER_GL_PROGRAM || CONFIG_RENDER_YUV || CONFIG_RENDER_GX

void resize_screen_standard(struct graphics_data *graphics, int w, int h)
{
  update_screen();
  update_palette();
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __RENDER_H
#define __RENDER_H

#include "compat.h"

__M_BEGIN_DECLS

#include "graphics.h"

extern void (*const set_colors8[4])(struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg);
extern void (*const set_colors16[4])(struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg);
extern void (*const set_colors32[4])(struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg);
#ifdef CONFIG_RENDER_YUV
extern void (*const yuv2_set_colors[4])(struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg);
#endif

void render_graph8(Uint8 *pixels, Uint32 pitch,
 struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8));
void render_graph16(Uint16 *pixels, Uint32 pitch,
 struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8));
void render_graph32(Uint32 *pixels, Uint32 pitch,
 struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8));
void render_graph32s(Uint32 *pixels, Uint32 pitch,
 struct graphics_data *graphics,
 void (*set_colors)(struct graphics_data *, Uint32 *, Uint8, Uint8));

void render_cursor(Uint32 *pixels, Uint32 pitch, Uint8 bpp, Uint32 x, Uint32 y,
 Uint32 color, Uint8 lines, Uint8 offset);
void render_mouse(Uint32 *pixels, Uint32 pitch, Uint8 bpp, Uint32 x, Uint32 y,
 Uint32 mask, Uint8 w, Uint8 h);

void get_screen_coords_centered(struct graphics_data *graphics, int screen_x,
 int screen_y, int *x, int *y, int *min_x, int *min_y, int *max_x, int *max_y);
void set_screen_coords_centered(struct graphics_data *graphics, int x, int y,
 int *screen_x, int *screen_y);

#if defined(CONFIG_RENDER_GL_FIXED) || defined(CONFIG_RENDER_GL_PROGRAM) \
 || defined(CONFIG_RENDER_YUV)

void get_screen_coords_scaled(struct graphics_data *graphics, int screen_x,
 int screen_y, int *x, int *y, int *min_x, int *min_y, int *max_x, int *max_y);
void set_screen_coords_scaled(struct graphics_data *graphics, int x, int y,
 int *screen_x, int *screen_y);

#endif // CONFIG_RENDER_GL_FIXED || CONFIG_RENDER_GL_PROGRAM || CONFIG_RENDER_YUV

#if defined(CONFIG_RENDER_GL_FIXED) || defined(CONFIG_RENDER_GL_PROGRAM) \
 || defined(CONFIG_RENDER_YUV) || defined(CONFIG_RENDER_GX)

void fix_viewport_ratio(int width, int height, int *v_width, int *v_height,
 enum ratio_type ratio);

#endif // CONFIG_RENDER_GL_FIXED || CONFIG_RENDER_GL_PROGRAM || CONFIG_RENDER_YUV || CONFIG_RENDER_GX

void resize_screen_standard(struct graphics_data *graphics, int w, int h);

__M_END_DECLS

#endif // __RENDER_H


code:
/* MegaZeux
 *
 * Copyright (C) 2009 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <GLES/gl.h>
#include "render_egl.h"

#include "util.h"

#include <assert.h>
#include <dlfcn.h>

static void *glso;
static enum gl_lib_type gl_type;

#ifdef ANDROID

#include "sfwrapper.h"

fn_ptr GL_GetProcAddress(const char *proc)
{
  return (fn_ptr)dlsym(glso, proc);
}

#endif /* ANDROID */

bool GL_LoadLibrary(enum gl_lib_type type)
{
  const char *filename;

  gl_type = type;

  if(type == GL_LIB_FIXED)
    filename = "libGLESv1_CM.so";
  else
    filename = "libGLESv2.so";

  glso = dlopen(filename, RTLD_NOW);
  return glso != NULL;
}

// EGL is not directly comparable to SDL as it does not handle "windows"
// in the traditional sense. It is expected that the platform provides a
// native "window" type that handles things like resolution and resizing.

static EGLint config16[] =
{
  EGL_RED_SIZE,        5,
  EGL_GREEN_SIZE,      6,
  EGL_BLUE_SIZE,       5,
  EGL_ALPHA_SIZE,      0,
  EGL_DEPTH_SIZE,      0,
  EGL_RENDERABLE_TYPE, 0, /* Placeholder */
  EGL_NONE,
};

static EGLint config32[] =
{
  EGL_RED_SIZE,        8,
  EGL_GREEN_SIZE,      8,
  EGL_BLUE_SIZE,       8,
  EGL_ALPHA_SIZE,      8,
  EGL_DEPTH_SIZE,      0,
  EGL_RENDERABLE_TYPE, 0, /* Placeholder */
  EGL_NONE,
};

static void update_config_attribs(EGLint *config)
{
  int i;

  for(i = 0; config[i] != EGL_NONE; i++)
  {
    if(config[i] != EGL_RENDERABLE_TYPE)
      continue;

    if(gl_type == GL_LIB_FIXED)
      config[i + 1] = EGL_OPENGL_ES_BIT;
    else
      config[i + 1] = EGL_OPENGL_ES2_BIT;

    break;
  }
}

static const EGLint *get_current_config(int depth)
{
  if(depth == 32)
  {
    debug("Selected RGBA 8888 EGLConfig\n");
    update_config_attribs(config32);
    return config32;
  }

  debug("Selected RGB 565 EGLConfig\n");
  update_config_attribs(config16);
  return config16;
}

static const EGLint gles_v1_attribs[] =
{
  EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE
};

static const EGLint gles_v2_attribs[] =
{
  EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};

bool gl_set_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize)
{
  struct egl_render_data *egl_render_data = graphics->render_data;
  const EGLint *attribs = gles_v1_attribs;
  EGLNativeWindowType window = 0;
  EGLint w, h;

  assert(egl_render_data != NULL);

#ifdef ANDROID
  window = SurfaceFlingerGetNativeWindow();
#endif

#ifdef CONFIG_X11
  window = XCreateSimpleWindow(egl_render_data->native_display,
                               RootWindow(egl_render_data->native_display, 0),
                               0, 0, 640, 350, 0,
                               BlackPixel(egl_render_data->native_display, 0),
                               BlackPixel(egl_render_data->native_display, 0));
  XMapWindow(egl_render_data->native_display, window);
  XFlush(egl_render_data->native_display);
#endif

  egl_render_data->surface = eglCreateWindowSurface(egl_render_data->display,
                                                    egl_render_data->config,
                                                    window, NULL);
  if(egl_render_data->surface == EGL_NO_SURFACE)
  {
    warn("eglCreateWindowSurface failed\n");
    goto err_cleanup;
  }

  if(!eglBindAPI(EGL_OPENGL_ES_API))
  {
    warn("eglBindAPI failed\n");
    goto err_cleanup;
  }

  if(gl_type == GL_LIB_PROGRAMMABLE)
    attribs = gles_v2_attribs;

  egl_render_data->context = eglCreateContext(egl_render_data->display,
                                              egl_render_data->config,
                                              EGL_NO_CONTEXT, attribs);
  if(egl_render_data->context == EGL_NO_CONTEXT)
  {
    warn("eglCreateContext failed\n");
    goto err_cleanup;
  }

  if(!eglQuerySurface(egl_render_data->display, egl_render_data->surface,
   EGL_WIDTH, &w))
  {
    warn("eglQuerySurface (width) failed\n");
    goto err_cleanup;
  }

  if(!eglQuerySurface(egl_render_data->display, egl_render_data->surface,
   EGL_HEIGHT, &h))
  {
    warn("eglQuerySurface (height) failed\n");
    goto err_cleanup;
  }

  if(fullscreen)
  {
    graphics->resolution_width = w;
    graphics->resolution_height = h;
  }
  else
  {
    graphics->window_width = w;
    graphics->window_height = h;
  }

#ifdef ANDROID
  SurfaceFlingerSetSwapRectangle(0, 0, w, h);
#endif

  if(!eglMakeCurrent(egl_render_data->display, egl_render_data->surface,
                     egl_render_data->surface, egl_render_data->context))
  {
    warn("eglMakeCurrent failed\n");
    goto err_cleanup;
  }

  // Can now issue GL ES commands
  return true;

err_cleanup:
  gl_cleanup(graphics);
  return false;
}

bool gl_check_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize)
{
  struct egl_render_data *egl_render_data = graphics->render_data;
  EGLint num_configs;

  assert(egl_render_data != NULL);

#ifdef ANDROID
  if(!SurfaceFlingerInitialize(width, height, depth, fullscreen))
  {
    warn("SurfaceFlingerInitialize failed\n");
    return false;
  }
#endif

#ifdef CONFIG_X11
  egl_render_data->native_display = XOpenDisplay(NULL);
#endif

  egl_render_data->display = eglGetDisplay(egl_render_data->native_display);
  if(egl_render_data->display == EGL_NO_DISPLAY)
  {
    warn("eglGetDisplay failed\n");
    return false;
  }

  if(!eglInitialize(egl_render_data->display, NULL, NULL))
  {
    warn("eglInitialize failed\n");
    return false;
  }

  if(!eglChooseConfig(egl_render_data->display,
                      get_current_config(depth),
                      &egl_render_data->config, 1, &num_configs))
  {
    warn("eglChooseConfig failed\n");
    return false;
  }

  return true;
}

void gl_set_attributes(struct graphics_data *graphics)
{
  struct egl_render_data *egl_render_data = graphics->render_data;

  assert(egl_render_data != NULL);

  // EGL is not concerned with double buffering -- per-platform hacks should
  // go here if double buffering isn't the default or can be toggled.

  if(graphics->gl_vsync == 0)
    eglSwapInterval(egl_render_data->display, 0);
  else if(graphics->gl_vsync >= 1)
    eglSwapInterval(egl_render_data->display, 1);
}

bool gl_swap_buffers(struct graphics_data *graphics)
{
  struct egl_render_data *egl_render_data = graphics->render_data;

  assert(egl_render_data != NULL);

  return eglSwapBuffers(egl_render_data->display, egl_render_data->surface);
}

void gl_cleanup(struct graphics_data *graphics)
{
  struct egl_render_data *egl_render_data = graphics->render_data;

  assert(egl_render_data != NULL);

  if(egl_render_data->display != EGL_NO_DISPLAY)
  {
    eglMakeCurrent(egl_render_data->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
     EGL_NO_CONTEXT);

    if(egl_render_data->context != EGL_NO_CONTEXT)
    {
      eglDestroyContext(egl_render_data->display, egl_render_data->context);
      egl_render_data->context = EGL_NO_CONTEXT;
    }

    if(egl_render_data->surface != EGL_NO_SURFACE)
    {
      eglDestroySurface(egl_render_data->display, egl_render_data->surface);
      egl_render_data->surface = EGL_NO_SURFACE;
    }

    eglTerminate(egl_render_data->display);
    egl_render_data->display = EGL_NO_DISPLAY;
  }

#ifdef ANDROID
  SurfaceFlingerDeinitialize();
#endif

#ifdef CONFIG_X11
  XCloseDisplay(egl_render_data->native_display);
  egl_render_data->native_display = 0;
#endif
}


code:
/* MegaZeux
 *
 * Copyright (C) 2009 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __RENDER_EGL_H
#define __RENDER_EGL_H

#include "compat.h"

__M_BEGIN_DECLS

#include "render_gl.h"

#include <EGL/egl.h>

bool gl_set_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize);
bool gl_check_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize);
void gl_set_attributes(struct graphics_data *graphics);
bool gl_swap_buffers(struct graphics_data *graphics);
void gl_cleanup(struct graphics_data *graphics);

bool GL_LoadLibrary(enum gl_lib_type type);

#ifndef ANDROID

static inline fn_ptr GL_GetProcAddress(const char *proc)
{
  return eglGetProcAddress(proc);
}

#else /* ANDROID */

/* Android's eglGetProcAddress is currently broken, so we
 * have to roll our own..
 */
fn_ptr GL_GetProcAddress(const char *proc);

#endif /* !ANDROID */

struct egl_render_data
{
  EGLNativeDisplayType native_display;
  EGLDisplay display;
  EGLConfig config;
  EGLContext context;
  EGLSurface surface;
};

__M_END_DECLS

#endif // __RENDER_EGL_H


code:
/* MegaZeux
 *
 * Copyright (C) 2004-2006 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007,2009 Alistair John Strachan <alistair@devzero.co.uk>
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "render.h"
#include "util.h"

#ifdef CONFIG_SDL
#include "render_sdl.h"
#endif

#ifdef CONFIG_EGL
#include <GLES/gl.h>
#include "render_egl.h"
#endif

#include "render_gl.h"

const float vertex_array_single[2 * 4] = {
  -1.0f,  1.0f,
  -1.0f, -1.0f,
   1.0f,  1.0f,
   1.0f, -1.0f,
};

#ifdef DEBUG

void gl_error(const char *file, int line,
              GLenum (GL_APIENTRY *glGetError)(void))
{
  const char *error;

  switch(glGetError())
  {
    case GL_NO_ERROR:
      return;
    case GL_INVALID_ENUM:
      error = "Invalid enum";
      break;
    case GL_INVALID_VALUE:
      error = "Invalid value";
      break;
    case GL_INVALID_OPERATION:
      error = "Invalid operation";
      break;
    case GL_STACK_OVERFLOW:
      error = "Stack overflow";
      break;
    case GL_STACK_UNDERFLOW:
      error = "Stack underflow";
      break;
    case GL_OUT_OF_MEMORY:
      error = "Out of memory";
      break;
    default:
      error = "Unknown error";
      break;
  }

  warn("%s:%d: GL Error: %s\n", file, line, error);
}

#endif // DEBUG

bool gl_load_syms(const struct dso_syms_map *map)
{
  int i = 0;

  for(i = 0; map[i].name != NULL; i++)
  {
    *map[i].sym_ptr = GL_GetProcAddress(map[i].name);
    if(!*map[i].sym_ptr)
    {
      warn("Failed to load GL function '%s', aborting..\n", map[i].name);
      return false;
    }
  }

  return true;
}

void gl_set_filter_method(const char *method,
 void (GL_APIENTRY *glTexParameterf_p)(GLenum target, GLenum pname, GLfloat param))
{
  GLfloat gl_filter_method = GL_LINEAR;

  if(!strcasecmp(method, CONFIG_GL_FILTER_NEAREST))
    gl_filter_method = GL_NEAREST;

  glTexParameterf_p(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_method);
  glTexParameterf_p(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_method);
  glTexParameterf_p(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf_p(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}

void get_context_width_height(struct graphics_data *graphics,
 int *width, int *height)
{
  if(!graphics->fullscreen)
  {
    *width = graphics->window_width;
    *height = graphics->window_height;
  }
  else
  {
    *width = graphics->resolution_width;
    *height = graphics->resolution_height;
  }
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004-2006 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007,2009 Alistair John Strachan <alistair@devzero.co.uk>
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __RENDER_GL_H
#define __RENDER_GL_H

#include "compat.h"

__M_BEGIN_DECLS

#include "graphics.h"
#include "util.h"

#ifndef CONFIG_EGL
#include "SDL_opengl.h"
#endif

#ifndef GLAPIENTRY
#define GLAPIENTRY APIENTRY
#endif

#ifndef GL_APIENTRY
#define GL_APIENTRY GLAPIENTRY
#endif

#define GL_NON_POWER_2_WIDTH      640
#define GL_NON_POWER_2_HEIGHT     350
#define GL_POWER_2_WIDTH          1024
#define GL_POWER_2_HEIGHT         512

#define CONFIG_GL_FILTER_LINEAR   "linear"
#define CONFIG_GL_FILTER_NEAREST  "nearest"

extern const float vertex_array_single[2 * 4];

#ifdef DEBUG
void gl_error(const char *file, int line,
              GLenum (GL_APIENTRY *glGetError)(void));
#else
static inline void gl_error(const char *file, int line,
                            GLenum (GL_APIENTRY *glGetError)(void)) { }
#endif

bool gl_load_syms(const struct dso_syms_map *map);
void gl_set_filter_method(const char *method,
 void (GL_APIENTRY *glTexParameterf_p)(GLenum target, GLenum pname, GLfloat param));
void get_context_width_height(struct graphics_data *graphics,
 int *width, int *height);

enum gl_lib_type
{
  GL_LIB_FIXED,
  GL_LIB_PROGRAMMABLE,
};

__M_END_DECLS

#endif // __RENDER_GL_H


code:
/* MegaZeux
 *
 * Copyright (C) 2006-2007 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007,2009 Alistair John Strachan <alistair@devzero.co.uk>
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#include <string.h>

#include "platform.h"
#include "render.h"
#include "renderers.h"
#include "util.h"

#ifdef CONFIG_SDL
#include "render_sdl.h"
#endif

#ifdef CONFIG_EGL
#include <GLES/gl.h>
#include "render_egl.h"
#endif

#include "render_gl.h"

// NOTE: This renderer should work with almost any GL capable video card.
//       However, if you plan to change it, please bear in mind that this
//       has been carefully written to work with both OpenGL 1.x and
//       OpenGL ES 1.x. The latter API lacks many functions present in
//       desktop OpenGL. GL ES is typically used on cellphones.

static struct
{
  void (GL_APIENTRY *glBindTexture)(GLenum target, GLuint texture);
  void (GL_APIENTRY *glClear)(GLbitfield mask);
  void (GL_APIENTRY *glDisableClientState)(GLenum cap);
  void (GL_APIENTRY *glDrawArrays)(GLenum mode, GLint first, GLsizei count);
  void (GL_APIENTRY *glEnable)(GLenum cap);
  void (GL_APIENTRY *glEnableClientState)(GLenum cap);
  void (GL_APIENTRY *glGenTextures)(GLsizei n, GLuint *textures);
  GLenum (GL_APIENTRY *glGetError)(void);
  const GLubyte* (GL_APIENTRY *glGetString)(GLenum name);
  void (GL_APIENTRY *glTexCoordPointer)(GLint size, GLenum type,
   GLsizei stride, const GLvoid *ptr);
  void (GL_APIENTRY *glTexImage2D)(GLenum target, GLint level,
   GLint internalformat,GLsizei width, GLsizei height, GLint border,
   GLenum format, GLenum type, const GLvoid *pixels);
  void (GL_APIENTRY *glTexParameterf)(GLenum target, GLenum pname,
   GLfloat param);
  void (GL_APIENTRY *glVertexPointer)(GLint size, GLenum type,
   GLsizei stride, const GLvoid *ptr);
  void (GL_APIENTRY *glViewport)(GLint x, GLint y, GLsizei width,
   GLsizei height);
}
gl1;

static const struct dso_syms_map gl1_syms_map[] =
{
  { "glBindTexture",        (fn_ptr *)&gl1.glBindTexture },
  { "glClear",              (fn_ptr *)&gl1.glClear },
  { "glDisableClientState", (fn_ptr *)&gl1.glDisableClientState },
  { "glDrawArrays",         (fn_ptr *)&gl1.glDrawArrays },
  { "glEnable",             (fn_ptr *)&gl1.glEnable },
  { "glEnableClientState",  (fn_ptr *)&gl1.glEnableClientState },
  { "glGenTextures",        (fn_ptr *)&gl1.glGenTextures },
  { "glGetError",           (fn_ptr *)&gl1.glGetError },
  { "glGetString",          (fn_ptr *)&gl1.glGetString },
  { "glTexCoordPointer",    (fn_ptr *)&gl1.glTexCoordPointer },
  { "glTexImage2D",         (fn_ptr *)&gl1.glTexImage2D },
  { "glTexParameterf",      (fn_ptr *)&gl1.glTexParameterf },
  { "glVertexPointer",      (fn_ptr *)&gl1.glVertexPointer },
  { "glViewport",           (fn_ptr *)&gl1.glViewport },
  { NULL, NULL }
};

#define gl_check_error() gl_error(__FILE__, __LINE__, gl1.glGetError)

struct gl1_render_data
{
#ifdef CONFIG_EGL
  struct egl_render_data egl;
#endif
  Uint32 *pixels;
  Uint32 w;
  Uint32 h;
  enum ratio_type ratio;
};

static bool gl1_init_video(struct graphics_data *graphics,
 struct config_info *conf)
{
  struct gl1_render_data *render_data = cmalloc(sizeof(struct gl1_render_data));

  if(!render_data)
    goto err_out;

  if(!GL_LoadLibrary(GL_LIB_FIXED))
    goto err_free_render_data;

  graphics->render_data = render_data;
  render_data->ratio = conf->video_ratio;

  graphics->gl_vsync = conf->gl_vsync;
  graphics->allow_resize = conf->allow_resize;
  graphics->gl_filter_method = conf->gl_filter_method;
  graphics->bits_per_pixel = 32;

  // OpenGL only supports 16/32bit colour
  if(conf->force_bpp == 16 || conf->force_bpp == 32)
    graphics->bits_per_pixel = conf->force_bpp;

  if(!set_video_mode())
    goto err_free_render_data;

  return true;

err_free_render_data:
  free(render_data);
err_out:
  return false;
}

static void gl1_resize_screen(struct graphics_data *graphics,
 int width, int height)
{
  struct gl1_render_data *render_data = graphics->render_data;
  GLuint texture_number;
  int v_width, v_height;

  get_context_width_height(graphics, &width, &height);
  fix_viewport_ratio(width, height, &v_width, &v_height, render_data->ratio);

  gl1.glViewport((width - v_width) >> 1, (height - v_height) >> 1,
   v_width, v_height);
  gl_check_error();

  gl1.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  gl1.glEnableClientState(GL_VERTEX_ARRAY);

  gl1.glEnable(GL_TEXTURE_2D);

  gl1.glGenTextures(1, &texture_number);
  gl_check_error();

  gl1.glBindTexture(GL_TEXTURE_2D, texture_number);
  gl_check_error();

  gl_set_filter_method(graphics->gl_filter_method, gl1.glTexParameterf);
}

static bool gl1_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  int internal_width = GL_POWER_2_WIDTH, internal_height = GL_POWER_2_HEIGHT;
  struct gl1_render_data *render_data = graphics->render_data;

  gl_set_attributes(graphics);

  if(!gl_set_video_mode(graphics, width, height, depth, fullscreen, resize))
    return false;

  if(!gl_load_syms(gl1_syms_map))
    return false;

  // We need a specific version of OpenGL; desktop GL must be 1.1.
  // All OpenGL ES 1.x implementations are supported, so don't do
  // the check with EGL configurations (EGL implies OpenGL ES).
  // No OpenGL ES 1.x supports NPOT textures, so we can also skip
  // the extension check.
#ifndef CONFIG_EGL
  {
    static bool initialized = false;

    if(!initialized)
    {
      const char *version, *extensions;
      double version_float;

      version = (const char *)gl1.glGetString(GL_VERSION);
      if(!version)
        return false;

      extensions = (const char *)gl1.glGetString(GL_EXTENSIONS);
      if(!extensions)
        return false;

      version_float = atof(version);
      if(version_float < 1.1)
      {
        warn("Need >= OpenGL 1.1, got OpenGL %.1f.\n", version_float);
        return false;
      }

      if(strstr(extensions, "GL_ARB_texture_non_power_of_two"))
      {
        internal_width = GL_NON_POWER_2_WIDTH;
        internal_height = GL_NON_POWER_2_HEIGHT;
      }

      initialized = true;
    }
  }
#endif

  render_data->pixels =
   cmalloc(sizeof(Uint32) * internal_width * internal_height);
  if(!render_data->pixels)
    return false;

  render_data->w = internal_width;
  render_data->h = internal_height;

  gl1_resize_screen(graphics, width, height);
  return true;
}

static void gl1_free_video(struct graphics_data *graphics)
{
  gl_cleanup(graphics);
  free(graphics->render_data);
  graphics->render_data = NULL;
}

static void gl1_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  Uint32 i;
  for(i = 0; i < count; i++)
  {
#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
    graphics->flat_intensity_palette[i] = (palette[i].r << 24) |
     (palette[i].g << 16) | (palette[i].b << 8);
#else
    graphics->flat_intensity_palette[i] = (palette[i].b << 16) |
     (palette[i].g << 8) | palette[i].r;
#endif
  }
}

static void gl1_render_graph(struct graphics_data *graphics)
{
  struct gl1_render_data *render_data = graphics->render_data;
  Uint32 mode = graphics->screen_mode;

  if(!mode)
  {
    render_graph32(render_data->pixels, render_data->w * 4, graphics,
     set_colors32[mode]);
  }
  else
  {
    render_graph32s(render_data->pixels, render_data->w * 4, graphics,
     set_colors32[mode]);
  }
}

static void gl1_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  struct gl1_render_data *render_data = graphics->render_data;

  render_cursor(render_data->pixels, render_data->w * 4, 32, x, y,
   graphics->flat_intensity_palette[color], lines, offset);
}

static void gl1_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  struct gl1_render_data *render_data = graphics->render_data;

  render_mouse(render_data->pixels, render_data->w * 4, 32, x, y, 0xFFFFFFFF,
   w, h);
}

static void gl1_sync_screen(struct graphics_data *graphics)
{
  struct gl1_render_data *render_data = graphics->render_data;

  const float texture_width = 640.0f / render_data->w;
  const float texture_height = 350.0f / render_data->h;

  const float tex_coord_array[2 * 4] = {
    0.0f,          0.0f,
    0.0f,          texture_height,
    texture_width, 0.0f,
    texture_width, texture_height
  };

  gl1.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, render_data->w, render_data->h,
   0, GL_RGBA, GL_UNSIGNED_BYTE, render_data->pixels);
  gl_check_error();

  gl1.glClear(GL_COLOR_BUFFER_BIT);
  gl_check_error();

  gl1.glTexCoordPointer(2, GL_FLOAT, 0, tex_coord_array);
  gl_check_error();

  gl1.glVertexPointer(2, GL_FLOAT, 0, vertex_array_single);
  gl_check_error();

  gl1.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  gl_check_error();

  gl_swap_buffers(graphics);
}

void render_gl1_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = gl1_init_video;
  renderer->free_video = gl1_free_video;
  renderer->check_video_mode = gl_check_video_mode;
  renderer->set_video_mode = gl1_set_video_mode;
  renderer->update_colors = gl1_update_colors;
  renderer->resize_screen = gl1_resize_screen;
  renderer->get_screen_coords = get_screen_coords_scaled;
  renderer->set_screen_coords = set_screen_coords_scaled;
  renderer->render_graph = gl1_render_graph;
  renderer->render_cursor = gl1_render_cursor;
  renderer->render_mouse = gl1_render_mouse;
  renderer->sync_screen = gl1_sync_screen;
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Joel Bouchard Lamontagne <logicow@gmail.com>
 * Copyright (C) 2007,2009 Alistair John Strachan <alistair@devzero.co.uk>
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#include <string.h>

#include "platform.h"
#include "render.h"
#include "renderers.h"
#include "util.h"

#ifdef CONFIG_SDL
#include "render_sdl.h"
#endif

#ifdef CONFIG_EGL
#include <GLES/gl.h>
#include "render_egl.h"
#endif

#include "render_gl.h"

// NOTE: This renderer should work with almost any GL capable video card.
//       However, if you plan to change it, please bear in mind that this
//       has been carefully written to work with both OpenGL 1.x and
//       OpenGL ES 1.x. The latter API lacks many functions present in
//       desktop OpenGL. GL ES is typically used on cellphones.

static struct
{
  void (GL_APIENTRY *glAlphaFunc)(GLenum func, GLclampf ref);
  void (GL_APIENTRY *glBindTexture)(GLenum target, GLuint texture);
  void (GL_APIENTRY *glBlendFunc)(GLenum sfactor, GLenum dfactor);
  void (GL_APIENTRY *glClear)(GLbitfield mask);
  void (GL_APIENTRY *glColorPointer)(GLint size, GLenum type, GLsizei stride,
   const GLvoid *pointer);
  void (GL_APIENTRY *glCopyTexImage2D)(GLenum target, GLint level,
   GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height,
   GLint border);
  void (GL_APIENTRY *glDisable)(GLenum cap);
  void (GL_APIENTRY *glDisableClientState)(GLenum cap);
  void (GL_APIENTRY *glDrawArrays)(GLenum mode, GLint first, GLsizei count);
  void (GL_APIENTRY *glEnable)(GLenum cap);
  void (GL_APIENTRY *glEnableClientState)(GLenum cap);
  void (GL_APIENTRY *glGenTextures)(GLsizei n, GLuint *textures);
  GLenum (GL_APIENTRY *glGetError)(void);
  const GLubyte* (GL_APIENTRY *glGetString)(GLenum name);
  void (GL_APIENTRY *glTexCoordPointer)(GLint size, GLenum type,
   GLsizei stride, const GLvoid *ptr);
  void (GL_APIENTRY *glTexImage2D)(GLenum target, GLint level,
   GLint internalformat, GLsizei width, GLsizei height, GLint border,
   GLenum format, GLenum type, const GLvoid *pixels);
  void (GL_APIENTRY *glTexParameterf)(GLenum target, GLenum pname,
   GLfloat param);
  void (GL_APIENTRY *glTexSubImage2D)(GLenum target, GLint level,
   GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
   GLenum format, GLenum type, const GLvoid *pixels);
  void (GL_APIENTRY *glVertexPointer)(GLint size, GLenum type,
   GLsizei stride, const GLvoid *ptr);
  void (GL_APIENTRY *glViewport)(GLint x, GLint y, GLsizei width,
   GLsizei height);
}
gl2;

static const struct dso_syms_map gl2_syms_map[] =
{
  { "glAlphaFunc",          (fn_ptr *)&gl2.glAlphaFunc },
  { "glBindTexture",        (fn_ptr *)&gl2.glBindTexture },
  { "glBlendFunc",          (fn_ptr *)&gl2.glBlendFunc },
  { "glClear",              (fn_ptr *)&gl2.glClear },
  { "glColorPointer",       (fn_ptr *)&gl2.glColorPointer },
  { "glCopyTexImage2D",     (fn_ptr *)&gl2.glCopyTexImage2D },
  { "glDisable",            (fn_ptr *)&gl2.glDisable },
  { "glDisableClientState", (fn_ptr *)&gl2.glDisableClientState },
  { "glDrawArrays",         (fn_ptr *)&gl2.glDrawArrays },
  { "glEnable",             (fn_ptr *)&gl2.glEnable },
  { "glEnableClientState",  (fn_ptr *)&gl2.glEnableClientState },
  { "glGenTextures",        (fn_ptr *)&gl2.glGenTextures },
  { "glGetError",           (fn_ptr *)&gl2.glGetError },
  { "glGetString",          (fn_ptr *)&gl2.glGetString },
  { "glTexCoordPointer",    (fn_ptr *)&gl2.glTexCoordPointer },
  { "glTexImage2D",         (fn_ptr *)&gl2.glTexImage2D },
  { "glTexParameterf",      (fn_ptr *)&gl2.glTexParameterf },
  { "glTexSubImage2D",      (fn_ptr *)&gl2.glTexSubImage2D },
  { "glVertexPointer",      (fn_ptr *)&gl2.glVertexPointer },
  { "glViewport",           (fn_ptr *)&gl2.glViewport },
  { NULL, NULL }
};

#define gl_check_error() gl_error(__FILE__, __LINE__, gl2.glGetError)

struct gl2_render_data
{
#ifdef CONFIG_EGL
  struct egl_render_data egl;
#endif
  Uint32 *pixels;
  Uint8 charset_texture[CHAR_H * CHARSET_SIZE * CHAR_W * 2];
  Uint32 background_texture[SCREEN_W * SCREEN_H];
  GLuint texture_number[3];
  GLubyte palette[3 * SMZX_PAL_SIZE];
  Uint8 remap_texture;
  Uint8 remap_char[CHARSET_SIZE * 2];
  Uint8 ignore_linear;
  enum ratio_type ratio;
  GLubyte color_array[SCREEN_W * SCREEN_H * 4 * 4];
  float tex_coord_array[SCREEN_W * SCREEN_H * 8];
  float vertex_array[SCREEN_W * SCREEN_H * 8];
};

static const GLubyte color_array_white[4 * 4] = {
  255, 255, 255, 255,
  255, 255, 255, 255,
  255, 255, 255, 255,
  255, 255, 255, 255
};

static bool gl2_init_video(struct graphics_data *graphics,
 struct config_info *conf)
{
  struct gl2_render_data *render_data = cmalloc(sizeof(struct gl2_render_data));

  if(!render_data)
    goto err_out;

  if(!GL_LoadLibrary(GL_LIB_FIXED))
    goto err_free_render_data;

  graphics->render_data = render_data;

  graphics->gl_vsync = conf->gl_vsync;
  graphics->allow_resize = conf->allow_resize;
  graphics->gl_filter_method = conf->gl_filter_method;
  graphics->bits_per_pixel = 32;

  // OpenGL only supports 16/32bit colour
  if(conf->force_bpp == 16 || conf->force_bpp == 32)
    graphics->bits_per_pixel = conf->force_bpp;

  // We want to deal internally with 32bit surfaces
  render_data->pixels = cmalloc(sizeof(Uint32) * GL_POWER_2_WIDTH *
   GL_POWER_2_HEIGHT);

  if(!render_data->pixels)
    goto err_free_render_data;

  render_data->ratio = conf->video_ratio;
  if(!set_video_mode())
    goto err_free_render_data;

  return true;

err_free_render_data:
  free(render_data);
err_out:
  return false;
}

static void gl2_free_video(struct graphics_data *graphics)
{
  free(graphics->render_data);
  graphics->render_data = NULL;
}

static void gl2_remap_charsets(struct graphics_data *graphics)
{
  struct gl2_render_data *render_data = graphics->render_data;
  render_data->remap_texture = true;
}

static void gl2_remap_char(struct graphics_data *graphics, Uint16 chr)
{
  struct gl2_render_data *render_data = graphics->render_data;
  render_data->remap_char[chr] = true;
}

static void gl2_remap_charbyte(struct graphics_data *graphics,
 Uint16 chr, Uint8 byte)
{
  gl2_remap_char(graphics, chr);
}

static void gl2_resize_screen(struct graphics_data *graphics,
 int width, int height)
{
  struct gl2_render_data *render_data = graphics->render_data;
  int v_width, v_height;

  // FIXME: Hack, remove
  get_context_width_height(graphics, &width, &height);

  // If the window is exactly 640x350, then any filtering is useless.
  // Turning filtering off here might speed things up.
  //
  // Otherwise, linear filtering breaks if the window is smaller than
  // 640x350, so also turn it off here.
  if(width == 640 && height == 350)
    render_data->ignore_linear = true;
  else if(width < 640 || height < 350)
    render_data->ignore_linear = true;
  else
    render_data->ignore_linear = false;

  fix_viewport_ratio(width, height, &v_width, &v_height, render_data->ratio);
  gl2.glViewport((width - v_width) >> 1, (height - v_height) >> 1,
   v_width, v_height);
  gl_check_error();

  gl2.glGenTextures(3, render_data->texture_number);
  gl_check_error();

  gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[0]);
  gl_check_error();

  gl_set_filter_method(graphics->gl_filter_method, gl2.glTexParameterf);

  gl2.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  gl2.glEnableClientState(GL_VERTEX_ARRAY);
  gl2.glEnableClientState(GL_COLOR_ARRAY);
  gl2.glEnable(GL_TEXTURE_2D);

  gl2.glAlphaFunc(GL_GREATER, 0.565f);
  gl_check_error();

  gl2.glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
  gl_check_error();

  memset(render_data->pixels, 255,
   sizeof(Uint32) * GL_POWER_2_WIDTH * GL_POWER_2_HEIGHT);

  gl2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GL_POWER_2_WIDTH,
   GL_POWER_2_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
   render_data->pixels);
  gl_check_error();

  gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
  gl_check_error();

  gl_set_filter_method(CONFIG_GL_FILTER_NEAREST, gl2.glTexParameterf);

  gl2.glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 256, 256, 0, GL_ALPHA,
   GL_UNSIGNED_BYTE, render_data->pixels);
  gl_check_error();

  gl2_remap_charsets(graphics);

  gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[2]);
  gl_check_error();

  gl_set_filter_method(CONFIG_GL_FILTER_NEAREST, gl2.glTexParameterf);

  gl2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 32, 0, GL_RGBA,
   GL_UNSIGNED_BYTE, render_data->pixels);
  gl_check_error();
}

static bool gl2_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  gl_set_attributes(graphics);

  if(!gl_set_video_mode(graphics, width, height, depth, fullscreen, resize))
    return false;

  if(!gl_load_syms(gl2_syms_map))
    return false;

  // We need a specific version of OpenGL; desktop GL must be 1.1.
  // All OpenGL ES 1.x implementations are supported, so don't do
  // the check with EGL configurations (EGL implies OpenGL ES).
#ifndef CONFIG_EGL
  {
    static bool initialized = false;

    if(!initialized)
    {
      const char *version;
      double version_float;

      version = (const char *)gl2.glGetString(GL_VERSION);
      if(!version)
        return false;

      version_float = atof(version);
      if(version_float < 1.1)
      {
        warn("Need >= OpenGL 1.1, got OpenGL %.1f.\n", version_float);
        return false;
      }

      initialized = true;
    }
  }
#endif

  gl2_resize_screen(graphics, width, height);
  return true;
}

static void gl2_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  struct gl2_render_data *render_data = graphics->render_data;
  Uint32 i;
  for(i = 0; i < count; i++)
  {
#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
    graphics->flat_intensity_palette[i] = (palette[i].r << 24) |
     (palette[i].g << 16) | (palette[i].b << 8);
#else
    graphics->flat_intensity_palette[i] = (palette[i].b << 16) |
     (palette[i].g << 8) | palette[i].r;
#endif
    render_data->palette[i*3  ] = (GLubyte)palette[i].r;
    render_data->palette[i*3+1] = (GLubyte)palette[i].g;
    render_data->palette[i*3+2] = (GLubyte)palette[i].b;
  }
}

static char *gl2_char_bitmask_to_texture(signed char *c, char *p)
{
  // This tests the 7th bit, if 0, result is 0.
  // If 1, result is 255 (because of sign extended bit shift!).
  // Note the use of char constants to force 8bit calculation.
  *(p++) = *c << 24 >> 31;
  *(p++) = *c << 25 >> 31;
  *(p++) = *c << 26 >> 31;
  *(p++) = *c << 27 >> 31;
  *(p++) = *c << 28 >> 31;
  *(p++) = *c << 29 >> 31;
  *(p++) = *c << 30 >> 31;
  *(p++) = *c << 31 >> 31;
  return p;
}

static inline void gl2_do_remap_charsets(struct graphics_data *graphics)
{
  struct gl2_render_data *render_data = graphics->render_data;
  signed char *c = (signed char *)graphics->charset;
  char *p = (char *)render_data->charset_texture;
  unsigned int i, j, k;

  for(i = 0; i < 16; i++, c += -14 + 32 * 14)
    for(j = 0; j < 14; j++, c += -32 * 14 + 1)
      for(k = 0; k < 32; k++, c += 14)
        p = gl2_char_bitmask_to_texture(c, p);

  gl2.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 32 * 8, 16 * 14, GL_ALPHA,
    GL_UNSIGNED_BYTE, render_data->charset_texture);
  gl_check_error();
}

static inline void gl2_do_remap_char(struct graphics_data *graphics,
 Uint16 chr)
{
  struct gl2_render_data *render_data = graphics->render_data;
  signed char *c = (signed char *)graphics->charset;
  char *p = (char *)render_data->charset_texture;
  unsigned int i;

  c += chr * 14;

  for(i = 0; i < 14; i++, c++)
    p = gl2_char_bitmask_to_texture(c, p);

  gl2.glTexSubImage2D(GL_TEXTURE_2D, 0, chr % 32 * 8, chr / 32 * 14, 8, 14,
   GL_ALPHA, GL_UNSIGNED_BYTE, render_data->charset_texture);
  gl_check_error();
}

static int gl2_linear_filter_method(struct graphics_data *graphics)
{
  struct gl2_render_data *render_data = graphics->render_data;

  if(render_data->ignore_linear)
    return false;
  return strcasecmp(graphics->gl_filter_method, CONFIG_GL_FILTER_LINEAR) == 0;
}

static void gl2_render_graph(struct graphics_data *graphics)
{
  struct gl2_render_data *render_data = graphics->render_data;
  struct char_element *src = graphics->text_video;
  Sint32 i, i2, i3;
  Uint32 *dest;

  if(!graphics->screen_mode)
  {
    float *tex_coord_array = render_data->tex_coord_array;
    float *vertex_array    = render_data->vertex_array;
    GLubyte *color_array   = render_data->color_array;

    static const float tex_coord_array_single[2 * 4] = {
      0.0f,           0.0f,
      0.0f,           25.0f / 32.0f,
      80.0f / 128.0f, 0.0f,
      80.0f / 128.0f, 25.0f / 32.0f
    };

    gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
    gl_check_error();

    if(render_data->remap_texture)
    {
      gl2_do_remap_charsets(graphics);
      render_data->remap_texture = false;
      memset(render_data->remap_char, false, sizeof(Uint8) * CHARSET_SIZE * 2);
    }
    else
    {
      for(i = 0; i < CHARSET_SIZE * 2; i++)
      {
        if(render_data->remap_char[i])
        {
          gl2_do_remap_char(graphics, i);
          render_data->remap_char[i] = false;
        }
      }
    }

    if(gl2_linear_filter_method(graphics))
    {
      int width, height;

      /* Put the initial rendering's viewport in the center of the screen
       * so that when it is linearly stretched in sync_screen() it is
       * masked by the stretched image.
       */
      get_context_width_height(graphics, &width, &height);
      gl2.glViewport((width - 640) >> 1, (height - 350) >> 1, 640, 350);
      gl_check_error();
    }

    dest = render_data->background_texture;

    for(i = 0; i < SCREEN_W * SCREEN_H; i++, dest++, src++)
     *dest = graphics->flat_intensity_palette[src->bg_color];

    gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[2]);
    gl_check_error();

    gl2.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, SCREEN_W, SCREEN_H, GL_RGBA,
     GL_UNSIGNED_BYTE, render_data->background_texture);
     gl_check_error();

    gl2.glTexCoordPointer(2, GL_FLOAT, 0, tex_coord_array_single);
    gl_check_error();

    gl2.glVertexPointer(2, GL_FLOAT, 0, vertex_array_single);
    gl_check_error();

    gl2.glColorPointer(4, GL_UNSIGNED_BYTE, 0, color_array_white);
    gl_check_error();

    gl2.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    gl_check_error();

    gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
    gl_check_error();

    gl2.glEnable(GL_ALPHA_TEST);

    src = graphics->text_video;

    gl2.glTexCoordPointer(2, GL_FLOAT, 0, render_data->tex_coord_array);
    gl_check_error();

    gl2.glVertexPointer(2, GL_FLOAT, 0, render_data->vertex_array);
    gl_check_error();

    gl2.glColorPointer(4, GL_UNSIGNED_BYTE, 0, render_data->color_array);
    gl_check_error();

    i = 0;
    while(true)
    {
      GLubyte *pal_base;

      for(i2 = 0; i2 < SCREEN_W; i2++)
      {
        GLubyte *pal_base = &render_data->palette[src->fg_color * 3];

        tex_coord_array[0] = ((src->char_value % 32) + 0) / 32.0f;
        tex_coord_array[1] = ((src->char_value / 32) + 1) * 14.0f / 256.0f;

        tex_coord_array[2] = ((src->char_value % 32) + 0) / 32.0f;
        tex_coord_array[3] = ((src->char_value / 32) + 0) * 14.0f / 256.0f;

        tex_coord_array[4] = ((src->char_value % 32) + 1) / 32.0f;
        tex_coord_array[5] = ((src->char_value / 32) + 1) * 14.0f / 256.0f;

        tex_coord_array[6] = ((src->char_value % 32) + 1) / 32.0f;
        tex_coord_array[7] = ((src->char_value / 32) + 0) * 14.0f / 256.0f;

        vertex_array[0] = (((i2     + 0) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[1] = (((25 - i - 1) * 2.0f) / 25.0f) - 1.0f;

        vertex_array[2] = (((i2     + 0) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[3] = (((25 - i + 0) * 2.0f) / 25.0f) - 1.0f;

        vertex_array[4] = (((i2     + 1) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[5] = (((25 - i - 1) * 2.0f) / 25.0f) - 1.0f;

        vertex_array[6] = (((i2     + 1) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[7] = (((25 - i + 0) * 2.0f) / 25.0f) - 1.0f;

        for(i3 = 0; i3 < 4; i3++)
        {
          memcpy(&color_array[i3 * 4], pal_base, 3);
          color_array[i3 * 4 + 3] = 255;
        }

        tex_coord_array += 8;
        vertex_array += 8;
        color_array += 4 * 4;
        src++;
      }

      if(++i == SCREEN_H)
        break;
      src += SCREEN_W;

      // Draw this row backwards, so we can keep the strip contiguous

      for(i2 = SCREEN_W - 1; i2 >= 0; i2--)
      {
        src--;

        pal_base = &render_data->palette[src->fg_color * 3];

        tex_coord_array[0] = ((src->char_value % 32) + 1) / 32.0f;
        tex_coord_array[1] = ((src->char_value / 32) + 1) * 14.0f / 256.0f;

        tex_coord_array[2] = ((src->char_value % 32) + 1) / 32.0f;
        tex_coord_array[3] = ((src->char_value / 32) + 0) * 14.0f / 256.0f;

        tex_coord_array[4] = ((src->char_value % 32) + 0) / 32.0f;
        tex_coord_array[5] = ((src->char_value / 32) + 1) * 14.0f / 256.0f;

        tex_coord_array[6] = ((src->char_value % 32) + 0) / 32.0f;
        tex_coord_array[7] = ((src->char_value / 32) + 0) * 14.0f / 256.0f;

        vertex_array[0] = (((i2     + 1) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[1] = (((25 - i - 1) * 2.0f) / 25.0f) - 1.0f;

        vertex_array[2] = (((i2     + 1) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[3] = (((25 - i + 0) * 2.0f) / 25.0f) - 1.0f;

        vertex_array[4] = (((i2     + 0) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[5] = (((25 - i - 1) * 2.0f) / 25.0f) - 1.0f;

        vertex_array[6] = (((i2     + 0) * 2.0f) / 80.0f) - 1.0f;
        vertex_array[7] = (((25 - i + 0) * 2.0f) / 25.0f) - 1.0f;

        for(i3 = 0; i3 < 4; i3++)
        {
          memcpy(&color_array[i3 * 4], pal_base, 3);
          color_array[i3 * 4 + 3] = 255;
        }

        tex_coord_array += 8;
        vertex_array += 8;
        color_array += 4 * 4;
      }

      if(++i == SCREEN_H)
        break;
      src += SCREEN_W;
    }

    gl2.glDrawArrays(GL_TRIANGLE_STRIP, 0, SCREEN_W * SCREEN_H * 4);
    gl_check_error();

    gl2.glDisable(GL_ALPHA_TEST);
  }
  else
  {
    static const float tex_coord_array_single[2 * 4] = {
      0.0f,             0.0f,
      0.0f,             350.0f / 512.0f,
      640.0f / 1024.0f, 0.0f,
      640.0f / 1024.0f, 350.0f / 512.0f,
    };

    render_graph32s(render_data->pixels, 640 * 4, graphics,
     set_colors32[graphics->screen_mode]);

    gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[0]);
    gl_check_error();

    gl2.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 640, 350, GL_RGBA,
      GL_UNSIGNED_BYTE, render_data->pixels);
    gl_check_error();

    gl2.glTexCoordPointer(2, GL_FLOAT, 0, tex_coord_array_single);
    gl_check_error();

    gl2.glVertexPointer(2, GL_FLOAT, 0, vertex_array_single);
    gl_check_error();

    gl2.glColorPointer(4, GL_UNSIGNED_BYTE, 0, color_array_white);
    gl_check_error();

    gl2.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    gl_check_error();

    gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
    gl_check_error();
  }
}

static void gl2_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  struct gl2_render_data *render_data = graphics->render_data;
  GLubyte *pal_base = &render_data->palette[color * 3];

  const float vertex_array[2 * 4] = {
    (x * 8)*2.0f/640.0f-1.0f,     (y * 14 + offset)*-2.0f/350.0f+1.0f,
    (x * 8)*2.0f/640.0f-1.0f,     (y * 14 + lines + offset)*-2.0f/350.0f+1.0f,
    (x * 8 + 8)*2.0f/640.0f-1.0f, (y * 14 + offset)*-2.0f/350.0f+1.0f,
    (x * 8 + 8)*2.0f/640.0f-1.0f, (y * 14 + lines + offset)*-2.0f/350.0f+1.0f
  };

  const GLubyte color_array[4 * 4] = {
    pal_base[0], pal_base[1], pal_base[2], 255,
    pal_base[0], pal_base[1], pal_base[2], 255,
    pal_base[0], pal_base[1], pal_base[2], 255,
    pal_base[0], pal_base[1], pal_base[2], 255
  };

  gl2.glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  gl2.glDisable(GL_TEXTURE_2D);

  gl2.glVertexPointer(2, GL_FLOAT, 0, vertex_array);
  gl_check_error();

  gl2.glColorPointer(4, GL_UNSIGNED_BYTE, 0, color_array);
  gl_check_error();

  gl2.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  gl_check_error();

  gl2.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  gl2.glEnable(GL_TEXTURE_2D);
}

static void gl2_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  const float vertex_array[2 * 4] = {
     x*2.0f/640.0f-1.0f,       y*-2.0f/350.0f+1.0f,
     x*2.0f/640.0f-1.0f,      (y + h)*-2.0f/350.0f+1.0f,
    (x + w)*2.0f/640.0f-1.0f,  y*-2.0f/350.0f+1.0f,
    (x + w)*2.0f/640.0f-1.0f, (y + h)*-2.0f/350.0f+1.0f
  };

  gl2.glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  gl2.glDisable(GL_TEXTURE_2D);
  gl2.glEnable(GL_BLEND);

  gl2.glVertexPointer(2, GL_FLOAT, 0, vertex_array);
  gl_check_error();

  gl2.glColorPointer(4, GL_UNSIGNED_BYTE, 0, color_array_white);
  gl_check_error();

  gl2.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  gl_check_error();

  gl2.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  gl2.glEnable(GL_TEXTURE_2D);
  gl2.glDisable(GL_BLEND);
}

static void gl2_sync_screen(struct graphics_data *graphics)
{
  struct gl2_render_data *render_data = graphics->render_data;

  if(gl2_linear_filter_method(graphics) && !graphics->screen_mode)
  {
    int v_width, v_height;
    int width, height;

    static const float tex_coord_array_single[2 * 4] = {
       0.0f,             350.0f / 512.0f,
       0.0f,             0.0f,
       640.0f / 1024.0f, 350.0f / 512.0f,
       640.0f / 1024.0f, 0.0f,
    };

    gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[0]);
    gl_check_error();

    get_context_width_height(graphics, &width, &height);
    gl2.glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
     (width - 640) >> 1, (height - 350) >> 1, 1024, 512, 0);
    gl_check_error();

    fix_viewport_ratio(width, height, &v_width, &v_height, render_data->ratio);
    gl2.glViewport((width - v_width) >> 1, (height - v_height) >> 1,
     v_width, v_height);
    gl_check_error();

    gl2.glClear(GL_COLOR_BUFFER_BIT);
    gl_check_error();

    gl2.glTexCoordPointer(2, GL_FLOAT, 0, tex_coord_array_single);
    gl_check_error();

    gl2.glVertexPointer(2, GL_FLOAT, 0, vertex_array_single);
    gl_check_error();

    gl2.glColorPointer(4, GL_UNSIGNED_BYTE, 0, color_array_white);
    gl_check_error();

    gl2.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    gl_check_error();

    gl2.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
    gl_check_error();
  }

  gl_swap_buffers(graphics);

  gl2.glClear(GL_COLOR_BUFFER_BIT);
  gl_check_error();
}

void render_gl2_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = gl2_init_video;
  renderer->free_video = gl2_free_video;
  renderer->check_video_mode = gl_check_video_mode;
  renderer->set_video_mode = gl2_set_video_mode;
  renderer->update_colors = gl2_update_colors;
  renderer->resize_screen = resize_screen_standard;
  renderer->remap_charsets = gl2_remap_charsets;
  renderer->remap_char = gl2_remap_char;
  renderer->remap_charbyte = gl2_remap_charbyte;
  renderer->get_screen_coords = get_screen_coords_scaled;
  renderer->set_screen_coords = set_screen_coords_scaled;
  renderer->render_graph = gl2_render_graph;
  renderer->render_cursor = gl2_render_cursor;
  renderer->render_mouse = gl2_render_mouse;
  renderer->sync_screen = gl2_sync_screen;
}