Using FrameBuffer's Graphics With AWTGLCanvas

Started by AGP, June 17, 2009, 11:27:18 PM

Previous topic - Next topic

AGP

I actually solved the ArrayIndexOutOfBoundsException. That little program I had written over a few years (it was the first one I built with jPCT) and it uses both the software and the hardware renderer (not at the same time obviously). And in changing it a million times I had introduced a stupid glitch in which I called run in one part of it (one attempt at getting the non-canvas OpenGL renderer to use the software loop), and started a separate thread that called it. So sometimes, draw() was being called twice at the same time (and before it draws it moves the objects). So that's that.

But now here's a question: what's the point of using AWTGLCanvas if the FrameBuffer's Graphics object will still be null (or useless, whichever it is)? I can no more use canvas.getGraphics()'s Graphics instance than I could a frame's (I can, but the screen will flicker). So is this a jPCT bug (I hope so)?

EgonOlsen

It's not a bug, it's by design. getGraphics() in FrameBuffer always refers to the Graphics instance of the software renderer's buffer.

The only option is to use the instance from canvas, but if that flickers...i don't know. I don't think that it's really meant to be used that way. Combining OpenGL and Swing/AWT in one canvas is questionable IMHO.

Looking at the AWTGLCanvas sources from LWJGL, it overrides both paint and update and let update call paint. There's also no call to super.paint() or something.

Edit: What exactly do you want to use the Graphics instance for? Maybe there's a more OpenGL friendly way for what you have in mind.

AGP


paulscode

One way might be to load the GIF and draw each frame onto a BufferedImage of the proper dimensions (or do this via external software and save the result as a PNG image).  Then you'd create a texture from that image and assign it to a quad overlay.  You could animate the graphic by cycling through the proper sets of u/v coordinates for the quad's vertices.

I'm sure that isn't the best/easiest method, but it was the first idea that came to mind for animating a GIF on top the frame buffer.

EgonOlsen

It doesn't have to be an overlay, you may also use the blitting methods for this. Anyway, is it possible to extract the frames of an animated GIF using plain Java2D? A quick search gave me this solution, which is using iText(!) to do it...:http://www.java2s.com/Tutorial/Java/0419__PDF/GetFramefromAnimatedGif.htm

AGP

Thanks to both of you again. I'm not sure the Overlay class would be my first choice, but I'll give it a try to see what happens. And Egon, a lot of my games use animated gifs. You load them as you would a static image, but with every iteration of the game loop Java2D draws a different frame (and loops them too).

EgonOlsen

Quote from: AGP on June 18, 2009, 05:23:50 PM
You load them as you would a static image, but with every iteration of the game loop Java2D draws a different frame (and loops them too).
Yes, but you have no control about the animation, haven't you? Something that loads a gif, extracts the single images and the timing information and puts that into a texture and animates it would be a handy addon to jPCT. I would write such a thing if someones provides me with a proper way to extracts those information from the gif. The way in the given link seems to be quite strange to me...

AGP

I can probably figure it out quickly enough. I'll report back with any findings soon.

AGP

I did this so far, but I have to do something else now. I might get back to it tonight. What I don't get about the Wikipedia article I found (http://en.wikipedia.org/wiki/Graphics_Interchange_Format) is whether I'm looking for a black (0, 0, 0) value and then a white (255, 255, 255) to determine the beginning and end of the color table or whether it's something else. This approach might be overkill anyway. There might be a cleverer way to find the individual frames with the provided Gif loaders.

import java.io.*;

public class AnimatedGIF {
      private static boolean animated;
      private static int width, height;
      private static int backColor;
      private static Pixel[] colorTable;//I THINK THE COLOR TABLE IS 24 BITS/PIXEL
      private static int[] thePixels;

      private static void readGIF(String[] args) throws IOException {
byte[] staticAnimated = new byte[6];
FileInputStream inStream =  new FileInputStream(args[0]);
inStream.read(staticAnimated);
if (staticAnimated[4] == 57)//IS IT GIF87a (STATIC) OR GIF89a (ANIMATED)?
      animated = true;
else animated = false;
byte[] twoBytes = new byte[2];
inStream.read(twoBytes);
width = fromSignedByte(twoBytes[0]) +fromSignedByte(twoBytes[1])*256;
inStream.read(twoBytes);
height = fromSignedByte(twoBytes[0]) +fromSignedByte(twoBytes[1])*256;
inStream.read(twoBytes);
backColor = twoBytes[1];//SKIPPED THE GCT BYTE ON PURPOSE (WHO CARES?)
System.out.println("Multiple Images: "+animated);
System.out.println("Width: "+width +", Height: "+height);
      }
      private static int fromSignedByte(byte signedBastard) {
int value = 0;
for (int i = 7; i >= 0; i--) {
     if ((signedBastard & (1 << i)) > 0) //IF (BYTE TOPRINT ANDED WITH A BYTE WHOSE ONLY BIT 1 IS ON POSITION i) > 0, ADD
value += (int)Math.pow(2.0, (double)i);
}
return value;
      }

      public static void main(String[] args) {
if (args == null || args.length == 0) {
      System.err.println("USAGE: java AnimatedGIF [filename]");
      System.exit(1);
}
try {
      readGIF(args);
}
catch (IOException e) {System.err.println("Problem loading file: "+e.getMessage());}
      }
}

