use crate::util::pack_u16_pair;
use vello_common::TextureId;
use vello_common::encode::{EncodedKind, EncodedPaint};
use vello_common::image_cache::ImageCache;
use vello_common::multi_atlas::AtlasId;
use vello_common::paint::{ImageSource, Paint};
const COLOR_SOURCE_PAYLOAD: u32 = 0;
pub(crate) const COLOR_SOURCE_LAYER: u32 = 1;
const PAINT_TYPE_SOLID: u32 = 0;
const PAINT_TYPE_IMAGE: u32 = 1;
const PAINT_TYPE_LINEAR_GRADIENT: u32 = 2;
const PAINT_TYPE_RADIAL_GRADIENT: u32 = 3;
const PAINT_TYPE_SWEEP_GRADIENT: u32 = 4;
const PAINT_TYPE_BLURRED_ROUNDED_RECT: u32 = 5;
pub(crate) const COLOR_SOURCE_SHIFT: u32 = 29;
const PAINT_TYPE_SHIFT: u32 = 26;
pub(crate) const EXTERNAL_TEXTURE_SLOT_SHIFT: u32 = 24;
const PAINT_TEXTURE_INDEX_MASK: u32 = (1 << EXTERNAL_TEXTURE_SLOT_SHIFT) - 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum TextureSourceId {
Atlas(AtlasId),
External(TextureId),
}
#[derive(Clone, Copy)]
pub(crate) struct PackedPaint {
payload: PaintPayload,
pub(crate) paint: u32,
pub(crate) texture_source: Option<TextureSourceId>,
pub(crate) opaque: bool,
}
impl PackedPaint {
pub(crate) fn payload_at(self, x: u16, y: u16) -> u32 {
match self.payload {
PaintPayload::Solid(rgba) => rgba,
PaintPayload::Position => pack_u16_pair(x, y),
}
}
}
#[derive(Clone, Copy)]
enum PaintPayload {
Solid(u32),
Position,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct PaintResolver<'a> {
encoded: &'a [EncodedPaint],
gpu_offsets: &'a [u32],
image_cache: Option<&'a ImageCache>,
}
impl<'a> PaintResolver<'a> {
pub(crate) fn new(encoded: &'a [EncodedPaint], gpu_offsets: &'a [u32]) -> Self {
Self {
encoded,
gpu_offsets,
image_cache: None,
}
}
pub(crate) fn with_image_cache(mut self, image_cache: &'a ImageCache) -> Self {
self.image_cache = Some(image_cache);
self
}
#[inline]
pub(crate) fn pack(self, paint: &Paint) -> PackedPaint {
match paint {
Paint::Solid(color) => PackedPaint {
payload: PaintPayload::Solid(color.as_premul_rgba8().to_u32()),
paint: (COLOR_SOURCE_PAYLOAD << COLOR_SOURCE_SHIFT)
| (PAINT_TYPE_SOLID << PAINT_TYPE_SHIFT),
texture_source: None,
opaque: color.is_opaque(),
},
Paint::Indexed(indexed_paint) => {
let paint_id = indexed_paint.index();
let gpu_offset = self.gpu_offsets[paint_id];
let encoded_paint = &self.encoded[paint_id];
let (paint_type, texture_source) = match encoded_paint {
EncodedPaint::Image(encoded_image) => match &encoded_image.source {
ImageSource::ExternalTexture { id, .. } => {
(PAINT_TYPE_IMAGE, Some(TextureSourceId::External(*id)))
}
ImageSource::OpaqueId { id, .. } => {
let image_resource = self.image_cache.unwrap().get(*id).unwrap();
(
PAINT_TYPE_IMAGE,
Some(TextureSourceId::Atlas(image_resource.atlas_id)),
)
}
ImageSource::Pixmap(_) => unimplemented!("Unsupported image source"),
},
EncodedPaint::Gradient(gradient) => {
let paint_type = match &gradient.kind {
EncodedKind::Linear(_) => PAINT_TYPE_LINEAR_GRADIENT,
EncodedKind::Radial(_) => PAINT_TYPE_RADIAL_GRADIENT,
EncodedKind::Sweep(_) => PAINT_TYPE_SWEEP_GRADIENT,
};
(paint_type, None)
}
EncodedPaint::BlurredRoundedRect(_) => (PAINT_TYPE_BLURRED_ROUNDED_RECT, None),
};
debug_assert!(
gpu_offset <= PAINT_TEXTURE_INDEX_MASK,
"paint offsets fit in 24 bits because resource textures are capped at 4096×4096"
);
PackedPaint {
payload: PaintPayload::Position,
paint: (COLOR_SOURCE_PAYLOAD << COLOR_SOURCE_SHIFT)
| (paint_type << PAINT_TYPE_SHIFT)
| (gpu_offset & PAINT_TEXTURE_INDEX_MASK),
texture_source,
opaque: !encoded_paint.may_have_transparency(),
}
}
}
}
}