2007-12-29

2007-12-03

Java2D 3 or more Colors gradient paint (use TexturePaint)

Java中的 GradientPaint 画刷好像仅仅只能支持2色渐变,下面我用动态生成纹理画刷的方式变相实现了一个简单的多色渐变的效果:

package test.draw;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;

import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * 彩虹纹理画刷
 *
 * @author h_Davy [dave3068 AT gmail DOT com]
 * @version 2007-11-28 上午02:09:46
 */
public class RainbowText extends JPanel
        implements Runnable, MouseListener {

    private static final long serialVersionUID = 1L;

    RainbowText() {
        super(true);
        init();
    }
    private void init() {
        setBackground(Color.white);
        setTextAttributes();
        //new Thread(this).start();
        addMouseListener(this);
    }


    private String text = "Rainbow Text";
    private Font f;
    private TextLayout tl;
    private AttributedCharacterIterator aci;
    //private FontMetrics fm;

    private void setTextAttributes() {
        f = new Font("serif",Font.BOLD,60);
        //fm = this.getFontMetrics(f);
        AttributedString as = new AttributedString(text);
        //as.addAttribute(TextAttribute.FONT, f, 0, text.length());
        AffineTransform at = new AffineTransform();
        //at.setToRotation(Math.toRadians(45));
        as.addAttribute(TextAttribute.FONT, f.deriveFont(at), 0, text.length());
        aci = as.getIterator();
        Color []c = new Color[]
            {Color.blue, Color.cyan, Color.green, Color.yellow,
                    Color.orange, Color.red, Color.magenta};
        float []w = new float[]
            {.09f, .15f, .16f, .17f, .15f, .16f};
        initTextImg(350, c, w, true);
    }




    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setStroke(new BasicStroke(2.f));

        // draw background
        g2.setColor(getBackground());
        g2.fillRect(0, 0, getWidth(), getHeight());
        g2.setColor(Color.blue);
        g2.drawRoundRect(5, 5, getWidth()-10, getHeight()-10, 10, 10);

        //
        g2.setStroke(new BasicStroke(1.5f));
        FontRenderContext frc = g2.getFontRenderContext();
        //tl = new TextLayout(text, f, frc);
        tl = new TextLayout(aci, frc);
        Rectangle2D rect = tl.getBounds();
        int x = getWidth()/2 - (int)rect.getWidth()/2,
            y = getHeight()/2 + (int)rect.getHeight()/2;
        g2.translate(x, y);
        //g2.rotate(Math.toRadians(-45));
        //g2.setColor(Color.yellow);
        //g2.fill(rect);
        g2.setPaint(new TexturePaint(bi, rect));
        g2.setComposite(AlphaComposite.SrcOver);
        //g2.rotate(Math.toRadians(30));
        tl.draw(g2, 0, 0);
        //AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f);
        //g2.setComposite(ac);
        W = (int)rect.getWidth();
        rect = new Rectangle2D.Float(X, 0, W, 1);
        g2.setPaint(new TexturePaint(bi, rect));
        R.setRect(-x+10, y, getWidth()-20, 20);
        g2.fillRect(R.x, 10, R.width, 20);
    }



    private boolean run = false;
    private int X = 0;
    private int W = 0;
    private static Rectangle R = new Rectangle();
    public void run() {
        try {
            while (run) {
                Thread.sleep(10);
                X+=1;
                if (X>=W) X = 0;
                //repaint();
                repaint(10, R.y+10, R.width, R.height);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void mouseClicked(MouseEvent e) {
        run = !run;
        new Thread(this).start();
    }



    // 纹理
    private BufferedImage bi;
    /**
     * 初始化纹理
     *
     * @param width 纹理总宽度
     * @param c 颜色值数组
     * @param w 各色带宽度所占百分比(w.length=c.length-1)
     * @param cyclic 是否循环
     */
    private void initTextImg(int width, Color []c, float w[], boolean cyclic) {
        bi = new BufferedImage(width, 1, BufferedImage.TYPE_4BYTE_ABGR);
        int n = c.length;
        int m = cyclic ? n : n - 1;
        // initialize widths
        float []ww = new float[m];
        float s = 0;
        int i;
        for (i = 0; i < m-1; i++) {
            s += w[i];
            if (s>=1) {
                break; // 以防输入的百分比过大时产生的异常
            } else {
                ww[i] = w[i];
            }
        }
        if (i<(m-1)) { // 若输入的百分比过大将剩下的均分
            float t = (1-s+w[i])/(m-i);
            for (; i<m; i++) {
                ww[i] = t;
            }
        } else { // 将剩下的分配给最后一个色块
            ww[m-1] = 1 - s;
        }
        // initialize colors
        int []rgbs = new int[n];
        for (i = 0; i < n; i++)
            rgbs[i] = c[i].getRGB();
        // the color's divides
        int a[] = new int[n];
        int r[] = new int[n];
        int g[] = new int[n];
        int b[] = new int[n];

        // initialize the color's divides
        for (i = 0; i < n; i++) {
            a[i] = (rgbs[i] >> 24) & 0xff;
            r[i] = (rgbs[i] >> 16) & 0xff;
            g[i] = (rgbs[i] >>  8) & 0xff;
            b[i] = (rgbs[i]      ) & 0xff;
        }

        // tail of color's divides (if you wana cyclic)
        int ta = ((rgbs[n-1] >> 24) & 0xff) - a[0];
        int tr = ((rgbs[n-1] >> 16) & 0xff) - r[0];
        int tg = ((rgbs[n-1] >>  8) & 0xff) - g[0];
        int tb = ((rgbs[n-1]      ) & 0xff) - b[0];

        // 用于存储平滑过渡的各颜色值
        int []interp = new int[cyclic ? n*256+1 : (n-1)*256+1];

        // 颜色线性平滑插值
        for (i = 0; i <= 256; i++) {
            float rel = i / 256.0f;
            int rgb;
            for (int j=0; j<n-1; j++) {
                rgb =
                    (((int) (a[j] + (a[j+1]-a[j]) * rel)) << 24) |
                    (((int) (r[j] + (r[j+1]-r[j]) * rel)) << 16) |
                    (((int) (g[j] + (g[j+1]-g[j]) * rel)) <<  8) |
                    (((int) (b[j] + (b[j+1]-b[j]) * rel))      );
                interp[i+256*j] = rgb;
            }
            if (cyclic) {
                rgb =
                    (((int) (a[0] + ta * rel)) << 24) |
                    (((int) (r[0] + tr * rel)) << 16) |
                    (((int) (g[0] + tg * rel)) <<  8) |
                    (((int) (b[0] + tb * rel))      );
                interp[n*256-i] = rgb;
            }
        }

        // fill the image
        /*float rt = (float)interp.length / width;
        for (i=0; i<width; i++) {
            bi.setRGB(i, 0, interp[(int)(i*rt)]);
        }*/
        int cur = 0;
        for (i=0; i<m; i++) {
            int wi = (int) (width*ww[i]);
            if (i==(m-1)) wi = width-cur;
            //System.out.print(wi+",");
            float rt = (float)256 / wi;
            for (int j=0; j<wi; j++) {
                bi.setRGB(cur+j, 0, interp[(int)(j*rt)+i*256]);
            }
            cur += wi;
        }
    }





    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }
    public void mousePressed(MouseEvent e) {
    }
    public void mouseReleased(MouseEvent e) {
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        JFrame f = new JFrame("");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(640, 480);
        f.setLocationRelativeTo(null);
        f.add(new RainbowText());
        f.setVisible(true);
    }

}


//EOF Read More...

Java2D Curves Control Point

大家刚接触Java图形编程的可能对 二次曲线、贝尔曲线 的几个控制点有点迷惑,我下面有段示例方便大家理解这两个曲线的几个控制点的作用:
package test.draw;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.GeneralPath;

import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * 方便理解Quadratic/Bezier曲线的几个控制点作用
 *
 * @author h_Davy [dave3068 AT gmail DOT com]
 * @version 2007-11-27 上午01:41:23
 */
public class CurvesTest extends JPanel
        implements MouseListener, MouseMotionListener, FocusListener {

    private static final long serialVersionUID = 1L;

    public CurvesTest() {
        super(true);
        init();
    }
    private void init() {
        setBackground(Color.white);
        setFocusable(true);
        addFocusListener(this);
        addMouseListener(this);
        addMouseMotionListener(this);
    }



    // 二次曲线
    private QuadCurve q = new QuadCurve(new Point(50, 100),
                new Point(200, 150), new Point(300, 100));
    // 贝尔曲线
    private BezierCurve b = new BezierCurve(
            new Point(100, 300), new Point(200, 400),
            new Point(300, 200), new Point(400, 300));


    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setStroke(new BasicStroke(2.f));

        // draw background
        g2.setColor(getBackground());
        g2.fillRect(0, 0, getWidth(), getHeight());
        g2.setColor(Color.blue);
        g2.drawRect(5, 5, getWidth()-10, getHeight()-10);

        // draw curves
        g2.draw(q.getPath());
        g2.draw(b.getPath());
        // fill path area
        g2.setColor(Color.lightGray);
        g2.fill(q.getPath());
        g2.fill(b.getPath());

        // paint drag handles
        g2.setColor(Color.orange);
        DragHandle []dhs = q.getDragHandles();
        for (int i = 0; i<dhs.length; i++)
            g2.fill(dhs[i]);
        dhs = b.getDragHandles();
        for (int i = 0; i<dhs.length; i++)
            g2.fill(dhs[i]);

    }

    private boolean isDrag = false; // 是否在拖动控制点
    private DragHandle curDrag = null; // 当前拖动中的控制点



    public void mousePressed(MouseEvent e) {
        // find out is mousePoint in any DragHandle
        DragHandle dh = q.findDragHandle(e.getPoint());
        if (dh != null) {
            curDrag = dh;
            isDrag = true;
            return;
        }
        dh = b.findDragHandle(e.getPoint());
        if (dh != null) {
            curDrag = dh;
            isDrag = true;
            return;
        }
    }
    public void mouseReleased(MouseEvent e) {
        isDrag = false;
    }
    public void mouseDragged(MouseEvent e) {
        if (isDrag) {
            curDrag.setPoint(e.getPoint());
            q.refresh();
            b.refresh();
            repaint();
        }
    }
    public void focusLost(FocusEvent e) {
        isDrag = false;
    }



    // 控制点区域
    @SuppressWarnings("serial")
    private class DragHandle extends Rectangle {
        private Point pt = new Point();

        DragHandle() {
            width = 5;
            height = 5;
        }
        DragHandle(Point p) {
            this();
            pt.x = p.x;
            pt.y = p.y;
            calcRect();
        }

        private void calcRect() {
            x = pt.x - 2;
            y = pt.y - 2;
        }
        public void setPoint(Point p) {
            pt.x = p.x;
            pt.y = p.y;
            calcRect();
        }
        public Point getPoint() {
            return pt;
        }
    }




    // Quadratic 曲线
    private class QuadCurve {
        // 曲线路径
        protected GeneralPath gp = new GeneralPath();
        // 曲线的几个控制点
        protected DragHandle []pt = null;

        protected QuadCurve () {}
        QuadCurve(Point p1, Point p2, Point p3) {
            pt = new DragHandle[3];
            pt[0] = new DragHandle(p1);
            pt[1] = new DragHandle(p2);
            pt[2] = new DragHandle(p3);
            refresh();
        }

        // 更新曲线
        public void refresh() {
            gp.reset();
            gp.moveTo((float) pt[0].getX(), (float) pt[0].getY());
            gp.quadTo((float) pt[1].getX(), (float) pt[1].getY(),
                    (float) pt[2].getX(), (float) pt[2].getY());
        }

        // 指定点是否在控制点区域内
        public DragHandle findDragHandle(Point p) {
            for (int i = 0; i<pt.length; i++)
                if (pt[i].contains(p)) return pt[i];
            return null;
        }
        // 获得所有控制点
        public DragHandle[] getDragHandles() {
            return pt;
        }
        // 获得该曲线的Path
        public GeneralPath getPath() {
            return gp;
        }
    }
    // Bezier 曲线
    private class BezierCurve extends QuadCurve {

        protected BezierCurve(Point p1, Point p2, Point p3) {}
        BezierCurve (Point p1, Point p2, Point p3, Point p4) {
            pt = new DragHandle[4];
            pt[0] = new DragHandle(p1);
            pt[1] = new DragHandle(p2);
            pt[2] = new DragHandle(p3);
            pt[3] = new DragHandle(p4);
            refresh();
        }

        public void refresh() {
            gp.reset();
            gp.moveTo((float) pt[0].getX(), (float) pt[0].getY());
            gp.curveTo((float) pt[1].getX(), (float) pt[1].getY(),
                    (float) pt[2].getX(), (float) pt[2].getY(),
                    (float) pt[3].getX(), (float) pt[3].getY());
        }
    }





    public void mouseClicked(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }
    public void mouseMoved(MouseEvent e) {
    }
    public void focusGained(FocusEvent e) {
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        JFrame f = new JFrame("拖动几个橙色的小点看看 ^_^");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(640, 480);
        f.setLocationRelativeTo(null);
        f.add(new CurvesTest());
        f.setVisible(true);
    }

}

//EOF Read More...