Since the Brazilian government does not have a public API for postal codes, I am trying to reverse engineer the code that correios.com.br uses to generate the image below so I can generate my own images to train the OCR program.
I believe that I have already got almost everything right besides the text spacing and the colors:
I am not interested on the colors now, but the text spacing is really bothering me. For instance, have a look on the 'Ti' in 'Tijuca'. The two letters are really close together in the original image and I can't reproduce this feature. I have already tried to derive the font, setting values to TextAttribute.TRACKING and TextAttribute.KERNING, but it did not work.
Here follows my code:
import java.awt.Color;
import java.awt.font.TextAttribute;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import javax.imageio.ImageIO;
public class CreateImage {
/**
* #param args
*/
public static void main(String[] args) {
int width = 570;
int height = 120;
boolean new_image = true;
BufferedImage img;
if (!new_image) {
try {
img = ImageIO.read(new File("original.jpg"));
} catch (IOException e) {
e.printStackTrace();
new_image = true;
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
} else {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
Graphics2D g2d = img.createGraphics();
if (new_image) {
// white background
g2d.setPaint(Color.WHITE);
g2d.fillRect(0, 0, width, height);
g2d.setPaint(Color.BLACK);
} else {
g2d.setPaint(Color.RED);
}
Font myfont = new Font("SansSerif", Font.BOLD, 11);
/*
Hashtable<TextAttribute, Float> attributes = new Hashtable<TextAttribute, Float>();
attributes.put(TextAttribute.TRACKING, new Float(-0.01));
myfont = myfont.deriveFont(attributes);
*/
g2d.setFont(myfont);
g2d.drawString("Logradouro:", 5, 13);
g2d.drawString("Bairro:", 5, 33);
g2d.drawString("Localidade / UF:", 5, 53);
g2d.drawString("CEP:", 5, 73);
g2d.drawString("Avenida das Américas - de 3979 a 5151 - lado ímpar", 105, 13);
g2d.drawString("Barra da Tijuca", 105, 33);
g2d.drawString("Rio de Janeiro/RJ", 105, 53);
g2d.drawString("22631-004", 105, 73);
g2d.dispose();
File file = new File("clone.jpg");
try {
ImageIO.write(img, "jpg", file);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("clone.jpg file created.");
}
}
My question is: What are the other options to control how a string is spaced when it's drawn? Do you have any ideas on what the original code may be doing?
Thanks!
I can't really say how to do it properly in Swing, but what you are looking for is in typography called "kerning", so that attribute ought to do something similar.
Related
I want to cut the pic to circle by using java graphics 2d, but the result is unsatisfying. I would like the final pic come as similar as the "object-fit: cover" comes out in css.
This is the original pic
original pic
Below are my codes and the final result.
BufferedImage testImage = ImageIO.read(new File("/Users/huangruixiang/Desktop/test.png"));
BufferedImage formatAvatarImage = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = formatAvatarImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, 200, 200);
graphics.setClip(shape);
graphics.drawImage(testImage, 0, 0, 200, 200, null);
graphics.dispose();
ImageIO.write(formatAvatarImage,"png",new File("/Users/huangruixiang/Desktop/circle.png"));
resule
And the effect I want is similar to this
Expected effect
Here you go.
You have to use a different drawImage method than the one you used.
Here's the complete runnable code.
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class CenterCutImage {
public static void main(String[] args) {
CenterCutImage cci = new CenterCutImage();
BufferedImage image = cci.readImage("/do3kO.png");
BufferedImage croppedImage = cci.createCenterCut(image, new Dimension(200, 200));
String path = "D:\\Eclipse\\Eclipse-2020-workspace\\com.ggl.testing3\\resources";
cci.writeImage(croppedImage, path, "circle.png");
}
public BufferedImage createCenterCut(BufferedImage inputImage, Dimension d) {
BufferedImage image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x = (inputImage.getWidth() - d.width) / 2;
int y = (inputImage.getHeight() - d.height) / 2;
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, d.width, d.height);
g2d.setClip(shape);
g2d.drawImage(inputImage, 0, 0, d.width, d.height, x, y, x + d.width, y + d.height, null);
g2d.dispose();
return image;
}
public BufferedImage readImage(String filename) {
try {
return ImageIO.read(getClass().getResourceAsStream(filename));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public void writeImage(BufferedImage croppedImage, String path, String filename) {
try {
ImageIO.write(croppedImage, "png", new File(path + "/" + filename));
} catch (IOException e) {
e.printStackTrace();
}
}
}
I am using PotraceJ (potrace library's java version) - note that potrace is used for vectorization of images.
Situation - potrace traces image, and stores the result in a BufferedImage, which is then displayed in a JPanel as an ImageIcon.
My problem - I want to save this BufferedImage instead as a file say vector.svg
Using ImageIO.write() I tried but no file is created when I do that. Possible reason could be Java's image writers may not write svg
I have to store this vectorized image as an svg file. How should I do that?
The code that mainly vectorzies a raster image is embeded here. While you can find the whole library here at github.
package potracej.src;
import potracej.src.compat.ConvertToJavaCurves;
import potracej.src.compat.PathElement;
import potracej.src.potracej.Bitmap;
import potracej.src.potracej.PoTraceJ;
import potracej.src.potracej.param_t;
import potracej.src.potracej.path_t;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.metal.MetalButtonUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Created with IntelliJ IDEA.
* User: san
* Date: 6/10/12
* Time: 12:55 PM
* To change this template use File | Settings | File Templates.
*/
public class Main {
static BufferedImage result;
static Bitmap bmp;
static param_t param = new param_t();
static double scale = 1;
static ImageIcon resultIcon;
static ImageIcon srcIcon;
static BufferedImage sourceImage;
static boolean renderSourceImage = false;
public static void main(String[] args) throws IOException {
Path p = Paths.get(new File(".").getCanonicalPath()+"/potracej/girl.png");
sourceImage = ImageIO.read(p.toFile());
//Toolkit.getDefaultToolkit().
WritableRaster raster = sourceImage.getRaster();
int[] iarr = new int[4];
bmp = new Bitmap((int)(sourceImage.getWidth()), (int)(sourceImage.getHeight()));
for(int y=0; y<sourceImage.getHeight(); y++) {
for(int x=0; x<sourceImage.getWidth(); x++) {
int[] pixel = raster.getPixel(x, y, iarr);
if (pixel[0] + pixel[1] + pixel[2] + pixel[3] != 0) {
bmp.put(x, y, 1);
}
}
}
BufferedImage d2 = new BufferedImage((int) (scale * sourceImage.getWidth()), (int)(scale * sourceImage.getHeight()), BufferedImage.TYPE_INT_ARGB);
Graphics2D d2g = (Graphics2D) d2.getGraphics();
d2g.scale(scale, scale);
d2g.drawImage(sourceImage, 0, 0, null);
d2g.dispose();
sourceImage.flush();
srcIcon = new ImageIcon(d2);
doTrace(scale);
JFrame frame = new JFrame("Result") {
{
setLayout(new BorderLayout());
resultIcon = new ImageIcon(result, "Result");
JButton resultButton = new JButton(resultIcon);
resultButton.setUI(new MetalButtonUI() {
#Override
protected void paintButtonPressed(Graphics g, AbstractButton b) {
//
}
});
add(resultButton, BorderLayout.CENTER);
resultButton.setPressedIcon(srcIcon);
JPanel stuff = new JPanel();
add(stuff, BorderLayout.NORTH);
stuff.setLayout(new GridLayout(4, 2));
stuff.add(new JLabel("Suppress speckles"));
final JSlider turdSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, param.turdsize);
stuff.add(turdSlider);
turdSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
param.turdsize = turdSlider.getValue();
doRetrace();
}
});
stuff.add(new JLabel("Smooth corners"));
final JSlider smoothSlider = new JSlider(JSlider.HORIZONTAL, 0, 300, (int) (param.opttolerance * 100));
stuff.add(smoothSlider);
smoothSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
param.opttolerance = smoothSlider.getValue() / 100.0;
doRetrace();
}
});
stuff.add(new JLabel("Optimize paths"));
final JSlider optSlider = new JSlider(JSlider.HORIZONTAL, 0, 125, (int) (param.alphamax * 100));
stuff.add(optSlider);
optSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
param.alphamax = optSlider.getValue()/100.0;
doRetrace();
}
});
final JCheckBox renderSource = new JCheckBox("Render source");
stuff.add(renderSource);
renderSource.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
renderSourceImage = renderSource.getModel().isArmed();
doRetrace();
}
});
}
private void doRetrace() {
doTrace(scale);
resultIcon.setImage(result);
repaint();
}
};
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void doTrace(double scale) {
PoTraceJ poTraceJ = new PoTraceJ(param);
long l = System.currentTimeMillis();
path_t trace = null;
for(int i=0; i<10; i++) {
trace = poTraceJ.trace(bmp);
Thread.yield();
}
poTraceJ.resetTimers();
for(int i=0; i<100; i++) {
trace = poTraceJ.trace(bmp);
}
poTraceJ.printTimers();
l = System.currentTimeMillis() - l;
System.out.println("L="+l);
ArrayList<PathElement> al = new ArrayList<PathElement>();
ConvertToJavaCurves.convert(trace, new HashSet<ConvertToJavaCurves.Point>(), al);
if (result != null)
result.flush();
result = new BufferedImage((int)(scale * bmp.getWidth()), (int)(scale * bmp.getHeight()), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D)result.getGraphics();
g2.scale(scale, scale);
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, bmp.getWidth(), bmp.getHeight());
g2.setColor(Color.BLACK);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
GeneralPath path = new GeneralPath();
for (PathElement pathElement : al) {
switch (pathElement.getType()) {
case CLOSE_PATH:
path.closePath();
break;
case LINE_TO:
path.lineTo(pathElement.getP0x(), pathElement.getP0y());
break;
case MOVE_TO:
path.moveTo(pathElement.getP0x(), pathElement.getP0y());
break;
case CURVE_TO:
path.curveTo(pathElement.getP0x(), pathElement.getP0y(), pathElement.getP1x(), pathElement.getP1y(), pathElement.getP2x(), pathElement.getP2y());
break;
}
}
g2.setPaint(Color.black);
g2.fill(path);
}
}
Thanks in advance.
We can convert this object:
GeneralPath path = new GeneralPath();
to SVGPath using Apache Batik
Add dependencies into pom.xml
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-anim</artifactId>
<version>1.9</version>
</dependency>
Create methods:
private static SVGOMDocument createSvgDocument(int width, int height) {
DOMImplementation domImpl = SVGDOMImplementation.getDOMImplementation();
SVGOMDocument document = (SVGOMDocument) domImpl.createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg", null);
Element svgTag = document.getRootElement();
svgTag.setAttribute("width", String.valueOf(width));
svgTag.setAttribute("height", String.valueOf(height));
return document;
}
private static void putPathToSvgDocument(SVGOMDocument document, GeneralPath path) {
SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document);
SVGPath svgPath = new SVGPath(ctx);
Element svgElement = svgPath.toSVG(path);
svgElement.setAttribute("fill", "#000");
document.getRootElement().appendChild(svgElement);
}
private static void saveSvgDocumentToFile(SVGOMDocument document, File file) throws FileNotFoundException, IOException {
SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
try (Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
svgGenerator.stream(document.getDocumentElement(), out);
}
}
Almost ready. Now we can create SVG file with our GeneralPath inside
GeneralPath path = new GeneralPath();
...
// TODO: set your image width and height ;)
SVGOMDocument document = createSvgDocument(1000,1000);
putPathToSvgDocument(document, path);
// TODO: set your filename :)
saveSvgDocumentToFile(document, new File("c:\\temp\\result.svg"));
Done!
Anyone looking forward to an answer to this would probably be someone like me. Newbie into image processing and looking for libraries that can quench the thirsts of your expedition. I haven't found solution to this problem and I cannot guide you anyone wrongly that what else can be done.
I observed that most of the image processing libraries are written in C++. Fewer libraries are there in Java. Why? Performance. And so for anyone who wants to produce efficient code and all the algorithms input be appreciated should use C++ too.
I want above solution for my Android application. I am going to write code in C++ and connect it via Android NDK.
For all other lost souls, be brave and take this decision. Here is my evidence to prove that.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I want to ask one question regarding my code which is how do I convert a multiple-line text into a single image using BufferReader. I am able to get the image of the text but all the lines of the text are coming as one single string in one line.
Enclosing my code. Please review my code and suggest me as soon as possible:
package mypack;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.imageio.ImageIO;
public class ImageDemo {
public static void main(String[] args) throws IOException {
// reading the String object from a file to be converted to image
/*File file = new File("D:/Vivek.txt");
StringBuilder sb=new StringBuilder();
BufferedReader reader = new BufferedReader(new FileReader(file));
while(true)
{
String text=reader.readLine();
System.out.println(text);
if(text==null)
break;
sb.append(text).append(' ');
sb.append("\n");
}
String text1=sb.toString();
reader.close();*/
String text=new String(Files.readAllBytes(Paths.get("D:/Vivek.txt")), StandardCharsets.UTF_8);
System.out.println(text);
// Image file name
String fileName = "Image";
// create a File Object
File newFile = new File("D:/Vivek.jpg");
// create the font you wish to use
Font font = new Font("Tahoma", Font.PLAIN, 20);
// create the FontRenderContext object which helps us to measure the
// text
FontRenderContext frc = new FontRenderContext(null, true, true);
// get the height and width of the text
Rectangle2D bounds = font.getStringBounds(text, frc);
int w = (int) bounds.getWidth();
int h = (int) bounds.getHeight();
// create a BufferedImage object
BufferedImage image = new BufferedImage(w, h,
BufferedImage.TYPE_INT_RGB);
// calling createGraphics() to get the Graphics2D
Graphics2D g = image.createGraphics();
// set color and other parameters
g.setColor(Color.white);
g.fillRect(0, 0, w, h);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString(text, (float) bounds.getX(), (float) -bounds.getY());
// releasing resources
g.dispose();
RenderedImage rImage = (RenderedImage) image;
// creating the file
ImageIO.write(rImage, "jpeg", newFile);
//merging the two buffered images to get a new image with text along with logo.
//load the source images
BufferedImage img1=ImageIO.read(new File("D:/Vivek.jpg"));
BufferedImage img2=ImageIO.read(new File("D:/logo.jpg"));
BufferedImage joined=joinBufferedImage(img1, img2);
ImageIO.write(joined, "png", new File("D:/Joined.png"));
System.out.println("Success");
}
public static BufferedImage joinBufferedImage(BufferedImage img1,BufferedImage img2) {
//do some calculate first
int offset = 500;
int wid = img1.getWidth()+img2.getWidth()+offset;
int height = Math.max(img1.getHeight(),img2.getHeight())+offset;
//create a new buffer and draw two image into the new image
BufferedImage newImage = new BufferedImage(wid,height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = newImage.createGraphics();
Color oldColor = g2.getColor();
//fill background
g2.setPaint(Color.WHITE);
g2.fillRect(0, 0, wid, height);
//draw image
g2.setColor(oldColor);
g2.drawImage(img2, null, 0, 0);
g2.drawImage(img1, null, 170, 150);
g2.dispose();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
return newImage;
}
}
Hi here I am writing multiple lines on JPanel. And you can write code which create Image from JPanel.
package Stakeoverflow.swingFrame;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
public class MultiLineTextToImage{
public static void main(String[] args) {
String text = "Hello";
/*
Because font metrics is based on a graphics context, we need to create
a small, temporary image so we can ascertain the width and height
of the final image
*/
int width = 1000;
int height = 1000;
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
Font font = new Font("Arial", Font.PLAIN, 48);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
g2d.dispose();
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setFont(font);
//fm = g2d.getFontMetrics();
g2d.setColor(Color.BLACK);
File file = new File("C:\\Read.text");
BufferedReader br=null;
int nextLinePosition=100;
int fontSize = 48;
try {
br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
g2d.drawString(line, 0, nextLinePosition);
nextLinePosition = nextLinePosition + fontSize;
}
br.close();
} catch (FileNotFoundException ex) {
Logger.getLogger(Testing.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Testing.class.getName()).log(Level.SEVERE, null, ex);
}
g2d.dispose();
try {
ImageIO.write(img, "png", new File("Text.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
I'm using g.drawString(str, x, y) to draw a String with a Graphics2D object g. The current font of g does not cover all the characters of str (I have e.g. Chinese chars in there). On Mac OS X, a fallback font seems to be automatically used, but not on Windows, where black square outlines appear instead of the wanted characters.
Why is the behavior different depending on the platform?
How do I specify a fallback font (or several fallback fonts) in case of missing characters?
(For instance, one of the nice fonts there.)
Update/More Info
So, the original font that doesn't support all characters is not one of the JVM's logical fonts, but is a bundled font that comes with my app and was obtained with Font.createFont(). So, adding fonts to the JRE's lib/fonts/fallback folder doesn't work here.
We could attribute string to switch font on "bad" symbols and use Graphics2D.drawString(AttributedCharacterIterator iterator, int x, int y) to render result. Advantage: this will work with any font. Drawback: without some sort of caching of intermediate objects this will work slower and dirtier.
So, i suggest using AttributedString with main font attribute on whole string:
AttributedString astr = new AttributedString(text);
astr.addAttribute(TextAttribute.FONT, mainFont, 0, textLength);
and with fallback font on specific parts:
astr.addAttribute(TextAttribute.FONT, fallbackFont, fallbackBegin, fallbackEnd);
The rendering itself:
g2d.drawString(astr.getIterator(), 20, 30);
The result (Physical "Segoe Print" as main font, logical "Serif" as fallback):
Complete supposed-to-be-SSCCE code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class FontTest extends JFrame {
public FontTest() {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(new TestStringComponent());
pack();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new FontTest().setVisible(true);
}
});
}
}
class TestStringComponent extends JComponent {
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(getForeground());
Font mainFont = new Font("Segoe Print", Font.PLAIN, 25);
Font fallbackFont = new Font("Serif", Font.PLAIN, 25);
String s = "Test 漢鼎繁古印 Test 漢鼎繁古印 Test";
g2d.drawString(createFallbackString(s, mainFont, fallbackFont).getIterator(), 20, 30);
}
public Dimension getPreferredSize() {
return new Dimension(500, 40);
}
private AttributedString createFallbackString(String text, Font mainFont, Font fallbackFont) {
AttributedString result = new AttributedString(text);
int textLength = text.length();
result.addAttribute(TextAttribute.FONT, mainFont, 0, textLength);
boolean fallback = false;
int fallbackBegin = 0;
for (int i = 0; i < text.length(); i++) {
boolean curFallback = !mainFont.canDisplay(text.charAt(i));
if (curFallback != fallback) {
fallback = curFallback;
if (fallback) {
fallbackBegin = i;
} else {
result.addAttribute(TextAttribute.FONT, fallbackFont, fallbackBegin, i);
}
}
}
return result;
}
}
I am writing aplha composite test app based on this example
/* Create an ARGB BufferedImage */
BufferedImage img = (BufferedImage)image;//ImageIO.read(imageSrc);
int w = img.getWidth(null);
int h = img.getHeight(null);
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics g = bi.getGraphics();
g.drawImage(img, 0, 0, null);
/* Create a rescale filter op that makes the image 50% opaque */
float[] scales = { 1f, 1f, 1f, 1f };
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);
/* Draw the image, applying the filter */
g2d.drawImage(bi, rop, 0, 0);
source link: http://download.oracle.com/javase/tutorial/2d/images/drawimage.html
It works fine with simple images but with photos (jpg etc) I get this exception like:
java.lang.IllegalArgumentException:
Number of scaling constants does not
equal the number of of color or
color/alpha components
To be more clear... Here is my adapted test code class. This code style throws the exception...
public class ImageTest extends JLabel {
public Image image;
private BufferedImage bImage;
ImageObserver imageObserver;
float[] scales = {1f, 1f, 1f, 1f};
float[] offsets = new float[4];
RescaleOp rop;
int width;
int height;
public ImageTest(ImageIcon icon) {
width = icon.getIconWidth();
height = icon.getIconHeight();
this.image = icon.getImage();
this.imageObserver = icon.getImageObserver();
//this.bImage=(BufferedImage)image; //previous version (makes exception?)...
this.bImage = new BufferedImage(
width, height, BufferedImage.TYPE_INT_ARGB);
this.bImage.createGraphics().drawImage(
this.image, 0, 0, width, height, imageObserver);
rop = new RescaleOp(scales, offsets, null);
this.repaint();
}
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;
}
}//class end
I am not pretty sure where the exception comes from so I need your advice where to look at?
P.S. I suppose it is the IndexColorModel problem but if so I am not sure how to fix it...
Any useful comment is appreciated :)
Andrew
Using the example below and the image you provided, I am unable to reproduce the effect you describe.
I was puzzled that a BufferedImage of TYPE_4BYTE_ABGR_PRE has a ComponentColorModel, in contrast to the more familiar DirectColorModel, but it's an IndexColorModel that cannot be rescaled.
Addendum: Updated code to use filter(), as the previous version was incorrectly reusing the BufferedImage.
Addendum: In another answer, you said,
I don't want to create a new BufferedImage each time. I want to filter the image on the fly using a JSlider.
The example you cited does this by creating the BufferedImage once in the SeeThroughComponent constructor and then adjusting just the RescaleOp in the slider's change handler. It seems quite responsive.
Addendum: In an edit to your question, you mention that the IllegalArgumentException may arise when encountering an image having an IndexColorModel. I am able to reproduce this using, e.g. TYPE_BYTE_INDEXED. You may be able to work around such images by trapping the exception and rendering them separately as shown here.
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/** #see https://stackoverflow.com/questions/5838842 */
public class RescaleOpTest extends JPanel {
public static final String LINK =
"http://www.freeimagehosting.net/uploads/576c64ef7b.png";
public RescaleOpTest() {
this.setLayout(new GridLayout(1, 0));
Image img = null;
try {
img = ImageIO.read(new URL(LINK));
// img = ImageIO.read(new File("image.jpg"));
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
int w = img.getWidth(null);
int h = img.getHeight(null);
BufferedImage bi = new BufferedImage(
w, h, BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D g = bi.createGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
/* Create a rescale filter op that makes the image 75% opaque */
float[] scales = {1f, 1f, 1f, 0.75f};
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);
bi = rop.filter(bi, null);
this.add(new JLabel(new ImageIcon(img)));
this.add(new JLabel(new ImageIcon(bi)));
}
private void display() {
JFrame f = new JFrame("RescaleOpTest");
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 RescaleOpTest().display();
}
});
}
}