JavaFX ImageView没有任何平滑

时间:2013-04-18 17:17:07

标签: javafx javafx-2

是否可以在JavaFX 2.2中的ImageView中渲染缩放图像而不应用任何平滑?我正在使用setSmooth(false)将50x50图像渲染到200x200 ImageView中,因此源图像中的每个像素都应映射到屏幕上的4x4正方形。

但是,生成的渲染仍然会平滑所有16个目标像素上的源像素。有没有人知道如何在不手动将每个像素复制到新图像的情况下执行此操作?

5 个答案:

答案 0 :(得分:23)

在JavaFX 2.2中ImageView始终会进行一些平滑,无论您向ImageView提供smooth提示。

(基于使用Java 7u15和带有ATI HD4600显卡的Windows 7进行测试)。

也许这是一个ImageView将始终平滑Image的错误,但文档并没有真正指定平滑做什么或不做什么,所以很难说它是什么意图是。您可能希望将此问题的引用发布到openjfx-dev mailing list,或在JavaFX issue tracker中记录问题,以便从开发人员那里获得更专家的意见。


我尝试了一些不同的方法来缩放图像:

  1. 缩放Image constructor
  2. 使用fitWidth / fitHeight缩放ImageView
  3. 使用ImageView上的scaleX / scaleY属性进行缩放。
  4. 使用PixelReaderImage进行抽样并使用PixelWriter创建新图片进行缩放。
  5. 我发现方法1& 4导致像你想要的那样锐利的像素化图像和2& 3导致模糊的别名图像。

    robot-sampling

    Sample code生成上述输出。


    更新有关实施您自己的图片过滤器的想法

    JavaFX效果与用于图像加载例程的过滤器不同,但可以创建过滤图像的效果。在JavaFX 2.2公开记录的API中,支持创建自定义效果,因此创建自定义效果可能会很困难。

    native code for image support最近作为openjfx project的一部分开源,因此您可以查看该内容,了解当前如何实施过滤。

    您可能还想提交feature request against the JavaFX runtime project以“允许我们制作自己的2D过滤器”。

答案 1 :(得分:3)

我知道这有点老了,但我最近需要这样的ImageView,而下面的小黑客正是我想要的(Windows)机器。不能保证它无处不在。

import com.sun.javafx.sg.prism.NGImageView;
import com.sun.javafx.sg.prism.NGNode;
import com.sun.prism.Graphics;
import com.sun.prism.Texture;
import com.sun.prism.impl.BaseResourceFactory;

import com.sun.prism.Image;
import javafx.scene.image.ImageView;

@SuppressWarnings("restriction")
public class PixelatedImageView extends ImageView {
    @Override protected NGNode impl_createPeer() {
        return new NGImageView() {
            private Image image;

            @Override public void setImage(Object img) {
                super.setImage(img);
                image = (Image) img;
            }

            @Override protected void renderContent(Graphics g) {
                BaseResourceFactory factory = (BaseResourceFactory) g.getResourceFactory();
                Texture tex = factory.getCachedTexture(image, Texture.WrapMode.CLAMP_TO_EDGE);
                tex.setLinearFiltering(false);
                tex.unlock();
                super.renderContent(g);
            }
        };
    }
}

这里的技巧是重新使用纹理,因此线性过滤设置保持“粘性”。然而,为什么NGImageView无法简单地将“平滑”标志传递给纹理的线性过滤设置。

答案 2 :(得分:0)

当您将以下构造函数添加到Martin Sojka的答案时,您只需将javafx Image传递给构造函数即可。尽管有关于已弃用功能的警告,但他的答案仍然可以正常工作(在JDK 1.8_121上)。

public PixelatedImageView (javafx.scene.image.Image image) {
    super(image);
}

答案 3 :(得分:0)

万一有人在Java 9或更高版本中遇到相同的问题,这是Martin Sojka解决方案的编辑版本,该解决方案使用反射来访问ImageView的某些私有成员:

import java.lang.reflect.InvocationTargetException;

import javafx.scene.Node;
import javafx.scene.image.ImageView;

import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.scene.ImageViewHelper;
import com.sun.javafx.sg.prism.NGImageView;
import com.sun.javafx.sg.prism.NGNode;
import com.sun.prism.Graphics;
import com.sun.prism.Image;
import com.sun.prism.Texture;
import com.sun.prism.impl.BaseResourceFactory;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;

public class PixelatedImageView extends ImageView {

    public PixelatedImageView(javafx.scene.image.Image image) {
        super(image);

        try {
            initialize();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void initialize() throws IllegalAccessException {
        Object nodeHelper = FieldUtils.readField(this, "nodeHelper", true);
        FieldUtils.writeField(nodeHelper, "imageViewAccessor", null, true);
        ImageViewHelper.setImageViewAccessor(new ImageViewHelper.ImageViewAccessor() {
            @Override
            public NGNode doCreatePeer(Node node) {
                return new NGImageView() {
                    private Image image;

                    @Override
                    public void setImage(Object img) {
                        super.setImage(img);
                        image = (Image) img;
                    }

                    @Override
                    protected void renderContent(Graphics g) {
                        BaseResourceFactory factory = (BaseResourceFactory) g.getResourceFactory();
                        Texture tex = factory.getCachedTexture(image, Texture.WrapMode.CLAMP_TO_EDGE);
                        tex.setLinearFiltering(false);
                        tex.unlock();
                        super.renderContent(g);
                    }
                };
            }

            @Override
            public void doUpdatePeer(Node node) {
                try {
                    MethodUtils.invokeMethod(node, "doUpdatePeer");
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public BaseBounds doComputeGeomBounds(Node node, BaseBounds bounds, BaseTransform tx) {
                try {
                    return (BaseBounds) MethodUtils.invokeMethod(node, "doComputeGeomBounds", bounds, tx);
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                    return null;
                }
            }

            @Override
            public boolean doComputeContains(Node node, double localX, double localY) {
                try {
                    return (boolean) MethodUtils.invokeMethod(node, "doComputeContains", localX, localY);
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                    return false;
                }
            }
        });
    }

}

答案 4 :(得分:0)

ImageView 没有修复,但它对我帮助很大。 在找了很久之后,我偶然发现了这篇文章:How can I disable antialiasing on a JavaFX Canvas?

为了在画布上绘制图像,从 JavaFX 12 开始可以禁用平滑

canvas.getGraphicsContext2D().setImageSmoothing(false);