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

Core Graphics back-end #176

Merged
merged 30 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bb921be
WIP CoreGraphics backend
jrmuizel Jan 4, 2019
14c509e
Get building after rebase
jrmuizel Apr 11, 2019
948f9e4
Update Core Graphics back-end to piet 0.12
raphlinus Apr 22, 2020
f4b4c71
Test paths, start images
raphlinus Apr 28, 2020
7c78164
Clean up fmt and clippy warnings
raphlinus Apr 28, 2020
642e19f
Fix clippy warning in cg example
raphlinus Apr 28, 2020
b6b47b6
CI changes to add coregraphics
raphlinus Apr 28, 2020
eb3a3e4
CI name improvements
raphlinus Apr 28, 2020
fa5975c
[cg] Add text module, move text stubs there
cmyr Apr 30, 2020
39d955f
[cg] Implement transform and current_transform methods
cmyr May 1, 2020
3ed7d1d
[cg] Add initial textlayout impl
cmyr Apr 30, 2020
3b70be3
[cg] Move to using wrappers for CT types
cmyr May 1, 2020
f58814d
[cg] Implement TextLayout::update_width
cmyr May 1, 2020
1ab5b8e
[cg] Implmenent line_text method
cmyr May 2, 2020
ac76e5f
[cg] Implement line_metric method
cmyr May 2, 2020
48d5e2c
[cg] Implement hit_test_point
cmyr May 4, 2020
a011ea0
[cg] Implement hit_test_text_position
cmyr May 4, 2020
4b92721
[cg] Implement gradient support
cmyr May 5, 2020
95e573c
[cg] Implement blurred rects
cmyr May 5, 2020
581ac9a
[cg] Add examples/test-picture.rs
cmyr May 6, 2020
cdecfad
[cg] API tweaks for integration with druid
cmyr May 6, 2020
7040943
Add piet-coregraphics backend to piet-common
cmyr May 6, 2020
6df132e
Final tweaks to integrate piet-coregraphics
cmyr May 7, 2020
f6a91df
Clippy
cmyr May 7, 2020
e2a611d
[cg] Fix hit_test_point
cmyr May 8, 2020
b5b5f69
[cg] Manually track our current transform
cmyr May 8, 2020
340ff85
[cg] Address code review
cmyr May 9, 2020
041635a
[cg] Correct image orientation when drawing
cmyr May 11, 2020
3940a1f
[cg] Use piet::util for utf8/16 conversion
cmyr May 11, 2020
a36c476
[cg] Be defensive when restoring saved context state
cmyr May 11, 2020
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
43 changes: 32 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,43 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --exclude piet-cairo -- -D warnings
args: --workspace --all-targets --exclude piet-cairo --exclude piet-coregraphics -- -D warnings
if: contains(matrix.os, 'windows')

- name: cargo clippy (not windows)
- name: cargo clippy (mac)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --exclude piet-direct2d -- -D warnings
if: contains(matrix.os, 'windows') != true
if: contains(matrix.os, 'macOS')

- name: cargo clippy (linux)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --exclude piet-direct2d --exclude piet-coregraphics -- -D warnings
if: contains(matrix.os, 'ubuntu')

- name: cargo test (windows)
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --exclude piet-cairo
args: --workspace --exclude piet-cairo --exclude piet-coregraphics
if: contains(matrix.os, 'windows')

- name: cargo test (not windows)
- name: cargo test (mac)
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --exclude piet-direct2d
if: contains(matrix.os, 'windows') != true
if: contains(matrix.os, 'macOS')

- name: cargo test (linux)
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --exclude piet-direct2d --exclude piet-coregraphics
if: contains(matrix.os, 'ubuntu')

test-stable-wasm:
runs-on: ${{ matrix.os }}
Expand All @@ -105,13 +119,13 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --exclude piet-cairo --exclude piet-direct2d --target wasm32-unknown-unknown -- -D warnings
args: --workspace --all-targets --exclude piet-cairo --exclude piet-direct2d --exclude piet-coregraphics --target wasm32-unknown-unknown -- -D warnings

