当前位置: 首页 > 知识库问答 >
问题:

JavaFX-防止缩放画布上的模糊,实现像素化缩放

宫俊远
2023-03-14

缩放画布时,有什么方法可以阻止这种模糊吗?我想这和GPU插值有关。但我这里需要的是一个像素完美的“像素化”变焦。只需使用最近的“真实”相邻像素的颜色。

我在这里看到了解决方案,但在这两个解决方案中,有一个建议工作(#1/#4),#4肯定是CPU扩展,而#1我猜也是。

这种缩放需要快速——我希望能够支持多达20-25层(可能是StackPane中的画布,但我对其他想法持开放态度,只要它们不融化中央处理器)。我怀疑没有JFX提供的图形处理器支持也能做到这一点,但也许没有足够灵活的应用编程接口。像链接答案中的#4这样依赖于CPU重新缩放的策略可能不会起作用。

如果使用此代码放大到最高缩放级别,模糊是显而易见的。

我们需要更新JFXAPI来支持这一点吗?这应该是可能的。

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;

public class ScaleTest extends Application {
    
    private static final int width = 1200;
    private static final int height = 800;
    private static final int topMargin = 32;
    private StackPane stackPane;
    private IntegerProperty zoomLevel = new SimpleIntegerProperty(100);
    
    @Override
    public void start(Stage stage) {
        stage.setWidth(width);
        stage.setMinHeight(height);
        
        stackPane = new StackPane();
        stackPane.setLayoutY(topMargin);
        Canvas canvas = new Canvas(width, height - topMargin);
        
        Label label = new Label();
        label.setLayoutY(2);
        label.setLayoutX(2);
        label.setStyle("-fx-text-fill: #FFFFFF");
        label.textProperty().bind(zoomLevel.asString());
        Button zoomInBtn = new Button("Zoom In");
        zoomInBtn.setLayoutY(2);
        zoomInBtn.setLayoutX(50);
        zoomInBtn.onActionProperty().set((e) -> {
            if (zoomLevel.get() < 3200) {
                zoomLevel.set(zoomLevel.get() * 2);
                stackPane.setScaleX(zoomLevel.get() / 100.0);
                stackPane.setScaleY(zoomLevel.get() / 100.0);
            }
        });
        Button zoomOutBtn = new Button("Zoom Out");
        zoomOutBtn.setLayoutY(2);
        zoomOutBtn.setLayoutX(140);
        zoomOutBtn.onActionProperty().set((e) -> {
            if (zoomLevel.get() > 25) {
                zoomLevel.set(zoomLevel.get() / 2);
                stackPane.setScaleX(zoomLevel.get() / 100.0);
                stackPane.setScaleY(zoomLevel.get() / 100.0);
            }
        });
        
        Pane mainPane = new Pane(stackPane, label, zoomInBtn, zoomOutBtn);
        mainPane.setStyle("-fx-background-color: #000000");
        Scene scene = new Scene(mainPane);
        stage.setScene(scene);
        
        drawGrid(canvas, 0, 0, width, height - topMargin, 16);
        stackPane.getChildren().add(canvas);
        
        stage.show();
    }
    
    private void drawGrid(Canvas canvas, int xPos, int yPos, int width, int height, int gridSize) {
        boolean darkX = true;
        String darkCol = "#111111";
        String lightCol = "#222266";
        
        for (int x = xPos; x < canvas.getWidth(); x += gridSize) {
            boolean dark = darkX;
            darkX = !darkX;
            if (x > width) {
                break;
            }
            
            for (int y = yPos; y < canvas.getHeight(); y += gridSize) {
                if (y > height) {
                    break;
                }
                
                dark = !dark;
                String color;
                if (dark) {
                    color = darkCol;
                } else {
                    color = lightCol;
                }
                canvas.getGraphicsContext2D().setFill(Paint.valueOf(color));
                canvas.getGraphicsContext2D().fillRect(x, y, gridSize, gridSize);
            }
        }
    }
}

共有2个答案

燕英逸
2023-03-14

这似乎起到了作用:


package com.analogideas.scratch;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;


public class ImagePixelator extends Application {

    private static final int width = 1200;
    private static final int height = 800;
    private static final int topMargin = 32;
    private IntegerProperty zoomLevel = new SimpleIntegerProperty(1);

    public static void main(String[] args) {
        launch(args);
    }
    
    @Override
    public void start(Stage stage) {
        stage.setWidth(width);
        stage.setMinHeight(height);

        Canvas canvasOut = new Canvas(width, height - topMargin);
        Canvas canvas = new Canvas(width, height - topMargin);
        drawGrid(canvas, 0, 0, width, height - topMargin, 16);
        WritableImage img = canvas.snapshot(null, null);
        drawImage(canvasOut, img, 0, 0, 1);
        StackPane pane = new StackPane(canvasOut);

        Label label = new Label();
        label.setLayoutY(2);
        label.setLayoutX(2);
        label.setStyle("-fx-text-fill: #FFFFFF");
        label.textProperty().bind(zoomLevel.asString());
        Button zoomInBtn = new Button("Zoom In");
        zoomInBtn.setLayoutY(2);
        zoomInBtn.setLayoutX(50);
        zoomInBtn.onActionProperty().set((e) -> {
            if (zoomLevel.get() < 3200) {
                zoomLevel.set(zoomLevel.get() * 2);
                int z = zoomLevel.get();
                drawImage(canvasOut, img, 0, 0, z);
            }
        });
        Button zoomOutBtn = new Button("Zoom Out");
        zoomOutBtn.setLayoutY(2);
        zoomOutBtn.setLayoutX(140);
        zoomOutBtn.onActionProperty().set((e) -> {
            if (zoomLevel.get() > 1) {
                zoomLevel.set(zoomLevel.get() /2);
                int z = zoomLevel.get();
                drawImage(canvasOut, img, 0, 0, z);
            }
        });

        Pane mainPane = new Pane(pane, label, zoomInBtn, zoomOutBtn);
        mainPane.setStyle("-fx-background-color: #000000");
        Scene scene = new Scene(mainPane);
        stage.setScene(scene);
        stage.show();
    }

    private void drawImage(Canvas canvas, Image image, int x, int y, float zoom) {
        GraphicsContext g = canvas.getGraphicsContext2D();
        g.setImageSmoothing(false);
        int w = (int) (image.getWidth() * zoom);
        int h = (int) (image.getHeight() * zoom);
        g.drawImage(image, x, y, w, h);

    }

    private void drawGrid(Canvas canvas, int xPos, int yPos, int width, int height, int gridSize) {
        Paint darkPaint = Paint.valueOf("#111111");
        Paint lightPaint = Paint.valueOf("#222266");
        GraphicsContext g = canvas.getGraphicsContext2D();
        g.setImageSmoothing(false);
        int xLimit = width / gridSize;
        int yLimit = height / gridSize;
        for (int x = 0; x <= xLimit; x++) {
            for (int y = 0; y <= yLimit; y++) {
                boolean dark = (((x ^ y) & 1) == 0);
                g.setFill(dark ? darkPaint : lightPaint);
                g.fillRect(xPos + x * gridSize, yPos + y * gridSize, gridSize, gridSize);
            }
        }
    }
}

邵弘伟
2023-03-14

您的示例调整节点的缩放属性以重新取样固定大小的图像,这不可避免地会导致此类工件。只有矢量表示可以任意精度缩放。您可能需要决定您的应用程序将如何支持向量和/或位图。例如,如果您的应用程序真的是关于缩放矩形,您将调用具有缩放坐标的fillRect(),而不是缩放较小矩形的图片。

你已经引用了一个很好的总结或调整大小,所以我将关注向量机会:

>

一个SVGPath是一个Shape,可以按此缩放。

在内部,字体存储单个图示符的矢量表示。实例化时,图示符将以特定的点大小进行栅格化。

如果已知合适的矢量表示,可以将绘图与画布的大小绑定,如下所示。

 类似资料:
  • 然后,我根据页面大小与图像大小计算缩放的维度,返回:java.awt.dimension[width=562,height=792]我使用下面的代码来计算缩放的维度: 为了实际执行图像缩放,我使用image Scalr API: 我的问题是我做错了什么?当缩放到较小的尺寸时,大的图像不应该被模糊。这与PDF页面的分辨率/大小有关吗?

  • 除了平移和旋转,HTML5的画布API还提供了缩放画布上下文的手段。本节,我们将使用scale()方法来按比例缩小画布上下文的高度。 图4-5 缩放画布上下文 绘制步骤 按照以下步骤,绘制一个按比例缩小的矩形: 1. 定义画布上下文,及矩形的尺寸: window.onload = function(){ var canvas  = document.getElementById("myCan

  • 我有一个表单,允许用户上传图像。 加载映像后,我们对其执行一些缩放,以便在将其传递回服务器之前减小其文件大小。 为此,我们将其放置在画布上并在画布上进行操作。 这段代码将在画布上呈现缩放后的图像,画布大小为320 x 240px: ... 哪里有帆布。宽度和画布。高度是图像高度和宽度x,是基于原始图像大小的比例因子。 但当我使用代码时: ...我只得到画布上图像的一部分,在这种情况下是左上角。我需

  • 我的JavaFX 8应用程序中有以下代码,用于控制添加到场景中的画布元素的变换比例: 问题是,就像它所说的,附加到转换中,并且由于浮点精度,每次滚动时缩放级别都会略有不同。 由于某些原因,我必须修改图形上下文转换,而不是直接修改画布(换句话说,要进行翻译,我必须使用transform.setTx(…) 而不是画布。setTranslateX(…) 。 解决方案是设置转换的尺度,同时考虑枢轴点(即鼠

  • 我的出发点如下: FXML: Java FX控制器:

  • 在 Sketch 中,你有两种方式可以查看作品。选用那种查看方式,取决于你所做工作的类型。这两种模式可以通过点击“视图(Vew)”菜单中的“显示/隐藏像素网格(Show/Hide Pixel Grid)”来切换。应当注意,在100%缩放尺寸(真实大小)下,这两种模式看起来是一样的,但视图放大的时候就可以看出区别。 如果你关心每一个独立的像素在文档中看起来是怎样的,那么这种模式适合你用。你看到的基本