Loading an animated image to a BufferedImage array - java

I'm trying to implement animated textures into an OpenGL game seamlessly. I made a generic ImageDecoder class to translate any BufferedImage into a ByteBuffer. It works perfectly for now, though it doesn't load animated images.
I'm not trying to load an animated image as an ImageIcon. I need the BufferedImage to get an OpenGL-compliant ByteBuffer.
How can I load every frames as a BufferedImage array in an animated image ?
On a similar note, how can I get the animation rate / period ?
Does Java handle APNG ?

The following code is an adaption from my own implementation to accommodate the "into array" part.
The problem with gifs is: There are different disposal methods which have to be considered, if you want this to work with all of them. The code below tries to compensate for that. For example there is a special implementation for "doNotDispose" mode, which takes all frames from start to N and paints them on top of each other into a BufferedImage.
The advantage of this method over the one posted by chubbsondubs is that it does not have to wait for the gif animation delays, but can be done basically instantly.
BufferedImage[] array = null;
ImageInputStream imageInputStream = ImageIO.createImageInputStream(new ByteArrayInputStream(data)); // or any other source stream
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
while (imageReaders.hasNext())
{
ImageReader reader = (ImageReader) imageReaders.next();
try
{
reader.setInput(imageInputStream);
frames = reader.getNumImages(true);
array = new BufferedImage[frames];
for (int frameId : frames)
{
int w = reader.getWidth(0);
int h = reader.getHeight(0);
int fw = reader.getWidth(frameId);
int fh = reader.getHeight(frameId);
if (h != fh || w != fw)
{
GifMeta gm = getGifMeta(reader.getImageMetadata(frameId));
// disposalMethodNames: "none", "doNotDispose","restoreToBackgroundColor","restoreToPrevious",
if ("doNotDispose".equals(gm.disposalMethod))
{
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) image.getGraphics();
for (int f = 0; f <= frameId; f++)
{
gm = getGifMeta(reader.getImageMetadata(f));
if ("doNotDispose".equals(gm.disposalMethod))
{
g.drawImage(reader.read(f), null, gm.imageLeftPosition, gm.imageTopPosition);
}
else
{
// XXX "Unimplemented disposalMethod (" + getName() + "): " + gm.disposalMethod);
}
}
g.dispose();
}
else
{
image = reader.read(frameId);
// XXX "Unimplemented disposalMethod (" + getName() + "): " + gm.disposalMethod;
}
}
else
{
image = reader.read(frameId);
}
if (image == null)
{
throw new NullPointerException();
}
array[frame] = image;
}
}
finally
{
reader.dispose();
}
}
return array;
private final static class GifMeta
{
String disposalMethod = "none";
int imageLeftPosition = 0;
int imageTopPosition = 0;
int delayTime = 0;
}
private GifMeta getGifMeta(IIOMetadata meta)
{
GifMeta gm = new GifMeta();
final IIOMetadataNode gifMeta = (IIOMetadataNode) meta.getAsTree("javax_imageio_gif_image_1.0");
NodeList childNodes = gifMeta.getChildNodes();
for (int i = 0; i < childNodes.getLength(); ++i)
{
IIOMetadataNode subnode = (IIOMetadataNode) childNodes.item(i);
if (subnode.getNodeName().equals("GraphicControlExtension"))
{
gm.disposalMethod = subnode.getAttribute("disposalMethod");
gm.delayTime = Integer.parseInt(subnode.getAttribute("delayTime"));
}
else if (subnode.getNodeName().equals("ImageDescriptor"))
{
gm.imageLeftPosition = Integer.parseInt(subnode.getAttribute("imageLeftPosition"));
gm.imageTopPosition = Integer.parseInt(subnode.getAttribute("imageTopPosition"));
}
}
return gm;
}

I don't think Java supports APNG by default, but you can use an 3rd party library to parse it:
http://code.google.com/p/javapng/source/browse/trunk/javapng2/src/apng/com/sixlegs/png/AnimatedPngImage.java?r=300
That might be your easiest method. As for getting the frames from an animated gif you have to register an ImageObserver:
new ImageIcon( url ).setImageObserver( new ImageObserver() {
public void imageUpdate( Image img, int infoFlags, int x, int y, int width, int height ) {
if( infoFlags & ImageObserver.FRAMEBITS == ImageObserver.FRAMEBITS ) {
// another frame was loaded do something with it.
}
}
});
This loads asynchronously on another thread so imageUpdate() won't be called immediately. But it will be called for each frame as it parses it.
http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/image/ImageObserver.html

