/*
* Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package javax.microedition.m2g;
import com.sun.j2me.proxy.lcdui.Graphics;
import com.sun.perseus.model.DocumentNode;
import com.sun.perseus.j2d.RGB;
import com.sun.perseus.j2d.RenderGraphics;
import com.sun.perseus.j2d.Tile;
import com.sun.pisces.GraphicsSurface;
import com.sun.pisces.NativeSurface;
import com.sun.pisces.PiscesRenderer;
/**
* This is the fundamental class for 2D rendering. The ScalableGraphics context
* class provides and handles all the rendering capability within this package.
* In other words, the rendering can only be achieved through the render methods
* provided in this class. Note that the ScalableGraphics instance must be bound
* to the rendering target prior to calling the render method. The
* implementation must clip to the viewport boundaries.
*/
public class ScalableGraphics {
/**
* Paint used to clear screens.
*/
static final RGB CLEAR_PAINT = new RGB(0, 0, 0, 0);
/**
* The Graphics object bound to this class
*/
Object g = null;
Graphics graphics = null;
/**
* The current quality mode.
*/
int qualityMode = RENDERING_QUALITY_HIGH;
/**
* The current transparency for rendering images.
*/
float alpha = 1f;
/**
* Adapter for Graphics to be used in PiscesRenderer as a Surface.
*/
GraphicsSurface gs;
/**
* The PiscesRenderer associated with the screen.
*/
PiscesRenderer pr;
/**
* The RenderGraphics used to draw to the PiscesRenderer
*/
RenderGraphics rg;
/**
* The offscreen native surface used for drawing with alpha < 1.
*/
NativeSurface offscreenNS;
RenderGraphics offscreenRG;
/**
* The Tile to which rendering is clipped.
*/
final Tile clipTile = new Tile();
/**
* Defines a low rendering quality level.
*/
public static final int RENDERING_QUALITY_LOW = 1;
/**
* Defines a high rendering quality level.
*/
public static final int RENDERING_QUALITY_HIGH = 2;
/**
* Constructor
*/
private ScalableGraphics() {
}
/**
*
* Bind the given Graphics as the rendering target of this ScalableGraphics
* context. The type of the Graphics object depends on the Java profile that
* this specification is implemented on, as follows:
*
* <ul>
* <li> javax.microedition.lcdui.Graphics on profiles supporting LCDUI;</li>
* <li> java.awt.Graphics on profiles supporting AWT;</li>
* <li> either of the above on profiles supporting both AWT and LCDUI.</li>
* </ul>
* @param target the object (Graphics) to receive the rendered image.
*
* @throws NullPointerException if <code>target</code> is null.
* @throws IllegalArgumentException if <code>target</code> is invalid.
* @throws IllegalStateException if <code>target</code> is already bound.
*/
public void bindTarget(java.lang.Object target) {
if (target == null) {
throw new NullPointerException();
}
if (!("javax.microedition.lcdui.Graphics".equals (target.getClass().getName ()))) {
throw new IllegalArgumentException("target is not instance of Graphics");
}
if (g != null) {
throw new IllegalStateException("bindTarget(" + target + ") with g : " + g);
}
g = target;
graphics = (Graphics)Graphics.__getInstance(target); // compile time
if (rg == null) {
gs = new GraphicsSurface();
pr = new PiscesRenderer(gs);
/*
* we want to reuse RenderGraphics for multiple target
* widths / heights, so we use the maximum values
*/
rg = new RenderGraphics(pr, Short.MAX_VALUE, Short.MAX_VALUE);
}
gs.bindTarget(g);
}
/**
*
* Flushes the rendered ScalableImage to the currently bound target and then
* releases the target. This ensures that the ScalableImage is actually made
* visible on the target that was set in bindTarget. Otherwise, the image
* may or may not become visible.
*
* @throws IllegalStateException if <code>target</code> is not bound.
*/
public void releaseTarget() {
if (g == null) {
throw new IllegalStateException("releaseTarget() with null current target");
}
/* allow g to be garbage collected */
g = null;
graphics = null;
gs.releaseTarget();
}
/**
* Renders the specified ScalableImage using the supplied anchor point. The
* anchor point given is relative to the upper left corner of the rendering
* surface. It is important to note that the content is made visible or
* flushed to the display only after a call is made to
* <code>releaseTarget</code>.
*
* @param x the X coordinate of the anchor point, in pixels.
* @param y the Y coordinate of the anchor point, in pixels.
* @param image the ScalableImage to be rendered.
*
* @throws NullPointerException if <code>image</code> is null.
* @throws IllegalStateException if <code>target</code> is not bound.
*
* @see #releaseTarget
*/
public void render(int x, int y, ScalableImage image) {
if (image == null) {
throw new NullPointerException();
}
if (graphics == null) {
throw new IllegalStateException();
}
DocumentNode documentNode =
(DocumentNode) ((SVGImage) image).getDocument();
int vpw = image.getViewportWidth();
int vph = image.getViewportHeight();
int clipX = graphics.getClipX();
int clipY = graphics.getClipY();
int clipW = graphics.getClipWidth();
int clipH = graphics.getClipHeight();
int translateX = 0;
int translateY = 0;
int intersectX2, intersectX = x;
int intersectY2, intersectY = y;
// calculate intersection of clip and viewport rectangles
intersectX2 = intersectX + vpw;
if ((intersectX2 < intersectX) || (intersectX2 > (clipX + clipW))) {
// (intersectX2 < x) = overflow test
intersectX2 = clipX + clipW;
}
intersectY2 = intersectY + vph;
if ((intersectY2 < intersectY) || (intersectY2 > (clipY + clipH))) {
// (intersectY2 < y) = overflow test
intersectY2 = clipY + clipH;
}
if (intersectX < clipX) {
translateX = intersectX - clipX;
intersectX = clipX;
}
if (intersectY < clipY) {
translateY = intersectY - clipY;
intersectY = clipY;
}
int intersectW = intersectX2 - intersectX;
int intersectH = intersectY2 - intersectY;
if ((intersectW <= 0) || (intersectH <= 0)) {
// empty intersection
return;
}
// intersection rectangle = (intersectX, intersectY, intersectW,
// intersectH)
rg.setRenderingQuality(qualityMode == RENDERING_QUALITY_HIGH);
documentNode.sample(documentNode.getCurrentTime());
documentNode.applyAnimations();
clipTile.setTile(intersectX + graphics.getTranslateX(), intersectY +
graphics.getTranslateY(), intersectW, intersectH);
// reset rg
rg.setTransform(null);
// finally paint
if (alpha != 0.0 && alpha < 1.0) {
if (offscreenNS == null ||
offscreenNS.getWidth() != intersectW ||
offscreenNS.getHeight() != intersectH) {
offscreenNS = new NativeSurface(intersectW, intersectH);
offscreenRG = new RenderGraphics(new PiscesRenderer(offscreenNS),
intersectW,
intersectH);
} else {
offscreenNS.clean();
}
offscreenRG.translate(translateX, translateY);
documentNode.paint(offscreenRG);
offscreenNS.draw(g, intersectX, intersectY, intersectW,
intersectH, alpha);
} else if (alpha == 1.0){
int renderX = x + graphics.getTranslateX();
int renderY = y + graphics.getTranslateY();
gs.bindTarget(g); // IMPL note: it is already bound, isn't it? (bindTarget was called, otherwise graphics==null and we could not get here)
rg.translate(renderX, renderY);
rg.setRenderingTile(clipTile);
documentNode.paint(rg);
gs.releaseTarget(); // IMPL note: why release?
}
}
/**
* Set the quality of rendering in the ScalableGraphics context. It can take
* one of the values, RENDERING_QUALITY_LOW or RENDERING_QUALITY_HIGH.
* Default=RENDERING_QUALITY_HIGH. The implementation of these quality
* levels is implementation dependent and should be mapped to definitions in
* SVG spec (shape, text, image and color rendering).
*
* @param mode this value indicates the quality of rendering required.
*
* @throws IllegalArgumentException if the <code>mode</code> is invalid.
*/
public void setRenderingQuality(int mode) {
if (mode != RENDERING_QUALITY_LOW && mode != RENDERING_QUALITY_HIGH) {
throw new IllegalArgumentException("" + mode);
}
this.qualityMode = mode;
}
/**
* Set the transparency in the ScalableGraphics context with the supplied
* alpha value. Alpha value must be a floating point number in the range
* [0.0, 1.0]. The source pixels are always combined with destination pixels
* using the <i>Source Over Destination</i> rule [Porter-Duff]. In this
* context, the Source Over Destination rule has the following properties:
* a fully opaque pixel in the source must replace the destination pixel, a
* fully transparent pixel in the source must leave the destination pixel
* unchanged, and a semitransparent pixel in the source must be alpha
* blended with the destination pixel using the supplied value. The default
* alpha value is 1.0 (fully opaque), when not specified.
*
* @param alpha the constant alpha value to be used for rendering.
*
* @throws IllegalArgumentException if <code>alpha</code> is out of range.
*/
public void setTransparency(float alpha) {
if (alpha < 0f || alpha > 1f) {
throw new IllegalArgumentException();
}
this.alpha = alpha;
}
/**
* Retrieve a new instance of ScalableGraphics that can be associated to
* an application.
* <p>
*
* @return the newly created <code>ScalableGraphics</code> instance.
*
*/
public static ScalableGraphics createInstance() {
return new ScalableGraphics();
}
}