How can I effectively implement java.awt.Composite?

Reference Information. I need to be able to create images in a "disconnected" form. Usually the proposed approach is to convert images to shades of gray and show the image in gradations. The disadvantage is that it only works with images, which makes it cumbersome to show graphics where you do not have direct access to the image in the off state. Now I thought that this can be done on the fly with java.awt.Composite (and then I would not need to know how, for example, the icon is implemented to disable it). Only, it seems there is no implementation for converting to grayscale, so I had to create my own ...

However, I have cracked the implementation (and this does what I expect). But I'm not sure that it will actually work correctly for all cases (Javadocs of Composite / CompositeContext seems extremely thin for such a complex operation). And, as you can see from my implementation, I use a roundabout way of processing pixels by pixels, because there seems to be no easy way to read / write pixels in bulk in a format not dictated by the related rasters.

Any pointers to more extensive documentation / examples / tips are appreciated.

Here's SSCCE - it displays the (color) GradientPaint through DisabledComposite to convert the gradient to grayscale. Please note that in the real world you will not know what is displayed using calls. A gradient is just an example (sorry, but too often people don’t understand this, so this time I will make it explicit).

import java.awt.BorderLayout; import java.awt.Color; import java.awt.Composite; import java.awt.CompositeContext; import java.awt.Dimension; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class CompositeSSCE implements Runnable { static class DisabledComposite implements Composite { @Override public CompositeContext createContext( final ColorModel srcColorModel, final ColorModel dstColorModel, final RenderingHints hints) { return new DisabledCompositeContext(srcColorModel, dstColorModel); } } static class DisabledCompositeContext implements CompositeContext { private final ColorModel srcCM; private final ColorModel dstCM; final static int PRECBITS = 22; final static int WEIGHT_R = (int) ((1 << PRECBITS) * 0.299); final static int WEIGHT_G = (int) ((1 << PRECBITS) * 0.578); final static int WEIGHT_B = (int) ((1 << PRECBITS) * 0.114); final static int SRCALPHA = (int) ((1 << PRECBITS) * 0.667); DisabledCompositeContext(final ColorModel srcCM, final ColorModel dstCM) { this.srcCM = srcCM; this.dstCM = dstCM; } public void compose(final Raster src, final Raster dstIn, final WritableRaster dstOut) { final int w = Math.min(src.getWidth(), dstIn.getWidth()); final int h = Math.min(src.getHeight(), dstIn.getHeight()); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { int rgb1 = srcCM.getRGB(src.getDataElements(x, y, null)); int a1 = ((rgb1 >>> 24) * SRCALPHA) >> PRECBITS; int gray = ( ((rgb1 >> 16) & 0xFF) * WEIGHT_R + ((rgb1 >> 8) & 0xFF) * WEIGHT_G + ((rgb1 ) & 0xFF) * WEIGHT_B ) >> PRECBITS; int rgb2 = dstCM.getRGB(dstIn.getDataElements(x, y, null)); int a2 = rgb2 >>> 24; int r2 = (rgb2 >> 16) & 0xFF; int g2 = (rgb2 >> 8) & 0xFF; int b2 = (rgb2 ) & 0xFF; // mix the two pixels gray = gray * a1 / 255; final int ta = a2 * (255 - a1); r2 = gray + (r2 * ta / (255*255)); g2 = gray + (g2 * ta / (255*255)); b2 = gray + (b2 * ta / (255*255)); a2 = a1 + (ta / 255); rgb2 = (a2 << 24) | (r2 << 16) | (g2 << 8) | b2; Object data = dstCM.getDataElements(rgb2, null); dstOut.setDataElements(x, y, data); } } } @Override public void dispose() { // nothing for this implementation } } // from here on out its only the fluff to make this a runnable example public static void main(String[] argv) { Runnable r = new CompositeSSCE(); SwingUtilities.invokeLater(r); } // simple component to use composite to render static class DemoComponent extends JComponent { // demonstrate rendering an icon in grayscale with the composite protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; GradientPaint p = new GradientPaint(0, 0, Color.GREEN, 127, 127, Color.BLUE, true); g2d.setComposite(new DisabledComposite()); g2d.setPaint(p); g2d.fillRect(0, 0, getWidth(), getHeight()); } } // Fluff to use the Composite in Swing public void run() { try { JFrame f = new JFrame("Test grayscale composite"); DemoComponent c = new DemoComponent(); c.setPreferredSize(new Dimension(500, 300)); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setLayout(new BorderLayout()); f.add(c, BorderLayout.CENTER); f.pack(); f.setVisible(true); } catch (Exception e) { throw new RuntimeException(e); } } } 
+7
source share
2 answers

You can always create a BufferedImage , draw everything that needs to be gray-scaled for the Graphics object for this image using bufferedImage.createGraphics() , and then use javax.swing.GreyFilter to create a gray-scaled copy of the image.

This page shows how to use Filter .

You can also compare your approach with SwingX BlendComposite , which I expect does the same as you. BlendComposite has a saturation mode that allows you to use the grayscale. (It also has more modes.)

There is a demo of BlendComposite on this page.

On efficiency, I expect that there is no important difference between the two, because the intermediate steps and from what I see complete the copies of the image data with both. But if you save the grayscale image using the first method I proposed, you can prevent the recalculation of non-dynamic controls.

If you do one of the above, the performance will be correct, and I expect what you really want.

From your comments, I think you can just apply the effect to the component. For this you can use JLayer , available only in Java 7. From Javadoc there is an example of blending translucent green. You can replace this with gray. If you want to use JLayer in previous versions of Java, you can use JXLayer , part of the SwingX project.

+3
source

I apologize if I do not understand your question correctly, but maybe this will help you? It uses css to create grayscale images.

Also this one might be what you are looking for? It uses jquery and canvas to manage images.

0
source

All Articles