3

I'm searching for a method to properly scale (almost) all of a JFrame's content. All the solutions I have tried so far had a huge lack in rendering speed. What I am looking for is a speed similar to what you have when scaling content on your smartphone.

The JFrame's content should be rescalable quickly and stay scaled even if you overdraw the JFrame with new content. It should also be flexible enough so it let's you choose which BufferedImage's (which is essentially the only type I'm drawing, I don't draw any other "shapes") to redraw. I'm drawing using an ordinary Graphics, resp. Graphics2D object.

What I've tried before is the Graphic2D's scale-method and using an AffineTransformat object to scale each BufferedImage individually:

    g.scale(scalingFactorX, scalingFactorY);

or alternatively:

    BufferedImage img = someImageToScale();
    AffineTransform scaleTransform = AffineTransform.getScaleInstance(scalingFactorX, scalingFactorY);
    AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform,
            AffineTransformOp.TYPE_NEAREST_NEIGHBOR);

    return bilinearScaleOp.filter(img, new BufferedImage(targetWidth, targetHeight,
            BufferedImage.TYPE_INT_ARGB));

where scalingFactorX/Y are the factors the content should be scaled by and targetWidth, resp. targetHeight denote the resulting (scaled) dimensions of the BufferedImage.

Both approaches are rather slow which seems to be because in both cases, each frame, the scaled version of the contents have to be recalculated. I feel like I'm missing something very obvious here.

SaphirShroom
  • 90
  • 1
  • 8
  • Welcome to Stack Overflow, try posting an [MCVE](http://stackoverflow.com/help/mcve) instead of pieces of code, so people are more able to help you, faster and easier. As you know we are voluntiers, we spend time helping, so the easier you make it for us to copy-paste code and see the same issue as you w/o styles such as colors or fonts (in case the problem isn't related to it), is better for us and for you. Help us to help you. And btw you can also take the [tour](http://stackoverflow.com/tour) to improve even more your questions. – Frakcool Sep 17 '15 at 16:08
  • Not sure I fully understand the requirements, but I don't think you need to transform the `BufferedImage`. Just specify the target width and height directly in `g.drawImage()`, like in http://stackoverflow.com/questions/13038411/how-to-fit-image-size-to-jframe-size – Eric Leibenguth Sep 17 '15 at 16:14
  • @EricLeibenguth I know what you mean, but that unfortunately has the same performance issue as my second attempt. I will try to get a full code example going. – SaphirShroom Sep 17 '15 at 16:22
  • I don't think there's a way around recalculating each pixel when you rescale an image. Perhaps you can optimize this part with hardware acceleration (see [this post](http://stackoverflow.com/questions/4627320/java-hardware-acceleration)). But is performance so bad? How can you tell exactly? How large are your images? – Eric Leibenguth Sep 17 '15 at 16:32
  • @EricLeibenguth Thank you, I'll take a look at the link. Chances are high I'll have to use hardware acceleration. My images are only 32x32 but there are a large amount of them (~500). That might screw the processor over pretty badly. I can tell because the framerate drops to like 15-20 FPS when before it is around 100 FPS. – SaphirShroom Sep 17 '15 at 16:39
  • Hum, that's pretty small (~6MB in memory)... Stupid question: are you sure the images aren't reloaded from disk at each paint()? – Eric Leibenguth Sep 17 '15 at 16:48
  • @EricLeibenguth Yes, images are in the main memory as long as the program is running. Couldn't scaling dramatically slow the program down though? I think there are probably ~500,000 pixels the CPU has to worry about each frame. Although, my benchmarking tells me the drawing (including scaling) of the Frame only takes about 40ms each redraw. There might be some sideeffects to scaling that are slowing my program down, I'll take a close look. – SaphirShroom Sep 17 '15 at 17:15
  • Have a look at this: http://stackoverflow.com/questions/2944442/zoom-in-java-swing-application – durron597 Sep 17 '15 at 18:19

1 Answers1

4

While drawImage() can resample an image, it must must do so each time paintComponent() is called. As suggested here, AffineTransformOp lets you choose the interpolation type; use the fastest one that meets your image quality requirements. Once you've satisfactorily resampled an image, ensure that you do not inadvertently resample the image a second time when rendering in a particular Graphics context. Override getPreferredSize() to make the destination component the same size as the resampled image, as shown in these examples.

My images are only 32x32, but there are a large amount of them (~500).

Consider using the flyweight pattern to render only visible images. JTable and JList are examples, as outlined here.

Are there performance benefits or other advantages to using separate components?

You may need to prototype and profile to be sure, perhaps by comparing representative examples:

  • Typical component-based examples include the button-based game cited here, or the chess games examined here, here and here.

  • The tile-based game cited here illustrates a single tile panel and multiple accessory panels that all listen to a common game model.

Community
  • 1
  • 1
trashgod
  • 196,350
  • 25
  • 213
  • 918
  • Thank you for your answer, I figured I'd probably have to do something like storing the images somewhere. I haven't looked at the flyweight pattern yet, I'll probably do that tomorrow, however, my game is tile-based so normally all 500 images are completely visible. I do have a question regarding your suggestion of overriding the preferredSize of the destination component though. As it is right now, I just have a single JFrame covering the whole screen. Are there performance benefits or other advantages to using separate components? – SaphirShroom Sep 17 '15 at 20:43
  • @SaphirShroom: There's no easy answer, but I've elaborated above. – trashgod Sep 17 '15 at 23:43