I have a (small) BufferedImage
wich needs to be enlarged using nearest neighbor interpolation and then drawn to a Graphics2D
. The Image has 1-bit alpha information and rendering it with antialiasing on and this code
AffineTransform oldT = g.getTransform();
Paint oldP = g.getPaint();
int w = img.getWidth(), h = img.getHeight();
g.transform(at);
g.setPaint(new TexturePaint(img, new Rectangle2D.Double(0, 0, w, h)));
g.fillRect(0, 0, w, h);
g.setPaint(oldP);
g.setTransform(oldT);
Where img
is theBufferedImage
to be rendered using at
as an AffineTransform
. The antialiasing activated by
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
produces good results, but only for the border of the entire image; looking very strange:
Notice the top border is actually the image border while the right is a transition from opaque to transparent pixel.
Is there a best practice to achieve the same antialiasing within the image as is applied to the border? Note that other interpolation methods than NearestNeighbor are unacceptable.
Thanks in advance for any hints.
The rendering shouldn't take too long since it is part of a paintComponent
method, but preprocessing the BufferedImage
is possible. The AffineTransform
is (very) variable, though.
EDIT
I have achieved a small improvement by using a two-step method:
AffineTransform oldT = g.getTransform();
Paint oldP = g.getPaint();
int w = img.getWidth(), h = img.getHeight(), texw = (int) (w*oldT.getScaleX()), texh = (int) (h * oldT.getScaleY());
BufferedImage tex = new BufferedImage(texw, texh, BufferedImage.TYPE_INT_ARGB);
Graphics2D tg = tex.createGraphics();
tg.scale(oldT.getScaleX(), oldT.getScaleY());
tg.drawImage(img, 0, 0, this);
g.transform(at);
g.scale(1/oldT.getScaleX(), 1/oldT.getScaleY());
g.setPaint(new TexturePaint(tex, new Rectangle2D.Double(0, 0, texw, texh)));
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.fillRect(0, 0, texw, texh);
g.setPaint(oldP);
g.setTransform(oldT);
Resulting in this image:
This is still not perfect, though. I tried VALUE_INTERPOLATION_BICUBIC
, but this is just incredibly slow and essentially produces the same result. I hope there is a way the get the exact same antialiasing effect, since it is still irritating.
A fairly complicated solution with a lot of tweaking and testing put into provides a very good quality by rendering each pixel as a shape reading its color and transparency from the BufferedImage
:
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
public class VectorizedImage {
private final BufferedImage img;
public VectorizedImage(BufferedImage img) {
this.img = img;
}
public void draw(Graphics2D g) {
Color oldC = g.getColor();
Composite oldCo = g.getComposite();
AlphaComposite comp = AlphaComposite.SrcOver;
double lap = 1/Math.sqrt(g.getTransform().getDeterminant()); // This deals with inter-pixel borders letting the background shine through
Path2D pixel = new Path2D.Double();
pixel.moveTo(0, 0);
pixel.lineTo(0, 1);
pixel.lineTo(1, 1);
pixel.lineTo(1, 0);
pixel.lineTo(0, 0);
pixel.transform(AffineTransform.getScaleInstance(1+lap, 1+lap));
for (int i = 0; i < img.getWidth(); i++)
for (int j = 0; j < img.getHeight(); j++) {
g.setColor(new Color(img.getRGB(i, j)));
g.setComposite(comp.derive(img.getAlphaRaster().getSampleFloat(i, j, 0) / 255));
g.fill(pixel.createTransformedShape(AffineTransform.getTranslateInstance(i, j)));
}
g.setColor(oldC);
g.setComposite(oldCo);
}
}
The result is this nicely rendered edge: