Skip to main content

Use Blend effect to composite with Graphics destination image

4 replies [Last post]
vync79
Offline
Joined: 2006-10-24
Points: 0

Hi,

I have been looking at java hardware accelerated multiply or overlay kind of blending mode for years... Chris Campbell was refering to a PhotoComposite API to do that in jdk7. I don't know whether it is still in the plans... Anyway, it looks like the scenegraph API offer such capabilities throught the Blend effect class.

My problem is that I am working on a fullscreen application using BufferStrategy where I cannot convert my entire screen into a scene graph. I would like to use several smaller scene graphs in some cases and render them on the screen using the Graphics2d object returned by the BufferedStrategy.

If I understood properly, the Blend objects take a bottom input and top input and perform the blending. The issue here is that I cannot access the destination offsreen buffer of the BufferedStrategy in order to provide it as an input to the Blend object...

Is there any way to have the Blend object use the Graphics object destination image as an input ? In other words, I am just trying to figure out if is posssible to perform a simple thing like using a Blend object to render an image in MULTIPLY Blend.Mode through a Graphics2D objects.

Regards.

Vincent.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
vync79
Offline
Joined: 2006-10-24
Points: 0

Hi william,

thanks for your answer, however it does not answer my needs.

To be a bit more specific about my request, here is the context of my application:

This is a full screen game based on Java2D in isometric 3D display (diablo-like).

The screen is fully repainted at 30 to 40fps and is made of numerous small images (landscape parts, players animations, particules...) that are displayed in z ordering to simulate a 3D perpective. One screen can be made of several hundreds of images.

To achieve this kind of fps speed, I am using the hardware optimization pipelines and the page flipping strategy though the BufferedStrategy API. (Basically it displays every image on an offscreen image and when it is ready, it is put on screen by VRAM pointer switching.) It is working really fine with D3D and OpenGL pipelines.

However, as of today, I am limited to hardware accelerated Java2D operations. For example, the optimized image compositing modes that are available are those in AlphaComposite API (12 Porter-Duff rules). I would like to use for example the MULTIPLY or OVERLAY blending modes to achieve some graphical effects in the game.

This is why I am now looking at the SceneGraph API and more particularly to the Blend class that offers this kind of blending modes with hardware acceleration.

The issue is that I would like to composite an image on what was already painted in the offscreen buffer and I don't have access to this buffer as an image that I could turn into an input for a Blend object. The only access given by BufferedStrategy is a Graphics2D object that renders on it.

This is why my question is: is it possible to use the Blend object to composite one image onto the destination of a Graphics2D object (without having any reference on the actual buffer that is being painted on by the Graphics2D object).

I don't know if it makes my request clearer...

Cheers.

Vince.

william13163
Offline
Joined: 2008-03-19
Points: 0

Hi Vince,

That would be tough for the Scenario if the buffer strategy isn't providing access to the rendering surface other that a one-way graphics object. The only way I could see Scenario working with the strategy is if you could "insert" Scenario in between, but I don't see how; at least not of the cuff.

Sorry, I couldn't help.

vync79
Offline
Joined: 2006-10-24
Points: 0

Hi William,

this is also what I thought...
It would be too much work for me to convert my degin to scenegraph after 4 years of developement. Moreover, the displayed images (and their ordering) are changing all the time and it would become quite complex and probably unefficient to update the scene graph that frequently.

It seems that my only hope would be to see the work done on Scenario around hardware optimzed blending integrated to standard java2d API as a Composite that could be set to the Graphics2D object like the current AlphaComposite.

From what I understood, that was the purpose of what Chris Campbell called the PhotoComposite API. However I don't see any input on whether or not this is still in the roadmap for Dolphin... I don't know if Chris would have the opportunity to comment on this ?

Cheers.

Vince.

william13163
Offline
Joined: 2008-03-19
Points: 0

Hi vync79,

I can't say I fully understand your requirement, but you can create your own SGFilter and then create a custom Effect to plugin into it.

You can also create your own SourceContent(image) as a feed into an Effect too. Here is one that converts a shape into a static image:

[code]
private class ShapeSourceContent
{
private SourceContent _sourceContext = null;

public ShapeSourceContent(Shape shape)
{
GraphicsConfiguration gc = _frame.getGraphicsConfiguration();

Image image = Effect.createCompatibleImage(gc, (int) Math.round(shape.getBounds().getWidth()), (int) Math.round(shape.getBounds().getHeight()));

// Resolve Shape to Image.
Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.setPaint(Color.WHITE);
// g2d.fill(shape);
g2d.fillRect(0, 0, 50, 50);
_sourceContext = new SourceContent(image);
}

public SourceContent getSourceContent()
{
return _sourceContext;
}
}
[/code]

In your case, I would imagine you would use your graphics2d as a source to the SourceContent and then use it as input to an Effect.

You could also go crazy and create your own SGFilter that deals with a graphic2d:

[code]
public class SGBrighten extends SGFilter
{
private double _x = 0.0;
private double _y = 0.0;

private Shape _shape = new Ellipse2D.Float(0, 0, 200f, 200f); // Default

private final float[] _rbga = new float[] { 1.3f, 1.3f, 1.3f, 1.0f };
private final float[] _offsets = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
private final RescaleOp _op = new RescaleOp(_rbga, _offsets, null);
// Or a Convolve
// private final float[] kdata = new float[] { 1.3f };
// private final Kernel kernel = new Kernel(1, 1, kdata);
// private final ConvolveOp _op = new ConvolveOp(kernel);

private final Rectangle2D _shapeBounds = new Rectangle2D.Double();
private final Rectangle2D _sourceBounds = new Rectangle2D.Double();
private final Rectangle2D _intersectBounds = new Rectangle2D.Double();

public SGBrighten()
{
super();
}

public void renderFinalImage(Graphics2D g, SGSourceContent srcContent)
{
// Render the first pass as normal
Image image = srcContent.getTransformedImage();
g.drawImage(image, 0, 0, null);

// Render the second pass with Op and Clipping.
if (_shape == null)
return;

if (image instanceof BufferedImage)
{
BufferedImage bImage = (BufferedImage) image;

_shapeBounds.setFrame(_x, _y, _shape.getBounds().getWidth(), _shape.getBounds().getHeight());

_sourceBounds.setFrame(bImage.getMinX(), bImage.getMinY(), bImage.getWidth(), bImage.getHeight());

Rectangle2D.intersect(_shapeBounds, _sourceBounds, _intersectBounds);

System.out.println("------------------------------------");
System.out.println(_x + "," + _y);
System.out.println(_shapeBounds);
System.out.println(_sourceBounds);
System.out.println(_intersectBounds);

double maxx = Math.min(_sourceBounds.getWidth(), _intersectBounds.getWidth());
double maxy = Math.min(_sourceBounds.getHeight(), _intersectBounds.getHeight());
if (maxx <= 0 || maxy <= 0)
return; // The shape is beyond the right and/or bottom.

double minx = Math.max(_sourceBounds.getMinX(), _intersectBounds.getMinX());
double miny = Math.max(_sourceBounds.getMinY(), _intersectBounds.getMinY());

// Subimage needs to be replaced by a fixed size buffer if performance
// problems arise.
BufferedImage subImage = ((BufferedImage) image).getSubimage((int) minx, (int) miny, (int) maxx, (int) maxy);
BufferedImage dstImage = _op.filter(subImage, null);

Graphics2D g2 = (Graphics2D) g.create();

AffineTransform af = g.getTransform();

// translate the clip shape
af.translate(_x, _y);
g2.setTransform(af);
g2.clip(_shape);

System.out.println(minx + "," + miny + "," + maxx + "," + maxy);
// draw image--and--remove previous shape translation.
if (minx == 0 && miny == 0)
g2.drawImage(dstImage, (int) -_x, (int) -_y, null);
else if (miny == 0)
g2.drawImage(dstImage, 0, (int) -_y, null);
else if (minx == 0)
g2.drawImage(dstImage, (int) -_x, 0, null);
else
g2.drawImage(dstImage, 0, 0, null);

g2.dispose();
}

}

public int needsSourceContent()
{
return TRANSFORMED;
}

public void setShape(Shape shape)
{
_shape = shape;
}

public void setLocation(double x, double y)
{
_x = x;
_y = y;
}

public Rectangle2D getBounds(AffineTransform transform)
{
// TODO May need to do something here.
return super.getBounds(transform);
}
}
[/code]

Any that is my 0.02c.

Cheers.