Skip to content
Open
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.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
members = [
"crate"
]
resolver = "2"
20 changes: 3 additions & 17 deletions crate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,11 @@ homepage = "https://silvia-odwyer.github.io/photon/"
crate-type = ["cdylib", "rlib"]

[dependencies]
image = { version = "0.24.8", default-features = false, features = [
"gif",
"jpeg",
"ico",
"png",
"pnm",
"tga",
"tiff",
"webp",
"bmp",
"hdr",
"dxt",
"dds",
"farbfeld",
] }
image = "0.25.8"
palette = "0.6.1"
rand = "0.7.2"
imageproc = { version = "0.23.0", default-features = false }
rusttype = "0.9.2"
imageproc = { version = "0.25.0", default-features = false }
ab_glyph = "0.2"
base64 = "0.13.0"
time = "0.3.21"
wasm-bindgen = { version = "0.2.92", optional = true }
Expand Down
42 changes: 30 additions & 12 deletions crate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
use base64::{decode, encode};
use image::DynamicImage::ImageRgba8;
use image::GenericImage;
use image::codecs::jpeg::JpegEncoder;
use image::codecs::avif::AvifEncoder;
use image::{ExtendedColorType, ImageEncoder};
use serde::{Deserialize, Serialize};
use std::io::Cursor;

