From aa1f4cb42b91ee43e7884c41966b87fb4e0e7290 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 12 May 2025 12:46:32 +0200 Subject: [PATCH] Fix alpha compositing in ft2font's draw_bitmap. The old formula (`*dst |= *src`) works fine when either dst or src is full transparent or fully opaque, but not for compositing intermediate values. Fix that (while keeping a fast-path for the common case of writing on an empty buffer). Example (note the more uniform gray zone between the two letters): ``` from matplotlib import pyplot as plt, ft2font as f, cbook import numpy as np font = f.FT2Font(str(cbook._get_data_path("fonts/ttf/DejaVuSans.ttf"))) font.set_size(24, 72) im = f.FT2Image(30, 30) ga = font.load_char(ord("A")) gv = font.load_char(ord("V")) font.draw_glyph_to_bitmap(im, 2, 2, ga) font.draw_glyph_to_bitmap(im, 12, 2, gv) (plt.figure(layout="constrained", figsize=(3, 3)) .add_subplot(xticks=[], yticks=[]) .imshow(np.asarray(im), cmap="gray")) plt.show() ``` --- src/ft2font.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index b2c2c0fa9bd1..1f58aa593e09 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -73,11 +73,20 @@ FT2Image::~FT2Image() free(m_buffer); } -void draw_bitmap( - py::array_t im, FT_Bitmap *bitmap, FT_Int x, FT_Int y) +static std::array const alpha_cov_merge_table{[]() { + auto table = std::array{}; + for (auto dst = 0; dst < 0x100; ++dst) { + for (auto src = 0; src < 0x100; ++src) { + table[(dst << 8) + src] = dst + src - (dst * src + 0x7f) / 0xff; + } + } + return table; +} ()}; + +void +draw_bitmap(py::array_t im, FT_Bitmap *bitmap, FT_Int x, FT_Int y) { auto buf = im.mutable_data(0); - FT_Int image_width = (FT_Int)im.shape(1); FT_Int image_height = (FT_Int)im.shape(0); FT_Int char_width = bitmap->width; @@ -96,7 +105,8 @@ void draw_bitmap( unsigned char *dst = buf + (i * image_width + x1); unsigned char *src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start); for (FT_Int j = x1; j < x2; ++j, ++dst, ++src) - *dst |= *src; + // Provide a fast-path for the common case of black background. + *dst = *dst ? alpha_cov_merge_table[(*dst << 8) + *src] : *src; } } else if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { for (FT_Int i = y1; i < y2; ++i) { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy