【问题标题】:Is there a "fill" function for arbitrary shapes in javafx?javafx中是否有用于任意形状的“填充”功能?
【发布时间】:2014-07-21 22:01:19
【问题描述】:

我需要知道使用 JavaFX 可以通过哪种方式为以下图像 (PNG) 着色。此图像当前包含在 JavaFX 的 ImageView 中:

我想将区域 1 着色为蓝色,第二个区域为红色,最后两个区域为紫色。我怎样才能在 JavaFX 中做到这一点? Windows Paint中没有某种功能吗? (你知道的,用边界之间的颜色填充某个区域的绘画桶)。

【问题讨论】:

  • 如果将所有四个区域都添加到Pane 中并根据需要为它们着色,并将Circle 设置为剪辑会怎样?这需要您创建一个反映您的图像的场景层次结构,这听起来不像您想要的。听起来您想要一个根据给定图像运行的动态解决方案,我认为这在 JavaFX 中是不可能的,至少使用节点是不可能的。您可能可以通过使用能够进行此类操作的库的WebView 来做到这一点。
  • 这听起来确实是最好的选择。你将如何在 javaFX 中为图像着色?我知道它可以逐个像素地完成,但肯定有更好的方法吗?
  • jewelsea 的答案看起来更适合您的需求,因为它是动态的(并且使用名为 Kraken 的类,您不会出错),并且提供的替代方法听起来像是一种操纵颜色的方法。

标签: java css javafx paint javafx-8


【解决方案1】:
Shape circle = new Circle(x,y,r);
Shape rect = new Rectangle(x,y,w,h);
Shape region1 = Shape.subtract(circle, rect);// to "cut" the rect away from a circle.
// You'll need to do this twice for each piece.
region1 = Shape.subtract(region1,anotherRect); 
region1.setFill(Color.BLUE);
// Then simply add your shape to a node and set it's translation.

这种方式的工作原理是矩形与圆重叠的地方,圆的那部分将被删除。

【讨论】:

    【解决方案2】:

    建议的方法

    您可以使用flood fill 算法。

    示例代码

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Point2D;
    import javafx.scene.Scene;
    import javafx.scene.image.*;
    import javafx.scene.layout.HBox;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    import java.util.Stack;
    
    public class UnleashTheKraken extends Application {
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(final Stage stage) {
            Image original = new Image(
                "http://s12.postimg.org/wofhjvy2h/image_2.jpg"
            );
    
            WritableImage updateable = new WritableImage(
                    original.getPixelReader(),
                    (int) original.getWidth(),
                    (int) original.getHeight()
            );
    
            Kraken kraken = new Kraken(updateable, Color.WHITE);
            kraken.unleash(new Point2D(40,   40), Color.BLUE);
            kraken.unleash(new Point2D(40,  100), Color.RED);
            kraken.unleash(new Point2D(100, 100), Color.GREEN);
            kraken.unleash(new Point2D(120,  40), Color.YELLOW);
    
            ImageView originalView = new ImageView(original);
            ImageView filledView   = new ImageView(updateable);
    
            HBox layout = new HBox(10, originalView, filledView);
            layout.setPadding(new Insets(10));
            stage.setScene(new Scene(layout));
            stage.show();
        }
    
        class Kraken {
            private final WritableImage image;
            private final Color colorToFill;
    
            // tolerance for color matching (on a scale of 0 to 1);
            private final double E = 0.3;
    
            public Kraken(WritableImage image, Color colorToFill) {
                this.image = image;
                this.colorToFill = colorToFill;
            }
    
            public void unleash(Point2D start, Color color) {
                PixelReader reader = image.getPixelReader();
                PixelWriter writer = image.getPixelWriter();
    
                Stack<Point2D> stack = new Stack<>();
                stack.push(start);
    
                while (!stack.isEmpty()) {
                    Point2D point = stack.pop();
                    int x = (int) point.getX();
                    int y = (int) point.getY();
                    if (filled(reader, x, y)) {
                        continue;
                    }
    
                    writer.setColor(x, y, color);
    
                    push(stack, x - 1, y - 1);
                    push(stack, x - 1, y    );
                    push(stack, x - 1, y + 1);
                    push(stack, x    , y + 1);
                    push(stack, x + 1, y + 1);
                    push(stack, x + 1, y    );
                    push(stack, x + 1, y - 1);
                    push(stack, x,     y - 1);
                }
            }
    
            private void push(Stack<Point2D> stack, int x, int y) {
                if (x < 0 || x > image.getWidth() ||
                    y < 0 || y > image.getHeight()) {
                    return;
                }
    
                stack.push(new Point2D(x, y));
            }
    
            private boolean filled(PixelReader reader, int x, int y) {
                Color color = reader.getColor(x, y);
    
                return !withinTolerance(color, colorToFill, E);
            }
    
            private boolean withinTolerance(Color a, Color b, double epsilon) {
                return
                        withinTolerance(a.getRed(),   b.getRed(),   epsilon) &&
                        withinTolerance(a.getGreen(), b.getGreen(), epsilon) &&
                        withinTolerance(a.getBlue(),  b.getBlue(),  epsilon);
            }
    
            private boolean withinTolerance(double a, double b, double epsilon) {
                return Math.abs(a - b) < epsilon;
            }
        }
    }
    

    其他问题的答案

    但是图像不会逐像素着色吗?

    是的,这就是重点,您需要对像素进行着色。带有位图显示的计算机图形中的一切最终都归结为着色像素。

    这是一种有效的着色方式吗?

    在您提供的示例图像上,它是即时的(据我所知)。在空间方面它会占用一些内存,但所有此类算法都将使用内存。我提供的示例代码不是可以设计的最有效的洪水填充着色算法(时间或空间方面)。我链接的维基百科页面有替代更有效(和更复杂)的算法,如果你需要,你可以应用。

    替代方法

    如果您对每个区域都有一个镂空模板形状,您可以堆叠模板并对它们应用ColorAdjust 效果(例如:How to change color of image in JavaFX)。 ColorAdjust (可能)是一种硬件加速效果。这种替代方法不是通用方法,因为它需要您了解模板形状。

    【讨论】:

    • 但是图像不会逐像素着色吗?这是一种有效的着色方式吗?
    • 更新答案以解决其他问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多