Проблемы с градиентом в Java

В моей программе я хотел иметь полупрозрачный градиент от белого до прозрачного на моем JFrame для наложения желтого фона. Это работает нормально, и оно должно быть от белого до прозрачного из-за того, как мои настройки программы работают для пользователя. Однако, когда я беру программу в колледж (от JRE7 до JRE6), градиент становится от белого к черноватому, а затем к прозрачному... Это не так уж плохо, пока вы не начнете увеличивать непрозрачность белого цвета... в любом случае, я можно это исправить?

вот соответствующий код из верхней части моего кода JFrame.

public class DictionaryGUI extends JFrame
{   
    protected JPanel pGradientPane;

    //Interface gradient specification
    private Color pInterfaceColour = new Color(255, 245, 62);
    protected int iDegreeWhite = 180
    protected int iDegreeBlack = 0

    DictionaryGUI(int iWidth, int iHeight)
    {
        /*General definitions*/
        super(String.format("French Verb Conjugator - Version %s", MainLauncher.version));
        setSize(iWidth, iHeight);
        new Menu(this);

        this.iWidth = iWidth;    
        this.iHeight = iHeight;

        getContentPane().setBackground(pInterfaceColour);
        pGradientPane = new JPanel(new GridBagLayout())
        {
            private static final long serialVersionUID = 1L;

            protected void paintComponent(Graphics pGraphics) 
            {
                Graphics2D pGraphicsGradientRender = (Graphics2D) pGraphics;
                pGraphicsGradientRender.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                GradientPaint pGradient = new GradientPaint(0, 0, new Color(255, 255, 255, iDegreeWhite), 0, getHeight(), new Color(0, 0, 0, iDegreeBlack));
                pGraphicsGradientRender.setPaint(pGradient);
                pGraphicsGradientRender.fillRect(0, 0, getWidth(), getHeight());
                super.paintComponent(pGraphics);
            }
        };
        pGradientPane.setOpaque(false);
        pGradientPane.setPreferredSize(new Dimension(iWidth - 16, iHeight - 62));
        /*components added to pGradientPane here!*/
        add(pGradientPane);
    }

И основной класс также:

public class MainLauncher
{
    static int iHeight = 400;
    static int iWidth = 730;
    static String version = "0A3B6";

