1 /*
2   pygame - Python Game Library
3   Copyright (C) 2009 Vicent Marti
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Library General Public
7   License as published by the Free Software Foundation; either
8   version 2 of the License, or (at your option) any later version.
9 
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Library General Public License for more details.
14 
15   You should have received a copy of the GNU Library General Public
16   License along with this library; if not, write to the Free
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19 */
20 
21 #define PYGAME_FREETYPE_INTERNAL
22 #define NO_PYGAME_C_API
23 
24 #include "ft_wrap.h"
25 #include FT_MODULE_H
26 
27 #if defined (PGFT_PYGAME1_COMPAT)
28 #   include "ft_pixel.h"
29 #elif defined (HAVE_PYGAME_SDL_VIDEO)
30 #   include "surface.h"
31 #endif
32 
__render_glyph_GRAY1(int x,int y,FontSurface * surface,const FT_Bitmap * bitmap,const FontColor * fg_color)33 void __render_glyph_GRAY1(int x, int y, FontSurface *surface,
34                           const FT_Bitmap *bitmap, const FontColor *fg_color)
35 {
36     FT_Byte *dst = ((FT_Byte *)surface->buffer) + x + (y * surface->pitch);
37     FT_Byte *dst_cpy;
38 
39     const FT_Byte *src = bitmap->buffer;
40     const FT_Byte *src_cpy;
41 
42     FT_Byte src_byte;
43     unsigned int j, i;
44 
45 #ifndef NDEBUG
46     const FT_Byte *src_end = src + (bitmap->rows * bitmap->pitch);
47     const FT_Byte *dst_end = ((FT_Byte *)surface->buffer +
48                               (surface->height * surface->pitch));
49 #endif
50 
51     assert(dst >= (FT_Byte *)surface->buffer && dst < dst_end);
52 
53     /*
54      * Assumption, target buffer was filled with zeros before any rendering.
55      */
56 
57     for (j = 0; j < bitmap->rows; ++j) {
58         src_cpy = src;
59         dst_cpy = dst;
60 
61         for (i = 0; i < bitmap->width; ++i) {
62             assert(src_cpy < src_end);
63             src_byte = *src_cpy;
64             if (src_byte) {
65                 assert(dst_cpy < dst_end);
66                 *dst_cpy = src_byte + *dst_cpy - src_byte * *dst_cpy / 255;
67             }
68             ++src_cpy;
69             ++dst_cpy;
70         }
71 
72         dst += surface->pitch;
73         src += bitmap->pitch;
74     }
75 }
76 
__render_glyph_MONO_as_GRAY1(int x,int y,FontSurface * surface,const FT_Bitmap * bitmap,const FontColor * fg_color)77 void __render_glyph_MONO_as_GRAY1(int x, int y, FontSurface *surface,
78                                   const FT_Bitmap *bitmap,
79                                   const FontColor *fg_color)
80 {
81     const int off_x = (x < 0) ? -x : 0;
82     const int off_y = (y < 0) ? -y : 0;
83 
84     const int max_x = MIN(x + bitmap->width, surface->width);
85     const int max_y = MIN(y + bitmap->rows, surface->height);
86 
87     const int rx = MAX(0, x);
88     const int ry = MAX(0, y);
89 
90     int i, j, shift;
91     const unsigned char* src;
92     unsigned char* dst;
93     const unsigned char*  src_cpy;
94     unsigned char* dst_cpy;
95     FT_UInt32 val;
96     FT_Byte shade = fg_color->a;
97 
98     src  = bitmap->buffer + (off_y * bitmap->pitch) + (off_x >> 3);
99     dst = (unsigned char *)surface->buffer + rx + (ry * surface->pitch);
100 
101     shift = off_x & 7;
102 
103     for (j = ry; j < max_y; ++j) {
104         src_cpy = src;
105         dst_cpy = dst;
106         val = (FT_UInt32)(*src_cpy++ | 0x100) << shift;
107 
108         for (i = rx; i < max_x; ++i, ++dst_cpy) {
109             if (val & 0x10000) {
110                 val = (FT_UInt32)(*src_cpy++ | 0x100);
111             }
112 
113             if (val & 0x80) {
114                 *dst_cpy = shade;
115             }
116 
117             val   <<= 1;
118         }
119 
120         src += bitmap->pitch;
121         dst += surface->pitch;
122     }
123 }
124 
__render_glyph_GRAY_as_MONO1(int x,int y,FontSurface * surface,const FT_Bitmap * bitmap,const FontColor * fg_color)125 void __render_glyph_GRAY_as_MONO1(int x, int y, FontSurface *surface,
126                                   const FT_Bitmap *bitmap,
127                                   const FontColor *fg_color)
128 {
129     FT_Byte *dst = ((FT_Byte *)surface->buffer) + x + (y * surface->pitch);
130     FT_Byte *dst_cpy;
131     FT_Byte shade = fg_color->a;
132 
133     const FT_Byte *src = bitmap->buffer;
134     const FT_Byte *src_cpy;
135 
136     unsigned int j, i;
137 
138     /*
139      * Assumption, target buffer was filled with the background color before
140      * any rendering.
141      */
142 
143     for (j = 0; j < bitmap->rows; ++j) {
144         src_cpy = src;
145         dst_cpy = dst;
146 
147         for (i = 0; i < bitmap->width; ++i) {
148             if (*src_cpy & '\200') /* Round up on 128 */ {
149                 *dst_cpy = shade;
150             }
151             ++src_cpy;
152             ++dst_cpy;
153         }
154 
155         dst += surface->pitch;
156         src += bitmap->pitch;
157     }
158 }
159 
__fill_glyph_GRAY1(FT_Fixed x,FT_Fixed y,FT_Fixed w,FT_Fixed h,FontSurface * surface,const FontColor * color)160 void __fill_glyph_GRAY1(FT_Fixed x, FT_Fixed y, FT_Fixed w, FT_Fixed h,
161                         FontSurface *surface, const FontColor *color)
162 {
163     int i, j;
164     FT_Byte *dst;
165     FT_Byte *dst_cpy;
166     FT_Byte shade = color->a;
167     FT_Byte edge_shade;
168 
169 #ifndef NDEBUG
170     FT_Byte *dst_end = ((FT_Byte *)surface->buffer +
171                         (surface->height * surface->pitch));
172 #endif
173 
174     x = MAX(0, x);
175     y = MAX(0, y);
176 
177     if (x + w > INT_TO_FX6(surface->width)) {
178         w = INT_TO_FX6(surface->width) - x;
179     }
180     if (y + h > INT_TO_FX6(surface->height)) {
181         h = INT_TO_FX6(surface->height) - y;
182     }
183 
184     dst = ((FT_Byte *)surface->buffer +
185            FX6_TRUNC(FX6_CEIL(x)) +
186            FX6_TRUNC(FX6_CEIL(y)) * surface->pitch);
187 
188     if (y < FX6_CEIL(y)) {
189         dst_cpy = dst - surface->pitch;
190         edge_shade = FX6_TRUNC(FX6_ROUND(shade * (FX6_CEIL(y) - y)));
191 
192         for (i = 0; i < FX6_TRUNC(FX6_CEIL(w)); ++i, ++dst_cpy) {
193             assert(dst_cpy < dst_end);
194             *dst_cpy = edge_shade;
195         }
196     }
197 
198     for (j = 0; j < FX6_TRUNC(FX6_FLOOR(h + y) - FX6_CEIL(y)); ++j) {
199         dst_cpy = dst;
200 
201         for (i = 0; i < FX6_TRUNC(FX6_CEIL(w)); ++i, ++dst_cpy) {
202             assert(dst_cpy < dst_end);
203             *dst_cpy = shade;
204         }
205 
206         dst += surface->pitch;
207     }
208 
209     if (h > FX6_FLOOR(h + y) - y) {
210         dst_cpy = dst;
211         edge_shade = FX6_TRUNC(FX6_ROUND(shade * (y + y - FX6_FLOOR(h + y))));
212         for (i = 0; i < FX6_TRUNC(FX6_CEIL(w)); ++i, ++dst_cpy) {
213             assert(dst_cpy < dst_end);
214             *dst_cpy = edge_shade;
215         }
216     }
217 }
218 
__render_glyph_INT(int x,int y,FontSurface * surface,const FT_Bitmap * bitmap,const FontColor * fg_color)219 void __render_glyph_INT(int x, int y, FontSurface *surface,
220                         const FT_Bitmap *bitmap, const FontColor *fg_color)
221 {
222     FT_Byte *dst = ((FT_Byte *)surface->buffer +
223                     x * surface->item_stride + y * surface->pitch);
224     int item_size = surface->format->BytesPerPixel;
225     int item_stride = surface->item_stride;
226     FT_Byte *dst_cpy;
227 
228     const FT_Byte *src = bitmap->buffer;
229     const FT_Byte *src_cpy;
230     FT_Byte src_byte;
231     FT_Byte mask = ~fg_color->a;
232 
233     unsigned int j, i;
234 
235     /*
236      * Assumption, target buffer was filled with the background color before
237      * any rendering.
238      */
239 
240     if (item_size == 1) {
241         for (j = 0; j < bitmap->rows; ++j) {
242             src_cpy = src;
243             dst_cpy = dst;
244 
245             for (i = 0; i < bitmap->width; ++i) {
246                 src_byte = *src_cpy;
247                 if (src_byte) {
248                     *dst_cpy = ((src_byte + *dst_cpy -
249                                 src_byte * *dst_cpy / 255) ^ mask);
250                 }
251                 ++src_cpy;
252                 dst_cpy += item_stride;
253             }
254 
255             dst += surface->pitch;
256             src += bitmap->pitch;
257         }
258     }
259     else {
260         FT_Byte dst_byte;
261         int b, int_offset = surface->format->Ashift / 8;
262 
263         for (j = 0; j < bitmap->rows; ++j) {
264             src_cpy = src;
265             dst_cpy = dst;
266 
267             for (i = 0; i < bitmap->width; ++i) {
268                 dst_byte = dst_cpy[int_offset];
269                 for (b = 0; b < item_size; ++b) {
270                     dst_cpy[b] = 0;
271                 }
272 
273                 src_byte = *src_cpy;
274                 if (src_byte) {
275                     dst_cpy[int_offset] = ((src_byte + dst_byte -
276                                             src_byte * dst_byte / 255) ^ mask);
277                 }
278                 ++src_cpy;
279                 dst_cpy += item_stride;
280             }
281 
282             dst += surface->pitch;
283             src += bitmap->pitch;
284         }
285     }
286 }
287 
__render_glyph_MONO_as_INT(int x,int y,FontSurface * surface,const FT_Bitmap * bitmap,const FontColor * fg_color)288 void __render_glyph_MONO_as_INT(int x, int y, FontSurface *surface,
289                                 const FT_Bitmap *bitmap,
290                                 const FontColor *fg_color)
291 {
292     const int off_x = (x < 0) ? -x : 0;
293     const int off_y = (y < 0) ? -y : 0;
294 
295     const int max_x = MIN(x + bitmap->width, surface->width);
296     const int max_y = MIN(y + bitmap->rows, surface->height);
297 
298     const int rx = MAX(0, x);
299     const int ry = MAX(0, y);
300 
301     int i, j, shift;
302     int item_stride = surface->item_stride;
303     int item_size = surface->format->BytesPerPixel;
304     unsigned char *src;
305     unsigned char *dst;
306     unsigned char *src_cpy;
307     unsigned char *dst_cpy;
308     FT_UInt32 val;
309     FT_Byte shade = fg_color->a;
310 
311     /*
312      * Assumption, target buffer was filled with the background color before
313      * any rendering.
314      */
315 
316     src  = bitmap->buffer + (off_y * bitmap->pitch) + (off_x >> 3);
317     dst = ((unsigned char *)surface->buffer +
318            rx * surface->item_stride + ry * surface->pitch);
319 
320     shift = off_x & 7;
321 
322     if (item_size == 1) {
323         /* Slightly optimized loop for 1 byte target int */
324         for (j = ry; j < max_y; ++j) {
325             src_cpy = src;
326             dst_cpy = dst;
327             val = (FT_UInt32)(*src_cpy++ | 0x100) << shift;
328 
329             for (i = rx; i < max_x; ++i, dst_cpy += item_stride) {
330                 if (val & 0x10000) {
331                     val = (FT_UInt32)(*src_cpy++ | 0x100);
332                 }
333 
334                 if (val & 0x80) {
335                     *dst_cpy = shade;
336                 }
337 
338                 val   <<= 1;
339             }
340 
341             src += bitmap->pitch;
342             dst += surface->pitch;
343         }
344     }
345     else {
346         /* Generic copy for arbitrary target int size */
347         int b, int_offset = surface->format->Ashift / 8;
348 
349         for (j = ry; j < max_y; ++j) {
350             src_cpy = src;
351             dst_cpy = dst;
352             val = (FT_UInt32)(*src_cpy++ | 0x100) << shift;
353 
354             for (i = rx; i < max_x; ++i, dst_cpy += item_stride) {
355                 for (b = 0; b < item_size; ++b) {
356                     dst_cpy[b] = 0;
357                 }
358 
359                 if (val & 0x10000) {
360                     val = (FT_UInt32)(*src_cpy++ | 0x100);
361                 }
362 
363                 if (val & 0x80) {
364                     dst_cpy[int_offset] = shade;
365                 }
366 
367                 val   <<= 1;
368             }
369 
370             src += bitmap->pitch;
371             dst += surface->pitch;
372         }
373     }
374 }
375 
__fill_glyph_INT(FT_Fixed x,FT_Fixed y,FT_Fixed w,FT_Fixed h,FontSurface * surface,const FontColor * color)376 void __fill_glyph_INT(FT_Fixed x, FT_Fixed y, FT_Fixed w, FT_Fixed h,
377                       FontSurface *surface, const FontColor *color)
378 {
379     int i, j;
380     FT_Byte *dst;
381     int itemsize = surface->format->BytesPerPixel;
382     int item_stride = surface->item_stride;
383     int byteoffset = surface->format->Ashift / 8;
384     FT_Byte *dst_cpy;
385     FT_Byte shade = color->a;
386     FT_Byte edge_shade;
387 
388     x = MAX(0, x);
389     y = MAX(0, y);
390 
391     if (x + w > INT_TO_FX6(surface->width)) {
392         w = INT_TO_FX6(surface->width) - x;
393     }
394     if (y + h > INT_TO_FX6(surface->height)) {
395         h = INT_TO_FX6(surface->height) - y;
396     }
397 
398     dst = ((FT_Byte *)surface->buffer +
399            FX6_TRUNC(FX6_CEIL(x)) * itemsize +
400            FX6_TRUNC(FX6_CEIL(y)) * surface->pitch);
401 
402     if (itemsize == 1) {
403         if (y < FX6_CEIL(y)) {
404             dst_cpy = dst - surface->pitch;
405             edge_shade = FX6_TRUNC(FX6_ROUND(shade * (FX6_CEIL(y) - y)));
406 
407             for (i = 0;
408                  i < FX6_TRUNC(FX6_CEIL(w));
409                  ++i, dst_cpy += item_stride) {
410                 *dst_cpy = edge_shade;
411             }
412         }
413 
414         for (j = 0; j < FX6_TRUNC(FX6_FLOOR(h + y) - FX6_CEIL(y)); ++j) {
415             dst_cpy = dst;
416 
417             for (i = 0;
418                  i < FX6_TRUNC(FX6_CEIL(w));
419                  ++i, dst_cpy += item_stride) {
420                 *dst_cpy = shade;
421             }
422 
423             dst += surface->pitch;
424         }
425 
426         if (h > FX6_FLOOR(h + y) - y) {
427             dst_cpy = dst;
428             edge_shade = FX6_TRUNC(FX6_ROUND(shade *
429                                              (y + y - FX6_FLOOR(h + y))));
430             for (i = 0;
431                  i < FX6_TRUNC(FX6_CEIL(w));
432                  ++i, dst_cpy += item_stride) {
433                 *dst_cpy = edge_shade;
434             }
435         }
436     }
437     else {
438         int b;
439 
440         if (y < FX6_CEIL(y)) {
441             dst_cpy = dst - surface->pitch;
442             edge_shade = FX6_TRUNC(FX6_ROUND(shade * (FX6_CEIL(y) - y)));
443 
444             for (i = 0;
445                  i < FX6_TRUNC(FX6_CEIL(w));
446                  ++i, dst_cpy += item_stride) {
447                 for (b = 0; b < itemsize; ++b) {
448                     dst_cpy[b] = 0;
449                 }
450                 dst_cpy[byteoffset] = edge_shade;
451             }
452         }
453 
454         for (j = 0; j < FX6_TRUNC(FX6_FLOOR(h + y) - FX6_CEIL(y)); ++j) {
455             dst_cpy = dst;
456 
457             for (i = 0;
458                  i < FX6_TRUNC(FX6_CEIL(w));
459                  ++i, dst_cpy += item_stride) {
460                 for (b = 0; b < itemsize; ++b) {
461                     dst_cpy[b] = 0;
462                 }
463                 dst_cpy[byteoffset] = shade;
464             }
465 
466             dst += surface->pitch;
467         }
468 
469         if (h > FX6_FLOOR(h + y) - y) {
470             dst_cpy = dst;
471             edge_shade = FX6_TRUNC(FX6_ROUND(shade *
472                                              (h + y - FX6_FLOOR(h + y))));
473             for (i = 0;
474                  i < FX6_TRUNC(FX6_CEIL(w));
475                  ++i, dst_cpy += item_stride) {
476                 for (b = 0; b < itemsize; ++b) {
477                     dst_cpy[b] = 0;
478                 }
479                 dst_cpy[byteoffset] = edge_shade;
480             }
481         }
482     }
483 }
484 
485 #ifdef HAVE_PYGAME_SDL_VIDEO
486 
487 #ifndef NDEBUG
488 #define POINTER_ASSERT_DECLARATIONS(s) \
489     const unsigned char *PA_bstart = ((unsigned char *)(s)->buffer);\
490     const unsigned char *PA_bend =\
491         (PA_bstart + (s)->height * (s)->pitch);
492 #define POINTER_ASSERT(p) \
493     assert((const unsigned char *)(p) >= PA_bstart);\
494     assert((const unsigned char *)(p) < PA_bend);
495 #else
496 #define POINTER_ASSERT_DECLARATIONS(s)
497 #define POINTER_ASSERT(p)
498 #endif
499 
500 #define _CREATE_RGB_FILLER(_bpp, _getp, _setp, _blendp)     \
501     void __fill_glyph_RGB##_bpp(FT_Fixed x, FT_Fixed y,     \
502                                 FT_Fixed w, FT_Fixed h,     \
503                                 FontSurface *surface,       \
504                                 const FontColor *color)     \
505     {                                                       \
506         FT_Fixed dh = 0;                                    \
507         int i;                                              \
508         unsigned char *dst;                                 \
509         FT_UInt32 bgR, bgG, bgB, bgA;                       \
510         FT_Byte edge_a;                                     \
511         POINTER_ASSERT_DECLARATIONS(surface)                \
512                                                             \
513         /* Crop the rectangle to the top and left of the    \
514          * surface.                                         \
515          */                                                 \
516         x = MAX(0, x);                                      \
517         y = MAX(0, y);                                      \
518                                                             \
519         /* Crop the rectangle to the bottom and right of    \
520          * the surface.                                     \
521          */                                                 \
522         if (x + w > INT_TO_FX6(surface->width)) {           \
523             w = INT_TO_FX6(surface->width) - x;             \
524         }                                                   \
525         if (y + h > INT_TO_FX6(surface->height)) {          \
526             h = INT_TO_FX6(surface->height) - y;            \
527         }                                                   \
528                                                             \
529         /* Start at the first pixel of the first row.       \
530          */                                                 \
531         dst = ((FT_Byte *)surface->buffer +                 \
532                FX6_TRUNC(FX6_CEIL(x)) * _bpp +              \
533                FX6_TRUNC(FX6_CEIL(y)) * surface->pitch);    \
534                                                             \
535         /* Take care of the top row of the rectangle if the \
536          * rectangle starts within the pixels: y is not on  \
537          * a pixel boundary. A special case is when the     \
538          * bottom of the rectangle is also with the pixel   \
539          * row.                                             \
540          */                                                 \
541         dh = FX6_CEIL(y) - y;                               \
542         if (dh > h) {                                       \
543             dh = h;                                         \
544         }                                                   \
545         h -= dh;                                            \
546         if (dh > 0) {                                       \
547             unsigned char *_dst = dst - surface->pitch;     \
548                                                             \
549             edge_a = FX6_TRUNC(FX6_ROUND(color->a * dh));   \
550                                                             \
551             for (i = 0;                                     \
552                  i < FX6_TRUNC(FX6_CEIL(w));                \
553                  ++i, _dst += _bpp) {                       \
554                 FT_UInt32 pixel = (FT_UInt32)_getp;         \
555                                                             \
556                 POINTER_ASSERT(_dst)                        \
557                                                             \
558                 if (_bpp == 1) {                            \
559                     GET_PALETTE_VALS(                       \
560                             pixel, surface->format,         \
561                             bgR, bgG, bgB, bgA);            \
562                 }                                           \
563                 else {                                      \
564                     GET_RGB_VALS(                           \
565                             pixel, surface->format,         \
566                             bgR, bgG, bgB, bgA);            \
567                                                             \
568                 }                                           \
569                                                             \
570                 ALPHA_BLEND(                                \
571                         color->r, color->g, color->b,       \
572                         edge_a, bgR, bgG, bgB, bgA);        \
573                                                             \
574                 _blendp;                                    \
575             }                                               \
576                                                             \
577             y += dh;                                        \
578         }                                                   \
579                                                             \
580         /* Fill in all entirely covered rows. These are     \
581          * pixels which are entirely within the upper and   \
582          * lower edges of the rectangle.                    \
583          */                                                 \
584         dh = FX6_FLOOR(h);                                  \
585         h -= dh;                                            \
586         while (dh > 0) {                                    \
587             unsigned char *_dst = dst;                      \
588                                                             \
589             for (i = 0;                                     \
590                  i < FX6_TRUNC(FX6_CEIL(w));                \
591                  ++i, _dst += _bpp) {                       \
592                 FT_UInt32 pixel = (FT_UInt32)_getp;         \
593                                                             \
594                 POINTER_ASSERT(_dst)                        \
595                                                             \
596                 if (_bpp == 1) {                            \
597                     GET_PALETTE_VALS(                       \
598                             pixel, surface->format,         \
599                             bgR, bgG, bgB, bgA);            \
600                 }                                           \
601                 else {                                      \
602                     GET_RGB_VALS(                           \
603                             pixel, surface->format,         \
604                             bgR, bgG, bgB, bgA);            \
605                                                             \
606                 }                                           \
607                                                             \
608                 ALPHA_BLEND(                                \
609                         color->r, color->g, color->b,       \
610                         color->a, bgR, bgG, bgB, bgA);      \
611                                                             \
612                 _blendp;                                    \
613             }                                               \
614                                                             \
615             dst += surface->pitch;                          \
616             dh -= FX6_ONE;                                  \
617             y += FX6_ONE;                                   \
618         }                                                   \
619                                                             \
620         /* Fill in the bottom row of pixels if these pixels \
621          * are only partially covered: the rectangle bottom \
622          * is not on a pixel boundary. Otherwise, done.     \
623          */                                                 \
624         if (h > 0) {                                        \
625             unsigned char *_dst = dst;                      \
626             edge_a = FX6_TRUNC(FX6_ROUND(color->a * h));    \
627                                                             \
628             for (i = 0;                                     \
629                  i < FX6_TRUNC(FX6_CEIL(w));                \
630                  ++i, _dst += _bpp) {                       \
631                 FT_UInt32 pixel = (FT_UInt32)_getp;         \
632                                                             \
633                 POINTER_ASSERT(_dst)                        \
634                                                             \
635                 if (_bpp == 1) {                            \
636                     GET_PALETTE_VALS(                       \
637                             pixel, surface->format,         \
638                             bgR, bgG, bgB, bgA);            \
639                 }                                           \
640                 else {                                      \
641                     GET_RGB_VALS(                           \
642                             pixel, surface->format,         \
643                             bgR, bgG, bgB, bgA);            \
644                                                             \
645                 }                                           \
646                                                             \
647                 ALPHA_BLEND(                                \
648                         color->r, color->g, color->b,       \
649                         edge_a, bgR, bgG, bgB, bgA);        \
650                                                             \
651                 _blendp;                                    \
652             }                                               \
653         }                                                   \
654     }
655 
656 
657 #define __MONO_RENDER_INNER_LOOP(_bpp, _code)               \
658     for (j = ry; j < max_y; ++j)                            \
659     {                                                       \
660         const unsigned char* _src = src;                    \
661         unsigned char* _dst = dst;                          \
662         FT_UInt32 val =                                     \
663             (FT_UInt32)(*_src++ | 0x100) << shift;          \
664                                                             \
665         for (i = rx; i < max_x; ++i, _dst += _bpp) {        \
666             if (val & 0x10000) {                            \
667                 val = (FT_UInt32)(*_src++ | 0x100);         \
668             }                                               \
669             if (val & 0x80) {                               \
670                 _code;                                      \
671             }                                               \
672             val <<= 1;                                      \
673         }                                                   \
674                                                             \
675         src += bitmap->pitch;                               \
676         dst += surface->pitch;                              \
677     }                                                       \
678 
679 #define _CREATE_MONO_RENDER(_bpp, _getp, _setp, _blendp)    \
680     void __render_glyph_MONO##_bpp(int x, int y,            \
681                                    FontSurface *surface,    \
682                                    const FT_Bitmap *bitmap, \
683                                    const FontColor *color)  \
684     {                                                       \
685         const int off_x = (x < 0) ? -x : 0;                 \
686         const int off_y = (y < 0) ? -y : 0;                 \
687                                                             \
688         const int max_x =                                   \
689              MIN(x + bitmap->width, surface->width);        \
690         const int max_y =                                   \
691              MIN(y + bitmap->rows, surface->height);        \
692                                                             \
693         const int rx = MAX(0, x);                           \
694         const int ry = MAX(0, y);                           \
695                                                             \
696         int i, j, shift;                                                \
697         const unsigned char* src;                                       \
698         unsigned char* dst;                                             \
699         FT_UInt32 full_color;                                           \
700         FT_UInt32 bgR, bgG, bgB, bgA;                                   \
701                                                                         \
702         src  = bitmap->buffer + (off_y * bitmap->pitch) + (off_x >> 3); \
703         dst = (unsigned char *)surface->buffer + (rx * _bpp) +          \
704                     (ry * surface->pitch);                              \
705                                                                         \
706         full_color = SDL_MapRGBA(surface->format, (FT_Byte)color->r,    \
707                 (FT_Byte)color->g, (FT_Byte)color->b, 255);             \
708                                                                         \
709         shift = off_x & 7;                                              \
710                                                                         \
711         if (color->a == 0xFF) {                                         \
712             __MONO_RENDER_INNER_LOOP(_bpp,                              \
713             {                                                           \
714                 _setp;                                                  \
715             });                                                         \
716         }                                                               \
717         else if (color->a > 0) {                                        \
718             __MONO_RENDER_INNER_LOOP(_bpp,                              \
719             {                                                           \
720                 FT_UInt32 pixel = (FT_UInt32)_getp;                     \
721                                                                         \
722                 if (_bpp == 1) {                                        \
723                     GET_PALETTE_VALS(                                   \
724                             pixel, surface->format,                     \
725                             bgR, bgG, bgB, bgA);                        \
726                 }                                                       \
727                 else {                                                  \
728                     GET_RGB_VALS(                                       \
729                             pixel, surface->format,                     \
730                             bgR, bgG, bgB, bgA);                        \
731                                                                         \
732                 }                                                       \
733                                                                         \
734                 ALPHA_BLEND(                                            \
735                         color->r, color->g, color->b, color->a,         \
736                         bgR, bgG, bgB, bgA);                            \
737                                                                         \
738                 _blendp;                                                \
739             });                                                         \
740         }                                                               \
741     }
742 
743 #define _CREATE_RGB_RENDER(_bpp, _getp, _setp, _blendp)                 \
744     void __render_glyph_RGB##_bpp(int x, int y, FontSurface *surface,   \
745                                   const FT_Bitmap *bitmap,              \
746                                   const FontColor *color)               \
747     {                                                                   \
748         const int off_x = (x < 0) ? -x : 0;                             \
749         const int off_y = (y < 0) ? -y : 0;                             \
750                                                                         \
751         const int max_x = MIN(x + bitmap->width, surface->width);       \
752         const int max_y = MIN(y + bitmap->rows, surface->height);       \
753                                                                         \
754         const int rx = MAX(0, x);                                       \
755         const int ry = MAX(0, y);                                       \
756                                                                         \
757         FT_Byte *dst = ((FT_Byte*)surface->buffer) + (rx * _bpp) +      \
758                         (ry * surface->pitch);                          \
759         FT_Byte *_dst;                                                  \
760                                                                         \
761         const FT_Byte *src = bitmap->buffer + off_x +                   \
762                                 (off_y * bitmap->pitch);                \
763         const FT_Byte *_src;                                            \
764                                                                         \
765         _DECLARE_full_color##_bpp(surface, color)                       \
766         /*                                                              \
767         const FT_UInt32 full_color =                                    \
768             SDL_MapRGBA(surface->format, (FT_Byte)color->r,             \
769                     (FT_Byte)color->g, (FT_Byte)color->b, 255);         \
770         */                                                              \
771                                                                         \
772         FT_UInt32 bgR, bgG, bgB, bgA;                                   \
773         int j, i;                                                       \
774                                                                         \
775         for (j = ry; j < max_y; ++j) {                                  \
776             _src = src;                                                 \
777             _dst = dst;                                                 \
778                                                                         \
779             for (i = rx; i < max_x; ++i, _dst += _bpp) {                \
780                 FT_UInt32 alpha = (*_src++);                            \
781                 alpha = (alpha * color->a) / 255;                       \
782                                                                         \
783                 if (alpha == 0xFF) {                                    \
784                     _setp;                                              \
785                 }                                                       \
786                 else if (alpha > 0) {                                   \
787                     FT_UInt32 pixel = (FT_UInt32)_getp;                 \
788                                                                         \
789                 if (_bpp == 1) {                                        \
790                     GET_PALETTE_VALS(                                   \
791                             pixel, surface->format,                     \
792                             bgR, bgG, bgB, bgA);                        \
793                 }                                                       \
794                 else {                                                  \
795                     GET_RGB_VALS(                                       \
796                             pixel, surface->format,                     \
797                             bgR, bgG, bgB, bgA);                        \
798                                                                         \
799                 }                                                       \
800                                                                         \
801                     ALPHA_BLEND(                                        \
802                             color->r, color->g, color->b, alpha,        \
803                             bgR, bgG, bgB, bgA);                        \
804                                                                         \
805                     _blendp;                                            \
806                 }                                                       \
807             }                                                           \
808                                                                         \
809             dst += surface->pitch;                                      \
810             src += bitmap->pitch;                                       \
811         }                                                               \
812     }
813 
814 /* These macros removes a gcc unused variable warning for __render_glyph_RGB3 */
815 #define _DECLARE_full_color(s, c) const FT_UInt32 full_color =          \
816     SDL_MapRGBA((s)->format, (FT_Byte)(c)->r, (FT_Byte)(c)->g,          \
817                 (FT_Byte)(c)->b, 255);
818 #define _DECLARE_full_color1(s, c) _DECLARE_full_color(s, c)
819 #define _DECLARE_full_color2(s, c) _DECLARE_full_color(s, c)
820 #define _DECLARE_full_color3(s, c)
821 #define _DECLARE_full_color4(s, c) _DECLARE_full_color(s, c)
822 
823 
824 #define _SET_PIXEL_24   \
825     SET_PIXEL24_RGB(_dst, surface->format, color->r, color->g, color->b);
826 
827 #define _BLEND_PIXEL_24 \
828     SET_PIXEL24_RGB(_dst, surface->format, bgR, bgG, bgB);
829 
830 #define _SET_PIXEL(T) \
831     *(T*)_dst = (T)full_color;
832 
833 #define _BLEND_PIXEL(T) *((T*)_dst) = (T)(                          \
834     ((bgR >> surface->format->Rloss) << surface->format->Rshift) |  \
835     ((bgG >> surface->format->Gloss) << surface->format->Gshift) |  \
836     ((bgB >> surface->format->Bloss) << surface->format->Bshift) |  \
837     ((bgA >> surface->format->Aloss) << surface->format->Ashift  &  \
838      surface->format->Amask)                                        )
839 
840 #define _BLEND_PIXEL_GENERIC(T) *(T*)_dst = (T)(    \
841     SDL_MapRGB(surface->format,                     \
842         (FT_Byte)bgR, (FT_Byte)bgG, (FT_Byte)bgB)   )
843 
844 #define _GET_PIXEL(T)    (*((T*)_dst))
845 
846 _CREATE_RGB_RENDER(4,  _GET_PIXEL(FT_UInt32),   _SET_PIXEL(FT_UInt32),  _BLEND_PIXEL(FT_UInt32))
847 _CREATE_RGB_RENDER(3,  GET_PIXEL24(_dst),       _SET_PIXEL_24,          _BLEND_PIXEL_24)
848 _CREATE_RGB_RENDER(2,  _GET_PIXEL(FT_UInt16),   _SET_PIXEL(FT_UInt16),  _BLEND_PIXEL(FT_UInt16))
849 _CREATE_RGB_RENDER(1,  _GET_PIXEL(FT_Byte),     _SET_PIXEL(FT_Byte),    _BLEND_PIXEL_GENERIC(FT_Byte))
850 
851 _CREATE_MONO_RENDER(4,  _GET_PIXEL(FT_UInt32),   _SET_PIXEL(FT_UInt32),  _BLEND_PIXEL(FT_UInt32))
852 _CREATE_MONO_RENDER(3,  GET_PIXEL24(_dst),       _SET_PIXEL_24,          _BLEND_PIXEL_24)
853 _CREATE_MONO_RENDER(2,  _GET_PIXEL(FT_UInt16),   _SET_PIXEL(FT_UInt16),  _BLEND_PIXEL(FT_UInt16))
854 _CREATE_MONO_RENDER(1,  _GET_PIXEL(FT_Byte),     _SET_PIXEL(FT_Byte),    _BLEND_PIXEL_GENERIC(FT_Byte))
855 
856 _CREATE_RGB_FILLER(4,  _GET_PIXEL(FT_UInt32),   _SET_PIXEL(FT_UInt32),  _BLEND_PIXEL(FT_UInt32))
857 _CREATE_RGB_FILLER(3,  GET_PIXEL24(_dst),       _SET_PIXEL_24,          _BLEND_PIXEL_24)
858 _CREATE_RGB_FILLER(2,  _GET_PIXEL(FT_UInt16),   _SET_PIXEL(FT_UInt16),  _BLEND_PIXEL(FT_UInt16))
859 _CREATE_RGB_FILLER(1,  _GET_PIXEL(FT_Byte),     _SET_PIXEL(FT_Byte),    _BLEND_PIXEL_GENERIC(FT_Byte))
860 #endif
861