Is java.awt.GraphicsConfiguration thread-safe? What are the alternatives - java

I'm extending javax.swing.JComponent to display a variable number of tiles, which all have the same size.
If a tile needs a new appearance, a SwingWorker's doInBackground() renders a new BufferedImage for it. In done(), the image is stored and JComponent.repaint() is called, indicating the updated area and an intended delay. The overridden JComponent.paintComponent() will know what to do.
The size of the tiles can be changed via the GUI. Obvioulsy, it could happen that such a request takes place while the the SwingWorker's StateValue is PENDING or STARTED.
I don't see much sense in supporting cancel(); it complicates the code and since the actual rendering does not take very long, its effect would be minimal (or even harmful if the worker had to wait longer than it will need to execute). Rather, I would like to add efficiency and have the EDT code not start a new SwingWorker if a PENDING one exists for the same tile. Then, the SwingWorker just needs to fetch the latest settings when doInBackground() starts and check whether it should really store its result in done().
So where should the BufferedImage used by the SwingWorker be cast into existence? These seem to be the options:
Create it upfront. Drawbacks: The maximum size must be chosen because the specific size is unknown, and since paintComponent() may run concurrently, two images of maximum size must be kept for all tiles at all times (think ViewPort; a dynamic solution would only require a second image of the actually needed size for visible tiles, temporarily).
Create it when creating the SwingWorker. Drawback: The maximum size must be provided since it's unknown which size is required once doInBackground() gets fired.
Create it in the SwingWOrker. Problem: Given that JComponent.paintComponent() may have to call drawImage() often, it's advisable to use GraphicsConfiguration.createCompatibleImage() to create this image. This may break the single-threadedness limitations of AWT.
I would prefer the following, but since GraphicsConfiguration belongs to AWT, and the implementation depends on the platform, is this a safe thing to do?
...
final GraphicsConfiguration gc = this.getGraphicsConfiguration();
if ((obj.worker == null) ||
(obj.worker.getState() != SwingWorker.StateValue.PENDING)) {
obj.worker = new SwingWorker<BufferedImage, Void>() {
#Override public BufferedImage doInBackground() {
... // acquire size info via synchronised access
final BufferedImage img = gc.createCompatibleImage(...);
...
return img;
}
#Override public void done() {
if (obj.worker == this) {
obj.worker = null;
try { obj.image = this.get(); }
catch (Throwable t) { ... System.exit(1); }
Outer.this.requestTileRepaint(...);
}
}
};
obj.worker.execute();
}
...
Clarification
Looking at the above code, one might argue that there is no real muti-threading issue with this solution, since the GraphicsConfiguration object is created on the EDT exclusively for this particular worker. However,
I was looking at the abstract class implementation and it contains static objects and
it might be the case that each call to Component.getGraphicsConfiguration() returns the same object reference.
I was thinking that the safest approach would be to extract all relevant information from the GraphicsConfiguration on the EDT, pass it to the worker, and get a new BufferedImage() there with the suitable configuration. But I found some hints on the web that the result may lead to a surprising performance hit for drawImage(), suggesting that there might be config aspects which may not be covered explicitly.

Picking up haraldK's ideas, here is a thread-safe solution, which I have tested on a Linux PC with Java SE 1.6.0_26 and a Windows 8.1 notebook with Java SE 1.8.0_40. (Obviously, the code can be improved, buit that's beyond this Q&A.)
On both platforms, performance was comparable adjusted for processor speed, and also on both platforms, Transparency.BITMASK was handled via BufferedImage.TYPE_CUSTOM, while Transparency.OPAQUE and Transparency.TRANSLUCENT use specific corresponding BufferedImage.TYPE_* values.
Also on both platforms, there was no noticeable performance difference between using any of the two new BufferedImage() calls, while GraphicsConfiguration.createCompatibleImage() was definitely (30% to 50%) slower.
The whole mechanism is provided by an inner class. The outer class extends javax.swing.JComponent so there's no synchronisation at all at that level. However, the SwingWorkers are anonymous inner classes and deploy the image creation sync mechanism.
The distinction between the two categories of BufferedImage.getType() seems to be unnecessary on the tested platforms, but who knows.
In my case, the innter class also contains other information which the SwingWorkers need.
private static final class WokerSync
{
private Object refImageMutex = new Object();
private BufferedImage refImageOpaque = null;
private BufferedImage refImageTranspMask = null;
private BufferedImage refImageTranslucent = null;
public void setRefImagesFromEDT(final GraphicsConfiguration grConf) {
if (grConf != null) {
synchronized(this.refImageMutex) {
this.refImageOpaque = grConf.createCompatibleImage(1, 1, Transparency.OPAQUE);
this.refImageTranspMask = grConf.createCompatibleImage(1, 1, Transparency.BITMASK);
this.refImageTranslucent = grConf.createCompatibleImage(1, 1, Transparency.TRANSLUCENT);
}
}
}
private BufferedImage getCompatibleImage(final BufferedImage refImage, final int width, final int height) {
BufferedImage img = null;
if (refImage != null) {
final int grType = refImage.getType();
if (grType == BufferedImage.TYPE_CUSTOM) {
final ColorModel cm = refImage.getColorModel();
final WritableRaster wr = cm.createCompatibleWritableRaster(width, height);
final String[] ps = refImage.getPropertyNames();
final int pl = (ps == null) ? 0 : ps.length;
final Hashtable<String,Object> ph = new Hashtable<String,Object>(pl);
for (int pi=0; pi<pl; pi++) {
ph.put(ps[pi], refImage.getProperty(ps[pi]));
}
img = new BufferedImage(cm, wr, cm.isAlphaPremultiplied(), ph);
} else {
img = new BufferedImage(width, height, grType);
}
}
return img;
}
public BufferedImage getCompatibleImageOpaque(final int width, final int height) {
BufferedImage img = null;
synchronized(this.refImageMutex) {
img = this.getCompatibleImage(this.refImageOpaque, width, height);
}
return img;
}
public BufferedImage getCompatibleImageTranspMask(final int width, final int height) {
BufferedImage img = null;
synchronized(this.refImageMutex) {
img = this.getCompatibleImage(this.refImageTranspMask, width, height);
}
return img;
}
public BufferedImage getCompatibleImageTranslucent(final int width, final int height) {
BufferedImage img = null;
synchronized(this.refImageMutex) {
img = this.getCompatibleImage(this.refImageTranslucent, width, height);
}
return img;
}
}

Related

Event Sourcing - complex aggregate design

I have following code specification sample that is modelling image inside Photoshop.
Image is given with PhotoshopImage. Every image has Layers which is an object that holds all layers that an image is made of and in my case it contains only two layers - first is solid layer (instance of DefaultLayer) and second one is transparent layer (instance of NotifiableLayer). Whenever DefaultLayer is updated, we have also to update NotifiableLayer that is listening to changes on DefaultLayer (that is below), so that it can update itself (like when you update some black pixel on layer below, then transparent layer with 50% opacity that is on top of that lower layer will show that pixel in gray color).
Implementation of this is given as:
public class ES2 {
public static void main(String[] args) {
PhotoshopImage image = new PhotoshopImage();
//draw ine black pixel at position 1,1 in layer 1 (top transparent layer)
DrawOneBlackPixelCommand command1 = new DrawOneBlackPixelCommand(1,1,new Coordinates(1,1));
image.drawOneBlackPixel(command1);
//draw one black pixel at position 0,0 in layer 0 (bottom solid layer)
//this command will also affect transparent layer 1 via callback
DrawOneBlackPixelCommand command2 = new DrawOneBlackPixelCommand(1,0,new Coordinates(0,0));
image.drawOneBlackPixel(command2);
int[][] imagePixels = image.getImagePixels();
//[2, 0]
//[0, 1]
System.out.println(Arrays.toString(imagePixels[0]));
System.out.println(Arrays.toString(imagePixels[1]));
}
}
record DrawOneBlackPixelCommand(
int imageId,
int layerType,
Coordinates pixelCoordinates
){}
record Coordinates(int x, int y){}
class PhotoshopImage{
Integer imageId = 1;
String imageName = "someName";
LocalDateTime dateTime = LocalDateTime.now();
Layers layers;
PhotoshopImage(){
layers = new Layers();
}
void drawOneBlackPixel(DrawOneBlackPixelCommand command){
if(LocalDateTime.now().isBefore(dateTime)){
throw new DrawingPixelTimeExpiredException();
}
layers.drawOneBlackPixel(command.layerType(), command.pixelCoordinates());
}
int[][] getImagePixels(){
return layers.getVisibleLayerPixels();
}
class DrawingPixelTimeExpiredException extends RuntimeException{}
}
class Layers{
Set<NotifiableLayer> notifiableLayerObservers = new HashSet<>();
NavigableMap<Integer, Layer> layers = new TreeMap<>();
Layers(){
DefaultLayer solid = new DefaultLayer();
NotifiableLayer transparent = new NotifiableLayer();
layers.put(0, solid);
layers.put(1, transparent);
notifiableLayerObservers.add(transparent);
}
void drawOneBlackPixel(int layerType, Coordinates pixelCoordinates){
if(!layers.containsKey(layerType)){
throw new LayerDoesNotExistException();
}
Layer change = layers.get(layerType);
change.drawOneBlackPixel(pixelCoordinates);
notifiableLayerObservers.forEach(l -> l.notifyLayer(change, pixelCoordinates));
}
public int[][] getVisibleLayerPixels() {
return layers.lastEntry().getValue().getLayerPixels();
}
class LayerDoesNotExistException extends RuntimeException{}
}
interface Layer{
void drawOneBlackPixel(Coordinates coordinates);
int[][] getLayerPixels();
}
class DefaultLayer implements Layer{
int[][] pixels = new int[][]{{0,0},{0,0}};
#Override
public void drawOneBlackPixel(Coordinates c) {
pixels[c.x()][c.y()] = 1;
}
#Override
public int[][] getLayerPixels() {
return pixels;
}
}
class NotifiableLayer implements Layer{
int[][] pixels = new int[][]{{0,0},{0,0}};
void notifyLayer(Layer changed, Coordinates c){
//if it is not this layer, then it is layer below (solid layer)
if(changed!=this){
int pixelInLayerBelow = changed.getLayerPixels()[c.x()][c.y()];
syncPixelWithLayerBelow(pixelInLayerBelow, c);
}
}
private void syncPixelWithLayerBelow(int pixelBelow, Coordinates c){
pixels[c.x()][c.y()] = pixelBelow + 1;
}
#Override
public void drawOneBlackPixel(Coordinates c) {
pixels[c.x()][c.y()] = 1;
}
#Override
public int[][] getLayerPixels() {
return pixels;
}
}
Now, this is implemented as mutable state objects (that is - it is not using event sourcing). Whatever manual about event sourcing that I read, it is based only on some super-simple examples.
In my case - I do not know how to create events OneBlackPixelDrawnEvent (one way is in updated answer below, but it looks too complex for the benefits ES brings) - that should be result of these 2 operations in the code, and how to apply those events - should it be applied in PhotoshopImage, or should each layer be in charge of updating part of its state? How to forward those events from PhotoshopImage aggregate to Layers and further down?
UPDATE - Example of one way to implement using event sourcing
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
public class ES2 {
public static void main(String[] args) {
PhotoshopImage image = new PhotoshopImage();
//draw ine black pixel at position 1,1 in layer 1 (top transparent layer)
DrawOneBlackPixelCommand command1 = new DrawOneBlackPixelCommand(1,1,new Coordinates(1,1));
List<Event> events1 = image.drawOneBlackPixel(command1);
//[OneBlackPixelDrawnEvent[layerType=1, pixelCoordinates=Coordinates[x=1, y=1], pixelValue=1]]
System.out.println(events1);
//draw one black pixel at position 0,0 in layer 0 (bottom solid layer)
//this command will also affect transparent layer 1 via callback
DrawOneBlackPixelCommand command2 = new DrawOneBlackPixelCommand(1,0,new Coordinates(0,0));
List<Event> events2 = image.drawOneBlackPixel(command2);
//[OneBlackPixelDrawnEvent[layerType=0, pixelCoordinates=Coordinates[x=0, y=0], pixelValue=1], LayerSyncedEvent[layerType=1, pixelCoordinates=Coordinates[x=0, y=0], pixelValue=2]]
System.out.println(events2);
int[][] imagePixels = image.getImagePixels();
//[2, 0]
//[0, 1]
System.out.println(Arrays.toString(imagePixels[0]));
System.out.println(Arrays.toString(imagePixels[1]));
}
}
interface Event{}
record DrawOneBlackPixelCommand(
int imageId,
int layerType,
Coordinates pixelCoordinates
){}
record Coordinates(int x, int y){}
record OneBlackPixelDrawnEvent(
Integer layerType,
Coordinates pixelCoordinates,
Integer pixelValue
) implements Event{}
class PhotoshopImage{
Integer imageId = 1;
String imageName = "someName";
LocalDateTime dateTime = LocalDateTime.now();
Layers layers;
PhotoshopImage(){
layers = new Layers();
}
List<Event> drawOneBlackPixel(DrawOneBlackPixelCommand command){
if(LocalDateTime.now().isBefore(dateTime)){
throw new DrawingPixelTimeExpiredException();
}
List<Event> events = layers.drawOneBlackPixel(command.layerType(), command.pixelCoordinates());
apply(events); //Only here we can update state of this aggregate, so it is not updated twice
return events;
}
void apply(List<Event> events){
layers.apply(events);
}
int[][] getImagePixels(){
return layers.getVisibleLayerPixels();
}
class DrawingPixelTimeExpiredException extends RuntimeException{}
}
class Layers{
Map<Integer, NotifiableLayer> notifiableLayerObservers = new HashMap<>();
NavigableMap<Integer, Layer> layers = new TreeMap<>();
Layers(){
DefaultLayer solid = new DefaultLayer();
NotifiableLayer transparent = new NotifiableLayer();
layers.put(0, solid);
layers.put(1, transparent);
notifiableLayerObservers.put(1, transparent);
}
List<Event> drawOneBlackPixel(int layerType, Coordinates pixelCoordinates){
if(!layers.containsKey(layerType)){
throw new LayerDoesNotExistException();
}
Layer change = layers.get(layerType);
OneBlackPixelDrawnEvent event = change.drawOneBlackPixel(pixelCoordinates);
//Here, I have to add layerType, since it is a missing info on event!
OneBlackPixelDrawnEvent updatedEvent = new OneBlackPixelDrawnEvent(layerType, event.pixelCoordinates(), event.pixelValue());
List<LayerSyncedEvent> syncedEvents = notifiableLayerObservers.entrySet().stream()
.map(en ->
en.getValue()
.notifyLayer(change, updatedEvent)
//Here we have to re-pack event, since it is missing some info that can be
//filled only on this level
.map(e -> new LayerSyncedEvent(en.getKey(), e.pixelCoordinates(), e.pixelValue()))
)
.flatMap(Optional::stream)
.collect(Collectors.toList());
List<Event> results = new ArrayList<>();
results.add(updatedEvent);
results.addAll(syncedEvents);
//apply(results); we still cannot apply here, since applying in aggregate root would apply twice!
return results;
}
public void apply(List<Event> events){
for(Event e : events){
if(e instanceof LayerSyncedEvent ev){
layers.get(ev.layerType()).apply(ev);
}
if(e instanceof OneBlackPixelDrawnEvent ev){
layers.get(ev.layerType()).apply(ev);
}
}
}
public int[][] getVisibleLayerPixels() {
return layers.lastEntry().getValue().getLayerPixels();
}
class LayerDoesNotExistException extends RuntimeException{}
}
interface Layer{
OneBlackPixelDrawnEvent drawOneBlackPixel(Coordinates coordinates);
int[][] getLayerPixels();
<T extends Event> void apply(T e);
}
class DefaultLayer implements Layer{
int[][] pixels = new int[][]{{0,0},{0,0}};
#Override
public OneBlackPixelDrawnEvent drawOneBlackPixel(Coordinates c) {
OneBlackPixelDrawnEvent event = new OneBlackPixelDrawnEvent(null, c, 1);
//apply(event); ! Since applying in aggregate root - cannot apply here!
return event;
}
#Override
public int[][] getLayerPixels() {
return pixels;
}
#Override
public <T extends Event> void apply(T e) {
if(e instanceof OneBlackPixelDrawnEvent ev){
Coordinates c = ev.pixelCoordinates();
pixels[c.x()][c.y()] = ev.pixelValue();
}
}
}
record LayerSyncedEvent(
Integer layerType,
Coordinates pixelCoordinates,
Integer pixelValue
) implements Event{}
class NotifiableLayer implements Layer{
int[][] pixels = new int[][]{{0,0},{0,0}};
Optional<LayerSyncedEvent> notifyLayer(Layer changed, OneBlackPixelDrawnEvent event){
//if it is not this layer, then it is layer below (solid layer)
if(changed!=this){
Coordinates c = event.pixelCoordinates();
//Since layer is not updated anymore in-place, we have to take changes from event!
//int pixelInLayerBelow = changed.getLayerPixels()[c.x()][c.y()];
int pixelInLayerBelow = event.pixelValue();
return Optional.of(syncPixelWithLayerBelow(pixelInLayerBelow, c));
}
return Optional.empty();
}
private LayerSyncedEvent syncPixelWithLayerBelow(int pixelBelow, Coordinates c){
LayerSyncedEvent event = new LayerSyncedEvent(null, c, pixelBelow + 1);
//apply(event); ! Since applying in aggregate root - cannot apply here!
return event;
}
#Override
public OneBlackPixelDrawnEvent drawOneBlackPixel(Coordinates c) {
OneBlackPixelDrawnEvent event = new OneBlackPixelDrawnEvent(null, c, 1);
//apply(event); ! Since applying in aggregate root - cannot apply here!
return event;
}
#Override
public int[][] getLayerPixels() {
return pixels;
}
#Override
public <T extends Event> void apply(T e) {
if(e instanceof LayerSyncedEvent ev){
Coordinates c = ev.pixelCoordinates();
pixels[c.x()][c.y()] = ev.pixelValue();
}
if(e instanceof OneBlackPixelDrawnEvent ev){
Coordinates c = ev.pixelCoordinates();
pixels[c.x()][c.y()] = ev.pixelValue();
}
}
}
I just updated example here with one way to implement Aggregate root, with methods that return events. I guess this is one possible implementation - but look how much complex this is now; even this simple example - increased 2x in complexity. Am I doing something wrong, or is this just not that easy to do in event-sourced system?
Although debatable, I'd argue whether "photoshopping" is a domain you'd want to implement with paradigms like DDD, CQRS and Event Sourcing in mind. As to some extend mentioned by VoicOfUnreason, sometimes the work doesn't out way the benefits; you might just have picked such a domain for which it isn't feasible.
Regardless, let me try to give some guidance to your questions and the snippets you've shared. First thing I'd like to emphasize on, is returning the List<Event> objects from your command handlers. Although reasonable in home grown DDD/CQRS/ES systems, this is not something you'd do with an Axon Framework based application (which I assume you are using through the axon tag).
Command handlers should only share whether an operation was successful, unsuccessful, or the identifier of newly created entities. That's it.
Another pointer worth sharing, is the placement of the Command Handler. You have currently designed it to start at the PhotoshopImage. However commands can perfectly well be targeted towards an exact entity within the Aggregate. From a definition stance this is also fine, as:
The Aggregate is a group of associated objects which in regard to data changes act as a single unit. There is a single reference to the Aggregate, called the Aggregate Root. Lastly, consistency rules apply within the boundaries of the Aggregate.
So, the entire Aggregate consists (in your sample) out of a PhotoshopImage and a list of Layer entities. The PhotoshopImage is your Aggregate Root in this case. Taking the "single reference" argument, this means commands would always flow through the Aggregate Root, which thus is the PhotoshopImage. This however doesn't make the PhotoshopImage entity the object in charge of deciding upon handling the command.
From the looks of the implementation, and if I follow your description correctly, there is a necessity to handle the operation in the root to delegate the operations to all layers. This would indeed opt for the command handler as it is situated right now.
It is upon the publication of events where you can greatly simplify things. Note though that I am basing myself on Axon Framework in this case, which I am assuming is fair since the axon tag is being used. Right now, it's the PhotoshopImage which publishes the events. I would have each of your Layers publish it's own OneBlackPixelDrawnEvent instead. When you would be using Event Sourcing, the publication and handling of such an event within the boundary of the aggregate will have precedence over furthering the command handling operation.
Thus, there would be no need whatsoever to invoke notifiableLayerObservers in your sample to correctly notify all Layers. This should simply be part of the CQRS/DDD/ES framework you are using, which Axon Framework would thus do for you out of the box. Simply mark a method as an #EventSourcingHandler and Axon Framework will not to invoke all event sourcing handlers for a given event, regardless of whether they reside in the Aggregate Root or any of the entities.
Following that route, you can adjust the right portions of state per entity when they are handling the (in your scenario) OneBlackPixelDrawnEvent.
As stated earlier, I am assuming you are using a framework like Axon in this case. Or otherwise that you have the correctly implementation layering in place to achieve the same. Having such a set up will allow you to get away with all the custom routing information you are currently doing in your Command Handling functions.
Last note, I am making assumption on a Domain I am not familiar with. If anything this hurts when using the above mentioned approach, make sure to place a comment so we can discuss it further. In the meantime, I hope this helps you out!
Event Sourcing is based on the assumption that the system records events happening on Aggregate roots. In your case, when a Layer is updated, the Image containing it will append an event to an internal collection. Something like LayerUpdated... although it's considered good practise to give meaningful names to events.
When all the operations (aka Commands) have been executed, the system starts persisting those events and for each one, it will also broadcast a notification.
Now you could either have each NotifiableLayer listening to specific notifications, or you can have a separate service who can do that and update all the NotifiableLayer instances accordingly. I would go for the service: I don't really like the idea of Domain Entities listening to notifications.

PGraphics2D should implement cloneable but throws exception

Using Java Processing, I'm trying to make a deep copy of a PGraphics2D object
PGraphics2D pg_render;
pg_render = (PGraphics2D) createGraphics(width, height, P2D);
PGraphics2D pg_postprocd = (PGraphics2D)pg_render.clone();
Which throws a CloneNotSupportedException :
Unhandled exception type CloneNotSupportedException
Yet reading the doc it seems like cloning is implemented.
I need to have two instances of the PGraphics2D objects so that I can apply post-processing effects on one and keep the other one clean for analysing motion vectors and such.
Exception
The PGraphics class does not itself implement Clonable. Rather, it extends PImage and this is the class that actually implements the Cloneable interface.
This is why your call to pg_render.clone() throws CloneNotSupportedException, since PGraphics doesn't actually support cloning (but happens to extend a class that does).
Solution
The static method below returns a clone of the input PGraphics object. It makes a new PGraphics object with createGraphics(), clones the style (style includes things like current fill colour), and finally clones the pixel buffer.
Code
static PGraphics clonePGraphics(PGraphics source) {
final String renderer;
switch (source.parent.sketchRenderer()) {
case "processing.opengl.PGraphics2D" :
renderer = PConstants.P2D;
break;
case "processing.opengl.PGraphics3D" :
renderer = PConstants.P3D;
break;
default : // secondary sketches cannot use FX2D
renderer = PConstants.JAVA2D;
}
PGraphics clone = source.parent.createGraphics(source.width, source.height, renderer);
clone.beginDraw();
clone.style(source.getStyle()); // copy style (fillColor, etc.)
source.loadPixels(); // graphics buffer -> int[] buffer
clone.pixels = source.pixels.clone(); // in's int[] buffer -> clone's int[] buffer
clone.updatePixels(); // int[] buffer -> graphics buffer
clone.endDraw();
return clone;
}

How can I decorate column images and text in an SWT TableViewer?

I have an SWT table that is wrapped with a JFace TableViewer.
My requirements are:
Decorate the column image with the decorators as they are defined in the workbench decorator manager
Decorate the text of the columns with different colors
I was able to #1 by extending DecoratingLabelProvider and implementing ITableLabelProvider. I passed it my original TableLabelProvider and the workbench decorator manager, and I got icons with decorators.
Then I started to work on #2. I asked this question here and was told that IStyleLabelProvider (what I was trying to use for the colored text) was incompatible with ITableLabelProvider. So I switched to using a ColumnLabelProvider that implements IStyledLabelProvider.
However, now I am stuck. These 2 requirements seem to be mutually exclusive. I cannot extend both ColumnLabelProvider and DecoratingLabelProvider. When I tried to simply pass in the workbench decorator manager to the ColumnLabelProvider like this, but it did not decorate the image at all. Did I pass it in wrong, or will that only work in a DecoratingLabelProvider? What else can I try?
public Image getColumnImage(final Object element, final int columnIndex) {
if (columnIndex == MY_COLUMN_INDEX) {
final MyObject myObj = (MyObject) element;
final Image image = myObj .getImage();
Image newImage = null;
if(this.decorator != null) {
newImage = this.decorator.decorateImage(image, myObj );
}
return newImage == null ? image : newImage;
}
return null;
}
You can use DecoratingStyledCellLabelProvider which takes an IStyledLabelProvider and an ILabelDecorator as parameters:
new DecoratingStyledCellLabelProvider(styledLabelProvider,
PlatformUI.getWorkbench().getDecoratorManager()
.getLabelDecorator(), null);

Java BufferedImage not loading

I am trying to write an imageloading function for my program, however it is doing something strange.
public void loadImage(BufferedImage img, String filepath) {
try {
img = ImageIO.read(new File(filepath));
}
catch (IOException e) {
}
}
And I am calling it like so:
BufferedImage background = null;
loadImage(background, path);
I see in debugging that img loads properly, but background remains null the whole time and causes a NullPointerException.
When I change to a direct reference to background like background = ImageIO.read(new File(filepath)); then it works just fine.
What's even more strange is that System.out.println(img == background) prints true.
What am I doing wrong?
This is because Java passes arguments by value, not reference. As far as Java is concerned, img from loadImage has nothing to do with background. All you did was pass over the address to whatever background refers to, not the reference background itself. background basically told the parameter img, "Hey, point at whatever I'm pointing at."
When you go
img = ImageIO.read(new File(filepath));
You've just set img to refer to some new object, but background will still refer to whatever it was referring to before.
Instead, you should return the BufferedImage and set background to the return value, so something like
public BufferedImage loadImage(String filepath) {
try {
return ImageIO.read(new File(filepath));
}
catch (IOException e) {
}
return null;
}
background = loadImage(path);
This previous question has a little more info on the Pass-by-Value vs Pass-by-Reference issue in Java. Personally, the idea of the value of references being passed took me a while to get it through my head until I read this article.
Hope this helps.
As for your second note that img == background returns true, I'm not sure where you are checking this, but if you check it before img = ImageIO..., that will return true, because img and background both refer to null, since they are uninitialized.
==, when dealing with two Objects, will return true if and only if they both refer to the same object in memory, not if they are the same exact reference.

Fast replacement for JComboBox / BasicComboBoxUI?

I've got a JComboBox that potentially can have thousands of items. They're sorted, and there's find-as-you-type, so in principle it's not completely unusable.
In practice, it's pretty unusable with just a couple of hundred items. I managed to improve the initial display performance using setPrototypeDisplayValue(), but BasicListUI still insists on configuring the list cell renderer for every item in the box (see BasicListUI.updateLayoutState()).
This, or something like it, is apparently a known issue to Sun; it has been for going on eight years now, so I'm not holding my breath.
Short of implementing my own UI, has anyone got a workaround?
JList might be a better choice, as it uses a fly-weight approach to rendering and appears to support find-as-you-type.
If you use JComboBox, add entries to the model before the component itself starts listening. This SortedComboBoxModel uses a simple insertion sort that is acceptable for a few thousand entries:
class SortedComboBoxModel extends DefaultComboBoxModel {
/** Add elements by inserting in lexical order. */
#Override
public void addElement(Object element) {
this.insertElementAt(element, 0);
}
/** Insert in lexical order by name; ignore index. */
#Override
public void insertElementAt(Object element, int index) {
String name = element.toString();
for (index = 0; index < this.getSize(); index++) {
String s = getElementAt(index).toString();
if (s.compareTo(name) > 0) {
break;
}
}
super.insertElementAt(element, index);
}
}
Here's the hack that I came up with. The drawbacks are:
if you want to maintain the look and feel, you have to separately subclass each BasicComboBoxUI extension you care about
you have to use reflection to load your UI classes, since (for instance) a subclass of WindowsComboBoxUI won't load on Linux
it won't work with L&Fs (e.g. MacOS?) that don't extend BasicComboBoxUI
it makes assumptions about the ListCellRenderer that may not always be warranted
I'm still open to cleaner solutions.
class FastBasicComboBoxUI extends BasicComboBoxUI {
#Override
public void installUI(JComponent c) {
super.installUI(c);
Object prototypeValue = this.comboBox.getPrototypeDisplayValue();
if (prototypeValue != null) {
ListCellRenderer renderer = comboBox.getRenderer();
Component rendererComponent = renderer
.getListCellRendererComponent(this.listBox,
prototypeValue, 0, false, false);
if (rendererComponent instanceof JLabel) {
// Preferred size of the renderer itself is (-1,-1) at this point,
// so we need this hack
Dimension prototypeSize = new JLabel(((JLabel) rendererComponent)
.getText()).getPreferredSize();
this.listBox.setFixedCellHeight(prototypeSize.height);
this.listBox.setFixedCellWidth(prototypeSize.width);
}
}
}
}
I'm still open to cleaner solutions.
Later
Turns out this only solved some of the problems. Initial display of a combo box with a large number of items could still be really slow. I had to make sure the popup list box immediately gets a fixed cell size, by moving the code into the ComboPopup itself, as follows. Note that, as above, this depends on the prototype value.
#Override
protected ComboPopup createPopup() {
return new BasicComboPopup(comboBox) {
#Override
protected JList createList() {
JList list = super.createList();
Object prototypeValue = comboBox.getPrototypeDisplayValue();
if (prototypeValue != null) {
ListCellRenderer renderer = comboBox.getRenderer();
Component rendererComponent = renderer
.getListCellRendererComponent(list, prototypeValue, 0, false, false);
if (rendererComponent instanceof JLabel) {
// Preferred size of the renderer itself is (-1,-1) at this point,
// so we need this hack
Dimension prototypeSize = new JLabel(((JLabel) rendererComponent)
.getText()).getPreferredSize();
list.setFixedCellHeight(prototypeSize.height);
list.setFixedCellWidth(prototypeSize.width);
}
}
return list;
}
};
}

Categories

Resources