    public static void main(String[] args)
    {
    try 
    {
        for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
        {
            if ("Nimbus".equals(info.getName()))
            {
                UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (Exception e) {e.printStackTrace();}
    DictionaryGUI window = new DictionaryGUI(iWidth, iHeight);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setLocationByPlatform(true);
    window.setVisible(true);
}

Это просто какая-то разница между JRE6 и JRE7? Должен ли я сделать нижний цвет белым? (было черным на случай, если люди захотят затемнить цвет внизу.)

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

Как должен выглядеть градиент

Как на самом деле выглядит градиент для некоторых

Спасибо, Джейми.

EDIT: я изменил второй (прозрачный) цвет градиента на белый, и это решило проблему. Однако меня все еще беспокоит, почему прозрачный черный цвет просвечивает посередине? это должно быть как-то связано с JRE7, потому что именно там это происходит ... может быть, они что-то изменили в том, как работает прозрачность в градиентах. Кто-нибудь знает, как устранить эту проблему, сохранив черный цвет?


person J_mie6    schedule 06.12.2012    source источник
comment
Хммм... Мой отступ в коде не отображается в поле кода? Прости! Вы знаете, как это исправить?   -  person J_mie6    schedule 06.12.2012
comment
Кроме того, рассмотрите возможность публикации минимального примера кода, демонстрирующего вашу проблему, — SSCCE. Это позволит нам запустить ваш код, изменить его и, возможно, даже исправить. Прежде чем отвечать, прочтите ссылку, так как в ней содержится много важной информации о требованиях SSCCE.   -  person Hovercraft Full Of Eels    schedule 06.12.2012
comment
Насколько я знаю, это минимальный код... для других компонентов дополнительного кода нет. К сожалению, отсутствует метод Main и анализатор настроек, но я могу просто заменить анализатор значениями по умолчанию. Я на самом деле не вижу отступ в коде. Почему не следует использовать вкладки?   -  person J_mie6    schedule 06.12.2012
comment
Готово :D Хорошо, я включил MainLauncher, так как не знал, как их интегрировать, но должно ли это быть проблемой?   -  person J_mie6    schedule 06.12.2012
comment
Я внес некоторые изменения и заставил ваш код работать, но это не демонстрирует проблему, на которую вы жалуетесь.   -  person Hovercraft Full Of Eels    schedule 06.12.2012
comment
хм... ну, ты используешь JRE6, как я? Я думаю, что машины с JRE7 вызывают проблему. Я опубликую скриншот того, как это должно выглядеть по цвету   -  person J_mie6    schedule 06.12.2012
comment
@ J_mie6: Я назначил награду в 500 повторений за этот вопрос, чтобы узнать больше об этой проблеме и посмотреть, существуют ли лучшие решения.   -  person Hovercraft Full Of Eels    schedule 09.12.2012
comment
j_mie6: Мне нужна твоя помощь - кто должен получить бонус. Я склоняюсь к Нику Риппе   -  person Hovercraft Full Of Eels    schedule 15.12.2012
comment
Я согласен с вами, извините, что так долго не отвечал, были проблемы с моей учетной записью :D   -  person J_mie6    schedule 17.12.2012


Ответы (5)


Проблема с кодом в этой строке:

GradientPaint pGradient = new GradientPaint(0, 0, new Color(255, 255, 255, iDegreeWhite), 0, getHeight(), new Color(0, 0, 0, iDegreeBlack));

должно быть так:

GradientPaint pGradient = new GradientPaint(0, 0, new Color(255, 255, 255, iDegreeWhite), 0, getHeight(), new Color(255, 245, 62, iDegreeWhite));

Оглядываясь назад на ваш вопрос, я вижу, что вы в основном нашли решение, но оно немного отличается. Вот почему:

При смешивании цветов в градиенте вы смешиваете все аспекты цвета: RBGA.

Видите ли, пока вы не достигнете полного второго цвета, вы смешиваете черный цвет с цветовым градиентом, и эта смесь не будет полной прозрачности. Таким образом, 20 % пути вниз по странице будут иметь этот цвет: 204 204 204 144 (это 80 % белого, 20 % черного и 56 % непрозрачного).

Самое простое решение — полностью избежать полупрозрачности, если вы ее не используете — просто растушуйте от светло-желтого вверху до темно-желтого внизу. Это также требует меньше ресурсов.

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

Если вы смешиваете от белого к белому (прозрачному), у вас будет та же проблема, что и раньше, только с белым (который будет менее заметен, поскольку это один из используемых вами цветов): градиент будет иметь белую «полосу» пока второй цвет не достигнет полной прозрачности.

Что касается того, почему он действует по-разному на разных JVM, я предполагаю, что Oracle, возможно, изменил способ смешивания альфа-каналов. Кажется, над улучшением альфа-поддержки они работали какое-то время, и это логичный шаг в этом направлении. Однако у меня нет никаких доказательств этого утверждения — оно просто основано на других изменениях, которые я видел в альфа-версии (например, прозрачные окна).

EDIT Этот SSCCE демонстрирует как проблему, так и решение:

import java.awt.*;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;


public class TransparencyDemo extends Box{

    protected JPanel pGradientPane;

    //Interface gradient specification
    private Color pInterfaceColour = new Color(255, 245, 62);
    protected int iDegreeWhite = 180;
    protected int iDegreeBlack = 0;

    public TransparencyDemo() {
        super(BoxLayout.X_AXIS);
        setOpaque(true);

        //Incorrect Solution
        pGradientPane = new JPanel(new GridBagLayout())
        {
            private static final long serialVersionUID = 1L;

            protected void paintComponent(Graphics pGraphics) 
            {
                Graphics2D pGraphicsGradientRender = (Graphics2D) pGraphics;
                pGraphicsGradientRender.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                GradientPaint pGradient = new GradientPaint(0, 0, new Color(255, 255, 255, iDegreeWhite), 0, getHeight(), new Color(0, 0, 0, iDegreeBlack));
                pGraphicsGradientRender.setPaint(pGradient);
                pGraphicsGradientRender.fillRect(0, 0, getWidth(), getHeight());
                super.paintComponent(pGraphics);
            }
        };
        pGradientPane.setOpaque(false);
        add(pGradientPane);

        //Correct Solution
        JPanel pGradientPane2 = new JPanel(new GridBagLayout())
        {
            private static final long serialVersionUID = 1L;

            protected void paintComponent(Graphics pGraphics) 
            {
                Graphics2D pGraphicsGradientRender = (Graphics2D) pGraphics;
                pGraphicsGradientRender.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                GradientPaint pGradient = new GradientPaint(0, 0, new Color(255, 255, 255, iDegreeWhite), 0, getHeight(),  new Color(255, 245, 62, iDegreeWhite));
                pGraphicsGradientRender.setPaint(pGradient);
                pGraphicsGradientRender.fillRect(0, 0, getWidth(), getHeight());
                super.paintComponent(pGraphics);
            }
        };
        pGradientPane2.setOpaque(false);
        add(pGradientPane2);


        setBackground(pInterfaceColour);

    }

    public static void main(String[] args){
        try {
             for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                   UIManager.setLookAndFeel(info.getClassName());
                   break;
                }
             }
          } catch (Exception e) {
             e.printStackTrace();
          }

        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new TransparencyDemo());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
person Nick Rippe    schedule 10.12.2012
comment
У Nimbus есть собственный Painter, чтобы проверить мою ссылку в ответе (@Hovercraft Full Of Eels), у вас есть какие-то проблемы с этим .... - person mKorbel; 10.12.2012
comment
@mKorbel Кажется, он избегает проблемы с Painter, напрямую рисуя градиент. Другим решением было бы иметь дело с Nimbus Painters... - person Nick Rippe; 10.12.2012
comment
@Tom Я добавил код SSCCE, который показывает обе версии рядом (с черной линией и без нее). - person Nick Rippe; 11.12.2012
comment
Я заметил некоторые странные вещи с демонстрацией, которую я разместил здесь: если вы либо 1) развернете окно в полный экран, либо 2) установите другой размер окна перед начальной отрисовкой -> обе стороны будут рисовать правильно, пока вы не уменьшите окно до его размера. снова минимальный размер. Возможно, это связано с ответом Тома и конвейером Direct3D, но, похоже, он ведет себя не так, как указано в статье. - person Nick Rippe; 11.12.2012
comment
Спасибо за хороший ответ! Я отказался от использования двух оттенков желтого для формирования градиента, потому что пользователи в программе могут изменить цвет. По-моему, они должны выбрать 1 цвет и использовать ползунок для изменения белизны вверху (отсюда и iDegreeWhite). Все равно спасибо! - person J_mie6; 17.12.2012

Вот моя версия вашего кода как sscce:

import java.awt.*;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;

public class MainLauncher {
   static int iHeight = 400;
   static int iWidth = 730;
   static String version = "0A3B6";

