-
Notifications
You must be signed in to change notification settings - Fork 421
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
The image displayed by the Canvas is corrupted #694
Comments
Looks like something is a bit messed up with pre-multiplied alpha, or lack of it. Possibly related: #301 |
Just tested, this issue is still acute. use ggez::ContextBuilder;
use ggez::conf::Conf;
use ggez::Context;
use ggez::event::{self, EventHandler};
use ggez::GameResult;
use ggez::graphics::{self, Canvas, Color, DrawParam, Image};
use mint::Point2;
struct Application {
dark_image: Image,
dark_canvas: Canvas,
light_image: Image,
light_canvas: Canvas,
canvas_image: Image
}
impl Application {
fn new(ctx: &mut Context) -> Self {
let (dark_image, dark_canvas) = Self::image_canvas(ctx, "/dark-image.png");
let (light_image, light_canvas) = Self::image_canvas(ctx, "/light-image.png");
let canvas_image = Self::canvas(ctx, &dark_image).to_image(ctx).unwrap();
Self {dark_image, dark_canvas, light_image, light_canvas, canvas_image}
}
fn image_canvas(ctx: &mut Context, file: &str) -> (Image, Canvas) {
let image = Image::new(ctx, file).unwrap();
let canvas = Self::canvas(ctx, &image);
(image, canvas)
}
fn canvas(ctx: &mut Context, image: &Image) -> Canvas {
let canvas = Canvas::with_window_size(ctx).unwrap();
graphics::set_canvas(ctx, Some(&canvas));
graphics::draw(ctx, image, DrawParam::new()).unwrap();
graphics::set_canvas(ctx, None);
canvas
}
}
impl EventHandler for Application {
fn update(&mut self, _: &mut Context) -> GameResult<()> {
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
graphics::clear(ctx, Color{r: 0., g: 0.3, b: 0., a: 1.});
graphics::draw(ctx, &self.dark_image, DrawParam::new()).unwrap();
let point2 = Point2 {x: 0., y: 60.};
graphics::draw(ctx, &self.dark_canvas, DrawParam::new().dest(point2)).unwrap();
let point2 = Point2 {x: 0., y: 200.};
graphics::draw(ctx, &self.light_image, DrawParam::new().dest(point2)).unwrap();
let point2 = Point2 {x: 0., y: 260.};
graphics::draw(ctx, &self.light_canvas, DrawParam::new().dest(point2)).unwrap();
graphics::draw(ctx, &self.canvas_image, DrawParam::new()).unwrap();
graphics::present(ctx).unwrap();
Ok(())
}
}
pub fn main() {
let c = Conf::new();
let (mut ctx, event_loop) = ContextBuilder::new("canvas", "athorus").build().unwrap();
let app = Application::new(&mut ctx);
event::run(ctx, event_loop, app)
} |
Ok two things: One: The
|
https://user-images.githubusercontent.com/17962514/120549217-94738500-c3f3-11eb-9c32-627038f2ec3c.png |
This is really annoying. For a moment I thought "maybe clearing newly created canvases with transparent white could solve this issue", since that would stop the darkening. But sadly that would still corrupt the image by shifting all values towards 1, thereby making it more white, or brighter if you will. The Add and Replace blend modes both work as a workaround in certain cases since they do not corrupt the image in any such way. But these cases do not include those where you want to draw multiple things on top of each other onto a canvas. There you suddenly DO care about whether your blend mode is Add/Replace or really Alpha... |
I guess the best we can really do is add a link to this issue and some explanations to the docs of BlendMode warning users of the behavior of There just aren't any ways to really fix this, are there? |
Ok, I think I've found somewhat of a solution to this: premultiplied alpha Imagine we didn't use the classic alpha blend mode for drawing the canvas here, but instead used the premultiplied alpha blend mode described in #905. When we draw on the canvas using the classic alpha blend mode we generate a premultiplied image. That's just the way this blend mode works. It premultiplies the color channels with the alpha channel before adding it to what's behind. We cannot get around that. But when we now draw this canvas containing the premultiplied (i.e. "corrupted") image onto the screen using the premultiplied alpha mode we get exactly the same result we'd have gotten by drawing all the things directly to the screen using the classic alpha blend mode (that is, if you ignore the minor inconsistency in the calculcation of the alpha value between these two methods for now). So really, this is not necessarily a bug, the image is not "corrupted", it just went from unpremultiplied, to premultiplied when drawn on the canvas. Side Note
I think this is true. Because imagine, if a user started off with PNGs containing premultiplied images already, then there'd be no hassle for him/her at all, because he could just use the premultiplied alpha blend mode all the way, drawing on opaque backgrounds, drawing on transparent backgrounds, it doesn't matter. Therefore I think we should create an example, or a doc page, explaining this concept and how to prepare your sprites for it. |
Just tested with the new devel branch with #905 merged using this code: use ggez::conf::Conf;
use ggez::event::{self, EventHandler};
use ggez::graphics::{self, Canvas, Color, DrawParam, Image, BlendMode};
use ggez::Context;
use ggez::ContextBuilder;
use ggez::GameResult;
use ggez::graphics::Drawable;
use mint::Point2;
use ggez::graphics::BlendMode::Add;
struct Application {
dark_image: Image,
dark_canvas: Canvas,
light_image: Image,
light_canvas: Canvas,
canvas_image: Image,
}
impl Application {
fn new(ctx: &mut Context) -> Self {
let (dark_image, dark_canvas) = Self::image_canvas(ctx, "/dark-image.png");
let (light_image, light_canvas) = Self::image_canvas(ctx, "/light-image.png");
let canvas_image = Self::canvas(ctx, &dark_image).to_image(ctx).unwrap();
Self {
dark_image,
dark_canvas,
light_image,
light_canvas,
canvas_image,
}
}
fn image_canvas(ctx: &mut Context, file: &str) -> (Image, Canvas) {
let mut image = Image::new(ctx, file).unwrap();
let canvas = Self::canvas(ctx, &image);
(image, canvas)
}
fn canvas(ctx: &mut Context, image: &Image) -> Canvas {
let mut canvas = Canvas::with_window_size(ctx).unwrap();
graphics::set_canvas(ctx, Some(&canvas));
graphics::draw(ctx, image, DrawParam::new()).unwrap();
graphics::present(ctx);
graphics::set_canvas(ctx, None);
canvas.set_blend_mode(Some(BlendMode::Premultiplied));
canvas
}
}
impl EventHandler for Application {
fn update(&mut self, _: &mut Context) -> GameResult<()> {
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
graphics::clear(
ctx,
Color {
r: 0.,
g: 0.3,
b: 0.,
a: 1.,
},
);
graphics::draw(ctx, &self.dark_image, DrawParam::new()).unwrap();
let point2 = Point2 { x: 0., y: 60. };
graphics::draw(ctx, &self.dark_canvas, DrawParam::new().dest(point2)).unwrap();
let point2 = Point2 { x: 0., y: 200. };
graphics::draw(ctx, &self.light_image, DrawParam::new().dest(point2)).unwrap();
let point2 = Point2 { x: 0., y: 260. };
graphics::draw(ctx, &self.light_canvas, DrawParam::new().dest(point2)).unwrap();
let point2 = Point2 { x: 0., y: 360. };
graphics::draw(ctx, &self.canvas_image, DrawParam::new().dest(point2)).unwrap();
graphics::present(ctx).unwrap();
Ok(())
}
}
pub fn main() {
let c = Conf::new();
let (mut ctx, event_loop) = ContextBuilder::new("canvas", "athorus").build().unwrap();
let app = Application::new(&mut ctx);
event::run(ctx, event_loop, app)
} Now, using the premultiplied blend mode for canvases it works. Notice that the last image still looks "corrputed" since it's just the image contained in the dark_canvas, but drawn with the default blend mode, which is Alpha and not Premultiplied. |
This being two years old, workaround implemented, and no input from the OP in nearly 2 years I am closing this one. |
Describe the bug
The image displayed at the screen by the canvas is a little bit corrupted. When Image is displayed directly, everything is fine.
To Reproduce
See code below.
File resources.zip is provided too (contains 2 png).
Expected behavior
Canvas should display the same image as the one in input. No corrupted data is expected.
Screenshots or pasted code
If you look closely at the two first dark text lines, you can observe the second line is a little bit corrupted. There are black pixels around text.
With a light image (maybe because the contrast is higher), I cannot see any difference.
Hardware and Software:
The text was updated successfully, but these errors were encountered: