Как вы можете получить четкие результаты рисования при вращении BufferedImage? - PullRequest
25 голосов
/ 17 июля 2011

Одной из попыток было использование TexturePaint и g.fillRect() для рисования изображения. Это, однако, требует от вас создания нового объекта TexturePaint и Rectangle2D каждый раз, когда вы рисуете изображение, что не является идеальным - и в любом случае не помогает.

Когда я использую g.drawImage(BufferedImage,...), повернутые изображения выглядят размытыми / мягкими.

Я знаком с RenderingHints и двойной буферизацией (это то, что я делаю, я думаю), мне просто трудно поверить, что вы не можете легко и эффективно вращать изображение в Java, которая дает резкие результаты.

Код для использования TexturePaint выглядит примерно так:

Grahics2D g2d = (Graphics2D)g; 
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);

Я использую AffineTransform, чтобы повернуть карточную руку в веер. Как лучше всего быстро нарисовать красивые картинки?

Вот скриншот:
Example of blurred rotations
9 четкое, но остальные карты явно не такие острые.

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

// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);

    Graphics2D g = img.createGraphics();
    setRenderingHints(g);
    g.drawImage(shadow, 0, 0, 86, 126, null);
    g.drawImage(white, 3, 3, 80, 120, null);
    g.drawImage(suit, 3, 3, 80, 120, null);
    g.drawImage(value, 3, 3, 80, 120, null);
    g.dispose();

    cardImages[i] = img;
}

private void setRenderingHints(Graphics2D g){
    g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
}

Как мне подходить к этому по-другому? Спасибо.

Edit:
Example of hand without RenderingHints
Без RenderingHints

Установка подсказок АА без разницы. Кроме того, настройка RenderingHints при создании изображений также не имеет значения. Только когда они поворачиваются с AffineTransform и окрашиваются с помощью g.drawImage(...), они кажутся размытыми.
Изображение выше показывает разницу между значением по умолчанию (ближайший сосед) и билинейной интерполяцией.

Вот как я сейчас их рисую (намного быстрее, чем TexturePaint):

// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y){
    g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
  }

// ImageLoader.java
public BufferedImage getCard(int code){
    return cardImages[code];
  }

Все мои карты имеют размер 80x120, а shadow .png - 86x126, чтобы вокруг карты оставалась полупрозрачная тень размером 3px. Я знаю, что это не реалистичная тень, но выглядит хорошо.

И поэтому возникает вопрос ... Как получить резкие результаты рисования при повороте BufferedImage?

Ссылка на предыдущий вопрос, также касающийся раздачи с раздутой картой:
Как обнаружить событие щелчка мыши на объекте Image в Java?

Баунти-Edit: Итак, после долгих обсуждений я сделал несколько тестовых карт .svg, чтобы посмотреть, как SVG Salamander будет делать их рендеринг. К сожалению, производительность ужасна. Моя реализация достаточно чиста, учитывая, что с BufferedImage с двойной буферизацией рисование было невероятно быстрым. Это означает, что я прошел полный круг и вернулся к своей первоначальной проблеме.

Я дам 50 наград тем, кто сможет дать мне решение для резких поворотов BufferedImage. Предлагалось сделать изображения больше, чем они должны быть, и уменьшить их до рисования, а также использовать бикубическую интерполяцию. Если это единственно возможные решения, то я действительно не знаю, куда идти дальше, и мне, возможно, придется иметь дело с размытыми поворотами - потому что оба из них налагают снижение производительности.

Я могу закончить игру, если найду способ сделать это хорошо. Спасибо всем. :)

Ответы [ 3 ]

9 голосов
/ 28 июля 2011

Когда вы поворачиваете растровое изображение (например, BufferedImage), вы теряете данные.Лучшее решение - сохранять изображения больше, чем они вам нужны, и уменьшать масштаб на лету, когда вы их рисуете.Я обнаружил, что 1,5-кратный размер, который вам нужен, является хорошей отправной точкой.

Затем, когда вы рисуете изображение, изменяйте размер на лету:

g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);

Вращения с использованием рекомендуется билинейная интерполяция .

Кредит за предложение переходит к гидо.

5 голосов
/ 17 июля 2011

Этот совет, вероятно, немного запоздал в вашем дизайне, но, возможно, стоит упомянуть.

Растеризованные изображения, вероятно, не та технология, которую следует использовать, если большое количество вращений и анимаций является частью вашего интерфейса;особенно со сложными изображениями с большим количеством кривых.Просто подождите, пока не попробуете масштабировать свой холст.Я мог бы предложить взглянуть на векторную графическую библиотеку.Они будут воспроизводить эффекты, которые вы хотите, с меньшим потенциалом для артефактов.

http://xmlgraphics.apache.org/batik/using/swing.htm

4 голосов
/ 17 июля 2011

Установка типа интерполяции, а также значения сглаживания в AffineTransformOp может предложить некоторое улучшение. Тип TYPE_BICUBIC медленнее, как правило, наилучшего качества; пример обрисован в общих чертах здесь . Обратите внимание, что вы можете указать несколько RenderingHints. Еще одна ловушка возникает из-за того, что не применяются подсказки каждый раз, когда изображение отображается. Вам также может понадобиться настроить прозрачность фона, как предложено здесь . Наконец, рассмотрите возможность создания sscce , который включает одно из ваших реальных изображений.

...