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

Implement canvas gradient #4891

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Implement gradient fill styles for canvas.

  • Loading branch information
mmatyas committed Mar 19, 2015
commit d3199aef74b548b9afe8281db3a6fe67b7a03874
@@ -5,6 +5,7 @@
use azure::azure::AzFloat;
use azure::azure_hl::{DrawTarget, SurfaceFormat, BackendType, StrokeOptions, DrawOptions, Pattern};
use azure::azure_hl::{ColorPattern, PathBuilder, JoinStyle, CapStyle, DrawSurfaceOptions, Filter};
use azure::azure_hl::{GradientStop, LinearGradientPattern, RadialGradientPattern, ExtendMode};
use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
use geom::rect::Rect;
@@ -167,11 +168,11 @@ impl<'a> CanvasPaintTask<'a> {
}

fn set_fill_style(&mut self, style: FillOrStrokeStyle) {
self.fill_style = style.to_azure_pattern()
self.fill_style = style.to_azure_pattern(&self.drawtarget)
}

fn set_stroke_style(&mut self, style: FillOrStrokeStyle) {
self.stroke_style = style.to_azure_pattern()
self.stroke_style = style.to_azure_pattern(&self.drawtarget)
}

fn set_transform(&mut self, transform: &Matrix2D<f32>) {
@@ -291,21 +292,105 @@ impl<'a> CanvasPaintTask<'a> {
}
}

#[derive(Clone)]
pub struct CanvasGradientStop {
pub offset: f64,
pub color: RGBA,
}

#[derive(Clone)]
pub struct LinearGradientStyle {
pub x0: f64,
pub y0: f64,
pub x1: f64,
pub y1: f64,
pub stops: Vec<CanvasGradientStop>
}

impl LinearGradientStyle {
pub fn new(x0: f64, y0: f64, x1: f64, y1: f64, stops: Vec<CanvasGradientStop>)
-> LinearGradientStyle {
LinearGradientStyle {
x0: x0,
y0: y0,
x1: x1,
y1: y1,
stops: stops,
}
}
}

#[derive(Clone)]
pub struct RadialGradientStyle {
pub x0: f64,
pub y0: f64,
pub r0: f64,
pub x1: f64,
pub y1: f64,
pub r1: f64,
pub stops: Vec<CanvasGradientStop>
}

impl RadialGradientStyle {
pub fn new(x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64, stops: Vec<CanvasGradientStop>)
-> RadialGradientStyle {
RadialGradientStyle {
x0: x0,
y0: y0,
r0: r0,
x1: x1,
y1: y1,
r1: r1,
stops: stops,
}
}
}

#[derive(Clone)]
pub enum FillOrStrokeStyle {
Color(RGBA),
LinearGradient(LinearGradientStyle),
RadialGradient(RadialGradientStyle),
}

impl FillOrStrokeStyle {
fn to_azure_pattern(&self) -> Pattern {
fn to_azure_pattern(&self, drawtarget: &DrawTarget) -> Pattern {
match *self {
FillOrStrokeStyle::Color(ref color) => {
Pattern::Color(ColorPattern::new(color::new(color.red,
color.green,
color.blue,
color.alpha)))
},
FillOrStrokeStyle::LinearGradient(ref linear_gradient_style) => {
let gradient_stops: Vec<GradientStop> = linear_gradient_style.stops.iter().map(|s| {
GradientStop {
offset: s.offset as AzFloat,
color: color::new(s.color.red, s.color.green, s.color.blue, s.color.alpha)
}
}).collect();

Pattern::LinearGradient(LinearGradientPattern::new(
&Point2D(linear_gradient_style.x0 as AzFloat, linear_gradient_style.y0 as AzFloat),
&Point2D(linear_gradient_style.x1 as AzFloat, linear_gradient_style.y1 as AzFloat),
drawtarget.create_gradient_stops(gradient_stops.as_slice(), ExtendMode::Clamp),
&Matrix2D::identity()))
},
FillOrStrokeStyle::RadialGradient(ref radial_gradient_style) => {
let gradient_stops: Vec<GradientStop> = radial_gradient_style.stops.iter().map(|s| {
GradientStop {
offset: s.offset as AzFloat,
color: color::new(s.color.red, s.color.green, s.color.blue, s.color.alpha)
}
}).collect();

Pattern::RadialGradient(RadialGradientPattern::new(
&Point2D(radial_gradient_style.x0 as AzFloat, radial_gradient_style.y0 as AzFloat),
&Point2D(radial_gradient_style.x1 as AzFloat, radial_gradient_style.y1 as AzFloat),
radial_gradient_style.r0 as AzFloat, radial_gradient_style.r1 as AzFloat,
drawtarget.create_gradient_stops(gradient_stops.as_slice(), ExtendMode::Clamp),
&Matrix2D::identity()))
}
}
}
}

@@ -34,6 +34,7 @@ use dom::bindings::refcounted::Trusted;
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use script_task::ScriptChan;

use canvas::canvas_paint_task::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
use cssparser::RGBA;
use encoding::types::EncodingRef;
use geom::matrix2d::Matrix2D;
@@ -233,6 +234,7 @@ no_jsmanaged_fields!(LengthOrPercentageOrAuto);
no_jsmanaged_fields!(RGBA);
no_jsmanaged_fields!(Matrix2D<T>);
no_jsmanaged_fields!(StorageType);
no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyle);

impl JSTraceable for Box<ScriptChan+Send> {
#[inline]
@@ -2,10 +2,80 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::utils::Reflector;
use cssparser::RGBA;
use canvas::canvas_paint_task::{CanvasGradientStop, FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CanvasGradientBinding;
use dom::bindings::codegen::Bindings::CanvasGradientBinding::CanvasGradientMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::canvasrenderingcontext2d::parse_color;

#[dom_struct]
pub struct CanvasGradient {
reflector_: Reflector,
style: CanvasGradientStyle,
stops: DOMRefCell<Vec<CanvasGradientStop>>,
}

#[jstraceable]
pub enum CanvasGradientStyle {
Linear(LinearGradientStyle),
Radial(RadialGradientStyle),
}

impl CanvasGradient {
fn new_inherited(style: CanvasGradientStyle) -> CanvasGradient {
CanvasGradient {
reflector_: Reflector::new(),
style: style,
stops: DOMRefCell::new(Vec::new()),
}
}

pub fn new(global: GlobalRef, style: CanvasGradientStyle) -> Temporary<CanvasGradient> {
reflect_dom_object(box CanvasGradient::new_inherited(style),
global, CanvasGradientBinding::Wrap)
}
}

impl<'a> CanvasGradientMethods for JSRef<'a, CanvasGradient> {
fn AddColorStop(self, offset: f32, color: String) {
let default_black = RGBA {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 1.0,
};

self.stops.borrow_mut().push(CanvasGradientStop {
offset: offset as f64,
color: parse_color(color.as_slice()).unwrap_or(default_black),
});
}
}

pub trait ToFillOrStrokeStyle {
fn to_fill_or_stroke_style(&self) -> FillOrStrokeStyle;
}

impl<'a> ToFillOrStrokeStyle for JSRef<'a, CanvasGradient> {
fn to_fill_or_stroke_style(&self) -> FillOrStrokeStyle {
let gradient_stops = self.stops.borrow().clone();
match self.style {
CanvasGradientStyle::Linear(ref gradient) => {
FillOrStrokeStyle::LinearGradient(
LinearGradientStyle::new(gradient.x0, gradient.y0,
gradient.x1, gradient.y1,
gradient_stops))
},
CanvasGradientStyle::Radial(ref gradient) => {
FillOrStrokeStyle::RadialGradient(
RadialGradientStyle::new(gradient.x0, gradient.y0, gradient.r0,
gradient.x1, gradient.y1, gradient.r1,
gradient_stops))
}
}
}
}
@@ -7,11 +7,12 @@ use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRen
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasWindingRule;
use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
use dom::bindings::error::Error::IndexSize;
use dom::bindings::error::Error::{IndexSize, TypeError};
use dom::bindings::error::Fallible;
use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JS, JSRef, LayoutJS, Temporary};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeStyle};
use dom::htmlcanvaselement::{HTMLCanvasElement, HTMLCanvasElementHelpers};
use dom::imagedata::{ImageData, ImageDataHelpers};

@@ -23,7 +24,9 @@ use geom::rect::Rect;
use geom::size::Size2D;

use canvas::canvas_paint_task::{CanvasMsg, CanvasPaintTask, FillOrStrokeStyle};
use canvas::canvas_paint_task::{LinearGradientStyle, RadialGradientStyle};

use std::borrow::ToOwned;
use std::cell::Cell;
use std::num::{Float, ToPrimitive};
use std::sync::mpsc::{channel, Sender};
@@ -215,6 +218,9 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
_ => {}
}
}
StringOrCanvasGradientOrCanvasPattern::eCanvasGradient(gradient) => {
self.renderer.send(CanvasMsg::SetFillStyle(gradient.root().r().to_fill_or_stroke_style())).unwrap();
}
_ => {}
}
}
@@ -264,6 +270,22 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
let canvas_size = self.canvas.root().r().get_size();
self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect, canvas_size)).unwrap()
}

fn CreateLinearGradient(self, x0: f64, y0: f64, x1: f64, y1: f64) -> Fallible<Temporary<CanvasGradient>> {
if [x0, y0, x1, y1].iter().any(|x| x.is_nan() || x.is_infinite()) {
return Err(TypeError("One of the arguments of createLinearGradient() is not a finite floating-point value.".to_owned()));
}
Ok(CanvasGradient::new(self.global.root().r(),
CanvasGradientStyle::Linear(LinearGradientStyle::new(x0, y0, x1, y1, Vec::new()))))
}

fn CreateRadialGradient(self, x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64) -> Fallible<Temporary<CanvasGradient>> {
if [x0, y0, r0, x1, y1, r1].iter().any(|x| x.is_nan() || x.is_infinite()) {
return Err(TypeError("One of the arguments of createRadialGradient() is not a finite floating-point value.".to_owned()));
}
Ok(CanvasGradient::new(self.global.root().r(),
CanvasGradientStyle::Radial(RadialGradientStyle::new(x0, y0, r0, x1, y1, r1, Vec::new()))))
}
}

#[unsafe_destructor]
@@ -6,7 +6,7 @@
interface CanvasGradient {
// opaque object
// addColorStop should take a double
//void addColorStop(float offset, DOMString color);
void addColorStop(float offset, DOMString color);
};


@@ -52,8 +52,10 @@ interface CanvasRenderingContext2D {
// colours and styles (see also the CanvasDrawingStyles interface)
attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black)
//CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
//CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
[Throws]
CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
[Throws]
CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
//CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);

// shadows

Some generated files are not rendered by default. Learn more.

Some generated files are not rendered by default. Learn more.

Some generated files are not rendered by default. Learn more.

@@ -65,7 +65,9 @@ flaky_cpu == append_style_a.html append_style_b.html
== box_sizing_sanity_check_a.html box_sizing_sanity_check_ref.html
== br.html br-ref.html
== canvas_as_block_element_a.html canvas_as_block_element_ref.html
== canvas_linear_gradient_a.html canvas_linear_gradient_ref.html
== canvas_lineto_a.html canvas_lineto_ref.html
== canvas_radial_gradient_a.html canvas_radial_gradient_ref.html
== canvas_transform_a.html canvas_transform_ref.html
== case-insensitive-font-family.html case-insensitive-font-family-ref.html
== clear_generated_content_table_a.html clear_generated_content_table_ref.html
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<body>
<style>
html, body {
margin: 0;
padding: 0;
}
canvas {
background-color: green;
}
</style>

<canvas id="c" width="200" height="200">
Your browser does not support the HTML5 canvas tag.</canvas>

<script>
var c = document.getElementById("c");
var ctx = c.getContext("2d");

var grd = ctx.createLinearGradient(10, 0, 190, 0);
grd.addColorStop(0, "red");
grd.addColorStop(1, "yellow");

ctx.fillStyle = grd;
ctx.fillRect(10, 10, 180, 180);
</script>

</body>
</html>
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<body>
<style>
html, body {
margin: 0;
padding: 0;
}
section, div {
width: 180px;
height: 180px;
}
section {
padding: 10px;
background-color: green;
}
div {
background: linear-gradient(to right, red, yellow);
}
</style>
<section><div></div></section>
</body>
</html>
Binary file not shown.
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.