зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #5433 - Implements drawImage for html image as ImageSource (from dmarcos:issue5290); r=jdm
Source-Repo: https://github.com/servo/servo Source-Revision: 58637a1174f94cb1ebbb394d3ba3c8c8f2d70639 --HG-- rename : servo/tests/html/test_canvas_drawimage.html => servo/tests/html/test_canvas_drawimage_canvas.html
This commit is contained in:
Родитель
3dea48290b
Коммит
5cfb1364bd
|
@ -16,7 +16,7 @@ use util::vec::byte_swap;
|
|||
|
||||
use cssparser::RGBA;
|
||||
use std::borrow::ToOwned;
|
||||
use std::num::Float;
|
||||
use std::num::{Float, ToPrimitive};
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -28,8 +28,8 @@ pub enum CanvasMsg {
|
|||
ClosePath,
|
||||
Fill,
|
||||
Stroke,
|
||||
DrawImage(Vec<u8>, Rect<i32>, Rect<i32>, bool),
|
||||
DrawImageSelf(Size2D<i32>, Rect<i32>, Rect<i32>, bool),
|
||||
DrawImage(Vec<u8>, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
|
||||
DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool),
|
||||
MoveTo(Point2D<f32>),
|
||||
LineTo(Point2D<f32>),
|
||||
QuadraticCurveTo(Point2D<f32>, Point2D<f32>),
|
||||
|
@ -41,8 +41,8 @@ pub enum CanvasMsg {
|
|||
SetTransform(Matrix2D<f32>),
|
||||
Recreate(Size2D<i32>),
|
||||
SendPixelContents(Sender<Vec<u8>>),
|
||||
GetImageData(Rect<i32>, Size2D<i32>, Sender<Vec<u8>>),
|
||||
PutImageData(Vec<u8>, Rect<i32>, Option<Rect<i32>>),
|
||||
GetImageData(Rect<f64>, Size2D<f64>, Sender<Vec<u8>>),
|
||||
PutImageData(Vec<u8>, Rect<f64>, Option<Rect<f64>>),
|
||||
Close,
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,9 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
/// It reads image data from the canvas
|
||||
/// canvas_size: The size of the canvas we're reading from
|
||||
/// read_rect: The area of the canvas we want to read from
|
||||
fn read_pixels(&self, read_rect: Rect<i32>, canvas_size: Size2D<i32>) -> Vec<u8>{
|
||||
fn read_pixels(&self, read_rect: Rect<f64>, canvas_size: Size2D<f64>) -> Vec<u8>{
|
||||
let read_rect = read_rect.to_i32();
|
||||
let canvas_size = canvas_size.to_i32();
|
||||
let canvas_rect = Rect(Point2D(0i32, 0i32), canvas_size);
|
||||
let src_read_rect = canvas_rect.intersection(&read_rect).unwrap_or(Rect::zero());
|
||||
|
||||
|
@ -81,9 +83,9 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
/// dest_rect: The area of the canvas where the imagedata will be copied
|
||||
/// smoothing_enabled: if smoothing is applied to the copied pixels
|
||||
fn write_pixels(&self, imagedata: &[u8],
|
||||
image_size: Size2D<i32>,
|
||||
source_rect: Rect<i32>,
|
||||
dest_rect: Rect<i32>,
|
||||
image_size: Size2D<f64>,
|
||||
source_rect: Rect<f64>,
|
||||
dest_rect: Rect<f64>,
|
||||
smoothing_enabled: bool) {
|
||||
// From spec https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage
|
||||
// When scaling up, if the imageSmoothingEnabled attribute is set to true, the user agent should attempt
|
||||
|
@ -94,8 +96,11 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
} else {
|
||||
Filter::Point
|
||||
};
|
||||
// azure_hl operates with integers. We need to cast the image size
|
||||
let image_size = image_size.to_i32();
|
||||
|
||||
let source_surface = self.drawtarget.create_source_surface_from_data(imagedata,
|
||||
let source_surface = self.drawtarget.create_source_surface_from_data(
|
||||
&imagedata,
|
||||
image_size, image_size.width * 4, SurfaceFormat::B8G8R8A8);
|
||||
|
||||
let draw_surface_options = DrawSurfaceOptions::new(filter, true);
|
||||
|
@ -112,33 +117,33 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
/// Result: It retuns the modified dirty_rect by the rules described in
|
||||
/// the spec https://html.spec.whatwg.org/#dom-context-2d-putimagedata
|
||||
fn calculate_dirty_rect(&self,
|
||||
mut dirty_rect: Rect<i32>,
|
||||
image_data_rect: Rect<i32>) -> Rect<i32>{
|
||||
mut dirty_rect: Rect<f64>,
|
||||
image_data_rect: Rect<f64>) -> Rect<f64>{
|
||||
// 1) If dirtyWidth is negative,
|
||||
// let dirtyX be dirtyX+dirtyWidth,
|
||||
// and let dirtyWidth be equal to the absolute magnitude of dirtyWidth.
|
||||
if dirty_rect.size.width < 0 {
|
||||
if dirty_rect.size.width < 0.0f64 {
|
||||
dirty_rect.origin.x = dirty_rect.origin.x + dirty_rect.size.width;
|
||||
dirty_rect.size.width = -dirty_rect.size.width;
|
||||
}
|
||||
|
||||
// 2) If dirtyHeight is negative, let dirtyY be dirtyY+dirtyHeight,
|
||||
// and let dirtyHeight be equal to the absolute magnitude of dirtyHeight.
|
||||
if dirty_rect.size.height < 0 {
|
||||
if dirty_rect.size.height < 0.0f64 {
|
||||
dirty_rect.origin.y = dirty_rect.origin.y + dirty_rect.size.height;
|
||||
dirty_rect.size.height = -dirty_rect.size.height;
|
||||
}
|
||||
|
||||
// 3) If dirtyX is negative, let dirtyWidth be dirtyWidth+dirtyX, and let dirtyX be zero.
|
||||
if dirty_rect.origin.x < 0 {
|
||||
if dirty_rect.origin.x < 0.0f64 {
|
||||
dirty_rect.size.width += dirty_rect.origin.x;
|
||||
dirty_rect.origin.x = 0;
|
||||
dirty_rect.origin.x = 0.0f64;
|
||||
}
|
||||
|
||||
// 3) If dirtyY is negative, let dirtyHeight be dirtyHeight+dirtyY, and let dirtyY be zero.
|
||||
if dirty_rect.origin.y < 0 {
|
||||
if dirty_rect.origin.y < 0.0f64 {
|
||||
dirty_rect.size.height += dirty_rect.origin.y;
|
||||
dirty_rect.origin.y = 0;
|
||||
dirty_rect.origin.y = 0.0f64;
|
||||
}
|
||||
|
||||
// 4) If dirtyX+dirtyWidth is greater than the width attribute of the imagedata argument,
|
||||
|
@ -157,21 +162,19 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
}
|
||||
|
||||
/// It writes an image to the destination canvas
|
||||
/// imagedata: Pixel information of the image to be written
|
||||
/// source_rect: Area of the source image to be copied
|
||||
/// imagedata: Pixel information of the image to be written. It takes RGBA8
|
||||
/// image_size: The size of the image to be written
|
||||
/// dest_rect: Area of the destination canvas where the pixels will be copied
|
||||
/// smoothing_enabled: It determines if smoothing is applied to the image result
|
||||
fn write_image(&self, mut imagedata: Vec<u8>,
|
||||
source_rect: Rect<i32>, dest_rect: Rect<i32>, smoothing_enabled: bool) {
|
||||
image_size: Size2D<f64>, dest_rect: Rect<f64>, smoothing_enabled: bool) {
|
||||
if imagedata.len() == 0 {
|
||||
return
|
||||
}
|
||||
// Image data already contains the portion of the image we want to draw
|
||||
// so the source rect corresponds to the whole area of the copied imagedata
|
||||
let source_rect = Rect(Point2D(0i32, 0i32), source_rect.size);
|
||||
let image_rect = Rect(Point2D(0f64, 0f64), image_size);
|
||||
// rgba -> bgra
|
||||
byte_swap(imagedata.as_mut_slice());
|
||||
self.write_pixels(&imagedata, source_rect.size, source_rect, dest_rect, smoothing_enabled);
|
||||
self.write_pixels(&imagedata, image_size, image_rect, dest_rect, smoothing_enabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -215,8 +218,8 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
CanvasMsg::ClosePath => painter.close_path(),
|
||||
CanvasMsg::Fill => painter.fill(),
|
||||
CanvasMsg::Stroke => painter.stroke(),
|
||||
CanvasMsg::DrawImage(imagedata, dest_rect, source_rect, smoothing_enabled) => {
|
||||
painter.draw_image(imagedata, dest_rect, source_rect, smoothing_enabled)
|
||||
CanvasMsg::DrawImage(imagedata, image_size, dest_rect, source_rect, smoothing_enabled) => {
|
||||
painter.draw_image(imagedata, image_size, dest_rect, source_rect, smoothing_enabled)
|
||||
}
|
||||
CanvasMsg::DrawImageSelf(image_size, dest_rect, source_rect, smoothing_enabled) => {
|
||||
painter.draw_image_self(image_size, dest_rect, source_rect, smoothing_enabled)
|
||||
|
@ -304,19 +307,23 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
};
|
||||
}
|
||||
|
||||
fn draw_image(&self, imagedata: Vec<u8>, dest_rect: Rect<i32>,
|
||||
source_rect: Rect<i32>, smoothing_enabled: bool) {
|
||||
self.write_image(imagedata, source_rect, dest_rect, smoothing_enabled);
|
||||
fn draw_image(&self, image_data: Vec<u8>, image_size: Size2D<f64>,
|
||||
dest_rect: Rect<f64>, source_rect: Rect<f64>, smoothing_enabled: bool) {
|
||||
// We round up the floating pixel values to draw the pixels
|
||||
let source_rect = source_rect.ceil();
|
||||
// It discards the extra pixels (if any) that won't be painted
|
||||
let image_data = crop_image(image_data, image_size, source_rect);
|
||||
self.write_image(image_data, source_rect.size, dest_rect, smoothing_enabled);
|
||||
}
|
||||
|
||||
fn draw_image_self(&self, image_size: Size2D<i32>,
|
||||
dest_rect: Rect<i32>, source_rect: Rect<i32>,
|
||||
fn draw_image_self(&self, image_size: Size2D<f64>,
|
||||
dest_rect: Rect<f64>, source_rect: Rect<f64>,
|
||||
smoothing_enabled: bool) {
|
||||
// Reads pixels from source image
|
||||
// In this case source and target are the same canvas
|
||||
let imagedata = self.read_pixels(source_rect, image_size);
|
||||
// Writes on target canvas
|
||||
self.write_image(imagedata, source_rect, dest_rect, smoothing_enabled);
|
||||
self.write_image(imagedata, image_size, dest_rect, smoothing_enabled);
|
||||
}
|
||||
|
||||
fn move_to(&self, point: &Point2D<AzFloat>) {
|
||||
|
@ -431,20 +438,20 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn get_image_data(&self, mut dest_rect: Rect<i32>, canvas_size: Size2D<i32>, chan: Sender<Vec<u8>>) {
|
||||
if dest_rect.size.width < 0 {
|
||||
fn get_image_data(&self, mut dest_rect: Rect<f64>, canvas_size: Size2D<f64>, chan: Sender<Vec<u8>>) {
|
||||
if dest_rect.size.width < 0.0 {
|
||||
dest_rect.size.width = -dest_rect.size.width;
|
||||
dest_rect.origin.x -= dest_rect.size.width;
|
||||
}
|
||||
if dest_rect.size.height < 0 {
|
||||
if dest_rect.size.height < 0.0 {
|
||||
dest_rect.size.height = -dest_rect.size.height;
|
||||
dest_rect.origin.y -= dest_rect.size.height;
|
||||
}
|
||||
if dest_rect.size.width == 0 {
|
||||
dest_rect.size.width = 1;
|
||||
if dest_rect.size.width == 0.0 {
|
||||
dest_rect.size.width = 1.0;
|
||||
}
|
||||
if dest_rect.size.height == 0 {
|
||||
dest_rect.size.height = 1;
|
||||
if dest_rect.size.height == 0.0 {
|
||||
dest_rect.size.height = 1.0;
|
||||
}
|
||||
|
||||
let mut dest_data = self.read_pixels(dest_rect, canvas_size);
|
||||
|
@ -455,18 +462,18 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
}
|
||||
|
||||
fn put_image_data(&mut self, mut imagedata: Vec<u8>,
|
||||
image_data_rect: Rect<i32>,
|
||||
dirty_rect: Option<Rect<i32>>) {
|
||||
image_data_rect: Rect<f64>,
|
||||
dirty_rect: Option<Rect<f64>>) {
|
||||
|
||||
if image_data_rect.size.width <= 0 || image_data_rect.size.height <= 0 {
|
||||
if image_data_rect.size.width <= 0.0 || image_data_rect.size.height <= 0.0 {
|
||||
return
|
||||
}
|
||||
|
||||
assert!(image_data_rect.size.width * image_data_rect.size.height * 4 == imagedata.len() as i32);
|
||||
assert!(image_data_rect.size.width * image_data_rect.size.height * 4.0 == imagedata.len() as f64);
|
||||
// rgba -> bgra
|
||||
byte_swap(imagedata.as_mut_slice());
|
||||
|
||||
let image_rect = Rect(Point2D(0i32, 0i32),
|
||||
let image_rect = Rect(Point2D(0f64, 0f64),
|
||||
Size2D(image_data_rect.size.width, image_data_rect.size.height));
|
||||
|
||||
// Dirty rectangle defines the area of the source image to be copied
|
||||
|
@ -481,7 +488,7 @@ impl<'a> CanvasPaintTask<'a> {
|
|||
|
||||
// 5) If either dirtyWidth or dirtyHeight is negative or zero,
|
||||
// stop without affecting any bitmaps
|
||||
if source_rect.size.width <= 0 || source_rect.size.height <= 0 {
|
||||
if source_rect.size.width <= 0.0 || source_rect.size.height <= 0.0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -603,11 +610,75 @@ impl FillOrStrokeStyle {
|
|||
}
|
||||
}
|
||||
|
||||
/// Used by drawImage to get rid of the extra pixels of the image data that
|
||||
/// won't be copied to the canvas
|
||||
/// image_data: Color pixel data of the image
|
||||
/// image_size: Image dimensions
|
||||
/// crop_rect: It determines the area of the image we want to keep
|
||||
fn crop_image(image_data: Vec<u8>,
|
||||
image_size: Size2D<f64>,
|
||||
crop_rect: Rect<f64>) -> Vec<u8>{
|
||||
// We're going to iterate over a pixel values array so we need integers
|
||||
let crop_rect = crop_rect.to_i32();
|
||||
let image_size = image_size.to_i32();
|
||||
// Assuming 4 bytes per pixel and row-major order for storage
|
||||
// (consecutive elements in a pixel row of the image are contiguous in memory)
|
||||
let stride = image_size.width * 4;
|
||||
let image_bytes_length = image_size.height * image_size.width * 4;
|
||||
let crop_area_bytes_length = crop_rect.size.height * crop_rect.size.height * 4;
|
||||
// If the image size is less or equal than the crop area we do nothing
|
||||
if image_bytes_length <= crop_area_bytes_length {
|
||||
return image_data;
|
||||
}
|
||||
|
||||
let mut new_image_data = Vec::new();
|
||||
let mut src = (crop_rect.origin.y * stride + crop_rect.origin.x * 4) as usize;
|
||||
for _ in (0..crop_rect.size.height) {
|
||||
let row = &image_data[src .. src + (4 * crop_rect.size.width) as usize];
|
||||
new_image_data.push_all(row);
|
||||
src += stride as usize;
|
||||
}
|
||||
new_image_data
|
||||
}
|
||||
|
||||
pub trait SizeToi32 {
|
||||
fn to_i32(&self) -> Size2D<i32>;
|
||||
}
|
||||
|
||||
impl SizeToi32 for Size2D<f64> {
|
||||
fn to_i32(&self) -> Size2D<i32> {
|
||||
Size2D(self.width.to_i32().unwrap(),
|
||||
self.height.to_i32().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RectToi32 {
|
||||
fn to_i32(&self) -> Rect<i32>;
|
||||
fn ceil(&self) -> Rect<f64>;
|
||||
}
|
||||
|
||||
impl RectToi32 for Rect<f64> {
|
||||
fn to_i32(&self) -> Rect<i32> {
|
||||
Rect(Point2D(self.origin.x.to_i32().unwrap(),
|
||||
self.origin.y.to_i32().unwrap()),
|
||||
Size2D(self.size.width.to_i32().unwrap(),
|
||||
self.size.height.to_i32().unwrap()))
|
||||
}
|
||||
|
||||
fn ceil(&self) -> Rect<f64> {
|
||||
Rect(Point2D(self.origin.x.ceil(),
|
||||
self.origin.y.ceil()),
|
||||
Size2D(self.size.width.ceil(),
|
||||
self.size.height.ceil()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub trait ToAzFloat {
|
||||
fn to_azfloat(&self) -> Rect<AzFloat>;
|
||||
}
|
||||
|
||||
impl ToAzFloat for Rect<i32> {
|
||||
impl ToAzFloat for Rect<f64> {
|
||||
fn to_azfloat(&self) -> Rect<AzFloat> {
|
||||
Rect(Point2D(self.origin.x as AzFloat, self.origin.y as AzFloat),
|
||||
Size2D(self.size.width as AzFloat, self.size.height as AzFloat))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![feature(collections)]
|
||||
#![feature(core)]
|
||||
#![feature(std_misc)]
|
||||
|
||||
extern crate azure;
|
||||
|
|
|
@ -57,6 +57,9 @@ git = "https://github.com/servo/html5ever"
|
|||
[dependencies.js]
|
||||
git = "https://github.com/servo/rust-mozjs"
|
||||
|
||||
[dependencies.png]
|
||||
git = "https://github.com/servo/rust-png"
|
||||
|
||||
[dependencies.uuid]
|
||||
git = "https://github.com/rust-lang/uuid"
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
|
|||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
|
||||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasWindingRule;
|
||||
use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
|
||||
use dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrCanvasRenderingContext2D;
|
||||
use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D;
|
||||
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
|
||||
use dom::bindings::error::Error::{IndexSize, NotSupported, Type};
|
||||
use dom::bindings::error::Error::{IndexSize, NotSupported, Type, InvalidState};
|
||||
use dom::bindings::error::Fallible;
|
||||
use dom::bindings::global::{GlobalRef, GlobalField};
|
||||
use dom::bindings::js::{JS, JSRef, LayoutJS, Temporary};
|
||||
|
@ -16,7 +16,9 @@ use dom::bindings::num::Finite;
|
|||
use dom::bindings::utils::{Reflector, reflect_dom_object};
|
||||
use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeStyle};
|
||||
use dom::htmlcanvaselement::{HTMLCanvasElement, HTMLCanvasElementHelpers};
|
||||
use dom::htmlimageelement::{HTMLImageElement, HTMLImageElementHelpers};
|
||||
use dom::imagedata::{ImageData, ImageDataHelpers};
|
||||
use dom::node::{window_from_node};
|
||||
|
||||
use cssparser::Color as CSSColor;
|
||||
use cssparser::{Parser, RGBA, ToCss};
|
||||
|
@ -28,11 +30,19 @@ use geom::size::Size2D;
|
|||
use canvas::canvas_paint_task::{CanvasMsg, CanvasPaintTask, FillOrStrokeStyle};
|
||||
use canvas::canvas_paint_task::{LinearGradientStyle, RadialGradientStyle};
|
||||
|
||||
use net_traits::image::base::Image;
|
||||
use net_traits::image_cache_task::{ImageResponseMsg, Msg};
|
||||
use png::PixelsByColorType;
|
||||
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::Cell;
|
||||
use std::num::{Float, ToPrimitive};
|
||||
use std::sync::{Arc};
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
|
||||
use url::Url;
|
||||
use util::vec::byte_swap;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct CanvasRenderingContext2D {
|
||||
reflector_: Reflector,
|
||||
|
@ -85,10 +95,9 @@ impl CanvasRenderingContext2D {
|
|||
// source rectangle = area of the original image to be copied
|
||||
// destination rectangle = area of the destination canvas where the source image is going to be drawn
|
||||
fn adjust_source_dest_rects(&self,
|
||||
canvas: JSRef<HTMLCanvasElement>,
|
||||
image_size: Size2D<f64>,
|
||||
sx: f64, sy: f64, sw: f64, sh: f64,
|
||||
dx: f64, dy: f64, dw: f64, dh: f64) -> (Rect<i32>, Rect<i32>) {
|
||||
let image_size = canvas.get_size();
|
||||
dx: f64, dy: f64, dw: f64, dh: f64) -> (Rect<f64>, Rect<f64>) {
|
||||
let image_rect = Rect(Point2D(0f64, 0f64),
|
||||
Size2D(image_size.width as f64, image_size.height as f64));
|
||||
|
||||
|
@ -112,15 +121,13 @@ impl CanvasRenderingContext2D {
|
|||
|
||||
// The destination rectangle is the rectangle whose corners are the four points (dx, dy),
|
||||
// (dx+dw, dy), (dx+dw, dy+dh), (dx, dy+dh).
|
||||
let dest_rect = Rect(Point2D(dx.to_i32().unwrap(),
|
||||
dy.to_i32().unwrap()),
|
||||
Size2D(dest_rect_width_scaled.to_i32().unwrap(),
|
||||
dest_rect_height_scaled.to_i32().unwrap()));
|
||||
let dest_rect = Rect(Point2D(dx, dy),
|
||||
Size2D(dest_rect_width_scaled, dest_rect_height_scaled));
|
||||
|
||||
let source_rect = Rect(Point2D(source_rect_clipped.origin.x.to_i32().unwrap(),
|
||||
source_rect_clipped.origin.y.to_i32().unwrap()),
|
||||
Size2D(source_rect_clipped.size.width.to_i32().unwrap(),
|
||||
source_rect_clipped.size.height.to_i32().unwrap()));
|
||||
let source_rect = Rect(Point2D(source_rect_clipped.origin.x,
|
||||
source_rect_clipped.origin.y),
|
||||
Size2D(source_rect_clipped.size.width,
|
||||
source_rect_clipped.size.height));
|
||||
|
||||
return (source_rect, dest_rect)
|
||||
}
|
||||
|
@ -150,39 +157,103 @@ impl CanvasRenderingContext2D {
|
|||
canvas: JSRef<HTMLCanvasElement>,
|
||||
sx: f64, sy: f64, sw: f64, sh: f64,
|
||||
dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
|
||||
|
||||
// 1. Check the usability of the image argument
|
||||
if !canvas.is_valid() {
|
||||
return Ok(())
|
||||
return Err(InvalidState)
|
||||
}
|
||||
|
||||
let canvas_size = canvas.get_size();
|
||||
let image_size = Size2D(canvas_size.width as f64, canvas_size.height as f64);
|
||||
// 2. Establish the source and destination rectangles
|
||||
let (source_rect, dest_rect) = self.adjust_source_dest_rects(canvas, sx, sy, sw, sh, dx, dy, dw, dh);
|
||||
let (source_rect, dest_rect) = self.adjust_source_dest_rects(image_size, sx, sy, sw, sh, dx, dy, dw, dh);
|
||||
|
||||
if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
|
||||
return Err(IndexSize)
|
||||
}
|
||||
|
||||
let smoothing_enabled = self.image_smoothing_enabled.get();
|
||||
let canvas_size = canvas.get_size();
|
||||
|
||||
// If the source and target canvas are the same
|
||||
let msg = if self.canvas.root().r() == canvas {
|
||||
CanvasMsg::DrawImageSelf(canvas_size, dest_rect, source_rect, smoothing_enabled)
|
||||
CanvasMsg::DrawImageSelf(image_size, dest_rect, source_rect, smoothing_enabled)
|
||||
} else { // Source and target canvases are different
|
||||
let context = canvas.get_2d_context().root();
|
||||
let renderer = context.r().get_renderer();
|
||||
let (sender, receiver) = channel::<Vec<u8>>();
|
||||
// Reads pixels from source image
|
||||
renderer.send(CanvasMsg::GetImageData(source_rect, canvas_size, sender)).unwrap();
|
||||
renderer.send(CanvasMsg::GetImageData(source_rect, image_size, sender)).unwrap();
|
||||
let imagedata = receiver.recv().unwrap();
|
||||
// Writes pixels to destination canvas
|
||||
CanvasMsg::DrawImage(imagedata, dest_rect, source_rect, smoothing_enabled)
|
||||
CanvasMsg::DrawImage(imagedata, source_rect.size, dest_rect, source_rect, smoothing_enabled)
|
||||
};
|
||||
|
||||
self.renderer.send(msg).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_image_data(&self,
|
||||
image_data: Vec<u8>,
|
||||
image_size: Size2D<f64>,
|
||||
sx: f64, sy: f64, sw: f64, sh: f64,
|
||||
dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
|
||||
// Establish the source and destination rectangles
|
||||
let (source_rect, dest_rect) = self.adjust_source_dest_rects(image_size, sx, sy, sw, sh, dx, dy, dw, dh);
|
||||
|
||||
if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
|
||||
return Err(IndexSize)
|
||||
}
|
||||
|
||||
let smoothing_enabled = self.image_smoothing_enabled.get();
|
||||
self.renderer.send(CanvasMsg::DrawImage(
|
||||
image_data, image_size, dest_rect,
|
||||
source_rect, smoothing_enabled)).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_image_data(&self,
|
||||
image_element: &JSRef<HTMLImageElement>)
|
||||
-> Option<(Vec<u8>, Size2D<f64>)> {
|
||||
let url = match image_element.get_url() {
|
||||
Some(url) => url,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let img = match self.request_image_from_cache(url) {
|
||||
Some(img) => img,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let image_size = Size2D(img.width as f64, img.height as f64);
|
||||
let mut 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"),
|
||||
};
|
||||
// Pixels come from cache in BGRA order and drawImage expects RGBA so we
|
||||
// have to swap the color values
|
||||
{
|
||||
let mut pixel_colors = image_data.as_mut_slice();
|
||||
byte_swap(pixel_colors);
|
||||
}
|
||||
return Some((image_data, image_size));
|
||||
}
|
||||
|
||||
fn request_image_from_cache(&self, url: Url) -> Option<Arc<Box<Image>>> {
|
||||
let canvas = self.canvas.root();
|
||||
let window = window_from_node(canvas.r()).root();
|
||||
let window = window.r();
|
||||
let image_cache_task = window.image_cache_task().clone();
|
||||
image_cache_task.send(Msg::Prefetch(url.clone()));
|
||||
image_cache_task.send(Msg::Decode(url.clone()));
|
||||
let (response_chan, response_port) = channel();
|
||||
image_cache_task.send(Msg::WaitForImage(url, response_chan));
|
||||
match response_port.recv().unwrap() {
|
||||
ImageResponseMsg::ImageReady(image) => Some(image),
|
||||
ImageResponseMsg::ImageFailed => None,
|
||||
_ => panic!("Image Cache: Unknown Result")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CanvasRenderingContext2DHelpers {
|
||||
|
@ -316,7 +387,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage
|
||||
fn DrawImage(self, image: HTMLCanvasElementOrCanvasRenderingContext2D,
|
||||
fn DrawImage(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
||||
dx: f64, dy: f64) -> Fallible<()> {
|
||||
if !(dx.is_finite() && dy.is_finite()) {
|
||||
return Ok(());
|
||||
|
@ -330,7 +401,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
let sy: f64 = 0f64;
|
||||
|
||||
match image {
|
||||
HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
|
||||
let canvas = image.root();
|
||||
let canvas_size = canvas.r().get_size();
|
||||
let dw: f64 = canvas_size.width as f64;
|
||||
|
@ -341,7 +412,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
|
||||
let image = image.root();
|
||||
let context = image.r();
|
||||
let canvas = context.Canvas().root();
|
||||
|
@ -354,11 +425,31 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
|
||||
let image = image.root();
|
||||
let image_element = image.r();
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#img-error
|
||||
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||
// then throw an InvalidStateError exception
|
||||
let (image_data, image_size) = match self.fetch_image_data(&image_element) {
|
||||
Some((data, size)) => (data, size),
|
||||
None => return Err(InvalidState),
|
||||
};
|
||||
let dw: f64 = image_size.width as f64;
|
||||
let dh: f64 = image_size.height as f64;
|
||||
let sw: f64 = dw;
|
||||
let sh: f64 = dh;
|
||||
return self.draw_image_data(image_data,
|
||||
image_size,
|
||||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage
|
||||
fn DrawImage_(self, image: HTMLCanvasElementOrCanvasRenderingContext2D,
|
||||
fn DrawImage_(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
||||
dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
|
||||
if !(dx.is_finite() && dy.is_finite() &&
|
||||
dw.is_finite() && dh.is_finite()) {
|
||||
|
@ -373,7 +464,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
let sy: f64 = 0f64;
|
||||
|
||||
match image {
|
||||
HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
|
||||
let canvas = image.root();
|
||||
let canvas_size = canvas.r().get_size();
|
||||
let sw: f64 = canvas_size.width as f64;
|
||||
|
@ -382,7 +473,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
|
||||
let image = image.root();
|
||||
let context = image.r();
|
||||
let canvas = context.Canvas().root();
|
||||
|
@ -393,11 +484,28 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
|
||||
let image = image.root();
|
||||
let image_element = image.r();
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#img-error
|
||||
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||
// then throw an InvalidStateError exception
|
||||
let (image_data, image_size) = match self.fetch_image_data(&image_element) {
|
||||
Some((data, size)) => (data, size),
|
||||
None => return Err(InvalidState),
|
||||
};
|
||||
let sw: f64 = image_size.width as f64;
|
||||
let sh: f64 = image_size.height as f64;
|
||||
return self.draw_image_data(image_data,
|
||||
image_size,
|
||||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage
|
||||
fn DrawImage__(self, image: HTMLCanvasElementOrCanvasRenderingContext2D,
|
||||
fn DrawImage__(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
||||
sx: f64, sy: f64, sw: f64, sh: f64,
|
||||
dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
|
||||
if !(sx.is_finite() && sy.is_finite() && sw.is_finite() && sh.is_finite() &&
|
||||
|
@ -406,13 +514,13 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
}
|
||||
|
||||
match image {
|
||||
HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
|
||||
let canvas = image.root();
|
||||
return self.draw_html_canvas_element(canvas.r(),
|
||||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
|
||||
let image = image.root();
|
||||
let context = image.r();
|
||||
let canvas = context.Canvas().root();
|
||||
|
@ -420,6 +528,21 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
|
||||
let image = image.root();
|
||||
let image_element = image.r();
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#img-error
|
||||
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||
// then throw an InvalidStateError exception
|
||||
let (image_data, image_size) = match self.fetch_image_data(&image_element) {
|
||||
Some((data, size)) => (data, size),
|
||||
None => return Err(InvalidState),
|
||||
};
|
||||
return self.draw_image_data(image_data,
|
||||
image_size,
|
||||
sx, sy, sw, sh,
|
||||
dx, dy, dw, dh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -574,6 +697,8 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
}
|
||||
|
||||
fn GetImageData(self, sx: Finite<f64>, sy: Finite<f64>, sw: Finite<f64>, sh: Finite<f64>) -> Fallible<Temporary<ImageData>> {
|
||||
let sx = *sx;
|
||||
let sy = *sy;
|
||||
let sw = *sw;
|
||||
let sh = *sh;
|
||||
|
||||
|
@ -582,14 +707,18 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
}
|
||||
|
||||
let (sender, receiver) = channel::<Vec<u8>>();
|
||||
let dest_rect = Rect(Point2D(sx.to_i32().unwrap(), sy.to_i32().unwrap()), Size2D(sw.to_i32().unwrap(), sh.to_i32().unwrap()));
|
||||
let dest_rect = Rect(Point2D(sx as f64, sy as f64), Size2D(sw as f64, sh as f64));
|
||||
let canvas_size = self.canvas.root().r().get_size();
|
||||
let canvas_size = Size2D(canvas_size.width as f64, canvas_size.height as f64);
|
||||
self.renderer.send(CanvasMsg::GetImageData(dest_rect, canvas_size, sender)).unwrap();
|
||||
let data = receiver.recv().unwrap();
|
||||
Ok(ImageData::new(self.global.root().r(), sw.abs().to_u32().unwrap(), sh.abs().to_u32().unwrap(), Some(data)))
|
||||
}
|
||||
|
||||
fn PutImageData(self, imagedata: JSRef<ImageData>, dx: Finite<f64>, dy: Finite<f64>) {
|
||||
let dx = *dx;
|
||||
let dy = *dy;
|
||||
|
||||
// XXX:
|
||||
// By the spec: http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/#dom-context-2d-putimagedata
|
||||
// "If any of the arguments to the method are infinite or NaN, the method must throw a NotSupportedError exception"
|
||||
|
@ -597,13 +726,22 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
// they will be TypeError by WebIDL spec before call this methods.
|
||||
|
||||
let data = imagedata.get_data_array(&self.global.root().r());
|
||||
let image_data_rect = Rect(Point2D(dx.to_i32().unwrap(), dy.to_i32().unwrap()), imagedata.get_size());
|
||||
let image_data_size = imagedata.get_size();
|
||||
let image_data_size = Size2D(image_data_size.width as f64, image_data_size.height as f64);
|
||||
let image_data_rect = Rect(Point2D(dx, dy), image_data_size);
|
||||
let dirty_rect = None;
|
||||
self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect)).unwrap()
|
||||
}
|
||||
|
||||
fn PutImageData_(self, imagedata: JSRef<ImageData>, dx: Finite<f64>, dy: Finite<f64>,
|
||||
dirtyX: Finite<f64>, dirtyY: Finite<f64>, dirtyWidth: Finite<f64>, dirtyHeight: Finite<f64>) {
|
||||
let dx = *dx;
|
||||
let dy = *dy;
|
||||
let dirtyX = *dirtyX;
|
||||
let dirtyY = *dirtyY;
|
||||
let dirtyWidth = *dirtyWidth;
|
||||
let dirtyHeight = *dirtyHeight;
|
||||
|
||||
// XXX:
|
||||
// By the spec: http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/#dom-context-2d-putimagedata
|
||||
// "If any of the arguments to the method are infinite or NaN, the method must throw a NotSupportedError exception"
|
||||
|
@ -611,12 +749,11 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
|
|||
// they will be TypeError by WebIDL spec before call this methods.
|
||||
|
||||
let data = imagedata.get_data_array(&self.global.root().r());
|
||||
let image_data_rect = Rect(Point2D(dx.to_i32().unwrap(), dy.to_i32().unwrap()),
|
||||
Size2D(imagedata.Width().to_i32().unwrap(),
|
||||
imagedata.Height().to_i32().unwrap()));
|
||||
let dirty_rect = Some(Rect(Point2D(dirtyX.to_i32().unwrap(), dirtyY.to_i32().unwrap()),
|
||||
Size2D(dirtyWidth.to_i32().unwrap(),
|
||||
dirtyHeight.to_i32().unwrap())));
|
||||
let image_data_rect = Rect(Point2D(dx, dy),
|
||||
Size2D(imagedata.Width() as f64,
|
||||
imagedata.Height() as f64));
|
||||
let dirty_rect = Some(Rect(Point2D(dirtyX, dirtyY),
|
||||
Size2D(dirtyWidth, dirtyHeight)));
|
||||
self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect)).unwrap()
|
||||
}
|
||||
|
||||
|
@ -667,6 +804,6 @@ pub fn parse_color(string: &str) -> Result<RGBA,()> {
|
|||
|
||||
// Used by drawImage to determine if a source or destination rectangle is valid
|
||||
// Origin coordinates and size cannot be negative. Size has to be greater than zero
|
||||
fn is_rect_valid(rect: Rect<i32>) -> bool {
|
||||
rect.origin.x >= 0 && rect.origin.y >= 0 && rect.size.width > 0 && rect.size.height > 0
|
||||
fn is_rect_valid(rect: Rect<f64>) -> bool {
|
||||
rect.size.width > 0.0 && rect.size.height > 0.0
|
||||
}
|
||||
|
|
|
@ -39,6 +39,16 @@ impl HTMLImageElementDerived for EventTarget {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait HTMLImageElementHelpers {
|
||||
fn get_url(&self) -> Option<Url>;
|
||||
}
|
||||
|
||||
impl<'a> HTMLImageElementHelpers for JSRef<'a, HTMLImageElement> {
|
||||
fn get_url(&self) -> Option<Url>{
|
||||
self.image.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
trait PrivateHTMLImageElementHelpers {
|
||||
fn update_image(self, value: Option<(DOMString, &Url)>);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
enum CanvasWindingRule { "nonzero", "evenodd" };
|
||||
|
||||
// http://www.whatwg.org/html/#2dcontext
|
||||
typedef (/* HTMLImageElement or
|
||||
HTMLVideoElement or */
|
||||
typedef (HTMLImageElement or
|
||||
/* HTMLVideoElement or */
|
||||
HTMLCanvasElement or
|
||||
CanvasRenderingContext2D /* or
|
||||
ImageBitmap */) CanvasImageSource;
|
||||
|
|
|
@ -39,6 +39,7 @@ extern crate js;
|
|||
extern crate libc;
|
||||
extern crate msg;
|
||||
extern crate net_traits;
|
||||
extern crate png;
|
||||
extern crate "rustc-serialize" as rustc_serialize;
|
||||
extern crate time;
|
||||
extern crate canvas;
|
||||
|
|
|
@ -803,6 +803,7 @@ dependencies = [
|
|||
"msg 0.0.1",
|
||||
"net_traits 0.0.1",
|
||||
"plugins 0.0.1",
|
||||
"png 0.1.0 (git+https://github.com/servo/rust-png)",
|
||||
"profile 0.0.1",
|
||||
"rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"script_traits 0.0.1",
|
||||
|
|
|
@ -807,6 +807,7 @@ dependencies = [
|
|||
"msg 0.0.1",
|
||||
"net_traits 0.0.1",
|
||||
"plugins 0.0.1",
|
||||
"png 0.1.0 (git+https://github.com/servo/rust-png)",
|
||||
"profile 0.0.1",
|
||||
"rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"script_traits 0.0.1",
|
||||
|
|
|
@ -732,6 +732,7 @@ dependencies = [
|
|||
"msg 0.0.1",
|
||||
"net_traits 0.0.1",
|
||||
"plugins 0.0.1",
|
||||
"png 0.1.0 (git+https://github.com/servo/rust-png)",
|
||||
"profile 0.0.1",
|
||||
"rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"script_traits 0.0.1",
|
||||
|
|
|
@ -171,50 +171,6 @@ function renderExample(title) {
|
|||
return container;
|
||||
}
|
||||
|
||||
function drawImage() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
||||
var div = renderExample('drawImage(' + args.toString() + ')');
|
||||
var canvasEls = div.querySelectorAll('canvas');
|
||||
var canvas1 = canvasEls[0];
|
||||
var canvas2 = canvasEls[1];
|
||||
|
||||
canvas1.width = imageSource.width;
|
||||
canvas1.height = imageSource.height;
|
||||
|
||||
var ctx1 = canvas1.getContext('2d');
|
||||
ctx1.fillStyle = "#00FFFF";
|
||||
ctx1.fillRect(0, 0, imageSource.width, imageSource.height);
|
||||
|
||||
ctx1.fillStyle = "#000000";
|
||||
ctx1.fillRect(5,5,40,40);
|
||||
ctx1.clearRect(10,10,30,30);
|
||||
ctx1.strokeRect(15,15,20,20);
|
||||
|
||||
canvas2.width = destCanvas.width;
|
||||
canvas2.height = destCanvas.height;
|
||||
|
||||
var ctx2 = canvas2.getContext('2d');
|
||||
ctx2.fillStyle = "#FF0000";
|
||||
ctx2.fillRect(0, 0, destCanvas.width, destCanvas.height);
|
||||
ctx2.imageSmoothingEnabled = smoothingEnabled;
|
||||
|
||||
args.unshift(canvas1);
|
||||
try {
|
||||
ctx2.drawImage.apply(ctx2, args);
|
||||
}
|
||||
catch(err) {
|
||||
var title = div.querySelector('.example-title');
|
||||
var error = document.createElement('h2');
|
||||
error.setAttribute('class', 'example-title error');
|
||||
div.insertBefore(error, title);
|
||||
error.textContent += "Call Failed: " + err.message;
|
||||
}
|
||||
|
||||
document.body.appendChild(div);
|
||||
};
|
||||
|
||||
|
||||
function drawImage() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<style type=''>
|
||||
html {
|
||||
font-family: helvetica, sans-serif;
|
||||
}
|
||||
|
||||
canvas, img {
|
||||
margin: 10px;
|
||||
border: 1px solid grey;
|
||||
}
|
||||
|
||||
.example {
|
||||
display: inline-block;
|
||||
width: 280px;
|
||||
margin-top: 50px;
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.description div {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.description .source {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.description .target {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<h1 class="title">DrawImage canvas to canvas</h1>
|
||||
</body>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var smoothingEnabled = false;
|
||||
var examplesNum = 0;
|
||||
|
||||
var imageSource = {
|
||||
width: 100,
|
||||
height: 100
|
||||
};
|
||||
|
||||
var destCanvas = {
|
||||
width: 100,
|
||||
height: 100
|
||||
};
|
||||
|
||||
// 2 arguments, the dest origin is 0,0
|
||||
// The source canvas will copied to the 0,0 position of the destination canvas
|
||||
drawImage(0, 0);
|
||||
// 2 arguments, the dest origin is not 0,0
|
||||
// The source canvas will copied to the 25, 25 position of the destination canvas
|
||||
drawImage(25, 25);
|
||||
// 4 arguments, the source origin is not 0,0, the dest size is provided
|
||||
// The source canvas will copied to the 50, 50 position of the destination canvas and
|
||||
// on an area of 50x50 pixels
|
||||
drawImage(50, 50, 50, 50);
|
||||
// 4 arguments, the dest origin is not 0,0 and the dest size is provided but
|
||||
// does not match the size of the source. The image will be distorted
|
||||
// The source canvas will copied to the 50,50 position of the destination canvas
|
||||
// and it will be shrunk to a and area of 25x25
|
||||
drawImage(50, 50, 25, 25);
|
||||
// The source canvas will copied to the 50,50 position of the destination canvas
|
||||
// over an area of 50x25 pixels
|
||||
// The copied image will be distorted along the x axis
|
||||
drawImage(50, 50, 50, 25);
|
||||
// 8 arguments, both destination and source origins are 0, 0
|
||||
// An area of 25x25 pixels of the source image will be copied to
|
||||
// an area of 25x25 pixels of the destination canvas
|
||||
drawImage(0, 0, 25, 25, 0, 0, 25, 25);
|
||||
// 8 arguments the destination origin is not 0,0
|
||||
// An area of 25x25 pixels of the source image will be copied to
|
||||
// an area of 25x25 pixels of the destination canvas in the position 25,25
|
||||
drawImage(0, 0, 25, 25, 25, 25, 25, 25);
|
||||
// The source rectangle overflows the source image
|
||||
// The source area is clipped to fit the source image
|
||||
// and the destination are is clipped in the same proportion
|
||||
drawImage(25, 25, 50, 50, 0, 0, 50, 50);
|
||||
// The destination rectangle has negative width and height
|
||||
// An exception is raised and nothing is drawn
|
||||
drawImage(25, 25, 50, 50, 0, 0, -100, -100);
|
||||
// The destination rectangle is larger thant the destination canvas
|
||||
// When the destination rectangle is outside the destination image (the scratch bitmap), the pixels
|
||||
// that land outside the scratch bitmap are discarded, as if the destination was an infinite canvas
|
||||
// whose rendering was clipped to the dimensions of the scratch bitmap.
|
||||
drawImage(0, 0, 50, 50, 0, 0, 200, 200);
|
||||
// The source rectangle is larger than the source canvas
|
||||
// The source area is clipped to fit the source image
|
||||
// and the destination are is clipped in the same proportion
|
||||
drawImage(0, 0, 100, 100, 0, 0, 50, 50);
|
||||
// Negative coorditanes of the source rectangle
|
||||
// The source area is clipped to fit the source image
|
||||
// and the destination are is clipped in the same proportion
|
||||
drawImage(-25, -25, 50, 50, 0, 0, 50, 50);
|
||||
|
||||
// Example with non squared origin and source canvases
|
||||
imageSource = {
|
||||
width: 100,
|
||||
height: 50
|
||||
};
|
||||
|
||||
destCanvas = {
|
||||
width: 100,
|
||||
height: 50
|
||||
};
|
||||
|
||||
//drawImage(0, 0);
|
||||
|
||||
function renderExample(title) {
|
||||
var container = document.createElement('div');
|
||||
container.id = 'example' + examplesNum++;
|
||||
container.setAttribute('class', 'example');
|
||||
|
||||
var h2 = document.createElement('h2');
|
||||
h2.textContent = title;
|
||||
h2.setAttribute('class', 'example-title');
|
||||
container.appendChild(h2);
|
||||
|
||||
var div1 = document.createElement('div');
|
||||
var canvas1 = document.createElement('canvas');
|
||||
var img = document.createElement('img');
|
||||
img.src = 'rust-0.png';
|
||||
div1.appendChild(img);
|
||||
div1.appendChild(canvas1);
|
||||
container.appendChild(div1);
|
||||
|
||||
var div2 = document.createElement('div');
|
||||
div2.setAttribute('class', 'description');
|
||||
var source = document.createElement('div');
|
||||
source.textContent = ' Source (' + imageSource.width + ',' + imageSource.height + ')';
|
||||
source.setAttribute('class', 'source');
|
||||
|
||||
var arrow = document.createElement('div');
|
||||
arrow.textContent = ' -> ';
|
||||
arrow.setAttribute('class', 'arrow');
|
||||
|
||||
var target = document.createElement('div');
|
||||
target.textContent = 'Target (' + destCanvas.width + ',' + destCanvas.height + ')';
|
||||
target.setAttribute('class', 'target');
|
||||
|
||||
div2.appendChild(source);
|
||||
div2.appendChild(arrow);
|
||||
div2.appendChild(target);
|
||||
container.appendChild(div2);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function drawImage() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
||||
var div = renderExample('drawImage(' + args.toString() + ')');
|
||||
var canvas = div.querySelectorAll('canvas')[0];
|
||||
var img = div.querySelectorAll('img')[0];
|
||||
img.width = imageSource.width;
|
||||
img.height = imageSource.height;
|
||||
|
||||
canvas.width = destCanvas.width;
|
||||
canvas.height = destCanvas.height;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = "#FF0000";
|
||||
ctx.fillRect(0, 0, destCanvas.width, destCanvas.height);
|
||||
ctx.imageSmoothingEnabled = smoothingEnabled;
|
||||
|
||||
args.unshift(img);
|
||||
try {
|
||||
ctx.drawImage.apply(ctx, args);
|
||||
}
|
||||
catch(err) {
|
||||
var title = div.querySelector('.example-title');
|
||||
var error = document.createElement('h2');
|
||||
error.setAttribute('class', 'example-title error');
|
||||
div.insertBefore(error, title);
|
||||
error.textContent += "Call Failed: " + err.message;
|
||||
}
|
||||
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче