[dojo-contributors] Draft: Unified 2D graphics subsystem for Dojo

Tom Trenka ttrenka at gmail.com
Mon Jun 5 11:36:22 EDT 2006


I'm sorry I haven't commented on this yet, I'm still looking it over.  First
impressions: it seems to be ok but it doesn't really seem to allow for
attaching any kind of events to individual rendering definitions directly
(just like Canvas doesn't)...at least at first run-through.

I'll look it over well today and post more commentary.  I do agree with the
Canvas emulation approach (although I would consider going back to OpenGL
and seeing if there's more we can do) but I would definitely like to have a
better definition of why this is being proposed; what problems does it solve
and how?  This was my initial issue with coming up with a vector API; it
seemed like I was working on a solution that had no (well-defined) problem.

Tom

On 5/30/06, Eugene Lazutkin <eugene at lazutkin.com> wrote:
>
> Better late than never. ;-)
>
> Unified 2D graphics subsystem for Dojo
> ======================================
>
> A realistic graphics subsystem should implement 6 major components:
>   * Graphics Context – a container for graphics, which implements
> drawing API.
>   * Shapes – a set of basic geometric shapes. The most basic shape is a
> path. One important concept is a group of shapes.
>   * Fill – a way to fill in a shape. The most basic fill is a solid
> color fill. A pattern is a more generic fill. Linear and radial
> gradients are useful too.
>   * Stroke – a way to stroke a shape. The most basic stroke should
> accept a color and a line width. Fancier styles include a pattern-based
> stroke, a line cap style, and line join style.
>   * Transformations – a way to transform shape's geometry before actual
> drawing.
>
> Fill, stroke, and transformation are separated as concepts because they
> can be applied to different shapes, or reused in some other way. This is
> a common scenario for many drawings.
>
> Advanced facilities include:
>   * Image operations – image-specific interpretation, composition,
> transformations, filters, and so on.
>   * Text operations. For example, generation of paths with a text
> string, selection of a font.
>   * Clipping – using shapes to clip drawings.
>   * Additional adornments like automatic shadows, and such.
>
> Current state of affairs
> ========================
>
> At this moment we have 3 vector-based APIs on the market: SVG, VML,
> Canvas. SVG is supported natively by FF1.5 and Opera9, VML is supported
> natively by IE, Canvas is supported natively by FF1.5, Opera9, and
> Safari2.0.
>
> SVG and VML define a markup language, while Canvas is provided as a
> JavaScript API + a canvas tag. SVG and VML expose much more than a way
> to draw pretty pictures: created objects can be modified on the fly,
> events (like, onclick) can be attached to provide easy interactivity.
>
> The idea is to define a generic API, which can be effectively
> implemented in a renderer in terms of native APIs. This API can be used
> by graphics widgets written in generic manner, which can be used in any
> supported environment transparently. Clearly such API is going to be
> simpler than underlying APIs. GCD functionality can be implemented
> directly by all renderers. More advanced functionality can be
> implemented using fallbacks.
>
> Additional requirement is to come up with a markup, which can be used
> inline independently of used renderer.
>
> Given the current situation following functionality can be implemented
> more or less uniformly across existing graphics APIs: graphics context
> (surface), shapes (path, rectangle, polyline, ellipse, arcs, bezier),
> fill (solid color, linear gradient, image pattern), stroke (color, line
> width, line caps, line joins), transformation (translate, rotate,
> scale), image primitive, state stack manipulations (save, restore).
> State includes current values of transformation, fill style, and stroke
> style.
>
> Proposed API
> ============
>
> Everything goes into a new package dojo.gfx (we can debate name later,
> e.g., it can be placed in the existing dojo.graphics).
> By necessity the API is heavily influenced by Canvas API --- the most
> basic one.
>
> Global package-level functions
> ------------------------------
>
> dojo.gfx.createSurface(parentNode, width, height, [renderer])
>
> It creates a new surface for a picture. It corresponds to <svg> element
> of SVG, <v:g> element of VML, and <canvas> element of Canvas. Width and
> height are in pixels. Parent node specifies a parent for newly created
> surface (there are several ways to create an element, e.g., before or
> after given node --- we can address them later). Optional renderer is a
> text string, which identifies a renderer: "svg", "vml", or "canvas". It
> is used to specify a renderer in an environment, where more than one
> renderer is available. If it is unspecified, the preferred/default
> renderer is used. createSurface returns a surface object or null, if
> cannot create a surface.
>
> dojo.gfx.attachSurface(node)
>
> It works just like createSurface but instead of creating new surface it
> attaches to existing element.
>
> Surface object
> --------------
>
> Note: this API follows (more or less) the Canvas API --- the most
> restrictive one.
>
> Path
> ++++
>
> surface.beginPath()
>
> Starts the path, returns nothing.
>
> surface.moveTo(x, y)
>
> Moves the current point of the path starting a new subpath. Returns
> nothing.
>
> surface.LineTo(x, y)
>
> Adds the point to the path connecting the last point of the path with
> new point using a straight line. Returns nothing.
>
> surface.arcTo(x1, y1, x2, y2, radius)
>
> Adds two points to the path. (x1, y1) is connected with the last point
> using a straight line, if they are different. (x2, y2) is connected with
> (x1, y1) with an arc. Returns nothing.
>
> surface.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
>
> Adds (x, y) connecting it to the last point with a cubic bezier curve.
> Returns nothing.
>
> surface.closePath()
>
> Closes the last subpath with a straight line. Returns nothing.
>
> surface.endPath()
>
> Ends the path and sets it as the current path. Returns a shape object,
> or null (for Canvas). A shape object can be used for later
> modifications, or event processing.
>
> Path (convenient)
> +++++++++++++++++
>
> surface.createRect(x, y, width, height)
>
> This is a logical equivalent of (pseudo code):
>
> function surface.createRect(x, y, width, height) {
>      this.moveTo(x, y);
>      this.lineTo(x + width, y);
>      this.lineTo(x + width, y + height);
>      this.lineTo(x, y + height);
>      this.lineTo(x, y);
> }
>
> surface.createEllipse(cx, cy, rx, [ry])
>
> Just like createRect it creates an ellipse. If ry is not specified, it
> creates a circle at (cx, cy) with rx radius. Returns nothing.
>
> surface.createPoly(points)
>
> Just like createRect it creates a polyline/polygon object. Returns
> nothing.
>
> Image
> +++++
>
> surface.createImage(url, dx, dy [x, y, width, height])
>
> Creates an image object from URL. Its top-left corner is placed at (dx,
> dy). Optional x, y, width, and height are used to select a subimage.
>
> Fills
> +++++
>
> surface.createLinearGradient(x1, y1, x2, y2)
>
> Returns a gradient object from (x1, y1) to (x2, y2) , which can be used
> as a fill style.
>
> surface.createPattern(url, repetition)
>
> Returns a (tiled) image pattern object, which can be used as a fill
> style. Repetition can be one of "repeat", or "no-repeat".
>
> surface.setFill(fill)
>
> Sets the current fill style using a color or a fill object. Returns
> nothing.
>
> surface.fill()
>
> Fills the current shape with the current fill. Returns nothing.
>
> Strokes
> +++++++
>
> surface.createStroke(width, cap, join, miter)
>
> Return a stroke object. Cap can be one of "butt", "round", or "square".
> Join can be one of "round", "bevel", or a numeric miter value.
>
> surface.setStroke(stroke)
>
> Sets the current stroke style using a color or a stroke object. Returns
> nothing.
>
> surface.stroke()
>
> Strokes the current path. Returns nothing.
>
> Transformations
> +++++++++++++++
>
> surface.translate(dx, dy)
>
> Applies a translation transformation to the current matrix. Effectively
> shifts all coordinates by (dx, dy). Returns nothing.
>
> surface.scale(sx, sy)
>
> Applies a scaling transformation to the current matrix. Effectively
> multiplies all x coordinates by sx, and all y coordinates by sy. Returns
> nothing.
>
> surface.rotate(angle)
>
> Applies a rotation transformation to the current matrix. Effectively
> rotates all coordinates around (0, 0) by "angle" radians.
>
> State
> +++++
>
> surface.save()
>
> Pushes the current state (transformation, fill style, and stroke style)
> on the state stack. Returns nothing.
>
> surface.restore()
>
> Pops the current state (transformation, fill style, and stroke style)
> from the state stack. Returns nothing.
>
> Shape object
> ------------
>
> See notes below.
>
> Gradient object
> ---------------
>
> addColorStop(offset, color)
>
> Adds a color point with specified offset (0.0-1.0). Returns nothing.
>
> Pattern object
> --------------
>
> No methods.
>
> Stroke object
> -------------
>
> No methods.
>
> Discussion
> ==========
>
> Random notes
> ------------
>
> The idea was to come up with an API to solve simple yet frequent
> problems like charting, simple mapping, and simple art. If somebody
> desires to use SVG-specific stuff (e.g., builtin animation), or
> VML-specific stuff (e.g., formulas) please use the existing mechanism to
> create different versions of your widget.
>
> I decided to use the term "surface" for now. Mainly because "canvas" and
> "context" are overused already. BTW, Cairo (FF's rendering engine) uses
> this term.
>
> Canvas restricted a lot of things --- that was the reason to use its API
> as a model. For example it dictated save/restore state pattern.
>
> VML has a pitiful support for radial gradient, which differs a lot from
> SVG/Canvas --- I decided not to support it for now. VML doesn't support
> more than 2 color stops. In most cases it is enough. Another VML
> deficiency is its pattern support --- only two styles are allowed. VML
> doesn't support path clipping --- it is not there either.
>
> Alpha and z-position support should be addressed at some point.
>
> It makes sense to implement some frequently used convenient
> transformation methods: rect-to-rect transformation, scale around a
> specified point, show an object, show a point, center. All of them can
> be implemented in renderer-independent way using existing basic methods.
>
> Text support is not there mainly because it is not implemented in
> Canvas. In any case it is a difficult topic. I suggest to solve it on
> HTML level in specific widgets. Anyway unless user wants to do a "word
> art" it rarely makes sense to use distorted or decorated text. The only
> exception I know of is a simple text rotated by pi/2 angle. This one is
> truly useful for legends/labels.
>
> I expect that beginPath()/endPath() will be implemented using <group> in
> VML, and <g> in SVG.
>
> It may be possible to implement a Flash-based renderer. I don't know for
> sure because my knowledge of Flash is limited.
>
> DOM support
> -----------
>
> Both VML and SVG support true objects, which can be used for event
> processing and/or animation. For example, it is possible to process
> onclick, change a position, or modify the fill style. In order to
> support the event processing, we can return raw objects for that. If we
> want to support an animation of some kind, we have to return some proxy
> objects, which will normalize its API.
>
> There are two ways to implement the Canvas renderer (surface):
>
> 1) Simple implementation: all shapes are stroked/filled immediately, no
> objects are returned from endPath(), which will be an empty method. I
> tried to make sure that the API supports this scenario.
>
> 2) More elaborate implementation: every time a proxy shape object is
> created, it is saved in an internal list, and returned. Proxy shape
> object will have all necessary geometry descriptions. If user modifies
> it using standard API, the whole list is re-rendered reflecting the
> change. Obviously either delayed regeneration is used, or a special
> surface method should be called, when user finished modifying shapes. It
> is theoretically possible to use the same list to analyze and
> reverse-project mouse position to proper shapes thus simulating mouse
> events.
>
> Unified markup language
> -----------------------
>
> It can be used to define an art, e.g., logos, or decorative elements of
> charts. I didn't include it here because it will depend on the API,
> which is going to interpret it. There are two possibilities: we can use
> some simplified subset of SVG (possibly augmented), or we can use our
> own markup, which closely corresponds to the API and can be interpreted
> consequently. Both approaches have obvious positive and negative sides.
> I need your input.
>
> That's all folks!
> =================
>
> Thoughts? Criticism? Improvements? Other ideas?
>
> Thanks,
>
> Eugene
>
> PS: Yes, I implemented renderers like that before (in several commercial
> systems) but not in js.
>
> _______________________________________________
> dojo-contributors mailing list
> dojo-contributors at dojotoolkit.org
> http://dojotoolkit.org/mailman/listinfo/dojo-contributors
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20060605/6d438d4e/attachment.htm 


More information about the dojo-contributors mailing list