javauis/lcdui_qt/src/javax/microedition/lcdui/Buffer.java
changeset 26 dc7c549001d5
child 35 85266cc22c7f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/lcdui_qt/src/javax/microedition/lcdui/Buffer.java	Thu May 27 12:49:31 2010 +0300
@@ -0,0 +1,662 @@
+/*
+* Copyright (c) 2009,2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+package javax.microedition.lcdui;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.internal.qt.graphics.GraphicsContext;
+import org.eclipse.swt.internal.qt.graphics.JavaCommandBuffer;
+import org.eclipse.swt.internal.qt.graphics.WindowSurface;
+
+/**
+ * This class works a proxy between instances of Graphics class and common graphics
+ * (command buffer and GraphicsContext). The relation of this class to Graphics is one-to-many, i.e.
+ * one GraphicsBuffer instance handles multiple Graphics instances.
+ *
+ * Each LCDUI API class instance providing access to Graphics (Image, Canvas, GameCanvas, CustomItem),
+ * must instantiate one GraphicsBuffer and use it for creating instances of Graphics class.
+ *
+ * The implementation is not thread safe, thus all serialization must be implemented in
+ * callers, for instance the Graphics implementation serializes all calls to this class.
+ * Furthermore this class does not take care of switching to UI thread, it must be done by
+ * caller object.
+ */
+
+abstract class Buffer
+{
+
+    // Constants for buffer host types
+    final static int HOST_TYPE_IMAGE = 1;
+    final static int HOST_TYPE_CANVAS = 2;
+    final static int HOST_TYPE_CUSTOMITEM = 3;
+
+    // Flags for raising settings validation
+    private final static int NONE            = 0;
+    private final static int COLOR           = 1;
+    private final static int CLIP            = 2;
+    private final static int FONT            = 4;
+    private final static int STROKESTYLE     = 8;
+    private final static int COORS_TRANSLATION = 16;
+
+    // Graphics settings active in buffer
+    // all values are comparable to those
+    // in Graphics except font which is stored
+    // as handle instead of LCDUI Font
+    private int bufferFontHandle;
+    private int bufferColor;
+    private int bufferStrokeStyle;
+    private int bufferTranslateX;
+    private int bufferTranslateY;
+    private Rectangle bufferClip;
+
+    private GraphicsContext gc;
+    private JavaCommandBuffer commandBuffer;
+    private Rectangle hostBounds;
+    private Graphics currentClient;
+    private boolean started;
+    private int clientCount;
+
+    // The target window surface where this
+    // instance flushes draw primitives
+    WindowSurface windowSurface;
+
+    /**
+     * Constructor
+     */
+    protected Buffer()
+    {
+        hostBounds = new Rectangle(0,0,0,0);
+        bufferClip = new Rectangle(0,0,0,0);
+        bufferFontHandle = 0;
+        bufferColor = 0xff000000;
+        bufferStrokeStyle = Graphics.SOLID;
+        bufferTranslateX = 0;
+        bufferTranslateY = 0;
+    }
+
+    /**
+     * Initializes data, called once
+     */
+    void init()
+    {
+        clientCount = 0;
+        gc = new GraphicsContext();
+        commandBuffer = new JavaCommandBuffer();
+        gc.bindTarget(commandBuffer);
+        writeControlBoundsToBuffer(false);
+        started = true;
+    }
+
+    /**
+     * Defines the bounds of the host.
+     * Bounds are used for restricting the rendering in
+     * the area of the control that is being updated. With Images
+     * the bounds are not used.
+     *
+     * @param crtl The Control of the host
+     * @param clienArea The area of the control which can be drawn by Graphics
+     */
+    void setControlBounds(final Control control)
+    {
+        ESWTUIThreadRunner.safeSyncExec(new Runnable()
+        {
+            public void run()
+            {
+                // This implementation is based on the fact that
+                // the QWindowSurface has the size of the shell active area
+                // not the whole display, thus Shell clientArea equals QWindowSurface.
+                // This might change in future if/when Qt starts
+                // rendering e.g. the status pane i.e. the whole display
+                // to window surface
+                Point controlLoc = control.toDisplay(0,0);
+                Point shellLoc = control.getShell().toDisplay(0,0);
+                hostBounds.x = controlLoc.x - shellLoc.x;
+                hostBounds.y = controlLoc.y - shellLoc.y;
+                hostBounds.width = control.getBounds().width;
+                hostBounds.height = control.getBounds().height;
+            }
+        });
+    }
+
+    /**
+     * Performs binding to target in host specific way
+     */
+    abstract void bindToHost(GraphicsContext gc);
+
+    /**
+     * Getter for the host of the buffer
+     * @return The host
+     */
+    abstract Object getHost();
+
+    /**
+     * Getter for the host type
+     * @return One of host types defined in this class
+     */
+    abstract int getHostType();
+
+    /**
+     *  Setups the window surface if not setup already.
+     *  This method must be called before flushing buffer to
+     *  window surface
+     *  Note. must be called in UI thread
+     */
+    abstract void setupWindowSurface();
+
+    /**
+     * Creates and returns new Graphics instance
+     * @return new Graphics instance
+     */
+    Graphics getGraphics()
+    {
+        if(!started)
+        {
+            init();
+        }
+        clientCount++;
+        return new Graphics(this);
+    }
+
+    /**
+     * Synchronizes this buffer with the actual target
+     * must be called in UI thread. If no Graphics instances
+     * are creates, sync has no effect
+     */
+    void sync()
+    {
+        if(!started)
+        {
+            return;
+        }
+        // if there's nothing to flush return
+        if(!commandBuffer.containsDrawnPrimitives())
+        {
+            return;
+        }
+        doRelease();
+        bindToHost(gc);
+        gc.render(commandBuffer);
+        doRelease();
+
+        // Reset commands
+        commandBuffer.reset();
+
+        // Write last settings to buffer
+        // as they are reset in bind
+        gc.bindTarget(commandBuffer);
+        gc.setFont(bufferFontHandle);
+        gc.setBackgroundColor(bufferColor, true);
+        gc.setForegroundColor(bufferColor, true);
+        writeControlBoundsToBuffer(true);
+    }
+
+    /**
+     * Decreases the client reference count,
+     * should be called by Graphics instances when
+     * they are about to be disposed
+     */
+    void removeRef()
+    {
+        clientCount--;
+    }
+
+    /**
+     * Disposes this instance
+     */
+    void dispose()
+    {
+        doRelease();
+        gc.dispose();
+        gc = null;
+        commandBuffer = null;
+    }
+
+    void copyArea(int x1, int y1, int width, int height, int x2, int y2, Graphics client)
+    {
+        gc.copyArea(x1, y1, width, height, x2, y2);
+    }
+
+    void fillRect(int x, int y, int w, int h, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION), client);
+        gc.fillRect(x, y, w, h);
+    }
+
+    void fillRoundRect(int x, int y, int w, int h, int arcW, int arcH, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION), client);
+        gc.fillRoundRect(x, y, w, h, arcW, arcH);
+    }
+
+    void fillArc(int x, int y, int w, int h, int startAngle, int arcAngle, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION), client);
+        gc.fillArc(x, y, w, h, startAngle, arcAngle);
+    }
+
+    void fillTriangle(int[] points, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION), client);
+        gc.fillPolygon(points);
+    }
+
+    void setClip(int x, int y, int w, int h, Graphics client)
+    {
+        validateAndApplySettings(COORS_TRANSLATION, client);
+        // check if given clip is already active in buffer
+        if((bufferClip.x == x) && (bufferClip.y == y) &&
+                (bufferClip.width == w) && (bufferClip.height== h))
+        {
+            return;
+        }
+        // Images do not need special handling
+        if(getHostType() == HOST_TYPE_IMAGE)
+        {
+            gc.setClip(x, y, w, h, false);
+            return;
+        }
+
+        // translate clip to display coordinates and apply
+        Rectangle rect = clipToDisplayCoords(x, y, w, h);
+        if(rect.isEmpty())
+        {
+            // check is buffer clip is already up to date
+            if(bufferClip.isEmpty())
+            {
+                return;
+            }
+            else
+            {
+                // This is a special case, where the clip defines
+                // an area outside control bounds and due to that the clip
+                // is set to zero size, in order t prevent drawing on top of other controls.
+                // In all other cases the buffer clip is always set
+                // to the same values as the client, i.e. Graphics has.
+                // So by setting the bufferClip here to zero means that the clip
+                // in Graphics and the buffer are not in sync and not comparable,
+                // however they will be back in sync when client sets clip
+                // with an area partly or completely inside the host control.
+                bufferClip = rect;
+            }
+        }
+        else
+        {
+            bufferClip.x = x;
+            bufferClip.y = y;
+            bufferClip.width = w;
+            bufferClip.height = h;
+        }
+        gc.setClip(rect.x, rect.y, rect.width, rect.height, false);
+    }
+
+    void setColor(int r, int g, int b, Graphics client)
+    {
+        // check if given color is already active in buffer
+        if(bufferColor == (Graphics.OPAQUE_ALPHA | (r << 16) | (g << 8) | b))
+        {
+            return;
+        }
+        gc.setForegroundColor(r, g, b);
+        gc.setBackgroundColor(r, g, b);
+
+        // Cache active color
+        bufferColor = (Graphics.OPAQUE_ALPHA | (r << 16) | (g << 8) | b);
+    }
+
+    void setFont(int fontHandle, Graphics client)
+    {
+        // check if given font is already active in buffer
+        if(bufferFontHandle == fontHandle)
+        {
+            return;
+        }
+        gc.setFont(fontHandle);
+        // Cache active setting
+        bufferFontHandle = fontHandle;
+    }
+
+    void setStrokeStyle(int cgfxStyle, int graphicsStyle, Graphics client)
+    {
+        if(bufferStrokeStyle == graphicsStyle)
+        {
+            return;
+        }
+        gc.setStrokeStyle(cgfxStyle);
+        // Cache active setting
+        bufferStrokeStyle = graphicsStyle;
+    }
+
+    void translate(int xDelta, int yDelta, Graphics client)
+    {
+        if((xDelta == 0) && (yDelta == 0))
+        {
+            return;
+        }
+        gc.translate(xDelta, yDelta);
+        // Cache active settings
+        bufferTranslateX += xDelta;
+        bufferTranslateY += yDelta;
+    }
+
+    void drawLine(int xStart, int yStart, int xEnd, int yEnd, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION|STROKESTYLE), client);
+        gc.drawLine(xStart, yStart, xEnd, yEnd);
+    }
+
+    void drawRect(int x, int y, int w, int h, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION|STROKESTYLE), client);
+        gc.drawRect(x, y, w, h);
+    }
+
+    void drawRoundRect(int x, int y, int w, int h, int arcW, int arcH, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION|STROKESTYLE), client);
+        gc.drawRoundRect(x, y, w, h, arcW, arcH);
+    }
+
+    void drawArc(int x, int y, int w, int h, int startAngle, int arcAngle, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION|STROKESTYLE), client);
+        gc.drawArc(x, y, w, h, startAngle, arcAngle);
+    }
+
+    void drawString(String string, int x, int y, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION|FONT), client);
+        gc.drawString(string, x, y, true);
+    }
+
+    void drawImage(org.eclipse.swt.internal.qt.graphics.Image image, int x,int y, Graphics client)
+    {
+        validateAndApplySettings((CLIP|COORS_TRANSLATION), client);
+        gc.drawImage(image, x, y);
+    }
+
+    void drawImage(org.eclipse.swt.internal.qt.graphics.Image image, int xDst, int yDst,
+                   int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc,
+                   int transform, Graphics client)
+    {
+        validateAndApplySettings((CLIP|COORS_TRANSLATION), client);
+        gc.drawImage(image, xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, transform);
+    }
+
+    void drawRGB(int[] rgb,
+                 int offset,
+                 int scanlength,
+                 int x,
+                 int y,
+                 int w,
+                 int h,
+                 boolean alpha,
+                 Graphics client)
+    {
+        validateAndApplySettings((CLIP|COORS_TRANSLATION), client);
+        gc.drawRGB(rgb, offset, scanlength, x, y, w, h, alpha);
+    }
+
+    void drawRGB(int[] rgb,
+                 int offset,
+                 int scanlength,
+                 int x,
+                 int y,
+                 int w,
+                 int h,
+                 boolean alpha,
+                 int manipulation,
+                 Graphics client)
+    {
+        validateAndApplySettings((CLIP|COORS_TRANSLATION), client);
+        gc.drawRGB(rgb, offset, scanlength, x, y, w, h, alpha, manipulation);
+    }
+
+    void drawRGB(byte[] rgb,
+                 byte[] transparencyMask,
+                 int offset,
+                 int scanlength,
+                 int x,
+                 int y,
+                 int w,
+                 int h,
+                 int manipulation,
+                 int format,
+                 Graphics client)
+    {
+        validateAndApplySettings((CLIP|COORS_TRANSLATION), client);
+        gc.drawRGB(rgb, transparencyMask, offset, scanlength, x, y, w, h, manipulation, format);
+    }
+
+    void drawRGB(short[] rgb,
+                 int offset,
+                 int scanlength,
+                 int x,
+                 int y,
+                 int w,
+                 int h,
+                 boolean alpha,
+                 int manipulation,
+                 int format,
+                 Graphics client)
+    {
+        validateAndApplySettings((CLIP|COORS_TRANSLATION), client);
+        gc.drawRGB(rgb, offset, scanlength, x, y, w, h, alpha, manipulation, format);
+    }
+
+    void drawPolygon(int[] points, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION|STROKESTYLE), client);
+        gc.drawPolygon(points);
+    }
+
+    void fillPolygon(int[] points, Graphics client)
+    {
+        validateAndApplySettings((COLOR|CLIP|COORS_TRANSLATION), client);
+        gc.fillPolygon(points);
+    }
+
+    void setARGBColor(int argb, Graphics client)
+    {
+        if(bufferColor == argb)
+        {
+            return;
+        }
+        gc.setBackgroundColor(argb, true);
+        gc.setForegroundColor(argb, true);
+        // Cache active color
+        bufferColor = argb;
+    }
+
+    /**
+     * Translates given rectangle to window coordinates.
+     * Coordinate system translation does not affect on this method.
+     *
+     * @param x The x-coordinate of the rectangle
+     * @param y The y-coordinate of the rectangle
+     * @param w The width of the rectangle
+     * @param h The height of the rectangle
+     * @return
+     */
+    Rectangle toWindowCoordinates(int x, int y, int w, int h)
+    {
+        final int xInDpy = hostBounds.x + x;
+        final int yInDpy = hostBounds.y + y;
+        return new Rectangle(xInDpy, yInDpy, xInDpy + w, yInDpy + h);
+    }
+
+
+    /**
+     * Translates given rectangle to display/window surface coordinates
+     * and outlines the clip inside the control bounds.
+     *
+     * @param x The x-coordinate of the rectangle
+     * @param y The y-coordinate of the rectangle
+     * @param w The width of the rectangle
+     * @param h The height of the rectangle
+     *
+     */
+    private Rectangle clipToDisplayCoords(int x, int y, int w, int h)
+    {
+        // Bottom-right corner of control bounds in window coordinates
+        final int hostX2 = hostBounds.x + hostBounds.width;
+        final int hostY2 = hostBounds.y + hostBounds.height;
+        // clip in window coordinates
+        final int clipX1Dpy = hostBounds.x + bufferTranslateX + x;
+        final int clipY1Dpy = hostBounds.y + bufferTranslateY + y;
+        final int clipX2Dpy = hostBounds.x + bufferTranslateX + x + w;
+        final int clipY2Dpy = hostBounds.y + bufferTranslateY + y + h;
+        int clipX1 = x;
+        int clipY1 = y;
+        int clipX2 = x + w;
+        int clipY2 = y + h;
+
+        // check if the clip is completely outside of control bounds
+        if(!hostBounds.contains(clipX1Dpy, clipY1Dpy) && !hostBounds.contains(clipX2Dpy, clipY2Dpy))
+        {
+            return new Rectangle(0,0,0,0);
+        }
+
+        // At least one corner is inside control bounds so
+        // adjust clip coordinates so that they lie within control bounds
+        clipX1 = clipX1Dpy < hostBounds.x ? (x + (hostBounds.x - clipX1Dpy)) : x;
+        clipX1 = clipX1Dpy > hostX2 ? (x - (clipX1Dpy - hostX2)) : x;
+        clipY1 = clipY1Dpy < hostBounds.y ? (y + (hostBounds.y - clipY1Dpy)) : y;
+        clipY1 = clipY1Dpy > hostY2 ? (y - (clipY1Dpy - hostY2)) : y;
+
+        clipX2 = clipX2Dpy < hostBounds.x ? (clipX2 + (hostBounds.x - clipX1Dpy)) : clipX2;
+        clipX2 = clipX2Dpy > hostX2 ? (clipX2 - (clipX1Dpy - hostX2)) : clipX2;
+        clipY2 = clipY2Dpy < hostBounds.y ? (clipY2 + (hostBounds.y - clipY1Dpy)) : clipY2;
+        clipY2 = clipY2Dpy > hostY2 ? (clipY2 - (clipY1Dpy - hostY2)) : clipY2;
+
+        return new Rectangle(clipX1, clipY1, (clipX2 - clipX1) , (clipY1 - clipY2));
+    }
+
+    /**
+     * Validates the current settings active in buffer against
+     * caller settings and updated needed settings in buffer when
+     * that is required. This method does not update anything if
+     * there is only one (reference_count == 1)Graphics instance using this buffer.
+     *
+     * @param flags The settings that need to be checked
+     * @param client The Graphics instance that made the call
+     */
+    private void validateAndApplySettings(int flags, Graphics client)
+    {
+        if(!clientChanged(client))
+        {
+            return;
+        }
+        if((COLOR & flags) != 0)
+        {
+            if(bufferColor != client.currentColor)
+            {
+                gc.setBackgroundColor(client.currentColor, true);
+                gc.setForegroundColor(client.currentColor, true);
+                bufferColor = client.currentColor;
+            }
+        }
+        if((CLIP & flags) != 0)
+        {
+            if(client.currentClip[0] != bufferClip.x &&
+                    client.currentClip[1] != bufferClip.y &&
+                    client.currentClip[2] != bufferClip.width &&
+                    client.currentClip[3] != bufferClip.height)
+            {
+
+                Rectangle rect = clipToDisplayCoords(client.currentClip[0], client.currentClip[1],
+                                                     client.currentClip[2], client.currentClip[3]);
+                gc.setClip(rect.x, rect.y, rect.width, rect.height, false);
+                bufferClip.x = client.currentClip[0];
+                bufferClip.y = client.currentClip[1];
+                bufferClip.width = client.currentClip[2];
+                bufferClip.height = client.currentClip[3];
+            }
+        }
+        if((COORS_TRANSLATION & flags) != 0)
+        {
+            if((bufferTranslateX != client.translateX) && (bufferTranslateY != client.translateY))
+            {
+                gc.translate((client.translateX - bufferTranslateX), (client.translateY - bufferTranslateY));
+                bufferTranslateX = client.translateX;
+                bufferTranslateY = client.translateY;
+            }
+        }
+        if((FONT & flags) != 0)
+        {
+            int fontHandle = Font.getESWTFont(client.currentFont).handle;
+            if(bufferFontHandle != fontHandle)
+            {
+                gc.setFont(fontHandle);
+                bufferFontHandle = fontHandle;
+            }
+        }
+        if((STROKESTYLE & flags) != 0)
+        {
+            if(bufferStrokeStyle != client.currentStrokeSyle)
+            {
+                gc.setStrokeStyle(Graphics.mapStrokeStyle(client.currentStrokeSyle));
+                bufferStrokeStyle = client.currentStrokeSyle;
+            }
+        }
+    }
+
+    private boolean clientChanged(Graphics client)
+    {
+        if(clientCount == 1)
+        {
+            return false;
+        }
+        if(currentClient != client)
+        {
+            currentClient = client;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Writes control bounds to buffer, including translation and clip
+     * @param writeClientTranslation If true write also client translation to buffer, otherwise not
+     */
+    private void writeControlBoundsToBuffer(boolean writeClientTranslation)
+    {
+        if((hostBounds.x != 0) || (hostBounds.y != 0))
+        {
+            gc.translate(hostBounds.x, hostBounds.y);
+        }
+        gc.setClip(0, 0, hostBounds.width, hostBounds.height, false);
+        // Cache buffer settings
+        bufferClip.x = 0;
+        bufferClip.y = 0;
+        bufferClip.width = hostBounds.width;
+        bufferClip.height = hostBounds.height;
+        // write client translation if requested
+        if(writeClientTranslation)
+        {
+            if((bufferTranslateX != 0) || (bufferTranslateY != 0))
+            {
+                gc.translate(bufferTranslateX, bufferTranslateY);
+            }
+        }
+    }
+
+    private void doRelease()
+    {
+        gc.releaseTarget();
+    }
+}