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

Commit 5de11a18 authored by Xavier Ducrohet's avatar Xavier Ducrohet
Browse files

Implement the layoutlib Bitmap through a native delegate.

This does not implement all the native methods of the
android.graphics.Bitmap class, only what's needed to draw an
ImageView object. The rest will be implemented after Canvas and
Paint have been moved to the native delegate.

Change-Id: Ia0c3b2cafa03871c298deaef5817a25ac1c35521
parent 5164246d
Loading
Loading
Loading
Loading
+0 −278
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 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 java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;

public final class Bitmap extends _Original_Bitmap {

    private BufferedImage mImage;

    public Bitmap(File input) throws IOException {
        super(1, true, null, -1);

        mImage = ImageIO.read(input);
    }

    public Bitmap(InputStream is) throws IOException {
        super(1, true, null, -1);

        mImage = ImageIO.read(is);
    }

    Bitmap(BufferedImage image) {
        super(1, true, null, -1);
        mImage = image;
    }

    public BufferedImage getImage() {
        return mImage;
    }

    // ----- overriden methods

    public enum Config {
        // these native values must match up with the enum in SkBitmap.h
        ALPHA_8     (2),
        RGB_565     (4),
        ARGB_4444   (5),
        ARGB_8888   (6);

        Config(int ni) {
            this.nativeInt = ni;
        }
        final int nativeInt;

        /* package */ static Config nativeToConfig(int ni) {
            return sConfigs[ni];
        }

        private static Config sConfigs[] = {
            null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
        };
    }

    @Override
    public int getWidth() {
        return mImage.getWidth();
    }

    @Override
    public int getHeight() {
        return mImage.getHeight();
    }

