2003/02/22 03:55:06
[org.ibex.core.git] / src / org / xwt / DoubleBuffer.java
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt;
3
4 /**
5  *  <p>
6  *  A block of pixels which can be drawn on and rapidly copied to the
7  *  screen. Drawing operations are performed on this class; it is
8  *  then rendered to the screen with Surface.blit().
9  *  </p>
10  *
11  *  <p>
12  *  Implementations of the Platform class should return subclasses of
13  *  DoubleBuffer from the _createDoubleBuffer() method. These
14  *  implementations may choose to use off-screen video ram for this
15  *  purpose (for example, a Pixmap on X11).
16  *  </p>
17  *
18  *  <p>
19  *  A note on coordinates: all members on DoubleBuffer specify
20  *  coordinates in terms of x1,y1,x2,y2 even though the Box class
21  *  represents regions internally as x,y,w,h.
22  *  </p>
23  */
24
25 public abstract class DoubleBuffer {
26
27     // Methods to Be Overridden//////////////////////////////////////////////////////////////////////////////
28
29     /** Stretch the source image to (dx1, dy1, dx2, dy2), but clip it to (cx1, cy1, cx2, cy2) */
30     protected abstract void _drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int cx1, int cy1, int cx2, int cy2);
31
32     /** Draw <tt>text</tt> in <tt>font</tt> and <tt>color</tt> on this DoubleBuffer, with the upper left corner of the text at (x, y) */
33     protected abstract void _drawString(String font, String text, int x, int y, int color);
34
35     /** Draw a point at (x, y) */
36     protected void _drawPoint(int x, int y, int color) { fillRect(x, y, x + 1, y + 1, color); }
37
38     /** Draw a line from (x1, y1) to (x2, y2) that is width pixels wide */
39     protected void _drawLine(int x1, int y1, int x2, int y2, int width, int color) {
40         if (Math.abs(x1 - x2) < 1) {
41             for(int y=Math.min(y1, y2); y<Math.max(y1, y2); y++)
42                 drawPoint(x1, y, color);
43             return;
44         }
45         if (Math.abs(y1 - y2) < 1) {
46             for(int x=Math.min(x1, x2); x<Math.max(x1, x2); x++)
47                 drawPoint(x, y1, color);
48             return;
49         }
50         if (x1 > x2) {
51             int _x1 = x1; x1 = x2; x2 = _x1;
52             int _y1 = y1; y1 = y2; y2 = _y1;
53         }
54         double slope = (double)(y2 - y1) / (double)(x2 - x1);
55         int last_y = y1;
56         for(int x=x1; x<=x2; x++) {
57             int new_y = (int)(slope * (double)(x - x1)) + y1;
58             if (slope >= 0) {
59                 for(int y=last_y + 1; y < new_y; y++) drawPoint(x, y, color);
60             } else {
61                 for(int y=last_y - 1; y > new_y; y--) drawPoint(x, y, color);
62             }
63             if (x != x2) drawPoint(x, new_y, color);
64             last_y = new_y;
65         }
66     }
67
68     /** Fill the region (x1, y1, x2, y2) with <tt>color</tt> (AARRGGBB format); the alpha channel component is ignored */
69     protected abstract void _fillRect(int x1, int y1, int x2, int y2, int color);
70
71     /** Fill in the trapezoid defined by (x1, y1), (x2, y1), (x3, y2), (x4, y2); leftSlope and rightSlope are provided for convenience */
72     protected void _fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, double leftSlope, double rightSlope, int color) {
73         for(int y=y1; y<y2; y++) {
74             int _x1 = (int)Math.floor((y - y1) * leftSlope + x1);
75             int _y1 = (int)Math.floor(y);
76             int _x2 = (int)Math.ceil((y - y1) * rightSlope + x2);
77             int _y2 = (int)Math.floor(y) + 1;
78             if (_x1 > _x2) { int _x0 = _x1; _x1 = _x2; _x2 = _x0; }
79             fillRect(_x1, _y1, _x2, _y2, color);
80         }
81     }
82
83
84     // Internal Stuff //////////////////////////////////////////////////////////////////////////////
85
86     protected int width, height;
87     private int clipx = 0, clipy = 0, clipw, cliph;
88     private DoubleBuffer() { }
89     protected DoubleBuffer(int width, int height) { this.width = width; this.height = height; clipw = width; cliph = height; }
90     protected SVG.Affine ctm = null;
91
92
93     // Final methods //////////////////////////////////////////////////////////////////////////////
94
95     public final int getHeight() { return width; }
96     public final int getWidth() { return height; }
97     public final void setTransform(SVG.Affine ctm) { this.ctm = ctm; }
98     public final void setClip(int x, int y, int x2, int y2) { clipx = x; clipy = y; clipw = x2 - x; cliph = y2 - y; }
99     public final void drawPicture(Picture p, int x, int y) { drawPicture(p, x, y, x + p.getWidth(), y + p.getHeight(), 0, 0, p.getWidth(), p.getHeight()); }
100
101     public final void drawPoint(int x, int y, int color) {
102         if (x > clipx && x < clipx + clipw && y > clipy && y < clipy + cliph)
103             _drawPoint(x, y, color);
104     }
105
106     public final void drawLine(int x1, int y1, int x2, int y2, int width, int color) {
107
108         if (x1 == x2 || y1 == y2) return;
109
110         // FIXME: don't compute slope if not needed
111         double slope = (y2 - y1) / (x2 - x1);
112         if (x1 < clipx) { y1 = (int)((clipx - x1) * slope + y1); x1 = clipx; }
113         if (y1 < clipy) { x1 = (int)((clipy - y1) / slope + x1); y1 = clipy; }
114         if (x2 > clipx + clipw) { x2 = clipx + clipw; y2 = (int)((x2 - x1) / slope + y1); }
115         if (y2 > clipy + cliph) { y2 = clipy + cliph; x2 = (int)((y2 - y1) * slope + y1); }
116
117         _drawLine(x1, y1, x2, y2, width, color);
118     }
119
120     /** Stretch the picture to fill (dx1, dy1, dx2, dy2) and blit it, clipping to (cx1, cy1, cx2, cy2) */
121     public final void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int cx1, int cy1, int cx2, int cy2) {
122
123         if (org.xwt.util.Log.assertionsEnabled) {
124             if (dx1 >= dx2) org.xwt.util.Log.log(DoubleBuffer.class, "drawPicture(): dx1 >= dx2: dx1=" + dx1 + " dx2=" + dx2, true);
125             if (dy1 >= dy2) org.xwt.util.Log.log(DoubleBuffer.class, "drawPicture(): dy1 >= dy2: dy1=" + dy1 + " dy2=" + dy2, true);
126             if (cx1 >= cx2) org.xwt.util.Log.log(DoubleBuffer.class, "drawPicture(): cx1 >= cx2: cx1=" + cx1 + " cx2=" + cx2, true);
127             if (cy1 >= cy2) org.xwt.util.Log.log(DoubleBuffer.class, "drawPicture(): cy1 >= cy2: cy1=" + cy1 + " cy2=" + cy2, true);
128         }
129
130         // shrink clipping rectangle to be no bigger than drawing rectangle
131         if (cx1 < dx1) cx1 = dx1;
132         if (cy1 < dy1) cy1 = dy1;
133         if (cx2 > dx2) cx2 = dx2;
134         if (cy2 > dy2) cy2 = dy2;
135
136         // intersect the two clipping regions
137         if (cx2 < clipx) return;
138         if (cy2 < clipy) return;
139         if (cx1 > clipx + clipw) return;
140         if (cy1 > clipy + cliph) return;
141         if (cx1 < clipx) cx1 = clipx;
142         if (cy1 < clipy) cy1 = clipy;
143         if (cx2 > clipx + clipw) cx2 = clipx + clipw;
144         if (cy2 > clipy + cliph) cy2 = clipy + cliph;
145     
146         if (ctm == null) {
147             _drawPicture(source, dx1, dy1, dx2, dy2, cx1, cy1, cx2, cy2);
148         } else {
149             _drawPicture(source,
150                          (int)ctm.multiply_px(dx1, dy1), (int)ctm.multiply_py(dx1, dy1), 
151                          (int)ctm.multiply_px(dx2, dy2), (int)ctm.multiply_py(dx2, dy2), 
152                          (int)ctm.multiply_px(cx1, cy1), (int)ctm.multiply_py(cx1, cy1), 
153                          (int)ctm.multiply_px(cx2, cy2), (int)ctm.multiply_py(cx2, cy2));
154         }
155     }
156
157     public final void drawString(String font, String text, int x, int y, int color) {
158         // FIXME: clipping
159         if (ctm == null) {
160             _drawString(font, text, x, y, color);
161         } else {
162             _drawString(font, text, (int)ctm.multiply_px(x, y), (int)ctm.multiply_py(x, y), color);
163         }
164     }
165
166     public final void fillRect(int x1, int y1, int x2, int y2, int color) {
167
168         x1 = Math.max(Math.min(clipx + clipw, x1), clipx);
169         x2 = Math.max(Math.min(clipx + clipw, x2), clipx);
170         y1 = Math.max(Math.min(clipy + cliph, y1), clipy);
171         y2 = Math.max(Math.min(clipy + cliph, y2), clipy);
172
173         if (ctm == null) {
174             _fillRect(x1, y1, x2, y2, color);
175         } else {
176             _fillRect((int)ctm.multiply_px(x1, y1), (int)ctm.multiply_py(x1, y1), 
177                       (int)ctm.multiply_px(x2, y2), (int)ctm.multiply_py(x2, y2), 
178                       color);
179         }
180     }
181
182     public final void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color) {
183         fillTrapezoid(x1, x2, y1, x3, x4, y2, (double)(x3 - x1) / (double)(y2 - y1), (double)(x4 - x2) / (double)(y2 - y1), color);
184     }
185
186     private final void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, double slope1, double slope2, int color) {
187
188         // invariant: x1 <= x2, x3 <= x4, y1 <= y2
189         if (y1 == y2) return;
190         if (org.xwt.util.Log.assertionsEnabled) {
191             if (x1 > x2) org.xwt.util.Log.log(DoubleBuffer.class, "fillTrapezoid(): x1 > x2: x1=" + x1 + " x2=" + x2, true);
192             if (x3 > x4) org.xwt.util.Log.log(DoubleBuffer.class, "fillTrapezoid(): x3 > x4: x3=" + x3 + " x4=" + x4, true);
193             if (y1 >= y2) org.xwt.util.Log.log(DoubleBuffer.class, "fillTrapezoid(): y1 >= y2: y1=" + y1 + " y2=" + y2, true);
194         }
195
196         // clip top and bottom edges
197         if (y1 > clipy + cliph) return;
198         if (y2 < clipy) return;
199         if (y1 < clipy) { x1 = (int)(y1 + slope1 * clipy); x2 = (int)(y1 + slope2 * clipy); y1 = clipy; }
200         if (y2 > clipy + cliph) { y2 = clipy + cliph; x3 = (int)(y1 + slope1 * y2); x4 = (int)(slope2 * y2); }
201
202         if (x1 < clipx && x3 < clipx) {
203             x1 = x3 = clipx;
204         } else if (!(x1 >= clipx && x3 >= clipx)) {
205             int y = (int)((clipx - x1) / slope1 + y1);
206             int x = (int)(slope2 * (y - y2) + x2);
207             // we have to recurse here because we don't know which subtrapezoid might get split on the other side
208             fillTrapezoid(x1, x2, y1, clipx, x, y, slope1, slope2, color);
209             fillTrapezoid(clipx, x, y, x3, x4, y2, slope1, slope2, color);
210             return;
211         }
212
213         if (x2 >= clipx + clipw && x4 >= clipx + clipw) {
214             x2 = x4 = clipx + clipw;
215         } else if (!(x2 < clipx + clipw && x4 < clipx + clipw)) {
216             int y = (int)((clipx + clipw - x3) / slope2 + y1);
217             int x = (int)(slope1 * (y - y1) + x1);
218             _fillTrapezoid(x1, x2, y1, x, clipx + clipw, y, slope1, slope2, color);
219             _fillTrapezoid(x, clipx + clipw, y, x3, x4, y2, slope1, slope2, color);
220             return;
221         }
222
223         _fillTrapezoid(x1, x2, y1, x3, x4, y2, slope1, slope2, color);
224     }
225
226 }