[vlc-devel] [PATCH] freetype: RFC knockout blending implementation

sarah-kamall sarah.kamal.soliman at gmail.com
Fri Dec 26 13:44:43 UTC 2025


This patch implements knockout blending logic for Freetype renderer.

RFC: Should this new knockout blending mode replace the regular mode (as implemented in this patch) or be introduced as a new style flag?

Note: This implementation introduces new warnings.
Fixes: #29195
Signed-off-by: sarah soliman <sarah.kamal.soliman at gmail.com>
---
 modules/text_renderer/freetype/blend/blend.h | 20 +++++++++++++++
 modules/text_renderer/freetype/blend/rgb.h   | 20 +++++++++++++++
 modules/text_renderer/freetype/blend/yuv.h   | 27 +++++++++++++++++---
 modules/text_renderer/freetype/freetype.c    | 10 ++++----
 4 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/modules/text_renderer/freetype/blend/blend.h b/modules/text_renderer/freetype/blend/blend.h
index dd5a39c7e1..8316392370 100644
--- a/modules/text_renderer/freetype/blend/blend.h
+++ b/modules/text_renderer/freetype/blend/blend.h
@@ -63,6 +63,26 @@ static inline void Blend##name##Pixel( uint8_t **dst, int a, int x, int y, int z
     }\
 }
 
+#define DECL_PIXEL_BLENDER_KNOCKOUT( name, APOS, XPOS, YPOS, ZPOS, APLANE, XPLANE, YPLANE, ZPLANE ) \
+static inline void Blend##name##PixelKnockout( uint8_t **dst, int a, int x, int y, int z, int glyph_a )\
+{\
+    if( glyph_a == 0 )\
+        return;\
+\
+    int i_ao = dst[APLANE][APOS];\
+    /* Knockout Mode: Interpolate between Source and Dest based on glyph shape */ \
+    /* Result = (Dest * (255 - Glyph) + Src * Glyph) / 255 */ \
+    int i_an = (i_ao * (255 - glyph_a) + a * glyph_a) / 255; \
+    dst[APLANE][APOS] = i_an; \
+\
+    if (i_an > 0)\
+    {\
+        dst[XPLANE][XPOS] = ( dst[XPLANE][XPOS] * (255 - glyph_a) + x * glyph_a ) / 255;\
+        dst[YPLANE][YPOS] = ( dst[YPLANE][YPOS] * (255 - glyph_a) + y * glyph_a ) / 255;\
+        dst[ZPLANE][ZPOS] = ( dst[ZPLANE][ZPOS] * (255 - glyph_a) + z * glyph_a ) / 255;\
+    }\
+}
+
 static void BlendAXYZLine( picture_t *p_picture,
                            int i_picture_x, int i_picture_y,
                            int i_a, int i_x, int i_y, int i_z,
diff --git a/modules/text_renderer/freetype/blend/rgb.h b/modules/text_renderer/freetype/blend/rgb.h
index e9490cccab..7e6e1bbfb3 100644
--- a/modules/text_renderer/freetype/blend/rgb.h
+++ b/modules/text_renderer/freetype/blend/rgb.h
@@ -57,9 +57,11 @@ static void Fill##name##Picture( picture_t *p_picture,\
 
 DECL_RGB_FILLER( RGBA, 3, 0, 1, 2 );
 DECL_PIXEL_BLENDER(RGBA, 3, 0, 1, 2, 0, 0, 0, 0);
+DECL_PIXEL_BLENDER_KNOCKOUT(RGBA, 3, 0, 1, 2, 0, 0, 0, 0);
 
 DECL_RGB_FILLER( ARGB, 0, 1, 2, 3 );
 DECL_PIXEL_BLENDER(ARGB, 0, 1, 2, 3, 0, 0, 0, 0);
+DECL_PIXEL_BLENDER_KNOCKOUT(ARGB, 0, 1, 2, 3, 0, 0, 0, 0);
 
 #undef DECL_RGB_FILLER
 
@@ -107,3 +109,21 @@ static void BlendGlyphToARGB( picture_t *p_picture,
                      i_a, i_x, i_y, i_z, p_glyph,
                      BlendARGBPixel );
 }
+static void BlendGlyphToRGBAKnockout( picture_t *p_picture,
+                                       int i_picture_x, int i_picture_y,
+                                       int i_a, int i_x, int i_y, int i_z,
+                                       FT_BitmapGlyph p_glyph )
+{
+    BlendGlyphToRGB( p_picture, i_picture_x, i_picture_y,
+                     i_a, i_x, i_y, i_z, p_glyph,
+                     BlendRGBAPixelKnockout );
+}
+static void BlendGlyphToARGBKnockout( picture_t *p_picture,
+                                       int i_picture_x, int i_picture_y,
+                                       int i_a, int i_x, int i_y, int i_z,
+                                       FT_BitmapGlyph p_glyph )
+{
+    BlendGlyphToRGB( p_picture, i_picture_x, i_picture_y,
+                     i_a, i_x, i_y, i_z, p_glyph,
+                     BlendARGBPixelKnockout );
+}
diff --git a/modules/text_renderer/freetype/blend/yuv.h b/modules/text_renderer/freetype/blend/yuv.h
index d6955a1448..4b6457c6eb 100644
--- a/modules/text_renderer/freetype/blend/yuv.h
+++ b/modules/text_renderer/freetype/blend/yuv.h
@@ -60,11 +60,13 @@ static void FillYUVAPicture( picture_t *p_picture,
 }
 
 DECL_PIXEL_BLENDER(YUVA, 0, 0, 0, 0, A_PLANE, Y_PLANE, U_PLANE, V_PLANE);
+DECL_PIXEL_BLENDER_KNOCKOUT(YUVA, 0, 0, 0, 0, A_PLANE, Y_PLANE, U_PLANE, V_PLANE);
 
-static void BlendGlyphToYUVA( picture_t *p_picture,
-                              int i_picture_x, int i_picture_y,
-                              int i_a, int i_x, int i_y, int i_z,
-                              FT_BitmapGlyph p_glyph )
+static void BlendGlyphToYUVACommon( picture_t *p_picture,
+                                     int i_picture_x, int i_picture_y,
+                                     int i_a, int i_x, int i_y, int i_z,
+                                     FT_BitmapGlyph p_glyph,
+                                     void (*BlendPixel)( uint8_t **, int, int, int, int, int ) )
 {
     const uint8_t *srcrow = p_glyph->bitmap.buffer;
     int i_pitch_src = p_glyph->bitmap.pitch;
@@ -101,3 +103,20 @@ static void BlendGlyphToYUVA( picture_t *p_picture,
         dstrows[3] += p_picture->p[3].i_pitch;
     }
 }
+
+static void BlendGlyphToYUVA( picture_t *p_picture,
+                              int i_picture_x, int i_picture_y,
+                              int i_a, int i_x, int i_y, int i_z,
+                              FT_BitmapGlyph p_glyph )
+{
+    BlendGlyphToYUVACommon( p_picture, i_picture_x, i_picture_y, i_a, i_x, i_y, i_z, p_glyph, BlendYUVAPixel );
+}
+
+static void BlendGlyphToYUVAKnockout( picture_t *p_picture,
+                                     int i_picture_x, int i_picture_y,
+                                     int i_a, int i_x, int i_y, int i_z,
+                                     FT_BitmapGlyph p_glyph )
+{
+    BlendGlyphToYUVACommon( p_picture, i_picture_x, i_picture_y, i_a, i_x, i_y, i_z, p_glyph, BlendYUVAPixelKnockout );
+}
+
diff --git a/modules/text_renderer/freetype/freetype.c b/modules/text_renderer/freetype/freetype.c
index 8b49ec7aee..9b4d1d3657 100644
--- a/modules/text_renderer/freetype/freetype.c
+++ b/modules/text_renderer/freetype/freetype.c
@@ -562,7 +562,7 @@ static void RenderCharAXYZ( filter_t *p_filter,
             i_color = ch->p_style->i_shadow_color;
             break;
         case 1:
-            i_a     = i_a * ch->p_style->i_outline_alpha / 255;
+            i_a     = ch->p_style->i_outline_alpha;
             i_color = ch->p_style->i_outline_color;
             break;
         default:
@@ -583,7 +583,7 @@ static void RenderCharAXYZ( filter_t *p_filter,
         }
 
         /* Don't render if invisible or not wanted */
-        if( i_a == STYLE_ALPHA_TRANSPARENT ||
+        if(
            (g == 0 && 0 == (ch->p_style->i_style_flags & STYLE_SHADOW) ) ||
            (g == 1 && 0 == (ch->p_style->i_style_flags & STYLE_OUTLINE) )
           )
@@ -1103,7 +1103,7 @@ static subpicture_region_t *Render( filter_t *p_filter,
                 static const ft_drawing_functions DRAW_YUVA =
                     { .extract = YUVFromXRGB,
                       .fill =    FillYUVAPicture,
-                      .blend =   BlendGlyphToYUVA };
+                      .blend =   BlendGlyphToYUVAKnockout};
                 func = &DRAW_YUVA;
             }
             else if( *p_chroma == VLC_CODEC_RGBA
@@ -1112,7 +1112,7 @@ static subpicture_region_t *Render( filter_t *p_filter,
                 static const ft_drawing_functions DRAW_RGBA =
                     { .extract = RGBFromXRGB,
                       .fill =    FillRGBAPicture,
-                      .blend =   BlendGlyphToRGBA };
+                      .blend =   BlendGlyphToRGBAKnockout };
                 func = &DRAW_RGBA;
             }
             else if( *p_chroma == VLC_CODEC_ARGB
@@ -1121,7 +1121,7 @@ static subpicture_region_t *Render( filter_t *p_filter,
                 static const ft_drawing_functions DRAW_ARGB =
                     { .extract = RGBFromXRGB,
                       .fill =    FillARGBPicture,
-                      .blend =   BlendGlyphToARGB };
+                      .blend =   BlendGlyphToARGBKnockout };
                 func = &DRAW_ARGB;
             }
             else
-- 
2.43.0



More information about the vlc-devel mailing list