Skip to content

Commit e541731

Browse files
committed
[cg] Address code review
A bunch of small fixes. Larger things include: - reusing unpremultiply_rgba function - allowing explicit CGGradientDrawingOptions - better signatures in FFI
1 parent 5dfd402 commit e541731

File tree

6 files changed

+71
-100
lines changed

6 files changed

+71
-100
lines changed

piet-common/src/cg_back.rs

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl Device {
8787
core_graphics::base::kCGImageAlphaPremultipliedLast,
8888
);
8989
ctx.scale(pix_scale, pix_scale);
90-
let height = height as f64 * pix_scale;
90+
let height = height as f64 * pix_scale.recip();
9191
Ok(BitmapTarget {
9292
ctx,
9393
height,
@@ -112,27 +112,8 @@ impl<'a> BitmapTarget<'a> {
112112
return Err(Error::NotSupported);
113113
}
114114

115-
let width = self.ctx.width() as usize;
116-
let height = self.ctx.height() as usize;
117-
let stride = self.ctx.bytes_per_row() as usize;
118-
119115
let data = self.ctx.data();
120-
if stride != width {
121-
let mut raw_data = vec![0; width * height * 4];
122-
for y in 0..height {
123-
let src_off = y * stride;
124-
let dst_off = y * width * 4;
125-
for x in 0..width {
126-
raw_data[dst_off + x * 4 + 0] = data[src_off + x * 4 + 2];
127-
raw_data[dst_off + x * 4 + 1] = data[src_off + x * 4 + 1];
128-
raw_data[dst_off + x * 4 + 2] = data[src_off + x * 4 + 0];
129-
raw_data[dst_off + x * 4 + 3] = data[src_off + x * 4 + 3];
130-
}
131-
}
132-
Ok(raw_data)
133-
} else {
134-
Ok(data.to_owned())
135-
}
116+
Ok(data.to_owned())
136117
}
137118

138119
/// Save bitmap to RGBA PNG file
@@ -141,7 +122,7 @@ impl<'a> BitmapTarget<'a> {
141122
let width = self.ctx.width() as usize;
142123
let height = self.ctx.height() as usize;
143124
let mut data = self.into_raw_pixels(ImageFormat::RgbaPremul)?;
144-
unpremultiply(&mut data);
125+
piet_coregraphics::unpremultiply(&mut data);
145126
let file = BufWriter::new(File::create(path).map_err(|e| Into::<Box<_>>::into(e))?);
146127
let mut encoder = Encoder::new(file, width as u32, height as u32);
147128
encoder.set_color(ColorType::RGBA);
@@ -160,15 +141,3 @@ impl<'a> BitmapTarget<'a> {
160141
Err(Error::MissingFeature)
161142
}
162143
}
163-
#[cfg(feature = "png")]
164-
fn unpremultiply(data: &mut [u8]) {
165-
for i in (0..data.len()).step_by(4) {
166-
let a = data[i + 3];
167-
if a != 0 {
168-
let scale = 255.0 / (a as f64);
169-
data[i] = (scale * (data[i] as f64)).round() as u8;
170-
data[i + 1] = (scale * (data[i + 1] as f64)).round() as u8;
171-
data[i + 2] = (scale * (data[i + 2] as f64)).round() as u8;
172-
}
173-
}
174-
}

piet-coregraphics/examples/basic-cg.rs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fn main() {
6868
piet.finish().unwrap();
6969
std::mem::drop(piet);
7070

71-
unpremultiply(cg_ctx.data());
71+
piet_coregraphics::unpremultiply_rgba(cg_ctx.data());
7272

7373
// Write image as PNG file.
7474
let path = Path::new("image.png");
@@ -82,15 +82,3 @@ fn main() {
8282

8383
writer.write_image_data(cg_ctx.data()).unwrap();
8484
}
85-
86-
fn unpremultiply(data: &mut [u8]) {
87-
for i in (0..data.len()).step_by(4) {
88-
let a = data[i + 3];
89-
if a != 0 {
90-
let scale = 255.0 / (a as f64);
91-
data[i] = (scale * (data[i] as f64)).round() as u8;
92-
data[i + 1] = (scale * (data[i + 1] as f64)).round() as u8;
93-
data[i + 2] = (scale * (data[i + 2] as f64)).round() as u8;
94-
}
95-
}
96-
}

piet-coregraphics/examples/test-picture.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ fn main() {
4242
encoder.set_depth(png::BitDepth::Eight);
4343
let mut writer = encoder.write_header().unwrap();
4444

45+
piet_coregraphics::unpremultiply_rgba(cg_ctx.data());
4546
writer.write_image_data(cg_ctx.data()).unwrap();
4647
}

piet-coregraphics/src/gradient.rs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(non_upper_case_globals)]
2+
13
//! core graphics gradient support
24
35
use core_foundation::{
@@ -16,14 +18,15 @@ use core_graphics::{
1618
use piet::kurbo::Point;
1719
use piet::{Color, FixedGradient, FixedLinearGradient, FixedRadialGradient, GradientStop};
1820

21+
//FIXME: remove all this when core-graphics 0.20.0 is released
1922
// core-graphics does not provide a CGGradient type
2023
pub enum CGGradientT {}
2124
pub type CGGradientRef = *mut CGGradientT;
25+
pub type CGGradientDrawingOptions = u32;
26+
pub const CGGradientDrawsBeforeStartLocation: CGGradientDrawingOptions = 1 << 0;
27+
pub const CGGradientDrawsAfterEndLocation: CGGradientDrawingOptions = 1 << 1;
2228

23-
declare_TCFType! {
24-
CGGradient, CGGradientRef
25-
}
26-
29+
declare_TCFType!(CGGradient, CGGradientRef);
2730
impl_TCFType!(CGGradient, CGGradientRef, CGGradientGetTypeID);
2831

2932
/// A wrapper around CGGradient
@@ -53,8 +56,7 @@ impl Gradient {
5356
.unwrap_or(Color::BLACK)
5457
}
5558

56-
pub(crate) fn fill(&self, ctx: &mut CGContextRef) {
57-
let context_ref: *mut u8 = ctx as *mut CGContextRef as *mut u8;
59+
pub(crate) fn fill(&self, ctx: &mut CGContextRef, options: CGGradientDrawingOptions) {
5860
match self.piet_grad {
5961
FixedGradient::Radial(FixedRadialGradient {
6062
center,
@@ -63,16 +65,16 @@ impl Gradient {
6365
..
6466
}) => {
6567
let start_center = to_cgpoint(center + origin_offset);
66-
let center = to_cgpoint(center);
68+
let end_center = to_cgpoint(center);
6769
unsafe {
6870
CGContextDrawRadialGradient(
69-
context_ref,
71+
ctx,
7072
self.cg_grad.as_concrete_TypeRef(),
7173
start_center,
72-
0.0,
73-
center,
74+
0.0, // start_radius
75+
end_center,
7476
radius as CGFloat,
75-
0,
77+
options,
7678
)
7779
}
7880
}
@@ -81,7 +83,7 @@ impl Gradient {
8183
let end = to_cgpoint(end);
8284
unsafe {
8385
CGContextDrawLinearGradient(
84-
context_ref,
86+
ctx,
8587
self.cg_grad.as_concrete_TypeRef(),
8688
start,
8789
end,
@@ -97,23 +99,19 @@ fn new_cg_gradient(stops: &[GradientStop]) -> CGGradient {
9799
unsafe {
98100
//FIXME: is this expensive enough we should be reusing it?
99101
let space = CGColorSpace::create_with_name(kCGColorSpaceSRGB).unwrap();
100-
let space_ref: *const u8 = &*space as *const CGColorSpaceRef as *const u8;
101102
let mut colors = Vec::<CGColor>::new();
102103
let mut locations = Vec::<CGFloat>::new();
103104
for GradientStop { pos, color } in stops {
104105
let (r, g, b, a) = Color::as_rgba(&color);
105-
let color = CGColorCreate(space_ref as *const u8, [r, g, b, a].as_ptr());
106+
let color = CGColorCreate(&*space, [r, g, b, a].as_ptr());
106107
let color = CGColor::wrap_under_create_rule(color);
107108
colors.push(color);
108109
locations.push(*pos as CGFloat);
109110
}
110111

111112
let colors = CFArray::from_CFTypes(&colors);
112-
let gradient = CGGradientCreateWithColors(
113-
space_ref as *const u8,
114-
colors.as_concrete_TypeRef(),
115-
locations.as_ptr(),
116-
);
113+
let gradient =
114+
CGGradientCreateWithColors(&*space, colors.as_concrete_TypeRef(), locations.as_ptr());
117115

118116
CGGradient::wrap_under_create_rule(gradient)
119117
}
@@ -126,21 +124,26 @@ fn to_cgpoint(point: Point) -> CGPoint {
126124
#[link(name = "CoreGraphics", kind = "framework")]
127125
extern "C" {
128126
fn CGGradientGetTypeID() -> CFTypeID;
127+
//CGColorSpaceRef is missing repr(c).
128+
#[allow(improper_ctypes)]
129129
fn CGGradientCreateWithColors(
130-
space: *const u8,
130+
space: *const CGColorSpaceRef,
131131
colors: CFArrayRef,
132132
locations: *const CGFloat,
133133
) -> CGGradientRef;
134-
fn CGColorCreate(space: *const u8, components: *const CGFloat) -> SysCGColorRef;
134+
#[allow(improper_ctypes)]
135+
fn CGColorCreate(space: *const CGColorSpaceRef, components: *const CGFloat) -> SysCGColorRef;
136+
#[allow(improper_ctypes)]
135137
fn CGContextDrawLinearGradient(
136-
ctx: *mut u8,
138+
ctx: *mut CGContextRef,
137139
gradient: CGGradientRef,
138140
startPoint: CGPoint,
139141
endPoint: CGPoint,
140142
options: u32,
141143
);
144+
#[allow(improper_ctypes)]
142145
fn CGContextDrawRadialGradient(
143-
ctx: *mut u8,
146+
ctx: *mut CGContextRef,
144147
gradient: CGGradientRef,
145148
startCenter: CGPoint,
146149
startRadius: CGFloat,

piet-coregraphics/src/lib.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ pub use crate::text::{
2929
CoreGraphicsTextLayoutBuilder,
3030
};
3131

32-
use gradient::Gradient;
32+
use gradient::{
33+
CGGradientDrawingOptions, CGGradientDrawsAfterEndLocation, CGGradientDrawsBeforeStartLocation,
34+
Gradient,
35+
};
36+
37+
const GRADIENT_DRAW_BEFORE_AND_AFTER: CGGradientDrawingOptions =
38+
CGGradientDrawsAfterEndLocation | CGGradientDrawsBeforeStartLocation;
3339

3440
pub struct CoreGraphicsContext<'a> {
3541
// Cairo has this as Clone and with &self methods, but we do this to avoid
3642
// concurrency problems.
3743
ctx: &'a mut CGContextRef,
38-
// the height of the context; we need this in order to correctly flip the coordinate space
3944
text: CoreGraphicsText<'a>,
4045
// because of the relationship between cocoa and coregraphics (where cocoa
4146
// may be asked to flip the y-axis) we cannot trust the transform returned
@@ -96,13 +101,8 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
96101
//type StrokeStyle = StrokeStyle;
97102

98103
fn clear(&mut self, color: Color) {
99-
let rgba = color.as_rgba_u32();
100-
self.ctx.set_rgb_fill_color(
101-
byte_to_frac(rgba >> 24),
102-
byte_to_frac(rgba >> 16),
103-
byte_to_frac(rgba >> 8),
104-
byte_to_frac(rgba),
105-
);
104+
let (r, g, b, a) = color.as_rgba();
105+
self.ctx.set_rgb_fill_color(r, g, b, a);
106106
self.ctx.fill_rect(self.ctx.clip_bounding_box());
107107
}
108108

@@ -127,7 +127,7 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
127127
Brush::Gradient(grad) => {
128128
self.ctx.save();
129129
self.ctx.clip();
130-
grad.fill(self.ctx);
130+
grad.fill(self.ctx, GRADIENT_DRAW_BEFORE_AND_AFTER);
131131
self.ctx.restore();
132132
}
133133
}
@@ -144,7 +144,7 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
144144
Brush::Gradient(grad) => {
145145
self.ctx.save();
146146
self.ctx.eo_clip();
147-
grad.fill(self.ctx);
147+
grad.fill(self.ctx, GRADIENT_DRAW_BEFORE_AND_AFTER);
148148
self.ctx.restore();
149149
}
150150
}
@@ -168,7 +168,7 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
168168
self.ctx.save();
169169
self.ctx.replace_path_with_stroked_path();
170170
self.ctx.clip();
171-
grad.fill(self.ctx);
171+
grad.fill(self.ctx, GRADIENT_DRAW_BEFORE_AND_AFTER);
172172
self.ctx.restore();
173173
}
174174
}
@@ -193,7 +193,7 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
193193
self.ctx.save();
194194
self.ctx.replace_path_with_stroked_path();
195195
self.ctx.clip();
196-
grad.fill(self.ctx);
196+
grad.fill(self.ctx, GRADIENT_DRAW_BEFORE_AND_AFTER);
197197
self.ctx.restore();
198198
}
199199
}
@@ -439,10 +439,6 @@ impl<'a> CoreGraphicsContext<'a> {
439439
}
440440
}
441441

442-
fn byte_to_frac(byte: u32) -> f64 {
443-
((byte & 255) as f64) * (1.0 / 255.0)
444-
}
445-
446442
fn to_cgpoint(point: Point) -> CGPoint {
447443
CGPoint::new(point.x as CGFloat, point.y as CGFloat)
448444
}
@@ -461,6 +457,19 @@ fn to_cgaffine(affine: Affine) -> CGAffineTransform {
461457
CGAffineTransform::new(a, b, c, d, tx, ty)
462458
}
463459

460+
#[allow(dead_code)]
461+
pub fn unpremultiply_rgba(data: &mut [u8]) {
462+
for i in (0..data.len()).step_by(4) {
463+
let a = data[i + 3];
464+
if a != 0 {
465+
let scale = 255.0 / (a as f64);
466+
data[i] = (scale * (data[i] as f64)).round() as u8;
467+
data[i + 1] = (scale * (data[i + 1] as f64)).round() as u8;
468+
data[i + 2] = (scale * (data[i + 2] as f64)).round() as u8;
469+
}
470+
}
471+
}
472+
464473
#[link(name = "CoreGraphics", kind = "framework")]
465474
extern "C" {
466475
fn CGContextClipToMask(ctx: *mut u8, rect: CGRect, mask: *const u8);

piet-coregraphics/src/text.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use piet::{
1717

1818
use crate::ct_helpers::{AttributedString, Frame, Framesetter, Line};
1919

20-
// inner is an nsfont.
2120
#[derive(Debug, Clone)]
2221
pub struct CoreGraphicsFont(CTFont);
2322

@@ -188,24 +187,13 @@ impl TextLayout for CoreGraphicsTextLayout {
188187
let offset_utf16 = line.get_string_index_for_position(point_in_string_space);
189188
let offset = match offset_utf16 {
190189
// this is 'kCFNotFound'.
191-
// if nothing is found just go end of string? should this be len - 1? do we have an
192-
// implicit newline at end of file? so many mysteries
193190
-1 => self.string.len(),
194191
n if n >= 0 => {
195192
let utf16_range = line.get_string_range();
196193
let utf8_range = self.line_range(line_num).unwrap();
197194
let line_txt = self.line_text(line_num).unwrap();
198195
let rel_offset = (n - utf16_range.location) as usize;
199-
let mut off16 = 0;
200-
let mut off8 = 0;
201-
for c in line_txt.chars() {
202-
if rel_offset == off16 {
203-
break;
204-
}
205-
off16 += c.len_utf16();
206-
off8 += c.len_utf8();
207-
}
208-
utf8_range.0 + off8
196+
utf8_range.0 + utf8_offset_for_utf16_offset(line_txt, rel_offset)
209197
}
210198
// some other value; should never happen
211199
_ => panic!("gross violation of api contract"),
@@ -323,6 +311,19 @@ impl CoreGraphicsTextLayout {
323311
}
324312
}
325313

314+
fn utf8_offset_for_utf16_offset(text: &str, utf16_offset: usize) -> usize {
315+
let mut off16 = 0;
316+
let mut off8 = 0;
317+
for c in text.chars() {
318+
if utf16_offset == off16 {
319+
break;
320+
}
321+
off16 += c.len_utf16();
322+
off8 += c.len_utf8();
323+
}
324+
off8
325+
}
326+
326327
#[cfg(test)]
327328
#[allow(clippy::float_cmp)]
328329
mod tests {

0 commit comments

Comments
 (0)