diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /graphics | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'graphics')
83 files changed, 16102 insertions, 0 deletions
diff --git a/graphics/java/android/graphics/AvoidXfermode.java b/graphics/java/android/graphics/AvoidXfermode.java new file mode 100644 index 0000000..d7b0225 --- /dev/null +++ b/graphics/java/android/graphics/AvoidXfermode.java @@ -0,0 +1,57 @@ +/* + * 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; + +/** + * AvoidXfermode xfermode will draw the src everywhere except on top of the + * opColor or, depending on the Mode, draw only on top of the opColor. + */ +public class AvoidXfermode extends Xfermode { + + // these need to match the enum in SkAvoidXfermode.h on the native side + public enum Mode { + AVOID (0), //!< draw everywhere except on the opColor + TARGET (1); //!< draw only on top of the opColor + + Mode(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * This xfermode will draw the src everywhere except on top of the opColor + * or, depending on the Mode, draw only on top of the opColor. + * + * @param opColor The color to avoid (or to target depending on Mode). Note + * that the alpha in opColor is ignored. + * @param tolerance How closely we compare a pixel to the opColor. + * 0 - only operate if exact match + * 255 - maximum gradation (blending) based on how + * similar the pixel is to our opColor (max tolerance) + * @param mode If we should avoid or target the opColor + */ + public AvoidXfermode(int opColor, int tolerance, Mode mode) { + if (tolerance < 0 || tolerance > 255) { + throw new IllegalArgumentException("tolerance must be 0..255"); + } + native_instance = nativeCreate(opColor, tolerance, mode.nativeInt); + } + + private static native int nativeCreate(int opColor, int tolerance, + int nativeMode); +} diff --git a/graphics/java/android/graphics/Bitmap.aidl b/graphics/java/android/graphics/Bitmap.aidl new file mode 100755 index 0000000..ce97b95 --- /dev/null +++ b/graphics/java/android/graphics/Bitmap.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2007, 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; + +parcelable Bitmap; diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java new file mode 100644 index 0000000..501c99f --- /dev/null +++ b/graphics/java/android/graphics/Bitmap.java @@ -0,0 +1,811 @@ +/* + * Copyright (C) 2006 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.lang.ref.WeakReference; +import java.util.ArrayList; +import java.io.OutputStream; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.nio.IntBuffer; + +import android.os.Parcel; +import android.os.Parcelable; + +public final class Bitmap implements Parcelable { + + // Note: mNativeBitmap is used by FaceDetector_jni.cpp + // Don't change/rename without updating FaceDetector_jni.cpp + private final int mNativeBitmap; + + private final boolean mIsMutable; + private byte[] mNinePatchChunk; // may be null + private int mWidth = -1; + private int mHeight = -1; + private boolean mRecycled; + + private static volatile Matrix sScaleMatrix; + + /** + * @noinspection UnusedDeclaration + */ + /* Private constructor that must received an already allocated native + bitmap int (pointer). + + This can be called from JNI code. + */ + private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk) { + if (nativeBitmap == 0) { + throw new RuntimeException("internal error: native bitmap is 0"); + } + + // we delete this in our finalizer + mNativeBitmap = nativeBitmap; + mIsMutable = isMutable; + mNinePatchChunk = ninePatchChunk; + } + + /** + * Free up the memory associated with this bitmap's pixels, and mark the + * bitmap as "dead", meaning it will throw an exception if getPixels() or + * setPixels() is called, and will draw nothing. This operation cannot be + * reversed, so it should only be called if you are sure there are no + * further uses for the bitmap. This is an advanced call, and normally need + * not be called, since the normal GC process will free up this memory when + * there are no more references to this bitmap. + */ + public void recycle() { + if (!mRecycled) { + nativeRecycle(mNativeBitmap); + mNinePatchChunk = null; + mRecycled = true; + } + } + + /** + * Returns true if this bitmap has been recycled. If so, then it is an error + * to try to access its pixels, and the bitmap will not draw. + * + * @return true if the bitmap has been recycled + */ + public final boolean isRecycled() { + return mRecycled; + } + + /** + * This is called by methods that want to throw an exception if the bitmap + * has already been recycled. + */ + private void checkRecycled(String errorMessage) { + if (mRecycled) { + throw new IllegalStateException(errorMessage); + } + } + + /** + * Common code for checking that x and y are >= 0 + * + * @param x x coordinate to ensure is >= 0 + * @param y y coordinate to ensure is >= 0 + */ + private static void checkXYSign(int x, int y) { + if (x < 0) { + throw new IllegalArgumentException("x must be >= 0"); + } + if (y < 0) { + throw new IllegalArgumentException("y must be >= 0"); + } + } + + /** + * Common code for checking that width and height are > 0 + * + * @param width width to ensure is > 0 + * @param height height to ensure is > 0 + */ + private static void checkWidthHeight(int width, int height) { + if (width <= 0) { + throw new IllegalArgumentException("width must be > 0"); + } + if (height <= 0) { + throw new IllegalArgumentException("height must be > 0"); + } + } + + 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 + }; + } + + /** + * Copy the bitmap's pixels into the specified buffer (allocated by the + * caller). An exception is thrown if the buffer is not large enough to + * hold all of the pixels (taking into account the number of bytes per + * pixel) or if the Buffer subclass is not one of the support types + * (ByteBuffer, ShortBuffer, IntBuffer). + */ + public void copyPixelsToBuffer(Buffer dst) { + int elements = dst.remaining(); + int shift; + if (dst instanceof ByteBuffer) { + shift = 0; + } else if (dst instanceof ShortBuffer) { + shift = 1; + } else if (dst instanceof IntBuffer) { + shift = 2; + } else { + throw new RuntimeException("unsupported Buffer subclass"); + } + + long bufferSize = (long)elements << shift; + long pixelSize = (long)getRowBytes() * getHeight(); + + if (bufferSize < pixelSize) { + throw new RuntimeException("Buffer not large enough for pixels"); + } + + nativeCopyPixelsToBuffer(mNativeBitmap, dst); + + // now update the buffer's position + int position = dst.position(); + position += pixelSize >> shift; + dst.position(position); + } + + /** + * Tries to make a new bitmap based on the dimensions of this bitmap, + * setting the new bitmap's config to the one specified, and then copying + * this bitmap's pixels into the new bitmap. If the conversion is not + * supported, or the allocator fails, then this returns NULL. + * + * @param config The desired config for the resulting bitmap + * @param isMutable True if the resulting bitmap should be mutable (i.e. + * its pixels can be modified) + * @return the new bitmap, or null if the copy could not be made. + */ + public Bitmap copy(Config config, boolean isMutable) { + checkRecycled("Can't copy a recycled bitmap"); + return nativeCopy(mNativeBitmap, config.nativeInt, isMutable); + } + + public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, + int dstHeight, boolean filter) { + Matrix m = null; + 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; + } + + /** + * 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()); + } + + /** + * 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 createBitmap(source, x, y, width, height, null, false); + } + + /** + * 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; + } + + int neww = width; + int newh = height; + Canvas canvas = new Canvas(); + Bitmap bitmap; + Paint paint; + + Rect srcR = new Rect(x, y, x + width, y + height); + RectF dstR = new RectF(0, 0, width, height); + + if (m == null || m.isIdentity()) { + bitmap = createBitmap(neww, newh, source.hasAlpha() ? + Config.ARGB_8888 : Config.RGB_565); + paint = null; // not needed + } else { + /* 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()); + bitmap = createBitmap(neww, newh, hasAlpha ? + Config.ARGB_8888 : Config.RGB_565); + if (hasAlpha) { + bitmap.eraseColor(0); + } + canvas.translate(-deviceR.left, -deviceR.top); + canvas.concat(m); + paint = new Paint(); + paint.setFilterBitmap(filter); + if (!m.rectStaysRect()) { + paint.setAntiAlias(true); + } + } + canvas.setBitmap(bitmap); + canvas.drawBitmap(source, srcR, dstR, paint); + + return bitmap; + } + + /** + * 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) { + Bitmap bm = nativeCreate(null, 0, width, width, height, + config.nativeInt, true); + bm.eraseColor(0); // start with black/transparent pixels + return bm; + } + + /** + * 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(); + } + return nativeCreate(colors, offset, stride, width, height, + config.nativeInt, false); + } + + /** + * 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); + } + + /** + * Returns an optional array of private data, used by the UI system for + * some bitmaps. Not intended to be called by applications. + */ + public byte[] getNinePatchChunk() { + return mNinePatchChunk; + } + + /** + * Specifies the known formats a bitmap can be compressed into + */ + public enum CompressFormat { + JPEG (0), + PNG (1); + + CompressFormat(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * Number of bytes of temp storage we use for communicating between the + * native compressor and the java OutputStream. + */ + private final static int WORKING_COMPRESS_STORAGE = 4096; + + /** + * Write a compressed version of the bitmap to the specified outputstream. + * If this returns true, the bitmap can be reconstructed by passing a + * corresponding inputstream to BitmapFactory.decodeStream(). Note: not + * all Formats support all bitmap configs directly, so it is possible that + * the returned bitmap from BitmapFactory could be in a different bitdepth, + * and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque + * pixels). + * + * @param format The format of the compressed image + * @param quality Hint to the compressor, 0-100. 0 meaning compress for + * small size, 100 meaning compress for max quality. Some + * formats, like PNG which is lossless, will ignore the + * quality setting + * @param stream The outputstream to write the compressed data. + * @return true if successfully compressed to the specified stream. + */ + public boolean compress(CompressFormat format, int quality, + OutputStream stream) { + checkRecycled("Can't compress a recycled bitmap"); + // do explicit check before calling the native method + if (stream == null) { + throw new NullPointerException(); + } + if (quality < 0 || quality > 100) { + throw new IllegalArgumentException("quality must be 0..100"); + } + return nativeCompress(mNativeBitmap, format.nativeInt, quality, + stream, new byte[WORKING_COMPRESS_STORAGE]); + } + + /** + * Returns true if the bitmap is marked as mutable (i.e. can be drawn into) + */ + public final boolean isMutable() { + return mIsMutable; + } + + /** Returns the bitmap's width */ + public final int getWidth() { + return mWidth == -1 ? mWidth = nativeWidth(mNativeBitmap) : mWidth; + } + + /** Returns the bitmap's height */ + public final int getHeight() { + return mHeight == -1 ? mHeight = nativeHeight(mNativeBitmap) : mHeight; + } + + /** + * Return the number of bytes between rows in the bitmap's pixels. Note that + * this refers to the pixels as stored natively by the bitmap. If you call + * getPixels() or setPixels(), then the pixels are uniformly treated as + * 32bit values, packed according to the Color class. + * + * @return number of bytes between rows of the native bitmap pixels. + */ + public final int getRowBytes() { + return nativeRowBytes(mNativeBitmap); + } + + /** + * If the bitmap's internal config is in one of the public formats, return + * that config, otherwise return null. + */ + public final Config getConfig() { + return Config.nativeToConfig(nativeConfig(mNativeBitmap)); + } + + /** Returns true if the bitmap's pixels support levels of alpha */ + public final boolean hasAlpha() { + return nativeHasAlpha(mNativeBitmap); + } + + /** + * Fills the bitmap's pixels with the specified {@link Color}. + * + * @throws IllegalStateException if the bitmap is not mutable. + */ + public void eraseColor(int c) { + checkRecycled("Can't erase a recycled bitmap"); + if (!isMutable()) { + throw new IllegalStateException("cannot erase immutable bitmaps"); + } + nativeErase(mNativeBitmap, c); + } + + /** + * Returns the {@link Color} at the specified location. Throws an exception + * if x or y are out of bounds (negative or >= to the width or height + * respectively). + * + * @param x The x coordinate (0...width-1) of the pixel to return + * @param y The y coordinate (0...height-1) of the pixel to return + * @return The argb {@link Color} at the specified coordinate + * @throws IllegalArgumentException if x, y exceed the bitmap's bounds + */ + public int getPixel(int x, int y) { + checkRecycled("Can't call getPixel() on a recycled bitmap"); + checkPixelAccess(x, y); + return nativeGetPixel(mNativeBitmap, x, y); + } + + /** + * Returns in pixels[] a copy of the data in the bitmap. Each value is + * a packed int representing a {@link Color}. The stride parameter allows + * the caller to allow for gaps in the returned pixels array between + * rows. For normal packed results, just pass width for the stride value. + * + * @param pixels The array to receive the bitmap's colors + * @param offset The first index to write into pixels[] + * @param stride The number of entries in pixels[] to skip between + * rows (must be >= bitmap's width). Can be negative. + * @param x The x coordinate of the first pixel to read from + * the bitmap + * @param y The y coordinate of the first pixel to read from + * the bitmap + * @param width The number of pixels to read from each row + * @param height The number of rows to read + * @throws IllegalArgumentException if x, y, width, height exceed the + * bounds of the bitmap, or if abs(stride) < width. + * @throws ArrayIndexOutOfBoundsException if the pixels array is too small + * to receive the specified number of pixels. + */ + public void getPixels(int[] pixels, int offset, int stride, + int x, int y, int width, int height) { + checkRecycled("Can't call getPixels() on a recycled bitmap"); + if (width == 0 || height == 0) { + return; // nothing to do + } + checkPixelsAccess(x, y, width, height, offset, stride, pixels); + nativeGetPixels(mNativeBitmap, pixels, offset, stride, + x, y, width, height); + } + + /** + * Shared code to check for illegal arguments passed to getPixel() + * or setPixel() + * @param x x coordinate of the pixel + * @param y y coordinate of the pixel + */ + private void checkPixelAccess(int x, int y) { + checkXYSign(x, y); + if (x >= getWidth()) { + throw new IllegalArgumentException("x must be < bitmap.width()"); + } + if (y >= getHeight()) { + throw new IllegalArgumentException("y must be < bitmap.height()"); + } + } + + /** + * Shared code to check for illegal arguments passed to getPixels() + * or setPixels() + * + * @param x left edge of the area of pixels to access + * @param y top edge of the area of pixels to access + * @param width width of the area of pixels to access + * @param height height of the area of pixels to access + * @param offset offset into pixels[] array + * @param stride number of elements in pixels[] between each logical row + * @param pixels array to hold the area of pixels being accessed + */ + private void checkPixelsAccess(int x, int y, int width, int height, + int offset, int stride, int pixels[]) { + checkXYSign(x, y); + if (width < 0) { + throw new IllegalArgumentException("width must be >= 0"); + } + if (height < 0) { + throw new IllegalArgumentException("height must be >= 0"); + } + if (x + width > getWidth()) { + throw new IllegalArgumentException( + "x + width must be <= bitmap.width()"); + } + if (y + height > getHeight()) { + throw new IllegalArgumentException( + "y + height must be <= bitmap.height()"); + } + if (Math.abs(stride) < width) { + throw new IllegalArgumentException("abs(stride) must be >= width"); + } + int lastScanline = offset + (height - 1) * stride; + int length = pixels.length; + if (offset < 0 || (offset + width > length) + || lastScanline < 0 + || (lastScanline + width > length)) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + /** + * Write the specified {@link Color} into the bitmap (assuming it is + * mutable) at the x,y coordinate. + * + * @param x The x coordinate of the pixel to replace (0...width-1) + * @param y The y coordinate of the pixel to replace (0...height-1) + * @param color The {@link Color} to write into the bitmap + * @throws IllegalStateException if the bitmap is not mutable + * @throws IllegalArgumentException if x, y are outside of the bitmap's + * bounds. + */ + public void setPixel(int x, int y, int color) { + checkRecycled("Can't call setPixel() on a recycled bitmap"); + if (!isMutable()) { + throw new IllegalStateException(); + } + checkPixelAccess(x, y); + nativeSetPixel(mNativeBitmap, x, y, color); + } + + /** + * Replace pixels in the bitmap with the colors in the array. Each element + * in the array is a packed int prepresenting a {@link Color} + * + * @param pixels The colors to write to the bitmap + * @param offset The index of the first color to read from pixels[] + * @param stride The number of colors in pixels[] to skip between rows. + * Normally this value will be the same as the width of + * the bitmap, but it can be larger (or negative). + * @param x The x coordinate of the first pixel to write to in + * the bitmap. + * @param y The y coordinate of the first pixel to write to in + * the bitmap. + * @param width The number of colors to copy from pixels[] per row + * @param height The number of rows to write to the bitmap + * @throws IllegalStateException if the bitmap is not mutable + * @throws IllegalArgumentException if x, y, width, height are outside of + * the bitmap's bounds. + * @throws ArrayIndexOutOfBoundsException if the pixels array is too small + * to receive the specified number of pixels. + */ + public void setPixels(int[] pixels, int offset, int stride, + int x, int y, int width, int height) { + checkRecycled("Can't call setPixels() on a recycled bitmap"); + if (!isMutable()) { + throw new IllegalStateException(); + } + if (width == 0 || height == 0) { + return; // nothing to do + } + checkPixelsAccess(x, y, width, height, offset, stride, pixels); + nativeSetPixels(mNativeBitmap, pixels, offset, stride, + x, y, width, height); + } + + public static final Parcelable.Creator<Bitmap> CREATOR + = new Parcelable.Creator<Bitmap>() { + /** + * Rebuilds a bitmap previously stored with writeToParcel(). + * + * @param p Parcel object to read the bitmap from + * @return a new bitmap created from the data in the parcel + */ + public Bitmap createFromParcel(Parcel p) { + Bitmap bm = nativeCreateFromParcel(p); + if (bm == null) { + throw new RuntimeException("Failed to unparcel Bitmap"); + } + return bm; + } + public Bitmap[] newArray(int size) { + return new Bitmap[size]; + } + }; + + /** + * No special parcel contents. + */ + public int describeContents() { + return 0; + } + + /** + * Write the bitmap and its pixels to the parcel. The bitmap can be + * rebuilt from the parcel by calling CREATOR.createFromParcel(). + * @param p Parcel object to write the bitmap data into + */ + public void writeToParcel(Parcel p, int flags) { + checkRecycled("Can't parcel a recycled bitmap"); + if (!nativeWriteToParcel(mNativeBitmap, mIsMutable, p)) { + throw new RuntimeException("native writeToParcel failed"); + } + } + + /** + * Returns a new bitmap that captures the alpha values of the original. + * This may be drawn with Canvas.drawBitmap(), where the color(s) will be + * taken from the paint that is passed to the draw call. + * + * @return new bitmap containing the alpha channel of the original bitmap. + */ + public Bitmap extractAlpha() { + return extractAlpha(null, null); + } + + /** + * Returns a new bitmap that captures the alpha values of the original. + * These values may be affected by the optional Paint parameter, which + * can contain its own alpha, and may also contain a MaskFilter which + * could change the actual dimensions of the resulting bitmap (e.g. + * a blur maskfilter might enlarge the resulting bitmap). If offsetXY + * is not null, it returns the amount to offset the returned bitmap so + * that it will logically align with the original. For example, if the + * paint contains a blur of radius 2, then offsetXY[] would contains + * -2, -2, so that drawing the alpha bitmap offset by (-2, -2) and then + * drawing the original would result in the blur visually aligning with + * the original. + * @param paint Optional paint used to modify the alpha values in the + * resulting bitmap. Pass null for default behavior. + * @param offsetXY Optional array that returns the X (index 0) and Y + * (index 1) offset needed to position the returned bitmap + * so that it visually lines up with the original. + * @return new bitmap containing the (optionally modified by paint) alpha + * channel of the original bitmap. This may be drawn with + * Canvas.drawBitmap(), where the color(s) will be taken from the + * paint that is passed to the draw call. + */ + public Bitmap extractAlpha(Paint paint, int[] offsetXY) { + checkRecycled("Can't extractAlpha on a recycled bitmap"); + int nativePaint = paint != null ? paint.mNativePaint : 0; + Bitmap bm = nativeExtractAlpha(mNativeBitmap, nativePaint, offsetXY); + if (bm == null) { + throw new RuntimeException("Failed to extractAlpha on Bitmap"); + } + return bm; + } + + protected void finalize() throws Throwable { + try { + nativeDestructor(mNativeBitmap); + } finally { + super.finalize(); + } + } + + //////////// native methods + + private static native Bitmap nativeCreate(int[] colors, int offset, + int stride, int width, int height, + int nativeConfig, boolean mutable); + private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig, + boolean isMutable); + private static native void nativeDestructor(int nativeBitmap); + private static native void nativeRecycle(int nativeBitmap); + + private static native boolean nativeCompress(int nativeBitmap, int format, + int quality, OutputStream stream, + byte[] tempStorage); + private static native void nativeErase(int nativeBitmap, int color); + private static native int nativeWidth(int nativeBitmap); + private static native int nativeHeight(int nativeBitmap); + private static native int nativeRowBytes(int nativeBitmap); + private static native int nativeConfig(int nativeBitmap); + private static native boolean nativeHasAlpha(int nativeBitmap); + + private static native int nativeGetPixel(int nativeBitmap, int x, int y); + private static native void nativeGetPixels(int nativeBitmap, int[] pixels, + int offset, int stride, int x, + int y, int width, int height); + + private static native void nativeSetPixel(int nativeBitmap, int x, int y, + int color); + private static native void nativeSetPixels(int nativeBitmap, int[] colors, + int offset, int stride, int x, + int y, int width, int height); + private static native void nativeCopyPixelsToBuffer(int nativeBitmap, + Buffer dst); + + private static native Bitmap nativeCreateFromParcel(Parcel p); + // returns true on success + private static native boolean nativeWriteToParcel(int nativeBitmap, + boolean isMutable, + Parcel p); + // returns a new bitmap built from the native bitmap's alpha, and the paint + private static native Bitmap nativeExtractAlpha(int nativeBitmap, + int nativePaint, + int[] offsetXY); + + /* package */ final int ni() { + return mNativeBitmap; + } +} diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java new file mode 100644 index 0000000..d1e6090 --- /dev/null +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2007 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 android.content.res.AssetManager; +import android.content.res.Resources; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.FileDescriptor; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + + +/** + * Creates Bitmap objects from various sources, including files, streams, + * and byte-arrays. + */ +public class BitmapFactory { + private static final String TAG = "BitmapFactory"; + private static final boolean DEBUG_LOAD = false; + + public static class Options { + /** + * Create a default Options object, which if left unchanged will give + * the same result from the decoder as if null were passed. + */ + public Options() { + inDither = true; + } + + /** + * If set to true, the decoder will return null (no bitmap), but + * the out... fields will still be set, allowing the caller to query + * the bitmap without having to allocate the memory for its pixels. + */ + public boolean inJustDecodeBounds; + /** + * If set to a value > 1, requests the decoder to subsample the original + * image, returning a smaller image to save memory. The sample size is + * the number of pixels in either dimension that correspond to a single + * pixel in the decoded bitmap. For example, inSampleSize == 4 returns + * an image that is 1/4 the width/height of the original, and 1/16 the + * number of pixels. Any value <= 1 is treated the same as 1. Note: the + * decoder will try to fulfill this request, but the resulting bitmap + * may have different dimensions that precisely what has been requested. + * Also, powers of 2 are often faster/easier for the decoder to honor. + */ + public int inSampleSize; + + /** + * If this is non-null, the decoder will try to decode into this + * internal configuration. If it is null, or the request cannot be met, + * the decoder will try to pick the best matching config based on the + * system's screen depth, and characteristics of the original image such + * as if it has per-pixel alpha (requiring a config that also does). + */ + public Bitmap.Config inPreferredConfig; + + /** + * If dither is true, the decoder will atttempt to dither the decoded + * image. + */ + public boolean inDither; + + /** + * The resulting width of the bitmap, set independent of the state of + * inJustDecodeBounds. However, if there is an error trying to decode, + * outWidth will be set to -1. + */ + public int outWidth; + /** + * The resulting height of the bitmap, set independent of the state of + * inJustDecodeBounds. However, if there is an error trying to decode, + * outHeight will be set to -1. + */ + public int outHeight; + + /** + * If known, this string is set to the mimetype of the decoded image. + * If not know, or there is an error, it is set to null. + */ + public String outMimeType; + + /** + * Temp storage to use for decoding. Suggest 16K or so. + */ + public byte [] inTempStorage; + + private native void requestCancel(); + + /** + * Flag to indicate that cancel has been called on this object. This + * is useful if there's an intermediary that wants to first decode the + * bounds and then decode the image. In that case the intermediary + * can check, inbetween the bounds decode and the image decode, to see + * if the operation is canceled. + */ + public boolean mCancel; + + /** + * This can be called from another thread while this options object is + * inside a decode... call. Calling this will notify the decoder that + * it should cancel its operation. This is not guaranteed to cancel + * the decode, but if it does, the decoder... operation will return + * null, or if inJustDecodeBounds is true, will set outWidth/outHeight + * to -1 + */ + public void requestCancelDecode() { + mCancel = true; + requestCancel(); + } + } + + /** + * Decode a file path into a bitmap. If the specified file name is null, + * or cannot be decoded into a bitmap, the function returns null. + * + * @param pathName complete path name for the file to be decoded. + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeFile(String pathName, Options opts) { + Bitmap bm = null; + InputStream stream = null; + try { + stream = new FileInputStream(pathName); + bm = decodeStream(stream, null, opts); + } catch (Exception e) { + /* do nothing. + If the exception happened on open, bm will be null. + */ + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + // do nothing here + } + } + } + return bm; + } + + /** + * Decode a file path into a bitmap. If the specified file name is null, + * or cannot be decoded into a bitmap, the function returns null. + * + * @param pathName complete path name for the file to be decoded. + * @return the resulting decoded bitmap, or null if it could not be decoded. + */ + public static Bitmap decodeFile(String pathName) { + return decodeFile(pathName, null); + } + + /** + * Decode an image referenced by a resource ID. + * + * @param res The resources object containing the image data + * @param id The resource id of the image data + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeResource(Resources res, int id, Options opts) { + Bitmap bm = null; + + try { + InputStream is = res.openRawResource(id); + bm = decodeStream(is, null, opts); + is.close(); + } + catch (java.io.IOException e) { + /* do nothing. + If the exception happened on open, bm will be null. + If it happened on close, bm is still valid. + */ + } + return bm; + } + + /** + * Decode an image referenced by a resource ID. + * + * @param res The resources object containing the image data + * @param id The resource id of the image data + * @return The decoded bitmap, or null if the image could not be decode. + */ + public static Bitmap decodeResource(Resources res, int id) { + return decodeResource(res, id, null); + } + + /** + * Decode an immutable bitmap from the specified byte array. + * + * @param data byte array of compressed image data + * @param offset offset into imageData for where the decoder should begin + * parsing. + * @param length the number of bytes, beginning at offset, to parse + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeByteArray(byte[] data, int offset, int length, + Options opts) { + if ((offset | length) < 0 || data.length < offset + length) { + throw new ArrayIndexOutOfBoundsException(); + } + return nativeDecodeByteArray(data, offset, length, opts); + } + + /** + * Decode an immutable bitmap from the specified byte array. + * + * @param data byte array of compressed image data + * @param offset offset into imageData for where the decoder should begin + * parsing. + * @param length the number of bytes, beginning at offset, to parse + * @return The decoded bitmap, or null if the image could not be decode. + */ + public static Bitmap decodeByteArray(byte[] data, int offset, int length) { + return decodeByteArray(data, offset, length, null); + } + + /** + * Decode an input stream into a bitmap. If the input stream is null, or + * cannot be used to decode a bitmap, the function returns null. + * The stream's position will be where ever it was after the encoded data + * was read. + * + * @param is The input stream that holds the raw data to be decoded into a + * bitmap. + * @param outPadding If not null, return the padding rect for the bitmap if + * it exists, otherwise set padding to [-1,-1,-1,-1]. If + * no bitmap is returned (null) then padding is + * unchanged. + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeStream(InputStream is, Rect outPadding, + Options opts) { + // we don't throw in this case, thus allowing the caller to only check + // the cache, and not force the image to be decoded. + if (is == null) { + return null; + } + + // we need mark/reset to work properly + + if (!is.markSupported()) { + is = new BufferedInputStream(is, 16 * 1024); + } + + // so we can call reset() if a given codec gives up after reading up to + // this many bytes. FIXME: need to find out from the codecs what this + // value should be. + is.mark(1024); + + Bitmap bm; + + if (is instanceof AssetManager.AssetInputStream) { + bm = nativeDecodeAsset( + ((AssetManager.AssetInputStream)is).getAssetInt(), outPadding, + opts); + } + else { + // pass some temp storage down to the native code. 1024 is made up, + // but should be large enough to avoid too many small calls back + // into is.read(...) This number is not related to the value passed + // to mark(...) above. + byte [] tempStorage = null; + if (opts != null) + tempStorage = opts.inTempStorage; + if (tempStorage == null) + tempStorage = new byte[16 * 1024]; + bm = nativeDecodeStream(is, tempStorage, outPadding, opts); + } + + try { + is.reset(); + } catch (IOException ex) { + // ignore + } + return bm; + } + + /** + * Decode an input stream into a bitmap. If the input stream is null, or + * cannot be used to decode a bitmap, the function returns null. + * The stream's position will be where ever it was after the encoded data + * was read. + * + * @param is The input stream that holds the raw data to be decoded into a + * bitmap. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeStream(InputStream is) { + return decodeStream(is, null, null); + } + + /** + * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded + * return null. The position within the descriptor will not be changed when + * this returns, so the descriptor can be used again as is. + * + * @param fd The file descriptor containing the bitmap data to decode + * @param outPadding If not null, return the padding rect for the bitmap if + * it exists, otherwise set padding to [-1,-1,-1,-1]. If + * no bitmap is returned (null) then padding is + * unchanged. + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return the decoded bitmap, or null + */ + public static Bitmap decodeFileDescriptor(FileDescriptor fd, + Rect outPadding, Options opts) { + return nativeDecodeFileDescriptor(fd, outPadding, opts); + } + + /** + * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded + * return null. The position within the descriptor will not be changed when + * this returns, so the descriptor can be used again as is. + * + * @param fd The file descriptor containing the bitmap data to decode + * @return the decoded bitmap, or null + */ + public static Bitmap decodeFileDescriptor(FileDescriptor fd) { + return nativeDecodeFileDescriptor(fd, null, null); + } + + private static native Bitmap nativeDecodeStream(InputStream is, + byte[] storage, Rect padding, Options opts); + private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, + Rect padding, Options opts); + private static native Bitmap nativeDecodeAsset(int asset, Rect padding, + Options opts); + private static native Bitmap nativeDecodeByteArray(byte[] data, int offset, + int length, Options opts); +} + diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java new file mode 100644 index 0000000..612b0ab --- /dev/null +++ b/graphics/java/android/graphics/BitmapShader.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 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; + +public class BitmapShader extends Shader { + + // we hold on just for the GC, since our native counterpart is using it + private Bitmap mBitmap; + + /** + * Call this to create a new shader that will draw with a bitmap. + * + * @param bitmap The bitmap to use inside the shader + * @param tileX The tiling mode for x to draw the bitmap in. + * @param tileY The tiling mode for y to draw the bitmap in. + */ + public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) { + mBitmap = bitmap; + native_instance = nativeCreate(bitmap.ni(), + tileX.nativeInt, tileY.nativeInt); + } + + private static native int nativeCreate(int native_bitmap, + int shaderTileModeX, + int shaderTileModeY); +} + diff --git a/graphics/java/android/graphics/BlurMaskFilter.java b/graphics/java/android/graphics/BlurMaskFilter.java new file mode 100644 index 0000000..dbf57ac --- /dev/null +++ b/graphics/java/android/graphics/BlurMaskFilter.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006 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; + +public class BlurMaskFilter extends MaskFilter { + + public enum Blur { + NORMAL(0), //!< fuzzy inside and outside + SOLID(1), //!< solid inside, fuzzy outside + OUTER(2), //!< nothing inside, fuzzy outside + INNER(3); //!< fuzzy inside, nothing outside + + Blur(int value) { + native_int = value; + } + final int native_int; + } + + /** + * Create a blur maskfilter. + * + * @param radius The radius to extend the blur from the original mask. Must be > 0. + * @param style The Blur to use + * @return The new blur maskfilter + */ + public BlurMaskFilter(float radius, Blur style) { + native_instance = nativeConstructor(radius, style.native_int); + } + + private static native int nativeConstructor(float radius, int style); +} diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java new file mode 100644 index 0000000..530655f --- /dev/null +++ b/graphics/java/android/graphics/Camera.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006 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; + + +public class Camera { + + public Camera() { + nativeConstructor(); + } + + public native void save(); + public native void restore(); + + public native void translate(float x, float y, float z); + public native void rotateX(float deg); + public native void rotateY(float deg); + public native void rotateZ(float deg); + + public void getMatrix(Matrix matrix) { + nativeGetMatrix(matrix.native_instance); + } + public void applyToCanvas(Canvas canvas) { + nativeApplyToCanvas(canvas.mNativeCanvas); + } + + public native float dotWithNormal(float dx, float dy, float dz); + + protected void finalize() throws Throwable { + nativeDestructor(); + } + + private native void nativeConstructor(); + private native void nativeDestructor(); + private native void nativeGetMatrix(int native_matrix); + private native void nativeApplyToCanvas(int native_canvas); + + int native_instance; +} + diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java new file mode 100644 index 0000000..b57f428 --- /dev/null +++ b/graphics/java/android/graphics/Canvas.java @@ -0,0 +1,1468 @@ +/* + * Copyright (C) 2006 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 android.text.TextUtils; +import android.text.SpannedString; +import android.text.SpannableString; +import android.text.GraphicsOperations; + +import javax.microedition.khronos.opengles.GL; + +/** + * The Canvas class holds the "draw" calls. To draw something, you need + * 4 basic components: A Bitmap to hold the pixels, a Canvas to host + * the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, + * Path, text, Bitmap), and a paint (to describe the colors and styles for the + * drawing). + */ +public class Canvas { + // assigned in constructors, freed in finalizer + final int mNativeCanvas; + + /* Our native canvas can be either a raster, gl, or picture canvas. + If we are raster, then mGL will be null, and mBitmap may or may not be + present (our default constructor creates a raster canvas but no + java-bitmap is). If we are a gl-based, then mBitmap will be null, and + mGL will not be null. Thus both cannot be non-null, but its possible + for both to be null. + */ + private Bitmap mBitmap; // if not null, mGL must be null + private GL mGL; // if not null, mBitmap must be null + + // optional field set by the caller + private DrawFilter mDrawFilter; + + // Used by native code + @SuppressWarnings({"UnusedDeclaration"}) + private int mSurfaceFormat; + + /** + * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to + * draw into. + */ + public Canvas() { + // 0 means no native bitmap + mNativeCanvas = initRaster(0); + } + + /** + * Construct a canvas with the specified bitmap to draw into. The bitmap + * must be mutable. + * + * @param bitmap Specifies a mutable bitmap for the canvas to draw into. + */ + public Canvas(Bitmap bitmap) { + if (!bitmap.isMutable()) { + throw new IllegalStateException( + "Immutable bitmap passed to Canvas constructor"); + } + throwIfRecycled(bitmap); + mNativeCanvas = initRaster(bitmap.ni()); + mBitmap = bitmap; + } + + /*package*/ Canvas(int nativeCanvas) { + if (nativeCanvas == 0) { + throw new IllegalStateException(); + } + mNativeCanvas = nativeCanvas; + } + + /** + * Construct a canvas with the specified gl context. All drawing through + * this canvas will be redirected to OpenGL. Note: some features may not + * be supported in this mode (e.g. some GL implementations may not support + * antialiasing or certain effects like ColorMatrix or certain Xfermodes). + * However, no exception will be thrown in those cases. + */ + public Canvas(GL gl) { + mNativeCanvas = initGL(); + mGL = gl; + } + + /** + * Return the GL object associated with this canvas, or null if it is not + * backed by GL. + */ + public GL getGL() { + return mGL; + } + + /** + * Call this to free up OpenGL resources that may be cached or allocated + * on behalf of the Canvas. Any subsequent drawing with a GL-backed Canvas + * will have to recreate those resources. + */ + public static native void freeGlCaches(); + + /** + * Specify a bitmap for the canvas to draw into. + * + * @param bitmap Specifies a mutable bitmap for the canvas to draw into. + */ + public void setBitmap(Bitmap bitmap) { + if (!bitmap.isMutable()) { + throw new IllegalStateException(); + } + if (mGL != null) { + throw new RuntimeException("Can't set a bitmap device on a GL canvas"); + } + throwIfRecycled(bitmap); + + native_setBitmap(mNativeCanvas, bitmap.ni()); + mBitmap = bitmap; + } + + /** + * Set the viewport dimensions if this canvas is GL based. If it is not, + * this method is ignored and no exception is thrown. + * + * @param width The width of the viewport + * @param height The height of the viewport + */ + public void setViewport(int width, int height) { + if (mGL != null) { + nativeSetViewport(mNativeCanvas, width, height); + } + } + + /** + * Return true if the device that the current layer draws into is opaque + * (i.e. does not support per-pixel alpha). + * + * @return true if the device that the current layer draws into is opaque + */ + public native boolean isOpaque(); + + /** + * Returns the width of the current drawing layer + * + * @return the width of the current drawing layer + */ + public native int getWidth(); + + /** + * Returns the height of the current drawing layer + * + * @return the height of the current drawing layer + */ + public native int getHeight(); + + // the SAVE_FLAG constants must match their native equivalents + + /** restore the current matrix when restore() is called */ + public static final int MATRIX_SAVE_FLAG = 0x01; + /** restore the current clip when restore() is called */ + public static final int CLIP_SAVE_FLAG = 0x02; + /** the layer needs to per-pixel alpha */ + public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 0x04; + /** the layer needs to 8-bits per color component */ + public static final int FULL_COLOR_LAYER_SAVE_FLAG = 0x08; + /** clip against the layer's bounds */ + public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10; + /** restore everything when restore() is called */ + public static final int ALL_SAVE_FLAG = 0x1F; + + /** + * Saves the current matrix and clip onto a private stack. Subsequent + * calls to translate,scale,rotate,skew,concat or clipRect,clipPath + * will all operate as usual, but when the balancing call to restore() + * is made, those calls will be forgotten, and the settings that existed + * before the save() will be reinstated. + * + * @return The value to pass to restoreToCount() to balance this save() + */ + public native int save(); + + /** + * Based on saveFlags, can save the current matrix and clip onto a private + * stack. Subsequent calls to translate,scale,rotate,skew,concat or + * clipRect,clipPath will all operate as usual, but when the balancing + * call to restore() is made, those calls will be forgotten, and the + * settings that existed before the save() will be reinstated. + * + * @param saveFlags flag bits that specify which parts of the Canvas state + * to save/restore + * @return The value to pass to restoreToCount() to balance this save() + */ + public native int save(int saveFlags); + + /** + * This behaves the same as save(), but in addition it allocates an + * offscreen bitmap. All drawing calls are directed there, and only when + * the balancing call to restore() is made is that offscreen transfered to + * the canvas (or the previous layer). Subsequent calls to translate, + * scale, rotate, skew, concat or clipRect, clipPath all operate on this + * copy. When the balancing call to restore() is made, this copy is + * deleted and the previous matrix/clip state is restored. + * + * @param bounds May be null. The maximum size the offscreen bitmap + * needs to be (in local coordinates) + * @param paint This is copied, and is applied to the offscreen when + * restore() is called. + * @param saveFlags see _SAVE_FLAG constants + * @return value to pass to restoreToCount() to balance this save() + */ + public int saveLayer(RectF bounds, Paint paint, int saveFlags) { + return native_saveLayer(mNativeCanvas, bounds, + paint != null ? paint.mNativePaint : 0, + saveFlags); + } + + /** + * Helper version of saveLayer() that takes 4 values rather than a RectF. + */ + public int saveLayer(float left, float top, float right, float bottom, + Paint paint, int saveFlags) { + return native_saveLayer(mNativeCanvas, left, top, right, bottom, + paint != null ? paint.mNativePaint : 0, + saveFlags); + } + + /** + * This behaves the same as save(), but in addition it allocates an + * offscreen bitmap. All drawing calls are directed there, and only when + * the balancing call to restore() is made is that offscreen transfered to + * the canvas (or the previous layer). Subsequent calls to translate, + * scale, rotate, skew, concat or clipRect, clipPath all operate on this + * copy. When the balancing call to restore() is made, this copy is + * deleted and the previous matrix/clip state is restored. + * + * @param bounds The maximum size the offscreen bitmap needs to be + * (in local coordinates) + * @param alpha The alpha to apply to the offscreen when when it is + drawn during restore() + * @param saveFlags see _SAVE_FLAG constants + * @return value to pass to restoreToCount() to balance this call + */ + public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) { + alpha = Math.min(255, Math.max(0, alpha)); + return native_saveLayerAlpha(mNativeCanvas, bounds, alpha, saveFlags); + } + + /** + * Helper for saveLayerAlpha() that takes 4 values instead of a RectF. + */ + public int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int saveFlags) { + return native_saveLayerAlpha(mNativeCanvas, left, top, right, bottom, + alpha, saveFlags); + } + + /** + * This call balances a previous call to save(), and is used to remove all + * modifications to the matrix/clip state since the last save call. It is + * an error to call restore() more times than save() was called. + */ + public native void restore(); + + /** + * Returns the number of matrix/clip states on the Canvas' private stack. + * This will equal # save() calls - # restore() calls. + */ + public native int getSaveCount(); + + /** + * Efficient way to pop any calls to save() that happened after the save + * count reached saveCount. It is an error for saveCount to be less than 1. + * + * Example: + * int count = canvas.save(); + * ... // more calls potentially to save() + * canvas.restoreToCount(count); + * // now the canvas is back in the same state it was before the initial + * // call to save(). + * + * @param saveCount The save level to restore to. + */ + public native void restoreToCount(int saveCount); + + /** + * Preconcat the current matrix with the specified translation + * + * @param dx The distance to translate in X + * @param dy The distance to translate in Y + */ + public native void translate(float dx, float dy); + + /** + * Preconcat the current matrix with the specified scale. + * + * @param sx The amount to scale in X + * @param sy The amount to scale in Y + */ + public native void scale(float sx, float sy); + + /** + * Preconcat the current matrix with the specified scale. + * + * @param sx The amount to scale in X + * @param sy The amount to scale in Y + * @param px The x-coord for the pivot point (unchanged by the rotation) + * @param py The y-coord for the pivot point (unchanged by the rotation) + */ + public final void scale(float sx, float sy, float px, float py) { + translate(px, py); + scale(sx, sy); + translate(-px, -py); + } + + /** + * Preconcat the current matrix with the specified rotation. + * + * @param degrees The amount to rotate, in degrees + */ + public native void rotate(float degrees); + + /** + * Preconcat the current matrix with the specified rotation. + * + * @param degrees The amount to rotate, in degrees + * @param px The x-coord for the pivot point (unchanged by the rotation) + * @param py The y-coord for the pivot point (unchanged by the rotation) + */ + public final void rotate(float degrees, float px, float py) { + translate(px, py); + rotate(degrees); + translate(-px, -py); + } + + /** + * Preconcat the current matrix with the specified skew. + * + * @param sx The amount to skew in X + * @param sy The amount to skew in Y + */ + public native void skew(float sx, float sy); + + /** + * Preconcat the current matrix with the specified matrix. + * + * @param matrix The matrix to preconcatenate with the current matrix + */ + public void concat(Matrix matrix) { + native_concat(mNativeCanvas, matrix.native_instance); + } + + /** + * Completely replace the current matrix with the specified matrix. If the + * matrix parameter is null, then the current matrix is reset to identity. + * + * @param matrix The matrix to replace the current matrix with. If it is + * null, set the current matrix to identity. + */ + public void setMatrix(Matrix matrix) { + native_setMatrix(mNativeCanvas, + matrix == null ? 0 : matrix.native_instance); + } + + /** + * Return, in ctm, the current transformation matrix. This does not alter + * the matrix in the canvas, but just returns a copy of it. + */ + public void getMatrix(Matrix ctm) { + native_getCTM(mNativeCanvas, ctm.native_instance); + } + + /** + * Return a new matrix with a copy of the canvas' current transformation + * matrix. + */ + public final Matrix getMatrix() { + Matrix m = new Matrix(); + getMatrix(m); + return m; + } + + /** + * Modify the current clip with the specified rectangle. + * + * @param rect The rect to intersect with the current clip + * @param op How the clip is modified + * @return true if the resulting clip is non-empty + */ + public boolean clipRect(RectF rect, Region.Op op) { + return native_clipRect(mNativeCanvas, + rect.left, rect.top, rect.right, rect.bottom, + op.nativeInt); + } + + /** + * Modify the current clip with the specified rectangle, which is + * expressed in local coordinates. + * + * @param rect The rectangle to intersect with the current clip. + * @param op How the clip is modified + * @return true if the resulting clip is non-empty + */ + public boolean clipRect(Rect rect, Region.Op op) { + return native_clipRect(mNativeCanvas, + rect.left, rect.top, rect.right, rect.bottom, + op.nativeInt); + } + + /** + * Intersect the current clip with the specified rectangle, which is + * expressed in local coordinates. + * + * @param rect The rectangle to intersect with the current clip. + * @return true if the resulting clip is non-empty + */ + public native boolean clipRect(RectF rect); + + /** + * Intersect the current clip with the specified rectangle, which is + * expressed in local coordinates. + * + * @param rect The rectangle to intersect with the current clip. + * @return true if the resulting clip is non-empty + */ + public native boolean clipRect(Rect rect); + + /** + * Modify the current clip with the specified rectangle, which is + * expressed in local coordinates. + * + * @param left The left side of the rectangle to intersect with the + * current clip + * @param top The top of the rectangle to intersect with the current + * clip + * @param right The right side of the rectangle to intersect with the + * current clip + * @param bottom The bottom of the rectangle to intersect with the current + * clip + * @param op How the clip is modified + * @return true if the resulting clip is non-empty + */ + public boolean clipRect(float left, float top, float right, float bottom, + Region.Op op) { + return native_clipRect(mNativeCanvas, left, top, right, bottom, + op.nativeInt); + } + + /** + * Intersect the current clip with the specified rectangle, which is + * expressed in local coordinates. + * + * @param left The left side of the rectangle to intersect with the + * current clip + * @param top The top of the rectangle to intersect with the current clip + * @param right The right side of the rectangle to intersect with the + * current clip + * @param bottom The bottom of the rectangle to intersect with the current + * clip + * @return true if the resulting clip is non-empty + */ + public native boolean clipRect(float left, float top, + float right, float bottom); + + /** + * Intersect the current clip with the specified rectangle, which is + * expressed in local coordinates. + * + * @param left The left side of the rectangle to intersect with the + * current clip + * @param top The top of the rectangle to intersect with the current clip + * @param right The right side of the rectangle to intersect with the + * current clip + * @param bottom The bottom of the rectangle to intersect with the current + * clip + * @return true if the resulting clip is non-empty + */ + public native boolean clipRect(int left, int top, + int right, int bottom); + + /** + * Modify the current clip with the specified path. + * + * @param path The path to operate on the current clip + * @param op How the clip is modified + * @return true if the resulting is non-empty + */ + public boolean clipPath(Path path, Region.Op op) { + return native_clipPath(mNativeCanvas, path.ni(), op.nativeInt); + } + + /** + * Intersect the current clip with the specified path. + * + * @param path The path to intersect with the current clip + * @return true if the resulting is non-empty + */ + public boolean clipPath(Path path) { + return clipPath(path, Region.Op.INTERSECT); + } + + /** + * Modify the current clip with the specified region. Note that unlike + * clipRect() and clipPath() which transform their arguments by the + * current matrix, clipRegion() assumes its argument is already in the + * coordinate system of the current layer's bitmap, and so not + * transformation is performed. + * + * @param region The region to operate on the current clip, based on op + * @param op How the clip is modified + * @return true if the resulting is non-empty + */ + public boolean clipRegion(Region region, Region.Op op) { + return native_clipRegion(mNativeCanvas, region.ni(), op.nativeInt); + } + + /** + * Intersect the current clip with the specified region. Note that unlike + * clipRect() and clipPath() which transform their arguments by the + * current matrix, clipRegion() assumes its argument is already in the + * coordinate system of the current layer's bitmap, and so not + * transformation is performed. + * + * @param region The region to operate on the current clip, based on op + * @return true if the resulting is non-empty + */ + public boolean clipRegion(Region region) { + return clipRegion(region, Region.Op.INTERSECT); + } + + public DrawFilter getDrawFilter() { + return mDrawFilter; + } + + public void setDrawFilter(DrawFilter filter) { + int nativeFilter = 0; + if (filter != null) { + nativeFilter = filter.mNativeInt; + } + mDrawFilter = filter; + nativeSetDrawFilter(mNativeCanvas, nativeFilter); + } + + public enum EdgeType { + BW(0), //!< treat edges by just rounding to nearest pixel boundary + AA(1); //!< treat edges by rounding-out, since they may be antialiased + + EdgeType(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * Return true if the specified rectangle, after being transformed by the + * current matrix, would lie completely outside of the current clip. Call + * this to check if an area you intend to draw into is clipped out (and + * therefore you can skip making the draw calls). + * + * @param rect the rect to compare with the current clip + * @param type specifies how to treat the edges (BW or antialiased) + * @return true if the rect (transformed by the canvas' matrix) + * does not intersect with the canvas' clip + */ + public boolean quickReject(RectF rect, EdgeType type) { + return native_quickReject(mNativeCanvas, rect, type.nativeInt); + } + + /** + * Return true if the specified path, after being transformed by the + * current matrix, would lie completely outside of the current clip. Call + * this to check if an area you intend to draw into is clipped out (and + * therefore you can skip making the draw calls). Note: for speed it may + * return false even if the path itself might not intersect the clip + * (i.e. the bounds of the path intersects, but the path does not). + * + * @param path The path to compare with the current clip + * @param type true if the path should be considered antialiased, + * since that means it may + * affect a larger area (more pixels) than + * non-antialiased. + * @return true if the path (transformed by the canvas' matrix) + * does not intersect with the canvas' clip + */ + public boolean quickReject(Path path, EdgeType type) { + return native_quickReject(mNativeCanvas, path.ni(), type.nativeInt); + } + + /** + * Return true if the specified rectangle, after being transformed by the + * current matrix, would lie completely outside of the current clip. Call + * this to check if an area you intend to draw into is clipped out (and + * therefore you can skip making the draw calls). + * + * @param left The left side of the rectangle to compare with the + * current clip + * @param top The top of the rectangle to compare with the current + * clip + * @param right The right side of the rectangle to compare with the + * current clip + * @param bottom The bottom of the rectangle to compare with the + * current clip + * @param type true if the rect should be considered antialiased, + * since that means it may affect a larger area (more + * pixels) than non-antialiased. + * @return true if the rect (transformed by the canvas' matrix) + * does not intersect with the canvas' clip + */ + public boolean quickReject(float left, float top, float right, float bottom, + EdgeType type) { + return native_quickReject(mNativeCanvas, left, top, right, bottom, + type.nativeInt); + } + + /** + * Retrieve the clip bounds, returning true if they are non-empty. + * + * @param bounds Return the clip bounds here. If it is null, ignore it but + * still return true if the current clip is non-empty. + * @return true if the current clip is non-empty. + */ + public boolean getClipBounds(Rect bounds) { + return native_getClipBounds(mNativeCanvas, bounds); + } + + /** + * Retrieve the clip bounds. + * + * @return the clip bounds, or [0, 0, 0, 0] if the clip is empty. + */ + public final Rect getClipBounds() { + Rect r = new Rect(); + getClipBounds(r); + return r; + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified RGB color, using srcover porterduff mode. + * + * @param r red component (0..255) of the color to draw onto the canvas + * @param g green component (0..255) of the color to draw onto the canvas + * @param b blue component (0..255) of the color to draw onto the canvas + */ + public void drawRGB(int r, int g, int b) { + native_drawRGB(mNativeCanvas, r, g, b); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified ARGB color, using srcover porterduff mode. + * + * @param a alpha component (0..255) of the color to draw onto the canvas + * @param r red component (0..255) of the color to draw onto the canvas + * @param g green component (0..255) of the color to draw onto the canvas + * @param b blue component (0..255) of the color to draw onto the canvas + */ + public void drawARGB(int a, int r, int g, int b) { + native_drawARGB(mNativeCanvas, a, r, g, b); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified color, using srcover porterduff mode. + * + * @param color the color to draw onto the canvas + */ + public void drawColor(int color) { + native_drawColor(mNativeCanvas, color); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified color and porter-duff xfermode. + * + * @param color the color to draw with + * @param mode the porter-duff mode to apply to the color + */ + public void drawColor(int color, PorterDuff.Mode mode) { + native_drawColor(mNativeCanvas, color, mode.nativeInt); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with + * the specified paint. This is equivalent (but faster) to drawing an + * infinitely large rectangle with the specified paint. + * + * @param paint The paint used to draw onto the canvas + */ + public void drawPaint(Paint paint) { + native_drawPaint(mNativeCanvas, paint.mNativePaint); + } + + /** + * Draw a series of points. Each point is centered at the coordinate + * specified by pts[], and its diameter is specified by the paint's stroke + * width (as transformed by the canvas' CTM), with special treatment for + * a stroke width of 0, which always draws exactly 1 pixel (or at most 4 + * if antialiasing is enabled). The shape of the point is controlled by + * the paint's Cap type. The shape is a square, unless the cap type is + * Round, in which case the shape is a circle. + * + * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...] + * @param offset Number of values to skip before starting to draw. + * @param count The number of values to process, after skipping offset + * of them. Since one point uses two values, the number of + * "points" that are drawn is really (count >> 1). + * @param paint The paint used to draw the points + */ + public native void drawPoints(float[] pts, int offset, int count, + Paint paint); + + /** + * Helper for drawPoints() that assumes you want to draw the entire array + */ + public void drawPoints(float[] pts, Paint paint) { + drawPoints(pts, 0, pts.length, paint); + } + + /** + * Helper for drawPoints() for drawing a single point. + */ + public native void drawPoint(float x, float y, Paint paint); + + /** + * Draw a line segment with the specified start and stop x,y coordinates, + * using the specified paint. NOTE: since a line is always "framed", the + * Style is ignored in the paint. + * + * @param startX The x-coordinate of the start point of the line + * @param startY The y-coordinate of the start point of the line + * @param paint The paint used to draw the line + */ + public void drawLine(float startX, float startY, float stopX, float stopY, + Paint paint) { + native_drawLine(mNativeCanvas, startX, startY, stopX, stopY, + paint.mNativePaint); + } + + /** + * Draw a series of lines. Each line is taken from 4 consecutive values + * in the pts array. Thus to draw 1 line, the array must contain at least 4 + * values. This is logically the same as drawing the array as follows: + * drawLine(pts[0], pts[1], pts[2], pts[3]) followed by + * drawLine(pts[4], pts[5], pts[6], pts[7]) and so on. + * + * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...] + * @param offset Number of values in the array to skip before drawing. + * @param count The number of values in the array to process, after + * skipping "offset" of them. Since each line uses 4 values, + * the number of "lines" that are drawn is really + * (count >> 2). + * @param paint The paint used to draw the points + */ + public native void drawLines(float[] pts, int offset, int count, + Paint paint); + + public void drawLines(float[] pts, Paint paint) { + drawLines(pts, 0, pts.length, paint); + } + + /** + * Draw the specified Rect using the specified paint. The rectangle will + * be filled or framed based on the Style in the paint. + * + * @param rect The rect to be drawn + * @param paint The paint used to draw the rect + */ + public void drawRect(RectF rect, Paint paint) { + native_drawRect(mNativeCanvas, rect, paint.mNativePaint); + } + + /** + * Draw the specified Rect using the specified Paint. The rectangle + * will be filled or framed based on the Style in the paint. + * + * @param r The rectangle to be drawn. + * @param paint The paint used to draw the rectangle + */ + public void drawRect(Rect r, Paint paint) { + drawRect(r.left, r.top, r.right, r.bottom, paint); + } + + + /** + * Draw the specified Rect using the specified paint. The rectangle will + * be filled or framed based on the Style in the paint. + * + * @param left The left side of the rectangle to be drawn + * @param top The top side of the rectangle to be drawn + * @param right The right side of the rectangle to be drawn + * @param bottom The bottom side of the rectangle to be drawn + * @param paint The paint used to draw the rect + */ + public void drawRect(float left, float top, float right, float bottom, + Paint paint) { + native_drawRect(mNativeCanvas, left, top, right, bottom, + paint.mNativePaint); + } + + /** + * Draw the specified oval using the specified paint. The oval will be + * filled or framed based on the Style in the paint. + * + * @param oval The rectangle bounds of the oval to be drawn + */ + public void drawOval(RectF oval, Paint paint) { + if (oval == null) { + throw new NullPointerException(); + } + native_drawOval(mNativeCanvas, oval, paint.mNativePaint); + } + + /** + * Draw the specified circle using the specified paint. If radius is <= 0, + * then nothing will be drawn. The circle will be filled or framed based + * on the Style in the paint. + * + * @param cx The x-coordinate of the center of the cirle to be drawn + * @param cy The y-coordinate of the center of the cirle to be drawn + * @param radius The radius of the cirle to be drawn + * @param paint The paint used to draw the circle + */ + public void drawCircle(float cx, float cy, float radius, Paint paint) { + native_drawCircle(mNativeCanvas, cx, cy, radius, + paint.mNativePaint); + } + + /** + * Draw the specified arc, which will be scaled to fit inside the + * specified oval. If the sweep angle is >= 360, then the oval is drawn + * completely. Note that this differs slightly from SkPath::arcTo, which + * treats the sweep angle mod 360. + * + * @param oval The bounds of oval used to define the shape and size + * of the arc + * @param startAngle Starting angle (in degrees) where the arc begins + * @param sweepAngle Sweep angle (in degrees) measured clockwise + * @param useCenter If true, include the center of the oval in the arc, and + close it if it is being stroked. This will draw a wedge + * @param paint The paint used to draw the arc + */ + public void drawArc(RectF oval, float startAngle, float sweepAngle, + boolean useCenter, Paint paint) { + if (oval == null) { + throw new NullPointerException(); + } + native_drawArc(mNativeCanvas, oval, startAngle, sweepAngle, + useCenter, paint.mNativePaint); + } + + /** + * Draw the specified round-rect using the specified paint. The roundrect + * will be filled or framed based on the Style in the paint. + * + * @param rect The rectangular bounds of the roundRect to be drawn + * @param rx The x-radius of the oval used to round the corners + * @param ry The y-radius of the oval used to round the corners + * @param paint The paint used to draw the roundRect + */ + public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) { + if (rect == null) { + throw new NullPointerException(); + } + native_drawRoundRect(mNativeCanvas, rect, rx, ry, + paint.mNativePaint); + } + + /** + * Draw the specified path using the specified paint. The path will be + * filled or framed based on the Style in the paint. + * + * @param path The path to be drawn + * @param paint The paint used to draw the path + */ + public void drawPath(Path path, Paint paint) { + native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint); + } + + private static void throwIfRecycled(Bitmap bitmap) { + if (bitmap.isRecycled()) { + throw new RuntimeException( + "Canvas: trying to use a recycled bitmap " + bitmap); + } + } + + /** + * Draw the specified bitmap, with its top/left corner at (x,y), using + * the specified paint, transformed by the current matrix. + * Note: if the paint contains a maskfilter that generates a mask which + * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), + * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. + * Thus the color outside of the original width/height will be the edge + * color replicated. + * + * @param bitmap The bitmap to be drawn + * @param left The position of the left side of the bitmap being drawn + * @param top The position of the top side of the bitmap being drawn + * @param paint The paint used to draw the bitmap (may be null) + */ + public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { + throwIfRecycled(bitmap); + native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top, + paint != null ? paint.mNativePaint : 0); + } + + /** + * Draw the specified bitmap, scaling/translating automatically to fill + * the destination rectangle. If the source rectangle is not null, it + * specifies the subset of the bitmap to draw. + * Note: if the paint contains a maskfilter that generates a mask which + * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), + * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. + * Thus the color outside of the original width/height will be the edge + * color replicated. + * + * @param bitmap The bitmap to be drawn + * @param src May be null. The subset of the bitmap to be drawn + * @param dst The rectangle that the bitmap will be scaled/translated + * to fit into + * @param paint May be null. The paint used to draw the bitmap + */ + public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { + if (dst == null) { + throw new NullPointerException(); + } + throwIfRecycled(bitmap); + native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst, + paint != null ? paint.mNativePaint : 0); + } + + /** + * Draw the specified bitmap, scaling/translating automatically to fill + * the destination rectangle. If the source rectangle is not null, it + * specifies the subset of the bitmap to draw. + * Note: if the paint contains a maskfilter that generates a mask which + * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), + * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. + * Thus the color outside of the original width/height will be the edge + * color replicated. + * + * @param bitmap The bitmap to be drawn + * @param src May be null. The subset of the bitmap to be drawn + * @param dst The rectangle that the bitmap will be scaled/translated + * to fit into + * @param paint May be null. The paint used to draw the bitmap + */ + public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { + if (dst == null) { + throw new NullPointerException(); + } + throwIfRecycled(bitmap); + native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst, + paint != null ? paint.mNativePaint : 0); + } + + /** + * Treat the specified array of colors as a bitmap, and draw it. This gives + * the same result as first creating a bitmap from the array, and then + * drawing it, but this method avoids explicitly creating a bitmap object + * which can be more efficient if the colors are changing often. + * + * @param colors Array of colors representing the pixels of the bitmap + * @param offset Offset into the array of colors for the first pixel + * @param stride The number of of colors in the array between rows (must be + * >= width or <= -width). + * @param x The X coordinate for where to draw the bitmap + * @param y The Y coordinate for where to draw the bitmap + * @param width The width of the bitmap + * @param height The height of the bitmap + * @param hasAlpha True if the alpha channel of the colors contains valid + * values. If false, the alpha byte is ignored (assumed to + * be 0xFF for every pixel). + * @param paint May be null. The paint used to draw the bitmap + */ + public void drawBitmap(int[] colors, int offset, int stride, int x, int y, + int width, int height, boolean hasAlpha, + Paint paint) { + // check for valid input + if (width < 0) { + throw new IllegalArgumentException("width must be >= 0"); + } + if (height < 0) { + throw new IllegalArgumentException("height must be >= 0"); + } + 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(); + } + // quick escape if there's nothing to draw + if (width == 0 || height == 0) { + return; + } + // punch down to native for the actual draw + native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, + width, height, hasAlpha, + paint != null ? paint.mNativePaint : 0); + } + + /** + * Draw the bitmap using the specified matrix. + * + * @param bitmap The bitmap to draw + * @param matrix The matrix used to transform the bitmap when it is drawn + * @param paint May be null. The paint used to draw the bitmap + */ + public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { + nativeDrawBitmapMatrix(mNativeCanvas, bitmap.ni(), matrix.ni(), + paint != null ? paint.mNativePaint : 0); + } + + private static void checkRange(int length, int offset, int count) { + if ((offset | count) < 0 || offset + count > length) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + /** + * Draw the bitmap through the mesh, where mesh vertices are evenly + * distributed across the bitmap. There are meshWidth+1 vertices across, and + * meshHeight+1 vertices down. The verts array is accessed in row-major + * order, so that the first meshWidth+1 vertices are distributed across the + * top of the bitmap from left to right. A more general version of this + * methid is drawVertices(). + * + * @param bitmap The bitmap to draw using the mesh + * @param meshWidth The number of columns in the mesh. Nothing is drawn if + * this is 0 + * @param meshHeight The number of rows in the mesh. Nothing is drawn if + * this is 0 + * @param verts Array of x,y pairs, specifying where the mesh should be + * drawn. There must be at least + * (meshWidth+1) * (meshHeight+1) * 2 + meshOffset values + * in the array + * @param vertOffset Number of verts elements to skip before drawing + * @param colors May be null. Specifies a color at each vertex, which is + * interpolated across the cell, and whose values are + * multiplied by the corresponding bitmap colors. If not null, + * there must be at least (meshWidth+1) * (meshHeight+1) + + * colorOffset values in the array. + * @param colorOffset Number of color elements to skip before drawing + * @param paint May be null. The paint used to draw the bitmap + */ + public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, + float[] verts, int vertOffset, + int[] colors, int colorOffset, Paint paint) { + if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + if (meshWidth == 0 || meshHeight == 0) { + return; + } + int count = (meshWidth + 1) * (meshHeight + 1); + // we mul by 2 since we need two floats per vertex + checkRange(verts.length, vertOffset, count * 2); + if (colors != null) { + // no mul by 2, since we need only 1 color per vertex + checkRange(colors.length, colorOffset, count); + } + nativeDrawBitmapMesh(mNativeCanvas, bitmap.ni(), meshWidth, meshHeight, + verts, vertOffset, colors, colorOffset, + paint != null ? paint.mNativePaint : 0); + } + + public enum VertexMode { + TRIANGLES(0), + TRIANGLE_STRIP(1), + TRIANGLE_FAN(2); + + VertexMode(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * Draw the array of vertices, interpreted as triangles (based on mode). The + * verts array is required, and specifies the x,y pairs for each vertex. If + * texs is non-null, then it is used to specify the coordinate in shader + * coordinates to use at each vertex (the paint must have a shader in this + * case). If there is no texs array, but there is a color array, then each + * color is interpolated across its corresponding triangle in a gradient. If + * both texs and colors arrays are present, then they behave as before, but + * the resulting color at each pixels is the result of multiplying the + * colors from the shader and the color-gradient together. The indices array + * is optional, but if it is present, then it is used to specify the index + * of each triangle, rather than just walking through the arrays in order. + * + * @param mode How to interpret the array of vertices + * @param vertexCount The number of values in the vertices array (and + * corresponding texs and colors arrays if non-null). Each logical + * vertex is two values (x, y), vertexCount must be a multiple of 2. + * @param verts Array of vertices for the mesh + * @param vertOffset Number of values in the verts to skip before drawing. + * @param texs May be null. If not null, specifies the coordinates to sample + * into the current shader (e.g. bitmap tile or gradient) + * @param texOffset Number of values in texs to skip before drawing. + * @param colors May be null. If not null, specifies a color for each + * vertex, to be interpolated across the triangle. + * @param colorOffset Number of values in colors to skip before drawing. + * @param indices If not null, array of indices to reference into the + * vertex (texs, colors) array. + * @param indexCount number of entries in the indices array (if not null). + * @param paint Specifies the shader to use if the texs array is non-null. + */ + public void drawVertices(VertexMode mode, int vertexCount, + float[] verts, int vertOffset, + float[] texs, int texOffset, + int[] colors, int colorOffset, + short[] indices, int indexOffset, + int indexCount, Paint paint) { + checkRange(verts.length, vertOffset, vertexCount); + if (texs != null) { + checkRange(texs.length, texOffset, vertexCount); + } + if (colors != null) { + checkRange(colors.length, colorOffset, vertexCount); + } + if (indices != null) { + checkRange(indices.length, indexOffset, indexCount); + } + nativeDrawVertices(mNativeCanvas, mode.nativeInt, vertexCount, verts, + vertOffset, texs, texOffset, colors, colorOffset, + indices, indexOffset, indexCount, paint.mNativePaint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint. The + * origin is interpreted based on the Align setting in the paint. + * + * @param text The text to be drawn + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the origin of the text being drawn + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawText(char[] text, int index, int count, float x, float y, + Paint paint) { + if ((index | count | (index + count) | + (text.length - index - count)) < 0) { + throw new IndexOutOfBoundsException(); + } + native_drawText(mNativeCanvas, text, index, count, x, y, + paint.mNativePaint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint. The + * origin is interpreted based on the Align setting in the paint. + * + * @param text The text to be drawn + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the origin of the text being drawn + * @param paint The paint used for the text (e.g. color, size, style) + */ + public native void drawText(String text, float x, float y, Paint paint); + + /** + * Draw the text, with origin at (x,y), using the specified paint. + * The origin is interpreted based on the Align setting in the paint. + * + * @param text The text to be drawn + * @param start The index of the first character in text to draw + * @param end (end - 1) is the index of the last character in text to draw + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the origin of the text being drawn + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawText(String text, int start, int end, float x, float y, + Paint paint) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + native_drawText(mNativeCanvas, text, start, end, x, y, + paint.mNativePaint); + } + + /** + * Draw the specified range of text, specified by start/end, with its + * origin at (x,y), in the specified Paint. The origin is interpreted + * based on the Align setting in the Paint. + * + * @param text The text to be drawn + * @param start The index of the first character in text to draw + * @param end (end - 1) is the index of the last character in text + * to draw + * @param x The x-coordinate of origin for where to draw the text + * @param y The y-coordinate of origin for where to draw the text + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawText(CharSequence text, int start, int end, float x, + float y, Paint paint) { + if (text instanceof String || text instanceof SpannedString || + text instanceof SpannableString) { + native_drawText(mNativeCanvas, text.toString(), start, end, x, y, + paint.mNativePaint); + } + else if (text instanceof GraphicsOperations) { + ((GraphicsOperations) text).drawText(this, start, end, x, y, + paint); + } + else { + char[] buf = TemporaryBuffer.obtain(end - start); + TextUtils.getChars(text, start, end, buf, 0); + drawText(buf, 0, end - start, x, y, paint); + TemporaryBuffer.recycle(buf); + } + } + + /** + * Draw the text in the array, with each character's origin specified by + * the pos array. + * + * @param text The text to be drawn + * @param index The index of the first character to draw + * @param count The number of characters to draw, starting from index. + * @param pos Array of [x,y] positions, used to position each + * character + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawPosText(char[] text, int index, int count, float[] pos, + Paint paint) { + if (index < 0 || index + count > text.length || count*2 > pos.length) { + throw new IndexOutOfBoundsException(); + } + native_drawPosText(mNativeCanvas, text, index, count, pos, + paint.mNativePaint); + } + + /** + * Draw the text in the array, with each character's origin specified by + * the pos array. + * + * @param text The text to be drawn + * @param pos Array of [x,y] positions, used to position each character + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawPosText(String text, float[] pos, Paint paint) { + if (text.length()*2 > pos.length) { + throw new ArrayIndexOutOfBoundsException(); + } + native_drawPosText(mNativeCanvas, text, pos, paint.mNativePaint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint, along + * the specified path. The paint's Align setting determins where along the + * path to start the text. + * + * @param text The text to be drawn + * @param path The path the text should follow for its baseline + * @param hOffset The distance along the path to add to the text's + * starting position + * @param vOffset The distance above(-) or below(+) the path to position + * the text + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawTextOnPath(char[] text, int index, int count, Path path, + float hOffset, float vOffset, Paint paint) { + if (index < 0 || index + count > text.length) { + throw new ArrayIndexOutOfBoundsException(); + } + native_drawTextOnPath(mNativeCanvas, text, index, count, + path.ni(), hOffset, vOffset, + paint.mNativePaint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint, along + * the specified path. The paint's Align setting determins where along the + * path to start the text. + * + * @param text The text to be drawn + * @param path The path the text should follow for its baseline + * @param hOffset The distance along the path to add to the text's + * starting position + * @param vOffset The distance above(-) or below(+) the path to position + * the text + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawTextOnPath(String text, Path path, float hOffset, + float vOffset, Paint paint) { + if (text.length() > 0) { + native_drawTextOnPath(mNativeCanvas, text, path.ni(), + hOffset, vOffset, paint.mNativePaint); + } + } + + /** + * Save the canvas state, draw the picture, and restore the canvas state. + * This differs from picture.draw(canvas), which does not perform any + * save/restore. + * + * @param picture The picture to be drawn + */ + public void drawPicture(Picture picture) { + picture.endRecording(); + native_drawPicture(mNativeCanvas, picture.ni()); + } + + /** + * Draw the picture, stretched to fit into the dst rectangle. + */ + public void drawPicture(Picture picture, RectF dst) { + save(); + translate(dst.left, dst.top); + if (picture.getWidth() > 0 && picture.getHeight() > 0) { + scale(dst.width() / picture.getWidth(), + dst.height() / picture.getHeight()); + } + drawPicture(picture); + restore(); + } + + /** + * Draw the picture, stretched to fit into the dst rectangle. + */ + public void drawPicture(Picture picture, Rect dst) { + save(); + translate(dst.left, dst.top); + if (picture.getWidth() > 0 && picture.getHeight() > 0) { + scale((float)dst.width() / picture.getWidth(), + (float)dst.height() / picture.getHeight()); + } + drawPicture(picture); + restore(); + } + + protected void finalize() throws Throwable { + super.finalize(); + finalizer(mNativeCanvas); + } + + private static native int initRaster(int nativeBitmapOrZero); + private static native int initGL(); + private static native void native_setBitmap(int nativeCanvas, int bitmap); + private static native void nativeSetViewport(int nCanvas, int w, int h); + private static native int native_saveLayer(int nativeCanvas, RectF bounds, + int paint, int layerFlags); + private static native int native_saveLayer(int nativeCanvas, float l, + float t, float r, float b, + int paint, int layerFlags); + private static native int native_saveLayerAlpha(int nativeCanvas, + RectF bounds, int alpha, + int layerFlags); + private static native int native_saveLayerAlpha(int nativeCanvas, float l, + float t, float r, float b, + int alpha, int layerFlags); + + private static native void native_concat(int nCanvas, int nMatrix); + private static native void native_setMatrix(int nCanvas, int nMatrix); + private static native boolean native_clipRect(int nCanvas, + float left, float top, + float right, float bottom, + int regionOp); + private static native boolean native_clipPath(int nativeCanvas, + int nativePath, + int regionOp); + private static native boolean native_clipRegion(int nativeCanvas, + int nativeRegion, + int regionOp); + private static native void nativeSetDrawFilter(int nativeCanvas, + int nativeFilter); + private static native boolean native_getClipBounds(int nativeCanvas, + Rect bounds); + private static native void native_getCTM(int canvas, int matrix); + private static native boolean native_quickReject(int nativeCanvas, + RectF rect, + int native_edgeType); + private static native boolean native_quickReject(int nativeCanvas, + int path, + int native_edgeType); + private static native boolean native_quickReject(int nativeCanvas, + float left, float top, + float right, float bottom, + int native_edgeType); + private static native void native_drawRGB(int nativeCanvas, int r, int g, + int b); + private static native void native_drawARGB(int nativeCanvas, int a, int r, + int g, int b); + private static native void native_drawColor(int nativeCanvas, int color); + private static native void native_drawColor(int nativeCanvas, int color, + int mode); + private static native void native_drawPaint(int nativeCanvas, int paint); + private static native void native_drawLine(int nativeCanvas, float startX, + float startY, float stopX, + float stopY, int paint); + private static native void native_drawRect(int nativeCanvas, RectF rect, + int paint); + private static native void native_drawRect(int nativeCanvas, float left, + float top, float right, + float bottom, int paint); + private static native void native_drawOval(int nativeCanvas, RectF oval, + int paint); + private static native void native_drawCircle(int nativeCanvas, float cx, + float cy, float radius, + int paint); + private static native void native_drawArc(int nativeCanvas, RectF oval, + float startAngle, float sweep, + boolean useCenter, int paint); + private static native void native_drawRoundRect(int nativeCanvas, + RectF rect, float rx, + float ry, int paint); + private static native void native_drawPath(int nativeCanvas, int path, + int paint); + private static native void native_drawBitmap(int nativeCanvas, int bitmap, + float left, float top, + int nativePaintOrZero); + private static native void native_drawBitmap(int nativeCanvas, int bitmap, + Rect src, RectF dst, + int nativePaintOrZero); + private static native void native_drawBitmap(int nativeCanvas, int bitmap, + Rect src, Rect dst, + int nativePaintOrZero); + private static native void native_drawBitmap(int nativeCanvas, int[] colors, + int offset, int stride, int x, + int y, int width, int height, + boolean hasAlpha, + int nativePaintOrZero); + private static native void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, + int nMatrix, int nPaint); + private static native void nativeDrawBitmapMesh(int nCanvas, int nBitmap, + int meshWidth, int meshHeight, float[] verts, int vertOffset, + int[] colors, int colorOffset, int nPaint); + private static native void nativeDrawVertices(int nCanvas, int mode, int n, + float[] verts, int vertOffset, float[] texs, int texOffset, + int[] colors, int colorOffset, short[] indices, + int indexOffset, int indexCount, int nPaint); + + private static native void native_drawText(int nativeCanvas, char[] text, + int index, int count, float x, + float y, int paint); + private static native void native_drawText(int nativeCanvas, String text, + int start, int end, float x, + float y, int paint); + private static native void native_drawPosText(int nativeCanvas, + char[] text, int index, + int count, float[] pos, + int paint); + private static native void native_drawPosText(int nativeCanvas, + String text, float[] pos, + int paint); + private static native void native_drawTextOnPath(int nativeCanvas, + char[] text, int index, + int count, int path, + float hOffset, + float vOffset, int paint); + private static native void native_drawTextOnPath(int nativeCanvas, + String text, int path, + float hOffset, + float vOffset, int paint); + private static native void native_drawPicture(int nativeCanvas, + int nativePicture); + private static native void finalizer(int nativeCanvas); +} diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java new file mode 100644 index 0000000..3fc391c --- /dev/null +++ b/graphics/java/android/graphics/Color.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2006 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.util.HashMap; +import java.util.Locale; + +/** + * The Color class defines methods for creating and converting color ints. + * Colors are represented as packed ints, made up of 4 bytes: alpha, red, + * green, blue. The values are unpremultiplied, meaning any transparency is + * stored solely in the alpha component, and not in the color components. The + * components are stored as follows (alpha << 24) | (red << 16) | + * (green << 8) | blue. Each component ranges between 0..255 with 0 + * meaning no contribution for that component, and 255 meaning 100% + * contribution. Thus opaque-black would be 0xFF000000 (100% opaque but + * no contributes from red, gree, blue, and opaque-white would be 0xFFFFFFFF + */ +public class Color { + public static final int BLACK = 0xFF000000; + public static final int DKGRAY = 0xFF444444; + public static final int GRAY = 0xFF888888; + public static final int LTGRAY = 0xFFCCCCCC; + public static final int WHITE = 0xFFFFFFFF; + public static final int RED = 0xFFFF0000; + public static final int GREEN = 0xFF00FF00; + public static final int BLUE = 0xFF0000FF; + public static final int YELLOW = 0xFFFFFF00; + public static final int CYAN = 0xFF00FFFF; + public static final int MAGENTA = 0xFFFF00FF; + public static final int TRANSPARENT = 0; + + /** + * Return the alpha component of a color int. This is the same as saying + * color >>> 24 + */ + public static int alpha(int color) { + return color >>> 24; + } + + /** + * Return the red component of a color int. This is the same as saying + * (color >> 16) & 0xFF + */ + public static int red(int color) { + return (color >> 16) & 0xFF; + } + + /** + * Return the green component of a color int. This is the same as saying + * (color >> 8) & 0xFF + */ + public static int green(int color) { + return (color >> 8) & 0xFF; + } + + /** + * Return the blue component of a color int. This is the same as saying + * color & 0xFF + */ + public static int blue(int color) { + return color & 0xFF; + } + + /** + * Return a color-int from red, green, blue components. + * The alpha component is implicity 255 (fully opaque). + * These component values should be [0..255], but there is no + * range check performed, so if they are out of range, the + * returned color is undefined. + * @param red Red component [0..255] of the color + * @param green Green component [0..255] of the color + * @param blue Blue component [0..255] of the color + */ + public static int rgb(int red, int green, int blue) { + return (0xFF << 24) | (red << 16) | (green << 8) | blue; + } + + /** + * Return a color-int from alpha, red, green, blue components. + * These component values should be [0..255], but there is no + * range check performed, so if they are out of range, the + * returned color is undefined. + * @param alpha Alpha component [0..255] of the color + * @param red Red component [0..255] of the color + * @param green Green component [0..255] of the color + * @param blue Blue component [0..255] of the color + */ + public static int argb(int alpha, int red, int green, int blue) { + return (alpha << 24) | (red << 16) | (green << 8) | blue; + } + + /** + * Parse the color string, and return the corresponding color-int. + * If the string cannot be parsed, throws an IllegalArgumentException + * exception. Supported formats are: + * #RRGGBB + * #AARRGGBB + * 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta', + * 'yellow', 'lightgray', 'darkgray' + */ + public static int parseColor(String colorString) { + if (colorString.charAt(0) == '#') { + // Use a long to avoid rollovers on #ffXXXXXX + long color = Long.parseLong(colorString.substring(1), 16); + if (colorString.length() == 7) { + // Set the alpha value + color |= 0x00000000ff000000; + } else if (colorString.length() != 9) { + throw new IllegalArgumentException("Unknown color"); + } + return (int)color; + } else { + Integer color = sColorNameMap.get(colorString.toLowerCase(Locale.US)); + if (color != null) { + return color; + } + } + throw new IllegalArgumentException("Unknown color"); + } + + /** + * Convert RGB components to HSV. + * hsv[0] is Hue [0 .. 360) + * hsv[1] is Saturation [0...1] + * hsv[2] is Value [0...1] + * @param red red component value [0..255] + * @param green green component value [0..255] + * @param blue blue component value [0..255] + * @param hsv 3 element array which holds the resulting HSV components. + */ + public static void RGBToHSV(int red, int green, int blue, float hsv[]) { + if (hsv.length < 3) { + throw new RuntimeException("3 components required for hsv"); + } + nativeRGBToHSV(red, green, blue, hsv); + } + + /** + * Convert the argb color to its HSV components. + * hsv[0] is Hue [0 .. 360) + * hsv[1] is Saturation [0...1] + * hsv[2] is Value [0...1] + * @param color the argb color to convert. The alpha component is ignored. + * @param hsv 3 element array which holds the resulting HSV components. + */ + public static void colorToHSV(int color, float hsv[]) { + RGBToHSV((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsv); + } + + /** + * Convert HSV components to an ARGB color. Alpha set to 0xFF. + * hsv[0] is Hue [0 .. 360) + * hsv[1] is Saturation [0...1] + * hsv[2] is Value [0...1] + * If hsv values are out of range, they are pinned. + * @param hsv 3 element array which holds the input HSV components. + * @return the resulting argb color + */ + public static int HSVToColor(float hsv[]) { + return HSVToColor(0xFF, hsv); + } + + /** + * Convert HSV components to an ARGB color. The alpha component is passed + * through unchanged. + * hsv[0] is Hue [0 .. 360) + * hsv[1] is Saturation [0...1] + * hsv[2] is Value [0...1] + * If hsv values are out of range, they are pinned. + * @param alpha the alpha component of the returned argb color. + * @param hsv 3 element array which holds the input HSV components. + * @return the resulting argb color + */ + public static int HSVToColor(int alpha, float hsv[]) { + if (hsv.length < 3) { + throw new RuntimeException("3 components required for hsv"); + } + return nativeHSVToColor(alpha, hsv); + } + + private static native void nativeRGBToHSV(int red, int greed, int blue, + float hsv[]); + private static native int nativeHSVToColor(int alpha, float hsv[]); + + private static final HashMap<String, Integer> sColorNameMap; + + static { + sColorNameMap = new HashMap(); + sColorNameMap.put("black", Integer.valueOf(BLACK)); + sColorNameMap.put("darkgray", Integer.valueOf(DKGRAY)); + sColorNameMap.put("gray", Integer.valueOf(GRAY)); + sColorNameMap.put("lightgray", Integer.valueOf(LTGRAY)); + sColorNameMap.put("white", Integer.valueOf(WHITE)); + sColorNameMap.put("red", Integer.valueOf(RED)); + sColorNameMap.put("green", Integer.valueOf(GREEN)); + sColorNameMap.put("blue", Integer.valueOf(BLUE)); + sColorNameMap.put("yellow", Integer.valueOf(YELLOW)); + sColorNameMap.put("cyan", Integer.valueOf(CYAN)); + sColorNameMap.put("magenta", Integer.valueOf(MAGENTA)); + } +} + diff --git a/graphics/java/android/graphics/ColorFilter.java b/graphics/java/android/graphics/ColorFilter.java new file mode 100644 index 0000000..76f2c7f --- /dev/null +++ b/graphics/java/android/graphics/ColorFilter.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2006 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. + */ + +// This file was generated from the C++ include file: SkColorFilter.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +package android.graphics; + + +public class ColorFilter { + + protected void finalize() throws Throwable { + finalizer(native_instance); + } + + private static native void finalizer(int native_instance); + + int native_instance; +} diff --git a/graphics/java/android/graphics/ColorMatrix.java b/graphics/java/android/graphics/ColorMatrix.java new file mode 100644 index 0000000..2478712 --- /dev/null +++ b/graphics/java/android/graphics/ColorMatrix.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2007 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 android.util.FloatMath; + +/** + * 5x4 matrix for transforming the color+alpha components of a Bitmap. + * The matrix is stored in a single array, and its treated as follows: + * [ a, b, c, d, e, + * f, g, h, i, j, + * k, l, m, n, o, + * p, q, r, s, t ] + * + * When applied to a color [r, g, b, a], the resulting color is computed as + * (after clamping) + * R' = a*R + b*G + c*B + d*A + e; + * G' = f*R + g*G + h*B + i*A + j; + * B' = k*R + l*G + m*B + n*A + o; + * A' = p*R + q*G + r*B + s*A + t; + */ +public class ColorMatrix { + + private final float[] mArray = new float[20]; + + /** + * Create a new colormatrix initialized to identity (as if reset() had + * been called). + */ + public ColorMatrix() { + reset(); + } + + /** + * Create a new colormatrix initialized with the specified array of values. + */ + public ColorMatrix(float[] src) { + System.arraycopy(src, 0, mArray, 0, 20); + } + + /** + * Create a new colormatrix initialized with the specified colormatrix. + */ + public ColorMatrix(ColorMatrix src) { + System.arraycopy(src.mArray, 0, mArray, 0, 20); + } + + /** + * Return the array of floats representing this colormatrix. + */ + public final float[] getArray() { return mArray; } + + /** + * Set this colormatrix to identity: + * [ 1 0 0 0 0 - red vector + * 0 1 0 0 0 - green vector + * 0 0 1 0 0 - blue vector + * 0 0 0 1 0 ] - alpha vector + */ + public void reset() { + final float[] a = mArray; + + for (int i = 19; i > 0; --i) { + a[i] = 0; + } + a[0] = a[6] = a[12] = a[18] = 1; + } + + /** + * Assign the src colormatrix into this matrix, copying all of its values. + */ + public void set(ColorMatrix src) { + System.arraycopy(src.mArray, 0, mArray, 0, 20); + } + + /** + * Assign the array of floats into this matrix, copying all of its values. + */ + public void set(float[] src) { + System.arraycopy(src, 0, mArray, 0, 20); + } + + /** + * Set this colormatrix to scale by the specified values. + */ + public void setScale(float rScale, float gScale, float bScale, + float aScale) { + final float[] a = mArray; + + for (int i = 19; i > 0; --i) { + a[i] = 0; + } + a[0] = rScale; + a[6] = gScale; + a[12] = bScale; + a[18] = aScale; + } + + public void setRotate(int axis, float degrees) { + reset(); + float radians = degrees * (float)Math.PI / 180; + float cosine = FloatMath.cos(radians); + float sine = FloatMath.sin(radians); + switch (axis) { + case 0: + mArray[6] = mArray[12] = cosine; + mArray[7] = sine; + mArray[11] = -sine; + break; + case 1: + mArray[0] = mArray[17] = cosine; + mArray[2] = sine; + mArray[15] = -sine; + break; + case 2: + mArray[0] = mArray[6] = cosine; + mArray[1] = sine; + mArray[5] = -sine; + break; + default: + throw new RuntimeException(); + } + } + + /** + * Set this colormatrix to the concatenation of the two specified + * colormatrices, such that the resulting colormatrix has the same effect + * as applying matB and then applying matA. It is legal for either matA or + * matB to be the same colormatrix as this. + */ + public void setConcat(ColorMatrix matA, ColorMatrix matB) { + float[] tmp = null; + + if (matA == this || matB == this) { + tmp = new float[20]; + } + else { + tmp = mArray; + } + + final float[] a = matA.mArray; + final float[] b = matB.mArray; + int index = 0; + for (int j = 0; j < 20; j += 5) { + for (int i = 0; i < 4; i++) { + tmp[index++] = a[j + 0] * b[i + 0] + a[j + 1] * b[i + 5] + + a[j + 2] * b[i + 10] + a[j + 3] * b[i + 15]; + } + tmp[index++] = a[j + 0] * b[4] + a[j + 1] * b[9] + + a[j + 2] * b[14] + a[j + 3] * b[19] + + a[j + 4]; + } + + if (tmp != mArray) { + System.arraycopy(tmp, 0, mArray, 0, 20); + } + } + + /** + * Concat this colormatrix with the specified prematrix. This is logically + * the same as calling setConcat(this, prematrix); + */ + public void preConcat(ColorMatrix prematrix) { + setConcat(this, prematrix); + } + + /** + * Concat this colormatrix with the specified postmatrix. This is logically + * the same as calling setConcat(postmatrix, this); + */ + public void postConcat(ColorMatrix postmatrix) { + setConcat(postmatrix, this); + } + + /////////////////////////////////////////////////////////////////////////// + + /** + * Set the matrix to affect the saturation of colors. A value of 0 maps the + * color to gray-scale. 1 is identity. + */ + public void setSaturation(float sat) { + reset(); + float[] m = mArray; + + final float invSat = 1 - sat; + final float R = 0.213f * invSat; + final float G = 0.715f * invSat; + final float B = 0.072f * invSat; + + m[0] = R + sat; m[1] = G; m[2] = B; + m[5] = R; m[6] = G + sat; m[7] = B; + m[10] = R; m[11] = G; m[12] = B + sat; + } + + /** + * Set the matrix to convert RGB to YUV + */ + public void setRGB2YUV() { + reset(); + float[] m = mArray; + // these coefficients match those in libjpeg + m[0] = 0.299f; m[1] = 0.587f; m[2] = 0.114f; + m[5] = -0.16874f; m[6] = -0.33126f; m[7] = 0.5f; + m[10] = 0.5f; m[11] = -0.41869f; m[12] = -0.08131f; + } + + /** + * Set the matrix to convert from YUV to RGB + */ + public void setYUV2RGB() { + reset(); + float[] m = mArray; + // these coefficients match those in libjpeg + m[2] = 1.402f; + m[5] = 1; m[6] = -0.34414f; m[7] = -0.71414f; + m[10] = 1; m[11] = 1.772f; m[12] = 0; + } +} + diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java new file mode 100644 index 0000000..5d73cff --- /dev/null +++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007 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; + +public class ColorMatrixColorFilter extends ColorFilter { + /** + * Create a colorfilter that transforms colors through a 4x5 color matrix. + * + * @param matrix 4x5 matrix used to transform colors. It is copied into + * the filter, so changes made to the matrix after the filter + * is constructed will not be reflected in the filter. + */ + public ColorMatrixColorFilter(ColorMatrix matrix) { + native_instance = nativeColorMatrixFilter(matrix.getArray()); + } + + /** + * Create a colorfilter that transforms colors through a 4x5 color matrix. + * + * @param array array of floats used to transform colors, treated as a 4x5 + * matrix. The first 20 entries of the array are copied into + * the filter. See ColorMatrix. + */ + public ColorMatrixColorFilter(float[] array) { + if (array.length < 20) { + throw new ArrayIndexOutOfBoundsException(); + } + native_instance = nativeColorMatrixFilter(array); + } + + private static native int nativeColorMatrixFilter(float[] array); +} diff --git a/graphics/java/android/graphics/ComposePathEffect.java b/graphics/java/android/graphics/ComposePathEffect.java new file mode 100644 index 0000000..beac78e --- /dev/null +++ b/graphics/java/android/graphics/ComposePathEffect.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2007 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; + +public class ComposePathEffect extends PathEffect { + + /** + * Construct a PathEffect whose effect is to apply first the inner effect + * and the the outer pathEffect (e.g. outer(inner(path))). + */ + public ComposePathEffect(PathEffect outerpe, PathEffect innerpe) { + native_instance = nativeCreate(outerpe.native_instance, + innerpe.native_instance); + } + + private static native int nativeCreate(int outerpe, int innerpe); +} + diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java new file mode 100644 index 0000000..a06d30b --- /dev/null +++ b/graphics/java/android/graphics/ComposeShader.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 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; + +/** A subclass of shader that returns the coposition of two other shaders, combined by + an {@link android.graphics.Xfermode} subclass. +*/ +public class ComposeShader extends Shader { + /** Create a new compose shader, given shaders A, B, and a combining mode. + When the mode is applied, it will be given the result from shader A as its + "dst", and the result of from shader B as its "src". + @param shaderA The colors from this shader are seen as the "dst" by the mode + @param shaderB The colors from this shader are seen as the "src" by the mode + @param mode The mode that combines the colors from the two shaders. If mode + is null, then SRC_OVER is assumed. + */ + public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) { + native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance, + (mode != null) ? mode.native_instance : 0); + } + + /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode. + When the mode is applied, it will be given the result from shader A as its + "dst", and the result of from shader B as its "src". + @param shaderA The colors from this shader are seen as the "dst" by the mode + @param shaderB The colors from this shader are seen as the "src" by the mode + @param mode The PorterDuff mode that combines the colors from the two shaders. + */ + public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) { + native_instance = nativeCreate2(shaderA.native_instance, shaderB.native_instance, + mode.nativeInt); + } + + private static native int nativeCreate1(int native_shaderA, int native_shaderB, int native_mode); + private static native int nativeCreate2(int native_shaderA, int native_shaderB, int porterDuffMode); +} + diff --git a/graphics/java/android/graphics/CornerPathEffect.java b/graphics/java/android/graphics/CornerPathEffect.java new file mode 100644 index 0000000..400c886 --- /dev/null +++ b/graphics/java/android/graphics/CornerPathEffect.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 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; + +public class CornerPathEffect extends PathEffect { + + /** + * Transforms geometries that are drawn (either STROKE or FILL styles) by + * replacing any sharp angles between line segments into rounded angles of + * the specified radius. + * @param radius Amount to round sharp angles between line segments. + */ + public CornerPathEffect(float radius) { + native_instance = nativeCreate(radius); + } + + private static native int nativeCreate(float radius); +} + diff --git a/graphics/java/android/graphics/DashPathEffect.java b/graphics/java/android/graphics/DashPathEffect.java new file mode 100644 index 0000000..3deca4a --- /dev/null +++ b/graphics/java/android/graphics/DashPathEffect.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 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; + +public class DashPathEffect extends PathEffect { + + /** + * The intervals array must contain an even number of entries (>=2), with + * the even indices specifying the "on" intervals, and the odd indices + * specifying the "off" intervals. phase is an offset into the intervals + * array (mod the sum of all of the intervals). The intervals array + * controlls the width of the dashes. The paint's strokeWidth controlls the + * height of the dashes. + * Note: this patheffect only affects drawing with the paint's style is set + * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with + * style == FILL. + * @param intervals array of ON and OFF distances + * @param phase offset before the first ON interval is drawn + */ + public DashPathEffect(float intervals[], float phase) { + if (intervals.length < 2) { + throw new ArrayIndexOutOfBoundsException(); + } + native_instance = nativeCreate(intervals, phase); + } + + private static native int nativeCreate(float intervals[], float phase); +} + diff --git a/graphics/java/android/graphics/DiscretePathEffect.java b/graphics/java/android/graphics/DiscretePathEffect.java new file mode 100644 index 0000000..de8b2f0 --- /dev/null +++ b/graphics/java/android/graphics/DiscretePathEffect.java @@ -0,0 +1,31 @@ +/* + * 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; + +public class DiscretePathEffect extends PathEffect { + + /** + * Chop the path into lines of segmentLength, randomly deviating from the + * original path by deviation. + */ + public DiscretePathEffect(float segmentLength, float deviation) { + native_instance = nativeCreate(segmentLength, deviation); + } + + private static native int nativeCreate(float length, float deviation); +} + diff --git a/graphics/java/android/graphics/DrawFilter.java b/graphics/java/android/graphics/DrawFilter.java new file mode 100644 index 0000000..6b44ed7 --- /dev/null +++ b/graphics/java/android/graphics/DrawFilter.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * A DrawFilter subclass can be installed in a Canvas. When it is present, it + * can modify the paint that is used to draw (temporarily). With this, a filter + * can disable/enable antialiasing, or change the color for everything this is + * drawn. + */ +public class DrawFilter { + + // this is set by subclasses, but don't make it public + /* package */ int mNativeInt; // pointer to native object + + protected void finalize() throws Throwable { + nativeDestructor(mNativeInt); + } + + private static native void nativeDestructor(int nativeDrawFilter); +} + diff --git a/graphics/java/android/graphics/EmbossMaskFilter.java b/graphics/java/android/graphics/EmbossMaskFilter.java new file mode 100644 index 0000000..5dd8611 --- /dev/null +++ b/graphics/java/android/graphics/EmbossMaskFilter.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2006 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; + +public class EmbossMaskFilter extends MaskFilter { + /** + * Create an emboss maskfilter + * + * @param direction array of 3 scalars [x, y, z] specifying the direction of the light source + * @param ambient 0...1 amount of ambient light + * @param specular coefficient for specular highlights (e.g. 8) + * @param blurRadius amount to blur before applying lighting (e.g. 3) + * @return the emboss maskfilter + */ + public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) { + if (direction.length < 3) { + throw new ArrayIndexOutOfBoundsException(); + } + native_instance = nativeConstructor(direction, ambient, specular, blurRadius); + } + + private static native int nativeConstructor(float[] direction, float ambient, float specular, float blurRadius); +} + diff --git a/graphics/java/android/graphics/Interpolator.java b/graphics/java/android/graphics/Interpolator.java new file mode 100644 index 0000000..75851a6 --- /dev/null +++ b/graphics/java/android/graphics/Interpolator.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2007 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 android.os.SystemClock; + +public class Interpolator { + + public Interpolator(int valueCount) { + mValueCount = valueCount; + mFrameCount = 2; + native_instance = nativeConstructor(valueCount, 2); + } + + public Interpolator(int valueCount, int frameCount) { + mValueCount = valueCount; + mFrameCount = frameCount; + native_instance = nativeConstructor(valueCount, frameCount); + } + + /** + * Reset the Interpolator to have the specified number of values and an + * implicit keyFrame count of 2 (just a start and end). After this call the + * values for each keyFrame must be assigned using setKeyFrame(). + */ + public void reset(int valueCount) { + reset(valueCount, 2); + } + + /** + * Reset the Interpolator to have the specified number of values and + * keyFrames. After this call the values for each keyFrame must be assigned + * using setKeyFrame(). + */ + public void reset(int valueCount, int frameCount) { + mValueCount = valueCount; + mFrameCount = frameCount; + nativeReset(native_instance, valueCount, frameCount); + } + + public final int getKeyFrameCount() { + return mFrameCount; + } + + public final int getValueCount() { + return mValueCount; + } + + /** + * Assign the keyFrame (specified by index) a time value and an array of key + * values (with an implicity blend array of [0, 0, 1, 1] giving linear + * transition to the next set of key values). + * + * @param index The index of the key frame to assign + * @param msec The time (in mililiseconds) for this key frame. Based on the + * SystemClock.uptimeMillis() clock + * @param values Array of values associated with theis key frame + */ + public void setKeyFrame(int index, int msec, float[] values) { + setKeyFrame(index, msec, values, null); + } + + /** + * Assign the keyFrame (specified by index) a time value and an array of key + * values and blend array. + * + * @param index The index of the key frame to assign + * @param msec The time (in mililiseconds) for this key frame. Based on the + * SystemClock.uptimeMillis() clock + * @param values Array of values associated with theis key frame + * @param blend (may be null) Optional array of 4 blend values + */ + public void setKeyFrame(int index, int msec, float[] values, float[] blend) { + if (index < 0 || index >= mFrameCount) { + throw new IndexOutOfBoundsException(); + } + if (values.length < mValueCount) { + throw new ArrayStoreException(); + } + if (blend != null && blend.length < 4) { + throw new ArrayStoreException(); + } + nativeSetKeyFrame(native_instance, index, msec, values, blend); + } + + /** + * Set a repeat count (which may be fractional) for the interpolator, and + * whether the interpolator should mirror its repeats. The default settings + * are repeatCount = 1, and mirror = false. + */ + public void setRepeatMirror(float repeatCount, boolean mirror) { + if (repeatCount >= 0) { + nativeSetRepeatMirror(native_instance, repeatCount, mirror); + } + } + + public enum Result { + NORMAL, + FREEZE_START, + FREEZE_END + } + + /** + * Calls timeToValues(msec, values) with the msec set to now (by calling + * (int)SystemClock.uptimeMillis().) + */ + public Result timeToValues(float[] values) { + return timeToValues((int)SystemClock.uptimeMillis(), values); + } + + /** + * Given a millisecond time value (msec), return the interpolated values and + * return whether the specified time was within the range of key times + * (NORMAL), was before the first key time (FREEZE_START) or after the last + * key time (FREEZE_END). In any event, computed values are always returned. + * + * @param msec The time (in milliseconds) used to sample into the + * Interpolator. Based on the SystemClock.uptimeMillis() clock + * @param values Where to write the computed values (may be NULL). + * @return how the values were computed (even if values == null) + */ + public Result timeToValues(int msec, float[] values) { + if (values != null && values.length < mValueCount) { + throw new ArrayStoreException(); + } + switch (nativeTimeToValues(native_instance, msec, values)) { + case 0: return Result.NORMAL; + case 1: return Result.FREEZE_START; + default: return Result.FREEZE_END; + } + } + + @Override + protected void finalize() throws Throwable { + nativeDestructor(native_instance); + } + + private int mValueCount; + private int mFrameCount; + private final int native_instance; + + private static native int nativeConstructor(int valueCount, int frameCount); + private static native void nativeDestructor(int native_instance); + private static native void nativeReset(int native_instance, int valueCount, int frameCount); + private static native void nativeSetKeyFrame(int native_instance, int index, int msec, float[] values, float[] blend); + private static native void nativeSetRepeatMirror(int native_instance, float repeatCount, boolean mirror); + private static native int nativeTimeToValues(int native_instance, int msec, float[] values); +} + diff --git a/graphics/java/android/graphics/LayerRasterizer.java b/graphics/java/android/graphics/LayerRasterizer.java new file mode 100644 index 0000000..9bd55a5 --- /dev/null +++ b/graphics/java/android/graphics/LayerRasterizer.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006 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; + +public class LayerRasterizer extends Rasterizer { + public LayerRasterizer() { + native_instance = nativeConstructor(); + } + + /** Add a new layer (above any previous layers) to the rasterizer. + The layer will extract those fields that affect the mask from + the specified paint, but will not retain a reference to the paint + object itself, so it may be reused without danger of side-effects. + */ + public void addLayer(Paint paint, float dx, float dy) { + nativeAddLayer(native_instance, paint.mNativePaint, dx, dy); + } + + public void addLayer(Paint paint) { + nativeAddLayer(native_instance, paint.mNativePaint, 0, 0); + } + + private static native int nativeConstructor(); + private static native void nativeAddLayer(int native_layer, int native_paint, float dx, float dy); +} + diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java new file mode 100644 index 0000000..5562389 --- /dev/null +++ b/graphics/java/android/graphics/LightingColorFilter.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2006 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. + */ + +// This file was generated from the C++ include file: SkColorFilter.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +package android.graphics; + +public class LightingColorFilter extends ColorFilter { + + /** + * Create a colorfilter that multiplies the RGB channels by one color, and then adds a second color, + * pinning the result for each component to [0..255]. The alpha components of the mul and add arguments + * are ignored. + */ + public LightingColorFilter(int mul, int add) { + native_instance = native_CreateLightingFilter(mul, add); + } + + private static native int native_CreateLightingFilter(int mul, int add); +} diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java new file mode 100644 index 0000000..e3db105 --- /dev/null +++ b/graphics/java/android/graphics/LinearGradient.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007 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; + +public class LinearGradient extends Shader { + + /** Create a shader that draws a linear gradient along a line. + @param x0 The x-coordinate for the start of the gradient line + @param y0 The y-coordinate for the start of the gradient line + @param x1 The x-coordinate for the end of the gradient line + @param y1 The y-coordinate for the end of the gradient line + @param colors The colors to be distributed along the gradient line + @param positions May be null. The relative positions [0..1] of + each corresponding color in the colors array. If this is null, + the the colors are distributed evenly along the gradient line. + @param tile The Shader tiling mode + */ + public LinearGradient(float x0, float y0, float x1, float y1, + int colors[], float positions[], TileMode tile) { + if (colors.length < 2) { + throw new IllegalArgumentException("needs >= 2 number of colors"); + } + if (positions != null && colors.length != positions.length) { + throw new IllegalArgumentException("color and position arrays must be of equal length"); + } + native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt); + } + + /** Create a shader that draws a linear gradient along a line. + @param x0 The x-coordinate for the start of the gradient line + @param y0 The y-coordinate for the start of the gradient line + @param x1 The x-coordinate for the end of the gradient line + @param y1 The y-coordinate for the end of the gradient line + @param color0 The color at the start of the gradient line. + @param color1 The color at the end of the gradient line. + @param tile The Shader tiling mode + */ + public LinearGradient(float x0, float y0, float x1, float y1, + int color0, int color1, TileMode tile) { + native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt); + } + + + private static native int nativeCreate1(float x0, float y0, float x1, float y1, + int colors[], float positions[], int tileMode); + private static native int nativeCreate2(float x0, float y0, float x1, float y1, + int color0, int color1, int tileMode); +} + diff --git a/graphics/java/android/graphics/MaskFilter.java b/graphics/java/android/graphics/MaskFilter.java new file mode 100644 index 0000000..4ebb619 --- /dev/null +++ b/graphics/java/android/graphics/MaskFilter.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006 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; + +/** + * MaskFilter is the base class for object that perform transformations on + * an alpha-channel mask before drawing it. A subclass of MaskFilter may be + * installed into a Paint. Blur and emboss are implemented as subclasses of MaskFilter. + */ +public class MaskFilter { + + protected void finalize() throws Throwable { + nativeDestructor(native_instance); + } + + private static native void nativeDestructor(int native_filter); + int native_instance; +} diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java new file mode 100644 index 0000000..2681eae --- /dev/null +++ b/graphics/java/android/graphics/Matrix.java @@ -0,0 +1,639 @@ +/* + * Copyright (C) 2006 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; + + +/** + * The Matrix class holds a 3x3 matrix for transforming coordinates. + * Matrix does not have a constructor, so it must be explicitly initialized + * using either reset() - to construct an identity matrix, or one of the set..() + * functions (e.g. setTranslate, setRotate, etc.). + */ +public class Matrix { + + public static final int MSCALE_X = 0; //!< use with getValues/setValues + public static final int MSKEW_X = 1; //!< use with getValues/setValues + public static final int MTRANS_X = 2; //!< use with getValues/setValues + public static final int MSKEW_Y = 3; //!< use with getValues/setValues + public static final int MSCALE_Y = 4; //!< use with getValues/setValues + public static final int MTRANS_Y = 5; //!< use with getValues/setValues + public static final int MPERSP_0 = 6; //!< use with getValues/setValues + public static final int MPERSP_1 = 7; //!< use with getValues/setValues + public static final int MPERSP_2 = 8; //!< use with getValues/setValues + + /* package */ int native_instance; + + /** + * Create an identity matrix + */ + public Matrix() { + native_instance = native_create(0); + } + + /** + * Create a matrix that is a (deep) copy of src + * @param src The matrix to copy into this matrix + */ + public Matrix(Matrix src) { + native_instance = native_create(src != null ? src.native_instance : 0); + } + + /** + * Returns true if the matrix is identity. + * This maybe faster than testing if (getType() == 0) + */ + public boolean isIdentity() { + return native_isIdentity(native_instance); + } + + /** + * Returns true if will map a rectangle to another rectangle. This can be + * true if the matrix is identity, scale-only, or rotates a multiple of 90 + * degrees. + */ + public boolean rectStaysRect() { + return native_rectStaysRect(native_instance); + } + + /** + * (deep) copy the src matrix into this matrix. If src is null, reset this + * matrix to the identity matrix. + */ + public void set(Matrix src) { + if (src == null) { + reset(); + } else { + native_set(native_instance, src.native_instance); + } + } + + /** Returns true iff obj is a Matrix and its values equal our values. + */ + public boolean equals(Object obj) { + return obj != null && + obj instanceof Matrix && + native_equals(native_instance, ((Matrix)obj).native_instance); + } + + /** Set the matrix to identity */ + public void reset() { + native_reset(native_instance); + } + + /** Set the matrix to translate by (dx, dy). */ + public void setTranslate(float dx, float dy) { + native_setTranslate(native_instance, dx, dy); + } + + /** + * Set the matrix to scale by sx and sy, with a pivot point at (px, py). + * The pivot point is the coordinate that should remain unchanged by the + * specified transformation. + */ + public void setScale(float sx, float sy, float px, float py) { + native_setScale(native_instance, sx, sy, px, py); + } + + /** Set the matrix to scale by sx and sy. */ + public void setScale(float sx, float sy) { + native_setScale(native_instance, sx, sy); + } + + /** + * Set the matrix to rotate by the specified number of degrees, with a pivot + * point at (px, py). The pivot point is the coordinate that should remain + * unchanged by the specified transformation. + */ + public void setRotate(float degrees, float px, float py) { + native_setRotate(native_instance, degrees, px, py); + } + + /** + * Set the matrix to rotate about (0,0) by the specified number of degrees. + */ + public void setRotate(float degrees) { + native_setRotate(native_instance, degrees); + } + + /** + * Set the matrix to rotate by the specified sine and cosine values, with a + * pivot point at (px, py). The pivot point is the coordinate that should + * remain unchanged by the specified transformation. + */ + public void setSinCos(float sinValue, float cosValue, float px, float py) { + native_setSinCos(native_instance, sinValue, cosValue, px, py); + } + + /** Set the matrix to rotate by the specified sine and cosine values. */ + public void setSinCos(float sinValue, float cosValue) { + native_setSinCos(native_instance, sinValue, cosValue); + } + + /** + * Set the matrix to skew by sx and sy, with a pivot point at (px, py). + * The pivot point is the coordinate that should remain unchanged by the + * specified transformation. + */ + public void setSkew(float kx, float ky, float px, float py) { + native_setSkew(native_instance, kx, ky, px, py); + } + + /** Set the matrix to skew by sx and sy. */ + public void setSkew(float kx, float ky) { + native_setSkew(native_instance, kx, ky); + } + + /** + * Set the matrix to the concatenation of the two specified matrices, + * returning true if the the result can be represented. Either of the two + * matrices may also be the target matrix. this = a * b + */ + public boolean setConcat(Matrix a, Matrix b) { + return native_setConcat(native_instance, a.native_instance, + b.native_instance); + } + + /** + * Preconcats the matrix with the specified translation. + * M' = M * T(dx, dy) + */ + public boolean preTranslate(float dx, float dy) { + return native_preTranslate(native_instance, dx, dy); + } + + /** + * Preconcats the matrix with the specified scale. + * M' = M * S(sx, sy, px, py) + */ + public boolean preScale(float sx, float sy, float px, float py) { + return native_preScale(native_instance, sx, sy, px, py); + } + + /** + * Preconcats the matrix with the specified scale. + * M' = M * S(sx, sy) + */ + public boolean preScale(float sx, float sy) { + return native_preScale(native_instance, sx, sy); + } + + /** + * Preconcats the matrix with the specified rotation. + * M' = M * R(degrees, px, py) + */ + public boolean preRotate(float degrees, float px, float py) { + return native_preRotate(native_instance, degrees, px, py); + } + + /** + * Preconcats the matrix with the specified rotation. + * M' = M * R(degrees) + */ + public boolean preRotate(float degrees) { + return native_preRotate(native_instance, degrees); + } + + /** + * Preconcats the matrix with the specified skew. + * M' = M * K(kx, ky, px, py) + */ + public boolean preSkew(float kx, float ky, float px, float py) { + return native_preSkew(native_instance, kx, ky, px, py); + } + + /** + * Preconcats the matrix with the specified skew. + * M' = M * K(kx, ky) + */ + public boolean preSkew(float kx, float ky) { + return native_preSkew(native_instance, kx, ky); + } + + /** + * Preconcats the matrix with the specified matrix. + * M' = M * other + */ + public boolean preConcat(Matrix other) { + return native_preConcat(native_instance, other.native_instance); + } + + /** + * Postconcats the matrix with the specified translation. + * M' = T(dx, dy) * M + */ + public boolean postTranslate(float dx, float dy) { + return native_postTranslate(native_instance, dx, dy); + } + + /** + * Postconcats the matrix with the specified scale. + * M' = S(sx, sy, px, py) * M + */ + public boolean postScale(float sx, float sy, float px, float py) { + return native_postScale(native_instance, sx, sy, px, py); + } + + /** + * Postconcats the matrix with the specified scale. + * M' = S(sx, sy) * M + */ + public boolean postScale(float sx, float sy) { + return native_postScale(native_instance, sx, sy); + } + + /** + * Postconcats the matrix with the specified rotation. + * M' = R(degrees, px, py) * M + */ + public boolean postRotate(float degrees, float px, float py) { + return native_postRotate(native_instance, degrees, px, py); + } + + /** + * Postconcats the matrix with the specified rotation. + * M' = R(degrees) * M + */ + public boolean postRotate(float degrees) { + return native_postRotate(native_instance, degrees); + } + + /** + * Postconcats the matrix with the specified skew. + * M' = K(kx, ky, px, py) * M + */ + public boolean postSkew(float kx, float ky, float px, float py) { + return native_postSkew(native_instance, kx, ky, px, py); + } + + /** + * Postconcats the matrix with the specified skew. + * M' = K(kx, ky) * M + */ + public boolean postSkew(float kx, float ky) { + return native_postSkew(native_instance, kx, ky); + } + + /** + * Postconcats the matrix with the specified matrix. + * M' = other * M + */ + public boolean postConcat(Matrix other) { + return native_postConcat(native_instance, other.native_instance); + } + + /** Controlls how the src rect should align into the dst rect for + setRectToRect(). + */ + public enum ScaleToFit { + /** + * Scale in X and Y independently, so that src matches dst exactly. + * This may change the aspect ratio of the src. + */ + FILL (0), + /** + * Compute a scale that will maintain the original src aspect ratio, + * but will also ensure that src fits entirely inside dst. At least one + * axis (X or Y) will fit exactly. START aligns the result to the + * left and top edges of dst. + */ + START (1), + /** + * Compute a scale that will maintain the original src aspect ratio, + * but will also ensure that src fits entirely inside dst. At least one + * axis (X or Y) will fit exactly. The result is centered inside dst. + */ + CENTER (2), + /** + * Compute a scale that will maintain the original src aspect ratio, + * but will also ensure that src fits entirely inside dst. At least one + * axis (X or Y) will fit exactly. END aligns the result to the + * right and bottom edges of dst. + */ + END (3); + + // the native values must match those in SkMatrix.h + ScaleToFit(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * Set the matrix to the scale and translate values that map the source + * rectangle to the destination rectangle, returning true if the the result + * can be represented. + * + * @param src the source rectangle to map from. + * @param dst the destination rectangle to map to. + * @param stf the ScaleToFit option + * @return true if the matrix can be represented by the rectangle mapping. + */ + public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) { + if (dst == null || src == null) { + throw new NullPointerException(); + } + return native_setRectToRect(native_instance, src, dst, stf.nativeInt); + } + + // private helper to perform range checks on arrays of "points" + private static void checkPointArrays(float[] src, int srcIndex, + float[] dst, int dstIndex, + int pointCount) { + // check for too-small and too-big indices + int srcStop = srcIndex + (pointCount << 1); + int dstStop = dstIndex + (pointCount << 1); + if ((pointCount | srcIndex | dstIndex | srcStop | dstStop) < 0 || + srcStop > src.length || dstStop > dst.length) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + /** + * Set the matrix such that the specified src points would map to the + * specified dst points. The "points" are represented as an array of floats, + * order [x0, y0, x1, y1, ...], where each "point" is 2 float values. + * + * @param src The array of src [x,y] pairs (points) + * @param srcIndex Index of the first pair of src values + * @param dst The array of dst [x,y] pairs (points) + * @param dstIndex Index of the first pair of dst values + * @param pointCount The number of pairs/points to be used. Must be [0..4] + * @return true if the matrix was set to the specified transformation + */ + public boolean setPolyToPoly(float[] src, int srcIndex, + float[] dst, int dstIndex, + int pointCount) { + if (pointCount > 4) { + throw new IllegalArgumentException(); + } + checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); + return native_setPolyToPoly(native_instance, src, srcIndex, + dst, dstIndex, pointCount); + } + + /** + * If this matrix can be inverted, return true and if inverse is not null, + * set inverse to be the inverse of this matrix. If this matrix cannot be + * inverted, ignore inverse and return false. + */ + public boolean invert(Matrix inverse) { + return native_invert(native_instance, inverse.native_instance); + } + + /** + * Apply this matrix to the array of 2D points specified by src, and write + * the transformed points into the array of points specified by dst. The + * two arrays represent their "points" as pairs of floats [x, y]. + * + * @param dst The array of dst points (x,y pairs) + * @param dstIndex The index of the first [x,y] pair of dst floats + * @param src The array of src points (x,y pairs) + * @param srcIndex The index of the first [x,y] pair of src floats + * @param pointCount The number of points (x,y pairs) to transform + */ + public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, + int pointCount) { + checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); + native_mapPoints(native_instance, dst, dstIndex, src, srcIndex, + pointCount, true); + } + + /** + * Apply this matrix to the array of 2D vectors specified by src, and write + * the transformed vectors into the array of vectors specified by dst. The + * two arrays represent their "vectors" as pairs of floats [x, y]. + * + * @param dst The array of dst vectors (x,y pairs) + * @param dstIndex The index of the first [x,y] pair of dst floats + * @param src The array of src vectors (x,y pairs) + * @param srcIndex The index of the first [x,y] pair of src floats + * @param vectorCount The number of vectors (x,y pairs) to transform + */ + public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, + int vectorCount) { + checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount); + native_mapPoints(native_instance, dst, dstIndex, src, srcIndex, + vectorCount, false); + } + + /** + * Apply this matrix to the array of 2D points specified by src, and write + * the transformed points into the array of points specified by dst. The + * two arrays represent their "points" as pairs of floats [x, y]. + * + * @param dst The array of dst points (x,y pairs) + * @param src The array of src points (x,y pairs) + */ + public void mapPoints(float[] dst, float[] src) { + if (dst.length != src.length) { + throw new ArrayIndexOutOfBoundsException(); + } + mapPoints(dst, 0, src, 0, dst.length >> 1); + } + + /** + * Apply this matrix to the array of 2D vectors specified by src, and write + * the transformed vectors into the array of vectors specified by dst. The + * two arrays represent their "vectors" as pairs of floats [x, y]. + * + * @param dst The array of dst vectors (x,y pairs) + * @param src The array of src vectors (x,y pairs) + */ + public void mapVectors(float[] dst, float[] src) { + if (dst.length != src.length) { + throw new ArrayIndexOutOfBoundsException(); + } + mapVectors(dst, 0, src, 0, dst.length >> 1); + } + + /** + * Apply this matrix to the array of 2D points, and write the transformed + * points back into the array + * + * @param pts The array [x0, y0, x1, y1, ...] of points to transform. + */ + public void mapPoints(float[] pts) { + mapPoints(pts, 0, pts, 0, pts.length >> 1); + } + + /** + * Apply this matrix to the array of 2D vectors, and write the transformed + * vectors back into the array. + * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform. + */ + public void mapVectors(float[] vecs) { + mapVectors(vecs, 0, vecs, 0, vecs.length >> 1); + } + + /** + * Apply this matrix to the src rectangle, and write the transformed + * rectangle into dst. This is accomplished by transforming the 4 corners of + * src, and then setting dst to the bounds of those points. + * + * @param dst Where the transformed rectangle is written. + * @param src The original rectangle to be transformed. + * @return the result of calling rectStaysRect() + */ + public boolean mapRect(RectF dst, RectF src) { + if (dst == null || src == null) { + throw new NullPointerException(); + } + return native_mapRect(native_instance, dst, src); + } + + /** + * Apply this matrix to the rectangle, and write the transformed rectangle + * back into it. This is accomplished by transforming the 4 corners of rect, + * and then setting it to the bounds of those points + * + * @param rect The rectangle to transform. + * @return the result of calling rectStaysRect() + */ + public boolean mapRect(RectF rect) { + return mapRect(rect, rect); + } + + /** + * Return the mean radius of a circle after it has been mapped by + * this matrix. NOTE: in perspective this value assumes the circle + * has its center at the origin. + */ + public float mapRadius(float radius) { + return native_mapRadius(native_instance, radius); + } + + /** Copy 9 values from the matrix into the array. + */ + public void getValues(float[] values) { + if (values.length < 9) { + throw new ArrayIndexOutOfBoundsException(); + } + native_getValues(native_instance, values); + } + + /** Copy 9 values from the array into the matrix. + Depending on the implementation of Matrix, these may be + transformed into 16.16 integers in the Matrix, such that + a subsequent call to getValues() will not yield exactly + the same values. + */ + public void setValues(float[] values) { + if (values.length < 9) { + throw new ArrayIndexOutOfBoundsException(); + } + native_setValues(native_instance, values); + } + + public String toString() { + return "Matrix{" + toShortString() + "}"; + + } + + public String toShortString() { + float[] values = new float[9]; + getValues(values); + return "[" + + values[0] + ", " + values[1] + ", " + values[2] + "][" + + values[3] + ", " + values[4] + ", " + values[5] + "][" + + values[6] + ", " + values[7] + ", " + values[8] + "]"; + + } + + protected void finalize() throws Throwable { + finalizer(native_instance); + } + + /*package*/ final int ni() { + return native_instance; + } + + private static native int native_create(int native_src_or_zero); + private static native boolean native_isIdentity(int native_object); + private static native boolean native_rectStaysRect(int native_object); + private static native void native_reset(int native_object); + private static native void native_set(int native_object, int other); + private static native void native_setTranslate(int native_object, + float dx, float dy); + private static native void native_setScale(int native_object, + float sx, float sy, float px, float py); + private static native void native_setScale(int native_object, + float sx, float sy); + private static native void native_setRotate(int native_object, + float degrees, float px, float py); + private static native void native_setRotate(int native_object, + float degrees); + private static native void native_setSinCos(int native_object, + float sinValue, float cosValue, float px, float py); + private static native void native_setSinCos(int native_object, + float sinValue, float cosValue); + private static native void native_setSkew(int native_object, + float kx, float ky, float px, float py); + private static native void native_setSkew(int native_object, + float kx, float ky); + private static native boolean native_setConcat(int native_object, + int a, int b); + private static native boolean native_preTranslate(int native_object, + float dx, float dy); + private static native boolean native_preScale(int native_object, + float sx, float sy, float px, float py); + private static native boolean native_preScale(int native_object, + float sx, float sy); + private static native boolean native_preRotate(int native_object, + float degrees, float px, float py); + private static native boolean native_preRotate(int native_object, + float degrees); + private static native boolean native_preSkew(int native_object, + float kx, float ky, float px, float py); + private static native boolean native_preSkew(int native_object, + float kx, float ky); + private static native boolean native_preConcat(int native_object, + int other_matrix); + private static native boolean native_postTranslate(int native_object, + float dx, float dy); + private static native boolean native_postScale(int native_object, + float sx, float sy, float px, float py); + private static native boolean native_postScale(int native_object, + float sx, float sy); + private static native boolean native_postRotate(int native_object, + float degrees, float px, float py); + private static native boolean native_postRotate(int native_object, + float degrees); + private static native boolean native_postSkew(int native_object, + float kx, float ky, float px, float py); + private static native boolean native_postSkew(int native_object, + float kx, float ky); + private static native boolean native_postConcat(int native_object, + int other_matrix); + private static native boolean native_setRectToRect(int native_object, + RectF src, RectF dst, int stf); + private static native boolean native_setPolyToPoly(int native_object, + float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount); + private static native boolean native_invert(int native_object, int inverse); + private static native void native_mapPoints(int native_object, + float[] dst, int dstIndex, float[] src, int srcIndex, + int ptCount, boolean isPts); + private static native boolean native_mapRect(int native_object, + RectF dst, RectF src); + private static native float native_mapRadius(int native_object, + float radius); + private static native void native_getValues(int native_object, + float[] values); + private static native void native_setValues(int native_object, + float[] values); + private static native boolean native_equals(int native_a, int native_b); + private static native void finalizer(int native_instance); +} diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java new file mode 100644 index 0000000..95e9946 --- /dev/null +++ b/graphics/java/android/graphics/Movie.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006 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.io.InputStream; +import java.io.FileInputStream; + +public class Movie { + private final int mNativeMovie; + + private Movie(int nativeMovie) { + if (nativeMovie == 0) { + throw new RuntimeException("native movie creation failed"); + } + mNativeMovie = nativeMovie; + } + + public native int width(); + public native int height(); + public native boolean isOpaque(); + public native int duration(); + + public native boolean setTime(int relativeMilliseconds); + + public native void draw(Canvas canvas, float x, float y, Paint paint); + + public void draw(Canvas canvas, float x, float y) { + draw(canvas, x, y, null); + } + + public static native Movie decodeStream(InputStream is); + public static native Movie decodeByteArray(byte[] data, int offset, + int length); + + public static Movie decodeFile(String pathName) { + InputStream is; + try { + is = new FileInputStream(pathName); + } + catch (java.io.FileNotFoundException e) { + return null; + } + return decodeTempStream(is); + } + + private static Movie decodeTempStream(InputStream is) { + Movie moov = null; + try { + moov = decodeStream(is); + is.close(); + } + catch (java.io.IOException e) { + /* do nothing. + If the exception happened on open, moov will be null. + If it happened on close, moov is still valid. + */ + } + return moov; + } +} diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java new file mode 100644 index 0000000..cabd848 --- /dev/null +++ b/graphics/java/android/graphics/NinePatch.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2006 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; + + +/** + * The NinePatch class permits drawing a bitmap in nine sections. + * The four corners are unscaled; the four edges are scaled in one axis, + * and the middle is scaled in both axes. Normally, the middle is + * transparent so that the patch can provide a selection about a rectangle. + * Essentially, it allows the creation of custom graphics that will scale the + * way that you define, when content added within the image exceeds the normal + * bounds of the graphic. For a thorough explanation of a NinePatch image, + * read the discussion in the + * <a href="{@docRoot}reference/available-resources.html#ninepatch">Available + * Resource Types</a> document. + * <p> + * The <a href="{@docRoot}reference/draw9patch.html">Draw 9-patch</a> + * tool offers an extremely handy way to create your NinePatch images, + * using a WYSIWYG graphics editor. + * </p> + */ +public class NinePatch { + /** + * Create a drawable projection from a bitmap to nine patches. + * + * @param bitmap The bitmap describing the patches. + * @param chunk The 9-patch data chunk describing how the underlying + * bitmap is split apart and drawn. + * @param srcName The name of the source for the bitmap. Might be null. + */ + public NinePatch(Bitmap bitmap, byte[] chunk, String srcName) { + mBitmap = bitmap; + mChunk = chunk; + mSrcName = srcName; + validateNinePatchChunk(mBitmap.ni(), chunk); + } + + public void setPaint(Paint p) { + mPaint = p; + } + + /** + * Draw a bitmap to nine patches. + * + * @param canvas A container for the current matrix and clip used to draw the bitmap. + * @param location Where to draw the bitmap. + */ + public void draw(Canvas canvas, RectF location) { + nativeDraw(canvas.mNativeCanvas, location, + mBitmap.ni(), mChunk, + mPaint != null ? mPaint.mNativePaint : 0); + } + + /** + * Draw a bitmap to nine patches. + * + * @param canvas A container for the current matrix and clip used to draw the bitmap. + * @param location Where to draw the bitmap. + */ + public void draw(Canvas canvas, Rect location) { + nativeDraw(canvas.mNativeCanvas, location, + mBitmap.ni(), mChunk, + mPaint != null ? mPaint.mNativePaint : 0); + } + + /** + * Draw a bitmap to nine patches. + * + * @param canvas A container for the current matrix and clip used to draw the bitmap. + * @param location Where to draw the bitmap. + * @param paint The Paint to draw through. + */ + public void draw(Canvas canvas, Rect location, Paint paint) { + nativeDraw(canvas.mNativeCanvas, location, + mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0); + } + + public int getWidth() { + return mBitmap.getWidth(); + } + + public int getHeight() { + return mBitmap.getHeight(); + } + + public final boolean hasAlpha() { + return mBitmap.hasAlpha(); + } + + public final Region getTransparentRegion(Rect location) { + int r = nativeGetTransparentRegion(mBitmap.ni(), mChunk, location); + return r != 0 ? new Region(r) : null; + } + + public native static boolean isNinePatchChunk(byte[] chunk); + + private final Rect mRect = new Rect(); + private final Bitmap mBitmap; + private final byte[] mChunk; + private Paint mPaint; + private String mSrcName; // Useful for debugging + + private static native void validateNinePatchChunk(int bitmap, byte[] chunk); + private static native void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance, + byte[] c, int paint_instance_or_null); + private static native void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance, + byte[] c, int paint_instance_or_null); + private static native int nativeGetTransparentRegion( + int bitmap, byte[] chunk, Rect location); +} diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java new file mode 100644 index 0000000..6fac969 --- /dev/null +++ b/graphics/java/android/graphics/Paint.java @@ -0,0 +1,1314 @@ +/* + * Copyright (C) 2006 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 android.text.TextUtils; +import android.text.SpannableString; +import android.text.SpannedString; +import android.text.GraphicsOperations; + +/** + * The Paint class holds the style and color information about how to draw + * geometries, text and bitmaps. + */ +public class Paint { + + /*package*/ int mNativePaint; + private ColorFilter mColorFilter; + private MaskFilter mMaskFilter; + private PathEffect mPathEffect; + private Rasterizer mRasterizer; + private Shader mShader; + private Typeface mTypeface; + private Xfermode mXfermode; + + private static final Style[] sStyleArray = { + Style.FILL, Style.STROKE, Style.FILL_AND_STROKE + }; + private static final Cap[] sCapArray = { + Cap.BUTT, Cap.ROUND, Cap.SQUARE + }; + private static final Join[] sJoinArray = { + Join.MITER, Join.ROUND, Join.BEVEL + }; + private static final Align[] sAlignArray = { + Align.LEFT, Align.CENTER, Align.RIGHT + }; + + /** bit mask for the flag enabling antialiasing */ + public static final int ANTI_ALIAS_FLAG = 0x01; + /** bit mask for the flag enabling bitmap filtering */ + public static final int FILTER_BITMAP_FLAG = 0x02; + /** bit mask for the flag enabling dithering */ + public static final int DITHER_FLAG = 0x04; + /** bit mask for the flag enabling underline text */ + public static final int UNDERLINE_TEXT_FLAG = 0x08; + /** bit mask for the flag enabling strike-thru text */ + public static final int STRIKE_THRU_TEXT_FLAG = 0x10; + /** bit mask for the flag enabling fake-bold text */ + public static final int FAKE_BOLD_TEXT_FLAG = 0x20; + /** bit mask for the flag enabling linear-text (no caching) */ + public static final int LINEAR_TEXT_FLAG = 0x40; + /** bit mask for the flag enabling subpixel-text */ + public static final int SUBPIXEL_TEXT_FLAG = 0x80; + /** bit mask for the flag enabling device kerning for text */ + public static final int DEV_KERN_TEXT_FLAG = 0x100; + + // we use this when we first create a paint + private static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG; + + /** + * The Style specifies if the primitive being drawn is filled, + * stroked, or both (in the same color). The default is FILL. + */ + public enum Style { + /** + * Geometry and text drawn with this style will be filled, ignoring all + * stroke-related settings in the paint. + */ + FILL (0), + /** + * Geometry and text drawn with this style will be stroked, respecting + * the stroke-related fields on the paint. + */ + STROKE (1), + /** + * Geometry and text drawn with this style will be both filled and + * stroked at the same time, respecting the stroke-related fields on + * the paint. + */ + FILL_AND_STROKE (2); + + Style(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * The Cap specifies the treatment for the beginning and ending of + * stroked lines and paths. The default is BUTT. + */ + public enum Cap { + /** + * The stroke ends with the path, and does not project beyond it. + */ + BUTT (0), + /** + * The stroke projects out as a square, with the center at the end + * of the path. + */ + ROUND (1), + /** + * The stroke projects out as a semicircle, with the center at the + * end of the path. + */ + SQUARE (2); + + private Cap(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * The Join specifies the treatment where lines and curve segments + * join on a stroked path. The default is MITER. + */ + public enum Join { + /** + * The outer edges of a join meet at a sharp angle + */ + MITER (0), + /** + * The outer edges of a join meet in a circular arc. + */ + ROUND (1), + /** + * The outer edges of a join meet with a straight line + */ + BEVEL (2); + + private Join(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * Align specifies how drawText aligns its text relative to the + * [x,y] coordinates. The default is LEFT. + */ + public enum Align { + /** + * The text is drawn to the right of the x,y origin + */ + LEFT (0), + /** + * The text is drawn centered horizontally on the x,y origin + */ + CENTER (1), + /** + * The text is drawn to the left of the x,y origin + */ + RIGHT (2); + + private Align(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * Create a new paint with default settings. + */ + public Paint() { + this(0); + } + + /** + * Create a new paint with the specified flags. Use setFlags() to change + * these after the paint is created. + * + * @param flags initial flag bits, as if they were passed via setFlags(). + */ + public Paint(int flags) { + mNativePaint = native_init(); + setFlags(flags | DEFAULT_PAINT_FLAGS); + } + + /** + * Create a new paint, initialized with the attributes in the specified + * paint parameter. + * + * @param paint Existing paint used to initialized the attributes of the + * new paint. + */ + public Paint(Paint paint) { + mNativePaint = native_initWithPaint(paint.mNativePaint); + } + + /** Restores the paint to its default settings. */ + public void reset() { + native_reset(mNativePaint); + setFlags(DEFAULT_PAINT_FLAGS); + } + + /** + * Copy the fields from src into this paint. This is equivalent to calling + * get() on all of the src fields, and calling the corresponding set() + * methods on this. + */ + public void set(Paint src) { + if (this != src) { + // copy over the native settings + native_set(mNativePaint, src.mNativePaint); + // copy over our java settings + mColorFilter = src.mColorFilter; + mMaskFilter = src.mMaskFilter; + mPathEffect = src.mPathEffect; + mRasterizer = src.mRasterizer; + mShader = src.mShader; + mTypeface = src.mTypeface; + mXfermode = src.mXfermode; + } + } + + /** + * Return the paint's flags. Use the Flag enum to test flag values. + * + * @return the paint's flags (see enums ending in _Flag for bit masks) + */ + public native int getFlags(); + + /** + * Set the paint's flags. Use the Flag enum to specific flag values. + * + * @param flags The new flag bits for the paint + */ + public native void setFlags(int flags); + + /** + * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set + * AntiAliasing smooths out the edges of what is being drawn, but is has + * no impact on the interior of the shape. See setDither() and + * setFilterBitmap() to affect how colors are treated. + * + * @return true if the antialias bit is set in the paint's flags. + */ + public final boolean isAntiAlias() { + return (getFlags() & ANTI_ALIAS_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit + * AntiAliasing smooths out the edges of what is being drawn, but is has + * no impact on the interior of the shape. See setDither() and + * setFilterBitmap() to affect how colors are treated. + * + * @param aa true to set the antialias bit in the flags, false to clear it + */ + public native void setAntiAlias(boolean aa); + + /** + * Helper for getFlags(), returning true if DITHER_FLAG bit is set + * Dithering affects how colors that are higher precision than the device + * are down-sampled. No dithering is generally faster, but higher precision + * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to + * distribute the error inherent in this process, to reduce the visual + * artifacts. + * + * @return true if the dithering bit is set in the paint's flags. + */ + public final boolean isDither() { + return (getFlags() & DITHER_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the DITHER_FLAG bit + * Dithering affects how colors that are higher precision than the device + * are down-sampled. No dithering is generally faster, but higher precision + * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to + * distribute the error inherent in this process, to reduce the visual + * artifacts. + * + * @param dither true to set the dithering bit in flags, false to clear it + */ + public native void setDither(boolean dither); + + /** + * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set + * + * @return true if the lineartext bit is set in the paint's flags + */ + public final boolean isLinearText() { + return (getFlags() & LINEAR_TEXT_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the LINEAR_TEXT_FLAG bit + * + * @param linearText true to set the linearText bit in the paint's flags, + * false to clear it. + */ + public native void setLinearText(boolean linearText); + + /** + * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set + * + * @return true if the subpixel bit is set in the paint's flags + */ + public final boolean isSubpixelText() { + return (getFlags() & SUBPIXEL_TEXT_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit + * + * @param subpixelText true to set the subpixelText bit in the paint's + * flags, false to clear it. + */ + public native void setSubpixelText(boolean subpixelText); + + /** + * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set + * + * @return true if the underlineText bit is set in the paint's flags. + */ + public final boolean isUnderlineText() { + return (getFlags() & UNDERLINE_TEXT_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the UNDERLINE_TEXT_FLAG bit + * + * @param underlineText true to set the underlineText bit in the paint's + * flags, false to clear it. + */ + public native void setUnderlineText(boolean underlineText); + + /** + * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set + * + * @return true if the strikeThruText bit is set in the paint's flags. + */ + public final boolean isStrikeThruText() { + return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit + * + * @param strikeThruText true to set the strikeThruText bit in the paint's + * flags, false to clear it. + */ + public native void setStrikeThruText(boolean strikeThruText); + + /** + * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set + * + * @return true if the fakeBoldText bit is set in the paint's flags. + */ + public final boolean isFakeBoldText() { + return (getFlags() & FAKE_BOLD_TEXT_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit + * + * @param fakeBoldText true to set the fakeBoldText bit in the paint's + * flags, false to clear it. + */ + public native void setFakeBoldText(boolean fakeBoldText); + + /** + * Whether or not the bitmap filter is activated. + * Filtering affects the sampling of bitmaps when they are transformed. + * Filtering does not affect how the colors in the bitmap are converted into + * device pixels. That is dependent on dithering and xfermodes. + * + * @see #setFilterBitmap(boolean) setFilterBitmap() + */ + public final boolean isFilterBitmap() { + return (getFlags() & FILTER_BITMAP_FLAG) != 0; + } + + /** + * Helper for setFlags(), setting or clearing the FILTER_BITMAP_FLAG bit. + * Filtering affects the sampling of bitmaps when they are transformed. + * Filtering does not affect how the colors in the bitmap are converted into + * device pixels. That is dependent on dithering and xfermodes. + * + * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's + * flags, false to clear it. + */ + public native void setFilterBitmap(boolean filter); + + /** + * Return the paint's style, used for controlling how primitives' + * geometries are interpreted (except for drawBitmap, which always assumes + * FILL_STYLE). + * + * @return the paint's style setting (Fill, Stroke, StrokeAndFill) + */ + public Style getStyle() { + return sStyleArray[native_getStyle(mNativePaint)]; + } + + /** + * Set the paint's style, used for controlling how primitives' + * geometries are interpreted (except for drawBitmap, which always assumes + * Fill). + * + * @param style The new style to set in the paint + */ + public void setStyle(Style style) { + native_setStyle(mNativePaint, style.nativeInt); + } + + /** + * Return the paint's color. Note that the color is a 32bit value + * containing alpha as well as r,g,b. This 32bit value is not premultiplied, + * meaning that its alpha can be any value, regardless of the values of + * r,g,b. See the Color class for more details. + * + * @return the paint's color (and alpha). + */ + public native int getColor(); + + /** + * Set the paint's color. Note that the color is an int containing alpha + * as well as r,g,b. This 32bit value is not premultiplied, meaning that + * its alpha can be any value, regardless of the values of r,g,b. + * See the Color class for more details. + * + * @param color The new color (including alpha) to set in the paint. + */ + public native void setColor(int color); + + /** + * Helper to getColor() that just returns the color's alpha value. This is + * the same as calling getColor() >>> 24. It always returns a value between + * 0 (completely transparent) and 255 (completely opaque). + * + * @return the alpha component of the paint's color. + */ + public native int getAlpha(); + + /** + * Helper to setColor(), that only assigns the color's alpha value, + * leaving its r,g,b values unchanged. Results are undefined if the alpha + * value is outside of the range [0..255] + * + * @param a set the alpha component [0..255] of the paint's color. + */ + public native void setAlpha(int a); + + /** + * Helper to setColor(), that takes a,r,g,b and constructs the color int + * + * @param a The new alpha component (0..255) of the paint's color. + * @param r The new red component (0..255) of the paint's color. + * @param g The new green component (0..255) of the paint's color. + * @param b The new blue component (0..255) of the paint's color. + */ + public void setARGB(int a, int r, int g, int b) { + setColor((a << 24) | (r << 16) | (g << 8) | b); + } + + /** + * Return the width for stroking. + * <p /> + * A value of 0 strokes in hairline mode. + * Hairlines always draws a single pixel independent of the canva's matrix. + * + * @return the paint's stroke width, used whenever the paint's style is + * Stroke or StrokeAndFill. + */ + public native float getStrokeWidth(); + + /** + * Set the width for stroking. + * Pass 0 to stroke in hairline mode. + * Hairlines always draws a single pixel independent of the canva's matrix. + * + * @param width set the paint's stroke width, used whenever the paint's + * style is Stroke or StrokeAndFill. + */ + public native void setStrokeWidth(float width); + + /** + * Return the paint's stroke miter value. Used to control the behavior + * of miter joins when the joins angle is sharp. + * + * @return the paint's miter limit, used whenever the paint's style is + * Stroke or StrokeAndFill. + */ + public native float getStrokeMiter(); + + /** + * Set the paint's stroke miter value. This is used to control the behavior + * of miter joins when the joins angle is sharp. This value must be >= 0. + * + * @param miter set the miter limit on the paint, used whenever the paint's + * style is Stroke or StrokeAndFill. + */ + public native void setStrokeMiter(float miter); + + /** + * Return the paint's Cap, controlling how the start and end of stroked + * lines and paths are treated. + * + * @return the line cap style for the paint, used whenever the paint's + * style is Stroke or StrokeAndFill. + */ + public Cap getStrokeCap() { + return sCapArray[native_getStrokeCap(mNativePaint)]; + } + + /** + * Set the paint's Cap. + * + * @param cap set the paint's line cap style, used whenever the paint's + * style is Stroke or StrokeAndFill. + */ + public void setStrokeCap(Cap cap) { + native_setStrokeCap(mNativePaint, cap.nativeInt); + } + + /** + * Return the paint's stroke join type. + * + * @return the paint's Join. + */ + public Join getStrokeJoin() { + return sJoinArray[native_getStrokeJoin(mNativePaint)]; + } + + /** + * Set the paint's Join. + * + * @param join set the paint's Join, used whenever the paint's style is + * Stroke or StrokeAndFill. + */ + public void setStrokeJoin(Join join) { + native_setStrokeJoin(mNativePaint, join.nativeInt); + } + + /** + * Applies any/all effects (patheffect, stroking) to src, returning the + * result in dst. The result is that drawing src with this paint will be + * the same as drawing dst with a default paint (at least from the + * geometric perspective). + * + * @param src input path + * @param dst output path (may be the same as src) + * @return true if the path should be filled, or false if it should be + * drawn with a hairline (width == 0) + */ + public boolean getFillPath(Path src, Path dst) { + return native_getFillPath(mNativePaint, src.ni(), dst.ni()); + } + + /** + * Get the paint's shader object. + * + * @return the paint's shader (or null) + */ + public Shader getShader() { + return mShader; + } + + /** + * Set or clear the shader object. + * <p /> + * Pass null to clear any previous shader. + * As a convenience, the parameter passed is also returned. + * + * @param shader May be null. the new shader to be installed in the paint + * @return shader + */ + public Shader setShader(Shader shader) { + int shaderNative = 0; + if (shader != null) + shaderNative = shader.native_instance; + native_setShader(mNativePaint, shaderNative); + mShader = shader; + return shader; + } + + /** + * Get the paint's colorfilter (maybe be null). + * + * @return the paint's colorfilter (maybe be null) + */ + public ColorFilter getColorFilter() { + return mColorFilter; + } + + /** + * Set or clear the paint's colorfilter, returning the parameter. + * + * @param filter May be null. The new filter to be installed in the paint + * @return filter + */ + public ColorFilter setColorFilter(ColorFilter filter) { + int filterNative = 0; + if (filter != null) + filterNative = filter.native_instance; + native_setColorFilter(mNativePaint, filterNative); + mColorFilter = filter; + return filter; + } + + /** + * Get the paint's xfermode object. + * + * @return the paint's xfermode (or null) + */ + public Xfermode getXfermode() { + return mXfermode; + } + + /** + * Set or clear the xfermode object. + * <p /> + * Pass null to clear any previous xfermode. + * As a convenience, the parameter passed is also returned. + * + * @param xfermode May be null. The xfermode to be installed in the paint + * @return xfermode + */ + public Xfermode setXfermode(Xfermode xfermode) { + int xfermodeNative = 0; + if (xfermode != null) + xfermodeNative = xfermode.native_instance; + native_setXfermode(mNativePaint, xfermodeNative); + mXfermode = xfermode; + return xfermode; + } + + /** + * Get the paint's patheffect object. + * + * @return the paint's patheffect (or null) + */ + public PathEffect getPathEffect() { + return mPathEffect; + } + + /** + * Set or clear the patheffect object. + * <p /> + * Pass null to clear any previous patheffect. + * As a convenience, the parameter passed is also returned. + * + * @param effect May be null. The patheffect to be installed in the paint + * @return effect + */ + public PathEffect setPathEffect(PathEffect effect) { + int effectNative = 0; + if (effect != null) { + effectNative = effect.native_instance; + } + native_setPathEffect(mNativePaint, effectNative); + mPathEffect = effect; + return effect; + } + + /** + * Get the paint's maskfilter object. + * + * @return the paint's maskfilter (or null) + */ + public MaskFilter getMaskFilter() { + return mMaskFilter; + } + + /** + * Set or clear the maskfilter object. + * <p /> + * Pass null to clear any previous maskfilter. + * As a convenience, the parameter passed is also returned. + * + * @param maskfilter May be null. The maskfilter to be installed in the + * paint + * @return maskfilter + */ + public MaskFilter setMaskFilter(MaskFilter maskfilter) { + int maskfilterNative = 0; + if (maskfilter != null) { + maskfilterNative = maskfilter.native_instance; + } + native_setMaskFilter(mNativePaint, maskfilterNative); + mMaskFilter = maskfilter; + return maskfilter; + } + + /** + * Get the paint's typeface object. + * <p /> + * The typeface object identifies which font to use when drawing or + * measuring text. + * + * @return the paint's typeface (or null) + */ + public Typeface getTypeface() { + return mTypeface; + } + + /** + * Set or clear the typeface object. + * <p /> + * Pass null to clear any previous typeface. + * As a convenience, the parameter passed is also returned. + * + * @param typeface May be null. The typeface to be installed in the paint + * @return typeface + */ + public Typeface setTypeface(Typeface typeface) { + int typefaceNative = 0; + if (typeface != null) { + typefaceNative = typeface.native_instance; + } + native_setTypeface(mNativePaint, typefaceNative); + mTypeface = typeface; + return typeface; + } + + /** + * Get the paint's rasterizer (or null). + * <p /> + * The raster controls/modifies how paths/text are turned into alpha masks. + * + * @return the paint's rasterizer (or null) + */ + public Rasterizer getRasterizer() { + return mRasterizer; + } + + /** + * Set or clear the rasterizer object. + * <p /> + * Pass null to clear any previous rasterizer. + * As a convenience, the parameter passed is also returned. + * + * @param rasterizer May be null. The new rasterizer to be installed in + * the paint. + * @return rasterizer + */ + public Rasterizer setRasterizer(Rasterizer rasterizer) { + int rasterizerNative = 0; + if (rasterizer != null) { + rasterizerNative = rasterizer.native_instance; + } + native_setRasterizer(mNativePaint, rasterizerNative); + mRasterizer = rasterizer; + return rasterizer; + } + + /** + * Temporary API to expose layer drawing. This draws a shadow layer below + * the main layer, with the specified offset and color, and blur radius. + * If radius is 0, then the shadow layer is removed. + */ + public native void setShadowLayer(float radius, float dx, float dy, + int color); + + /** + * Temporary API to clear the shadow layer. + */ + public void clearShadowLayer() { + setShadowLayer(0, 0, 0, 0); + } + + /** + * Return the paint's Align value for drawing text. This controls how the + * text is positioned relative to its origin. LEFT align means that all of + * the text will be drawn to the right of its origin (i.e. the origin + * specifieds the LEFT edge of the text) and so on. + * + * @return the paint's Align value for drawing text. + */ + public Align getTextAlign() { + return sAlignArray[native_getTextAlign(mNativePaint)]; + } + + /** + * Set the paint's text alignment. This controls how the + * text is positioned relative to its origin. LEFT align means that all of + * the text will be drawn to the right of its origin (i.e. the origin + * specifieds the LEFT edge of the text) and so on. + * + * @param align set the paint's Align value for drawing text. + */ + public void setTextAlign(Align align) { + native_setTextAlign(mNativePaint, align.nativeInt); + } + + /** + * Return the paint's text size. + * + * @return the paint's text size. + */ + public native float getTextSize(); + + /** + * Set the paint's text size. This value must be > 0 + * + * @param textSize set the paint's text size. + */ + public native void setTextSize(float textSize); + + /** + * Return the paint's horizontal scale factor for text. The default value + * is 1.0. + * + * @return the paint's scale factor in X for drawing/measuring text + */ + public native float getTextScaleX(); + + /** + * Set the paint's horizontal scale factor for text. The default value + * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will + * stretch the text narrower. + * + * @param scaleX set the paint's scale in X for drawing/measuring text. + */ + public native void setTextScaleX(float scaleX); + + /** + * Return the paint's horizontal skew factor for text. The default value + * is 0. + * + * @return the paint's skew factor in X for drawing text. + */ + public native float getTextSkewX(); + + /** + * Set the paint's horizontal skew factor for text. The default value + * is 0. For approximating oblique text, use values around -0.25. + * + * @param skewX set the paint's skew factor in X for drawing text. + */ + public native void setTextSkewX(float skewX); + + /** + * Return the distance above (negative) the baseline (ascent) based on the + * current typeface and text size. + * + * @return the distance above (negative) the baseline (ascent) based on the + * current typeface and text size. + */ + public native float ascent(); + + /** + * Return the distance below (positive) the baseline (descent) based on the + * current typeface and text size. + * + * @return the distance below (positive) the baseline (descent) based on + * the current typeface and text size. + */ + public native float descent(); + + /** + * Class that describes the various metrics for a font at a given text size. + * Remember, Y values increase going down, so those values will be positive, + * and values that measure distances going up will be negative. This class + * is returned by getFontMetrics(). + */ + public static class FontMetrics { + /** + * The maximum distance above the baseline for the tallest glyph in + * the font at a given text size. + */ + public float top; + /** + * The recommended distance above the baseline for singled spaced text. + */ + public float ascent; + /** + * The recommended distance below the baseline for singled spaced text. + */ + public float descent; + /** + * The maximum distance below the baseline for the lowest glyph in + * the font at a given text size. + */ + public float bottom; + /** + * The recommended additional space to add between lines of text. + */ + public float leading; + } + + /** + * Return the font's recommended interline spacing, given the Paint's + * settings for typeface, textSize, etc. If metrics is not null, return the + * fontmetric values in it. + * + * @param metrics If this object is not null, its fields are filled with + * the appropriate values given the paint's text attributes. + * @return the font's recommended interline spacing. + */ + public native float getFontMetrics(FontMetrics metrics); + + /** + * Allocates a new FontMetrics object, and then calls getFontMetrics(fm) + * with it, returning the object. + */ + public FontMetrics getFontMetrics() { + FontMetrics fm = new FontMetrics(); + getFontMetrics(fm); + return fm; + } + + /** + * Convenience method for callers that want to have FontMetrics values as + * integers. + */ + public static class FontMetricsInt { + public int top; + public int ascent; + public int descent; + public int bottom; + public int leading; + + @Override public String toString() { + return "FontMetricsInt: top=" + top + " ascent=" + ascent + + " descent=" + descent + " bottom=" + bottom + + " leading=" + leading; + } + } + + /** + * Return the font's interline spacing, given the Paint's settings for + * typeface, textSize, etc. If metrics is not null, return the fontmetric + * values in it. Note: all values have been converted to integers from + * floats, in such a way has to make the answers useful for both spacing + * and clipping. If you want more control over the rounding, call + * getFontMetrics(). + * + * @return the font's interline spacing. + */ + public native int getFontMetricsInt(FontMetricsInt fmi); + + public FontMetricsInt getFontMetricsInt() { + FontMetricsInt fm = new FontMetricsInt(); + getFontMetricsInt(fm); + return fm; + } + + /** + * Return the recommend line spacing based on the current typeface and + * text size. + * + * @return recommend line spacing based on the current typeface and + * text size. + */ + public float getFontSpacing() { + return getFontMetrics(null); + } + + /** + * Return the width of the text. + * + * @param text The text to measure + * @param index The index of the first character to start measuring + * @param count THe number of characters to measure, beginning with start + * @return The width of the text + */ + public native float measureText(char[] text, int index, int count); + + /** + * Return the width of the text. + * + * @param text The text to measure + * @param start The index of the first character to start measuring + * @param end 1 beyond the index of the last character to measure + * @return The width of the text + */ + public native float measureText(String text, int start, int end); + + /** + * Return the width of the text. + * + * @param text The text to measure + * @return The width of the text + */ + public native float measureText(String text); + + /** + * Return the width of the text. + * + * @param text The text to measure + * @param start The index of the first character to start measuring + * @param end 1 beyond the index of the last character to measure + * @return The width of the text + */ + public float measureText(CharSequence text, int start, int end) { + if (text instanceof String) { + return measureText((String)text, start, end); + } + if (text instanceof SpannedString || + text instanceof SpannableString) { + return measureText(text.toString(), start, end); + } + if (text instanceof GraphicsOperations) { + return ((GraphicsOperations)text).measureText(start, end, this); + } + + char[] buf = TemporaryBuffer.obtain(end - start); + TextUtils.getChars(text, start, end, buf, 0); + float result = measureText(buf, 0, end - start); + TemporaryBuffer.recycle(buf); + return result; + } + + /** + * Measure the text, stopping early if the measured width exceeds maxWidth. + * Return the number of chars that were measured, and if measuredWidth is + * not null, return in it the actual width measured. + * + * @param text The text to measure + * @param index The offset into text to begin measuring at + * @param count The number of maximum number of entries to measure. If count + * is negative, then the characters before index are measured + * in reverse order. This allows for measuring the end of + * string. + * @param maxWidth The maximum width to accumulate. + * @param measuredWidth Optional. If not null, returns the actual width + * measured. + * @return The number of chars that were measured. Will always be <= + * abs(count). + */ + public native int breakText(char[] text, int index, int count, + float maxWidth, float[] measuredWidth); + + /** + * Measure the text, stopping early if the measured width exceeds maxWidth. + * Return the number of chars that were measured, and if measuredWidth is + * not null, return in it the actual width measured. + * + * @param text The text to measure + * @param start The offset into text to begin measuring at + * @param end The end of the text slice to measure. + * @param measureForwards If true, measure forwards, starting at start. + * Otherwise, measure backwards, starting with end. + * @param maxWidth The maximum width to accumulate. + * @param measuredWidth Optional. If not null, returns the actual width + * measured. + * @return The number of chars that were measured. Will always be <= + * abs(end - start). + */ + public int breakText(CharSequence text, int start, int end, + boolean measureForwards, + float maxWidth, float[] measuredWidth) { + if (start == 0 && text instanceof String && end == text.length()) { + return breakText((String) text, measureForwards, maxWidth, + measuredWidth); + } + + char[] buf = TemporaryBuffer.obtain(end - start); + int result; + + TextUtils.getChars(text, start, end, buf, 0); + + if (measureForwards) { + result = breakText(buf, 0, end - start, maxWidth, measuredWidth); + } else { + result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth); + } + + TemporaryBuffer.recycle(buf); + return result; + } + + /** + * Measure the text, stopping early if the measured width exceeds maxWidth. + * Return the number of chars that were measured, and if measuredWidth is + * not null, return in it the actual width measured. + * + * @param text The text to measure + * @param measureForwards If true, measure forwards, starting at index. + * Otherwise, measure backwards, starting with the + * last character in the string. + * @param maxWidth The maximum width to accumulate. + * @param measuredWidth Optional. If not null, returns the actual width + * measured. + * @return The number of chars that were measured. Will always be <= + * abs(count). + */ + public native int breakText(String text, boolean measureForwards, + float maxWidth, float[] measuredWidth); + + /** + * Return the advance widths for the characters in the string. + * + * @param text The text to measure + * @param index The index of the first char to to measure + * @param count The number of chars starting with index to measure + * @param widths array to receive the advance widths of the characters. + * Must be at least a large as count. + * @return the actual number of widths returned. + */ + public int getTextWidths(char[] text, int index, int count, + float[] widths) { + if ((index | count) < 0 || index + count > text.length + || count > widths.length) { + throw new ArrayIndexOutOfBoundsException(); + } + return native_getTextWidths(mNativePaint, text, index, count, widths); + } + + /** + * Return the advance widths for the characters in the string. + * + * @param text The text to measure + * @param start The index of the first char to to measure + * @param end The end of the text slice to measure + * @param widths array to receive the advance widths of the characters. + * Must be at least a large as (end - start). + * @return the actual number of widths returned. + */ + public int getTextWidths(CharSequence text, int start, int end, + float[] widths) { + if (text instanceof String) { + return getTextWidths((String) text, start, end, widths); + } + if (text instanceof SpannedString || + text instanceof SpannableString) { + return getTextWidths(text.toString(), start, end, widths); + } + if (text instanceof GraphicsOperations) { + return ((GraphicsOperations) text).getTextWidths(start, end, + widths, this); + } + + char[] buf = TemporaryBuffer.obtain(end - start); + TextUtils.getChars(text, start, end, buf, 0); + int result = getTextWidths(buf, 0, end - start, widths); + TemporaryBuffer.recycle(buf); + return result; + } + + /** + * Return the advance widths for the characters in the string. + * + * @param text The text to measure + * @param start The index of the first char to to measure + * @param end The end of the text slice to measure + * @param widths array to receive the advance widths of the characters. + * Must be at least a large as the text. + * @return the number of unichars in the specified text. + */ + public int getTextWidths(String text, int start, int end, float[] widths) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + if (end - start > widths.length) { + throw new ArrayIndexOutOfBoundsException(); + } + return native_getTextWidths(mNativePaint, text, start, end, widths); + } + + /** + * Return the advance widths for the characters in the string. + * + * @param text The text to measure + * @param widths array to receive the advance widths of the characters. + * Must be at least a large as the text. + * @return the number of unichars in the specified text. + */ + public int getTextWidths(String text, float[] widths) { + return getTextWidths(text, 0, text.length(), widths); + } + + /** + * Return the path (outline) for the specified text. + * Note: just like Canvas.drawText, this will respect the Align setting in + * the paint. + * + * @param text The text to retrieve the path from + * @param index The index of the first character in text + * @param count The number of characterss starting with index + * @param x The x coordinate of the text's origin + * @param y The y coordinate of the text's origin + * @param path The path to receive the data describing the text. Must + * be allocated by the caller. + */ + public void getTextPath(char[] text, int index, int count, + float x, float y, Path path) { + if ((index | count) < 0 || index + count > text.length) { + throw new ArrayIndexOutOfBoundsException(); + } + native_getTextPath(mNativePaint, text, index, count, x, y, path.ni()); + } + + /** + * Return the path (outline) for the specified text. + * Note: just like Canvas.drawText, this will respect the Align setting + * in the paint. + * + * @param text The text to retrieve the path from + * @param start The first character in the text + * @param end 1 past the last charcter in the text + * @param x The x coordinate of the text's origin + * @param y The y coordinate of the text's origin + * @param path The path to receive the data describing the text. Must + * be allocated by the caller. + */ + public void getTextPath(String text, int start, int end, + float x, float y, Path path) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + native_getTextPath(mNativePaint, text, start, end, x, y, path.ni()); + } + + /** + * Return in bounds (allocated by the caller) the smallest rectangle that + * encloses all of the characters, with an implied origin at (0,0). + * + * @param text String to measure and return its bounds + * @param start Index of the first char in the string to measure + * @param end 1 past the last char in the string measure + * @param bounds Returns the unioned bounds of all the text. Must be + * allocated by the caller. + */ + public void getTextBounds(String text, int start, int end, Rect bounds) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + if (bounds == null) { + throw new NullPointerException("need bounds Rect"); + } + nativeGetStringBounds(mNativePaint, text, start, end, bounds); + } + + /** + * Return in bounds (allocated by the caller) the smallest rectangle that + * encloses all of the characters, with an implied origin at (0,0). + * + * @param text Array of chars to measure and return their unioned bounds + * @param index Index of the first char in the array to measure + * @param count The number of chars, beginning at index, to measure + * @param bounds Returns the unioned bounds of all the text. Must be + * allocated by the caller. + */ + public void getTextBounds(char[] text, int index, int count, Rect bounds) { + if ((index | count) < 0 || index + count > text.length) { + throw new ArrayIndexOutOfBoundsException(); + } + if (bounds == null) { + throw new NullPointerException("need bounds Rect"); + } + nativeGetCharArrayBounds(mNativePaint, text, index, count, bounds); + } + + protected void finalize() throws Throwable { + finalizer(mNativePaint); + } + + private static native int native_init(); + private static native int native_initWithPaint(int paint); + private static native void native_reset(int native_object); + private static native void native_set(int native_dst, int native_src); + private static native int native_getStyle(int native_object); + private static native void native_setStyle(int native_object, int style); + private static native int native_getStrokeCap(int native_object); + private static native void native_setStrokeCap(int native_object, int cap); + private static native int native_getStrokeJoin(int native_object); + private static native void native_setStrokeJoin(int native_object, + int join); + private static native boolean native_getFillPath(int native_object, + int src, int dst); + private static native int native_setShader(int native_object, int shader); + private static native int native_setColorFilter(int native_object, + int filter); + private static native int native_setXfermode(int native_object, + int xfermode); + private static native int native_setPathEffect(int native_object, + int effect); + private static native int native_setMaskFilter(int native_object, + int maskfilter); + private static native int native_setTypeface(int native_object, + int typeface); + private static native int native_setRasterizer(int native_object, + int rasterizer); + + private static native int native_getTextAlign(int native_object); + private static native void native_setTextAlign(int native_object, + int align); + + private static native float native_getFontMetrics(int native_paint, + FontMetrics metrics); + private static native int native_getTextWidths(int native_object, + char[] text, int index, int count, float[] widths); + private static native int native_getTextWidths(int native_object, + String text, int start, int end, float[] widths); + private static native void native_getTextPath(int native_object, + char[] text, int index, int count, float x, float y, int path); + private static native void native_getTextPath(int native_object, + String text, int start, int end, float x, float y, int path); + private static native void nativeGetStringBounds(int nativePaint, + String text, int start, int end, Rect bounds); + private static native void nativeGetCharArrayBounds(int nativePaint, + char[] text, int index, int count, Rect bounds); + private static native void finalizer(int nativePaint); +} + diff --git a/graphics/java/android/graphics/PaintFlagsDrawFilter.java b/graphics/java/android/graphics/PaintFlagsDrawFilter.java new file mode 100644 index 0000000..0f0d03d --- /dev/null +++ b/graphics/java/android/graphics/PaintFlagsDrawFilter.java @@ -0,0 +1,37 @@ +/* + * 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; + +public class PaintFlagsDrawFilter extends DrawFilter { + + /** + * Subclass of DrawFilter that affects every paint by first clearing + * the specified clearBits in the paint's flags, and then setting the + * specified setBits in the paint's flags. + * + * @param clearBits These bits will be cleared in the paint's flags + * @param setBits These bits will be set in the paint's flags + */ + public PaintFlagsDrawFilter(int clearBits, int setBits) { + // our native constructor can return 0, if the specified bits + // are effectively a no-op + mNativeInt = nativeConstructor(clearBits, setBits); + } + + private static native int nativeConstructor(int clearBits, int setBits); +} + diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java new file mode 100644 index 0000000..c3f63d7 --- /dev/null +++ b/graphics/java/android/graphics/Path.java @@ -0,0 +1,599 @@ +/* + * Copyright (C) 2006 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; + +/** + * The Path class encapsulates compound (multiple contour) geometric paths + * consisting of straight line segments, quadratic curves, and cubic curves. + * It can be drawn with canvas.drawPath(path, paint), either filled or stroked + * (based on the paint's Style), or it can be used for clipping or to draw + * text on a path. + */ +public class Path { + + /** + * Create an empty path + */ + public Path() { + mNativePath = init1(); + } + + /** + * Create a new path, copying the contents from the src path. + * + * @param src The path to copy from when initializing the new path + */ + public Path(Path src) { + int valNative = 0; + if (src != null) { + valNative = src.mNativePath; + } + mNativePath = init2(valNative); + } + + /** + * Clear any lines and curves from the path, making it empty. + * This does NOT change the fill-type setting. + */ + public void reset() { + native_reset(mNativePath); + } + + /** + * Rewinds the path: clears any lines and curves from the path but + * keeps the internal data structure for faster reuse. + */ + public void rewind() { + native_rewind(mNativePath); + } + + /** Replace the contents of this with the contents of src. + */ + public void set(Path src) { + if (this != src) { + native_set(mNativePath, src.mNativePath); + } + } + + /** Enum for the ways a path may be filled + */ + public enum FillType { + // these must match the values in SkPath.h + WINDING (0), + EVEN_ODD (1), + INVERSE_WINDING (2), + INVERSE_EVEN_ODD(3); + + FillType(int ni) { + nativeInt = ni; + } + final int nativeInt; + } + + // these must be in the same order as their native values + private static final FillType[] sFillTypeArray = { + FillType.WINDING, + FillType.EVEN_ODD, + FillType.INVERSE_WINDING, + FillType.INVERSE_EVEN_ODD + }; + + /** + * Return the path's fill type. This defines how "inside" is + * computed. The default value is WINDING. + * + * @return the path's fill type + */ + public FillType getFillType() { + return sFillTypeArray[native_getFillType(mNativePath)]; + } + + /** + * Set the path's fill type. This defines how "inside" is computed. + * + * @param ft The new fill type for this path + */ + public void setFillType(FillType ft) { + native_setFillType(mNativePath, ft.nativeInt); + } + + /** + * Returns true if the filltype is one of the INVERSE variants + * + * @return true if the filltype is one of the INVERSE variants + */ + public boolean isInverseFillType() { + final int ft = native_getFillType(mNativePath); + return (ft & 2) != 0; + } + + /** + * Toggles the INVERSE state of the filltype + */ + public void toggleInverseFillType() { + int ft = native_getFillType(mNativePath); + ft ^= 2; + native_setFillType(mNativePath, ft); + } + + /** + * Returns true if the path is empty (contains no lines or curves) + * + * @return true if the path is empty (contains no lines or curves) + */ + public boolean isEmpty() { + return native_isEmpty(mNativePath); + } + + /** + * Returns true if the path specifies a rectangle. If so, and if rect is + * not null, set rect to the bounds of the path. If the path does not + * specify a rectangle, return false and ignore rect. + * + * @param rect If not null, returns the bounds of the path if it specifies + * a rectangle + * @return true if the path specifies a rectangle + */ + public boolean isRect(RectF rect) { + return native_isRect(mNativePath, rect); + } + + /** + * Compute the bounds of the path, and write the answer into bounds. If the + * path contains 0 or 1 points, the bounds is set to (0,0,0,0) + * + * @param bounds Returns the computed bounds of the path + * @param exact If true, return the exact (but slower) bounds, else return + * just the bounds of all control points + */ + public void computeBounds(RectF bounds, boolean exact) { + // 1-exact, 0-fast correspond to the values in SkPath.h + native_computeBounds(mNativePath, bounds, exact ? 1 : 0); + } + + /** + * Hint to the path to prepare for adding more points. This can allow the + * path to more efficiently allocate its storage. + * + * @param extraPtCount The number of extra points that may be added to this + * path + */ + public void incReserve(int extraPtCount) { + native_incReserve(mNativePath, extraPtCount); + } + + /** + * Set the beginning of the next contour to the point (x,y). + * + * @param x The x-coordinate of the start of a new contour + * @param y The y-coordinate of the start of a new contour + */ + public void moveTo(float x, float y) { + native_moveTo(mNativePath, x, y); + } + + /** + * Set the beginning of the next contour relative to the last point on the + * previous contour. If there is no previous contour, this is treated the + * same as moveTo(). + * + * @param dx The amount to add to the x-coordinate of the end of the + * previous contour, to specify the start of a new contour + * @param dy The amount to add to the y-coordinate of the end of the + * previous contour, to specify the start of a new contour + */ + public void rMoveTo(float dx, float dy) { + native_rMoveTo(mNativePath, dx, dy); + } + + /** + * Add a line from the last point to the specified point (x,y). + * If no moveTo() call has been made for this contour, the first point is + * automatically set to (0,0). + * + * @param x The x-coordinate of the end of a line + * @param y The y-coordinate of the end of a line + */ + public void lineTo(float x, float y) { + native_lineTo(mNativePath, x, y); + } + + /** + * Same as lineTo, but the coordinates are considered relative to the last + * point on this contour. If there is no previous point, then a moveTo(0,0) + * is inserted automatically. + * + * @param dx The amount to add to the x-coordinate of the previous point on + * this contour, to specify a line + * @param dy The amount to add to the y-coordinate of the previous point on + * this contour, to specify a line + */ + public void rLineTo(float dx, float dy) { + native_rLineTo(mNativePath, dx, dy); + } + + /** + * Add a quadratic bezier from the last point, approaching control point + * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for + * this contour, the first point is automatically set to (0,0). + * + * @param x1 The x-coordinate of the control point on a quadratic curve + * @param y1 The y-coordinate of the control point on a quadratic curve + * @param x2 The x-coordinate of the end point on a quadratic curve + * @param y2 The y-coordinate of the end point on a quadratic curve + */ + public void quadTo(float x1, float y1, float x2, float y2) { + native_quadTo(mNativePath, x1, y1, x2, y2); + } + + /** + * Same as quadTo, but the coordinates are considered relative to the last + * point on this contour. If there is no previous point, then a moveTo(0,0) + * is inserted automatically. + * + * @param dx1 The amount to add to the x-coordinate of the last point on + * this contour, for the control point of a quadratic curve + * @param dy1 The amount to add to the y-coordinate of the last point on + * this contour, for the control point of a quadratic curve + * @param dx2 The amount to add to the x-coordinate of the last point on + * this contour, for the end point of a quadratic curve + * @param dy2 The amount to add to the y-coordinate of the last point on + * this contour, for the end point of a quadratic curve + */ + public void rQuadTo(float dx1, float dy1, float dx2, float dy2) { + native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2); + } + + /** + * Add a cubic bezier from the last point, approaching control points + * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been + * made for this contour, the first point is automatically set to (0,0). + * + * @param x1 The x-coordinate of the 1st control point on a cubic curve + * @param y1 The y-coordinate of the 1st control point on a cubic curve + * @param x2 The x-coordinate of the 2nd control point on a cubic curve + * @param y2 The y-coordinate of the 2nd control point on a cubic curve + * @param x3 The x-coordinate of the end point on a cubic curve + * @param y3 The y-coordinate of the end point on a cubic curve + */ + public void cubicTo(float x1, float y1, float x2, float y2, + float x3, float y3) { + native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3); + } + + /** + * Same as cubicTo, but the coordinates are considered relative to the + * current point on this contour. If there is no previous point, then a + * moveTo(0,0) is inserted automatically. + */ + public void rCubicTo(float x1, float y1, float x2, float y2, + float x3, float y3) { + native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3); + } + + /** + * Append the specified arc to the path as a new contour. If the start of + * the path is different from the path's current last point, then an + * automatic lineTo() is added to connect the current contour to the + * start of the arc. However, if the path is empty, then we call moveTo() + * with the first point of the arc. The sweep angle is tread mod 360. + * + * @param oval The bounds of oval defining shape and size of the arc + * @param startAngle Starting angle (in degrees) where the arc begins + * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated + * mod 360. + * @param forceMoveTo If true, always begin a new contour with the arc + */ + public void arcTo(RectF oval, float startAngle, float sweepAngle, + boolean forceMoveTo) { + native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo); + } + + /** + * Append the specified arc to the path as a new contour. If the start of + * the path is different from the path's current last point, then an + * automatic lineTo() is added to connect the current contour to the + * start of the arc. However, if the path is empty, then we call moveTo() + * with the first point of the arc. + * + * @param oval The bounds of oval defining shape and size of the arc + * @param startAngle Starting angle (in degrees) where the arc begins + * @param sweepAngle Sweep angle (in degrees) measured clockwise + */ + public void arcTo(RectF oval, float startAngle, float sweepAngle) { + native_arcTo(mNativePath, oval, startAngle, sweepAngle, false); + } + + /** + * Close the current contour. If the current point is not equal to the + * first point of the contour, a line segment is automatically added. + */ + public void close() { + native_close(mNativePath); + } + + /** + * Specifies how closed shapes (e.g. rects, ovals) are oriented when they + * are added to a path. + */ + public enum Direction { + /** clockwise */ + CW (0), // must match enum in SkPath.h + /** counter-clockwise */ + CCW (1); // must match enum in SkPath.h + + Direction(int ni) { + nativeInt = ni; + } + final int nativeInt; + } + + /** + * Add a closed rectangle contour to the path + * + * @param rect The rectangle to add as a closed contour to the path + * @param dir The direction to wind the rectangle's contour + */ + public void addRect(RectF rect, Direction dir) { + if (rect == null) { + throw new NullPointerException("need rect parameter"); + } + native_addRect(mNativePath, rect, dir.nativeInt); + } + + /** + * Add a closed rectangle contour to the path + * + * @param left The left side of a rectangle to add to the path + * @param top The top of a rectangle to add to the path + * @param right The right side of a rectangle to add to the path + * @param bottom The bottom of a rectangle to add to the path + * @param dir The direction to wind the rectangle's contour + */ + public void addRect(float left, float top, float right, float bottom, + Direction dir) { + native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt); + } + + /** + * Add a closed oval contour to the path + * + * @param oval The bounds of the oval to add as a closed contour to the path + * @param dir The direction to wind the oval's contour + */ + public void addOval(RectF oval, Direction dir) { + if (oval == null) { + throw new NullPointerException("need oval parameter"); + } + native_addOval(mNativePath, oval, dir.nativeInt); + } + + /** + * Add a closed circle contour to the path + * + * @param x The x-coordinate of the center of a circle to add to the path + * @param y The y-coordinate of the center of a circle to add to the path + * @param radius The radius of a circle to add to the path + * @param dir The direction to wind the circle's contour + */ + public void addCircle(float x, float y, float radius, Direction dir) { + native_addCircle(mNativePath, x, y, radius, dir.nativeInt); + } + + /** + * Add the specified arc to the path as a new contour. + * + * @param oval The bounds of oval defining the shape and size of the arc + * @param startAngle Starting angle (in degrees) where the arc begins + * @param sweepAngle Sweep angle (in degrees) measured clockwise + */ + public void addArc(RectF oval, float startAngle, float sweepAngle) { + if (oval == null) { + throw new NullPointerException("need oval parameter"); + } + native_addArc(mNativePath, oval, startAngle, sweepAngle); + } + + /** + * Add a closed round-rectangle contour to the path + * + * @param rect The bounds of a round-rectangle to add to the path + * @param rx The x-radius of the rounded corners on the round-rectangle + * @param ry The y-radius of the rounded corners on the round-rectangle + * @param dir The direction to wind the round-rectangle's contour + */ + public void addRoundRect(RectF rect, float rx, float ry, Direction dir) { + if (rect == null) { + throw new NullPointerException("need rect parameter"); + } + native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt); + } + + /** + * Add a closed round-rectangle contour to the path. Each corner receives + * two radius values [X, Y]. The corners are ordered top-left, top-right, + * bottom-right, bottom-left + * + * @param rect The bounds of a round-rectangle to add to the path + * @param radii Array of 8 values, 4 pairs of [X,Y] radii + * @param dir The direction to wind the round-rectangle's contour + */ + public void addRoundRect(RectF rect, float[] radii, Direction dir) { + if (rect == null) { + throw new NullPointerException("need rect parameter"); + } + if (radii.length < 8) { + throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values"); + } + native_addRoundRect(mNativePath, rect, radii, dir.nativeInt); + } + + /** + * Add a copy of src to the path, offset by (dx,dy) + * + * @param src The path to add as a new contour + * @param dx The amount to translate the path in X as it is added + */ + public void addPath(Path src, float dx, float dy) { + native_addPath(mNativePath, src.mNativePath, dx, dy); + } + + /** + * Add a copy of src to the path + * + * @param src The path that is appended to the current path + */ + public void addPath(Path src) { + native_addPath(mNativePath, src.mNativePath); + } + + /** + * Add a copy of src to the path, transformed by matrix + * + * @param src The path to add as a new contour + */ + public void addPath(Path src, Matrix matrix) { + native_addPath(mNativePath, src.mNativePath, matrix.native_instance); + } + + /** + * Offset the path by (dx,dy), returning true on success + * + * @param dx The amount in the X direction to offset the entire path + * @param dy The amount in the Y direction to offset the entire path + * @param dst The translated path is written here. If this is null, then + * the original path is modified. + */ + public void offset(float dx, float dy, Path dst) { + int dstNative = 0; + if (dst != null) { + dstNative = dst.mNativePath; + } + native_offset(mNativePath, dx, dy, dstNative); + } + + /** + * Offset the path by (dx,dy), returning true on success + * + * @param dx The amount in the X direction to offset the entire path + * @param dy The amount in the Y direction to offset the entire path + */ + public void offset(float dx, float dy) { + native_offset(mNativePath, dx, dy); + } + + /** + * Sets the last point of the path. + * + * @param dx The new X coordinate for the last point + * @param dy The new Y coordinate for the last point + */ + public void setLastPoint(float dx, float dy) { + native_setLastPoint(mNativePath, dx, dy); + } + + /** + * Transform the points in this path by matrix, and write the answer + * into dst. If dst is null, then the the original path is modified. + * + * @param matrix The matrix to apply to the path + * @param dst The transformed path is written here. If dst is null, + * then the the original path is modified + */ + public void transform(Matrix matrix, Path dst) { + int dstNative = 0; + if (dst != null) { + dstNative = dst.mNativePath; + } + native_transform(mNativePath, matrix.native_instance, dstNative); + } + + /** + * Transform the points in this path by matrix. + * + * @param matrix The matrix to apply to the path + */ + public void transform(Matrix matrix) { + native_transform(mNativePath, matrix.native_instance); + } + + protected void finalize() throws Throwable { + try { + finalizer(mNativePath); + } finally { + super.finalize(); + } + } + + /*package*/ final int ni() { + return mNativePath; + } + + private static native int init1(); + private static native int init2(int nPath); + private static native void native_reset(int nPath); + private static native void native_rewind(int nPath); + private static native void native_set(int native_dst, int native_src); + private static native int native_getFillType(int nPath); + private static native void native_setFillType(int nPath, int ft); + private static native boolean native_isEmpty(int nPath); + private static native boolean native_isRect(int nPath, RectF rect); + private static native void native_computeBounds(int nPath, RectF bounds, + int btype); + private static native void native_incReserve(int nPath, int extraPtCount); + private static native void native_moveTo(int nPath, float x, float y); + private static native void native_rMoveTo(int nPath, float dx, float dy); + private static native void native_lineTo(int nPath, float x, float y); + private static native void native_rLineTo(int nPath, float dx, float dy); + private static native void native_quadTo(int nPath, float x1, float y1, + float x2, float y2); + private static native void native_rQuadTo(int nPath, float dx1, float dy1, + float dx2, float dy2); + private static native void native_cubicTo(int nPath, float x1, float y1, + float x2, float y2, float x3, float y3); + private static native void native_rCubicTo(int nPath, float x1, float y1, + float x2, float y2, float x3, float y3); + private static native void native_arcTo(int nPath, RectF oval, + float startAngle, float sweepAngle, boolean forceMoveTo); + private static native void native_close(int nPath); + private static native void native_addRect(int nPath, RectF rect, int dir); + private static native void native_addRect(int nPath, float left, float top, + float right, float bottom, int dir); + private static native void native_addOval(int nPath, RectF oval, int dir); + private static native void native_addCircle(int nPath, float x, float y, + float radius, int dir); + private static native void native_addArc(int nPath, RectF oval, + float startAngle, float sweepAngle); + private static native void native_addRoundRect(int nPath, RectF rect, + float rx, float ry, int dir); + private static native void native_addRoundRect(int nPath, RectF r, + float[] radii, int dir); + private static native void native_addPath(int nPath, int src, float dx, + float dy); + private static native void native_addPath(int nPath, int src); + private static native void native_addPath(int nPath, int src, int matrix); + private static native void native_offset(int nPath, float dx, float dy, + int dst_path); + private static native void native_offset(int nPath, float dx, float dy); + private static native void native_setLastPoint(int nPath, float dx, float dy); + private static native void native_transform(int nPath, int matrix, + int dst_path); + private static native void native_transform(int nPath, int matrix); + private static native void finalizer(int nPath); + + private final int mNativePath; +} diff --git a/graphics/java/android/graphics/PathDashPathEffect.java b/graphics/java/android/graphics/PathDashPathEffect.java new file mode 100644 index 0000000..e8ad5fd --- /dev/null +++ b/graphics/java/android/graphics/PathDashPathEffect.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 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; + +public class PathDashPathEffect extends PathEffect { + + public enum Style { + TRANSLATE(0), //!< translate the shape to each position + ROTATE(1), //!< rotate the shape about its center + MORPH(2); //!< transform each point, and turn lines into curves + + Style(int value) { + native_style = value; + } + int native_style; + } + + /** + * Dash the drawn path by stamping it with the specified shape. This only + * applies to drawings when the paint's style is STROKE or STROKE_AND_FILL. + * If the paint's style is FILL, then this effect is ignored. The paint's + * strokeWidth does not affect the results. + * @param shape The path to stamp along + * @param advance spacing between each stamp of shape + * @param phase amount to offset before the first shape is stamped + * @param style how to transform the shape at each position as it is stamped + */ + public PathDashPathEffect(Path shape, float advance, float phase, + Style style) { + native_instance = nativeCreate(shape.ni(), advance, phase, + style.native_style); + } + + private static native int nativeCreate(int native_path, float advance, + float phase, int native_style); +} + diff --git a/graphics/java/android/graphics/PathEffect.java b/graphics/java/android/graphics/PathEffect.java new file mode 100644 index 0000000..9b2cd66 --- /dev/null +++ b/graphics/java/android/graphics/PathEffect.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006 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; + +/** + * PathEffect is the base class for objects in the Paint that affect + * the geometry of a drawing primitive before it is transformed by the + * canvas' matrix and drawn. + */ +public class PathEffect { + + protected void finalize() throws Throwable { + nativeDestructor(native_instance); + } + + private static native void nativeDestructor(int native_patheffect); + int native_instance; +} diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java new file mode 100644 index 0000000..98f7821 --- /dev/null +++ b/graphics/java/android/graphics/PathMeasure.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2007 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; + +public class PathMeasure { + + /** + * Create an empty PathMeasure object. To uses this to measure the length + * of a path, and/or to find the position and tangent along it, call + * setPath. + * + * Note that once a path is associated with the measure object, it is + * undefined if the path is subsequently modified and the the measure object + * is used. If the path is modified, you must call setPath with the path. + */ + public PathMeasure() { + native_instance = native_create(0, false); + } + + /** + * Create a PathMeasure object associated with the specified path object + * (already created and specified). The meansure object can now return the + * path's length, and the position and tangent of any position along the + * path. + * + * Note that once a path is associated with the measure object, it is + * undefined if the path is subsequently modified and the the measure object + * is used. If the path is modified, you must call setPath with the path. + * + * @param path The path that will be measured by this object + * @param forceClosed If true, then the path will be considered as "closed" + * even if its contour was not explicitly closed. + */ + public PathMeasure(Path path, boolean forceClosed) { + // note: the native side makes a copy of path, so we don't need a java + // reference to it here, since it's fine if it gets GC'd + native_instance = native_create(path != null ? path.ni() : 0, + forceClosed); + } + + /** + * Assign a new path, or null to have none. + */ + public void setPath(Path path, boolean forceClosed) { + // note: the native side makes a copy of path, so we don't need a java + // reference to it here, since it's fine if it gets GC'd + native_setPath(native_instance, + path != null ? path.ni() : 0, + forceClosed); + } + + /** + * Return the total length of the current contour, or 0 if no path is + * associated with this measure object. + */ + public float getLength() { + return native_getLength(native_instance); + } + + /** + * Pins distance to 0 <= distance <= getLength(), and then computes the + * corresponding position and tangent. Returns false if there is no path, + * or a zero-length path was specified, in which case position and tangent + * are unchanged. + * + * @param distance The distance along the current contour to sample + * @param pos If not null, eturns the sampled position (x==[0], y==[1]) + * @param tan If not null, returns the sampled tangent (x==[0], y==[1]) + * @return false if there was no path associated with this measure object + */ + public boolean getPosTan(float distance, float pos[], float tan[]) { + if (pos != null && pos.length < 2 || + tan != null && tan.length < 2) { + throw new ArrayIndexOutOfBoundsException(); + } + return native_getPosTan(native_instance, distance, pos, tan); + } + + public static final int POSITION_MATRIX_FLAG = 0x01; // must match flags in SkPathMeasure.h + public static final int TANGENT_MATRIX_FLAG = 0x02; // must match flags in SkPathMeasure.h + + /** + * Pins distance to 0 <= distance <= getLength(), and then computes the + * corresponding matrix. Returns false if there is no path, or a zero-length + * path was specified, in which case matrix is unchanged. + * + * @param distance The distance along the associated path + * @param matrix Allocated by the caller, this is set to the transformation + * associated with the position and tangent at the specified distance + * @param flags Specified what aspects should be returned in the matrix. + */ + public boolean getMatrix(float distance, Matrix matrix, int flags) { + return native_getMatrix(native_instance, distance, matrix.native_instance, flags); + } + + /** + * Given a start and stop distance, return in dst the intervening + * segment(s). If the segment is zero-length, return false, else return + * true. startD and stopD are pinned to legal values (0..getLength()). + * If startD <= stopD then return false (and leave dst untouched). + * Begin the segment with a moveTo if startWithMoveTo is true + */ + public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) { + return native_getSegment(native_instance, startD, stopD, dst.ni(), startWithMoveTo); + } + + /** + * Return true if the current contour is closed() + */ + public boolean isClosed() { + return native_isClosed(native_instance); + } + + /** + * Move to the next contour in the path. Return true if one exists, or + * false if we're done with the path. + */ + public boolean nextContour() { + return native_nextContour(native_instance); + } + + protected void finalize() throws Throwable { + native_destroy(native_instance); + } + + private static native int native_create(int native_path, boolean forceClosed); + private static native void native_setPath(int native_instance, int native_path, boolean forceClosed); + private static native float native_getLength(int native_instance); + private static native boolean native_getPosTan(int native_instance, float distance, float pos[], float tan[]); + private static native boolean native_getMatrix(int native_instance, float distance, int native_matrix, int flags); + private static native boolean native_getSegment(int native_instance, float startD, float stopD, int native_path, boolean startWithMoveTo); + private static native boolean native_isClosed(int native_instance); + private static native boolean native_nextContour(int native_instance); + private static native void native_destroy(int native_instance); + + /* package */private final int native_instance; +} + diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java new file mode 100644 index 0000000..bbb2dbf --- /dev/null +++ b/graphics/java/android/graphics/Picture.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2007 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.io.InputStream; +import java.io.OutputStream; + +/** + * A picture records drawing calls (via the canvas returned by beginRecording) + * and can then play them back (via picture.draw(canvas) or canvas.drawPicture). + * The picture's contents can also be written to a stream, and then later + * restored to a new picture (via writeToStream / createFromStream). For most + * content (esp. text, lines, rectangles), drawing a sequence from a picture can + * be faster than the equivalent API calls, since the picture performs its + * playback without incurring any java-call overhead. + */ +public class Picture { + private Canvas mRecordingCanvas; + private final int mNativePicture; + + private static final int WORKING_STREAM_STORAGE = 16 * 1024; + + public Picture() { + this(nativeConstructor(0)); + } + + /** + * Create a picture by making a copy of what has already been recorded in + * src. The contents of src are unchanged, and if src changes later, those + * changes will not be reflected in this picture. + */ + public Picture(Picture src) { + this(nativeConstructor(src != null ? src.mNativePicture : 0)); + } + + /** + * To record a picture, call beginRecording() and then draw into the Canvas + * that is returned. Nothing we appear on screen, but all of the draw + * commands (e.g. drawRect(...)) will be recorded. To stop recording, call + * endRecording(). At this point the Canvas that was returned must no longer + * be referenced, and nothing should be drawn into it. + */ + public Canvas beginRecording(int width, int height) { + int ni = nativeBeginRecording(mNativePicture, width, height); + mRecordingCanvas = new RecordingCanvas(this, ni); + return mRecordingCanvas; + } + + /** + * Call endRecording when the picture is built. After this call, the picture + * may be drawn, but the canvas that was returned by beginRecording must not + * be referenced anymore. This is automatically called if Picture.draw() or + * Canvas.drawPicture() is called. + */ + public void endRecording() { + if (mRecordingCanvas != null) { + mRecordingCanvas = null; + nativeEndRecording(mNativePicture); + } + } + + /** + * Get the width of the picture as passed to beginRecording. This + * does not reflect (per se) the content of the picture. + */ + public native int getWidth(); + + /** + * Get the height of the picture as passed to beginRecording. This + * does not reflect (per se) the content of the picture. + */ + public native int getHeight(); + + /** + * Draw this picture on the canvas. The picture may have the side effect + * of changing the matrix and clip of the canvas. + * + * @param canvas The picture is drawn to this canvas + */ + public void draw(Canvas canvas) { + if (mRecordingCanvas != null) { + endRecording(); + } + nativeDraw(canvas.mNativeCanvas, mNativePicture); + } + + /** + * Create a new picture (already recorded) from the data in the stream. This + * data was generated by a previous call to writeToStream(). + */ + public static Picture createFromStream(InputStream stream) { + return new Picture( + nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE])); + } + + /** + * Write the picture contents to a stream. The data can be used to recreate + * the picture in this or another process by calling createFromStream. + */ + public void writeToStream(OutputStream stream) { + // do explicit check before calling the native method + if (stream == null) { + throw new NullPointerException(); + } + if (!nativeWriteToStream(mNativePicture, stream, + new byte[WORKING_STREAM_STORAGE])) { + throw new RuntimeException(); + } + } + + protected void finalize() throws Throwable { + nativeDestructor(mNativePicture); + } + + /*package*/ final int ni() { + return mNativePicture; + } + + private Picture(int nativePicture) { + if (nativePicture == 0) { + throw new RuntimeException(); + } + mNativePicture = nativePicture; + } + + // return empty picture if src is 0, or a copy of the native src + private static native int nativeConstructor(int nativeSrcOr0); + private static native int nativeCreateFromStream(InputStream stream, + byte[] storage); + private static native int nativeBeginRecording(int nativeCanvas, + int w, int h); + private static native void nativeEndRecording(int nativeCanvas); + private static native void nativeDraw(int nativeCanvas, int nativePicture); + private static native boolean nativeWriteToStream(int nativePicture, + OutputStream stream, byte[] storage); + private static native void nativeDestructor(int nativePicture); + + private static class RecordingCanvas extends Canvas { + private final Picture mPicture; + + public RecordingCanvas(Picture pict, int nativeCanvas) { + super(nativeCanvas); + mPicture = pict; + } + + @Override + public void setBitmap(Bitmap bitmap) { + throw new RuntimeException( + "Cannot call setBitmap on a picture canvas"); + } + + @Override + public void drawPicture(Picture picture) { + if (mPicture == picture) { + throw new RuntimeException( + "Cannot draw a picture into its recording canvas"); + } + super.drawPicture(picture); + } + } +} + diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java new file mode 100644 index 0000000..159accc --- /dev/null +++ b/graphics/java/android/graphics/PixelFormat.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2006 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; + +public class PixelFormat +{ + /* these constants need to match those + in ui/PixelFormat.h & pixelflinger/format.h */ + + public static final int UNKNOWN = 0; + + /** System chooses a format that supports translucency (many alpha bits) */ + public static final int TRANSLUCENT = -3; + + /** + * System chooses a format that supports transparency + * (at least 1 alpha bit) + */ + public static final int TRANSPARENT = -2; + + /** System chooses an opaque format (no alpha bits required) */ + public static final int OPAQUE = -1; + + public static final int RGBA_8888 = 1; + public static final int RGBX_8888 = 2; + public static final int RGB_888 = 3; + public static final int RGB_565 = 4; + + public static final int RGBA_5551 = 6; + public static final int RGBA_4444 = 7; + public static final int A_8 = 8; + public static final int L_8 = 9; + public static final int LA_88 = 0xA; + public static final int RGB_332 = 0xB; + + /** + * YCbCr formats, used for video. These are not necessarily supported + * by the hardware. + */ + public static final int YCbCr_422_SP= 0x10; + public static final int YCbCr_420_SP= 0x11; + + /** + * Encoded formats. These are not necessarily supported by the hardware. + */ + public static final int JPEG = 0x100; + + /* + * We use a class initializer to allow the native code to cache some + * field offsets. + */ + native private static void nativeClassInit(); + static { nativeClassInit(); } + + public static native void getPixelFormatInfo(int format, PixelFormat info); + public static boolean formatHasAlpha(int format) { + switch (format) { + case PixelFormat.A_8: + case PixelFormat.LA_88: + case PixelFormat.RGBA_4444: + case PixelFormat.RGBA_5551: + case PixelFormat.RGBA_8888: + case PixelFormat.TRANSLUCENT: + case PixelFormat.TRANSPARENT: + return true; + } + return false; + } + + public int bytesPerPixel; + public int bitsPerPixel; +} diff --git a/graphics/java/android/graphics/PixelXorXfermode.java b/graphics/java/android/graphics/PixelXorXfermode.java new file mode 100644 index 0000000..18d15cf --- /dev/null +++ b/graphics/java/android/graphics/PixelXorXfermode.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2007 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; + +/** + * PixelXorXfermode implements a simple pixel xor (op ^ src ^ dst). + * This transformation does not follow premultiplied conventions, therefore + * this mode *always* returns an opaque color (alpha == 255). Thus it is + * not really usefull for operating on blended colors. + */ +public class PixelXorXfermode extends Xfermode { + + public PixelXorXfermode(int opColor) { + native_instance = nativeCreate(opColor); + } + + private static native int nativeCreate(int opColor); +} diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java new file mode 100644 index 0000000..c351444 --- /dev/null +++ b/graphics/java/android/graphics/Point.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007 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; + + +/** + * Point holds two integer coordinates + */ +public class Point { + public int x; + public int y; + + public Point() {} + + public Point(int x, int y) { + this.x = x; + this.y = y; + } + + public Point(Point src) { + this.x = src.x; + this.y = src.y; + } + + /** + * Set the point's x and y coordinates + */ + public void set(int x, int y) { + this.x = x; + this.y = y; + } + + /** + * Negate the point's coordinates + */ + public final void negate() { + x = -x; + y = -y; + } + + /** + * Offset the point's coordinates by dx, dy + */ + public final void offset(int dx, int dy) { + x += dx; + y += dy; + } + + /** + * Returns true if the point's coordinates equal (x,y) + */ + public final boolean equals(int x, int y) { + return this.x == x && this.y == y; + } + + @Override public boolean equals(Object o) { + if (o instanceof Point) { + Point p = (Point) o; + return this.x == p.x && this.y == p.y; + } + return false; + } + + @Override public int hashCode() { + return x * 32713 + y; + } + + @Override public String toString() { + return "Point(" + x + ", " + y+ ")"; + } +} diff --git a/graphics/java/android/graphics/PointF.java b/graphics/java/android/graphics/PointF.java new file mode 100644 index 0000000..0f045a1 --- /dev/null +++ b/graphics/java/android/graphics/PointF.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 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 android.util.FloatMath; + + +/** + * PointF holds two float coordinates + */ +public class PointF { + public float x; + public float y; + + public PointF() {} + + public PointF(float x, float y) { + this.x = x; + this.y = y; + } + + public PointF(Point p) { + this.x = p.x; + this.y = p.y; + } + + /** + * Set the point's x and y coordinates + */ + public final void set(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * Set the point's x and y coordinates to the coordinates of p + */ + public final void set(PointF p) { + this.x = p.x; + this.y = p.y; + } + + public final void negate() { + x = -x; + y = -y; + } + + public final void offset(float dx, float dy) { + x += dx; + y += dy; + } + + /** + * Returns true if the point's coordinates equal (x,y) + */ + public final boolean equals(float x, float y) { + return this.x == x && this.y == y; + } + + /** + * Return the euclidian distance from (0,0) to the point + */ + public final float length() { + return length(x, y); + } + + /** + * Returns the euclidian distance from (0,0) to (x,y) + */ + public static float length(float x, float y) { + return FloatMath.sqrt(x * x + y * y); + } +} + diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java new file mode 100644 index 0000000..3904234 --- /dev/null +++ b/graphics/java/android/graphics/PorterDuff.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2006 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; + +public class PorterDuff { + + // these value must match their native equivalents. See SkPorterDuff.h + public enum Mode { + /** [0, 0] */ + CLEAR (0), + /** [Sa, Sc] */ + SRC (1), + /** [Da, Dc] */ + DST (2), + /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */ + SRC_OVER (3), + /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */ + DST_OVER (4), + /** [Sa * Da, Sc * Da] */ + SRC_IN (5), + /** [Sa * Da, Sa * Dc] */ + DST_IN (6), + /** [Sa * (1 - Da), Sc * (1 - Da)] */ + SRC_OUT (7), + /** [Da * (1 - Sa), Dc * (1 - Sa)] */ + DST_OUT (8), + /** [Da, Sc * Da + (1 - Sa) * Dc] */ + SRC_ATOP (9), + /** [Sa, Sa * Dc + Sc * (1 - Da)] */ + DST_ATOP (10), + /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */ + XOR (11), + /** [Sa + Da - Sa*Da, + Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */ + DARKEN (12), + /** [Sa + Da - Sa*Da, + Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */ + LIGHTEN (13), + /** [Sa * Da, Sc * Dc] */ + MULTIPLY (14), + /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */ + SCREEN (15); + + Mode(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } +} diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java new file mode 100644 index 0000000..06724bd --- /dev/null +++ b/graphics/java/android/graphics/PorterDuffColorFilter.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2006 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; + +public class PorterDuffColorFilter extends ColorFilter { + /** + * Create a colorfilter that uses the specified color and porter-duff mode. + * + * @param srcColor The source color used with the specified + * porter-duff mode + * @param mode The porter-duff mode that is applied + */ + public PorterDuffColorFilter(int srcColor, PorterDuff.Mode mode) { + native_instance = native_CreatePorterDuffFilter(srcColor, + mode.nativeInt); + } + + private static native int native_CreatePorterDuffFilter(int srcColor, + int porterDuffMode); +} diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java new file mode 100644 index 0000000..cb127fd --- /dev/null +++ b/graphics/java/android/graphics/PorterDuffXfermode.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2006 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; + +public class PorterDuffXfermode extends Xfermode { + /** + * Create an xfermode that uses the specified porter-duff mode. + * + * @param mode The porter-duff mode that is applied + */ + public PorterDuffXfermode(PorterDuff.Mode mode) { + native_instance = nativeCreateXfermode(mode.nativeInt); + } + + private static native int nativeCreateXfermode(int mode); +} diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java new file mode 100644 index 0000000..b4e902d --- /dev/null +++ b/graphics/java/android/graphics/RadialGradient.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 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; + +public class RadialGradient extends Shader { + + /** Create a shader that draws a radial gradient given the center and radius. + @param x The x-coordinate of the center of the radius + @param y The y-coordinate of the center of the radius + @param radius Must be positive. The radius of the circle for this gradient + @param colors The colors to be distributed between the center and edge of the circle + @param positions May be NULL. The relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the center and edge of the circle. + @param tile The Shader tiling mode + */ + public RadialGradient(float x, float y, float radius, + int colors[], float positions[], TileMode tile) { + if (radius <= 0) { + throw new IllegalArgumentException("radius must be > 0"); + } + if (colors.length < 2) { + throw new IllegalArgumentException("needs >= 2 number of colors"); + } + if (positions != null && colors.length != positions.length) { + throw new IllegalArgumentException("color and position arrays must be of equal length"); + } + native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt); + } + + /** Create a shader that draws a radial gradient given the center and radius. + @param x The x-coordinate of the center of the radius + @param y The y-coordinate of the center of the radius + @param radius Must be positive. The radius of the circle for this gradient + @param color0 The color at the center of the circle. + @param color1 The color at the edge of the circle. + @param tile The Shader tiling mode + */ + public RadialGradient(float x, float y, float radius, + int color0, int color1, TileMode tile) { + if (radius <= 0) { + throw new IllegalArgumentException("radius must be > 0"); + } + native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt); + } + + private static native int nativeCreate1(float x, float y, float radius, + int colors[], float positions[], int tileMode); + private static native int nativeCreate2(float x, float y, float radius, + int color0, int color1, int tileMode); +} + diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java new file mode 100644 index 0000000..feb5f0c --- /dev/null +++ b/graphics/java/android/graphics/Rasterizer.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2006 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. + */ + +// This file was generated from the C++ include file: SkRasterizer.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +package android.graphics; + +public class Rasterizer { + + protected void finalize() throws Throwable { + finalizer(native_instance); + } + + private static native void finalizer(int native_instance); + + int native_instance; +} diff --git a/graphics/java/android/graphics/Rect.aidl b/graphics/java/android/graphics/Rect.aidl new file mode 100644 index 0000000..09edbfe --- /dev/null +++ b/graphics/java/android/graphics/Rect.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/graphics/Rect.aidl +** +** Copyright 2007, 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; + +parcelable Rect; diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java new file mode 100644 index 0000000..d8d7136 --- /dev/null +++ b/graphics/java/android/graphics/Rect.java @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2006 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 android.os.Parcel; +import android.os.Parcelable; + +/** + * Rect holds four integer coordinates for a rectangle. The rectangle is + * represented by the coordinates of its 4 edges (left, top, right bottom). + * These fields can be accessed directly. Use width() and height() to retrieve + * the rectangle's width and height. Note: most methods do not check to see that + * the coordinates are sorted correctly (i.e. left <= right and top <= bottom). + */ +public final class Rect implements Parcelable { + public int left; + public int top; + public int right; + public int bottom; + + /** + * Create a new empty Rect. All coordinates are initialized to 0. + */ + public Rect() {} + + /** + * Create a new rectangle with the specified coordinates. Note: no range + * checking is performed, so the caller must ensure that left <= right and + * top <= bottom. + * + * @param left The X coordinate of the left side of the rectagle + * @param top The Y coordinate of the top of the rectangle + * @param right The X coordinate of the right side of the rectagle + * @param bottom The Y coordinate of the bottom of the rectangle + */ + public Rect(int left, int top, int right, int bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + /** + * Create a new rectangle, initialized with the values in the specified + * rectangle (which is left unmodified). + * + * @param r The rectangle whose coordinates are copied into the new + * rectangle. + */ + public Rect(Rect r) { + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; + } + + public boolean equals(Object obj) { + Rect r = (Rect) obj; + if (r != null) { + return left == r.left && top == r.top && right == r.right + && bottom == r.bottom; + } + return false; + } + + public String toString() { + return "Rect(" + left + ", " + top + ", " + right + ", " + bottom + ")"; + } + + /** + * Returns true if the rectangle is empty (left >= right or top >= bottom) + */ + public final boolean isEmpty() { + return left >= right || top >= bottom; + } + + /** + * @return the rectangle's width. This does not check for a valid rectangle + * (i.e. left <= right) so the result may be negative. + */ + public final int width() { + return right - left; + } + + /** + * @return the rectangle's height. This does not check for a valid rectangle + * (i.e. top <= bottom) so the result may be negative. + */ + public final int height() { + return bottom - top; + } + + /** + * @return the horizontal center of the rectangle. If the computed value + * is fractional, this method returns the largest integer that is + * less than the computed value. + */ + public final int centerX() { + return (left + right) >> 1; + } + + /** + * @return the vertical center of the rectangle. If the computed value + * is fractional, this method returns the largest integer that is + * less than the computed value. + */ + public final int centerY() { + return (top + bottom) >> 1; + } + + /** + * @return the exact horizontal center of the rectangle as a float. + */ + public final float exactCenterX() { + return (left + right) * 0.5f; + } + + /** + * @return the exact vertical center of the rectangle as a float. + */ + public final float exactCenterY() { + return (top + bottom) * 0.5f; + } + + /** + * Set the rectangle to (0,0,0,0) + */ + public void setEmpty() { + left = right = top = bottom = 0; + } + + /** + * Set the rectangle's coordinates to the specified values. Note: no range + * checking is performed, so it is up to the caller to ensure that + * left <= right and top <= bottom. + * + * @param left The X coordinate of the left side of the rectagle + * @param top The Y coordinate of the top of the rectangle + * @param right The X coordinate of the right side of the rectagle + * @param bottom The Y coordinate of the bottom of the rectangle + */ + public void set(int left, int top, int right, int bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + /** + * Copy the coordinates from src into this rectangle. + * + * @param src The rectangle whose coordinates are copied into this + * rectangle. + */ + public void set(Rect src) { + this.left = src.left; + this.top = src.top; + this.right = src.right; + this.bottom = src.bottom; + } + + /** + * Offset the rectangle by adding dx to its left and right coordinates, and + * adding dy to its top and bottom coordinates. + * + * @param dx The amount to add to the rectangle's left and right coordinates + * @param dy The amount to add to the rectangle's top and bottom coordinates + */ + public void offset(int dx, int dy) { + left += dx; + top += dy; + right += dx; + bottom += dy; + } + + /** + * Offset the rectangle to a specific (left, top) position, + * keeping its width and height the same. + * + * @param newLeft The new "left" coordinate for the rectangle + * @param newTop The new "top" coordinate for the rectangle + */ + public void offsetTo(int newLeft, int newTop) { + right += newLeft - left; + bottom += newTop - top; + left = newLeft; + top = newTop; + } + + /** + * Inset the rectangle by (dx,dy). If dx is positive, then the sides are + * moved inwards, making the rectangle narrower. If dx is negative, then the + * sides are moved outwards, making the rectangle wider. The same holds true + * for dy and the top and bottom. + * + * @param dx The amount to add(subtract) from the rectangle's left(right) + * @param dy The amount to add(subtract) from the rectangle's top(bottom) + */ + public void inset(int dx, int dy) { + left += dx; + top += dy; + right -= dx; + bottom -= dy; + } + + /** + * Returns true if (x,y) is inside the rectangle. The left and top are + * considered to be inside, while the right and bottom are not. This means + * that for a x,y to be contained: left <= x < right and top <= y < bottom. + * An empty rectangle never contains any point. + * + * @param x The X coordinate of the point being tested for containment + * @param y The Y coordinate of the point being tested for containment + * @return true iff (x,y) are contained by the rectangle, where containment + * means left <= x < right and top <= y < bottom + */ + public boolean contains(int x, int y) { + return left < right && top < bottom // check for empty first + && x >= left && x < right && y >= top && y < bottom; + } + + /** + * Returns true iff the 4 specified sides of a rectangle are inside or equal + * to this rectangle. i.e. is this rectangle a superset of the specified + * rectangle. An empty rectangle never contains another rectangle. + * + * @param left The left side of the rectangle being tested for containment + * @param top The top of the rectangle being tested for containment + * @param right The right side of the rectangle being tested for containment + * @param bottom The bottom of the rectangle being tested for containment + * @return true iff the the 4 specified sides of a rectangle are inside or + * equal to this rectangle + */ + public boolean contains(int left, int top, int right, int bottom) { + // check for empty first + return this.left < this.right && this.top < this.bottom + // now check for containment + && this.left <= left && this.top <= top + && this.right >= right && this.bottom >= bottom; + } + + /** + * Returns true iff the specified rectangle r is inside or equal to this + * rectangle. An empty rectangle never contains another rectangle. + * + * @param r The rectangle being tested for containment. + * @return true iff the specified rectangle r is inside or equal to this + * rectangle + */ + public boolean contains(Rect r) { + // check for empty first + return this.left < this.right && this.top < this.bottom + // now check for containment + && left <= r.left && top <= r.top + && right >= r.right && bottom >= r.bottom; + } + + /** + * If the rectangle specified by left,top,right,bottom intersects this + * rectangle, return true and set this rectangle to that intersection, + * otherwise return false and do not change this rectangle. No check is + * performed to see if either rectangle is empty. Note: To just test for + * intersection, use intersects() + * + * @param left The left side of the rectangle being intersected with this + * rectangle + * @param top The top of the rectangle being intersected with this rectangle + * @param right The right side of the rectangle being intersected with this + * rectangle. + * @param bottom The bottom of the rectangle being intersected with this + * rectangle. + * @return true if the specified rectangle and this rectangle intersect + * (and this rectangle is then set to that intersection) else + * return false and do not change this rectangle. + */ + public boolean intersect(int left, int top, int right, int bottom) { + if (this.left < right && left < this.right + && this.top < bottom && top < this.bottom) { + if (this.left < left) { + this.left = left; + } + if (this.top < top) { + this.top = top; + } + if (this.right > right) { + this.right = right; + } + if (this.bottom > bottom) { + this.bottom = bottom; + } + return true; + } + return false; + } + + /** + * If the specified rectangle intersects this rectangle, return true and set + * this rectangle to that intersection, otherwise return false and do not + * change this rectangle. No check is performed to see if either rectangle + * is empty. To just test for intersection, use intersects() + * + * @param r The rectangle being intersected with this rectangle. + * @return true if the specified rectangle and this rectangle intersect + * (and this rectangle is then set to that intersection) else + * return false and do not change this rectangle. + */ + public boolean intersect(Rect r) { + return intersect(r.left, r.top, r.right, r.bottom); + } + + /** + * If rectangles a and b intersect, return true and set this rectangle to + * that intersection, otherwise return false and do not change this + * rectangle. No check is performed to see if either rectangle is empty. + * To just test for intersection, use intersects() + * + * @param a The first rectangle being intersected with + * @param b The second rectangle being intersected with + * @return true iff the two specified rectangles intersect. If they do, set + * this rectangle to that intersection. If they do not, return + * false and do not change this rectangle. + */ + public boolean setIntersect(Rect a, Rect b) { + if (a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom) { + left = Math.max(a.left, b.left); + top = Math.max(a.top, b.top); + right = Math.min(a.right, b.right); + bottom = Math.min(a.bottom, b.bottom); + return true; + } + return false; + } + + /** + * Returns true if this rectangle intersects the specified rectangle. + * In no event is this rectangle modified. No check is performed to see + * if either rectangle is empty. To record the intersection, use intersect() + * or setIntersect(). + * + * @param left The left side of the rectangle being tested for intersection + * @param top The top of the rectangle being tested for intersection + * @param right The right side of the rectangle being tested for + * intersection + * @param bottom The bottom of the rectangle being tested for intersection + * @return true iff the specified rectangle intersects this rectangle. In + * no event is this rectangle modified. + */ + public boolean intersects(int left, int top, int right, int bottom) { + return this.left < right && left < this.right + && this.top < bottom && top < this.bottom; + } + + /** + * Returns true iff the two specified rectangles intersect. In no event are + * either of the rectangles modified. To record the intersection, + * use intersect() or setIntersect(). + * + * @param a The first rectangle being tested for intersection + * @param b The second rectangle being tested for intersection + * @return true iff the two specified rectangles intersect. In no event are + * either of the rectangles modified. + */ + public static boolean intersects(Rect a, Rect b) { + return a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom; + } + + /** + * Update this Rect to enclose itself and the specified rectangle. If the + * specified rectangle is empty, nothing is done. If this rectangle is empty + * it is set to the specified rectangle. + * + * @param left The left edge being unioned with this rectangle + * @param top The top edge being unioned with this rectangle + * @param right The right edge being unioned with this rectangle + * @param bottom The bottom edge being unioned with this rectangle + */ + public void union(int left, int top, int right, int bottom) { + if ((left < right) && (top < bottom)) { + if ((this.left < this.right) && (this.top < this.bottom)) { + if (this.left > left) + this.left = left; + if (this.top > top) + this.top = top; + if (this.right < right) + this.right = right; + if (this.bottom < bottom) + this.bottom = bottom; + } else { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + } + } + + /** + * Update this Rect to enclose itself and the specified rectangle. If the + * specified rectangle is empty, nothing is done. If this rectangle is empty + * it is set to the specified rectangle. + * + * @param r The rectangle being unioned with this rectangle + */ + public void union(Rect r) { + union(r.left, r.top, r.right, r.bottom); + } + + /** + * Update this Rect to enclose itself and the [x,y] coordinate. There is no + * check to see that this rectangle is non-empty. + * + * @param x The x coordinate of the point to add to the rectangle + * @param y The y coordinate of the point to add to the rectangle + */ + public void union(int x, int y) { + if (x < left) { + left = x; + } else if (x > right) { + right = x; + } + if (y < top) { + top = y; + } else if (y > bottom) { + bottom = y; + } + } + + /** + * Swap top/bottom or left/right if there are flipped (i.e. left > right + * and/or top > bottom). This can be called if + * the edges are computed separately, and may have crossed over each other. + * If the edges are already correct (i.e. left <= right and top <= bottom) + * then nothing is done. + */ + public void sort() { + if (left > right) { + int temp = left; + left = right; + right = temp; + } + if (top > bottom) { + int temp = top; + top = bottom; + bottom = temp; + } + } + + /** + * Parcelable interface methods + */ + public int describeContents() { + return 0; + } + + /** + * Write this rectangle to the specified parcel. To restore a rectangle from + * a parcel, use readFromParcel() + * @param out The parcel to write the rectangle's coordinates into + */ + public void writeToParcel(Parcel out, int flags) { + out.writeInt(left); + out.writeInt(top); + out.writeInt(right); + out.writeInt(bottom); + } + + public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { + /** + * Return a new rectangle from the data in the specified parcel. + */ + public Rect createFromParcel(Parcel in) { + Rect r = new Rect(); + r.readFromParcel(in); + return r; + } + + /** + * Return an array of rectangles of the specified size. + */ + public Rect[] newArray(int size) { + return new Rect[size]; + } + }; + + /** + * Set the rectangle's coordinates from the data stored in the specified + * parcel. To write a rectangle to a parcel, call writeToParcel(). + * + * @param in The parcel to read the rectangle's coordinates from + */ + public void readFromParcel(Parcel in) { + left = in.readInt(); + top = in.readInt(); + right = in.readInt(); + bottom = in.readInt(); + } +} diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java new file mode 100644 index 0000000..d71f2dc --- /dev/null +++ b/graphics/java/android/graphics/RectF.java @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2006 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 android.util.FloatMath; +import com.android.internal.util.FastMath; + +/** + * RectF holds four float coordinates for a rectangle. The rectangle is + * represented by the coordinates of its 4 edges (left, top, right bottom). + * These fields can be accessed directly. Use width() and height() to retrieve + * the rectangle's width and height. Note: most methods do not check to see that + * the coordinates are sorted correctly (i.e. left <= right and top <= bottom). + */ +public class RectF { + public float left; + public float top; + public float right; + public float bottom; + + /** + * Create a new empty RectF. All coordinates are initialized to 0. + */ + public RectF() {} + + /** + * Create a new rectangle with the specified coordinates. Note: no range + * checking is performed, so the caller must ensure that left <= right and + * top <= bottom. + * + * @param left The X coordinate of the left side of the rectagle + * @param top The Y coordinate of the top of the rectangle + * @param right The X coordinate of the right side of the rectagle + * @param bottom The Y coordinate of the bottom of the rectangle + */ + public RectF(float left, float top, float right, float bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + /** + * Create a new rectangle, initialized with the values in the specified + * rectangle (which is left unmodified). + * + * @param r The rectangle whose coordinates are copied into the new + * rectangle. + */ + public RectF(RectF r) { + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; + } + + public RectF(Rect r) { + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; + } + + public String toString() { + return "RectF(" + left + ", " + top + ", " + + right + ", " + bottom + ")"; + } + + /** + * Returns true if the rectangle is empty (left >= right or top >= bottom) + */ + public final boolean isEmpty() { + return left >= right || top >= bottom; + } + + /** + * @return the rectangle's width. This does not check for a valid rectangle + * (i.e. left <= right) so the result may be negative. + */ + public final float width() { + return right - left; + } + + /** + * @return the rectangle's height. This does not check for a valid rectangle + * (i.e. top <= bottom) so the result may be negative. + */ + public final float height() { + return bottom - top; + } + + /** + * @return the horizontal center of the rectangle. This does not check for + * a valid rectangle (i.e. left <= right) + */ + public final float centerX() { + return (left + right) * 0.5f; + } + + /** + * @return the vertical center of the rectangle. This does not check for + * a valid rectangle (i.e. top <= bottom) + */ + public final float centerY() { + return (top + bottom) * 0.5f; + } + + /** + * Set the rectangle to (0,0,0,0) + */ + public void setEmpty() { + left = right = top = bottom = 0; + } + + /** + * Set the rectangle's coordinates to the specified values. Note: no range + * checking is performed, so it is up to the caller to ensure that + * left <= right and top <= bottom. + * + * @param left The X coordinate of the left side of the rectagle + * @param top The Y coordinate of the top of the rectangle + * @param right The X coordinate of the right side of the rectagle + * @param bottom The Y coordinate of the bottom of the rectangle + */ + public void set(float left, float top, float right, float bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + /** + * Copy the coordinates from src into this rectangle. + * + * @param src The rectangle whose coordinates are copied into this + * rectangle. + */ + public void set(RectF src) { + this.left = src.left; + this.top = src.top; + this.right = src.right; + this.bottom = src.bottom; + } + + /** + * Copy the coordinates from src into this rectangle. + * + * @param src The rectangle whose coordinates are copied into this + * rectangle. + */ + public void set(Rect src) { + this.left = src.left; + this.top = src.top; + this.right = src.right; + this.bottom = src.bottom; + } + + /** + * Offset the rectangle by adding dx to its left and right coordinates, and + * adding dy to its top and bottom coordinates. + * + * @param dx The amount to add to the rectangle's left and right coordinates + * @param dy The amount to add to the rectangle's top and bottom coordinates + */ + public void offset(float dx, float dy) { + left += dx; + top += dy; + right += dx; + bottom += dy; + } + + /** + * Offset the rectangle to a specific (left, top) position, + * keeping its width and height the same. + * + * @param newLeft The new "left" coordinate for the rectangle + * @param newTop The new "top" coordinate for the rectangle + */ + public void offsetTo(float newLeft, float newTop) { + right += newLeft - left; + bottom += newTop - top; + left = newLeft; + top = newTop; + } + + /** + * Inset the rectangle by (dx,dy). If dx is positive, then the sides are + * moved inwards, making the rectangle narrower. If dx is negative, then the + * sides are moved outwards, making the rectangle wider. The same holds true + * for dy and the top and bottom. + * + * @param dx The amount to add(subtract) from the rectangle's left(right) + * @param dy The amount to add(subtract) from the rectangle's top(bottom) + */ + public void inset(float dx, float dy) { + left += dx; + top += dy; + right -= dx; + bottom -= dy; + } + + /** + * Returns true if (x,y) is inside the rectangle. The left and top are + * considered to be inside, while the right and bottom are not. This means + * that for a x,y to be contained: left <= x < right and top <= y < bottom. + * An empty rectangle never contains any point. + * + * @param x The X coordinate of the point being tested for containment + * @param y The Y coordinate of the point being tested for containment + * @return true iff (x,y) are contained by the rectangle, where containment + * means left <= x < right and top <= y < bottom + */ + public boolean contains(float x, float y) { + return left < right && top < bottom // check for empty first + && x >= left && x < right && y >= top && y < bottom; + } + + /** + * Returns true iff the 4 specified sides of a rectangle are inside or equal + * to this rectangle. i.e. is this rectangle a superset of the specified + * rectangle. An empty rectangle never contains another rectangle. + * + * @param left The left side of the rectangle being tested for containment + * @param top The top of the rectangle being tested for containment + * @param right The right side of the rectangle being tested for containment + * @param bottom The bottom of the rectangle being tested for containment + * @return true iff the the 4 specified sides of a rectangle are inside or + * equal to this rectangle + */ + public boolean contains(float left, float top, float right, float bottom) { + // check for empty first + return this.left < this.right && this.top < this.bottom + // now check for containment + && this.left <= left && this.top <= top + && this.right >= right && this.bottom >= bottom; + } + + /** + * Returns true iff the specified rectangle r is inside or equal to this + * rectangle. An empty rectangle never contains another rectangle. + * + * @param r The rectangle being tested for containment. + * @return true iff the specified rectangle r is inside or equal to this + * rectangle + */ + public boolean contains(RectF r) { + // check for empty first + return this.left < this.right && this.top < this.bottom + // now check for containment + && left <= r.left && top <= r.top + && right >= r.right && bottom >= r.bottom; + } + + /** + * If the rectangle specified by left,top,right,bottom intersects this + * rectangle, return true and set this rectangle to that intersection, + * otherwise return false and do not change this rectangle. No check is + * performed to see if either rectangle is empty. Note: To just test for + * intersection, use intersects() + * + * @param left The left side of the rectangle being intersected with this + * rectangle + * @param top The top of the rectangle being intersected with this rectangle + * @param right The right side of the rectangle being intersected with this + * rectangle. + * @param bottom The bottom of the rectangle being intersected with this + * rectangle. + * @return true if the specified rectangle and this rectangle intersect + * (and this rectangle is then set to that intersection) else + * return false and do not change this rectangle. + */ + public boolean intersect(float left, float top, float right, float bottom) { + if (this.left < right && left < this.right + && this.top < bottom && top < this.bottom) { + if (this.left < left) { + this.left = left; + } + if (this.top < top) { + this.top = top; + } + if (this.right > right) { + this.right = right; + } + if (this.bottom > bottom) { + this.bottom = bottom; + } + return true; + } + return false; + } + + /** + * If the specified rectangle intersects this rectangle, return true and set + * this rectangle to that intersection, otherwise return false and do not + * change this rectangle. No check is performed to see if either rectangle + * is empty. To just test for intersection, use intersects() + * + * @param r The rectangle being intersected with this rectangle. + * @return true if the specified rectangle and this rectangle intersect + * (and this rectangle is then set to that intersection) else + * return false and do not change this rectangle. + */ + public boolean intersect(RectF r) { + return intersect(r.left, r.top, r.right, r.bottom); + } + + /** + * If rectangles a and b intersect, return true and set this rectangle to + * that intersection, otherwise return false and do not change this + * rectangle. No check is performed to see if either rectangle is empty. + * To just test for intersection, use intersects() + * + * @param a The first rectangle being intersected with + * @param b The second rectangle being intersected with + * @return true iff the two specified rectangles intersect. If they do, set + * this rectangle to that intersection. If they do not, return + * false and do not change this rectangle. + */ + public boolean setIntersect(RectF a, RectF b) { + if (a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom) { + left = Math.max(a.left, b.left); + top = Math.max(a.top, b.top); + right = Math.min(a.right, b.right); + bottom = Math.min(a.bottom, b.bottom); + return true; + } + return false; + } + + /** + * Returns true if this rectangle intersects the specified rectangle. + * In no event is this rectangle modified. No check is performed to see + * if either rectangle is empty. To record the intersection, use intersect() + * or setIntersect(). + * + * @param left The left side of the rectangle being tested for intersection + * @param top The top of the rectangle being tested for intersection + * @param right The right side of the rectangle being tested for + * intersection + * @param bottom The bottom of the rectangle being tested for intersection + * @return true iff the specified rectangle intersects this rectangle. In + * no event is this rectangle modified. + */ + public boolean intersects(float left, float top, float right, + float bottom) { + return this.left < right && left < this.right + && this.top < bottom && top < this.bottom; + } + + /** + * Returns true iff the two specified rectangles intersect. In no event are + * either of the rectangles modified. To record the intersection, + * use intersect() or setIntersect(). + * + * @param a The first rectangle being tested for intersection + * @param b The second rectangle being tested for intersection + * @return true iff the two specified rectangles intersect. In no event are + * either of the rectangles modified. + */ + public static boolean intersects(RectF a, RectF b) { + return a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom; + } + + /** + * Set the dst integer Rect by rounding this rectangle's coordinates + * to their nearest integer values. + */ + public void round(Rect dst) { + dst.set(FastMath.round(left), FastMath.round(top), + FastMath.round(right), FastMath.round(bottom)); + } + + /** + * Set the dst integer Rect by rounding "out" this rectangle, choosing the + * floor of top and left, and the ceiling of right and bottom. + */ + public void roundOut(Rect dst) { + dst.set((int) FloatMath.floor(left), (int) FloatMath.floor(top), + (int) FloatMath.ceil(right), (int) FloatMath.ceil(bottom)); + } + + /** + * Update this Rect to enclose itself and the specified rectangle. If the + * specified rectangle is empty, nothing is done. If this rectangle is empty + * it is set to the specified rectangle. + * + * @param left The left edge being unioned with this rectangle + * @param top The top edge being unioned with this rectangle + * @param right The right edge being unioned with this rectangle + * @param bottom The bottom edge being unioned with this rectangle + */ + public void union(float left, float top, float right, float bottom) { + if ((left < right) && (top < bottom)) { + if ((this.left < this.right) && (this.top < this.bottom)) { + if (this.left > left) + this.left = left; + if (this.top > top) + this.top = top; + if (this.right < right) + this.right = right; + if (this.bottom < bottom) + this.bottom = bottom; + } else { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + } + } + + /** + * Update this Rect to enclose itself and the specified rectangle. If the + * specified rectangle is empty, nothing is done. If this rectangle is empty + * it is set to the specified rectangle. + * + * @param r The rectangle being unioned with this rectangle + */ + public void union(RectF r) { + union(r.left, r.top, r.right, r.bottom); + } + + /** + * Update this Rect to enclose itself and the [x,y] coordinate. There is no + * check to see that this rectangle is non-empty. + * + * @param x The x coordinate of the point to add to the rectangle + * @param y The y coordinate of the point to add to the rectangle + */ + public void union(float x, float y) { + if (x < left) { + left = x; + } else if (x > right) { + right = x; + } + if (y < top) { + top = y; + } else if (y > bottom) { + bottom = y; + } + } + + /** + * Swap top/bottom or left/right if there are flipped (i.e. left > right + * and/or top > bottom). This can be called if + * the edges are computed separately, and may have crossed over each other. + * If the edges are already correct (i.e. left <= right and top <= bottom) + * then nothing is done. + */ + public void sort() { + if (left > right) { + float temp = left; + left = right; + right = temp; + } + if (top > bottom) { + float temp = top; + top = bottom; + bottom = temp; + } + } +} diff --git a/graphics/java/android/graphics/Region.aidl b/graphics/java/android/graphics/Region.aidl new file mode 100644 index 0000000..7d1286f --- /dev/null +++ b/graphics/java/android/graphics/Region.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/graphics/Rect.aidl +** +** Copyright 2007, 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; + +parcelable Region; diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java new file mode 100644 index 0000000..544ff4f --- /dev/null +++ b/graphics/java/android/graphics/Region.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2006 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 android.os.Parcel; +import android.os.Parcelable; + +public class Region implements Parcelable { + + // the native values for these must match up with the enum in SkRegion.h + public enum Op { + DIFFERENCE(0), + INTERSECT(1), + UNION(2), + XOR(3), + REVERSE_DIFFERENCE(4), + REPLACE(5); + + Op(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** Create an empty region + */ + public Region() { + this(nativeConstructor()); + } + + /** Return a copy of the specified region + */ + public Region(Region region) { + this(nativeConstructor()); + nativeSetRegion(mNativeRegion, region.mNativeRegion); + } + + /** Return a region set to the specified rectangle + */ + public Region(Rect r) { + mNativeRegion = nativeConstructor(); + nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom); + } + + /** Return a region set to the specified rectangle + */ + public Region(int left, int top, int right, int bottom) { + mNativeRegion = nativeConstructor(); + nativeSetRect(mNativeRegion, left, top, right, bottom); + } + + /** Set the region to the empty region + */ + public void setEmpty() { + nativeSetRect(mNativeRegion, 0, 0, 0, 0); + } + + /** Set the region to the specified region. + */ + public boolean set(Region region) { + return nativeSetRegion(mNativeRegion, region.mNativeRegion); + } + + /** Set the region to the specified rectangle + */ + public boolean set(Rect r) { + return nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom); + } + + /** Set the region to the specified rectangle + */ + public boolean set(int left, int top, int right, int bottom) { + return nativeSetRect(mNativeRegion, left, top, right, bottom); + } + + /** + * Set the region to the area described by the path and clip. + * Return true if the resulting region is non-empty. This produces a region + * that is identical to the pixels that would be drawn by the path + * (with no antialiasing). + */ + public boolean setPath(Path path, Region clip) { + return nativeSetPath(mNativeRegion, path.ni(), clip.mNativeRegion); + } + + /** + * Return true if this region is empty + */ + public native boolean isEmpty(); + + /** + * Return true if the region contains a single rectangle + */ + public native boolean isRect(); + + /** + * Return true if the region contains more than one rectangle + */ + public native boolean isComplex(); + + /** + * Return a new Rect set to the bounds of the region. If the region is + * empty, the Rect will be set to [0, 0, 0, 0] + */ + public Rect getBounds() { + Rect r = new Rect(); + nativeGetBounds(mNativeRegion, r); + return r; + } + + /** + * Set the Rect to the bounds of the region. If the region is empty, the + * Rect will be set to [0, 0, 0, 0] + */ + public boolean getBounds(Rect r) { + if (r == null) { + throw new NullPointerException(); + } + return nativeGetBounds(mNativeRegion, r); + } + + /** + * Return the boundary of the region as a new Path. If the region is empty, + * the path will also be empty. + */ + public Path getBoundaryPath() { + Path path = new Path(); + nativeGetBoundaryPath(mNativeRegion, path.ni()); + return path; + } + + /** + * Set the path to the boundary of the region. If the region is empty, the + * path will also be empty. + */ + public boolean getBoundaryPath(Path path) { + return nativeGetBoundaryPath(mNativeRegion, path.ni()); + } + + /** + * Return true if the region contains the specified point + */ + public native boolean contains(int x, int y); + + /** + * Return true if the region is a single rectangle (not complex) and it + * contains the specified rectangle. Returning false is not a guarantee + * that the rectangle is not contained by this region, but return true is a + * guarantee that the rectangle is contained by this region. + */ + public boolean quickContains(Rect r) { + return quickContains(r.left, r.top, r.right, r.bottom); + } + + /** + * Return true if the region is a single rectangle (not complex) and it + * contains the specified rectangle. Returning false is not a guarantee + * that the rectangle is not contained by this region, but return true is a + * guarantee that the rectangle is contained by this region. + */ + public native boolean quickContains(int left, int top, int right, + int bottom); + + /** + * Return true if the region is empty, or if the specified rectangle does + * not intersect the region. Returning false is not a guarantee that they + * intersect, but returning true is a guarantee that they do not. + */ + public boolean quickReject(Rect r) { + return quickReject(r.left, r.top, r.right, r.bottom); + } + + /** + * Return true if the region is empty, or if the specified rectangle does + * not intersect the region. Returning false is not a guarantee that they + * intersect, but returning true is a guarantee that they do not. + */ + public native boolean quickReject(int left, int top, int right, int bottom); + + /** + * Return true if the region is empty, or if the specified region does not + * intersect the region. Returning false is not a guarantee that they + * intersect, but returning true is a guarantee that they do not. + */ + public native boolean quickReject(Region rgn); + + /** + * Translate the region by [dx, dy]. If the region is empty, do nothing. + */ + public void translate(int dx, int dy) { + translate(dx, dy, null); + } + + /** + * Set the dst region to the result of translating this region by [dx, dy]. + * If this region is empty, then dst will be set to empty. + */ + public native void translate(int dx, int dy, Region dst); + + public final boolean union(Rect r) { + return op(r, Op.UNION); + } + + /** + * Perform the specified Op on this region and the specified rect. Return + * true if the result of the op is not empty. + */ + public boolean op(Rect r, Op op) { + return nativeOp(mNativeRegion, r.left, r.top, r.right, r.bottom, + op.nativeInt); + } + + /** + * Perform the specified Op on this region and the specified rect. Return + * true if the result of the op is not empty. + */ + public boolean op(int left, int top, int right, int bottom, Op op) { + return nativeOp(mNativeRegion, left, top, right, bottom, + op.nativeInt); + } + + /** + * Perform the specified Op on this region and the specified region. Return + * true if the result of the op is not empty. + */ + public boolean op(Region region, Op op) { + return op(this, region, op); + } + + /** + * Set this region to the result of performing the Op on the specified rect + * and region. Return true if the result is not empty. + */ + public boolean op(Rect rect, Region region, Op op) { + return nativeOp(mNativeRegion, rect, region.mNativeRegion, + op.nativeInt); + } + + /** + * Set this region to the result of performing the Op on the specified + * regions. Return true if the result is not empty. + */ + public boolean op(Region region1, Region region2, Op op) { + return nativeOp(mNativeRegion, region1.mNativeRegion, + region2.mNativeRegion, op.nativeInt); + } + + ////////////////////////////////////////////////////////////////////////// + + public static final Parcelable.Creator<Region> CREATOR + = new Parcelable.Creator<Region>() { + /** + * Rebuild a Region previously stored with writeToParcel(). + * @param p Parcel object to read the region from + * @return a new region created from the data in the parcel + */ + public Region createFromParcel(Parcel p) { + int ni = nativeCreateFromParcel(p); + if (ni == 0) { + throw new RuntimeException(); + } + return new Region(ni); + } + public Region[] newArray(int size) { + return new Region[size]; + } + }; + + public int describeContents() { + return 0; + } + + /** + * Write the region and its pixels to the parcel. The region can be + * rebuilt from the parcel by calling CREATOR.createFromParcel(). + * @param p Parcel object to write the region data into + */ + public void writeToParcel(Parcel p, int flags) { + if (!nativeWriteToParcel(mNativeRegion, p)) { + throw new RuntimeException(); + } + } + + protected void finalize() throws Throwable { + nativeDestructor(mNativeRegion); + } + + /*package*/ Region(int ni) { + if (ni == 0) { + throw new RuntimeException(); + } + mNativeRegion = ni; + } + + /* add dummy parameter so constructor can be called from jni without + triggering 'not cloneable' exception */ + private Region(int ni, int dummy) { + this(ni); + } + + /*package*/ final int ni() { + return mNativeRegion; + } + + private static native int nativeConstructor(); + private static native void nativeDestructor(int native_region); + + private static native boolean nativeSetRegion(int native_dst, + int native_src); + private static native boolean nativeSetRect(int native_dst, int left, + int top, int right, int bottom); + private static native boolean nativeSetPath(int native_dst, int native_path, + int native_clip); + private static native boolean nativeGetBounds(int native_region, Rect rect); + private static native boolean nativeGetBoundaryPath(int native_region, + int native_path); + + private static native boolean nativeOp(int native_dst, int left, int top, + int right, int bottom, int op); + private static native boolean nativeOp(int native_dst, Rect rect, + int native_region, int op); + private static native boolean nativeOp(int native_dst, int native_region1, + int native_region2, int op); + + private static native int nativeCreateFromParcel(Parcel p); + private static native boolean nativeWriteToParcel(int native_region, + Parcel p); + + private final int mNativeRegion; +} diff --git a/graphics/java/android/graphics/RegionIterator.java b/graphics/java/android/graphics/RegionIterator.java new file mode 100644 index 0000000..817f853 --- /dev/null +++ b/graphics/java/android/graphics/RegionIterator.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006 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; + +public class RegionIterator { + + /** + * Construct an iterator for all of the rectangles in a region. This + * effectively makes a private copy of the region, so any subsequent edits + * to region will not affect the iterator. + * + * @param region the region that will be iterated + */ + public RegionIterator(Region region) { + mNativeIter = nativeConstructor(region.ni()); + } + + /** + * Return the next rectangle in the region. If there are no more rectangles + * this returns false and r is unchanged. If there is at least one more, + * this returns true and r is set to that rectangle. + */ + public final boolean next(Rect r) { + if (r == null) { + throw new NullPointerException("The Rect must be provided"); + } + return nativeNext(mNativeIter, r); + } + + protected void finalize() throws Throwable { + nativeDestructor(mNativeIter); + } + + private static native int nativeConstructor(int native_region); + private static native void nativeDestructor(int native_iter); + private static native boolean nativeNext(int native_iter, Rect r); + + private final int mNativeIter; +} + diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java new file mode 100644 index 0000000..ae0304e --- /dev/null +++ b/graphics/java/android/graphics/Shader.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2006 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; + +/** + * Shader is the based class for objects that return horizontal spans of colors + * during drawing. A subclass of Shader is installed in a Paint calling + * paint.setShader(shader). After that any object (other than a bitmap) that is + * drawn with that paint will get its color(s) from the shader. + */ +public class Shader { + + // this is set by subclasses, but don't make it public + /* package */ int native_instance; + + public enum TileMode { + /** + * replicate the edge color if the shader draws outside of its + * original bounds + */ + CLAMP (0), + /** + * repeat the shader's image horizontally and vertically + */ + REPEAT (1), + /** + * repeat the shader's image horizontally and vertically, alternating + * mirror images so that adjacent images always seam + */ + MIRROR (2); + + TileMode(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + /** + * Return true if the shader has a non-identity local matrix. + * @param localM If not null, it is set to the shader's local matrix. + * @return true if the shader has a non-identity local matrix + */ + public boolean getLocalMatrix(Matrix localM) { + return nativeGetLocalMatrix(native_instance, localM.native_instance); + } + + /** + * Set the shader's local matrix. Passing null will reset the shader's + * matrix to identity + * @param localM The shader's new local matrix, or null to specify identity + */ + public void setLocalMatrix(Matrix localM) { + nativeSetLocalMatrix(native_instance, + localM != null ? localM.native_instance : 0); + } + + protected void finalize() throws Throwable { + nativeDestructor(native_instance); + } + + private static native void nativeDestructor(int native_shader); + private static native boolean nativeGetLocalMatrix(int native_shader, + int matrix_instance); + private static native void nativeSetLocalMatrix(int native_shader, + int matrix_instance); +} diff --git a/graphics/java/android/graphics/SumPathEffect.java b/graphics/java/android/graphics/SumPathEffect.java new file mode 100644 index 0000000..cc7c778 --- /dev/null +++ b/graphics/java/android/graphics/SumPathEffect.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2007 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; + +public class SumPathEffect extends PathEffect { + + /** + * Construct a PathEffect whose effect is to apply two effects, in sequence. + * (e.g. first(path) + second(path)) + */ + public SumPathEffect(PathEffect first, PathEffect second) { + native_instance = nativeCreate(first.native_instance, + second.native_instance); + } + + private static native int nativeCreate(int first, int second); +} + diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java new file mode 100644 index 0000000..7456993 --- /dev/null +++ b/graphics/java/android/graphics/SweepGradient.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007 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; + +public class SweepGradient extends Shader { + + /** + * A subclass of Shader that draws a sweep gradient around a center point. + * + * @param cx The x-coordinate of the center + * @param cy The y-coordinate of the center + * @param colors The colors to be distributed between around the center. + * There must be at least 2 colors in the array. + * @param positions May be NULL. The relative position of + * each corresponding color in the colors array, beginning + * with 0 and ending with 1.0. If the values are not + * monotonic, the drawing may produce unexpected results. + * If positions is NULL, then the colors are automatically + * spaced evenly. + */ + public SweepGradient(float cx, float cy, + int colors[], float positions[]) { + if (colors.length < 2) { + throw new IllegalArgumentException("needs >= 2 number of colors"); + } + if (positions != null && colors.length != positions.length) { + throw new IllegalArgumentException( + "color and position arrays must be of equal length"); + } + native_instance = nativeCreate1(cx, cy, colors, positions); + } + + /** + * A subclass of Shader that draws a sweep gradient around a center point. + * + * @param cx The x-coordinate of the center + * @param cy The y-coordinate of the center + * @param color0 The color to use at the start of the sweep + * @param color1 The color to use at the end of the sweep + */ + public SweepGradient(float cx, float cy, int color0, int color1) { + native_instance = nativeCreate2(cx, cy, color0, color1); + } + + private static native int nativeCreate1(float x, float y, + int colors[], float positions[]); + private static native int nativeCreate2(float x, float y, + int color0, int color1); +} + diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java new file mode 100644 index 0000000..1d7fe01 --- /dev/null +++ b/graphics/java/android/graphics/TemporaryBuffer.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007 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.internal.util.ArrayUtils; + +/* package */ class TemporaryBuffer +{ + /* package */ static char[] obtain(int len) { + char[] buf; + + synchronized (TemporaryBuffer.class) { + buf = sTemp; + sTemp = null; + } + + if (buf == null || buf.length < len) + buf = new char[ArrayUtils.idealCharArraySize(len)]; + + return buf; + } + + /* package */ static void recycle(char[] temp) { + if (temp.length > 1000) + return; + + synchronized (TemporaryBuffer.class) { + sTemp = temp; + } + } + + private static char[] sTemp = null; +} diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java new file mode 100644 index 0000000..c69c92c --- /dev/null +++ b/graphics/java/android/graphics/Typeface.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2006 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 android.content.res.AssetManager; + +/** + * The Typeface class specifies the typeface and intrinsic style of a font. + * This is used in the paint, along with optionally Paint settings like + * textSize, textSkewX, textScaleX to specify + * how text appears when drawn (and measured). + */ +public class Typeface { + + /** The default NORMAL typeface object */ + public static final Typeface DEFAULT; + /** + * The default BOLD typeface object. Note: this may be not actually be + * bold, depending on what fonts are installed. Call getStyle() to know + * for sure. + */ + public static final Typeface DEFAULT_BOLD; + /** The NORMAL style of the default sans serif typeface. */ + public static final Typeface SANS_SERIF; + /** The NORMAL style of the default serif typeface. */ + public static final Typeface SERIF; + /** The NORMAL style of the default monospace typeface. */ + public static final Typeface MONOSPACE; + + private static Typeface[] sDefaults; + + /* package */ int native_instance; + + // Style + public static final int NORMAL = 0; + public static final int BOLD = 1; + public static final int ITALIC = 2; + public static final int BOLD_ITALIC = 3; + + /** Returns the typeface's intrinsic style attributes */ + public int getStyle() { + return nativeGetStyle(native_instance); + } + + /** Returns true if getStyle() has the BOLD bit set. */ + public final boolean isBold() { + return (getStyle() & BOLD) != 0; + } + + /** Returns true if getStyle() has the ITALIC bit set. */ + public final boolean isItalic() { + return (getStyle() & ITALIC) != 0; + } + + /** + * Create a typeface object given a family name, and option style information. + * If null is passed for the name, then the "default" font will be chosen. + * The resulting typeface object can be queried (getStyle()) to discover what + * its "real" style characteristics are. + * + * @param familyName May be null. The name of the font family. + * @param style The style (normal, bold, italic) of the typeface. + * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC + * @return The best matching typeface. + */ + public static Typeface create(String familyName, int style) { + return new Typeface(nativeCreate(familyName, style)); + } + + /** + * Create a typeface object that best matches the specified existing + * typeface and the specified Style. Use this call if you want to pick a new + * style from the same family of an existing typeface object. If family is + * null, this selects from the default font's family. + * + * @param family May be null. The name of the existing type face. + * @param style The style (normal, bold, italic) of the typeface. + * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC + * @return The best matching typeface. + */ + public static Typeface create(Typeface family, int style) { + int ni = 0; + if (family != null) { + ni = family.native_instance; + } + return new Typeface(nativeCreateFromTypeface(ni, style)); + } + + /** + * Returns one of the default typeface objects, based on the specified style + * + * @return the default typeface that corresponds to the style + */ + public static Typeface defaultFromStyle(int style) { + return sDefaults[style]; + } + + /** + * Create a new typeface from the specified font data. + * @param mgr The application's asset manager + * @param path The file name of the font data in the assets directory + * @return The new typeface. + */ + public static Typeface createFromAsset(AssetManager mgr, String path) { + return new Typeface(nativeCreateFromAsset(mgr, path)); + } + + // don't allow clients to call this directly + private Typeface(int ni) { + native_instance = ni; + } + + static { + DEFAULT = create((String)null, 0); + DEFAULT_BOLD = create((String)null, Typeface.BOLD); + SANS_SERIF = create("sans-serif", 0); + SERIF = create("serif", 0); + MONOSPACE = create("monospace", 0); + + sDefaults = new Typeface[] { + DEFAULT, + DEFAULT_BOLD, + create((String)null, Typeface.ITALIC), + create((String)null, Typeface.BOLD_ITALIC), + }; + } + + protected void finalize() throws Throwable { + nativeUnref(native_instance); + } + + private static native int nativeCreate(String familyName, int style); + private static native int nativeCreateFromTypeface(int native_instance, + int style); + private static native void nativeUnref(int native_instance); + private static native int nativeGetStyle(int native_instance); + private static native int nativeCreateFromAsset(AssetManager mgr, + String path); +} diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java new file mode 100644 index 0000000..42c410e --- /dev/null +++ b/graphics/java/android/graphics/Xfermode.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006 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. + */ + +// This file was generated from the C++ include file: SkXfermode.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +package android.graphics; + +/** + * Xfermode is the base class for objects that are called to implement custom + * "transfer-modes" in the drawing pipeline. The static function Create(Modes) + * can be called to return an instance of any of the predefined subclasses as + * specified in the Modes enum. When an Xfermode is assigned to an Paint, then + * objects drawn with that paint have the xfermode applied. + */ +public class Xfermode { + + protected void finalize() throws Throwable { + finalizer(native_instance); + } + + private static native void finalizer(int native_instance); + + int native_instance; +} diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java new file mode 100644 index 0000000..26e50cd --- /dev/null +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2006 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.drawable; + +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.os.SystemClock; +import android.util.AttributeSet; + +/** + * + * An object used to define frame-by-frame animations that can be used as a View object's + * background. + * <p>Each frame in a frame-by-frame animation is a drawable + * <a href="{@docRoot}devel/resources-i18n.html">resource</a>. + * The simplest way to create a frame-by-frame animation is to define the animation in an XML + * file in the drawable/ folder, set it as the background to a View object, then call + * AnimationDrawable.run() to start the animation, as shown here. More details about the + * format of the animation XML file are given in + * <a href="{@docRoot}reference/available-resources.html#animationdrawable">Frame by Frame + * Animation</a>. + * spin_animation.xml file in res/drawable/ folder:</p> + * <pre><!-- Animation frames are wheel0.png -- wheel5.png files inside the + * res/drawable/ folder --> + * <animation-list android:id="selected" android:oneshot="false"> + * <item android:drawable="@drawable/wheel0" android:duration="50" /> + * <item android:drawable="@drawable/wheel1" android:duration="50" /> + * <item android:drawable="@drawable/wheel2" android:duration="50" /> + * <item android:drawable="@drawable/wheel3" android:duration="50" /> + * <item android:drawable="@drawable/wheel4" android:duration="50" /> + * <item android:drawable="@drawable/wheel5" android:duration="50" /> + * </animation-list></pre> + * + * <p>Here is the code to load and play this animation.</p> + * <pre>// Load the ImageView that will host the animation and + * // set its background to our AnimationDrawable XML resource. + * ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image); + * img.setBackgroundResource(R.drawable.spin_animation); + * + * // Get the background, which has been compiled to an AnimationDrawable object. + * AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground(); + * + * // Start the animation (looped playback by default). + * frameAnimation.start() + * </pre> + */ +public class AnimationDrawable extends DrawableContainer implements Runnable { + public AnimationDrawable() { + this(null); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (visible) { + if (changed || restart) { + setFrame(0, true, true); + } + } else { + unscheduleSelf(this); + } + return changed; + } + + /** + * <p>Starts the animation, looping if necessary. This method has no effect + * if the animation is running.</p> + * + * @see #isRunning() + * @see #stop() + */ + public void start() { + if (!isRunning()) { + run(); + } + } + + /** + * <p>Stops the animation. This method has no effect if the animation is + * not running.</p> + * + * @see #isRunning() + * @see #start() + */ + public void stop() { + if (isRunning()) { + unscheduleSelf(this); + } + } + + /** + * <p>Indicates whether the animation is currently running or not.</p> + * + * @return true if the animation is running, false otherwise + */ + public boolean isRunning() { + return mCurFrame > -1; + } + + /** + * <p>This method exists for implementation purpose only and should not be + * called directly. Invoke {@link #start()} instead.</p> + * + * @see #start() + */ + public void run() { + nextFrame(false); + } + + @Override + public void unscheduleSelf(Runnable what) { + mCurFrame = -1; + super.unscheduleSelf(what); + } + + /** + * @return The number of frames in the animation + */ + public int getNumberOfFrames() { + return mAnimationState.getChildCount(); + } + + /** + * @return The Drawable at the specified frame index + */ + public Drawable getFrame(int index) { + return mAnimationState.getChildren()[index]; + } + + /** + * @return The duration in milliseconds of the frame at the + * specified index + */ + public int getDuration(int i) { + return mAnimationState.mDurations[i]; + } + + /** + * @return True of the animation will play once, false otherwise + */ + public boolean isOneShot() { + return mAnimationState.mOneShot; + } + + /** + * Sets whether the animation should play once or repeat. + * + * @param oneShot Pass true if the animation should only play once + */ + public void setOneShot(boolean oneShot) { + mAnimationState.mOneShot = oneShot; + } + + /** + * Add a frame to the animation + * + * @param frame The frame to add + * @param duration How long in milliseconds the frame should appear + */ + public void addFrame(Drawable frame, int duration) { + mAnimationState.addFrame(frame, duration); + } + + private void nextFrame(boolean unschedule) { + int next = mCurFrame+1; + final int N = mAnimationState.getChildCount(); + if (next >= N) { + next = 0; + } + setFrame(next, unschedule, !mAnimationState.mOneShot || next < (N-1)); + } + + private void setFrame(int frame, boolean unschedule, boolean animate) { + if (frame >= mAnimationState.getChildCount()) { + return; + } + mCurFrame = frame; + selectDrawable(frame); + if (unschedule) { + unscheduleSelf(this); + } + if (animate) { + scheduleSelf(this, SystemClock.uptimeMillis() + + mAnimationState.mDurations[frame]); + } + } + + @Override + public void inflate(Resources r, XmlPullParser parser, + AttributeSet attrs) + throws XmlPullParserException, IOException { + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.AnimationDrawable); + + super.inflateWithAttributes(r, parser, a, + com.android.internal.R.styleable.AnimationDrawable_visible); + + mAnimationState.setVariablePadding(a.getBoolean( + com.android.internal.R.styleable.AnimationDrawable_variablePadding, false)); + + mAnimationState.mOneShot = a.getBoolean( + com.android.internal.R.styleable.AnimationDrawable_oneshot, false); + + a.recycle(); + + int type; + + final int innerDepth = parser.getDepth()+1; + int depth; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth=parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth || !parser.getName().equals("item")) { + continue; + } + + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.AnimationDrawableItem); + + int duration = a.getInt( + com.android.internal.R.styleable.AnimationDrawableItem_duration, -1); + if (duration < 0) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": <item> tag requires a 'duration' attribute"); + } + int drawableRes = a.getResourceId( + com.android.internal.R.styleable.AnimationDrawableItem_drawable, 0); + + a.recycle(); + + Drawable dr; + if (drawableRes != 0) { + dr = r.getDrawable(drawableRes); + } else { + while ((type=parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": <item> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + dr = Drawable.createFromXmlInner(r, parser, attrs); + } + + mAnimationState.addFrame(dr, duration); + if (dr != null) { + dr.setCallback(this); + } + } + + setFrame(0, true, false); + } + + private final static class AnimationState extends DrawableContainerState + { + AnimationState(AnimationState orig, AnimationDrawable owner) + { + super(orig, owner); + + if (orig != null) { + mDurations = orig.mDurations; + mOneShot = orig.mOneShot; + } else { + mDurations = new int[getChildren().length]; + mOneShot = true; + } + } + + @Override + public Drawable newDrawable() + { + return new AnimationDrawable(this); + } + + public void addFrame(Drawable dr, int dur) + { + int pos = super.addChild(dr); + mDurations[pos] = dur; + } + + @Override + public void growArray(int oldSize, int newSize) + { + super.growArray(oldSize, newSize); + int[] newDurations = new int[newSize]; + System.arraycopy(mDurations, 0, newDurations, 0, oldSize); + mDurations = newDurations; + } + + private int[] mDurations; + private boolean mOneShot; + } + + private AnimationDrawable(AnimationState state) { + AnimationState as = new AnimationState(state, this); + mAnimationState = as; + setConstantState(as); + if (state != null) { + setFrame(0, true, false); + } + } + + private final AnimationState mAnimationState; + + private int mCurFrame = -1; +} + diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java new file mode 100644 index 0000000..97b44ba --- /dev/null +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2006 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.drawable; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.BitmapShader; +import android.util.AttributeSet; +import android.view.Gravity; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class BitmapDrawable extends Drawable { + + private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG; + private BitmapState mBitmapState; + private Bitmap mBitmap; + private final Rect mDstRect = new Rect(); // Gravity.apply() sets this + + private boolean mApplyGravity; + private boolean mRebuildShader; + + public BitmapDrawable() { + mBitmapState = new BitmapState(null); + } + + public BitmapDrawable(Bitmap bitmap) { + this(new BitmapState(bitmap)); + } + + public BitmapDrawable(String filepath) { + this(new BitmapState(BitmapFactory.decodeFile(filepath))); + if (mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + } + } + + public BitmapDrawable(java.io.InputStream is) { + this(new BitmapState(BitmapFactory.decodeStream(is))); + if (mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + } + } + + public final Paint getPaint() { + return mBitmapState.mPaint; + } + + public final Bitmap getBitmap() { + return mBitmap; + } + + /** Get the gravity used to position/stretch the bitmap within its bounds. + See android.view.Gravity + * @return the gravity applied to the bitmap + */ + public int getGravity() { + return mBitmapState.mGravity; + } + + /** Set the gravity used to position/stretch the bitmap within its bounds. + See android.view.Gravity + * @param gravity the gravity + */ + public void setGravity(int gravity) { + mBitmapState.mGravity = gravity; + mApplyGravity = true; + } + + public void setAntiAlias(boolean aa) { + mBitmapState.mPaint.setAntiAlias(aa); + } + + @Override + public void setFilterBitmap(boolean filter) { + mBitmapState.mPaint.setFilterBitmap(filter); + } + + @Override + public void setDither(boolean dither) { + mBitmapState.mPaint.setDither(dither); + } + + public Shader.TileMode getTileModeX() { + return mBitmapState.mTileModeX; + } + + public Shader.TileMode getTileModeY() { + return mBitmapState.mTileModeY; + } + + public void setTileModeX(Shader.TileMode mode) { + setTileModeXY(mode, mBitmapState.mTileModeY); + } + + public final void setTileModeY(Shader.TileMode mode) { + setTileModeXY(mBitmapState.mTileModeX, mode); + } + + public void setTileModeXY(Shader.TileMode xmode, Shader.TileMode ymode) { + final BitmapState state = mBitmapState; + if (state.mTileModeX != xmode || state.mTileModeY != ymode) { + state.mTileModeX = xmode; + state.mTileModeY = ymode; + mRebuildShader = true; + } + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() | mBitmapState.mChangingConfigurations; + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + mApplyGravity = true; + } + + @Override + public void draw(Canvas canvas) { + Bitmap bitmap = mBitmap; + if (bitmap != null) { + final BitmapState state = mBitmapState; + if (mRebuildShader) { + Shader.TileMode tmx = state.mTileModeX; + Shader.TileMode tmy = state.mTileModeY; + + if (tmx == null && tmy == null) { + state.mPaint.setShader(null); + } else { + Shader s = new BitmapShader(bitmap, + tmx == null ? Shader.TileMode.CLAMP : tmx, + tmy == null ? Shader.TileMode.CLAMP : tmy); + state.mPaint.setShader(s); + } + mRebuildShader = false; + copyBounds(mDstRect); + } + + Shader shader = state.mPaint.getShader(); + if (shader == null) { + if (mApplyGravity) { + Gravity.apply(state.mGravity, bitmap.getWidth(), bitmap.getHeight(), + getBounds(), mDstRect); + mApplyGravity = false; + } + canvas.drawBitmap(bitmap, null, mDstRect, state.mPaint); + } else { + if (mApplyGravity) { + mDstRect.set(getBounds()); + mApplyGravity = false; + } + canvas.drawRect(mDstRect, state.mPaint); + } + } + } + + @Override + public void setAlpha(int alpha) { + mBitmapState.mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mBitmapState.mPaint.setColorFilter(cf); + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); + + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.BitmapDrawable); + + final int id = a.getResourceId(com.android.internal.R.styleable.BitmapDrawable_src, 0); + if (id == 0) { + throw new XmlPullParserException(parser.getPositionDescription() + + ": <bitmap> requires a valid src attribute"); + } + final Bitmap bitmap = BitmapFactory.decodeResource(r, id); + if (bitmap == null) { + throw new XmlPullParserException(parser.getPositionDescription() + + ": <bitmap> requires a valid src attribute"); + } + mBitmapState.mBitmap = mBitmap = bitmap; + + final Paint paint = mBitmapState.mPaint; + paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias, + paint.isAntiAlias())); + paint.setFilterBitmap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_filter, + paint.isFilterBitmap())); + paint.setDither(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_dither, + paint.isDither())); + setGravity(a.getInt(com.android.internal.R.styleable.BitmapDrawable_gravity, Gravity.FILL)); + int tileMode = a.getInt(com.android.internal.R.styleable.BitmapDrawable_tileMode, -1); + if (tileMode != -1) { + switch (tileMode) { + case 0: + setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + break; + case 1: + setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + break; + case 2: + setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR); + break; + } + } + + a.recycle(); + } + + @Override + public int getIntrinsicWidth() { + Bitmap bitmap = mBitmap; + return bitmap != null ? bitmap.getWidth() : -1; + } + + @Override + public int getIntrinsicHeight() { + Bitmap bitmap = mBitmap; + return bitmap != null ? bitmap.getHeight() : -1; + } + + @Override + public int getOpacity() { + if (mBitmapState.mGravity != Gravity.FILL) { + return PixelFormat.TRANSLUCENT; + } + Bitmap bm = mBitmap; + return (bm == null || bm.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255) ? + PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; + } + + @Override + public final ConstantState getConstantState() { + mBitmapState.mChangingConfigurations = super.getChangingConfigurations(); + return mBitmapState; + } + + final static class BitmapState extends ConstantState { + Bitmap mBitmap; + int mChangingConfigurations; + int mGravity = Gravity.FILL; + Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS); + Shader.TileMode mTileModeX; + Shader.TileMode mTileModeY; + + BitmapState(Bitmap bitmap) { + mBitmap = bitmap; + } + + @Override + public Drawable newDrawable() { + return new BitmapDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + } + + private BitmapDrawable(BitmapState state) { + mBitmapState = state; + mBitmap = state.mBitmap; + } +} diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java new file mode 100644 index 0000000..86c0747 --- /dev/null +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2006 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.drawable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.*; +import android.view.Gravity; +import android.util.AttributeSet; +import android.util.Log; + +import java.io.IOException; + +/** + * A drawable that clips another drawable based on this drawable's current + * level value. You can control how much the child drawable gets clipped in width + * and height based on the level, as well as a gravity to control where it is + * placed in its overall container. Most often used to implement things like + * progress bars. + */ +public class ClipDrawable extends Drawable implements Drawable.Callback { + private ClipState mClipState; + private final Rect mTmpRect = new Rect(); + + public static final int HORIZONTAL = 1; + public static final int VERTICAL = 2; + + ClipDrawable() { + this(null); + } + + /** + * @param orientation Bitwise-or of {@link #HORIZONTAL} and/or {@link #VERTICAL} + */ + public ClipDrawable(Drawable drawable, int gravity, int orientation) { + this(null); + + mClipState.mDrawable = drawable; + mClipState.mGravity = gravity; + mClipState.mOrientation = orientation; + + if (drawable != null) { + drawable.setCallback(this); + } + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); + + int type; + + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ClipDrawable); + + int orientation = a.getInt( + com.android.internal.R.styleable.ClipDrawable_clipOrientation, + HORIZONTAL); + int g = a.getInt(com.android.internal.R.styleable.ClipDrawable_gravity, Gravity.LEFT); + Drawable dr = a.getDrawable(com.android.internal.R.styleable.ClipDrawable_drawable); + + a.recycle(); + + final int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + dr = Drawable.createFromXmlInner(r, parser, attrs); + } + + if (dr == null) { + throw new IllegalArgumentException("No drawable specified for <clip>"); + } + + mClipState.mDrawable = dr; + mClipState.mOrientation = orientation; + mClipState.mGravity = g; + if (dr != null) { + dr.setCallback(this); + } + } + + // overrides from Drawable.Callback + + public void invalidateDrawable(Drawable who) { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + // overrides from Drawable + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mClipState.mChangingConfigurations + | mClipState.mDrawable.getChangingConfigurations(); + } + + @Override + public boolean getPadding(Rect padding) { + // XXX need to adjust padding! + return mClipState.mDrawable.getPadding(padding); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + mClipState.mDrawable.setVisible(visible, restart); + return super.setVisible(visible, restart); + } + + @Override + public void setAlpha(int alpha) { + mClipState.mDrawable.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mClipState.mDrawable.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return mClipState.mDrawable.getOpacity(); + } + + @Override + public boolean isStateful() { + return mClipState.mDrawable.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + boolean changed = mClipState.mDrawable.setState(state); + return changed; + } + + @Override + protected boolean onLevelChange(int level) { + mClipState.mDrawable.setLevel(level); + invalidateSelf(); + return true; + } + + @Override + protected void onBoundsChange(Rect bounds) { + mClipState.mDrawable.setBounds(bounds); + } + + @Override + public void draw(Canvas canvas) { + + if (mClipState.mDrawable.getLevel() == 0) { + return; + } + + final Rect r = mTmpRect; + final Rect bounds = getBounds(); + int level = getLevel(); + int w = bounds.width(); + final int iw = 0; //mClipState.mDrawable.getIntrinsicWidth(); + if ((mClipState.mOrientation & HORIZONTAL) != 0) { + w -= (w - iw) * (10000 - level) / 10000; + } + int h = bounds.height(); + final int ih = 0; //mClipState.mDrawable.getIntrinsicHeight(); + if ((mClipState.mOrientation & VERTICAL) != 0) { + h -= (h - ih) * (10000 - level) / 10000; + } + Gravity.apply(mClipState.mGravity, w, h, bounds, r); + + if (w > 0 && h > 0) { + canvas.save(); + canvas.clipRect(r); + mClipState.mDrawable.draw(canvas); + canvas.restore(); + } + } + + @Override + public int getIntrinsicWidth() { + return mClipState.mDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mClipState.mDrawable.getIntrinsicHeight(); + } + + @Override + public ConstantState getConstantState() { + if (mClipState.canConstantState()) { + mClipState.mChangingConfigurations = super.getChangingConfigurations(); + return mClipState; + } + return null; + } + + final static class ClipState extends ConstantState { + ClipState(ClipState orig, ClipDrawable owner) { + if (orig != null) { + mDrawable = orig.mDrawable.getConstantState().newDrawable(); + mDrawable.setCallback(owner); + mOrientation = orig.mOrientation; + mGravity = orig.mGravity; + mCheckedConstantState = mCanConstantState = true; + } + } + + @Override + public Drawable newDrawable() { + return new ClipDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = mDrawable.getConstantState() != null; + mCheckedConstantState = true; + } + + return mCanConstantState; + } + + Drawable mDrawable; + int mChangingConfigurations; + int mOrientation; + int mGravity; + + private boolean mCheckedConstantState; + private boolean mCanConstantState; + } + + private ClipDrawable(ClipState state) { + mClipState = new ClipState(state, this); + } +} + diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java new file mode 100644 index 0000000..4f4eda6 --- /dev/null +++ b/graphics/java/android/graphics/drawable/ColorDrawable.java @@ -0,0 +1,150 @@ +/* + * 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.drawable; + +import android.graphics.*; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * A ColorDrawable is a specialized drawable that fills the Canvas with a specified color, + * and with respect to the clip region. Note that a ColorDrawable ignores the ColorFilter. + * It also ignores the Bounds, meaning it will draw everywhere in the current clip, + * even if setBounds(...) was called with a smaller area. + */ +public class ColorDrawable extends Drawable { + private ColorState mState; + + /** + * Creates a new black ColorDrawable. + */ + public ColorDrawable() { + this(null); + } + + /** + * Creates a new ColorDrawable with the specified color. + * + * @param color The color to draw. + */ + public ColorDrawable(int color) { + this(null); + mState.mBaseColor = mState.mUseColor = color; + } + + private ColorDrawable(ColorState state) { + mState = new ColorState(state); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mState.mChangingConfigurations; + } + + @Override + public void draw(Canvas canvas) { + canvas.drawColor(mState.mUseColor); + } + + /** + * Returns the alpha value of this drawable's color. + * + * @return A value between 0 and 255. + */ + public int getAlpha() { + return mState.mUseColor >>> 24; + } + + /** + * Sets the color's alpha value. + * + * @param alpha The alpha value to set, between 0 and 255. + */ + public void setAlpha(int alpha) { + alpha += alpha >> 7; // make it 0..256 + int baseAlpha = mState.mBaseColor >>> 24; + int useAlpha = baseAlpha * alpha >> 8; + mState.mUseColor = (mState.mBaseColor << 8 >>> 8) | (useAlpha << 24); + } + + /** + * Setting a color filter on a ColorDrawable has no effect. + * + * @param colorFilter Ignore. + */ + public void setColorFilter(ColorFilter colorFilter) { + } + + public int getOpacity() { + switch (mState.mUseColor >>> 24) { + case 255: + return PixelFormat.OPAQUE; + case 0: + return PixelFormat.TRANSPARENT; + } + return PixelFormat.TRANSLUCENT; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); + + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ColorDrawable); + + int color = mState.mBaseColor; + color = a.getColor(com.android.internal.R.styleable.ColorDrawable_color, color); + mState.mBaseColor = mState.mUseColor = color; + + a.recycle(); + } + + @Override + public ConstantState getConstantState() { + mState.mChangingConfigurations = super.getChangingConfigurations(); + return mState; + } + + final static class ColorState extends ConstantState { + int mBaseColor; // initial color. never changes + int mUseColor; // basecolor modulated by setAlpha() + int mChangingConfigurations; + + ColorState(ColorState state) { + if (state != null) { + mBaseColor = state.mBaseColor; + mUseColor = state.mUseColor; + } + } + + @Override + public Drawable newDrawable() { + return new ColorDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + } +} diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java new file mode 100644 index 0000000..0021241 --- /dev/null +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2006 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.drawable; + +import java.io.InputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.*; +import android.util.AttributeSet; +import android.util.StateSet; +import android.util.Xml; + +/** + * A Drawable is a general abstraction for "something that can be drawn." Most + * often you will deal with Drawable as the type of resource retrieved for + * drawing things to the screen; the Drawable class provides a generic API for + * dealing with an underlying visual resource that may take a variety of forms. + * Unlike a {@link android.view.View}, a Drawable does not have any facility to + * receive events or otherwise interact with the user. + * + * <p>In addition to simple drawing, Drawable provides a number of generic + * mechanisms for its client to interact with what is being drawn: + * + * <ul> + * <li> The {@link #setBounds} method <var>must</var> be called to tell the + * Drawable where it is drawn and how large it should be. All Drawables + * should respect the requested size, often simply by scaling their + * imagery. A client can find the preferred size for some Drawables with + * the {@link #getIntrinsicHeight} and {@link #getIntrinsicWidth} methods. + * + * <li> The {@link #getPadding} method can return from some Drawables + * information about how to frame content that is placed inside of them. + * For example, a Drawable that is intended to be the frame for a button + * widget would need to return padding that correctly places the label + * inside of itself. + * + * <li> The {@link #setState} method allows the client to tell the Drawable + * in which state it is to be drawn, such as "focused", "selected", etc. + * Some drawables may modify their imagery based on the selected state. + * + * <li> The {@link #setLevel} method allows the client to supply a single + * continuous controller that can modify the Drawable is displayed, such as + * a battery level or progress level. Some drawables may modify their + * imagery based on the current level. + * + * <li> A Drawable can perform animations by calling back to its client + * through the {@link Callback} interface. All clients should support this + * interface (via {@link #setCallback}) so that animations will work. A + * simple way to do this is through the system facilities such as + * {@link android.view.View#setBackgroundDrawable(Drawable)} and + * {@link android.widget.ImageView}. + * </ul> + * + * Though usually not visible to the application, Drawables may take a variety + * of forms: + * + * <ul> + * <li> <b>Bitmap</b>: the simplest Drawable, a PNG or JPEG image. + * <li> <b>Nine Patch</b>: an extension to the PNG format allows it to + * specify information about how to stretch it and place things inside of + * it. + * <li> <b>Shape</b>: contains simple drawing commands instead of a raw + * bitmap, allowing it to resize better in some cases. + * <li> <b>Layers</b>: a compound drawable, which draws multiple underlying + * drawables on top of each other. + * <li> <b>States</b>: a compound drawable that selects one of a set of + * drawables based on its state. + * <li> <b>Levels</b>: a compound drawable that selects one of a set of + * drawables based on its level. + * <li> <b>Scale</b>: a compound drawable with a single child drawable, + * whose overall size is modified based on the current level. + * </ul> + * <p>For information and examples of creating drawable resources (XML or bitmap files that + * can be loaded in code), see <a href="{@docRoot}devel/resources-i18n.html">Resources + * and Internationalization</a>. + */ +public abstract class Drawable { + + private int[] mStateSet = StateSet.WILD_CARD; + private int mLevel = 0; + private int mChangingConfigurations = 0; + private Rect mBounds = new Rect(); + /*package*/ Callback mCallback = null; + private boolean mVisible = true; + + /** + * Draw in its bounds (set via setBounds) respecting optional effects such + * as alpha (set via setAlpha) and color filter (set via setColorFilter). + * + * @param canvas The canvas to draw into + */ + public abstract void draw(Canvas canvas); + + /** + * Specify a bounding rectangle for the Drawable. This is where the drawable + * will draw when its draw() method is called. + */ + public void setBounds(int left, int top, int right, int bottom) { + Rect oldBounds = mBounds; + + if (oldBounds.left != left || oldBounds.top != top || + oldBounds.right != right || oldBounds.bottom != bottom) { + mBounds.set(left, top, right, bottom); + onBoundsChange(mBounds); + } + } + + /** + * Specify a bounding rectangle for the Drawable. This is where the drawable + * will draw when its draw() method is called. + */ + public void setBounds(Rect bounds) { + setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + /** + * Return a copy of the drawable's bounds in the specified Rect (allocated + * by the caller). The bounds specify where this will draw when its draw() + * method is called. + * + * @param bounds Rect to receive the drawable's bounds (allocated by the + * caller). + */ + public final void copyBounds(Rect bounds) { + bounds.set(mBounds); + } + + /** + * Return a copy of the drawable's bounds in a new Rect. This returns the + * same values as getBounds(), but the returned object is guaranteed to not + * be changed later by the drawable (i.e. it retains no reference to this + * rect). If the caller already has a Rect allocated, call copyBounds(rect) + * + * @return A copy of the drawable's bounds + */ + public final Rect copyBounds() { + return new Rect(mBounds); + } + + /** + * Return the drawable's bounds Rect. Note: for efficiency, the returned + * object may be the same object stored in the drawable (though this is not + * guaranteed), so if a persistent copy of the bounds is needed, call + * copyBounds(rect) instead. + * + * @return The bounds of the drawable (which may change later, so caller + * beware). + */ + public final Rect getBounds() { + return mBounds; + } + + /** + * Set a mask of the configuration parameters for which this drawable + * may change, requiring that it be re-created. + * + * @param configs A mask of the changing configuration parameters, as + * defined by {@link android.content.res.Configuration}. + * + * @see android.content.res.Configuration + */ + public void setChangingConfigurations(int configs) { + mChangingConfigurations = configs; + } + + /** + * Return a mask of the configuration parameters for which this drawable + * mau change, requiring that it be re-created. The default implementation + * returns whatever was provided through + * {@link #setChangingConfigurations(int)} or 0 by default. Subclasses + * may extend this to or in the changing configurations of any other + * drawables they hold. + * + * @return Returns a mask of the changing configuration parameters, as + * defined by {@link android.content.res.Configuration}. + * + * @see android.content.res.Configuration + */ + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + /** + * Set to true to have the drawable dither its colors when drawn to a device + * with fewer than 8-bits per color component. This can improve the look on + * those devices, but can also slow down the drawing a little. + */ + public void setDither(boolean dither) {} + + /** + * Set to true to have the drawable filter its bitmap when scaled or rotated + * (for drawables that use bitmaps). If the drawable does not use bitmaps, + * this call is ignored. This can improve the look when scaled or rotated, + * but also slows down the drawing. + */ + public void setFilterBitmap(boolean filter) {} + + /** + * Implement this interface if you want to create an animated drawable that + * extends {@link android.graphics.drawable.Drawable Drawable}. + * Upon retrieving a drawable, use + * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)} + * to supply your implementation of the interface to the drawable; it uses + * this interface to schedule and execute animation changes. + */ + public static interface Callback { + /** + * Called when the drawable needs to be redrawn. A view at this point + * should invalidate itself (or at least the part of itself where the + * drawable appears). + * + * @param who The drawable that is requesting the update. + */ + public void invalidateDrawable(Drawable who); + + /** + * A Drawable can call this to schedule the next frame of its + * animation. An implementation can generally simply call + * {@link android.os.Handler#postAtTime(Runnable, Object, long)} with + * the parameters <var>(what, who, when)</var> to perform the + * scheduling. + * + * @param who The drawable being scheduled. + * @param what The action to execute. + * @param when The time (in milliseconds) to run. The timebase is + * {@link android.os.SystemClock#uptimeMillis} + */ + public void scheduleDrawable(Drawable who, Runnable what, long when); + + /** + * A Drawable can call this to unschedule an action previously + * scheduled with {@link #scheduleDrawable}. An implementation can + * generally simply call + * {@link android.os.Handler#removeCallbacks(Runnable, Object)} with + * the parameters <var>(what, who)</var> to unschedule the drawable. + * + * @param who The drawable being unscheduled. + * @param what The action being unscheduled. + */ + public void unscheduleDrawable(Drawable who, Runnable what); + } + + /** + * Bind a {@link Callback} object to this Drawable. Required for clients + * that want to support animated drawables. + * + * @param cb The client's Callback implementation. + */ + public final void setCallback(Callback cb) { + mCallback = cb; + } + + /** + * Use the current {@link Callback} implementation to have this Drawable + * redrawn. Does nothing if there is no Callback attached to the + * Drawable. + * + * @see Callback#invalidateDrawable + */ + public void invalidateSelf() + { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + /** + * Use the current {@link Callback} implementation to have this Drawable + * scheduled. Does nothing if there is no Callback attached to the + * Drawable. + * + * @param what The action being scheduled. + * @param when The time (in milliseconds) to run. + * + * @see Callback#scheduleDrawable + */ + public void scheduleSelf(Runnable what, long when) + { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + /** + * Use the current {@link Callback} implementation to have this Drawable + * unscheduled. Does nothing if there is no Callback attached to the + * Drawable. + * + * @param what The runnable that you no longer want called. + * + * @see Callback#unscheduleDrawable + */ + public void unscheduleSelf(Runnable what) + { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + /** + * Specify an alpha value for the drawable. 0 means fully transparent, and + * 255 means fully opaque. + */ + public abstract void setAlpha(int alpha); + + /** + * Specify an optional colorFilter for the drawable. Pass null to remove + * any filters. + */ + public abstract void setColorFilter(ColorFilter cf); + + /** + * Specify a color and porterduff mode to be the colorfilter for this + * drawable. + */ + public void setColorFilter(int color, PorterDuff.Mode mode) { + setColorFilter(new PorterDuffColorFilter(color, mode)); + } + + public void clearColorFilter() { + setColorFilter(null); + } + + /** + * Indicates whether this view will change its appearance based on state. + * Clients can use this to determine whether it is necessary to calculate + * their state and call setState. + * + * @return True if this view changes its appearance based on state, false + * otherwise. + * + * @see #setState(int[]) + */ + public boolean isStateful() { + return false; + } + + /** + * Specify a set of states for the drawable. These are use-case specific, + * so see the relevant documentation. As an example, the background for + * widgets like Button understand the following states: + * [{@link android.R.attr#state_focused}, + * {@link android.R.attr#state_pressed}]. + * + * <p>If the new state you are supplying causes the appearance of the + * Drawable to change, then it is responsible for calling + * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em> + * true will be returned from this function. + * + * <p>Note: The Drawable holds a reference on to <var>stateSet</var> + * until a new state array is given to it, so you must not modify this + * array during that time.</p> + * + * @param stateSet The new set of states to be displayed. + * + * @return Returns true if this change in state has caused the appearance + * of the Drawable to change (hence requiring an invalidate), otherwise + * returns false. + */ + public boolean setState(final int[] stateSet) { + if (!Arrays.equals(mStateSet, stateSet)) { + mStateSet = stateSet; + return onStateChange(stateSet); + } + return false; + } + + /** + * Describes the current state, as a union of primitve states, such as + * {@link android.R.attr#state_focused}, + * {@link android.R.attr#state_selected}, etc. + * Some drawables may modify their imagery based on the selected state. + * @return An array of resource Ids describing the current state. + */ + public int[] getState() { + return mStateSet; + } + + /** + * @return The current drawable that will be used by this drawable. For simple drawables, this + * is just the drawable itself. For drawables that change state like + * {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable + * currently in use. + */ + public Drawable getCurrent() { + return this; + } + + /** + * Specify the level for the drawable. This allows a drawable to vary its + * imagery based on a continuous controller, for example to show progress + * or volume level. + * + * <p>If the new level you are supplying causes the appearance of the + * Drawable to change, then it is responsible for calling + * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em> + * true will be returned from this function. + * + * @param level The new level, from 0 (minimum) to 10000 (maximum). + * + * @return Returns true if this change in level has caused the appearance + * of the Drawable to change (hence requiring an invalidate), otherwise + * returns false. + */ + public final boolean setLevel(int level) { + if (mLevel != level) { + mLevel = level; + return onLevelChange(level); + } + return false; + } + + /** + * Retrieve the current level. + * + * @return int Current level, from 0 (minimum) to 10000 (maximum). + */ + public final int getLevel() { + return mLevel; + } + + /** + * Set whether this Drawable is visible. This generally does not impact + * the Drawable's behavior, but is a hint that can be used by some + * Drawables, for example, to decide whether run animations. + * + * @param visible Set to true if visible, false if not. + * @param restart You can supply true here to force the drawable to behave + * as if it has just become visible, even if it had last + * been set visible. Used for example to force animations + * to restart. + * + * @return boolean Returns true if the new visibility is different than + * its previous state. + */ + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = mVisible != visible; + mVisible = visible; + return changed; + } + + public final boolean isVisible() { + return mVisible; + } + + /** + * Return the opacity/transparency of this Drawable. The returned value is + * one of the abstract format constants in + * {@link android.graphics.PixelFormat}: + * {@link android.graphics.PixelFormat#UNKNOWN}, + * {@link android.graphics.PixelFormat#TRANSLUCENT}, + * {@link android.graphics.PixelFormat#TRANSPARENT}, or + * {@link android.graphics.PixelFormat#OPAQUE}. + * + * <p>Generally a Drawable should be as conservative as possible with the + * value it returns. For example, if it contains multiple child drawables + * and only shows one of them at a time, if only one of the children is + * TRANSLUCENT and the others are OPAQUE then TRANSLUCENT should be + * returned. You can use the method {@link #resolveOpacity} to perform a + * standard reduction of two opacities to the appropriate single output. + * + * <p>Note that the returned value does <em>not</em> take into account a + * custom alpha or color filter that has been applied by the client through + * the {@link #setAlpha} or {@link #setColorFilter} methods. + * + * @return int The opacity class of the Drawable. + * + * @see android.graphics.PixelFormat + */ + public abstract int getOpacity(); + + /** + * Return the appropriate opacity value for two source opacities. If + * either is UNKNOWN, that is returned; else, if either is TRANSLUCENT, + * that is returned; else, if either is TRANSPARENT, that is returned; + * else, OPAQUE is returned. + * + * <p>This is to help in implementing {@link #getOpacity}. + * + * @param op1 One opacity value. + * @param op2 Another opacity value. + * + * @return int The combined opacity value. + * + * @see #getOpacity + */ + public static int resolveOpacity(int op1, int op2) { + if (op1 == op2) { + return op1; + } + if (op1 == PixelFormat.UNKNOWN || op2 == PixelFormat.UNKNOWN) { + return PixelFormat.UNKNOWN; + } + if (op1 == PixelFormat.TRANSLUCENT || op2 == PixelFormat.TRANSLUCENT) { + return PixelFormat.TRANSLUCENT; + } + if (op1 == PixelFormat.TRANSPARENT || op2 == PixelFormat.TRANSPARENT) { + return PixelFormat.TRANSPARENT; + } + return PixelFormat.OPAQUE; + } + + /** + * Returns a Region representing the part of the Drawable that is completely + * transparent. This can be used to perform drawing operations, identifying + * which parts of the target will not change when rendering the Drawable. + * The default implementation returns null, indicating no transparent + * region; subclasses can optionally override this to return an actual + * Region if they want to supply this optimization information, but it is + * not required that they do so. + * + * @return Returns null if the Drawables has no transparent region to + * report, else a Region holding the parts of the Drawable's bounds that + * are transparent. + */ + public Region getTransparentRegion() { + return null; + } + + /** + * Override this in your subclass to change appearance if you recognize the + * specified state. + * + * @return Returns true if the state change has caused the appearance of + * the Drawable to change (that is, it needs to be drawn), else false + * if it looks the same and there is no need to redraw it since its + * last state. + */ + protected boolean onStateChange(int[] state) { return false; } + /** Override this in your subclass to change appearance if you vary based + * on level. + * @return Returns true if the level change has caused the appearance of + * the Drawable to change (that is, it needs to be drawn), else false + * if it looks the same and there is no need to redraw it since its + * last level. + */ + protected boolean onLevelChange(int level) { return false; } + /** + * Override this in your subclass to change appearance if you recognize the + * specified state. + */ + protected void onBoundsChange(Rect bounds) {} + + /** + * Return the intrinsic width of the underlying drawable object. Returns + * -1 if it has no intrinsic width, such as with a solid color. + */ + public int getIntrinsicWidth() { + return -1; + } + + /** + * Return the intrinsic height of the underlying drawable object. Returns + * -1 if it has no intrinsic height, such as with a solid color. + */ + public int getIntrinsicHeight() { + return -1; + } + + /** + * Returns the minimum width suggested by this Drawable. If a View uses this + * Drawable as a background, it is suggested that the View use at least this + * value for its width. (There will be some scenarios where this will not be + * possible.) This value should INCLUDE any padding. + * + * @return The minimum width suggested by this Drawable. If this Drawable + * doesn't have a suggested minimum width, 0 is returned. + */ + public int getMinimumWidth() { + final int intrinsicWidth = getIntrinsicWidth(); + return intrinsicWidth > 0 ? intrinsicWidth : 0; + } + + /** + * Returns the minimum height suggested by this Drawable. If a View uses this + * Drawable as a background, it is suggested that the View use at least this + * value for its height. (There will be some scenarios where this will not be + * possible.) This value should INCLUDE any padding. + * + * @return The minimum height suggested by this Drawable. If this Drawable + * doesn't have a suggested minimum height, 0 is returned. + */ + public int getMinimumHeight() { + final int intrinsicHeight = getIntrinsicHeight(); + return intrinsicHeight > 0 ? intrinsicHeight : 0; + } + + /** + * Return in padding the insets suggested by this Drawable for placing + * content inside the drawable's bounds. Positive values move toward the + * center of the Drawable (set Rect.inset). Returns true if this drawable + * actually has a padding, else false. When false is returned, the padding + * is always set to 0. + */ + public boolean getPadding(Rect padding) { + padding.set(0, 0, 0, 0); + return false; + } + + /** + * Create a drawable from an inputstream + */ + public static Drawable createFromStream(InputStream is, String srcName) { + if (is == null) { + return null; + } + + /* ugh. The decodeStream contract is that we have already allocated + the pad rect, but if the bitmap does not had a ninepatch chunk, + then the pad will be ignored. If we could change this to lazily + alloc/assign the rect, we could avoid the GC churn of making new + Rects only to drop them on the floor. + */ + Rect pad = new Rect(); + Bitmap bm = BitmapFactory.decodeStream(is, pad, null); + if (bm != null) { + byte[] np = bm.getNinePatchChunk(); + if (np == null || !NinePatch.isNinePatchChunk(np)) { + np = null; + pad = null; + } + return drawableFromBitmap(bm, np, pad, srcName); + } + return null; + } + + /** + * Create a drawable from an XML document. For more information on how to + * create resources in XML, see + * <a href="{@docRoot}devel/resources-i18n.html">Resources and + * Internationalization</a>. + */ + public static Drawable createFromXml(Resources r, XmlPullParser parser) + throws XmlPullParserException, IOException { + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty loop + } + + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + + Drawable drawable = createFromXmlInner(r, parser, attrs); + + if (drawable == null) { + throw new RuntimeException("Unknown initial tag: " + parser.getName()); + } + + return drawable; + } + + /** + * Create from inside an XML document. Called on a parser positioned at + * a tag in an XML document, tries to create a Drawable from that tag. + * Returns null if the tag is not a valid drawable. + */ + public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + Drawable drawable; + + final String name = parser.getName(); + + if (name.equals("selector")) { + drawable = new StateListDrawable(); + } else if (name.equals("level-list")) { + drawable = new LevelListDrawable(); + } else if (name.equals("layer-list")) { + drawable = new LayerDrawable(); + } else if (name.equals("transition")) { + drawable = new TransitionDrawable(); + } else if (name.equals("color")) { + drawable = new ColorDrawable(); + } else if (name.equals("shape")) { + drawable = new GradientDrawable(); + } else if (name.equals("scale")) { + drawable = new ScaleDrawable(); + } else if (name.equals("clip")) { + drawable = new ClipDrawable(); + } else if (name.equals("rotate")) { + drawable = new RotateDrawable(); + } else if (name.equals("animation-list")) { + drawable = new AnimationDrawable(); + } else if (name.equals("inset")) { + drawable = new InsetDrawable(); + } else if (name.equals("bitmap")) { + drawable = new BitmapDrawable(); + } else { + throw new XmlPullParserException(parser.getPositionDescription() + + ": invalid drawable tag " + name); + } + + drawable.inflate(r, parser, attrs); + return drawable; + } + + + /** + * Create a drawable from file path name. + */ + public static Drawable createFromPath(String pathName) { + if (pathName == null) { + return null; + } + + Bitmap bm = BitmapFactory.decodeFile(pathName); + if (bm != null) { + return drawableFromBitmap(bm, null, null, pathName); + } + + return null; + } + + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable); + inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible); + a.recycle(); + } + + void inflateWithAttributes(Resources r, XmlPullParser parser, + TypedArray attrs, int visibleAttr) + throws XmlPullParserException, IOException { + + mVisible = attrs.getBoolean(visibleAttr, mVisible); + } + + public static abstract class ConstantState { + public abstract Drawable newDrawable(); + public abstract int getChangingConfigurations(); + } + + public ConstantState getConstantState() { + return null; + } + + private static Drawable drawableFromBitmap(Bitmap bm, byte[] np, Rect pad, String srcName) { + if (np != null) { + return new NinePatchDrawable(bm, np, pad, srcName); + } + return new BitmapDrawable(bm); + } +} + diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java new file mode 100644 index 0000000..e6c48be --- /dev/null +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2006 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.drawable; + +import android.graphics.*; + +public class DrawableContainer extends Drawable implements Drawable.Callback { + + private DrawableContainerState mDrawableContainerState; + private Drawable mCurrDrawable; + private int mAlpha = 0xFF; + private ColorFilter mColorFilter; + private boolean mDither; + + private int mCurIndex = -1; + + // overrides from Drawable + + @Override + public void draw(Canvas canvas) { + if (mCurrDrawable != null) { + mCurrDrawable.draw(canvas); + } + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mDrawableContainerState.mChangingConfigurations + | mDrawableContainerState.mChildrenChangingConfigurations; + } + + @Override + public boolean getPadding(Rect padding) { + final Rect r = mDrawableContainerState.getConstantPadding(); + if (r != null) { + padding.set(r); + return true; + } + if (mCurrDrawable != null) { + return mCurrDrawable.getPadding(padding); + } else { + return super.getPadding(padding); + } + } + + @Override + public void setAlpha(int alpha) { + if (mAlpha != alpha) { + mAlpha = alpha; + if (mCurrDrawable != null) { + mCurrDrawable.setAlpha(alpha); + } + } + } + + @Override + public void setDither(boolean dither) { + if (mDither != dither) { + mDither = dither; + if (mCurrDrawable != null) { + mCurrDrawable.setDither(mDither); + } + } + } + + @Override + public void setColorFilter(ColorFilter cf) { + if (mColorFilter != cf) { + mColorFilter = cf; + if (mCurrDrawable != null) { + mCurrDrawable.setColorFilter(cf); + } + } + } + + @Override + protected void onBoundsChange(Rect bounds) { + if (mCurrDrawable != null) { + mCurrDrawable.setBounds(bounds); + } + } + + @Override + public boolean isStateful() { + return mDrawableContainerState.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + if (mCurrDrawable != null) { + return mCurrDrawable.setState(state); + } + return false; + } + + @Override + protected boolean onLevelChange(int level) { + if (mCurrDrawable != null) { + return mCurrDrawable.setLevel(level); + } + return false; + } + + @Override + public int getIntrinsicWidth() { + if (mDrawableContainerState.isConstantSize()) { + return mDrawableContainerState.getConstantWidth(); + } + return mCurrDrawable != null ? mCurrDrawable.getIntrinsicWidth() : -1; + } + + @Override + public int getIntrinsicHeight() { + if (mDrawableContainerState.isConstantSize()) { + return mDrawableContainerState.getConstantHeight(); + } + return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1; + } + + @Override + public int getMinimumWidth() { + if (mDrawableContainerState.isConstantSize()) { + return mDrawableContainerState.getConstantMinimumWidth(); + } + return mCurrDrawable != null ? mCurrDrawable.getMinimumWidth() : 0; + } + + @Override + public int getMinimumHeight() { + if (mDrawableContainerState.isConstantSize()) { + return mDrawableContainerState.getConstantMinimumHeight(); + } + return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0; + } + + public void invalidateDrawable(Drawable who) + { + if (who == mCurrDrawable && mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) + { + if (who == mCurrDrawable && mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) + { + if (who == mCurrDrawable && mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (mCurrDrawable != null) { + mCurrDrawable.setVisible(visible, restart); + } + return changed; + } + + @Override + public int getOpacity() { + return mDrawableContainerState.getOpacity(); + } + + public boolean selectDrawable(int idx) + { + if (idx == mCurIndex) { + return false; + } + if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) { + Drawable d = mDrawableContainerState.mDrawables[idx]; + if (mCurrDrawable != null) { + mCurrDrawable.setVisible(false, false); + } + mCurrDrawable = d; + mCurIndex = idx; + if (d != null) { + d.setVisible(isVisible(), true); + d.setAlpha(mAlpha); + d.setDither(mDither); + d.setColorFilter(mColorFilter); + d.setState(getState()); + d.setLevel(getLevel()); + d.setBounds(getBounds()); + } + } else { + if (mCurrDrawable != null) { + mCurrDrawable.setVisible(false, false); + } + mCurrDrawable = null; + mCurIndex = -1; + } + invalidateSelf(); + return true; + } + + @Override + public Drawable getCurrent() { + return mCurrDrawable; + } + + @Override + public ConstantState getConstantState() { + if (mDrawableContainerState.canConstantState()) { + mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations(); + return mDrawableContainerState; + } + return null; + } + + public abstract static class DrawableContainerState extends ConstantState { + final DrawableContainer mOwner; + + int mChangingConfigurations; + int mChildrenChangingConfigurations; + + Drawable[] mDrawables; + int mNumChildren; + + boolean mVariablePadding = false; + Rect mConstantPadding = null; + + boolean mConstantSize = false; + boolean mComputedConstantSize = false; + int mConstantWidth; + int mConstantHeight; + int mConstantMinimumWidth; + int mConstantMinimumHeight; + + boolean mHaveOpacity = false; + int mOpacity; + + boolean mHaveStateful = false; + boolean mStateful; + + boolean mCheckedConstantState; + boolean mCanConstantState; + + DrawableContainerState(DrawableContainerState orig, DrawableContainer owner) { + mOwner = owner; + + if (orig != null) { + mChangingConfigurations = orig.mChangingConfigurations; + mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; + + final Drawable[] origDr = orig.mDrawables; + + mDrawables = new Drawable[origDr.length]; + mNumChildren = orig.mNumChildren; + + final int N = mNumChildren; + for (int i=0; i<N; i++) { + mDrawables[i] = origDr[i].getConstantState().newDrawable(); + mDrawables[i].setCallback(owner); + } + + mCheckedConstantState = mCanConstantState = true; + mVariablePadding = orig.mVariablePadding; + mConstantPadding = orig.mConstantPadding; + mConstantSize = orig.mConstantSize; + mComputedConstantSize = orig.mComputedConstantSize; + mConstantWidth = orig.mConstantWidth; + mConstantHeight = orig.mConstantHeight; + + mHaveOpacity = orig.mHaveOpacity; + mOpacity = orig.mOpacity; + mHaveStateful = orig.mHaveStateful; + mStateful = orig.mStateful; + + } else { + mDrawables = new Drawable[10]; + mNumChildren = 0; + mCheckedConstantState = mCanConstantState = false; + } + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + public final int addChild(Drawable dr) { + final int pos = mNumChildren; + + if (pos >= mDrawables.length) { + growArray(pos, pos+10); + } + + dr.setVisible(false, true); + dr.setCallback(mOwner); + + mDrawables[pos] = dr; + mNumChildren++; + mChildrenChangingConfigurations |= dr.getChangingConfigurations(); + mHaveOpacity = false; + mHaveStateful = false; + + mConstantPadding = null; + mComputedConstantSize = false; + + return pos; + } + + public final int getChildCount() + { + return mNumChildren; + } + + public final Drawable[] getChildren() + { + return mDrawables; + } + + /** A boolean value indicating whether to use the maximum padding value of + * all frames in the set (false), or to use the padding value of the frame + * being shown (true). Default value is false. + */ + public final void setVariablePadding(boolean variable) + { + mVariablePadding = variable; + } + + public final Rect getConstantPadding() + { + if (mVariablePadding) { + return null; + } + if (mConstantPadding != null) { + return mConstantPadding; + } + + Rect r = new Rect(0, 0, 0, 0); + Rect t = new Rect(); + final int N = getChildCount(); + for (int i=0; i<N; i++) { + if (mDrawables[i].getPadding(t)) { + if (t.left > r.left) r.left = t.left; + if (t.top > r.top) r.top = t.top; + if (t.right > r.right) r.right = t.right; + if (t.bottom > r.bottom) r.bottom = t.bottom; + } + } + return (mConstantPadding=r); + } + + public final void setConstantSize(boolean constant) + { + mConstantSize = constant; + } + + public final boolean isConstantSize() + { + return mConstantSize; + } + + public final int getConstantWidth() + { + if (!mComputedConstantSize) { + computeConstantSize(); + } + + return mConstantWidth; + } + + public final int getConstantHeight() + { + if (!mComputedConstantSize) { + computeConstantSize(); + } + + return mConstantHeight; + } + + public final int getConstantMinimumWidth() + { + if (!mComputedConstantSize) { + computeConstantSize(); + } + + return mConstantMinimumWidth; + } + + public final int getConstantMinimumHeight() + { + if (!mComputedConstantSize) { + computeConstantSize(); + } + + return mConstantMinimumHeight; + } + + private void computeConstantSize() + { + mComputedConstantSize = true; + + final int N = getChildCount(); + mConstantWidth = mConstantHeight = 0; + mConstantMinimumWidth = mConstantMinimumHeight = 0; + for (int i=0; i<N; i++) { + Drawable dr = mDrawables[i]; + int s = dr.getIntrinsicWidth(); + if (s > mConstantWidth) mConstantWidth = s; + s = dr.getIntrinsicHeight(); + if (s > mConstantHeight) mConstantHeight = s; + s = dr.getMinimumWidth(); + if (s > mConstantMinimumWidth) mConstantMinimumWidth = s; + s = dr.getMinimumHeight(); + if (s > mConstantMinimumHeight) mConstantMinimumHeight = s; + } + } + + public final int getOpacity() + { + if (mHaveOpacity) { + return mOpacity; + } + + final int N = getChildCount(); + int op = N > 0 + ? mDrawables[0].getOpacity() : PixelFormat.TRANSPARENT; + for (int i=1; i<N; i++) { + op = Drawable.resolveOpacity(op, mDrawables[i].getOpacity()); + } + mOpacity = op; + mHaveOpacity = true; + return op; + } + + public final boolean isStateful() { + if (mHaveStateful) { + return mStateful; + } + + boolean stateful = false; + final int N = getChildCount(); + for (int i = 0; i < N; i++) { + if (mDrawables[i].isStateful()) { + stateful = true; + break; + } + } + + mStateful = stateful; + mHaveStateful = true; + return stateful; + } + + public void growArray(int oldSize, int newSize) + { + Drawable[] newDrawables = new Drawable[newSize]; + System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize); + mDrawables = newDrawables; + } + + public synchronized boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = true; + final int N = mNumChildren; + for (int i=0; i<N; i++) { + if (mDrawables[i].getConstantState() == null) { + mCanConstantState = false; + break; + } + } + mCheckedConstantState = true; + } + + return mCanConstantState; + } + } + + protected void setConstantState(DrawableContainerState state) + { + mDrawableContainerState = state; + } +} diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java new file mode 100644 index 0000000..ab3b23d --- /dev/null +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -0,0 +1,891 @@ +/* + * Copyright (C) 2006 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.drawable; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.DashPathEffect; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.Path; +import android.graphics.RadialGradient; +import android.graphics.SweepGradient; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * A simple color gradient for buttons, backgrounds, etc. See + * <a href="{@docRoot}reference/available-resources.html#gradientdrawable">Gradient</a> + * in the Resources topic to learn how to specify this type as an XML resource. + */ +public class GradientDrawable extends Drawable { + /** + * Shape is a rectangle, possibly with rounded corners + */ + public static final int RECTANGLE = 0; + + /** + * Shape is an ellipse + */ + public static final int OVAL = 1; + + /** + * Shape is a line + */ + public static final int LINE = 2; + + /** + * Shape is a ring. + */ + public static final int RING = 3; + + /** + * Gradient is linear (default.) + */ + public static final int LINEAR_GRADIENT = 0; + + /** + * Gradient is circular. + */ + public static final int RADIAL_GRADIENT = 1; + + /** + * Gradient is a sweep. + */ + public static final int SWEEP_GRADIENT = 2; + + private final GradientState mGradientState; + + private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Rect mPadding; + private Paint mStrokePaint; // optional, set by the caller + private ColorFilter mColorFilter; // optional, set by the caller + private int mAlpha = 0xFF; // modified by the caller + private boolean mDither; + + private final Path mPath = new Path(); + private final RectF mRect = new RectF(); + + private Paint mLayerPaint; // internal, used if we use saveLayer() + private boolean mRectIsDirty; // internal state + + /** + * Controls how the gradient is oriented relative to the drawable's bounds + */ + public enum Orientation { + /** draw the gradient from the top to the bottom */ + TOP_BOTTOM, + /** draw the gradient from the top-right to the bottom-left */ + TR_BL, + /** draw the gradient from the right to the left */ + RIGHT_LEFT, + /** draw the gradient from the bottom-right to the top-left */ + BR_TL, + /** draw the gradient from the bottom to the top */ + BOTTOM_TOP, + /** draw the gradient from the bottom-left to the top-right */ + BL_TR, + /** draw the gradient from the left to the right */ + LEFT_RIGHT, + /** draw the gradient from the top-left to the bottom-right */ + TL_BR, + } + + public GradientDrawable() { + this(new GradientState(Orientation.TOP_BOTTOM, null)); + } + + /** + * Create a new gradient drawable given an orientation and an array + * of colors for the gradient. + */ + public GradientDrawable(Orientation orientation, int[] colors) { + this(new GradientState(orientation, colors)); + } + + @Override + public boolean getPadding(Rect padding) { + if (mPadding != null) { + padding.set(mPadding); + return true; + } else { + return super.getPadding(padding); + } + } + + /** + * Specify radii for each of the 4 corners. For each corner, the array + * contains 2 values, [X_radius, Y_radius]. The corners are ordered + * top-left, top-right, bottom-right, bottom-left + */ + public void setCornerRadii(float[] radii) { + mGradientState.setCornerRadii(radii); + } + + /** + * Specify radius for the corners of the gradient. If this is > 0, then the + * drawable is drawn in a round-rectangle, rather than a rectangle. + */ + public void setCornerRadius(float radius) { + mGradientState.setCornerRadius(radius); + } + + /** + * Set the stroke width and color for the drawable. If width is zero, + * then no stroke is drawn. + */ + public void setStroke(int width, int color) { + setStroke(width, color, 0, 0); + } + + public void setStroke(int width, int color, float dashWidth, float dashGap) { + mGradientState.setStroke(width, color, dashWidth, dashGap); + + if (mStrokePaint == null) { + mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mStrokePaint.setStyle(Paint.Style.STROKE); + } + mStrokePaint.setStrokeWidth(width); + mStrokePaint.setColor(color); + + DashPathEffect e = null; + if (dashWidth > 0) { + e = new DashPathEffect(new float[] { dashWidth, dashGap }, 0); + } + mStrokePaint.setPathEffect(e); + } + + public void setSize(int width, int height) { + mGradientState.setSize(width, height); + } + + public void setShape(int shape) { + mGradientState.setShape(shape); + } + + public void setGradientType(int gradient) { + mGradientState.setGradientType(gradient); + mRectIsDirty = true; + } + + public void setGradientCenter(float x, float y) { + mGradientState.setGradientCenter(x, y); + } + + public void setGradientRadius(float gradientRadius) { + mGradientState.setGradientRadius(gradientRadius); + } + + public void setUseLevel(boolean useLevel) { + mGradientState.mUseLevel = useLevel; + } + + private int modulateAlpha(int alpha) { + int scale = mAlpha + (mAlpha >> 7); + return alpha * scale >> 8; + } + + @Override + public void draw(Canvas canvas) { + if (!ensureValidRect()) { + // nothing to draw + return; + } + + // remember the alpha values, in case we temporarily overwrite them + // when we modulate them with mAlpha + final int prevFillAlpha = mFillPaint.getAlpha(); + final int prevStrokeAlpha = mStrokePaint != null ? + mStrokePaint.getAlpha() : 0; + // compute the modulate alpha values + final int currFillAlpha = modulateAlpha(prevFillAlpha); + final int currStrokeAlpha = modulateAlpha(prevStrokeAlpha); + + final boolean haveStroke = currStrokeAlpha > 0 && + mStrokePaint.getStrokeWidth() > 0; + final boolean haveFill = currFillAlpha > 0; + final GradientState st = mGradientState; + /* we need a layer iff we're drawing both a fill and stroke, and the + stroke is non-opaque, and our shapetype actually supports + fill+stroke. Otherwise we can just draw the stroke (if any) on top + of the fill (if any) without worrying about blending artifacts. + */ + final boolean useLayer = haveStroke && haveFill && st.mShape != LINE && + currStrokeAlpha < 255; + + /* Drawing with a layer is slower than direct drawing, but it + allows us to apply paint effects like alpha and colorfilter to + the result of multiple separate draws. In our case, if the user + asks for a non-opaque alpha value (via setAlpha), and we're + stroking, then we need to apply the alpha AFTER we've drawn + both the fill and the stroke. + */ + if (useLayer) { + if (mLayerPaint == null) { + mLayerPaint = new Paint(); + } + mLayerPaint.setDither(mDither); + mLayerPaint.setAlpha(mAlpha); + mLayerPaint.setColorFilter(mColorFilter); + + float rad = mStrokePaint.getStrokeWidth(); + canvas.saveLayer(mRect.left - rad, mRect.top - rad, + mRect.right + rad, mRect.bottom + rad, + mLayerPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG); + + // don't perform the filter in our individual paints + // since the layer will do it for us + mFillPaint.setColorFilter(null); + mStrokePaint.setColorFilter(null); + } else { + /* if we're not using a layer, apply the dither/filter to our + individual paints + */ + mFillPaint.setAlpha(currFillAlpha); + mFillPaint.setDither(mDither); + mFillPaint.setColorFilter(mColorFilter); + if (haveStroke) { + mStrokePaint.setAlpha(currStrokeAlpha); + mStrokePaint.setDither(mDither); + mStrokePaint.setColorFilter(mColorFilter); + } + } + + switch (st.mShape) { + case RECTANGLE: + if (st.mRadiusArray != null) { + mPath.reset(); + mPath.addRoundRect(mRect, st.mRadiusArray, + Path.Direction.CW); + canvas.drawPath(mPath, mFillPaint); + if (haveStroke) { + canvas.drawPath(mPath, mStrokePaint); + } + } + else { + float rad = st.mRadius; + canvas.drawRoundRect(mRect, rad, rad, mFillPaint); + if (haveStroke) { + canvas.drawRoundRect(mRect, rad, rad, mStrokePaint); + } + } + break; + case OVAL: + canvas.drawOval(mRect, mFillPaint); + if (haveStroke) { + canvas.drawOval(mRect, mStrokePaint); + } + break; + case LINE: { + RectF r = mRect; + float y = r.centerY(); + canvas.drawLine(r.left, y, r.right, y, mStrokePaint); + break; + } + case RING: + Path ring = buildRing(st); + canvas.drawPath(ring, mFillPaint); + if (haveStroke) { + canvas.drawPath(ring, mStrokePaint); + } + break; + } + + if (useLayer) { + canvas.restore(); + } else { + mFillPaint.setAlpha(prevFillAlpha); + if (haveStroke) { + mStrokePaint.setAlpha(prevStrokeAlpha); + } + } + } + + private Path buildRing(GradientState st) { + float sweep = st.mUseLevelForShape ? (360.0f * getLevel() / 10000.0f) : 360f; + + RectF bounds = new RectF(mRect); + + float x = bounds.width() / 2.0f; + float y = bounds.height() / 2.0f; + + float thickness = bounds.width() / st.mThickness; + // inner radius + float radius = bounds.width() / st.mInnerRadius; + + RectF innerBounds = new RectF(bounds); + innerBounds.inset(x - radius, y - radius); + + bounds = new RectF(innerBounds); + bounds.inset(-thickness, -thickness); + + Path path = new Path(); + // arcTo treats the sweep angle mod 360, so check for that, since we + // think 360 means draw the entire oval + if (sweep < 360 && sweep > -360) { + path.setFillType(Path.FillType.EVEN_ODD); + // inner top + path.moveTo(x + radius, y); + // outer top + path.lineTo(x + radius + thickness, y); + // outer arc + path.arcTo(bounds, 0.0f, sweep, false); + // inner arc + path.arcTo(innerBounds, sweep, -sweep, false); + path.close(); + } else { + // add the entire ovals + path.addOval(bounds, Path.Direction.CW); + path.addOval(innerBounds, Path.Direction.CCW); + } + + return path; + } + + public void setColor(int argb) { + mGradientState.setSolidColor(argb); + mFillPaint.setColor(argb); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mGradientState.mChangingConfigurations; + } + + @Override + public void setAlpha(int alpha) { + mAlpha = alpha; + } + + @Override + public void setDither(boolean dither) { + mDither = dither; + } + + @Override + public void setColorFilter(ColorFilter cf) { + mColorFilter = cf; + } + + @Override + public int getOpacity() { + // XXX need to figure out the actual opacity... + return PixelFormat.TRANSLUCENT; + } + + @Override + protected void onBoundsChange(Rect r) { + super.onBoundsChange(r); + mRectIsDirty = true; + } + + @Override + protected boolean onLevelChange(int level) { + super.onLevelChange(level); + mRectIsDirty = true; + invalidateSelf(); + return true; + } + + /** + * This checks mRectIsDirty, and if it is true, recomputes both our drawing + * rectangle (mRect) and the gradient itself, since it depends on our + * rectangle too. + * @return true if the resulting rectangle is not empty, false otherwise + */ + private boolean ensureValidRect() { + if (mRectIsDirty) { + mRectIsDirty = false; + + Rect bounds = getBounds(); + float inset = 0; + + if (mStrokePaint != null) { + inset = mStrokePaint.getStrokeWidth() * 0.5f; + } + + final GradientState st = mGradientState; + + mRect.set(bounds.left + inset, bounds.top + inset, + bounds.right - inset, bounds.bottom - inset); + + if (st.mColors != null) { + RectF r = mRect; + float x0, x1, y0, y1; + + if (st.mGradient == LINEAR_GRADIENT) { + final float level = st.mUseLevel ? (float) getLevel() / 10000.0f : 1.0f; + switch (st.mOrientation) { + case TOP_BOTTOM: + x0 = r.left; y0 = r.top; + x1 = x0; y1 = level * r.bottom; + break; + case TR_BL: + x0 = r.right; y0 = r.top; + x1 = level * r.left; y1 = level * r.bottom; + break; + case RIGHT_LEFT: + x0 = r.right; y0 = r.top; + x1 = level * r.left; y1 = y0; + break; + case BR_TL: + x0 = r.right; y0 = r.bottom; + x1 = level * r.left; y1 = level * r.top; + break; + case BOTTOM_TOP: + x0 = r.left; y0 = r.bottom; + x1 = x0; y1 = level * r.top; + break; + case BL_TR: + x0 = r.left; y0 = r.bottom; + x1 = level * r.right; y1 = level * r.top; + break; + case LEFT_RIGHT: + x0 = r.left; y0 = r.top; + x1 = level * r.right; y1 = y0; + break; + default:/* TL_BR */ + x0 = r.left; y0 = r.top; + x1 = level * r.right; y1 = level * r.bottom; + break; + } + + mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1, + st.mColors, st.mPositions, + Shader.TileMode.CLAMP)); + } else if (st.mGradient == RADIAL_GRADIENT) { + x0 = r.left + (r.right - r.left) * st.mCenterX; + y0 = r.top + (r.bottom - r.top) * st.mCenterY; + + final float level = st.mUseLevel ? (float) getLevel() / 10000.0f : 1.0f; + + mFillPaint.setShader(new RadialGradient(x0, y0, + level * st.mGradientRadius, st.mColors, null, + Shader.TileMode.CLAMP)); + } else if (st.mGradient == SWEEP_GRADIENT) { + x0 = r.left + (r.right - r.left) * st.mCenterX; + y0 = r.top + (r.bottom - r.top) * st.mCenterY; + + float[] positions = null; + int[] colors = st.mColors; + + if (st.mUseLevel) { + final int length = st.mColors.length; + colors = new int[length + 1]; + System.arraycopy(st.mColors, 0, colors, 0, length); + colors[length] = st.mColors[length - 1]; + + final float fraction = 1.0f / (float) (length - 1); + positions = new float[length + 1]; + final float level = (float) getLevel() / 10000.0f; + for (int i = 0; i < length; i++) { + positions[i] = i * fraction * level; + } + positions[length] = 1.0f; + } + mFillPaint.setShader(new SweepGradient(x0, y0, colors, positions)); + } + } + } + return !mRect.isEmpty(); + } + + @Override + public void inflate(Resources r, XmlPullParser parser, + AttributeSet attrs) + throws XmlPullParserException, IOException { + + final GradientState st = mGradientState; + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawable); + + super.inflateWithAttributes(r, parser, a, + com.android.internal.R.styleable.GradientDrawable_visible); + + int shapeType = a.getInt( + com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE); + + if (shapeType == RING) { + st.mInnerRadius = a.getFloat( + com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f); + st.mThickness = a.getFloat( + com.android.internal.R.styleable.GradientDrawable_thicknessRatio, 9.0f); + st.mUseLevelForShape = a.getBoolean( + com.android.internal.R.styleable.GradientDrawable_useLevel, true); + } + + a.recycle(); + + setShape(shapeType); + + int type; + + final int innerDepth = parser.getDepth()+1; + int depth; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth=parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth) { + continue; + } + + String name = parser.getName(); + + if (name.equals("size")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawableSize); + int width = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawableSize_width, -1); + int height = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawableSize_height, -1); + a.recycle(); + setSize(width, height); + } else if (name.equals("gradient")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawableGradient); + int startColor = a.getColor( + com.android.internal.R.styleable.GradientDrawableGradient_startColor, 0); + boolean hasCenterColor = a + .hasValue(com.android.internal.R.styleable.GradientDrawableGradient_centerColor); + int centerColor = a.getColor( + com.android.internal.R.styleable.GradientDrawableGradient_centerColor, 0); + int endColor = a.getColor( + com.android.internal.R.styleable.GradientDrawableGradient_endColor, 0); + int gradientType = a.getInt( + com.android.internal.R.styleable.GradientDrawableGradient_type, + LINEAR_GRADIENT); + + st.mCenterX = getFloatOrFraction( + a, + com.android.internal.R.styleable.GradientDrawableGradient_centerX, + 0.5f); + + st.mCenterY = getFloatOrFraction( + a, + com.android.internal.R.styleable.GradientDrawableGradient_centerY, + 0.5f); + + st.mUseLevel = a.getBoolean( + com.android.internal.R.styleable.GradientDrawableGradient_useLevel, false); + st.mGradient = gradientType; + + if (gradientType == LINEAR_GRADIENT) { + int angle = (int)a.getFloat( + com.android.internal.R.styleable.GradientDrawableGradient_angle, 0); + angle %= 360; + if (angle % 45 != 0) { + throw new XmlPullParserException(a.getPositionDescription() + + "<gradient> tag requires 'angle' attribute to " + + "be a multiple of 45"); + } + + switch (angle) { + case 0: + st.mOrientation = Orientation.LEFT_RIGHT; + break; + case 45: + st.mOrientation = Orientation.BL_TR; + break; + case 90: + st.mOrientation = Orientation.BOTTOM_TOP; + break; + case 135: + st.mOrientation = Orientation.BR_TL; + break; + case 180: + st.mOrientation = Orientation.RIGHT_LEFT; + break; + case 225: + st.mOrientation = Orientation.TR_BL; + break; + case 270: + st.mOrientation = Orientation.TOP_BOTTOM; + break; + case 315: + st.mOrientation = Orientation.TL_BR; + break; + } + } else { + TypedValue tv = a.peekValue( + com.android.internal.R.styleable.GradientDrawableGradient_gradientRadius); + if (tv != null) { + boolean radiusRel = tv.type == TypedValue.TYPE_FRACTION; + st.mGradientRadius = radiusRel ? + tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + } else if (gradientType == RADIAL_GRADIENT) { + throw new XmlPullParserException( + a.getPositionDescription() + + "<gradient> tag requires 'gradientRadius' " + + "attribute with radial type"); + } + } + + a.recycle(); + + if (hasCenterColor) { + st.mColors = new int[3]; + st.mColors[0] = startColor; + st.mColors[1] = centerColor; + st.mColors[2] = endColor; + + st.mPositions = new float[3]; + st.mPositions[0] = 0.0f; + // Since 0.5f is default value, try to take the one that isn't 0.5f + st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY; + st.mPositions[2] = 1f; + } else { + st.mColors = new int[2]; + st.mColors[0] = startColor; + st.mColors[1] = endColor; + } + + } else if (name.equals("solid")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawableSolid); + int argb = a.getColor( + com.android.internal.R.styleable.GradientDrawableSolid_color, 0); + a.recycle(); + setColor(argb); + } else if (name.equals("stroke")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawableStroke); + int width = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawableStroke_width, 0); + int color = a.getColor( + com.android.internal.R.styleable.GradientDrawableStroke_color, 0); + float dashWidth = a.getDimension( + com.android.internal.R.styleable.GradientDrawableStroke_dashWidth, 0); + if (dashWidth != 0.0f) { + float dashGap = a.getDimension( + com.android.internal.R.styleable.GradientDrawableStroke_dashGap, 0); + setStroke(width, color, dashWidth, dashGap); + } else { + setStroke(width, color); + } + a.recycle(); + } else if (name.equals("corners")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.DrawableCorners); + int radius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_radius, 0); + setCornerRadius(radius); + int topLeftRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius); + int topRightRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_topRightRadius, radius); + int bottomLeftRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_bottomLeftRadius, radius); + int bottomRightRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_bottomRightRadius, radius); + if (topLeftRadius != radius && topRightRadius != radius && + bottomLeftRadius != radius && bottomRightRadius != radius) { + setCornerRadii(new float[] { + topLeftRadius, topLeftRadius, + topRightRadius, topRightRadius, + bottomLeftRadius, bottomLeftRadius, + bottomRightRadius, bottomRightRadius + }); + } + a.recycle(); + } else if (name.equals("padding")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawablePadding); + mPadding = new Rect( + a.getDimensionPixelOffset( + com.android.internal.R.styleable.GradientDrawablePadding_left, 0), + a.getDimensionPixelOffset( + com.android.internal.R.styleable.GradientDrawablePadding_top, 0), + a.getDimensionPixelOffset( + com.android.internal.R.styleable.GradientDrawablePadding_right, 0), + a.getDimensionPixelOffset( + com.android.internal.R.styleable.GradientDrawablePadding_bottom, 0)); + a.recycle(); + mGradientState.mPadding = mPadding; + } else { + Log.w("drawable", "Bad element under <shape>: " + name); + } + } + } + + private static float getFloatOrFraction(TypedArray a, int index, float defaultValue) { + TypedValue tv = a.peekValue(index); + float v = defaultValue; + if (tv != null) { + boolean vIsFraction = tv.type == TypedValue.TYPE_FRACTION; + v = vIsFraction ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + } + return v; + } + + @Override + public int getIntrinsicWidth() { + return mGradientState.mWidth; + } + + @Override + public int getIntrinsicHeight() { + return mGradientState.mHeight; + } + + @Override + public ConstantState getConstantState() { + mGradientState.mChangingConfigurations = super.getChangingConfigurations(); + return mGradientState; + } + + final static class GradientState extends ConstantState { + public int mChangingConfigurations; + public int mShape = RECTANGLE; + public int mGradient = LINEAR_GRADIENT; + public Orientation mOrientation; + public int[] mColors; + public float[] mPositions; + public boolean mHasSolidColor; + public int mSolidColor; + public int mStrokeWidth = -1; // if >= 0 use stroking. + public int mStrokeColor; + public float mStrokeDashWidth; + public float mStrokeDashGap; + public float mRadius; // use this if mRadiusArray is null + public float[] mRadiusArray; + public Rect mPadding; + public int mWidth = -1; + public int mHeight = -1; + public float mInnerRadius; + public float mThickness; + private float mCenterX = 0.5f; + private float mCenterY = 0.5f; + private float mGradientRadius = 0.5f; + private boolean mUseLevel; + private boolean mUseLevelForShape; + + + GradientState() { + mOrientation = Orientation.TOP_BOTTOM; + } + + GradientState(Orientation orientation, int[] colors) { + mOrientation = orientation; + mColors = colors; + } + + @Override + public Drawable newDrawable() { + return new GradientDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + public void setShape(int shape) { + mShape = shape; + } + + public void setGradientType(int gradient) { + mGradient = gradient; + } + + public void setGradientCenter(float x, float y) { + mCenterX = x; + mCenterY = y; + } + + public void setSolidColor(int argb) { + mHasSolidColor = true; + mSolidColor = argb; + mColors = null; + } + + public void setStroke(int width, int color) { + mStrokeWidth = width; + mStrokeColor = color; + } + + public void setStroke(int width, int color, float dashWidth, float dashGap) { + mStrokeWidth = width; + mStrokeColor = color; + mStrokeDashWidth = dashWidth; + mStrokeDashGap = dashGap; + } + + public void setCornerRadius(float radius) { + if (radius < 0) { + radius = 0; + } + mRadius = radius; + mRadiusArray = null; + } + + public void setCornerRadii(float[] radii) { + mRadiusArray = radii; + if (radii == null) { + mRadius = 0; + } + } + + public void setSize(int width, int height) { + mWidth = width; + mHeight = height; + } + + public void setGradientRadius(float gradientRadius) { + mGradientRadius = gradientRadius; + } + } + + private GradientDrawable(GradientState state) { + mGradientState = state; + if (state.mHasSolidColor) { + mFillPaint.setColor(state.mSolidColor); + } + mPadding = state.mPadding; + if (state.mStrokeWidth >= 0) { + mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mStrokePaint.setStyle(Paint.Style.STROKE); + mStrokePaint.setStrokeWidth(state.mStrokeWidth); + mStrokePaint.setColor(state.mStrokeColor); + + if (state.mStrokeDashWidth != 0.0f) { + DashPathEffect e = new DashPathEffect(new float[] { + state.mStrokeDashWidth, state.mStrokeDashGap}, 0); + mStrokePaint.setPathEffect(e); + } + } + mRectIsDirty = true; + } +} + diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java new file mode 100644 index 0000000..fe21692 --- /dev/null +++ b/graphics/java/android/graphics/drawable/InsetDrawable.java @@ -0,0 +1,285 @@ +/* + * 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.drawable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.*; +import android.util.AttributeSet; +import android.util.Log; + +import java.io.IOException; + +/** + * A Drawable that insets another Drawable by a specified distance. + * This is used when a View needs a background that is smaller than + * the View's actual bounds. + */ +public class InsetDrawable extends Drawable implements Drawable.Callback +{ + // Most of this is copied from ScaleDrawable. + + /*package*/ InsetDrawable() { + this(null); + } + + public InsetDrawable(Drawable drawable, int inset) { + this(drawable, inset, inset, inset, inset); + } + + public InsetDrawable(Drawable drawable, int insetLeft, int insetTop, + int insetRight, int insetBottom) { + this(null); + + mInsetState.mDrawable = drawable; + mInsetState.mInsetLeft = insetLeft; + mInsetState.mInsetTop = insetTop; + mInsetState.mInsetRight = insetRight; + mInsetState.mInsetBottom = insetBottom; + + if (drawable != null) { + drawable.setCallback(this); + } + } + + @Override public void inflate(Resources r, XmlPullParser parser, + AttributeSet attrs) + throws XmlPullParserException, IOException { + int type; + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.InsetDrawable); + + super.inflateWithAttributes(r, parser, a, + com.android.internal.R.styleable.InsetDrawable_visible); + + int drawableRes = a.getResourceId(com.android.internal.R.styleable. + InsetDrawable_drawable, 0); + + int inLeft = a.getDimensionPixelOffset(com.android.internal.R.styleable. + InsetDrawable_insetLeft, 0); + int inTop = a.getDimensionPixelOffset(com.android.internal.R.styleable. + InsetDrawable_insetTop, 0); + int inRight = a.getDimensionPixelOffset(com.android.internal.R.styleable. + InsetDrawable_insetRight, 0); + int inBottom = a.getDimensionPixelOffset(com.android.internal.R.styleable. + InsetDrawable_insetBottom, 0); + + a.recycle(); + + Drawable dr; + if (drawableRes != 0) { + dr = r.getDrawable(drawableRes); + } else { + while ((type=parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": <inset> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + dr = Drawable.createFromXmlInner(r, parser, attrs); + } + + if (dr == null) { + Log.w("drawable", "No drawable specified for <inset>"); + } + + mInsetState.mDrawable = dr; + mInsetState.mInsetLeft = inLeft; + mInsetState.mInsetRight = inRight; + mInsetState.mInsetTop = inTop; + mInsetState.mInsetBottom = inBottom; + + if (dr != null) { + dr.setCallback(this); + } + } + + // overrides from Drawable.Callback + + public void invalidateDrawable(Drawable who) { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + // overrides from Drawable + + @Override + public void draw(Canvas canvas) { + mInsetState.mDrawable.draw(canvas); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mInsetState.mChangingConfigurations + | mInsetState.mDrawable.getChangingConfigurations(); + } + + @Override + public boolean getPadding(Rect padding) { + boolean pad = mInsetState.mDrawable.getPadding(padding); + + padding.left += mInsetState.mInsetLeft; + padding.right += mInsetState.mInsetRight; + padding.top += mInsetState.mInsetTop; + padding.bottom += mInsetState.mInsetBottom; + + if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight | + mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) { + return true; + } else { + return false; + } + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + mInsetState.mDrawable.setVisible(visible, restart); + return super.setVisible(visible, restart); + } + + @Override + public void setAlpha(int alpha) { + mInsetState.mDrawable.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mInsetState.mDrawable.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return mInsetState.mDrawable.getOpacity(); + } + + @Override + public boolean isStateful() { + return mInsetState.mDrawable.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + boolean changed = mInsetState.mDrawable.setState(state); + onBoundsChange(getBounds()); + return changed; + } + + @Override + protected void onBoundsChange(Rect bounds) { + final Rect r = mTmpRect; + r.set(bounds); + + r.left += mInsetState.mInsetLeft; + r.top += mInsetState.mInsetTop; + r.right -= mInsetState.mInsetRight; + r.bottom -= mInsetState.mInsetBottom; + + mInsetState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); + } + + @Override + public int getIntrinsicWidth() { + return mInsetState.mDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mInsetState.mDrawable.getIntrinsicHeight(); + } + + @Override + public ConstantState getConstantState() { + if (mInsetState.canConstantState()) { + mInsetState.mChangingConfigurations = super.getChangingConfigurations(); + return mInsetState; + } + return null; + } + + final static class InsetState extends ConstantState { + InsetState(InsetState orig, InsetDrawable owner) { + if (orig != null) { + mDrawable = orig.mDrawable.getConstantState().newDrawable(); + mDrawable.setCallback(owner); + mInsetLeft = orig.mInsetLeft; + mInsetTop = orig.mInsetTop; + mInsetRight = orig.mInsetRight; + mInsetBottom = orig.mInsetBottom; + mCheckedConstantState = mCanConstantState = true; + } + } + + @Override + public Drawable newDrawable() { + return new InsetDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = mDrawable.getConstantState() != null; + mCheckedConstantState = true; + } + + return mCanConstantState; + } + + Drawable mDrawable; + int mChangingConfigurations; + + int mInsetLeft; + int mInsetTop; + int mInsetRight; + int mInsetBottom; + + boolean mCheckedConstantState; + boolean mCanConstantState; + } + + private InsetDrawable(InsetState state) { + InsetState as = new InsetState(state, this); + mInsetState = as; + } + + private InsetState mInsetState; + private final Rect mTmpRect = new Rect(); +} + diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java new file mode 100644 index 0000000..6e9d9ba --- /dev/null +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2006 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.drawable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.*; +import android.util.AttributeSet; +import android.view.View; + +import java.io.IOException; + +/** Drawable that manages an array of other drawables. These are drawn in array + order, so the element with the largest index will be drawn on top. +*/ +public class LayerDrawable extends Drawable implements Drawable.Callback { + + /* package */ LayerState mLayerState; + + private int[] mPaddingL; + private int[] mPaddingT; + private int[] mPaddingR; + private int[] mPaddingB; + + private final Rect mTmpRect = new Rect(); + + public LayerDrawable(Drawable[] array) { + this((LayerState)null); + int length = array.length; + Rec[] r = new Rec[length]; + + for (int i = 0; i < length; i++) { + r[i] = new Rec(); + r[i].mDrawable = array[i]; + array[i].setCallback(this); + mLayerState.mChildrenChangingConfigurations + |= array[i].getChangingConfigurations(); + } + mLayerState.mNum = length; + mLayerState.mArray = r; + ensurePadding(); + } + + /* package */ LayerDrawable() { + this((LayerState) null); + } + + + /* package */ LayerDrawable(LayerState state) { + LayerState as = createConstantState(state); + mLayerState = as; + if (as.mNum > 0) { + ensurePadding(); + } + } + + /* package */ LayerState createConstantState(LayerState state) { + return new LayerState(state, this); + } + + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); + + int type; + + final int innerDepth = parser.getDepth() + 1; + int depth; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth || !parser.getName().equals("item")) { + continue; + } + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.LayerDrawableItem); + + int left = a.getDimensionPixelOffset( + com.android.internal.R.styleable.LayerDrawableItem_left, 0); + int top = a.getDimensionPixelOffset( + com.android.internal.R.styleable.LayerDrawableItem_top, 0); + int right = a.getDimensionPixelOffset( + com.android.internal.R.styleable.LayerDrawableItem_right, 0); + int bottom = a.getDimensionPixelOffset( + com.android.internal.R.styleable.LayerDrawableItem_bottom, 0); + int drawableRes = a.getResourceId( + com.android.internal.R.styleable.LayerDrawableItem_drawable, 0); + int id = a.getResourceId(com.android.internal.R.styleable.LayerDrawableItem_id, + View.NO_ID); + + a.recycle(); + + Drawable dr; + if (drawableRes != 0) { + dr = r.getDrawable(drawableRes); + } else { + while ((type = parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException(parser.getPositionDescription() + + ": <item> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + dr = Drawable.createFromXmlInner(r, parser, attrs); + } + + addLayer(id, dr, left, top, right, bottom); + } + + ensurePadding(); + onStateChange(getState()); + } + + private void addLayer(int id, Drawable dr, int l, int t, int r, int b) { + final LayerState st = mLayerState; + int N = st.mArray != null ? st.mArray.length : 0; + int i = st.mNum; + if (i >= N) { + Rec[] nu = new Rec[N + 10]; + if (i > 0) { + System.arraycopy(st.mArray, 0, nu, 0, i); + } + st.mArray = nu; + } + + mLayerState.mChildrenChangingConfigurations + |= dr.getChangingConfigurations(); + + Rec rec = new Rec(); + st.mArray[i] = rec; + rec.mId = id; + rec.mDrawable = dr; + rec.mInsetL = l; + rec.mInsetT = t; + rec.mInsetR = r; + rec.mInsetB = b; + st.mNum++; + + dr.setCallback(this); + } + + /** + * Look for a layer with the given id, and returns its {@link Drawable}. + * + * @param id The layer ID to search for. + * @return The {@link Drawable} of the layer that has the given id in the hierarchy or null. + */ + public Drawable findDrawableByLayerId(int id) { + final Rec[] layers = mLayerState.mArray; + + for (int i = mLayerState.mNum - 1; i >= 0; i--) { + if (layers[i].mId == id) { + return layers[i].mDrawable; + } + } + + return null; + } + + /** + * Sets the ID of a layer. + * + * @param index The index of the layer which will received the ID. + * @param id The ID to assign to the layer. + */ + public void setId(int index, int id) { + mLayerState.mArray[index].mId = id; + } + + /** + * Returns the number of layers contained within this. + * @return The number of layers. + */ + // TODO: Remove this once XML inflation is there for ShapeDrawable? + public int getNumberOfLayers() { + return mLayerState.mNum; + } + + // TODO: Remove once XML inflation... + public Drawable getDrawable(int index) { + return mLayerState.mArray[index].mDrawable; + } + + public int getId(int index) { + return mLayerState.mArray[index].mId; + } + + + /** + * Sets (or replaces) the {@link Drawable} for the layer with the given id. + * + * @param id The layer ID to search for. + * @param drawable The replacement {@link Drawable}. + * @return Whether the {@link Drawable} was replaced (could return false if + * the id was not found). + */ + public boolean setDrawableByLayerId(int id, Drawable drawable) { + final Rec[] layers = mLayerState.mArray; + + for (int i = mLayerState.mNum - 1; i >= 0; i--) { + if (layers[i].mId == id) { + layers[i].mDrawable = drawable; + return true; + } + } + + return false; + } + + /** Specify modifiers to the bounds for the drawable[index]. + left += l + top += t; + right -= r; + bottom -= b; + */ + public void setLayerInset(int index, int l, int t, int r, int b) { + Rec rec = mLayerState.mArray[index]; + rec.mInsetL = l; + rec.mInsetT = t; + rec.mInsetR = r; + rec.mInsetB = b; + } + + // overrides from Drawable.Callback + + public void invalidateDrawable(Drawable who) { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + // overrides from Drawable + + @Override + public void draw(Canvas canvas) { + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + for (int i=0; i<N; i++) { + array[i].mDrawable.draw(canvas); + } + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mLayerState.mChangingConfigurations + | mLayerState.mChildrenChangingConfigurations; + } + + @Override + public boolean getPadding(Rect padding) { + // Arbitrarily get the padding from the first image. + // Technically we should maybe do something more intelligent, + // like take the max padding of all the images. + padding.left = 0; + padding.top = 0; + padding.right = 0; + padding.bottom = 0; + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + for (int i=0; i<N; i++) { + reapplyPadding(i, array[i]); + padding.left += mPaddingL[i]; + padding.top += mPaddingT[i]; + padding.right += mPaddingR[i]; + padding.bottom += mPaddingB[i]; + } + return true; + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + for (int i=0; i<N; i++) { + array[i].mDrawable.setVisible(visible, restart); + } + return changed; + } + + @Override + public void setDither(boolean dither) { + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + for (int i=0; i<N; i++) { + array[i].mDrawable.setDither(dither); + } + } + + @Override + public void setAlpha(int alpha) { + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + for (int i=0; i<N; i++) { + array[i].mDrawable.setAlpha(alpha); + } + } + + @Override + public void setColorFilter(ColorFilter cf) { + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + for (int i=0; i<N; i++) { + array[i].mDrawable.setColorFilter(cf); + } + } + + @Override + public int getOpacity() { + return mLayerState.getOpacity(); + } + + @Override + public boolean isStateful() { + return mLayerState.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + boolean paddingChanged = false; + boolean changed = false; + for (int i=0; i<N; i++) { + final Rec r = array[i]; + if (r.mDrawable.setState(state)) { + changed = true; + } + if (reapplyPadding(i, r)) { + paddingChanged = true; + } + } + if (paddingChanged) { + onBoundsChange(getBounds()); + } + return changed; + } + + @Override + protected boolean onLevelChange(int level) { + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + boolean paddingChanged = false; + boolean changed = false; + for (int i=0; i<N; i++) { + final Rec r = array[i]; + if (r.mDrawable.setLevel(level)) { + changed = true; + } + if (reapplyPadding(i, r)) { + paddingChanged = true; + } + } + if (paddingChanged) { + onBoundsChange(getBounds()); + } + return changed; + } + + @Override + protected void onBoundsChange(Rect bounds) { + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + int padL=0, padT=0, padR=0, padB=0; + for (int i=0; i<N; i++) { + final Rec r = array[i]; + r.mDrawable.setBounds(bounds.left + r.mInsetL + padL, + bounds.top + r.mInsetT + padT, + bounds.right - r.mInsetR - padR, + bounds.bottom - r.mInsetB - padB); + padL += mPaddingL[i]; + padR += mPaddingR[i]; + padT += mPaddingT[i]; + padB += mPaddingB[i]; + } + } + + @Override + public int getIntrinsicWidth() { + int width = -1; + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + int padL=0, padR=0; + for (int i=0; i<N; i++) { + final Rec r = array[i]; + int w = r.mDrawable.getIntrinsicWidth() + + r.mInsetL + r.mInsetR + padL + padR; + if (w > width) { + width = w; + } + padL += mPaddingL[i]; + padR += mPaddingR[i]; + } + //System.out.println("Intrinsic width: " + width); + return width; + } + + @Override + public int getIntrinsicHeight() { + int height = -1; + final Rec[] array = mLayerState.mArray; + final int N = mLayerState.mNum; + int padT=0, padB=0; + for (int i=0; i<N; i++) { + final Rec r = array[i]; + int h = r.mDrawable.getIntrinsicHeight() + + r.mInsetT + r.mInsetB + + padT + padB; + if (h > height) { + height = h; + } + padT += mPaddingT[i]; + padB += mPaddingB[i]; + } + //System.out.println("Intrinsic height: " + height); + return height; + } + + private boolean reapplyPadding(int i, Rec r) { + final Rect rect = mTmpRect; + r.mDrawable.getPadding(rect); + if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] + || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) { + mPaddingL[i] = rect.left; + mPaddingT[i] = rect.top; + mPaddingR[i] = rect.right; + mPaddingB[i] = rect.bottom; + return true; + } + return false; + } + + private void ensurePadding() { + final int N = mLayerState.mNum; + if (mPaddingL != null && mPaddingL.length >= N) { + return; + } + mPaddingL = new int[N]; + mPaddingT = new int[N]; + mPaddingR = new int[N]; + mPaddingB = new int[N]; + } + + @Override + public ConstantState getConstantState() { + if (mLayerState.canConstantState()) { + mLayerState.mChangingConfigurations = super.getChangingConfigurations(); + return mLayerState; + } + return null; + } + + /* package */ static class Rec { + public Drawable mDrawable; + public int mInsetL, mInsetT, mInsetR, mInsetB; + public int mId; + } + + /* package */ static class LayerState extends ConstantState { + int mNum; + Rec[] mArray; + + int mChangingConfigurations; + int mChildrenChangingConfigurations; + + private boolean mHaveOpacity = false; + private int mOpacity; + + private boolean mHaveStateful = false; + private boolean mStateful; + + private boolean mCheckedConstantState; + private boolean mCanConstantState; + + LayerState(LayerState orig, LayerDrawable owner) { + if (orig != null) { + final Rec[] origRec = orig.mArray; + final int N = orig.mNum; + + mNum = N; + mArray = new Rec[N]; + + mChangingConfigurations = orig.mChangingConfigurations; + mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; + + for (int i = 0; i < N; i++) { + final Rec r = mArray[i] = new Rec(); + final Rec or = origRec[i]; + r.mDrawable = or.mDrawable.getConstantState().newDrawable(); + r.mDrawable.setCallback(owner); + r.mInsetL = or.mInsetL; + r.mInsetT = or.mInsetT; + r.mInsetR = or.mInsetR; + r.mInsetB = or.mInsetB; + r.mId = or.mId; + } + + mHaveOpacity = orig.mHaveOpacity; + mOpacity = orig.mOpacity; + mHaveStateful = orig.mHaveStateful; + mStateful = orig.mStateful; + mCheckedConstantState = mCanConstantState = true; + } else { + mNum = 0; + mArray = null; + } + } + + @Override + public Drawable newDrawable() { + return new LayerDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + public final int getOpacity() { + if (mHaveOpacity) { + return mOpacity; + } + + final int N = mNum; + int op = N > 0 ? mArray[0].mDrawable.getOpacity() + : PixelFormat.TRANSPARENT; + for (int i = 1; i < N; i++) { + op = Drawable.resolveOpacity(op, mArray[i].mDrawable + .getOpacity()); + } + mOpacity = op; + mHaveOpacity = true; + return op; + } + + public final boolean isStateful() { + if (mHaveStateful) { + return mStateful; + } + + boolean stateful = false; + final int N = mNum; + for (int i = 0; i < N; i++) { + if (mArray[i].mDrawable.isStateful()) { + stateful = true; + break; + } + } + + mStateful = stateful; + mHaveStateful = true; + return stateful; + } + + public synchronized boolean canConstantState() { + if (!mCheckedConstantState && mArray != null) { + mCanConstantState = true; + final int N = mNum; + for (int i=0; i<N; i++) { + if (mArray[i].mDrawable.getConstantState() == null) { + mCanConstantState = false; + break; + } + } + mCheckedConstantState = true; + } + + return mCanConstantState; + } + } +} + diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java new file mode 100644 index 0000000..61f45b3 --- /dev/null +++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2006 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.drawable; + +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +/** + * + * A resource that contains a number of alternate images, each assigned a maximum numerical value. + * Setting the level value of the object with {@link #setLevel(int)} will load the image with the next + * greater or equal value assigned to its max attribute. See <a href="{@docRoot}reference/available-resources.html#levellistdrawable"> + * Level List</a> in the Resources topic to learn how to specify this type as an XML resource. A good example use of + * a LevelListDrawable would be a battery level indicator icon, with different images to indicate the current + * battery level. + * + */ +public class LevelListDrawable extends DrawableContainer { + public LevelListDrawable() + { + this(null); + } + + public void addLevel(int low, int high, Drawable drawable) { + if (drawable != null) { + mLevelListState.addLevel(low, high, drawable); + // in case the new state matches our current state... + onLevelChange(getLevel()); + } + } + + // overrides from Drawable + + @Override + protected boolean onLevelChange(int level) { + int idx = mLevelListState.indexOfLevel(level); + if (selectDrawable(idx)) { + return true; + } + return super.onLevelChange(level); + } + + @Override public void inflate(Resources r, XmlPullParser parser, + AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); + + int type; + + int low = 0; + + final int innerDepth = parser.getDepth()+1; + int depth; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth=parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth || !parser.getName().equals("item")) { + continue; + } + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.LevelListDrawableItem); + + low = a.getInt( + com.android.internal.R.styleable.LevelListDrawableItem_minLevel, 0); + int high = a.getInt( + com.android.internal.R.styleable.LevelListDrawableItem_maxLevel, 0); + int drawableRes = a.getResourceId( + com.android.internal.R.styleable.LevelListDrawableItem_drawable, 0); + + a.recycle(); + + if (high < 0) { + throw new XmlPullParserException(parser.getPositionDescription() + + ": <item> tag requires a 'maxLevel' attribute"); + } + + Drawable dr; + if (drawableRes != 0) { + dr = r.getDrawable(drawableRes); + } else { + while ((type=parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": <item> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + dr = Drawable.createFromXmlInner(r, parser, attrs); + } + + mLevelListState.addLevel(low, high, dr); + low = high+1; + } + + onLevelChange(getLevel()); + } + + private final static class LevelListState extends DrawableContainerState + { + LevelListState(LevelListState orig, LevelListDrawable owner) + { + super(orig, owner); + + if (orig != null) { + mLows = orig.mLows; + mHighs = orig.mHighs; + } else { + mLows = new int[getChildren().length]; + mHighs = new int[getChildren().length]; + } + } + + public void addLevel(int low, int high, Drawable drawable) + { + int pos = addChild(drawable); + mLows[pos] = low; + mHighs[pos] = high; + } + + public int indexOfLevel(int level) + { + final int[] lows = mLows; + final int[] highs = mHighs; + final int N = getChildCount(); + for (int i=0; i<N; i++) { + if (level >= lows[i] && level <= highs[i]) { + return i; + } + } + return -1; + } + + @Override + public Drawable newDrawable() + { + return new LevelListDrawable(this); + } + + @Override + public void growArray(int oldSize, int newSize) + { + super.growArray(oldSize, newSize); + int[] newInts = new int[newSize]; + System.arraycopy(mLows, 0, newInts, 0, oldSize); + mLows = newInts; + newInts = new int[newSize]; + System.arraycopy(mHighs, 0, newInts, 0, oldSize); + mHighs = newInts; + } + + private int[] mLows; + private int[] mHighs; + } + + private LevelListDrawable(LevelListState state) + { + LevelListState as = new LevelListState(state, this); + mLevelListState = as; + setConstantState(as); + onLevelChange(getLevel()); + } + + private final LevelListState mLevelListState; +} + diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java new file mode 100644 index 0000000..c98ef5b --- /dev/null +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2006 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.drawable; + +import android.graphics.*; + +/** + * + * A resizeable bitmap, with stretchable areas that you define. This type of image + * is defined in a .png file with a special format, described in <a link="../../../resources.html#ninepatch"> + * Resources</a>. + * + */ +public class NinePatchDrawable extends Drawable { + + public NinePatchDrawable(Bitmap bitmap, byte[] chunk, + Rect padding, String srcName) { + this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding)); + } + + public NinePatchDrawable(NinePatch patch) { + this(new NinePatchState(patch, null)); + } + + // overrides + + @Override + public void draw(Canvas canvas) { + mNinePatch.draw(canvas, getBounds(), mPaint); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mNinePatchState.mChangingConfigurations; + } + + @Override + public boolean getPadding(Rect padding) { + padding.set(mPadding); + return true; + } + + @Override + public void setAlpha(int alpha) { + getPaint().setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + getPaint().setColorFilter(cf); + } + + @Override + public void setDither(boolean dither) { + getPaint().setDither(dither); + } + + public Paint getPaint() { + if (mPaint == null) { + mPaint = new Paint(); + } + return mPaint; + } + + /** + * Retrieves the width of the source .png file (before resizing). + */ + @Override + public int getIntrinsicWidth() { + return mNinePatch.getWidth(); + } + + /** + * Retrieves the height of the source .png file (before resizing). + */ + @Override + public int getIntrinsicHeight() { + return mNinePatch.getHeight(); + } + + @Override + public int getMinimumWidth() { + return mNinePatch.getWidth(); + } + + @Override + public int getMinimumHeight() { + return mNinePatch.getHeight(); + } + + /** + * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat} value of OPAQUE or TRANSLUCENT. + */ + @Override + public int getOpacity() { + return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) + ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; + } + + @Override + public Region getTransparentRegion() { + return mNinePatch.getTransparentRegion(getBounds()); + } + + @Override + public ConstantState getConstantState() { + mNinePatchState.mChangingConfigurations = super.getChangingConfigurations(); + return mNinePatchState; + } + + final static class NinePatchState extends ConstantState { + NinePatchState(NinePatch ninePatch, Rect padding) + { + mNinePatch = ninePatch; + mPadding = padding; + } + + @Override + public Drawable newDrawable() + { + return new NinePatchDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + final NinePatch mNinePatch; + final Rect mPadding; + int mChangingConfigurations; + } + + private NinePatchDrawable(NinePatchState state) { + mNinePatchState = state; + mNinePatch = state.mNinePatch; + mPadding = state.mPadding; + } + + private final NinePatchState mNinePatchState; + private final NinePatch mNinePatch; + private final Rect mPadding; + private Paint mPaint; +} + diff --git a/graphics/java/android/graphics/drawable/PaintDrawable.java b/graphics/java/android/graphics/drawable/PaintDrawable.java new file mode 100644 index 0000000..c86fc46 --- /dev/null +++ b/graphics/java/android/graphics/drawable/PaintDrawable.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2006 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.drawable; + +import android.graphics.drawable.shapes.RoundRectShape; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import org.xmlpull.v1.XmlPullParser; + +/** + * Drawable that draws its bounds in the given paint, with optional + * rounded corners. +*/ +public class PaintDrawable extends ShapeDrawable { + + public PaintDrawable() { + } + + public PaintDrawable(int color) { + getPaint().setColor(color); + } + + /** + * Specify radius for the corners of the rectangle. If this is > 0, then the + * drawable is drawn in a round-rectangle, rather than a rectangle. + * @param radius the radius for the corners of the rectangle + */ + public void setCornerRadius(float radius) { + float[] radii = null; + if (radius > 0) { + radii = new float[8]; + for (int i = 0; i < 8; i++) { + radii[i] = radius; + } + } + setCornerRadii(radii); + } + + /** + * Specify radii for each of the 4 corners. For each corner, the array + * contains 2 values, [X_radius, Y_radius]. The corners are ordered + * top-left, top-right, bottom-right, bottom-left + * @param radii the x and y radii of the corners + */ + public void setCornerRadii(float[] radii) { + if (radii == null) { + if (getShape() != null) { + setShape(null); + } + } else { + setShape(new RoundRectShape(radii, null, null)); + } + } + + @Override + protected boolean inflateTag(String name, Resources r, XmlPullParser parser, + AttributeSet attrs) { + if (name.equals("corners")) { + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.DrawableCorners); + int radius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_radius, 0); + setCornerRadius(radius); + + // now check of they have any per-corner radii + + int topLeftRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius); + int topRightRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_topRightRadius, radius); + int bottomLeftRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_bottomLeftRadius, radius); + int bottomRightRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.DrawableCorners_bottomRightRadius, radius); + + if (topLeftRadius != radius || topRightRadius != radius || + bottomLeftRadius != radius || bottomRightRadius != radius) { + setCornerRadii(new float[] { + topLeftRadius, topLeftRadius, + topRightRadius, topRightRadius, + bottomLeftRadius, bottomLeftRadius, + bottomRightRadius, bottomRightRadius + }); + } + a.recycle(); + return true; + } + return super.inflateTag(name, r, parser, attrs); + } +} + diff --git a/graphics/java/android/graphics/drawable/PictureDrawable.java b/graphics/java/android/graphics/drawable/PictureDrawable.java new file mode 100644 index 0000000..1f5d4d8 --- /dev/null +++ b/graphics/java/android/graphics/drawable/PictureDrawable.java @@ -0,0 +1,106 @@ +/* + * 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.drawable; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Matrix.ScaleToFit; +import android.graphics.drawable.Drawable; +import android.graphics.Picture; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.view.Gravity; + +/** + * Drawable subclass that wraps a Picture, allowing the picture to be used + * whereever a Drawable is supported. + */ +public class PictureDrawable extends Drawable { + + private Picture mPicture; + + /** + * Construct a new drawable referencing the specified picture. The picture + * may be null. + * + * @param picture The picture to associate with the drawable. May be null. + */ + public PictureDrawable(Picture picture) { + mPicture = picture; + } + + /** + * Return the picture associated with the drawable. May be null. + * + * @return the picture associated with the drawable, or null. + */ + public Picture getPicture() { + return mPicture; + } + + /** + * Associate a picture with this drawable. The picture may be null. + * + * @param picture The picture to associate with the drawable. May be null. + */ + public void setPicture(Picture picture) { + mPicture = picture; + } + + @Override + public void draw(Canvas canvas) { + if (mPicture != null) { + Rect bounds = getBounds(); + canvas.save(); + canvas.clipRect(bounds); + canvas.translate(bounds.left, bounds.top); + canvas.drawPicture(mPicture); + canvas.restore(); + } + } + + @Override + public int getIntrinsicWidth() { + return mPicture != null ? mPicture.getWidth() : -1; + } + + @Override + public int getIntrinsicHeight() { + return mPicture != null ? mPicture.getHeight() : -1; + } + + @Override + public int getOpacity() { + // not sure, so be safe + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setFilterBitmap(boolean filter) {} + + @Override + public void setDither(boolean dither) {} + + @Override + public void setColorFilter(ColorFilter colorFilter) {} + + @Override + public void setAlpha(int alpha) {} +} + diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java new file mode 100644 index 0000000..c2f05ec --- /dev/null +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2007 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.drawable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Rect; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.TypedValue; +import android.util.AttributeSet; +import android.util.Log; + +import java.io.IOException; + +/** + * <p>A drawable that can rotate another drawable based on the current level + * value. The start and end angles of rotation can be controlled to map any + * circular arc to the level values range.</p> + */ +public class RotateDrawable extends Drawable implements Drawable.Callback { + + /** + * <p>Create a new rotating drawable with an empty state.</p> + */ + public RotateDrawable() { + this(null); + } + + /** + * <p>Create a new rotating drawable with the specified state. A copy of + * this state is used as the internal state for the newly created + * drawable.</p> + * + * @param rotateState the state for this drawable + */ + private RotateDrawable(RotateState rotateState) { + mState = new RotateState(rotateState, this); + } + + public void draw(Canvas canvas) { + int saveCount = canvas.save(); + + Rect bounds = mState.mDrawable.getBounds(); + + int w = bounds.right - bounds.left; + int h = bounds.bottom - bounds.top; + + final RotateState st = mState; + + float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX; + float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY; + + canvas.rotate(st.mCurrentDegrees, px, py); + + st.mDrawable.draw(canvas); + + canvas.restoreToCount(saveCount); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mState.mChangingConfigurations + | mState.mDrawable.getChangingConfigurations(); + } + + public void setAlpha(int alpha) { + mState.mDrawable.setAlpha(alpha); + } + + public void setColorFilter(ColorFilter cf) { + mState.mDrawable.setColorFilter(cf); + } + + public int getOpacity() { + return mState.mDrawable.getOpacity(); + } + + public void invalidateDrawable(Drawable who) { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + @Override + public boolean getPadding(Rect padding) { + return mState.mDrawable.getPadding(padding); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + mState.mDrawable.setVisible(visible, restart); + return super.setVisible(visible, restart); + } + + @Override + public boolean isStateful() { + return mState.mDrawable.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + boolean changed = mState.mDrawable.setState(state); + onBoundsChange(getBounds()); + return changed; + } + + @Override + protected boolean onLevelChange(int level) { + mState.mDrawable.setLevel(level); + onBoundsChange(getBounds()); + + mState.mCurrentDegrees = mState.mFromDegrees + + (mState.mToDegrees - mState.mFromDegrees) * + ((float) level / MAX_LEVEL); + + invalidateSelf(); + return true; + } + + @Override + protected void onBoundsChange(Rect bounds) { + mState.mDrawable.setBounds(bounds.left, bounds.top, + bounds.right, bounds.bottom); + } + + @Override + public int getIntrinsicWidth() { + return mState.mDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mState.mDrawable.getIntrinsicHeight(); + } + + @Override + public ConstantState getConstantState() { + if (mState.canConstantState()) { + mState.mChangingConfigurations = super.getChangingConfigurations(); + return mState; + } + return null; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.RotateDrawable); + + super.inflateWithAttributes(r, parser, a, + com.android.internal.R.styleable.RotateDrawable_visible); + + TypedValue tv = a.peekValue(com.android.internal.R.styleable.RotateDrawable_pivotX); + boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION; + float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + + tv = a.peekValue(com.android.internal.R.styleable.RotateDrawable_pivotY); + boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION; + float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + + float fromDegrees = a.getFloat( + com.android.internal.R.styleable.RotateDrawable_fromDegrees, 0.0f); + float toDegrees = a.getFloat( + com.android.internal.R.styleable.RotateDrawable_toDegrees, 360.0f); + + toDegrees = Math.max(fromDegrees, toDegrees); + + int res = a.getResourceId( + com.android.internal.R.styleable.RotateDrawable_drawable, 0); + Drawable drawable = null; + if (res > 0) { + drawable = r.getDrawable(res); + } + + a.recycle(); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && + (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) { + Log.w("drawable", "Bad element under <rotate>: " + + parser .getName()); + } + } + + if (drawable == null) { + Log.w("drawable", "No drawable specified for <rotate>"); + } + + mState.mDrawable = drawable; + mState.mPivotXRel = pivotXRel; + mState.mPivotX = pivotX; + mState.mPivotYRel = pivotYRel; + mState.mPivotY = pivotY; + mState.mFromDegrees = mState.mCurrentDegrees = fromDegrees; + mState.mToDegrees = toDegrees; + + if (drawable != null) { + drawable.setCallback(this); + } + } + + /** + * <p>Represents the state of a rotation for a given drawable. The same + * rotate drawable can be invoked with different states to drive several + * rotations at the same time.</p> + */ + final static class RotateState extends Drawable.ConstantState { + Drawable mDrawable; + + int mChangingConfigurations; + + boolean mPivotXRel; + float mPivotX; + boolean mPivotYRel; + float mPivotY; + + float mFromDegrees; + float mToDegrees; + + float mCurrentDegrees; + + public RotateState(RotateState source, RotateDrawable owner) { + if (source != null) { + mDrawable = source.mDrawable.getConstantState().newDrawable(); + mDrawable.setCallback(owner); + mPivotXRel = source.mPivotXRel; + mPivotX = source.mPivotX; + mPivotYRel = source.mPivotYRel; + mPivotY = source.mPivotY; + mFromDegrees = mCurrentDegrees = source.mFromDegrees; + mToDegrees = source.mToDegrees; + mCanConstantState = mCheckedConstantState = true; + } + } + + @Override + public Drawable newDrawable() { + return new RotateDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + public boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = mDrawable.getConstantState() != null; + mCheckedConstantState = true; + } + + return mCanConstantState; + } + + private boolean mCanConstantState; + private boolean mCheckedConstantState; + } + + private static final float MAX_LEVEL = 10000.0f; + + private RotateState mState; +} diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java new file mode 100644 index 0000000..c39f1fa --- /dev/null +++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2006 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.drawable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.*; +import android.view.Gravity; +import android.util.AttributeSet; +import android.util.Log; + +import java.io.IOException; + +/** + * A drawable that changes the size of another drawable based on its current + * level value. You can control how much the child drawable changes in width + * and height based on the level, as well as a gravity to control where it is + * placed in its overall container. Most often used to implement things like + * progress bars. + */ +public class ScaleDrawable extends Drawable implements Drawable.Callback { + private ScaleState mScaleState; + private final Rect mTmpRect = new Rect(); + + ScaleDrawable() { + this(null); + } + + public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) { + this(null); + + mScaleState.mDrawable = drawable; + mScaleState.mGravity = gravity; + mScaleState.mScaleWidth = scaleWidth; + mScaleState.mScaleHeight = scaleHeight; + + if (drawable != null) { + drawable.setCallback(this); + } + } + + private static float getPercent(TypedArray a, int name) { + String s = a.getString(name); + if (s != null) { + if (s.endsWith("%")) { + String f = s.substring(0, s.length() - 1); + return Float.parseFloat(f) / 100.0f; + } + } + return -1; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); + + int type; + + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ScaleDrawable); + + float sw = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleWidth); + float sh = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleHeight); + int g = a.getInt(com.android.internal.R.styleable.ScaleDrawable_scaleGravity, Gravity.LEFT); + Drawable dr = a.getDrawable(com.android.internal.R.styleable.ScaleDrawable_drawable); + + a.recycle(); + + final int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + dr = Drawable.createFromXmlInner(r, parser, attrs); + } + + if (dr == null) { + throw new IllegalArgumentException("No drawable specified for <scale>"); + } + + mScaleState.mDrawable = dr; + mScaleState.mScaleWidth = sw; + mScaleState.mScaleHeight = sh; + mScaleState.mGravity = g; + if (dr != null) { + dr.setCallback(this); + } + } + + // overrides from Drawable.Callback + + public void invalidateDrawable(Drawable who) { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + // overrides from Drawable + + @Override + public void draw(Canvas canvas) { + if (mScaleState.mDrawable.getLevel() != 0) + mScaleState.mDrawable.draw(canvas); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mScaleState.mChangingConfigurations + | mScaleState.mDrawable.getChangingConfigurations(); + } + + @Override + public boolean getPadding(Rect padding) { + // XXX need to adjust padding! + return mScaleState.mDrawable.getPadding(padding); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + mScaleState.mDrawable.setVisible(visible, restart); + return super.setVisible(visible, restart); + } + + @Override + public void setAlpha(int alpha) { + mScaleState.mDrawable.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mScaleState.mDrawable.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return mScaleState.mDrawable.getOpacity(); + } + + @Override + public boolean isStateful() { + return mScaleState.mDrawable.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + boolean changed = mScaleState.mDrawable.setState(state); + onBoundsChange(getBounds()); + return changed; + } + + @Override + protected boolean onLevelChange(int level) { + mScaleState.mDrawable.setLevel(level); + onBoundsChange(getBounds()); + invalidateSelf(); + return true; + } + + @Override + protected void onBoundsChange(Rect bounds) { + final Rect r = mTmpRect; + int level = getLevel(); + int w = bounds.width(); + final int iw = 0; //mScaleState.mDrawable.getIntrinsicWidth(); + if (mScaleState.mScaleWidth > 0) { + w -= (int) ((w - iw) * (10000 - level) * mScaleState.mScaleWidth / 10000); + } + int h = bounds.height(); + final int ih = 0; //mScaleState.mDrawable.getIntrinsicHeight(); + if (mScaleState.mScaleHeight > 0) { + h -= (int) ((h - ih) * (10000 - level) * mScaleState.mScaleHeight / 10000); + } + Gravity.apply(mScaleState.mGravity, w, h, bounds, r); + + if (w > 0 && h > 0) { + mScaleState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); + } + } + + @Override + public int getIntrinsicWidth() { + return mScaleState.mDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mScaleState.mDrawable.getIntrinsicHeight(); + } + + @Override + public ConstantState getConstantState() { + if (mScaleState.canConstantState()) { + mScaleState.mChangingConfigurations = super.getChangingConfigurations(); + return mScaleState; + } + return null; + } + + final static class ScaleState extends ConstantState { + ScaleState(ScaleState orig, ScaleDrawable owner) { + if (orig != null) { + mDrawable = orig.mDrawable.getConstantState().newDrawable(); + mDrawable.setCallback(owner); + mScaleWidth = orig.mScaleWidth; + mScaleHeight = orig.mScaleHeight; + mGravity = orig.mGravity; + mCheckedConstantState = mCanConstantState = true; + } + } + + @Override + public Drawable newDrawable() { + return new ScaleDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = mDrawable.getConstantState() != null; + mCheckedConstantState = true; + } + + return mCanConstantState; + } + + Drawable mDrawable; + int mChangingConfigurations; + float mScaleWidth; + float mScaleHeight; + int mGravity; + + private boolean mCheckedConstantState; + private boolean mCanConstantState; + } + + private ScaleDrawable(ScaleState state) { + mScaleState = new ScaleState(state, this); + } +} + diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java new file mode 100644 index 0000000..7e1284f --- /dev/null +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2007 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.drawable; + +import android.graphics.*; +import android.graphics.drawable.shapes.Shape; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * An object that draws primitive shapes. + * A ShapeDrawable takes a {@link android.graphics.drawable.shapes.Shape} + * object and manages its presence on the screen. If no Shape is given, then + * the ShapeDrawable will default to a + * {@link android.graphics.drawable.shapes.RectShape}. + */ +public class ShapeDrawable extends Drawable { + private ShapeState mShapeState; + + /** + * ShapeDrawable constructor. + */ + public ShapeDrawable() { + this((ShapeState) null); + } + + /** + * Creates a ShapeDrawable with a specified Shape. + * + * @param s the Shape that this ShapeDrawable should be + */ + public ShapeDrawable(Shape s) { + this((ShapeState) null); + + mShapeState.mShape = s; + } + + private ShapeDrawable(ShapeState state) { + mShapeState = new ShapeState(state); + } + + /** + * Returns the Shape of this ShapeDrawable. + */ + public Shape getShape() { + return mShapeState.mShape; + } + + /** + * Sets the Shape of this ShapeDrawable. + */ + public void setShape(Shape s) { + mShapeState.mShape = s; + updateShape(); + } + + /** + * Sets a ShaderFactory to which requests for a + * {@link android.graphics.Shader} object will be made. + * + * @param fact an instance of your ShaderFactory implementation + */ + public void setShaderFactory(ShaderFactory fact) { + mShapeState.mShaderFactory = fact; + } + + /** + * Returns the ShaderFactory used by this ShapeDrawable for requesting a + * {@link android.graphics.Shader}. + */ + public ShaderFactory getShaderFactory() { + return mShapeState.mShaderFactory; + } + + /** + * Returns the Paint used to draw the shape. + */ + public Paint getPaint() { + return mShapeState.mPaint; + } + + /** + * Sets padding for the shape. + * + * @param left padding for the left side (in pixels) + * @param top padding for the top (in pixels) + * @param right padding for the right side (in pixels) + * @param bottom padding for the bottom (in pixels) + */ + public void setPadding(int left, int top, int right, int bottom) { + if ((left | top | right | bottom) == 0) { + mShapeState.mPadding = null; + } else { + if (mShapeState.mPadding == null) { + mShapeState.mPadding = new Rect(); + } + mShapeState.mPadding.set(left, top, right, bottom); + } + } + + /** + * Sets padding for this shape, defined by a Rect object. + * Define the padding in the Rect object as: left, top, right, bottom. + */ + public void setPadding(Rect padding) { + if (padding == null) { + mShapeState.mPadding = null; + } else { + if (mShapeState.mPadding == null) { + mShapeState.mPadding = new Rect(); + } + mShapeState.mPadding.set(padding); + } + } + + /** + * Sets the intrinsic (default) width for this shape. + * + * @param width the intrinsic width (in pixels) + */ + public void setIntrinsicWidth(int width) { + mShapeState.mIntrinsicWidth = width; + } + + /** + * Sets the intrinsic (default) height for this shape. + * + * @param height the intrinsic height (in pixels) + */ + public void setIntrinsicHeight(int height) { + mShapeState.mIntrinsicHeight = height; + } + + @Override + public int getIntrinsicWidth() { + return mShapeState.mIntrinsicWidth; + } + + @Override + public int getIntrinsicHeight() { + return mShapeState.mIntrinsicHeight; + } + + @Override + public boolean getPadding(Rect padding) { + if (mShapeState.mPadding != null) { + padding.set(mShapeState.mPadding); + return true; + } else { + return super.getPadding(padding); + } + } + + private static int modulateAlpha(int paintAlpha, int alpha) { + int scale = alpha + (alpha >>> 7); // convert to 0..256 + return paintAlpha * scale >>> 8; + } + + /** + * Called from the drawable's draw() method after the canvas has been set + * to draw the shape at (0,0). Subclasses can override for special effects + * such as multiple layers, stroking, etc. + */ + protected void onDraw(Shape shape, Canvas canvas, Paint paint) { + shape.draw(canvas, paint); + } + + @Override + public void draw(Canvas canvas) { + Rect r = getBounds(); + Paint paint = mShapeState.mPaint; + + int prevAlpha = paint.getAlpha(); + paint.setAlpha(modulateAlpha(prevAlpha, mShapeState.mAlpha)); + + if (mShapeState.mShape != null) { + // need the save both for the translate, and for the (unknown) Shape + int count = canvas.save(); + canvas.translate(r.left, r.top); + onDraw(mShapeState.mShape, canvas, paint); + canvas.restoreToCount(count); + } else { + canvas.drawRect(r, paint); + } + + // restore + paint.setAlpha(prevAlpha); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mShapeState.mChangingConfigurations; + } + + /** + * Set the alpha level for this drawable [0..255]. Note that this drawable + * also has a color in its paint, which has an alpha as well. These two + * values are automatically combined during drawing. Thus if the color's + * alpha is 75% (i.e. 192) and the drawable's alpha is 50% (i.e. 128), then + * the combined alpha that will be used during drawing will be 37.5% + * (i.e. 96). + */ + @Override public void setAlpha(int alpha) { + mShapeState.mAlpha = alpha; + } + + @Override + public void setColorFilter(ColorFilter cf) { + mShapeState.mPaint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + if (mShapeState.mShape == null) { + final Paint p = mShapeState.mPaint; + if (p.getXfermode() == null) { + final int alpha = p.getAlpha(); + if (alpha == 0) { + return PixelFormat.TRANSPARENT; + } + if (alpha == 255) { + return PixelFormat.OPAQUE; + } + } + } + // not sure, so be safe + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setDither(boolean dither) { + mShapeState.mPaint.setDither(dither); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + updateShape(); + } + + /** + * Subclasses override this to parse custom subelements. + * If you handle it, return true, else return <em>super.inflateTag(...)</em>. + */ + protected boolean inflateTag(String name, Resources r, XmlPullParser parser, + AttributeSet attrs) { + + if (name.equals("padding")) { + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.ShapeDrawablePadding); + setPadding(a.getInt(com.android.internal.R.styleable.ShapeDrawablePadding_left, 0), + a.getInt(com.android.internal.R.styleable.ShapeDrawablePadding_top, 0), + a.getInt(com.android.internal.R.styleable.ShapeDrawablePadding_right, 0), + a.getInt(com.android.internal.R.styleable.ShapeDrawablePadding_bottom, 0)); + a.recycle(); + return true; + } + + return false; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); + + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawable); + + int color = mShapeState.mPaint.getColor(); + color = a.getColor(com.android.internal.R.styleable.ShapeDrawable_color, color); + mShapeState.mPaint.setColor(color); + + setIntrinsicWidth((int) + a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f)); + setIntrinsicHeight((int) + a.getDimension(com.android.internal.R.styleable.ShapeDrawable_height, 0f)); + + a.recycle(); + + int type; + final int outerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + final String name = parser.getName(); + // call our subclass + if (!inflateTag(name, r, parser, attrs)) { + android.util.Log.w("drawable", "Unknown element: " + name + + " for ShapeDrawable " + this); + } + } + } + + private void updateShape() { + if (mShapeState.mShape != null) { + final Rect r = getBounds(); + final int w = r.width(); + final int h = r.height(); + + mShapeState.mShape.resize(w, h); + if (mShapeState.mShaderFactory != null) { + mShapeState.mPaint.setShader(mShapeState.mShaderFactory.resize(w, h)); + } + } + } + + @Override + public ConstantState getConstantState() { + mShapeState.mChangingConfigurations = super.getChangingConfigurations(); + return mShapeState; + } + + /** + * Defines the intrinsic properties of this ShapeDrawable's Shape. + */ + final static class ShapeState extends ConstantState { + int mChangingConfigurations; + Paint mPaint; + Shape mShape; + Rect mPadding; + int mIntrinsicWidth; + int mIntrinsicHeight; + int mAlpha = 255; + ShaderFactory mShaderFactory; + + ShapeState(ShapeState orig) { + if (orig != null) { + mPaint = orig.mPaint; + mShape = orig.mShape; + mPadding = orig.mPadding; + mIntrinsicWidth = orig.mIntrinsicWidth; + mIntrinsicHeight = orig.mIntrinsicHeight; + mAlpha = orig.mAlpha; + mShaderFactory = orig.mShaderFactory; + } else { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + } + } + + @Override + public Drawable newDrawable() { + return new ShapeDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + } + + /** + * Base class defines a factory object that is called each time the drawable + * is resized (has a new width or height). Its resize() method returns a + * corresponding shader, or null. + * Implement this class if you'd like your ShapeDrawable to use a special + * {@link android.graphics.Shader}, such as a + * {@link android.graphics.LinearGradient}. + * + */ + public static abstract class ShaderFactory { + /** + * Returns the Shader to be drawn when a Drawable is drawn. + * The dimensions of the Drawable are passed because they may be needed + * to adjust how the Shader is configured for drawing. + * This is called by ShapeDrawable.setShape(). + * + * @param width the width of the Drawable being drawn + * @param height the heigh of the Drawable being drawn + * @return the Shader to be drawn + */ + public abstract Shader resize(int width, int height); + } + + // other subclass could wack the Shader's localmatrix based on the + // resize params (e.g. scaletofit, etc.). This could be used to scale + // a bitmap to fill the bounds without needing any other special casing. +} + diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java new file mode 100644 index 0000000..17d5a2e --- /dev/null +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2006 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.drawable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.StateSet; + +/** + * + * Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string + * ID value. + * + */ +public class StateListDrawable extends DrawableContainer { + public StateListDrawable() + { + this(null); + } + + /** + * Add a new image/string ID to the set of images. + * @param stateSet - An array of resource Ids to associate with the image. + * Switch to this image by calling setState(). + * @param drawable -The image to show. + */ + public void addState(int[] stateSet, Drawable drawable) { + if (drawable != null) { + mStateListState.addStateSet(stateSet, drawable); + // in case the new state matches our current state... + onStateChange(getState()); + } + } + + @Override + public boolean isStateful() { + return true; + } + + @Override + protected boolean onStateChange(int[] stateSet) { + int idx = mStateListState.indexOfStateSet(stateSet); + if (idx < 0) { + idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD); + } + if (selectDrawable(idx)) { + return true; + } + return super.onStateChange(stateSet); + } + + @Override public void inflate(Resources r, XmlPullParser parser, + AttributeSet attrs) + throws XmlPullParserException, IOException { + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.StateListDrawable); + + super.inflateWithAttributes(r, parser, a, + com.android.internal.R.styleable.StateListDrawable_visible); + + mStateListState.setVariablePadding(a.getBoolean( + com.android.internal.R.styleable.StateListDrawable_variablePadding, false)); + mStateListState.setConstantSize(a.getBoolean( + com.android.internal.R.styleable.StateListDrawable_constantSize, false)); + + a.recycle(); + + int type; + + final int innerDepth = parser.getDepth()+1; + int depth; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth=parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth || !parser.getName().equals("item")) { + continue; + } + + int drawableRes = 0; + + int i; + int j = 0; + final int numAttrs = attrs.getAttributeCount(); + int[] states = new int[numAttrs]; + for (i = 0; i < numAttrs; i++) { + final int stateResId = attrs.getAttributeNameResource(i); + if (stateResId == 0) break; + if (stateResId == com.android.internal.R.attr.drawable) { + drawableRes = attrs.getAttributeResourceValue(i, 0); + } else { + states[j++] = attrs.getAttributeBooleanValue(i, false) + ? stateResId + : -stateResId; + } + } + states = StateSet.trimStateSet(states, j); + + Drawable dr; + if (drawableRes != 0) { + dr = r.getDrawable(drawableRes); + } else { + while ((type=parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": <item> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + dr = Drawable.createFromXmlInner(r, parser, attrs); + } + + mStateListState.addStateSet(states, dr); + } + + onStateChange(getState()); + } + + StateListState getStateListState() { + return mStateListState; + } + + static final class StateListState extends DrawableContainerState + { + StateListState(StateListState orig, StateListDrawable owner) + { + super(orig, owner); + + if (orig != null) { + mStateSets = orig.mStateSets; + } else { + mStateSets = new int[getChildren().length][]; + } + } + + int addStateSet(int[] stateSet, Drawable drawable) { + final int pos = addChild(drawable); + mStateSets[pos] = stateSet; + return pos; + } + + private int indexOfStateSet(int[] stateSet) + { + final int[][] stateSets = mStateSets; + final int N = getChildCount(); + for (int i=0; i<N; i++) { + if (StateSet.stateSetMatches(stateSets[i], stateSet)) { + return i; + } + } + return -1; + } + + @Override + public Drawable newDrawable() + { + return new StateListDrawable(this); + } + + @Override + public void growArray(int oldSize, int newSize) + { + super.growArray(oldSize, newSize); + final int[][] newStateSets = new int[newSize][]; + System.arraycopy(mStateSets, 0, newStateSets, 0, oldSize); + mStateSets = newStateSets; + } + + private int[][] mStateSets; + } + + private StateListDrawable(StateListState state) + { + StateListState as = new StateListState(state, this); + mStateListState = as; + setConstantState(as); + onStateChange(getState()); + } + + private final StateListState mStateListState; +} + diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java new file mode 100644 index 0000000..dd65636 --- /dev/null +++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java @@ -0,0 +1,222 @@ +/* + * 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.drawable; + +import android.graphics.Canvas; +import android.os.SystemClock; + +/** + * Transition drawables are an extension of LayerDrawables and are intended to cross fade between + * the first and second layers. To start the transition, call {@link #startTransition(int)}. To + * display just the first layer, call {@link #resetTransition()} + * + */ +public class TransitionDrawable extends LayerDrawable implements Drawable.Callback { + + /** + * A transition is about to start. + */ + private static final int TRANSITION_STARTING = 0; + + /** + * The transition has started and the animation is in progress + */ + private static final int TRANSITION_RUNNING = 1; + + /** + * No transition will be applied + */ + private static final int TRANSITION_NONE = 2; + + /** + * The current state of the transition. One of {@link #TRANSITION_STARTING}, + * {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE} + */ + private int mTransitionState = TRANSITION_NONE; + + private boolean mReverse; + private long mStartTimeMillis; + private int mFrom; + private int mTo; + private int mDuration; + private TransitionState mState; + + TransitionDrawable() { + this(new TransitionState(null, null)); + } + + private TransitionDrawable(TransitionState state) { + super(state); + mState = state; + } + + @Override + LayerState createConstantState(LayerState state) { + return new TransitionState((TransitionState)state, this); + } + + /** + * Begin the second layer on top of the first layer. + * + * @param durationMillis The length of the transition in milliseconds + */ + public void startTransition(int durationMillis) { + mFrom = 0; + mTo = 255; + mState.mAlpha = 0; + mState.mDuration = mDuration = durationMillis; + mReverse = false; + mTransitionState = TRANSITION_STARTING; + invalidateSelf(); + } + + /** + * Show only the first layer. + */ + public void resetTransition() { + mState.mAlpha = 0; + mTransitionState = TRANSITION_NONE; + invalidateSelf(); + } + + /** + * Reverses the transition, picking up where the transition currently is. + * If the transition is not currently running, this will start the transition + * with the specified duration. If the transition is already running, the last + * known duration will be used. + * + * @param duration The duration to use if no transition is running. + */ + public void reverseTransition(int duration) { + final long time = SystemClock.uptimeMillis(); + // Animation is over + if (time - mStartTimeMillis > mState.mDuration) { + if (mState.mAlpha == 0) { + mFrom = 0; + mTo = 255; + mState.mAlpha = 0; + mReverse = false; + } else { + mFrom = 255; + mTo = 0; + mState.mAlpha = 255; + mReverse = true; + } + mDuration = mState.mDuration = duration; + mTransitionState = TRANSITION_STARTING; + invalidateSelf(); + return; + } + + mReverse = !mReverse; + mFrom = mState.mAlpha; + mTo = mReverse ? 0 : 255; + mDuration = (int) (mReverse ? time - mStartTimeMillis : + mState.mDuration - (time - mStartTimeMillis)); + mTransitionState = TRANSITION_STARTING; + } + + @Override + public void draw(Canvas canvas) { + boolean done = true; + final TransitionState state = mState; + + switch (mTransitionState) { + case TRANSITION_STARTING: + mStartTimeMillis = SystemClock.uptimeMillis(); + done = false; + mTransitionState = TRANSITION_RUNNING; + break; + + case TRANSITION_RUNNING: + if (mStartTimeMillis >= 0) { + float normalized = (float) + (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration; + done = normalized >= 1.0f; + normalized = Math.min(normalized, 1.0f); + state.mAlpha = (int) (mFrom + (mTo - mFrom) * normalized); + } + break; + } + + final int alpha = state.mAlpha; + final boolean crossFade = state.mCrossFade; + final Rec[] array = mLayerState.mArray; + Drawable d; + + d = array[0].mDrawable; + if (crossFade) { + d.setAlpha(255 - alpha); + } + d.draw(canvas); + if (crossFade) { + d.setAlpha(0xFF); + } + + if (alpha > 0) { + d = array[1].mDrawable; + d.setAlpha(alpha); + d.draw(canvas); + d.setAlpha(0xFF); + } + + if (!done) { + invalidateSelf(); + } + } + + /** + * Enables or disables the cross fade of the drawables. When cross fade + * is disabled, the first drawable is always drawn opaque. With cross + * fade enabled, the first drawable is drawn with the opposite alpha of + * the second drawable. + * + * @param enabled True to enable cross fading, false otherwise. + */ + public void setCrossFadeEnabled(boolean enabled) { + mState.mCrossFade = enabled; + } + + /** + * Indicates whether the cross fade is enabled for this transition. + * + * @return True if cross fading is enabled, false otherwise. + */ + public boolean isCrossFadeEnabled() { + return mState.mCrossFade; + } + + static class TransitionState extends LayerState { + int mAlpha = 0; + int mDuration; + boolean mCrossFade; + + TransitionState(TransitionState orig, TransitionDrawable owner) { + super(orig, owner); + } + + @Override + public Drawable newDrawable() { + return new TransitionDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + } +} diff --git a/graphics/java/android/graphics/drawable/package.html b/graphics/java/android/graphics/drawable/package.html new file mode 100644 index 0000000..17ee865 --- /dev/null +++ b/graphics/java/android/graphics/drawable/package.html @@ -0,0 +1,9 @@ +<HTML> +<BODY> +Provides classes to manage a variety of visual elements that are intended for +display only, such as bitmaps and gradients. These elements are often used +by widgets as background images or simply as indicators (for example, a volume +level indicator). You can create most of these in XML as described in <a +href="{@docRoot}reference/available-resources.html">Resources</a>. +</BODY> +</HTML> diff --git a/graphics/java/android/graphics/drawable/shapes/ArcShape.java b/graphics/java/android/graphics/drawable/shapes/ArcShape.java new file mode 100644 index 0000000..b90e853 --- /dev/null +++ b/graphics/java/android/graphics/drawable/shapes/ArcShape.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 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.drawable.shapes; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; + +/** + * Creates an arc shape. The arc shape starts at a specified + * angle and sweeps clockwise, drawing slices of pie. + * The arc can be drawn to a Canvas with its own draw() method, + * but more graphical control is available if you instead pass + * the ArcShape to a {@link android.graphics.drawable.ShapeDrawable}. + */ +public class ArcShape extends RectShape { + private float mStart; + private float mSweep; + + /** + * ArcShape constructor. + * + * @param startAngle the angle (in degrees) where the arc begins + * @param sweepAngle the sweep angle (in degrees). Anything equal to or + * greater than 360 results in a complete circle/oval. + */ + public ArcShape(float startAngle, float sweepAngle) { + mStart = startAngle; + mSweep = sweepAngle; + } + + @Override + public void draw(Canvas canvas, Paint paint) { + canvas.drawArc(rect(), mStart, mSweep, true, paint); + } +} + diff --git a/graphics/java/android/graphics/drawable/shapes/OvalShape.java b/graphics/java/android/graphics/drawable/shapes/OvalShape.java new file mode 100644 index 0000000..c914999 --- /dev/null +++ b/graphics/java/android/graphics/drawable/shapes/OvalShape.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007 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.drawable.shapes; + +import android.graphics.Canvas; +import android.graphics.Paint; + +/** + * Defines an oval shape. + * The oval can be drawn to a Canvas with its own draw() method, + * but more graphical control is available if you instead pass + * the OvalShape to a {@link android.graphics.drawable.ShapeDrawable}. + */ +public class OvalShape extends RectShape { + + /** + * OvalShape constructor. + */ + public OvalShape() {} + + @Override + public void draw(Canvas canvas, Paint paint) { + canvas.drawOval(rect(), paint); + } +} + diff --git a/graphics/java/android/graphics/drawable/shapes/PathShape.java b/graphics/java/android/graphics/drawable/shapes/PathShape.java new file mode 100644 index 0000000..3656a0b --- /dev/null +++ b/graphics/java/android/graphics/drawable/shapes/PathShape.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 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.drawable.shapes; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; + +/** + * Creates geometric paths, utilizing the {@link android.graphics.Path} class. + * The path can be drawn to a Canvas with its own draw() method, + * but more graphical control is available if you instead pass + * the PathShape to a {@link android.graphics.drawable.ShapeDrawable}. + */ +public class PathShape extends Shape { + private Path mPath; + private float mStdWidth; + private float mStdHeight; + + private float mScaleX; // cached from onResize + private float mScaleY; // cached from onResize + + /** + * PathShape constructor. + * + * @param path a Path that defines the geometric paths for this shape + * @param stdWidth the standard width for the shape. Any changes to the + * width with resize() will result in a width scaled based + * on the new width divided by this width. + * @param stdHeight the standard height for the shape. Any changes to the + * height with resize() will result in a height scaled based + * on the new height divided by this height. + */ + public PathShape(Path path, float stdWidth, float stdHeight) { + mPath = path; + mStdWidth = stdWidth; + mStdHeight = stdHeight; + } + + @Override + public void draw(Canvas canvas, Paint paint) { + canvas.save(); + canvas.scale(mScaleX, mScaleY); + canvas.drawPath(mPath, paint); + canvas.restore(); + } + + @Override + protected void onResize(float width, float height) { + mScaleX = width / mStdWidth; + mScaleY = height / mStdHeight; + } +} + diff --git a/graphics/java/android/graphics/drawable/shapes/RectShape.java b/graphics/java/android/graphics/drawable/shapes/RectShape.java new file mode 100644 index 0000000..4f038ae --- /dev/null +++ b/graphics/java/android/graphics/drawable/shapes/RectShape.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 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.drawable.shapes; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; + +/** + * Defines a rectangle shape. + * The rectangle can be drawn to a Canvas with its own draw() method, + * but more graphical control is available if you instead pass + * the RectShape to a {@link android.graphics.drawable.ShapeDrawable}. + */ +public class RectShape extends Shape { + private RectF mRect = new RectF(); + + /** + * RectShape constructor. + */ + public RectShape() {} + + @Override + public void draw(Canvas canvas, Paint paint) { + canvas.drawRect(mRect, paint); + } + + @Override + protected void onResize(float width, float height) { + mRect.set(0, 0, width, height); + } + + /** + * Returns the RectF that defines this rectangle's bounds. + */ + protected final RectF rect() { + return mRect; + } +} diff --git a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java new file mode 100644 index 0000000..54ef3f7 --- /dev/null +++ b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2007 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.drawable.shapes; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; + +/** + * Creates a rounded-corner rectangle. Optionally, an inset (rounded) rectangle + * can be included (to make a sort of "O" shape). + * The rounded rectangle can be drawn to a Canvas with its own draw() method, + * but more graphical control is available if you instead pass + * the RoundRectShape to a {@link android.graphics.drawable.ShapeDrawable}. + */ +public class RoundRectShape extends RectShape { + private float[] mOuterRadii; + private RectF mInset; + private float[] mInnerRadii; + + private RectF mInnerRect; + private Path mPath; // this is what we actually draw + + /** + * RoundRectShape constructor. + * Specifies an outer (round)rect and an optional inner (round)rect. + * + * @param outerRadii An array of 8 radius values, for the outer roundrect. + * The first two floats are for the + * top-left corner (remaining pairs correspond clockwise). + * For no rounded corners on the outer rectangle, + * pass null. + * @param inset A RectF that specifies the distance from the inner + * rect to each side of the outer rect. + * For no inner, pass null. + * @param innerRadii An array of 8 radius values, for the inner roundrect. + * The first two floats are for the + * top-left corner (remaining pairs correspond clockwise). + * For no rounded corners on the inner rectangle, + * pass null. + * If inset parameter is null, this parameter is ignored. + */ + public RoundRectShape(float[] outerRadii, RectF inset, + float[] innerRadii) { + if (outerRadii.length < 8) { + throw new ArrayIndexOutOfBoundsException( + "outer radii must have >= 8 values"); + } + if (innerRadii != null && innerRadii.length < 8) { + throw new ArrayIndexOutOfBoundsException( + "inner radii must have >= 8 values"); + } + mOuterRadii = outerRadii; + mInset = inset; + mInnerRadii = innerRadii; + + if (inset != null) { + mInnerRect = new RectF(); + } + mPath = new Path(); + } + + @Override + public void draw(Canvas canvas, Paint paint) { + canvas.drawPath(mPath, paint); + } + + @Override + protected void onResize(float w, float h) { + super.onResize(w, h); + + RectF r = rect(); + mPath.reset(); + + if (mOuterRadii != null) { + mPath.addRoundRect(r, mOuterRadii, Path.Direction.CW); + } else { + mPath.addRect(r, Path.Direction.CW); + } + if (mInnerRect != null) { + mInnerRect.set(r.left + mInset.left, r.top + mInset.top, + r.right - mInset.right, r.bottom - mInset.bottom); + if (mInnerRect.width() < w && mInnerRect.height() < h) { + if (mInnerRadii != null) { + mPath.addRoundRect(mInnerRect, mInnerRadii, + Path.Direction.CCW); + } else { + mPath.addRect(mInnerRect, Path.Direction.CCW); + } + } + } + } +} diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java new file mode 100644 index 0000000..fc54bc1 --- /dev/null +++ b/graphics/java/android/graphics/drawable/shapes/Shape.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007 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.drawable.shapes; + +import android.graphics.Canvas; +import android.graphics.Paint; + +/** + * Defines a generic graphical "shape." + * Any Shape can be drawn to a Canvas with its own draw() method, + * but more graphical control is available if you instead pass + * it to a {@link android.graphics.drawable.ShapeDrawable}. + */ +public abstract class Shape { + private float mWidth; + private float mHeight; + + /** + * Returns the width of the Shape. + */ + public final float getWidth() { + return mWidth; + } + + /** + * Returns the height of the Shape. + */ + public final float getHeight() { + return mHeight; + } + + + /** + * Draw this shape into the provided Canvas, with the provided Paint. + * Before calling this, you must call {@link #resize(float,float)}. + * + * @param canvas the Canvas within which this shape should be drawn + * @param paint the Paint object that defines this shape's characteristics + */ + public abstract void draw(Canvas canvas, Paint paint); + + + /** + * Resizes the dimensions of this shape. + * Must be called before {@link #draw(Canvas,Paint)}. + * + * @param width the width of the shape (in pixels) + * @param height the height of the shape (in pixels) + */ + public final void resize(float width, float height) { + if (width < 0) { + width = 0; + } + if (height < 0) { + height =0; + } + if (mWidth != width || mHeight != height) { + mWidth = width; + mHeight = height; + onResize(width, height); + } + } + + /** + * Checks whether the Shape is opaque. + * Default impl returns true. Override if your subclass can be opaque. + * + * @return true if any part of the drawable is <em>not</em> opaque. + */ + public boolean hasAlpha() { + return true; + } + + /** + * Callback method called when {@link #resize(float,float)} is executed. + * + * @param width the new width of the Shape + * @param height the new height of the Shape + */ + protected void onResize(float width, float height) {} +} diff --git a/graphics/java/android/graphics/drawable/shapes/package.html b/graphics/java/android/graphics/drawable/shapes/package.html new file mode 100644 index 0000000..5bcfa0f --- /dev/null +++ b/graphics/java/android/graphics/drawable/shapes/package.html @@ -0,0 +1,5 @@ +<HTML> +<BODY> +Contains classes for drawing geometric shapes. +</BODY> +</HTML>
\ No newline at end of file diff --git a/graphics/java/android/graphics/package.html b/graphics/java/android/graphics/package.html new file mode 100644 index 0000000..b4a2769 --- /dev/null +++ b/graphics/java/android/graphics/package.html @@ -0,0 +1,6 @@ +<HTML> +<BODY> +Provides low level graphics tools such as canvases, color filters, points, and +rectangles that let you handle drawing to the screen directly. +</BODY> +</HTML> diff --git a/graphics/java/com/android/internal/graphics/NativeUtils.java b/graphics/java/com/android/internal/graphics/NativeUtils.java new file mode 100644 index 0000000..c91b7d9 --- /dev/null +++ b/graphics/java/com/android/internal/graphics/NativeUtils.java @@ -0,0 +1,40 @@ +/* + * 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 com.android.internal.graphics; + +import android.graphics.Canvas; +import android.graphics.Rect; + +public final class NativeUtils { + /** + * This class is uninstantiable. + */ + private NativeUtils() { + // This space intentionally left blank. + } + + /** + * Scroll a rectangular portion of a canvas. + * + * @param canvas the canvas to manipulate + * @param src the source rectangle + * @param dx horizontal offset + * @param dy vertical offset + */ + public static native boolean nativeScrollRect(Canvas canvas, Rect src, + int dx, int dy); +} |