Small bugfixes.
[xuni.git] / src / widget / font.c
blobd7b66ca00f8198c9a548099d4fdee2ae4be214ac
1 /*! \file font.c
2 Font handling functions.
3 */
5 #include <stdlib.h>
6 #include <string.h>
7 #include <limits.h>
8 #include <math.h>
10 #include "SDL.h"
11 #include "SDL_ttf.h"
12 #include "SDL_gfxPrimitives.h"
13 #include "SDL_rotozoom.h"
15 /* split cannot parse gfxPrimitivesFontdata[] */
16 #ifdef SPLINT
17 #define GFX_FONTDATAMAX (8*256)
19 extern unsigned char gfxPrimitivesFontdata[GFX_FONTDATAMAX];
20 #else
21 #include "SDL_gfxPrimitives_font.h"
22 #endif
24 #include "../graphics.h"
25 #include "../memory.h"
26 #include "../error.h"
27 #include "widgets.h"
28 #include "font.h"
30 #define GFX_FONT_WIDTH CHAR_BIT
31 #define GFX_FONT_HEIGHT (GFX_FONTDATAMAX / UCHAR_MAX)
33 static void free_font(struct xuni_t *xuni, struct widget_t *widget);
34 static void rescale_font(struct xuni_t *xuni, struct widget_t *widget);
36 static void new_font_t(struct widget_t *widget, const char *name);
37 static int pick_font_point(struct xuni_t *xuni);
38 static SDL_Surface *render_gfx_text(const char *text, int r, int g, int b);
40 void font_widget_event(struct xuni_t *xuni, struct widget_t *widget,
41 enum widget_event_t event) {
43 static void (*function[])(struct xuni_t *xuni, struct widget_t *widget)
44 = {
46 free_font,
50 rescale_font
53 call_widget_event_func(xuni, widget, event, function,
54 sizeof(function) / sizeof(*function));
57 void init_font(struct widget_t *widget, const char *name) {
58 widget->type = WIDGET_FONT;
59 new_font_t(widget, name);
62 static void new_font_t(struct widget_t *widget, const char *name) {
63 widget->p.font = xuni_memory_allocate(sizeof(*widget->p.font));
65 widget->p.font->name = xuni_memory_duplicate_string(name);
66 widget->p.font->point = 0;
67 widget->p.font->ttf = 0;
70 /*! Finds or creates a font widget with the file name \a name. In other words,
71 if a font of this name has been loaded in the past, this existing widget
72 is used. Otherwise, a new widget is created and returned. Presumably this
73 will be added to the widget tree and can be found by a later call to this
74 function.
76 \param widget The structure to search through for existing font widgets.
77 (Actually, all searching is done in the first widget found called
78 "font".)
79 \param name The name of the font to find, or if not found, to create.
81 struct widget_t *load_font(struct widget_t *widget, const char *name) {
82 struct widget_t *fontpanel = find_widget(widget, "font"); /* !!! */
83 size_t x;
85 if(!fontpanel || !name || !*name) return 0;
87 for(x = 0; x < fontpanel->compose->widgets; x ++) {
88 if(fontpanel->compose->widget[x]->type == WIDGET_FONT
89 && !strcmp(fontpanel->compose->widget[x]->p.font->name, name)) {
91 return fontpanel->compose->widget[x];
95 add_allocate_widget(fontpanel, (char *)name);
96 init_widget_pos(last_compose_widget(fontpanel), 0, 0, 1, 1,
97 POS_PACK_NONE);
98 init_font(last_compose_widget(fontpanel), name);
100 return last_compose_widget(fontpanel);
103 void use_font_by_name(struct xuni_t *xuni, size_t n,
104 struct widget_t *widget) {
106 struct widget_t **font;
108 if(get_theme_widget(xuni, n)) {
109 font = &xuni->theme->current->p.theme->nameid->widget[n].widget;
110 free_font(xuni, *font);
111 if(widget) {
112 xuni_memory_increment(widget->p.font);
113 (*font)->p.font = widget->p.font;
115 else new_font_t(*font, "");
117 widget_event(xuni, *font, WIDGET_EVENT_RESCALE);
118 widget_event(xuni, xuni->gui->widget, WIDGET_EVENT_UPDATE_TEXT);
122 static void free_font(struct xuni_t *xuni, struct widget_t *widget) {
123 if(xuni_memory_decrement(widget->p.font)) {
124 if(widget->p.font->ttf) TTF_CloseFont(widget->p.font->ttf);
125 else {
126 /* hack to free SDL_gfx's statically stored, dynamically allocated
127 memory for each character (glyph) */
128 gfxPrimitivesSetFont(gfxPrimitivesFontdata,
129 GFX_FONT_WIDTH, GFX_FONT_HEIGHT);
132 xuni_memory_free((void *)widget->p.font->name);
134 free(widget->p.font);
138 static void rescale_font(struct xuni_t *xuni, struct widget_t *widget) {
139 struct font_t *font = widget->p.font;
141 font->point = pick_font_point(xuni);
143 if(font->ttf) TTF_CloseFont(font->ttf);
144 font->ttf = TTF_OpenFont(font->name, font->point);
146 if(font->ttf) {
147 TTF_SetFontStyle(font->ttf, TTF_STYLE_NORMAL);
149 else {
150 if(!font->name || *font->name) {
151 log_message(ERROR_TYPE_WARNING, 0, __FILE__, __LINE__,
152 "Can't open font \"%s\", using built-in fixed-size %ix%i"
153 " bitmap font", font->name, GFX_FONT_WIDTH, GFX_FONT_HEIGHT);
156 gfxPrimitivesSetFont(gfxPrimitivesFontdata,
157 GFX_FONT_WIDTH, GFX_FONT_HEIGHT);
161 double get_font_ratio(struct xuni_t *xuni) {
162 return 1.0;
163 return (xuni->smode->screen->w / (double)xuni->smode->screen->h)
164 / (4.0 / 3.0);
167 /*! Picks a reasonable font size for the current screen resolution, according
168 to the following formula:
169 point-size = round(min(screen-width / ratio, screen-height) / 30.0)
170 \param xuni Contains information about the current screen resolution that
171 xuni is using.
172 \return The right font size for the current screen resolution.
174 static int pick_font_point(struct xuni_t *xuni) {
175 double ratio = get_font_ratio(xuni);
176 int mindim = (xuni->smode->width / ratio < xuni->smode->height
177 ? xuni->smode->width / ratio : xuni->smode->height);
178 int point = (int)(mindim / 30.0 + 0.5);
180 /*printf("Picking font size %i\n", (int)point);*/
182 /* restrict the font point size to a minimum of 2
183 libfreetype6 2.3.5-1+b1 often crashes with point size 1
185 if(point <= 1) return 2;
187 return point;
190 int font_height(struct xuni_t *xuni, struct widget_t *widget) {
191 if(widget && widget->p.font->ttf) {
192 return TTF_FontHeight(widget->p.font->ttf);
194 else return GFX_FONT_HEIGHT;
197 int font_string_width(struct xuni_t *xuni, struct widget_t *widget,
198 const char *str) {
200 int width;
202 if(widget && widget->p.font->ttf) {
203 TTF_SizeText(widget->p.font->ttf, str, &width, 0);
205 else width = GFX_FONT_WIDTH * strlen(str);
207 return (int)(width * get_font_ratio(xuni) + 0.5);
210 /*! Creates a new surface with text blitted to it via SDL_gfx's string*()
211 code; that is, with a built-in bitmap font. A surface is generated to make
212 this function compatible with SDL_ttf's TTF_RenderText_*() functions.
214 Uses a bit of a hack to do the transparency, because SDL_gfx's string*()
215 functions calculate their own alpha. For example, when the surface to blit
216 to has an alpha of 128, and SDL_gfx is requested to blit an alpha of 128,
217 the actual alpha in the end is (128/256)*(128/256)*256 = 64, not the
218 expected 128.
220 \param text The string to blit to the new surface.
221 \param r The red component of the colour of the new surface
222 (0 to 255 inclusive, like \a g and \a b).
223 \param g The green component of the colour of the text on the new surface.
224 \param b The blue component of the text colour, again from 0 to 255
225 inclusive.
226 \return A new SDL_Surface with the string in \a text blitted to it in the
227 colour (\a r, \a g, \a b).
229 static SDL_Surface *render_gfx_text(const char *text, int r, int g, int b) {
230 int bg = ((r + g + b) / 3.0 < 128) ? 255 : 0;
232 SDL_Surface *image = new_surface(
233 GFX_FONT_WIDTH * strlen(text), GFX_FONT_HEIGHT, 32, 0);
234 SDL_FillRect(image, NULL, SDL_MapRGB(image->format, bg, bg, bg));
236 stringRGBA(image, 0, 0, text, r, g, b, 255);
238 SDL_SetColorKey(image, SDL_SRCCOLORKEY,
239 SDL_MapRGB(image->format, bg, bg, bg));
241 return image;
244 SDL_Surface *render_unrescaled_text(struct widget_t *widget, const char *text,
245 Uint8 r, Uint8 g, Uint8 b) {
247 if(!text || !*text) return NULL;
249 if(widget && widget->p.font->ttf) {
250 SDL_Color col;
252 col.r = r;
253 col.g = g;
254 col.b = b;
256 return TTF_RenderText_Blended(widget->p.font->ttf, text, col);
259 return render_gfx_text(text, r, g, b);
262 SDL_Surface *render_text(struct xuni_t *xuni, struct widget_t *widget,
263 const char *text, Uint8 r, Uint8 g, Uint8 b) {
265 SDL_Surface *rescaled, *unrescaled;
266 double ratio, xratio, yratio;
268 unrescaled = render_unrescaled_text(widget, text, r, g, b);
270 ratio = get_font_ratio(xuni);
272 if(!unrescaled || fabs(ratio - 1.0) < 0.001) return unrescaled;
274 #if 0
275 xratio = (ratio > 1.0) ? ratio : 1.0;
276 yratio = (ratio < 1.0) ? /*1.0 /*/ ratio : 1.0;
277 #else
278 if(ratio > 1.0) {
279 xratio = ratio;
280 yratio = 1.0;
282 else {
283 xratio = ratio;
284 yratio = 1.0 / ratio;
286 #endif
288 #if 0
289 rescaled = zoomSurface(unrescaled,
290 /*widget->pos->scale.w / 100.0
291 / ((double)xuni->smode->screen->w / widget->base->pos->real.w)
292 * xuni->smode->screen->w / temp->w*/ xratio,
293 /*widget->pos->scale.h / 100.0
294 / ((double)xuni->smode->screen->h / widget->base->pos->real.h)
295 * xuni->smode->screen->h / temp->h*/ yratio,
296 SMOOTHING_ON);
297 #elif 0
298 rescaled = sge_transform_surface(unrescaled,
299 SDL_MapRGB(xuni->smode->screen->format, 0, 0, 0),
300 0.0f, xratio, yratio, 0);
301 #else
302 rescaled = zoomSurface(unrescaled,
303 (int)(unrescaled->w * xratio + 0.5) / (double)unrescaled->w,
304 (int)(unrescaled->h * yratio + 0.5) / (double)unrescaled->h,
305 SMOOTHING_ON);
306 #endif
308 SDL_FreeSurface(unrescaled);
310 return rescaled;