class Pixel {
      protected int red, green, blue;
      public Pixel(byte[] pixel) {
red = pixel[0];
green = pixel[1];
blue = pixel[2];
      }
}

AGP

And here's the cleverer approach by Alexander Hristov under the lesser general public license (which probably means you can use on jPCT but I haven't read it). The first is the tester I wrote (ArrayIndexOutOfBoundsException will be thrown if your gif doesn't have at least 4 frames), and the second is his FrameReader.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;

public class FrameReaderTester extends Frame implements WindowListener {
      private BufferedImage[] frames;
      private int currentFrame = 0;
      public FrameReaderTester(String fileName) {
this.setTitle("AGP's");
try {
      frames = FrameReader.getAllFrames(new FileInputStream(fileName));
}
catch (IOException e) {System.err.println("Shit happened: "+e.getMessage());}
this.addWindowListener(this);
this.setSize(800, 600);
this.setVisible(true);
      }

      public void paint(Graphics g) {
g.drawImage(frames[0], 10, 30, null);
g.drawImage(frames[1], 20+frames[0].getWidth(), 30, null);
g.drawImage(frames[2], 10, 40+frames[0].getHeight(), null);
g.drawImage(frames[3], 20+frames[0].getWidth(), 40+frames[1].getHeight(), null);
      }

      public void windowClosing(WindowEvent e) {
if (e.getWindow().equals(this)) {
      this.dispose();
      System.exit(0);
}
      }
      public void windowClosed(WindowEvent e) {}
      public void windowOpened(WindowEvent e) {}
      public void windowActivated(WindowEvent e) {}
      public void windowDeactivated(WindowEvent e) {}
      public void windowIconified(WindowEvent e) {}
      public void windowDeiconified(WindowEvent e) {}
      public static void main(String[] args) {
if (args == null || args.length == 0)
      return;
new FrameReaderTester(args[0]);
      }
}


import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;

class FrameReader {
  public static BufferedImage[] getAllFrames(InputStream in) throws IOException {
    IIORegistry providers = IIORegistry.getDefaultInstance();
    Iterator it = providers.getServiceProviders(ImageInputStreamSpi.class,true);
    ImageInputStream imgStream = null;
   
    // Create an ImageInputStream with the first appropriate SPI
    while (it.hasNext()) {
      ImageInputStreamSpi spi = (ImageInputStreamSpi)it.next();
      if (spi.getInputClass().isInstance(in)) {
        imgStream = spi.createInputStreamInstance(in);
        break;
      }
    }
    if (imgStream == null)
      throw new IOException("No appropriate SPI for this input stream ");
   
    it = providers.getServiceProviders(
        ImageReaderSpi.class, new CanDecodeFilter(imgStream),true);
    if (!it.hasNext())
      throw new IOException("No class can read this image format");
   
    ImageReaderSpi readerSpi = (ImageReaderSpi)it.next();
    ImageReader reader = readerSpi.createReaderInstance();
    ImageReadParam param = reader.getDefaultReadParam();
   
    reader.setInput(imgStream, false, true);
    int numFrames = reader.getNumImages(true);
    BufferedImage[] frames = new BufferedImage[numFrames];
    for (int i = 0; i < numFrames; i++) {
      frames[i] = reader.read(i,param);
    }
   
    imgStream.close();
    reader.dispose();
    return frames;
  }
 
  static class CanDecodeFilter implements ServiceRegistry.Filter {
    ImageInputStream stream;
    public CanDecodeFilter(ImageInputStream stream) {
      this.stream = stream;
    }
    public boolean filter(Object element) {
      try {
          ImageReaderSpi spi = (ImageReaderSpi)element;
          boolean canDecode = false;
          stream.mark();
          canDecode = spi.canDecodeInput(stream);
          stream.reset();
          return canDecode;
      } catch (IOException e) {
          return false;
      }
    }
  }
}

paulscode

Quote from: AGP on June 19, 2009, 12:30:15 AM
And here's the cleverer approach by Alexander Hristov under the lesser general public license (which probably means you can use on jPCT but I haven't read it).
Using it in jPCT would require one of two things:

1) jPCT be licensed by LGPL (i.e. source code release, i.e. no go)
OR:
2) A method be provided for swapping out Hristov's work for other versions (i.e. a seperate JAR from jpct.jar)

Niether of those options sounds ideal.  I have had some experience with the GIF format (I wrote an ad rotator in c++ a few years ago for a company I was working for).  I'll look into the format again and see if I can come up with a GIF loader class.

EgonOlsen

I wouldn't add it to the core anyway but as a separate download, but if you could come up with a loader, that would be even better.

AGP

Egon, if you're thinking of making a separate download with utilities such as a gif loader, why not include my previously-requested Math class?

EgonOlsen

I have no problem with a dedicated Math-class in the core...it's just that i don't know what to put in there that doesn't fit into other Matrix or SimpleVector. But i admit that i forgot about adding the calcAngle-method to SimpleVector. It will surface in the next release.
Any other suggestions on what to add math wise?

AGP

Thanks. And well, I would add generally-useful trigonometric functions. The difference between SimpleVector and Math would be that Math doesn't assume specific use. You can create a SimpleVector instance just to get the result of a function and apply as you will to your object, but that's counter-intuitive. With Math, you can have your objects oriented however you want to, for instance, and all you're really doing is calculating the product of your input (as opposed to necessarily applying a certain way).