async buffered image: fix listener leak from subscribing to already loaded images

Listeners are never removed from the image, and for hot images in the
image cache, the listeners are leaking. Since the images only change
once after loading, do not queue any further listeners once it has been
loaded.
This commit is contained in:
Adam
2019-10-11 20:56:26 -04:00
parent 4d43f08e0d
commit bdd9feb3ea

View File

@@ -26,8 +26,8 @@
package net.runelite.client.game;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
@@ -35,7 +35,9 @@ import javax.swing.JLabel;
public class AsyncBufferedImage extends BufferedImage
{
private final List<Runnable> listeners = new CopyOnWriteArrayList<>();
private final List<Runnable> listeners = new ArrayList<>();
private boolean loaded;
public AsyncBufferedImage(int width, int height, int imageType)
{
super(width, height, imageType);
@@ -44,19 +46,28 @@ public class AsyncBufferedImage extends BufferedImage
/**
* Call when the buffer has been changed
*/
public void changed()
public synchronized void changed()
{
loaded = true;
for (Runnable r : listeners)
{
r.run();
}
listeners.clear();
}
/**
* Register a function to be ran when the buffer has changed
*/
public void onChanged(Runnable r)
public synchronized void onChanged(Runnable r)
{
if (loaded)
{
// If the image has already been loaded, further listeners will not fire. Do not
// queue them to avoid leaking listeners.
return;
}
listeners.add(r);
}
@@ -78,7 +89,13 @@ public class AsyncBufferedImage extends BufferedImage
private ImageIcon makeIcon(JComponent c)
{
listeners.add(c::repaint);
synchronized (this)
{
if (!loaded)
{
listeners.add(c::repaint);
}
}
return new ImageIcon(this);
}
}