-
Bug
-
Resolution: Unresolved
-
P3
-
7, 8
-
x86
-
os_x
FULL PRODUCT VERSION :
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b128)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b69, mixed mode)
This was also tested on Java 1.7
ADDITIONAL OS VERSION INFORMATION :
Mac OS X version 10.9.1
EXTRA RELEVANT SYSTEM CONFIGURATION :
Mac mini late 2012, Also tested on macbookpro mid 2009 with similar results
A DESCRIPTION OF THE PROBLEM :
Custom Paint operations (Via Graphics2D.setPaint, Paint & Paint Context) on the mac are extremely slow, up to 100x slow then doing an equivalent paint operation using BufferedImages.
Custom Paint operations for nonantialias rendering is about 20x slower than custom paint operations for antialias rendering. There is obviously something very wrong with the nonantialias rendering pipeline.
REGRESSION. Last worked in version 6u43
ADDITIONAL REGRESSION INFORMATION:
The apple version of the JVM did not show this performance issue
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the supplied test case. It will execute 4 scenarios under both non-antialias and antialias rendering modes. It takes about 80 seconds to complete the test. The final benchmark results are displayed on the screen.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
1) The custom painter using standard Graphics2D Paint / PaintContext should perform just as good or better then the alternative of painting to BufferedImage, iterating through all pixels change all the color, paint the BufferedImage to the screen.
2) The nonantialias painting should perform the same or better than the antialias painting. This only occurs for the standard java paint classes and the alternative BufferedImage workaround but not for subclasses of standard paint classes or custom paint implementations that are rendered through the standard Graphics2D.setPaint and fill methods.
ACTUAL -
Rendering custom Paint implementations seem to take 4x longer than expected for anitalias rendering and 100X longer than expected for non-antialias rendering
The custom paint when rendered through standard Graphics2D.setPaint / fill also produce artifact on the right edge of the Ellipse shape. The artifact were different for both nonantialias and alias rendering modes.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.geom.*;
import javax.swing.JFrame;
import javax.swing.JComponent;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
/**
* Benchmark showing performance issues with custom painting operations. Note
* the benchmark should be run with maximum java heap size of 1GB (-Xmx1024m)
* in order to avoid excessive time in GC cycles.
*
*
* The bench mark Results
* ----------------------
* 2012 mac mini running OS X 10.9.1 and JRE 1.8.0 b128
*
* non anti-alias painting issues
* 20x slower than with anti-alias turned on.
*
* Slow painting issues in general
* 100x slower than alternative (directly manipulating a BufferedImage) for non-anti alias rendering
* 4x slower than alternative (directly manipulating a BufferedImage) for anti alias rendering
*
* These performance issues were not seen on windows machines.
*
* Benchmark Approach
* ------------------
* A custom Paint and PaintContext class has been created to select RGB color
* based on the x and y position of the pixel. The custom paint implementation
* is used to fill an oval on top of striped background. The background is
* provided to show that 1) only pixels within the specified Shape object is
* painted and 2) the effects of anti-aliasing under different background colors.
*
* The custom paint implementation is then compared to 3 similar operations:
* 1) Rendering the custom paint using an alternative BufferedImage approach
* 2) Drawing the same scene with an alternative custom paint that is simple a
* subclass of a standard java2D paint without any implementation changes
* 3) Drawing the same scene with an standard java paint class
*
*
* Benchmark Considerations
* --------------------
* 1) The buffered image approach is really doing a paint operation twice. Once
* through the standard Graphcis2D draw methods and a second time through direct
* manipulation of packed ARGB
*
* 2) No device vs user space scaling has been taken into consideration. However
* the original case where this issues was observed does take this into
* consideration and shows the same benchmark results. However that code cannot
* be shared.
*
* 3) ColorModel is always assumed pack ARGB. Even if conversion was need to
* another color model it would be expected that this conversion would only
* double the time to render. The slow down using standard paint methods however,
* show a much greater slow down.
*
* 4) A fourth scenario was tested where the custom paint class immediately
* returned a cached raster without doing any operations on the raster. The
* timing results were very similar to the supplied custom paint method. So most
* of the time spent is not related to calculating and filling pixel data but
* instead on some kind of rendering overhead.
*/
public class PaintBenchmark{
int transparencyType = Paint.OPAQUE;
public static void main(String args[]){
Test t;
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setContentPane(new Test());
frame.setBounds(150,0, 2000, 1400);
frame.setVisible(true);
}
}
class Test extends JComponent{
//*** Variables to capture test information
int test = 8;
String[] title = new String[test]; // title[x] = title of test x
int[] frames = new int[test] ; // frames[x] = frams drawn for text x
long[] time = new long[test]; // time[x] = cummulative time spent drawing for text x
Object[] aliasMode = new Object[test];
//*** Variables to capture timing information
long periodEnd = 0; // start of current run (in ns)
long testPeriod = 10_000000000L; // 10 second runs for each test (in ns)
/** Variable that is changes the paint parameters for every paint call to visually show the speed of the rendering operation */
int adj =0;
/** Hold the different rendering hints to be used in testing */
Object[] modes = {RenderingHints.VALUE_ANTIALIAS_OFF, RenderingHints.VALUE_ANTIALIAS_ON};
public void paint(Graphics g0){
Graphics2D g = (Graphics2D) g0;
Rectangle b = g.getClipBounds();
//The test shape to be painted
Shape s = new Ellipse2D.Float(0,0,b.width, b.height);
//*** Background Pattern - Alpha Test ***
int tenth = b.height / 10;
for(int i = 10; 0 <= --i;){
g.fillRect(0, i*tenth, b.width, tenth/2);
}
adj++;
double a = Math.PI * (periodEnd-System.nanoTime()) / testPeriod;
int x2 = (int) (Math.cos(a) * b.height/2);
int y2 = (int) (Math.sin(a) * b.height/2);
//*** Start the timer ***
long timer = System.nanoTime();
if((periodEnd < timer) && (0 <= --test)){ // new test should start
periodEnd = System.nanoTime() + testPeriod;
aliasMode[test] = modes[test >> 2];
adj = 0;
}else if(test < 0){
paintSummary(g);
return;
}
switch(test & 3){
case 3:
title[test] = "Standard Graphics2D Painter";
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aliasMode[test]);
g.setPaint(new GradientPaint((int)b.getCenterX()-x2, (int)b.getCenterY()-y2, Color.black, (int)b.getCenterX() + x2, (int)b.getCenterY() + y2, Color.red));
g.fill(s);
break;
case 2:
title[test] = "Subclassed Graphics2D Painter";
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aliasMode[test]);
g.setPaint(new MyGradientPaint(b.x, b.y, Color.black, b.x + b.width, b.y + b.height, Color.red));
g.fill(s);
break;
case 1:
title[test] = "Custom Painter - Standard Graphics2D Painter Interface";
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aliasMode[test]);
g.setPaint(new CustomPaint(adj));
g.fill(s);
break;
case 0:
title[test] = "Custom Painter - Benchmark Reference using BufferedImage";
Filler customFill = new Filler(b.width, b.height, new CustomPaint(adj));
customFill.setRenderingHints(aliasMode[test]);
customFill.fill(s);
g.drawImage(customFill, 0, 0, null, null);
break;
default:
}
timer = System.nanoTime() - timer;
//*** End the timer ***
time[test] += timer;
frames[test]++;
String timingInfo = (timer/1_000_000) +" ms\t avg:"+ (time[test] / frames[test] / 1_000_000) +" ms";
g.setPaint(Color.BLACK);
g.setXORMode(Color.red);
g.drawString(test +": "+ title[test], 0, 20);
g.drawString("("+aliasMode[test]+")", 0, 40);
g.drawString(timingInfo, 0, 60);
repaint(0);
}
void paintSummary(Graphics2D g){
g.fill(g.getClipBounds());
g.setPaint(Color.WHITE);
for(int i = time.length; 0 <= --i;){
String summaryInfo = (time[i] / frames[i] / 1_000_000) + " ms";
int row = (4-(i&3))*20;
int col = (i>>2)*150;
g.drawString(aliasMode[i].toString().split(" ")[0], col, 20);
g.drawString(summaryInfo, col, row + 20);
g.drawString(title[i], 300, row + 20);
}
}
}
/**
* No changes to a standard paint implementation just subclassing an existing
* java paint class. Note the result is a significant slow down.
*/
class MyGradientPaint extends GradientPaint{
MyGradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2) {
super(x1, y1, color1, x2, y2, color2);
}
}
/**
* Custom paint with a simple fill
*/
class CustomPaint implements Paint {
int startVal;
CustomPaint(int val){
startVal = val*10;
}
public int getTransparency() {
return OPAQUE;
}
public CustomPaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
return new CustomPaintContext(ColorModel.getRGBdefault(), startVal);
}
}
class CustomPaintContext implements PaintContext{
WritableRaster raster;
int[] data;
ColorModel model;
int startVal;
CustomPaintContext(ColorModel colorModel, int val){
model = colorModel;
startVal = val;
}
public void dispose() {}
public ColorModel getColorModel() {
return model;
}
public Raster getRaster(int x, int y, int w, int h) {
if (raster == null || raster.getWidth() < w || raster.getHeight() < h) {
raster = model.createCompatibleWritableRaster(w, h);
data = ((DataBufferInt) raster.getDataBuffer()).getData();
}
fill2(data, x, y, w, h, raster.getWidth(), 0xFF000000);
return raster;
}
public void fill(int data[], int x, int y, int w, int h, int scanLineStride, int alpha){
for(int j = h; 0 <= --j;){
int index = (j + 1) * scanLineStride;
for(int i = w; 0 <= --i;){
data[--index] |= (x + i) * (y + j) + startVal | alpha;
}
}
}
public void fill2(int data[], int x, int y, int w, int h, int scanLineStride, int alpha){
for(int j = h; 0 <= --j;){
int index = (j + 1) * scanLineStride;
for(int i = w; 0 <= --i;){
data[--index] = (x + i) * (y + j) + startVal| alpha;
}
}
}
}
/**
* Custom alternative filling routing that uses BufferedImage instead of
* Graphics2d.setPaint to fill pixel values.
*
* 1) ImageBuffer is created to render the entire shape. Note this could be done
* in strips to reduce memory requirements.
* 2) Standard Graphics2D paint / fill operations are called on the
* BufferedImage to do a solid fill. This is used to identify what pixels
* should be filled and with what alpha values
* 3) The packed ARGB values of the resulting image is passed to the custom
* paint class to update pixel colors while retaining the alpha values in the
* image
*
*/
class Filler extends BufferedImage{
final private int[] mem;
CustomPaint p;
Object alphaRendering = RenderingHints.VALUE_ANTIALIAS_OFF;
public Filler(int w, int h, CustomPaint p) {
super(w, h, BufferedImage.TYPE_INT_ARGB);
mem = ((DataBufferInt) getRaster().getDataBuffer()).getData();
this.p = p;
}
public void fill(Shape s){
Rectangle bounds = new Rectangle(0,0,getWidth(), getHeight());
CustomPaintContext pc = p.createContext(getColorModel(), bounds, bounds, null, null);
Graphics2D g = (Graphics2D) createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, alphaRendering);
g.setPaint(new Color(0,0,0,255));
g.fill(s);
g.dispose();
pc.fill(mem, 0, 0, getWidth(), getHeight(), getWidth(), 0);
}
public void setRenderingHints(Object alphaHint){
alphaRendering = alphaHint;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Role your own graphics rendering
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b128)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b69, mixed mode)
This was also tested on Java 1.7
ADDITIONAL OS VERSION INFORMATION :
Mac OS X version 10.9.1
EXTRA RELEVANT SYSTEM CONFIGURATION :
Mac mini late 2012, Also tested on macbookpro mid 2009 with similar results
A DESCRIPTION OF THE PROBLEM :
Custom Paint operations (Via Graphics2D.setPaint, Paint & Paint Context) on the mac are extremely slow, up to 100x slow then doing an equivalent paint operation using BufferedImages.
Custom Paint operations for nonantialias rendering is about 20x slower than custom paint operations for antialias rendering. There is obviously something very wrong with the nonantialias rendering pipeline.
REGRESSION. Last worked in version 6u43
ADDITIONAL REGRESSION INFORMATION:
The apple version of the JVM did not show this performance issue
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the supplied test case. It will execute 4 scenarios under both non-antialias and antialias rendering modes. It takes about 80 seconds to complete the test. The final benchmark results are displayed on the screen.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
1) The custom painter using standard Graphics2D Paint / PaintContext should perform just as good or better then the alternative of painting to BufferedImage, iterating through all pixels change all the color, paint the BufferedImage to the screen.
2) The nonantialias painting should perform the same or better than the antialias painting. This only occurs for the standard java paint classes and the alternative BufferedImage workaround but not for subclasses of standard paint classes or custom paint implementations that are rendered through the standard Graphics2D.setPaint and fill methods.
ACTUAL -
Rendering custom Paint implementations seem to take 4x longer than expected for anitalias rendering and 100X longer than expected for non-antialias rendering
The custom paint when rendered through standard Graphics2D.setPaint / fill also produce artifact on the right edge of the Ellipse shape. The artifact were different for both nonantialias and alias rendering modes.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.geom.*;
import javax.swing.JFrame;
import javax.swing.JComponent;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
/**
* Benchmark showing performance issues with custom painting operations. Note
* the benchmark should be run with maximum java heap size of 1GB (-Xmx1024m)
* in order to avoid excessive time in GC cycles.
*
*
* The bench mark Results
* ----------------------
* 2012 mac mini running OS X 10.9.1 and JRE 1.8.0 b128
*
* non anti-alias painting issues
* 20x slower than with anti-alias turned on.
*
* Slow painting issues in general
* 100x slower than alternative (directly manipulating a BufferedImage) for non-anti alias rendering
* 4x slower than alternative (directly manipulating a BufferedImage) for anti alias rendering
*
* These performance issues were not seen on windows machines.
*
* Benchmark Approach
* ------------------
* A custom Paint and PaintContext class has been created to select RGB color
* based on the x and y position of the pixel. The custom paint implementation
* is used to fill an oval on top of striped background. The background is
* provided to show that 1) only pixels within the specified Shape object is
* painted and 2) the effects of anti-aliasing under different background colors.
*
* The custom paint implementation is then compared to 3 similar operations:
* 1) Rendering the custom paint using an alternative BufferedImage approach
* 2) Drawing the same scene with an alternative custom paint that is simple a
* subclass of a standard java2D paint without any implementation changes
* 3) Drawing the same scene with an standard java paint class
*
*
* Benchmark Considerations
* --------------------
* 1) The buffered image approach is really doing a paint operation twice. Once
* through the standard Graphcis2D draw methods and a second time through direct
* manipulation of packed ARGB
*
* 2) No device vs user space scaling has been taken into consideration. However
* the original case where this issues was observed does take this into
* consideration and shows the same benchmark results. However that code cannot
* be shared.
*
* 3) ColorModel is always assumed pack ARGB. Even if conversion was need to
* another color model it would be expected that this conversion would only
* double the time to render. The slow down using standard paint methods however,
* show a much greater slow down.
*
* 4) A fourth scenario was tested where the custom paint class immediately
* returned a cached raster without doing any operations on the raster. The
* timing results were very similar to the supplied custom paint method. So most
* of the time spent is not related to calculating and filling pixel data but
* instead on some kind of rendering overhead.
*/
public class PaintBenchmark{
int transparencyType = Paint.OPAQUE;
public static void main(String args[]){
Test t;
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setContentPane(new Test());
frame.setBounds(150,0, 2000, 1400);
frame.setVisible(true);
}
}
class Test extends JComponent{
//*** Variables to capture test information
int test = 8;
String[] title = new String[test]; // title[x] = title of test x
int[] frames = new int[test] ; // frames[x] = frams drawn for text x
long[] time = new long[test]; // time[x] = cummulative time spent drawing for text x
Object[] aliasMode = new Object[test];
//*** Variables to capture timing information
long periodEnd = 0; // start of current run (in ns)
long testPeriod = 10_000000000L; // 10 second runs for each test (in ns)
/** Variable that is changes the paint parameters for every paint call to visually show the speed of the rendering operation */
int adj =0;
/** Hold the different rendering hints to be used in testing */
Object[] modes = {RenderingHints.VALUE_ANTIALIAS_OFF, RenderingHints.VALUE_ANTIALIAS_ON};
public void paint(Graphics g0){
Graphics2D g = (Graphics2D) g0;
Rectangle b = g.getClipBounds();
//The test shape to be painted
Shape s = new Ellipse2D.Float(0,0,b.width, b.height);
//*** Background Pattern - Alpha Test ***
int tenth = b.height / 10;
for(int i = 10; 0 <= --i;){
g.fillRect(0, i*tenth, b.width, tenth/2);
}
adj++;
double a = Math.PI * (periodEnd-System.nanoTime()) / testPeriod;
int x2 = (int) (Math.cos(a) * b.height/2);
int y2 = (int) (Math.sin(a) * b.height/2);
//*** Start the timer ***
long timer = System.nanoTime();
if((periodEnd < timer) && (0 <= --test)){ // new test should start
periodEnd = System.nanoTime() + testPeriod;
aliasMode[test] = modes[test >> 2];
adj = 0;
}else if(test < 0){
paintSummary(g);
return;
}
switch(test & 3){
case 3:
title[test] = "Standard Graphics2D Painter";
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aliasMode[test]);
g.setPaint(new GradientPaint((int)b.getCenterX()-x2, (int)b.getCenterY()-y2, Color.black, (int)b.getCenterX() + x2, (int)b.getCenterY() + y2, Color.red));
g.fill(s);
break;
case 2:
title[test] = "Subclassed Graphics2D Painter";
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aliasMode[test]);
g.setPaint(new MyGradientPaint(b.x, b.y, Color.black, b.x + b.width, b.y + b.height, Color.red));
g.fill(s);
break;
case 1:
title[test] = "Custom Painter - Standard Graphics2D Painter Interface";
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aliasMode[test]);
g.setPaint(new CustomPaint(adj));
g.fill(s);
break;
case 0:
title[test] = "Custom Painter - Benchmark Reference using BufferedImage";
Filler customFill = new Filler(b.width, b.height, new CustomPaint(adj));
customFill.setRenderingHints(aliasMode[test]);
customFill.fill(s);
g.drawImage(customFill, 0, 0, null, null);
break;
default:
}
timer = System.nanoTime() - timer;
//*** End the timer ***
time[test] += timer;
frames[test]++;
String timingInfo = (timer/1_000_000) +" ms\t avg:"+ (time[test] / frames[test] / 1_000_000) +" ms";
g.setPaint(Color.BLACK);
g.setXORMode(Color.red);
g.drawString(test +": "+ title[test], 0, 20);
g.drawString("("+aliasMode[test]+")", 0, 40);
g.drawString(timingInfo, 0, 60);
repaint(0);
}
void paintSummary(Graphics2D g){
g.fill(g.getClipBounds());
g.setPaint(Color.WHITE);
for(int i = time.length; 0 <= --i;){
String summaryInfo = (time[i] / frames[i] / 1_000_000) + " ms";
int row = (4-(i&3))*20;
int col = (i>>2)*150;
g.drawString(aliasMode[i].toString().split(" ")[0], col, 20);
g.drawString(summaryInfo, col, row + 20);
g.drawString(title[i], 300, row + 20);
}
}
}
/**
* No changes to a standard paint implementation just subclassing an existing
* java paint class. Note the result is a significant slow down.
*/
class MyGradientPaint extends GradientPaint{
MyGradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2) {
super(x1, y1, color1, x2, y2, color2);
}
}
/**
* Custom paint with a simple fill
*/
class CustomPaint implements Paint {
int startVal;
CustomPaint(int val){
startVal = val*10;
}
public int getTransparency() {
return OPAQUE;
}
public CustomPaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
return new CustomPaintContext(ColorModel.getRGBdefault(), startVal);
}
}
class CustomPaintContext implements PaintContext{
WritableRaster raster;
int[] data;
ColorModel model;
int startVal;
CustomPaintContext(ColorModel colorModel, int val){
model = colorModel;
startVal = val;
}
public void dispose() {}
public ColorModel getColorModel() {
return model;
}
public Raster getRaster(int x, int y, int w, int h) {
if (raster == null || raster.getWidth() < w || raster.getHeight() < h) {
raster = model.createCompatibleWritableRaster(w, h);
data = ((DataBufferInt) raster.getDataBuffer()).getData();
}
fill2(data, x, y, w, h, raster.getWidth(), 0xFF000000);
return raster;
}
public void fill(int data[], int x, int y, int w, int h, int scanLineStride, int alpha){
for(int j = h; 0 <= --j;){
int index = (j + 1) * scanLineStride;
for(int i = w; 0 <= --i;){
data[--index] |= (x + i) * (y + j) + startVal | alpha;
}
}
}
public void fill2(int data[], int x, int y, int w, int h, int scanLineStride, int alpha){
for(int j = h; 0 <= --j;){
int index = (j + 1) * scanLineStride;
for(int i = w; 0 <= --i;){
data[--index] = (x + i) * (y + j) + startVal| alpha;
}
}
}
}
/**
* Custom alternative filling routing that uses BufferedImage instead of
* Graphics2d.setPaint to fill pixel values.
*
* 1) ImageBuffer is created to render the entire shape. Note this could be done
* in strips to reduce memory requirements.
* 2) Standard Graphics2D paint / fill operations are called on the
* BufferedImage to do a solid fill. This is used to identify what pixels
* should be filled and with what alpha values
* 3) The packed ARGB values of the resulting image is passed to the custom
* paint class to update pixel colors while retaining the alpha values in the
* image
*
*/
class Filler extends BufferedImage{
final private int[] mem;
CustomPaint p;
Object alphaRendering = RenderingHints.VALUE_ANTIALIAS_OFF;
public Filler(int w, int h, CustomPaint p) {
super(w, h, BufferedImage.TYPE_INT_ARGB);
mem = ((DataBufferInt) getRaster().getDataBuffer()).getData();
this.p = p;
}
public void fill(Shape s){
Rectangle bounds = new Rectangle(0,0,getWidth(), getHeight());
CustomPaintContext pc = p.createContext(getColorModel(), bounds, bounds, null, null);
Graphics2D g = (Graphics2D) createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, alphaRendering);
g.setPaint(new Color(0,0,0,255));
g.fill(s);
g.dispose();
pc.fill(mem, 0, 0, getWidth(), getHeight(), getWidth(), 0);
}
public void setRenderingHints(Object alphaHint){
alphaRendering = alphaHint;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Role your own graphics rendering