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

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

Merge "Implement the layoutlib Bitmap through a native delegate."

parents b417115a 5de11a18
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