Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71e83ff5 authored by Xavier Ducrohet's avatar Xavier Ducrohet Committed by Android (Google) Code Review
Browse files

Merge "LayoutLib: improve bitmap support."

parents afd8d608 b1da1afa
Loading
Loading
Loading
Loading
+194 −25
Original line number Diff line number Diff line
@@ -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;
    }

}
+230 −113

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -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*/);

    }

+4 −0
Original line number Diff line number Diff line
@@ -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);
+83 −45
Original line number Diff line number Diff line
@@ -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,10 +714,14 @@ public class GcSnapshot {
        // it contains the alpha
        int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;

        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(paint.getXfermode());
                Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(xfermode);
                assert xfermodeDelegate != null;
                if (xfermodeDelegate != null) {
                    if (xfermodeDelegate.isSupported()) {
@@ -705,14 +743,14 @@ public class GcSnapshot {
            // 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));
                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[] {