blob: b116d2b3bc2d258990fef4e1d68994f79d31d6fb [file] [log] [blame]
/*
* 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);
}
}
}
}