Related

Printing out a bitmap QR code image with Brother Label Printer SDK prints out a blank label

I need to be able to print out a bitmap QR Code using my Brother QL-720NW.
As of right now, I'm able to generate a QR code bitmap and display it properly in an ImageView. On a button press, the user needs to be able to print that QR code bitmap from the Brother label printer.
I am able to make a connection to the printer, but I can only print out blank labels that do not show the QR code. How can I fix this so that the bitmap appears on the printed label properly?
Method for printing bitmap:
void printImage(Bitmap bitmap) {
// Specify printer
final Printer printer = new Printer();
PrinterInfo settings = printer.getPrinterInfo();
settings.ipAddress = "192.168.2.149";
settings.workPath = "/storage/emulated/0/Download";
settings.printerModel = PrinterInfo.Model.QL_720NW;
settings.port = PrinterInfo.Port.NET;
settings.orientation = PrinterInfo.Orientation.LANDSCAPE;
//settings.paperSize = PrinterInfo.PaperSize.CUSTOM;
settings.align = PrinterInfo.Align.CENTER;
settings.valign = PrinterInfo.VAlign.MIDDLE;
settings.printMode = PrinterInfo.PrintMode.ORIGINAL;
settings.numberOfCopies = 1;
settings.labelNameIndex = LabelInfo.QL700.W62RB.ordinal();
settings.isAutoCut = true;
settings.isCutAtEnd = false;
printer.setPrinterInfo(settings);
// Connect, then print
new Thread(new Runnable() {
#Override
public void run() {
if (printer.startCommunication()) {
Log.e("Tag: ", "Connection made.");
PrinterStatus result = printer.printImage(bitmap);
Log.e("Tag: ", "Printing!");
if (result.errorCode != PrinterInfo.ErrorCode.ERROR_NONE) {
Log.d("TAG", "ERROR - " + result.errorCode);
}
printer.endCommunication();
}
else {
Log.e("Tag: ", "Cannot make a connection.");
}
}
}).start();
}
Generating bitmap:
Bitmap encodeAsBitmap(String str) throws WriterException {
QRCodeWriter writer = new QRCodeWriter();
BitMatrix bitMatrix = writer.encode(str, BarcodeFormat.QR_CODE, 100, 100);
int w = bitMatrix.getWidth();
int h = bitMatrix.getHeight();
int[] pixels = new int[w * h];
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
pixels[y * w + x] = bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, w, 0, 0, w, h);
return bitmap;
}
Solved it, I was using LabelInfo.QL700.W62RB.ordinal() for the LabelNameIndex when I should have been using LabelInfo.QL700.W62.ordinal().
Works perfectly now!

Find dynamic image on sikuliX created problems with buffer

