I want to create a PNG Image with Java. In this image I want to show some random Text.
Normaly I would create a picture like this:
BufferedImage bi = new BufferedImage(300,300,BufferedImage.TYPE_INT_ARGB);
bi.getGraphics().drawString("Hello world", 0, 0);
ImageIO.write(bi, "png", File.createTempFile("out", ".png"));
I know that I can calculate the String length with the following code:
bi.getGraphics().getFontMetrics().stringWidth("Hello world");
But to execute this, I need a Graphics Object (which I grab from the BufferedImage). So I must declare BufferedImage before I can use stringWidth.
The result is, that the image is much bigger than needed.
The only way I see is to create a "dummy BufferedImage". So I can calculate the needed width & height and after that I can create a BufferedImage that fit.
I can't find a better solution, but maybe someone can help me.
Thanks a lot.
Sorry, in a hurry, but consider this test:
public static void main(String[] args) {
Font font = Font.decode(Font.MONOSPACED);
Rectangle2D bounds;
String str = "Hello World";
BufferedImage dummy = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
FontRenderContext context = new FontRenderContext(new AffineTransform(), true, true);
bounds = font.getStringBounds(str, context);
System.err.println("bounds: " + bounds);
bounds = font.getStringBounds(str, dummy.createGraphics().getFontRenderContext());
System.err.println("bounds: " + bounds);
Graphics2D graphics = dummy.createGraphics();
FontMetrics fontMetrics = graphics.getFontMetrics(font);
bounds = fontMetrics.getStringBounds(str, graphics);
System.err.println("bounds: " + bounds);
}
Output:
bounds: java.awt.geom.Rectangle2D$Float[x=0.0,y=-12.064453,w=79.21289,h=15.667969]
bounds: java.awt.geom.Rectangle2D$Float[x=0.0,y=-12.064453,w=77.0,h=15.667969]
bounds: java.awt.geom.Rectangle2D$Float[x=0.0,y=-12.064453,w=77.0,h=15.667969]
So it seems that just creating a dummy is most likely to get the desired result (the docs also states that creating a FontRenderContext might give unexpected/undefined results).
Related
I just want to place two different images on one canvas and make it a .jpg file in Java. I just want to make a resulting file, not a GUI.
I want to make a result file like below with both images above:
You can use BufferedImage to combine the two images. The following code shows a simple implementation:
public static void combineImages(String imagePath1, String imagePath2, String outputPath) throws IOException {
int intervalWidth = 20; // The interval between two images
BufferedImage image1 = ImageIO.read(new File(imagePath1));
BufferedImage image2 = ImageIO.read(new File(imagePath2));
int combinedWidth = image1.getWidth() + image2.getWidth() + intervalWidth;
int combinedHeight = Math.max(image1.getHeight(), image2.getHeight());
BufferedImage combined = new BufferedImage(combinedWidth, combinedHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = combined.createGraphics();
g.setColor(Color.WHITE);
// Fill the background with white
g.fillRect(0, 0, combinedWidth, combinedHeight);
// Draw the two images on the combined image
g.drawImage(image1, 0, 0, null);
g.drawImage(image2, image1.getWidth() + intervalWidth, 0, null);
ImageIO.write(combined, "jpg", new File(outputPath));
}
How to blur a portion of an image, to hide some privates parts like credit card informations.
I try to use ConvolveOp.class like :
float[] matrix = new float[400];
for (int i = 0; i < 400; i++)
matrix[i] = 1.0f/500.0f;
BufferedImage sourceImage = (BufferedImage) image; ;
BufferedImage destImage = null ;
BufferedImageOp op = new ConvolveOp( new Kernel(20, 20, matrix), ConvolveOp.EDGE_NO_OP, null );
BufferedImage blurredImage = op.filter(sourceImage, destImage);
it seems to work, except that the image is completely blurred.
In the case you want to focus on the application and not on the specifics of image processing, you can use an image processing framework like Marvin. Thus, you can do more with less code.
Input image:
Output image:
Source code:
import static marvin.MarvinPluginCollection.*;
public class PortionBlur {
public PortionBlur(){
// 1. Load image
MarvinImage image = MarvinImageIO.loadImage("./res/credit_card.jpg");
// 2. Create masks for each blurred region
MarvinImageMask mask1 = new MarvinImageMask(image.getWidth(), image.getHeight(), 38,170,345,24);
MarvinImageMask mask2 = new MarvinImageMask(image.getWidth(), image.getHeight(), 52,212,65,24);
MarvinImageMask mask3 = new MarvinImageMask(image.getWidth(), image.getHeight(), 196,212,65,20);
MarvinImageMask mask4 = new MarvinImageMask(image.getWidth(), image.getHeight(), 38,240,200,20);
// 3. Process Image with each mask
GaussianBlur gaussianBlur = new GaussianBlur();
gaussianBlur.load();
gaussianBlur.attributes.set("radius",15);
gaussianBlur.process(image.clone(), image, mask1);
gaussianBlur.process(image.clone(), image, mask2);
gaussianBlur.process(image.clone(), image, mask3);
gaussianBlur.process(image.clone(), image, mask4);
// 4. Save the final image
MarvinImageIO.saveImage(image, "./res/credit_card_out.jpg");
}
public static void main(String[] args) {
new PortionBlur();
System.exit(0);
}
}
Gaussian blur algorithm source code:
https://github.com/gabrielarchanjo/marvinproject/blob/master/marvinproject/dev/MarvinPlugins/src/org/marvinproject/image/blur/gaussianBlur/GaussianBlur.java
I don't know whether this can be done by changing the matrix values, but this should definitely be possible by filtering a subimage, since, according to the BufferedImage.getSubimage() documentation:
The returned BufferedImage shares the same data array as the original image.
So the original BufferedImage should change with code like this:
BufferedImage image = /* ... */;
BufferedImage subImage = image.getSubimage(10, 20, 30, 40); // x, y, width, height
new ConvolveOp(new Kernel(20, 20, matrix), ConvolveOp.EDGE_NO_OP, null).filter(subImage, subImage);
I didn't test this though, and I can imagine that filter doesn't work as expected if source and destination are the same, in which case you could use a copy of the subimage, using the solution from this question:
BufferedImage image = /* ... */;
BufferedImage dest = image.getSubimage(10, 20, 30, 40); // x, y, width, height
ColorModel cm = dest.getColorModel();
BufferedImage src = new BufferedImage(cm, dest.copyData(dest.getRaster().createCompatibleWritableRaster()), cm.isAlphaPremultiplied(), null).getSubimage(0, 0, dest.getWidth(), dest.getHeight());
new ConvolveOp(new Kernel(20, 20, matrix), ConvolveOp.EDGE_NO_OP, null).filter(src, dest);
After that, continue working with image (not subImage, src or dest!)
Is it possible to implement the first example with Scalr?
My code is the following:
BufferedImage thumbnail = Scalr.resize(ImageIO.read(sourceFile), Scalr.Method.ULTRA_QUALITY, Scalr.Mode.FIT_TO_WIDTH,
width, height, Scalr.OP_ANTIALIAS);
ImageIO.write(thumbnail, destinationfile.getExtension(), destinationfile);
What I want is to receive the image like this:
where the blue bars are the space I want to fill with the color.
Thank you
Update: maybe it is possible to implement with Thumbnailator?
Just done! Perhaps it can help you!
public static BufferedImage resizeAndCrop(BufferedImage bufferedImage) throws IOException {
int himg = bufferedImage.getHeight();
int wimg = bufferedImage.getWidth();
double rateh = himg/dim;
double ratew = wimg/dim;
double rate = ratew;
if(rateh>ratew)
rate = rateh;
int dimhimg = (int) (himg/rate);
int dimwimg = (int) (wimg/rate);
double startw = dim/2 - dimwimg/2;
double starth = dim/2 - dimhimg/2;
BufferedImage tThumbImage = new BufferedImage( dim, dim, BufferedImage.TYPE_INT_RGB );
Graphics2D tGraphics2D = tThumbImage.createGraphics(); //create a graphics object to paint to
tGraphics2D.setBackground( Color.WHITE );
tGraphics2D.setPaint( Color.WHITE );
tGraphics2D.fillRect( 0, 0, dim, dim );
tGraphics2D.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
tGraphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
tGraphics2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
tGraphics2D.drawImage( bufferedImage, (int)startw, (int)starth, dimwimg, dimhimg, null ); //draw the image scaled
File ff = new File(path + "jdata/tmp/prova.jpg");
ImageIO.write( tThumbImage, "JPG", ff); //write the image to a file
BufferedImage croppedContainMethod = ImageIO.read(ff);
return croppedContainMethod;
}
Nobody has idea so I will publish my solution...
I decided to continue to use Scalr (I didn't checked the Thumbnailator's last version but the previous ones failed on big pictures).
So first of all I call resize method, and then, if sizes of the new thumbnail are bigger then given ones I call crop method that crops a thumbnail by the center.. The code is the following:
BufferedImage thumbnail = Scalr.resize(sourceFile, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, destinationSize.width, destinationSize.height);
if (thumbnail.getWidth() > destinationSize.width)
thumbnail = Scalr.crop(thumbnail, (thumbnail.getWidth() - destinationSize.width) / 2, 0, destinationSize.width, destinationSize.height);
else if (thumbnail.getHeight() > destinationSize.height)
thumbnail = Scalr.crop(thumbnail, 0, (thumbnail.getHeight() - destinationSize.height) / 2, destinationSize.width, destinationSize.height);
It is not ideal, but at least it handles 'wide' images after generation of thumbnails
I want to remove a strip (widthways) of an image by moving the top part of the image to the bottom. Currently I'm doing this but I think that maybe there is a more efficiently way to achieve this without creating a new BufferedImage.
Snippet code:
BufferedImage myImage = ...;
...
BufferedImage imgPart_1 = myImage.getSubimage(0, 0, myImage.getWidth(), (myImage.getHeight()/2)-50);
BufferedImage imgPart_2 = myImage.getSubimage(0, myImage.getHeight()/2, myImage.getWidth(), myImage.getHeight()/2);
BufferedImage newImage = new BufferedImage(myImage.getWidth(), myImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = newImage.createGraphics();
g.drawImage(imgPart_1, 0, 0, null);
g.drawImage(imgPart_2, 0, imgPart_1.getHeight(), null);
myImage = newImage;
...
Thanks in advance.
You will have to create a new Image, but you don't have to paint it yourself.
You can use the CropImageFilter to get your image.
Toolkit toolkit = Toolkit.getDefaultToolkit();
CropImageFilter cropFilter = new CropImageFilter
(x, y, imageWidth, imageHeight);
Image croppedImage = toolkit.createImage(new FilteredImageSource
(image.getSource(), cropFilter));
Looks pretty efficient to me: are you really sure there is a performance problem here?
If you really want to avoid creating a new bufferedimage, you can use myImage as the destination, i.e. just do:
Graphics g = myImage.createGraphics();
g.drawImage(imgPart_1, 0, 0, null);
g.drawImage(imgPart_2, 0, imgPart_1.getHeight(), null);
I think this will work OK in your case, although you will need to test (there can be some odd effects when the source and destination areas are overlapping!)
I have generated the barcode using barbecue and now I want to resize the barcode as per my need. I tried with BufferedImage and then I got barcode with different size but then I get an additional black line under the barcode.
public class GenerateBarcode {
public static void main(String[] args) throws Exception {
String initialString = JOptionPane.showInputDialog("Enter the text here");
Barcode barcode = BarcodeFactory.createCode128(initialString);
BufferedImage bi = BarcodeImageHandler.getImage(barcode);
}
}
Here I want to resize "bi".
To resize any BufferedImage, you can create a new one and draw your old one on top of it with a scaling applied. For example:
double scale = 2;
BufferedImage scaledBi = new BufferedImage((int)(bi.getWidth()*scale), (int) (bi.getHeight()*scale), bi.getType());
Graphics2D g2 = scaledBi.createGraphics();
g2.drawImage(bi, 0, 0, scaledBi.getWidth(), scaledBi.getHeight(), 0, 0, bi.getWidth(), bi.getHeight(), null);
scaledBi now contains your scaled image. Note that this is not vector based, so I am not sure of the quality. To increase scaling quality, you can play with the rendering hints.
Try this code:
Barcode b = BarcodeFactory.create2of7(jTextField1.getText());
b.setBarHeight(5);
b.setBarWidth(1);