There are many (many) questions about computing the size (width or height) of a string that should be painted into a Swing component. And there are many proposed solutions. However, I noticed that most of these solutions do not work properly for small fonts.
The following is an MCVE that shows some of the approaches:
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.function.BiFunction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TextBoundsTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Font baseFont = new Font("Sans Serif", Font.PLAIN, 10);
Font smallFont0 = baseFont.deriveFont(0.5f);
Font smallFont1 = baseFont.deriveFont(0.4f);
f.getContentPane().setLayout(new GridLayout(5,2));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont0,
TextBoundsTest::computeBoundsWithFontMetrics,
"FontMetrics"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont1,
TextBoundsTest::computeBoundsWithFontMetrics,
"FontMetrics"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont0,
TextBoundsTest::computeBoundsWithFontAndFontRenderContext,
"Font+FontRenderContext"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont1,
TextBoundsTest::computeBoundsWithFontAndFontRenderContext,
"Font+FontRenderContext"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont0,
TextBoundsTest::computeBoundsWithGlyphVectorLogicalBounds,
"GlyphVectorLogicalBounds"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont1,
TextBoundsTest::computeBoundsWithGlyphVectorLogicalBounds,
"GlyphVectorLogicalBounds"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont0,
TextBoundsTest::computeBoundsWithGlyphVectorVisualBounds,
"GlyphVectorVisualBounds"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont1,
TextBoundsTest::computeBoundsWithGlyphVectorVisualBounds,
"GlyphVectorVisualBounds"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont0,
TextBoundsTest::computeBoundsWithTextLayout,
"TextLayout"));
f.getContentPane().add(
new TextBoundsTestPanel(smallFont1,
TextBoundsTest::computeBoundsWithTextLayout,
"TextLayout"));
f.setSize(600,800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static Rectangle2D computeBoundsWithFontMetrics(
String string, Graphics2D g)
{
FontMetrics fontMetrics = g.getFontMetrics();
Rectangle2D bounds = fontMetrics.getStringBounds(string, g);
return bounds;
}
private static Rectangle2D computeBoundsWithFontAndFontRenderContext(
String string, Graphics2D g)
{
FontRenderContext fontRenderContext =
new FontRenderContext(g.getTransform(),true, true);
Font font = g.getFont();
Rectangle2D bounds = font.getStringBounds(string, fontRenderContext);
return bounds;
}
private static Rectangle2D computeBoundsWithGlyphVectorLogicalBounds(
String string, Graphics2D g)
{
FontRenderContext fontRenderContext = g.getFontRenderContext();
Font font = g.getFont();
GlyphVector glyphVector = font.createGlyphVector(
fontRenderContext, string);
return glyphVector.getLogicalBounds();
}
private static Rectangle2D computeBoundsWithGlyphVectorVisualBounds(
String string, Graphics2D g)
{
FontRenderContext fontRenderContext = g.getFontRenderContext();
Font font = g.getFont();
GlyphVector glyphVector = font.createGlyphVector(
fontRenderContext, string);
return glyphVector.getVisualBounds();
}
private static Rectangle2D computeBoundsWithTextLayout(
String string, Graphics2D g)
{
FontRenderContext fontRenderContext = g.getFontRenderContext();
Font font = g.getFont();
TextLayout textLayout = new TextLayout(string, font, fontRenderContext);
return textLayout.getBounds();
}
}
class TextBoundsTestPanel extends JPanel
{
private final Font textFont;
private final BiFunction<String, Graphics2D, Rectangle2D> boundsComputer;
private final String boundsComputerName;
TextBoundsTestPanel(Font textFont,
BiFunction<String, Graphics2D, Rectangle2D> boundsComputer,
String boundsComputerName)
{
this.textFont = textFont;
this.boundsComputer = boundsComputer;
this.boundsComputerName = boundsComputerName;
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
g.drawString("Font size: "+textFont.getSize2D(), 10, 20);
g.drawString("Bounds : "+boundsComputerName, 10, 40);
AffineTransform oldAt = g.getTransform();
AffineTransform at = AffineTransform.getScaleInstance(50, 50);
g.transform(at);
g.translate(1, 2);
g.setFont(textFont);
String string = "Test";
g.drawString(string, 0, 0);
Rectangle2D bounds = boundsComputer.apply(string, g);
Shape boundsShape = at.createTransformedShape(bounds);
g.setTransform(oldAt);
g.setColor(Color.RED);
g.translate(50, 100);
g.draw(boundsShape);
}
}
The result of this program is shown in this screenshot:
As one can see, the simple methods work nicely for a font with size 0.5, but suddenly bail out and return bounds with a height of 0.0 for the font with size 0.4.
(Side note: I wonder whether this is simply a bug - although it might be caused by some internal round-off-errors, as it happens exactly between font sizes of 0.5 and 0.49 ...)
The only solutions that work for these smaller fonts are the computation using a GlyphVector, or the TextLayout. But both of these approaches are tremendously expensive, as they require the creation of the Shape of the string and lots of auxiliary objects. Furthermore, they only return the visual bounds (that is, the bounds of the actual shape), and not the logical bounds of the text.
Is there any efficient solution for computing the logical bounds of strings in small fonts?
You can normalize the font first. Measure that then scale the dimensions of the rectangle by the true size2D of the font.
private static Rectangle2D computeBoundsUsingNormalizedFont(
String string, Graphics2D g) {
Font normalizedFont = g.getFont().deriveFont(1f);
Rectangle2D bounds = normalizedFont.getStringBounds(string, g.getFontRenderContext());
float scale = g.getFont().getSize2D();
return new Rectangle2D.Double(bounds.getX() * scale,
bounds.getY() * scale,
bounds.getWidth() * scale,
bounds.getHeight() * scale);
}
Then obviously you can cache the normalized font and hide this work around inside a calculator class, something like this:
TextBoundsCalculator textBoundsCalculator = TextBoundsCalculator.forFont(smallFontX);
Rectangle2D bounds = textBoundsCalculator.boundsFor(string, g);
Where TextBoundsCalculator:
import java.awt.*;
import java.awt.geom.Rectangle2D;
public final class TextBoundsCalculator {
private interface MeasureStrategy {
Rectangle2D boundsFor(String string, Graphics2D g);
}
private MeasureStrategy measureStrategy;
private TextBoundsCalculator(MeasureStrategy measureStrategy) {
this.measureStrategy = measureStrategy;
}
public static TextBoundsCalculator forFont(Font font) {
if (font.getSize() == 0)
return new TextBoundsCalculator(new ScaleMeasureStrategy(font));
// The bug appears to be only when font.getSize()==0.
// So there's no need to normalize, measure and scale with fonts
// where this is not the case
return new TextBoundsCalculator(new NormalMeasureStrategy(font));
}
public Rectangle2D boundsFor(String string, Graphics2D g) {
return measureStrategy.boundsFor(string, g);
}
private static class ScaleMeasureStrategy implements MeasureStrategy {
private final float scale;
private final Font normalizedFont;
public ScaleMeasureStrategy(Font font) {
scale = font.getSize2D();
normalizedFont = font.deriveFont(1f);
}
public Rectangle2D boundsFor(String string, Graphics2D g) {
Rectangle2D bounds = NormalMeasureStrategy.boundsForFont(normalizedFont, string, g);
return scaleRectangle2D(bounds, scale);
}
}
private static class NormalMeasureStrategy implements MeasureStrategy {
private final Font font;
public NormalMeasureStrategy(Font font) {
this.font = font;
}
public Rectangle2D boundsFor(String string, Graphics2D g) {
return boundsForFont(font, string, g);
}
private static Rectangle2D boundsForFont(Font font, String string, Graphics2D g) {
return font.getStringBounds(string, g.getFontRenderContext());
}
}
private static Rectangle2D scaleRectangle2D(Rectangle2D rectangle2D, float scale) {
return new Rectangle2D.Double(
rectangle2D.getX() * scale,
rectangle2D.getY() * scale,
rectangle2D.getWidth() * scale,
rectangle2D.getHeight() * scale);
}
}
Related
I created a pie chart using JFrame and swings. But I want to convert the pie chart into the image and save in desktop/local path. But not having idea how to create image using JFrame Pie.
package test;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Main1 {
double value;
Color color;
public Main1(double value, Color color) {
this.value = value;
this.color = color;
}
}
class MyComponent extends JComponent {
Main1[] slices = { new Main1(1, Color.black), new Main1(1, Color.green),
new Main1(1, Color.yellow), new Main1(1, Color.red) };
MyComponent() {
}
public void paint(Graphics g) {
drawPie((Graphics2D) g, getBounds(), slices);
}
void drawPie(Graphics2D g, Rectangle area, Main1[] slices) {
double total = 0.0D;
for (int i = 0; i < slices.length; i++) {
total += slices[i].value;
}
double curValue = 0.0D;
int startAngle = 0;
for (int i = 0; i < slices.length; i++) {
startAngle = (int) (curValue * 360 / total);
int arcAngle = (int) (slices[i].value * 360 / total);
g.setColor(slices[i].color);
g.fillArc(area.x, area.y, area.width, area.height, startAngle, arcAngle);
curValue += slices[i].value;
}
}
}
public class Main2 {
public JPanel contentPane;
public static void main(String[] argv) {
JFrame frame = new JFrame();
frame.getContentPane().add(new MyComponent());
frame.setSize(300, 200);
frame.setVisible(true);
}
}
The basic logic for creating an Image of a Swing component is:
Dimension d = component.getSize();
BufferedImage image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
component.print( g2d );
g2d.dispose();
ImageIO.write(image, ".jpg", new File(...));
You can also check out the Screen Image class for convenience methods that paint the entire frame, a component on the frame or a rectangle of a component on the frame.
Note the above suggestion assumes you do the custom painting correctly which means you:
extend JPanel
override paintComponent(...), not paint(...)
invoke super.paintComponent(...)
If you want to extend JComponent then you need to:
override paintComponent(...);
fill the background of your component first before invoking the drawPie(...) method
But I want to convert the pie chart into the image
Create a BufferedImage and paint to it
BufferedImage bi = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
new MyComponent().drawPie(g2d, new Rectangle(0, 0, 200, 200), slices);
g2d.dispose();
and save in desktop/local path
Use ImageIO
ImageIO.write(bi, "jpg", new File("the/path/andName.jpg"));
I want to write a multiline text (3-4 lines is ok) on the bottom right corner of a JDesktopPane, how can I do this?
The text is not fixed, it can change every time i start the swing application but once the application is started it remains the same, I don't need to update from the application.
My first thought was to create an image, put it as background of the JDesktopPane and then write on it, but it doesn't seem a simple solution.
Thanks for the help.
Combining the examples seen here and here, the print() method in the variation below illustrates using FontMetrics to right justify multiple lines of text in the bottom, right corner of a JDesktopPane.
import java.awt.*;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
/** #see http://stackoverflow.com/a/45055215/230513 */
public class JDPTest extends JDesktopPane {
private MyFrame one = new MyFrame("One", 100, 100);
public JDPTest() {
this.setPreferredSize(new Dimension(640, 480));
this.add(one);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.lightGray);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.BLACK);
g2d.setFont(new Font(Font.SERIF, Font.BOLD, 16));
print(g2d, 3, "Hello, world!");
print(g2d, 2, "This is a test.");
print(g2d, 1, "This is another test.");
print(g2d, 0, "This is still another test.");
}
private void print(Graphics2D g2d, int line, String s) {
FontMetrics fm = g2d.getFontMetrics();
int x = this.getWidth() - fm.stringWidth(s) - 5;
int y = this.getHeight() - fm.getDescent()
- line * (fm.getHeight() + fm.getLeading());
g2d.drawString(s, x, y);
}
private final class MyFrame extends JInternalFrame {
MyFrame(String name, int x, int y) {
super(name, true, true, true, true);
this.setSize(320, 240);
this.setLocation(x, y);
this.setVisible(true);
}
}
private void display() {
JFrame f = new JFrame("JDPTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new JDPTest().display();
}
});
}
}
Use this example class.
The background if set will be scaled.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.swing.JDesktopPane;
public class MyDesktopPane extends JDesktopPane {
Image img;
public MyDesktopPane() {
super();
}
#Override
public void paintComponent(Graphics g) {
int width = this.getWidth();
int height = this.getHeight();
int infoWidth = 150;
int infoHeight = 100;
super.paintComponent(g);
// alpha
final Float alpha = new Float(0.9);
final Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(makeComposite(alpha.floatValue()));
// draw bacground image is set
if (img != null) {
g.drawImage(img, 0, 0, width, height, this);
}
//draw 3 line text in red reound rectangle
g.setColor(Color.RED);
g.fillRoundRect(width - infoWidth, height - infoHeight, infoWidth, infoHeight, 5, 5);
g.setColor(Color.BLACK);
g.drawString("Line 1", width - infoWidth + 5, height - infoHeight + 20);
g.drawString("Line 2", width - infoWidth + 5, height - infoHeight + 40);
g.drawString("Line 3", width - infoWidth + 5, height - infoHeight + 60);
}
public void setBackGroundImage(String path) {
try {
boolean file = new File(path).isFile();
if (file) {
img = javax.imageio.ImageIO.read(new FileInputStream(path));
}
} catch (IOException e) {
e.printStackTrace();
}
this.repaint();
}
private AlphaComposite makeComposite(final float alpha) {
final int type = AlphaComposite.SRC_OVER;
return (AlphaComposite.getInstance(type, alpha));
}
}
I want to display an image to zoom in and out and can be shifted. I have the script below, but it can not be shifted. how to fix it.
the code :
package dispertasih;
import static dispertasih.PanPanel.startX;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class PadAndZoom implements ChangeListener {
BufferedImage image;
PanPanel label;
public void stateChanged(ChangeEvent e) {
int value = ((JSlider)e.getSource()).getValue();
double scale = value/200.0;
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
label.revalidate(); // signal scrollpane
}
private BufferedImage getScaledImage(double scale) {
int w = (int)(scale*image.getWidth());
int h = (int)(scale*image.getHeight());
BufferedImage bi = new BufferedImage(w, h, image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
AffineTransform at = AffineTransform.getScaleInstance(scale, scale);
g2.drawRenderedImage(image, at);
g2.dispose();
return bi;
}
private PanPanel getContent() {
createAnImage();
label = new PanPanel(image);
//label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
private void createAnImage() {
int w = 500;
int h = 500;
int type = BufferedImage.TYPE_INT_RGB; // many options
try {
image = ImageIO.read(PadAndZoom.class.getResource("/tampilan/background5.jpg"));
} catch (IOException ex) {
Logger.getLogger(PadAndZoom.class.getName()).log(Level.SEVERE, null, ex);
}
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 50, 200, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(10);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
PadAndZoom app = new PadAndZoom();
JFrame f = new JFrame();
JButton b = new JButton();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(app.getContent()));
//f.getContentPane().add(new JButton("tessss"));
f.getContentPane().add(app.getControl(), "Last");
f.setSize(400, 400);
f.setLocation(200,200);
f.setVisible(true);
}
}
class PanPanelX extends JPanel {
private int x, y;
private int width = 800, height = 800;
BufferedImage img;
private final static RenderingHints textRenderHints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
private final static RenderingHints imageRenderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
private final static RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
static int startX, startY;
public PanPanelX(BufferedImage img) {
x = 20;
y = 20;
this.img = img;
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent me) {
super.mousePressed(me);
startX = me.getX();
startY = me.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
super.mouseDragged(me);
if (me.getX() < startX) {//moving image to right
x -= 2;
} else if (me.getX() > startX) {//moving image to left
x += 2;
}
if (me.getY() < startY) {//moving image up
y -= 2;
} else if (me.getY() > startY) {//moving image to down
y += 2;
}
repaint();
}
});
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
//turn on some nice effects
applyRenderHints(g2d);
g2d.drawImage(img, x, y, null);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public static void applyRenderHints(Graphics2D g2d) {
g2d.setRenderingHints(textRenderHints);
g2d.setRenderingHints(imageRenderHints);
g2d.setRenderingHints(renderHints);
}
}
i think on :
public void stateChanged(ChangeEvent e) {
int value = ((JSlider)e.getSource()).getValue();
double scale = value/200.0;
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
label.revalidate(); // signal scrollpane
}
there are mistakes, but I've stuck fixing it....
thank you ...
The problem is, the reference of the image that is displayed on the screen is not the same as the image you are creating when you resize it...
You create PanPanel by using...
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
getScaledImage creates a new image each time it is called, based on the original image.
BufferedImage bi = new BufferedImage(w, h, image.getType());
This means, that the image in PadAndZoom is not the same as the one in PanPanel, so any changes you make to image or any new scaled instances will never be painted because PanPanel knows nothing about them...
Now, in stateChanged, instead of updating the instance of PanPanel that you already have, you create a new instance of it...and that's it...it's never added to any displayable component, so it will never appear on the screen.
Instead, in your PanPanel, you need to supply some way for the stateChanged method to pass the scaled instance of the image to it...
public void setImage(BufferedImage img) {
this.img = img;
revalidate();
repaint();
}
Then in your stateChanged method you can do...
int value = ((JSlider) e.getSource()).getValue();
double scale = value / 200.0;
BufferedImage scaled = getScaledImage(scale);
label.setImage(scaled);
I have Line2D and Arc2D objects laid out on my JPanel by Graphics2D drawing. You can have a look a part of it on this question " How to make pixel perfect Line2D in - Graphics2D ". Now what I want to achieve is, I want to create two parallel lines and arcs for all Line2D and Arc2D objects. Visually,
Normal Line2D and Arc2D drawn currently,
Want to decorate it like this,
My Thoughts so far,
I might be able to achieve this by creating two different line and give an offset +gap and -gap from my normal line position. However This will make lots of objects which I don't want to.
Now, is it possible to make my normal line thicker like this,
and give them a border and delete middle bit from it?
Is it possible to achieve this? if yes, May I please have some direction.
Thank you for any kind of help.
Use a BasicStroke and draw it twice, thicker and thinner.
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.io.File;
class PaintThick {
public static void main(String[] args) throws Exception {
int size = 150;
final BufferedImage bi = new BufferedImage(
size,size,BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
double pad = 20;
Line2D.Double line1 = new Line2D.Double(
pad,pad,(double)(size-pad),(double)(size-pad));
int cap = BasicStroke.CAP_BUTT;
int join = BasicStroke.JOIN_MITER;
BasicStroke thick = new BasicStroke(15,cap,join);
BasicStroke thinner = new BasicStroke(13,cap,join);
g.setColor(Color.WHITE);
g.fillRect(0,0,size,size);
g.setColor(Color.BLACK);
g.setStroke(thick);
g.draw(line1);
g.setColor(Color.WHITE);
g.setStroke(thinner);
g.draw(line1);
ImageIO.write(bi,"png",new File("img.png"));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(
null, new JLabel(new ImageIcon(bi)));
}
});
}
}
You can implement the Stroke interface to create a CompositeStroke, as shown here.
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* #see http://www.jhlabs.com/java/java2d/strokes/
* #see http://stackoverflow.com/questions/7342979
*/
class StrokeTest {
private static final int SIZE = 200;
private static final double PAD = 20d;
private static class CompositeStroke implements Stroke {
private Stroke stroke1, stroke2;
public CompositeStroke(Stroke stroke1, Stroke stroke2) {
this.stroke1 = stroke1;
this.stroke2 = stroke2;
}
#Override
public Shape createStrokedShape(Shape shape) {
return stroke2.createStrokedShape(
stroke1.createStrokedShape(shape));
}
}
public static void main(String[] args) throws Exception {
final BufferedImage bi = new BufferedImage(
SIZE, SIZE, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Arc2D.Double shape = new Arc2D.Double(PAD, 2 * PAD,
(SIZE - 2 * PAD), (SIZE - 2 * PAD), 0, 180d, Arc2D.OPEN);
g.setColor(Color.white);
g.fillRect(0, 0, SIZE, SIZE);
BasicStroke s1 = new BasicStroke(16f);
BasicStroke s2 = new BasicStroke(1f);
g.setStroke(new CompositeStroke(s1, s2));
g.setColor(Color.black);
g.draw(shape);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JLabel(new ImageIcon(bi)));
f.pack();
f.setVisible(true);
}
});
}
}
I am writing a test app. To set Alpha for image I use paintComponent method. Watch next snippet...
public class TestImage extends JLabel{
public void paintComponent( Graphics g ) {
super.paintComponent( g );
Graphics2D g2d=(Graphics2D)g;
g2d.drawImage(this.bImage, rop, 0, 0);
}
public void setRescaleOp(RescaleOp rop){this.rop=rop;}
}
As you can see,
g2d.drawImage(this.bImage, rop, 0, 0);
does not allow to set width and height as if I use g.drawImage(bImage, 0, 0,width,height, null);
So the question is... How to set width and height for bImage in this case?
Any useful comment is appreciated
Andrew
First filter(), as shown here, and then scale using drawImage() or AffineTransformOp, as shown here.
Addendum: Alternatively, you can scale the image first (using either approach above) and then use your RescaleOp in drawImage().
As an aside, RescaleOp scales the image's color bands; it does not change the image's dimensions. To avoid confusion, dimensional scaling is sometimes called resampling.
Addendum: Here's an example of using drawImage() to resample and RescaleOp to adjust the alpha of an image.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* #see https://stackoverflow.com/questions/5838842
* #see https://stackoverflow.com/questions/5864490
*/
public class AlphaTest {
private static void display() {
JFrame f = new JFrame("AlphaTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ImageIcon icon = new ImageIcon("image.jpg");
final AlphaPanel ip = new AlphaPanel(icon, 0.75);
final JSlider slider = new JSlider();
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int v = slider.getValue();
ip.setAlpha((float) v / slider.getMaximum());
ip.repaint();
}
});
f.add(ip, BorderLayout.CENTER);
f.add(slider, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
display();
}
});
}
}
class AlphaPanel extends JPanel {
private BufferedImage bi;
private float[] scales = {1f, 1f, 1f, 0.5f};
private float[] offsets = new float[4];
private RescaleOp rop;
public AlphaPanel(ImageIcon icon, double scale) {
int width = (int) (scale * icon.getIconWidth());
int height = (int) (scale * icon.getIconHeight());
this.setPreferredSize(new Dimension(width, height));
this.bi = new BufferedImage(
width, height, BufferedImage.TYPE_INT_ARGB);
this.bi.createGraphics().drawImage(
icon.getImage(), 0, 0, width, height, null);
rop = new RescaleOp(scales, offsets, null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(bi, rop, 0, 0);
}
public void setAlpha(float alpha) {
this.scales[3] = alpha;
this.rop = new RescaleOp(scales, offsets, null);
}
}