【问题标题】:JavaFX rendering / image manipulationJavaFX 渲染/图像处理
【发布时间】:2017-03-19 22:44:03
【问题描述】:

我制作了一个生成长阴影的 JavaFX 小应用程序。在这一点上,我在渲染中挣扎(见图)。

  1. 矩形角上的缺失线似乎很难修复。更改应用操作的循环会弄乱其他形状的阴影(例如圆形)。
  2. 我猜,'a' 处的故障与Bresenham algorithm 有关。(?)

附加信息:

更改图像分辨率没有任何区别:Gitches 不断出现。

问题:

如何解决? SDK 是否提供了一些有用的东西?我必须重写代码吗?

代码

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import javax.imageio.ImageIO;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;


public class Main extends Application {

    private PrintWriter writer;

    private String colorObjFilter = "0x009688ff";

    private static final String IMG_PATH = "img/ls-test-1k.png";

    private static final int LONGSHADOW_LENGTH = 100;

    private static final String
            ANSI_GREEN = "\u001B[32m",
            ANSI_RESET = "\u001B[0m";


    @Override
    public void start(Stage stage) throws Exception {
        writer = new PrintWriter("out.txt", "UTF-8");

        InputStream is = new FileInputStream(new File(IMG_PATH));
        ImageView imageView = new ImageView(new Image(is));
        Image image = imageView.getImage();

        StackPane root = new StackPane();
        Scene scene = new Scene(root, image.getWidth(), image.getHeight(), Paint.valueOf
                ("#EEEEEE"));
        scene.addEventFilter(KeyEvent.KEY_PRESSED, evt -> {
            if (evt.getCode().equals(KeyCode.ESCAPE)) {
                stage.close();
            }
        });

        final Canvas canvas = new Canvas(image.getWidth(), image.getHeight());

        canvas.setOnMouseClicked((MouseEvent e) -> {
            Color color = image.getPixelReader().getColor((int) e.getX(), (int) e.getY());
            System.out.println(ANSI_GREEN + " -> " + color.toString() + ANSI_RESET);
            colorObjFilter = color.toString();

            try {
                processImage(root, canvas, image);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        });

        root.getChildren().addAll(imageView, canvas);
        stage.setScene(scene);
        stage.show();
    }


    private void processImage(StackPane root, Canvas canvas, Image image) throws IOException {
        long delta = System.currentTimeMillis();

        int width = (int) image.getWidth();
        int height = (int) image.getHeight();

        GraphicsContext gc = canvas.getGraphicsContext2D();
        System.out.println("width: " + width + "\theight: " + height);

        BufferedImage bufferedImage = ImageIO.read(new File(IMG_PATH));

        // keep threshold small to get clean paths to draw
        edgeDetection(gc, image, 0.00000001d);

        writer.close();
        Label label = new Label();
        root.setAlignment(Pos.BOTTOM_LEFT);
        root.setOnMouseMoved(event -> label.setText(event.getX() + "|" + event.getY()
                + "|" + bufferedImage.getRGB((int) event.getX(), (int) event.getY())));
        root.getChildren().addAll(label);
        System.out.println("took: " + (System.currentTimeMillis() - delta) + " ms");

    }


    public void edgeDetection(GraphicsContext gc, Image image, double threshold) {
        Color topPxl, lowerPxl;
        double topIntensity, lowerIntensity;
        PixelWriter pw = gc.getPixelWriter();

        for (int y = 0; y < image.getHeight() - 1; y++) {
            for (int x = 1; x < image.getWidth(); x++) {

                topPxl = image.getPixelReader().getColor(x, y);
                lowerPxl = image.getPixelReader().getColor(x - 1, y + 1);

                topIntensity = (topPxl.getRed() + topPxl.getGreen() + topPxl.getBlue()) / 3;
                lowerIntensity = (lowerPxl.getRed() + lowerPxl.getGreen() + lowerPxl.getBlue()) / 3;

                if (Math.abs(topIntensity - lowerIntensity) > threshold) {
                    int y2 = y;
                    for (int x2 = x; x2 < x + LONGSHADOW_LENGTH; x2++) {
                        y2++;
                        try {
                            Color color = image.getPixelReader().getColor(x2, y2);
                            // colorObjFilter protects the purple letter being manipulated
                            if (!color.toString().toLowerCase()
                                    .contains(colorObjFilter.toLowerCase())) {
                                pw.setColor(x2, y2, Color.color(.7f, .7f, .7f, .9f));
                            }
                        } catch (Exception e) {
                            System.out.println("Error: " + e.getMessage());
                        }
                    }
                }
            }
        }
    }


    public static void main(String[] args) {
        launch(args);
    }
}

【问题讨论】:

    标签: java image-processing javafx bresenham


    【解决方案1】:

    我不知道为什么你的原始程序有一些渲染工件。

    这是另一种解决方案,它是非常暴力的,因为它只是生成一个阴影图像,它会以不同的偏移量一遍又一遍地渲染,最终得到一个长阴影。演示了几种生成shadowImage的方法,一种是在原始图像上使用ColorAdjust效果,另一种是使用PixelWriter生成阴影图像。

    您最初的解决方案是使用 PixelWriter 来处理具有适当阴影生成算法的所有内容,这样会更加优雅(如果您可以让它工作 ;-)。

    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.canvas.Canvas;
    import javafx.scene.canvas.GraphicsContext;
    import javafx.scene.control.Label;
    import javafx.scene.effect.ColorAdjust;
    import javafx.scene.image.Image;
    import javafx.scene.image.PixelReader;
    import javafx.scene.image.PixelWriter;
    import javafx.scene.image.WritableImage;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    public class ShadowSpray extends Application {
        private static final double W = 400;
        private static final double H = 400;
        private static final int SHADOW_LENGTH = 100;
    
        private static final double IMG_X = 20;
        private static final double IMG_Y = 20;
    
        private static final int FONT_SIZE = 200;
    
        private static final double SHADOW_SLOPE_FACTOR = 1.5;
    
        Color SHADOW_COLOR = Color.GRAY.brighter();
    
        @Override
        public void start(Stage stage) {
            Image image = getImage();
    
            Canvas canvas = new Canvas(W, H);
            GraphicsContext gc = canvas.getGraphicsContext2D();
    //        drawWithShadowUsingStencil(gc, image, IMG_X, IMG_Y, SHADOW_LENGTH, SHADOW_COLOR);
            drawWithShadowUsingColorAdjust(gc, image, IMG_X, IMG_Y, SHADOW_LENGTH);
    
            stage.setScene(new Scene(new Group(canvas)));
            stage.show();
        }
    
        private void drawWithShadowUsingColorAdjust(GraphicsContext gc, Image image, double x, double y, int shadowLength) {
            // here the color adjust for the shadow is based upon the intensity of the input image color
            // which is a weird way to calculate a shadow color, but does come out nicely
            // because it appropriately handles antialiased input images.
            ColorAdjust monochrome = new ColorAdjust();
            monochrome.setBrightness(+0.5);
            monochrome.setSaturation(-1.0);
    
            gc.setEffect(monochrome);
    
            for (int offset = shadowLength; offset > 0; --offset) {
                gc.drawImage(image, x + offset, y + offset / SHADOW_SLOPE_FACTOR);
            }
    
            gc.setEffect(null);
    
            gc.drawImage(image, x, y);
        }
    
        private void drawWithShadowUsingStencil(GraphicsContext gc, Image image, double x, double y, int shadowLength, Color shadowColor) {
            Image shadow = createShadowImage(image, shadowColor);
    
            for (int offset = shadowLength; offset > 0; --offset) {
                gc.drawImage(shadow, x + offset, y + offset / SHADOW_SLOPE_FACTOR);
            }
    
            gc.drawImage(image, x, y);
        }
    
    
        private Image createShadowImage(Image image, Color shadowColor) {
            WritableImage shadow = new WritableImage(image.getPixelReader(), (int) image.getWidth(), (int) image.getHeight());
            PixelReader reader = shadow.getPixelReader();
            PixelWriter writer = shadow.getPixelWriter();
            for (int ix = 0; ix < image.getWidth(); ix++) {
                for (int iy = 0; iy < image.getHeight(); iy++) {
                    int argb = reader.getArgb(ix, iy);
                    int a = (argb >> 24) & 0xFF;
                    int r = (argb >> 16) & 0xFF;
                    int g = (argb >>  8) & 0xFF;
                    int b =  argb        & 0xFF;
    
                    // because we use a binary choice, we lose anti-alising info in the shadow so it looks a bit jaggy.
                    Color fill = (r > 0 || g > 0 || b > 0) ? shadowColor : Color.TRANSPARENT;
    
                    writer.setColor(ix, iy, fill);
                }
            }
            return shadow;
        }
    
        private Image getImage() {
            Label label = new Label("a");
            label.setStyle("-fx-text-fill: forestgreen; -fx-background-color: transparent; -fx-font-size: " + FONT_SIZE + "px;");
            Scene scene = new Scene(label, Color.TRANSPARENT);
            SnapshotParameters snapshotParameters = new SnapshotParameters();
            snapshotParameters.setFill(Color.TRANSPARENT);
            return label.snapshot(snapshotParameters, null);
        }
    
        public static void main(String[] args) {
            launch();
        }
    }
    

    内置,JavaFX 有一个DropShadow 效果,这几乎是你想要的,尤其是当你将散布设置为 1 和半径设置为 0 时,然而,它只生成单个偏移阴影图像而不是长阴影效果.


    带有一些替代文字和较短的“长阴影”:

    【讨论】:

    • 谢谢jewelsea,我会用单色效果检查我的代码并告诉你。很好的答案。
    • 在仔细查看了您的代码后,您的技能给我留下了深刻的印象。帮助很大,让我对在 JavaFX 中使用画布有了新的认识。
    猜你喜欢
    • 2017-09-21
    • 2014-06-21
    • 2022-01-25
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-07
    相关资源
    最近更新 更多