   public static void main(String[] args) {
      try {
         for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
               UIManager.setLookAndFeel(info.getClassName());
               break;
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
      DictionaryGUI window = new DictionaryGUI(iWidth, iHeight);
      window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      window.setLocationByPlatform(true);
      window.setVisible(true);
   }

}

class DictionaryGUI extends JFrame {
   protected JPanel pGradientPane;

   // Interface gradient specification
   private Color pInterfaceColour = new Color(255, 245, 62);
   protected int iDegreeWhite = 180;
   protected int iDegreeBlack = 0;

   DictionaryGUI(int iWidth, int iHeight) {
      /* General definitions */
      super(String.format("French Verb Conjugator - Version %s",
            MainLauncher.version));
      setSize(iWidth, iHeight);

      getContentPane().setBackground(pInterfaceColour);
      pGradientPane = new JPanel() {
         private static final long serialVersionUID = 1L;

         protected void paintComponent(Graphics pGraphics) {
            Graphics2D pGraphicsGradientRender = (Graphics2D) pGraphics;
            pGraphicsGradientRender.setRenderingHint(
                  RenderingHints.KEY_ANTIALIASING,
                  RenderingHints.VALUE_ANTIALIAS_ON);
            GradientPaint pGradient = new GradientPaint(0, 0, new Color(255,
                  255, 255, iDegreeWhite), 0, getHeight(), new Color(0, 0, 0,
                  iDegreeBlack));
            pGraphicsGradientRender.setPaint(pGradient);
            pGraphicsGradientRender.fillRect(0, 0, getWidth(), getHeight());
            super.paintComponent(pGraphics);
         }
      };
      pGradientPane.setOpaque(false);
      pGradientPane.setPreferredSize(new Dimension(iWidth - 16, iHeight - 62));
      /* components added to pGradientPane here! */
      add(pGradientPane);
   }
}

Но опять же, это не демонстрирует вашу проблему. Я предполагаю, однако, что ваша проблема заключается в использовании прозрачного фона с графическим интерфейсом Swing, где артефакты рисования не исправляются полностью. Если да, прочитайте, что Роб Камик говорит об этом в своем блоге: Фоны с прозрачностью< /а>

person Hovercraft Full Of Eels    schedule 06.12.2012
comment
Мне действительно трудно объяснить проблему. Для моего на моем компьютере это идеально. но когда я использую его на своем ноутбуке или на компьютерах в колледже (оба с JRE7), он выглядит по-другому. И поэтому я не могу показать проблему, потому что она не возникает у некоторых людей, но когда она возникает, она всегда присутствует... - person J_mie6; 06.12.2012
comment
@ J_mie6: Но произойдет ли это с этим минимальным кодом выше? или у вас должны быть изменяющиеся компоненты, сидящие поверх этого фона. Другими словами, показали ли вы, что приведенный выше SSCCE воспроизводит проблему? Или нужно больше? - person Hovercraft Full Of Eels; 06.12.2012
comment
Я пойду и проверю это на машине, у которой есть проблема. - person J_mie6; 06.12.2012
comment
да, этот код вызывает проблему... см. мой снимок экрана выше. - person J_mie6; 06.12.2012
comment
Я взглянул, но разве это не говорило о методе setOpaque()? и вроде бы я его выполнил. Я посмотрю еще раз... Я просто не понимаю, почему JRE7 создают плохой градиент. Как вы думаете, сработает ли это, если я сделаю так, чтобы проект по умолчанию использовал сборку JRE6 на этих компьютерах? - person J_mie6; 06.12.2012
comment
Итак, я изменил второй (прозрачный) цвет в градиенте на белый, и это решило проблему. Однако меня все еще беспокоит, почему прозрачный черный цвет просвечивает посередине? это должно быть как-то связано с JRE7, потому что именно там это происходит ... может быть, они что-то изменили в том, как работает прозрачность в градиентах. Спасибо за вашу помощь! - person J_mie6; 06.12.2012
comment
@ J_mie6: Хотел бы я знать ответ, но для ответа на него потребуется более знающий человек, чем я. Спасибо, что приняли мой ответ, но на самом деле я не отвечал на него, поэтому вы можете отказаться от принятия моего ответа и посмотреть, может ли кто-нибудь еще предложить лучшее решение. Я рассматриваю возможность предложить награду, если потребуется, через день или два, если не будет хорошего ответа. Господь свидетель, у меня есть лишняя репутация, и я мог бы ее потратить. - person Hovercraft Full Of Eels; 06.12.2012
comment
@j_mie6: Не за что. Меня тоже очень интересует решение! - person Hovercraft Full Of Eels; 06.12.2012
comment
@Hovercraft Full Of Eels, Пит, пожалуйста, посмотрите мой пост здесь о RepaintManager (будет удален), я уверен, что у Painter есть (для Nimbus реализован низкоуровневый графический процессор, есть диаметральная разница в качестве между Metal и Nimbus) - person mKorbel; 11.12.2012
comment
Спасибо, mKorbel, но не удаляйте его. Мне придется изучить всю информацию через несколько дней. - person Hovercraft Full Of Eels; 11.12.2012


У меня есть фатаморгана, я уверен, что GradientPaint темнее, темнее и темнее, фааа сумасшедшая иллюзия глаз, брррр

//http://stackoverflow.com/questions/13748810/gradient-problems-in-java/13806210#comment18995490_13806210

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;

public class MainLauncher {

    private JFrame window = new JFrame();

    public MainLauncher() {
        GradientPane pane = new GradientPane();
        pane.setLayout(new GridLayout(6, 4, 15, 15));
        for (int i = 1; i <= 24; i++) {
            pane.add(createButton(i));
        }
        pane.setOpaque(false);
        window.add(pane);
        RepaintManager.setCurrentManager(new RepaintManager() {

            @Override
            public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
                Container con = c.getParent();
                while (con instanceof JComponent) {
                    if (!con.isVisible()) {
                        return;
                    }
                    if (con instanceof GradientPane) {
                        c = (JComponent) con;
                        x = 0;
                        y = 0;
                        w = con.getWidth();
                        h = con.getHeight();
                    }
                    con = con.getParent();
                }
                super.addDirtyRegion(c, x, y, w, h);
            }
        });
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setLocationByPlatform(true);
        window.setSize(400, 300);
        //window.pack();
        window.setVisible(true);
    }

    private JButton createButton(final int text) {
        JButton button = new JButton(Integer.toString(text));
        return button;
    }

    class GradientPane extends JPanel {

        private static final long serialVersionUID = 1L;
        private final int h = 150;
        private BufferedImage img = null;
        private BufferedImage shadow = new BufferedImage(1, h, BufferedImage.TYPE_INT_ARGB);

