2

I am trying to draw an image in a JavaFX Canvas. However, the conventional drawImage() method of GraphicsContext seems to produce some sort of blurred or aliased results. Probably because I am using a Retina MacBook Pro.

I found a solution here: https://stackoverflow.com/a/26706028/555690

public class ImageRenderer {
   public void render(GraphicsContext context, Image image, int sx, int sy, int sw, int sh, int tx, int ty) {
      PixelReader reader = image.getPixelReader();
      PixelWriter writer = context.getPixelWriter();
      for (int x = 0; x < sw; x++) {
         for (int y = 0; y < sh; y++) {
            Color color = reader.getColor(sx + x, sy + y);
            if (color.isOpaque()) {
              writer.setColor(tx + x, ty + y, color);
            }
         }
      }
   }
}

It works, but sometimes I need to draw the target rect with a particular size (so I need to add parameters for int tw and int th), but I don't know how to adjust the logic of the above method to make use of them.

How can I draw images into JavaFX Canvas without blur/aliasing in a given area?

Community
  • 1
  • 1
Saturn
  • 17,888
  • 49
  • 145
  • 271

1 Answers1

0

I also have a MacBook Pro Retina and I had the same problem but if you do it right, the images that you draw on a canvas are not blurred.

What you have to keep in mind is that the Retina Mac has a pixel scale factor of 2. So if you ask for a canvas of say 1000x1000 you will get a canvas which has a size of 1000x1000 raster units or virtual pixels but internally it is backed by an image of 2000x2000 pixels. Now assume you have an image with a size of 400x400 pixels and you want to display that in the upper left corner of your canvas without blurring. You then have to use the drawImage function in the following way:

gc.drawImage(img, 0, 0, 400, 400, 0, 0, 200, 200);

Another scenario might be that you want to fill the upper quarter of the canvas with an image which is an area of 500x500 raster units. That means you will have to provide an image with 1000x1000 pixels and place it at

gc.drawImage(img, 0, 0, 1000, 1000, 0, 0, 500, 500);

I hope this helps to understand the issue.

mipa
  • 10,369
  • 2
  • 16
  • 35
  • But if I want to draw a 400x400 image in a 400x400 area, and I use the method `gc.drawImage(img, 0, 0, 400, 400, 0, 0, 200, 200);`, visually speaking, won't the image cover 200x200 pixels? That'd be a problem as visually I want it to be 400x400. I imagine a solution would be to have a 800x800 version of the image to draw it in a 400x400 space, but the problem is that my users are the ones who provide the image, and it's not easy to ask them for a 800x800 version if they can only offer 400x400 images. – Saturn Oct 28 '15 at 15:40
  • No, you are still confusing raster units with pixels. On a MacBook Pro Retina 1 raster unit covers an area of 2x2 pixels. If you have a Retina display you either have to provide HiDPI content or you have to upscale your content if you want to see it at a specific size. Upscaling leads to blurring of course if you compare it to other HiDPI content. There is no way arround this. – mipa Oct 28 '15 at 17:28
  • When I draw a 400x400 image in a 200x200 area, it *looks* 200x200 instead of 400x400. – Saturn Oct 28 '15 at 17:34
  • Yes of course because 400px x 400px correspond to 200 ru x 200 ru. If you want the same image to be displayed at a size of 400 ru x 400 ru it has to be upscaled to 800 px x 800 px and that is what the canvas does internally. However the result is a blurry image. – mipa Oct 28 '15 at 17:41
  • So I do have to ask my users to provide images twice as large? – Saturn Oct 28 '15 at 17:45
  • Yes if you cannot live with either small or blurry images. That's also the reason why map tile servers deliver normal map tiles and generally twice as large retina map tiles. – mipa Oct 28 '15 at 17:54