Combining images where BG is a JPEG causes unexpec

2019-02-17 06:39发布

问题:

Why does combining images where BG is a JPEG cause unexpected results?

This is a follow-up to my answer in Overlaying of 2 images doesnt work properly. The source posted there (using a BG image created in memory) looks like this:

  • The BG image is on the left.
  • The FG image (a PNG with transparency) is in the middle.
  • The combined image is on the right.

So far, so good. But then the person who asked the question commented that if the BG was a JPEG, it failed. Thinking they were mistaken, I altered my example to encode the BG image to a JPEG. Now if I use BufferedImage.TYPE_INT_ARGB or BufferedImage.TYPE_INT_RGB for the final image I get what they were referring to:

TYPE_INT_ARGB

TYPE_INT_RGB

I expected the result to be the same as the original for at least one of those (more so the ARGB variant).

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import javax.imageio.ImageIO;

class CombineImages {

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                try {
                    URL urlImage1 =
                        new URL("http://i.stack.imgur.com/T5uTa.png");

                    // Load the FG image
                    Image fgImage = ImageIO.read(urlImage1);
                    int w = fgImage.getWidth(null);
                    int h = fgImage.getHeight(null);
                    // Create a non-trasparent BG image
                    BufferedImage bgImageTemp =
                            new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);

                    ByteArrayOutputStream baos =
                        new ByteArrayOutputStream();
                    ImageIO.write(bgImageTemp, "jpg", baos);
                    ByteArrayInputStream bais =
                        new ByteArrayInputStream(baos.toByteArray());
                    BufferedImage bgImageJpeg = ImageIO.read(bais);

                    int result = JOptionPane.showConfirmDialog(
                        null,
                        "Use a final image with transparency?",
                        "Transparency",
                        JOptionPane.YES_NO_OPTION);

                    int type = (result==JOptionPane.OK_OPTION ?
                        BufferedImage.TYPE_INT_ARGB :
                        BufferedImage.TYPE_INT_RGB);

                    // Create the final image
                    BufferedImage finalImage =
                            new BufferedImage(w,h,type);
                    Graphics2D g = finalImage.createGraphics();
                    g.drawImage(bgImageJpeg, w, h, null);
                    g.drawImage(fgImage, w, h, null);
                    g.dispose();

                    JPanel gui = new JPanel(new GridLayout(1,0,5,5));

                    gui.add(new JLabel(new ImageIcon(bgImageJpeg)));
                    gui.add(new JLabel(new ImageIcon(fgImage)));
                    gui.add(new JLabel(new ImageIcon(finalImage)));

                    JOptionPane.showMessageDialog(null, gui);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

回答1:

Looks like this is due to a typo.

In your referenced answer, the code that formed the combined image was

Graphics2D g = finalImage.createGraphics();
g.drawImage(bgImage, 0, 0, null);
g.drawImage(fgImage, 0, 0, null);

But in this question, it's been changed to,

Graphics2D g = finalImage.createGraphics();
g.drawImage(bgImageJpeg, w, h, null);
g.drawImage(fgImage, w, h, null);

The latter begins drawing at the "top-left corner", which happens to be the bottom-right corner of the images, so nothing is really drawn. The former, however, draws the entire images, as expected.



回答2:

gui.repaint();

Try that after since you effectively painted the panel and components while constructing the joptionpane, but, even then because construction threading matching visibility invocation will not hold truth you should invoke the g.drawImage in a paint override method after joptionpane invocation anyhow.

It cannot draw something that does not yet actually exist onscreen, however there is tolerance for the call because it theoretically exists as a set of objects enough for the method.