Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SubpixelAA FreeType rasterize_glyph fix #254

Merged
merged 7 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
34 changes: 26 additions & 8 deletions src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Canvas {
pub fn new(size: Vector2I, format: Format) -> Canvas {
Canvas::with_stride(
size,
size.x() as usize * format.bytes_per_pixel() as usize,
size.x() as usize * format.bytes_per_pixel(),
format,
)
}
Expand Down Expand Up @@ -85,6 +85,10 @@ impl Canvas {
)
}

/// Blits to a rectangle with origin at `dst_point` and size according to `src_size`.
/// If the target area overlaps the boundaries of the canvas, only the drawable region is blitted.
/// `dst_point` and `src_size` are specified in pixels. `src_stride` is specified in bytes.
/// `src_stride` must be equal or larger than the actual data length.
#[allow(dead_code)]
pub(crate) fn blit_from(
&mut self,
Expand All @@ -94,6 +98,17 @@ impl Canvas {
src_stride: usize,
src_format: Format,
) {
assert_eq!(
src_stride * src_size.y() as usize,
src_bytes.len(),
"Number of pixels in src_bytes does not match stride and size."
);
assert!(
src_stride >= src_size.x() as usize * src_format.bytes_per_pixel(),
"src_stride must be >= than src_size.x()"
);


let dst_rect = RectI::new(dst_point, src_size);
let dst_rect = dst_rect.intersection(RectI::new(Vector2I::default(), self.size));
let dst_rect = match dst_rect {
Expand Down Expand Up @@ -142,7 +157,7 @@ impl Canvas {

let size = dst_rect.size();

let dest_bytes_per_pixel = self.format.bytes_per_pixel() as usize;
let dest_bytes_per_pixel = self.format.bytes_per_pixel();
let dest_row_stride = size.x() as usize * dest_bytes_per_pixel;
let src_row_stride = utils::div_round_up(size.x() as usize, 8);

Expand All @@ -166,15 +181,18 @@ impl Canvas {
}
}

/// Blits to area `rect` using the data given in the buffer `src_bytes`.
/// `src_stride` must be specified in bytes.
/// The dimensions of `rect` must be in pixels.
fn blit_from_with<B: Blit>(
&mut self,
rect: RectI,
src_bytes: &[u8],
src_stride: usize,
src_format: Format,
) {
let src_bytes_per_pixel = src_format.bytes_per_pixel() as usize;
let dest_bytes_per_pixel = self.format.bytes_per_pixel() as usize;
let src_bytes_per_pixel = src_format.bytes_per_pixel();
let dest_bytes_per_pixel = self.format.bytes_per_pixel();

for y in 0..rect.height() {
let (dest_row_start, src_row_start) = (
Expand Down Expand Up @@ -216,7 +234,7 @@ pub enum Format {
impl Format {
/// Returns the number of bits per pixel that this image format corresponds to.
#[inline]
pub fn bits_per_pixel(self) -> u8 {
pub fn bits_per_pixel(self) -> usize {
match self {
Format::Rgba32 => 32,
Format::Rgb24 => 24,
Expand All @@ -226,7 +244,7 @@ impl Format {

/// Returns the number of color channels per pixel that this image format corresponds to.
#[inline]
pub fn components_per_pixel(self) -> u8 {
pub fn components_per_pixel(self) -> usize{
match self {
Format::Rgba32 => 4,
Format::Rgb24 => 3,
Expand All @@ -236,13 +254,13 @@ impl Format {

/// Returns the number of bits per color channel that this image format contains.
#[inline]
pub fn bits_per_component(self) -> u8 {
pub fn bits_per_component(self) -> usize {
self.bits_per_pixel() / self.components_per_pixel()
}

/// Returns the number of bytes per pixel that this image format corresponds to.
#[inline]
pub fn bytes_per_pixel(self) -> u8 {
pub fn bytes_per_pixel(self) -> usize {
self.bits_per_pixel() / 8
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/loaders/core_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ impl Font {
Some(canvas.pixels.as_mut_ptr() as *mut _),
canvas.size.x() as usize,
canvas.size.y() as usize,
canvas.format.bits_per_component() as usize,
canvas.format.bits_per_component(),
canvas.stride,
&cg_color_space,
cg_image_format,
Expand Down
3 changes: 1 addition & 2 deletions src/loaders/directwrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,7 @@ impl Font {
} else {
Format::Rgb24
};
let texture_bits_per_pixel = texture_format.bits_per_pixel();
let texture_bytes_per_pixel = texture_bits_per_pixel as usize / 8;
let texture_bytes_per_pixel = texture_format.bytes_per_pixel();
let texture_size = Vector2I::new(texture_width, texture_height);
let texture_stride = texture_width as usize * texture_bytes_per_pixel;

Expand Down
6 changes: 5 additions & 1 deletion src/loaders/freetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -838,9 +838,9 @@ impl Font {
// that mode.
let bitmap = &(*(*self.freetype_face).glyph).bitmap;
let bitmap_stride = bitmap.pitch as usize;
// bitmap_width is given in bytes.
let bitmap_width = bitmap.width;
let bitmap_height = bitmap.rows;
let bitmap_size = Vector2I::new(bitmap_width, bitmap_height);
let bitmap_buffer = bitmap.buffer as *const i8 as *const u8;
let bitmap_length = bitmap_stride * bitmap_height as usize;
if bitmap_buffer.is_null() {
Expand All @@ -858,9 +858,12 @@ impl Font {
// FIXME(pcwalton): This function should return a Result instead.
match bitmap.pixel_mode as u32 {
FT_PIXEL_MODE_GRAY => {
let bitmap_size = Vector2I::new(bitmap_width, bitmap_height);
canvas.blit_from(dst_point, buffer, bitmap_size, bitmap_stride, Format::A8);
}
FT_PIXEL_MODE_LCD | FT_PIXEL_MODE_LCD_V => {
// Three bytes per pixel for Rgb24 format
let bitmap_size = Vector2I::new(bitmap_width / 3, bitmap_height);
canvas.blit_from(
dst_point,
buffer,
Expand All @@ -870,6 +873,7 @@ impl Font {
);
}
FT_PIXEL_MODE_MONO => {
let bitmap_size = Vector2I::new(bitmap_width, bitmap_height);
canvas.blit_from_bitmap_1bpp(dst_point, buffer, bitmap_size, bitmap_stride);
}
_ => panic!("Unexpected FreeType pixel mode!"),
Expand Down
58 changes: 57 additions & 1 deletion tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ pub fn rasterize_glyph_with_full_hinting() {
RasterizationOptions::Bilevel,
)
.unwrap();
let origin = -raster_rect.origin().to_f32();
let origin: Vector2F = -raster_rect.origin().to_f32();
let mut canvas = Canvas::new(raster_rect.size(), Format::A8);
font.rasterize_glyph(
&mut canvas,
Expand Down Expand Up @@ -808,6 +808,61 @@ pub fn rasterize_glyph_with_full_hinting() {
}
}

// https://github.com/servo/font-kit/issues/252
// Panic when targeting Canvas larger than glyph with SubpixelAa option in Freetype.
#[cfg(all(
feature = "source",
any(
not(any(target_os = "macos", target_os = "ios", target_family = "windows")),
feature = "loader-freetype-default"
)
))]
#[test]
pub fn rasterize_glyph_with_full_hinting_subpixel() {
let font = SystemSource::new()
.select_best_match(&[FamilyName::SansSerif], &Properties::new())
.unwrap()
.load()
.unwrap();
let glyph_id = font.glyph_for_char('L').unwrap();
let size = 32.0;
let raster_rect = font
.raster_bounds(
glyph_id,
size,
Transform2F::default(),
HintingOptions::Full(size),
RasterizationOptions::SubpixelAa,
)
.unwrap();
let origin: Vector2F = -raster_rect.origin().to_f32();
let mut canvas = Canvas::new(raster_rect.size(), Format::Rgb24);
font.rasterize_glyph(
&mut canvas,
glyph_id,
size,
Transform2F::from_translation(origin),
HintingOptions::Full(size),
RasterizationOptions::SubpixelAa,
)
.unwrap();
check_L_shape(&canvas);

// Test with larger canvas
let mut canvas = Canvas::new(Vector2I::new(100, 100), Format::Rgb24);
font.rasterize_glyph(
&mut canvas,
glyph_id,
size,
Transform2F::from_translation(origin),
HintingOptions::Full(size),
RasterizationOptions::SubpixelAa,
)
.unwrap();
check_L_shape(&canvas);
}


#[cfg(all(feature = "source", target_family = "windows"))]
#[test]
pub fn rasterize_glyph() {
Expand Down Expand Up @@ -888,6 +943,7 @@ pub fn rasterize_empty_glyph_on_empty_canvas() {
.unwrap();
}


#[cfg(feature = "source")]
#[test]
pub fn font_transform() {
Expand Down
Loading