mirror of
https://github.com/servo/servo.git
synced 2025-07-24 07:40:27 +01:00
Share some code between 2D canvas and WebGL
This commit is contained in:
parent
05ef233097
commit
6c469b90b1
6 changed files with 58 additions and 84 deletions
|
@ -6,7 +6,6 @@ use canvas_traits::webgl::*;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
use ipc_channel::ipc::IpcBytesSender;
|
|
||||||
use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
|
use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
|
||||||
use pixels;
|
use pixels;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -791,8 +790,16 @@ impl WebGLImpl {
|
||||||
ctx.gl().pixel_store_i(name, val),
|
ctx.gl().pixel_store_i(name, val),
|
||||||
WebGLCommand::PolygonOffset(factor, units) =>
|
WebGLCommand::PolygonOffset(factor, units) =>
|
||||||
ctx.gl().polygon_offset(factor, units),
|
ctx.gl().polygon_offset(factor, units),
|
||||||
WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, ref chan) => {
|
WebGLCommand::ReadPixels(rect, format, pixel_type, ref sender) => {
|
||||||
Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan)
|
let pixels = ctx.gl().read_pixels(
|
||||||
|
rect.origin.x as i32,
|
||||||
|
rect.origin.y as i32,
|
||||||
|
rect.size.width as i32,
|
||||||
|
rect.size.height as i32,
|
||||||
|
format,
|
||||||
|
pixel_type,
|
||||||
|
);
|
||||||
|
sender.send(&pixels).unwrap();
|
||||||
}
|
}
|
||||||
WebGLCommand::RenderbufferStorage(target, format, width, height) =>
|
WebGLCommand::RenderbufferStorage(target, format, width, height) =>
|
||||||
ctx.gl().renderbuffer_storage(target, format, width, height),
|
ctx.gl().renderbuffer_storage(target, format, width, height),
|
||||||
|
@ -1335,20 +1342,6 @@ impl WebGLImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_pixels(
|
|
||||||
gl: &gl::Gl,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
format: u32,
|
|
||||||
pixel_type: u32,
|
|
||||||
chan: &IpcBytesSender,
|
|
||||||
) {
|
|
||||||
let result = gl.read_pixels(x, y, width, height, format, pixel_type);
|
|
||||||
chan.send(&result).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(gl: &gl::Gl, chan: &WebGLSender<()>) {
|
fn finish(gl: &gl::Gl, chan: &WebGLSender<()>) {
|
||||||
gl.finish();
|
gl.finish();
|
||||||
chan.send(()).unwrap();
|
chan.send(()).unwrap();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use euclid::Size2D;
|
use euclid::{Rect, Size2D};
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender};
|
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender};
|
||||||
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
||||||
|
@ -227,7 +227,7 @@ pub enum WebGLCommand {
|
||||||
GetRenderbufferParameter(u32, u32, WebGLSender<i32>),
|
GetRenderbufferParameter(u32, u32, WebGLSender<i32>),
|
||||||
PolygonOffset(f32, f32),
|
PolygonOffset(f32, f32),
|
||||||
RenderbufferStorage(u32, u32, i32, i32),
|
RenderbufferStorage(u32, u32, i32, i32),
|
||||||
ReadPixels(i32, i32, i32, i32, u32, u32, IpcBytesSender),
|
ReadPixels(Rect<u32>, u32, u32, IpcBytesSender),
|
||||||
SampleCoverage(f32, bool),
|
SampleCoverage(f32, bool),
|
||||||
Scissor(i32, i32, u32, u32),
|
Scissor(i32, i32, u32, u32),
|
||||||
StencilFunc(u32, i32, u32),
|
StencilFunc(u32, i32, u32),
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
extern crate euclid;
|
extern crate euclid;
|
||||||
|
|
||||||
use euclid::{Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub fn get_rect(pixels: &[u8], size: Size2D<u32>, rect: Rect<u32>) -> Cow<[u8]> {
|
pub fn get_rect(pixels: &[u8], size: Size2D<u32>, rect: Rect<u32>) -> Cow<[u8]> {
|
||||||
|
@ -63,3 +63,21 @@ pub fn premultiply_inplace(pixels: &mut [u8]) -> bool {
|
||||||
pub fn multiply_u8_color(a: u8, b: u8) -> u8 {
|
pub fn multiply_u8_color(a: u8, b: u8) -> u8 {
|
||||||
return (a as u32 * b as u32 / 255) as u8;
|
return (a as u32 * b as u32 / 255) as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clip(
|
||||||
|
mut origin: Point2D<i32>,
|
||||||
|
mut size: Size2D<u32>,
|
||||||
|
surface: Size2D<u32>,
|
||||||
|
) -> Option<Rect<u32>> {
|
||||||
|
if origin.x < 0 {
|
||||||
|
size.width = size.width.saturating_sub(-origin.x as u32);
|
||||||
|
origin.x = 0;
|
||||||
|
}
|
||||||
|
if origin.y < 0 {
|
||||||
|
size.height = size.height.saturating_sub(-origin.y as u32);
|
||||||
|
origin.y = 0;
|
||||||
|
}
|
||||||
|
Rect::new(origin.to_u32(), size)
|
||||||
|
.intersection(&Rect::from_size(surface))
|
||||||
|
.filter(|rect| !rect.is_empty())
|
||||||
|
}
|
||||||
|
|
|
@ -1168,7 +1168,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
// FIXME(nox): This is probably wrong when this is a context for an
|
// FIXME(nox): This is probably wrong when this is a context for an
|
||||||
// offscreen canvas.
|
// offscreen canvas.
|
||||||
let canvas_size = self.canvas.as_ref().map_or(Size2D::zero(), |c| c.get_size());
|
let canvas_size = self.canvas.as_ref().map_or(Size2D::zero(), |c| c.get_size());
|
||||||
let read_rect = match clip(origin, size, canvas_size) {
|
let read_rect = match pixels::clip(origin, size, canvas_size) {
|
||||||
Some(rect) => rect,
|
Some(rect) => rect,
|
||||||
None => {
|
None => {
|
||||||
// All the pixels are outside the canvas surface.
|
// All the pixels are outside the canvas surface.
|
||||||
|
@ -1220,7 +1220,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
Point2D::new(dirty_x, dirty_y),
|
Point2D::new(dirty_x, dirty_y),
|
||||||
Size2D::new(dirty_width, dirty_height),
|
Size2D::new(dirty_width, dirty_height),
|
||||||
);
|
);
|
||||||
let src_rect = match clip(src_origin, src_size, imagedata_size) {
|
let src_rect = match pixels::clip(src_origin, src_size, imagedata_size) {
|
||||||
Some(rect) => rect,
|
Some(rect) => rect,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
@ -1230,7 +1230,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
);
|
);
|
||||||
// By clipping to the canvas surface, we avoid sending any pixel
|
// By clipping to the canvas surface, we avoid sending any pixel
|
||||||
// that would fall outside it.
|
// that would fall outside it.
|
||||||
let dst_rect = match clip(dst_origin, src_rect.size, canvas_size) {
|
let dst_rect = match pixels::clip(dst_origin, src_rect.size, canvas_size) {
|
||||||
Some(rect) => rect,
|
Some(rect) => rect,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
@ -1537,21 +1537,3 @@ fn adjust_size_sign(
|
||||||
}
|
}
|
||||||
(origin, size.to_u32())
|
(origin, size.to_u32())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clip(
|
|
||||||
mut origin: Point2D<i32>,
|
|
||||||
mut size: Size2D<u32>,
|
|
||||||
surface: Size2D<u32>,
|
|
||||||
) -> Option<Rect<u32>> {
|
|
||||||
if origin.x < 0 {
|
|
||||||
size.width = size.width.saturating_sub(-origin.x as u32);
|
|
||||||
origin.x = 0;
|
|
||||||
}
|
|
||||||
if origin.y < 0 {
|
|
||||||
size.height = size.height.saturating_sub(-origin.y as u32);
|
|
||||||
origin.y = 0;
|
|
||||||
}
|
|
||||||
Rect::new(origin.to_u32(), size)
|
|
||||||
.intersection(&Rect::from_size(surface))
|
|
||||||
.filter(|rect| !rect.is_empty())
|
|
||||||
}
|
|
||||||
|
|
|
@ -368,13 +368,13 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement {
|
||||||
context.get_rect(Rect::from_size(self.get_size()))
|
context.get_rect(Rect::from_size(self.get_size()))
|
||||||
},
|
},
|
||||||
Some(CanvasContext::WebGL(ref context)) => {
|
Some(CanvasContext::WebGL(ref context)) => {
|
||||||
match context.get_image_data(self.Width(), self.Height()) {
|
match context.get_image_data(self.get_size()) {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return Ok(USVString("data:,".into())),
|
None => return Ok(USVString("data:,".into())),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(CanvasContext::WebGL2(ref context)) => {
|
Some(CanvasContext::WebGL2(ref context)) => {
|
||||||
match context.base_context().get_image_data(self.Width(), self.Height()) {
|
match context.base_context().get_image_data(self.get_size()) {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return Ok(USVString("data:,".into())),
|
None => return Ok(USVString("data:,".into())),
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ use dom::webgluniformlocation::WebGLUniformLocation;
|
||||||
use dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
|
use dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::Size2D;
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use half::f16;
|
use half::f16;
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use js::jsapi::{JSContext, JSObject, Type};
|
use js::jsapi::{JSContext, JSObject, Type};
|
||||||
|
@ -1070,7 +1070,7 @@ impl WebGLRenderingContext {
|
||||||
// can fail and that it is UB what happens in that case.
|
// can fail and that it is UB what happens in that case.
|
||||||
//
|
//
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
|
||||||
pub fn get_image_data(&self, width: u32, height: u32) -> Option<Vec<u8>> {
|
pub fn get_image_data(&self, mut size: Size2D<u32>) -> Option<Vec<u8>> {
|
||||||
handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
|
handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
|
||||||
|
|
||||||
let (fb_width, fb_height) = handle_potential_webgl_error!(
|
let (fb_width, fb_height) = handle_potential_webgl_error!(
|
||||||
|
@ -1078,15 +1078,12 @@ impl WebGLRenderingContext {
|
||||||
self.get_current_framebuffer_size().ok_or(InvalidOperation),
|
self.get_current_framebuffer_size().ok_or(InvalidOperation),
|
||||||
return None
|
return None
|
||||||
);
|
);
|
||||||
let width = cmp::min(width, fb_width as u32);
|
size.width = cmp::min(size.width, fb_width as u32);
|
||||||
let height = cmp::min(height, fb_height as u32);
|
size.height = cmp::min(size.height, fb_height as u32);
|
||||||
|
|
||||||
let (sender, receiver) = ipc::bytes_channel().unwrap();
|
let (sender, receiver) = ipc::bytes_channel().unwrap();
|
||||||
self.send_command(WebGLCommand::ReadPixels(
|
self.send_command(WebGLCommand::ReadPixels(
|
||||||
0,
|
Rect::from_size(size),
|
||||||
0,
|
|
||||||
width as i32,
|
|
||||||
height as i32,
|
|
||||||
constants::RGBA,
|
constants::RGBA,
|
||||||
constants::UNSIGNED_BYTE,
|
constants::UNSIGNED_BYTE,
|
||||||
sender,
|
sender,
|
||||||
|
@ -2879,45 +2876,29 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
return self.webgl_error(InvalidOperation);
|
return self.webgl_error(InvalidOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut src_x = x;
|
let src_origin = Point2D::new(x, y);
|
||||||
let mut src_y = y;
|
let src_size = Size2D::new(width as u32, height as u32);
|
||||||
let mut src_width = width;
|
let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
|
||||||
let mut src_height = height;
|
let src_rect = match pixels::clip(src_origin, src_size, fb_size) {
|
||||||
|
Some(rect) => rect,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
let mut dest_offset = 0;
|
let mut dest_offset = 0;
|
||||||
|
if x < 0 {
|
||||||
if src_x < 0 {
|
dest_offset += -x * bytes_per_pixel;
|
||||||
if src_width <= -src_x {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
dest_offset += bytes_per_pixel * -src_x;
|
if y < 0 {
|
||||||
src_width += src_x;
|
dest_offset += -y * row_len;
|
||||||
src_x = 0;
|
|
||||||
}
|
|
||||||
if src_y < 0 {
|
|
||||||
if src_height <= -src_y {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dest_offset += row_len * -src_y;
|
|
||||||
src_height += src_y;
|
|
||||||
src_y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if src_x + src_width > fb_width {
|
|
||||||
src_width = fb_width - src_x;
|
|
||||||
}
|
|
||||||
if src_y + src_height > fb_height {
|
|
||||||
src_height = fb_height - src_y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sender, receiver) = ipc::bytes_channel().unwrap();
|
let (sender, receiver) = ipc::bytes_channel().unwrap();
|
||||||
self.send_command(WebGLCommand::ReadPixels(
|
self.send_command(WebGLCommand::ReadPixels(src_rect, format, pixel_type, sender));
|
||||||
src_x, src_y, src_width, src_height, format, pixel_type, sender,
|
|
||||||
));
|
|
||||||
|
|
||||||
let src = receiver.recv().unwrap();
|
let src = receiver.recv().unwrap();
|
||||||
let src_row_len = (src_width * bytes_per_pixel) as usize;
|
|
||||||
for i in 0..src_height {
|
let src_row_len = src_rect.size.width as usize * bytes_per_pixel as usize;
|
||||||
let dest_start = (dest_offset + i * dest_stride) as usize;
|
for i in 0..src_rect.size.height {
|
||||||
|
let dest_start = dest_offset as usize + i as usize * dest_stride as usize;
|
||||||
let dest_end = dest_start + src_row_len;
|
let dest_end = dest_start + src_row_len;
|
||||||
let src_start = i as usize * src_row_len;
|
let src_start = i as usize * src_row_len;
|
||||||
let src_end = src_start + src_row_len;
|
let src_end = src_start + src_row_len;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue