Navigation Menu

Skip to content

Commit

Permalink
Implementation of pattern fill style for canvas.
Browse files Browse the repository at this point in the history
  • Loading branch information
hyowon committed Jun 13, 2015
1 parent 2168ec3 commit 00240e5
Show file tree
Hide file tree
Showing 35 changed files with 165 additions and 157 deletions.
17 changes: 16 additions & 1 deletion components/canvas/canvas_paint_task.rs
Expand Up @@ -301,7 +301,22 @@ impl<'a> CanvasPaintTask<'a> {
}

fn fill_rect(&self, rect: &Rect<f32>) {
self.drawtarget.fill_rect(rect, self.state.fill_style.to_pattern_ref(),
let draw_rect = Rect(rect.origin,
match self.state.fill_style {
Pattern::Surface(ref surface) => {
let surface_size = surface.size();
match (surface.repeat_x, surface.repeat_y) {
(true, true) => rect.size,
(true, false) => Size2D(rect.size.width, surface_size.height as f32),
(false, true) => Size2D(surface_size.width as f32, rect.size.height),
(false, false) => Size2D(surface_size.width as f32, surface_size.height as f32),
}
},
_ => rect.size,
}
);

self.drawtarget.fill_rect(&draw_rect, self.state.fill_style.to_pattern_ref(),
Some(&self.state.draw_options));
}

Expand Down
55 changes: 55 additions & 0 deletions components/canvas_traits/lib.rs
Expand Up @@ -17,6 +17,7 @@ use azure::azure::{AzFloat, AzColor};
use azure::azure_hl::{DrawTarget, Pattern, ColorPattern};
use azure::azure_hl::{GradientStop, LinearGradientPattern, RadialGradientPattern, ExtendMode};
use azure::azure_hl::{JoinStyle, CapStyle, CompositionOp};
use azure::azure_hl::{SurfacePattern, SurfaceFormat};
use cssparser::RGBA;
use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
Expand Down Expand Up @@ -176,11 +177,33 @@ impl RadialGradientStyle {
}
}

#[derive(Clone)]
pub struct SurfaceStyle {
pub surface_data: Vec<u8>,
pub surface_size: Size2D<i32>,
pub repeat_x: bool,
pub repeat_y: bool,
}

impl SurfaceStyle {
pub fn new(surface_data: Vec<u8>, surface_size: Size2D<i32>, repeat_x: bool, repeat_y: bool)
-> SurfaceStyle {
SurfaceStyle {
surface_data: surface_data,
surface_size: surface_size,
repeat_x: repeat_x,
repeat_y: repeat_y,
}
}
}


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

impl FillOrStrokeStyle {
Expand Down Expand Up @@ -220,6 +243,18 @@ impl FillOrStrokeStyle {
radial_gradient_style.r0 as AzFloat, radial_gradient_style.r1 as AzFloat,
drawtarget.create_gradient_stops(&gradient_stops, ExtendMode::Clamp),
&Matrix2D::identity()))
},
FillOrStrokeStyle::Surface(ref surface_style) => {
let source_surface = drawtarget.create_source_surface_from_data(
&surface_style.surface_data,
surface_style.surface_size,
surface_style.surface_size.width * 4,
SurfaceFormat::B8G8R8A8);

Pattern::Surface(SurfacePattern::new(
source_surface.azure_source_surface,
surface_style.repeat_x,
surface_style.repeat_y))
}
}
}
Expand Down Expand Up @@ -277,6 +312,26 @@ impl LineJoinStyle {
}
}

#[derive(Copy, Clone, PartialEq)]
pub enum RepetitionStyle {
Repeat,
RepeatX,
RepeatY,
NoRepeat,
}

impl RepetitionStyle {
pub fn from_str(string: &str) -> Option<RepetitionStyle> {
match string {
"repeat" => Some(RepetitionStyle::Repeat),
"repeat-x" => Some(RepetitionStyle::RepeatX),
"repeat-y" => Some(RepetitionStyle::RepeatY),
"no-repeat" => Some(RepetitionStyle::NoRepeat),
_ => None
}
}
}

#[derive(Copy, Clone, PartialEq)]
pub enum CompositionStyle {
SrcIn,
Expand Down
5 changes: 4 additions & 1 deletion components/script/dom/bindings/trace.rs
Expand Up @@ -35,11 +35,12 @@ use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use script_task::ScriptChan;

use canvas_traits::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
use canvas_traits::{LineCapStyle, LineJoinStyle, CompositionOrBlending};
use canvas_traits::{LineCapStyle, LineJoinStyle, CompositionOrBlending, RepetitionStyle};
use cssparser::RGBA;
use encoding::types::EncodingRef;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use html5ever::tree_builder::QuirksMode;
use hyper::header::Headers;
use hyper::method::Method;
Expand Down Expand Up @@ -249,6 +250,7 @@ no_jsmanaged_fields!(isize, i8, i16, i32, i64);
no_jsmanaged_fields!(Sender<T>);
no_jsmanaged_fields!(Receiver<T>);
no_jsmanaged_fields!(Rect<T>);
no_jsmanaged_fields!(Size2D<T>);
no_jsmanaged_fields!(Arc<T>);
no_jsmanaged_fields!(Image, ImageCacheChan, ImageCacheTask, ScriptControlChan);
no_jsmanaged_fields!(Atom, Namespace);
Expand All @@ -272,6 +274,7 @@ no_jsmanaged_fields!(Matrix2D<T>);
no_jsmanaged_fields!(StorageType);
no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyle);
no_jsmanaged_fields!(LineCapStyle, LineJoinStyle, CompositionOrBlending);
no_jsmanaged_fields!(RepetitionStyle);

impl JSTraceable for Box<ScriptChan+Send> {
#[inline]
Expand Down
44 changes: 43 additions & 1 deletion components/script/dom/canvaspattern.rs
Expand Up @@ -2,12 +2,54 @@
* 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 canvas_traits::{FillOrStrokeStyle, SurfaceStyle, RepetitionStyle};
use dom::bindings::codegen::Bindings::CanvasPatternBinding;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::canvasgradient::ToFillOrStrokeStyle;
use geom::size::Size2D;

// https://html.spec.whatwg.org/multipage/#canvaspattern
#[dom_struct]
pub struct CanvasPattern {
reflector_: Reflector,
surface_data: Vec<u8>,
surface_size: Size2D<i32>,
repeat_x: bool,
repeat_y: bool,
}

impl CanvasPattern {
fn new_inherited(surface_data: Vec<u8>, surface_size: Size2D<i32>, repeat: RepetitionStyle) -> CanvasPattern {
let (x, y) = match repeat {
RepetitionStyle::Repeat => (true, true),
RepetitionStyle::RepeatX => (true, false),
RepetitionStyle::RepeatY => (false, true),
RepetitionStyle::NoRepeat => (false, false),
};

CanvasPattern {
reflector_: Reflector::new(),
surface_data: surface_data,
surface_size: surface_size,
repeat_x: x,
repeat_y: y,
}
}
pub fn new(global: GlobalRef,
surface_data: Vec<u8>,
surface_size: Size2D<i32>,
repeat: RepetitionStyle)
-> Temporary<CanvasPattern> {
reflect_dom_object(box CanvasPattern::new_inherited(surface_data, surface_size, repeat),
global, CanvasPatternBinding::Wrap)
}
}

impl<'a> ToFillOrStrokeStyle for JSRef<'a, CanvasPattern> {
fn to_fill_or_stroke_style(&self) -> FillOrStrokeStyle {
FillOrStrokeStyle::Surface(
SurfaceStyle::new(self.surface_data.clone(), self.surface_size, self.repeat_x, self.repeat_y))
}
}
48 changes: 45 additions & 3 deletions components/script/dom/canvasrenderingcontext2d.rs
Expand Up @@ -9,13 +9,14 @@ use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D;
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
use dom::bindings::error::Error::{IndexSize, NotSupported, Type, InvalidState};
use dom::bindings::error::Error::{IndexSize, NotSupported, Type, InvalidState, Syntax};
use dom::bindings::error::Fallible;
use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JS, JSRef, LayoutJS, Rootable, Temporary, Unrooted};
use dom::bindings::num::Finite;
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeStyle};
use dom::canvaspattern::CanvasPattern;
use dom::htmlcanvaselement::{HTMLCanvasElement, HTMLCanvasElementHelpers};
use dom::htmlimageelement::{HTMLImageElement, HTMLImageElementHelpers};
use dom::imagedata::{ImageData, ImageDataHelpers};
Expand All @@ -29,7 +30,7 @@ use geom::rect::Rect;
use geom::size::Size2D;

use canvas_traits::{CanvasMsg, Canvas2dMsg, CanvasCommonMsg};
use canvas_traits::{FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle};
use canvas_traits::{FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle, RepetitionStyle};
use canvas_traits::{LineCapStyle, LineJoinStyle, CompositionOrBlending};
use canvas::canvas_paint_task::CanvasPaintTask;

Expand Down Expand Up @@ -865,7 +866,10 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
Canvas2dMsg::SetFillStyle(gradient_root.r().to_fill_or_stroke_style()));
self.renderer.send(msg).unwrap();
}
_ => {}
StringOrCanvasGradientOrCanvasPattern::eCanvasPattern(pattern) => {
self.renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetFillStyle(
pattern.root().r().to_fill_or_stroke_style()))).unwrap();
}
}
}

Expand Down Expand Up @@ -997,6 +1001,44 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
RadialGradientStyle::new(x0, y0, r0, x1, y1, r1, Vec::new()))))
}

// https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern
fn CreatePattern(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
repetition: DOMString) -> Fallible<Temporary<CanvasPattern>> {
match image {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
let image = image.root();
let image_element = image.r();

let url = match image_element.get_url() {
Some(url) => url,
None => return Err(InvalidState),
};

let img = match self.request_image_from_cache(url) {
ImageResponse::Loaded(img) => img,
ImageResponse::PlaceholderLoaded(_) | ImageResponse::None => return Err(InvalidState),
};

let image_size = Size2D(img.width as f64, img.height as f64);
let image_data = match img.pixels {
PixelsByColorType::RGBA8(ref pixels) => pixels.to_vec(),
PixelsByColorType::K8(_) => panic!("K8 color type not supported"),
PixelsByColorType::RGB8(_) => panic!("RGB8 color type not supported"),
PixelsByColorType::KA8(_) => panic!("KA8 color type not supported"),
};

if let Some(rep) = RepetitionStyle::from_str(&repetition) {
return Ok(CanvasPattern::new(self.global.root().r(),
image_data,
Size2D(image_size.width as i32, image_size.height as i32),
rep));
}
return Err(Syntax);
},
_ => return Err(Type("Not implemented".to_owned())),
}
}

// https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth
fn LineWidth(self) -> f64 {
let state = self.state.borrow();
Expand Down
Expand Up @@ -62,7 +62,8 @@ interface CanvasRenderingContext2D {
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);
[Throws]
CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);

// shadows
attribute unrestricted double shadowOffsetX; // (default 0)
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 00240e5

Please sign in to comment.