diff options
author | Xavier Ducrohet <xav@android.com> | 2010-12-22 10:13:23 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2010-12-22 10:30:53 -0800 |
commit | b1da1afa7418960b650780250bbd34c81af61aa3 (patch) | |
tree | 45b4798c9a796223bb854d8a5c56a2b5b2c9461b /tools | |
parent | d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5 (diff) | |
download | frameworks_base-b1da1afa7418960b650780250bbd34c81af61aa3.zip frameworks_base-b1da1afa7418960b650780250bbd34c81af61aa3.tar.gz frameworks_base-b1da1afa7418960b650780250bbd34c81af61aa3.tar.bz2 |
LayoutLib: improve bitmap support.
Change-Id: I703c2bdf51380b54fd5c20b08d3bc74833d9bc6e
Diffstat (limited to 'tools')
5 files changed, 512 insertions, 184 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index b5f5005..2ce6a36 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.ide.common.rendering.api.ResourceDensity; +import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import android.graphics.Bitmap.Config; @@ -29,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.Buffer; +import java.util.Arrays; import javax.imageio.ImageIO; @@ -57,6 +59,7 @@ public final class Bitmap_Delegate { private final Config mConfig; private BufferedImage mImage; private boolean mHasAlpha = true; + private int mGenerationId = 0; // ---- Public Helper methods ---- @@ -173,6 +176,22 @@ public final class Bitmap_Delegate { return mConfig; } + /** + * Returns the hasAlpha rendering hint + * @return true if the bitmap alpha should be used at render time + */ + public boolean hasAlpha() { + return mHasAlpha && mConfig != Config.RGB_565; + } + + /** + * Update the generationId. + * + * @see Bitmap#getGenerationId() + */ + public void change() { + mGenerationId++; + } // ---- native methods ---- @@ -183,7 +202,9 @@ public final class Bitmap_Delegate { // create the image BufferedImage image = new BufferedImage(width, height, imageType); - // FIXME fill the bitmap! + if (colors != null) { + image.setRGB(0, 0, width, height, colors, offset, stride); + } // create a delegate with the content of the stream. Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.sConfigs[nativeConfig]); @@ -192,8 +213,31 @@ public final class Bitmap_Delegate { } /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap"); + Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap); + if (srcBmpDelegate == null) { + assert false; + return null; + } + + BufferedImage srcImage = srcBmpDelegate.getImage(); + + int width = srcImage.getWidth(); + int height = srcImage.getHeight(); + + int imageType = getBufferedImageType(nativeConfig); + + // create the image + BufferedImage image = new BufferedImage(width, height, imageType); + + // copy the source image into the image. + int[] argb = new int[width * height]; + srcImage.getRGB(0, 0, width, height, argb, 0, width); + image.setRGB(0, 0, width, height, argb, 0, width); + + // create a delegate with the content of the stream. + Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.sConfigs[nativeConfig]); + + return createBitmap(delegate, isMutable, Bitmap.getDefaultDensity()); } /*package*/ static void nativeDestructor(int nativeBitmap) { @@ -206,8 +250,8 @@ public final class Bitmap_Delegate { /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap"); + Bridge.getLog().error(null, "Bitmap.compress() is not supported"); + return true; } /*package*/ static void nativeErase(int nativeBitmap, int color) { @@ -222,7 +266,7 @@ public final class Bitmap_Delegate { Graphics2D g = image.createGraphics(); try { - g.setColor(new java.awt.Color(color, delegate.mHasAlpha)); + g.setColor(new java.awt.Color(color, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); } finally { @@ -298,20 +342,35 @@ public final class Bitmap_Delegate { /*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeGetPixels"); + Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); + if (delegate == null) { + assert false; + return; + } + + delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride); } /*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSetPixel"); + Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); + if (delegate == null) { + assert false; + return; + } + + delegate.getImage().setRGB(x, y, color); } /*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSetPixels"); + Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); + if (delegate == null) { + assert false; + return; + } + + delegate.getImage().setRGB(x, y, width, height, colors, offset, stride); } /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) { @@ -325,31 +384,68 @@ public final class Bitmap_Delegate { } /*package*/ static int nativeGenerationId(int nativeBitmap) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeGenerationId"); + Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mGenerationId; } /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCreateFromParcel"); + // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only + // used during aidl call so really this should not be called. + Bridge.getLog().error(null, + "AIDL is not suppored, and therefore bitmap cannot be created from parcels"); + return null; } /*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable, int density, Parcel p) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeWriteToParcel"); + // This is only called when sending a bitmap through aidl, so really this should not + // be called. + Bridge.getLog().error(null, + "AIDL is not suppored, and therefore bitmap cannot be written to parcels"); + return false; } /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint, int[] offsetXY) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeExtractAlpha"); - } + Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap); + if (bitmap == null) { + assert false; + return null; + } + + Paint_Delegate paint = null; + if (nativePaint > 0) { + paint = Paint_Delegate.getDelegate(nativePaint); + if (paint == null) { + assert false; + return null; + } + } + + if (paint != null && paint.getMaskFilter() != 0) { + Bridge.getLog().fidelityWarning(null, + "MaskFilter not supported in Bitmap.extractAlpha", + null); + } + int alpha = paint != null ? paint.getAlpha() : 0xFF; + BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha); + + // create the delegate. The actual Bitmap config is only an alpha channel + Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8); + + // the density doesn't matter, it's set by the Java method. + return createBitmap(delegate, false /*isMutable*/, + ResourceDensity.DEFAULT_DENSITY /*density*/); + } /*package*/ static void nativePrepareToDraw(int nativeBitmap) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativePrepareToDraw"); + // nothing to be done here. } /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) { @@ -364,8 +460,48 @@ public final class Bitmap_Delegate { } /*package*/ static boolean nativeSameAs(int nb0, int nb1) { - // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSameAs"); + Bitmap_Delegate delegate1 = sManager.getDelegate(nb0); + if (delegate1 == null) { + assert false; + return false; + } + + Bitmap_Delegate delegate2 = sManager.getDelegate(nb1); + if (delegate2 == null) { + assert false; + return false; + } + + BufferedImage image1 = delegate1.getImage(); + BufferedImage image2 = delegate2.getImage(); + if (delegate1.mConfig != delegate2.mConfig || + image1.getWidth() != image2.getWidth() || + image1.getHeight() != image2.getHeight()) { + return false; + } + + // get the internal data + int w = image1.getWidth(); + int h = image2.getHeight(); + int[] argb1 = new int[w*h]; + int[] argb2 = new int[w*h]; + + image1.getRGB(0, 0, w, h, argb1, 0, w); + image2.getRGB(0, 0, w, h, argb2, 0, w); + + // compares + if (delegate1.mConfig == Config.ALPHA_8) { + // in this case we have to manually compare the alpha channel as the rest is garbage. + final int length = w*h; + for (int i = 0 ; i < length ; i++) { + if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) { + return false; + } + } + return true; + } + + return Arrays.equals(argb1, argb2); } // ---- Private delegate/helper methods ---- @@ -382,4 +518,37 @@ public final class Bitmap_Delegate { // and create/return a new Bitmap with it return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density); } + + /** + * Creates and returns a copy of a given BufferedImage. + * <p/> + * if alpha is different than 255, then it is applied to the alpha channel of each pixel. + * + * @param image the image to copy + * @param imageType the type of the new image + * @param alpha an optional alpha modifier + * @return a new BufferedImage + */ + /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) { + int w = image.getWidth(); + int h = image.getHeight(); + + BufferedImage result = new BufferedImage(w, h, imageType); + + int[] argb = new int[w * h]; + image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); + + if (alpha != 255) { + final int length = argb.length; + for (int i = 0 ; i < length; i++) { + int a = (argb[i] >>> 24 * alpha) / 255; + argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF); + } + } + + result.setRGB(0, 0, w, h, argb, 0, w); + + return result; + } + } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index df0b997..88ec88e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -55,8 +55,10 @@ public final class Canvas_Delegate { // ---- delegate helper data ---- + private final static boolean[] sBoolOut = new boolean[1]; + // ---- delegate data ---- - private BufferedImage mBufferedImage; + private Bitmap_Delegate mBitmap; private GcSnapshot mSnapshot; // ---- Public Helper methods ---- @@ -97,7 +99,7 @@ public final class Canvas_Delegate { return 0; } - return canvasDelegate.mBufferedImage.getWidth(); + return canvasDelegate.mBitmap.getImage().getWidth(); } /*package*/ static int getHeight(Canvas thisCanvas) { @@ -108,7 +110,7 @@ public final class Canvas_Delegate { return 0; } - return canvasDelegate.mBufferedImage.getHeight(); + return canvasDelegate.mBitmap.getImage().getHeight(); } /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { @@ -258,7 +260,7 @@ public final class Canvas_Delegate { final float[] pts, final int offset, final int count, Paint paint) { draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/, - new GcSnapshot.Drawable() { + false /*forceSrcMode*/, new GcSnapshot.Drawable() { public void draw(Graphics2D graphics, Paint_Delegate paint) { for (int i = 0 ; i < count ; i += 4) { graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1], @@ -279,7 +281,7 @@ public final class Canvas_Delegate { 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()); + Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate); return sManager.addDelegate(newDelegate); } else { @@ -305,7 +307,7 @@ public final class Canvas_Delegate { return; } - canvasDelegate.setBitmap(bitmapDelegate.getImage()); + canvasDelegate.setBitmap(bitmapDelegate); } /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds, @@ -535,8 +537,8 @@ public final class Canvas_Delegate { return; } - final int w = canvasDelegate.mBufferedImage.getWidth(); - final int h = canvasDelegate.mBufferedImage.getHeight(); + final int w = canvasDelegate.mBitmap.getImage().getWidth(); + final int h = canvasDelegate.mBitmap.getImage().getHeight(); draw(nativeCanvas, new GcSnapshot.Drawable() { public void draw(Graphics2D graphics, Paint_Delegate paint) { @@ -566,10 +568,11 @@ public final class Canvas_Delegate { final float startX, final float startY, final float stopX, final float stopY, int paint) { - draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - public void draw(Graphics2D graphics, Paint_Delegate paint) { - graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); - } + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); + } }); } @@ -581,43 +584,47 @@ public final class Canvas_Delegate { /*package*/ static void native_drawRect(int nativeCanvas, final float left, final float top, final float right, final float bottom, int paint) { - draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - public void draw(Graphics2D graphics, Paint_Delegate paint) { - int style = paint.getStyle(); + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + int style = paint.getStyle(); - // draw - if (style == Paint.Style.FILL.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - graphics.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); - } + // draw + if (style == Paint.Style.FILL.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.fillRect((int)left, (int)top, + (int)(right-left), (int)(bottom-top)); + } - if (style == Paint.Style.STROKE.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - graphics.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); - } - } + if (style == Paint.Style.STROKE.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.drawRect((int)left, (int)top, + (int)(right-left), (int)(bottom-top)); + } + } }); } /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) { if (oval.right > oval.left && oval.bottom > oval.top) { - draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - public void draw(Graphics2D graphics, Paint_Delegate paint) { - int style = paint.getStyle(); - - // draw - if (style == Paint.Style.FILL.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - graphics.fillOval((int)oval.left, (int)oval.top, - (int)oval.width(), (int)oval.height()); - } + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + int style = paint.getStyle(); + + // draw + if (style == Paint.Style.FILL.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.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) { - graphics.drawOval((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) { + graphics.drawOval((int)oval.left, (int)oval.top, + (int)oval.width(), (int)oval.height()); + } + } }); } } @@ -640,26 +647,28 @@ public final class Canvas_Delegate { /*package*/ static void native_drawRoundRect(int nativeCanvas, final RectF rect, final float rx, final float ry, int paint) { - draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - - public void draw(Graphics2D graphics, Paint_Delegate paint) { - int style = paint.getStyle(); - - // draw - if (style == Paint.Style.FILL.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - graphics.fillRoundRect( - (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), - (int)rx, (int)ry); - } + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + int style = paint.getStyle(); + + // draw + if (style == Paint.Style.FILL.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.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) { - graphics.drawRoundRect( - (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) { + graphics.drawRoundRect( + (int)rect.left, (int)rect.top, + (int)rect.width(), (int)rect.height(), + (int)rx, (int)ry); + } + } }); } @@ -671,21 +680,22 @@ public final class Canvas_Delegate { return; } - draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - public void draw(Graphics2D graphics, Paint_Delegate paint) { - Shape shape = pathDelegate.getJavaShape(); - int style = paint.getStyle(); + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + Shape shape = pathDelegate.getJavaShape(); + int style = paint.getStyle(); - if (style == Paint.Style.FILL.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - graphics.fill(shape); - } + if (style == Paint.Style.FILL.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.fill(shape); + } - if (style == Paint.Style.STROKE.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - graphics.draw(shape); - } - } + if (style == Paint.Style.STROKE.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.draw(shape); + } + } }); } @@ -706,7 +716,7 @@ public final class Canvas_Delegate { float right = left + image.getWidth(); float bottom = top + image.getHeight(); - drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, + drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(), image.getHeight(), (int)left, (int)top, (int)right, (int)bottom); } @@ -726,11 +736,11 @@ public final class Canvas_Delegate { BufferedImage image = bitmapDelegate.getImage(); if (src == null) { - drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, + drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(), image.getHeight(), (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); } else { - drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, + drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, src.left, src.top, src.width(), src.height(), (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); } @@ -751,29 +761,87 @@ public final class Canvas_Delegate { BufferedImage image = bitmapDelegate.getImage(); if (src == null) { - drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, + drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(), image.getHeight(), dst.left, dst.top, dst.right, dst.bottom); } else { - drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, + drawBitmap(nativeCanvas, bitmapDelegate, 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, + int offset, int stride, final float x, + final float y, int width, int height, boolean hasAlpha, int nativePaintOrZero) { - // FIXME - throw new UnsupportedOperationException(); + + // create a temp BufferedImage containing the content. + final BufferedImage image = new BufferedImage(width, height, + hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + image.setRGB(0, 0, width, height, colors, offset, stride); + + draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + if (paint != null && paint.isFilterBitmap()) { + graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + } + + graphics.drawImage(image, (int) x, (int) y, null); + } + }); } /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, int nMatrix, int nPaint) { - // FIXME - throw new UnsupportedOperationException(); + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); + if (canvasDelegate == null) { + assert false; + return; + } + + // get the delegate from the native int. + Paint_Delegate paintDelegate = null; + if (nPaint > 0) { + paintDelegate = Paint_Delegate.getDelegate(nPaint); + if (paintDelegate == null) { + assert false; + return; + } + } + + // get the delegate from the native int. + Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap); + if (bitmapDelegate == null) { + assert false; + return; + } + + final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut); + + Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); + if (matrixDelegate == null) { + assert false; + return; + } + + final AffineTransform mtx = matrixDelegate.getAffineTransform(); + + canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + if (paint != null && paint.isFilterBitmap()) { + graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + } + + //FIXME add support for canvas, screen and bitmap densities. + graphics.drawImage(image, mtx, null); + } + }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/); } /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap, @@ -795,8 +863,8 @@ public final class Canvas_Delegate { /*package*/ static void native_drawText(int nativeCanvas, final char[] text, final int index, final int count, final float startX, final float startY, int flags, int paint) { - draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, + new GcSnapshot.Drawable() { public void draw(Graphics2D graphics, Paint_Delegate paint) { // WARNING: the logic in this method is similar to Paint.measureText. // Any change to this method should be reflected in Paint.measureText @@ -974,11 +1042,11 @@ public final class Canvas_Delegate { // ---- Private delegate/helper methods ---- /** - * Executes a {@link Drawable} with a given canvas and paint. + * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint. * <p>Note that the drawable may actually be executed several times if there are * layers involved (see {@link #saveLayer(RectF, int, int)}. */ - private static void draw(int nCanvas, int nPaint, boolean compositeOnly, + private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode, GcSnapshot.Drawable drawable) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); @@ -997,11 +1065,11 @@ public final class Canvas_Delegate { } } - canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly); + canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode); } /** - * Executes a {@link Drawable} with a given canvas. No paint object will be provided + * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}. * <p>Note that the drawable may actually be executed several times if there are * layers involved (see {@link #saveLayer(RectF, int, int)}. @@ -1017,8 +1085,8 @@ public final class Canvas_Delegate { canvasDelegate.mSnapshot.draw(drawable); } - private Canvas_Delegate(BufferedImage image) { - mSnapshot = GcSnapshot.createDefaultSnapshot(mBufferedImage = image); + private Canvas_Delegate(Bitmap_Delegate bitmap) { + mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap); } private Canvas_Delegate() { @@ -1078,16 +1146,15 @@ public final class Canvas_Delegate { return mSnapshot.clipRect(left, top, right, bottom, regionOp); } - private void setBitmap(BufferedImage image) { - mBufferedImage = image; + private void setBitmap(Bitmap_Delegate bitmap) { + mBitmap = bitmap; assert mSnapshot.size() == 1; - mSnapshot.setImage(mBufferedImage); + mSnapshot.setBitmap(mBitmap); } private static void drawBitmap( int nativeCanvas, - final BufferedImage image, - final Bitmap.Config mBitmapConfig, + Bitmap_Delegate bitmap, int nativePaintOrZero, final int sleft, final int stop, final int sright, final int sbottom, final int dleft, final int dtop, final int dright, final int dbottom) { @@ -1108,23 +1175,9 @@ public final class Canvas_Delegate { } } - // if the bitmap config is alpha_8, then we erase all color value from it - // before drawing it. - if (mBitmapConfig == Bitmap.Config.ALPHA_8) { - int w = image.getWidth(); - int h = image.getHeight(); - int[] argb = new int[w*h]; - image.getRGB(0, 0, image.getWidth(), image.getHeight(), - argb, 0, image.getWidth()); - - final int length = argb.length; - for (int i = 0 ; i < length; i++) { - argb[i] &= 0xFF000000; - } - image.setRGB(0, 0, w, h, argb, 0, w); - } + final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut); - draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, + draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0], new GcSnapshot.Drawable() { public void draw(Graphics2D graphics, Paint_Delegate paint) { if (paint != null && paint.isFilterBitmap()) { @@ -1138,5 +1191,69 @@ public final class Canvas_Delegate { } }); } + + + /** + * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate. + * The image returns, through a 1-size boolean array, whether the drawing code should + * use a SRC composite no matter what the paint says. + * + * @param bitmap the bitmap + * @param paint the paint that will be used to draw + * @param forceSrcMode whether the composite will have to be SRC + * @return the image to draw + */ + private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint, + boolean[] forceSrcMode) { + BufferedImage image = bitmap.getImage(); + forceSrcMode[0] = false; + + // if the bitmap config is alpha_8, then we erase all color value from it + // before drawing it. + if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) { + fixAlpha8Bitmap(image); + } else if (bitmap.hasAlpha() == false) { + // hasAlpha is merely a rendering hint. There can in fact be alpha values + // in the bitmap but it should be ignored at drawing time. + // There is two ways to do this: + // - override the composite to be SRC. This can only be used if the composite + // was going to be SRC or SRC_OVER in the first place + // - Create a different bitmap to draw in which all the alpha channel values is set + // to 0xFF. + if (paint != null) { + int xfermode = paint.getXfermode(); + if (xfermode > 0) { + Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(xfermode); + if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) { + PorterDuff.Mode mode = + ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode(); + + forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER || + mode == PorterDuff.Mode.SRC; + } + } + } + + // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB + if (forceSrcMode[0] == false) { + image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF); + } + } + + return image; + } + + private static void fixAlpha8Bitmap(final BufferedImage image) { + int w = image.getWidth(); + int h = image.getHeight(); + int[] argb = new int[w * h]; + image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); + + final int length = argb.length; + for (int i = 0 ; i < length; i++) { + argb[i] &= 0xFF000000; + } + image.setRGB(0, 0, w, h, argb, 0, w); + } } diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index e64a69a..25e0795 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -164,7 +164,7 @@ public final class NinePatch_Delegate { chunkObject.draw(bitmap_delegate.getImage(), graphics, left, top, right - left, bottom - top, destDensity, srcDensity); } - }, paint_delegate, true /*compositeOnly*/); + }, paint_delegate, true /*compositeOnly*/, false /*forceSrcMode*/); } diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java index 097dfce..b7d5dc9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java @@ -45,6 +45,10 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate { // ---- Public Helper methods ---- + public PorterDuff.Mode getMode() { + return getPorterDuffMode(mMode); + } + @Override public Composite getComposite(int alpha) { return getComposite(getPorterDuffMode(mMode), alpha); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java index 103e262..27268fc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -18,12 +18,11 @@ package com.android.layoutlib.bridge.impl; import com.android.layoutlib.bridge.Bridge; +import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint_Delegate; import android.graphics.PathEffect_Delegate; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode_Delegate; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -81,11 +80,33 @@ public class GcSnapshot { */ private static class Layer { private final Graphics2D mGraphics; + private final Bitmap_Delegate mBitmap; private final BufferedImage mImage; private BufferedImage mOriginalCopy; + /** + * Creates a layer with a graphics and a bitmap. + * + * @param graphics the graphics + * @param bitmap the bitmap + */ + Layer(Graphics2D graphics, Bitmap_Delegate bitmap) { + mGraphics = graphics; + mBitmap = bitmap; + mImage = mBitmap.getImage(); + } + + /** + * Creates a layer with a graphics and an image. If the image belongs to a + * {@link Bitmap_Delegate}, then {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should + * be used. + * + * @param graphics the graphics + * @param image the image + */ Layer(Graphics2D graphics, BufferedImage image) { mGraphics = graphics; + mBitmap = null; mImage = image; } @@ -100,6 +121,10 @@ public class GcSnapshot { } Layer makeCopy() { + if (mBitmap != null) { + return new Layer((Graphics2D) mGraphics.create(), mBitmap); + } + return new Layer((Graphics2D) mGraphics.create(), mImage); } @@ -111,22 +136,28 @@ public class GcSnapshot { BufferedImage getOriginalCopy() { return mOriginalCopy; } + + void change() { + if (mBitmap != null) { + mBitmap.change(); + } + } } /** - * Creates the root snapshot associating it with a given image. + * Creates the root snapshot associating it with a given bitmap. * <p> - * If <var>image</var> is null, then {@link GcSnapshot#setImage(BufferedImage)} must be + * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be * called before the snapshot can be used to draw. Transform and clip operations are permitted * before. * * @param image the image to associate to the snapshot or null. * @return the root snapshot */ - public static GcSnapshot createDefaultSnapshot(BufferedImage image) { + public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) { GcSnapshot snapshot = new GcSnapshot(); - if (image != null) { - snapshot.setImage(image); + if (bitmap != null) { + snapshot.setBitmap(bitmap); } return snapshot; @@ -295,19 +326,19 @@ public class GcSnapshot { } /** - * Link the snapshot to a BufferedImage. + * Link the snapshot to a Bitmap_Delegate. * <p/> * This is only for the case where the snapshot was created with a null image when calling - * {@link #createDefaultSnapshot(BufferedImage)}, and is therefore not yet linked to + * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to * a previous snapshot. * <p/> * If any transform or clip information was set before, they are put into the Graphics object. - * @param image the buffered image to link to. + * @param bitmap the bitmap to link to. */ - public void setImage(BufferedImage image) { + public void setBitmap(Bitmap_Delegate bitmap) { assert mLayers.size() == 0; - Graphics2D graphics2D = image.createGraphics(); - mLayers.add(new Layer(graphics2D, image)); + Graphics2D graphics2D = bitmap.getImage().createGraphics(); + mLayers.add(new Layer(graphics2D, bitmap)); if (mTransform != null) { graphics2D.setTransform(mTransform); mTransform = null; @@ -483,7 +514,7 @@ public class GcSnapshot { * @param drawable */ public void draw(Drawable drawable) { - draw(drawable, null, false /*compositeOnly*/); + draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/); } /** @@ -494,30 +525,33 @@ public class GcSnapshot { * @param paint * @param compositeOnly whether the paint is used for composite only. This is typically * the case for bitmaps. + * @param forceSrcMode if true, this overrides the composite to be SRC */ - public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly) { + public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly, + boolean forceSrcMode) { // if we clip to the layer, then we only draw in the layer if (mLocalLayer != null && (mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) != 0) { - drawInLayer(mLocalLayer, drawable, paint, compositeOnly); + drawInLayer(mLocalLayer, drawable, paint, compositeOnly, forceSrcMode); } else { // draw in all the layers for (Layer layer : mLayers) { - drawInLayer(layer, drawable, paint, compositeOnly); + drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode); } } } private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint, - boolean compositeOnly) { + boolean compositeOnly, boolean forceSrcMode) { Graphics2D originalGraphics = layer.getGraphics(); // get a Graphics2D object configured with the drawing parameters. Graphics2D configuredGraphics2D = paint != null ? - createCustomGraphics(originalGraphics, paint, compositeOnly) : + createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) : (Graphics2D) originalGraphics.create(); try { drawable.draw(configuredGraphics2D, paint); + layer.change(); } finally { // dispose Graphics2D object configuredGraphics2D.dispose(); @@ -557,7 +591,7 @@ public class GcSnapshot { // now draw put the content of the local layer onto the layer, using the paint // information Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint, - true /*alphaOnly*/); + true /*alphaOnly*/, false /*forceSrcMode*/); g.drawImage(mLocalLayer.getImage(), mLocalLayerRegion.left, mLocalLayerRegion.top, @@ -603,7 +637,7 @@ public class GcSnapshot { * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. */ private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint, - boolean compositeOnly) { + boolean compositeOnly, boolean forceSrcMode) { // make new one graphics Graphics2D g = (Graphics2D) original.create(); @@ -680,39 +714,43 @@ public class GcSnapshot { // it contains the alpha int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF; - boolean customXfermode = false; - int xfermode = paint.getXfermode(); - if (xfermode > 0) { - Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode()); - assert xfermodeDelegate != null; - if (xfermodeDelegate != null) { - if (xfermodeDelegate.isSupported()) { - Composite composite = xfermodeDelegate.getComposite(alpha); - assert composite != null; - if (composite != null) { - g.setComposite(composite); - customXfermode = true; + if (forceSrcMode) { + g.setComposite(AlphaComposite.getInstance( + AlphaComposite.SRC, (float) alpha / 255.f)); + } else { + boolean customXfermode = false; + int xfermode = paint.getXfermode(); + if (xfermode > 0) { + Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(xfermode); + assert xfermodeDelegate != null; + if (xfermodeDelegate != null) { + if (xfermodeDelegate.isSupported()) { + Composite composite = xfermodeDelegate.getComposite(alpha); + assert composite != null; + if (composite != null) { + g.setComposite(composite); + customXfermode = true; + } + } else { + Bridge.getLog().fidelityWarning(null, + xfermodeDelegate.getSupportMessage(), + null); } - } else { - Bridge.getLog().fidelityWarning(null, - xfermodeDelegate.getSupportMessage(), - null); } } - } - // if there was no custom xfermode, but we have alpha (due to a shader and a non - // opaque alpha channel in the paint color), then we create an AlphaComposite anyway - // that will handle the alpha. - if (customXfermode == false && alpha != 0xFF) { - g.setComposite(PorterDuffXfermode_Delegate.getComposite( - PorterDuff.Mode.SRC_OVER, alpha)); + // if there was no custom xfermode, but we have alpha (due to a shader and a non + // opaque alpha channel in the paint color), then we create an AlphaComposite anyway + // that will handle the alpha. + if (customXfermode == false && alpha != 0xFF) { + g.setComposite(AlphaComposite.getInstance( + AlphaComposite.SRC_OVER, (float) alpha / 255.f)); + } } return g; } - private void mapRect(AffineTransform matrix, RectF dst, RectF src) { // array with 4 corners float[] corners = new float[] { |