mirror of https://github.com/Aloxaf/silicon
Make silicon also a lib (#8)
* Make it also a lib * More comment * Adjust code structure * Add the 'bin' feature * Remove failure dependency from lib * Update README
This commit is contained in:
parent
d6f05ec24c
commit
5775e8a1da
19
Cargo.toml
19
Cargo.toml
|
@ -14,25 +14,42 @@ syntect = "3.2"
|
|||
image = "0.21"
|
||||
imageproc = "0.18"
|
||||
font-kit = "0.3.1"
|
||||
failure = "0.1.5"
|
||||
clipboard = "0.5.0"
|
||||
tempfile = "3.1.0"
|
||||
conv = "0.3.3"
|
||||
euclid = "0.19"
|
||||
log = "0.4.7"
|
||||
|
||||
[lib]
|
||||
name = "silicon"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "silicon"
|
||||
path = "src/bin.rs"
|
||||
|
||||
[dependencies.failure]
|
||||
version = "0.1.5"
|
||||
optional = true
|
||||
|
||||
[dependencies.structopt]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
features = [ "color", "wrap_help" ]
|
||||
optional = true
|
||||
|
||||
[dependencies.env_logger]
|
||||
version = "0.6.2"
|
||||
default-features = false
|
||||
features = [ "termcolor", "atty", "humantime" ]
|
||||
optional = true
|
||||
|
||||
[patch.crates-io]
|
||||
font-kit = { version = "0.3.1", git = "https://github.com/pcwalton/font-kit" }
|
||||
|
||||
[features]
|
||||
default = [ "bin" ]
|
||||
bin = [ "structopt", "env_logger", "failure" ]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# Silicon
|
||||
|
||||
[![crates.io](https://img.shields.io/crates/v/silicon.svg)](https://crates.io/crates/silicon)
|
||||
[![Crates.io](https://img.shields.io/crates/v/silicon.svg)](https://crates.io/crates/silicon)
|
||||
[![Documentation](https://docs.rs/silicon/badge.svg)](https://docs.rs/silicon)
|
||||
[![Build Status](https://travis-ci.org/Aloxaf/silicon.svg?branch=master)](https://travis-ci.org/Aloxaf/silicon)
|
||||
![license](https://img.shields.io/crates/l/silicon.svg)
|
||||
![License](https://img.shields.io/crates/l/silicon.svg)
|
||||
|
||||
Silicon is an alternative to [Carbon](https://github.com/dawnlabs/carbon) implemented in Rust.
|
||||
|
||||
|
|
|
@ -4,26 +4,50 @@ extern crate log;
|
|||
extern crate failure;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::utils::{add_window_controls, dump_image_to_clipboard, round_corner};
|
||||
use crate::utils::*;
|
||||
use failure::Error;
|
||||
use image::DynamicImage;
|
||||
use structopt::StructOpt;
|
||||
use syntect::dumps;
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::ThemeSet;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::LinesWithEndings;
|
||||
#[cfg(target_os = "linux")]
|
||||
use {image::ImageOutputFormat, std::process::Command};
|
||||
|
||||
mod blur;
|
||||
mod config;
|
||||
mod font;
|
||||
mod formatter;
|
||||
mod utils;
|
||||
pub mod blur;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod font;
|
||||
pub mod formatter;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn dump_image_to_clipboard(image: &DynamicImage) -> Result<(), Error> {
|
||||
let mut temp = tempfile::NamedTempFile::new()?;
|
||||
image.write_to(&mut temp, ImageOutputFormat::PNG)?;
|
||||
Command::new("xclip")
|
||||
.args(&[
|
||||
"-sel",
|
||||
"clip",
|
||||
"-t",
|
||||
"image/png",
|
||||
temp.path().to_str().unwrap(),
|
||||
])
|
||||
.status()
|
||||
.map_err(|e| format_err!("Failed to copy image to clipboard: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn dump_image_to_clipboard(_image: &DynamicImage) -> Result<(), Error> {
|
||||
Err(format_err!(
|
||||
"This feature hasn't been implemented for your system"
|
||||
))
|
||||
}
|
||||
|
||||
fn run() -> Result<(), Error> {
|
||||
let config: Config = Config::from_args();
|
||||
|
||||
let ps = dumps::from_binary::<SyntaxSet>(include_bytes!("../assets/syntaxes.bin")); //SyntaxSet::load_defaults_newlines();
|
||||
let ts = dumps::from_binary::<ThemeSet>(include_bytes!("../assets/themes.bin")); // ThemeSet::load();
|
||||
let (ps, ts) = init_syntect();
|
||||
|
||||
if config.list_themes {
|
||||
for i in ts.themes.keys() {
|
||||
|
@ -43,16 +67,7 @@ fn run() -> Result<(), Error> {
|
|||
|
||||
let mut formatter = config.get_formatter()?;
|
||||
|
||||
let mut image = formatter.format(&highlight, &theme);
|
||||
|
||||
if !config.no_window_controls {
|
||||
add_window_controls(&mut image);
|
||||
}
|
||||
if !config.no_round_corner {
|
||||
round_corner(&mut image, 12);
|
||||
}
|
||||
|
||||
let image = config.get_shadow_adder().apply_to(&image);
|
||||
let image = formatter.format(&highlight, &theme);
|
||||
|
||||
if config.to_clipboard {
|
||||
dump_image_to_clipboard(&image)?;
|
|
@ -206,26 +206,22 @@ impl Config {
|
|||
|
||||
pub fn theme(&self, ts: &ThemeSet) -> Result<Theme, Error> {
|
||||
if let Some(theme) = ts.themes.get(&self.theme) {
|
||||
return Ok(theme.clone());
|
||||
Ok(theme.clone())
|
||||
} else {
|
||||
return Ok(ThemeSet::get_theme(&self.theme)?);
|
||||
Ok(ThemeSet::get_theme(&self.theme)?)
|
||||
}
|
||||
// &ts.themes[&self.theme]
|
||||
}
|
||||
|
||||
pub fn get_formatter(&self) -> Result<ImageFormatter, Error> {
|
||||
let mut formatter = ImageFormatterBuilder::new()
|
||||
let formatter = ImageFormatterBuilder::new()
|
||||
.line_pad(self.line_pad)
|
||||
.window_controls(!self.no_window_controls)
|
||||
.line_number(!self.no_line_number)
|
||||
.font(self.font.clone().unwrap_or_else(|| vec![]))
|
||||
.round_corner(!self.no_round_corner)
|
||||
.window_controls(!self.no_window_controls)
|
||||
.shadow_adder(self.get_shadow_adder())
|
||||
.highlight_lines(self.highlight_lines.clone().unwrap_or_else(|| vec![]));
|
||||
if let Some(fonts) = &self.font {
|
||||
formatter = formatter.font(fonts);
|
||||
}
|
||||
if self.no_line_number {
|
||||
formatter = formatter.line_number(false);
|
||||
}
|
||||
if self.no_window_controls {
|
||||
formatter = formatter.code_pad_top(0);
|
||||
}
|
||||
|
||||
Ok(formatter.build()?)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
use font_kit::error::{FontLoadingError, SelectionError};
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FontError {
|
||||
SelectionError(SelectionError),
|
||||
FontLoadingError(FontLoadingError),
|
||||
}
|
||||
|
||||
impl Error for FontError {}
|
||||
|
||||
impl Display for FontError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
FontError::SelectionError(e) => write!(f, "Font error: {}", e),
|
||||
FontError::FontLoadingError(e) => write!(f, "Font error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SelectionError> for FontError {
|
||||
fn from(e: SelectionError) -> Self {
|
||||
FontError::SelectionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FontLoadingError> for FontError {
|
||||
fn from(e: FontLoadingError) -> Self {
|
||||
FontError::FontLoadingError(e)
|
||||
}
|
||||
}
|
32
src/font.rs
32
src/font.rs
|
@ -1,6 +1,19 @@
|
|||
//! A basic font manager with fallback support
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use image::{RgbImage, Rgb};
|
||||
//! use silicon::font::{FontCollection, FontStyle};
|
||||
//!
|
||||
//! let mut image = RgbImage::new(200, 100);
|
||||
//! let font = FontCollection::new(&[("Hack", 27.0), ("FiraCode", 27.0)]).unwrap();
|
||||
//!
|
||||
//! font.draw_text_mut(&mut image, Rgb([255, 0, 0]), 0, 0, FontStyle::REGULAR, "Hello, world");
|
||||
//! ```
|
||||
use crate::error::FontError;
|
||||
use conv::ValueInto;
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use failure::Error;
|
||||
use font_kit::canvas::{Canvas, Format, RasterizationOptions};
|
||||
use font_kit::font::Font;
|
||||
use font_kit::hinting::HintingOptions;
|
||||
|
@ -14,6 +27,7 @@ use std::collections::HashMap;
|
|||
use std::sync::Arc;
|
||||
use syntect::highlighting;
|
||||
|
||||
/// Font style
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum FontStyle {
|
||||
REGULAR,
|
||||
|
@ -40,6 +54,7 @@ impl From<highlighting::FontStyle> for FontStyle {
|
|||
|
||||
use FontStyle::*;
|
||||
|
||||
/// A single font with specific size
|
||||
#[derive(Debug)]
|
||||
pub struct ImageFont {
|
||||
pub fonts: HashMap<FontStyle, Font>,
|
||||
|
@ -47,6 +62,7 @@ pub struct ImageFont {
|
|||
}
|
||||
|
||||
impl Default for ImageFont {
|
||||
/// It will use Hack font (size: 26.0) by default
|
||||
fn default() -> Self {
|
||||
let l = vec![
|
||||
(
|
||||
|
@ -77,7 +93,7 @@ impl Default for ImageFont {
|
|||
}
|
||||
|
||||
impl ImageFont {
|
||||
pub fn new(name: &str, size: f32) -> Result<Self, Error> {
|
||||
pub fn new(name: &str, size: f32) -> Result<Self, FontError> {
|
||||
// Silicon already contains Hack font
|
||||
if name == "Hack" {
|
||||
let mut font = Self::default();
|
||||
|
@ -122,17 +138,19 @@ impl ImageFont {
|
|||
Ok(Self { fonts, size })
|
||||
}
|
||||
|
||||
/// Get a font by style. If there is no such a font, it will return the REGULAR font.
|
||||
pub fn get_by_style(&self, style: FontStyle) -> &Font {
|
||||
self.fonts
|
||||
.get(&style)
|
||||
.unwrap_or_else(|| self.fonts.get(®ULAR).unwrap())
|
||||
}
|
||||
|
||||
/// Get the regular font
|
||||
pub fn get_regular(&self) -> &Font {
|
||||
self.fonts.get(®ULAR).unwrap()
|
||||
}
|
||||
|
||||
/// get the height of the font
|
||||
/// Get the height of the font
|
||||
pub fn get_font_height(&self) -> u32 {
|
||||
let font = self.get_regular();
|
||||
let metrics = font.metrics();
|
||||
|
@ -140,6 +158,9 @@ impl ImageFont {
|
|||
}
|
||||
}
|
||||
|
||||
/// A collection of font
|
||||
///
|
||||
/// It can be used to draw text on the image.
|
||||
#[derive(Debug)]
|
||||
pub struct FontCollection(Vec<ImageFont>);
|
||||
|
||||
|
@ -150,7 +171,8 @@ impl Default for FontCollection {
|
|||
}
|
||||
|
||||
impl FontCollection {
|
||||
pub fn new<S: AsRef<str>>(font_list: &[(S, f32)]) -> Result<Self, Error> {
|
||||
/// Create a FontCollection with several fonts.
|
||||
pub fn new<S: AsRef<str>>(font_list: &[(S, f32)]) -> Result<Self, FontError> {
|
||||
let mut fonts = vec![];
|
||||
for (name, size) in font_list {
|
||||
let name = name.as_ref();
|
||||
|
@ -218,12 +240,14 @@ impl FontCollection {
|
|||
(glyphs, delta_x)
|
||||
}
|
||||
|
||||
/// Get the width of the given glyph
|
||||
fn get_glyph_width(font: &Font, id: u32, size: f32) -> u32 {
|
||||
let metrics = font.metrics();
|
||||
let advance = font.advance(id).unwrap();
|
||||
(advance / metrics.units_per_em as f32 * size).x.ceil() as u32
|
||||
}
|
||||
|
||||
/// Get the width of the given text
|
||||
pub fn get_text_len(&self, text: &str) -> u32 {
|
||||
self.layout(text, REGULAR).1
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Format the output of syntect into an image
|
||||
use crate::error::FontError;
|
||||
use crate::font::{FontCollection, FontStyle};
|
||||
use crate::utils::{copy_alpha, ToRgba};
|
||||
use failure::Error;
|
||||
use crate::utils::*;
|
||||
use image::{DynamicImage, GenericImageView, Rgba, RgbaImage};
|
||||
use syntect::highlighting::{Color, Style, Theme};
|
||||
|
||||
|
@ -8,7 +9,7 @@ pub struct ImageFormatter {
|
|||
/// pad between lines
|
||||
/// Default: 2
|
||||
line_pad: u32,
|
||||
/// pad between code and edge of code area. [top, bottom, left, right]
|
||||
/// pad between code and edge of code area.
|
||||
/// Default: 25
|
||||
code_pad: u32,
|
||||
/// pad of top of the code area
|
||||
|
@ -17,6 +18,9 @@ pub struct ImageFormatter {
|
|||
/// show line number
|
||||
/// Default: true
|
||||
line_number: bool,
|
||||
/// round corner
|
||||
/// Default: true
|
||||
round_corner: bool,
|
||||
/// pad between code and line number
|
||||
/// Default: 6
|
||||
line_number_pad: u32,
|
||||
|
@ -28,71 +32,101 @@ pub struct ImageFormatter {
|
|||
font: FontCollection,
|
||||
/// Highlight lines
|
||||
highlight_lines: Vec<u32>,
|
||||
/// Shadow adder
|
||||
shadow_adder: Option<ShadowAdder>,
|
||||
}
|
||||
|
||||
pub struct ImageFormatterBuilder<'a, S: AsRef<str>> {
|
||||
/// pad between lines
|
||||
#[derive(Default)]
|
||||
pub struct ImageFormatterBuilder<S> {
|
||||
/// Pad between lines
|
||||
line_pad: u32,
|
||||
/// show line number
|
||||
/// Show line number
|
||||
line_number: bool,
|
||||
/// pad of top of the code area
|
||||
code_pad_top: u32,
|
||||
/// font of english character, should be mono space font
|
||||
font: &'a [(S, f32)],
|
||||
/// Font of english character, should be mono space font
|
||||
font: Vec<(S, f32)>,
|
||||
/// Highlight lines
|
||||
highlight_lines: Vec<u32>,
|
||||
/// Whether show the window controls
|
||||
window_controls: bool,
|
||||
/// Whether round the corner of the image
|
||||
round_corner: bool,
|
||||
/// Shadow adder,
|
||||
shadow_adder: Option<ShadowAdder>,
|
||||
}
|
||||
|
||||
impl<'a, S: AsRef<str>> ImageFormatterBuilder<'a, S> {
|
||||
// FXIME: cannot use `ImageFormatterBuilder::new().build()` bacuse cannot infer type for `S`
|
||||
impl<S: AsRef<str> + Default> ImageFormatterBuilder<S> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
line_pad: 2,
|
||||
line_number: true,
|
||||
code_pad_top: 50,
|
||||
font: &[],
|
||||
highlight_lines: vec![],
|
||||
window_controls: true,
|
||||
round_corner: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether show the line number
|
||||
pub fn line_number(mut self, show: bool) -> Self {
|
||||
self.line_number = show;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the pad between lines
|
||||
pub fn line_pad(mut self, pad: u32) -> Self {
|
||||
self.line_pad = pad;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn code_pad_top(mut self, pad: u32) -> Self {
|
||||
self.code_pad_top = pad;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn font(mut self, fonts: &'a [(S, f32)]) -> Self {
|
||||
/// Set the font
|
||||
pub fn font(mut self, fonts: Vec<(S, f32)>) -> Self {
|
||||
self.font = fonts;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether show the windows controls
|
||||
pub fn window_controls(mut self, show: bool) -> Self {
|
||||
self.window_controls = show;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether round the corner
|
||||
pub fn round_corner(mut self, b: bool) -> Self {
|
||||
self.round_corner = b;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add the shadow
|
||||
pub fn shadow_adder(mut self, adder: ShadowAdder) -> Self {
|
||||
self.shadow_adder = Some(adder);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the lines to highlight.
|
||||
pub fn highlight_lines(mut self, lines: Vec<u32>) -> Self {
|
||||
self.highlight_lines = lines;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<ImageFormatter, Error> {
|
||||
pub fn build(self) -> Result<ImageFormatter, FontError> {
|
||||
let font = if self.font.is_empty() {
|
||||
FontCollection::default()
|
||||
} else {
|
||||
FontCollection::new(self.font)?
|
||||
FontCollection::new(&self.font)?
|
||||
};
|
||||
|
||||
let code_pad_top = if self.window_controls { 50 } else { 0 };
|
||||
|
||||
Ok(ImageFormatter {
|
||||
line_pad: self.line_pad,
|
||||
code_pad: 25,
|
||||
code_pad_top: self.code_pad_top,
|
||||
line_number: self.line_number,
|
||||
line_number_pad: 6,
|
||||
line_number_chars: 0,
|
||||
highlight_lines: self.highlight_lines,
|
||||
round_corner: self.round_corner,
|
||||
shadow_adder: self.shadow_adder,
|
||||
code_pad_top,
|
||||
font,
|
||||
})
|
||||
}
|
||||
|
@ -242,6 +276,19 @@ impl ImageFormatter {
|
|||
.draw_text_mut(&mut image, color, x, y, style, &text);
|
||||
}
|
||||
|
||||
image
|
||||
// draw_window_controls == true
|
||||
if self.code_pad_top != 0 {
|
||||
add_window_controls(&mut image);
|
||||
}
|
||||
|
||||
if self.round_corner {
|
||||
round_corner(&mut image, 12);
|
||||
}
|
||||
|
||||
if let Some(adder) = &self.shadow_adder {
|
||||
adder.apply_to(&image)
|
||||
} else {
|
||||
image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
//! `silicon` is a tool to create beautiful image of your source code.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use syntect::easy::HighlightLines;
|
||||
//! use syntect::util::LinesWithEndings;
|
||||
//! use silicon::utils::{init_syntect, ShadowAdder};
|
||||
//! use silicon::formatter::ImageFormatterBuilder;
|
||||
//!
|
||||
//! let (ps, ts) = init_syntect();
|
||||
//! let code = r#"
|
||||
//! fn main() {
|
||||
//! println!("Hello, world!");
|
||||
//! }
|
||||
//! "#;
|
||||
//!
|
||||
//! let syntax = ps.find_syntax_by_token("rs").unwrap();
|
||||
//! let theme = &ts.themes["Dracula"];
|
||||
//!
|
||||
//! let mut h = HighlightLines::new(syntax, theme);
|
||||
//! let highlight = LinesWithEndings::from(&code)
|
||||
//! .map(|line| h.highlight(line, &ps))
|
||||
//! .collect::<Vec<_>>();
|
||||
//!
|
||||
//! let mut formatter = ImageFormatterBuilder::new()
|
||||
//! .font(vec![("Hack", 26.0)])
|
||||
//! .shadow_adder(ShadowAdder::default())
|
||||
//! .build()
|
||||
//! .unwrap();
|
||||
//! let image = formatter.format(&highlight, theme);
|
||||
//!
|
||||
//! image.save("hello.png").unwrap();
|
||||
//! ```
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod blur;
|
||||
pub mod error;
|
||||
pub mod font;
|
||||
pub mod formatter;
|
||||
pub mod utils;
|
66
src/utils.rs
66
src/utils.rs
|
@ -1,14 +1,21 @@
|
|||
use failure::Error;
|
||||
use image::imageops::{crop, resize};
|
||||
use image::Pixel;
|
||||
use image::{DynamicImage, FilterType, GenericImage, GenericImageView, Rgba, RgbaImage};
|
||||
use imageproc::drawing::{draw_filled_rect_mut, draw_line_segment_mut};
|
||||
use imageproc::rect::Rect;
|
||||
use syntect::dumps;
|
||||
use syntect::highlighting::ThemeSet;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use {image::ImageOutputFormat, std::process::Command};
|
||||
/// Load the default SyntaxSet and ThemeSet.
|
||||
pub fn init_syntect() -> (SyntaxSet, ThemeSet) {
|
||||
(
|
||||
dumps::from_binary(include_bytes!("../assets/syntaxes.bin")),
|
||||
dumps::from_binary(include_bytes!("../assets/themes.bin")),
|
||||
)
|
||||
}
|
||||
|
||||
pub trait ToRgba {
|
||||
pub(crate) trait ToRgba {
|
||||
type Target;
|
||||
fn to_rgba(&self) -> Self::Target;
|
||||
}
|
||||
|
@ -33,7 +40,8 @@ impl ToRgba for syntect::highlighting::Color {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_window_controls(image: &mut DynamicImage) {
|
||||
/// Add the window controls for image
|
||||
pub(crate) fn add_window_controls(image: &mut DynamicImage) {
|
||||
let color = [
|
||||
("#FF5F56", "#E0443E"),
|
||||
("#FFBD2E", "#DEA123"),
|
||||
|
@ -66,6 +74,7 @@ pub fn add_window_controls(image: &mut DynamicImage) {
|
|||
copy_alpha(&title_bar, image.as_mut_rgba8().unwrap(), 15, 15);
|
||||
}
|
||||
|
||||
/// Add the shadow for image
|
||||
#[derive(Debug)]
|
||||
pub struct ShadowAdder {
|
||||
background: Rgba<u8>,
|
||||
|
@ -90,16 +99,19 @@ impl ShadowAdder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the background color
|
||||
pub fn background(mut self, color: Rgba<u8>) -> Self {
|
||||
self.background = color;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the shadow color
|
||||
pub fn shadow_color(mut self, color: Rgba<u8>) -> Self {
|
||||
self.shadow_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the shadow size
|
||||
pub fn blur_radius(mut self, sigma: f32) -> Self {
|
||||
self.blur_radius = sigma;
|
||||
self
|
||||
|
@ -159,8 +171,14 @@ impl ShadowAdder {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for ShadowAdder {
|
||||
fn default() -> Self {
|
||||
ShadowAdder::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// copy from src to dst, taking into account alpha channels
|
||||
pub fn copy_alpha(src: &RgbaImage, dst: &mut RgbaImage, x: u32, y: u32) {
|
||||
pub(crate) fn copy_alpha(src: &RgbaImage, dst: &mut RgbaImage, x: u32, y: u32) {
|
||||
assert!(src.width() + x <= dst.width());
|
||||
assert!(src.height() + y <= dst.height());
|
||||
for j in 0..src.height() {
|
||||
|
@ -179,8 +197,8 @@ pub fn copy_alpha(src: &RgbaImage, dst: &mut RgbaImage, x: u32, y: u32) {
|
|||
}
|
||||
}
|
||||
|
||||
/// round the corner of an image
|
||||
pub fn round_corner(image: &mut DynamicImage, radius: u32) {
|
||||
/// Round the corner of the image
|
||||
pub(crate) fn round_corner(image: &mut DynamicImage, radius: u32) {
|
||||
// draw a circle with given foreground on given background
|
||||
// then split it into four pieces and paste them to the four corner of the image
|
||||
let mut circle =
|
||||
|
@ -217,8 +235,12 @@ pub fn round_corner(image: &mut DynamicImage, radius: u32) {
|
|||
// issue: https://github.com/image-rs/imageproc/issues/328
|
||||
// PR: https://github.com/image-rs/imageproc/pull/330
|
||||
/// Draw as much of a circle, including its contents, as lies inside the image bounds.
|
||||
pub fn draw_filled_circle_mut<I>(image: &mut I, center: (i32, i32), radius: i32, color: I::Pixel)
|
||||
where
|
||||
pub(crate) fn draw_filled_circle_mut<I>(
|
||||
image: &mut I,
|
||||
center: (i32, i32),
|
||||
radius: i32,
|
||||
color: I::Pixel,
|
||||
) where
|
||||
I: GenericImage,
|
||||
I::Pixel: 'static,
|
||||
{
|
||||
|
@ -263,27 +285,3 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn dump_image_to_clipboard(image: &DynamicImage) -> Result<(), Error> {
|
||||
let mut temp = tempfile::NamedTempFile::new()?;
|
||||
image.write_to(&mut temp, ImageOutputFormat::PNG)?;
|
||||
Command::new("xclip")
|
||||
.args(&[
|
||||
"-sel",
|
||||
"clip",
|
||||
"-t",
|
||||
"image/png",
|
||||
temp.path().to_str().unwrap(),
|
||||
])
|
||||
.status()
|
||||
.map_err(|e| format_err!("Failed to copy image to clipboard: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn dump_image_to_clipboard(_image: &DynamicImage) -> Result<(), Error> {
|
||||
Err(format_err!(
|
||||
"This feature hasn't been implemented for your system"
|
||||
))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue