Java 2D – Kết hợp các đối tượng hình học

5/5 - (2 votes)

Trong phần này chúng ta sẽ kết hợp các đối tượng hình học với nhau.

Lớp AlphaComposite

Trong bài trước chúng ta đã dùng lớp AlphaComposite với giá trị rule để tạo hiệu ứng trong suốt, trong phần này chúng ta tiếp tục tìm hiểu về giá trị này để trộn các đối tượng với nhau.

Giả sử bạn vẽ 2 hình chữ nhật lên JPanel, trong Java đối tượng đầu tiên được vẽ được gọi là đối tượng đích (Destination – DST), đối tượng thứ hai được gọi là đối tượng nguồn (Source – SRC), giả sử bạn dùng rule là AlphaComposite.SRC_OVER, thì khi vẽ ra đối tượng nguồn sẽ nằm đè lên đối tượng đích.

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);
            }
        });
    }
}

Trong đoạn code trên chúng ta vẽ hai hình vuông cắt nhau với 6 rule khác nhau.

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

Chúng ta lưu sẵn các rule trong mảng rules.

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

Tiếp theo là tạo đối tượng AlphaComposite.

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

Ở đây chúng ta không dùng đối tượng g2d để vẽ trực tiếp mà vẽ trong một BufferedImage rồi mới đưa buffer này vào trong g2d. Bạn có thể hiểu BufferedImage là một tờ giấy, chúng ta vẽ lên tờ giấy đó rồi dán lên tường là JPanel.

Graphics2D gbi = buffImg.createGraphics();

Để vẽ lên BufferedImage thì chúng ta dùng đối tượng Graphics2D tạo từ buffer với phương thức createGraphics() chứ không dùng g2d.

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

Sau khi đã vẽ lên buffer thì chúng ta dùng g2d để vẽ lên JPanel với phương thức drawImage().

gbi.dispose();

Nhớ phải loại bỏ tất cả các đối tượng Graphics2D sau khi dùng xong.

Capture

Ví dụ 1

Chúng ta luyện tập tiếp với AlphaComposite thông qua ví dụ dưới đây.
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().getScaledInstance(100, 100, 
                                                     Image.SCALE_DEFAULT);
        cloud = new ImageIcon("cloud.png").getImage().getScaledInstance(200, 200,                                                           Image.SCALE_DEFAULT);
    }

    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);
            }
        });
    }
}

Trong ví dụ này, chúng ta vẽ mặt trời và đám mây, đám mây sẽ từ từ biến mất nhường chỗ cho mặt trời.

private void loadImages() {
    
    sun = new ImageIcon("sun.png").getImage().getScaledInstance(100, 100,                                                                   Image.SCALE_DEFAULT);
    cloud = new ImageIcon("cloud.png").getImage().getScaledInstance(200, 200,                                                               Image.SCALE_DEFAULT);
}

Chúng ta dùng lớp ImageIcon để load ảnh và thay đổi lại kích thước cho phù hợp nếu cần.

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

Ở đây chúng ta dùng rule là SRC_OVER để trộn màu đám mây với màu nền.

Untitled

Ví dụ 2

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);
            }
        });
    }
}

Trong ví dụ này, chúng ta tạo hiệu ứng đèn pin.

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

Ảnh nền của chúng ta có phần nền trong suốt (chỉ có ảnh PNG mới có) nên ở đây chúng ta dùng kiểu 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);
}

Chúng ta vẽ một hình tròn tại vị trí của chuột. Giá trị AlphaComposite.SrcAtop vẽ hình rõ hoàn toàn, không mờ, tức là mặc định rule này dùng alpha là 1.0

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

Hai dòng trên vẽ toàn bộ hình còn lại. Giá trị AlphaComposite.SrcOver.derive(0.1f) vẽ hình gần như mờ hoàn toàn.

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

Tất cả những thứ trên chúng ta vẽ trong buffer vì thế cuối cùng chúng ta vẽ lại toàn bộ buffer và JPanel.

Untitled
0 0 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments