package com.glview.graphics;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Build;
import android.util.DisplayMetrics;
public class Bitmap {
/**
* Indicates that the bitmap was created for an unknown pixel density.
*
* @see Bitmap#getDensity()
* @see Bitmap#setDensity(int)
*/
public static final int DENSITY_NONE = 0;
private android.graphics.Bitmap mBitmap;
private byte[] lock = new byte[0];
private int mWidth;
private int mHeight;
private boolean mRecycled;
private int mGenerationId;
private int mAllocationByteCount;
private int mByteCount;
private int mRowBytes;
private byte[] mNinePatchChunk;
private boolean mIsMutable;
private boolean mHasMipMap;
private boolean mHasAlpha;
// Package-scoped for fast access.
int mDensity = getDefaultDensity();
private static volatile int sDefaultDensity = -1;
/**
* For backwards compatibility, allows the app layer to change the default
* density when running old apps.
* @hide
*/
public static void setDefaultDensity(int density) {
sDefaultDensity = density;
}
static int getDefaultDensity() {
if (sDefaultDensity >= 0) {
return sDefaultDensity;
}
//noinspection deprecation
sDefaultDensity = DisplayMetrics.DENSITY_DEFAULT;
return sDefaultDensity;
}
public Bitmap() {
}
public Bitmap(android.graphics.Bitmap bitmap) {
setBitmap(bitmap);
}
protected final void setBitmap(android.graphics.Bitmap bitmap) {
synchronized (lock) {
this.mBitmap = bitmap;
initialProperties();
}
}
public final android.graphics.Bitmap getBitmap() {
synchronized (lock) {
if (mBitmap == null) {
setBitmap(onGotBitmap());
}
return mBitmap;
}
}
private void initialProperties() {
if (mBitmap != null) {
mWidth = mBitmap.getWidth();
mHeight = mBitmap.getHeight();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
mGenerationId = mBitmap.getGenerationId();
} else {
mGenerationId = 0;
}
mByteCount = mBitmap.getByteCount();
mAllocationByteCount = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? mBitmap.getAllocationByteCount() : mByteCount;
mRowBytes = mBitmap.getRowBytes();
mDensity = mBitmap.getDensity();
mNinePatchChunk = mBitmap.getNinePatchChunk();
mRecycled = mBitmap.isRecycled();
mIsMutable = mBitmap.isMutable();
mHasAlpha = mBitmap.hasAlpha();
// mHasMipMap = BitmapCompat.hasMipMap(mBitmap);
}
}
public void invalidate() {
synchronized (lock) {
initialProperties();
}
}
protected android.graphics.Bitmap onGotBitmap() {
return null;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public int getGenerationId() {
synchronized (lock) {
if (mBitmap != null) {
return mBitmap.getGenerationId();
}
}
return mGenerationId;
}
public int getAllocationByteCount() {
return mAllocationByteCount;
}
public int getByteCount() {
return mByteCount;
}
public int getRowBytes() {
return mRowBytes;
}
public int getDensity() {
return mDensity;
}
public byte[] getNinePatchChunk() {
return mNinePatchChunk;
}
public boolean isRecycled() {
return mRecycled;
}
public boolean isMutable() {
return mIsMutable;
}
/**
* Indicates whether the renderer responsible for drawing this
* bitmap should attempt to use mipmaps when this bitmap is drawn
* scaled down.
*
* If you know that you are going to draw this bitmap at less than
* 50% of its original size, you may be able to obtain a higher
* quality
*
* This property is only a suggestion that can be ignored by the
* renderer. It is not guaranteed to have any effect.
*
* @return true if the renderer should attempt to use mipmaps,
* false otherwise
*
* @see #setHasMipMap(boolean)
*/
public final boolean hasMipMap() {
return mHasMipMap;
}
/**
* Set a hint for the renderer responsible for drawing this bitmap
* indicating that it should attempt to use mipmaps when this bitmap
* is drawn scaled down.
*
* If you know that you are going to draw this bitmap at less than
* 50% of its original size, you may be able to obtain a higher
* quality by turning this property on.
*
* Note that if the renderer respects this hint it might have to
* allocate extra memory to hold the mipmap levels for this bitmap.
*
* This property is only a suggestion that can be ignored by the
* renderer. It is not guaranteed to have any effect.
*
* @param hasMipMap indicates whether the renderer should attempt
* to use mipmaps
*
* @see #hasMipMap()
*/
public final void setHasMipMap(boolean hasMipMap) {
mHasMipMap = hasMipMap;
}
public boolean hasAlpha() {
synchronized (lock) {
if (mBitmap != null) {
return mBitmap.hasAlpha();
}
}
return mHasAlpha;
}
public void recycle() {
synchronized (lock) {
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
}
mBitmap = null;
}
mRecycled = true;
}
/**
* Convenience for calling {@link #getScaledWidth(int)} with the target
* density of the given {@link Canvas}.
*/
public int getScaledWidth(Canvas canvas) {
return scaleFromDensity(getWidth(), mDensity, canvas.getDensity());
}
/**
* Convenience for calling {@link #getScaledHeight(int)} with the target
* density of the given {@link Canvas}.
*/
public int getScaledHeight(Canvas canvas) {
return scaleFromDensity(getHeight(), mDensity, canvas.getDensity());
}
/**
* Convenience for calling {@link #getScaledWidth(int)} with the target
* density of the given {@link DisplayMetrics}.
*/
public int getScaledWidth(DisplayMetrics metrics) {
return scaleFromDensity(getWidth(), mDensity, metrics.densityDpi);
}
/**
* Convenience for calling {@link #getScaledHeight(int)} with the target
* density of the given {@link DisplayMetrics}.
*/
public int getScaledHeight(DisplayMetrics metrics) {
return scaleFromDensity(getHeight(), mDensity, metrics.densityDpi);
}
/**
* Convenience method that returns the width of this bitmap divided
* by the density scale factor.
*
* @param targetDensity The density of the target canvas of the bitmap.
* @return The scaled width of this bitmap, according to the density scale factor.
*/
public int getScaledWidth(int targetDensity) {
return scaleFromDensity(getWidth(), mDensity, targetDensity);
}
/**
* Convenience method that returns the height of this bitmap divided
* by the density scale factor.
*
* @param targetDensity The density of the target canvas of the bitmap.
* @return The scaled height of this bitmap, according to the density scale factor.
*/
public int getScaledHeight(int targetDensity) {
return scaleFromDensity(getHeight(), mDensity, targetDensity);
}
/**
* @hide
*/
static public int scaleFromDensity(int size, int sdensity, int tdensity) {
if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) {
return size;
}
// Scale by tdensity / sdensity, rounding up.
return ((size * tdensity) + (sdensity >> 1)) / sdensity;
}
/**
* Returns a mutable bitmap with the specified width and height. Its
* initial density is as per {@link #getDensity}.
*
* @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(android.graphics.Bitmap.createBitmap(width, height, config));
}
/**
* Returns a immutable bitmap with the specified width and height, with each
* pixel value set to the corresponding value in the colors array. Its
* initial density is as per {@link #getDensity}.
*
* @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) {
return new Bitmap(android.graphics.Bitmap.createBitmap(colors, offset, stride, width, height, config));
}
/**
* Returns a immutable bitmap with the specified width and height, with each
* pixel value set to the corresponding value in the colors array. Its
* initial density is as per {@link #getDensity}.
*
* @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 new Bitmap(android.graphics.Bitmap.createBitmap(colors, width, height, config));
}
/**
* <p>Modifies the bitmap to have a specified width, height, and {@link
* Config}, without affecting the underlying allocation backing the bitmap.
* Bitmap pixel data is not re-initialized for the new configuration.</p>
*
* <p>This method can be used to avoid allocating a new bitmap, instead
* reusing an existing bitmap's allocation for a new configuration of equal
* or lesser size. If the Bitmap's allocation isn't large enough to support
* the new configuration, an IllegalArgumentException will be thrown and the
* bitmap will not be modified.</p>
*
* <p>The result of {@link #getByteCount()} will reflect the new configuration,
* while {@link #getAllocationByteCount()} will reflect that of the initial
* configuration.</p>
*
* <p>Note: This may change this result of hasAlpha(). When converting to 565,
* the new bitmap will always be considered opaque. When converting from 565,
* the new bitmap will be considered non-opaque, and will respect the value
* set by setPremultiplied().</p>
*
* <p>WARNING: This method should NOT be called on a bitmap currently used
* by the view system. It does not make guarantees about how the underlying
* pixel buffer is remapped to the new config, just that the allocation is
* reused. Additionally, the view system does not account for bitmap
* properties being modifying during use, e.g. while attached to
* drawables.</p>
*
* @see #setWidth(int)
* @see #setHeight(int)
* @see #setConfig(Config)
*/
public void reconfigure(int width, int height, Config config) {
synchronized (lock) {
if (mBitmap != null) {
mBitmap.reconfigure(width, height, config);
initialProperties();
} else {
throw new IllegalArgumentException("bitmap has been recycled.");
}
}
}
protected boolean desireFreeBitmap(){
return false;
}
/**
* TODO 需要特别注意bitmap释放后,如果外部修改了bitmap,需要重新bind纹理
* @hide
*/
public final void freeBitmap() {
if (desireFreeBitmap()) {
synchronized (lock) {
mBitmap = null;
}
onFreeBitmap();
}
}
protected void onFreeBitmap() {
}
private final String mCacheKey = super.toString();
public final String generateCacheKey() {
return mCacheKey;
}
}