- name: cargo test compile
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --exclude piet-cairo --exclude piet-direct2d --no-run --target wasm32-unknown-unknown
args: --workspace --exclude piet-cairo --exclude piet-direct2d --exclude piet-coregraphics --no-run --target wasm32-unknown-unknown

test-nightly:
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -143,15 +157,22 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --exclude piet-cairo
args: --workspace --exclude piet-cairo --exclude piet-coregraphics
if: contains(matrix.os, 'windows')

- name: cargo test (not windows)
- name: cargo test (mac)
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --exclude piet-direct2d
if: contains(matrix.os, 'windows') != true
if: contains(matrix.os, 'macOS')

- name: cargo test (linux)
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --exclude piet-direct2d --exclude piet-coregraphics
if: contains(matrix.os, 'ubuntu')

check-docs:
name: Docs
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"piet",
"piet-cairo",
"piet-common",
"piet-coregraphics",
"piet-direct2d",
"piet-web",
"piet-web/examples/basic",
Expand Down
8 changes: 7 additions & 1 deletion piet-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@ web = ["piet-web"]
cfg-if = "0.1.10"
piet = { version = "0.0.12", path = "../piet" }
piet-cairo = { version = "0.0.12", path = "../piet-cairo", optional = true }
piet-coregraphics = { version = "0.0.12", path = "../piet-coregraphics", optional = true }
piet-direct2d = { version = "0.0.12", path = "../piet-direct2d", optional = true }
piet-web = { version = "0.0.12", path = "../piet-web", optional = true }
cairo-rs = { version = "0.8.1", default_features = false, optional = true}

[target.'cfg(not(any(target_arch="wasm32", target_os="windows")))'.dependencies]
[target.'cfg(not(any(target_arch="wasm32", target_os="windows", target_os="macos")))'.dependencies]
piet-cairo = { version = "0.0.12", path = "../piet-cairo" }
cairo-rs = { version = "0.8.1", default_features = false}
png = { version = "0.16.1", optional = true }

[target.'cfg(target_os="macos")'.dependencies]
piet-coregraphics = { version = "0.0.12", path = "../piet-coregraphics" }
core-graphics = { version = "0.19" }
png = { version = "0.16.1", optional = true }

[target.'cfg(target_os="windows")'.dependencies]
piet-direct2d = { version = "0.0.12", path = "../piet-direct2d" }
direct2d = "0.2.0"
Expand Down
143 changes: 143 additions & 0 deletions piet-common/src/cg_back.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// allows e.g. raw_data[dst_off + x * 4 + 2] = buf[src_off + x * 4 + 0];
#![allow(clippy::identity_op)]

//! Support for piet CoreGraphics back-end.

use std::marker::PhantomData;
use std::path::Path;
#[cfg(feature = "png")]
use std::{fs::File, io::BufWriter};

use core_graphics::{color_space::CGColorSpace, context::CGContext, image::CGImage};
#[cfg(feature = "png")]
use png::{ColorType, Encoder};

use piet::{Error, ImageFormat};
#[doc(hidden)]
pub use piet_coregraphics::*;

/// The `RenderContext` for the CoreGraphics backend, which is selected.
pub type Piet<'a> = CoreGraphicsContext<'a>;

/// The associated brush type for this backend.
///
/// This type matches `RenderContext::Brush`
pub type Brush = piet_coregraphics::Brush;

/// The associated text factory for this backend.
///
/// This type matches `RenderContext::Text`
pub type PietText<'a> = CoreGraphicsText<'a>;

/// The associated font type for this backend.
///
/// This type matches `RenderContext::Text::Font`
pub type PietFont = CoreGraphicsFont;

/// The associated font builder for this backend.
///
/// This type matches `RenderContext::Text::FontBuilder`
pub type PietFontBuilder<'a> = CoreGraphicsFontBuilder;