    /**
     * Returns an immutable bitmap from the source bitmap. The new bitmap may
     * be the same object as source, or a copy may have been made.
     */
    public static Bitmap createBitmap(Bitmap src) {
        return createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), null, false);
    }

    /**
     * Returns an immutable bitmap from the specified subset of the source
     * bitmap. The new bitmap may be the same object as source, or a copy may
     * have been made.
     *
     * @param source   The bitmap we are subsetting
     * @param x        The x coordinate of the first pixel in source
     * @param y        The y coordinate of the first pixel in source
     * @param width    The number of pixels in each row
     * @param height   The number of rows
     */
    public static Bitmap createBitmap(Bitmap source, int x, int y,
                                      int width, int height) {
        return new Bitmap(source.mImage.getSubimage(x, y, width, height));
    }

    /**
     * Returns an immutable bitmap from subset of the source bitmap,
     * transformed by the optional matrix.
     *
     * @param source   The bitmap we are subsetting
     * @param x        The x coordinate of the first pixel in source
     * @param y        The y coordinate of the first pixel in source
     * @param width    The number of pixels in each row
     * @param height   The number of rows
     * @param m        Option matrix to be applied to the pixels
     * @param filter   true if the source should be filtered.
     *                   Only applies if the matrix contains more than just
     *                   translation.
     * @return A bitmap that represents the specified subset of source
     * @throws IllegalArgumentException if the x, y, width, height values are
     *         outside of the dimensions of the source bitmap.
     */
    public static Bitmap createBitmap(Bitmap source, int x, int y, int width,
                                      int height, Matrix m, boolean filter) {
        checkXYSign(x, y);
        checkWidthHeight(width, height);
        if (x + width > source.getWidth()) {
            throw new IllegalArgumentException(
                    "x + width must be <= bitmap.width()");
        }
        if (y + height > source.getHeight()) {
            throw new IllegalArgumentException(
                    "y + height must be <= bitmap.height()");
        }

        // check if we can just return our argument unchanged
        if (!source.isMutable() && x == 0 && y == 0
                && width == source.getWidth() && height == source.getHeight()
                && (m == null || m.isIdentity())) {
            return source;
        }

        if (m == null || m.isIdentity()) {
            return new Bitmap(source.mImage.getSubimage(x, y, width, height));
        }

        int neww = width;
        int newh = height;
        Paint paint;

        Rect srcR = new Rect(x, y, x + width, y + height);
        RectF dstR = new RectF(0, 0, width, height);

        /*  the dst should have alpha if the src does, or if our matrix
            doesn't preserve rectness
        */
        boolean hasAlpha = source.hasAlpha() || !m.rectStaysRect();
        RectF deviceR = new RectF();
        m.mapRect(deviceR, dstR);
        neww = Math.round(deviceR.width());
        newh = Math.round(deviceR.height());

        Canvas canvas = new Canvas(neww, newh);

        canvas.translate(-deviceR.left, -deviceR.top);
        canvas.concat(m);
        paint = new Paint();
        paint.setFilterBitmap(filter);
        if (!m.rectStaysRect()) {
            paint.setAntiAlias(true);
        }

        canvas.drawBitmap(source, srcR, dstR, paint);

        return new Bitmap(canvas.getImage());
    }

    /**
     * Returns a mutable bitmap with the specified width and height.
     *
     * @param width    The width of the bitmap
     * @param height   The height of the bitmap
     * @param config   The bitmap config to create.
     * @throws IllegalArgumentException if the width or height are <= 0
     */
    public static Bitmap createBitmap(int width, int height, Config config) {
        return new Bitmap(new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB));
    }

    /**
     * Returns a immutable bitmap with the specified width and height, with each
     * pixel value set to the corresponding value in the colors array.
     *
     * @param colors   Array of {@link Color} used to initialize the pixels.
     * @param offset   Number of values to skip before the first color in the
     *                 array of colors.
     * @param stride   Number of colors in the array between rows (must be >=
     *                 width or <= -width).
     * @param width    The width of the bitmap
     * @param height   The height of the bitmap
     * @param config   The bitmap config to create. If the config does not
     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
     *                 bytes in the colors[] will be ignored (assumed to be FF)
     * @throws IllegalArgumentException if the width or height are <= 0, or if
     *         the color array's length is less than the number of pixels.
     */
    public static Bitmap createBitmap(int colors[], int offset, int stride,
                                      int width, int height, Config config) {
        checkWidthHeight(width, height);
        if (Math.abs(stride) < width) {
            throw new IllegalArgumentException("abs(stride) must be >= width");
        }
        int lastScanline = offset + (height - 1) * stride;
        int length = colors.length;
        if (offset < 0 || (offset + width > length)
            || lastScanline < 0
            || (lastScanline + width > length)) {
            throw new ArrayIndexOutOfBoundsException();
        }

        // TODO: create an immutable bitmap...
        throw new UnsupportedOperationException();
    }

    /**
     * Returns a immutable bitmap with the specified width and height, with each
     * pixel value set to the corresponding value in the colors array.
     *
     * @param colors   Array of {@link Color} used to initialize the pixels.
     *                 This array must be at least as large as width * height.
     * @param width    The width of the bitmap
     * @param height   The height of the bitmap
     * @param config   The bitmap config to create. If the config does not
     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
     *                 bytes in the colors[] will be ignored (assumed to be FF)
     * @throws IllegalArgumentException if the width or height are <= 0, or if
     *         the color array's length is less than the number of pixels.
     */
    public static Bitmap createBitmap(int colors[], int width, int height,
                                      Config config) {
        return createBitmap(colors, 0, width, width, height, config);
    }

    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
            int dstHeight, boolean filter) {
        Matrix m;
        synchronized (Bitmap.class) {
            // small pool of just 1 matrix
            m = sScaleMatrix;
            sScaleMatrix = null;
        }

        if (m == null) {
            m = new Matrix();
        }

        final int width = src.getWidth();
        final int height = src.getHeight();
        final float sx = dstWidth  / (float)width;
        final float sy = dstHeight / (float)height;
        m.setScale(sx, sy);
        Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);

        synchronized (Bitmap.class) {
            // do we need to check for null? why not just assign everytime?
            if (sScaleMatrix == null) {
                sScaleMatrix = m;
            }
        }

        return b;
    }


}
+3 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.graphics;

