You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Swing实现图像像素化动画时JLabel首图后显示白屏问题

问题原因
  1. Swing的UI渲染完全依赖事件派发线程(EDT),你当前所有像素计算、sleep休眠、UI更新操作都在主线程执行,直接阻塞了EDT线程,repaint请求无法被调度执行,界面就会卡死白屏。
  2. display方法里反复移除再新增JLabel的操作完全冗余,每次修改组件树都会触发布局重算,进一步加剧线程阻塞。
  3. 每修改1个像素就触发1次全量更新+休眠1秒,执行效率极低,也完全没有必要。
修复后的完整代码
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import java.awt.Toolkit;
import java.awt.Dimension;
import javax.imageio.ImageIO;
import java.util.concurrent.TimeUnit;

class ImageUtil {
    static JFrame f;
    static Dimension screenSize;
    static JLabel lbl;

    public static BufferedImage pixelate(BufferedImage imageToPixelate, int pixelSize) {
        BufferedImage pixelateImage = new BufferedImage(
                imageToPixelate.getWidth(),
                imageToPixelate.getHeight(),
                imageToPixelate.getType());

        for (int y = 0; y < imageToPixelate.getHeight(); y += pixelSize) {
            for (int x = 0; x < imageToPixelate.getWidth(); x += pixelSize) {
                BufferedImage croppedImage = getCroppedImage(imageToPixelate, x, y, pixelSize, pixelSize);
                Color dominantColor = getDominantColor(croppedImage);
                // 整块填充完再更新
                for (int yd = y; (yd < y + pixelSize) && (yd < pixelateImage.getHeight()); yd++) {
                    for (int xd = x; (xd < x + pixelSize) && (xd < pixelateImage.getWidth()); xd++) {
                        pixelateImage.setRGB(xd, yd, dominantColor.getRGB());
                    }
                }
                // 填充完一个区块后更新UI
                ImageIcon img = new ImageIcon(pixelateImage);
                display(img);
            }
        }
        return pixelateImage;
    }

    public static BufferedImage getCroppedImage(BufferedImage image, int startx, int starty, int width, int height) {
        if (startx < 0)
            startx = 0;
        if (starty < 0)
            starty = 0;
        if (startx > image.getWidth())
            startx = image.getWidth();
        if (starty > image.getHeight())
            starty = image.getHeight();
        if (startx + width > image.getWidth())
            width = image.getWidth() - startx;
        if (starty + height > image.getHeight())
            height = image.getHeight() - starty;
        return image.getSubimage(startx, starty, width, height);
    }

    public static Color getDominantColor(BufferedImage image) {
        int sumR = 0, sumB = 0, sumG = 0, sum2 = 0;
        int color = 0;
        for (int x = 0; x < image.getWidth(); x++) {
            for (int y = 0; y < image.getHeight(); y++) {
                color = image.getRGB(x, y);
                Color c = new Color(color);
                sumR += c.getRed();
                sumB += c.getBlue();
                sumG += c.getGreen();
                sum2++;
            }
        }
        return new Color(sumR / sum2, sumG / sum2, sumB / sum2);
    }

    public static void display(ImageIcon image) {
        // UI更新放到EDT线程执行
        SwingUtilities.invokeLater(() -> {
            lbl.setIcon(image);
        });
        try {
            // 可自行调整休眠时间,单位毫秒
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String filename = args[0];
        int PIX_SIZE = Integer.parseInt(args[1]);

        f = new JFrame();
        screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        f.setUndecorated(true);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        try {
            // 修复写死文件名的问题
            BufferedImage img = ImageIO.read(new File(filename));
            ImageIcon image = new ImageIcon(img);
            lbl = new JLabel(image);
            f.setSize(image.getIconWidth(), image.getIconHeight());
            int x = (screenSize.width - f.getWidth()) / 2;
            int y = (screenSize.height - f.getHeight()) / 2;
            f.setLocation(x, y);
            f.getContentPane().add(lbl);
            f.setVisible(true);

            // 像素化逻辑放到独立工作线程执行,不阻塞EDT
            new Thread(() -> {
                BufferedImage imagePixelated = ImageUtil.pixelate(img, PIX_SIZE);
                // 可自行取消注释保存结果
                // try {
                //     ImageIO.write(imagePixelated, "jpg", new File("pixelated.jpg"));
                // } catch (IOException e) {
                //     e.printStackTrace();
                // }
            }).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
核心修改说明
  • 线程模型适配:将耗时的像素化计算逻辑放到独立工作线程运行,所有UI更新操作通过SwingUtilities.invokeLater调度到EDT线程执行,避免阻塞UI渲染
  • 简化UI更新逻辑:不再反复移除新增JLabel,直接调用setIcon方法更新标签内容,大幅降低UI开销
  • 优化更新频率:每填充完一个完整的像素区块后再触发UI更新,既保留了逐步像素化的演示效果,又避免了冗余操作
  • 修复细节问题:修正了原代码中读取文件写死的问题,补充了窗口关闭逻辑,调整了休眠时间提升演示流畅度

内容的提问来源于stack exchange,提问作者Turqay Umudzade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 00:54:05