| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.graphics; |
| |
| import com.android.layoutlib.bridge.Bridge; |
| import com.android.layoutlib.bridge.impl.DelegateManager; |
| import com.android.layoutlib.bridge.impl.Stack; |
| |
| import android.graphics.Paint_Delegate.FontInfo; |
| import android.text.TextUtils; |
| |
| import java.awt.AlphaComposite; |
| import java.awt.BasicStroke; |
| import java.awt.Color; |
| import java.awt.Composite; |
| import java.awt.Graphics2D; |
| import java.awt.Rectangle; |
| import java.awt.RenderingHints; |
| import java.awt.geom.AffineTransform; |
| import java.awt.image.BufferedImage; |
| import java.util.List; |
| |
| |
| /** |
| * Delegate implementing the native methods of android.graphics.Canvas |
| * |
| * Through the layoutlib_create tool, the original native methods of Canvas have been replaced |
| * by calls to methods of the same name in this delegate class. |
| * |
| * This class behaves like the original native implementation, but in Java, keeping previously |
| * native data into its own objects and mapping them to int that are sent back and forth between |
| * it and the original Canvas class. |
| * |
| * @see DelegateManager |
| * |
| */ |
| public class Canvas_Delegate { |
| |
| // ---- delegate manager ---- |
| private static final DelegateManager<Canvas_Delegate> sManager = |
| new DelegateManager<Canvas_Delegate>(); |
| |
| // ---- delegate helper data ---- |
| |
| // ---- delegate data ---- |
| private BufferedImage mBufferedImage; |
| private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>(); |
| |
| // ---- Public Helper methods ---- |
| |
| /** |
| * Returns the native delegate associated to a given {@link Canvas} object. |
| */ |
| public static Canvas_Delegate getDelegate(Canvas canvas) { |
| return sManager.getDelegate(canvas.mNativeCanvas); |
| } |
| |
| /** |
| * Returns the native delegate associated to a given an int referencing a {@link Canvas} object. |
| */ |
| public static Canvas_Delegate getDelegate(int native_canvas) { |
| return sManager.getDelegate(native_canvas); |
| } |
| |
| /** |
| * Returns the current {@link Graphics2D} used to draw. |
| */ |
| public Graphics2D getGraphics2d() { |
| return mGraphicsStack.peek(); |
| } |
| |
| // ---- native methods ---- |
| |
| /*package*/ static boolean isOpaque(Canvas thisCanvas) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static int getWidth(Canvas thisCanvas) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return 0; |
| } |
| |
| return canvasDelegate.mBufferedImage.getWidth(); |
| } |
| |
| /*package*/ static int getHeight(Canvas thisCanvas) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return 0; |
| } |
| |
| return canvasDelegate.mBufferedImage.getHeight(); |
| } |
| |
| /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| canvasDelegate.getGraphics2d().translate(dx, dy); |
| } |
| |
| /*package*/ static void rotate(Canvas thisCanvas, float degrees) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| canvasDelegate.getGraphics2d().rotate(Math.toRadians(degrees)); |
| } |
| |
| /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| canvasDelegate.getGraphics2d().scale(sx, sy); |
| } |
| |
| /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the current top graphics2D object. |
| Graphics2D g = canvasDelegate.getGraphics2d(); |
| |
| // get its current matrix |
| AffineTransform currentTx = g.getTransform(); |
| // get the AffineTransform for the given skew. |
| float[] mtx = Matrix_Delegate.getSkew(kx, ky); |
| AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx); |
| |
| // combine them so that the given matrix is applied after. |
| currentTx.preConcatenate(matrixTx); |
| |
| // give it to the graphics2D as a new matrix replacing all previous transform |
| g.setTransform(currentTx); |
| } |
| |
| /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) { |
| return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom); |
| } |
| |
| /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) { |
| return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom); |
| } |
| |
| /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, |
| float bottom) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return false; |
| } |
| |
| canvasDelegate.getGraphics2d().clipRect((int)left, (int)top, (int)(right-left), |
| (int)(bottom-top)); |
| return true; |
| } |
| |
| /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right, |
| int bottom) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return false; |
| } |
| |
| canvasDelegate.getGraphics2d().clipRect(left, top, right - left, bottom - top); |
| return true; |
| } |
| |
| /*package*/ static int save(Canvas thisCanvas) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return 0; |
| } |
| |
| // get the current save count |
| int count = canvasDelegate.mGraphicsStack.size(); |
| |
| // create a new graphics and add it to the stack |
| Graphics2D g = (Graphics2D)canvasDelegate.getGraphics2d().create(); |
| canvasDelegate.mGraphicsStack.push(g); |
| |
| // return the old save count |
| return count; |
| |
| } |
| |
| /*package*/ static int save(Canvas thisCanvas, int saveFlags) { |
| // FIXME implement save(flags) |
| return save(thisCanvas); |
| } |
| |
| /*package*/ static void restore(Canvas thisCanvas) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| canvasDelegate.mGraphicsStack.pop(); |
| } |
| |
| /*package*/ static int getSaveCount(Canvas thisCanvas) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return 0; |
| } |
| |
| return canvasDelegate.mGraphicsStack.size(); |
| } |
| |
| /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| while (canvasDelegate.mGraphicsStack.size() > saveCount) { |
| canvasDelegate.mGraphicsStack.pop(); |
| } |
| } |
| |
| /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count, |
| Paint paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void drawLines(Canvas thisCanvas, float[] pts, int offset, int count, |
| Paint paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void freeCaches() { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static int initRaster(int nativeBitmapOrZero) { |
| if (nativeBitmapOrZero > 0) { |
| // get the Bitmap from the int |
| Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero); |
| |
| // create a new Canvas_Delegate with the given bitmap and return its new native int. |
| Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate.getImage()); |
| |
| return sManager.addDelegate(newDelegate); |
| } else { |
| // create a new Canvas_Delegate and return its new native int. |
| Canvas_Delegate newDelegate = new Canvas_Delegate(); |
| |
| return sManager.addDelegate(newDelegate); |
| } |
| } |
| |
| /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the delegate from the native int. |
| Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); |
| if (bitmapDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| canvasDelegate.setBitmap(bitmapDelegate.getImage()); |
| } |
| |
| /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds, |
| int paint, int layerFlags) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static int native_saveLayer(int nativeCanvas, float l, |
| float t, float r, float b, |
| int paint, int layerFlags) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static int native_saveLayerAlpha(int nativeCanvas, |
| RectF bounds, int alpha, |
| int layerFlags) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l, |
| float t, float r, float b, |
| int alpha, int layerFlags) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| |
| /*package*/ static void native_concat(int nCanvas, int nMatrix) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); |
| if (matrixDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the current top graphics2D object. |
| Graphics2D g = canvasDelegate.getGraphics2d(); |
| |
| // get its current matrix |
| AffineTransform currentTx = g.getTransform(); |
| // get the AffineTransform of the given matrix |
| AffineTransform matrixTx = matrixDelegate.getAffineTransform(); |
| |
| // combine them so that the given matrix is applied after. |
| currentTx.preConcatenate(matrixTx); |
| |
| // give it to the graphics2D as a new matrix replacing all previous transform |
| g.setTransform(currentTx); |
| } |
| |
| /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| } |
| |
| Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); |
| if (matrixDelegate == null) { |
| assert false; |
| } |
| |
| // get the current top graphics2D object. |
| Graphics2D g = canvasDelegate.getGraphics2d(); |
| |
| // get the AffineTransform of the given matrix |
| AffineTransform matrixTx = matrixDelegate.getAffineTransform(); |
| |
| // give it to the graphics2D as a new matrix replacing all previous transform |
| g.setTransform(matrixTx); |
| |
| if (matrixDelegate.hasPerspective()) { |
| Bridge.getLog().warning(null, |
| "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " + |
| "supports affine transformations in the Layout Preview."); |
| } |
| } |
| |
| /*package*/ static boolean native_clipRect(int nCanvas, |
| float left, float top, |
| float right, float bottom, |
| int regionOp) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static boolean native_clipPath(int nativeCanvas, |
| int nativePath, |
| int regionOp) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static boolean native_clipRegion(int nativeCanvas, |
| int nativeRegion, |
| int regionOp) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void nativeSetDrawFilter(int nativeCanvas, |
| int nativeFilter) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static boolean native_getClipBounds(int nativeCanvas, |
| Rect bounds) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return false; |
| } |
| |
| Rectangle rect = canvasDelegate.getGraphics2d().getClipBounds(); |
| if (rect != null) { |
| bounds.left = rect.x; |
| bounds.top = rect.y; |
| bounds.right = rect.x + rect.width; |
| bounds.bottom = rect.y + rect.height; |
| return true; |
| } |
| return false; |
| } |
| |
| /*package*/ static void native_getCTM(int canvas, int matrix) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static boolean native_quickReject(int nativeCanvas, |
| RectF rect, |
| int native_edgeType) { |
| // FIXME properly implement quickReject |
| return false; |
| } |
| |
| /*package*/ static boolean native_quickReject(int nativeCanvas, |
| int path, |
| int native_edgeType) { |
| // FIXME properly implement quickReject |
| return false; |
| } |
| |
| /*package*/ static boolean native_quickReject(int nativeCanvas, |
| float left, float top, |
| float right, float bottom, |
| int native_edgeType) { |
| // FIXME properly implement quickReject |
| return false; |
| } |
| |
| /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) { |
| native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF), |
| PorterDuff.Mode.SRC_OVER.nativeInt); |
| |
| } |
| |
| /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) { |
| native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF), |
| PorterDuff.Mode.SRC_OVER.nativeInt); |
| } |
| |
| /*package*/ static void native_drawColor(int nativeCanvas, int color) { |
| native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt); |
| } |
| |
| /*package*/ static void native_drawColor(int nativeCanvas, int color, int mode) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get a new graphics context. |
| Graphics2D graphics = (Graphics2D)canvasDelegate.getGraphics2d().create(); |
| try { |
| // reset its transform just in case |
| graphics.setTransform(new AffineTransform()); |
| |
| // set the color |
| graphics.setColor(new Color(color, true /*alpha*/)); |
| |
| setModeInGraphics(graphics, mode); |
| |
| graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(), |
| canvasDelegate.mBufferedImage.getHeight()); |
| } finally { |
| // dispose Graphics2D object |
| graphics.dispose(); |
| } |
| } |
| |
| /*package*/ static void native_drawPaint(int nativeCanvas, int paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawLine(int nativeCanvas, float startX, |
| float startY, float stopX, |
| float stopY, int paint) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the delegate from the native int. |
| Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); |
| if (paintDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get a Graphics2D object configured with the drawing parameters. |
| Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); |
| |
| g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); |
| |
| // dispose Graphics2D object |
| g.dispose(); |
| } |
| |
| /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, |
| int paint) { |
| native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint); |
| } |
| |
| /*package*/ static void native_drawRect(int nativeCanvas, float left, |
| float top, float right, |
| float bottom, int paint) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the delegate from the native int. |
| Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); |
| if (paintDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| if (right > left && bottom > top) { |
| // get a Graphics2D object configured with the drawing parameters. |
| Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); |
| |
| int style = paintDelegate.getStyle(); |
| |
| // draw |
| if (style == Paint.Style.FILL.nativeInt || |
| style == Paint.Style.FILL_AND_STROKE.nativeInt) { |
| g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); |
| } |
| |
| if (style == Paint.Style.STROKE.nativeInt || |
| style == Paint.Style.FILL_AND_STROKE.nativeInt) { |
| g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); |
| } |
| |
| // dispose Graphics2D object |
| g.dispose(); |
| } |
| } |
| |
| /*package*/ static void native_drawOval(int nativeCanvas, RectF oval, |
| int paint) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the delegate from the native int. |
| Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); |
| if (paintDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| if (oval.right > oval.left && oval.bottom > oval.top) { |
| // get a Graphics2D object configured with the drawing parameters. |
| Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); |
| |
| int style = paintDelegate.getStyle(); |
| |
| // draw |
| if (style == Paint.Style.FILL.nativeInt || |
| style == Paint.Style.FILL_AND_STROKE.nativeInt) { |
| g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); |
| } |
| |
| if (style == Paint.Style.STROKE.nativeInt || |
| style == Paint.Style.FILL_AND_STROKE.nativeInt) { |
| g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); |
| } |
| |
| // dispose Graphics2D object |
| g.dispose(); |
| } |
| } |
| |
| /*package*/ static void native_drawCircle(int nativeCanvas, float cx, |
| float cy, float radius, |
| int paint) { |
| native_drawOval(nativeCanvas, |
| new RectF(cx - radius, cy - radius, radius*2, radius*2), |
| paint); |
| } |
| |
| /*package*/ static void native_drawArc(int nativeCanvas, RectF oval, |
| float startAngle, float sweep, |
| boolean useCenter, int paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawRoundRect(int nativeCanvas, |
| RectF rect, float rx, |
| float ry, int paint) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the delegate from the native int. |
| Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); |
| if (paintDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| if (rect.right > rect.left && rect.bottom > rect.top) { |
| // get a Graphics2D object configured with the drawing parameters. |
| Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); |
| |
| int style = paintDelegate.getStyle(); |
| |
| // draw |
| if (style == Paint.Style.FILL.nativeInt || |
| style == Paint.Style.FILL_AND_STROKE.nativeInt) { |
| g.fillRoundRect( |
| (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), |
| (int)rx, (int)ry); |
| } |
| |
| if (style == Paint.Style.STROKE.nativeInt || |
| style == Paint.Style.FILL_AND_STROKE.nativeInt) { |
| g.drawRoundRect( |
| (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), |
| (int)rx, (int)ry); |
| } |
| |
| // dispose Graphics2D object |
| g.dispose(); |
| } |
| } |
| |
| /*package*/ static void native_drawPath(int nativeCanvas, int path, |
| int paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, |
| float left, float top, |
| int nativePaintOrZero, |
| int canvasDensity, |
| int screenDensity, |
| int bitmapDensity) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, |
| Rect src, RectF dst, |
| int nativePaintOrZero, |
| int screenDensity, |
| int bitmapDensity) { |
| // get the delegate from the native int. |
| Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); |
| if (bitmapDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| BufferedImage image = bitmapDelegate.getImage(); |
| |
| if (src == null) { |
| drawBitmap(nativeCanvas, image, nativePaintOrZero, |
| 0, 0, image.getWidth(), image.getHeight(), |
| (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); |
| } else { |
| drawBitmap(nativeCanvas, image, nativePaintOrZero, |
| src.left, src.top, src.width(), src.height(), |
| (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); |
| } |
| } |
| |
| /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap, |
| Rect src, Rect dst, |
| int nativePaintOrZero, |
| int screenDensity, |
| int bitmapDensity) { |
| // get the delegate from the native int. |
| Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); |
| if (bitmapDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| BufferedImage image = bitmapDelegate.getImage(); |
| |
| if (src == null) { |
| drawBitmap(nativeCanvas, image, nativePaintOrZero, |
| 0, 0, image.getWidth(), image.getHeight(), |
| dst.left, dst.top, dst.right, dst.bottom); |
| } else { |
| drawBitmap(nativeCanvas, image, nativePaintOrZero, |
| src.left, src.top, src.width(), src.height(), |
| dst.left, dst.top, dst.right, dst.bottom); |
| } |
| } |
| |
| /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors, |
| int offset, int stride, float x, |
| float y, int width, int height, |
| boolean hasAlpha, |
| int nativePaintOrZero) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, |
| int nMatrix, int nPaint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap, |
| int meshWidth, int meshHeight, |
| float[] verts, int vertOffset, |
| int[] colors, int colorOffset, int nPaint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n, |
| float[] verts, int vertOffset, float[] texs, int texOffset, |
| int[] colors, int colorOffset, short[] indices, |
| int indexOffset, int indexCount, int nPaint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawText(int nativeCanvas, char[] text, |
| int index, int count, float x, |
| float y, int flags, int paint) { |
| // WARNING: the logic in this method is similar to Paint.measureText. |
| // Any change to this method should be reflected in Paint.measureText |
| |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the delegate from the native int. |
| Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); |
| if (paintDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| Graphics2D g = (Graphics2D) canvasDelegate.getCustomGraphics(paintDelegate); |
| try { |
| // Paint.TextAlign indicates how the text is positioned relative to X. |
| // LEFT is the default and there's nothing to do. |
| if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { |
| float m = paintDelegate.measureText(text, index, count); |
| if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { |
| x -= m / 2; |
| } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { |
| x -= m; |
| } |
| } |
| |
| List<FontInfo> fonts = paintDelegate.getFonts(); |
| |
| if (fonts.size() > 0) { |
| FontInfo mainFont = fonts.get(0); |
| int i = index; |
| int lastIndex = index + count; |
| while (i < lastIndex) { |
| // always start with the main font. |
| int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); |
| if (upTo == -1) { |
| // draw all the rest and exit. |
| g.setFont(mainFont.mFont); |
| g.drawChars(text, i, lastIndex - i, (int)x, (int)y); |
| return; |
| } else if (upTo > 0) { |
| // draw what's possible |
| g.setFont(mainFont.mFont); |
| g.drawChars(text, i, upTo - i, (int)x, (int)y); |
| |
| // compute the width that was drawn to increase x |
| x += mainFont.mMetrics.charsWidth(text, i, upTo - i); |
| |
| // move index to the first non displayed char. |
| i = upTo; |
| |
| // don't call continue at this point. Since it is certain the main font |
| // cannot display the font a index upTo (now ==i), we move on to the |
| // fallback fonts directly. |
| } |
| |
| // no char supported, attempt to read the next char(s) with the |
| // fallback font. In this case we only test the first character |
| // and then go back to test with the main font. |
| // Special test for 2-char characters. |
| boolean foundFont = false; |
| for (int f = 1 ; f < fonts.size() ; f++) { |
| FontInfo fontInfo = fonts.get(f); |
| |
| // need to check that the font can display the character. We test |
| // differently if the char is a high surrogate. |
| int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; |
| upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); |
| if (upTo == -1) { |
| // draw that char |
| g.setFont(fontInfo.mFont); |
| g.drawChars(text, i, charCount, (int)x, (int)y); |
| |
| // update x |
| x += fontInfo.mMetrics.charsWidth(text, i, charCount); |
| |
| // update the index in the text, and move on |
| i += charCount; |
| foundFont = true; |
| break; |
| |
| } |
| } |
| |
| // in case no font can display the char, display it with the main font. |
| // (it'll put a square probably) |
| if (foundFont == false) { |
| int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; |
| |
| g.setFont(mainFont.mFont); |
| g.drawChars(text, i, charCount, (int)x, (int)y); |
| |
| // measure it to advance x |
| x += mainFont.mMetrics.charsWidth(text, i, charCount); |
| |
| // and move to the next chars. |
| i += charCount; |
| } |
| } |
| } |
| } finally { |
| g.dispose(); |
| } |
| } |
| |
| /*package*/ static void native_drawText(int nativeCanvas, String text, |
| int start, int end, float x, |
| float y, int flags, int paint) { |
| int count = end - start; |
| char[] buffer = TemporaryBuffer.obtain(count); |
| TextUtils.getChars(text, start, end, buffer, 0); |
| |
| native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); |
| } |
| |
| /*package*/ static void native_drawTextRun(int nativeCanvas, String text, |
| int start, int end, int contextStart, int contextEnd, |
| float x, float y, int flags, int paint) { |
| int count = end - start; |
| char[] buffer = TemporaryBuffer.obtain(count); |
| TextUtils.getChars(text, start, end, buffer, 0); |
| |
| native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint); |
| } |
| |
| /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text, |
| int start, int count, int contextStart, int contextCount, |
| float x, float y, int flags, int paint) { |
| native_drawText(nativeCanvas, text, 0, count, x, y, flags, paint); |
| } |
| |
| /*package*/ static void native_drawPosText(int nativeCanvas, |
| char[] text, int index, |
| int count, float[] pos, |
| int paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawPosText(int nativeCanvas, |
| String text, float[] pos, |
| int paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawTextOnPath(int nativeCanvas, |
| char[] text, int index, |
| int count, int path, |
| float hOffset, |
| float vOffset, int bidiFlags, |
| int paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawTextOnPath(int nativeCanvas, |
| String text, int path, |
| float hOffset, |
| float vOffset, |
| int flags, int paint) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void native_drawPicture(int nativeCanvas, |
| int nativePicture) { |
| // FIXME |
| throw new UnsupportedOperationException(); |
| } |
| |
| /*package*/ static void finalizer(int nativeCanvas) { |
| // get the delegate from the native int so that it can be disposed. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| canvasDelegate.dispose(); |
| |
| // remove it from the manager. |
| sManager.removeDelegate(nativeCanvas); |
| } |
| |
| // ---- Private delegate/helper methods ---- |
| |
| private Canvas_Delegate(BufferedImage image) { |
| setBitmap(image); |
| } |
| |
| private Canvas_Delegate() { |
| } |
| |
| /** |
| * Disposes of the {@link Graphics2D} stack. |
| */ |
| private void dispose() { |
| while (mGraphicsStack.size() > 0) { |
| mGraphicsStack.pop().dispose(); |
| } |
| } |
| |
| private void setBitmap(BufferedImage image) { |
| mBufferedImage = image; |
| mGraphicsStack.push(mBufferedImage.createGraphics()); |
| } |
| |
| /** |
| * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. |
| * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. |
| */ |
| /*package*/ Graphics2D getCustomGraphics(Paint_Delegate paint) { |
| // make new one |
| Graphics2D g = getGraphics2d(); |
| g = (Graphics2D)g.create(); |
| |
| // configure it |
| |
| if (paint.isAntiAliased()) { |
| g.setRenderingHint( |
| RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| g.setRenderingHint( |
| RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); |
| } |
| |
| boolean useColorPaint = true; |
| |
| // get the shader first, as it'll replace the color if it can be used it. |
| Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader()); |
| if (shaderDelegate != null) { |
| java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint(); |
| if (shaderPaint != null) { |
| g.setPaint(shaderPaint); |
| useColorPaint = false; |
| } else { |
| Bridge.getLog().warning(null, |
| String.format( |
| "Shader '%1$s' is not supported in the Layout Preview.", |
| shaderDelegate.getClass().getCanonicalName())); |
| } |
| } |
| |
| if (useColorPaint) { |
| g.setColor(new Color(paint.getColor(), true /*hasAlpha*/)); |
| } |
| |
| int style = paint.getStyle(); |
| if (style == Paint.Style.STROKE.nativeInt || |
| style == Paint.Style.FILL_AND_STROKE.nativeInt) { |
| |
| PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate( |
| paint.getPathEffect()); |
| |
| if (effectDelegate instanceof DashPathEffect_Delegate) { |
| DashPathEffect_Delegate dpe = (DashPathEffect_Delegate)effectDelegate; |
| g.setStroke(new BasicStroke( |
| paint.getStrokeWidth(), |
| paint.getJavaCap(), |
| paint.getJavaJoin(), |
| paint.getStrokeMiter(), |
| dpe.getIntervals(), |
| dpe.getPhase())); |
| } else { |
| g.setStroke(new BasicStroke( |
| paint.getStrokeWidth(), |
| paint.getJavaCap(), |
| paint.getJavaJoin(), |
| paint.getStrokeMiter())); |
| } |
| } |
| |
| Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode()); |
| if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) { |
| int mode = ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode(); |
| |
| setModeInGraphics(g, mode); |
| } else { |
| // default mode is src_over |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); |
| |
| // if xfermode wasn't null, then it's something we don't support. log it. |
| if (xfermodeDelegate != null) { |
| Bridge.getLog().warning(null, |
| String.format( |
| "Xfermode '%1$s' is not supported in the Layout Preview.", |
| xfermodeDelegate.getClass().getCanonicalName())); |
| } |
| } |
| |
| return g; |
| } |
| |
| private static void setModeInGraphics(Graphics2D g, int mode) { |
| for (PorterDuff.Mode m : PorterDuff.Mode.values()) { |
| if (m.nativeInt == mode) { |
| setModeInGraphics(g, m); |
| return; |
| } |
| } |
| } |
| |
| private static void setModeInGraphics(Graphics2D g, PorterDuff.Mode mode) { |
| switch (mode) { |
| case CLEAR: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f /*alpha*/)); |
| break; |
| case DARKEN: |
| break; |
| case DST: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, 1.0f /*alpha*/)); |
| break; |
| case DST_ATOP: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1.0f /*alpha*/)); |
| break; |
| case DST_IN: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0f /*alpha*/)); |
| break; |
| case DST_OUT: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, 1.0f /*alpha*/)); |
| break; |
| case DST_OVER: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f /*alpha*/)); |
| break; |
| case LIGHTEN: |
| break; |
| case MULTIPLY: |
| break; |
| case SCREEN: |
| break; |
| case SRC: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f /*alpha*/)); |
| break; |
| case SRC_ATOP: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f /*alpha*/)); |
| break; |
| case SRC_IN: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1.0f /*alpha*/)); |
| break; |
| case SRC_OUT: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, 1.0f /*alpha*/)); |
| break; |
| case SRC_OVER: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f /*alpha*/)); |
| break; |
| case XOR: |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 1.0f /*alpha*/)); |
| break; |
| } |
| } |
| |
| |
| private static void drawBitmap( |
| int nativeCanvas, |
| BufferedImage image, |
| int nativePaintOrZero, |
| int sleft, int stop, int sright, int sbottom, |
| int dleft, int dtop, int dright, int dbottom) { |
| // get the delegate from the native int. |
| Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); |
| if (canvasDelegate == null) { |
| assert false; |
| return; |
| } |
| |
| // get the delegate from the native int. |
| Paint_Delegate paintDelegate = null; |
| if (nativePaintOrZero > 0) { |
| paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero); |
| if (paintDelegate == null) { |
| assert false; |
| return; |
| } |
| } |
| |
| drawBitmap(canvasDelegate, image, paintDelegate, |
| sleft, stop, sright, sbottom, |
| dleft, dtop, dright, dbottom); |
| } |
| |
| private static void drawBitmap( |
| Canvas_Delegate canvasDelegate, |
| BufferedImage image, |
| Paint_Delegate paintDelegate, |
| int sleft, int stop, int sright, int sbottom, |
| int dleft, int dtop, int dright, int dbottom) { |
| |
| Graphics2D g = canvasDelegate.getGraphics2d(); |
| |
| Composite c = null; |
| |
| if (paintDelegate != null) { |
| if (paintDelegate.isFilterBitmap()) { |
| g = (Graphics2D)g.create(); |
| g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, |
| RenderingHints.VALUE_INTERPOLATION_BILINEAR); |
| } |
| } |
| |
| g.drawImage(image, dleft, dtop, dright, dbottom, |
| sleft, stop, sright, sbottom, null); |
| |
| if (paintDelegate != null) { |
| if (paintDelegate.isFilterBitmap()) { |
| g.dispose(); |
| } |
| if (c != null) { |
| g.setComposite(c); |
| } |
| } |
| } |
| |
| } |
| |