I'm using the JAI library to do adjustments on a series of images, each about 1300x1000 in size. I adjust pixel intensities with "Rescale" and add text and lines with the Graphics2D object from the TiledImage.createGraphics() method. Then I resize the image to fit the screen with "subsampleaverage" and render to screen with Graphics2D.drawRenderedImage(). Up to this point, there is little slowdown, with rendering taking about 40-60 milliseconds.
However, if I only add the text and lines, the display slows down to 100-200 milliseconds. I can't seem to figure out why this is, as adding the text after or before adjust pixel intensities is fine.
I've been searching through the site, but I can't seem to find any concrete answer. Many suggestions have been to use BufferedImages, but converting from PlanarImages to BufferedImages seems to also have a slowdown issue.
Apparently text is still rendered very slowly in Java. The glyphs for each Font object has to be rendered and painted on the Graphics object. With a lot of text on the object, the Font object along with all the used Glyphs are recreated, causing a massive slowdown.
Even using JOGL, there is a significant slowdown. But using the same TextRenderer object alleviates this by creating a single Font object and reusing it as long as the TextRenderer is alive. Of course, this restricts you from using multiple Font objects, as JOGL has yet to implement a setFont function, requiring you to create a new TextRenderer object for each new font, font style, and font weight.
Hope this helps anyone with similar issues.
Related
I am working on an app that has a Swing user interface. Text is being written to the window with a Graphics2D object (specifically SunGraphics2D) using the drawString method.
I have an issue where on high DPI displays some of the text renders very poorly, for example on a 4K monitor set to 150% scale.
The rendering can be improved by using -Dsun.java2d.uiScale=1.0, but this is simply disabling scaling, so everything is tiny.
If I had to guess what was happening, I would say that the graphics is being rendered at 1.0 scale, anti-aliasing is being applied, and then it is being scaled 1.5 times using Nearest Neighbour Interpolation. My reasoning for this is that it appears as though sometimes "anti-aliased pixels" (i.e. for black text, the light grey ones) are sampled for the scaling operation, which causes specific letters to be rendered unusually.
For example, this image shows the string "123" (I have enlarged it by 200% to make the issue more obvious):
As you can see the line that makes up the bottom of the "2" is 1px high, but the anti-aliasing is 2px high.
Another example with the string "555", where the base of the "5" is again very skinny:
Interestingly this doesn't affect all text, for example the text on JButton and JLabel objects renders at the correct scale and looks good:
This issue does also affect images which are rendered within the window, but I am not very familiar with the image rendering classes of this particular application, so I cannot provide more info, other than to say this might point to the Graphics object being the issue.
I have tried to create a minimum example of the issue, but unfortunately when I create a brand new project the text renders perfectly, so it seems that this issue is specific to the application I am working on, which had many thousands of lines of code.
Is there something I should be looking into or trying? Perhaps there is a "Scaling Mode" for the graphics object, which is set to "Nearest Neighbour" or something like that?
I have tried to use a few RenderingHints such as RenderingHints.VALUE_FRACTIONALMETRICS_ON, but that didn't make any difference.
JDK is Eclipse Temurin 11.
Edit:
In case it is helpful, here are the RenderingHints being used:
I create png Images from short pieces of text to be used as floating labels, prompts, speechbubbles, etc in a 3D graphics application. Occasionally some of my text needs to be rotated. Unfortunately the 3D application does not provide rotation for this kind of image. I have it fully working for both rotated and non-rotated text, and even the rotated images are drawn correctly if the text is short, but if I use a longer string, it gets cut off because the BufferedImage is not sized to handle it. Do I need to use trigonometry to size it? I know I could always use a huge rectangle in the BufferedImage, but that seems inelegant.
Note that I only ever create single lines of text. If I need multiple lines occasionally, I use multiple images and stack them, it just works better for me that way.
In LibGDX, I am currently trying to achieve the effect of a pixellated GUI, meaning the buttons are pixellated, like an 8-bit theme.
When I make my GUI elements, such as a TextButton, I am using images that are small, say 34x16 pixels, but I am rendering them on a large resolution like 1920x1080. What is the best way to render such a small image at a high resolution?
My thoughts were to use stage.setViewport(WIDTH,HEIGHT), and set the width and height to a scaled down resolution, so the gui elements wouldn't be so big. This works, however when adding elements things go wrong.
This my stage/table currently, using a scaled down resolution. I am adding widgets using to the table like this:
table.add(playButton);
table.row();
table.add(title);
but as you can see, the table.row() makes a row that is much too large, perhaps a row fit for a larger resolution. Is there any way to change the gap that is made by the row? I would have thought that the gap would be zero, and by using table.add().pad() you could change the gap, but I am not doing any of that.
Any help would be greatly appreciated.
Ninepatch
This is a common issue in all apps. To overcome this, images which can be safely resized in parts are used. they are called ninepatches.
Here is a nice Tutorial about using them in libgdx.
Distance Field Fonts
Although you haven't mentioned it here, you'd also find font sizing (pixellated fonts) as an issue. For that Distance Field Fonts are used.
Hope this helps.
I would say don't worry about scaling them up and making the virtual resolution bigger. If you want to see picture still pixelated when you scale it use Texture filter. For your case you want to use Nearest filter. use it like this:
yourTexture.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
where yourTexture is the texture that you have all your bitmaps and skin elements on. If you are using texturePacker than you can specify the filter before packing too. Or just open the .pack file and on the top you will see filtering options, and edit those to Nearest.
I have a custom report which draws via Graphics2D, and uses a lot of tiny BufferedImage sprites. PrinterJob.print() seems to be calling Printable.print() roughly once for each sprite (the actual count can vary both ways), so some pages are re-rendered 150 times... This causes printing to be unacceptably slow, about 10 seconds for two pages.
I found this: Why does the java Printable's print method get called multiple times with the same page number?
But it doesn't appear to explain my particular problem (or only partially explains it). I created a test report which has only a few sprites, and there was a small number of resizes that went up and down as I added and removed images on either the vertical or horizontal axes.
When printing to a PDF using Bullzip, I noticed that after zooming in on the images, they are being scaled up using a bilinear or bicubic algorithm. One of these images, which is unique in having an indexed color palette, does not appear to be scaled. I confirmed that the scaling is a Java behavior and not being performed by Bullzip by printing to a real printer and observing the same images being scaled versus not.
So it strikes me as the print API trying to rescale images to whatever DPI it has in mind, but for some reason it's calling Printable.print() each time it encounters an image that it deems as needing this treatment.
How do I fix this behavior? I tried setting rendering hints on the Graphics2D that I get when Printable.print() is called, to no avail. I don't know what else to do short of try to find and examine the print API's source code.
I think I just figured it out by accident. A report I just modified now draws an image over some geometry, and I noticed that the part of the geometry that's behind the box of the image is being rasterized and looks blurry compared to outside of the box. The image in question (and all other than the one indexed color image) has an 8 bit alpha channel.
I noticed before that Java's print rasterizer doesn't like things with translucency (one report which used it was being completely rasterized at I think 300dpi...), but I forgot that these images also had alpha channels.
When I get a chance, I'm probably going to fix this by further increasing the images' resolution and using 1 bit alpha. When scaled down for screen viewing, it will have a few bits of alpha again and look okay.
I'm in the process of writing a custom heatmap generator. I'm wondering what the fastest way is to draw boxes (up to around 1 million) in Java. Most questions I've found have concentrated on dynamic images (like in games), and I'm wondering if there's a better way to go for static images. I've tried using swing (via a GridLayout and adding a colored canvas to each box), drawing directly on the panel with Graphics2D, and also by using the Processing libraries. While Processing is pretty fast and generates a clean image, the window seems to have problems keeping it; it generates different parts of the image whenever you minimize, move the windows, etc.
I've heard of OpenGL, but I've never touched it, and I wanted some feedback as to whether that (or something else) would be a better approach before investing time in it.
For static images, I paint them to a BufferedImage (BI) and then draw that via Graphics2D.
I keep a boolean that tells me whether the BI is up to date. That way I only incur the expensive painting cost once. If you want to get fancy, you can scale the BI to handle minor resizing. For a major resizing you'll probably want to repaint the BI so as not to introduce artifacts. It's also useful for overlaying data (such as cross hairs, the value under the cursor, etc) as you're only painting the BI and the data.