diff --git a/webrender/res/ps_image.fs.glsl b/webrender/res/ps_image.fs.glsl index f21a7c2e55..64619314c8 100644 --- a/webrender/res/ps_image.fs.glsl +++ b/webrender/res/ps_image.fs.glsl @@ -31,5 +31,5 @@ void main(void) { alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize)))); - oFragColor = vec4(1.0, 1.0, 1.0, alpha) * textureLod(sColor0, st, 0.0); + oFragColor = vec4(alpha) * textureLod(sColor0, st, 0.0); } diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 194d88749e..b29d9eddc8 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -1947,7 +1947,7 @@ impl Device { } pub fn set_blend_mode_premultiplied_alpha(&self) { - gl::blend_func(gl::SRC_ALPHA, gl::ZERO); + gl::blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA); gl::blend_equation(gl::FUNC_ADD); } diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 05ecad61c1..49da3d3d81 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -79,6 +79,7 @@ const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag { label: "Blur", color: debug_ pub enum BlendMode { None, Alpha, + PremultipliedAlpha, // Use the color of the text itself as a constant color blend factor. Subpixel(ColorF), @@ -1178,7 +1179,9 @@ impl Renderer { target_dimensions: DeviceUintSize) { let transform_kind = batch.key.flags.transform_kind(); let needs_clipping = batch.key.flags.needs_clipping(); - debug_assert!(!needs_clipping || batch.key.blend_mode == BlendMode::Alpha); + debug_assert!(!needs_clipping || + batch.key.blend_mode == BlendMode::Alpha || + batch.key.blend_mode == BlendMode::PremultipliedAlpha); match batch.data { PrimitiveBatchData::Instances(ref data) => { @@ -1203,7 +1206,7 @@ impl Renderer { AlphaBatchKind::TextRun => { let shader = match batch.key.blend_mode { BlendMode::Subpixel(..) => self.ps_text_run_subpixel.get(&mut self.device, transform_kind), - BlendMode::Alpha | BlendMode::None => self.ps_text_run.get(&mut self.device, transform_kind), + BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::None => self.ps_text_run.get(&mut self.device, transform_kind), }; (GPU_TAG_PRIM_TEXT_RUN, shader) } @@ -1505,6 +1508,10 @@ impl Renderer { self.device.set_blend(true); self.device.set_blend_mode_alpha(); } + BlendMode::PremultipliedAlpha => { + self.device.set_blend(true); + self.device.set_blend_mode_premultiplied_alpha(); + } BlendMode::Subpixel(color) => { self.device.set_blend(true); self.device.set_blend_mode_subpixel(color); diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index f25a743ff1..3138755320 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -128,6 +128,13 @@ impl AlphaBatchHelpers for PrimitiveStore { BlendMode::Alpha } } + PrimitiveKind::Image => { + if needs_blending { + BlendMode::PremultipliedAlpha + } else { + BlendMode::None + } + } _ => { if needs_blending { BlendMode::Alpha diff --git a/wrench/src/json_frame_writer.rs b/wrench/src/json_frame_writer.rs index edd79ae9ca..4bfaaf7c96 100644 --- a/wrench/src/json_frame_writer.rs +++ b/wrench/src/json_frame_writer.rs @@ -7,6 +7,7 @@ #![allow(dead_code)] use image::{ColorType, save_buffer}; +use premultiply::unpremultiply; use serde_json; use std::borrow::BorrowMut; use std::collections::HashMap; @@ -141,7 +142,7 @@ impl JsonFrameWriter { // Remove the data to munge it let mut data = self.images.remove(&key).unwrap(); - let bytes = data.bytes.take().unwrap(); + let mut bytes = data.bytes.take().unwrap(); let (path_file, path) = Self::next_rsrc_paths(&self.rsrc_prefix, &mut self.next_rsrc_num, &self.rsrc_base, @@ -159,6 +160,7 @@ impl JsonFrameWriter { } ImageFormat::RGBA8 => { if data.stride == data.width * 4 { + unpremultiply(bytes.as_mut_slice()); save_buffer(&path_file, &bytes, data.width, data.height, ColorType::RGBA(8)).unwrap(); true } else { diff --git a/wrench/src/main.rs b/wrench/src/main.rs index 98669831dd..51f12dba5f 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -31,6 +31,7 @@ mod binary_frame_reader; mod json_frame_writer; mod parse_function; mod png; +mod premultiply; mod reftest; mod scene; mod wrench; diff --git a/wrench/src/premultiply.rs b/wrench/src/premultiply.rs new file mode 100644 index 0000000000..81ae226a54 --- /dev/null +++ b/wrench/src/premultiply.rs @@ -0,0 +1,51 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// These are slow. Gecko's gfx/2d/Swizzle.cpp has better versions +pub fn premultiply(data: &mut [u8]) { + for pixel in data.chunks_mut(4) { + let a = pixel[3] as u32; + let r = pixel[2] as u32; + let g = pixel[1] as u32; + let b = pixel[0] as u32; + + pixel[3] = a as u8; + pixel[2] = ((r * a + 128) / 255) as u8; + pixel[1] = ((g * a + 128) / 255) as u8; + pixel[0] = ((b * a + 128) / 255) as u8; + } +} + +pub fn unpremultiply(data: &mut [u8]) { + for pixel in data.chunks_mut(4) { + let a = pixel[3] as u32; + let mut r = pixel[2] as u32; + let mut g = pixel[1] as u32; + let mut b = pixel[0] as u32; + + if a > 0 { + r = r * 255 / a; + g = g * 255 / a; + b = b * 255 / a; + } + + pixel[3] = a as u8; + pixel[2] = r as u8; + pixel[1] = g as u8; + pixel[0] = b as u8; + } +} + +#[test] +fn it_works() { + let mut f = [0xff, 0xff, 0xff, 0x80, 0x00, 0xff, 0x00, 0x80]; + premultiply(&mut f); + println!("{:?}", f); + assert!(f[0] == 0x80 && f[1] == 0x80 && f[2] == 0x80 && f[3] == 0x80 && + f[4] == 0x00 && f[5] == 0x80 && f[6] == 0x00 && f[7] == 0x80); + unpremultiply(&mut f); + println!("{:?}", f); + assert!(f[0] == 0xff && f[1] == 0xff && f[2] == 0xff && f[3] == 0x80 && + f[4] == 0x00 && f[5] == 0xff && f[6] == 0x00 && f[7] == 0x80); +} diff --git a/wrench/src/wrench.rs b/wrench/src/wrench.rs index 4f55eebe25..ec5dd0f94b 100644 --- a/wrench/src/wrench.rs +++ b/wrench/src/wrench.rs @@ -16,6 +16,7 @@ use image; use image::GenericImage; use json_frame_writer::JsonFrameWriter; use parse_function::parse_function; +use premultiply::premultiply; use std::collections::HashMap; use std::path::{Path, PathBuf}; use time; @@ -302,7 +303,10 @@ impl Wrench { image::ImageRgba8(_) => ImageFormat::RGBA8, _ => panic!("We don't support whatever your crazy image type is, come on"), }; - let bytes = image.raw_pixels(); + let mut bytes = image.raw_pixels(); + if format == ImageFormat::RGBA8 { + premultiply(bytes.as_mut_slice()); + } let descriptor = ImageDescriptor::new(image_dims.0, image_dims.1, format, diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index d4f9e67a4e..0b6303e099 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -6,6 +6,7 @@ extern crate yaml_rust; use euclid::{TypedMatrix4D, TypedPoint2D, TypedRect, TypedSize2D}; use image::{ColorType, save_buffer}; +use premultiply::unpremultiply; use scene::Scene; use std::borrow::BorrowMut; use std::collections::HashMap; @@ -410,7 +411,7 @@ impl YamlFrameWriter { // Remove the data to munge it let mut data = self.images.remove(&key).unwrap(); - let bytes = data.bytes.take().unwrap(); + let mut bytes = data.bytes.take().unwrap(); let (path_file, path) = Self::next_rsrc_paths(&self.rsrc_prefix, &mut self.next_rsrc_num, &self.rsrc_base, @@ -436,13 +437,19 @@ impl YamlFrameWriter { }; if data.stride == data.width * bpp { + if data.format == ImageFormat::RGBA8 { + unpremultiply(bytes.as_mut_slice()); + } save_buffer(&path_file, &bytes, data.width, data.height, color_type).unwrap(); } else { // takes a buffer with a stride and copies it into a new buffer that has stride == width assert!(data.stride > data.width * bpp); - let tmp: Vec<_> = bytes[..].chunks(data.stride as usize) - .flat_map(|chunk| chunk[..(data.width * bpp) as usize].iter().cloned()) - .collect(); + let mut tmp: Vec<_> = bytes[..].chunks(data.stride as usize) + .flat_map(|chunk| chunk[..(data.width * bpp) as usize].iter().cloned()) + .collect(); + if data.format == ImageFormat::RGBA8 { + unpremultiply(tmp.as_mut_slice()); + } save_buffer(&path_file, &tmp, data.width, data.height, color_type).unwrap(); }