Пользовательский CompositeContext и сглаживание Java AWT: RasterFormatException при рисовании за пределами клиентской области

Я пытаюсь внедрить SWT GC-подобный рисунок xor-режима для AWT Графика2D. Использование встроенного XORComposite не вариант, так как он не реализует отрисовку в режиме xor, как в SWT.

Рисунок SWT в режиме xor объединяет исходный и конечный цвета с помощью двоичного исключающего ИЛИ. AWT XORComposite (можно использовать через g2d.setXORMode(Color)) использует постоянный xor-color, который комбинируется с исходными цветами через бинарное исключающее ИЛИ, т.е. конечные цвета не влияют на результирующие цвета.

Так что единственный вариант, который я имею в виду, это написать свой собственный Composite. и CompositeContext, которые соответствующим образом сочетают источник и место назначения.

После некоторого чтения я придумал эту простую реализацию: (Да, я знаю о накладных расходах getPixel(...), setPixel(...). Я хочу, чтобы он работал правильно перед оптимизацией.)

private static class XorComposite implements Composite {

    public static XorComposite INSTANCE = new XorComposite();

    private XorContext context = new XorContext();

    @Override
    public CompositeContext createContext(ColorModel srcColorModel,
            ColorModel dstColorModel, RenderingHints hints) {
        return context;
    }

}

private static class XorContext implements CompositeContext {

    public XorContext() {
    }

    @Override
    public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
        int w = Math.min(src.getWidth(), dstIn.getWidth());
        int h = Math.min(src.getHeight(), dstIn.getHeight());

        int[] srcRgba = new int[4];
        int[] dstRgba = new int[4];

        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                src.getPixel(x, y, srcRgba);
                dstIn.getPixel(x, y, dstRgba);
                for (int i = 0; i < 3; i++) {
                    dstRgba[i] ^= srcRgba[i];
                }
                dstOut.setPixel(x, y, dstRgba);
            }
        }
    }

    @Override
    public void dispose() {
    }

}

Эта реализация отлично работает, когда сглаживание отключено. Если сглаживание включено, оно работает до тех пор, пока мои рисунки полностью видны, то есть внутри моей панели JPanel, на которой я рисую. Но если рисунок выходит за границы JPanel, возникает исключение RasterFormatException:

Exception in thread "AWT-EventQueue-0" java.awt.image.RasterFormatException: (y + height) is outside raster
    at sun.awt.image.IntegerInterleavedRaster.createWritableChild(IntegerInterleavedRaster.java:470)
    at sun.awt.image.IntegerInterleavedRaster.createChild(IntegerInterleavedRaster.java:514)
    at sun.java2d.pipe.GeneralCompositePipe.renderPathTile(GeneralCompositePipe.java:106)
    at sun.java2d.pipe.AAShapePipe.renderTiles(AAShapePipe.java:201)
    at sun.java2d.pipe.AAShapePipe.fillParallelogram(AAShapePipe.java:102)
    at sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(PixelToParallelogramConverter.java:322)
    at sun.java2d.pipe.PixelToParallelogramConverter.fill(PixelToParallelogramConverter.java:159)
    at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:160)
    at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2422)
    at org.eclipse.gef4.graphics.examples.AwtXorTestPanel.paint(AwtXorTest.java:117)
    ... (irrelevant)

Примечательно, что мой Composite/CompositeContext не генерирует исключение, но внутренние компоненты AWT выдают исключение при попытке создать растровые объекты, которые он хочет передать моему CompositeContext.

К сожалению, PixelToParallelogramConverter используется только для пользовательских композитов, когда включено сглаживание. Например, встроенный XORComposite использует собственный метод для рисования. Я предполагаю ошибку AWT, но я не уверен.

Помощь будет принята с благодарностью :)

Обновление:

Как предложил Дюрандаль, я протестировал код с помощью java-6-sun и java-1.6.0-openjdk. Я узнал, что OpenJDK выдает исключение, а Sun-JDK — нет. Поэтому я сообщил об ошибке в системе отслеживания ошибок OpenJDK.

Я обновлю этот вопрос, когда проблема будет решена. Пожалуйста, посетите соответствующую ошибку OpenJDK для получения информации о текущем прогрессе.

С уважением, Матиас

Вот пример программы, так что вы можете протестировать ее локально:

/*******************************************************************************
 * Copyright (c) 2013 itemis AG and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Matthias Wienand (itemis AG) - initial API and implementation
 * 
 *******************************************************************************/
package org.eclipse.gef4.graphics.examples;

import java.awt.Color;

public class AwtXorTest extends JApplet {

    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setTitle("AWT XorMode Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JApplet applet = new AwtXorTest();
        applet.init();
        frame.getContentPane().add(applet);
        frame.pack();
        frame.setVisible(true);
    }

    @Override
    public void init() {
        JPanel panel = new AwtXorTestPanel();
        getContentPane().add(panel);
    }

}

class AwtXorTestPanel extends JPanel {

    private static class XorComposite implements Composite {

        public static XorComposite INSTANCE = new XorComposite();

        private XorContext context = new XorContext();

        @Override
        public CompositeContext createContext(ColorModel srcColorModel,
                ColorModel dstColorModel, RenderingHints hints) {
            return context;
        }

    }

    private static class XorContext implements CompositeContext {

        public XorContext() {
        }

        @Override
        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
            int w = Math.min(src.getWidth(), dstIn.getWidth());
            int h = Math.min(src.getHeight(), dstIn.getHeight());

            int[] srcRgba = new int[4];
            int[] dstRgba = new int[4];

            for (int x = 0; x < w; x++) {
                for (int y = 0; y < h; y++) {
                    src.getPixel(x, y, srcRgba);
                    dstIn.getPixel(x, y, dstRgba);
                    for (int i = 0; i < 3; i++) {
                        dstRgba[i] ^= srcRgba[i];
                    }
                    dstOut.setPixel(x, y, dstRgba);
                }
            }
        }

        @Override
        public void dispose() {
        }

    }

    private static final long serialVersionUID = 1L;

    public AwtXorTestPanel() {
        setPreferredSize(new Dimension(640, 480));
    }

    @Override
    public void paint(Graphics graphics) {
        Graphics2D g2d = (Graphics2D) graphics;

        // comment out to see it working:
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setComposite(XorComposite.INSTANCE);
        g2d.setColor(new Color(0, 255, 255)); // resulting color should be red
        g2d.fill(new Rectangle(100, 100, 500, 500));
    }

}

person Matthias    schedule 31.01.2013    source источник
comment
У меня нет исключений из вашего примера (WinXP, Oracle JRE 1.6.0_41/1.7.0_15). Возможно, это ваша java ИЛИ, возможно, проблема с драйвером (и да, у меня включено сглаживание).   -  person Durandal    schedule 22.02.2013
comment
Ваш пример работает для меня, и я согласен, я действительно думаю, что это ошибка AWT. Без AA конвейер рендеринга обрезает прямоугольник назначения, чтобы убедиться, что он находится в пределах доступного пространства. Вы проверили AlphaComposite.Xor ?   -  person naugler    schedule 23.02.2013
comment
@Durandal, я работаю над системой Ubuntu, используя java 1.6. Я не думаю, что это проблема драйвера, потому что код, который дает сбой, — это код, генерирующий растровые объекты для пользовательского композита.   -  person Matthias    schedule 01.03.2013
comment
@naugler, я знаю об AlphaComposite.Xor, но это не то, что мне нужно. Я хочу объединить цвета источника и назначения с двоичным эксклюзивным или. Вот пример функциональности, которую я хочу реализовать: eclipse.org/ статьи/Article-SWT-graphics/SWT_graphics.html#XOR   -  person Matthias    schedule 01.03.2013


Ответы (2)


Предостережение: я давно не прикасался к растрам.

Похоже, вы, вероятно, получаете доступ к пикселям за пределами растра.

Растры имеют minX, minY, поэтому ваши циклы должны быть примерно такими:

int srcMinX = src.getMinX();
int srcMinY = src.getMinY();
int dstInMinX = dstIn.getMinX();
int dstInMinY = dstIn.getMinY();
int dstOutMinX = dstOut.getMinX();
int dstOutMinY = dstOut.getMinY();

for (int x = 0; x < w; x++) {
    for (int y = 0; y < h; y++) {
        src.getPixel(x+srcMinX, y+srcMinY, srcRgba);
        dstIn.getPixel(x+dstInMinX, y+dstInMinY, dstRgba);
        for (int i = 0; i < 3; i++) {
            dstRgba[i] ^= srcRgba[i];
        }
        dstOut.setPixel(x+dstOutMinX, y+dstOutMinY, dstRgba);
    }
}
person Devon_C_Miller    schedule 26.07.2013
comment
В этом случае эти значения minX и minY всегда равны нулю. Поверьте мне, я был точен, когда искал жука. Позор мне, если я должен был пропустить что-то настолько очевидное, как это. Во всяком случае, я только что проверил ваш код, и он все еще не работает с тем же сообщением об ошибке. - person Matthias; 29.07.2013

Область клипа g2d имеет более одного блока и вызовет исключение RasterFormatException, поскольку его полноразмерная растровая ссылка заменяется растром сглаженного блока.

person WonderWorker    schedule 18.04.2013
comment
не могли бы вы уточнить это, пожалуйста? Откуда вы взяли информацию? ИМХО, область клипа не управляет никакими растровыми объектами, они получены из объекта, возвращаемого SurfaceData.getRaster(). - person Matthias; 18.04.2013