Domanda

I have several Areas that I need to fill with a color gradient. I have a function that associates a pixel position with its color, like this:

(x,y) -> some RGBA color

My question is: what part of the Java API would allow me to use such a function to fill my Areas?

  • I have looked into gradients in Java 2D, but I think they are too specific to achieve what I want (they don't accept a function like mine).
  • I tried to understand the Paint/Raster/ColorModel stuff, but it's still very blurry in my mind and I still don't understand if what I want can be represented by a ColorModel. I'm under the impression that the purpose of this class is not to associate a pixel position with its color, but a color representation to another representation, am I right on this?
  • The only viable option I thought of is using a BufferedImage, and use setRGB() on every pixel with my function's value. However, as it's a rectangle, I would have to generate transparent pixels when out of the bounds of my Area, and that might not be the best way regarding performance. Would that be the right way to go, anyway?

Are there some more appropriate solutions that I'm missing here?

Note: I'm not looking for detailed implementation of a solution, I just want to go in the right direction ;-)

È stato utile?

Soluzione

I was curious, and implemented the approach mentioned in the comment:

Could one solution be to simply fill a BufferedImage completely with the desired colors, and then paint this image using the given area as the Graphics#setClip? (Not sure about the performance here, but most likely better than manual tests...)

The result is the following:

enter image description here

This example uses some "dummy" class

class ColorFunction
{
    int getColor(int x, int y) 
    {
        ...
    }
}

that is backed by a randomly filled BufferedImage, just for this test. This function is transferred into a BufferedImage in the paintComponent method. In a "real" application case, this could and should be done somewhere else, maybe in some constructor, so that it has to be done only once, but this depends on how this should be used. However, then, the image is just painted, using the Area as the clipping shape of the Graphics2D.

It seems feasible, but I have not yet made any detailed performance tests.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class CustomFillingTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        ColorFunction colorFunction = new ColorFunction();
        Area area = createTestArea();

        CustomFillingPanel customFillingPanel = 
            new CustomFillingPanel(colorFunction, area);
        f.getContentPane().add(customFillingPanel, BorderLayout.CENTER);

        f.setSize(400,200);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static Area createTestArea()
    {
        Font font = new Font("Monospaced", Font.BOLD, 120);
        final FontRenderContext fontRenderContext = 
            new FontRenderContext(null, true, true);
        GlyphVector glyphVector = font.createGlyphVector(
            fontRenderContext, "Test");
        Shape shape = glyphVector.getOutline(0,0);
        AffineTransform at = AffineTransform.getTranslateInstance(40, 100);
        Area area = new Area(at.createTransformedShape(shape));
        return area;
    }




}



class ColorFunction
{
    private final BufferedImage bufferedImage;

    ColorFunction()
    {
        this.bufferedImage = createDummyImage(1000, 1000);
    }

    private static BufferedImage createDummyImage(int w, int h)
    {
        Random random = new Random(1);
        BufferedImage image = 
            new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics graphics = image.createGraphics();
        graphics.setColor(Color.BLACK);
        graphics.fillRect(0, 0, w, h);
        for (int i=0; i<1000; i++)
        {
            int r = random.nextInt(255);
            int g = random.nextInt(255);
            int b = random.nextInt(255);
            Color c = new Color(r,g,b);
            int x = random.nextInt(w);
            int y = random.nextInt(h);
            int n = random.nextInt(w/10);
            graphics.setColor(c);
            graphics.fillRect(x,y,n,n);
        }
        graphics.dispose();
        return image;
    }

    int getColor(int x, int y)
    {
        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        return bufferedImage.getRGB(x%w, y%h);
    }
}


class CustomFillingPanel extends JPanel
{
    private final ColorFunction colorFunction;
    private final Area area;

    CustomFillingPanel(ColorFunction colorFunction, Area area)
    {
        this.colorFunction = colorFunction;
        this.area = area;
    }

    private static void paintIntoImage(
        ColorFunction colorFunction, BufferedImage bufferedImage)
    {
        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        for (int y=0; y<h; y++)
        {
            for (int x=0; x<w; x++)
            {
                int rgb = colorFunction.getColor(x, y);
                bufferedImage.setRGB(x, y, rgb);
            }
        }
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        Rectangle b = area.getBounds();
        BufferedImage bufferedImage = 
            new BufferedImage(b.width,  b.height, BufferedImage.TYPE_INT_ARGB);
        paintIntoImage(colorFunction, bufferedImage);

        g.setClip(area);
        g.drawImage(bufferedImage, b.x, b.y, null);

    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top