/// The associated text layout type for this backend.
///
/// This type matches `RenderContext::Text::TextLayout`
pub type PietTextLayout = CoreGraphicsTextLayout;

/// The associated text layout builder for this backend.
///
/// This type matches `RenderContext::Text::TextLayoutBuilder`
pub type PietTextLayoutBuilder<'a> = CoreGraphicsTextLayout;

/// The associated image type for this backend.
///
/// This type matches `RenderContext::Image`
pub type Image = CGImage;

/// A struct that can be used to create bitmap render contexts.
pub struct Device;

/// A struct provides a `RenderContext` and then can have its bitmap extracted.
pub struct BitmapTarget<'a> {
ctx: CGContext,
height: f64,
phantom: PhantomData<&'a ()>,
}

impl Device {
/// Create a new device.
pub fn new() -> Result<Device, piet::Error> {
Ok(Device)
}

/// Create a new bitmap target.
pub fn bitmap_target(
&mut self,
width: usize,
height: usize,
pix_scale: f64,
) -> Result<BitmapTarget, piet::Error> {
let ctx = CGContext::create_bitmap_context(
None,
width,
height,
8,
0,
&CGColorSpace::create_device_rgb(),
core_graphics::base::kCGImageAlphaPremultipliedLast,
);
ctx.scale(pix_scale, pix_scale);
let height = height as f64 * pix_scale.recip();
Ok(BitmapTarget {
ctx,
height,
phantom: PhantomData,
})
}
}

impl<'a> BitmapTarget<'a> {
/// Get a piet `RenderContext` for the bitmap.
///
/// Note: caller is responsible for calling `finish` on the render
/// context at the end of rendering.
pub fn render_context(&mut self) -> CoreGraphicsContext {
CoreGraphicsContext::new_y_up(&mut self.ctx, self.height)
}

/// Get raw RGBA pixels from the bitmap.
pub fn into_raw_pixels(mut self, fmt: ImageFormat) -> Result<Vec<u8>, piet::Error> {
// TODO: convert other formats.
if fmt != ImageFormat::RgbaPremul {
return Err(Error::NotSupported);
}

let data = self.ctx.data();
Ok(data.to_owned())
}

/// Save bitmap to RGBA PNG file
#[cfg(feature = "png")]
pub fn save_to_file<P: AsRef<Path>>(self, path: P) -> Result<(), piet::Error> {
let width = self.ctx.width() as usize;
let height = self.ctx.height() as usize;
let mut data = self.into_raw_pixels(ImageFormat::RgbaPremul)?;
piet_coregraphics::unpremultiply(&mut data);
let file = BufWriter::new(File::create(path).map_err(|e| Into::<Box<_>>::into(e))?);
let mut encoder = Encoder::new(file, width as u32, height as u32);
encoder.set_color(ColorType::RGBA);
encoder.set_depth(png::BitDepth::Eight);
encoder
.write_header()
.map_err(|e| Into::<Box<_>>::into(e))?
.write_image_data(&data)
.map_err(|e| Into::<Box<_>>::into(e))?;
Ok(())
}

/// Stub for feature is missing
#[cfg(not(feature = "png"))]
pub fn save_to_file<P: AsRef<Path>>(self, _path: P) -> Result<(), piet::Error> {
Err(Error::MissingFeature)
}
}
5 changes: 4 additions & 1 deletion piet-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ pub use piet::kurbo;

cfg_if::cfg_if! {
// if we have explicitly asked for cairo *or* we are not wasm, web, or windows:
if #[cfg(any(feature = "cairo", not(any(target_arch = "wasm32", feature="web", target_os = "windows"))))] {
if #[cfg(any(feature = "cairo", not(any(target_arch = "wasm32", feature="web", target_os="macos", target_os = "windows"))))] {
#[path = "cairo_back.rs"]
mod backend;
} else if #[cfg(target_os = "macos")] {
#[path = "cg_back.rs"]
mod backend;
} else if #[cfg(target_os = "windows")] {
#[path = "direct2d_back.rs"]
mod backend;
Expand Down
20 changes: 20 additions & 0 deletions piet-coregraphics/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "piet-coregraphics"
version = "0.0.12"
authors = ["Jeff Muizelaar <[email protected]>, Raph Levien <[email protected]>, Colin Rofls <[email protected]>"]
description = "CoreGraphics backend for piet 2D graphics abstraction."
license = "MIT/Apache-2.0"
edition = "2018"
keywords = ["graphics", "2d"]
categories = ["rendering::graphics-api"]

