From 1019725cfa88d9ad5dfabaf178e87da79c9c3e17 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:38:19 +0000 Subject: [PATCH] 2003/09/27 06:42:26 darcs-hash:20040130073819-2ba56-68023fd33311bc6619d2217c2132e2db10dbc001.gz --- src/org/xwt/Box.java.pp | 42 ++- src/org/xwt/PixelBuffer.java | 76 ++++- src/org/xwt/Res.java | 2 + src/org/xwt/Surface.java | 7 +- src/org/xwt/VectorGraphics.java | 602 ++++++--------------------------------- src/org/xwt/plat/AWT.java | 16 +- 6 files changed, 211 insertions(+), 534 deletions(-) diff --git a/src/org/xwt/Box.java.pp b/src/org/xwt/Box.java.pp index f2c45a0..71da82f 100644 --- a/src/org/xwt/Box.java.pp +++ b/src/org/xwt/Box.java.pp @@ -92,6 +92,7 @@ public final class Box extends JS.Scope { static int TILE_FLAG = 0x00000020; static int FONT_CHANGED_FLAG = 0x00000040; // set when font changes, cleared during repack static int ISROOT_FLAG = 0x00000080; + static int NOCLIP_FLAG = 0x00000100; // Geometry //////////////////////////////////////////////////////////////////////////// @@ -150,10 +151,12 @@ public final class Box extends JS.Scope { public final void dirty() { dirty(0, 0, width, height); } public final void dirty(int x, int y, int w, int h) { for(Box cur = this; cur != null; cur = cur.parent) { - w = min(x + w, cur.width) - max(x, 0); - h = min(y + h, cur.height) - max(y, 0); - x = max(x, 0); - y = max(y, 0); + if ((flags & NOCLIP_FLAG) == 0) { + w = min(x + w, cur.width) - max(x, 0); + h = min(y + h, cur.height) - max(y, 0); + x = max(x, 0); + y = max(y, 0); + } if (w <= 0 || h <= 0) return; if (cur.parent == null && cur.surface != null) cur.surface.dirty(x, y, w, h); x += cur.x; @@ -391,14 +394,16 @@ public final class Box extends JS.Scope { int globaly = parenty + (parent == null ? 0 : y); // intersect the x,y,w,h rendering window with ourselves; quit if it's empty - clipw = min(max(clipx, parent == null ? 0 : globalx) + clipw, (parent == null ? 0 : globalx) + width) - globalx; - cliph = min(max(clipy, parent == null ? 0 : globaly) + cliph, (parent == null ? 0 : globaly) + height) - globaly; - clipx = max(clipx, parent == null ? 0 : globalx); - clipy = max(clipy, parent == null ? 0 : globaly); + if ((flags & NOCLIP_FLAG) == 0) { + clipw = min(max(clipx, parent == null ? 0 : globalx) + clipw, (parent == null ? 0 : globalx) + width) - globalx; + cliph = min(max(clipy, parent == null ? 0 : globaly) + cliph, (parent == null ? 0 : globaly) + height) - globaly; + clipx = max(clipx, parent == null ? 0 : globalx); + clipy = max(clipy, parent == null ? 0 : globaly); + } if (clipw <= 0 || cliph <= 0) return; if ((fillcolor & 0xFF000000) != 0x00000000) - buf.fillRect(clipx, clipy, clipx + clipw, clipy + cliph, fillcolor); + buf.fillTrapezoid(clipx, clipx + clipw, clipy, clipx, clipx + clipw, clipy + cliph, fillcolor); if (image != null) if ((flags & TILE_FLAG) != 0) renderTiledImage(globalx, globaly, clipx, clipy, clipw, cliph, buf); @@ -408,10 +413,12 @@ public final class Box extends JS.Scope { renderText(globalx, globaly, clipx, clipy, clipw, cliph, buf); // now subtract the pad region from the clip region before proceeding - clipw = min(max(clipx, globalx + hpad) + clipw, globalx + width - hpad) - clipx; - cliph = min(max(clipy, globaly + vpad) + cliph, globaly + height - vpad) - clipy; - clipx = max(clipx, globalx + hpad); - clipy = max(clipy, globaly + vpad); + if ((flags & NOCLIP_FLAG) == 0) { + clipw = min(max(clipx, globalx + hpad) + clipw, globalx + width - hpad) - clipx; + cliph = min(max(clipy, globaly + vpad) + cliph, globaly + height - vpad) - clipy; + clipx = max(clipx, globalx + hpad); + clipy = max(clipy, globaly + vpad); + } for(Box b = getChild(0); b != null; b = b.nextSibling()) b.render(globalx, globaly, clipx, clipy, clipw, cliph, buf); @@ -1056,6 +1063,15 @@ public final class Box extends JS.Scope { b.dirty(); } }); + specialBoxProperties.put("noclip", new SpecialBoxProperty() { + public Object get(Box b) { return ((b.flags & NOCLIP_FLAG) != 0) ? Boolean.TRUE : Boolean.FALSE; } + public void put(Box b, Object value) { + if (((b.flags & NOCLIP_FLAG) != 0) == stob(value)) return; + if (stob(value)) b.flags |= NOCLIP_FLAG; else b.flags &= ~NOCLIP_FLAG; + MARK_FOR_REFLOW_b; + b.dirty(); + } }); + specialBoxProperties.put("invisible", new SpecialBoxProperty() { public Object get(Box b) { for (Box cur = b; cur != null; cur = cur.parent) { diff --git a/src/org/xwt/PixelBuffer.java b/src/org/xwt/PixelBuffer.java index 3d6e09a..065b027 100644 --- a/src/org/xwt/PixelBuffer.java +++ b/src/org/xwt/PixelBuffer.java @@ -3,8 +3,7 @@ package org.xwt; /** *

- * A block of pixels which can be drawn on and rapidly copied to the - * screen. + * A block of pixels which can be drawn on. *

* *

@@ -13,19 +12,80 @@ package org.xwt; * method. These implementations may choose to use off-screen video * ram for this purpose (for example, a Pixmap on X11). *

+ * + *

+ * Many of these functions come in pairs, one that uses ints and one + * that uses floats. The int functions are intended for situations + * in which the CTM is the identity transform. + *

*/ public abstract class PixelBuffer { - /** Draw the region of source within s onto the region d on this PixelBuffer, scaling as needed */ - public abstract void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2); - - /** Fill the region (x1, y1, x2, y2) with color (AARRGGBB format); the alpha channel component is ignored */ - public abstract void fillRect(int x1, int y1, int x2, int y2, int color); - + // FIXME: try to remove /** returns the height of the PixelBuffer */ public abstract int getHeight(); + // FIXME: try to remove /** returns the width of the PixelBuffer */ public abstract int getWidth(); + + + /** Draw the region of source within s onto the region d on this PixelBuffer, scaling as needed */ + public abstract void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2); + + /** fill a trapezoid whose top and bottom edges are horizontal */ + public abstract void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color); + + // FIXME: we want floats (inter-pixel spacing) for antialiasing, but this hoses the fastpath line drawing... argh! + /** draws a line of width w; note that the coordinates here are post-transform */ + public void drawLine(int x1, int y1, int x2, int y2, int w, int color, boolean capped) { + + if (y1 > y2) { int t = x1; x1 = x2; x2 = t; t = y1; y1 = y2; y2 = t; } + + if (x1 == x2) { + fillTrapezoid(x1 - w / 2, x2 + w / 2, y1 - (capped ? w / 2 : 0), x1 - w / 2, x2 + w / 2, y2 + (capped ? w / 2 : 0), color); + return; + } + + // fastpath for single-pixel width lines + if (w == 1) { + float slope = (float)(y2 - y1) / (float)(x2 - x1); + int last_x = x1; + for(int y=y1; y<=y2; y++) { + int new_x = (int)((float)(y - y1) / slope) + x1; + if (slope >= 0) fillTrapezoid(last_x + 1, y != y2 ? new_x + 1 : new_x, y, + last_x + 1, y != y2 ? new_x + 1 : new_x, y + 1, color); + else fillTrapezoid(y != y2 ? new_x : new_x + 1, last_x, y, + y != y2 ? new_x : new_x + 1, last_x, y + 1, color); + last_x = new_x; + } + return; + } + + // actually half-width + float width = (float)w / 2; + float phi = (float)Math.atan((y2 - y1) / (x2 - x1)); + if (phi < 0.0) phi += (float)Math.PI * 2; + float theta = (float)Math.PI / 2 - phi; + + // dx and dy are the x and y distance between each endpoint and the corner of the stroke + int dx = (int)(width * Math.cos(theta)); + int dy = (int)(width * Math.sin(theta)); + + // slice is the longest possible length of a horizontal line across the stroke + int slice = (int)(2 * width / Math.cos(theta)); + + if (capped) { + x1 -= width * Math.cos(phi); + x2 += width * Math.cos(phi); + y1 -= width * Math.sin(phi); + y2 += width * Math.sin(phi); + } + + fillTrapezoid(x1 + dx, x1 + dx, y1 - dy, x1 - dx, x1 - dx + slice, y1 + dy, color); // top corner + fillTrapezoid(x2 + dx - slice, x2 + dx, y2 - dy, x2 - dx, x2 - dx, y2 + dy, color); // bottom corner + fillTrapezoid(x1 - dx, x1 - dx + slice, y1 + dy, x2 + dx - slice, x2 + dx, y2 - dy, color); // middle + } + } diff --git a/src/org/xwt/Res.java b/src/org/xwt/Res.java index 37cdc29..1f1a927 100644 --- a/src/org/xwt/Res.java +++ b/src/org/xwt/Res.java @@ -6,6 +6,7 @@ import java.util.*; import java.util.zip.*; import org.xwt.js.*; import org.xwt.util.*; +import org.bouncycastle.util.encoders.Base64; /** base class for XWT resources */ public abstract class Res extends JS { @@ -52,6 +53,7 @@ public abstract class Res extends JS { if (url.startsWith("https://")) return new HTTP(url); if (url.startsWith("file:") && permitLocalFilesystem) return new File(url.substring(5)); if (url.startsWith("cab:")) return new CAB(stringToRes(url.substring(4))); + if (url.startsWith("data:")) return new ByteArray(Base64.decode(url.substring(5))); throw new JS.Exn("invalid resource specifier " + url); } diff --git a/src/org/xwt/Surface.java b/src/org/xwt/Surface.java index 8b888e7..6ed2377 100644 --- a/src/org/xwt/Surface.java +++ b/src/org/xwt/Surface.java @@ -361,14 +361,15 @@ public abstract class Surface extends PixelBuffer { screenDirtyRegions.dirty(dx1, dy1, dx2 - dx1, dy2 - dy1); backbuffer.drawPicture(source, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2); } - public void fillRect(int x1, int y1, int x2, int y2, int color) { - screenDirtyRegions.dirty(x1, y1, x2 - x1, y2 - y1); - backbuffer.fillRect(x1, y1, x2, y2, color); } + public void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color) { + screenDirtyRegions.dirty(Math.min(x1, x3), y1, Math.max(x2, x4) - Math.min(x1, x3), y2 - y1); + backbuffer.fillTrapezoid(x1, x2, y1, x3, x4, y2, color); } public void render() { super.render(); render_(); } + public void render_() { int[][] dirt = screenDirtyRegions.flush(); for(int i = 0; dirt != null && i < dirt.length; i++) { diff --git a/src/org/xwt/VectorGraphics.java b/src/org/xwt/VectorGraphics.java index cd62d89..fc19ba4 100644 --- a/src/org/xwt/VectorGraphics.java +++ b/src/org/xwt/VectorGraphics.java @@ -3,23 +3,18 @@ package org.xwt; import org.xwt.util.*; import java.util.*; -// NOTE: we have to make sure that a box is never smaller than the -// bounding box of its path unless the user specifically forced -// it to be so. - +// FIXME: offer a "subpixel" mode where we pass floats to the Platform and don't do any snapping // FIXME: fracture when realizing instead of when parsing? + /* v1.0 - - Base64 "data:" URL support - textpath - gradients - patterns - clipping/masking - filters (filtering of a group must be performed AFTER the group is assembled; sep. canvas) - - style sheets v1.1 - - markers (do this in the parser; remember the order: fill, stroke, marker) - bump caps [requires Paint that can fill circles...] [remember to distinguish between closed/unclosed] - line joins - mitre (hard) @@ -31,16 +26,18 @@ import java.util.*; - clip on trapezoids, not pixels - faster gradients and patterns: - transform each corner of the trapezoid and then interpolate - - */ +*/ /** XWT's fully conformant Static SVG Viewer; see SVG spec, section G.7 */ public final class VectorGraphics { - /* - // Public entry points ///////////////////////////////////////////////////////////////// - // FIXME - public static SVG.Font currentFont = null; + // Private Constants /////////////////////////////////////////////////////////////////// + + private static final int DEFAULT_PATHLEN = 1000; + private static final float PI = (float)Math.PI; + + + // Public entry points ///////////////////////////////////////////////////////////////// public static VectorPath parseVectorPath(String s) { PathTokenizer t = new PathTokenizer(s); @@ -59,272 +56,10 @@ public final class VectorGraphics { return ret; } - public static void parseNode(String name, String[] keys, Object[] vals, Template t) { - Hash h = new Hash(); - for(int i=0; iname property * / - Hashtable glyphByName = new Hashtable(); - - / ** linked list of glyphs, stored by the first character of their unicode property * / - Hashtable glyphByUnicode = new Hashtable(); - - / ** a Glyph in an SVG font * / - public static class Glyph { - - // FIXME: lang attribute - boolean isVerticallyOriented = false; - Template t = null; - Box b = null; - - float horiz_adv_x = 0; - float vert_origin_x = 0; - float vert_origin_y = 0; - float vert_adv_y = 0; - - String unicode = null; - - / ** forms the linked list in glyphByUnicode; glyphs appear in the order specified in the font * / - public Glyph next = null; - - Glyph(String name, String unicode, Template t, SVG.Font f) { - if (unicode != null) - if (f.glyphByUnicode.get(unicode.substring(0, 1)) == null) { - f.glyphByUnicode.put(unicode.substring(0, 1), this); - } else { - Glyph g; - for(g = (Glyph)f.glyphByUnicode.get(unicode.substring(0, 1)); g.next != null; g = g.next); - g.next = this; - } - if (name != null) f.glyphByUnicode.put(name, this); - this.unicode = unicode; - this.t = t; - horiz_adv_x = f.horiz_adv_x; - vert_origin_x = f.vert_origin_x; - vert_origin_y = f.vert_origin_y; - vert_adv_y = f.vert_adv_y; - } - public void render(DoubleBuffer buf, int x, int y, int fillcolor, int strokecolor, float scaleFactor) { - // FEATURE: make b double-buffered for increased performance - if (b == null) { - b = new Box(t, new org.xwt.util.Vec(), new org.xwt.util.Vec(), null, 0, 0); - b.put("absolute", Boolean.TRUE); - b.prerender(); - t = null; - } - // FIXME - b.put("width", new Integer(1000)); - b.put("height", new Integer(1000)); - b.fillcolor = fillcolor; - b.strokecolor = strokecolor; - - // we toss an extra flip on the ctm so that fonts stick "up" instead of down - b.render(0, 0, buf.getWidth(), buf.getHeight(), buf, - Affine.flip(false, true).multiply(Affine.scale(scaleFactor, scaleFactor).multiply(Affine.translate(x, y))).multiply(buf.a)); - } - } - } // Affine ////////////////////////////////////////////////////////////////////////////// - / ** an affine transform; all operations are destructive * / + /** an affine transform; all operations are destructive */ public static final class Affine { // [ a b e ] @@ -350,7 +85,7 @@ public final class VectorGraphics { return new Affine(c, s, -s, c, 0, 0); } - / ** this = this * a * / + /** this = this * a */ public Affine multiply(Affine A) { float _a = this.a * A.a + this.b * A.c; float _b = this.a * A.b + this.b * A.d; @@ -408,7 +143,7 @@ public final class VectorGraphics { char c = s.charAt(i); if (Character.isWhitespace(c) || c == ',' || (c == '-' && i != start)) break; if (!((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == '-')) { - if (c == '%') { // FIXME + if (c == '%') { // FIXME } else if (s.regionMatches(i, "pt", 0, i+2)) { // FIXME } else if (s.regionMatches(i, "em", 0, i+2)) { // FIXME } else if (s.regionMatches(i, "pc", 0, i+2)) { // FIXME @@ -429,7 +164,7 @@ public final class VectorGraphics { // Abstract Path ////////////////////////////////////////////////////////////////////////////// - / ** an abstract path; may contain splines and arcs * / + /** an abstract path; may contain splines and arcs */ public static class VectorPath { // the number of vertices on this path @@ -456,7 +191,7 @@ public final class VectorGraphics { static final byte TYPE_CUBIC = 3; static final byte TYPE_QUADRADIC = 4; - / ** Creates a concrete vector path transformed through the given matrix. * / + /** Creates a concrete vector path transformed through the given matrix. */ public RasterPath realize(Affine a) { RasterPath ret = new RasterPath(); @@ -678,7 +413,8 @@ public final class VectorGraphics { default: // FIXME } - / * + + /* // invariant: after this loop, no two lines intersect other than at a vertex // FIXME: cleanup int index = numvertices - 2; @@ -709,7 +445,7 @@ public final class VectorGraphics { } } } - * / + */ } } @@ -717,9 +453,9 @@ public final class VectorGraphics { - // Concrete Vector Path ////////////////////////////////////////////////////////////////////////////// + // Rasterized Vector Path ////////////////////////////////////////////////////////////////////////////// - / ** a vector path * / + /** a vector path */ public static class RasterPath { // the vertices of this path @@ -727,18 +463,15 @@ public final class VectorGraphics { int[] y = new int[DEFAULT_PATHLEN]; int numvertices = 0; - / ** - * A list of the vertices on this path which *start* an edge (rather than a moveto), sorted by increasing y. + /** + * A list of the vertices on this path which *start* an *edge* (rather than a moveto), sorted by increasing y. * example: x[edges[1]],y[edges[1]] - x[edges[i]+1],y[edges[i]+1] is the second-topmost edge * note that if x[i],y[i] - x[i+1],y[i+1] is a MOVETO, then no element in edges will be equal to i - * / + */ int[] edges = new int[DEFAULT_PATHLEN]; int numedges = 0; - / ** FIXME: if a path is closed "manually" you get caps on the ends; otherwise you get a marker... * / - boolean closed = false; - - / ** simple quicksort, from http://sourceforge.net/snippet/detail.php?type=snippet&id=100240 * / + /** simple quicksort, from http://sourceforge.net/snippet/detail.php?type=snippet&id=100240 */ int sort(int left, int right, boolean partition) { if (partition) { int i, j, middle; @@ -761,7 +494,7 @@ public final class VectorGraphics { } } - / ** finds the x value at which the line intercepts the line y=_y * / + /** finds the x value at which the line intercepts the line y=_y */ private int intercept(int i, float _y, boolean includeTop, boolean includeBottom) { if (includeTop ? (_y < Math.min(y[i], y[i+1])) : (_y <= Math.min(y[i], y[i+1]))) return Integer.MIN_VALUE; @@ -771,8 +504,8 @@ public final class VectorGraphics { ((float)(y[i + 1] - y[i])) ) * ((float)(_y - y[i])) + x[i]); } - / ** fill the interior of the path * / - public void fill(DoubleBuffer buf, RasterPath pen, Paint paint) { + /** fill the interior of the path */ + public void fill(PixelBuffer buf, Paint paint) { if (numedges == 0) return; int y0 = y[edges[0]], y1 = y0; boolean useEvenOdd = false; @@ -822,79 +555,91 @@ public final class VectorGraphics { } } - / ** stroke the outline of the path * / - public void stroke(DoubleBuffer buf, int width, int color, boolean mitre, - String dashArray, int dashOffset, float segLength) { - if (dashArray != null) { - float ratio = 1; - if (segLength > 0) { - float actualLength = 0; - for(int i=0; i 0) { + float actualLength = 0; for(int i=0; i> 16, (argb & 0x0000FF00) >> 8, (argb & 0x000000FF))); - g.fillRect(x, y, x2 - x, y2 - y); } + // FIXME: try to use os acceleration + public void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int argb) { + g.setColor(new Color((argb & 0x00FF0000) >> 16, (argb & 0x0000FF00) >> 8, (argb & 0x000000FF))); + if (x1 == x3 && x2 == x4) { + g.fillRect(x1, y1, x4 - x1, y2 - y1); + } else for(int y=y1; y _x2) { int _x0 = _x1; _x1 = _x2; _x2 = _x0; } + g.fillRect(_x1, _y1, _x2 - _x1, _y2 - _y1); + } + } } -- 1.7.10.4