Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# выживший мутант = молчаливая дыра в наборе (coverage=пол, mutation=правда).
examine_globs = [
"crates/labcolors-core/src/alpha.rs",
"crates/labcolors-core/src/appearance.rs",
"crates/labcolors-core/src/cleanliness.rs",
"crates/labcolors-core/src/glow.rs",
"crates/labcolors-core/src/material.rs",
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ NamedRoleTable

- **Клиентский словарь.** Имена ролей, тем и алиасов приходят из `ThemeConfig`. Core не выводит смысл из слов `primary`, `danger`, `hover` или имени компонента.
- **Контекстный resolve.** Одна таблица решается заново для переданного локального фона и темы.
- **Специализированные зависимости.** Например, `PairLabel` решается против эмитированного композита своей `PairFill`, а не против фона страницы.
- **Вложенные зависимости через generic-компонент.** Например, `PairLabel` решается против объявленной derived-поверхности (точный source-over композит своего тинта над локальным фоном), которую собирает приватный appearance-граф; поверхность не является эмитированным `PairFill`, и роль не решается против фона страницы.
- **Несколько видов результата.** Роль может вернуть solid, translucent, material, точечный glow, явное отсутствие значения или недостижимость.
- **Версионированные численные свидетельства.** Exact encoded-sRGB8
source-over/screen операции несут проверяемый профиль и `bit-exact`
Expand Down Expand Up @@ -139,7 +139,8 @@ Core владеет:
| `DecorativeLc` | декоративную контрастную величину | нормативную доступность текста |
| `Ladder` | клиентский preset тинта и alpha | общий закон уровней core |
| `AlphaAnalog` | точечный композит заданной solid-цели | воспринимаемую глубину |
| `PairFill` / `PairLabel` | вложенную пару fill → composite → label | произвольный generic graph |
| `PairFill` | сдвинутую до победы лейбл-стороны солид-заливку | подложку `PairLabel` |
| `PairLabel` | foreground против derived тинт-поверхности (generic source-over компонент) | публичный произвольный graph API |
| `Material` | объявленную многослойную точечную композицию | blur, преломление или физическое стекло |
| `Glow` | рецепт точечного эффекта | пространственный glow после blur |
| `Zero` | явное отсутствие цветового значения | пропущенный ключ |
Expand Down
23 changes: 18 additions & 5 deletions crates/labcolors-core/src/alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,37 @@ pub fn composite_over_encoded(
///
/// `Err`, если `alpha` не конечна или лежит вне `[0,1]`.
pub fn composite_over_srgb8(tint: [u8; 3], alpha: f64, bg: [u8; 3]) -> Result<[u8; 3], String> {
if !alpha.is_finite() || !(0.0..=1.0).contains(&alpha) {
return Err(format!("alpha вне конечного [0,1]: {alpha}"));
}
validate_alpha(alpha)?;
Ok([
source_over_channel_srgb8(tint[0], alpha, bg[0]),
source_over_channel_srgb8(tint[1], alpha, bg[1]),
source_over_channel_srgb8(tint[2], alpha, bg[2]),
])
}

fn encoded_to_srgb8(rgb: [f64; 3], label: &str) -> Result<[u8; 3], String> {
/// SSOT-валидатор домена straight-alpha: конечная и в `[0,1]`. Единый и для
/// композитора, и для графовых bindings — доменный отказ по альфе обязан
/// звучать одинаково на всех путях (тексты отказов публично наблюдаемы).
pub(crate) fn validate_alpha(alpha: f64) -> Result<(), String> {
if !alpha.is_finite() || !(0.0..=1.0).contains(&alpha) {
return Err(format!("alpha вне конечного [0,1]: {alpha}"));
}
Ok(())
}

/// Квантизация кодированного цвета в эмитируемые sRGB8-байты с доменной
/// проверкой — тот же контракт, которым композитор готовит свои входы.
/// `label` попадает в текст доменного отказа (`tint`/`bg` исторические).
pub(crate) fn encoded_to_srgb8(rgb: [f64; 3], label: &str) -> Result<[u8; 3], String> {
if !is_encoded_rgb(rgb) {
return Err(format!("{label} вне конечного encoded-sRGB [0,1]: {rgb:?}"));
}
Ok(rgb.map(|channel| (channel * 255.0).round() as u8))
}

fn hex_from_srgb8(rgb: [u8; 3]) -> String {
/// Форматирование эмитируемых sRGB8-байт в `#RRGGBB` — единая точка формата
/// для композитора и потребителей его байтовых результатов (appearance-граф).
pub(crate) fn hex_from_srgb8(rgb: [u8; 3]) -> String {
format!("#{:02X}{:02X}{:02X}", rgb[0], rgb[1], rgb[2])
}

Expand Down
Loading
Loading