import com.android.layoutlib.api.IDensityBasedResourceValue.Density;

import android.content.res.AssetManager;
import android.content.res.Resources;
import android.util.DisplayMetrics;
@@ -454,7 +456,7 @@ public class BitmapFactory {
            // into is.read(...) This number is not related to the value passed
            // to mark(...) above.
            try {
                bm = new Bitmap(is);
                bm = Bitmap_Delegate.createBitmap(is, Density.MEDIUM);
            } catch (IOException e) {
                return null;
            }
+327 −0
Original line number Diff line number Diff line
/*
 * 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.api.IDensityBasedResourceValue.Density;
import com.android.layoutlib.bridge.DelegateManager;

import android.graphics.Bitmap.Config;
import android.os.Parcel;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.Buffer;

import javax.imageio.ImageIO;

/**
 * Delegate implementing the native methods of android.graphics.Bitmap
 *
 * Through the layoutlib_create tool, the original native methods of Bitmap 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 Bitmap class.
 *
 * @see DelegateManager
 *
 */
public class Bitmap_Delegate {

    // ---- delegate manager ----
    private static final DelegateManager<Bitmap_Delegate> sManager =
            new DelegateManager<Bitmap_Delegate>();

    // ---- delegate helper data ----

    // ---- delegate data ----
    private BufferedImage mImage;
    private boolean mHasAlpha = true;

    // ---- Public Helper methods ----

    /**
     * Creates and returns a {@link Bitmap} initialized with the given file content.
     */
    public static Bitmap createBitmap(File input, Density density) throws IOException {
        // create a delegate with the content of the file.
        Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));

        return createBitmap(delegate, density.getValue());
    }

    /**
     * Creates and returns a {@link Bitmap} initialized with the given stream content.
     */
    public static Bitmap createBitmap(InputStream input, Density density) throws IOException {
        // create a delegate with the content of the stream.
        Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));

        return createBitmap(delegate, density.getValue());
    }

    /**
     * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
     */
    public static Bitmap createBitmap(BufferedImage image, Density density) throws IOException {
        // create a delegate with the given image.
        Bitmap_Delegate delegate = new Bitmap_Delegate(image);

        return createBitmap(delegate, density.getValue());
    }

    /**
     * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
     */
    public static BufferedImage getImage(Bitmap bitmap) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap);
        if (delegate == null) {
            assert false;
            return null;
        }

        return delegate.mImage;
    }

    public static int getBufferedImageType(int nativeBitmapConfig) {
        switch (Config.sConfigs[nativeBitmapConfig]) {
            case ALPHA_8:
                return BufferedImage.TYPE_INT_ARGB;
            case RGB_565:
                return BufferedImage.TYPE_INT_ARGB;
            case ARGB_4444:
                return BufferedImage.TYPE_INT_ARGB;
            case ARGB_8888:
                return BufferedImage.TYPE_INT_ARGB;
        }

        return BufferedImage.TYPE_INT_ARGB;
    }

    // ---- native methods ----

    /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
            int height, int nativeConfig, boolean mutable) {
        int imageType = getBufferedImageType(nativeConfig);

        // create the image
        BufferedImage image = new BufferedImage(width, height, imageType);

        // fill it
        //image.setRGB(x, y, rgb)

        // create a delegate with the content of the stream.
        Bitmap_Delegate delegate = new Bitmap_Delegate(image);

        return createBitmap(delegate, Bitmap.getDefaultDensity());
    }

    /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
    }

    /*package*/ static void nativeDestructor(int nativeBitmap) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
    }

    /*package*/ static void nativeRecycle(int nativeBitmap) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
    }

    /*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");
    }

    /*package*/ static void nativeErase(int nativeBitmap, int color) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
        if (delegate == null) {
            assert false;
            return;
        }

        BufferedImage image = delegate.mImage;

        Graphics2D g = image.createGraphics();
        try {
            if (delegate.mHasAlpha == false) {
                color |= color & 0xFF000000;
            }
            g.setColor(new java.awt.Color(color));

            g.fillRect(0, 0, image.getWidth(), image.getHeight());
        } finally {
            g.dispose();
        }
    }

    /*package*/ static int nativeWidth(int nativeBitmap) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
        if (delegate == null) {
            assert false;
            return 0;
        }

        return delegate.mImage.getWidth();
    }

    /*package*/ static int nativeHeight(int nativeBitmap) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
        if (delegate == null) {
            assert false;
            return 0;
        }

        return delegate.mImage.getHeight();
    }

    /*package*/ static int nativeRowBytes(int nativeBitmap) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
        if (delegate == null) {
            assert false;
            return 0;
        }

        return delegate.mImage.getWidth();
    }

    /*package*/ static int nativeConfig(int nativeBitmap) {
        return Config.ARGB_8888.nativeInt;
    }

    /*package*/ static boolean nativeHasAlpha(int nativeBitmap) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
        if (delegate == null) {
            assert false;
            return true;
        }

        return delegate.mHasAlpha;
    }

    /*package*/ static int nativeGetPixel(int nativeBitmap, int x, int y) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
        if (delegate == null) {
            assert false;
            return 0;
        }

        return delegate.mImage.getRGB(x, y);
    }

    /*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");
    }


    /*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");
    }

    /*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");
    }

    /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsToBuffer");
    }

    /*package*/ static void nativeCopyPixelsFromBuffer(int nb, Buffer src) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsFromBuffer");
    }

    /*package*/ static int nativeGenerationId(int nativeBitmap) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeGenerationId");
    }

    /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCreateFromParcel");
    }

    /*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");
    }

    /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint,
            int[] offsetXY) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeExtractAlpha");
    }


    /*package*/ static void nativePrepareToDraw(int nativeBitmap) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativePrepareToDraw");
    }

    /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) {
        // get the delegate from the native int.
        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
        if (delegate == null) {
            assert false;
            return;
        }

        delegate.mHasAlpha = hasAlpha;
    }

    /*package*/ static boolean nativeSameAs(int nb0, int nb1) {
        // FIXME implement native delegate
        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSameAs");
    }

    // ---- Private delegate/helper methods ----

    private Bitmap_Delegate(BufferedImage image) {
        mImage = image;
    }

    private static Bitmap createBitmap(Bitmap_Delegate delegate, int density) {
        // get its native_int
        int nativeInt = sManager.addDelegate(delegate);

        // and create/return a new Bitmap with it
        return new Bitmap(nativeInt, true /*isMutable*/, null /*ninePatchChunk*/, density);
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ public class Canvas extends _Original_Canvas {

    public Canvas(Bitmap bitmap) {
        mLogger = null;
        mBufferedImage = bitmap.getImage();
        mBufferedImage = Bitmap_Delegate.getImage(bitmap);
        mGraphicsStack.push(mBufferedImage.createGraphics());
    }

@@ -227,7 +227,7 @@ public class Canvas extends _Original_Canvas {
     */
    @Override
    public void setBitmap(Bitmap bitmap) {
        mBufferedImage = bitmap.getImage();
        mBufferedImage = Bitmap_Delegate.getImage(bitmap);
        mGraphicsStack.push(mBufferedImage.createGraphics());
    }

@@ -557,7 +557,7 @@ public class Canvas extends _Original_Canvas {

    private void drawBitmap(Bitmap bitmap, int sleft, int stop, int sright, int sbottom, int dleft,
            int dtop, int dright, int dbottom, Paint paint) {
        BufferedImage image = bitmap.getImage();
        BufferedImage image = Bitmap_Delegate.getImage(bitmap);

        Graphics2D g = getGraphics2d();

+44 −44

File changed.

Preview size limit exceeded, changes collapsed.

Loading