I'm trying to find an image that I create previously inside an empty template with this function that insert text on it receiving color, content and font and return the path of generate image:
the template
public String insertTextOnBlanck(String colorLetter,String text,Font font) {
//path is a private varibable initialized with the constructor
File blankFile = new File("images/dinamic/"+path);
BufferedImage image = null;
String exit_path = null;
try {
image = ImageIO.read(blankFile);
int type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType();
Graphics2D g2 = image.createGraphics();
FontMetrics metrics = g2.getFontMetrics(font);
BufferedImage resizeImage = resizeImage(image,type, text,metrics);
image.flush();
int w = resizeImage.getWidth();
int h = resizeImage.getHeight();
g2 = resizeImage.createGraphics();
g2.setColor(Color.decode(colorLetter));
g2.setFont(font);
// Get the FontMetrics
int x = (w - metrics.stringWidth(text)) / 2;
int y = (metrics.getAscent() + (h - (metrics.getAscent() + metrics.getDescent())) / 2);
g2.setBackground(Color.decode("#d1e8f8"));
g2.drawString(text, x, y);
g2.dispose();
//create image with text
exit_path = "images/dinamic/changed_"+path;
File file = new File(exit_path);
ImageIO.write(resizeImage, "png", file);
resizeImage.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return exit_path;
}
and this works the fist time when i call this other function
public void dinamicClick(String path,String input,String fontLetter,String colorLetter, int fontType,int size) throws FindFailed {
DinamicImg DimImg = new DinamicImg();
DimImg.setPath(path);
String modPath = DimImg.insertTextOnBlanck(
colorLetter,
input,//Inventario de recurso
new Font(fontLetter,fontType, size)
);
Iterator<Match> myIt = s.findAll(modPath);
while (myIt.hasNext()) {
Location loc = myIt.next().getTarget();
s.click(loc);
}
myIt.remove();
removeFile(modPath);
}
the removeFile function is:
private void removeFile(String toRemove) {
File file = new File(toRemove);
if(file.delete()){
System.out.println(file.getName() + " is deleted!");
}else{
System.out.println("Delete operation is failed.");
}
}
The result:
but next calls dont work at all, just when i change the name of exit path, so i thought was a cache problem but adding ImageIO.setUseCache(false); at start of "insertTextOnBlanck" function still doesn`t work. Im out of ideas please help, thanks.
I resolve it , with the libary org.sikuli.script.ImagePath you ve to reset the paths of the internal cache of SikuliX with ImagePath.reset().

PDFBox: put two A4 pages on one A3

I have a pdf document with one or more pages A4 paper.
The resulting pdf document should be A3 paper where each page contains two from the first one (odd on the left, even on the right side).
I already got it to render the A4 pages into images and the odd pages are successfully placed on the first parts of a new A3 pages but I cannot get the even pages to be placed.
public class CreateLandscapePDF {
public void renderPDF(File inputFile, String output) {
PDDocument docIn = null;
PDDocument docOut = null;
float width = 0;
float height = 0;
float posX = 0;
float posY = 0;
try {
docIn = PDDocument.load(inputFile);
PDFRenderer pdfRenderer = new PDFRenderer(docIn);
docOut = new PDDocument();
int pageCounter = 0;
for(PDPage pageIn : docIn.getPages()) {
pageIn.setRotation(270);
BufferedImage bufferedImage = pdfRenderer.renderImage(pageCounter);
width = bufferedImage.getHeight();
height = bufferedImage.getWidth();
PDPage pageOut = new PDPage(PDRectangle.A3);
PDImageXObject image = LosslessFactory.createFromImage(docOut, bufferedImage);
PDPageContentStream contentStream = new PDPageContentStream(docOut, pageOut, AppendMode.APPEND, true, true);
if((pageCounter & 1) == 0) {
pageOut.setRotation(90);
docOut.addPage(pageOut);
posX = 0;
posY = 0;
} else {
posX = 0;
posY = width;
}
contentStream.drawImage(image, posX, posY);
contentStream.close();
bufferedImage.flush();
pageCounter++;
}
docOut.save(output + "\\LandscapeTest.pdf");
docOut.close();
docIn.close();
} catch(IOException io) {
io.printStackTrace();
}
}
}
I'm using Apache PDFBox 2.0.2 (pdfbox-app-2.0.2.jar)
Thank you very much for your help and the link to the other question - I think I already read it but wasn't able to use in in my code yet.
But finally the PDFClown made the job, though I think it's not very nice to use PDFBox and PDFClown in the same program.
Anyway here's my working code to combine A4 pages on A3 paper.
public class CombinePages {
public void run(String input, String output) {
try {
Document source = new File(input).getDocument();
Pages sourcePages = source.getPages();
Document target = new File().getDocument();
Page targetPage = null;
int pageCounter = 0;
double moveByX = .0;
for(Page sourcePage : source.getPages()) {
if((pageCounter & 1) == 0) {
//even page gets a blank page
targetPage = new Page(target);
target.setPageSize(PageFormat.getSize(PageFormat.SizeEnum.A3, PageFormat.OrientationEnum.Landscape));
target.getPages().add(targetPage);
moveByX = .0;
} else {
moveByX = .50;
}
//get content from source page
XObject xObject = sourcePages.get(pageCounter).toXObject(target);
PrimitiveComposer composer = new PrimitiveComposer(targetPage);
Dimension2D targetSize = targetPage.getSize();
Dimension2D sourceSize = xObject.getSize();
composer.showXObject(xObject, new Point2D.Double(targetSize.getWidth() * moveByX, targetSize.getHeight() * .0), new Dimension(sourceSize.getWidth(), sourceSize.getHeight()), XAlignmentEnum.Left, YAlignmentEnum.Top, 0);
composer.flush();
pageCounter++;
}
target.getFile().save(output + "\\CombinePages.pdf", SerializationModeEnum.Standard);
source.getFile().close();
} catch (FileNotFoundException fnf) {
log.error(fnf);
} catch (IOException io) {
log.error(io);
}
}
}

Write text to image in multiple fonts with wrapping in java

I am trying to create an image with a given text and style. eg;
" textStyle(Offer ends 25/12/2016. Exclusions Apply., disclaimer) textStyle(See Details,underline) "
In above line i am splitting and creating a map that stores the first parameter of textStyle block as key and second parameter as value where second param defines the style to be applied on first param. Hence an entry of map will look like .
Now when i iterate over this map to write the text to image i check if the text is overflowing the width. If yes then it breaks the text and adds it to next line in the horizontal center. So for example lets say i am trying to write "Offer ends 25/12/2016. Exclusions Apply." with Arial and font size 12. While writing i find that i can write till "Offer ends 23/12/2016. " only and "Exclusions apply" has to go in next line. But it writes the text in horizontal center neglecting that as there is space left horizontally i can write "See Details" too in the same line.
Please help. Below is the code what i have tried. I have also tried creating a JTextPane and then converting it to image but this cannot be an option as it first creates the frame, makes it visible, writes it and then disposes it. And most of the times i was getting Nullpointer exception on SwingUtilities.invokeAndWait.
Actual : http://imgur.com/7aIlcEQ
Expected http://imgur.com/038zQTZ
public static BufferedImage getTextImage(String textWithoutStyle, Map<String, String> textToThemeMap, Properties prop, int height, int width) {
BufferedImage img = new BufferedImage(width,height,BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2d = img.createGraphics();
g2d.setPaint(Color.WHITE);
FontMetrics fm = g2d.getFontMetrics();
Map<String, Font> textToFontMap = new LinkedHashMap<String, Font>();
for(Map.Entry<String, String> entry : textToThemeMap.entrySet()) {
if(StringUtils.isNotBlank(entry.getKey()) && StringUtils.isNotBlank(entry.getValue())) {
Font font = getFont(prop, entry.getValue().trim());
g2d.setFont(font);
fm = g2d.getFontMetrics();
String string = entry.getKey();
char[] chars = null;
int i = 0, pixelWidth = 0;
List<String> newTextList = new ArrayList<String>();
if(fm.stringWidth(string) > (width - 10)) {
chars = string.toCharArray();
for (i = 0; i < chars.length; i++) {
pixelWidth = pixelWidth + fm.charWidth(chars[i]);
if(pixelWidth >= (width - 10)) {
break;
}
}
String newString = WordUtils.wrap(string, i, "\n",false);
String[] splitString = newString.split("\n");
for(String str : splitString) {
newTextList.add(str);
textToFontMap.put(string, font);
}
} else {
newTextList.add(string);
textToFontMap.put(string, font);
}
}
}
Font font = new Font("Arial", Font.BOLD, 14);
int spaceOfLineHeight = (textToFontMap.size() - 1) * 7;
int spaceOfText = textToFontMap.size() * font.getSize();
int totalSpace = spaceOfLineHeight + spaceOfText ;
int marginRemaining = height - totalSpace;
int tempHt = marginRemaining / 2 + 10;
String txt = null;
for(Map.Entry<String, Font> entry : textToFontMap.entrySet()) {
txt = entry.getKey();
font = entry.getValue();
g2d.setFont(font);
fm = g2d.getFontMetrics();
int x = (width - fm.stringWidth(txt)) / 2;
int y = tempHt;
g2d.drawString(txt, x, y);
tempHt = tempHt + fm.getHeight();
}
// g2d.drawString(text.getIterator(), 0, (int)lm.getAscent() + lm.getHeight());
// g2d.dispose();
return img;
}
// Code with JTextPane ------------------------------------------
public static BufferedImage getTextImage(final Map < String, String > textToThemeMap, final Properties prop, final int height, final int width) throws Exception {
JFrame f = new JFrame();
f.setSize(width, height);
final StyleContext sc = new StyleContext();
DefaultStyledDocument doc = new DefaultStyledDocument(sc);
final JTextPane pane = new JTextPane(doc);
pane.setSize(width, height);
// Build the styles
final Paragraph[] content = new Paragraph[1];
Run[] runArray = new Run[textToThemeMap.size()];
int i = 0;
for (Map.Entry < String, String > entry: textToThemeMap.entrySet()) {
if (StringUtils.isNotBlank(entry.getValue().trim()) && StringUtils.isNotBlank(entry.getKey().trim())) {
Run run = new Run(entry.getValue().trim(), entry.getKey());
runArray[i++] = run;
}
}
content[0] = new Paragraph(null, runArray);
/*createDocumentStyles(sc, prop,textToThemeMap.values());
addText(pane, sc, sc.getStyle("default"), content);
pane.setEditable(false);*/
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
try {
createDocumentStyles(sc, prop, textToThemeMap.values());
} catch (MalformedURLException e) {
//e.printStackTrace();
}
addText(pane, sc, sc.getStyle("default"), content);
pane.setEditable(false);
}
});
} catch (Exception e) {
System.out.println("Exception when constructing document: " + e);
}
f.getContentPane().add(pane);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D gd = img.createGraphics();
f.paint(gd);
f.dispose();
/*ImageIO.write(img, "png", new File("C:\\Users\\spande0\\Desktop\\a.png"));
System.out.println("done");*/
return img;
}
I suspect the issue is in your 'Y' computation.
int spaceOfLineHeight = (newTextList.size() - 1) * 7;
int spaceOfText = newTextList.size() * font.getSize();
int totalSpace = spaceOfLineHeight + spaceOfText;
int marginRemaining = height - totalSpace;
int tempHt = marginRemaining / 2 + 10;
You have to keep the height occupied by the previous lines, and add it to the current 'Y'.
At the moment, for all the lines, the 'Y' values is same.
Declare prevHeight outside the for loop. and then do the following.
int tempHt = marginRemaining / 2 + 10;
tempHT += prevHeight;
prevHeight = tempHeight
Based on the comments, I will suggest you to break down your function into two smaller functions.
// Loop through the strings and find out how lines are split and calculate the X, Y
// This function will give the expected lines
splitLinesAndComputeResult
// Just render the lines
renderLines

Adding jpg images together using java

I am trying to take severial jpg images with the same dimensions(30*30) and create a single image. Like this:
Image i = new BufferedImage(30, 30, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) i.getGraphics();
if (this instanceof Node) {
Image img;
img = getImageFromFile(Node.icon);
g2.drawImage(img, 0, 0, null);
}
if(this instanceof ForceNode){
Image img;
img = getImageFromFile(ForceNode.forceicon);
g2.drawImage(img, 0, 0, null);
}
if(this instanceof TunnelNode){
Image img;
img = getImageFromFile(TunnelNode.tunnelicon);
g2.drawImage(img, 0, 0, null);
}
....
public Image getImageFromFile(File file) {
Image image = null;
try {
image = ImageIO.read(file);
} catch (IOException e) {
Logger.getLogger(HackerGame.class.getName()).log(Level.SEVERE, null, e);
return null;
}
return image;
}
I realize there are some issues with G2D not being strictly necessary, but my issue is this:
These images needs to be put on top of each other to create a whole image. Eash of the images are small areas of the whole picture, that needs to be put on top of(Not next to) each other to create the actual image. The problem right now however is that the last drawImage method overwrites the entire image, so i am left with the last "bit of image" instead of my compiled image.
I suspect this is because the white areas of my pictures are not being treated as transparent, but how do i get around this. I have next to no experience with image encoding so i am sort of going by trial and error:)
Anyway HELP!
Solution:
public void generateIcon() {
BufferedImage i = new BufferedImage(30, 30, BufferedImage.TYPE_INT_ARGB);
if (this instanceof Node) {
i = compileImages(i, Node.icon);
}
if(this instanceof ForceNode){
i = compileImages(i, ForceNode.forceicon);
}
if(this instanceof TunnelNode){
i = compileImages(i, TunnelNode.tunnelicon);
}
if (this instanceof EntranceNode) {
i = compileImages(i, EntranceNode.entranceicon);
}
if (this instanceof NetworkNode) {
i = compileImages(i, NetworkNode.networkicon);
}
if(this instanceof DataNode){
i = compileImages(i, DataNode.dataicon);
}
//if(this instanceof )
nodeicon = i;
}
public BufferedImage compileImages(BufferedImage image, File f) {
BufferedImage im = null;
try {
im = ImageIO.read(f);
for(int i = 0 ; i<image.getWidth();i++){
for(int j = 0 ; j<image.getHeight();j++){
int rgb = im.getRGB(i, j);
//System.out.println(i + " " + j + " " + rgb);
if(!(rgb < 1 && rgb > -2)){
image.setRGB(i, j, rgb);
//System.out.println("Printing " + i + " " + j + " " + rgb);
}
}
}
} catch (IOException e) {
Logger.getLogger(HackerGame.class.getName()).log(Level.SEVERE, null, e);
return null;
}
return image;
}
Iterate the pixels of the source images. If they are not white (use getRGB(x,y)to compare them), write them to the destination image.

Categories