--- /dev/null
+/*
+ sdlfont.c
+
+ Copyright (C) 2010-2019 Amf
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "util.h"
+
+/* If USE_FREETYPE is defined, a pleasing stroked font is used. Alternative
+ code is provided to fall back to SDL_ttf, but this is plain by comparison.
+ */
+#define USE_FREETYPE
+
+#ifdef USE_FREETYPE
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_STROKER_H
+#else
+#include <SDL/SDL_ttf.h>
+#endif
+
+void font_init();
+void font_set_size(int size);
+void font_box(int x, int y, int w, int h, int c);
+int font_calculate_width(char *text, int cache);
+
+extern struct SDL_Surface* psurfacelogo;
+extern struct SDL_Surface* psurfacelogosmall;
+extern int screen_width;
+
+int font_colours[48][3] = {
+/* normal */
+ {0xcc, 0xcc, 0xcc}, {0x7f, 0x7f, 0x7f}, /* 0 */
+ {0xff, 0x99, 0x99}, {0xff, 0x00, 0x00}, /* 1 */
+ {0x99, 0xff, 0x99}, {0x00, 0xcc, 0x00}, /* 2 */
+ {0xff, 0xff, 0x33}, {0xff, 0xa0, 0x00}, /* 3 */
+ {0x99, 0x99, 0xff}, {0x00, 0x00, 0xff}, /* 4 */
+ {0xcc, 0x99, 0xff}, {0x80, 0x00, 0xff}, /* 5 */
+ {0x66, 0xb3, 0xff}, {0x00, 0x80, 0xff}, /* 6 */
+ {0xff, 0xff, 0xff}, {0x7f, 0x7f, 0x7f}, /* 7 */
+
+/* light */
+ {0xee, 0xee, 0xee}, {0x7f, 0x7f, 0x7f}, /* 0 */
+ {0xff, 0xcc, 0xcc}, {0xff, 0x00, 0x00}, /* 1 */
+ {0xcc, 0xff, 0xcc}, {0x00, 0xcc, 0x00}, /* 2 */
+ {0xff, 0xff, 0x99}, {0xff, 0xa0, 0x00}, /* 3 */
+ {0xcc, 0xcc, 0xff}, {0x00, 0x00, 0xff}, /* 4 */
+ {0xe6, 0xcc, 0xff}, {0x80, 0x00, 0xff}, /* 5 */
+ {0xb3, 0xd9, 0xff}, {0x00, 0x80, 0xff}, /* 6 */
+ {0xff, 0xff, 0xff}, {0x7f, 0x7f, 0x7f}, /* 7 */
+
+/* bold */
+ {0xaa, 0xaa, 0xaa}, {0x7f, 0x7f, 0x7f}, /* 0 */
+ {0xff, 0x5c, 0x5c}, {0xff, 0x00, 0x00}, /* 1 */
+ {0x5c, 0xff, 0x5c}, {0x00, 0xcc, 0x00}, /* 2 */
+ {0xff, 0xc0, 0x00}, {0xff, 0xa0, 0x00}, /* 3 */
+ {0x5c, 0x5c, 0xff}, {0x00, 0x00, 0xff}, /* 4 */
+ {0xae, 0x5c, 0xff}, {0x80, 0x00, 0xff}, /* 5 */
+ {0x29, 0x94, 0xff}, {0x00, 0x80, 0xff}, /* 6 */
+ {0xff, 0xff, 0xff}, {0x00, 0x00, 0x00} /* 7 white, black outline */
+};
+
+ /* chroma 1.01 : */
+char font_logo_colours[] = "1326454646644";
+
+int font_border = 0;
+int font_border_partial = 0;
+int font_padding = 0;
+
+extern SDL_Surface* screen_surface;
+
+int font_size_menu = 0;
+int font_size_game = 0;
+int font_height_menu = 0;
+int font_height_game = 0;
+
+#ifdef USE_FREETYPE
+
+/* ************ FreeType version ************ */
+
+FT_Library font_library;
+FT_Face font_face;
+FT_Stroker font_stroker;
+int font_height;
+int font_width;
+int font_descent;
+int font_size;
+
+#define FT_CEIL(X) (((X + 63) & -64) / 64)
+
+void font_init()
+{
+ char directory[FILENAME_MAX];
+ char filename[FILENAME_MAX];
+ FT_Error error;
+
+ getfilename("graphics", directory, 0, LOCATION_SYSTEM);
+
+ sprintf(filename, "%s/font.ttf", directory);
+
+ if((error = FT_Init_FreeType(&font_library)) != 0)
+ {
+ sprintf(filename, gettext("Unable to initialise freetype: error=%d"), error);
+ fatal(filename);
+ }
+ if((error = FT_New_Face(font_library, filename, 0, &font_face)) != 0)
+ {
+printf("%s\n", filename);
+ sprintf(filename, gettext("Unable to load font: error=%d"), error);
+ fatal(filename);
+ }
+
+ font_stroker = NULL;
+ font_height = 0;
+ font_descent = 0;
+ font_width = 0;
+ font_size = 0;
+}
+
+void font_set_size(int size)
+{
+ FT_Fixed scale;
+
+ if(size == font_size)
+ return;
+
+ if(FT_Set_Char_Size(font_face, 0, size * 64, 0, 0))
+ {
+ fatal(gettext("Unable to set font size"));
+ }
+ if(font_stroker != NULL)
+ {
+ FT_Stroker_Done(font_stroker);
+ }
+ if(FT_Stroker_New(font_library, &font_stroker))
+ {
+ fatal(gettext("Unable to create stroker"));
+ }
+ FT_Stroker_Set(font_stroker, size * 2, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
+
+ font_size = size;
+ scale = font_face->size->metrics.y_scale;
+ font_height = FT_CEIL(FT_MulFix(font_face->ascender, scale)) - FT_CEIL(FT_MulFix(font_face->descender, scale)) + 1;
+ font_descent = FT_CEIL(FT_MulFix(font_face->descender, scale));
+}
+
+int font_calculate_width(char *text, int cache)
+{
+ int n, x;
+ FT_Glyph glyph;
+ Uint16 c;
+ FT_UInt glyph_index;
+
+ /* Determine width of text */
+ x = 0;
+ for(n = 0; n < strlen(text); n ++)
+ {
+ /* Translate UTF8 to UNICODE */
+ c = ((unsigned char *)text)[n];
+ if(c >= 0xf0)
+ {
+ c = (text[n++] & 0x07) << 18;
+ c |= (text[n++] & 0x3f) << 12;
+ c |= (text[n++] & 0x3f) << 6;
+ c |= text[n] & 0x3f;
+ }
+ else if(c >= 0xe0)
+ {
+ c = (text[n++] & 0x0f) << 12;
+ c |= (text[n++] & 0x3f) << 6;
+ c |= text[n] & 0x3f;
+ }
+ else if(c >= 0xc0)
+ {
+ c = (text[n++] & 0x1f) << 6;
+ c |= text[n] & 0x3f;
+ }
+
+ glyph_index = FT_Get_Char_Index( font_face, c );
+ FT_Load_Glyph( font_face, glyph_index, FT_LOAD_DEFAULT );
+ FT_Get_Glyph( font_face->glyph, &glyph );
+ x += (glyph->advance.x) >> 16;
+ FT_Done_Glyph(glyph);
+ }
+
+ /* Add a little extra padding for the strokes around the edges */
+ x += (font_size / 10);
+ x += (font_size / 10);
+
+
+ if(cache)
+ font_width = x;
+
+ return x;
+}
+
+SDL_Surface* font_render(char *text, int foreground)
+{
+ int height, width;
+ int m, n, x;
+ int i, j, p, q;
+ struct SDL_Surface* psurface;
+ struct SDL_Surface* psurfacetmp = NULL;
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+ int pass;
+ Uint32 *dst;
+ Uint32 colour;
+ Uint16 c;
+ FT_UInt glyph_index;
+ FT_Bitmap* bitmap;
+
+ height = font_height;
+
+ /* Sometimes we work the width out in advanced, eg, for centering.
+ This optimisation saves us having to calculate it twice. */
+ if(font_width != 0)
+ width = font_width;
+ else
+ width = font_calculate_width(text, 1);
+ font_width = 0;
+
+ /* Render font in two passes - unstroked, then stroked */
+ for(pass = 0; pass < 2; pass ++)
+ {
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ psurface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xff000000, 0xff0000, 0xff00, 0xff);
+#else
+ psurface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xff, 0xff00, 0xff0000, 0xff000000);
+#endif
+ SDL_LockSurface(psurface);
+
+ x = 0;
+ for(n = 0, m = 0; n < strlen(text); n ++)
+ {
+ if(foreground < 0)
+ {
+ if(m < strlen(font_logo_colours))
+ colour = font_logo_colours[m ++] - 48;
+ else
+ colour = -foreground - 1;
+ }
+ else
+ colour = foreground;
+
+ colour =
+ font_colours[2 * colour + pass][0] +
+ font_colours[2 * colour + pass][1] * 0x100 +
+ font_colours[2 * colour + pass][2] * 0x10000;
+
+ /* Translate UTF8 to UNICODE */
+ c = ((unsigned char *)text)[n];
+ if(c >= 0xf0)
+ {
+ c = (text[n++] & 0x07) << 18;
+ c |= (text[n++] & 0x3f) << 12;
+ c |= (text[n++] & 0x3f) << 6;
+ c |= text[n] & 0x3f;
+ }
+ else if(c >= 0xe0)
+ {
+ c = (text[n++] & 0x0f) << 12;
+ c |= (text[n++] & 0x3f) << 6;
+ c |= text[n] & 0x3f;
+ }
+ else if(c >= 0xc0)
+ {
+ c = (text[n++] & 0x1f) << 6;
+ c |= text[n] & 0x3f;
+ }
+
+ glyph_index = FT_Get_Char_Index( font_face, c );
+ FT_Load_Glyph( font_face, glyph_index, FT_LOAD_DEFAULT );
+ FT_Get_Glyph( font_face->glyph, &glyph );
+ if(pass == 1)
+ FT_Glyph_StrokeBorder(&glyph, font_stroker, 0, 1);
+ FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
+ glyph_bitmap=(FT_BitmapGlyph)glyph;
+ bitmap = &(glyph_bitmap->bitmap);
+
+ for ( i = x + (font_size / 10) + glyph_bitmap->left, p = 0; p < bitmap->width; i++, p++ )
+ {
+ for ( j = height - glyph_bitmap->top - 1 + font_descent, q = 0; q < bitmap->rows; j++, q++ )
+ {
+ if(i < 0 || i >= width || j < 0 || j >= height)
+ continue;
+ if(bitmap->buffer[q * bitmap->width + p] == 0)
+ continue;
+ dst = psurface->pixels
+ + j * psurface->pitch
+ + i * psurface->format->BytesPerPixel;
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ *dst |= bitmap->buffer[q * bitmap->width + p] + colour * 0x100;
+#else
+ *dst |= bitmap->buffer[q * bitmap->width + p] * 0x1000000 + colour;
+#endif
+ }
+ }
+
+ x += ((glyph->advance.x) >> 16);
+ FT_Done_Glyph(glyph);
+ }
+
+ SDL_UnlockSurface(psurface);
+
+ if(pass == 0)
+ {
+ psurfacetmp = psurface;
+ }
+ }
+
+ SDL_BlitSurface(psurfacetmp, NULL, psurface, NULL);
+ SDL_FreeSurface(psurfacetmp);
+
+ return psurface;
+}
+
+#else
+
+/* ************ SDL_TTF version ************ */
+
+TTF_Font* font_face;
+int font_height;
+int font_width;
+
+void font_init()
+{
+ TTF_Init();
+
+ font_face = NULL;
+}
+
+void font_set_size(int size)
+{
+ char directory[FILENAME_MAX];
+ char filename[FILENAME_MAX];
+
+ getfilename("graphics", directory, 0, LOCATION_SYSTEM);
+
+ sprintf(filename, "%s/font.ttf", directory);
+
+ if(font_face != NULL)
+ TTF_CloseFont(font_face);
+
+ font_face = TTF_OpenFont(filename, size);
+ if(font_face == NULL)
+ fatal(gettext("Unable to open font"));
+ font_height = TTF_FontHeight(font_face);
+}
+
+int font_calculate_width(char *text, int cache)
+{
+ TTF_SizeUTF8(font_face, text, &font_width, NULL);
+ return font_width;
+}
+
+SDL_Surface* font_render(char *text, int foreground)
+{
+ SDL_Color colour;
+ SDL_Rect drect;
+ int i, j, w;
+ char buffer[256];
+ int c;
+ int width, height;
+ SDL_Surface* psurface;
+ SDL_Surface* psurfacetmp;
+
+ if(foreground < 0)
+ {
+ TTF_SizeUTF8(font_face, text, &width, &height);
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ psurface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xff000000, 0xff0000, 0xff00, 0xff);
+#else
+ psurface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xff, 0xff00, 0xff0000, 0xff000000);
+#endif
+ SDL_SetAlpha(psurface, SDL_SRCALPHA, 0);
+ SDL_FillRect(psurface, NULL, SDL_MapRGBA(screen_surface->format, 128, 0, 255, 128));
+
+ w = 0;
+ for(i = 0; i < strlen(text); i ++)
+ {
+ j = 0;
+ buffer[j] = text[i]; j ++;
+ while((text[i + j] & 0xc0) == 0x80)
+ {
+ buffer[j] = text[i + j]; j ++;
+ }
+ buffer[j] = 0;
+ i += j - 1;
+
+ if(i < strlen(font_logo_colours))
+ c = font_logo_colours[i] - 48;
+ else
+ c = -foreground - 1;
+
+ colour.r = font_colours[2 * c + 1][0];
+ colour.g = font_colours[2 * c + 1][1];
+ colour.b = font_colours[2 * c + 1][2];
+
+ psurfacetmp = TTF_RenderUTF8_Blended(font_face, buffer, colour);
+ SDL_SetAlpha(psurfacetmp, 0, 255);
+
+ drect.x = w;
+ drect.y = 0;
+ drect.w = psurfacetmp->w;
+ drect.h = psurfacetmp->h;
+
+ SDL_BlitSurface(psurfacetmp, NULL, psurface, &drect);
+ w += psurfacetmp->w;
+ SDL_FreeSurface(psurfacetmp);
+ }
+
+ return psurface;
+ }
+
+ colour.r = font_colours[2 * foreground + 1][0];
+ colour.g = font_colours[2 * foreground + 1][1];
+ colour.b = font_colours[2 * foreground + 1][2];
+ if(text == NULL || strcmp(text, "") == 0)
+ return TTF_RenderUTF8_Blended(font_face, " ", colour);
+ else
+ return TTF_RenderUTF8_Blended(font_face, text, colour);
+}
+
+#endif
+
+void font_box(int x, int y, int w, int h, int c)
+{
+ SDL_Rect rect;
+ int r, g, b;
+
+ /* If the box is too small, fill it all with the border colour */
+ if(w <= font_border * 2 || h <= font_border * 2)
+ {
+
+ r = font_colours[2 * c + 1][0];
+ g = font_colours[2 * c + 1][1];
+ b = font_colours[2 * c + 1][2];
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+
+ return;
+ }
+
+ /* Plot the inside of the box */
+ r = font_colours[2 * c][0];
+ g = font_colours[2 * c][1];
+ b = font_colours[2 * c][2];
+
+ rect.x = x + font_border;
+ rect.y = y + font_border;
+ rect.w = w - (font_border * 2);
+ rect.h = h - (font_border * 2);
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+
+ r = font_colours[2 * c + 1][0];
+ g = font_colours[2 * c + 1][1];
+ b = font_colours[2 * c + 1][2];
+
+ /* Plot the outer border in solid border colour */
+ if(font_border > 1)
+ {
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = (font_border - 1);
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+ rect.x = x;
+ rect.y = y + h - (font_border - 1);
+ rect.w = w;
+ rect.h = (font_border - 1);
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = (font_border - 1);
+ rect.h = h;
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+ rect.x = x + w - (font_border - 1);
+ rect.y = y;
+ rect.w = (font_border - 1);
+ rect.h = h;
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+ }
+
+ /* Plot a one-pixel inner border to give an anti-aliased effect */
+ r = (font_colours[2 * c + 0][0] * (256 - font_border_partial) + font_colours[2 * c + 1][0] * font_border_partial ) / 256;
+ g = (font_colours[2 * c + 0][1] * (256 - font_border_partial) + font_colours[2 * c + 1][1] * font_border_partial ) / 256;
+ b = (font_colours[2 * c + 0][2] * (256 - font_border_partial) + font_colours[2 * c + 1][2] * font_border_partial ) / 256;
+
+ rect.x = x + (font_border - 1);
+ rect.y = y + (font_border - 1);
+ rect.w = w - 2 * (font_border - 1);
+ rect.h = 1;
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+ rect.x = x + (font_border - 1);
+ rect.y = y + h - font_border;
+ rect.w = w - 2 * (font_border - 1);
+ rect.h = 1;
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+ rect.x = x + (font_border - 1);
+ rect.y = y + (font_border - 1);
+ rect.w = 1;
+ rect.h = h - 2 * (font_border - 1);
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+ rect.x = x + w - font_border;
+ rect.y = y + (font_border - 1);
+ rect.w = 1;
+ rect.h = h - 2 * (font_border - 1);
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, r, g, b));
+}
+
+void font_resize()
+{
+ int size;
+
+ size = screen_width / 32;
+
+ font_size_menu = size;
+ font_size_game = size * 3 / 4;
+
+ if(psurfacelogo != NULL)
+ SDL_FreeSurface(psurfacelogo);
+ font_set_size(size * 6);
+ psurfacelogo = font_render("chroma", -1);
+
+ if(psurfacelogo != NULL)
+ SDL_FreeSurface(psurfacelogosmall);
+ font_set_size(size * 3 / 2);
+ psurfacelogosmall = font_render("chroma", -1);
+
+ font_set_size(font_size_game);
+ font_height_game = font_height;
+
+ font_set_size(font_size_menu);
+ font_height_menu = font_height;
+
+ font_border = size / 24;
+ font_padding = size / 24;
+ font_border_partial = ((256 * size) / 24) - (256 * font_border);
+
+ font_border ++;
+ font_padding ++;
+}