JavaFX ImageView без сглаживания

Можно ли отображать масштабированное изображение в ImageView в JavaFX 2.2 без какого-либо сглаживания? Я представляю изображение 50x50 в ImageView с разрешением 200x200 с помощью setSmooth (false), поэтому каждый пиксель исходного изображения должен отображаться на квадрат 4x4 на экране.

Однако результирующий рендеринг все еще сглаживает исходный пиксель по всем 16 целевым пикселям. Кто-нибудь знает способ сделать это без ручного копирования каждого пикселя в новое изображение?

Ответ 1

В JavaFX 2.2 ImageView всегда будет выполняться сглаживание независимо от smooth, который вы указываете на ImageView.

(На основе тестирования с использованием Java 7u15 и Windows 7 с видеокартой ATI HD4600).

Возможно, это ошибка, что ImageView всегда будет сглаживать Image, но в документации не указано точно, что сглаживание делает или не делает, поэтому трудно сказать, каково ее реальное намерение. Вы можете отправить ссылку на этот вопрос в список рассылки openjfx-dev или зарегистрировать проблему в JavaFX вопрос трекера, чтобы получить более экспертное мнение от разработчика.


Я попробовал несколько разных способов масштабирования изображения:

Я обнаружил, что методы 1 и 4 приводили к резкому пикселированному изображению по вашему желанию, а 2 и 3 приводили к размытому псевдониму.

robot-sampling

Пример кода для создания указанного выше результата.


Обновление с идеями по внедрению собственного фильтра изображений

Эффект JavaFX не совпадает с фильтром, используемым для подпрограмм загрузки изображений, хотя может быть создан Эффект для фильтрации изображения. В JavaFX 2.2 публично документированный API для поддержки создания пользовательских эффектов, поэтому создание настраиваемого эффекта может оказаться затруднительным.

собственный код для поддержки изображений недавно был открыт в составе openjfx, чтобы вы могли посмотреть, как это происходит в настоящее время.

Вы также можете запросить запрос функции против проекта среды JavaFX, чтобы "позволить нам создавать собственные 2D-фильтры".

Ответ 2

Я знаю, что это немного старше, но мне недавно понадобился такой 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 не мог просто передать флаг "гладкий", чтобы настройка линейной фильтрации текстуры была вне меня.

Ответ 3

Когда вы добавляете следующий конструктор в ответ Martin Sojka, вы можете просто передать javafx Image в конструктор. Кроме того, несмотря на предупреждения об устаревших функциях, его ответ все еще работает нормально (на JDK 1.8_121).

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

Ответ 4

В случае, если у кого-то возникла та же проблема в Java 9 или выше, вот отредактированная версия решения Мартина Сойки, которая использует отражение для получения доступа к некоторым теперь закрытым членам 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;
                }
            }
        });
    }

}