Expand Down Expand Up @@ -187,7 +190,7 @@ impl PhotonImage {
img = ImageRgba8(img.to_rgba8());

let mut buffer = vec![];
img.write_to(&mut Cursor::new(&mut buffer), image::ImageOutputFormat::Png)
img.write_to(&mut Cursor::new(&mut buffer), image::ImageFormat::Png)
.unwrap();
let base64 = encode(&buffer);

Expand All @@ -201,19 +204,21 @@ impl PhotonImage {
let mut img = helpers::dyn_image_from_raw(self);
img = ImageRgba8(img.to_rgba8());
let mut buffer = vec![];
img.write_to(&mut Cursor::new(&mut buffer), image::ImageOutputFormat::Png)
.unwrap();
img.write_to(&mut Cursor::new(&mut buffer), image::ImageFormat::Png).unwrap();
buffer
}

/// Convert the PhotonImage to raw bytes. Returns a JPEG.
pub fn get_bytes_jpeg(&self, quality: u8) -> Vec<u8> {
let mut img = helpers::dyn_image_from_raw(self);
img = ImageRgba8(img.to_rgba8());
let mut buffer = vec![];
let out_format = image::ImageOutputFormat::Jpeg(quality);
img.write_to(&mut Cursor::new(&mut buffer), out_format)
.unwrap();
let img = helpers::dyn_image_from_raw(self);

// JPEG has no alpha; convert to RGB8.
let rgb = img.to_rgb8();
let (w, h) = rgb.dimensions();

let mut buffer = Vec::new();
let encoder = JpegEncoder::new_with_quality(&mut buffer, quality);
encoder.write_image(rgb.as_raw(), w, h, ExtendedColorType::Rgb8).unwrap();
buffer
}

Expand All @@ -222,9 +227,22 @@ impl PhotonImage {
let mut img = helpers::dyn_image_from_raw(self);
img = ImageRgba8(img.to_rgba8());
let mut buffer = vec![];
let out_format = image::ImageOutputFormat::WebP;
img.write_to(&mut Cursor::new(&mut buffer), out_format)
.unwrap();
let out_format = image::ImageFormat::WebP;
img.write_to(&mut Cursor::new(&mut buffer), out_format).unwrap();
buffer
}

/// Convert the PhotonImage to raw bytes. Returns an AVIF.
pub fn get_bytes_avif(&self, speed: u8, quality: u8) -> Vec<u8> {
let img = helpers::dyn_image_from_raw(self);

// AVIF supports alpha; preserve it.
let rgba = img.to_rgba8();
let (w, h) = rgba.dimensions();

let mut buffer = Vec::new();
let encoder = AvifEncoder::new_with_speed_quality(&mut buffer, speed, quality);
encoder.write_image(rgba.as_raw(), w, h, ExtendedColorType::Rgba8).unwrap();
buffer
}

Expand Down
55 changes: 20 additions & 35 deletions crate/src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

use crate::iter::ImageIterator;
use crate::{helpers, PhotonImage};
use image::{DynamicImage, Rgba};
use image::{DynamicImage, GrayImage, Luma, Rgba};
use imageproc::distance_transform::Norm;
use imageproc::drawing::draw_text_mut;
use imageproc::morphology::dilate_mut;
use rusttype::{Font, Scale};
use ab_glyph::{FontArc, PxScale};

#[cfg(feature = "enable_wasm")]
use wasm_bindgen::prelude::*;
Expand Down Expand Up @@ -45,34 +45,19 @@ pub fn draw_text_with_border(
) {
let mut image = helpers::dyn_image_from_raw(photon_img).to_rgba8();

let mut image2: DynamicImage =
DynamicImage::new_luma8(image.width(), image.height());
let mut mask: GrayImage = GrayImage::new(image.width(), image.height());
let font_bytes: &[u8] = include_bytes!("../fonts/Roboto-Regular.ttf");
let font = FontArc::try_from_slice(font_bytes).expect("invalid font bytes");

let font = Vec::from(include_bytes!("../fonts/Roboto-Regular.ttf") as &[u8]);
let font = Font::try_from_bytes(&font).unwrap();
let scale = Scale {
x: font_size * 1.0,
y: font_size,
};
draw_text_mut(
&mut image2,
Rgba([255u8, 255u8, 255u8, 255u8]),
x,
y,
scale,
&font,
text,
);

let mut image2 = image2.to_luma8();
dilate_mut(&mut image2, Norm::LInf, 4u8);
let scale: PxScale = PxScale::from(font_size);
draw_text_mut(&mut mask, Luma([255u8]), x, y, scale, &font, text);
dilate_mut(&mut mask, Norm::LInf, 4u8);

// Add a border to the text.
for (x, y) in ImageIterator::with_dimension(&image2.dimensions()) {
let pixval = 255 - image2.get_pixel(x, y)[0];
for (px, py) in ImageIterator::with_dimension(&mask.dimensions()) {
let v = mask.get_pixel(px, py)[0];
let pixval = 255u8 - v;
if pixval != 255 {
let new_pix = Rgba([pixval, pixval, pixval, 255]);
image.put_pixel(x, y, new_pix);
image.put_pixel(px, py, Rgba([pixval, pixval, pixval, 255]));
}
}

Expand All @@ -85,7 +70,8 @@ pub fn draw_text_with_border(
&font,
text,
);
let dynimage = image::DynamicImage::ImageRgba8(image);

let dynimage = DynamicImage::ImageRgba8(image);
photon_img.raw_pixels = dynimage.into_bytes();
}

Expand Down Expand Up @@ -121,12 +107,10 @@ pub fn draw_text(
) {
let mut image = helpers::dyn_image_from_raw(photon_img).to_rgba8();

let font = Vec::from(include_bytes!("../fonts/Roboto-Regular.ttf") as &[u8]);
let font = Font::try_from_bytes(&font).unwrap();
let scale = Scale {
x: font_size * 1.0,
y: font_size,
};
let font_bytes: &[u8] = include_bytes!("../fonts/Roboto-Regular.ttf");
let font = FontArc::try_from_slice(font_bytes).expect("invalid font bytes");

let scale: PxScale = PxScale::from(font_size);

draw_text_mut(
&mut image,
Expand All @@ -137,6 +121,7 @@ pub fn draw_text(
&font,
text,
);
let dynimage = image::DynamicImage::ImageRgba8(image);

let dynimage = DynamicImage::ImageRgba8(image);
photon_img.raw_pixels = dynimage.into_bytes();
}