Java 2D – Composition

Composition là sự kết hợp của phần tử ảo từ source code vào image. Chúng được sử dụng để tạo ảo ảnh mà tất cả các phần tử là các phần của cùng 1 cảnh. Composition được sử dụng rộng rãi trong công nghiệp phim để tạo ra hiệu ứng đám động thay vì tạo ra toàn bộ khung cảnh thật, rất tốn kém và khó thực hiện.

Operation

Có nhiều kĩ thuật composition. Chúng ta show một vài kĩ thuật trong các ví dụ tiếp theo. Class AlphaComposite implement rule alpha để kết hợp màu nguồn và đích để đạt được hiệu ứng màu như mong muốn.

Chúng ta muốn vẽ 2 object trên panel. Vẽ object thứ nhất được gọi đích, object thứ 2 là nguồn. Class AlphaComposite quyết định 2 object nguồn và đích được trộn với nhau như thế nào. Nếu chúng ta sử dụng rule AlphaComposite.SRC_OVER rule, pixel của nguồn được vẽ vào vùng mà 2 object chồng nhau (overlap).

CompositionEx.java

package net.vncoding;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private final int rules[] = {
        AlphaComposite.DST,
        AlphaComposite.DST_ATOP,
        AlphaComposite.DST_OUT,
        AlphaComposite.SRC,
        AlphaComposite.SRC_ATOP,
        AlphaComposite.SRC_OUT
    };    
    
    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        for (int x = 20, y = 20, i = 0; i < rules.length; x += 60, i++) {

            AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f);

            BufferedImage buffImg = new BufferedImage(60, 60,
                    BufferedImage.TYPE_INT_ARGB);
            Graphics2D gbi = buffImg.createGraphics();

            gbi.setPaint(Color.blue);
            gbi.fillRect(0, 0, 40, 40);
            gbi.setComposite(ac);

            gbi.setPaint(Color.green);
            gbi.fillRect(5, 5, 40, 40);

            g2d.drawImage(buffImg, x, y, null);
            gbi.dispose();
        }
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class CompositionEx extends JFrame {

    public CompositionEx() {

        add(new Surface());

        setTitle("Composition");
        setSize(400, 120);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                CompositionEx ex = new CompositionEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:
Trong ví dụ này, chúng ta vẽ 2 hình chữ nhật và kết hợp chúng với 6 thủ thuật composition

private final int rules[] = {
    AlphaComposite.DST,
    AlphaComposite.DST_ATOP,
    AlphaComposite.DST_OUT,
    AlphaComposite.SRC,
    AlphaComposite.SRC_ATOP,
    AlphaComposite.SRC_OUT
}; 

Đây là 6 rule composition

AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f);

Lấy object AlphaComposite.

BufferedImage buffImg = new BufferedImage(60, 60,
        BufferedImage.TYPE_INT_ARGB);

Sử dụng buffer image để thực hiện composition

Graphics2D gbi = buffImg.createGraphics();

Object Graphics2D được tạo từ buffer image bằng việc sử dụng phương thức createGraphics().

gbi.setComposite(ac);

Phương thức setComposite() set composition cho ngữ cảnh Graphics2D.

g2d.drawImage(buffImg, x, y, null);

Sử dụng phương thức drawImage() để vẽ buffer image trên panel.

gbi.dispose();

Object graphics phải được giải phóng.

Kết quả:

Java2D - Composition
Java2D – Composition

Sun và Cloud

Trong ví dụ tiếp theo, chúng ta show cảnh “Mặt trời xuất hiện sau đám mây”. Chúng ta sử dụng công nghệ composition trong hiệu ứng chuyển đọng này

SunAndCloudEx.java

package net.vncoding;

import java.awt.AlphaComposite;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

class Surface extends JPanel implements ActionListener {

    private Image sun;
    private Image cloud;
    private Timer timer;
    private float alpha = 1f;
    
    private final int DELAY = 600;

    public Surface() {

        loadImages();
        initTimer();
    }

    private void loadImages() {

        sun = new ImageIcon("sun.png").getImage();
        cloud = new ImageIcon("cloud.png").getImage();
    }

    private void initTimer() {

        timer = new Timer(DELAY, this);
        timer.start();
    }

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        BufferedImage buffImg = new BufferedImage(220, 140,
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D gbi = buffImg.createGraphics();

        AlphaComposite ac = AlphaComposite.getInstance(
                AlphaComposite.SRC_OVER, alpha);

        gbi.drawImage(sun, 40, 30, null);
        gbi.setComposite(ac);
        gbi.drawImage(cloud, 0, 0, null);

        g2d.drawImage(buffImg, 20, 20, null);

        gbi.dispose();
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }

    private void step() {
        
        alpha -= 0.1;

        if (alpha <= 0) {

            alpha = 0;
            timer.stop();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        step();
        repaint();
    }
}

public class SunAndCloudEx extends JFrame {

    public SunAndCloudEx() {

        initUI();
    }

    private void initUI() {

        add(new Surface());

        setTitle("Sun and cloud");
        setSize(300, 210);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                SunAndCloudEx ex = new SunAndCloudEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:
Trong ví dụ này, chúng ta tạo 1 animation đám mây sẽ dần biến mất và mặt trời hiện ra.

private void loadImages() {
    
    sun = new ImageIcon("sun.png").getImage();
    cloud = new ImageIcon("cloud.png").getImage();
}

Load image từ file.

private void initTimer() {

    timer = new Timer(DELAY, this);
    timer.start();
}

Trong phương thức initTimer(), bộ timer được active.

AlphaComposite ac = AlphaComposite.getInstance(
        AlphaComposite.SRC_OVER, alpha);

Chúng ta sử dụng rule AlphaComposite.SRC_OVER để image nguồn trộn với đích và ghi đè vài pixel trống.

gbi.drawImage(sun, 40, 30, null);
gbi.setComposite(ac);
gbi.drawImage(cloud, 0, 0, null);

g2d.drawImage(buffImg, 20, 20, null);

Image được render vào BufferedImage và được copy tới screen. Phương thức setComposite() chỉ pixel ới được kết hợp với các pixel đang tồn tại như thế nào trên graphics device trong khi xử lý render.

Kết quả:

Java2D - Sun & Cloud
Java2D – Sun & Cloud

Spotlight

Spotlight là một kĩ thuật sử dụng chùm ánh sáng mạnh chiếu sáng chỉ một khu vực nhỏ, được sử dụng đặc biệt để tập trung sự chú ý vào một nghệ sĩ sân khấu.

package net.vncoding;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;


class Surface extends JPanel {

    private final int RADIUS = 50;
    private Image image;
    private int iw;
    private int ih;
    private int x;
    private int y;
    private boolean mouseIn;

    public Surface() {

        initUI();
    }

    private void initUI() {

        loadImage();

        iw = image.getWidth(null);
        ih = image.getHeight(null);

        addMouseMotionListener(new MyMouseAdapter());
        addMouseListener(new MyMouseAdapter());
    }

    private void loadImage() {

        image = new ImageIcon("penguin.png").getImage();
    }

    @Override
    protected void paintComponent(Graphics g) {
    
        super.paintComponent(g);
        doDrawing(g);
    }

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        int midX = (getWidth() - iw) / 2;
        int midY = (getHeight() - ih) / 2;

        BufferedImage bi = new BufferedImage(getWidth(),
                getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D bigr = bi.createGraphics();

        if (mouseIn) {
            bigr.setPaint(Color.white);
            bigr.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2,
                    RADIUS * 2);
            bigr.setComposite(AlphaComposite.SrcAtop);
            bigr.drawImage(image, midX, midY, iw, ih, this);
        }

        bigr.setComposite(AlphaComposite.SrcOver.derive(0.1f));
        bigr.drawImage(image, midX, midY, iw, ih, this);
        bigr.dispose();

        g2d.drawImage(bi, 0, 0, getWidth(), getHeight(), this);

        g2d.dispose();
    }

    private class MyMouseAdapter extends MouseAdapter {

        @Override
        public void mouseExited(MouseEvent e) {
            mouseIn = false;
            repaint();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            mouseIn = true;
        }

        @Override
        public void mouseMoved(MouseEvent e) {

            x = e.getX();
            y = e.getY();

            repaint();
        }
    }
}

public class SpotlightEx extends JFrame {

    public SpotlightEx() {

        initUI();
    }

    private void initUI() {

        add(new Surface());

        setSize(350, 300);
        setTitle("Spotlight");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                SpotlightEx ex = new SpotlightEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:
Hiệu ứng spotlight được tạo với rule composition và giá trị alpha. Một điều quan trọng là image phải có background trong suốt (transparent)

BufferedImage bi = new BufferedImage(getWidth(),
        getHeight(), BufferedImage.TYPE_INT_ARGB);

BufferedImage được tạo. Kích thước của image bằng kích thước panel. Ảnh sử dụng trong ví dụ này có background trong suốt. Do đó, chúng ta sử dụng loại image là BufferedImage.TYPE_INT_ARGB

if (mouseIn) {
    bigr.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2,
            RADIUS * 2);
    bigr.setComposite(AlphaComposite.SrcAtop);
    bigr.drawImage(image, midX, midY, iw, ih, this);
}

Nếu chúng ta di chuyển chuột trong panel, Rule AlphaComposite.SrcAtop được sử dụng để vẽ vòng tròn bao quanh con trỏ chuột

bigr.setComposite(AlphaComposite.SrcOver.derive(0.1f));
bigr.drawImage(image, midX, midY, iw, ih, this);

Đoạn code này vẽ phần còn lại của image. Rule AlphaComposite.SrcOver được sử dụng để tạo image độ trong suốt cao

g2d.drawImage(bi, 0, 0, getWidth(), getHeight(), this);

Bước cuối cùng, buffer image được vẽ qua toàn bộ area của panel.

Kết quả:

Java2D - Spotlight
Java2D – Spotlight

Be the first to comment

Leave a Reply