1010#include <math.h>
1111#include <structmember.h>
1212#include <ft2build.h>
13+ #include FT_DRIVER_H
1314#include <hb-ft.h>
15+ #include <fontconfig/fontconfig.h>
1416
1517#if HB_VERSION_MAJOR > 1 || (HB_VERSION_MAJOR == 1 && (HB_VERSION_MINOR > 6 || (HB_VERSION_MINOR == 6 && HB_VERSION_MICRO >= 3 )))
1618#define HARFBUZZ_HAS_CHANGE_FONT
@@ -29,6 +31,7 @@ typedef struct {
2931 unsigned int units_per_EM ;
3032 int ascender , descender , height , max_advance_width , max_advance_height , underline_position , underline_thickness ;
3133 int hinting , hintstyle , index ;
34+ int rgba ;
3235 bool is_scalable , has_color ;
3336 float size_in_pts ;
3437 FT_F26Dot6 char_width , char_height ;
@@ -159,13 +162,14 @@ get_load_flags(int hinting, int hintstyle, int base) {
159162
160163
161164static inline bool
162- init_ft_face (Face * self , PyObject * path , int hinting , int hintstyle , FONTS_DATA_HANDLE fg ) {
165+ init_ft_face (Face * self , PyObject * path , int hinting , int hintstyle , int rgba , FONTS_DATA_HANDLE fg ) {
163166#define CPY (n ) self->n = self->face->n;
164167 CPY (units_per_EM ); CPY (ascender ); CPY (descender ); CPY (height ); CPY (max_advance_width ); CPY (max_advance_height ); CPY (underline_position ); CPY (underline_thickness );
165168#undef CPY
166169 self -> is_scalable = FT_IS_SCALABLE (self -> face );
167170 self -> has_color = FT_HAS_COLOR (self -> face );
168171 self -> hinting = hinting ; self -> hintstyle = hintstyle ;
172+ self -> rgba = rgba ;
169173 if (!set_size_for_face ((PyObject * )self , 0 , false, fg )) return false;
170174 self -> harfbuzz_font = hb_ft_font_create (self -> face , NULL );
171175 if (self -> harfbuzz_font == NULL ) { PyErr_NoMemory (); return false; }
@@ -189,16 +193,24 @@ face_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) {
189193 long index = 0 ;
190194 bool hinting = false;
191195 long hint_style = 0 ;
196+ long rgba = 0 ;
192197 D (path , PyUnicode_AsUTF8 , false);
193198 D (index , PyLong_AsLong , true);
194199 D (hinting , PyObject_IsTrue , true);
195200 D (hint_style , PyLong_AsLong , true);
201+ D (rgba , PyLong_AsLong , true);
196202#undef D
197203 Face * self = (Face * )Face_Type .tp_alloc (& Face_Type , 0 );
198204 if (self != NULL ) {
199205 int error = FT_New_Face (library , path , index , & (self -> face ));
200206 if (error ) { set_freetype_error ("Failed to load face, with error:" , error ); Py_CLEAR (self ); return NULL ; }
201- if (!init_ft_face (self , PyDict_GetItemString (descriptor , "path" ), hinting , hint_style , fg )) { Py_CLEAR (self ); return NULL ; }
207+ if (!init_ft_face (self , PyDict_GetItemString (descriptor , "path" ), hinting , hint_style , rgba , fg )) { Py_CLEAR (self ); return NULL ; }
208+ FT_Parameter property_darkening ;
209+ FT_Bool darken_stems = 1 ;
210+ property_darkening .tag = FT_PARAM_TAG_STEM_DARKENING ;
211+ property_darkening .data = & darken_stems ;
212+ error = FT_Face_Properties (self -> face , 1 , & property_darkening );
213+ if (error ) { set_freetype_error ("Failed to set face property, with error:" , error ); Py_CLEAR (self ); return NULL ; }
202214 }
203215 return (PyObject * )self ;
204216}
@@ -210,7 +222,7 @@ face_from_path(const char *path, int index, FONTS_DATA_HANDLE fg) {
210222 int error ;
211223 error = FT_New_Face (library , path , index , & ans -> face );
212224 if (error ) { set_freetype_error ("Failed to load face, with error:" , error ); ans -> face = NULL ; return NULL ; }
213- if (!init_ft_face (ans , Py_None , true, 3 , fg )) { Py_CLEAR (ans ); return NULL ; }
225+ if (!init_ft_face (ans , Py_None , true, 3 , 1 , fg )) { Py_CLEAR (ans ); return NULL ; }
214226 return (PyObject * )ans ;
215227}
216228
@@ -351,7 +363,10 @@ populate_processed_bitmap(FT_GlyphSlotRec *slot, FT_Bitmap *bitmap, ProcessedBit
351363
352364static inline bool
353365render_bitmap (Face * self , int glyph_id , ProcessedBitmap * ans , unsigned int cell_width , unsigned int cell_height , unsigned int num_cells , bool bold , bool italic , bool rescale , FONTS_DATA_HANDLE fg ) {
354- if (!load_glyph (self , glyph_id , FT_LOAD_RENDER )) return false;
366+ int flags = FT_LOAD_RENDER ;
367+ if (OPT (linux_use_subpixel_rendering ))
368+ flags |= FT_LOAD_TARGET_LCD ;
369+ if (!load_glyph (self , glyph_id , flags )) return false;
355370 unsigned int max_width = cell_width * num_cells ;
356371
357372 // Embedded bitmap glyph?
@@ -372,6 +387,9 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
372387 }
373388 populate_processed_bitmap (self -> face -> glyph , & bitmap , ans , true);
374389 FT_Bitmap_Done (library , & bitmap );
390+ } else if (self -> face -> glyph -> bitmap .pixel_mode == FT_PIXEL_MODE_LCD ) {
391+ populate_processed_bitmap (self -> face -> glyph , & self -> face -> glyph -> bitmap , ans , false);
392+ ans -> width /= 3 ;
375393 } else {
376394 populate_processed_bitmap (self -> face -> glyph , & self -> face -> glyph -> bitmap , ans , false);
377395 }
@@ -488,7 +506,24 @@ copy_color_bitmap(uint8_t *src, pixel* dest, Region *src_rect, Region *dest_rect
488506}
489507
490508static inline void
491- place_bitmap_in_canvas (pixel * cell , ProcessedBitmap * bm , size_t cell_width , size_t cell_height , float x_offset , float y_offset , size_t baseline ) {
509+ copy_lcd_bitmap (uint8_t * src , pixel * dest , Region * src_rect , Region * dest_rect , size_t src_stride , size_t dest_stride , bool bgr ) {
510+ for (size_t sr = src_rect -> top , dr = dest_rect -> top ; sr < src_rect -> bottom && dr < dest_rect -> bottom ; sr ++ , dr ++ ) {
511+ pixel * d = dest + dest_stride * dr ;
512+ uint8_t * s = src + src_stride * sr ;
513+ for (size_t sc = src_rect -> left , dc = dest_rect -> left ; sc < src_rect -> right && dc < dest_rect -> right ; sc ++ , dc ++ ) {
514+ uint8_t * rgb = s + 3 * sc ;
515+ #define C (idx , shift ) ( rgb[idx] << shift)
516+ if (!bgr )
517+ d [dc ] = C (0 , 24 ) | C (1 , 16 ) | C (2 , 8 ) | 0xff ;
518+ else
519+ d [dc ] = C (2 , 24 ) | C (1 , 16 ) | C (0 , 8 ) | 0xff ;
520+ #undef C
521+ }
522+ }
523+ }
524+
525+ static inline void
526+ place_bitmap_in_canvas (pixel * cell , ProcessedBitmap * bm , size_t cell_width , size_t cell_height , float x_offset , float y_offset , size_t baseline , bool bgr ) {
492527 // We want the glyph to be positioned inside the cell based on the bearingX
493528 // and bearingY values, making sure that it does not overflow the cell.
494529
@@ -517,13 +552,15 @@ place_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size
517552
518553 if (bm -> pixel_mode == FT_PIXEL_MODE_BGRA ) {
519554 copy_color_bitmap (bm -> buf , cell , & src , & dest , bm -> stride , cell_width );
555+ } else if (bm -> pixel_mode == FT_PIXEL_MODE_LCD ) {
556+ copy_lcd_bitmap (bm -> buf , cell , & src , & dest , bm -> stride , cell_width , bgr );
520557 } else render_alpha_mask (bm -> buf , cell , & src , & dest , bm -> stride , cell_width );
521558}
522559
523560static const ProcessedBitmap EMPTY_PBM = {.factor = 1 };
524561
525562bool
526- render_glyphs_in_cells (PyObject * f , bool bold , bool italic , hb_glyph_info_t * info , hb_glyph_position_t * positions , unsigned int num_glyphs , pixel * canvas , unsigned int cell_width , unsigned int cell_height , unsigned int num_cells , unsigned int baseline , bool * was_colored , FONTS_DATA_HANDLE fg , bool center_glyph ) {
563+ render_glyphs_in_cells (PyObject * f , bool bold , bool italic , hb_glyph_info_t * info , hb_glyph_position_t * positions , unsigned int num_glyphs , pixel * canvas , unsigned int cell_width , unsigned int cell_height , unsigned int num_cells , unsigned int baseline , bool * was_colored , bool * was_subpixel , FONTS_DATA_HANDLE fg , bool center_glyph ) {
527564 Face * self = (Face * )f ;
528565 bool is_emoji = * was_colored ; * was_colored = is_emoji && self -> has_color ;
529566 float x = 0.f , y = 0.f , x_offset = 0.f ;
@@ -540,10 +577,11 @@ render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *inf
540577 } else {
541578 if (!render_bitmap (self , info [i ].codepoint , & bm , cell_width , cell_height , num_cells , bold , italic , true, fg )) return false;
542579 }
580+ * was_subpixel = (bm .pixel_mode == FT_PIXEL_MODE_LCD );
543581 x_offset = x + (float )positions [i ].x_offset / 64.0f ;
544582 y = (float )positions [i ].y_offset / 64.0f ;
545583 if ((* was_colored || self -> face -> glyph -> metrics .width > 0 ) && bm .width > 0 ) {
546- place_bitmap_in_canvas (canvas , & bm , canvas_width , cell_height , x_offset , y , baseline );
584+ place_bitmap_in_canvas (canvas , & bm , canvas_width , cell_height , x_offset , y , baseline , ( self -> rgba == FC_RGBA_BGR ) );
547585 }
548586 x += (float )positions [i ].x_advance / 64.0f ;
549587 free_processed_bitmap (& bm );
@@ -591,12 +629,17 @@ render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {
591629 FT_UInt glyph_index = FT_Get_Char_Index (self -> face , text [n ]);
592630 int error = FT_Load_Glyph (self -> face , glyph_index , FT_LOAD_DEFAULT );
593631 if (error ) continue ;
594- error = FT_Render_Glyph (self -> face -> glyph , FT_RENDER_MODE_NORMAL );
632+ int flags = 0 ;
633+ if (OPT (linux_use_subpixel_rendering ))
634+ flags |= FT_RENDER_MODE_LCD ;
635+ else
636+ flags |= FT_RENDER_MODE_NORMAL ;
637+ error = FT_Render_Glyph (self -> face -> glyph , flags );
595638 if (error ) continue ;
596639 FT_Bitmap * bitmap = & self -> face -> glyph -> bitmap ;
597640 pbm = EMPTY_PBM ;
598641 populate_processed_bitmap (self -> face -> glyph , bitmap , & pbm , false);
599- place_bitmap_in_canvas (canvas , & pbm , canvas_width , canvas_height , pen_x , 0 , baseline );
642+ place_bitmap_in_canvas (canvas , & pbm , canvas_width , canvas_height , pen_x , 0 , baseline , ( self -> rgba == FC_RGBA_BGR ) );
600643 pen_x += self -> face -> glyph -> advance .x >> 6 ;
601644 }
602645 ans .width = pen_x ; ans .height = canvas_height ;
0 commit comments