        public GradientPane() {
            paintBackGround(new Color(150, 250, 150));
        }

        public void paintBackGround(Color g) {
            Graphics2D g2 = shadow.createGraphics();
            g2.setPaint(g);
            g2.fillRect(0, 0, 1, h);
            g2.setComposite(AlphaComposite.DstIn);
            g2.setPaint(new GradientPaint(0, 0, new Color(0, 0, 0, 0f), 0, h, new Color(0.1f, 0.8f, 0.8f, 0.5f)));
            g2.fillRect(0, 0, 1, h);
            g2.dispose();
        }

        @Override
        public void paintComponent(Graphics g) {
            if (img == null || img.getWidth() != getWidth() || img.getHeight() != getHeight()) {
                img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
            }
            Graphics2D g2 = img.createGraphics();
            super.paintComponent(g2);
            Rectangle bounds = this.getVisibleRect();
            g2.scale(bounds.getWidth(), -1);
            g2.drawImage(shadow, bounds.x, -bounds.y - h, null);
            g2.scale(1, -1);
            g2.drawImage(shadow, bounds.x, bounds.y + bounds.height - h, null);
            g2.dispose();
            g.drawImage(img, 0, 0, null);
        }
    }

    public static void main(String[] args) {
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                MainLauncher ml = new MainLauncher();
            }
        });
    }
}
person mKorbel    schedule 10.12.2012

Как указал Ник, проблема в том, что вы используете прозрачный черный, а не прозрачный белый. Таким образом, полупрозрачные цвета представляют собой оттенок между белым и черным.

Попробуйте заменить эту строку в своем коде:

GradientPaint pGradient = new GradientPaint(0, 0, new Color(255, 255, 255, iDegreeWhite), 0, getHeight(), new Color(255, 255, 255, iDegreeBlack));
person Toby Sharp    schedule 11.12.2012
comment
Это на самом деле очень интересно - единственный раз, когда вы не видите полосу, это когда совпадают первый и второй цвета. Таким образом, этот ответ работает правильно, но если бы первый цвет был черным, вы бы получили белую полосу. Можем ли мы получить некоторую последовательность здесь??? (Да, я смотрю на тебя, Оракул) - person Nick Rippe; 11.12.2012