Java,paintComponent 中的 setForeground 融化 CPU

Java, setForeground in paintComponent melts CPU

我在查找/创建支持所有旧 JLabel 功能(Html 标记)的带有阴影的 JLabel 时遇到了问题。所以我尝试了这个,它看起来不错,而且 JLabel 像以前一样工作。

public class SLabel extends JLabel {
    public SLabel() {
        super();
    }

    @Override
    public void paintComponent(Graphics g) {
        // Use Foregroundcolor for the text
        // Use Backgroundcolor for the shadow
        Color c = getForeground();
        setForeground(getBackground());
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g.create(-2, -2, getWidth() - 1, getHeight() - 1);
        setForeground(c); //<-Remove this line and CPU acts normal
        super.paintComponent(g2.create());
        g2.dispose();
    }
}

但我最近买了一台 windows 10 的新电脑,我在显示此 JLabel 时观察到 cpu 使用率非常高(随后是很大的风扇噪音)。

似乎 setForeground(c) 是增加 CPU 使用率的代码行。

知道为什么吗,带有 HTML 标记的阴影 JLabel 的任何替代代码示例或我的代码的改进?

是的,这就是它的作用。绘画应该只是简单地绘制当前状态,它永远不应该尝试改变组件的状态,因为这会产生新的绘画请求

你真正需要做的是首先生成一个 "snapshot" 标签,它成为你想要生成的 "effect" 的一部分,然后将其绘制到提供的 Graphics 调用 paintComponent 时的上下文,例如...

DropShadowLabel

import java.awt.*;
import java.awt.image.BufferedImage;

public class DropShadowLabel extends AbstractEffectLabel {

    private int shadowSize;
    private Color shadowColor;
    private float shadowAlpha;

    public DropShadowLabel() {

        setShadowSize(2);
        setShadowColor(Color.BLACK);
        setShadowAlpha(0.5f);

    }

    @Override
    protected BufferedImage applyEffectTo(BufferedImage img) {
        return ImageEffectUtilities.applyDropShadow(img, getShadowSize(), getShadowColor(), getShadowAlpha());
    }

    @Override
    public Insets getEffectInsets() {

        return new Insets(0, 0, (getShadowSize() * 2), (getShadowSize() * 2));

    }

    public void setShadowAlpha(float value) {

        if (shadowAlpha != value) {

            float old = shadowAlpha;
            shadowAlpha = value;

            firePropertyChange("shadowAlpha", old, value);
            invalidate();
            repaint();

        }

    }

    public void setShadowColor(Color value) {

        if (shadowColor != value) {

            Color old = shadowColor;
            shadowColor = value;

            firePropertyChange("shadowColor", old, value);
            invalidate();
            repaint();

        }

    }

    public void setShadowSize(int value) {

        if (shadowSize != value) {

            int old = shadowSize;
            shadowSize = value;

            updateRendererSize();

            firePropertyChange("shadowSize", old, value);
            invalidate();
            repaint();

        }

    }

    public float getShadowAlpha() {
        return shadowAlpha;
    }

    public Color getShadowColor() {
        return shadowColor;
    }

    public int getShadowSize() {
        return shadowSize;
    }

}

抽象效果标签

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.JLabel;

public abstract class AbstractEffectLabel extends JComponent {

    private JLabel renderer;

    private BufferedImage imgCache;

    public AbstractEffectLabel() {
    }

    protected JLabel getRenderer() {

        if (renderer == null) {

            renderer = new JLabel();
            renderer.setHorizontalTextPosition(JLabel.LEFT);
            renderer.setVerticalTextPosition(JLabel.TOP);

        }

        return renderer;

    }

    @Override
    public void setForeground(Color fg) {
        super.setForeground(fg);
        getRenderer().setForeground(fg);
    }

    @Override
    public void setBackground(Color bg) {
        super.setBackground(bg);
        getRenderer().setBackground(bg);
    }

    @Override
    public Font getFont() {
        return getRenderer().getFont();
    }

    public void setHorizontalAlignment(int alignment) {

        getRenderer().setHorizontalAlignment(alignment);

    }

    public void setVerticalAlignment(int alignment) {

        getRenderer().setVerticalAlignment(alignment);

    }

    public void setHorizontalTextPosition(int alignment) {

        getRenderer().setHorizontalTextPosition(alignment);

    }

    public void setVerticalTextPosition(int alignment) {

        getRenderer().setVerticalTextPosition(alignment);

    }

    public int getHorizontalAlignment() {

        return getRenderer().getHorizontalAlignment();

    }

    public int getVerticalAlignment() {

        return getRenderer().getVerticalAlignment();

    }

    public int getHorizontalTextPosition() {

        return getRenderer().getHorizontalTextPosition();

    }

    public int getVerticalTextPosition() {

        return getRenderer().getVerticalTextPosition();

    }

    @Override
    public void setFont(Font font) {

        super.setFont(font);

        getRenderer().setFont(font);

        updateRendererSize();

    }

    @Override
    public void invalidate() {

        imgCache = null;

        super.invalidate();

    }

    public void setText(String text) {

        getRenderer().setText(text);

        updateRendererSize();

        invalidate();
        repaint();

    }

