I'm currently working on a screen recorder application that uses JNA to capture the frames of the video. I want to insert a semi-transparent marker onto the captured hbitmap before adding the cursor to better highlight the location of the cursor in the video. After much searching I haven't been able to find an example of how to get the pixel data out of, say, a BufferedImage and write it manually to the object pointed to by hbitmap. Here is my best attempt:
GuiTest.java:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.MemoryImageSource;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
public class GuiTest extends JFrame
{
private static final long serialVersionUID = 1L;
private JLabel lblNewLabel;
public GuiTest()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initGui();
}
private void initGui()
{
setPreferredSize(new Dimension(400, 400));
Rectangle area = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage bi = new BufferedImage(area.width, area.height, BufferedImage.TYPE_INT_ARGB);
int[] ia = new int[area.width * area.height];
bi.getRGB(0, 0, area.width, area.height, ia, 0, area.width);
MemoryImageSource mis = new MemoryImageSource(area.width, area.height, ia, 0, area.width);
mis.setAnimated(true);
mis.newPixels(captureScreen(area), ColorModel.getRGBdefault(), 0, area.width);
lblNewLabel = new JLabel("");
lblNewLabel.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(mis)));
getContentPane().add(lblNewLabel, BorderLayout.CENTER);
}
public int[] captureScreen(Rectangle recordArea)
{ // This code captures the desktop. I wish I could say I understood it.
Pointer handle = User32.INSTANCE.GetDesktopWindow();
Pointer hdcSrc = User32.INSTANCE.GetWindowDC(handle);
Pointer hdcDest = Gdi32.INSTANCE.CreateCompatibleDC(hdcSrc);
Pointer hBitmap = Gdi32.INSTANCE.CreateCompatibleBitmap(hdcSrc, recordArea.width, recordArea.height);
Pointer hOld = Gdi32.INSTANCE.SelectObject(hdcDest, hBitmap);
Gdi32.INSTANCE.BitBlt(hdcDest, 0, 0, recordArea.width, recordArea.height, hdcSrc, 0, 0, Gdi32.SOURCE_COPY | Gdi32.CAPTURE_BLT);
try
{ // Start broken code:
BufferedImage MOUSE_SHADE = ImageIO.read(GuiTest.class.getResource("mouse_shade48.png"));
Point cursorPosition = MouseInfo.getPointerInfo().getLocation();
int[] pixels = MOUSE_SHADE.getRGB(0, 0, 48, 48, new int[48 * 48], 0, 48);
Memory shadeMem = new Memory(recordArea.width * recordArea.height * 4);
shadeMem.write(0, copyToOffsetCentered(pixels, new Rectangle(48, 48),
new int[recordArea.width * recordArea.height], new Rectangle(recordArea.width, recordArea.height),
cursorPosition.x, cursorPosition.y), 0, recordArea.width * recordArea.height);
BitmapInfo shadebmi = new BitmapInfo(1);
shadebmi.bmiHeader.biWidth = recordArea.width;
shadebmi.bmiHeader.biHeight = -recordArea.height;
shadebmi.bmiHeader.biPlanes = 1;
shadebmi.bmiHeader.biBitCount = 32;
shadebmi.bmiHeader.biCompression = Gdi32.BI_RGB;
Pointer hdc = User32.INSTANCE.GetDC(null);
Pointer hdcDest2 = Gdi32.INSTANCE.CreateCompatibleDC(hdc);
Pointer hBitmap2 = Gdi32.INSTANCE.CreateCompatibleBitmap(hdc, recordArea.width, recordArea.height);
Gdi32.INSTANCE.SetDIBits(hdc, hBitmap2, 0, recordArea.height, shadeMem, shadebmi, Gdi32.DIB_RGB_COLORS);
Msimg32.INSTANCE.TransparentBlt(hdcDest, 0, 0, recordArea.width, recordArea.height, hdc, 0, 0, recordArea.width, recordArea.height, new BlendFunction());
// I have also tried Gdi32.BitBlt
}
catch (IOException e1)
{
e1.printStackTrace();
} // End broken code
try
{
CursorData cursorData = new CursorData();
cursorData.drawCursorToHandle(hdcDest);
cursorData.close();
}
catch (Exception e)
{
e.printStackTrace();
}
Gdi32.INSTANCE.SelectObject(hdcDest, hOld);
Gdi32.INSTANCE.DeleteDC(hdcDest);
int nBits = recordArea.width * recordArea.height * 4;
BitmapInfo bmi = new BitmapInfo(1);
bmi.bmiHeader.biWidth = recordArea.width;
bmi.bmiHeader.biHeight = -recordArea.height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = Gdi32.BI_RGB;
Memory colorBitsMem = new Memory(nBits);
Gdi32.INSTANCE.GetDIBits(hdcSrc, hBitmap, 0, recordArea.height, colorBitsMem, bmi, Gdi32.DIB_RGB_COLORS);
int[] colorBits = colorBitsMem.getIntArray(0, recordArea.width * recordArea.height);
User32.INSTANCE.ReleaseDC(handle, hdcSrc);
Gdi32.INSTANCE.DeleteObject(hBitmap);
return colorBits;
}
private int[] copyToOffsetCentered(int[] src, Rectangle srcDim, int[] dest, Rectangle destDim, int dx, int dy)
{
int startx = dx - srcDim.width / 2;
int endx = startx + srcDim.width;
int starty = dy - srcDim.height / 2;
int endy = starty + srcDim.height;
for (int x = Math.max(startx, 0); x < Math.min(endx, destDim.width); x++)
{
for (int y = Math.max(starty, 0); y < Math.min(endy, destDim.height); y++)
{
int xSrc = x - startx;
int ySrc = y - starty;
dest[y * destDim.width + x] = src[ySrc * srcDim.width + xSrc];
}
}
return dest;
}
/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
GuiTest window = new GuiTest();
window.pack();
window.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
}
CursorData.java:
import java.awt.MouseInfo;
import java.awt.Point;
import com.sun.jna.Pointer;
public class CursorData
{
public boolean isVisible;
public Pointer iconHandle;
public Point position;
// private static final HBITMAP MOUSE_SHADE;
// static
// {
// BufferedImage bi = new BufferedImage(0, 0, 0);
// try
// {
// bi = ImageIO.read(Images.class.getResource("/icons/com/valsphere/mouse_shade48.png"));
// }
// catch (IOException e)
// {
// e.printStackTrace();
// }
// int[] pixels = bi.getRaster().getPixels(0, 0, 48, 48, new int[48 * 48]);
//
// }
public CursorData()
{
updateCursorData();
}
public void updateCursorData()
{
CursorInfo cursorInfo = new CursorInfo();
if (User32.INSTANCE.GetCursorInfo(cursorInfo))
{
isVisible = cursorInfo.flags.intValue() == User32.CURSOR_SHOWING;
if (isVisible)
{
iconHandle = User32.INSTANCE.CopyIcon(cursorInfo.hCursor.getPointer());
IconInfo iconInfo = new IconInfo();
if (User32.INSTANCE.GetIconInfo(iconHandle, iconInfo))
{
Point cursorPosition = MouseInfo.getPointerInfo().getLocation();
position = new Point(cursorPosition.x - iconInfo.xHotspot.intValue(), cursorPosition.y - iconInfo.yHotspot.intValue());
if (iconInfo.hbmMask != null)
{
Gdi32.INSTANCE.DeleteObject(iconInfo.hbmMask.getPointer());
}
if (iconInfo.hbmColor != null)
{
Gdi32.INSTANCE.DeleteObject(iconInfo.hbmColor.getPointer());
}
}
}
}
}
public void drawCursorToHandle(Pointer hdcDest)
{
drawCursorToHandle(hdcDest, new Point());
}
public void drawCursorToHandle(Pointer hdcDest, Point cursorOffset)
{
if (iconHandle != null)
{
Point drawPosition = new Point(position.x - cursorOffset.x, position.y - cursorOffset.y);
User32.INSTANCE.DrawIconEx(hdcDest, drawPosition.x, drawPosition.y, iconHandle, 0, 0, 0, null, User32.DI_NORMAL);
}
}
public void close() throws Exception
{
if (iconHandle != null)
{
User32.INSTANCE.DestroyIcon(iconHandle);
iconHandle = null;
}
}
}
mouse_shade48.png:
I have verified that my int[] is being created and populated correctly, I just don't know how to get that data into an hbitmap and properly alpha blend it with the captured screen hbitmap that I already have. What I'm currently seeing is that the image is showing up as though the broken code is being skipped. Any help is much appreciated.
Also, I realize that the code above is missing the Msimg32, Gdi32, and User32 JNA interfaces I'm using, so if you need them I can create a gist and put all the pertinent code there.
Related
I've been working on creating shapes such as rectangles using an ArrayList in java. I'm trying to get the rectangles to appear in the panel one at a time and slowly build an actual image. I've had no problem getting them to appear and stay as random rects (location, size, color)...but when I attempt to make these rects static...it all falls apart. Suggestions will be welcome.
Full code so far below:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.List;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GraphicMusicPlayer
implements ControllerEventListener {
static JFrame f = new JFrame("Graphic Music");
static MyDrawPanel myPanel;
#Override
public void controlChange(ShortMessage event) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
GraphicMusicPlayer mini = new GraphicMusicPlayer();
mini.go();
// TODO Auto-generated method stub
}
public void setUpGui() {
// Create the panel within the frame
myPanel = new MyDrawPanel();
f.setContentPane(myPanel);
f.setBounds(700, 700, 800, 800);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void go() {
setUpGui();
try {
// Make and open a sequencer.
Sequencer sequencer = MidiSystem.getSequencer();
sequencer.open();
// Register for events. 127 is the event.
sequencer.addControllerEventListener(myPanel, new int[] {93});
Sequence seq = new Sequence(Sequence.PPQ, 4);
Track track = seq.createTrack();
int r = 0;
int[] tonesList = new int[] {
65, 64, 65, 64, 65, 60, 63, 61,
58,65, 64, 65, 64, 65, 60, 63, 61,
58};
for (int i = 0; i < 18; i +=1) {
r = tonesList[i];
if (r>0){
track.add(makeEvent(144,5,r,93,i));//144 = NOTE_ON
track.add(makeEvent(176,5,93,125,i));//176 = get event
track.add(makeEvent(128,5,r,46,i+1));//128 = NOTE_OFF
}//end loop
sequencer.setSequence(seq);
sequencer.setTempoInBPM(30);
sequencer.start();
}
}
catch (Exception ex) {ex.printStackTrace();}
}
public static MidiEvent makeEvent(int comd, int chan, int one, int two, int tick) {
MidiEvent event = null;
try {
ShortMessage a = new ShortMessage();
a.setMessage(comd, chan, one, two);
event = new MidiEvent(a, tick);
} catch (Exception e) { }
return event;
}
#SuppressWarnings("serial")
class MyDrawPanel extends JPanel implements ControllerEventListener {
boolean msg = false;
ArrayList<Rectangle> rectangleList = new ArrayList<Rectangle>();
Map<Rectangle, Color> rectangleColors = new HashMap<Rectangle, Color>();
public void addRectangle(Rectangle rect){
rectangleList.add(rect);
}
public void controlChange(ShortMessage event) {
msg = true;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.BLACK);
while (msg) {
Graphics2D g2 = (Graphics2D) g;
int red = (int) (Math.random() * 250);
int green = (int) (Math.random() * 250);
int blue = (int) (Math.random() * 250);
Color rcolor = new Color(red, green, blue);
int x = (int) (Math.random() * 750);
int y = (int) (Math.random() * 750);
int width = (int) (Math.random() * 100);
int height = (int) (Math.random() * 100);
Rectangle nextRect = new Rectangle(x,y,width,height);
rectangleList.add(nextRect);
g.setColor(rcolor);
rectangleColors.put(nextRect, rcolor);
//paint all of the rectangles
for (Rectangle rect : rectangleList){
Color rectColor = rectangleColors.get(rect);
g.setColor(rectColor);
// Fill the rectangle with the rectangle color
g2.fill(rect);
}
msg = false;
}
}
}
}
I'm trying to display an animated gif inside a java application. So far it displays and animates, but where it's supposed to be either entirely black or entirely white it appears to become transparent and I can see the colour of the rectangle drawn underneath. For example, here's one of the gifs I'm using:
On a black background it's not noticeable, however when I change it to cyan:
What's really strange is that it seems like it's displaying correctly for the very first frame and then only messes up afterwards. This is the method I'm using to get and draw the image:
//Draw title
img = new ImageIcon(Display.class.getResource(titlePath)).getImage();
g.drawImage(img, 404, 430, this);
I'm overriding paintcomponent in a class that extends JPanel, and I'm only calling repaint on that class once.
What could be causing this, is it a known issue or am I making a mistake in the way I'm going about actually drawing the image? I haven't been able to find any sort of documentation about this issue.
Thanks in advance!
Here are two of the original gif files.
EDIT: Here's a sample of the code I wrote. I removed any unnecessary code to keep it short but kept everything related to the issue. It also may be worth noting that when I draw a png file (which is one of the frames used to create the gif) with the same method there's no issue. The gif was made on http://gifmaker.me/ in case it's relevant.
public class Display extends JPanel implements Constants{
private GameData GD;
private String currentImagePath;
public Display(GameData gd){
GD = gd;
this.setPreferredSize(screenDimension);
this.repaint();
}
public void draw(Location loc){
currentImagePath = loc.getCurrentImagePath();
this.repaint();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(CYAN);
g.fillRect(0, 0, screenW, screenH);
Image img;
if(OtherWorlds.isOnTitle == true){ //Title screen
//Draw image
img = new ImageIcon(Display.class.getResource(currentImagePath)).getImage();
g.drawImage(img, 10, 10, this);
//Draw other stuff
{
else{ //Regular location
//Draw image
img = new ImageIcon(Display.class.getResource(currentImagePath)).getImage();
g.drawImage(img, 10, 10, this);
//Other images and stuff in different areas
//Drawn using the exact same method as above
}
//Random other lines and stuff
}
}
}
When ever you have these types of problems, you want to start playing around with the disposalMethod of the frames.
I ran your gif through some inspection code and found the disposalMethod to be set to RESTORE_TO_BACKGROUND
So, basically, I took your gif and ran it through the following code, which created a new gif with the disposalMethod of none
So your original image is on top and the "fixed" image is on the bottom. The background is colored red to highlight the difference
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class MirrorImage {
public static void main(String[] args) {
new MirrorImage();
}
public MirrorImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private ImageIcon orig;
private ImageIcon mirror;
public TestPane() {
mirror(new File("Qzlxj.gif"), new File("Test.gif"));
orig = new ImageIcon("Qzlxj.gif");
mirror = new ImageIcon("Test.gif");
}
#Override
public Dimension getPreferredSize() {
return mirror == null ? new Dimension(200, 200) : new Dimension(orig.getIconWidth(), orig.getIconHeight() * 2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (orig != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - orig.getIconWidth()) / 2;
int y = (getHeight() - (orig.getIconHeight() * 2)) / 2;
g2d.drawImage(orig.getImage(), x, y, this);
// AffineTransform at = new AffineTransform();
// at.setToScale(1, -1);
// at.translate(0, -mirror.getIconHeight());
// g2d.setTransform(at);
g2d.drawImage(mirror.getImage(), x, y + mirror.getIconHeight(), this);
g2d.dispose();
}
}
}
public static void mirror(File source, File dest) {
List<BufferedImage> images = new ArrayList<>(25);
List<Integer> delays = new ArrayList<>(25);
int delay = 0;
ImageOutputStream output = null;
GifSequenceWriter writer = null;
try {
String[] imageatt = new String[]{
"imageLeftPosition",
"imageTopPosition",
"imageWidth",
"imageHeight"
};
ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName("gif").next();
ImageInputStream ciis = ImageIO.createImageInputStream(source);
reader.setInput(ciis, false);
int noi = reader.getNumImages(true);
BufferedImage master = null;
for (int i = 0; i < noi; i++) {
BufferedImage image = reader.read(i);
IIOMetadata metadata = reader.getImageMetadata(i);
Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0");
NodeList children = tree.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node nodeItem = children.item(j);
System.out.println(nodeItem.getNodeName());
if (nodeItem.getNodeName().equals("ImageDescriptor")) {
Map<String, Integer> imageAttr = new HashMap<String, Integer>();
NamedNodeMap attr = nodeItem.getAttributes();
// for (int index = 0; index < attr.getLength(); index++) {
// Node node = attr.item(index);
// System.out.println("----> " + node.getNodeName() + "=" + node.getNodeValue());
// }
for (int k = 0; k < imageatt.length; k++) {
Node attnode = attr.getNamedItem(imageatt[k]);
imageAttr.put(imageatt[k], Integer.valueOf(attnode.getNodeValue()));
}
if (master == null) {
master = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2d = master.createGraphics();
g2d.drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
g2d.dispose();
// BufferedImage frame = mirror(copyImage(master));
BufferedImage frame = copyImage(master);
ImageIO.write(frame, "png", new File("img" + i + ".png"));
images.add(frame);
} else if (nodeItem.getNodeName().equals("GraphicControlExtension")) {
NamedNodeMap attr = nodeItem.getAttributes();
Node delayNode = attr.getNamedItem("delayTime");
if (delayNode != null) {
delay = Math.max(delay, Integer.valueOf(delayNode.getNodeValue()));
delays.add(delay);
}
}
}
}
output = new FileImageOutputStream(dest);
writer = new GifSequenceWriter(output, images.get(0).getType(), delay * 10, true);
for (int i = 0; i < images.size(); i++) {
BufferedImage nextImage = images.get(i);
writer.writeToSequence(nextImage);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
writer.close();
} catch (Exception e) {
}
try {
output.close();
} catch (Exception e) {
}
}
}
public static BufferedImage mirror(BufferedImage img) {
BufferedImage mirror = createCompatibleImage(img);
Graphics2D g2d = mirror.createGraphics();
AffineTransform at = new AffineTransform();
at.setToScale(1, -1);
at.translate(0, -img.getHeight());
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, null);
g2d.dispose();
return mirror;
}
public static BufferedImage copyImage(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
BufferedImage newImage = createCompatibleImage(img);
Graphics graphics = newImage.createGraphics();
int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;
graphics.drawImage(img, x, y, img.getWidth(), img.getHeight(), null);
graphics.dispose();
return newImage;
}
public static BufferedImage createCompatibleImage(BufferedImage image) {
return getGraphicsConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
public static class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
/**
* Creates a new GifSequenceWriter
*
* #param outputStream the ImageOutputStream to be written to
* #param imageType one of the imageTypes specified in BufferedImage
* #param timeBetweenFramesMS the time between frames in miliseconds
* #param loopContinuously wether the gif should loop repeatedly
* #throws IIOException if no gif ImageWriters are found
*
* #author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(
ImageOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
// my method to create a writer
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier
= ImageTypeSpecifier.createFromBufferedImageType(imageType);
imageMetaData
= gifWriter.getDefaultImageMetadata(imageTypeSpecifier,
imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(
root,
"GraphicControlExtension");
//restoreToBackgroundColor
//restoreToPrevious
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
"transparentColorFlag",
"FALSE");
graphicsControlExtensionNode.setAttribute(
"delayTime",
Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute(
"transparentColorIndex",
"0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
IIOMetadataNode appEntensionsNode = getNode(
root,
"ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int loop = loopContinuously ? 0 : 1;
child.setUserObject(new byte[]{0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF)});
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
gifWriter.setOutput(outputStream);
gifWriter.prepareWriteSequence(null);
}
public void writeToSequence(RenderedImage img) throws IOException {
gifWriter.writeToSequence(
new IIOImage(
img,
null,
imageMetaData),
imageWriteParam);
}
/**
* Close this GifSequenceWriter object. This does not close the
* underlying stream, just finishes off the GIF.
*/
public void close() throws IOException {
gifWriter.endWriteSequence();
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
*
* #return a GIF ImageWriter object
* #throws IIOException if no GIF image writers are returned
*/
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if (!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
} else {
return iter.next();
}
}
/**
* Returns an existing child node, or creates and returns a new child
* node (if the requested node does not exist).
*
* #param rootNode the <tt>IIOMetadataNode</tt> to search for the child
* node.
* #param nodeName the name of the child node.
*
* #return the child node, if found or a new node created with the given
* name.
*/
private static IIOMetadataNode getNode(
IIOMetadataNode rootNode,
String nodeName) {
int nNodes = rootNode.getLength();
for (int i = 0; i < nNodes; i++) {
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName)
== 0) {
return ((IIOMetadataNode) rootNode.item(i));
}
}
IIOMetadataNode node = new IIOMetadataNode(nodeName);
rootNode.appendChild(node);
return (node);
}
}
}
And finally, the "fixed" gif
The above is based on the investigations from Mirroring animated gif on load in Java - ImageIcon
My current code for grid to show video stream is as follow
//Function for displaying grid window//
public void createMap(int maxX, int maxY) {
gameGrid = new JPanel(new GridLayout(maxX, maxY, 1, 1));
gameGrid.setBackground(Color.GREEN);
for (int i = 0; i < maxX; i++) {
for (int j = 0; j < maxY; j++) {
JPanel panel = new JPanel();
panel.setPreferredSize(PREF_SIZE);
String name = String.format("[%d, %d]", i, j);
panel.setName(name);
panel.setBackground(Color.black);
gameGrid.add(panel);
}
}
}
I call this function as createMap(2,2) so it create grid for 2x2(4 window).
I want to show video stream in each of these grid.
I receiving the video stream through 4 cctv cameras attached with DVR.
I used following code for Showing video streams using opencv
package cctvmonitorsystem;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
// DB connectivity classes
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.SQLException;
import java.sql.ResultSet;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;
import org.opencv.imgproc.Imgproc;
import java.util.Arrays;
public class Video {
public static void main(String args[]) throws SQLException {
int maxCamNo = 4;
//create connection
DbConnect dbcon = new DbConnect();
Connection con = dbcon.openCon();
Statement stmt = con.createStatement();
String getDevice="select * from device order by d_id limit 1";
ResultSet res=stmt.executeQuery(getDevice);
// DECLARE VARIABLES FOR DEVICE ADDRESS & PORT
String devAddress = null;
int httpPort=0;
while (res.next())
{
devAddress=res.getString("d_address");
httpPort=res.getInt("d_http_port");
}
dbcon.closeCon(con); // Connection closed
VideoCapture vcam[] = new VideoCapture[maxCamNo];
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
for (int i = 0; i < vcam.length; i++) {
try {
vcam[i] = new VideoCapture();
vcam[i].open("http://"+devAddress+":"+httpPort+"/cgi-bin/view.cgi?chn=" + i + "&u=admin&p=");
//vcam[i]=new VideoCapture(i);
} catch (Exception e) {
e.printStackTrace();
}
if (!vcam[i].isOpened()) {
System.out.println("Camera " + i + " error");
}
}
AccessVideo av = new AccessVideo(vcam);
//Initialize swing components
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(av);
//frame.setSize(1024,768);
frame.setMaximumSize(null);
frame.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
frame.setVisible(true);
while (vcam[0].isOpened()) {
av.repaint();
}
}
}
class AccessVideo extends JPanel {
VideoCapture[] camera = new VideoCapture[6];
AccessVideo(VideoCapture[] camView) {
camera = camView;
}
public BufferedImage Mat2BufferedImage(Mat m) {
System.out.println("Channels - "+m.channels());
int type = BufferedImage.TYPE_BYTE_GRAY;
if (m.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = m.channels() * m.cols() * m.rows();
byte[] b = new byte[bufferSize];
m.get(0, 0, b); // get all the pixels
BufferedImage img = new BufferedImage(m.cols(), m.rows(), type);
final byte[] targetPixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
return img;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Mat[] mat = new Mat[camera.length];
int width=400;
int height=300;
int xpos = 0;
int ypos = 0;
int y = 1;
for (int i = 0; i < camera.length; i++) {
mat[i] = new Mat();
camera[i].read(mat[i]);
BufferedImage image = Mat2BufferedImage(mat[i]);
g.drawImage(image, xpos, ypos, width, height, null);
xpos = xpos + width;
if (y == 3) {
xpos = 0;
ypos = ypos + height;
y = 0;
}
y++;
}
}
}
Here the video streams will show according to x & y co-ordinates.
But I want to show these video streams in grids which i mention above.
Can anyone help me to resolve this issue.
I did something similar here is my code, but this is hardcoded to only 6 cameras.
This is the main function where I called the addComponentsToPane.
public static void main(String[] args) {
CamtestMonitor f = new CamtestMonitor("Monitor");
f.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//Set up the content pane.
f.addComponentsToPane(f.getContentPane());
//Display the window.
f.pack();
f.setVisible(true);
}
This is the addComponentsToPane function where we add 6 different ServerUI's. Each one extends the JLabel class and implements DataListener.
public void addComponentsToPane(final Container pane) {
final JPanel jp = new JPanel();
jp.setLayout(gl);
//Set up components preferred size
jp.add(new ServerUI(8880));
jp.add(new ServerUI(8891));
jp.add(new ServerUI(8892));
jp.add(new ServerUI(8893));
jp.add(new ServerUI(8894));
jp.add(new ServerUI(8895));
jp.setPreferredSize(new Dimension(1600, 900));
//Set up the horizontal gap value
gl.setHgap(gapSize);
//Set up the vertical gap value
gl.setVgap(gapSize);
//Set up the layout of the buttons
gl.layoutContainer(jp);
pane.add(jp, BorderLayout.NORTH);
pane.add(new JSeparator(), BorderLayout.SOUTH);
}
There you have the Constructor of the ServerUI:
public ServerUI(int port) {
SocketServer server = new SocketServer(port);
server.setOnDataListener(this);
server.start();
}
Finally, here we have the overrrided function that refresh the image on each JLabel.
#Override
public void paint(Graphics g) {
synchronized (queue) {
if (queue.size() > 0) {
lastFrame = queue.poll();
}
}
if (lastFrame != null) {
g.drawImage(lastFrame, 0, 0, null);
}
}
You can have one thread for each camera, and also you extend JPanel class and override the paintComponent() method, like example below
public class MyJPanel extends JPanel {
private BufferedImage image;
public MyJPanel() {
super();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (image != null) {
g2.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), this);
}
}
public void refresh(BufferedImage image) {
this.image = image;
this.repaint();
}
}
public class CameraHandler implements Runnable {
VideoCapture vCapture;
MyJPanel mPanel;
public CameraHandler(VideoCapture vCapture, MyJPanel mPanel) {
this.vCapture = vCapture;
this.mPanel = mPanel;
}
public void run() {
while(vCapture.canReadFrame...) {
then convert the Mat to BufferedImage;
then call this:
mPanel.refresh(convertedImage);
}
}
}
public class MainClass {
public static void main(String[] args) {
VideoCapture vcam[] = new VideoCapture[maxCamNo];
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
for (int i = 0; i < vcam.length; i++) {
try {
vcam[i] = new VideoCapture();
vcam[i].open("http://"+devAddress+":"+httpPort+"/cgi-bin/view.cgi?chn=" + i + "&u=admin&p=");
//vcam[i]=new VideoCapture(i);
if(vcam[i].isOpen) {
new Thread(new CameraHandler(vcam[i], and you MyJPanel reference)).start();
}
} catch (Exception e) {
e.printStackTrace();
}
if (!vcam[i].isOpened()) {
System.out.println("Camera " + i + " error");
}
}
}
}
The code above is just an example to show the way, if you use this then need to arrange the code as your need. Hope this code help you!
This question already has answers here:
Java: Rotating Images
(8 answers)
Closed 7 years ago.
All right, so I am attempting to create a simple-ish game and I have a galaxy swirly id like to rotate, only thing is though, I am unsure how, if I use the Graphics2D "g.rotate(x)" everything on screen I had drawn spins but I only want the galaxy to rotate and not the words and stuff.
Background class:
package TileMap;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Background {
private BufferedImage image;
private int x;
private int y;
private int dx;
private int dy;
public Background(String imgLctn){
try{
image = ImageIO.read(getClass().getResourceAsStream(imgLctn));
}
catch(Exception e){
e.printStackTrace();
}
}
public void draw(Graphics2D g){
g.drawImage(image, x - 44, y - 33, null);
}
}
The MainMenu class:
package GameState;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import Main.GamePanel;
import TileMap.Background;
public class MainMenu extends GameState {
private int currentChoice = 0;
private Background bg;
private String[] options = {
"Start",
"Options",
"Quit"
};
private Color titleColor;
private Font titleFont;
private Font subFont;
public MainMenu(GameStateManager gsm){
this.gsm = gsm;
try{
bg = new Background("/Backgrounds/MenuBG.png");
titleColor = new Color(0, 255, 0);
titleFont = new Font("Youre gone", Font.ITALIC, 30);
subFont = new Font("Youre gone", Font.PLAIN, 18);
}
catch(Exception e){
e.printStackTrace();
}
}
#Override
public void init() {
}
#Override
public void update() {
}
#Override
public void draw(Graphics2D g) {
bg.draw(g);
g.setColor(titleColor);
g.setFont(titleFont);
g.drawString("On My Way To Mars", GamePanel.WIDTH1 / 8, GamePanel.HEIGHT1 / 3);
g.fillRect(GamePanel.WIDTH1 / 8, GamePanel.HEIGHT1 / 3, (int) (GamePanel.WIDTH1/1.53), 3);
for(int i = 0;i < options.length;i++){
g.setFont(subFont);
if(i == currentChoice){
g.setColor(new Color(255,0,255));
}
else{
g.setColor(new Color(0,255,255));
}
if(i < 1){
g.drawString(options[i], GamePanel.WIDTH1 / 8, GamePanel.HEIGHT1 / 2 + i);
}
else{
g.drawString(options[i], GamePanel.WIDTH1 / 8, (GamePanel.HEIGHT1 - 2) /2 + (i*20));
}
}
}
private void select(){
if(currentChoice == 0){
}
if(currentChoice == 1){
}
if(currentChoice == 2){
System.exit(0);
}
}
public void keyPressed(int k){
if(k == KeyEvent.VK_W){
System.exit(0);
}
}
}
I'm not an expert myself but this might help:
Java: Rotating Images
Here's their answer:
`// The required drawing location
int drawLocationX = 300;
int drawLocationY = 300;
// Rotation information
double rotationRequired = Math.toRadian(45);
double locationX = image.getWidth() / 2;
double locationY = image.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
// Drawing the rotated image at the required drawing locations
g2d.drawImage(op.filter(image, null), drawLocationX, drawLocationY, null);
Please note I'm not taking credit for the original posters code!!!
I'm making a fun little test screen recording program in java, and I want it to have a preview of your screen before you start recording.. but its a very slow and poor method of which I am using, involving capturing an image, saving it, then reading it in through a bufferedimage and drawing that image using Graphics. Its very slow and not useful as a "preview" is there a way to speed up and have a more efficient "previewing system".
Here is what I have so far:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class MainFrame implements ActionListener, Runnable {
//add frame components
public static JFrame frame = new JFrame("Screen Caper - v1.0.1");
JButton start = new JButton("record");
JButton close = new JButton("Exit");
JPanel preview = new JPanel();
public static boolean running = false;
public static boolean recording = false;
public static boolean paused = false;
public static String curDir = System.getProperty("user.dir");
//get the screen width
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getHeight();
Container a = new Container();
Container b = new Container();
public MainFrame() {
frame.setSize((int)(width) - 80, (int)(height) - 80);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//setup the buttons and JPanel
a.setLayout(new GridLayout(1, 2));
a.add(start);
start.addActionListener(this);
a.add(close);
close.addActionListener(this);
frame.add(a, BorderLayout.NORTH);
b.setLayout(new GridLayout(1, 2));
b.add(preview);
frame.add(b, BorderLayout.CENTER);
//add anything else
running = true;
//set frame to visible
frame.setVisible(true);
run();
}
public static void main(String[] args) {
new MainFrame();
}
public void run() {
Graphics g = frame.getGraphics();
while (running) {
//draw the preview of the computer screen on the JPanel if its not recording already
if (!recording && !paused) {
drawPreview(g);
}
}
}
public void drawPreview(Graphics g) {
BufferedImage image;
try {
image = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIO.write(image, "png", new File("test.png"));
} catch (Exception ex) {
ex.printStackTrace();
}
BufferedImage prevIm;
try {
prevIm = ImageIO.read(new File("test.png"));
g.setColor(new Color(0, 0, 0));
g.fillRect(preview.getX() + 3, preview.getY() + 51, preview.getWidth(), preview.getHeight() + 1);
g.drawImage(prevIm, preview.getX() + 3, preview.getY() + 51, preview.getX() + preview.getWidth(), preview.getY() + preview.getHeight(), null);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void record(Graphics g) {
}
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource().equals(start)) {
if (!recording) {
//if the program isn't recording, then start recording
Graphics g = frame.getGraphics();
record(g);
start.setText("Finish");
recording = true;
System.out.println("recording...");
} else {
//else stop recording
start.setText("record");
recording = false;
System.out.println("done");
}
}
if (event.getSource().equals(close)) {
paused = true;
int ans = JOptionPane.showConfirmDialog(null, "Woah there! You're about to quit the application\nAre you sure you want to procced?", "Caution!", JOptionPane.YES_NO_OPTION);
if (ans == JOptionPane.YES_OPTION) {
System.exit(0);
} else if (ans == JOptionPane.NO_OPTION) {
paused = false;
}
}
}
}
any help is appreciated!
Don't use getGraphics, this is not how custom painting is done.
Your run method may simply be running to fast, consider using a javax.swing.Timer instead
Toolkit.getDefaultToolkit().getScreenSize() only returns the "default" screen and does not take into consideration split screens
Capturing the screen is a time consuming process and there's not much you can do about reducing it (as it's outside of your control). You "could" setup a series of Threads whose job it was to capture a given section of the desktop. You could also achieve this using SwingWorkers which would make it easier to sync the updates back to the UI...
Take a look at:
Performing Custom Painting
Concurrency in Swing
How to Use Swing Timers
Updated with example
This is a proof of concept only! You should understand what it's trying to do and borrow ideas from it...
You can see the preview window change inside the preview window...kinda freaky...
It tested this on a virtual desktop of 4480x1600.
Basically, it divides the desktop up into a 4x4 grid and starts a Thread for each section. Each thread is responsible for capturing it's own section of the screen.
It also scales that resulting image down and feeds it back to the UI.
I had started with SwingWorkers, but it seems to be hard to be limited to 10 threads. You could reduce the grid size and use SwingWorkers, they tend to be simpler to use and manage then raw Threads.
Each section is given an id, which allows me keep track of what's changed. Technically, you could just add elements to a List, but how do you determine what's new and what's old?
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class PreviewDesktop {
public static void main(String[] args) {
new PreviewDesktop();
}
public PreviewDesktop() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements Puzzler {
private Rectangle virtualBounds;
private double scale;
private Map<Integer, PuzzlePiece> pieces;
public TestPane() {
virtualBounds = getVirtualBounds();
int columns = 4;
int rows = 4;
pieces = new HashMap<>(columns * rows);
int columnWidth = Math.round(virtualBounds.width / (float) columns);
int rowHeight = Math.round(virtualBounds.height / (float) rows);
int id = 0;
for (int row = 0; row < rows; row++) {
int y = virtualBounds.y + (row * rowHeight);
for (int column = 0; column < columns; column++) {
int x = virtualBounds.x + (column * columnWidth);
Rectangle bounds = new Rectangle(x, y, columnWidth, rowHeight);
GrabberWorker worker = new GrabberWorker(id, this, bounds);
System.out.println(id);
id++;
startThread(worker);
}
}
}
#Override
public double getScale() {
return scale;
}
#Override
public void invalidate() {
super.invalidate();
scale = getScaleFactorToFit(virtualBounds.getSize(), getSize());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (Integer id : pieces.keySet()) {
PuzzlePiece piece = pieces.get(id);
Rectangle bounds = piece.getBounds();
BufferedImage img = piece.getImage();
g2d.drawImage(img, bounds.x, bounds.y, this);
// If you want to see each sections bounds, uncomment below...
//g2d.draw(bounds);
}
g2d.dispose();
}
#Override
public void setPiece(int id, PuzzlePiece piece) {
pieces.put(id, piece);
repaint();
}
protected void startThread(GrabberWorker worker) {
Thread thread = new Thread(worker);
thread.setDaemon(true);
thread.start();
}
}
public class PuzzlePiece {
private final Rectangle bounds;
private final BufferedImage img;
public PuzzlePiece(Rectangle bounds, BufferedImage img) {
this.bounds = bounds;
this.img = img;
}
public Rectangle getBounds() {
return bounds;
}
public BufferedImage getImage() {
return img;
}
}
public interface Puzzler {
public void setPiece(int id, PuzzlePiece piece);
public double getScale();
}
public class GrabberWorker implements Runnable {
private Rectangle bounds;
private Puzzler puzzler;
private int id;
private volatile PuzzlePiece parked;
private ReentrantLock lckParked;
public GrabberWorker(int id, Puzzler puzzler, Rectangle bounds) {
this.id = id;
this.bounds = bounds;
this.puzzler = puzzler;
lckParked = new ReentrantLock();
}
protected void process(PuzzlePiece piece) {
// puzzler.setPiece(bounds, chunks.get(chunks.size() - 1));
puzzler.setPiece(id, piece);
}
protected void publish(PuzzlePiece piece) {
lckParked.lock();
try {
parked = piece;
} finally {
lckParked.unlock();
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
lckParked.lock();
try {
process(parked);
} finally {
lckParked.unlock();
}
}
});
}
#Override
public void run() {
try {
Robot bot = new Robot();
while (true) {
BufferedImage img = bot.createScreenCapture(bounds);
double scale = puzzler.getScale();
Rectangle scaled = new Rectangle(bounds);
scaled.x *= scale;
scaled.y *= scale;
scaled.width *= scale;
scaled.height *= scale;
BufferedImage imgScaled = getScaledInstance(img, scale);
publish(new PuzzlePiece(scaled, imgScaled));
Thread.sleep(500);
}
} catch (AWTException | InterruptedException exp) {
exp.printStackTrace();
}
}
}
public static Rectangle getVirtualBounds() {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
bounds.add(gd.getDefaultConfiguration().getBounds());
}
return bounds;
}
public static double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
public static double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = (double) iTargetSize / (double) iMasterSize;
return dScale;
}
public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
}
protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) {
BufferedImage imgScale = img;
int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);
// System.out.println("Scale Size = " + iImageWidth + "x" + iImageHeight);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
} else {
imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
}
return imgScale;
}
protected static BufferedImage getScaledDownInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
if (targetHeight > 0 || targetWidth > 0) {
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
} else {
ret = new BufferedImage(1, 1, type);
}
return ret;
}
protected static BufferedImage getScaledUpInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
w = img.getWidth();
h = img.getHeight();
} else {
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w < targetWidth) {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h < targetHeight) {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp = null;
} while (w != targetWidth || h != targetHeight);
return ret;
}
}
Now, if you're feeling really adventurous, you could update this idea so that if the underlying image for a section didn't change and the scale didn't change, it didn't send that to the UI, which might help reduce some of the overhead ... and no, I don't have code to do that ;)