# Jak chyba v imageproc vedla k změně API image-rs: technický rozbor
Při práci s knihovnou pro zpracování obrázků imageproc vývojáři objevili kritickou chybu při vykreslování textu na obrázky s alfa kanálem. Oprava vyžadovala nejen úpravu v imageproc, ale i změnu kontraktu základní knihovny image-rs. Probereme příčiny a řešení problému, které je aktuální pro všechny, kdo pracují s grafikou v Rust.
Podstata problému: artefakty při renderování textu na RGBA
Chyba se projevovala výhradně při zpracování obrázků s alfa kanálem (RGBA, LumaA). Pro RGB obrázky algoritmus fungoval správně, ale přidání průhlednosti způsobovalo vizuální artefakty. Konkrétní symptomy:
- Při nízké alfa průhlednosti se text stával prakticky neviditelným
- Při plné neprůhlednosti (alfa=255) vykreslování fungovalo normálně
- U poloprůhledných barev docházelo k deformacím glyfů
Klíčové pozorování: problém vznikl pouze při přítomnosti alfa kanálu, což ukazovalo na nesprávné zpracování průhlednosti v algoritmu míchání barev.
Analýza implementace draw_text_mut
Prohlédneme si zdrojový kód funkce pro vykreslování textu:
pub fn draw_text_mut<C>(
canvas: &mut C,
color: C::Pixel,
x: i32,
y: i32,
scale: impl Into<PxScale> + Copy,
font: &impl Font,
text: &str,
) where
C: Canvas,
<C::Pixel as Pixel>::Subpixel: Into<f32> + Clamp<f32>,
{
// ...
let weighted_color = weighted_sum(pixel, color, 1.0 - gv, gv);
canvas.draw_pixel(image_x, image_y, weighted_color);
}
Kritický řádek je volání weighted_sum. Tato funkce používala gv (koeficient pokrytí glyfu) pro míchání RGB komponent, přičemž úplně ignorovala alfa kanál. Podle dokumentace gv nabírá hodnoty od 0.0 (plná průhlednost) do 1.0 (plná neprůhlednost). V současné implementaci:
- Pro RGB: správně, protože alfa kanál chybí
- Pro RGBA: chyba, protože
gvse aplikoval na barevné kanály místo alfa komponenty
Matematicky by pro obrázky s průhledností mělo platit: alpha' = alpha * gv, zatímco původní algoritmus měnil RGB úměrně gv.
Cesta k řešení: od weighted_sum k blend
V knihovně image-rs již existuje metoda Pixel::blend, která správně zpracovává alfa kanály. Pro její použití však bylo nutné správně připravit barvu textu. Klíčové kroky řešení:
- Použít
map_with_alphapro aplikacigvpouze na alfa kanál:
let color = color.map_with_alpha(|f| f, |g| Clamp::clamp(g.into() * gv));
- Aplikovat metodu
blendpro míchání s pozadím:
pixel.blend(&color);
Toto řešení zohledňuje specifika RGBA, kde gv ovlivňuje výhradně průhlednost, nikoli barevné komponenty. Přitom zachovává správné zpracování pro RGB prostřednictvím původního weighted_sum.
Problém určení alfa kanálu
Pro větvení algoritmu (blend pro RGBA / weighted_sum pro RGB) je nutné určit přítomnost alfa kanálu. V původní implementaci image-rs chyběl přímý způsob kontroly. Zvažované varianty:
- Dočasné řešení přes COLOR_MODEL:
let has_alpha = match C::Pixel::COLOR_MODEL {
"RGBA" | "YA" => true,
_ => false,
};
Tento přístup je nepřijatelný z dvou důvodů:
- Závislost na řetězcových konstantách, které se mohou změnit v budoucích verzích
- Porušení abstrakce – logika zpracování obrázků by neměla znát interní detaily barevných modelů
Správné řešení vyžadovalo přidání metadat na úrovni traitu Pixel.
Změna API v image-rs
Byl navržen PR, který přidává konstantu HAS_ALPHA do traitu Pixel:
pub trait Pixel: Copy + Clone {
// ...
const HAS_ALPHA: bool;
// ...
}
Implementace přes makro define_colors zajistila automatické nastavení hodnoty:
macro_rules! define_colors {
// ...
const HAS_ALPHA: bool = $alphas > 0;
// ...
}
Tato změna:
- Přenáší odpovědnost za informace o struktuře pixelu do jádra knihovny
- Zajišťuje type-safe kontrolu v době kompilace
- Nenarušuje zpětnou kompatibilitu, protože přidává novou konstantu
Finální algoritmus
S ohledem na nové API byla funkce draw_text_mut přepsána s podmínkovou obsluhou:
if C::Pixel::HAS_ALPHA {
let color = color.map_with_alpha(|f| f, |g| Clamp::clamp(g.into() * gv));
pixel.blend(&color);
} else {
pixel = weighted_sum(pixel, color, 1.0 - gv, gv);
}
Nyní algoritmus:
- Pro RGBA: správně zpracovává průhlednost přes
blend - Pro RGB: zachovává původní logiku přes
weighted_sum - Zajišťuje absence artefaktů u libovolného typu obrázku
Co je důležité
- Absence metadat o pixelu – klíčový problém spočíval ne ve vzorci míchání, ale v nedostatku informací o struktuře pixelu v okamžiku vykreslování.
- Změna kontraktu knihovny – lokální chyba vedla k nutnosti rozšíření API image-rs, což podtrhuje důležitost návrhu rozhraní s ohledem na budoucí scénáře.
- Type-safe řešení – konstanta
HAS_ALPHAzajišťuje kontrolu v době kompilace a eliminuje křehká řetězcová porovnání. - Kompatibilita – přidání konstanty nenarušuje stávající kód a zachovává zpětnou kompatibilitu.
— Editorial Team
Zatím žádné komentáře.