    public String getText() {

        return getRenderer().getText();

    }

    protected abstract BufferedImage applyEffectTo(BufferedImage img);

    protected BufferedImage getImageCache() {

        if (imgCache == null) {

            JLabel renderer = getRenderer();

            imgCache = ImageEffectUtilities.createCompatibleImage(Math.max(1, renderer.getWidth()), Math.max(renderer.getHeight(), 1));
            Graphics2D g2d = imgCache.createGraphics();

            renderer.setForeground(getForeground());

            renderer.paint(g2d);

            g2d.dispose();

//          imgCache = GlowEffectFactory.applyDropShadow(imgCache, getShadowSize(), getShadowColor(), getShadowAlpha());
            imgCache = applyEffectTo(imgCache);

        }

        return imgCache;

    }

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;

        Rectangle bounds = UIUtilities.getSafeBounds(this);
        g2d.drawImage(getImageCache(), bounds.x, bounds.y, this);

    }

    public abstract Insets getEffectInsets();

    protected void updateRendererSize() {

        Dimension prefSize = getRenderer().getPreferredSize();

        Insets insets = getEffectInsets();

        prefSize.width += (insets.left + insets.right);
        prefSize.height += (insets.top + insets.bottom);

        getRenderer().setSize(prefSize);

        setPreferredSize(prefSize);
        setMinimumSize(prefSize);

    }

}

ImageEffectUtilities

import com.jhlabs.image.GaussianFilter;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import javax.swing.ImageIcon;

public class ImageEffectUtilities {

    public static BufferedImage applyDropShadow(BufferedImage imgMaster, int size, Color color, float opactity) {
        return applyEffect(imgMaster, 0, 0, size, color, opactity);
    }

    protected static BufferedImage applyEffect(BufferedImage imgMaster, int xOffset, int yOffset, int size, Color color, float opactity) {

        BufferedImage imgShadow = generateShadow(imgMaster, size, color, opactity);

        BufferedImage imgCombined = createCompatibleImage(imgShadow);
        Graphics2D g2d = imgCombined.createGraphics();
        GraphicsUtilities.applyQualityRenderingHints(g2d);

        g2d.drawImage(imgShadow, -(size / 2), -(size / 2), null);
        g2d.drawImage(imgMaster, xOffset, yOffset, null);

        g2d.dispose();

        return imgCombined;

    }

    public static BufferedImage generateShadow(BufferedImage imgSource, int size, Color color, float alpha) {

        int imgWidth = imgSource.getWidth() + (size * 2);
        int imgHeight = imgSource.getHeight() + (size * 2);

        BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight);
        Graphics2D g2 = imgMask.createGraphics();

        int    x = Math.round((imgWidth - imgSource.getWidth()) / 2f);
        int    y = Math.round((imgHeight - imgSource.getHeight()) / 2f);
        g2.drawImage(imgSource, x, y, null);
        g2.dispose();

        // ---- Blur here ---

        BufferedImage imgGlow = generateBlur(imgMask, (size * 2), color, alpha);

        // ---- Blur here ----

        return imgGlow;

    }


    public static BufferedImage generateBlur(BufferedImage imgSource, int size, Color color, float alpha) {

        GaussianFilter filter = new GaussianFilter(size);

        int imgWidth = imgSource.getWidth();
        int imgHeight = imgSource.getHeight();

        BufferedImage imgBlur = createCompatibleImage(imgWidth, imgHeight);
        Graphics2D g2 = imgBlur.createGraphics();

        g2.drawImage(imgSource, 0, 0, null);
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
        g2.setColor(color);

        g2.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight());
        g2.dispose();

        imgBlur = filter.filter(imgBlur, null);

        return imgBlur;

    }


    public static BufferedImage createCompatibleImage(int width, int height) {
        return createCompatibleImage(width, height, Transparency.TRANSLUCENT);
    }

    public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
        BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
        image.coerceData(true);
        return image;
    }

    public static GraphicsConfiguration getGraphicsConfiguration() {
        return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    }

    public static Rectangle getSafeBounds(JComponent comp) {

        Insets insets = comp.getInsets();

        return getSafeBounds(insets, comp.getBounds());
    }


    public static Rectangle getSafeBounds(Insets insets, Rectangle bounds) {

        int x = insets.left;
        int y = insets.top;
        int width = bounds.width - (insets.left + insets.right);
        int height = bounds.height - (insets.top + insets.bottom);

        return new Rectangle(x, y, width, height);

    }

}

您还需要 JHLabs Filters

扩展示例

JLabel是一个复杂的组件,有图标支持,文本和图标定位。我按原样编写上面代码的主要原因是因为我真的不想重新实现很多工作,还因为标签可以呈现 html 以及标签的渲染方式呈现方式与 Graphics#drawString 不同(不知道为什么,就是这样)。

下面是一个扩展示例,其中我为 AbstractEffectLabel 添加了 icon 支持,为了比较,底部的只是一个普通的旧 JLabel

当使用不同的颜色调用 setForeground 时,会触发重绘,因此您会不断触发重绘。参见 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/javax/swing/JComponent.java#JComponent.setForeground%28java.awt.Color%29