summaryrefslogtreecommitdiffstats
path: root/graphics/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/java/android')
-rw-r--r--graphics/java/android/graphics/Atlas.java441
-rw-r--r--graphics/java/android/graphics/Bitmap.java304
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java322
-rw-r--r--graphics/java/android/graphics/BitmapRegionDecoder.java12
-rw-r--r--graphics/java/android/graphics/BitmapShader.java15
-rw-r--r--graphics/java/android/graphics/Camera.java10
-rw-r--r--graphics/java/android/graphics/Canvas.java151
-rw-r--r--graphics/java/android/graphics/Color.java18
-rw-r--r--graphics/java/android/graphics/ComposeShader.java39
-rw-r--r--graphics/java/android/graphics/ImageFormat.java117
-rw-r--r--graphics/java/android/graphics/LinearGradient.java61
-rw-r--r--graphics/java/android/graphics/Matrix.java45
-rw-r--r--graphics/java/android/graphics/Movie.java18
-rw-r--r--graphics/java/android/graphics/NinePatch.java209
-rw-r--r--graphics/java/android/graphics/Paint.java128
-rw-r--r--graphics/java/android/graphics/Path.java111
-rw-r--r--graphics/java/android/graphics/PixelFormat.java64
-rw-r--r--graphics/java/android/graphics/RadialGradient.java57
-rw-r--r--graphics/java/android/graphics/Shader.java22
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java70
-rw-r--r--graphics/java/android/graphics/SweepGradient.java48
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java89
-rw-r--r--graphics/java/android/graphics/drawable/ClipDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/ColorDrawable.java1
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java51
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java401
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java24
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java22
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java35
-rw-r--r--graphics/java/android/graphics/drawable/LevelListDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/MipmapDrawable.java312
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java93
-rw-r--r--graphics/java/android/graphics/drawable/RotateDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/ScaleDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/ShapeDrawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java17
-rw-r--r--graphics/java/android/graphics/pdf/PdfDocument.java456
-rw-r--r--graphics/java/android/graphics/pdf/package.html5
-rw-r--r--graphics/java/android/renderscript/Allocation.java122
-rw-r--r--graphics/java/android/renderscript/AllocationAdapter.java1
-rw-r--r--graphics/java/android/renderscript/BaseObj.java33
-rw-r--r--graphics/java/android/renderscript/Element.java7
-rw-r--r--graphics/java/android/renderscript/RenderScript.java86
-rw-r--r--graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java130
-rw-r--r--graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java14
-rw-r--r--graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java16
-rw-r--r--graphics/java/android/renderscript/ScriptIntrinsicHistogram.java186
-rw-r--r--graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java6
-rw-r--r--graphics/java/android/renderscript/Type.java16
51 files changed, 3469 insertions, 951 deletions
diff --git a/graphics/java/android/graphics/Atlas.java b/graphics/java/android/graphics/Atlas.java
new file mode 100644
index 0000000..39a5a53
--- /dev/null
+++ b/graphics/java/android/graphics/Atlas.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2013 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;
+
+/**
+ * @hide
+ */
+public class Atlas {
+ /**
+ * This flag indicates whether the packing algorithm will attempt
+ * to rotate entries to make them fit better in the atlas.
+ */
+ public static final int FLAG_ALLOW_ROTATIONS = 0x1;
+ /**
+ * This flag indicates whether the packing algorithm should leave
+ * an empty 1 pixel wide border around each bitmap. This border can
+ * be useful if the content of the atlas will be used in OpenGL using
+ * bilinear filtering.
+ */
+ public static final int FLAG_ADD_PADDING = 0x2;
+ /**
+ * Default flags: allow rotations and add padding.
+ */
+ public static final int FLAG_DEFAULTS = FLAG_ADD_PADDING;
+
+ /**
+ * Each type defines a different packing algorithm that can
+ * be used by an {@link Atlas}. The best algorithm to use
+ * will depend on the source dataset and the dimensions of
+ * the atlas.
+ */
+ public enum Type {
+ SliceMinArea,
+ SliceMaxArea,
+ SliceShortAxis,
+ SliceLongAxis
+ }
+
+ /**
+ * Represents a bitmap packed in the atlas. Each entry has a location in
+ * pixels in the atlas and a rotation flag. If the entry was rotated, the
+ * bitmap must be rotated by 90 degrees (in either direction as long as
+ * the origin remains the same) before being rendered into the atlas.
+ */
+ public static class Entry {
+ /**
+ * Location, in pixels, of the bitmap on the X axis in the atlas.
+ */
+ public int x;
+ /**
+ * Location, in pixels, of the bitmap on the Y axis in the atlas.
+ */
+ public int y;
+
+ /**
+ * If true, the bitmap must be rotated 90 degrees in the atlas.
+ */
+ public boolean rotated;
+ }
+
+ private final Policy mPolicy;
+
+ /**
+ * Creates a new atlas with the specified algorithm and dimensions
+ * in pixels. Calling this constructor is equivalent to calling
+ * {@link #Atlas(Atlas.Type, int, int, int)} with {@link #FLAG_DEFAULTS}.
+ *
+ * @param type The algorithm to use to pack rectangles in the atlas
+ * @param width The width of the atlas in pixels
+ * @param height The height of the atlas in pixels
+ *
+ * @see #Atlas(Atlas.Type, int, int, int)
+ */
+ public Atlas(Type type, int width, int height) {
+ this(type, width, height, FLAG_DEFAULTS);
+ }
+
+ /**
+ * Creates a new atlas with the specified algorithm and dimensions
+ * in pixels. A set of flags can also be specified to control the
+ * behavior of the atlas.
+ *
+ * @param type The algorithm to use to pack rectangles in the atlas
+ * @param width The width of the atlas in pixels
+ * @param height The height of the atlas in pixels
+ * @param flags Optional flags to control the behavior of the atlas:
+ * {@link #FLAG_ADD_PADDING}, {@link #FLAG_ALLOW_ROTATIONS}
+ *
+ * @see #Atlas(Atlas.Type, int, int)
+ */
+ public Atlas(Type type, int width, int height, int flags) {
+ mPolicy = findPolicy(type, width, height, flags);
+ }
+
+ /**
+ * Packs a rectangle of the specified dimensions in this atlas.
+ *
+ * @param width The width of the rectangle to pack in the atlas
+ * @param height The height of the rectangle to pack in the atlas
+ *
+ * @return An {@link Entry} instance if the rectangle was packed in
+ * the atlas, or null if the rectangle could not fit
+ *
+ * @see #pack(int, int, Atlas.Entry)
+ */
+ public Entry pack(int width, int height) {
+ return pack(width, height, null);
+ }
+
+ /**
+ * Packs a rectangle of the specified dimensions in this atlas.
+ *
+ * @param width The width of the rectangle to pack in the atlas
+ * @param height The height of the rectangle to pack in the atlas
+ * @param entry Out parameter that will be filled in with the location
+ * and attributes of the packed rectangle, can be null
+ *
+ * @return An {@link Entry} instance if the rectangle was packed in
+ * the atlas, or null if the rectangle could not fit
+ *
+ * @see #pack(int, int)
+ */
+ public Entry pack(int width, int height, Entry entry) {
+ if (entry == null) entry = new Entry();
+ return mPolicy.pack(width, height, entry);
+ }
+
+ private static Policy findPolicy(Type type, int width, int height, int flags) {
+ switch (type) {
+ case SliceMinArea:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.MinAreaSplitDecision());
+ case SliceMaxArea:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.MaxAreaSplitDecision());
+ case SliceShortAxis:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.ShorterFreeAxisSplitDecision());
+ case SliceLongAxis:
+ return new SlicePolicy(width, height, flags,
+ new SlicePolicy.LongerFreeAxisSplitDecision());
+ }
+ return null;
+ }
+
+ /**
+ * A policy defines how the atlas performs the packing operation.
+ */
+ private static abstract class Policy {
+ abstract Entry pack(int width, int height, Entry entry);
+ }
+
+ /**
+ * The Slice algorightm divides the remaining empty space either
+ * horizontally or vertically after a bitmap is placed in the atlas.
+ *
+ * NOTE: the algorithm is explained below using a tree but is
+ * implemented using a linked list instead for performance reasons.
+ *
+ * The algorithm starts with a single empty cell covering the entire
+ * atlas:
+ *
+ * -----------------------
+ * | |
+ * | |
+ * | |
+ * | Empty space |
+ * | (C0) |
+ * | |
+ * | |
+ * | |
+ * -----------------------
+ *
+ * The tree of cells looks like this:
+ *
+ * N0(free)
+ *
+ * The algorithm then places a bitmap B1, if possible:
+ *
+ * -----------------------
+ * | | |
+ * | B1 | |
+ * | | |
+ * |-------- |
+ * | |
+ * | |
+ * | |
+ * | |
+ * -----------------------
+ *
+ * After placing a bitmap in an empty cell, the algorithm splits
+ * the remaining space in two new empty cells. The split can occur
+ * vertically or horizontally (this is controlled by the "split
+ * decision" parameter of the algorithm.)
+ *
+ * Here is for the instance the result of a vertical split:
+ *
+ * -----------------------
+ * | | |
+ * | B1 | |
+ * | | |
+ * |--------| C2 |
+ * | | |
+ * | | |
+ * | C1 | |
+ * | | |
+ * -----------------------
+ *
+ * The cells tree now looks like this:
+ *
+ * C0(occupied)
+ * / \
+ * / \
+ * / \
+ * / \
+ * C1(free) C2(free)
+ *
+ * For each bitmap to place in the atlas, the Slice algorithm
+ * will visit the free cells until it finds one where a bitmap can
+ * fit. It will then split the now occupied cell and proceed onto
+ * the next bitmap.
+ */
+ private static class SlicePolicy extends Policy {
+ private final Cell mRoot = new Cell();
+
+ private final SplitDecision mSplitDecision;
+
+ private final boolean mAllowRotation;
+ private final int mPadding;
+
+ /**
+ * A cell represents a sub-rectangle of the atlas. A cell is
+ * a node in a linked list representing the available free
+ * space in the atlas.
+ */
+ private static class Cell {
+ int x;
+ int y;
+
+ int width;
+ int height;
+
+ Cell next;
+
+ @Override
+ public String toString() {
+ return String.format("cell[x=%d y=%d width=%d height=%d", x, y, width, height);
+ }
+ }
+
+ SlicePolicy(int width, int height, int flags, SplitDecision splitDecision) {
+ mAllowRotation = (flags & FLAG_ALLOW_ROTATIONS) != 0;
+ mPadding = (flags & FLAG_ADD_PADDING) != 0 ? 1 : 0;
+
+ // The entire atlas is empty at first, minus padding
+ Cell first = new Cell();
+ first.x = first.y = mPadding;
+ first.width = width - 2 * mPadding;
+ first.height = height - 2 * mPadding;
+
+ mRoot.next = first;
+ mSplitDecision = splitDecision;
+ }
+
+ @Override
+ Entry pack(int width, int height, Entry entry) {
+ Cell cell = mRoot.next;
+ Cell prev = mRoot;
+
+ while (cell != null) {
+ if (insert(cell, prev, width, height, entry)) {
+ return entry;
+ }
+
+ prev = cell;
+ cell = cell.next;
+ }
+
+ return null;
+ }
+
+ /**
+ * Defines how the remaining empty space should be split up:
+ * vertically or horizontally.
+ */
+ private static interface SplitDecision {
+ /**
+ * Returns true if the remaining space defined by
+ * <code>freeWidth</code> and <code>freeHeight</code>
+ * should be split horizontally.
+ *
+ * @param freeWidth The rectWidth of the free space after packing a rectangle
+ * @param freeHeight The rectHeight of the free space after packing a rectangle
+ * @param rectWidth The rectWidth of the rectangle that was packed in a cell
+ * @param rectHeight The rectHeight of the rectangle that was packed in a cell
+ */
+ boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight);
+ }
+
+ // Splits the free area horizontally to minimize the horizontal section area
+ private static class MinAreaSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return rectWidth * freeHeight > freeWidth * rectHeight;
+ }
+ }
+
+ // Splits the free area horizontally to maximize the horizontal section area
+ private static class MaxAreaSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return rectWidth * freeHeight <= freeWidth * rectHeight;
+ }
+ }
+
+ // Splits the free area horizontally if the horizontal axis is shorter
+ private static class ShorterFreeAxisSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return freeWidth <= freeHeight;
+ }
+ }
+
+ // Splits the free area horizontally if the vertical axis is shorter
+ private static class LongerFreeAxisSplitDecision implements SplitDecision {
+ @Override
+ public boolean splitHorizontal(int freeWidth, int freeHeight,
+ int rectWidth, int rectHeight) {
+ return freeWidth > freeHeight;
+ }
+ }
+
+ /**
+ * Attempts to pack a rectangle of specified dimensions in the available
+ * empty space.
+ *
+ * @param cell The cell representing free space in which to pack the rectangle
+ * @param prev The previous cell in the free space linked list
+ * @param width The width of the rectangle to pack
+ * @param height The height of the rectangle to pack
+ * @param entry Stores the location of the packged rectangle, if it fits
+ *
+ * @return True if the rectangle was packed in the atlas, false otherwise
+ */
+ @SuppressWarnings("SuspiciousNameCombination")
+ private boolean insert(Cell cell, Cell prev, int width, int height, Entry entry) {
+ boolean rotated = false;
+
+ // If the rectangle doesn't fit we'll try to rotate it
+ // if possible before giving up
+ if (cell.width < width || cell.height < height) {
+ if (mAllowRotation) {
+ if (cell.width < height || cell.height < width) {
+ return false;
+ }
+
+ // Rotate the rectangle
+ int temp = width;
+ width = height;
+ height = temp;
+ rotated = true;
+ } else {
+ return false;
+ }
+ }
+
+ // Remaining free space after packing the rectangle
+ int deltaWidth = cell.width - width;
+ int deltaHeight = cell.height - height;
+
+ // Split the remaining free space into two new cells
+ Cell first = new Cell();
+ Cell second = new Cell();
+
+ first.x = cell.x + width + mPadding;
+ first.y = cell.y;
+ first.width = deltaWidth - mPadding;
+
+ second.x = cell.x;
+ second.y = cell.y + height + mPadding;
+ second.height = deltaHeight - mPadding;
+
+ if (mSplitDecision.splitHorizontal(deltaWidth, deltaHeight,
+ width, height)) {
+ first.height = height;
+ second.width = cell.width;
+ } else {
+ first.height = cell.height;
+ second.width = width;
+
+ // The order of the cells matters for efficient packing
+ // We want to give priority to the cell chosen by the
+ // split decision heuristic
+ Cell temp = first;
+ first = second;
+ second = temp;
+ }
+
+ // Remove degenerate cases to keep the free list as small as possible
+ if (first.width > 0 && first.height > 0) {
+ prev.next = first;
+ prev = first;
+ }
+
+ if (second.width > 0 && second.height > 0) {
+ prev.next = second;
+ second.next = cell.next;
+ } else {
+ prev.next = cell.next;
+ }
+
+ // The cell is now completely removed from the free list
+ cell.next = null;
+
+ // Return the location and rotation of the packed rectangle
+ entry.x = cell.x;
+ entry.y = cell.y;
+ entry.rotated = rotated;
+
+ return true;
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 89abdef..74838ee 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -46,20 +46,32 @@ public final class Bitmap implements Parcelable {
/**
* Backing buffer for the Bitmap.
* Made public for quick access from drawing methods -- do NOT modify
- * from outside this class.
+ * from outside this class
*
* @hide
*/
+ @SuppressWarnings("UnusedDeclaration") // native code only
public byte[] mBuffer;
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
private final BitmapFinalizer mFinalizer;
private final boolean mIsMutable;
+
+ /**
+ * Represents whether the Bitmap's content is expected to be pre-multiplied.
+ * Note that isPremultiplied() does not directly return this value, because
+ * isPremultiplied() may never return true for a 565 Bitmap.
+ *
+ * setPremultiplied() does directly set the value so that setConfig() and
+ * setPremultiplied() aren't order dependent, despite being setters.
+ */
+ private boolean mIsPremultiplied;
+
private byte[] mNinePatchChunk; // may be null
private int[] mLayoutBounds; // may be null
- private int mWidth = -1;
- private int mHeight = -1;
+ private int mWidth;
+ private int mHeight;
private boolean mRecycled;
// Package-scoped for fast access.
@@ -88,38 +100,26 @@ public final class Bitmap implements Parcelable {
}
/**
- * @noinspection UnusedDeclaration
- */
- /* Private constructor that must received an already allocated native
- bitmap int (pointer).
-
- This can be called from JNI code.
- */
- Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
- int density) {
- this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density);
- }
-
- /**
- * @noinspection UnusedDeclaration
+ * Private constructor that must received an already allocated native bitmap
+ * int (pointer).
*/
- /* Private constructor that must received an already allocated native
- bitmap int (pointer).
-
- This can be called from JNI code.
- */
- Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
- int[] layoutBounds, int density) {
+ @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
+ Bitmap(int nativeBitmap, byte[] buffer, int width, int height, int density,
+ boolean isMutable, boolean isPremultiplied,
+ byte[] ninePatchChunk, int[] layoutBounds) {
if (nativeBitmap == 0) {
throw new RuntimeException("internal error: native bitmap is 0");
}
+ mWidth = width;
+ mHeight = height;
+ mIsMutable = isMutable;
+ mIsPremultiplied = isPremultiplied;
mBuffer = buffer;
// we delete this in our finalizer
mNativeBitmap = nativeBitmap;
mFinalizer = new BitmapFinalizer(nativeBitmap);
- mIsMutable = isMutable;
mNinePatchChunk = ninePatchChunk;
mLayoutBounds = layoutBounds;
if (density >= 0) {
@@ -128,6 +128,17 @@ public final class Bitmap implements Parcelable {
}
/**
+ * Native bitmap has been reconfigured, so set premult and cached
+ * width/height values
+ */
+ @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
+ void reinit(int width, int height, boolean isPremultiplied) {
+ mWidth = width;
+ mHeight = height;
+ mIsPremultiplied = isPremultiplied;
+ }
+
+ /**
* <p>Returns the density for this bitmap.</p>
*
* <p>The default density is the same density as the current display,
@@ -167,7 +178,98 @@ public final class Bitmap implements Parcelable {
public void setDensity(int density) {
mDensity = density;
}
-
+
+ /**
+ * <p>Modifies the bitmap to have a specified width, height, and {@link
+ * Config}, without affecting the underlying allocation backing the bitmap.
+ * Bitmap pixel data is not re-initialized for the new configuration.</p>
+ *
+ * <p>This method can be used to avoid allocating a new bitmap, instead
+ * reusing an existing bitmap's allocation for a new configuration of equal
+ * or lesser size. If the Bitmap's allocation isn't large enough to support
+ * the new configuration, an IllegalArgumentException will be thrown and the
+ * bitmap will not be modified.</p>
+ *
+ * <p>The result of {@link #getByteCount()} will reflect the new configuration,
+ * while {@link #getAllocationByteCount()} will reflect that of the initial
+ * configuration.</p>
+ *
+ * <p>WARNING: This method should NOT be called on a bitmap currently used
+ * by the view system. It does not make guarantees about how the underlying
+ * pixel buffer is remapped to the new config, just that the allocation is
+ * reused. Additionally, the view system does not account for bitmap
+ * properties being modifying during use, e.g. while attached to
+ * drawables.</p>
+ *
+ * @see #setWidth(int)
+ * @see #setHeight(int)
+ * @see #setConfig(Config)
+ */
+ public void reconfigure(int width, int height, Config config) {
+ checkRecycled("Can't call reconfigure() on a recycled bitmap");
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width and height must be > 0");
+ }
+ if (!isMutable()) {
+ throw new IllegalStateException("only mutable bitmaps may be reconfigured");
+ }
+ if (mBuffer == null) {
+ throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
+ }
+
+ nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length);
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+ * with the current height and config.</p>
+ *
+ * <p>WARNING: this method should not be used on bitmaps currently used by
+ * the view system, see {@link #reconfigure(int, int, Config)} for more
+ * details.</p>
+ *
+ * @see #reconfigure(int, int, Config)
+ * @see #setHeight(int)
+ * @see #setConfig(Config)
+ */
+ public void setWidth(int width) {
+ reconfigure(width, getHeight(), getConfig());
+ }
+
+ /**
+ * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+ * with the current width and config.</p>
+ *
+ * <p>WARNING: this method should not be used on bitmaps currently used by
+ * the view system, see {@link #reconfigure(int, int, Config)} for more
+ * details.</p>
+ *
+ * @see #reconfigure(int, int, Config)
+ * @see #setWidth(int)
+ * @see #setConfig(Config)
+ */
+ public void setHeight(int height) {
+ reconfigure(getWidth(), height, getConfig());
+ }
+
+ /**
+ * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+ * with the current height and width.</p>
+ *
+ * <p>WARNING: this method should not be used on bitmaps currently used by
+ * the view system, see {@link #reconfigure(int, int, Config)} for more
+ * details.</p>
+ *
+ * @see #reconfigure(int, int, Config)
+ * @see #setWidth(int)
+ * @see #setHeight(int)
+ */
+ public void setConfig(Config config) {
+ reconfigure(getWidth(), getHeight(), config);
+ }
+
/**
* Sets the nine patch chunk.
*
@@ -289,7 +391,7 @@ public final class Bitmap implements Parcelable {
* No color information is stored.
* With this configuration, each pixel requires 1 byte of memory.
*/
- ALPHA_8 (2),
+ ALPHA_8 (1),
/**
* Each pixel is stored on 2 bytes and only the RGB channels are
@@ -305,7 +407,7 @@ public final class Bitmap implements Parcelable {
* This configuration may be useful when using opaque bitmaps
* that do not require high color fidelity.
*/
- RGB_565 (4),
+ RGB_565 (3),
/**
* Each pixel is stored on 2 bytes. The three RGB color channels
@@ -318,12 +420,16 @@ public final class Bitmap implements Parcelable {
*
* It is recommended to use {@link #ARGB_8888} instead of this
* configuration.
+ *
+ * Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},
+ * any bitmap created with this configuration will be created
+ * using {@link #ARGB_8888} instead.
*
* @deprecated Because of the poor quality of this configuration,
* it is advised to use {@link #ARGB_8888} instead.
*/
@Deprecated
- ARGB_4444 (5),
+ ARGB_4444 (4),
/**
* Each pixel is stored on 4 bytes. Each channel (RGB and alpha
@@ -333,13 +439,13 @@ public final class Bitmap implements Parcelable {
* This configuration is very flexible and offers the best
* quality. It should be used whenever possible.
*/
- ARGB_8888 (6);
+ ARGB_8888 (5);
final int nativeInt;
@SuppressWarnings({"deprecation"})
private static Config sConfigs[] = {
- null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
+ null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
};
Config(int ni) {
@@ -449,6 +555,7 @@ public final class Bitmap implements Parcelable {
checkRecycled("Can't copy a recycled bitmap");
Bitmap b = nativeCopy(mNativeBitmap, config.nativeInt, isMutable);
if (b != null) {
+ b.setAlphaAndPremultiplied(hasAlpha(), mIsPremultiplied);
b.mDensity = mDensity;
}
return b;
@@ -457,7 +564,7 @@ public final class Bitmap implements Parcelable {
/**
* Creates a new bitmap, scaled from an existing bitmap, when possible. If the
* specified width and height are the same as the current width and height of
- * the source btimap, the source bitmap is returned and now new bitmap is
+ * the source bitmap, the source bitmap is returned and no new bitmap is
* created.
*
* @param src The source bitmap.
@@ -465,6 +572,7 @@ public final class Bitmap implements Parcelable {
* @param dstHeight The new bitmap's desired height.
* @param filter true if the source should be filtered.
* @return The new scaled bitmap or the source bitmap if no scaling is required.
+ * @throws IllegalArgumentException if width is <= 0, or height is <= 0
*/
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight,
boolean filter) {
@@ -517,6 +625,9 @@ public final class Bitmap implements Parcelable {
* @param width The number of pixels in each row
* @param height The number of rows
* @return A copy of a subset of the source bitmap or the source bitmap itself.
+ * @throws IllegalArgumentException if the x, y, width, height values are
+ * outside of the dimensions of the source bitmap, or width is <= 0,
+ * or height is <= 0
*/
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {
return createBitmap(source, x, y, width, height, null, false);
@@ -543,7 +654,8 @@ public final class Bitmap implements Parcelable {
* 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.
+ * outside of the dimensions of the source bitmap, or width is <= 0,
+ * or height is <= 0
*/
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
Matrix m, boolean filter) {
@@ -616,11 +728,12 @@ public final class Bitmap implements Parcelable {
paint.setAntiAlias(true);
}
}
-
+
// The new bitmap was created from a known bitmap source so assume that
// they use the same density
bitmap.mDensity = source.mDensity;
-
+ bitmap.setAlphaAndPremultiplied(source.hasAlpha(), source.mIsPremultiplied);
+
canvas.setBitmap(bitmap);
canvas.drawBitmap(source, srcR, dstR, paint);
canvas.setBitmap(null);
@@ -698,15 +811,13 @@ public final class Bitmap implements Parcelable {
if (display != null) {
bm.mDensity = display.densityDpi;
}
+ bm.setHasAlpha(hasAlpha);
if (config == Config.ARGB_8888 && !hasAlpha) {
nativeErase(bm.mNativeBitmap, 0xff000000);
- nativeSetHasAlpha(bm.mNativeBitmap, hasAlpha);
- } else {
- // No need to initialize it to zeroes; it is backed by a VM byte array
- // which is by definition preinitialized to all zeroes.
- //
- //nativeErase(bm.mNativeBitmap, 0);
}
+ // No need to initialize the bitmap to zeroes with other configs;
+ // it is backed by a VM byte array which is by definition preinitialized
+ // to all zeroes.
return bm;
}
@@ -904,22 +1015,60 @@ public final class Bitmap implements Parcelable {
* <p>This method only returns true if {@link #hasAlpha()} returns true.
* A bitmap with no alpha channel can be used both as a pre-multiplied and
* as a non pre-multiplied bitmap.</p>
- *
+ *
+ * <p>Only pre-multiplied bitmaps may be drawn by the view system or
+ * {@link Canvas}. If a non-pre-multiplied bitmap with an alpha channel is
+ * drawn to a Canvas, a RuntimeException will be thrown.</p>
+ *
* @return true if the underlying pixels have been pre-multiplied, false
* otherwise
+ *
+ * @see Bitmap#setPremultiplied(boolean)
+ * @see BitmapFactory.Options#inPremultiplied
*/
public final boolean isPremultiplied() {
- return getConfig() != Config.RGB_565 && hasAlpha();
+ return mIsPremultiplied && getConfig() != Config.RGB_565 && hasAlpha();
+ }
+
+ /**
+ * Sets whether the bitmap should treat its data as pre-multiplied.
+ *
+ * <p>Bitmaps are always treated as pre-multiplied by the view system and
+ * {@link Canvas} for performance reasons. Storing un-pre-multiplied data in
+ * a Bitmap (through {@link #setPixel}, {@link #setPixels}, or {@link
+ * BitmapFactory.Options#inPremultiplied BitmapFactory.Options.inPremultiplied})
+ * can lead to incorrect blending if drawn by the framework.</p>
+ *
+ * <p>This method will not affect the behavior of a bitmap without an alpha
+ * channel, or if {@link #hasAlpha()} returns false.</p>
+ *
+ * <p>Calling createBitmap() or createScaledBitmap() with a source
+ * Bitmap whose colors are not pre-multiplied may result in a RuntimeException,
+ * since those functions require drawing the source, which is not supported for
+ * un-pre-multiplied Bitmaps.</p>
+ *
+ * @see Bitmap#isPremultiplied()
+ * @see BitmapFactory.Options#inPremultiplied
+ */
+ public final void setPremultiplied(boolean premultiplied) {
+ mIsPremultiplied = premultiplied;
+ nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha(), premultiplied);
+ }
+
+ /** Helper function to set both alpha and premultiplied. **/
+ private final void setAlphaAndPremultiplied(boolean hasAlpha, boolean premultiplied) {
+ mIsPremultiplied = premultiplied;
+ nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, premultiplied);
}
/** Returns the bitmap's width */
public final int getWidth() {
- return mWidth == -1 ? mWidth = nativeWidth(mNativeBitmap) : mWidth;
+ return mWidth;
}
/** Returns the bitmap's height */
public final int getHeight() {
- return mHeight == -1 ? mHeight = nativeHeight(mNativeBitmap) : mHeight;
+ return mHeight;
}
/**
@@ -994,6 +1143,10 @@ public final class Bitmap implements Parcelable {
* getPixels() or setPixels(), then the pixels are uniformly treated as
* 32bit values, packed according to the Color class.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, this method
+ * should not be used to calculate the memory usage of the bitmap. Instead,
+ * see {@link #getAllocationByteCount()}.
+ *
* @return number of bytes between rows of the native bitmap pixels.
*/
public final int getRowBytes() {
@@ -1001,7 +1154,11 @@ public final class Bitmap implements Parcelable {
}
/**
- * Returns the number of bytes used to store this bitmap's pixels.
+ * Returns the minimum number of bytes that can be used to store this bitmap's pixels.
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, the result of this method can
+ * no longer be used to determine memory usage of a bitmap. See {@link
+ * #getAllocationByteCount()}.</p>
*/
public final int getByteCount() {
// int result permits bitmaps up to 46,340 x 46,340
@@ -1009,6 +1166,29 @@ public final class Bitmap implements Parcelable {
}
/**
+ * Returns the size of the allocated memory used to store this bitmap's pixels.
+ *
+ * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
+ * decode other bitmaps of smaller size, or by manual reconfiguration. See {@link
+ * #reconfigure(int, int, Config)}, {@link #setWidth(int)}, {@link #setHeight(int)}, {@link
+ * #setConfig(Bitmap.Config)}, and {@link BitmapFactory.Options#inBitmap
+ * BitmapFactory.Options.inBitmap}. If a bitmap is not modified in this way, this value will be
+ * the same as that returned by {@link #getByteCount()}.</p>
+ *
+ * <p>This value will not change over the lifetime of a Bitmap.</p>
+ *
+ * @see #reconfigure(int, int, Config)
+ */
+ public final int getAllocationByteCount() {
+ if (mBuffer == null) {
+ // native backed bitmaps don't support reconfiguration,
+ // so alloc size is always content size
+ return getByteCount();
+ }
+ return mBuffer.length;
+ }
+
+ /**
* If the bitmap's internal config is in one of the public formats, return
* that config, otherwise return null.
*/
@@ -1039,7 +1219,7 @@ public final class Bitmap implements Parcelable {
* non-opaque per-pixel alpha values.
*/
public void setHasAlpha(boolean hasAlpha) {
- nativeSetHasAlpha(mNativeBitmap, hasAlpha);
+ nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, mIsPremultiplied);
}
/**
@@ -1113,7 +1293,7 @@ public final class Bitmap implements Parcelable {
public int getPixel(int x, int y) {
checkRecycled("Can't call getPixel() on a recycled bitmap");
checkPixelAccess(x, y);
- return nativeGetPixel(mNativeBitmap, x, y);
+ return nativeGetPixel(mNativeBitmap, x, y, mIsPremultiplied);
}
/**
@@ -1147,7 +1327,7 @@ public final class Bitmap implements Parcelable {
}
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
nativeGetPixels(mNativeBitmap, pixels, offset, stride,
- x, y, width, height);
+ x, y, width, height, mIsPremultiplied);
}
/**
@@ -1227,7 +1407,7 @@ public final class Bitmap implements Parcelable {
throw new IllegalStateException();
}
checkPixelAccess(x, y);
- nativeSetPixel(mNativeBitmap, x, y, color);
+ nativeSetPixel(mNativeBitmap, x, y, color, mIsPremultiplied);
}
/**
@@ -1264,7 +1444,7 @@ public final class Bitmap implements Parcelable {
}
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
nativeSetPixels(mNativeBitmap, pixels, offset, stride,
- x, y, width, height);
+ x, y, width, height, mIsPremultiplied);
}
public static final Parcelable.Creator<Bitmap> CREATOR
@@ -1400,31 +1580,32 @@ public final class Bitmap implements Parcelable {
private static native Bitmap nativeCreate(int[] colors, int offset,
int stride, int width, int height,
- int nativeConfig, boolean mutable);
+ 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 boolean nativeRecycle(int nativeBitmap);
+ private static native void nativeReconfigure(int nativeBitmap, int width, int height,
+ int config, int allocSize);
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 int nativeGetPixel(int nativeBitmap, int x, int y);
+ private static native int nativeGetPixel(int nativeBitmap, int x, int y,
+ boolean isPremultiplied);
private static native void nativeGetPixels(int nativeBitmap, int[] pixels,
- int offset, int stride, int x,
- int y, int width, int height);
+ int offset, int stride, int x, int y,
+ int width, int height, boolean isPremultiplied);
private static native void nativeSetPixel(int nativeBitmap, int x, int y,
- int color);
+ int color, boolean isPremultiplied);
private static native void nativeSetPixels(int nativeBitmap, int[] colors,
- int offset, int stride, int x,
- int y, int width, int height);
+ int offset, int stride, int x, int y,
+ int width, int height, boolean isPremultiplied);
private static native void nativeCopyPixelsToBuffer(int nativeBitmap,
Buffer dst);
private static native void nativeCopyPixelsFromBuffer(int nb, Buffer src);
@@ -1443,7 +1624,8 @@ public final class Bitmap implements Parcelable {
private static native void nativePrepareToDraw(int nativeBitmap);
private static native boolean nativeHasAlpha(int nativeBitmap);
- private static native void nativeSetHasAlpha(int nBitmap, boolean hasAlpha);
+ private static native void nativeSetAlphaAndPremultiplied(int nBitmap, boolean hasAlpha,
+ boolean isPremul);
private static native boolean nativeHasMipMap(int nativeBitmap);
private static native void nativeSetHasMipMap(int nBitmap, boolean hasMipMap);
private static native boolean nativeSameAs(int nb0, int nb1);
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index f155cd2..9b07da9 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,11 +18,11 @@ package android.graphics;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
-import java.io.BufferedInputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
@@ -43,28 +43,59 @@ public class BitmapFactory {
public Options() {
inDither = false;
inScaled = true;
+ inPremultiplied = true;
}
/**
* If set, decode methods that take the Options object will attempt to
- * reuse this bitmap when loading content. If the decode operation cannot
- * use this bitmap, the decode method will return <code>null</code> and
- * will throw an IllegalArgumentException. The current implementation
- * necessitates that the reused bitmap be mutable and of the same size as the
- * source content. The source content must be in jpeg or png format (whether as
- * a resource or as a stream). The {@link android.graphics.Bitmap.Config
- * configuration} of the reused bitmap will override the setting of
- * {@link #inPreferredConfig}, if set. The reused bitmap will continue to
- * remain mutable even when decoding a resource which would normally result
- * in an immutable bitmap.
+ * reuse this bitmap when loading content. If the decode operation
+ * cannot use this bitmap, the decode method will return
+ * <code>null</code> and will throw an IllegalArgumentException. The
+ * current implementation necessitates that the reused bitmap be
+ * mutable, and the resulting reused bitmap will continue to remain
+ * mutable even when decoding a resource which would normally result in
+ * an immutable bitmap.</p>
*
* <p>You should still always use the returned Bitmap of the decode
* method and not assume that reusing the bitmap worked, due to the
* constraints outlined above and failure situations that can occur.
* Checking whether the return value matches the value of the inBitmap
- * set in the Options structure is a way to see if the bitmap was reused,
- * but in all cases you should use the returned Bitmap to make sure
- * that you are using the bitmap that was used as the decode destination.</p>
+ * set in the Options structure will indicate if the bitmap was reused,
+ * but in all cases you should use the Bitmap returned by the decoding
+ * function to ensure that you are using the bitmap that was used as the
+ * decode destination.</p>
+ *
+ * <h3>Usage with BitmapFactory</h3>
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, any
+ * mutable bitmap can be reused by {@link BitmapFactory} to decode any
+ * other bitmaps as long as the resulting {@link Bitmap#getByteCount()
+ * byte count} of the decoded bitmap is less than or equal to the {@link
+ * Bitmap#getAllocationByteCount() allocated byte count} of the reused
+ * bitmap. This can be because the intrinsic size is smaller, or its
+ * size post scaling (for density / sample size) is smaller.</p>
+ *
+ * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT}
+ * additional constraints apply: The image being decoded (whether as a
+ * resource or as a stream) must be in jpeg or png format. Only equal
+ * sized bitmaps are supported, with {@link #inSampleSize} set to 1.
+ * Additionally, the {@link android.graphics.Bitmap.Config
+ * configuration} of the reused bitmap will override the setting of
+ * {@link #inPreferredConfig}, if set.</p>
+ *
+ * <h3>Usage with BitmapRegionDecoder</h3>
+ *
+ * <p>BitmapRegionDecoder will draw its requested content into the Bitmap
+ * provided, clipping if the output content size (post scaling) is larger
+ * than the provided Bitmap. The provided Bitmap's width, height, and
+ * {@link Bitmap.Config} will not be changed.
+ *
+ * <p class="note">BitmapRegionDecoder support for {@link #inBitmap} was
+ * introduced in {@link android.os.Build.VERSION_CODES#JELLY_BEAN}. All
+ * formats supported by BitmapRegionDecoder support Bitmap reuse via
+ * {@link #inBitmap}.</p>
+ *
+ * @see Bitmap#reconfigure(int,int, android.graphics.Bitmap.Config)
*/
public Bitmap inBitmap;
@@ -108,6 +139,30 @@ public class BitmapFactory {
public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
/**
+ * If true (which is the default), the resulting bitmap will have its
+ * color channels pre-multipled by the alpha channel.
+ *
+ * <p>This should NOT be set to false for images to be directly drawn by
+ * the view system or through a {@link Canvas}. The view system and
+ * {@link Canvas} assume all drawn images are pre-multiplied to simplify
+ * draw-time blending, and will throw a RuntimeException when
+ * un-premultiplied are drawn.</p>
+ *
+ * <p>This is likely only useful if you want to manipulate raw encoded
+ * image data, e.g. with RenderScript or custom OpenGL.</p>
+ *
+ * <p>This does not affect bitmaps without an alpha channel.</p>
+ *
+ * <p>Setting this flag to false while setting {@link #inScaled} to true
+ * may result in incorrect colors.</p>
+ *
+ * @see Bitmap#hasAlpha()
+ * @see Bitmap#isPremultiplied()
+ * @see #inScaled
+ */
+ public boolean inPremultiplied;
+
+ /**
* If dither is true, the decoder will attempt to dither the decoded
* image.
*/
@@ -192,9 +247,15 @@ public class BitmapFactory {
* rather than relying on the graphics system scaling it each time it
* is drawn to a Canvas.
*
+ * <p>BitmapRegionDecoder ignores this flag, and will not scale output
+ * based on density. (though {@link #inSampleSize} is supported)</p>
+ *
* <p>This flag is turned on by default and should be turned off if you need
* a non-scaled version of the bitmap. Nine-patch bitmaps ignore this
* flag and are always scaled.
+ *
+ * <p>If {@link #inPremultiplied} is set to false, and the image has alpha,
+ * setting this flag to true may result in incorrect colors.
*/
public boolean inScaled;
@@ -205,14 +266,26 @@ public class BitmapFactory {
* (e.g. the bitmap is drawn, getPixels() is called), they will be
* automatically re-decoded.
*
- * For the re-decode to happen, the bitmap must have access to the
+ * <p>For the re-decode to happen, the bitmap must have access to the
* encoded data, either by sharing a reference to the input
* or by making a copy of it. This distinction is controlled by
* inInputShareable. If this is true, then the bitmap may keep a shallow
* reference to the input. If this is false, then the bitmap will
* explicitly make a copy of the input data, and keep that. Even if
* sharing is allowed, the implementation may still decide to make a
- * deep copy of the input data.
+ * deep copy of the input data.</p>
+ *
+ * <p>While inPurgeable can help avoid big Dalvik heap allocations (from
+ * API level 11 onward), it sacrifices performance predictability since any
+ * image that the view system tries to draw may incur a decode delay which
+ * can lead to dropped frames. Therefore, most apps should avoid using
+ * inPurgeable to allow for a fast and fluid UI. To minimize Dalvik heap
+ * allocations use the {@link #inBitmap} flag instead.</p>
+ *
+ * <p class="note"><strong>Note:</strong> This flag is ignored when used
+ * with {@link #decodeResource(Resources, int,
+ * android.graphics.BitmapFactory.Options)} or {@link #decodeFile(String,
+ * android.graphics.BitmapFactory.Options)}.</p>
*/
public boolean inPurgeable;
@@ -426,11 +499,21 @@ public class BitmapFactory {
if ((offset | length) < 0 || data.length < offset + length) {
throw new ArrayIndexOutOfBoundsException();
}
- Bitmap bm = nativeDecodeByteArray(data, offset, length, opts);
- if (bm == null && opts != null && opts.inBitmap != null) {
- throw new IllegalArgumentException("Problem decoding into existing bitmap");
+ Bitmap bm;
+
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
+ try {
+ bm = nativeDecodeByteArray(data, offset, length, opts);
+
+ if (bm == null && opts != null && opts.inBitmap != null) {
+ throw new IllegalArgumentException("Problem decoding into existing bitmap");
+ }
+ setDensityFromOptions(bm, opts);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
+
return bm;
}
@@ -448,6 +531,31 @@ public class BitmapFactory {
}
/**
+ * Set the newly decoded bitmap's density based on the Options.
+ */
+ private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
+ if (outputBitmap == null || opts == null) return;
+
+ final int density = opts.inDensity;
+ if (density != 0) {
+ outputBitmap.setDensity(density);
+ final int targetDensity = opts.inTargetDensity;
+ if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
+ return;
+ }
+
+ byte[] np = outputBitmap.getNinePatchChunk();
+ final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
+ if (opts.inScaled || isNinePatch) {
+ outputBitmap.setDensity(targetDensity);
+ }
+ } else if (opts.inBitmap != null) {
+ // bitmap was reused, ensure density is reset
+ outputBitmap.setDensity(Bitmap.getDefaultDensity());
+ }
+ }
+
+ /**
* 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
@@ -464,6 +572,11 @@ public class BitmapFactory {
* @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)
+ *
+ * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
+ * if {@link InputStream#markSupported is.markSupported()} returns true,
+ * <code>is.mark(1024)</code> would be called. As of
+ * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
*/
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
// we don't throw in this case, thus allowing the caller to only check
@@ -472,123 +585,41 @@ public class BitmapFactory {
return null;
}
- // we need mark/reset to work properly
-
- if (!is.markSupported()) {
- is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
- }
-
- // 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;
- boolean finish = true;
-
- if (is instanceof AssetManager.AssetInputStream) {
- final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
-
- if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
- float scale = 1.0f;
- int targetDensity = 0;
- if (opts != null) {
- final int density = opts.inDensity;
- targetDensity = opts.inTargetDensity;
- if (density != 0 && targetDensity != 0) {
- scale = targetDensity / (float) density;
- }
- }
-
- bm = nativeDecodeAsset(asset, outPadding, opts, true, scale);
- if (bm != null && targetDensity != 0) bm.setDensity(targetDensity);
+ Bitmap bm = null;
- finish = false;
- } else {
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
+ try {
+ if (is instanceof AssetManager.AssetInputStream) {
+ final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
bm = nativeDecodeAsset(asset, 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];
-
- if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
- float scale = 1.0f;
- int targetDensity = 0;
- if (opts != null) {
- final int density = opts.inDensity;
- targetDensity = opts.inTargetDensity;
- if (density != 0 && targetDensity != 0) {
- scale = targetDensity / (float) density;
- }
- }
-
- bm = nativeDecodeStream(is, tempStorage, outPadding, opts, true, scale);
- if (bm != null && targetDensity != 0) bm.setDensity(targetDensity);
-
- finish = false;
} else {
- bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
+ bm = decodeStreamInternal(is, outPadding, opts);
}
- }
-
- if (bm == null && opts != null && opts.inBitmap != null) {
- throw new IllegalArgumentException("Problem decoding into existing bitmap");
- }
-
- return finish ? finishDecode(bm, outPadding, opts) : bm;
- }
- private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
- if (bm == null || opts == null) {
- return bm;
- }
-
- final int density = opts.inDensity;
- if (density == 0) {
- return bm;
- }
-
- bm.setDensity(density);
- final int targetDensity = opts.inTargetDensity;
- if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
- return bm;
- }
- byte[] np = bm.getNinePatchChunk();
- int[] lb = bm.getLayoutBounds();
- final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
- if (opts.inScaled || isNinePatch) {
- float scale = targetDensity / (float) density;
- if (scale != 1.0f) {
- final Bitmap oldBitmap = bm;
- bm = Bitmap.createScaledBitmap(oldBitmap,
- Math.max(1, (int) (bm.getWidth() * scale + 0.5f)),
- Math.max(1, (int) (bm.getHeight() * scale + 0.5f)), true);
- if (bm != oldBitmap) oldBitmap.recycle();
-
- if (isNinePatch) {
- np = nativeScaleNinePatch(np, scale, outPadding);
- bm.setNinePatchChunk(np);
- }
- if (lb != null) {
- int[] newLb = new int[lb.length];
- for (int i=0; i<lb.length; i++) {
- newLb[i] = (int)((lb[i]*scale)+.5f);
- }
- bm.setLayoutBounds(newLb);
- }
+ if (bm == null && opts != null && opts.inBitmap != null) {
+ throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
- bm.setDensity(targetDensity);
+ setDensityFromOptions(bm, opts);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
return bm;
}
-
+
+ /**
+ * Private helper function for decoding an InputStream natively. Buffers the input enough to
+ * do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
+ */
+ private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
+ // ASSERT(is != null);
+ byte [] tempStorage = null;
+ if (opts != null) tempStorage = opts.inTempStorage;
+ if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
+ return nativeDecodeStream(is, tempStorage, outPadding, opts);
+ }
+
/**
* 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.
@@ -614,26 +645,36 @@ public class BitmapFactory {
* 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.
+ * image should be completely decoded, or just its size returned.
* @return the decoded bitmap, or null
*/
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
- if (nativeIsSeekable(fd)) {
- Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+ Bitmap bm;
+
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
+ try {
+ if (nativeIsSeekable(fd)) {
+ bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+ } else {
+ FileInputStream fis = new FileInputStream(fd);
+ try {
+ bm = decodeStreamInternal(fis, outPadding, opts);
+ } finally {
+ try {
+ fis.close();
+ } catch (Throwable t) {/* ignore */}
+ }
+ }
+
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
- return finishDecode(bm, outPadding, opts);
- } else {
- FileInputStream fis = new FileInputStream(fd);
- try {
- return decodeStream(fis, outPadding, opts);
- } finally {
- try {
- fis.close();
- } catch (Throwable t) {/* ignore */}
- }
+
+ setDensityFromOptions(bm, opts);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
+ return bm;
}
/**
@@ -650,15 +691,10 @@ public class BitmapFactory {
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
- private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
- Rect padding, Options opts, boolean applyScale, float scale);
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 nativeDecodeAsset(int asset, Rect padding, Options opts,
- boolean applyScale, float scale);
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
- private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
private static native boolean nativeIsSeekable(FileDescriptor fd);
}
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index b38d107..3a99977 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -17,7 +17,6 @@ package android.graphics;
import android.content.res.AssetManager;
-import java.io.BufferedInputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
@@ -105,15 +104,14 @@ public final class BitmapRegionDecoder {
* allowing sharing may degrade the decoding speed.
* @return BitmapRegionDecoder, or null if the image data could not be decoded.
* @throws IOException if the image format is not supported or can not be decoded.
+ *
+ * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
+ * if {@link InputStream#markSupported is.markSupported()} returns true,
+ * <code>is.mark(1024)</code> would be called. As of
+ * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
*/
public static BitmapRegionDecoder newInstance(InputStream is,
boolean isShareable) throws IOException {
- // we need mark/reset to work properly in JNI
-
- if (!is.markSupported()) {
- is = new BufferedInputStream(is, 16 * 1024);
- }
-
if (is instanceof AssetManager.AssetInputStream) {
return nativeNewInstance(
((AssetManager.AssetInputStream) is).getAssetInt(),
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index f74d0ef..a4f75b9 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -28,6 +28,9 @@ public class BitmapShader extends Shader {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
public final Bitmap mBitmap;
+ private TileMode mTileX;
+ private TileMode mTileY;
+
/**
* Call this to create a new shader that will draw with a bitmap.
*
@@ -37,11 +40,23 @@ public class BitmapShader extends Shader {
*/
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
mBitmap = bitmap;
+ mTileX = tileX;
+ mTileY = tileY;
final int b = bitmap.ni();
native_instance = nativeCreate(b, tileX.nativeInt, tileY.nativeInt);
native_shader = nativePostCreate(native_instance, b, tileX.nativeInt, tileY.nativeInt);
}
+ /**
+ * @hide
+ */
+ @Override
+ protected Shader copy() {
+ final BitmapShader copy = new BitmapShader(mBitmap, mTileX, mTileY);
+ copyLocalMatrix(copy);
+ return copy;
+ }
+
private static native int nativeCreate(int native_bitmap, int shaderTileModeX,
int shaderTileModeY);
private static native int nativePostCreate(int native_shader, int native_bitmap,
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index 6f71a2b..9e07bd4 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -22,6 +22,8 @@ package android.graphics;
* {@link Canvas}.
*/
public class Camera {
+ private Matrix mMatrix;
+
/**
* Creates a new camera, with empty transformations.
*/
@@ -147,7 +149,13 @@ public class Camera {
* @param canvas The Canvas to set the transform matrix onto
*/
public void applyToCanvas(Canvas canvas) {
- nativeApplyToCanvas(canvas.mNativeCanvas);
+ if (canvas.isHardwareAccelerated()) {
+ if (mMatrix == null) mMatrix = new Matrix();
+ getMatrix(mMatrix);
+ canvas.concat(mMatrix);
+ } else {
+ nativeApplyToCanvas(canvas.mNativeCanvas);
+ }
}
public native float dotWithNormal(float dx, float dy, float dz);
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index c851844..d46238f 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -37,12 +37,14 @@ import javax.microedition.khronos.opengles.GL;
* Canvas and Drawables</a> developer guide.</p></div>
*/
public class Canvas {
+
// assigned in constructors or setBitmap, freed in finalizer
- int mNativeCanvas;
-
+ /** @hide */
+ public int mNativeCanvas;
+
// may be null
private Bitmap mBitmap;
-
+
// optional field set by the caller
private DrawFilter mDrawFilter;
@@ -59,7 +61,7 @@ public class Canvas {
protected int mScreenDensity = Bitmap.DENSITY_NONE;
// Used by native code
- @SuppressWarnings({"UnusedDeclaration"})
+ @SuppressWarnings("UnusedDeclaration")
private int mSurfaceFormat;
/**
@@ -79,10 +81,9 @@ public class Canvas {
private static final int MAXMIMUM_BITMAP_SIZE = 32766;
// This field is used to finalize the native Canvas properly
- @SuppressWarnings({"UnusedDeclaration"})
private final CanvasFinalizer mFinalizer;
- private static class CanvasFinalizer {
+ private static final class CanvasFinalizer {
private int mNativeCanvas;
public CanvasFinalizer(int nativeCanvas) {
@@ -92,13 +93,18 @@ public class Canvas {
@Override
protected void finalize() throws Throwable {
try {
- if (mNativeCanvas != 0) {
- finalizer(mNativeCanvas);
- }
+ dispose();
} finally {
super.finalize();
}
}
+
+ public void dispose() {
+ if (mNativeCanvas != 0) {
+ finalizer(mNativeCanvas);
+ mNativeCanvas = 0;
+ }
+ }
}
/**
@@ -108,9 +114,13 @@ public class Canvas {
* canvas.
*/
public Canvas() {
- // 0 means no native bitmap
- mNativeCanvas = initRaster(0);
- mFinalizer = new CanvasFinalizer(mNativeCanvas);
+ if (!isHardwareAccelerated()) {
+ // 0 means no native bitmap
+ mNativeCanvas = initRaster(0);
+ mFinalizer = new CanvasFinalizer(mNativeCanvas);
+ } else {
+ mFinalizer = null;
+ }
}
/**
@@ -126,19 +136,20 @@ public class Canvas {
if (!bitmap.isMutable()) {
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
}
- throwIfRecycled(bitmap);
+ throwIfCannotDraw(bitmap);
mNativeCanvas = initRaster(bitmap.ni());
mFinalizer = new CanvasFinalizer(mNativeCanvas);
mBitmap = bitmap;
mDensity = bitmap.mDensity;
}
-
- Canvas(int nativeCanvas) {
+
+ /** @hide */
+ public Canvas(int nativeCanvas) {
if (nativeCanvas == 0) {
throw new IllegalStateException();
}
mNativeCanvas = nativeCanvas;
- mFinalizer = new CanvasFinalizer(nativeCanvas);
+ mFinalizer = new CanvasFinalizer(mNativeCanvas);
mDensity = Bitmap.getDefaultDensity();
}
@@ -155,7 +166,18 @@ public class Canvas {
}
finalizer(oldCanvas);
}
-
+
+ /**
+ * Gets the native canvas pointer.
+ *
+ * @return The native pointer.
+ *
+ * @hide
+ */
+ public int getNativeCanvas() {
+ return mNativeCanvas;
+ }
+
/**
* Returns null.
*
@@ -203,7 +225,7 @@ public class Canvas {
if (!bitmap.isMutable()) {
throw new IllegalStateException();
}
- throwIfRecycled(bitmap);
+ throwIfCannotDraw(bitmap);
safeCanvasSwap(initRaster(bitmap.ni()), true);
mDensity = bitmap.mDensity;
@@ -364,8 +386,8 @@ public class Canvas {
*/
public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
return native_saveLayer(mNativeCanvas, bounds,
- paint != null ? paint.mNativePaint : 0,
- saveFlags);
+ paint != null ? paint.mNativePaint : 0,
+ saveFlags);
}
/**
@@ -374,8 +396,8 @@ public class Canvas {
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);
+ paint != null ? paint.mNativePaint : 0,
+ saveFlags);
}
/**
@@ -495,12 +517,13 @@ public class Canvas {
public native void skew(float sx, float sy);
/**
- * Preconcat the current matrix with the specified matrix.
+ * Preconcat the current matrix with the specified matrix. If the specified
+ * matrix is null, this method does nothing.
*
* @param matrix The matrix to preconcatenate with the current matrix
*/
public void concat(Matrix matrix) {
- native_concat(mNativeCanvas, matrix.native_instance);
+ if (matrix != null) native_concat(mNativeCanvas, matrix.native_instance);
}
/**
@@ -1022,7 +1045,7 @@ public class Canvas {
throw new NullPointerException();
}
native_drawArc(mNativeCanvas, oval, startAngle, sweepAngle,
- useCenter, paint.mNativePaint);
+ useCenter, paint.mNativePaint);
}
/**
@@ -1052,28 +1075,47 @@ public class Canvas {
public void drawPath(Path path, Paint paint) {
native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint);
}
-
- private static void throwIfRecycled(Bitmap bitmap) {
+
+ /**
+ * @hide
+ */
+ protected static void throwIfCannotDraw(Bitmap bitmap) {
if (bitmap.isRecycled()) {
throw new RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap);
}
+ if (!bitmap.isPremultiplied() && bitmap.getConfig() == Bitmap.Config.ARGB_8888 &&
+ bitmap.hasAlpha()) {
+ throw new RuntimeException("Canvas: trying to use a non-premultiplied bitmap "
+ + bitmap);
+ }
}
/**
* Draws the specified bitmap as an N-patch (most often, a 9-patches.)
*
- * Note: Only supported by hardware accelerated canvas at the moment.
- *
- * @param bitmap The bitmap to draw as an N-patch
- * @param chunks The patches information (matches the native struct Res_png_9patch)
+ * @param patch The ninepatch object to render
* @param dst The destination rectangle.
* @param paint The paint to draw the bitmap with. may be null
*
* @hide
*/
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
- }
-
+ public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
+ patch.drawSoftware(this, dst, paint);
+ }
+
+ /**
+ * Draws the specified bitmap as an N-patch (most often, a 9-patches.)
+ *
+ * @param patch The ninepatch object to render
+ * @param dst The destination rectangle.
+ * @param paint The paint to draw the bitmap with. may be null
+ *
+ * @hide
+ */
+ public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+ patch.drawSoftware(this, dst, paint);
+ }
+
/**
* Draw the specified bitmap, with its top/left corner at (x,y), using
* the specified paint, transformed by the current matrix.
@@ -1094,7 +1136,7 @@ public class Canvas {
* @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);
+ throwIfCannotDraw(bitmap);
native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top,
paint != null ? paint.mNativePaint : 0, mDensity, mScreenDensity, bitmap.mDensity);
}
@@ -1125,7 +1167,7 @@ public class Canvas {
if (dst == null) {
throw new NullPointerException();
}
- throwIfRecycled(bitmap);
+ throwIfCannotDraw(bitmap);
native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
}
@@ -1156,9 +1198,9 @@ public class Canvas {
if (dst == null) {
throw new NullPointerException();
}
- throwIfRecycled(bitmap);
+ throwIfCannotDraw(bitmap);
native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
- paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+ paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
}
/**
@@ -1279,8 +1321,8 @@ public class Canvas {
checkRange(colors.length, colorOffset, count);
}
nativeDrawBitmapMesh(mNativeCanvas, bitmap.ni(), meshWidth, meshHeight,
- verts, vertOffset, colors, colorOffset,
- paint != null ? paint.mNativePaint : 0);
+ verts, vertOffset, colors, colorOffset,
+ paint != null ? paint.mNativePaint : 0);
}
public enum VertexMode {
@@ -1342,8 +1384,8 @@ public class Canvas {
checkRange(indices.length, indexOffset, indexCount);
}
nativeDrawVertices(mNativeCanvas, mode.nativeInt, vertexCount, verts,
- vertOffset, texs, texOffset, colors, colorOffset,
- indices, indexOffset, indexCount, paint.mNativePaint);
+ vertOffset, texs, texOffset, colors, colorOffset,
+ indices, indexOffset, indexCount, paint.mNativePaint);
}
/**
@@ -1414,10 +1456,10 @@ public class Canvas {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
- paint.mBidiFlags, paint.mNativePaint);
+ paint.mBidiFlags, paint.mNativePaint);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawText(this, start, end, x, y,
- paint);
+ paint);
} else {
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
@@ -1538,7 +1580,7 @@ public class Canvas {
throw new IndexOutOfBoundsException();
}
native_drawPosText(mNativeCanvas, text, index, count, pos,
- paint.mNativePaint);
+ paint.mNativePaint);
}
/**
@@ -1579,8 +1621,8 @@ public class Canvas {
throw new ArrayIndexOutOfBoundsException();
}
native_drawTextOnPath(mNativeCanvas, text, index, count,
- path.ni(), hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ path.ni(), hOffset, vOffset,
+ paint.mBidiFlags, paint.mNativePaint);
}
/**
@@ -1616,7 +1658,9 @@ public class Canvas {
*/
public void drawPicture(Picture picture) {
picture.endRecording();
- native_drawPicture(mNativeCanvas, picture.ni());
+ int restoreCount = save();
+ picture.draw(this);
+ restoreToCount(restoreCount);
}
/**
@@ -1647,6 +1691,15 @@ public class Canvas {
}
/**
+ * Releases the resources associated with this canvas.
+ *
+ * @hide
+ */
+ public void release() {
+ mFinalizer.dispose();
+ }
+
+ /**
* Free up as much memory as possible from private caches (e.g. fonts, images)
*
* @hide
@@ -1792,7 +1845,5 @@ public class Canvas {
float hOffset,
float vOffset,
int flags, 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
index 933948d..8fbedae 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -406,18 +406,18 @@ public class Color {
sColorNameMap.put("yellow", YELLOW);
sColorNameMap.put("cyan", CYAN);
sColorNameMap.put("magenta", MAGENTA);
- sColorNameMap.put("aqua", 0x00FFFF);
- sColorNameMap.put("fuchsia", 0xFF00FF);
+ sColorNameMap.put("aqua", 0xFF00FFFF);
+ sColorNameMap.put("fuchsia", 0xFFFF00FF);
sColorNameMap.put("darkgrey", DKGRAY);
sColorNameMap.put("grey", GRAY);
sColorNameMap.put("lightgrey", LTGRAY);
- sColorNameMap.put("lime", 0x00FF00);
- sColorNameMap.put("maroon", 0x800000);
- sColorNameMap.put("navy", 0x000080);
- sColorNameMap.put("olive", 0x808000);
- sColorNameMap.put("purple", 0x800080);
- sColorNameMap.put("silver", 0xC0C0C0);
- sColorNameMap.put("teal", 0x008080);
+ sColorNameMap.put("lime", 0xFF00FF00);
+ sColorNameMap.put("maroon", 0xFF800000);
+ sColorNameMap.put("navy", 0xFF000080);
+ sColorNameMap.put("olive", 0xFF808000);
+ sColorNameMap.put("purple", 0xFF800080);
+ sColorNameMap.put("silver", 0xFFC0C0C0);
+ sColorNameMap.put("teal", 0xFF008080);
}
}
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 241ab17..de0d3d6 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -16,10 +16,22 @@
package android.graphics;
-/** A subclass of shader that returns the coposition of two other shaders, combined by
+/** A subclass of shader that returns the composition of two other shaders, combined by
an {@link android.graphics.Xfermode} subclass.
*/
public class ComposeShader extends Shader {
+
+ private static final int TYPE_XFERMODE = 1;
+ private static final int TYPE_PORTERDUFFMODE = 2;
+
+ /**
+ * Type of the ComposeShader: can be either TYPE_XFERMODE or TYPE_PORTERDUFFMODE
+ */
+ private int mType;
+
+ private Xfermode mXferMode;
+ private PorterDuff.Mode mPorterDuffMode;
+
/**
* Hold onto the shaders to avoid GC.
*/
@@ -37,8 +49,10 @@ public class ComposeShader extends Shader {
is null, then SRC_OVER is assumed.
*/
public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) {
+ mType = TYPE_XFERMODE;
mShaderA = shaderA;
mShaderB = shaderB;
+ mXferMode = mode;
native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
(mode != null) ? mode.native_instance : 0);
if (mode instanceof PorterDuffXfermode) {
@@ -59,14 +73,37 @@ public class ComposeShader extends Shader {
@param mode The PorterDuff mode that combines the colors from the two shaders.
*/
public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
+ mType = TYPE_PORTERDUFFMODE;
mShaderA = shaderA;
mShaderB = shaderB;
+ mPorterDuffMode = mode;
native_instance = nativeCreate2(shaderA.native_instance, shaderB.native_instance,
mode.nativeInt);
native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
shaderB.native_shader, mode.nativeInt);
}
+ /**
+ * @hide
+ */
+ @Override
+ protected Shader copy() {
+ final ComposeShader copy;
+ switch (mType) {
+ case TYPE_XFERMODE:
+ copy = new ComposeShader(mShaderA.copy(), mShaderB.copy(), mXferMode);
+ break;
+ case TYPE_PORTERDUFFMODE:
+ copy = new ComposeShader(mShaderA.copy(), mShaderB.copy(), mPorterDuffMode);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "ComposeShader should be created with either Xfermode or PorterDuffMode");
+ }
+ copyLocalMatrix(copy);
+ return copy;
+ }
+
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,
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index f6b747a..1bcfc18 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -73,6 +73,66 @@ public class ImageFormat {
public static final int YV12 = 0x32315659;
/**
+ * <p>Android Y8 format.</p>
+ *
+ * <p>Y8 is a YUV planar format comprised of a WxH Y plane only, with each pixel
+ * being represented by 8 bits. It is equivalent to just the Y plane from {@link #YV12}
+ * format.</p>
+ *
+ * <p>This format assumes
+ * <ul>
+ * <li>an even width</li>
+ * <li>an even height</li>
+ * <li>a horizontal stride multiple of 16 pixels</li>
+ * </ul>
+ * </p>
+ *
+ * <pre> y_size = stride * height </pre>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.camera2.CameraDevice}
+ * through a {@link android.media.ImageReader} object if this format is
+ * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.camera2.CameraDevice
+ *
+ * @hide
+ */
+ public static final int Y8 = 0x20203859;
+
+ /**
+ * <p>Android Y16 format.</p>
+ *
+ * Y16 is a YUV planar format comprised of a WxH Y plane, with each pixel
+ * being represented by 16 bits. It is just like {@link #Y8}, but has 16
+ * bits per pixel (little endian).</p>
+ *
+ * <p>This format assumes
+ * <ul>
+ * <li>an even width</li>
+ * <li>an even height</li>
+ * <li>a horizontal stride multiple of 16 pixels</li>
+ * </ul>
+ * </p>
+ *
+ * <pre> y_size = stride * height </pre>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.camera2.CameraDevice}
+ * through a {@link android.media.ImageReader} object if this format is
+ * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.camera2.CameraDevice
+ *
+ * @hide
+ */
+ public static final int Y16 = 0x20363159;
+
+ /**
* YCbCr format, used for video. Whether this format is supported by the
* camera hardware can be determined by
* {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
@@ -100,6 +160,55 @@ public class ImageFormat {
public static final int JPEG = 0x100;
/**
+ * <p>Multi-plane Android YUV format</p>
+ *
+ * <p>This format is a generic YCbCr format, capable of describing any 4:2:0
+ * chroma-subsampled planar or semiplanar buffer (but not fully interleaved),
+ * with 8 bits per color sample.</p>
+ *
+ * <p>Images in this format are always represented by three separate buffers
+ * of data, one for each color plane. Additional information always
+ * accompanies the buffers, describing the row stride and the pixel stride
+ * for each plane.</p>
+ *
+ * <p>The order of planes in the array returned by
+ * {@link android.media.Image#getPlanes() Image#getPlanes()} is guaranteed such that
+ * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V (Cr).</p>
+ *
+ * <p>The Y-plane is guaranteed not to be interleaved with the U/V planes
+ * (in particular, pixel stride is always 1 in
+ * {@link android.media.Image.Plane#getPixelStride() yPlane.getPixelStride()}).</p>
+ *
+ * <p>The U/V planes are guaranteed to have the same row stride and pixel stride
+ * (in particular,
+ * {@link android.media.Image.Plane#getRowStride() uPlane.getRowStride()}
+ * == {@link android.media.Image.Plane#getRowStride() vPlane.getRowStride()} and
+ * {@link android.media.Image.Plane#getPixelStride() uPlane.getPixelStride()}
+ * == {@link android.media.Image.Plane#getPixelStride() vPlane.getPixelStride()};
+ * ).</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.camera2.CameraDevice
+ */
+ public static final int YUV_420_888 = 0x23;
+
+ /**
+ * <p>General raw camera sensor image format, usually representing a
+ * single-channel Bayer-mosaic image. Each pixel color sample is stored with
+ * 16 bits of precision.</p>
+ *
+ * <p>The layout of the color mosaic, the maximum and minimum encoding
+ * values of the raw pixel data, the color space of the image, and all other
+ * needed information to interpret a raw sensor image must be queried from
+ * the {@link android.hardware.camera2.CameraDevice} which produced the
+ * image.</p>
+ *
+ * @hide
+ */
+ public static final int RAW_SENSOR = 0x20;
+
+ /**
* Raw bayer format used for images, which is 10 bit precision samples
* stored in 16 bit words. The filter pattern is RGGB. Whether this format
* is supported by the camera hardware can be determined by
@@ -127,8 +236,16 @@ public class ImageFormat {
return 16;
case YV12:
return 12;
+ case Y8:
+ return 8;
+ case Y16:
+ return 16;
case NV21:
return 12;
+ case YUV_420_888:
+ return 12;
+ case RAW_SENSOR:
+ return 16;
case BAYER_RGGB:
return 16;
}
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 96a71e3..4c88de3 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -17,6 +17,27 @@
package android.graphics;
public class LinearGradient extends Shader {
+
+ private static final int TYPE_COLORS_AND_POSITIONS = 1;
+ private static final int TYPE_COLOR_START_AND_COLOR_END = 2;
+
+ /**
+ * Type of the LinearGradient: can be either TYPE_COLORS_AND_POSITIONS or
+ * TYPE_COLOR_START_AND_COLOR_END.
+ */
+ private int mType;
+
+ private float mX0;
+ private float mY0;
+ private float mX1;
+ private float mY1;
+ private int[] mColors;
+ private float[] mPositions;
+ private int mColor0;
+ private int mColor1;
+
+ private TileMode mTileMode;
+
/** 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
@@ -36,6 +57,14 @@ public class LinearGradient extends Shader {
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
+ mType = TYPE_COLORS_AND_POSITIONS;
+ mX0 = x0;
+ mY0 = y0;
+ mX1 = x1;
+ mY1 = y1;
+ mColors = colors;
+ mPositions = positions;
+ mTileMode = tile;
native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt);
native_shader = nativePostCreate1(native_instance, x0, y0, x1, y1, colors, positions,
tile.nativeInt);
@@ -52,12 +81,42 @@ public class LinearGradient extends Shader {
*/
public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
TileMode tile) {
+ mType = TYPE_COLOR_START_AND_COLOR_END;
+ mX0 = x0;
+ mY0 = y0;
+ mX1 = x1;
+ mY1 = y1;
+ mColor0 = color0;
+ mColor1 = color1;
+ mTileMode = tile;
native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt);
native_shader = nativePostCreate2(native_instance, x0, y0, x1, y1, color0, color1,
tile.nativeInt);
}
- private native int nativeCreate1(float x0, float y0, float x1, float y1,
+ /**
+ * @hide
+ */
+ @Override
+ protected Shader copy() {
+ final LinearGradient copy;
+ switch (mType) {
+ case TYPE_COLORS_AND_POSITIONS:
+ copy = new LinearGradient(mX0, mY0, mX1, mY1, mColors.clone(),
+ mPositions != null ? mPositions.clone() : null, mTileMode);
+ break;
+ case TYPE_COLOR_START_AND_COLOR_END:
+ copy = new LinearGradient(mX0, mY0, mX1, mY1, mColor0, mColor1, mTileMode);
+ break;
+ default:
+ throw new IllegalArgumentException("LinearGradient should be created with either " +
+ "colors and positions or start color and end color");
+ }
+ copyLocalMatrix(copy);
+ return copy;
+ }
+
+ private native int nativeCreate1(float x0, float y0, float x1, float y1,
int colors[], float positions[], int tileMode);
private native int nativeCreate2(float x0, float y0, float x1, float y1,
int color0, int color1, int tileMode);
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index a837294..32e0c01 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -21,9 +21,6 @@ import java.io.PrintWriter;
/**
* 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 {
@@ -267,13 +264,23 @@ public class Matrix {
native_set(native_instance, src.native_instance);
}
}
-
+
/** Returns true iff obj is a Matrix and its values equal our values.
*/
+ @Override
public boolean equals(Object obj) {
- return obj != null &&
- obj instanceof Matrix &&
- native_equals(native_instance, ((Matrix)obj).native_instance);
+ //if (obj == this) return true; -- NaN value would mean matrix != itself
+ if (!(obj instanceof Matrix)) return false;
+ return native_equals(native_instance, ((Matrix)obj).native_instance);
+ }
+
+ @Override
+ public int hashCode() {
+ // This should generate the hash code by performing some arithmetic operation on all
+ // the matrix elements -- our equals() does an element-by-element comparison, and we
+ // need to ensure that the hash code for two equal objects is the same. We're not
+ // really using this at the moment, so we take the easy way out.
+ return 44;
}
/** Set the matrix to identity */
@@ -512,7 +519,7 @@ public class Matrix {
*/
END (3);
- // the native values must match those in SkMatrix.h
+ // the native values must match those in SkMatrix.h
ScaleToFit(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -535,7 +542,7 @@ public class Matrix {
}
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,
@@ -598,7 +605,7 @@ public class Matrix {
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
@@ -620,7 +627,7 @@ public class Matrix {
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
@@ -713,7 +720,7 @@ public class Matrix {
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) {
@@ -736,13 +743,14 @@ public class Matrix {
native_setValues(native_instance, values);
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
sb.append("Matrix{");
toShortString(sb);
sb.append('}');
return sb.toString();
-
+
}
public String toShortString() {
@@ -780,13 +788,18 @@ public class Matrix {
pw.print(values[5]); pw.print("][");
pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", ");
pw.print(values[8]); pw.print(']');
-
+
}
+ @Override
protected void finalize() throws Throwable {
- finalizer(native_instance);
+ try {
+ finalizer(native_instance);
+ } finally {
+ super.finalize();
+ }
}
-
+
/*package*/ final int ni() {
return native_instance;
}
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 4a33453..9419faf 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -16,12 +16,13 @@
package android.graphics;
+import android.content.res.AssetManager;
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");
@@ -42,7 +43,20 @@ public class Movie {
draw(canvas, x, y, null);
}
- public static native Movie decodeStream(InputStream is);
+ public static Movie decodeStream(InputStream is) {
+ if (is == null) {
+ return null;
+ }
+ if (is instanceof AssetManager.AssetInputStream) {
+ final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
+ return nativeDecodeAsset(asset);
+ }
+
+ return nativeDecodeStream(is);
+ }
+
+ private static native Movie nativeDecodeAsset(int asset);
+ private static native Movie nativeDecodeStream(InputStream is);
public static native Movie decodeByteArray(byte[] data, int offset,
int length);
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 6de4d84..528d9de 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -18,11 +18,8 @@ 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
+ * The NinePatch class permits drawing a bitmap in nine or more sections.
+ * 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
@@ -36,24 +33,40 @@ package android.graphics;
*/
public class NinePatch {
private final Bitmap mBitmap;
- private final byte[] mChunk;
+
+ /**
+ * Used by native code. This pointer is an instance of Res_png_9patch*.
+ *
+ * @hide
+ */
+ public final int mNativeChunk;
+
private Paint mPaint;
- private String mSrcName; // Useful for debugging
- private final RectF mRect = new RectF();
-
+ private String mSrcName;
+
+ /**
+ * 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.
+ */
+ public NinePatch(Bitmap bitmap, byte[] chunk) {
+ this(bitmap, chunk, null);
+ }
+
/**
* 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.
+ * @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);
+ mNativeChunk = validateNinePatchChunk(mBitmap.ni(), chunk);
}
/**
@@ -61,69 +74,103 @@ public class NinePatch {
*/
public NinePatch(NinePatch patch) {
mBitmap = patch.mBitmap;
- mChunk = patch.mChunk;
mSrcName = patch.mSrcName;
if (patch.mPaint != null) {
mPaint = new Paint(patch.mPaint);
}
- validateNinePatchChunk(mBitmap.ni(), mChunk);
+ // No need to validate the 9patch chunk again, it was done by
+ // the instance we're copying from
+ mNativeChunk = patch.mNativeChunk;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nativeFinalize(mNativeChunk);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Returns the name of this NinePatch object if one was specified
+ * when calling the constructor.
+ */
+ public String getName() {
+ return mSrcName;
+ }
+
+ /**
+ * Returns the paint used to draw this NinePatch. The paint can be null.
+ *
+ * @see #setPaint(Paint)
+ * @see #draw(Canvas, Rect)
+ * @see #draw(Canvas, RectF)
+ */
+ public Paint getPaint() {
+ return mPaint;
}
+ /**
+ * Sets the paint to use when drawing the NinePatch.
+ *
+ * @param p The paint that will be used to draw this NinePatch.
+ *
+ * @see #getPaint()
+ * @see #draw(Canvas, Rect)
+ * @see #draw(Canvas, RectF)
+ */
public void setPaint(Paint p) {
mPaint = p;
}
+
+ /**
+ * Returns the bitmap used to draw this NinePatch.
+ */
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
/**
- * Draw a bitmap of nine patches.
+ * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
*
- * @param canvas A container for the current matrix and clip used to draw the bitmap.
- * @param location Where to draw the bitmap.
+ * @param canvas A container for the current matrix and clip used to draw the NinePatch.
+ * @param location Where to draw the NinePatch.
*/
public void draw(Canvas canvas, RectF location) {
- if (!canvas.isHardwareAccelerated()) {
- nativeDraw(canvas.mNativeCanvas, location,
- mBitmap.ni(), mChunk,
- mPaint != null ? mPaint.mNativePaint : 0,
- canvas.mDensity, mBitmap.mDensity);
- } else {
- canvas.drawPatch(mBitmap, mChunk, location, mPaint);
- }
+ canvas.drawPatch(this, location, mPaint);
}
-
+
/**
- * Draw a bitmap of nine patches.
+ * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
*
- * @param canvas A container for the current matrix and clip used to draw the bitmap.
- * @param location Where to draw the bitmap.
+ * @param canvas A container for the current matrix and clip used to draw the NinePatch.
+ * @param location Where to draw the NinePatch.
*/
public void draw(Canvas canvas, Rect location) {
- if (!canvas.isHardwareAccelerated()) {
- nativeDraw(canvas.mNativeCanvas, location,
- mBitmap.ni(), mChunk,
- mPaint != null ? mPaint.mNativePaint : 0,
- canvas.mDensity, mBitmap.mDensity);
- } else {
- mRect.set(location);
- canvas.drawPatch(mBitmap, mChunk, mRect, mPaint);
- }
+ canvas.drawPatch(this, location, mPaint);
}
/**
- * Draw a bitmap of nine patches.
+ * Draws the NinePatch. This method will ignore the paint returned
+ * by {@link #getPaint()} and use the specified paint instead.
*
- * @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.
+ * @param canvas A container for the current matrix and clip used to draw the NinePatch.
+ * @param location Where to draw the NinePatch.
+ * @param paint The Paint to draw through.
*/
public void draw(Canvas canvas, Rect location, Paint paint) {
- if (!canvas.isHardwareAccelerated()) {
- nativeDraw(canvas.mNativeCanvas, location,
- mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0,
- canvas.mDensity, mBitmap.mDensity);
- } else {
- mRect.set(location);
- canvas.drawPatch(mBitmap, mChunk, mRect, paint);
- }
+ canvas.drawPatch(this, location, paint);
+ }
+
+ void drawSoftware(Canvas canvas, RectF location, Paint paint) {
+ nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk,
+ paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
+ }
+
+ void drawSoftware(Canvas canvas, Rect location, Paint paint) {
+ nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk,
+ paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
}
/**
@@ -133,33 +180,67 @@ public class NinePatch {
public int getDensity() {
return mBitmap.mDensity;
}
-
+
+ /**
+ * Returns the intrinsic width, in pixels, of this NinePatch. This is equivalent
+ * to querying the width of the underlying bitmap returned by {@link #getBitmap()}.
+ */
public int getWidth() {
return mBitmap.getWidth();
}
+ /**
+ * Returns the intrinsic height, in pixels, of this NinePatch. This is equivalent
+ * to querying the height of the underlying bitmap returned by {@link #getBitmap()}.
+ */
public int getHeight() {
return mBitmap.getHeight();
}
+ /**
+ * Indicates whether this NinePatch contains transparent or translucent pixels.
+ * This is equivalent to calling <code>getBitmap().hasAlpha()</code> on this
+ * NinePatch.
+ */
public final boolean hasAlpha() {
return mBitmap.hasAlpha();
}
- public final Region getTransparentRegion(Rect location) {
- int r = nativeGetTransparentRegion(mBitmap.ni(), mChunk, location);
+ /**
+ * Returns a {@link Region} representing the parts of the NinePatch that are
+ * completely transparent.
+ *
+ * @param bounds The location and size of the NinePatch.
+ *
+ * @return null if the NinePatch has no transparent region to
+ * report, else a {@link Region} holding the parts of the specified bounds
+ * that are transparent.
+ */
+ public final Region getTransparentRegion(Rect bounds) {
+ int r = nativeGetTransparentRegion(mBitmap.ni(), mNativeChunk, bounds);
return r != 0 ? new Region(r) : null;
}
-
+
+ /**
+ * Verifies that the specified byte array is a valid 9-patch data chunk.
+ *
+ * @param chunk A byte array representing a 9-patch data chunk.
+ *
+ * @return True if the specified byte array represents a 9-patch data chunk,
+ * false otherwise.
+ */
public native static boolean isNinePatchChunk(byte[] chunk);
- private static native void validateNinePatchChunk(int bitmap, byte[] chunk);
+ /**
+ * Validates the 9-patch chunk and throws an exception if the chunk is invalid.
+ * If validation is successful, this method returns a native Res_png_9patch*
+ * object used by the renderers.
+ */
+ private static native int validateNinePatchChunk(int bitmap, byte[] chunk);
+ private static native void nativeFinalize(int chunk);
private static native void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
- byte[] c, int paint_instance_or_null,
- int destDensity, int srcDensity);
+ int c, int paint_instance_or_null, int destDensity, int srcDensity);
private static native void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
- byte[] c, int paint_instance_or_null,
- int destDensity, int srcDensity);
- private static native int nativeGetTransparentRegion(
- int bitmap, byte[] chunk, Rect location);
+ int c, int paint_instance_or_null, int destDensity, int srcDensity);
+ private static native int nativeGetTransparentRegion(int bitmap, int chunk, Rect location);
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 1485d8d..5fc2588 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -87,35 +87,133 @@ public class Paint {
Align.LEFT, Align.CENTER, Align.RIGHT
};
- /** bit mask for the flag enabling antialiasing */
+ /**
+ * Paint flag that enables antialiasing when drawing.
+ *
+ * <p>Enabling this flag will cause all draw operations that support
+ * antialiasing to use it.</p>
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int ANTI_ALIAS_FLAG = 0x01;
- /** bit mask for the flag enabling bitmap filtering */
+ /**
+ * Paint flag that enables bilinear sampling on scaled bitmaps.
+ *
+ * <p>If cleared, scaled bitmaps will be drawn with nearest neighbor
+ * sampling, likely resulting in artifacts. This should generally be on
+ * when drawing bitmaps, unless performance-bound (rendering to software
+ * canvas) or preferring pixelation artifacts to blurriness when scaling
+ * significantly.</p>
+ *
+ * <p>If bitmaps are scaled for device density at creation time (as
+ * resource bitmaps often are) the filtering will already have been
+ * done.</p>
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int FILTER_BITMAP_FLAG = 0x02;
- /** bit mask for the flag enabling dithering */
+ /**
+ * Paint flag that enables dithering when blitting.
+ *
+ * <p>Enabling this flag applies a dither to any blit operation where the
+ * target's colour space is more constrained than the source.
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int DITHER_FLAG = 0x04;
- /** bit mask for the flag enabling underline text */
+ /**
+ * Paint flag that applies an underline decoration to drawn text.
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int UNDERLINE_TEXT_FLAG = 0x08;
- /** bit mask for the flag enabling strike-thru text */
+ /**
+ * Paint flag that applies a strike-through decoration to drawn text.
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int STRIKE_THRU_TEXT_FLAG = 0x10;
- /** bit mask for the flag enabling fake-bold text */
+ /**
+ * Paint flag that applies a synthetic bolding effect to drawn text.
+ *
+ * <p>Enabling this flag will cause text draw operations to apply a
+ * simulated bold effect when drawing a {@link Typeface} that is not
+ * already bold.</p>
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int FAKE_BOLD_TEXT_FLAG = 0x20;
- /** bit mask for the flag enabling linear-text (no caching) */
+ /**
+ * Paint flag that enables smooth linear scaling of text.
+ *
+ * <p>Enabling this flag does not actually scale text, but rather adjusts
+ * text draw operations to deal gracefully with smooth adjustment of scale.
+ * When this flag is enabled, font hinting is disabled to prevent shape
+ * deformation between scale factors, and glyph caching is disabled due to
+ * the large number of glyph images that will be generated.</p>
+ *
+ * <p>{@link #SUBPIXEL_TEXT_FLAG} should be used in conjunction with this
+ * flag to prevent glyph positions from snapping to whole pixel values as
+ * scale factor is adjusted.</p>
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int LINEAR_TEXT_FLAG = 0x40;
- /** bit mask for the flag enabling subpixel-text */
+ /**
+ * Paint flag that enables subpixel positioning of text.
+ *
+ * <p>Enabling this flag causes glyph advances to be computed with subpixel
+ * accuracy.</p>
+ *
+ * <p>This can be used with {@link #LINEAR_TEXT_FLAG} to prevent text from
+ * jittering during smooth scale transitions.</p>
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
public static final int SUBPIXEL_TEXT_FLAG = 0x80;
- /** bit mask for the flag enabling device kerning for text */
+ /** Legacy Paint flag, no longer used. */
public static final int DEV_KERN_TEXT_FLAG = 0x100;
+ /** @hide bit mask for the flag enabling subpixel glyph rendering for text */
+ public static final int LCD_RENDER_TEXT_FLAG = 0x200;
+ /**
+ * Paint flag that enables the use of bitmap fonts when drawing text.
+ *
+ * <p>Disabling this flag will prevent text draw operations from using
+ * embedded bitmap strikes in fonts, causing fonts with both scalable
+ * outlines and bitmap strikes to draw only the scalable outlines, and
+ * fonts with only bitmap strikes to not draw at all.</p>
+ *
+ * @see #Paint(int)
+ * @see #setFlags(int)
+ */
+ public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400;
+ /** @hide bit mask for the flag forcing freetype's autohinter on for text */
+ public static final int AUTO_HINTING_TEXT_FLAG = 0x800;
+ /** @hide bit mask for the flag enabling vertical rendering for text */
+ public static final int VERTICAL_TEXT_FLAG = 0x1000;
// we use this when we first create a paint
- static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG;
+ static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;
/**
- * Option for {@link #setHinting}: disable hinting.
+ * Font hinter option that disables font hinting.
+ *
+ * @see #setHinting(int)
*/
public static final int HINTING_OFF = 0x0;
/**
- * Option for {@link #setHinting}: enable hinting.
+ * Font hinter option that enables font hinting.
+ *
+ * @see #setHinting(int)
*/
public static final int HINTING_ON = 0x1;
@@ -421,7 +519,11 @@ public class Paint {
mMaskFilter = paint.mMaskFilter;
mPathEffect = paint.mPathEffect;
mRasterizer = paint.mRasterizer;
- mShader = paint.mShader;
+ if (paint.mShader != null) {
+ mShader = paint.mShader.copy();
+ } else {
+ mShader = null;
+ }
mTypeface = paint.mTypeface;
mXfermode = paint.mXfermode;
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 157c7d1..5b04a91 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -78,7 +78,11 @@ public class Path {
mLastDirection = null;
if (rects != null) rects.setEmpty();
}
+ // We promised not to change this, so preserve it around the native
+ // call, which does now reset fill type.
+ final FillType fillType = getFillType();
native_reset(mNativePath);
+ setFillType(fillType);
}
/**
@@ -103,21 +107,106 @@ public class Path {
}
}
- /** Enum for the ways a path may be filled
- */
+ /**
+ * The logical operations that can be performed when combining two paths.
+ *
+ * @see #op(Path, android.graphics.Path.Op)
+ * @see #op(Path, Path, android.graphics.Path.Op)
+ */
+ public enum Op {
+ /**
+ * Subtract the second path from the first path.
+ */
+ DIFFERENCE,
+ /**
+ * Intersect the two paths.
+ */
+ INTERSECT,
+ /**
+ * Union (inclusive-or) the two paths.
+ */
+ UNION,
+ /**
+ * Exclusive-or the two paths.
+ */
+ XOR,
+ /**
+ * Subtract the first path from the second path.
+ */
+ REVERSE_DIFFERENCE
+ }
+
+ /**
+ * Set this path to the result of applying the Op to this path and the specified path.
+ * The resulting path will be constructed from non-overlapping contours.
+ * The curve order is reduced where possible so that cubics may be turned
+ * into quadratics, and quadratics maybe turned into lines.
+ *
+ * @param path The second operand (for difference, the subtrahend)
+ *
+ * @return True if operation succeeded, false otherwise and this path remains unmodified.
+ *
+ * @see Op
+ * @see #op(Path, Path, android.graphics.Path.Op)
+ */
+ public boolean op(Path path, Op op) {
+ return op(this, path, op);
+ }
+
+ /**
+ * Set this path to the result of applying the Op to the two specified paths.
+ * The resulting path will be constructed from non-overlapping contours.
+ * The curve order is reduced where possible so that cubics may be turned
+ * into quadratics, and quadratics maybe turned into lines.
+ *
+ * @param path1 The first operand (for difference, the minuend)
+ * @param path2 The second operand (for difference, the subtrahend)
+ *
+ * @return True if operation succeeded, false otherwise and this path remains unmodified.
+ *
+ * @see Op
+ * @see #op(Path, android.graphics.Path.Op)
+ */
+ public boolean op(Path path1, Path path2, Op op) {
+ if (native_op(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
+ isSimplePath = false;
+ rects = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Enum for the ways a path may be filled.
+ */
public enum FillType {
// these must match the values in SkPath.h
+ /**
+ * Specifies that "inside" is computed by a non-zero sum of signed
+ * edge crossings.
+ */
WINDING (0),
+ /**
+ * Specifies that "inside" is computed by an odd number of edge
+ * crossings.
+ */
EVEN_ODD (1),
+ /**
+ * Same as {@link #WINDING}, but draws outside of the path, rather than inside.
+ */
INVERSE_WINDING (2),
+ /**
+ * Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.
+ */
INVERSE_EVEN_ODD(3);
FillType(int ni) {
nativeInt = ni;
}
+
final int nativeInt;
}
-
+
// these must be in the same order as their native values
static final FillType[] sFillTypeArray = {
FillType.WINDING,
@@ -644,24 +733,20 @@ public class Path {
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_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_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, 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, int dst_path);
private static native void native_transform(int nPath, int matrix);
+ private static native boolean native_op(int path1, int path2, int op, int result);
private static native void finalizer(int nPath);
}
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index f7c202f..d96d6d8 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -19,16 +19,16 @@ package android.graphics;
public class PixelFormat
{
/* these constants need to match those in hardware/hardware.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)
- */
+ * (at least 1 alpha bit)
+ */
public static final int TRANSPARENT = -2;
/** System chooses an opaque format (no alpha bits required) */
@@ -43,7 +43,9 @@ public class PixelFormat
public static final int RGBA_5551 = 6;
@Deprecated
public static final int RGBA_4444 = 7;
+ @Deprecated
public static final int A_8 = 8;
+ @Deprecated
public static final int L_8 = 9;
@Deprecated
public static final int LA_88 = 0xA;
@@ -52,41 +54,71 @@ public class PixelFormat
/**
- * @deprecated use {@link android.graphics.ImageFormat#NV16
+ * @deprecated use {@link android.graphics.ImageFormat#NV16
* ImageFormat.NV16} instead.
*/
@Deprecated
public static final int YCbCr_422_SP= 0x10;
/**
- * @deprecated use {@link android.graphics.ImageFormat#NV21
+ * @deprecated use {@link android.graphics.ImageFormat#NV21
* ImageFormat.NV21} instead.
*/
@Deprecated
public static final int YCbCr_420_SP= 0x11;
/**
- * @deprecated use {@link android.graphics.ImageFormat#YUY2
+ * @deprecated use {@link android.graphics.ImageFormat#YUY2
* ImageFormat.YUY2} instead.
*/
@Deprecated
public static final int YCbCr_422_I = 0x14;
/**
- * @deprecated use {@link android.graphics.ImageFormat#JPEG
+ * @deprecated use {@link android.graphics.ImageFormat#JPEG
* ImageFormat.JPEG} instead.
*/
@Deprecated
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 void getPixelFormatInfo(int format, PixelFormat info) {
+ switch (format) {
+ case RGBA_8888:
+ case RGBX_8888:
+ info.bitsPerPixel = 32;
+ info.bytesPerPixel = 4;
+ break;
+ case RGB_888:
+ info.bitsPerPixel = 24;
+ info.bytesPerPixel = 3;
+ break;
+ case RGB_565:
+ case RGBA_5551:
+ case RGBA_4444:
+ case LA_88:
+ info.bitsPerPixel = 16;
+ info.bytesPerPixel = 2;
+ break;
+ case A_8:
+ case L_8:
+ case RGB_332:
+ info.bitsPerPixel = 8;
+ info.bytesPerPixel = 1;
+ break;
+ case YCbCr_422_SP:
+ case YCbCr_422_I:
+ info.bitsPerPixel = 16;
+ info.bytesPerPixel = 1;
+ break;
+ case YCbCr_420_SP:
+ info.bitsPerPixel = 12;
+ info.bytesPerPixel = 1;
+ break;
+ default:
+ throw new IllegalArgumentException("unkonwon pixel format " + format);
+ }
+ }
- public static native void getPixelFormatInfo(int format, PixelFormat info);
public static boolean formatHasAlpha(int format) {
switch (format) {
case PixelFormat.A_8:
@@ -100,7 +132,7 @@ public class PixelFormat
}
return false;
}
-
+
public int bytesPerPixel;
public int bitsPerPixel;
}
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 897762c..f011e5c 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -18,6 +18,25 @@ package android.graphics;
public class RadialGradient extends Shader {
+ private static final int TYPE_COLORS_AND_POSITIONS = 1;
+ private static final int TYPE_COLOR_CENTER_AND_COLOR_EDGE = 2;
+
+ /**
+ * Type of the RadialGradient: can be either TYPE_COLORS_AND_POSITIONS or
+ * TYPE_COLOR_CENTER_AND_COLOR_EDGE.
+ */
+ private int mType;
+
+ private float mX;
+ private float mY;
+ private float mRadius;
+ private int[] mColors;
+ private float[] mPositions;
+ private int mColor0;
+ private int mColor1;
+
+ private TileMode mTileMode;
+
/** 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
@@ -39,6 +58,13 @@ public class RadialGradient extends Shader {
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
+ mType = TYPE_COLORS_AND_POSITIONS;
+ mX = x;
+ mY = y;
+ mRadius = radius;
+ mColors = colors;
+ mPositions = positions;
+ mTileMode = tile;
native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt);
native_shader = nativePostCreate1(native_instance, x, y, radius, colors, positions,
tile.nativeInt);
@@ -57,12 +83,41 @@ public class RadialGradient extends Shader {
if (radius <= 0) {
throw new IllegalArgumentException("radius must be > 0");
}
+ mType = TYPE_COLOR_CENTER_AND_COLOR_EDGE;
+ mX = x;
+ mY = y;
+ mRadius = radius;
+ mColor0 = color0;
+ mColor1 = color1;
+ mTileMode = tile;
native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt);
native_shader = nativePostCreate2(native_instance, x, y, radius, color0, color1,
tile.nativeInt);
}
- private static native int nativeCreate1(float x, float y, float radius,
+ /**
+ * @hide
+ */
+ @Override
+ protected Shader copy() {
+ final RadialGradient copy;
+ switch (mType) {
+ case TYPE_COLORS_AND_POSITIONS:
+ copy = new RadialGradient(mX, mY, mRadius, mColors.clone(),
+ mPositions != null ? mPositions.clone() : null, mTileMode);
+ break;
+ case TYPE_COLOR_CENTER_AND_COLOR_EDGE:
+ copy = new RadialGradient(mX, mY, mRadius, mColor0, mColor1, mTileMode);
+ break;
+ default:
+ throw new IllegalArgumentException("RadialGradient should be created with either " +
+ "colors and positions or center color and edge color");
+ }
+ copyLocalMatrix(copy);
+ return copy;
+ }
+
+ 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/Shader.java b/graphics/java/android/graphics/Shader.java
index 43758e7..afc68d8 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -90,6 +90,28 @@ public class Shader {
}
}
+ /**
+ * @hide
+ */
+ protected Shader copy() {
+ final Shader copy = new Shader();
+ copyLocalMatrix(copy);
+ return copy;
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyLocalMatrix(Shader dest) {
+ if (mLocalMatrix != null) {
+ final Matrix lm = new Matrix();
+ getLocalMatrix(lm);
+ dest.setLocalMatrix(lm);
+ } else {
+ dest.setLocalMatrix(null);
+ }
+ }
+
private static native void nativeDestructor(int native_shader, int native_skiaShader);
private static native void nativeSetLocalMatrix(int native_shader,
int native_skiaShader, int matrix_instance);
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index af1a447..b910a24 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -21,6 +21,7 @@ import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.view.Surface;
/**
* Captures frames from an image stream as an OpenGL ES texture.
@@ -69,6 +70,7 @@ public class SurfaceTexture {
* These fields are used by native code, do not access or modify.
*/
private int mSurfaceTexture;
+ private int mBufferQueue;
private int mFrameAvailableListener;
/**
@@ -79,8 +81,12 @@ public class SurfaceTexture {
}
/**
- * Exception thrown when a surface couldn't be created or resized
+ * Exception thrown when a SurfaceTexture couldn't be created or resized.
+ *
+ * @deprecated No longer thrown. {@link Surface.OutOfResourcesException} is used instead.
*/
+ @SuppressWarnings("serial")
+ @Deprecated
public static class OutOfResourcesException extends Exception {
public OutOfResourcesException() {
}
@@ -93,32 +99,32 @@ public class SurfaceTexture {
* Construct a new SurfaceTexture to stream images to a given OpenGL texture.
*
* @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
+ *
+ * @throws OutOfResourcesException If the SurfaceTexture cannot be created.
*/
public SurfaceTexture(int texName) {
- this(texName, false);
+ init(texName, false);
}
/**
* Construct a new SurfaceTexture to stream images to a given OpenGL texture.
*
+ * In single buffered mode the application is responsible for serializing access to the image
+ * content buffer. Each time the image content is to be updated, the
+ * {@link #releaseTexImage()} method must be called before the image content producer takes
+ * ownership of the buffer. For example, when producing image content with the NDK
+ * ANativeWindow_lock and ANativeWindow_unlockAndPost functions, {@link #releaseTexImage()}
+ * must be called before each ANativeWindow_lock, or that call will fail. When producing
+ * image content with OpenGL ES, {@link #releaseTexImage()} must be called before the first
+ * OpenGL ES function call each frame.
+ *
* @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
- * @param allowSynchronousMode whether the SurfaceTexture can run in the synchronous mode.
- * When the image stream comes from OpenGL, SurfaceTexture may run in the synchronous
- * mode where the producer side may be blocked to avoid skipping frames. To avoid the
- * thread block, set allowSynchronousMode to false.
+ * @param singleBufferMode whether the SurfaceTexture will be in single buffered mode.
*
- * @hide
+ * @throws throws OutOfResourcesException If the SurfaceTexture cannot be created.
*/
- public SurfaceTexture(int texName, boolean allowSynchronousMode) {
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(looper);
- } else {
- mEventHandler = null;
- }
- nativeInit(texName, new WeakReference<SurfaceTexture>(this), allowSynchronousMode);
+ public SurfaceTexture(int texName, boolean singleBufferMode) {
+ init(texName, singleBufferMode);
}
/**
@@ -143,9 +149,9 @@ public class SurfaceTexture {
* android.view.Surface#lockCanvas} is called. For OpenGL ES, the EGLSurface should be
* destroyed (via eglDestroySurface), made not-current (via eglMakeCurrent), and then recreated
* (via eglCreateWindowSurface) to ensure that the new default size has taken effect.
- *
+ *
* The width and height parameters must be no greater than the minimum of
- * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see
+ * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see
* {@link javax.microedition.khronos.opengles.GL10#glGetIntegerv glGetIntegerv}).
* An error due to invalid dimensions might not be reported until
* updateTexImage() is called.
@@ -164,6 +170,15 @@ public class SurfaceTexture {
}
/**
+ * Releases the the texture content. This is needed in single buffered mode to allow the image
+ * content producer to take ownership of the image buffer.
+ * For more information see {@link #SurfaceTexture(int, boolean)}.
+ */
+ public void releaseTexImage() {
+ nativeReleaseTexImage();
+ }
+
+ /**
* Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object.
* This call must be made with the OpenGL ES context current on the calling thread. The OpenGL
* ES texture object will be deleted as a result of this call. After calling this method all
@@ -261,6 +276,7 @@ public class SurfaceTexture {
nativeRelease();
}
+ @Override
protected void finalize() throws Throwable {
try {
nativeFinalize();
@@ -299,12 +315,26 @@ public class SurfaceTexture {
}
}
- private native void nativeInit(int texName, Object weakSelf, boolean allowSynchronousMode);
+ private void init(int texName, boolean singleBufferMode) throws Surface.OutOfResourcesException {
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler = new EventHandler(looper);
+ } else {
+ mEventHandler = null;
+ }
+ nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
+ }
+
+ private native void nativeInit(int texName, boolean singleBufferMode, Object weakSelf)
+ throws Surface.OutOfResourcesException;
private native void nativeFinalize();
private native void nativeGetTransformMatrix(float[] mtx);
private native long nativeGetTimestamp();
private native void nativeSetDefaultBufferSize(int width, int height);
private native void nativeUpdateTexImage();
+ private native void nativeReleaseTexImage();
private native int nativeDetachFromGLContext();
private native int nativeAttachToGLContext(int texName);
private native int nativeGetQueuedCount();
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 2afdd4d..e9cda39 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -18,6 +18,22 @@ package android.graphics;
public class SweepGradient extends Shader {
+ private static final int TYPE_COLORS_AND_POSITIONS = 1;
+ private static final int TYPE_COLOR_START_AND_COLOR_END = 2;
+
+ /**
+ * Type of the LinearGradient: can be either TYPE_COLORS_AND_POSITIONS or
+ * TYPE_COLOR_START_AND_COLOR_END.
+ */
+ private int mType;
+
+ private float mCx;
+ private float mCy;
+ private int[] mColors;
+ private float[] mPositions;
+ private int mColor0;
+ private int mColor1;
+
/**
* A subclass of Shader that draws a sweep gradient around a center point.
*
@@ -41,6 +57,11 @@ public class SweepGradient extends Shader {
throw new IllegalArgumentException(
"color and position arrays must be of equal length");
}
+ mType = TYPE_COLORS_AND_POSITIONS;
+ mCx = cx;
+ mCy = cy;
+ mColors = colors;
+ mPositions = positions;
native_instance = nativeCreate1(cx, cy, colors, positions);
native_shader = nativePostCreate1(native_instance, cx, cy, colors, positions);
}
@@ -54,10 +75,37 @@ public class SweepGradient extends Shader {
* @param color1 The color to use at the end of the sweep
*/
public SweepGradient(float cx, float cy, int color0, int color1) {
+ mType = TYPE_COLOR_START_AND_COLOR_END;
+ mCx = cx;
+ mCy = cy;
+ mColor0 = color0;
+ mColor1 = color1;
native_instance = nativeCreate2(cx, cy, color0, color1);
native_shader = nativePostCreate2(native_instance, cx, cy, color0, color1);
}
+ /**
+ * @hide
+ */
+ @Override
+ protected Shader copy() {
+ final SweepGradient copy;
+ switch (mType) {
+ case TYPE_COLORS_AND_POSITIONS:
+ copy = new SweepGradient(mCx, mCy, mColors.clone(),
+ mPositions != null ? mPositions.clone() : null);
+ break;
+ case TYPE_COLOR_START_AND_COLOR_END:
+ copy = new SweepGradient(mCx, mCy, mColor0, mColor1);
+ break;
+ default:
+ throw new IllegalArgumentException("SweepGradient should be created with either " +
+ "colors and positions or start color and end color");
+ }
+ copyLocalMatrix(copy);
+ return copy;
+ }
+
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/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index f0e9723..9accbbc 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -153,6 +153,11 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
@Override
+ public int getAlpha() {
+ return mState.mDrawable.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 7c7cd01..bde978d 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -167,7 +167,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
* @return The Drawable at the specified frame index
*/
public Drawable getFrame(int index) {
- return mAnimationState.getChildren()[index];
+ return mAnimationState.getChild(index);
}
/**
@@ -322,7 +322,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
mDurations = orig.mDurations;
mOneShot = orig.mOneShot;
} else {
- mDurations = new int[getChildren().length];
+ mDurations = new int[getCapacity()];
mOneShot = true;
}
}
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index a97ed2c..98e3386 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -23,12 +23,15 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Shader;
+import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.LayoutDirection;
import android.view.Gravity;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -72,7 +75,10 @@ public class BitmapDrawable extends Drawable {
// These are scaled to match the target density.
private int mBitmapWidth;
private int mBitmapHeight;
-
+
+ // Mirroring matrix for using with Shaders
+ private Matrix mMirrorMatrix;
+
/**
* Create an empty drawable, not dealing with density.
* @deprecated Use {@link #BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap)}
@@ -399,14 +405,51 @@ public class BitmapDrawable extends Drawable {
}
@Override
+ public void setAutoMirrored(boolean mirrored) {
+ if (mBitmapState.mAutoMirrored != mirrored) {
+ mBitmapState.mAutoMirrored = mirrored;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public final boolean isAutoMirrored() {
+ return mBitmapState.mAutoMirrored;
+ }
+
+ @Override
public int getChangingConfigurations() {
return super.getChangingConfigurations() | mBitmapState.mChangingConfigurations;
}
-
+
+ private boolean needMirroring() {
+ return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+ }
+
+ private void updateMirrorMatrix(float dx) {
+ if (mMirrorMatrix == null) {
+ mMirrorMatrix = new Matrix();
+ }
+ mMirrorMatrix.setTranslate(dx, 0);
+ mMirrorMatrix.preScale(-1.0f, 1.0f);
+ }
+
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mApplyGravity = true;
+ Shader shader = mBitmapState.mPaint.getShader();
+ if (shader != null) {
+ if (needMirroring()) {
+ updateMirrorMatrix(bounds.right - bounds.left);
+ shader.setLocalMatrix(mMirrorMatrix);
+ } else {
+ if (mMirrorMatrix != null) {
+ mMirrorMatrix = null;
+ shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
+ }
+ }
+ }
}
@Override
@@ -430,6 +473,7 @@ public class BitmapDrawable extends Drawable {
}
Shader shader = state.mPaint.getShader();
+ final boolean needMirroring = needMirroring();
if (shader == null) {
if (mApplyGravity) {
final int layoutDirection = getLayoutDirection();
@@ -437,12 +481,31 @@ public class BitmapDrawable extends Drawable {
getBounds(), mDstRect, layoutDirection);
mApplyGravity = false;
}
+ if (needMirroring) {
+ canvas.save();
+ // Mirror the bitmap
+ canvas.translate(mDstRect.right - mDstRect.left, 0);
+ canvas.scale(-1.0f, 1.0f);
+ }
canvas.drawBitmap(bitmap, null, mDstRect, state.mPaint);
+ if (needMirroring) {
+ canvas.restore();
+ }
} else {
if (mApplyGravity) {
copyBounds(mDstRect);
mApplyGravity = false;
}
+ if (needMirroring) {
+ // Mirror the bitmap
+ updateMirrorMatrix(mDstRect.right - mDstRect.left);
+ shader.setLocalMatrix(mMirrorMatrix);
+ } else {
+ if (mMirrorMatrix != null) {
+ mMirrorMatrix = null;
+ shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
+ }
+ }
canvas.drawRect(mDstRect, state.mPaint);
}
}
@@ -458,12 +521,25 @@ public class BitmapDrawable extends Drawable {
}
@Override
+ public int getAlpha() {
+ return mBitmapState.mPaint.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mBitmapState.mPaint.setColorFilter(cf);
invalidateSelf();
}
/**
+ * @hide Candidate for future API inclusion
+ */
+ public void setXfermode(Xfermode xfermode) {
+ mBitmapState.mPaint.setXfermode(xfermode);
+ invalidateSelf();
+ }
+
+ /**
* A mutable BitmapDrawable still shares its Bitmap with any other Drawable
* that comes from the same resource.
*
@@ -500,6 +576,8 @@ public class BitmapDrawable extends Drawable {
setTargetDensity(r.getDisplayMetrics());
setMipMap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_mipMap,
bitmap.hasMipMap()));
+ setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_autoMirrored,
+ false));
final Paint paint = mBitmapState.mPaint;
paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias,
@@ -562,6 +640,7 @@ public class BitmapDrawable extends Drawable {
Shader.TileMode mTileModeY = null;
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
boolean mRebuildShader;
+ boolean mAutoMirrored;
BitmapState(Bitmap bitmap) {
mBitmap = bitmap;
@@ -576,6 +655,12 @@ public class BitmapDrawable extends Drawable {
mTargetDensity = bitmapState.mTargetDensity;
mPaint = new Paint(bitmapState.mPaint);
mRebuildShader = bitmapState.mRebuildShader;
+ mAutoMirrored = bitmapState.mAutoMirrored;
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ return mBitmap;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index eb9f046..2a9a14b 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -158,6 +158,11 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
}
@Override
+ public int getAlpha() {
+ return mClipState.mDrawable.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mClipState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index d11e554..61dd675 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -116,6 +116,7 @@ public class ColorDrawable extends Drawable {
*
* @return A value between 0 and 255.
*/
+ @Override
public int getAlpha() {
return mState.mUseColor >>> 24;
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index d5183d5..8a3d940 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.graphics.Insets;
+import android.graphics.Xfermode;
import android.os.Trace;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -411,12 +412,32 @@ public abstract class Drawable {
public abstract void setAlpha(int alpha);
/**
+ * Gets the current alpha value for the drawable. 0 means fully transparent,
+ * 255 means fully opaque. This method is implemented by
+ * Drawable subclasses and the value returned is specific to how that class treats alpha.
+ * The default return value is 255 if the class does not override this method to return a value
+ * specific to its use of alpha.
+ */
+ public int getAlpha() {
+ return 0xFF;
+ }
+
+ /**
* Specify an optional colorFilter for the drawable. Pass null to remove
* any filters.
*/
public abstract void setColorFilter(ColorFilter cf);
/**
+ * @hide Consider for future API inclusion
+ */
+ public void setXfermode(Xfermode mode) {
+ // Base implementation drops it on the floor for compatibility. Whee!
+ // TODO: For this to be included in the API proper, all framework drawables need impls.
+ // For right now only BitmapDrawable has it.
+ }
+
+ /**
* Specify a color and porterduff mode to be the colorfilter for this
* drawable.
*/
@@ -561,6 +582,25 @@ public abstract class Drawable {
}
/**
+ * Set whether this Drawable is automatically mirrored when its layout direction is RTL
+ * (right-to left). See {@link android.util.LayoutDirection}.
+ *
+ * @param mirrored Set to true if the Drawable should be mirrored, false if not.
+ */
+ public void setAutoMirrored(boolean mirrored) {
+ }
+
+ /**
+ * Tells if this Drawable will be automatically mirrored when its layout direction is RTL
+ * right-to-left. See {@link android.util.LayoutDirection}.
+ *
+ * @return boolean Returns true if this Drawable will be automatically mirrored.
+ */
+ public boolean isAutoMirrored() {
+ return false;
+ }
+
+ /**
* Return the opacity/transparency of this Drawable. The returned value is
* one of the abstract format constants in
* {@link android.graphics.PixelFormat}:
@@ -858,10 +898,6 @@ public abstract class Drawable {
drawable = new StateListDrawable();
} else if (name.equals("level-list")) {
drawable = new LevelListDrawable();
- /* Probably not doing this.
- } else if (name.equals("mipmap")) {
- drawable = new MipmapDrawable();
- */
} else if (name.equals("layer-list")) {
drawable = new LayerDrawable();
} else if (name.equals("transition")) {
@@ -985,6 +1021,13 @@ public abstract class Drawable {
* this drawable (and thus require completely reloading it).
*/
public abstract int getChangingConfigurations();
+
+ /**
+ * @hide
+ */
+ public Bitmap getBitmap() {
+ return null;
+ }
}
/**
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 0f84e86..aac7876 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -23,6 +23,8 @@ import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
+import android.util.LayoutDirection;
+import android.util.SparseArray;
/**
* A helper class that contains several {@link Drawable}s and selects which one to use.
@@ -58,6 +60,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
private long mExitAnimationEnd;
private Drawable mLastDrawable;
+ private Insets mInsets = Insets.NONE;
+
// overrides from Drawable
@Override
@@ -76,19 +80,32 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
| mDrawableContainerState.mChangingConfigurations
| mDrawableContainerState.mChildrenChangingConfigurations;
}
-
+
+ private boolean needsMirroring() {
+ return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+ }
+
@Override
public boolean getPadding(Rect padding) {
final Rect r = mDrawableContainerState.getConstantPadding();
+ boolean result;
if (r != null) {
padding.set(r);
- return true;
- }
- if (mCurrDrawable != null) {
- return mCurrDrawable.getPadding(padding);
+ result = (r.left | r.top | r.bottom | r.right) != 0;
} else {
- return super.getPadding(padding);
+ if (mCurrDrawable != null) {
+ result = mCurrDrawable.getPadding(padding);
+ } else {
+ result = super.getPadding(padding);
+ }
+ }
+ if (needsMirroring()) {
+ final int left = padding.left;
+ final int right = padding.right;
+ padding.left = right;
+ padding.right = left;
}
+ return result;
}
/**
@@ -96,7 +113,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
*/
@Override
public Insets getOpticalInsets() {
- return (mCurrDrawable == null) ? Insets.NONE : mCurrDrawable.getOpticalInsets();
+ return mInsets;
}
@Override
@@ -114,6 +131,11 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
@Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
public void setDither(boolean dither) {
if (mDrawableContainerState.mDither != dither) {
mDrawableContainerState.mDither = dither;
@@ -132,7 +154,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
}
}
-
+
/**
* Change the global fade duration when a new drawable is entering
* the scene.
@@ -141,7 +163,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
public void setEnterFadeDuration(int ms) {
mDrawableContainerState.mEnterFadeDuration = ms;
}
-
+
/**
* Change the global fade duration when a new drawable is leaving
* the scene.
@@ -150,7 +172,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
public void setExitFadeDuration(int ms) {
mDrawableContainerState.mExitFadeDuration = ms;
}
-
+
@Override
protected void onBoundsChange(Rect bounds) {
if (mLastDrawable != null) {
@@ -160,12 +182,25 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mCurrDrawable.setBounds(bounds);
}
}
-
+
@Override
public boolean isStateful() {
return mDrawableContainerState.isStateful();
}
-
+
+ @Override
+ public void setAutoMirrored(boolean mirrored) {
+ mDrawableContainerState.mAutoMirrored = mirrored;
+ if (mCurrDrawable != null) {
+ mCurrDrawable.mutate().setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+ }
+ }
+
+ @Override
+ public boolean isAutoMirrored() {
+ return mDrawableContainerState.mAutoMirrored;
+ }
+
@Override
public void jumpToCurrentState() {
boolean changed = false;
@@ -228,7 +263,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1;
}
-
+
@Override
public int getMinimumWidth() {
if (mDrawableContainerState.isConstantSize()) {
@@ -245,18 +280,21 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0;
}
+ @Override
public void invalidateDrawable(Drawable who) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().invalidateDrawable(this);
}
}
+ @Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().scheduleDrawable(this, what, when);
}
}
+ @Override
public void unscheduleDrawable(Drawable who, Runnable what) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().unscheduleDrawable(this, what);
@@ -308,10 +346,11 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
- Drawable d = mDrawableContainerState.mDrawables[idx];
+ final Drawable d = mDrawableContainerState.getChild(idx);
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
+ mInsets = d.getOpticalInsets();
d.mutate();
if (mDrawableContainerState.mEnterFadeDuration > 0) {
mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
@@ -325,9 +364,13 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
d.setLevel(getLevel());
d.setBounds(getBounds());
d.setLayoutDirection(getLayoutDirection());
+ d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+ } else {
+ mInsets = Insets.NONE;
}
} else {
mCurrDrawable = null;
+ mInsets = Insets.NONE;
mCurIndex = -1;
}
@@ -350,7 +393,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return true;
}
-
+
void animate(boolean schedule) {
final long now = SystemClock.uptimeMillis();
boolean animating = false;
@@ -410,11 +453,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
- final int N = mDrawableContainerState.getChildCount();
- final Drawable[] drawables = mDrawableContainerState.getChildren();
- for (int i = 0; i < N; i++) {
- if (drawables[i] != null) drawables[i].mutate();
- }
+ mDrawableContainerState.mutate();
mMutated = true;
}
return this;
@@ -428,90 +467,108 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
*/
public abstract static class DrawableContainerState extends ConstantState {
final DrawableContainer mOwner;
+ final Resources mRes;
+
+ SparseArray<ConstantStateFuture> mDrawableFutures;
+
+ int mChangingConfigurations;
+ int mChildrenChangingConfigurations;
- int mChangingConfigurations;
- int mChildrenChangingConfigurations;
-
- Drawable[] mDrawables;
- int mNumChildren;
+ Drawable[] mDrawables;
+ int mNumChildren;
- boolean mVariablePadding = false;
- Rect mConstantPadding = null;
+ boolean mVariablePadding;
+ boolean mPaddingChecked;
+ Rect mConstantPadding;
- boolean mConstantSize = false;
- boolean mComputedConstantSize = false;
- int mConstantWidth;
- int mConstantHeight;
- int mConstantMinimumWidth;
- int mConstantMinimumHeight;
+ boolean mConstantSize;
+ boolean mComputedConstantSize;
+ int mConstantWidth;
+ int mConstantHeight;
+ int mConstantMinimumWidth;
+ int mConstantMinimumHeight;
- int mOpacity;
+ boolean mCheckedOpacity;
+ int mOpacity;
- boolean mHaveStateful = false;
- boolean mStateful;
+ boolean mCheckedStateful;
+ boolean mStateful;
- boolean mCheckedConstantState;
- boolean mCanConstantState;
+ boolean mCheckedConstantState;
+ boolean mCanConstantState;
- boolean mPaddingChecked = false;
-
- boolean mDither = DEFAULT_DITHER;
+ boolean mDither = DEFAULT_DITHER;
- int mEnterFadeDuration;
- int mExitFadeDuration;
+ boolean mMutated;
+ int mLayoutDirection;
+
+ int mEnterFadeDuration;
+ int mExitFadeDuration;
+
+ boolean mAutoMirrored;
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
+ mRes = res;
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++) {
- if (res != null) {
- mDrawables[i] = origDr[i].getConstantState().newDrawable(res);
- } else {
- mDrawables[i] = origDr[i].getConstantState().newDrawable();
- }
- mDrawables[i].setCallback(owner);
- mDrawables[i].setLayoutDirection(origDr[i].getLayoutDirection());
- }
+ mCheckedConstantState = true;
+ mCanConstantState = true;
- mCheckedConstantState = mCanConstantState = true;
mVariablePadding = orig.mVariablePadding;
- if (orig.mConstantPadding != null) {
- mConstantPadding = new Rect(orig.mConstantPadding);
- }
mConstantSize = orig.mConstantSize;
- mComputedConstantSize = orig.mComputedConstantSize;
- mConstantWidth = orig.mConstantWidth;
- mConstantHeight = orig.mConstantHeight;
- mConstantMinimumWidth = orig.mConstantMinimumWidth;
- mConstantMinimumHeight = orig.mConstantMinimumHeight;
-
- mOpacity = orig.mOpacity;
- mHaveStateful = orig.mHaveStateful;
- mStateful = orig.mStateful;
-
mDither = orig.mDither;
-
+ mMutated = orig.mMutated;
+ mLayoutDirection = orig.mLayoutDirection;
mEnterFadeDuration = orig.mEnterFadeDuration;
mExitFadeDuration = orig.mExitFadeDuration;
+ mAutoMirrored = orig.mAutoMirrored;
+
+ // Cloning the following values may require creating futures.
+ mConstantPadding = orig.getConstantPadding();
+ mPaddingChecked = true;
+
+ mConstantWidth = orig.getConstantWidth();
+ mConstantHeight = orig.getConstantHeight();
+ mConstantMinimumWidth = orig.getConstantMinimumWidth();
+ mConstantMinimumHeight = orig.getConstantMinimumHeight();
+ mComputedConstantSize = true;
+
+ mOpacity = orig.getOpacity();
+ mCheckedOpacity = true;
+
+ mStateful = orig.isStateful();
+ mCheckedStateful = true;
+
+ // Postpone cloning children and futures until we're absolutely
+ // sure that we're done computing values for the original state.
+ final Drawable[] origDr = orig.mDrawables;
+ mDrawables = new Drawable[origDr.length];
+ mNumChildren = orig.mNumChildren;
+
+ final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures;
+ if (origDf != null) {
+ mDrawableFutures = origDf.clone();
+ } else {
+ mDrawableFutures = new SparseArray<ConstantStateFuture>(mNumChildren);
+ }
+ final int N = mNumChildren;
+ for (int i = 0; i < N; i++) {
+ if (origDr[i] != null) {
+ mDrawableFutures.put(i, new ConstantStateFuture(origDr[i]));
+ }
+ }
} else {
mDrawables = new Drawable[10];
mNumChildren = 0;
- mCheckedConstantState = mCanConstantState = false;
}
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations | mChildrenChangingConfigurations;
@@ -530,7 +587,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mDrawables[pos] = dr;
mNumChildren++;
mChildrenChangingConfigurations |= dr.getChangingConfigurations();
- mHaveStateful = false;
+ mCheckedStateful = false;
+ mCheckedOpacity = false;
mConstantPadding = null;
mPaddingChecked = false;
@@ -539,18 +597,89 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return pos;
}
+ final int getCapacity() {
+ return mDrawables.length;
+ }
+
+ private final void createAllFutures() {
+ if (mDrawableFutures != null) {
+ final int futureCount = mDrawableFutures.size();
+ for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) {
+ final int index = mDrawableFutures.keyAt(keyIndex);
+ mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this);
+ }
+
+ mDrawableFutures = null;
+ }
+ }
+
public final int getChildCount() {
return mNumChildren;
}
+ /*
+ * @deprecated Use {@link #getChild} instead.
+ */
public final Drawable[] getChildren() {
+ // Create all futures for backwards compatibility.
+ createAllFutures();
+
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 Drawable getChild(int index) {
+ final Drawable result = mDrawables[index];
+ if (result != null) {
+ return result;
+ }
+
+ // Prepare future drawable if necessary.
+ if (mDrawableFutures != null) {
+ final int keyIndex = mDrawableFutures.indexOfKey(index);
+ if (keyIndex >= 0) {
+ final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this);
+ mDrawables[index] = prepared;
+ mDrawableFutures.removeAt(keyIndex);
+ return prepared;
+ }
+ }
+
+ return null;
+ }
+
+ final void setLayoutDirection(int layoutDirection) {
+ // No need to call createAllFutures, since future drawables will
+ // change layout direction when they are prepared.
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i] != null) {
+ drawables[i].setLayoutDirection(layoutDirection);
+ }
+ }
+
+ mLayoutDirection = layoutDirection;
+ }
+
+ final void mutate() {
+ // No need to call createAllFutures, since future drawables will
+ // mutate when they are prepared.
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i] != null) {
+ drawables[i].mutate();
+ }
+ }
+
+ mMutated = true;
+ }
+
+ /**
+ * 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;
}
@@ -559,13 +688,16 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mVariablePadding) {
return null;
}
- if (mConstantPadding != null || mPaddingChecked) {
+
+ if ((mConstantPadding != null) || mPaddingChecked) {
return mConstantPadding;
}
+ createAllFutures();
+
Rect r = null;
final Rect t = new Rect();
- final int N = getChildCount();
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
if (drawables[i].getPadding(t)) {
@@ -576,6 +708,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (t.bottom > r.bottom) r.bottom = t.bottom;
}
}
+
mPaddingChecked = true;
return (mConstantPadding = r);
}
@@ -623,12 +756,14 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
protected void computeConstantSize() {
mComputedConstantSize = true;
- final int N = getChildCount();
+ createAllFutures();
+
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
mConstantWidth = mConstantHeight = -1;
mConstantMinimumWidth = mConstantMinimumHeight = 0;
for (int i = 0; i < N; i++) {
- Drawable dr = drawables[i];
+ final Drawable dr = drawables[i];
int s = dr.getIntrinsicWidth();
if (s > mConstantWidth) mConstantWidth = s;
s = dr.getIntrinsicHeight();
@@ -657,33 +792,45 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
public final int getOpacity() {
- final int N = getChildCount();
+ if (mCheckedOpacity) {
+ return mOpacity;
+ }
+
+ createAllFutures();
+
+ mCheckedOpacity = true;
+
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
- int op = N > 0 ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
+ int op = (N > 0) ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
for (int i = 1; i < N; i++) {
op = Drawable.resolveOpacity(op, drawables[i].getOpacity());
}
+
mOpacity = op;
return op;
}
public final boolean isStateful() {
- if (mHaveStateful) {
+ if (mCheckedStateful) {
return mStateful;
}
-
- boolean stateful = false;
- final int N = getChildCount();
+
+ createAllFutures();
+
+ mCheckedStateful = true;
+
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
- if (mDrawables[i].isStateful()) {
- stateful = true;
- break;
+ if (drawables[i].isStateful()) {
+ mStateful = true;
+ return true;
}
}
-
- mStateful = stateful;
- mHaveStateful = true;
- return stateful;
+
+ mStateful = false;
+ return false;
}
public void growArray(int oldSize, int newSize) {
@@ -693,24 +840,60 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
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;
- }
+ if (mCheckedConstantState) {
+ return mCanConstantState;
+ }
+
+ createAllFutures();
+
+ mCheckedConstantState = true;
+
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i].getConstantState() == null) {
+ mCanConstantState = false;
+ return false;
}
- mCheckedConstantState = true;
}
- return mCanConstantState;
+ mCanConstantState = true;
+ return true;
+ }
+
+ /**
+ * Class capable of cloning a Drawable from another Drawable's
+ * ConstantState.
+ */
+ private static class ConstantStateFuture {
+ private final ConstantState mConstantState;
+
+ private ConstantStateFuture(Drawable source) {
+ mConstantState = source.getConstantState();
+ }
+
+ /**
+ * Obtains and prepares the Drawable represented by this future.
+ *
+ * @param state the container into which this future will be placed
+ * @return a prepared Drawable
+ */
+ public Drawable get(DrawableContainerState state) {
+ final Drawable result = (state.mRes == null) ?
+ mConstantState.newDrawable() : mConstantState.newDrawable(state.mRes);
+ result.setLayoutDirection(state.mLayoutDirection);
+ result.setCallback(state.mOwner);
+
+ if (state.mMutated) {
+ result.mutate();
+ }
+
+ return result;
+ }
}
}
- protected void setConstantState(DrawableContainerState state)
- {
+ 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
index b966bb4..b340777 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -19,6 +19,7 @@ package android.graphics.drawable;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
@@ -639,6 +640,11 @@ public class GradientDrawable extends Drawable {
}
@Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
public void setDither(boolean dither) {
if (dither != mDither) {
mDither = dither;
@@ -742,9 +748,6 @@ public class GradientDrawable extends Drawable {
mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1,
colors, st.mPositions, Shader.TileMode.CLAMP));
- if (!mGradientState.mHasSolidColor) {
- mFillPaint.setColor(mAlpha << 24);
- }
} else if (st.mGradient == RADIAL_GRADIENT) {
x0 = r.left + (r.right - r.left) * st.mCenterX;
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
@@ -754,9 +757,6 @@ public class GradientDrawable extends Drawable {
mFillPaint.setShader(new RadialGradient(x0, y0,
level * st.mGradientRadius, colors, null,
Shader.TileMode.CLAMP));
- if (!mGradientState.mHasSolidColor) {
- mFillPaint.setColor(mAlpha << 24);
- }
} else if (st.mGradient == SWEEP_GRADIENT) {
x0 = r.left + (r.right - r.left) * st.mCenterX;
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
@@ -787,9 +787,12 @@ public class GradientDrawable extends Drawable {
}
mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions));
- if (!mGradientState.mHasSolidColor) {
- mFillPaint.setColor(mAlpha << 24);
- }
+ }
+
+ // If we don't have a solid color, the alpha channel must be
+ // maxed out so that alpha modulation works correctly.
+ if (!st.mHasSolidColor) {
+ mFillPaint.setColor(Color.BLACK);
}
}
}
@@ -1276,6 +1279,9 @@ public class GradientDrawable extends Drawable {
// the app is stroking the shape, set the color to the default
// value of state.mSolidColor
mFillPaint.setColor(0);
+ } else {
+ // Otherwise, make sure the fill alpha is maxed out.
+ mFillPaint.setColor(Color.BLACK);
}
mPadding = state.mPadding;
if (state.mStrokeWidth >= 0) {
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 2576f42e..8188782 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -192,12 +192,23 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
public void setAlpha(int alpha) {
mInsetState.mDrawable.setAlpha(alpha);
}
-
+
+ @Override
+ public int getAlpha() {
+ return mInsetState.mDrawable.getAlpha();
+ }
+
@Override
public void setColorFilter(ColorFilter cf) {
mInsetState.mDrawable.setColorFilter(cf);
}
-
+
+ /** {@hide} */
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ mInsetState.mDrawable.setLayoutDirection(layoutDirection);
+ }
+
@Override
public int getOpacity() {
return mInsetState.mDrawable.getOpacity();
@@ -256,6 +267,13 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
return this;
}
+ /**
+ * Returns the drawable wrapped by this InsetDrawable. May be null.
+ */
+ public Drawable getDrawable() {
+ return mInsetState.mDrawable;
+ }
+
final static class InsetState extends ConstantState {
Drawable mDrawable;
int mChangingConfigurations;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 6b59dba..81cc11b 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -119,6 +119,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
mOpacityOverride = a.getInt(com.android.internal.R.styleable.LayerDrawable_opacity,
PixelFormat.UNKNOWN);
+ setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.LayerDrawable_autoMirrored,
+ false));
+
a.recycle();
final int innerDepth = parser.getDepth() + 1;
@@ -200,6 +203,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
st.mChildren[i] = childDrawable;
childDrawable.mId = id;
childDrawable.mDrawable = layer;
+ childDrawable.mDrawable.setAutoMirrored(isAutoMirrored());
childDrawable.mInsetL = left;
childDrawable.mInsetT = top;
childDrawable.mInsetR = right;
@@ -402,7 +406,18 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
array[i].mDrawable.setAlpha(alpha);
}
}
-
+
+ @Override
+ public int getAlpha() {
+ final ChildDrawable[] array = mLayerState.mChildren;
+ if (mLayerState.mNum > 0) {
+ // All layers should have the same alpha set on them - just return the first one
+ return array[0].mDrawable.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+
@Override
public void setColorFilter(ColorFilter cf) {
final ChildDrawable[] array = mLayerState.mChildren;
@@ -437,6 +452,21 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
@Override
+ public void setAutoMirrored(boolean mirrored) {
+ mLayerState.mAutoMirrored = mirrored;
+ final ChildDrawable[] array = mLayerState.mChildren;
+ final int N = mLayerState.mNum;
+ for (int i=0; i<N; i++) {
+ array[i].mDrawable.setAutoMirrored(mirrored);
+ }
+ }
+
+ @Override
+ public boolean isAutoMirrored() {
+ return mLayerState.mAutoMirrored;
+ }
+
+ @Override
public boolean isStateful() {
return mLayerState.isStateful();
}
@@ -619,6 +649,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
private boolean mCheckedConstantState;
private boolean mCanConstantState;
+ private boolean mAutoMirrored;
+
LayerState(LayerState orig, LayerDrawable owner, Resources res) {
if (orig != null) {
final ChildDrawable[] origChildDrawable = orig.mChildren;
@@ -652,6 +684,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
mHaveStateful = orig.mHaveStateful;
mStateful = orig.mStateful;
mCheckedConstantState = mCanConstantState = true;
+ mAutoMirrored = orig.mAutoMirrored;
} else {
mNum = 0;
mChildren = null;
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index 21be983..872fdce 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -164,8 +164,8 @@ public class LevelListDrawable extends DrawableContainer {
mLows = orig.mLows;
mHighs = orig.mHighs;
} else {
- mLows = new int[getChildren().length];
- mHighs = new int[getChildren().length];
+ mLows = new int[getCapacity()];
+ mHighs = new int[getCapacity()];
}
}
diff --git a/graphics/java/android/graphics/drawable/MipmapDrawable.java b/graphics/java/android/graphics/drawable/MipmapDrawable.java
deleted file mode 100644
index cd39719..0000000
--- a/graphics/java/android/graphics/drawable/MipmapDrawable.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * 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.Rect;
-import android.util.AttributeSet;
-
-import java.io.IOException;
-
-/**
- * @hide -- we are probably moving to do MipMaps in another way (more integrated
- * with the resource system).
- *
- * A resource that manages a number of alternate Drawables, and which actually draws the one which
- * size matches the most closely the drawing bounds. Providing several pre-scaled version of the
- * drawable helps minimizing the aliasing artifacts that can be introduced by the scaling.
- *
- * <p>
- * Use {@link #addDrawable(Drawable)} to define the different Drawables that will represent the
- * mipmap levels of this MipmapDrawable. The mipmap Drawable that will actually be used when this
- * MipmapDrawable is drawn is the one which has the smallest intrinsic height greater or equal than
- * the bounds' height. This selection ensures that the best available mipmap level is scaled down to
- * draw this MipmapDrawable.
- * </p>
- *
- * If the bounds' height is larger than the largest mipmap, the largest mipmap will be scaled up.
- * Note that Drawables without intrinsic height (i.e. with a negative value, such as Color) will
- * only be used if no other mipmap Drawable are provided. The Drawables' intrinsic heights should
- * not be changed after the Drawable has been added to this MipmapDrawable.
- *
- * <p>
- * The different mipmaps' parameters (opacity, padding, color filter, gravity...) should typically
- * be similar to ensure a continuous visual appearance when the MipmapDrawable is scaled. The aspect
- * ratio of the different mipmaps should especially be equal.
- * </p>
- *
- * A typical example use of a MipmapDrawable would be for an image which is intended to be scaled at
- * various sizes, and for which one wants to provide pre-scaled versions to precisely control its
- * appearance.
- *
- * <p>
- * The intrinsic size of a MipmapDrawable are inferred from those of the largest mipmap (in terms of
- * {@link Drawable#getIntrinsicHeight()}). On the opposite, its minimum
- * size is defined by the smallest provided mipmap.
- * </p>
-
- * It can be defined in an XML file with the <code>&lt;mipmap></code> element.
- * Each mipmap Drawable is defined in a nested <code>&lt;item></code>. For example:
- * <pre>
- * &lt;mipmap xmlns:android="http://schemas.android.com/apk/res/android">
- * &lt;item android:drawable="@drawable/my_image_8" />
- * &lt;item android:drawable="@drawable/my_image_32" />
- * &lt;item android:drawable="@drawable/my_image_128" />
- * &lt;/mipmap>
- *</pre>
- * <p>
- * With this XML saved into the res/drawable/ folder of the project, it can be referenced as
- * the drawable for an {@link android.widget.ImageView}. Assuming that the heights of the provided
- * drawables are respectively 8, 32 and 128 pixels, the first one will be scaled down when the
- * bounds' height is lower or equal than 8 pixels. The second drawable will then be used up to a
- * height of 32 pixels and the largest drawable will be used for greater heights.
- * </p>
- * @attr ref android.R.styleable#MipmapDrawableItem_drawable
- */
-public class MipmapDrawable extends DrawableContainer {
- private final MipmapContainerState mMipmapContainerState;
- private boolean mMutated;
-
- public MipmapDrawable() {
- this(null, null);
- }
-
- /**
- * Adds a Drawable to the list of available mipmap Drawables. The Drawable actually used when
- * this MipmapDrawable is drawn is determined from its bounds.
- *
- * This method has no effect if drawable is null.
- *
- * @param drawable The Drawable that will be added to list of available mipmap Drawables.
- */
-
- public void addDrawable(Drawable drawable) {
- if (drawable != null) {
- mMipmapContainerState.addDrawable(drawable);
- onDrawableAdded();
- }
- }
-
- private void onDrawableAdded() {
- // selectDrawable assumes that the container content does not change.
- // When a Drawable is added, the same index can correspond to a new Drawable, and since
- // selectDrawable has a fast exit case when oldIndex==newIndex, the new drawable could end
- // up not being used in place of the previous one if they happen to share the same index.
- // This make sure the new computed index can actually replace the previous one.
- selectDrawable(-1);
- onBoundsChange(getBounds());
- }
-
- // overrides from Drawable
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- final int index = mMipmapContainerState.indexForBounds(bounds);
-
- // Will call invalidateSelf() if needed
- selectDrawable(index);
-
- super.onBoundsChange(bounds);
- }
-
- @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.MipmapDrawableItem);
-
- int drawableRes = a.getResourceId(
- com.android.internal.R.styleable.MipmapDrawableItem_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);
- }
-
- mMipmapContainerState.addDrawable(dr);
- }
-
- onDrawableAdded();
- }
-
- @Override
- public Drawable mutate() {
- if (!mMutated && super.mutate() == this) {
- mMipmapContainerState.mMipmapHeights = mMipmapContainerState.mMipmapHeights.clone();
- mMutated = true;
- }
- return this;
- }
-
- private final static class MipmapContainerState extends DrawableContainerState {
- private int[] mMipmapHeights;
-
- MipmapContainerState(MipmapContainerState orig, MipmapDrawable owner, Resources res) {
- super(orig, owner, res);
-
- if (orig != null) {
- mMipmapHeights = orig.mMipmapHeights;
- } else {
- mMipmapHeights = new int[getChildren().length];
- }
-
- // Change the default value
- setConstantSize(true);
- }
-
- /**
- * Returns the index of the child mipmap drawable that will best fit the provided bounds.
- * This index is determined by comparing bounds' height and children intrinsic heights.
- * The returned mipmap index is the smallest mipmap which height is greater or equal than
- * the bounds' height. If the bounds' height is larger than the largest mipmap, the largest
- * mipmap index is returned.
- *
- * @param bounds The bounds of the MipMapDrawable.
- * @return The index of the child Drawable that will best fit these bounds, or -1 if there
- * are no children mipmaps.
- */
- public int indexForBounds(Rect bounds) {
- final int boundsHeight = bounds.height();
- final int N = getChildCount();
- for (int i = 0; i < N; i++) {
- if (boundsHeight <= mMipmapHeights[i]) {
- return i;
- }
- }
-
- // No mipmap larger than bounds found. Use largest one which will be scaled up.
- if (N > 0) {
- return N - 1;
- }
- // No Drawable mipmap at all
- return -1;
- }
-
- /**
- * Adds a Drawable to the list of available mipmap Drawables. This list can be retrieved
- * using {@link DrawableContainer.DrawableContainerState#getChildren()} and this method
- * ensures that it is always sorted by increasing {@link Drawable#getIntrinsicHeight()}.
- *
- * @param drawable The Drawable that will be added to children list
- */
- public void addDrawable(Drawable drawable) {
- // Insert drawable in last position, correctly resetting cached values and
- // especially mComputedConstantSize
- int pos = addChild(drawable);
-
- // Bubble sort the last drawable to restore the sort by intrinsic height
- final int drawableHeight = drawable.getIntrinsicHeight();
-
- while (pos > 0) {
- final Drawable previousDrawable = mDrawables[pos-1];
- final int previousIntrinsicHeight = previousDrawable.getIntrinsicHeight();
-
- if (drawableHeight < previousIntrinsicHeight) {
- mDrawables[pos] = previousDrawable;
- mMipmapHeights[pos] = previousIntrinsicHeight;
-
- mDrawables[pos-1] = drawable;
- mMipmapHeights[pos-1] = drawableHeight;
- pos--;
- } else {
- break;
- }
- }
- }
-
- /**
- * Intrinsic sizes are those of the largest available mipmap.
- * Minimum sizes are those of the smallest available mipmap.
- */
- @Override
- protected void computeConstantSize() {
- final int N = getChildCount();
- if (N > 0) {
- final Drawable smallestDrawable = mDrawables[0];
- mConstantMinimumWidth = smallestDrawable.getMinimumWidth();
- mConstantMinimumHeight = smallestDrawable.getMinimumHeight();
-
- final Drawable largestDrawable = mDrawables[N-1];
- mConstantWidth = largestDrawable.getIntrinsicWidth();
- mConstantHeight = largestDrawable.getIntrinsicHeight();
- } else {
- mConstantWidth = mConstantHeight = -1;
- mConstantMinimumWidth = mConstantMinimumHeight = 0;
- }
- mComputedConstantSize = true;
- }
-
- @Override
- public Drawable newDrawable() {
- return new MipmapDrawable(this, null);
- }
-
- @Override
- public Drawable newDrawable(Resources res) {
- return new MipmapDrawable(this, res);
- }
-
- @Override
- public void growArray(int oldSize, int newSize) {
- super.growArray(oldSize, newSize);
- int[] newInts = new int[newSize];
- System.arraycopy(mMipmapHeights, 0, newInts, 0, oldSize);
- mMipmapHeights = newInts;
- }
- }
-
- private MipmapDrawable(MipmapContainerState state, Resources res) {
- MipmapContainerState as = new MipmapContainerState(state, this, res);
- mMipmapContainerState = as;
- setConstantState(as);
- onDrawableAdded();
- }
-}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index a9dc22b..9c57a2c 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -30,6 +30,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.LayoutDirection;
import android.util.TypedValue;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -52,7 +53,7 @@ import java.io.InputStream;
*/
public class NinePatchDrawable extends Drawable {
// dithering helps a lot, and is pretty cheap, so default is true
- private static final boolean DEFAULT_DITHER = true;
+ private static final boolean DEFAULT_DITHER = false;
private NinePatchState mNinePatchState;
private NinePatch mNinePatch;
private Rect mPadding;
@@ -132,6 +133,7 @@ public class NinePatchDrawable extends Drawable {
// lazy allocation of a paint
setDither(state.mDither);
}
+ setAutoMirrored(state.mAutoMirrored);
if (mNinePatch != null) {
computeBitmapSize();
}
@@ -197,10 +199,8 @@ public class NinePatchDrawable extends Drawable {
mBitmapHeight = mNinePatch.getHeight();
mOpticalInsets = mNinePatchState.mOpticalInsets;
} else {
- mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(),
- sdensity, tdensity);
- mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(),
- sdensity, tdensity);
+ mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), sdensity, tdensity);
+ mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), sdensity, tdensity);
if (mNinePatchState.mPadding != null && mPadding != null) {
Rect dest = mPadding;
Rect src = mNinePatchState.mPadding;
@@ -218,7 +218,18 @@ public class NinePatchDrawable extends Drawable {
@Override
public void draw(Canvas canvas) {
- mNinePatch.draw(canvas, getBounds(), mPaint);
+ final Rect bounds = getBounds();
+ final boolean needsMirroring = needsMirroring();
+ if (needsMirroring) {
+ canvas.save();
+ // Mirror the 9patch
+ canvas.translate(bounds.right - bounds.left, 0);
+ canvas.scale(-1.0f, 1.0f);
+ }
+ mNinePatch.draw(canvas, bounds, mPaint);
+ if (needsMirroring) {
+ canvas.restore();
+ }
}
@Override
@@ -228,8 +239,12 @@ public class NinePatchDrawable extends Drawable {
@Override
public boolean getPadding(Rect padding) {
- padding.set(mPadding);
- return true;
+ if (needsMirroring()) {
+ padding.set(mPadding.right, mPadding.top, mPadding.left, mPadding.bottom);
+ } else {
+ padding.set(mPadding);
+ }
+ return (padding.left | padding.top | padding.right | padding.bottom) != 0;
}
/**
@@ -237,7 +252,12 @@ public class NinePatchDrawable extends Drawable {
*/
@Override
public Insets getOpticalInsets() {
- return mOpticalInsets;
+ if (needsMirroring()) {
+ return Insets.of(mOpticalInsets.right, mOpticalInsets.top, mOpticalInsets.right,
+ mOpticalInsets.bottom);
+ } else {
+ return mOpticalInsets;
+ }
}
@Override
@@ -251,6 +271,15 @@ public class NinePatchDrawable extends Drawable {
}
@Override
+ public int getAlpha() {
+ if (mPaint == null) {
+ // Fast common case -- normal alpha.
+ return 0xFF;
+ }
+ return getPaint().getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
if (mPaint == null && cf == null) {
// Fast common case -- leave at no color filter.
@@ -262,6 +291,7 @@ public class NinePatchDrawable extends Drawable {
@Override
public void setDither(boolean dither) {
+ //noinspection PointlessBooleanExpression
if (mPaint == null && dither == DEFAULT_DITHER) {
// Fast common case -- leave at default dither.
return;
@@ -271,6 +301,20 @@ public class NinePatchDrawable extends Drawable {
}
@Override
+ public void setAutoMirrored(boolean mirrored) {
+ mNinePatchState.mAutoMirrored = mirrored;
+ }
+
+ private boolean needsMirroring() {
+ return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+ }
+
+ @Override
+ public boolean isAutoMirrored() {
+ return mNinePatchState.mAutoMirrored;
+ }
+
+ @Override
public void setFilterBitmap(boolean filter) {
getPaint().setFilterBitmap(filter);
invalidateSelf();
@@ -290,8 +334,7 @@ public class NinePatchDrawable extends Drawable {
}
final boolean dither = a.getBoolean(
- com.android.internal.R.styleable.NinePatchDrawable_dither,
- DEFAULT_DITHER);
+ com.android.internal.R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER);
final BitmapFactory.Options options = new BitmapFactory.Options();
if (dither) {
options.inDither = false;
@@ -321,9 +364,11 @@ public class NinePatchDrawable extends Drawable {
": <nine-patch> requires a valid 9-patch source image");
}
- setNinePatchState(new NinePatchState(
- new NinePatch(bitmap, bitmap.getNinePatchChunk(), "XML 9-patch"),
- padding, opticalInsets, dither), r);
+ final boolean automirrored = a.getBoolean(
+ com.android.internal.R.styleable.NinePatchDrawable_autoMirrored, false);
+
+ setNinePatchState(new NinePatchState(new NinePatch(bitmap, bitmap.getNinePatchChunk()),
+ padding, opticalInsets, dither, automirrored), r);
mNinePatchState.mTargetDensity = mTargetDensity;
a.recycle();
@@ -394,39 +439,49 @@ public class NinePatchDrawable extends Drawable {
return this;
}
- private final static class NinePatchState extends ConstantState {
+ final static class NinePatchState extends ConstantState {
final NinePatch mNinePatch;
final Rect mPadding;
final Insets mOpticalInsets;
final boolean mDither;
int mChangingConfigurations;
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+ boolean mAutoMirrored;
NinePatchState(NinePatch ninePatch, Rect padding) {
- this(ninePatch, padding, new Rect(), DEFAULT_DITHER);
+ this(ninePatch, padding, new Rect(), DEFAULT_DITHER, false);
}
NinePatchState(NinePatch ninePatch, Rect padding, Rect opticalInsets) {
- this(ninePatch, padding, opticalInsets, DEFAULT_DITHER);
+ this(ninePatch, padding, opticalInsets, DEFAULT_DITHER, false);
}
- NinePatchState(NinePatch ninePatch, Rect rect, Rect opticalInsets, boolean dither) {
+ NinePatchState(NinePatch ninePatch, Rect rect, Rect opticalInsets, boolean dither,
+ boolean autoMirror) {
mNinePatch = ninePatch;
mPadding = rect;
mOpticalInsets = Insets.of(opticalInsets);
mDither = dither;
+ mAutoMirrored = autoMirror;
}
// Copy constructor
NinePatchState(NinePatchState state) {
- mNinePatch = new NinePatch(state.mNinePatch);
+ // Note we don't copy the nine patch because it is immutable.
+ mNinePatch = state.mNinePatch;
// Note we don't copy the padding because it is immutable.
mPadding = state.mPadding;
mOpticalInsets = state.mOpticalInsets;
mDither = state.mDither;
mChangingConfigurations = state.mChangingConfigurations;
mTargetDensity = state.mTargetDensity;
+ mAutoMirrored = state.mAutoMirrored;
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ return mNinePatch.getBitmap();
}
@Override
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 83d9581..aec3a4b 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -108,6 +108,11 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
mState.mDrawable.setAlpha(alpha);
}
+ @Override
+ public int getAlpha() {
+ return mState.mDrawable.getAlpha();
+ }
+
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 0664214..ec6b2c1 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -177,6 +177,11 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback {
}
@Override
+ public int getAlpha() {
+ return mScaleState.mDrawable.getAlpha();
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
mScaleState.mDrawable.setColorFilter(cf);
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 1dbcddb..93f2dc6 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -252,7 +252,12 @@ public class ShapeDrawable extends Drawable {
mShapeState.mAlpha = alpha;
invalidateSelf();
}
-
+
+ @Override
+ public int getAlpha() {
+ return mShapeState.mAlpha;
+ }
+
@Override
public void setColorFilter(ColorFilter cf) {
mShapeState.mPaint.setColorFilter(cf);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index f8f3ac9..48d66b7 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -132,6 +132,9 @@ public class StateListDrawable extends DrawableContainer {
setDither(a.getBoolean(com.android.internal.R.styleable.StateListDrawable_dither,
DEFAULT_DITHER));
+ setAutoMirrored(a.getBoolean(
+ com.android.internal.R.styleable.StateListDrawable_autoMirrored, false));
+
a.recycle();
int type;
@@ -228,7 +231,7 @@ public class StateListDrawable extends DrawableContainer {
* @see #getStateSet(int)
*/
public Drawable getStateDrawable(int index) {
- return mStateListState.getChildren()[index];
+ return mStateListState.getChild(index);
}
/**
@@ -264,11 +267,11 @@ public class StateListDrawable extends DrawableContainer {
/** @hide */
@Override
public void setLayoutDirection(int layoutDirection) {
- final int numStates = getStateCount();
- for (int i = 0; i < numStates; i++) {
- getStateDrawable(i).setLayoutDirection(layoutDirection);
- }
super.setLayoutDirection(layoutDirection);
+
+ // Let the container handle setting its own layout direction. Otherwise,
+ // we're accessing potentially unused states.
+ mStateListState.setLayoutDirection(layoutDirection);
}
static final class StateListState extends DrawableContainerState {
@@ -278,9 +281,9 @@ public class StateListDrawable extends DrawableContainer {
super(orig, owner, res);
if (orig != null) {
- mStateSets = orig.mStateSets;
+ mStateSets = Arrays.copyOf(orig.mStateSets, orig.mStateSets.length);
} else {
- mStateSets = new int[getChildren().length][];
+ mStateSets = new int[getCapacity()][];
}
}
diff --git a/graphics/java/android/graphics/pdf/PdfDocument.java b/graphics/java/android/graphics/pdf/PdfDocument.java
new file mode 100644
index 0000000..29d14a2
--- /dev/null
+++ b/graphics/java/android/graphics/pdf/PdfDocument.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2013 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.pdf;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import dalvik.system.CloseGuard;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>
+ * This class enables generating a PDF document from native Android content. You
+ * open a new document and then for every page you want to add you start a page,
+ * write content to the page, and finish the page. After you are done with all
+ * pages, you write the document to an output stream and close the document.
+ * After a document is closed you should not use it anymore. Note that pages are
+ * created one by one, i.e. you can have only a single page to which you are
+ * writing at any given time. This class is not thread safe.
+ * </p>
+ * <p>
+ * A typical use of the APIs looks like this:
+ * </p>
+ * <pre>
+ * // create a new document
+ * PdfDocument document = new PdfDocument();
+ *
+ * // crate a page description
+ * PageInfo pageInfo = new PageInfo.Builder(new Rect(0, 0, 100, 100), 1).create();
+ *
+ * // start a page
+ * Page page = document.startPage(pageInfo);
+ *
+ * // draw something on the page
+ * View content = getContentView();
+ * content.draw(page.getCanvas());
+ *
+ * // finish the page
+ * document.finishPage(page);
+ * . . .
+ * // add more pages
+ * . . .
+ * // write the document content
+ * document.writeTo(getOutputStream());
+ *
+ * //close the document
+ * document.close();
+ * </pre>
+ */
+public class PdfDocument {
+
+ // TODO: We need a constructor that will take an OutputStream to
+ // support online data serialization as opposed to the current
+ // on demand one. The current approach is fine until Skia starts
+ // to support online PDF generation at which point we need to
+ // handle this.
+
+ private final byte[] mChunk = new byte[4096];
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ private final List<PageInfo> mPages = new ArrayList<PageInfo>();
+
+ private int mNativeDocument;
+
+ private Page mCurrentPage;
+
+ /**
+ * Creates a new instance.
+ */
+ public PdfDocument() {
+ mNativeDocument = nativeCreateDocument();
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Starts a page using the provided {@link PageInfo}. After the page
+ * is created you can draw arbitrary content on the page's canvas which
+ * you can get by calling {@link Page#getCanvas()}. After you are done
+ * drawing the content you should finish the page by calling
+ * {@link #finishPage(Page)}. After the page is finished you should
+ * no longer access the page or its canvas.
+ * <p>
+ * <strong>Note:</strong> Do not call this method after {@link #close()}.
+ * Also do not call this method if the last page returned by this method
+ * is not finished by calling {@link #finishPage(Page)}.
+ * </p>
+ *
+ * @param pageInfo The page info. Cannot be null.
+ * @return A blank page.
+ *
+ * @see #finishPage(Page)
+ */
+ public Page startPage(PageInfo pageInfo) {
+ throwIfClosed();
+ throwIfCurrentPageNotFinished();
+ if (pageInfo == null) {
+ throw new IllegalArgumentException("page cannot be null");
+ }
+ Canvas canvas = new PdfCanvas(nativeStartPage(mNativeDocument, pageInfo.mPageWidth,
+ pageInfo.mPageHeight, pageInfo.mContentRect.left, pageInfo.mContentRect.top,
+ pageInfo.mContentRect.right, pageInfo.mContentRect.bottom));
+ mCurrentPage = new Page(canvas, pageInfo);
+ return mCurrentPage;
+ }
+
+ /**
+ * Finishes a started page. You should always finish the last started page.
+ * <p>
+ * <strong>Note:</strong> Do not call this method after {@link #close()}.
+ * You should not finish the same page more than once.
+ * </p>
+ *
+ * @param page The page. Cannot be null.
+ *
+ * @see #startPage(PageInfo)
+ */
+ public void finishPage(Page page) {
+ throwIfClosed();
+ if (page == null) {
+ throw new IllegalArgumentException("page cannot be null");
+ }
+ if (page != mCurrentPage) {
+ throw new IllegalStateException("invalid page");
+ }
+ if (page.isFinished()) {
+ throw new IllegalStateException("page already finished");
+ }
+ mPages.add(page.getInfo());
+ mCurrentPage = null;
+ nativeFinishPage(mNativeDocument);
+ page.finish();
+ }
+
+ /**
+ * Writes the document to an output stream. You can call this method
+ * multiple times.
+ * <p>
+ * <strong>Note:</strong> Do not call this method after {@link #close()}.
+ * Also do not call this method if a page returned by {@link #startPage(
+ * PageInfo)} is not finished by calling {@link #finishPage(Page)}.
+ * </p>
+ *
+ * @param out The output stream. Cannot be null.
+ *
+ * @throws IOException If an error occurs while writing.
+ */
+ public void writeTo(OutputStream out) throws IOException {
+ throwIfClosed();
+ throwIfCurrentPageNotFinished();
+ if (out == null) {
+ throw new IllegalArgumentException("out cannot be null!");
+ }
+ nativeWriteTo(mNativeDocument, out, mChunk);
+ }
+
+ /**
+ * Gets the pages of the document.
+ *
+ * @return The pages or an empty list.
+ */
+ public List<PageInfo> getPages() {
+ return Collections.unmodifiableList(mPages);
+ }
+
+ /**
+ * Closes this document. This method should be called after you
+ * are done working with the document. After this call the document
+ * is considered closed and none of its methods should be called.
+ * <p>
+ * <strong>Note:</strong> Do not call this method if the page
+ * returned by {@link #startPage(PageInfo)} is not finished by
+ * calling {@link #finishPage(Page)}.
+ * </p>
+ */
+ public void close() {
+ throwIfCurrentPageNotFinished();
+ dispose();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ dispose();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void dispose() {
+ if (mNativeDocument != 0) {
+ nativeClose(mNativeDocument);
+ mCloseGuard.close();
+ mNativeDocument = 0;
+ }
+ }
+
+ /**
+ * Throws an exception if the document is already closed.
+ */
+ private void throwIfClosed() {
+ if (mNativeDocument == 0) {
+ throw new IllegalStateException("document is closed!");
+ }
+ }
+
+ /**
+ * Throws an exception if the last started page is not finished.
+ */
+ private void throwIfCurrentPageNotFinished() {
+ if (mCurrentPage != null) {
+ throw new IllegalStateException("Current page not finished!");
+ }
+ }
+
+ private native int nativeCreateDocument();
+
+ private native void nativeClose(int document);
+
+ private native void nativeFinishPage(int document);
+
+ private native void nativeWriteTo(int document, OutputStream out, byte[] chunk);
+
+ private static native int nativeStartPage(int documentPtr, int pageWidth, int pageHeight,
+ int contentLeft, int contentTop, int contentRight, int contentBottom);
+
+ private final class PdfCanvas extends Canvas {
+
+ public PdfCanvas(int nativeCanvas) {
+ super(nativeCanvas);
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * This class represents meta-data that describes a PDF {@link Page}.
+ */
+ public static final class PageInfo {
+ private int mPageWidth;
+ private int mPageHeight;
+ private Rect mContentRect;
+ private int mPageNumber;
+
+ /**
+ * Creates a new instance.
+ */
+ private PageInfo() {
+ /* do nothing */
+ }
+
+ /**
+ * Gets the page width in PostScript points (1/72th of an inch).
+ *
+ * @return The page width.
+ */
+ public int getPageWidth() {
+ return mPageWidth;
+ }
+
+ /**
+ * Gets the page height in PostScript points (1/72th of an inch).
+ *
+ * @return The page height.
+ */
+ public int getPageHeight() {
+ return mPageHeight;
+ }
+
+ /**
+ * Get the content rectangle in PostScript points (1/72th of an inch).
+ * This is the area that contains the page content and is relative to
+ * the page top left.
+ *
+ * @return The content rectangle.
+ */
+ public Rect getContentRect() {
+ return mContentRect;
+ }
+
+ /**
+ * Gets the page number.
+ *
+ * @return The page number.
+ */
+ public int getPageNumber() {
+ return mPageNumber;
+ }
+
+ /**
+ * Builder for creating a {@link PageInfo}.
+ */
+ public static final class Builder {
+ private final PageInfo mPageInfo = new PageInfo();
+
+ /**
+ * Creates a new builder with the mandatory page info attributes.
+ *
+ * @param pageWidth The page width in PostScript (1/72th of an inch).
+ * @param pageHeight The page height in PostScript (1/72th of an inch).
+ * @param pageNumber The page number.
+ */
+ public Builder(int pageWidth, int pageHeight, int pageNumber) {
+ if (pageWidth <= 0) {
+ throw new IllegalArgumentException("page width must be positive");
+ }
+ if (pageHeight <= 0) {
+ throw new IllegalArgumentException("page width must be positive");
+ }
+ if (pageNumber < 0) {
+ throw new IllegalArgumentException("pageNumber must be non negative");
+ }
+ mPageInfo.mPageWidth = pageWidth;
+ mPageInfo.mPageHeight = pageHeight;
+ mPageInfo.mPageNumber = pageNumber;
+ }
+
+ /**
+ * Sets the content rectangle in PostScript point (1/72th of an inch).
+ * This is the area that contains the page content and is relative to
+ * the page top left.
+ *
+ * @param contentRect The content rectangle. Must fit in the page.
+ */
+ public Builder setContentRect(Rect contentRect) {
+ if (contentRect != null && (contentRect.left < 0
+ || contentRect.top < 0
+ || contentRect.right > mPageInfo.mPageWidth
+ || contentRect.bottom > mPageInfo.mPageHeight)) {
+ throw new IllegalArgumentException("contentRect does not fit the page");
+ }
+ mPageInfo.mContentRect = contentRect;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PageInfo}.
+ *
+ * @return The new instance.
+ */
+ public PageInfo create() {
+ if (mPageInfo.mContentRect == null) {
+ mPageInfo.mContentRect = new Rect(0, 0,
+ mPageInfo.mPageWidth, mPageInfo.mPageHeight);
+ }
+ return mPageInfo;
+ }
+ }
+ }
+
+ /**
+ * This class represents a PDF document page. It has associated
+ * a canvas on which you can draw content and is acquired by a
+ * call to {@link #getCanvas()}. It also has associated a
+ * {@link PageInfo} instance that describes its attributes. Also
+ * a page has
+ */
+ public static final class Page {
+ private final PageInfo mPageInfo;
+ private Canvas mCanvas;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param canvas The canvas of the page.
+ * @param pageInfo The info with meta-data.
+ */
+ private Page(Canvas canvas, PageInfo pageInfo) {
+ mCanvas = canvas;
+ mPageInfo = pageInfo;
+ }
+
+ /**
+ * Gets the {@link Canvas} of the page.
+ *
+ * <p>
+ * <strong>Note: </strong> There are some draw operations that are not yet
+ * supported by the canvas returned by this method. More specifically:
+ * <ul>
+ * <li>Inverse path clipping performed via {@link Canvas#clipPath(android.graphics.Path,
+ * android.graphics.Region.Op) Canvas.clipPath(android.graphics.Path,
+ * android.graphics.Region.Op)} for {@link
+ * android.graphics.Region.Op#REVERSE_DIFFERENCE
+ * Region.Op#REVERSE_DIFFERENCE} operations.</li>
+ * <li>{@link Canvas#drawVertices(android.graphics.Canvas.VertexMode, int,
+ * float[], int, float[], int, int[], int, short[], int, int,
+ * android.graphics.Paint) Canvas.drawVertices(
+ * android.graphics.Canvas.VertexMode, int, float[], int, float[],
+ * int, int[], int, short[], int, int, android.graphics.Paint)}</li>
+ * <li>Color filters set via {@link Paint#setColorFilter(
+ * android.graphics.ColorFilter)}</li>
+ * <li>Mask filters set via {@link Paint#setMaskFilter(
+ * android.graphics.MaskFilter)}</li>
+ * <li>Some XFER modes such as
+ * {@link android.graphics.PorterDuff.Mode#SRC_ATOP PorterDuff.Mode SRC},
+ * {@link android.graphics.PorterDuff.Mode#DST_ATOP PorterDuff.DST_ATOP},
+ * {@link android.graphics.PorterDuff.Mode#XOR PorterDuff.XOR},
+ * {@link android.graphics.PorterDuff.Mode#ADD PorterDuff.ADD}</li>
+ * </ul>
+ *
+ * @return The canvas if the page is not finished, null otherwise.
+ *
+ * @see PdfDocument#finishPage(Page)
+ */
+ public Canvas getCanvas() {
+ return mCanvas;
+ }
+
+ /**
+ * Gets the {@link PageInfo} with meta-data for the page.
+ *
+ * @return The page info.
+ *
+ * @see PdfDocument#finishPage(Page)
+ */
+ public PageInfo getInfo() {
+ return mPageInfo;
+ }
+
+ boolean isFinished() {
+ return mCanvas == null;
+ }
+
+ private void finish() {
+ if (mCanvas != null) {
+ mCanvas.release();
+ mCanvas = null;
+ }
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/pdf/package.html b/graphics/java/android/graphics/pdf/package.html
new file mode 100644
index 0000000..51f2460
--- /dev/null
+++ b/graphics/java/android/graphics/pdf/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Contains classes for manipulation of PDF content.
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index b460a4d..dca934f 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -28,6 +28,7 @@ import android.graphics.SurfaceTexture;
import android.util.Log;
import android.util.TypedValue;
import android.graphics.Canvas;
+import android.os.Trace;
/**
* <p> This class provides the primary method through which data is passed to
@@ -60,6 +61,7 @@ public class Allocation extends BaseObj {
Bitmap mBitmap;
int mUsage;
Allocation mAdaptedAllocation;
+ int mSize;
boolean mConstrainedLOD;
boolean mConstrainedFace;
@@ -78,7 +80,7 @@ public class Allocation extends BaseObj {
int mCurrentCount;
static HashMap<Integer, Allocation> mAllocationMap =
new HashMap<Integer, Allocation>();
- IoInputNotifier mBufferNotifier;
+ OnBufferAvailableListener mBufferNotifier;
/**
* The usage of the Allocation. These signal to RenderScript where to place
@@ -269,8 +271,23 @@ public class Allocation extends BaseObj {
mUsage = usage;
if (t != null) {
+ // TODO: A3D doesn't have Type info during creation, so we can't
+ // calculate the size ahead of time. We can possibly add a method
+ // to update the size in the future if it seems reasonable.
+ mSize = mType.getCount() * mType.getElement().getBytesSize();
updateCacheInfo(t);
}
+ try {
+ RenderScript.registerNativeAllocation.invoke(RenderScript.sRuntime, mSize);
+ } catch (Exception e) {
+ Log.e(RenderScript.LOG_TAG, "Couldn't invoke registerNativeAllocation:" + e);
+ throw new RSRuntimeException("Couldn't invoke registerNativeAllocation:" + e);
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ RenderScript.registerNativeFree.invoke(RenderScript.sRuntime, mSize);
+ super.finalize();
}
private void validateIsInt32() {
@@ -352,6 +369,7 @@ public class Allocation extends BaseObj {
*
*/
public void syncAll(int srcLocation) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "syncAll");
switch (srcLocation) {
case USAGE_GRAPHICS_TEXTURE:
case USAGE_SCRIPT:
@@ -372,6 +390,7 @@ public class Allocation extends BaseObj {
}
mRS.validate();
mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -382,12 +401,14 @@ public class Allocation extends BaseObj {
*
*/
public void ioSend() {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "ioSend");
if ((mUsage & USAGE_IO_OUTPUT) == 0) {
throw new RSIllegalArgumentException(
"Can only send buffer if IO_OUTPUT usage specified.");
}
mRS.validate();
mRS.nAllocationIoSend(getID(mRS));
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -404,12 +425,14 @@ public class Allocation extends BaseObj {
*
*/
public void ioReceive() {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "ioReceive");
if ((mUsage & USAGE_IO_INPUT) == 0) {
throw new RSIllegalArgumentException(
"Can only receive if IO_INPUT usage specified.");
}
mRS.validate();
mRS.nAllocationIoReceive(getID(mRS));
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -418,6 +441,7 @@ public class Allocation extends BaseObj {
* @param d Source array.
*/
public void copyFrom(BaseObj[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
validateIsObject();
if (d.length != mCurrentCount) {
@@ -429,6 +453,7 @@ public class Allocation extends BaseObj {
i[ct] = d[ct].getID(mRS);
}
copy1DRangeFromUnchecked(0, mCurrentCount, i);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
private void validateBitmapFormat(Bitmap b) {
@@ -494,6 +519,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFromUnchecked(int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -502,7 +528,9 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This method does not guarantee
* that the Allocation is compatible with the input buffer; it copies memory
@@ -511,6 +539,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFromUnchecked(short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -519,7 +548,9 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This method does not guarantee
* that the Allocation is compatible with the input buffer; it copies memory
@@ -528,6 +559,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFromUnchecked(byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -536,7 +568,9 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This method does not guarantee
* that the Allocation is compatible with the input buffer; it copies memory
@@ -545,6 +579,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFromUnchecked(float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -553,8 +588,10 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy into this Allocation from an array. This variant is type checked
* and will generate exceptions if the Allocation's {@link
@@ -563,6 +600,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFrom(int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -571,6 +609,7 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -581,6 +620,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFrom(short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -589,6 +629,7 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -599,6 +640,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFrom(byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -607,6 +649,7 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -617,6 +660,7 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copyFrom(float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (mCurrentDimZ > 0) {
copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
@@ -625,6 +669,7 @@ public class Allocation extends BaseObj {
} else {
copy1DRangeFrom(0, mCurrentCount, d);
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -641,6 +686,7 @@ public class Allocation extends BaseObj {
* @param b the source bitmap
*/
public void copyFrom(Bitmap b) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (b.getConfig() == null) {
Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
@@ -652,6 +698,7 @@ public class Allocation extends BaseObj {
validateBitmapSize(b);
validateBitmapFormat(b);
mRS.nAllocationCopyFromBitmap(getID(mRS), b);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -661,14 +708,15 @@ public class Allocation extends BaseObj {
* @param a the source allocation
*/
public void copyFrom(Allocation a) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
mRS.validate();
if (!mType.equals(a.getType())) {
throw new RSIllegalArgumentException("Types of allocations must match.");
}
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, a, 0, 0);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
-
/**
* This is only intended to be used by auto-generated code reflected from
* the RenderScript script files and should not be used by developers.
@@ -759,10 +807,13 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length * 4, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy an array into part of this Allocation. This method does not
* guarantee that the Allocation is compatible with the input buffer.
@@ -772,10 +823,13 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length * 2, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy an array into part of this Allocation. This method does not
* guarantee that the Allocation is compatible with the input buffer.
@@ -785,10 +839,13 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
+
/**
* Copy an array into part of this Allocation. This method does not
* guarantee that the Allocation is compatible with the input buffer.
@@ -798,9 +855,11 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
int dataSize = mType.mElement.getBytesSize() * count;
data1DChecks(off, count, d.length * 4, dataSize);
mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -813,8 +872,10 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt32();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -827,8 +888,10 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt16();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -841,8 +904,10 @@ public class Allocation extends BaseObj {
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt8();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -855,10 +920,11 @@ public class Allocation extends BaseObj {
* @param d the source data array.
*/
public void copy1DRangeFrom(int off, int count, float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsFloat32();
copy1DRangeFromUnchecked(off, count, d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
-
/**
* Copy part of an Allocation into this Allocation.
*
@@ -869,6 +935,7 @@ public class Allocation extends BaseObj {
* be copied.
*/
public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
mRS.nAllocationData2D(getIDSafe(), off, 0,
mSelectedLOD, mSelectedFace.mID,
count, 1, data.getID(mRS), dataOff, 0,
@@ -893,34 +960,41 @@ public class Allocation extends BaseObj {
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, byte[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, short[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 2);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, int[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 4);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, float[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 4);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
-
/**
* Copy from an array into a rectangular region in this Allocation. The
* array is assumed to be tightly packed.
@@ -932,8 +1006,10 @@ public class Allocation extends BaseObj {
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt8();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -947,8 +1023,10 @@ public class Allocation extends BaseObj {
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt16();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -962,8 +1040,10 @@ public class Allocation extends BaseObj {
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt32();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -977,8 +1057,10 @@ public class Allocation extends BaseObj {
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsFloat32();
copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -995,12 +1077,14 @@ public class Allocation extends BaseObj {
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h,
Allocation data, int dataXoff, int dataYoff) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
mSelectedLOD, mSelectedFace.mID,
w, h, data.getID(mRS), dataXoff, dataYoff,
data.mSelectedLOD, data.mSelectedFace.mID);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1013,6 +1097,7 @@ public class Allocation extends BaseObj {
* @param data the Bitmap to be copied
*/
public void copy2DRangeFrom(int xoff, int yoff, Bitmap data) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
mRS.validate();
if (data.getConfig() == null) {
Bitmap newBitmap = Bitmap.createBitmap(data.getWidth(), data.getHeight(), Bitmap.Config.ARGB_8888);
@@ -1024,6 +1109,7 @@ public class Allocation extends BaseObj {
validateBitmapFormat(data);
validate2DRange(xoff, yoff, data.getWidth(), data.getHeight());
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
private void validate3DRange(int xoff, int yoff, int zoff, int w, int h, int d) {
@@ -1166,10 +1252,12 @@ public class Allocation extends BaseObj {
* @param b The bitmap to be set from the Allocation.
*/
public void copyTo(Bitmap b) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
mRS.validate();
validateBitmapFormat(b);
validateBitmapSize(b);
mRS.nAllocationCopyToBitmap(getID(mRS), b);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1180,9 +1268,11 @@ public class Allocation extends BaseObj {
* @param d The array to be set from the Allocation.
*/
public void copyTo(byte[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt8();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1193,9 +1283,11 @@ public class Allocation extends BaseObj {
* @param d The array to be set from the Allocation.
*/
public void copyTo(short[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt16();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1206,9 +1298,11 @@ public class Allocation extends BaseObj {
* @param d The array to be set from the Allocation.
*/
public void copyTo(int[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt32();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1219,9 +1313,11 @@ public class Allocation extends BaseObj {
* @param d The array to be set from the Allocation.
*/
public void copyTo(float[] d) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsFloat32();
mRS.validate();
mRS.nAllocationRead(getID(mRS), d);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1271,6 +1367,7 @@ public class Allocation extends BaseObj {
* utilized
*/
static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createTyped");
rs.validate();
if (type.getID(rs) == 0) {
throw new RSInvalidStateException("Bad Type");
@@ -1279,6 +1376,7 @@ public class Allocation extends BaseObj {
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
return new Allocation(id, rs, type, usage);
}
@@ -1323,6 +1421,7 @@ public class Allocation extends BaseObj {
*/
static public Allocation createSized(RenderScript rs, Element e,
int count, int usage) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createSized");
rs.validate();
Type.Builder b = new Type.Builder(rs, e);
b.setX(count);
@@ -1332,6 +1431,7 @@ public class Allocation extends BaseObj {
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
return new Allocation(id, rs, t, usage);
}
@@ -1391,6 +1491,7 @@ public class Allocation extends BaseObj {
static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
MipmapControl mips,
int usage) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "createFromBitmap");
rs.validate();
// WAR undocumented color formats
@@ -1426,6 +1527,7 @@ public class Allocation extends BaseObj {
if (id == 0) {
throw new RSRuntimeException("Load failed.");
}
+ Trace.traceEnd(RenderScript.TRACE_TAG);
return new Allocation(id, rs, t, usage);
}
@@ -1739,26 +1841,22 @@ public class Allocation extends BaseObj {
}
/**
- * @hide
- *
* Interface to handle notification when new buffers are available via
* {@link #USAGE_IO_INPUT}. An application will receive one notification
* when a buffer is available. Additional buffers will not trigger new
* notifications until a buffer is processed.
*/
- public interface IoInputNotifier {
+ public interface OnBufferAvailableListener {
public void onBufferAvailable(Allocation a);
}
/**
- * @hide
- *
* Set a notification handler for {@link #USAGE_IO_INPUT}.
*
- * @param callback instance of the IoInputNotifier class to be called
- * when buffer arrive.
+ * @param callback instance of the OnBufferAvailableListener
+ * class to be called when buffer arrive.
*/
- public void setIoInputNotificationHandler(IoInputNotifier callback) {
+ public void setOnBufferAvailableListener(OnBufferAvailableListener callback) {
synchronized(mAllocationMap) {
mAllocationMap.put(new Integer(getID(mRS)), this);
mBufferNotifier = callback;
diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java
index a6645bb..84a9ad3 100644
--- a/graphics/java/android/renderscript/AllocationAdapter.java
+++ b/graphics/java/android/renderscript/AllocationAdapter.java
@@ -224,7 +224,6 @@ public class AllocationAdapter extends Allocation {
}
static public AllocationAdapter create2D(RenderScript rs, Allocation a) {
- android.util.Log.e("rs", "create2d " + a);
rs.validate();
AllocationAdapter aa = new AllocationAdapter(0, rs, a);
aa.mConstrainedLOD = true;
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index e17d79a..fc54532 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -16,7 +16,7 @@
package android.renderscript;
-import android.util.Log;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* BaseObj is the base class for all RenderScript objects owned by a RS context.
@@ -109,17 +109,31 @@ public class BaseObj {
return mName;
}
- protected void finalize() throws Throwable {
- if (!mDestroyed) {
- if(mID != 0 && mRS.isAlive()) {
+ private void helpDestroy() {
+ boolean shouldDestroy = false;
+ synchronized(this) {
+ if (!mDestroyed) {
+ shouldDestroy = true;
+ mDestroyed = true;
+ }
+ }
+
+ if (shouldDestroy) {
+ // must include nObjDestroy in the critical section
+ ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
+ rlock.lock();
+ // AllocationAdapters are BaseObjs with an ID of 0 but should not be passed to nObjDestroy
+ if(mRS.isAlive() && mID != 0) {
mRS.nObjDestroy(mID);
}
+ rlock.unlock();
mRS = null;
mID = 0;
- mDestroyed = true;
- //Log.v(RenderScript.LOG_TAG, getClass() +
- // " auto finalizing object without having released the RS reference.");
}
+ }
+
+ protected void finalize() throws Throwable {
+ helpDestroy();
super.finalize();
}
@@ -128,12 +142,11 @@ public class BaseObj {
* primary use is to force immediate cleanup of resources when it is
* believed the GC will not respond quickly enough.
*/
- synchronized public void destroy() {
+ public void destroy() {
if(mDestroyed) {
throw new RSInvalidStateException("Object already destroyed.");
}
- mDestroyed = true;
- mRS.nObjDestroy(mID);
+ helpDestroy();
}
/**
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 3838c61..68badfa 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -725,6 +725,13 @@ public class Element extends BaseObj {
return rs.mElement_LONG_4;
}
+ public static Element YUV(RenderScript rs) {
+ if (rs.mElement_YUV == null) {
+ rs.mElement_YUV = createPixel(rs, DataType.UNSIGNED_8, DataKind.PIXEL_YUV);
+ }
+ return rs.mElement_YUV;
+ }
+
public static Element MATRIX_4X4(RenderScript rs) {
if(rs.mElement_MATRIX_4X4 == null) {
rs.mElement_MATRIX_4X4 = createUser(rs, DataType.MATRIX_4X4);
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 1264adc..35fe8c4 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -18,6 +18,8 @@ package android.renderscript;
import java.io.File;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -29,8 +31,8 @@ import android.graphics.SurfaceTexture;
import android.os.Process;
import android.util.Log;
import android.view.Surface;
-
-
+import android.os.SystemProperties;
+import android.os.Trace;
/**
* This class provides access to a RenderScript context, which controls RenderScript
@@ -44,6 +46,8 @@ import android.view.Surface;
* </div>
**/
public class RenderScript {
+ static final long TRACE_TAG = Trace.TRACE_TAG_RS;
+
static final String LOG_TAG = "RenderScript_jni";
static final boolean DEBUG = false;
@SuppressWarnings({"UnusedDeclaration", "deprecation"})
@@ -55,20 +59,35 @@ public class RenderScript {
* We use a class initializer to allow the native code to cache some
* field offsets.
*/
- @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // TODO: now used locally; remove?
static boolean sInitialized;
native static void _nInit();
+ static Object sRuntime;
+ static Method registerNativeAllocation;
+ static Method registerNativeFree;
static {
sInitialized = false;
- try {
- System.loadLibrary("rs_jni");
- _nInit();
- sInitialized = true;
- } catch (UnsatisfiedLinkError e) {
- Log.e(LOG_TAG, "Error loading RS jni library: " + e);
- throw new RSRuntimeException("Error loading RS jni library: " + e);
+ if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
+ try {
+ Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
+ Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
+ sRuntime = get_runtime.invoke(null);
+ registerNativeAllocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
+ registerNativeFree = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error loading GC methods: " + e);
+ throw new RSRuntimeException("Error loading GC methods: " + e);
+ }
+ try {
+ System.loadLibrary("rs_jni");
+ _nInit();
+ sInitialized = true;
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(LOG_TAG, "Error loading RS jni library: " + e);
+ throw new RSRuntimeException("Error loading RS jni library: " + e);
+ }
}
}
@@ -84,6 +103,20 @@ public class RenderScript {
static File mCacheDir;
+ // this should be a monotonically increasing ID
+ // used in conjunction with the API version of a device
+ static final long sMinorID = 1;
+
+ /**
+ * Returns an identifier that can be used to identify a particular
+ * minor version of RS.
+ *
+ * @hide
+ */
+ public static long getMinorID() {
+ return sMinorID;
+ }
+
/**
* Sets the directory to use as a persistent storage for the
* renderscript object file cache.
@@ -92,6 +125,11 @@ public class RenderScript {
* @param cacheDir A directory the current process can write to
*/
public static void setupDiskCache(File cacheDir) {
+ if (!sInitialized) {
+ Log.e(LOG_TAG, "RenderScript.setupDiskCache() called when disabled");
+ return;
+ }
+
// Defer creation of cache path to nScriptCCreate().
mCacheDir = cacheDir;
}
@@ -128,6 +166,7 @@ public class RenderScript {
}
ContextType mContextType;
+ ReentrantReadWriteLock mRWLock;
// Methods below are wrapped to protect the non-threadsafe
// lockless fifo.
@@ -155,7 +194,18 @@ public class RenderScript {
native void rsnContextDestroy(int con);
synchronized void nContextDestroy() {
validate();
- rsnContextDestroy(mContext);
+
+ // take teardown lock
+ // teardown lock can only be taken when no objects are being destroyed
+ ReentrantReadWriteLock.WriteLock wlock = mRWLock.writeLock();
+ wlock.lock();
+
+ int curCon = mContext;
+ // context is considered dead as of this point
+ mContext = 0;
+
+ wlock.unlock();
+ rsnContextDestroy(curCon);
}
native void rsnContextSetSurface(int con, int w, int h, Surface sur);
synchronized void nContextSetSurface(int w, int h, Surface sur) {
@@ -240,8 +290,9 @@ public class RenderScript {
validate();
return rsnGetName(mContext, obj);
}
+ // nObjDestroy is explicitly _not_ synchronous to prevent crashes in finalizers
native void rsnObjDestroy(int con, int id);
- synchronized void nObjDestroy(int id) {
+ void nObjDestroy(int id) {
// There is a race condition here. The calling code may be run
// by the gc while teardown is occuring. This protects againts
// deleting dead objects.
@@ -875,6 +926,8 @@ public class RenderScript {
Element mElement_LONG_3;
Element mElement_LONG_4;
+ Element mElement_YUV;
+
Element mElement_MATRIX_4X4;
Element mElement_MATRIX_3X3;
Element mElement_MATRIX_2X2;
@@ -1111,6 +1164,7 @@ public class RenderScript {
if (ctx != null) {
mApplicationContext = ctx.getApplicationContext();
}
+ mRWLock = new ReentrantReadWriteLock();
}
/**
@@ -1137,6 +1191,11 @@ public class RenderScript {
* @return RenderScript
*/
public static RenderScript create(Context ctx, int sdkVersion, ContextType ct) {
+ if (!sInitialized) {
+ Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash");
+ return null;
+ }
+
RenderScript rs = new RenderScript(ctx);
rs.mDev = rs.nDeviceCreate();
@@ -1200,6 +1259,8 @@ public class RenderScript {
*/
public void destroy() {
validate();
+ nContextFinish();
+
nContextDeinitToClient(mContext);
mMessageThread.mRun = false;
try {
@@ -1208,7 +1269,6 @@ public class RenderScript {
}
nContextDestroy();
- mContext = 0;
nDeviceDestroy(mDev);
mDev = 0;
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
index 77b9385..32c3d15 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
@@ -21,15 +21,27 @@ import android.util.Log;
/**
* Intrinsic for applying a color matrix to allocations.
*
- * This has the same effect as loading each element and
- * converting it to a {@link Element#F32_4}, multiplying the
- * result by the 4x4 color matrix as performed by
- * rsMatrixMultiply() and writing it to the output after
- * conversion back to {@link Element#U8_4}.
+ * If the element type is {@link Element.DataType#UNSIGNED_8},
+ * it is converted to {@link Element.DataType#FLOAT_32} and
+ * normalized from (0-255) to (0-1). If the incoming vector size
+ * is less than four, a {@link Element#F32_4} is created by
+ * filling the missing vector channels with zero. This value is
+ * then multiplied by the 4x4 color matrix as performed by
+ * rsMatrixMultiply(), adding a {@link Element#F32_4}, and then
+ * writing it to the output {@link Allocation}.
+ *
+ * If the ouptut type is unsigned, the value is normalized from
+ * (0-1) to (0-255) and converted. If the output vector size is
+ * less than four, the unused channels are discarded.
+ *
+ * Supported elements types are {@link Element#U8}, {@link
+ * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
+ * {@link Element#F32}, {@link Element#F32_2}, {@link
+ * Element#F32_3}, and {@link Element#F32_4}.
**/
public final class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
private final Matrix4f mMatrix = new Matrix4f();
- private Allocation mInput;
+ private final Float4 mAdd = new Float4();
private ScriptIntrinsicColorMatrix(int id, RenderScript rs) {
super(id, rs);
@@ -39,18 +51,31 @@ public final class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
* Create an intrinsic for applying a color matrix to an
* allocation.
*
- * Supported elements types are {@link Element#U8_4}
- *
* @param rs The RenderScript context
- * @param e Element type for intputs and outputs
+ * @param e Element type for inputs and outputs, As of API 19,
+ * this parameter is ignored. The Element type check is
+ * performed in the kernel launch.
+ *
+ * @deprecated Use the single argument version as Element is now
+ * ignored.
*
* @return ScriptIntrinsicColorMatrix
*/
+ @Deprecated
public static ScriptIntrinsicColorMatrix create(RenderScript rs, Element e) {
- if (!e.isCompatible(Element.U8_4(rs))) {
- throw new RSIllegalArgumentException("Unsuported element type.");
- }
- int id = rs.nScriptIntrinsicCreate(2, e.getID(rs));
+ return create(rs);
+ }
+
+ /**
+ * Create an intrinsic for applying a color matrix to an
+ * allocation.
+ *
+ * @param rs The RenderScript context
+ *
+ * @return ScriptIntrinsicColorMatrix
+ */
+ public static ScriptIntrinsicColorMatrix create(RenderScript rs) {
+ int id = rs.nScriptIntrinsicCreate(2, 0);
return new ScriptIntrinsicColorMatrix(id, rs);
}
@@ -84,6 +109,49 @@ public final class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
}
/**
+ * Set the value to be added after the color matrix has been
+ * applied. The default value is {0, 0, 0, 0}
+ *
+ * @param f The float4 value to be added.
+ */
+ public void setAdd(Float4 f) {
+ mAdd.x = f.x;
+ mAdd.y = f.y;
+ mAdd.z = f.z;
+ mAdd.w = f.w;
+
+ FieldPacker fp = new FieldPacker(4*4);
+ fp.addF32(f.x);
+ fp.addF32(f.y);
+ fp.addF32(f.z);
+ fp.addF32(f.w);
+ setVar(1, fp);
+ }
+
+ /**
+ * Set the value to be added after the color matrix has been
+ * applied. The default value is {0, 0, 0, 0}
+ *
+ * @param r The red add value.
+ * @param g The green add value.
+ * @param b The blue add value.
+ * @param a The alpha add value.
+ */
+ public void setAdd(float r, float g, float b, float a) {
+ mAdd.x = r;
+ mAdd.y = g;
+ mAdd.z = b;
+ mAdd.w = a;
+
+ FieldPacker fp = new FieldPacker(4*4);
+ fp.addF32(mAdd.x);
+ fp.addF32(mAdd.y);
+ fp.addF32(mAdd.z);
+ fp.addF32(mAdd.w);
+ setVar(1, fp);
+ }
+
+ /**
* Set a color matrix to convert from RGB to luminance. The alpha channel
* will be a copy.
*
@@ -142,13 +210,45 @@ public final class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
/**
- * Invoke the kernel and apply the matrix to each cell of ain and copy to
- * aout.
+ * Invoke the kernel and apply the matrix to each cell of input
+ * {@link Allocation} and copy to the output {@link Allocation}.
+ *
+ * If the vector size of the input is less than four, the
+ * remaining components are treated as zero for the matrix
+ * multiply.
+ *
+ * If the output vector size is less than four, the unused
+ * vector components are discarded.
+ *
*
* @param ain Input allocation
* @param aout Output allocation
*/
public void forEach(Allocation ain, Allocation aout) {
+ if (!ain.getElement().isCompatible(Element.U8(mRS)) &&
+ !ain.getElement().isCompatible(Element.U8_2(mRS)) &&
+ !ain.getElement().isCompatible(Element.U8_3(mRS)) &&
+ !ain.getElement().isCompatible(Element.U8_4(mRS)) &&
+ !ain.getElement().isCompatible(Element.F32(mRS)) &&
+ !ain.getElement().isCompatible(Element.F32_2(mRS)) &&
+ !ain.getElement().isCompatible(Element.F32_3(mRS)) &&
+ !ain.getElement().isCompatible(Element.F32_4(mRS))) {
+
+ throw new RSIllegalArgumentException("Unsuported element type.");
+ }
+
+ if (!aout.getElement().isCompatible(Element.U8(mRS)) &&
+ !aout.getElement().isCompatible(Element.U8_2(mRS)) &&
+ !aout.getElement().isCompatible(Element.U8_3(mRS)) &&
+ !aout.getElement().isCompatible(Element.U8_4(mRS)) &&
+ !aout.getElement().isCompatible(Element.F32(mRS)) &&
+ !aout.getElement().isCompatible(Element.F32_2(mRS)) &&
+ !aout.getElement().isCompatible(Element.F32_3(mRS)) &&
+ !aout.getElement().isCompatible(Element.F32_4(mRS))) {
+
+ throw new RSIllegalArgumentException("Unsuported element type.");
+ }
+
forEach(0, ain, aout, null);
}
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
index c9c54b2..5d3c1d3 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -31,7 +31,10 @@ public final class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic {
}
/**
- * Supported elements types are {@link Element#U8_4}
+ * Supported elements types are {@link Element#U8}, {@link
+ * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
+ * {@link Element#F32}, {@link Element#F32_2}, {@link
+ * Element#F32_3}, and {@link Element#F32_4}
*
* The default coefficients are.
*
@@ -48,7 +51,14 @@ public final class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic {
*/
public static ScriptIntrinsicConvolve3x3 create(RenderScript rs, Element e) {
float f[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0};
- if (!e.isCompatible(Element.U8_4(rs))) {
+ if (!e.isCompatible(Element.U8(rs)) &&
+ !e.isCompatible(Element.U8_2(rs)) &&
+ !e.isCompatible(Element.U8_3(rs)) &&
+ !e.isCompatible(Element.U8_4(rs)) &&
+ !e.isCompatible(Element.F32(rs)) &&
+ !e.isCompatible(Element.F32_2(rs)) &&
+ !e.isCompatible(Element.F32_3(rs)) &&
+ !e.isCompatible(Element.F32_4(rs))) {
throw new RSIllegalArgumentException("Unsuported element type.");
}
int id = rs.nScriptIntrinsicCreate(1, e.getID(rs));
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
index c6e1e39..ad09f95 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -31,7 +31,10 @@ public final class ScriptIntrinsicConvolve5x5 extends ScriptIntrinsic {
}
/**
- * Supported elements types are {@link Element#U8_4}
+ * Supported elements types are {@link Element#U8}, {@link
+ * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
+ * {@link Element#F32}, {@link Element#F32_2}, {@link
+ * Element#F32_3}, and {@link Element#F32_4}
*
* The default coefficients are.
* <code>
@@ -48,6 +51,17 @@ public final class ScriptIntrinsicConvolve5x5 extends ScriptIntrinsic {
* @return ScriptIntrinsicConvolve5x5
*/
public static ScriptIntrinsicConvolve5x5 create(RenderScript rs, Element e) {
+ if (!e.isCompatible(Element.U8(rs)) &&
+ !e.isCompatible(Element.U8_2(rs)) &&
+ !e.isCompatible(Element.U8_3(rs)) &&
+ !e.isCompatible(Element.U8_4(rs)) &&
+ !e.isCompatible(Element.F32(rs)) &&
+ !e.isCompatible(Element.F32_2(rs)) &&
+ !e.isCompatible(Element.F32_3(rs)) &&
+ !e.isCompatible(Element.F32_4(rs))) {
+ throw new RSIllegalArgumentException("Unsuported element type.");
+ }
+
int id = rs.nScriptIntrinsicCreate(4, e.getID(rs));
return new ScriptIntrinsicConvolve5x5(id, rs);
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicHistogram.java b/graphics/java/android/renderscript/ScriptIntrinsicHistogram.java
new file mode 100644
index 0000000..adc2d95
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicHistogram.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 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.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+/**
+ * Intrinsic Histogram filter.
+ *
+ *
+ **/
+public final class ScriptIntrinsicHistogram extends ScriptIntrinsic {
+ private Allocation mOut;
+
+ private ScriptIntrinsicHistogram(int id, RenderScript rs) {
+ super(id, rs);
+ }
+
+ /**
+ * Create an intrinsic for calculating the histogram of an uchar
+ * or uchar4 image.
+ *
+ * Supported elements types are
+ * {@link Element#U8_4}, {@link Element#U8_3},
+ * {@link Element#U8_2}, {@link Element#U8}
+ *
+ * @param rs The RenderScript context
+ * @param e Element type for inputs
+ *
+ * @return ScriptIntrinsicHistogram
+ */
+ public static ScriptIntrinsicHistogram create(RenderScript rs, Element e) {
+ if ((!e.isCompatible(Element.U8_4(rs))) &&
+ (!e.isCompatible(Element.U8_3(rs))) &&
+ (!e.isCompatible(Element.U8_2(rs))) &&
+ (!e.isCompatible(Element.U8(rs)))) {
+ throw new RSIllegalArgumentException("Unsuported element type.");
+ }
+ int id = rs.nScriptIntrinsicCreate(9, e.getID(rs));
+ ScriptIntrinsicHistogram sib = new ScriptIntrinsicHistogram(id, rs);
+ return sib;
+ }
+
+ /**
+ * Process an input buffer and place the histogram into the
+ * output allocation. The output allocation may be a narrower
+ * vector size than the input. In this case the vector size of
+ * the output is used to determine how many of the input
+ * channels are used in the computation. This is useful if you
+ * have an RGBA input buffer but only want the histogram for
+ * RGB.
+ *
+ * 1D and 2D input allocations are supported.
+ *
+ * @param ain The input image
+ */
+ public void forEach(Allocation ain) {
+ if (ain.getType().getElement().getVectorSize() <
+ mOut.getType().getElement().getVectorSize()) {
+
+ throw new RSIllegalArgumentException(
+ "Input vector size must be >= output vector size.");
+ }
+ if (ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+ ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+ throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+ }
+
+ forEach(0, ain, null, null);
+ }
+
+ /**
+ * Set the coefficients used for the RGBA to Luminocity
+ * calculation. The default is {0.299f, 0.587f, 0.114f, 0.f}.
+ *
+ * Coefficients must be >= 0 and sum to 1.0 or less.
+ *
+ * @param r Red coefficient
+ * @param g Green coefficient
+ * @param b Blue coefficient
+ * @param a Alpha coefficient
+ */
+ public void setDotCoefficients(float r, float g, float b, float a) {
+ if ((r < 0.f) || (g < 0.f) || (b < 0.f) || (a < 0.f)) {
+ throw new RSIllegalArgumentException("Coefficient may not be negative.");
+ }
+ if ((r + g + b + a) > 1.f) {
+ throw new RSIllegalArgumentException("Sum of coefficients must be 1.0 or less.");
+ }
+
+ FieldPacker fp = new FieldPacker(16);
+ fp.addF32(r);
+ fp.addF32(g);
+ fp.addF32(b);
+ fp.addF32(a);
+ setVar(0, fp);
+ }
+
+ /**
+ * Set the output of the histogram. 32 bit integer types are
+ * supported.
+ *
+ * @param aout The output allocation
+ */
+ public void setOutput(Allocation aout) {
+ mOut = aout;
+ if (mOut.getType().getElement() != Element.U32(mRS) &&
+ mOut.getType().getElement() != Element.U32_2(mRS) &&
+ mOut.getType().getElement() != Element.U32_3(mRS) &&
+ mOut.getType().getElement() != Element.U32_4(mRS) &&
+ mOut.getType().getElement() != Element.I32(mRS) &&
+ mOut.getType().getElement() != Element.I32_2(mRS) &&
+ mOut.getType().getElement() != Element.I32_3(mRS) &&
+ mOut.getType().getElement() != Element.I32_4(mRS)) {
+
+ throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+ }
+ if ((mOut.getType().getX() != 256) ||
+ (mOut.getType().getY() != 0) ||
+ mOut.getType().hasMipmaps() ||
+ (mOut.getType().getYuv() != 0)) {
+
+ throw new RSIllegalArgumentException("Output must be 1D, 256 elements.");
+ }
+ setVar(1, aout);
+ }
+
+ /**
+ * Process an input buffer and place the histogram into the
+ * output allocation. The dot product of the input channel and
+ * the coefficients from 'setDotCoefficients' are used to
+ * calculate the output values.
+ *
+ * 1D and 2D input allocations are supported.
+ *
+ * @param ain The input image
+ */
+ public void forEach_Dot(Allocation ain) {
+ if (mOut.getType().getElement().getVectorSize() != 1) {
+ throw new RSIllegalArgumentException("Output vector size must be one.");
+ }
+ if (ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+ ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+ throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+ }
+
+ forEach(1, ain, null, null);
+ }
+
+
+
+ /**
+ * Get a KernelID for this intrinsic kernel.
+ *
+ * @return Script.KernelID The KernelID object.
+ */
+ public Script.KernelID getKernelID_Separate() {
+ return createKernelID(0, 3, null, null);
+ }
+
+ /**
+ * Get a FieldID for the input field of this intrinsic.
+ *
+ * @return Script.FieldID The FieldID object.
+ */
+ public Script.FieldID getFieldID_Input() {
+ return createFieldID(1, null);
+ }
+}
+
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java b/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
index 9b5de9b..845625d 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
@@ -20,9 +20,9 @@ package android.renderscript;
/**
* Intrinsic for converting an Android YUV buffer to RGB.
*
- * The input allocation is supplied in NV21 format as a U8
- * element type. The output is RGBA, the alpha channel will be
- * set to 255.
+ * The input allocation should be supplied in a supported YUV format
+ * as a YUV element Allocation. The output is RGBA; the alpha channel
+ * will be set to 255.
*/
public final class ScriptIntrinsicYuvToRGB extends ScriptIntrinsic {
private Allocation mInput;
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index ef08c29..e023739 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -37,10 +37,11 @@ import android.util.Log;
* faces. LOD and cube map faces are booleans to indicate present or not
* present. </p>
*
- * <p>A Type also supports YUV format information to support an {@link
- * android.renderscript.Allocation} in a YUV format. The YUV formats supported
- * are {@link android.graphics.ImageFormat#YV12} and {@link
- * android.graphics.ImageFormat#NV21}.</p>
+ * <p>A Type also supports YUV format information to support an
+ * {@link android.renderscript.Allocation} in a YUV format. The YUV formats
+ * supported are {@link android.graphics.ImageFormat#YV12},
+ * {@link android.graphics.ImageFormat#NV21}, and
+ * {@link android.graphics.ImageFormat#YUV_420_888}</p>
*
* <div class="special reference">
* <h3>Developer Guides</h3>
@@ -284,16 +285,19 @@ public class Type extends BaseObj {
/**
* Set the YUV layout for a Type.
*
- * @param yuvFormat {@link android.graphics.ImageFormat#YV12} or {@link android.graphics.ImageFormat#NV21}
+ * @param yuvFormat {@link android.graphics.ImageFormat#YV12}, {@link android.graphics.ImageFormat#NV21}, or
+ * {@link android.graphics.ImageFormat#YUV_420_888}.
*/
public Builder setYuvFormat(int yuvFormat) {
switch (yuvFormat) {
case android.graphics.ImageFormat.NV21:
case android.graphics.ImageFormat.YV12:
+ case android.graphics.ImageFormat.YUV_420_888:
break;
default:
- throw new RSIllegalArgumentException("Only NV21 and YV12 are supported..");
+ throw new RSIllegalArgumentException(
+ "Only ImageFormat.NV21, .YV12, and .YUV_420_888 are supported..");
}
mYuv = yuvFormat;