[dependencies]
piet = { version = "0.0.12", path = "../piet" }
core-graphics = "0.19"
core-text = "15.0"
core-foundation = "0.7"
core-foundation-sys = "0.7"

[dev-dependencies]
png = "0.16.2"
piet = { version = "0.0.12", path = "../piet", features = ["samples"] }
84 changes: 84 additions & 0 deletions piet-coregraphics/examples/basic-cg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::fs::File;
use std::io::BufWriter;
use std::path::Path;

use core_graphics::color_space::CGColorSpace;
use core_graphics::context::CGContext;

use piet::kurbo::{Circle, Rect, Size};
use piet::{
Color, FontBuilder, LinearGradient, RadialGradient, RenderContext, Text, TextLayout,
TextLayoutBuilder, UnitPoint,
};

const WIDTH: usize = 800;
const HEIGHT: usize = 600;

fn main() {
let mut cg_ctx = CGContext::create_bitmap_context(
None,
WIDTH,
HEIGHT,
8,
0,
&CGColorSpace::create_device_rgb(),
core_graphics::base::kCGImageAlphaPremultipliedLast,
);
let mut piet = piet_coregraphics::CoreGraphicsContext::new_y_up(&mut cg_ctx, HEIGHT as f64);
let bounds = Size::new(WIDTH as f64, HEIGHT as f64).to_rect();

let linear = LinearGradient::new(
UnitPoint::TOP_LEFT,
UnitPoint::BOTTOM_RIGHT,
(
Color::rgba(1.0, 0.2, 0.5, 0.4),
Color::rgba(0.9, 0.0, 0.9, 0.8),
),
);
let radial = RadialGradient::new(0.8, (Color::WHITE, Color::BLACK))
.with_origin(UnitPoint::new(0.2, 0.7));

piet.fill(bounds.inset((0., 0., -bounds.width() * 0.5, 0.)), &radial);
piet.fill(Circle::new((100.0, 100.0), 50.0), &linear);
piet.stroke(bounds, &linear, 20.0);

let font = piet
.text()
.new_font_by_name("Georgia", 24.0)
.build()
.unwrap();

let mut layout = piet
.text()
.new_text_layout(&font, "this is my cool\nmultiline string, I like it very much, do you also like it? why or why not? Show your work.", None)
.build()
.unwrap();

piet.blurred_rect(Rect::new(100.0, 100., 150., 150.), 5.0, &Color::BLACK);
piet.fill(
Rect::new(95.0, 105., 145., 155.),
&Color::rgb(0.0, 0.4, 0.2),
);
piet.draw_text(&layout, (0., 00.0), &Color::WHITE);
layout.update_width(400.).unwrap();
piet.draw_text(&layout, (200.0, 200.0), &Color::BLACK);
layout.update_width(200.).unwrap();
piet.draw_text(&layout, (400.0, 400.0), &Color::rgba8(255, 0, 0, 150));

piet.finish().unwrap();
std::mem::drop(piet);

piet_coregraphics::unpremultiply_rgba(cg_ctx.data());

// Write image as PNG file.
let path = Path::new("image.png");
let file = File::create(path).unwrap();
let w = BufWriter::new(file);

let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32);
encoder.set_color(png::ColorType::RGBA);
encoder.set_depth(png::BitDepth::Eight);
let mut writer = encoder.write_header().unwrap();

writer.write_image_data(cg_ctx.data()).unwrap();
}
Loading