How to flip and save Image - java

My Image declaration:
ImageIcon imageIcon1 = new ImageIcon(main.class.getResource("image1.png"));
Image image1 = imageIcon1.getImage();
How do I take image1, flip it along it's vertical axis and save it as another image?
I have googled and every solution I have found has come with some type of casting error.
Also, if there is a more efficient way to declare my image please let me know.

You state:
I have googled and every solution I have found has come with some type of casting error.
Which only tells us that you're doing something wrong but doesn't tell us what, limiting how we can help you. I can only tell you some steps that have worked for me:
Create another BufferedImage the same size as the first,
get its Graphics2D context via createGraphics(),
flip the graphics via an AffineTransform.getScaleInstance(-1, 1) for a horizontal flip
Don't forget to then translate the transform to bring the flipped image to where you want it.
draw the old image into the new image,
dispose the Graphics2D object.
If you need more help, then please show us what you've tried and include any and all error messages.
For instance, I played with this when playing with mirror sprite images a while back. Compile and run this to see what I mean:
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class FlipViaTransform {
private static final String SPRITE_SHEET_SPEC = "http://www.funorb.com/img/images/game/"
+ "central/dev_diary/sprite_sheet_full.gif";
private static final int TIMER_DELAY = 200;
private static final int SPRITE_ROWS = 8; // an 8 x 8 sprite sheet
public static void main(String[] args) {
try {
URL spriteSheetUrl = new URL(SPRITE_SHEET_SPEC);
BufferedImage spriteSheet = ImageIO.read(spriteSheetUrl);
final ImageIcon[] iconsA = new ImageIcon[64];
final ImageIcon[] iconsB = new ImageIcon[64];
double wD = (double) spriteSheet.getWidth() / SPRITE_ROWS;
double hD = (double) spriteSheet.getHeight() / SPRITE_ROWS;
int w = (int) wD;
int h = (int) hD;
// *** here's what I used to flip
AffineTransform at = AffineTransform.getScaleInstance(-1, 1); // *** flip
at.translate(-wD, 0); // *** translate so that flipped image is visible
for (int i = 0; i < SPRITE_ROWS; i++) {
for (int j = 0; j < SPRITE_ROWS; j++) {
int x = (int) (i * wD);
int y = (int) (j * hD);
BufferedImage imgA = spriteSheet.getSubimage(x, y, w, h);
BufferedImage imgB = new BufferedImage(imgA.getWidth(),
imgA.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = imgB.createGraphics();
g2.setTransform(at); // *** transform
g2.drawImage(imgA, 0, 0, null); // *** draw old image into new
g2.dispose(); // *** get rid of graphics2d object
iconsA[j * SPRITE_ROWS + i] = new ImageIcon(imgA);
iconsB[j * SPRITE_ROWS + i] = new ImageIcon(imgB);
}
}
final JLabel labelA = new JLabel("Image");
final JLabel labelB = new JLabel("Mirror Image");
labelA.setVerticalTextPosition(JLabel.BOTTOM);
labelB.setVerticalTextPosition(JLabel.BOTTOM);
labelA.setHorizontalTextPosition(JLabel.CENTER);
labelB.setHorizontalTextPosition(JLabel.CENTER);
labelA.setIcon(iconsA[0]);
labelB.setIcon(iconsB[0]);
final JPanel panel = new JPanel(new GridLayout(1, 0));
panel.add(labelA);
panel.add(labelB);
Timer spriteTimer = new Timer(TIMER_DELAY, new ActionListener() {
int spriteIndex = 0;
#Override
public void actionPerformed(ActionEvent arg0) {
labelA.setIcon(iconsA[spriteIndex]);
labelB.setIcon(iconsB[spriteIndex]);
spriteIndex++;
spriteIndex %= iconsA.length;
}
});
spriteTimer.start();
JOptionPane.showMessageDialog(null, panel, "AffineTransform Example", JOptionPane.PLAIN_MESSAGE);
spriteTimer.stop();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
which displays as:

I have never done this, but you can try to research if you can use a 3d party library such as ImageMagick or GraphicsMagic with Java. Those libraries can read PNG images and perform graphics operation on them.

Related

Image Processing and comparing in java

I need to compare two images of similar kind but both have different size in JAVA, i.e one has low background width and another one has high background width.
And size of images are also different.
I have attached two files.
In which person and t-shirt are same so I need to show result as Image is Same.
But when I go with pixel wise image matching then it does not show me the true result due to different width of background.
Then I have tried to remove background and then comparing it still same issue.
Please fine the attached images and code.
Please Help
Links to images :
Image one&
Image two
Code:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JOptionPane;
public class ImageProcesing extends Canvas {
private static int x1 = 0, y1 = 0;
private static int h, w;
private static final Random random = new Random();
private Color mycolor;
BufferedImage img, img1;
public BufferedImage scaleImage(int WIDTH, int HEIGHT, String filename) {
BufferedImage bi = null;
try {
ImageIcon ii = new ImageIcon(filename);//path to image
h = 512;
w = 512;
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(ii.getImage(), 0, 0, w, h, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return bi;
}
public BufferedImage scaleImage2(String filename) {
BufferedImage bi = null;
try {
ImageIcon ii = new ImageIcon(filename);//path to image
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(ii.getImage(), 0, 0, w, h, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return bi;
}
public ImageProcesing() {
try {
this.img = scaleImage(512, 512, "D:\\I Tech Solutions\\Rahul Ratda\\Experiments\\1.jpeg");
this.img1 = scaleImage2("D:\\I Tech Solutions\\Rahul Ratda\\Experiments\\3.jpeg");
} catch (Exception ex) {
Logger.getLogger(ImageProcesing.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void paint(Graphics g) {
int n = 0, k = 0, l = 0;
int tp = 0;
super.paint(g);
Color oldColor = new Color(img1.getRGB(0, 0));
for (int x = 0; x < h; x++) {
tp = 0;
w=512;
for (int y = 0; y < w; y++) {
oldColor = new Color(img1.getRGB(y, x));
mycolor = new Color(img.getRGB(y, x));
if (tp == 0 && oldColor.equals(Color.WHITE)) {
continue;
} else {
if (tp == 0) {
tp = 1;
w = w - y;
}
k++;
if ((mycolor.equals(oldColor))) {
g.setColor(mycolor);
g.drawLine(y, x, y, x);
n++;
}
}
}
}
System.out.println("K : "+k+"\n N : "+n);
if (n >= (k * 0.70)) {
System.out.println("Same");
}
else
System.out.println("Not Same");
/* oldColor=new Color(img1.getRGB(0,0));
for(int i = 0 ; i < WIDTH1; i++) {
for(int y = 0; y < HEIGHT1; y++) {
mycolor=new Color(img1.getRGB(i,y));
if((mycolor.equals(oldColor))){
y1++;
g.setColor(mycolor);
g.drawLine(i, y, i, y);
}
else
oldColor=mycolor;
}
}*/
/*if(x1>y1)
{
if(x1*0.6<y1)
JOptionPane.showMessageDialog(null,"Images are More than 60% Equal.");
else
JOptionPane.showMessageDialog(null,"Images are Less than 60% Equal.");
}
else{
if(y1*0.6<x1)
JOptionPane.showMessageDialog(null,"Images are More than 60% Equal.");
else
JOptionPane.showMessageDialog(null,"Images are Less than 60% Equal.");
}*/
}
/* private Color randomColor() {
return new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
}*/
public static void main(String[] args) {
JFrame frame = new JFrame();
System.out.println("width = " + w);
System.out.println("height = " + h);
frame.setSize(1000, 1000);
frame.add(new ImageProcesing());
frame.setVisible(true);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
You can develop your own algorithm for this kind of structure.
As I can see that background of your images are white so first of all you perform background removal operation on your images.
Then you resize your image to lower level like 256x256 or something likethat.
No you have only object in the image as color is already moved out.
So you can go with your pixel by pixel comparison and keeping thrshold of 84-90% would give you expected result which you want.
Direct image comparison by pixel by pixel will not work as images are different in size as well as backgrounds space is also different and also objects size varies.
You need to apply some image processing technique like histogram before comparing the images. Java API of opencv is a nice tool for such operations.See the link

JAVA Using ImageIO.read and paintComponents()

I programmed a class, which helps me to get 32x32 images from a large one. But I have a problem. My class looks like this:
package tool;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageLoader {
private String file;
private BufferedImage image;
private BufferedImage[][] subImage;
public ImageLoader(String FILE) {
file = FILE;
try {
image = ImageIO.read(new File(file));
} catch (IOException e) {
}
subImage = new BufferedImage[image.getWidth() / 32][image.getHeight() / 32];
for (int i = 0; i < image.getWidth() / 32; i++) {
for (int j = 0; j < image.getHeight() / 32; j++) {
subImage[i][j] = image.getSubimage(i * 32, j * 32, (1 + i) * 32, (1 + j) * 32);
}
}
}
public BufferedImage getSubImage(int X, int Y) {
return subImage[X][Y];
}
}
If I do it that way, it seems the ImageIO.read(new File(String file)) command prevents the use of paintComponent() of that Swing object, where I want to draw the image. I experimented a little bit and found out, that when you load the image in the getSubImage(int X, int Y) method, it works fine. But I think, it's not the smartest idea, because then you always load this image again, if you call the method. I need help, how I can load that image just one time and that the Swing object draw everthing correctly.
Thanks in advance.
Write the thumbnail and load it back in a JLabel instead.
//get the large file and create a new 32x32 thumbnail
BufferedImage sourceImage = ImageIO.read(new FileInputStream("c://filename"));
Image thumbnail = sourceImage.getScaledInstance(32, 32, Image.SCALE_SMOOTH);
BufferedImage bufferedThumbnail = new BufferedImage(thumbnail.getWidth(null),
thumbnail.getHeight(null),
BufferedImage.TYPE_INT_RGB);
bufferedThumbnail.getGraphics().drawImage(thumbnail, 0, 0, null);
//read the file back
ImageIO.write(bufferedThumbnail, "jpeg", outputStream);
//read the file back
image = ImageIO.read(new File(path));
JLabel picLabel = new JLabel(new ImageIcon(image));

How do I flip an image upside-down?

I was wondering if I could find some help on this problem. I was asked to use an image ("corn.jpg"), and flip it entirely upside down. I know I need to write a program which will switch pixels from the top left corner with the bottom left, and so on, but I wasn't able to get my program to work properly before time ran out. Could anyone provide a few tips or suggestions to solve this problem? I'd like to be able to write my code out myself, so suggestions only please. Please note that my knowledge of APImage and Pixel is very limited. I am programming in Java.
Here is what I managed to get done.
import images.APImage;
import images.Pixel;
public class Test2
{
public static void main(String [] args)
{
APImage image = new APImage("corn.jpg");
int width = image.getImageWidth();
int height = image.getImageHeight();
int middle = height / 2;
//need to switch pixels in bottom half with the pixels in the top half
//top half of image
for(int y = 0; y < middle; y++)
{
for (int x = 0; x < width; x++)
{
//bottom half of image
for (int h = height; h > middle; h++)
{
for(int w = 0; w < width; w++)
{
Pixel bottomHalf = image.getPixel(h, w);
Pixel topHalf = image.getPixel(x, y);
//set bottom half pixels to corresponding top ones?
bottomHalf.setRed(topHalf.getRed());
bottomHalf.setGreen(topHalf.getGreen());
bottomHalf.setBlue(topHalf.getBlue());
//set top half pixels to corresponding bottom ones?
topHalf.setRed(bottomHalf.getRed());
topHalf.setGreen(bottomHalf.getGreen());
topHalf.setBlue(bottomHalf.getBlue());
}
}
}
}
image.draw();
}
}
Thank you for your help!
See Transforming Shapes, Text, and Images.
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class FlipVertical {
public static BufferedImage getFlippedImage(BufferedImage bi) {
BufferedImage flipped = new BufferedImage(
bi.getWidth(),
bi.getHeight(),
bi.getType());
AffineTransform tran = AffineTransform.getTranslateInstance(0, bi.getHeight());
AffineTransform flip = AffineTransform.getScaleInstance(1d, -1d);
tran.concatenate(flip);
Graphics2D g = flipped.createGraphics();
g.setTransform(tran);
g.drawImage(bi, 0, 0, null);
g.dispose();
return flipped;
}
FlipVertical(BufferedImage bi) {
JPanel gui = new JPanel(new GridLayout(1,2,2,2));
gui.add(new JLabel(new ImageIcon(bi)));
gui.add(new JLabel(new ImageIcon(getFlippedImage(bi))));
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) throws AWTException {
final Robot robot = new Robot();
Runnable r = new Runnable() {
#Override
public void run() {
final BufferedImage bi = robot.createScreenCapture(
new Rectangle(0, 660, 200, 100));
new FlipVertical(bi);
}
};
SwingUtilities.invokeLater(r);
}
}
Whenever you're swapping variables, if your language doesn't allow for simultaneous assignment (and Java doesn't), you need to use a temporary variable.
Consider this:
a = 1;
b = 2;
a = b; // a is now 2, just like b
b = a; // b now uselessly becomes 2 again
Rather than that, do this:
t = a; // t is now 1
a = b; // a is now 2
b = t; // b is now 1
EDIT: And also what #vandale says in comments :P
If you are able to use the Graphics class, the following may be of use:
http://www.javaworld.com/javatips/jw-javatip32.html
And the Graphics class documentation:
http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html
Instead of using
Pixel bottomHalf = image.getPixel(h, w);
Pixel topHalf = image.getPixel(x, y);
//set bottom half pixels to corresponding top ones?
bottomHalf.setRed(topHalf.getRed());
bottomHalf.setGreen(topHalf.getGreen());
bottomHalf.setBlue(topHalf.getBlue());
//set top half pixels to corresponding bottom ones?
topHalf.setRed(bottomHalf.getRed());
topHalf.setGreen(bottomHalf.getGreen());
topHalf.setBlue(bottomHalf.getBlue());
You should have stored the bottomHalf's RGB into a temporary Pixel and used that to set topHalf after replacing bottomHalf's values (if you follow). You could have also really used something like this.... assuming your pixel operates on integer rgb values (which would have improved your main method).
private static final Pixel updateRGB(Pixel in, int red, int green, int blue) {
in.setRed(red); in.setGreen(green); in.setBlue(blue);
}
You want to flip the image upside down, not swap the top and bottom half.
The loop could look like this.
int topRow = 0;
int bottomRow = height-1;
while(topRow < bottomRow) {
for(int x = 0; x < width; x++) {
Pixel t = image.getPixel(x, topRow);
image.setPixel(x, topRow, image.getPixel(x, bottomRow));
image.setPixel(x, bottomRow, t);
}
topRow++;
bottomRow--;
}

Generate random "dark" colours only in Java

So I have the following method for generating random colours that I use in my application:
public final Color generateRandomColour() {
return Color.getHSBColor(new Random().nextFloat(),
new Random().nextFloat(), new Random().nextFloat());
}
I get a range of different colours, but as I'm using these colours to paint "rectangles" in Swing on a background with light colours, I'm interested in generating colours which are relatively dark. The background is a light grey so sometimes the generated random colours are also light grey which makes it hard to see the rectangles.
I've tried to put a cap on the max float values but it doesn't seem to get me darker colours only. Any help appreciated.
You might try:
public final Color generateDarkColor() {
return generateRandomColour().darker();
}
See also http://docs.oracle.com/javase/6/docs/api/java/awt/Color.html#darker()
This seems to work. Note to use the same instance of Random! The 1st image limits the B of the HSB to .5f, while 2nd image shows the effect of using Color.darker() instead.
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.util.Random;
import java.util.logging.*;
import javax.imageio.ImageIO;
import java.io.*;
class DarkHSB {
float darker = .5f;
Random r = new Random();
public final Color generateRandomColor(boolean useHsbApi) {
float brightness = (useHsbApi ? r.nextFloat() * darker : r.nextFloat());
// Random objects created sequentially will have the same seed!
Color c = Color.getHSBColor(
r.nextFloat(),
r.nextFloat(),
brightness);
if (!useHsbApi) c = c.darker();
return c;
}
public void paint(Graphics g, int x, int y, int w, int h, boolean useApi) {
g.setColor(generateRandomColor(useApi));
g.fillRect(x,y,w,h);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
final JPanel gui = new JPanel(new GridLayout(0,1));
final DarkHSB dhsb = new DarkHSB();
int w = 300;
int h = 100;
BufferedImage hsb = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
Graphics g = hsb.getGraphics();
int sz = 5;
for (int xx=0; xx<w; xx+=sz) {
for (int yy=0; yy<h; yy+=sz) {
dhsb.paint(g,xx,yy,sz,sz,true);
}
}
g.dispose();
BufferedImage darker = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
g = darker.getGraphics();
for (int xx=0; xx<w; xx+=sz) {
for (int yy=0; yy<h; yy+=sz) {
dhsb.paint(g,xx,yy,sz,sz,false);
}
}
g.dispose();
gui.add(new JLabel(new ImageIcon(hsb)));
gui.add(new JLabel(new ImageIcon(darker)));
JOptionPane.showMessageDialog(null, gui);
File userHome = new File(System.getProperty("user.home"));
File img = new File(userHome,"image-hsb.png");
dhsb.saveImage(hsb,img);
img = new File(userHome,"image-darker.png");
dhsb.saveImage(darker,img);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
public void saveImage(BufferedImage bi, File file) {
try {
ImageIO.write(bi, "png", file);
} catch (IOException ex) {
Logger.getLogger(DarkHSB.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
You could use % :
new Random().nextFloat() % maxValue;
Edit:
Didn't see you are using HSB (Hue Saturation Brightness).
Just decreasing the last value should be enough.
Try to generate your colour coordingates in another colour space, then transform to RGB. Is suggest you use LAB Color space. If you keep you L low then you get dark colours.
I've not checked but it looks like the conversion to RBG can be done by the ColorSpace class in the JDK.
i never used hsb model, but third value in HSB model is brightness so all waht you need to do is limit range of third value. this depends on how dark colours you want.
This should generate colours which are relatively dark.
Random r = new Random();
Color c = new Color(r.nextInt(100),r.nextInt(100),r.nextInt(100));

Java bitmap font: blitting 1-bit image with different colors

I'd like to implement a simple bitmap font drawing in Java AWT-based application. Application draws on a Graphics object, where I'd like to implement a simple algorithm:
1) Load a file (probably using ImageIO.read(new File(fileName))), which is 1-bit PNG that looks something like that:
I.e. it's 16*16 (or 16*many, if I'd like to support Unicode) matrix of 8*8 characters. Black corresponds to background color, white corresponds to foreground.
2) Draw strings character-by-character, blitting relevant parts of this bitmap to target Graphics. So far I've only succeeded with something like that:
int posX = ch % 16;
int posY = ch / 16;
int fontX = posX * CHAR_WIDTH;
int fontY = posY * CHAR_HEIGHT;
g.drawImage(
font,
dx, dy, dx + CHAR_WIDTH, dy + CHAR_HEIGHT,
fontX, fontY, fontX + CHAR_WIDTH, fontY + CHAR_HEIGHT,
null
);
It works, but, alas, it blits the text as is, i.e. I can't substitute black and white with desired foreground and background colors, and I can't even make background transparent.
So, the question is: is there a simple (and fast!) way in Java to blit part of one 1-bit bitmap to another, colorizing it in process of blitting (i.e. replacing all 0 pixels with one given color and all 1 pixels with another)?
I've researched into a couple of solutions, all of them look suboptimal to me:
Using a custom colorizing BufferedImageOp, as outlined in this solution - it should work, but it seems that it would be very inefficient to recolorize a bitmap before every blit operation.
Using multiple 32-bit RGBA PNG, with alpha channel set to 0 for black pixels and to maximum for foreground. Every desired foreground color should get its own pre-rendered bitmap. This way I can make background transparent and draw it as a rectangle separately before blitting and then select one bitmap with my font, pre-colorized with desired color and draw a portion of it over that rectangle. Seems like a huge overkill to me - and what makes this option even worse - it limits number of foreground colors to a relatively small amount (i.e. I can realistically load up and hold like hundreds or thousands of bitmaps, not millions)
Bundling and loading a custom font, as outlined in this solution could work, but as far as I see in Font#createFont documentation, AWT's Font seems to work only with vector-based fonts, not with bitmap-based.
May be there's already any libraries that implement such functionality? Or it's time for me to switch to some sort of more advanced graphics library, something like lwjgl?
Benchmarking results
I've tested a couple of algorithms in a simple test: I have 2 strings, 71 characters each, and draw them continuously one after another, right on the same place:
for (int i = 0; i < N; i++) {
cv.putString(5, 5, STR, Color.RED, Color.BLUE);
cv.putString(5, 5, STR2, Color.RED, Color.BLUE);
}
Then I measure time taken and calculate speed: string per second and characters per second. So far, various implementation I've tested yield the following results:
bitmap font, 16*16 characters bitmap: 10991 strings / sec, 780391 chars / sec
bitmap font, pre-split images: 11048 strings / sec, 784443 chars / sec
g.drawString(): 8952 strings / sec, 635631 chars / sec
colored bitmap font, colorized using LookupOp and ByteLookupTable: 404 strings / sec, 28741 chars / sec
You might turn each bitmap into a Shape (or many of them) and draw the Shape. See Smoothing a jagged path for the process of gaining the Shape.
E.G.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.Random;
/* Gain the outline of an image for further processing. */
class ImageShape {
private BufferedImage image;
private BufferedImage ImageShape;
private Area areaOutline = null;
private JLabel labelOutline;
private JLabel output;
private BufferedImage anim;
private Random random = new Random();
private int count = 0;
private long time = System.currentTimeMillis();
private String rate = "";
public ImageShape(BufferedImage image) {
this.image = image;
}
public void drawOutline() {
if (areaOutline!=null) {
Graphics2D g = ImageShape.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0,0,ImageShape.getWidth(),ImageShape.getHeight());
g.setColor(Color.RED);
g.setClip(areaOutline);
g.fillRect(0,0,ImageShape.getWidth(),ImageShape.getHeight());
g.setColor(Color.BLACK);
g.setClip(null);
g.draw(areaOutline);
g.dispose();
}
}
public Area getOutline(Color target, BufferedImage bi) {
// construct the GeneralPath
GeneralPath gp = new GeneralPath();
boolean cont = false;
int targetRGB = target.getRGB();
for (int xx=0; xx<bi.getWidth(); xx++) {
for (int yy=0; yy<bi.getHeight(); yy++) {
if (bi.getRGB(xx,yy)==targetRGB) {
if (cont) {
gp.lineTo(xx,yy);
gp.lineTo(xx,yy+1);
gp.lineTo(xx+1,yy+1);
gp.lineTo(xx+1,yy);
gp.lineTo(xx,yy);
} else {
gp.moveTo(xx,yy);
}
cont = true;
} else {
cont = false;
}
}
cont = false;
}
gp.closePath();
// construct the Area from the GP & return it
return new Area(gp);
}
public JPanel getGui() {
JPanel images = new JPanel(new GridLayout(1,2,2,2));
JPanel gui = new JPanel(new BorderLayout(3,3));
JPanel originalImage = new JPanel(new BorderLayout(2,2));
final JLabel originalLabel = new JLabel(new ImageIcon(image));
originalImage.add(originalLabel);
images.add(originalImage);
ImageShape = new BufferedImage(
image.getWidth(),
image.getHeight(),
BufferedImage.TYPE_INT_RGB
);
labelOutline = new JLabel(new ImageIcon(ImageShape));
images.add(labelOutline);
anim = new BufferedImage(
image.getWidth()*2,
image.getHeight()*2,
BufferedImage.TYPE_INT_RGB);
output = new JLabel(new ImageIcon(anim));
gui.add(output, BorderLayout.CENTER);
updateImages();
gui.add(images, BorderLayout.NORTH);
animate();
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
animate();
}
};
Timer timer = new Timer(1,al);
timer.start();
return gui;
}
private void updateImages() {
areaOutline = getOutline(Color.BLACK, image);
drawOutline();
}
private void animate() {
Graphics2D gr = anim.createGraphics();
gr.setColor(Color.BLUE);
gr.fillRect(0,0,anim.getWidth(),anim.getHeight());
count++;
if (count%100==0) {
long now = System.currentTimeMillis();
long duration = now-time;
double fraction = (double)duration/1000;
rate = "" + (double)100/fraction;
time = now;
}
gr.setColor(Color.WHITE);
gr.translate(0,0);
gr.drawString(rate, 20, 20);
int x = random.nextInt(image.getWidth());
int y = random.nextInt(image.getHeight());
gr.translate(x,y);
int r = 128+random.nextInt(127);
int g = 128+random.nextInt(127);
int b = 128+random.nextInt(127);
gr.setColor(new Color(r,g,b));
gr.draw(areaOutline);
gr.dispose();
output.repaint();
}
public static void main(String[] args) throws Exception {
int size = 150;
final BufferedImage outline = javax.imageio.ImageIO.read(new java.io.File("img.gif"));
ImageShape io = new ImageShape(outline);
JFrame f = new JFrame("Image Outline");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(io.getGui());
f.pack();
f.setResizable(false);
f.setLocationByPlatform(true);
f.setVisible(true);
}
}
I have to figure there is a factor of ten error in the FPS count on the top left of the blue image though. 50 FPS I could believe, but 500 FPS seems ..wrong.
Okay, looks like I've found the best solution. The key to success was accessing raw pixel arrays in underlying AWT structures. Initialization goes something like that:
public class ConsoleCanvas extends Canvas {
protected BufferedImage buffer;
protected int w;
protected int h;
protected int[] data;
public ConsoleCanvas(int w, int h) {
super();
this.w = w;
this.h = h;
}
public void initialize() {
data = new int[h * w];
// Fill data array with pure solid black
Arrays.fill(data, 0xff000000);
// Java's endless black magic to get it working
DataBufferInt db = new DataBufferInt(data, h * w);
ColorModel cm = ColorModel.getRGBdefault();
SampleModel sm = cm.createCompatibleSampleModel(w, h);
WritableRaster wr = Raster.createWritableRaster(sm, db, null);
buffer = new BufferedImage(cm, wr, false, null);
}
#Override
public void paint(Graphics g) {
update(g);
}
#Override
public void update(Graphics g) {
g.drawImage(buffer, 0, 0, null);
}
}
After this one, you've got both a buffer that you can blit on canvas updates and underlying array of ARGB 4-byte ints - data.
Single character can be drawn like that:
private void putChar(int dx, int dy, char ch, int fore, int back) {
int charIdx = 0;
int canvasIdx = dy * canvas.w + dx;
for (int i = 0; i < CHAR_HEIGHT; i++) {
for (int j = 0; j < CHAR_WIDTH; j++) {
canvas.data[canvasIdx] = font[ch][charIdx] ? fore : back;
charIdx++;
canvasIdx++;
}
canvasIdx += canvas.w - CHAR_WIDTH;
}
}
This one uses a simple boolean[][] array, where first index chooses character and second index iterates over raw 1-bit character pixel data (true => foreground, false => background).
I'll try to publish a complete solution as a part of my Java terminal emulation class set soon.
This solution benchmarks for impressive 26007 strings / sec or 1846553 chars / sec - that's 2.3x times faster than previous best non-colorized drawImage().

Categories