1 /*  SDL_Pango.c -- A companion library to SDL for working with Pango.
2     Copyright (C) 2004 NAKAMURA Ken'ichi
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Lesser General Public
6     License as published by the Free Software Foundation; either
7     version 2.1 of the License, or (at your option) any later version.
8 
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Lesser General Public License for more details.
13 
14     You should have received a copy of the GNU Lesser General Public
15     License along with this library; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
17 */
18 
19 /*!
20     \mainpage
21 
22     \section intro Introduction
23 
24     Pango is the text rendering engine of GNOME 2.x. SDL_Pango connects the
25     engine to SDL. In Windows, pre-built binary package (MSI and merge module)
26     is provided.
27 
28     \subsection dist Distribution
29 
30     If you are a game software developer, you should know the difficulties of
31     distribution. So I will start to introduce SDL_Pango from the viewpoint
32     of distribution.
33 
34     In Un*x, SDL_Pango is hard to use as system-independent module, because
35     it depends on fontconfig and Pango which are designed as system-singleton
36     modules. If you use SDL_Pango, your software will require those modules
37     installed to target system. If your software is shipped as shrink-wrap
38     package, it may cause much problem on your support desk. You should
39     carefully design your installation process.
40 
41     In Windows, SDL_Pango is distributed as "merge module" which contains
42     fontconfig and Pango. Those binaries are modified as side-by-side components.
43     You should use Windows Installer and merge the module
44     on your MSI package. The merge module not only contains files, but also includes
45     custom action which must be run at installation.
46 
47     \subsection api High-level API
48 
49     From the viewpoint of text rendering, the heart of SDL_Pango is high-level API.
50     Other text rendering APIs, like DrawText() of Windows, font and text must be
51     specified separately. In SDL_Pango, font specification is embedded in text like
52     HTML:
53 
54     \code
55     <span font_family="Courier New"><i>This is Courier New and italic.</i></span>
56     \endcode
57 
58     Color, size, subscript/superscript, obliquing, weight, and other many features
59     are also available in same way.
60 
61     \subsection i18n Internationalized Text
62 
63     Internationalized text is another key feature. Text is specified by UTF-8. RTL
64     script (Arabic and Hebrew) and complicated rendering (Arabic, Indic and Thai) are
65     supported. You can see it with GNOME 2.x.
66 
67     \section get Getting Started
68 
69     \subsection getlatest Get latest files
70 
71     Get latest files from http://sourceforge.net/projects/sdlpango/ .
72 
73     \subsection install Install Header and Library
74 
75     In Windows and VS2003, I strongly recommend you to install MSI package. It contains Pango
76     and fontconfig binaries which are modified as side-by-side components. It is
77     nearly impossible to build them. (I spent much time to build them...)
78 
79     In MinGW, I recommend you to use VS2003. Otherwise you may run into the maze of
80     distribution. If you insist MinGW, you should use MinGW binary archive.
81 
82     In Un*x, installation consists of:
83 
84     \code
85     ./configure
86     make
87     make install
88     \endcode
89 
90     \subsection inc Includes
91 
92     To use SDL_Pango functions in a C/C++ source code file, you must use the SDL_Pango.h
93     include file:
94 
95     \code
96     #include "SDL_Pango.h"
97     \endcode
98 
99     In Windows, SDL_Pango.h is installed on \c \%ProgramFiles\%\\SDL_Pango \c Development\\include
100     (usually \c C:\\Program \c Files\\SDL_Pango \c Development\\include). You should add this
101     directory to include path.
102 
103     \subsection comp Compiling
104 
105     In Un*x, to link with SDL_Pango you should use sdl-config to get the required SDL
106     compilation options. After that, compiling with SDL_Pango is quite easy.
107 
108     Note: Some systems may not have the SDL_Pango library and include file in the same
109     place as the SDL library and includes are located, in that case you will need to
110     add more -I and -L paths to these command lines.
111 
112     Simple Example for compiling an object file:
113 
114     \code
115     cc -c `sdl-config --cflags` mysource.c
116     \endcode
117 
118     Simple Example for linking an object file:
119 
120     \code
121     cc -o myprogram mysource.o `sdl-config --libs` -lSDL_Pango
122     \endcode
123 
124     Now myprogram is ready to run.
125 
126     You can see a sample of autoconfiscation in 'test' directory.
127 
128     In Windows, MSI package installs many dlls to \c \%ProgramFiles\%\\SDL_Pango \c Development\\import_lib.
129     To link with SDL_Pango you should use SDL_Pango.lib.
130 
131     SDL_Pango.dll depends on many dlls and other many files. Those dlls are installed on
132     \c \%ProgramFiles\%\\SDL_Pango \c Development\\bin. MSI package adds the directory to PATH environment
133     variable.
134 
135     \section devel Development
136 
137     \subsection font Font Handling
138 
139     In Un*x, font handling depends on fontconfig of your system.
140 
141     In Windows, local.conf of fontconfig is placed on \c \%ProgramFiles\%\\SDL_Pango \c Development\\etc\\fonts.
142     You should know about fontconfig's font cache mechanism.
143 
144     \subsection example Step-by-step Example
145 
146     The operation of SDL_Pango is done via context.
147 
148     \code
149     SDLPango_Context *context = SDLPango_CreateContext();
150     \endcode
151 
152     Specify default colors and minimum surface size.
153 
154     \code
155     SDLPango_SetDefaultColor(context, MATRIX_TRANSPARENT_BACK_WHITE_LETTER);
156     SDLPango_SetMinimumSize(context, 640, 0);
157     \endcode
158 
159     Set markup text.
160 
161     \code
162     SDLPango_SetMarkup(context, "This is <i>markup</i> text.", -1);
163     \endcode
164 
165     Now you can get the size of surface.
166 
167     \code
168     int w = SDLPango_GetLayoutWidth(context);
169     int h = SDLPango_GetLayoutHeight(context);
170     \endcode
171 
172     Create surface to draw.
173 
174     \code
175     int margin_x = 10;
176     int margin_y = 10;
177     SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
178 	w + margin_x * 2, h + margin_y * 2,
179 	32, (Uint32)(255 << (8 * 3)), (Uint32)(255 << (8 * 2)),
180 	(Uint32)(255 << (8 * 1)), 255);
181     \endcode
182 
183     And draw on it.
184 
185     \code
186     SDLPango_Draw(context, surface, margin_x, margin_y);
187     \endcode
188 
189     You must free the surface by yourself.
190 
191     \code
192     SDL_FreeSurface(surface);
193     \endcode
194 
195     Free context.
196 
197     \code
198     SDLPango_FreeContext(context);
199     \endcode
200 
201     You can see actual code in \c test/testbench.cpp.
202 
203     \subsection pack Packaging
204 
205     In Un*x, do it yourself.
206 
207     In Windows, font files must be installed on apprication folder (usually
208     \c C:\\Program \c Files\\[Manufacturer]\\[ProductName]). The property of
209     apprication folder must be \c TARGETDIR (this is default setting of VS2003).
210     SDL.dll also must be installed on apprication folder. Add SDL_Pango.msm to
211     your MSI package.
212 
213     \section ack Acknowledgment
214 
215     SDL_Pango is developed with financial assistance of Information-technology Promotion Agency, Japan.
216 
217 - NAKAMURA Ken'ichi <nakamura@sbp.fp.a.u-tokyo.ac.jp>
218 
219 */
220 
221 /*! @file
222     @brief Implementation of SDL_Pango
223 
224     @author NAKAMURA Ken'ichi
225     @date   2004/12/07
226     $Revision: 1.6 $
227 */
228 
229 #include <pango/pango.h>
230 #include <pango/pangoft2.h>
231 
232 #include "SDL_Pango.h"
233 
234 //! non-zero if initialized
235 static int IS_INITIALIZED = 0;
236 
237 #define DEFAULT_FONT_FAMILY "Sans"
238 #define DEFAULT_FONT_SIZE 12
239 #define DEFAULT_DPI 96
240 #define _MAKE_FONT_NAME(family, size) family " " #size
241 #define MAKE_FONT_NAME(family, size) _MAKE_FONT_NAME(family, size)
242 #define DEFAULT_DEPTH 32
243 #define DEFAULT_RMASK (Uint32)(255 << (8 * 3))
244 #define DEFAULT_GMASK (Uint32)(255 << (8 * 2))
245 #define DEFAULT_BMASK (Uint32)(255 << (8 * 1))
246 #define DEFAULT_AMASK (Uint32)255
247 
248 static FT_Bitmap *createFTBitmap(int width, int height);
249 
250 static void freeFTBitmap(FT_Bitmap *bitmap);
251 
252 static void getItemProperties (
253     PangoItem      *item,
254     PangoUnderline *uline,
255     gboolean       *strikethrough,
256     gint           *rise,
257     PangoColor     *fg_color,
258     gboolean       *fg_set,
259     PangoColor     *bg_color,
260     gboolean       *bg_set,
261     gboolean       *shape_set,
262     PangoRectangle *ink_rect,
263     PangoRectangle *logical_rect);
264 
265 static void clearFTBitmap(FT_Bitmap *bitmap);
266 
267 typedef struct _surfaceArgs {
268     Uint32 flags;
269     int depth;
270     Uint32 Rmask;
271     Uint32 Gmask;
272     Uint32 Bmask;
273     Uint32 Amask;
274 } surfaceArgs;
275 
276 typedef struct _contextImpl {
277     PangoContext *context;
278     PangoFontMap *font_map;
279     PangoFontDescription *font_desc;
280     PangoLayout *layout;
281     surfaceArgs surface_args;
282     FT_Bitmap *tmp_ftbitmap;
283     SDLPango_Matrix color_matrix;
284     int min_width;
285     int min_height;
286 } contextImpl;
287 
288 
289 /*!
290     Initialize the Glib and Pango API.
291     This must be called before using other functions in this library,
292     excepting SDLPango_WasInit.
293     SDL does not have to be initialized before this call.
294 
295 
296     @return always 0.
297 */
298 int
SDLPango_Init()299 SDLPango_Init()
300 {
301     g_type_init();
302 
303     IS_INITIALIZED = -1;
304 
305     return 0;
306 }
307 
308 /*!
309     Query the initilization status of the Glib and Pango API.
310     You may, of course, use this before SDLPango_Init to avoid
311     initilizing twice in a row.
312 
313     @return zero when already initialized.
314     non-zero when not initialized.
315 */
316 int
SDLPango_WasInit()317 SDLPango_WasInit()
318 {
319     return IS_INITIALIZED;
320 }
321 
322 /*!
323     Draw glyphs on rect.
324 
325     @param *context [in] Context
326     @param *surface [out] Surface to draw on it
327     @param *color_matrix [in] Foreground and background color
328     @param *font [in] Innter variable of Pango
329     @param *glyphs [in] Innter variable of Pango
330     @param *rect [in] Draw on this area
331     @param baseline [in] Horizontal location of glyphs
332 */
333 void SDLPango_CopyFTBitmapToSurface(
334     const FT_Bitmap *bitmap,
335     SDL_Surface *surface,
336     const SDLPango_Matrix *matrix,
337     SDL_Rect *rect);
338 static void
drawGlyphString(SDLPango_Context * context,SDL_Surface * surface,SDLPango_Matrix * color_matrix,PangoFont * font,PangoGlyphString * glyphs,SDL_Rect * rect,int baseline)339 drawGlyphString(
340     SDLPango_Context *context,
341     SDL_Surface *surface,
342     SDLPango_Matrix *color_matrix,
343     PangoFont *font,
344     PangoGlyphString *glyphs,
345     SDL_Rect *rect,
346     int baseline)
347 {
348     pango_ft2_render(context->tmp_ftbitmap, font, glyphs, rect->x, rect->y + baseline);
349 
350     SDLPango_CopyFTBitmapToSurface(
351 	context->tmp_ftbitmap,
352 	surface,
353 	color_matrix,
354 	rect);
355 
356     clearFTBitmap(context->tmp_ftbitmap);
357 }
358 
359 /*!
360     Draw horizontal line of a pixel.
361 
362     @param *surface [out] Surface to draw on it
363     @param *color_matrix [in] Foreground and background color
364     @param y [in] Y location of line
365     @param start [in] Left of line
366     @param end [in] Right of line
367 */
drawHLine(SDL_Surface * surface,SDLPango_Matrix * color_matrix,int y,int start,int end)368 static void drawHLine(
369     SDL_Surface *surface,
370     SDLPango_Matrix *color_matrix,
371     int y,
372     int start,
373     int end)
374 {
375     Uint8 *p;
376     Uint16 *p16;
377     Uint32 *p32;
378     Uint32 color;
379     int ix;
380     int pixel_bytes = surface->format->BytesPerPixel;
381 
382     if (y < 0 || y >= surface->h)
383 	return;
384 
385     if (end <= 0 || start >= surface->w)
386 	return;
387 
388     if (start < 0)
389 	start = 0;
390 
391     if (end >= surface->w)
392 	end = surface->w;
393 
394     p = (Uint8 *)(surface->pixels) + y * surface->pitch + start * pixel_bytes;
395     color = SDL_MapRGBA(surface->format,
396 	color_matrix->m[0][1],
397 	color_matrix->m[1][1],
398 	color_matrix->m[2][1],
399 	color_matrix->m[3][1]);
400 
401     switch(pixel_bytes) {
402     case 2:
403 	p16 = (Uint16 *)p;
404 	for (ix = 0; ix < end - start; ix++)
405 	    *p16++ = (Uint16)color;
406 	break;
407     case 4:
408 	p32 = (Uint32 *)p;
409 	for (ix = 0; ix < end - start; ix++)
410 	    *p32++ = color;
411 	break;
412     default:
413 	SDL_SetError("surface->format->BytesPerPixel is invalid value");
414 	break;
415     }
416 }
417 
418 /*!
419     Draw a line.
420 
421     @param *context [in] Context
422     @param *surface [out] Surface to draw on it
423     @param *line [in] Innter variable of Pango
424     @param x [in] X location of line
425     @param y [in] Y location of line
426     @param height [in] Height of line
427     @param baseline [in] Rise / sink of line (for super/subscript)
428 */
429 static void
drawLine(SDLPango_Context * context,SDL_Surface * surface,PangoLayoutLine * line,gint x,gint y,gint height,gint baseline)430 drawLine(
431     SDLPango_Context *context,
432     SDL_Surface *surface,
433     PangoLayoutLine *line,
434     gint x,
435     gint y,
436     gint height,
437     gint baseline)
438 {
439     GSList *tmp_list = line->runs;
440     PangoColor fg_color, bg_color;
441     PangoRectangle logical_rect;
442     PangoRectangle ink_rect;
443     int x_off = 0;
444 
445     while (tmp_list) {
446 	SDLPango_Matrix color_matrix = context->color_matrix;
447 	PangoUnderline uline = PANGO_UNDERLINE_NONE;
448 	gboolean strike, fg_set, bg_set, shape_set;
449 	gint rise, risen_y;
450 	PangoLayoutRun *run = tmp_list->data;
451 	SDL_Rect d_rect;
452 
453 	tmp_list = tmp_list->next;
454 
455 	getItemProperties(run->item,
456 	    &uline, &strike, &rise,
457 	    &fg_color, &fg_set, &bg_color, &bg_set,
458 	    &shape_set, &ink_rect, &logical_rect);
459 
460 	risen_y = y + baseline - PANGO_PIXELS (rise);
461 
462 	if(fg_set) {
463 	    color_matrix.m[0][1] = (Uint8)(fg_color.red >> 8);
464 	    color_matrix.m[1][1] = (Uint8)(fg_color.green >> 8);
465 	    color_matrix.m[2][1] = (Uint8)(fg_color.blue >> 8);
466 	    color_matrix.m[3][1] = 255;
467 	    if(color_matrix.m[3][0] == 0) {
468 		color_matrix.m[0][0] = (Uint8)(fg_color.red >> 8);
469 		color_matrix.m[1][0] = (Uint8)(fg_color.green >> 8);
470 		color_matrix.m[2][0] = (Uint8)(fg_color.blue >> 8);
471 	    }
472 	}
473 
474 	if (bg_set) {
475 	    color_matrix.m[0][0] = (Uint8)(bg_color.red >> 8);
476 	    color_matrix.m[1][0] = (Uint8)(bg_color.green >> 8);
477 	    color_matrix.m[2][0] = (Uint8)(bg_color.blue >> 8);
478 	    color_matrix.m[3][0] = 255;
479 	}
480 
481 	if(! shape_set) {
482 	    if (uline == PANGO_UNDERLINE_NONE)
483 		pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
484 					    NULL, &logical_rect);
485 	    else
486 		pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
487 					    &ink_rect, &logical_rect);
488 
489 	    d_rect.w = (Uint16)PANGO_PIXELS(logical_rect.width);
490 	    d_rect.h = (Uint16)height;
491 	    d_rect.x = (Uint16)(x + PANGO_PIXELS (x_off));
492 	    d_rect.y = (Uint16)(risen_y - baseline);
493 
494 	    if((! context->tmp_ftbitmap) || d_rect.w + d_rect.x > context->tmp_ftbitmap->width
495 		|| d_rect.h + d_rect.y > context->tmp_ftbitmap->rows)
496 	    {
497 		freeFTBitmap(context->tmp_ftbitmap);
498 		context->tmp_ftbitmap = createFTBitmap(d_rect.w + d_rect.x, d_rect.h + d_rect.y);
499 	    }
500 
501 	    drawGlyphString(context, surface,
502 		&color_matrix,
503 		run->item->analysis.font, run->glyphs, &d_rect, baseline);
504 	}
505         switch (uline) {
506 	case PANGO_UNDERLINE_NONE:
507 	    break;
508 	case PANGO_UNDERLINE_DOUBLE:
509 	    drawHLine(surface, &color_matrix,
510 		risen_y + 4,
511 		x + PANGO_PIXELS (x_off + ink_rect.x),
512 		x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width));
513 	  /* Fall through */
514 	case PANGO_UNDERLINE_SINGLE:
515 	    drawHLine(surface, &color_matrix,
516 		risen_y + 2,
517 		x + PANGO_PIXELS (x_off + ink_rect.x),
518 		x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width));
519 	    break;
520 	case PANGO_UNDERLINE_ERROR:
521 	    {
522 		int point_x;
523 		int counter = 0;
524 		int end_x = x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width);
525 
526 		for (point_x = x + PANGO_PIXELS (x_off + ink_rect.x) - 1;
527 		    point_x <= end_x;
528 		    point_x += 2)
529 		{
530 		    if (counter)
531 			drawHLine(surface, &color_matrix,
532 			    risen_y + 2,
533 			    point_x, MIN (point_x + 1, end_x));
534 		    else
535 			drawHLine(surface, &color_matrix,
536 			    risen_y + 3,
537 			    point_x, MIN (point_x + 1, end_x));
538 
539 		    counter = (counter + 1) % 2;
540 		}
541 	    }
542 	    break;
543 	case PANGO_UNDERLINE_LOW:
544 	    drawHLine(surface, &color_matrix,
545 		risen_y + PANGO_PIXELS (ink_rect.y + ink_rect.height),
546 		x + PANGO_PIXELS (x_off + ink_rect.x),
547 		x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width));
548 	  break;
549 	}
550 
551         if (strike)
552 	    drawHLine(surface, &color_matrix,
553 		risen_y + PANGO_PIXELS (logical_rect.y + logical_rect.height / 2),
554 		x + PANGO_PIXELS (x_off + logical_rect.x),
555 		x + PANGO_PIXELS (x_off + logical_rect.x + logical_rect.width));
556 
557 	x_off += logical_rect.width;
558     }
559 }
560 
561 /*!
562     Innter function of Pango. Stolen from GDK.
563 
564     @param *item [in] The item to get property
565     @param *uline [out] Kind of underline
566     @param *strikethrough [out] Strike-through line
567     @param *rise [out] Rise/sink of line (for super/subscript)
568     @param *fg_color [out] Color of foreground
569     @param *fg_set [out] True if fg_color set
570     @param *bg_color [out] Color of background
571     @param *bg_set [out] True if bg_color valid
572     @param *shape_set [out] True if ink_rect and logical_rect valid
573     @param *ink_rect [out] Ink rect
574     @param *logical_rect [out] Logical rect
575 */
576 static void
getItemProperties(PangoItem * item,PangoUnderline * uline,gboolean * strikethrough,gint * rise,PangoColor * fg_color,gboolean * fg_set,PangoColor * bg_color,gboolean * bg_set,gboolean * shape_set,PangoRectangle * ink_rect,PangoRectangle * logical_rect)577 getItemProperties (
578     PangoItem *item,
579     PangoUnderline *uline,
580     gboolean *strikethrough,
581     gint *rise,
582     PangoColor *fg_color,
583     gboolean *fg_set,
584     PangoColor *bg_color,
585     gboolean *bg_set,
586     gboolean *shape_set,
587     PangoRectangle *ink_rect,
588     PangoRectangle *logical_rect)
589 {
590     GSList *tmp_list = item->analysis.extra_attrs;
591 
592     if (strikethrough)
593 	*strikethrough = FALSE;
594 
595     if (fg_set)
596         *fg_set = FALSE;
597 
598     if (bg_set)
599 	*bg_set = FALSE;
600 
601     if (shape_set)
602 	*shape_set = FALSE;
603 
604     if (rise)
605 	*rise = 0;
606 
607     while (tmp_list) {
608 	PangoAttribute *attr = tmp_list->data;
609 
610 	switch (attr->klass->type) {
611 	case PANGO_ATTR_UNDERLINE:
612 	    if (uline)
613 		*uline = ((PangoAttrInt *)attr)->value;
614 	    break;
615 
616 	case PANGO_ATTR_STRIKETHROUGH:
617 	    if (strikethrough)
618 		*strikethrough = ((PangoAttrInt *)attr)->value;
619 	    break;
620 
621 	case PANGO_ATTR_FOREGROUND:
622 	    if (fg_color)
623 		*fg_color = ((PangoAttrColor *)attr)->color;
624 	    if (fg_set)
625 		*fg_set = TRUE;
626 	    break;
627 
628 	case PANGO_ATTR_BACKGROUND:
629 	    if (bg_color)
630 		*bg_color = ((PangoAttrColor *)attr)->color;
631 	    if (bg_set)
632 		*bg_set = TRUE;
633 	    break;
634 
635 	case PANGO_ATTR_SHAPE:
636 	    if (shape_set)
637 		*shape_set = TRUE;
638 	    if (logical_rect)
639 		*logical_rect = ((PangoAttrShape *)attr)->logical_rect;
640 	    if (ink_rect)
641 		*ink_rect = ((PangoAttrShape *)attr)->ink_rect;
642 	    break;
643 
644 	case PANGO_ATTR_RISE:
645 	    if (rise)
646 		*rise = ((PangoAttrInt *)attr)->value;
647 	    break;
648 
649 	default:
650 	    break;
651 	}
652 	tmp_list = tmp_list->next;
653     }
654 }
655 
656 /*!
657     Copy bitmap to surface.
658     From (x, y)-(w, h) to (x, y)-(w, h) of rect.
659 
660     @param *bitmap [in] Grayscale bitmap
661     @param *surface [out] Surface
662     @param *matrix [in] Foreground and background color
663     @param *rect [in] Rect to copy
664 */
665 void
SDLPango_CopyFTBitmapToSurface(const FT_Bitmap * bitmap,SDL_Surface * surface,const SDLPango_Matrix * matrix,SDL_Rect * rect)666 SDLPango_CopyFTBitmapToSurface(
667     const FT_Bitmap *bitmap,
668     SDL_Surface *surface,
669     const SDLPango_Matrix *matrix,
670     SDL_Rect *rect)
671 {
672     int i;
673     Uint8 *p_ft;
674     Uint8 *p_sdl;
675     int width = rect->w;
676     int height = rect->h;
677     int x = rect->x;
678     int y = rect->y;
679 
680     if(x + width > surface->w) {
681 	width = surface->w - x;
682 	if(width <= 0)
683 	    return;
684     }
685     if(y + height > surface->h) {
686 	height = surface->h - y;
687 	if(height <= 0)
688 	    return;
689     }
690 
691     if(SDL_LockSurface(surface)) {
692 	SDL_SetError("surface lock failed");
693 	SDL_FreeSurface(surface);
694 	return;
695     }
696 
697     p_ft = (Uint8 *)bitmap->buffer + (bitmap->pitch * y);
698     p_sdl = (Uint8 *)surface->pixels + (surface->pitch * y);
699     for(i = 0; i < height; i ++) {
700 	int k;
701 	for(k = 0; k < width; k ++) {
702 	    /* TODO: rewrite by matrix calculation library */
703 	    Uint8 pixel[4];	/* 4: RGBA */
704 	    int n;
705 
706 	    for(n = 0; n < 4; n ++) {
707 		Uint16 w;
708 		w = ((Uint16)matrix->m[n][0] * (256 - p_ft[k + x])) + ((Uint16)matrix->m[n][1] * p_ft[k + x]);
709 		pixel[n] = (Uint8)(w >> 8);
710 	    }
711 
712 	    switch(surface->format->BytesPerPixel) {
713 	    case 2:
714 		((Uint16 *)p_sdl)[k + x] = (Uint16)SDL_MapRGBA(surface->format, pixel[0], pixel[1], pixel[2], pixel[3]);
715 		break;
716 	    case 4:
717 		((Uint32 *)p_sdl)[k + x] = SDL_MapRGBA(surface->format, pixel[0], pixel[1], pixel[2], pixel[3]);
718 		break;
719 	    default:
720 		SDL_SetError("surface->format->BytesPerPixel is invalid value");
721 		return;
722 	    }
723 	}
724 	p_ft += bitmap->pitch;
725 	p_sdl += surface->pitch;
726     }
727 
728     SDL_UnlockSurface(surface);
729 }
730 
731 SDLPango_Context*
SDLPango_CreateContext_GivenFontDesc(const char * font_desc)732 SDLPango_CreateContext_GivenFontDesc(const char* font_desc)
733 {
734     SDLPango_Context *context = g_malloc(sizeof(SDLPango_Context));
735     G_CONST_RETURN char *charset;
736 
737     context->font_map = pango_ft2_font_map_new ();
738     pango_ft2_font_map_set_resolution (PANGO_FT2_FONT_MAP (context->font_map), DEFAULT_DPI, DEFAULT_DPI);
739 
740     context->context = pango_ft2_font_map_create_context (PANGO_FT2_FONT_MAP (context->font_map));
741 
742     g_get_charset(&charset);
743     pango_context_set_language (context->context, pango_language_from_string (charset));
744     pango_context_set_base_dir (context->context, PANGO_DIRECTION_LTR);
745 
746     context->font_desc = pango_font_description_from_string(font_desc);
747 
748     context->layout = pango_layout_new (context->context);
749 
750     SDLPango_SetSurfaceCreateArgs(context, SDL_SWSURFACE | SDL_SRCALPHA, DEFAULT_DEPTH,
751 	DEFAULT_RMASK, DEFAULT_GMASK, DEFAULT_BMASK, DEFAULT_AMASK);
752 
753     context->tmp_ftbitmap = NULL;
754 
755     context->color_matrix = *MATRIX_TRANSPARENT_BACK_BLACK_LETTER;
756 
757     context->min_height = 0;
758     context->min_width = 0;
759 
760     return context;
761 }
762 
763 /*!
764     Create a context which contains Pango objects.
765 
766     @return A pointer to the context as a SDLPango_Context*.
767 */
768 SDLPango_Context*
SDLPango_CreateContext()769 SDLPango_CreateContext()
770 {
771      SDLPango_CreateContext_GivenFontDesc(MAKE_FONT_NAME(DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE));
772 }
773 
774 /*!
775     Free a context.
776 
777     @param *context [i/o] Context to be free
778 */
779 void
SDLPango_FreeContext(SDLPango_Context * context)780 SDLPango_FreeContext(SDLPango_Context *context)
781 {
782     freeFTBitmap(context->tmp_ftbitmap);
783 
784     g_object_unref (context->layout);
785 
786     pango_font_description_free(context->font_desc);
787 
788     g_object_unref(context->context);
789 
790     g_object_unref(context->font_map);
791 
792     g_free(context);
793 }
794 
795 /*!
796     Specify Arguments when create a surface.
797     When SDL_Pango creates a surface, the arguments are used.
798 
799     @param *context [i/o] Context
800     @param flags [in] Same as SDL_CreateRGBSurface()
801     @param depth [in] Same as SDL_CreateRGBSurface()
802     @param Rmask [in] Same as SDL_CreateRGBSurface()
803     @param Gmask [in] Same as SDL_CreateRGBSurface()
804     @param Bmask [in] Same as SDL_CreateRGBSurface()
805     @param Amask [in] Same as SDL_CreateRGBSurface()
806 */
807 void
SDLPango_SetSurfaceCreateArgs(SDLPango_Context * context,Uint32 flags,int depth,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)808 SDLPango_SetSurfaceCreateArgs(
809     SDLPango_Context *context,
810     Uint32 flags,
811     int depth,
812     Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
813 {
814     context->surface_args.flags = flags;
815     context->surface_args.depth = depth;
816     context->surface_args.Rmask = Rmask;
817     context->surface_args.Gmask = Gmask;
818     context->surface_args.Bmask = Bmask;
819     context->surface_args.Amask = Amask;
820 }
821 
822 /*!
823     Create a surface and draw text on it.
824     The size of surface is same as lauout size.
825 
826     @param *context [in] Context
827     @return A newly created surface
828 */
SDLPango_CreateSurfaceDraw(SDLPango_Context * context)829 SDL_Surface * SDLPango_CreateSurfaceDraw(
830     SDLPango_Context *context)
831 {
832     PangoRectangle logical_rect;
833     SDL_Surface *surface;
834     int width, height;
835 
836     pango_layout_get_extents (context->layout, NULL, &logical_rect);
837     width = PANGO_PIXELS (logical_rect.width);
838     height = PANGO_PIXELS (logical_rect.height);
839     if(width < context->min_width)
840 	width = context->min_width;
841     if(height < context->min_height)
842 	height = context->min_height;
843 
844     surface = SDL_CreateRGBSurface(
845 	context->surface_args.flags,
846 	width, height, context->surface_args.depth,
847 	context->surface_args.Rmask,
848 	context->surface_args.Gmask,
849 	context->surface_args.Bmask,
850 	context->surface_args.Amask);
851 
852     SDLPango_Draw(context, surface, 0, 0);
853 
854     return surface;
855 }
856 
857 /*!
858     Draw text on a existing surface.
859 
860     @param *context [in] Context
861     @param *surface [i/o] Surface to draw on it
862     @param x [in] X of left-top of drawing area
863     @param y [in] Y of left-top of drawing area
864 */
865 void
SDLPango_Draw(SDLPango_Context * context,SDL_Surface * surface,int x,int y)866 SDLPango_Draw(
867     SDLPango_Context *context,
868     SDL_Surface *surface,
869     int x, int y)
870 {
871     PangoLayoutIter *iter;
872     PangoRectangle logical_rect;
873     int width, height;
874 
875     if(! surface) {
876 	SDL_SetError("surface is NULL");
877 	return;
878     }
879 
880     iter = pango_layout_get_iter (context->layout);
881 
882     pango_layout_get_extents (context->layout, NULL, &logical_rect);
883     width = PANGO_PIXELS (logical_rect.width);
884     height = PANGO_PIXELS (logical_rect.height);
885 
886     SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, 0, 0, 0, 0));
887 
888     if((! context->tmp_ftbitmap) || context->tmp_ftbitmap->width < width
889 	|| context->tmp_ftbitmap->rows < height)
890     {
891 	freeFTBitmap(context->tmp_ftbitmap);
892         context->tmp_ftbitmap = createFTBitmap(width, height);
893     }
894 
895     do {
896 	PangoLayoutLine *line;
897 	int baseline;
898 
899 	line = pango_layout_iter_get_line (iter);
900 
901 	pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
902 	baseline = pango_layout_iter_get_baseline (iter);
903 
904 	drawLine(
905 	    context,
906 	    surface,
907 	    line,
908 	    x + PANGO_PIXELS (logical_rect.x),
909 	    y + PANGO_PIXELS (logical_rect.y),
910 	    PANGO_PIXELS (logical_rect.height),
911 	    PANGO_PIXELS (baseline - logical_rect.y));
912     } while (pango_layout_iter_next_line (iter));
913 
914     pango_layout_iter_free (iter);
915 }
916 
917 /*!
918     Allocate buffer and create a FTBitmap object.
919 
920     @param width [in] Width
921     @param height [in] Height
922     @return FTBitmap object
923 */
924 static FT_Bitmap *
createFTBitmap(int width,int height)925 createFTBitmap(
926     int width, int height)
927 {
928     FT_Bitmap *bitmap;
929     guchar *buf;
930 
931     bitmap = g_malloc(sizeof(FT_Bitmap));
932     bitmap->width = width;
933     bitmap->rows = height;
934     bitmap->pitch = (width + 3) & ~3;
935     bitmap->num_grays = 256;
936     bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
937     buf = g_malloc (bitmap->pitch * bitmap->rows);
938     memset (buf, 0x00, bitmap->pitch * bitmap->rows);
939     bitmap->buffer = buf;
940 
941     return bitmap;
942 }
943 
944 /*!
945     Free a FTBitmap object.
946 
947     @param *bitmap [i/o] FTbitmap to be free
948 */
949 static void
freeFTBitmap(FT_Bitmap * bitmap)950 freeFTBitmap(
951     FT_Bitmap *bitmap)
952 {
953     if(bitmap) {
954 	g_free(bitmap->buffer);
955 	g_free(bitmap);
956     }
957 }
958 
959 /*!
960     Clear a FTBitmap object.
961 
962     @param *bitmap [i/o] FTbitmap to be clear
963 */
964 static void
clearFTBitmap(FT_Bitmap * bitmap)965 clearFTBitmap(
966     FT_Bitmap *bitmap)
967 {
968     Uint8 *p = (Uint8 *)bitmap->buffer;
969     int length = bitmap->pitch * bitmap->rows;
970 
971     memset(p, 0, length);
972 }
973 
974 /*!
975     Specify minimum size of drawing rect.
976 
977     @param *context [i/o] Context
978     @param width [in] Width. -1 means no wrapping mode.
979     @param height [in] Height. zero/minus value means non-specified.
980 */
981 void
SDLPango_SetMinimumSize(SDLPango_Context * context,int width,int height)982 SDLPango_SetMinimumSize(
983     SDLPango_Context *context,
984     int width, int height)
985 {
986     int pango_width;
987     if(width > 0)
988 	pango_width = width * PANGO_SCALE;
989     else
990 	pango_width = -1;
991     pango_layout_set_width(context->layout, pango_width);
992 
993     context->min_width = width;
994     context->min_height = height;
995 }
996 
997 /*!
998     Specify default color.
999 
1000     @param *context [i/o] Context
1001     @param *color_matrix [in] Foreground and background color
1002 */
1003 void
SDLPango_SetDefaultColor(SDLPango_Context * context,const SDLPango_Matrix * color_matrix)1004 SDLPango_SetDefaultColor(
1005     SDLPango_Context *context,
1006     const SDLPango_Matrix *color_matrix)
1007 {
1008     context->color_matrix = *color_matrix;
1009 }
1010 
1011 /*!
1012     Get layout width.
1013 
1014     @param *context [in] Context
1015     @return Width
1016 */
1017 int
SDLPango_GetLayoutWidth(SDLPango_Context * context)1018 SDLPango_GetLayoutWidth(
1019     SDLPango_Context *context)
1020 {
1021     PangoRectangle logical_rect;
1022 
1023     pango_layout_get_extents (context->layout, NULL, &logical_rect);
1024 
1025     return PANGO_PIXELS (logical_rect.width);
1026 }
1027 
1028 /*!
1029     Get layout height.
1030 
1031     @param *context [in] Context
1032     @return Height
1033 */
1034 int
SDLPango_GetLayoutHeight(SDLPango_Context * context)1035 SDLPango_GetLayoutHeight(
1036     SDLPango_Context *context)
1037 {
1038     PangoRectangle logical_rect;
1039 
1040     pango_layout_get_extents (context->layout, NULL, &logical_rect);
1041 
1042     return PANGO_PIXELS (logical_rect.height);
1043 }
1044 
1045 /*!
1046     Set markup text to context.
1047     Text must be utf-8.
1048     Markup format is same as pango.
1049 
1050     @param *context [i/o] Context
1051     @param *markup [in] Markup text
1052     @param length [in] Text length. -1 means NULL-terminated text.
1053 */
1054 void
SDLPango_SetMarkup(SDLPango_Context * context,const char * markup,int length)1055 SDLPango_SetMarkup(
1056     SDLPango_Context *context,
1057     const char *markup,
1058     int length)
1059 {
1060     pango_layout_set_markup (context->layout, markup, length);
1061     pango_layout_set_auto_dir (context->layout, TRUE);
1062     pango_layout_set_alignment (context->layout, PANGO_ALIGN_LEFT);
1063     pango_layout_set_font_description (context->layout, context->font_desc);
1064 }
1065 
1066 void
SDLPango_SetText_GivenAlignment(SDLPango_Context * context,const char * text,int length,SDLPango_Alignment alignment)1067 SDLPango_SetText_GivenAlignment(
1068     SDLPango_Context *context,
1069     const char *text,
1070     int length,
1071     SDLPango_Alignment alignment)
1072 {
1073     pango_layout_set_attributes(context->layout, NULL);
1074     pango_layout_set_text (context->layout, text, length);
1075     pango_layout_set_auto_dir (context->layout, TRUE);
1076     pango_layout_set_alignment (context->layout, alignment);
1077     pango_layout_set_font_description (context->layout, context->font_desc);
1078 }
1079 
1080 /*!
1081     Set plain text to context.
1082     Text must be utf-8.
1083 
1084     @param *context [i/o] Context
1085     @param *text [in] Plain text
1086     @param length [in] Text length. -1 means NULL-terminated text.
1087 */
1088 void
SDLPango_SetText(SDLPango_Context * context,const char * text,int length)1089 SDLPango_SetText(
1090     SDLPango_Context *context,
1091     const char *text,
1092     int length)
1093 {
1094      SDLPango_SetText_GivenAlignment(context, text, length, SDLPANGO_ALIGN_LEFT);
1095 }
1096 
1097 /*!
1098     Set DPI to context.
1099 
1100     @param *context [i/o] Context
1101     @param dpi_x [in] X dpi
1102     @param dpi_y [in] Y dpi
1103 */
1104 void
SDLPango_SetDpi(SDLPango_Context * context,double dpi_x,double dpi_y)1105 SDLPango_SetDpi(
1106     SDLPango_Context *context,
1107     double dpi_x, double dpi_y)
1108 {
1109     pango_ft2_font_map_set_resolution (PANGO_FT2_FONT_MAP (context->font_map), dpi_x, dpi_y);
1110 }
1111 
1112 /*!
1113     Set language to context.
1114 
1115     @param *context [i/o] Context
1116     @param *language_tag [in] A RFC-3066 format language tag
1117 */
SDLPango_SetLanguage(SDLPango_Context * context,const char * language_tag)1118 void SDLCALL SDLPango_SetLanguage(
1119     SDLPango_Context *context,
1120     const char *language_tag)
1121 {
1122     pango_context_set_language (context->context, pango_language_from_string (language_tag));
1123 }
1124 
1125 /*!
1126     Set base direction to context.
1127 
1128     @param *context [i/o] Context
1129     @param direction [in] Direction
1130 */
SDLPango_SetBaseDirection(SDLPango_Context * context,SDLPango_Direction direction)1131 void SDLCALL SDLPango_SetBaseDirection(
1132     SDLPango_Context *context,
1133     SDLPango_Direction direction)
1134 {
1135     PangoDirection pango_dir;
1136 
1137     switch(direction) {
1138     case SDLPANGO_DIRECTION_LTR:
1139 	pango_dir = PANGO_DIRECTION_LTR;
1140 	break;
1141     case SDLPANGO_DIRECTION_RTL:
1142 	pango_dir = PANGO_DIRECTION_RTL;
1143 	break;
1144     case SDLPANGO_DIRECTION_WEAK_LTR:
1145 	pango_dir = PANGO_DIRECTION_WEAK_LTR;
1146 	break;
1147     case SDLPANGO_DIRECTION_WEAK_RTL:
1148 	pango_dir = PANGO_DIRECTION_WEAK_RTL;
1149 	break;
1150     case SDLPANGO_DIRECTION_NEUTRAL:
1151 	pango_dir = PANGO_DIRECTION_NEUTRAL;
1152 	break;
1153     default:
1154 	SDL_SetError("unknown direction value");
1155 	return;
1156     }
1157 
1158     pango_context_set_base_dir (context->context, pango_dir);
1159 }
1160 
1161 /*!
1162     Get font map from context.
1163 
1164     @param *context [in] Context
1165     @return Font map
1166 */
SDLPango_GetPangoFontMap(SDLPango_Context * context)1167 PangoFontMap* SDLCALL SDLPango_GetPangoFontMap(
1168     SDLPango_Context *context)
1169 {
1170     return context->font_map;
1171 }
1172 
1173 /*!
1174     Get font description from context.
1175 
1176     @param *context [in] Context
1177     @return Font description
1178 */
SDLPango_GetPangoFontDescription(SDLPango_Context * context)1179 PangoFontDescription* SDLCALL SDLPango_GetPangoFontDescription(
1180     SDLPango_Context *context)
1181 {
1182     return context->font_desc;
1183 }
1184 
1185 /*!
1186     Get layout from context.
1187 
1188     @param *context [in] Context
1189     @return Layout
1190 */
SDLPango_GetPangoLayout(SDLPango_Context * context)1191 PangoLayout* SDLCALL SDLPango_GetPangoLayout(
1192     SDLPango_Context *context)
1193 {
1194     return context->layout;
1195 }
1196