summaryrefslogtreecommitdiff
path: root/graphics/java
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/java')
-rw-r--r--graphics/java/android/graphics/Atlas.java416
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java557
-rw-r--r--graphics/java/android/graphics/Bitmap.java86
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java36
-rw-r--r--graphics/java/android/graphics/BitmapRegionDecoder.java4
-rw-r--r--graphics/java/android/graphics/Canvas.java1654
-rw-r--r--graphics/java/android/graphics/Color.java19
-rw-r--r--graphics/java/android/graphics/ColorSpace.java3845
-rw-r--r--graphics/java/android/graphics/ComposeShader.java53
-rw-r--r--graphics/java/android/graphics/FontListParser.java74
-rw-r--r--graphics/java/android/graphics/LayerRasterizer.java18
-rw-r--r--graphics/java/android/graphics/Matrix.java632
-rw-r--r--graphics/java/android/graphics/Outline.java36
-rw-r--r--graphics/java/android/graphics/Paint.java409
-rw-r--r--graphics/java/android/graphics/Path.java204
-rw-r--r--graphics/java/android/graphics/PathMeasure.java2
-rw-r--r--graphics/java/android/graphics/PorterDuffXfermode.java10
-rw-r--r--graphics/java/android/graphics/Rasterizer.java13
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java1
-rw-r--r--graphics/java/android/graphics/Xfermode.java15
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java20
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java9
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java20
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java31
-rw-r--r--graphics/java/android/graphics/drawable/DrawableWrapper.java11
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java2
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java130
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java10
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java8
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java68
-rw-r--r--graphics/java/android/graphics/pdf/PdfRenderer.java14
-rw-r--r--graphics/java/android/view/PixelCopy.java139
33 files changed, 6264 insertions, 2289 deletions
diff --git a/graphics/java/android/graphics/Atlas.java b/graphics/java/android/graphics/Atlas.java
deleted file mode 100644
index e0a534583b67..000000000000
--- a/graphics/java/android/graphics/Atlas.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * 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 {
- /**
- * WARNING: These flag values are part of the on-disk configuration information,
- * do not change their values.
- */
-
- /** DELETED: FLAG_ROTATION = 0x01 */
-
- /**
- * 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.
- */
- 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;
- }
-
- 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 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) {
- 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
- */
- private boolean insert(Cell cell, Cell prev, int width, int height, Entry entry) {
- if (cell.width < width || cell.height < height) {
- 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;
-
- return true;
- }
- }
-}
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
new file mode 100644
index 000000000000..f135484adcc0
--- /dev/null
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Size;
+import android.graphics.Canvas.VertexMode;
+import android.text.GraphicsOperations;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextUtils;
+import android.view.RecordingCanvas;
+
+/**
+ * This class is a base class for Canvas's drawing operations. Any modifications here
+ * should be accompanied by a similar modification to {@link RecordingCanvas}.
+ *
+ * The purpose of this class is to minimize the cost of deciding between regular JNI
+ * and @FastNative JNI to just the virtual call that Canvas already has.
+ *
+ * @hide
+ */
+public abstract class BaseCanvas {
+ /**
+ * Should only be assigned in constructors (or setBitmap if software canvas),
+ * freed by NativeAllocation.
+ */
+ protected long mNativeCanvasWrapper;
+
+ /**
+ * Used to determine when compatibility scaling is in effect.
+ */
+ protected int mScreenDensity = Bitmap.DENSITY_NONE;
+ protected int mDensity = Bitmap.DENSITY_NONE;
+
+ protected 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);
+ }
+ }
+
+ protected final static void checkRange(int length, int offset, int count) {
+ if ((offset | count) < 0 || offset + count > length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ public boolean isHardwareAccelerated() {
+ return false;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Drawing methods
+ // These are also implemented in DisplayListCanvas so that we can
+ // selectively apply on them
+ // Everything below here is copy/pasted from Canvas.java
+ // The JNI registration is handled by android_view_Canvas.cpp
+ // ---------------------------------------------------------------------------
+
+ public void drawArc(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle, boolean useCenter, @NonNull Paint paint) {
+ nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
+ useCenter, paint.getNativeInstance());
+ }
+
+ public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
+ @NonNull Paint paint) {
+ drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
+ paint);
+ }
+
+ public void drawARGB(int a, int r, int g, int b) {
+ drawColor(Color.argb(a, r, g, b));
+ }
+
+ public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
+ throwIfCannotDraw(bitmap);
+ nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top,
+ paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
+ bitmap.mDensity);
+ }
+
+ public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
+ nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap, matrix.ni(),
+ paint != null ? paint.getNativeInstance() : 0);
+ }
+
+ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
+ @Nullable Paint paint) {
+ if (dst == null) {
+ throw new NullPointerException();
+ }
+ throwIfCannotDraw(bitmap);
+ final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
+
+ int left, top, right, bottom;
+ if (src == null) {
+ left = top = 0;
+ right = bitmap.getWidth();
+ bottom = bitmap.getHeight();
+ } else {
+ left = src.left;
+ right = src.right;
+ top = src.top;
+ bottom = src.bottom;
+ }
+
+ nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+ bitmap.mDensity);
+ }
+
+ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
+ @Nullable Paint paint) {
+ if (dst == null) {
+ throw new NullPointerException();
+ }
+ throwIfCannotDraw(bitmap);
+ final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
+
+ float left, top, right, bottom;
+ if (src == null) {
+ left = top = 0;
+ right = bitmap.getWidth();
+ bottom = bitmap.getHeight();
+ } else {
+ left = src.left;
+ right = src.right;
+ top = src.top;
+ bottom = src.bottom;
+ }
+
+ nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+ bitmap.mDensity);
+ }
+
+ @Deprecated
+ public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y,
+ int width, int height, boolean hasAlpha, @Nullable Paint paint) {
+ // check for valid input
+ if (width < 0) {
+ throw new IllegalArgumentException("width must be >= 0");
+ }
+ if (height < 0) {
+ throw new IllegalArgumentException("height must be >= 0");
+ }
+ if (Math.abs(stride) < width) {
+ throw new IllegalArgumentException("abs(stride) must be >= width");
+ }
+ int lastScanline = offset + (height - 1) * stride;
+ int length = colors.length;
+ if (offset < 0 || (offset + width > length) || lastScanline < 0
+ || (lastScanline + width > length)) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ // quick escape if there's nothing to draw
+ if (width == 0 || height == 0) {
+ return;
+ }
+ // punch down to native for the actual draw
+ nDrawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha,
+ paint != null ? paint.getNativeInstance() : 0);
+ }
+
+ @Deprecated
+ public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y,
+ int width, int height, boolean hasAlpha, @Nullable Paint paint) {
+ // call through to the common float version
+ drawBitmap(colors, offset, stride, (float) x, (float) y, width, height,
+ hasAlpha, paint);
+ }
+
+ public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
+ @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
+ @Nullable Paint paint) {
+ if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ if (meshWidth == 0 || meshHeight == 0) {
+ return;
+ }
+ int count = (meshWidth + 1) * (meshHeight + 1);
+ // we mul by 2 since we need two floats per vertex
+ checkRange(verts.length, vertOffset, count * 2);
+ if (colors != null) {
+ // no mul by 2, since we need only 1 color per vertex
+ checkRange(colors.length, colorOffset, count);
+ }
+ nDrawBitmapMesh(mNativeCanvasWrapper, bitmap, meshWidth, meshHeight,
+ verts, vertOffset, colors, colorOffset,
+ paint != null ? paint.getNativeInstance() : 0);
+ }
+
+ public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
+ nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
+ }
+
+ public void drawColor(@ColorInt int color) {
+ nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
+ }
+
+ public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
+ nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
+ }
+
+ public void drawLine(float startX, float startY, float stopX, float stopY,
+ @NonNull Paint paint) {
+ nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
+ }
+
+ public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
+ @NonNull Paint paint) {
+ nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
+ }
+
+ public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
+ drawLines(pts, 0, pts.length, paint);
+ }
+
+ public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
+ }
+
+ public void drawOval(@NonNull RectF oval, @NonNull Paint paint) {
+ if (oval == null) {
+ throw new NullPointerException();
+ }
+ drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
+ }
+
+ public void drawPaint(@NonNull Paint paint) {
+ nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
+ }
+
+ public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
+ Bitmap bitmap = patch.getBitmap();
+ throwIfCannotDraw(bitmap);
+ final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
+ nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint,
+ mDensity, patch.getDensity());
+ }
+
+ public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
+ Bitmap bitmap = patch.getBitmap();
+ throwIfCannotDraw(bitmap);
+ final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
+ nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint,
+ mDensity, patch.getDensity());
+ }
+
+ public void drawPath(@NonNull Path path, @NonNull Paint paint) {
+ if (path.isSimplePath && path.rects != null) {
+ nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
+ } else {
+ nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
+ }
+ }
+
+ public void drawPoint(float x, float y, @NonNull Paint paint) {
+ nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
+ }
+
+ public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
+ @NonNull Paint paint) {
+ nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
+ }
+
+ public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
+ drawPoints(pts, 0, pts.length, paint);
+ }
+
+ @Deprecated
+ public void drawPosText(@NonNull char[] text, int index, int count,
+ @NonNull @Size(multiple = 2) float[] pos,
+ @NonNull Paint paint) {
+ if (index < 0 || index + count > text.length || count * 2 > pos.length) {
+ throw new IndexOutOfBoundsException();
+ }
+ for (int i = 0; i < count; i++) {
+ drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
+ }
+ }
+
+ @Deprecated
+ public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
+ @NonNull Paint paint) {
+ drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
+ }
+
+ public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
+ }
+
+ public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
+ drawRect(r.left, r.top, r.right, r.bottom, paint);
+ }
+
+ public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
+ nDrawRect(mNativeCanvasWrapper,
+ rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
+ }
+
+ public void drawRGB(int r, int g, int b) {
+ drawColor(Color.rgb(r, g, b));
+ }
+
+ public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
+ @NonNull Paint paint) {
+ nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
+ paint.getNativeInstance());
+ }
+
+ public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
+ drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
+ }
+
+ public void drawText(@NonNull char[] text, int index, int count, float x, float y,
+ @NonNull Paint paint) {
+ if ((index | count | (index + count) |
+ (text.length - index - count)) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
+ paint.getNativeInstance(), paint.mNativeTypeface);
+ }
+
+ public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
+ @NonNull Paint paint) {
+ if ((start | end | (end - start) | (text.length() - end)) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
+ paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawText(this, start, end, x, y,
+ paint);
+ } else {
+ char[] buf = TemporaryBuffer.obtain(end - start);
+ TextUtils.getChars(text, start, end, buf, 0);
+ nDrawText(mNativeCanvasWrapper, buf, 0, end - start, x, y,
+ paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ TemporaryBuffer.recycle(buf);
+ }
+ }
+
+ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
+ nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
+ paint.getNativeInstance(), paint.mNativeTypeface);
+ }
+
+ public void drawText(@NonNull String text, int start, int end, float x, float y,
+ @NonNull Paint paint) {
+ if ((start | end | (end - start) | (text.length() - end)) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
+ paint.getNativeInstance(), paint.mNativeTypeface);
+ }
+
+ public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,
+ float hOffset, float vOffset, @NonNull Paint paint) {
+ if (index < 0 || index + count > text.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
+ path.readOnlyNI(), hOffset, vOffset,
+ paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ }
+
+ public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
+ float vOffset, @NonNull Paint paint) {
+ if (text.length() > 0) {
+ nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
+ paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ }
+ }
+
+ public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex,
+ int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) {
+
+ if (text == null) {
+ throw new NullPointerException("text is null");
+ }
+ if (paint == null) {
+ throw new NullPointerException("paint is null");
+ }
+ if ((index | count | contextIndex | contextCount | index - contextIndex
+ | (contextIndex + contextCount) - (index + count)
+ | text.length - (contextIndex + contextCount)) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
+ x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
+ }
+
+ public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
+ int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) {
+
+ if (text == null) {
+ throw new NullPointerException("text is null");
+ }
+ if (paint == null) {
+ throw new NullPointerException("paint is null");
+ }
+ if ((start | end | contextStart | contextEnd | start - contextStart | end - start
+ | contextEnd - end | text.length() - contextEnd) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
+ contextEnd, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawTextRun(this, start, end,
+ contextStart, contextEnd, x, y, isRtl, paint);
+ } else {
+ int contextLen = contextEnd - contextStart;
+ int len = end - start;
+ char[] buf = TemporaryBuffer.obtain(contextLen);
+ TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+ nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
+ 0, contextLen, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
+ TemporaryBuffer.recycle(buf);
+ }
+ }
+
+ public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
+ int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
+ int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
+ @NonNull Paint paint) {
+ checkRange(verts.length, vertOffset, vertexCount);
+ if (isHardwareAccelerated()) {
+ return;
+ }
+ if (texs != null) {
+ checkRange(texs.length, texOffset, vertexCount);
+ }
+ if (colors != null) {
+ checkRange(colors.length, colorOffset, vertexCount / 2);
+ }
+ if (indices != null) {
+ checkRange(indices.length, indexOffset, indexCount);
+ }
+ nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
+ vertOffset, texs, texOffset, colors, colorOffset,
+ indices, indexOffset, indexCount, paint.getNativeInstance());
+ }
+
+ private static native void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top,
+ long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity);
+
+ private static native void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float srcLeft,
+ float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
+ float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity);
+
+ private static native void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride,
+ float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero);
+
+ private static native void nDrawColor(long nativeCanvas, int color, int mode);
+
+ private static native void nDrawPaint(long nativeCanvas, long nativePaint);
+
+ private static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle);
+
+ private static native void nDrawPoints(long canvasHandle, float[] pts, int offset, int count,
+ long paintHandle);
+
+ private static native void nDrawLine(long nativeCanvas, float startX, float startY, float stopX,
+ float stopY, long nativePaint);
+
+ private static native void nDrawLines(long canvasHandle, float[] pts, int offset, int count,
+ long paintHandle);
+
+ private static native void nDrawRect(long nativeCanvas, float left, float top, float right,
+ float bottom, long nativePaint);
+
+ private static native void nDrawOval(long nativeCanvas, float left, float top, float right,
+ float bottom, long nativePaint);
+
+ private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius,
+ long nativePaint);
+
+ private static native void nDrawArc(long nativeCanvas, float left, float top, float right,
+ float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint);
+
+ private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right,
+ float bottom, float rx, float ry, long nativePaint);
+
+ private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
+
+ private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint);
+
+ private static native void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch,
+ float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero,
+ int screenDensity, int bitmapDensity);
+
+ private static native void nDrawBitmapMatrix(long nativeCanvas, Bitmap bitmap,
+ long nativeMatrix, long nativePaint);
+
+ private static native void nDrawBitmapMesh(long nativeCanvas, Bitmap bitmap, int meshWidth,
+ int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset,
+ long nativePaint);
+
+ private static native void nDrawVertices(long nativeCanvas, int mode, int n, float[] verts,
+ int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset,
+ short[] indices, int indexOffset, int indexCount, long nativePaint);
+
+ private static native void nDrawText(long nativeCanvas, char[] text, int index, int count,
+ float x, float y, int flags, long nativePaint, long nativeTypeface);
+
+ private static native void nDrawText(long nativeCanvas, String text, int start, int end,
+ float x, float y, int flags, long nativePaint, long nativeTypeface);
+
+ private static native void nDrawTextRun(long nativeCanvas, String text, int start, int end,
+ int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint,
+ long nativeTypeface);
+
+ private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
+ int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
+ long nativeTypeface);
+
+ private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
+ long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint,
+ long nativeTypeface);
+
+ private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
+ float hOffset, float vOffset, int flags, long nativePaint, long nativeTypeface);
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 49721cf42666..cd75fe95121a 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -48,11 +48,6 @@ public final class Bitmap implements Parcelable {
// pixel data.
private static final long NATIVE_ALLOCATION_SIZE = 32;
- /**
- * Backing buffer for the Bitmap.
- */
- private byte[] mBuffer;
-
// Convenience for JNI access
private final long mNativePtr;
@@ -78,8 +73,8 @@ public final class Bitmap implements Parcelable {
private int mHeight;
private boolean mRecycled;
- // Package-scoped for fast access.
- int mDensity = getDefaultDensity();
+ /** @hide */
+ public int mDensity = getDefaultDensity();
private static volatile Matrix sScaleMatrix;
@@ -108,7 +103,7 @@ public final class Bitmap implements Parcelable {
* int (pointer).
*/
// called from JNI
- Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,
+ Bitmap(long nativeBitmap, int width, int height, int density,
boolean isMutable, boolean requestPremultiplied,
byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {
if (nativeBitmap == 0) {
@@ -119,7 +114,6 @@ public final class Bitmap implements Parcelable {
mHeight = height;
mIsMutable = isMutable;
mRequestPremultiplied = requestPremultiplied;
- mBuffer = buffer;
mNinePatchChunk = ninePatchChunk;
mNinePatchInsets = ninePatchInsets;
@@ -128,10 +122,7 @@ public final class Bitmap implements Parcelable {
}
mNativePtr = nativeBitmap;
- long nativeSize = NATIVE_ALLOCATION_SIZE;
- if (buffer == null) {
- nativeSize += getByteCount();
- }
+ long nativeSize = NATIVE_ALLOCATION_SIZE + getAllocationByteCount();
NativeAllocationRegistry registry = new NativeAllocationRegistry(
Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), nativeSize);
registry.registerNativeAllocation(this, nativeBitmap);
@@ -139,8 +130,9 @@ public final class Bitmap implements Parcelable {
/**
* Return the pointer to the native object.
+ * @hide
*/
- long getNativeInstance() {
+ public long getNativeInstance() {
return mNativePtr;
}
@@ -256,12 +248,8 @@ public final class Bitmap implements Parcelable {
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(mNativePtr, width, height, config.nativeInt,
- mBuffer.length, mRequestPremultiplied);
+ nativeReconfigure(mNativePtr, width, height, config.nativeInt, mRequestPremultiplied);
mWidth = width;
mHeight = height;
}
@@ -343,7 +331,6 @@ public final class Bitmap implements Parcelable {
// false indicates that it is still in use at the native level and these
// objects should not be collected now. They will be collected later when the
// Bitmap itself is collected.
- mBuffer = null;
mNinePatchChunk = null;
}
mRecycled = true;
@@ -476,12 +463,22 @@ public final class Bitmap implements Parcelable {
* This configuration is very flexible and offers the best
* quality. It should be used whenever possible.
*/
- ARGB_8888 (5);
+ ARGB_8888 (5),
+
+
+ /**
+ * Special configuration, when bitmap is stored only in graphic memory.
+ * Bitmaps in this configuration are always immutable.
+ *
+ * It is optimal for cases, when the only operation with the bitmap is to draw it on a
+ * screen.
+ */
+ HARDWARE (6);
final int nativeInt;
private static Config sConfigs[] = {
- null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
+ null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, HARDWARE
};
Config(int ni) {
@@ -817,7 +814,8 @@ public final class Bitmap implements Parcelable {
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
public static Bitmap createBitmap(int width, int height, Config config) {
return createBitmap(width, height, config, true);
@@ -832,7 +830,8 @@ public final class Bitmap implements Parcelable {
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
public static Bitmap createBitmap(DisplayMetrics display, int width,
int height, Config config) {
@@ -850,7 +849,8 @@ public final class Bitmap implements Parcelable {
* bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
*
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
return createBitmap(null, width, height, config, hasAlpha);
@@ -869,13 +869,17 @@ public final class Bitmap implements Parcelable {
* bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
*
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
private static Bitmap createBitmap(DisplayMetrics display, int width, int height,
Config config, boolean hasAlpha) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("width and height must be > 0");
}
+ if (config == Config.HARDWARE) {
+ throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
+ }
Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
if (display != null) {
bm.mDensity = display.densityDpi;
@@ -1254,6 +1258,11 @@ public final class Bitmap implements Parcelable {
* #getAllocationByteCount()}.</p>
*/
public final int getByteCount() {
+ if (mRecycled) {
+ Log.w(TAG, "Called getByteCount() on a recycle()'d bitmap! "
+ + "This is undefined behavior!");
+ return 0;
+ }
// int result permits bitmaps up to 46,340 x 46,340
return getRowBytes() * getHeight();
}
@@ -1273,12 +1282,12 @@ public final class Bitmap implements Parcelable {
* @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();
+ if (mRecycled) {
+ Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! "
+ + "This is undefined behavior!");
+ return 0;
}
- return mBuffer.length;
+ return nativeGetAllocationByteCount(mNativePtr);
}
/**
@@ -1673,16 +1682,6 @@ public final class Bitmap implements Parcelable {
nativePrepareToDraw(mNativePtr);
}
- /**
- * Refs the underlying SkPixelRef and returns a pointer to it.
- *
- * @hide
- * */
- public final long refSkPixelRef() {
- checkRecycled("Can't refSkPixelRef on a recycled bitmap!");
- return nativeRefPixelRef(mNativePtr);
- }
-
//////////// native methods
private static native Bitmap nativeCreate(int[] colors, int offset,
@@ -1695,8 +1694,7 @@ public final class Bitmap implements Parcelable {
private static native long nativeGetNativeFinalizer();
private static native boolean nativeRecycle(long nativeBitmap);
private static native void nativeReconfigure(long nativeBitmap, int width, int height,
- int config, int allocSize,
- boolean isPremultiplied);
+ int config, boolean isPremultiplied);
private static native boolean nativeCompress(long nativeBitmap, int format,
int quality, OutputStream stream,
@@ -1740,6 +1738,6 @@ public final class Bitmap implements Parcelable {
private static native boolean nativeHasMipMap(long nativeBitmap);
private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
- private static native long nativeRefPixelRef(long nativeBitmap);
private static native void nativePrepareToDraw(long nativeBitmap);
+ private static native int nativeGetAllocationByteCount(long nativeBitmap);
}
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 447a4c4e8345..a5517f091d3c 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -16,6 +16,8 @@
package android.graphics;
+import static android.graphics.BitmapFactory.Options.validate;
+
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Trace;
@@ -103,6 +105,9 @@ public class BitmapFactory {
* If set, decode methods will always return a mutable Bitmap instead of
* an immutable one. This can be used for instance to programmatically apply
* effects to a Bitmap loaded through BitmapFactory.
+ * <p>Can not be set simultaneously with inPreferredConfig =
+ * {@link android.graphics.Bitmap.Config#HARDWARE},
+ * because hardware bitmaps are always immutable.
*/
@SuppressWarnings({"UnusedDeclaration"}) // used in native code
public boolean inMutable;
@@ -381,6 +386,12 @@ public class BitmapFactory {
public void requestCancelDecode() {
mCancel = true;
}
+
+ static void validate(Options opts) {
+ if (opts != null && opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
+ throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
+ }
+ }
}
/**
@@ -393,8 +404,12 @@ 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)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeFile(String pathName, Options opts) {
+ validate(opts);
Bitmap bm = null;
InputStream stream = null;
try {
@@ -431,10 +446,13 @@ public class BitmapFactory {
/**
* Decode a new Bitmap from an InputStream. This InputStream was obtained from
* resources, which we pass to be able to scale the bitmap accordingly.
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
-
+ validate(opts);
if (opts == null) {
opts = new Options();
}
@@ -466,8 +484,12 @@ 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)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeResource(Resources res, int id, Options opts) {
+ validate(opts);
Bitmap bm = null;
InputStream is = null;
@@ -520,11 +542,15 @@ 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)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
if ((offset | length) < 0 || data.length < offset + length) {
throw new ArrayIndexOutOfBoundsException();
}
+ validate(opts);
Bitmap bm;
@@ -598,6 +624,9 @@ 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)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*
* <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
* if {@link InputStream#markSupported is.markSupported()} returns true,
@@ -610,6 +639,7 @@ public class BitmapFactory {
if (is == null) {
return null;
}
+ validate(opts);
Bitmap bm = null;
@@ -673,8 +703,12 @@ public class BitmapFactory {
* @param opts null-ok; Options that control downsampling and whether the
* image should be completely decoded, or just its size returned.
* @return the decoded bitmap, or null
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+ validate(opts);
Bitmap bm;
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index e689b083ad97..04abca1f4bd4 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -178,8 +178,12 @@ public final class BitmapRegionDecoder {
* inPurgeable is not supported.
* @return The decoded bitmap, or null if the image data could not be
* decoded.
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+ BitmapFactory.Options.validate(options);
synchronized (mNativeLock) {
checkRecycled("decodeRegion called on recycled region decoder");
if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 40b877de131c..b0934586b27c 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -26,13 +26,15 @@ import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.microedition.khronos.opengles.GL;
-import libcore.util.NativeAllocationRegistry;
-
/**
* The Canvas class holds the "draw" calls. To draw something, you need
* 4 basic components: A Bitmap to hold the pixels, a Canvas to host
@@ -46,17 +48,10 @@ import libcore.util.NativeAllocationRegistry;
* <a href="{@docRoot}guide/topics/graphics/2d-graphics.html">
* Canvas and Drawables</a> developer guide.</p></div>
*/
-public class Canvas {
+public class Canvas extends BaseCanvas {
/** @hide */
public static boolean sCompatibilityRestore = false;
- /**
- * Should only be assigned in constructors (or setBitmap if software canvas),
- * freed by NativeAllocation.
- * @hide
- */
- protected long mNativeCanvasWrapper;
-
/** @hide */
public long getNativeCanvasWrapper() {
return mNativeCanvasWrapper;
@@ -71,18 +66,6 @@ public class Canvas {
// optional field set by the caller
private DrawFilter mDrawFilter;
- /**
- * @hide
- */
- protected int mDensity = Bitmap.DENSITY_NONE;
-
- /**
- * Used to determine when compatibility scaling is in effect.
- *
- * @hide
- */
- protected int mScreenDensity = Bitmap.DENSITY_NONE;
-
// Maximum bitmap size as defined in Skia's native code
// (see SkCanvas.cpp, SkDraw.cpp)
private static final int MAXMIMUM_BITMAP_SIZE = 32766;
@@ -94,7 +77,7 @@ public class Canvas {
// Use a Holder to allow static initialization of Canvas in the boot image.
private static class NoImagePreloadHolder {
public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- Canvas.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+ Canvas.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
}
// This field is used to finalize the native Canvas properly
@@ -109,7 +92,7 @@ public class Canvas {
public Canvas() {
if (!isHardwareAccelerated()) {
// 0 means no native bitmap
- mNativeCanvasWrapper = initRaster(null);
+ mNativeCanvasWrapper = nInitRaster(null);
mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
this, mNativeCanvasWrapper);
} else {
@@ -131,7 +114,7 @@ public class Canvas {
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
}
throwIfCannotDraw(bitmap);
- mNativeCanvasWrapper = initRaster(bitmap);
+ mNativeCanvasWrapper = nInitRaster(bitmap);
mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
this, mNativeCanvasWrapper);
mBitmap = bitmap;
@@ -190,7 +173,7 @@ public class Canvas {
}
if (bitmap == null) {
- native_setBitmap(mNativeCanvasWrapper, null);
+ nSetBitmap(mNativeCanvasWrapper, null);
mDensity = Bitmap.DENSITY_NONE;
} else {
if (!bitmap.isMutable()) {
@@ -198,7 +181,7 @@ public class Canvas {
}
throwIfCannotDraw(bitmap);
- native_setBitmap(mNativeCanvasWrapper, bitmap);
+ nSetBitmap(mNativeCanvasWrapper, bitmap);
mDensity = bitmap.mDensity;
}
@@ -207,7 +190,7 @@ public class Canvas {
/** @hide */
public void setHighContrastText(boolean highContrastText) {
- native_setHighContrastText(mNativeCanvasWrapper, highContrastText);
+ nSetHighContrastText(mNativeCanvasWrapper, highContrastText);
}
/** @hide */
@@ -223,7 +206,7 @@ public class Canvas {
* @return true if the device that the current layer draws into is opaque
*/
public boolean isOpaque() {
- return native_isOpaque(mNativeCanvasWrapper);
+ return nIsOpaque(mNativeCanvasWrapper);
}
/**
@@ -232,7 +215,7 @@ public class Canvas {
* @return the width of the current drawing layer
*/
public int getWidth() {
- return native_getWidth(mNativeCanvasWrapper);
+ return nGetWidth(mNativeCanvasWrapper);
}
/**
@@ -241,7 +224,7 @@ public class Canvas {
* @return the height of the current drawing layer
*/
public int getHeight() {
- return native_getHeight(mNativeCanvasWrapper);
+ return nGetHeight(mNativeCanvasWrapper);
}
/**
@@ -369,7 +352,7 @@ public class Canvas {
* @return The value to pass to restoreToCount() to balance this save()
*/
public int save() {
- return native_save(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
+ return nSave(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
}
/**
@@ -389,7 +372,7 @@ public class Canvas {
* @return The value to pass to restoreToCount() to balance this save()
*/
public int save(@Saveflags int saveFlags) {
- return native_save(mNativeCanvasWrapper, saveFlags);
+ return nSave(mNativeCanvasWrapper, saveFlags);
}
/**
@@ -441,7 +424,7 @@ public class Canvas {
*/
public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint,
@Saveflags int saveFlags) {
- return native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom,
+ return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom,
paint != null ? paint.getNativeInstance() : 0,
saveFlags);
}
@@ -501,7 +484,7 @@ public class Canvas {
public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
@Saveflags int saveFlags) {
alpha = Math.min(255, Math.max(0, alpha));
- return native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom,
+ return nSaveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom,
alpha, saveFlags);
}
@@ -519,7 +502,7 @@ public class Canvas {
*/
public void restore() {
boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
- native_restore(mNativeCanvasWrapper, throwOnUnderflow);
+ nRestore(mNativeCanvasWrapper, throwOnUnderflow);
}
/**
@@ -527,7 +510,7 @@ public class Canvas {
* This will equal # save() calls - # restore() calls.
*/
public int getSaveCount() {
- return native_getSaveCount(mNativeCanvasWrapper);
+ return nGetSaveCount(mNativeCanvasWrapper);
}
/**
@@ -545,7 +528,7 @@ public class Canvas {
*/
public void restoreToCount(int saveCount) {
boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
- native_restoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow);
+ nRestoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow);
}
/**
@@ -555,7 +538,8 @@ public class Canvas {
* @param dy The distance to translate in Y
*/
public void translate(float dx, float dy) {
- native_translate(mNativeCanvasWrapper, dx, dy);
+ if (dx == 0.0f && dy == 0.0f) return;
+ nTranslate(mNativeCanvasWrapper, dx, dy);
}
/**
@@ -565,7 +549,8 @@ public class Canvas {
* @param sy The amount to scale in Y
*/
public void scale(float sx, float sy) {
- native_scale(mNativeCanvasWrapper, sx, sy);
+ if (sx == 1.0f && sy == 1.0f) return;
+ nScale(mNativeCanvasWrapper, sx, sy);
}
/**
@@ -577,6 +562,7 @@ public class Canvas {
* @param py The y-coord for the pivot point (unchanged by the scale)
*/
public final void scale(float sx, float sy, float px, float py) {
+ if (sx == 1.0f && sy == 1.0f) return;
translate(px, py);
scale(sx, sy);
translate(-px, -py);
@@ -588,7 +574,8 @@ public class Canvas {
* @param degrees The amount to rotate, in degrees
*/
public void rotate(float degrees) {
- native_rotate(mNativeCanvasWrapper, degrees);
+ if (degrees == 0.0f) return;
+ nRotate(mNativeCanvasWrapper, degrees);
}
/**
@@ -599,6 +586,7 @@ public class Canvas {
* @param py The y-coord for the pivot point (unchanged by the rotation)
*/
public final void rotate(float degrees, float px, float py) {
+ if (degrees == 0.0f) return;
translate(px, py);
rotate(degrees);
translate(-px, -py);
@@ -611,7 +599,8 @@ public class Canvas {
* @param sy The amount to skew in Y
*/
public void skew(float sx, float sy) {
- native_skew(mNativeCanvasWrapper, sx, sy);
+ if (sx == 0.0f && sy == 0.0f) return;
+ nSkew(mNativeCanvasWrapper, sx, sy);
}
/**
@@ -621,7 +610,7 @@ public class Canvas {
* @param matrix The matrix to preconcatenate with the current matrix
*/
public void concat(@Nullable Matrix matrix) {
- if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance);
+ if (matrix != null) nConcat(mNativeCanvasWrapper, matrix.native_instance);
}
/**
@@ -638,7 +627,7 @@ public class Canvas {
* @see #concat(Matrix)
*/
public void setMatrix(@Nullable Matrix matrix) {
- native_setMatrix(mNativeCanvasWrapper,
+ nSetMatrix(mNativeCanvasWrapper,
matrix == null ? 0 : matrix.native_instance);
}
@@ -654,7 +643,7 @@ public class Canvas {
*/
@Deprecated
public void getMatrix(@NonNull Matrix ctm) {
- native_getCTM(mNativeCanvasWrapper, ctm.native_instance);
+ nGetCTM(mNativeCanvasWrapper, ctm.native_instance);
}
/**
@@ -683,7 +672,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) {
- return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
+ return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
op.nativeInt);
}
@@ -696,7 +685,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(@NonNull Rect rect, @NonNull Region.Op op) {
- return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
+ return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
op.nativeInt);
}
@@ -708,7 +697,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(@NonNull RectF rect) {
- return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
+ return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
Region.Op.INTERSECT.nativeInt);
}
@@ -720,7 +709,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(@NonNull Rect rect) {
- return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
+ return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
Region.Op.INTERSECT.nativeInt);
}
@@ -741,7 +730,7 @@ public class Canvas {
*/
public boolean clipRect(float left, float top, float right, float bottom,
@NonNull Region.Op op) {
- return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
+ return nClipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
}
/**
@@ -758,7 +747,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(float left, float top, float right, float bottom) {
- return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom,
+ return nClipRect(mNativeCanvasWrapper, left, top, right, bottom,
Region.Op.INTERSECT.nativeInt);
}
@@ -776,7 +765,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(int left, int top, int right, int bottom) {
- return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom,
+ return nClipRect(mNativeCanvasWrapper, left, top, right, bottom,
Region.Op.INTERSECT.nativeInt);
}
@@ -788,7 +777,7 @@ public class Canvas {
* @return true if the resulting is non-empty
*/
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
- return native_clipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
+ return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
}
/**
@@ -817,7 +806,7 @@ public class Canvas {
*/
@Deprecated
public boolean clipRegion(@NonNull Region region, @NonNull Region.Op op) {
- return native_clipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt);
+ return nClipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt);
}
/**
@@ -848,9 +837,18 @@ public class Canvas {
nativeFilter = filter.mNativeInt;
}
mDrawFilter = filter;
- nativeSetDrawFilter(mNativeCanvasWrapper, nativeFilter);
+ nSetDrawFilter(mNativeCanvasWrapper, nativeFilter);
}
+ /**
+ * Constant values used as parameters to {@code quickReject()} calls. These values
+ * specify how much space around the shape should be accounted for, depending on whether
+ * the shaped area is antialiased or not.
+ *
+ * @see #quickReject(float, float, float, float, EdgeType)
+ * @see #quickReject(Path, EdgeType)
+ * @see #quickReject(RectF, EdgeType)
+ */
public enum EdgeType {
/**
@@ -887,7 +885,7 @@ public class Canvas {
* does not intersect with the canvas' clip
*/
public boolean quickReject(@NonNull RectF rect, @NonNull EdgeType type) {
- return native_quickReject(mNativeCanvasWrapper,
+ return nQuickReject(mNativeCanvasWrapper,
rect.left, rect.top, rect.right, rect.bottom);
}
@@ -907,7 +905,7 @@ public class Canvas {
* does not intersect with the canvas' clip
*/
public boolean quickReject(@NonNull Path path, @NonNull EdgeType type) {
- return native_quickReject(mNativeCanvasWrapper, path.readOnlyNI());
+ return nQuickReject(mNativeCanvasWrapper, path.readOnlyNI());
}
/**
@@ -932,7 +930,7 @@ public class Canvas {
*/
public boolean quickReject(float left, float top, float right, float bottom,
@NonNull EdgeType type) {
- return native_quickReject(mNativeCanvasWrapper, left, top, right, bottom);
+ return nQuickReject(mNativeCanvasWrapper, left, top, right, bottom);
}
/**
@@ -946,7 +944,7 @@ public class Canvas {
* @return true if the current clip is non-empty.
*/
public boolean getClipBounds(@Nullable Rect bounds) {
- return native_getClipBounds(mNativeCanvasWrapper, bounds);
+ return nGetClipBounds(mNativeCanvasWrapper, bounds);
}
/**
@@ -961,320 +959,504 @@ public class Canvas {
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified RGB color, using srcover porterduff mode.
+ * Save the canvas state, draw the picture, and restore the canvas state.
+ * This differs from picture.draw(canvas), which does not perform any
+ * save/restore.
*
- * @param r red component (0..255) of the color to draw onto the canvas
- * @param g green component (0..255) of the color to draw onto the canvas
- * @param b blue component (0..255) of the color to draw onto the canvas
+ * <p>
+ * <strong>Note:</strong> This forces the picture to internally call
+ * {@link Picture#endRecording} in order to prepare for playback.
+ *
+ * @param picture The picture to be drawn
*/
- public void drawRGB(int r, int g, int b) {
- drawColor(Color.rgb(r, g, b));
+ public void drawPicture(@NonNull Picture picture) {
+ picture.endRecording();
+ int restoreCount = save();
+ picture.draw(this);
+ restoreToCount(restoreCount);
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified ARGB color, using srcover porterduff mode.
- *
- * @param a alpha component (0..255) of the color to draw onto the canvas
- * @param r red component (0..255) of the color to draw onto the canvas
- * @param g green component (0..255) of the color to draw onto the canvas
- * @param b blue component (0..255) of the color to draw onto the canvas
+ * Draw the picture, stretched to fit into the dst rectangle.
*/
- public void drawARGB(int a, int r, int g, int b) {
- drawColor(Color.argb(a, r, g, b));
+ public void drawPicture(@NonNull Picture picture, @NonNull RectF dst) {
+ save();
+ translate(dst.left, dst.top);
+ if (picture.getWidth() > 0 && picture.getHeight() > 0) {
+ scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
+ }
+ drawPicture(picture);
+ restore();
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified color, using srcover porterduff mode.
- *
- * @param color the color to draw onto the canvas
+ * Draw the picture, stretched to fit into the dst rectangle.
*/
- public void drawColor(@ColorInt int color) {
- native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
+ public void drawPicture(@NonNull Picture picture, @NonNull Rect dst) {
+ save();
+ translate(dst.left, dst.top);
+ if (picture.getWidth() > 0 && picture.getHeight() > 0) {
+ scale((float) dst.width() / picture.getWidth(),
+ (float) dst.height() / picture.getHeight());
+ }
+ drawPicture(picture);
+ restore();
+ }
+
+ public enum VertexMode {
+ TRIANGLES(0),
+ TRIANGLE_STRIP(1),
+ TRIANGLE_FAN(2);
+
+ VertexMode(int nativeInt) {
+ this.nativeInt = nativeInt;
+ }
+
+ /**
+ * @hide
+ */
+ public final int nativeInt;
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified color and porter-duff xfermode.
+ * Releases the resources associated with this canvas.
*
- * @param color the color to draw with
- * @param mode the porter-duff mode to apply to the color
+ * @hide
*/
- public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
- native_drawColor(mNativeCanvasWrapper, color, mode.nativeInt);
+ public void release() {
+ mNativeCanvasWrapper = 0;
+ if (mFinalizer != null) {
+ mFinalizer.run();
+ mFinalizer = null;
+ }
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with
- * the specified paint. This is equivalent (but faster) to drawing an
- * infinitely large rectangle with the specified paint.
+ * Free up as much memory as possible from private caches (e.g. fonts, images)
*
- * @param paint The paint used to draw onto the canvas
+ * @hide
*/
- public void drawPaint(@NonNull Paint paint) {
- native_drawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
+ public static void freeCaches() {
+ nFreeCaches();
}
/**
- * Draw a series of points. Each point is centered at the coordinate
- * specified by pts[], and its diameter is specified by the paint's stroke
- * width (as transformed by the canvas' CTM), with special treatment for
- * a stroke width of 0, which always draws exactly 1 pixel (or at most 4
- * if antialiasing is enabled). The shape of the point is controlled by
- * the paint's Cap type. The shape is a square, unless the cap type is
- * Round, in which case the shape is a circle.
+ * Free up text layout caches
*
- * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
- * @param offset Number of values to skip before starting to draw.
- * @param count The number of values to process, after skipping offset
- * of them. Since one point uses two values, the number of
- * "points" that are drawn is really (count >> 1).
- * @param paint The paint used to draw the points
+ * @hide
*/
- public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count,
- @NonNull Paint paint) {
- native_drawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
+ public static void freeTextLayoutCaches() {
+ nFreeTextLayoutCaches();
}
+ private static native void nFreeCaches();
+ private static native void nFreeTextLayoutCaches();
+ private static native long nInitRaster(Bitmap bitmap);
+ private static native long nGetNativeFinalizer();
+
+ // ---------------- @FastNative -------------------
+
+ @FastNative
+ private static native void nSetBitmap(long canvasHandle,
+ Bitmap bitmap);
+ @FastNative
+ private static native boolean nIsOpaque(long canvasHandle);
+ @FastNative
+ private static native void nSetHighContrastText(long renderer, boolean highContrastText);
+ @FastNative
+ private static native int nGetWidth(long canvasHandle);
+ @FastNative
+ private static native int nGetHeight(long canvasHandle);
+
+ @FastNative
+ private static native int nSave(long canvasHandle, int saveFlags);
+ @FastNative
+ private static native int nSaveLayer(long nativeCanvas, float l,
+ float t, float r, float b,
+ long nativePaint,
+ int layerFlags);
+ @FastNative
+ private static native int nSaveLayerAlpha(long nativeCanvas, float l,
+ float t, float r, float b,
+ int alpha, int layerFlags);
+ @FastNative
+ private static native void nRestore(long canvasHandle, boolean tolerateUnderflow);
+ @FastNative
+ private static native void nRestoreToCount(long canvasHandle,
+ int saveCount,
+ boolean tolerateUnderflow);
+ @FastNative
+ private static native int nGetSaveCount(long canvasHandle);
+
+ @FastNative
+ private static native void nTranslate(long canvasHandle,
+ float dx, float dy);
+ @FastNative
+ private static native void nScale(long canvasHandle,
+ float sx, float sy);
+ @FastNative
+ private static native void nRotate(long canvasHandle, float degrees);
+ @FastNative
+ private static native void nSkew(long canvasHandle,
+ float sx, float sy);
+ @FastNative
+ private static native void nConcat(long nativeCanvas,
+ long nativeMatrix);
+ @FastNative
+ private static native void nSetMatrix(long nativeCanvas,
+ long nativeMatrix);
+ @FastNative
+ private static native boolean nClipRect(long nativeCanvas,
+ float left, float top,
+ float right, float bottom,
+ int regionOp);
+ @FastNative
+ private static native boolean nClipPath(long nativeCanvas,
+ long nativePath,
+ int regionOp);
+ @FastNative
+ private static native boolean nClipRegion(long nativeCanvas,
+ long nativeRegion,
+ int regionOp);
+ @FastNative
+ private static native void nSetDrawFilter(long nativeCanvas,
+ long nativeFilter);
+ @FastNative
+ private static native boolean nGetClipBounds(long nativeCanvas,
+ Rect bounds);
+ @FastNative
+ private static native void nGetCTM(long nativeCanvas,
+ long nativeMatrix);
+ @FastNative
+ private static native boolean nQuickReject(long nativeCanvas,
+ long nativePath);
+ @FastNative
+ private static native boolean nQuickReject(long nativeCanvas,
+ float left, float top,
+ float right, float bottom);
+
/**
- * Helper for drawPoints() that assumes you want to draw the entire array
+ * <p>
+ * Draw the specified arc, which will be scaled to fit inside the specified oval.
+ * </p>
+ * <p>
+ * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
+ * 360.
+ * </p>
+ * <p>
+ * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
+ * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
+ * negative, the sweep angle is treated as sweep angle modulo 360
+ * </p>
+ * <p>
+ * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
+ * degrees (3 o'clock on a watch.)
+ * </p>
+ *
+ * @param oval The bounds of oval used to define the shape and size of the arc
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ * @param useCenter If true, include the center of the oval in the arc, and close it if it is
+ * being stroked. This will draw a wedge
+ * @param paint The paint used to draw the arc
*/
- public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint) {
- drawPoints(pts, 0, pts.length, paint);
+ public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
+ @NonNull Paint paint) {
+ super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
}
/**
- * Helper for drawPoints() for drawing a single point.
+ * <p>
+ * Draw the specified arc, which will be scaled to fit inside the specified oval.
+ * </p>
+ * <p>
+ * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
+ * 360.
+ * </p>
+ * <p>
+ * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
+ * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
+ * negative, the sweep angle is treated as sweep angle modulo 360
+ * </p>
+ * <p>
+ * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
+ * degrees (3 o'clock on a watch.)
+ * </p>
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ * @param useCenter If true, include the center of the oval in the arc, and close it if it is
+ * being stroked. This will draw a wedge
+ * @param paint The paint used to draw the arc
*/
- public void drawPoint(float x, float y, @NonNull Paint paint) {
- native_drawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
+ public void drawArc(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle, boolean useCenter, @NonNull Paint paint) {
+ super.drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
}
/**
- * Draw a line segment with the specified start and stop x,y coordinates,
- * using the specified paint.
- *
- * <p>Note that since a line is always "framed", the Style is ignored in the paint.</p>
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified ARGB
+ * color, using srcover porterduff mode.
*
- * <p>Degenerate lines (length is 0) will not be drawn.</p>
- *
- * @param startX The x-coordinate of the start point of the line
- * @param startY The y-coordinate of the start point of the line
- * @param paint The paint used to draw the line
+ * @param a alpha component (0..255) of the color to draw onto the canvas
+ * @param r red component (0..255) of the color to draw onto the canvas
+ * @param g green component (0..255) of the color to draw onto the canvas
+ * @param b blue component (0..255) of the color to draw onto the canvas
*/
- public void drawLine(float startX, float startY, float stopX, float stopY,
- @NonNull Paint paint) {
- native_drawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
+ public void drawARGB(int a, int r, int g, int b) {
+ super.drawARGB(a, r, g, b);
}
/**
- * Draw a series of lines. Each line is taken from 4 consecutive values
- * in the pts array. Thus to draw 1 line, the array must contain at least 4
- * values. This is logically the same as drawing the array as follows:
- * drawLine(pts[0], pts[1], pts[2], pts[3]) followed by
- * drawLine(pts[4], pts[5], pts[6], pts[7]) and so on.
+ * Draw the specified bitmap, with its top/left corner at (x,y), using the specified paint,
+ * transformed by the current matrix.
+ * <p>
+ * Note: if the paint contains a maskfilter that generates a mask which extends beyond the
+ * bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it
+ * were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be
+ * the edge color replicated.
+ * <p>
+ * If the bitmap and canvas have different densities, this function will take care of
+ * automatically scaling the bitmap to draw at the same density as the canvas.
*
- * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
- * @param offset Number of values in the array to skip before drawing.
- * @param count The number of values in the array to process, after
- * skipping "offset" of them. Since each line uses 4 values,
- * the number of "lines" that are drawn is really
- * (count >> 2).
- * @param paint The paint used to draw the points
+ * @param bitmap The bitmap to be drawn
+ * @param left The position of the left side of the bitmap being drawn
+ * @param top The position of the top side of the bitmap being drawn
+ * @param paint The paint used to draw the bitmap (may be null)
*/
- public void drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count,
- @NonNull Paint paint) {
- native_drawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
+ public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
+ super.drawBitmap(bitmap, left, top, paint);
}
- public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint) {
- drawLines(pts, 0, pts.length, paint);
+ /**
+ * Draw the specified bitmap, scaling/translating automatically to fill the destination
+ * rectangle. If the source rectangle is not null, it specifies the subset of the bitmap to
+ * draw.
+ * <p>
+ * Note: if the paint contains a maskfilter that generates a mask which extends beyond the
+ * bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it
+ * were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be
+ * the edge color replicated.
+ * <p>
+ * This function <em>ignores the density associated with the bitmap</em>. This is because the
+ * source and destination rectangle coordinate spaces are in their respective densities, so must
+ * already have the appropriate scaling factor applied.
+ *
+ * @param bitmap The bitmap to be drawn
+ * @param src May be null. The subset of the bitmap to be drawn
+ * @param dst The rectangle that the bitmap will be scaled/translated to fit into
+ * @param paint May be null. The paint used to draw the bitmap
+ */
+ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
+ @Nullable Paint paint) {
+ super.drawBitmap(bitmap, src, dst, paint);
}
/**
- * Draw the specified Rect using the specified paint. The rectangle will
- * be filled or framed based on the Style in the paint.
+ * Draw the specified bitmap, scaling/translating automatically to fill the destination
+ * rectangle. If the source rectangle is not null, it specifies the subset of the bitmap to
+ * draw.
+ * <p>
+ * Note: if the paint contains a maskfilter that generates a mask which extends beyond the
+ * bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it
+ * were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be
+ * the edge color replicated.
+ * <p>
+ * This function <em>ignores the density associated with the bitmap</em>. This is because the
+ * source and destination rectangle coordinate spaces are in their respective densities, so must
+ * already have the appropriate scaling factor applied.
*
- * @param rect The rect to be drawn
- * @param paint The paint used to draw the rect
+ * @param bitmap The bitmap to be drawn
+ * @param src May be null. The subset of the bitmap to be drawn
+ * @param dst The rectangle that the bitmap will be scaled/translated to fit into
+ * @param paint May be null. The paint used to draw the bitmap
*/
- public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
- native_drawRect(mNativeCanvasWrapper,
- rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
+ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
+ @Nullable Paint paint) {
+ super.drawBitmap(bitmap, src, dst, paint);
}
/**
- * Draw the specified Rect using the specified Paint. The rectangle
- * will be filled or framed based on the Style in the paint.
+ * Treat the specified array of colors as a bitmap, and draw it. This gives the same result as
+ * first creating a bitmap from the array, and then drawing it, but this method avoids
+ * explicitly creating a bitmap object which can be more efficient if the colors are changing
+ * often.
*
- * @param r The rectangle to be drawn.
- * @param paint The paint used to draw the rectangle
+ * @param colors Array of colors representing the pixels of the bitmap
+ * @param offset Offset into the array of colors for the first pixel
+ * @param stride The number of colors in the array between rows (must be >= width or <= -width).
+ * @param x The X coordinate for where to draw the bitmap
+ * @param y The Y coordinate for where to draw the bitmap
+ * @param width The width of the bitmap
+ * @param height The height of the bitmap
+ * @param hasAlpha True if the alpha channel of the colors contains valid values. If false, the
+ * alpha byte is ignored (assumed to be 0xFF for every pixel).
+ * @param paint May be null. The paint used to draw the bitmap
+ * @deprecated Usage with a {@link #isHardwareAccelerated() hardware accelerated} canvas
+ * requires an internal copy of color buffer contents every time this method is
+ * called. Using a Bitmap avoids this copy, and allows the application to more
+ * explicitly control the lifetime and copies of pixel data.
*/
- public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
- drawRect(r.left, r.top, r.right, r.bottom, paint);
+ @Deprecated
+ public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y,
+ int width, int height, boolean hasAlpha, @Nullable Paint paint) {
+ super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
}
-
/**
- * Draw the specified Rect using the specified paint. The rectangle will
- * be filled or framed based on the Style in the paint.
+ * Legacy version of drawBitmap(int[] colors, ...) that took ints for x,y
*
- * @param left The left side of the rectangle to be drawn
- * @param top The top side of the rectangle to be drawn
- * @param right The right side of the rectangle to be drawn
- * @param bottom The bottom side of the rectangle to be drawn
- * @param paint The paint used to draw the rect
+ * @deprecated Usage with a {@link #isHardwareAccelerated() hardware accelerated} canvas
+ * requires an internal copy of color buffer contents every time this method is
+ * called. Using a Bitmap avoids this copy, and allows the application to more
+ * explicitly control the lifetime and copies of pixel data.
*/
- public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
- native_drawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
+ @Deprecated
+ public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y,
+ int width, int height, boolean hasAlpha, @Nullable Paint paint) {
+ super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
}
/**
- * Draw the specified oval using the specified paint. The oval will be
- * filled or framed based on the Style in the paint.
+ * Draw the bitmap using the specified matrix.
*
- * @param oval The rectangle bounds of the oval to be drawn
+ * @param bitmap The bitmap to draw
+ * @param matrix The matrix used to transform the bitmap when it is drawn
+ * @param paint May be null. The paint used to draw the bitmap
*/
- public void drawOval(@NonNull RectF oval, @NonNull Paint paint) {
- if (oval == null) {
- throw new NullPointerException();
- }
- drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
+ public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
+ super.drawBitmap(bitmap, matrix, paint);
}
/**
- * Draw the specified oval using the specified paint. The oval will be
- * filled or framed based on the Style in the paint.
+ * Draw the bitmap through the mesh, where mesh vertices are evenly distributed across the
+ * bitmap. There are meshWidth+1 vertices across, and meshHeight+1 vertices down. The verts
+ * array is accessed in row-major order, so that the first meshWidth+1 vertices are distributed
+ * across the top of the bitmap from left to right. A more general version of this method is
+ * drawVertices().
+ *
+ * @param bitmap The bitmap to draw using the mesh
+ * @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
+ * @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
+ * @param verts Array of x,y pairs, specifying where the mesh should be drawn. There must be at
+ * least (meshWidth+1) * (meshHeight+1) * 2 + vertOffset values in the array
+ * @param vertOffset Number of verts elements to skip before drawing
+ * @param colors May be null. Specifies a color at each vertex, which is interpolated across the
+ * cell, and whose values are multiplied by the corresponding bitmap colors. If not
+ * null, there must be at least (meshWidth+1) * (meshHeight+1) + colorOffset values
+ * in the array.
+ * @param colorOffset Number of color elements to skip before drawing
+ * @param paint May be null. The paint used to draw the bitmap
*/
- public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
- native_drawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
+ public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
+ @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
+ @Nullable Paint paint) {
+ super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset,
+ paint);
}
/**
- * Draw the specified circle using the specified paint. If radius is <= 0,
- * then nothing will be drawn. The circle will be filled or framed based
- * on the Style in the paint.
+ * Draw the specified circle using the specified paint. If radius is <= 0, then nothing will be
+ * drawn. The circle will be filled or framed based on the Style in the paint.
*
- * @param cx The x-coordinate of the center of the cirle to be drawn
- * @param cy The y-coordinate of the center of the cirle to be drawn
+ * @param cx The x-coordinate of the center of the cirle to be drawn
+ * @param cy The y-coordinate of the center of the cirle to be drawn
* @param radius The radius of the cirle to be drawn
- * @param paint The paint used to draw the circle
+ * @param paint The paint used to draw the circle
*/
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
- native_drawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
+ super.drawCircle(cx, cy, radius, paint);
}
/**
- * <p>Draw the specified arc, which will be scaled to fit inside the
- * specified oval.</p>
- *
- * <p>If the start angle is negative or >= 360, the start angle is treated
- * as start angle modulo 360.</p>
- *
- * <p>If the sweep angle is >= 360, then the oval is drawn
- * completely. Note that this differs slightly from SkPath::arcTo, which
- * treats the sweep angle modulo 360. If the sweep angle is negative,
- * the sweep angle is treated as sweep angle modulo 360</p>
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color,
+ * using srcover porterduff mode.
*
- * <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
- * geometric angle of 0 degrees (3 o'clock on a watch.)</p>
- *
- * @param oval The bounds of oval used to define the shape and size
- * of the arc
- * @param startAngle Starting angle (in degrees) where the arc begins
- * @param sweepAngle Sweep angle (in degrees) measured clockwise
- * @param useCenter If true, include the center of the oval in the arc, and
- close it if it is being stroked. This will draw a wedge
- * @param paint The paint used to draw the arc
+ * @param color the color to draw onto the canvas
*/
- public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
- @NonNull Paint paint) {
- drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
- paint);
+ public void drawColor(@ColorInt int color) {
+ super.drawColor(color);
}
/**
- * <p>Draw the specified arc, which will be scaled to fit inside the
- * specified oval.</p>
- *
- * <p>If the start angle is negative or >= 360, the start angle is treated
- * as start angle modulo 360.</p>
- *
- * <p>If the sweep angle is >= 360, then the oval is drawn
- * completely. Note that this differs slightly from SkPath::arcTo, which
- * treats the sweep angle modulo 360. If the sweep angle is negative,
- * the sweep angle is treated as sweep angle modulo 360</p>
- *
- * <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
- * geometric angle of 0 degrees (3 o'clock on a watch.)</p>
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
+ * porter-duff xfermode.
*
- * @param startAngle Starting angle (in degrees) where the arc begins
- * @param sweepAngle Sweep angle (in degrees) measured clockwise
- * @param useCenter If true, include the center of the oval in the arc, and
- close it if it is being stroked. This will draw a wedge
- * @param paint The paint used to draw the arc
+ * @param color the color to draw with
+ * @param mode the porter-duff mode to apply to the color
*/
- public void drawArc(float left, float top, float right, float bottom, float startAngle,
- float sweepAngle, boolean useCenter, @NonNull Paint paint) {
- native_drawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
- useCenter, paint.getNativeInstance());
+ public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
+ super.drawColor(color, mode);
}
/**
- * Draw the specified round-rect using the specified paint. The roundrect
- * will be filled or framed based on the Style in the paint.
+ * Draw a line segment with the specified start and stop x,y coordinates, using the specified
+ * paint.
+ * <p>
+ * Note that since a line is always "framed", the Style is ignored in the paint.
+ * </p>
+ * <p>
+ * Degenerate lines (length is 0) will not be drawn.
+ * </p>
*
- * @param rect The rectangular bounds of the roundRect to be drawn
- * @param rx The x-radius of the oval used to round the corners
- * @param ry The y-radius of the oval used to round the corners
- * @param paint The paint used to draw the roundRect
+ * @param startX The x-coordinate of the start point of the line
+ * @param startY The y-coordinate of the start point of the line
+ * @param paint The paint used to draw the line
*/
- public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
- drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
+ public void drawLine(float startX, float startY, float stopX, float stopY,
+ @NonNull Paint paint) {
+ super.drawLine(startX, startY, stopX, stopY, paint);
}
/**
- * Draw the specified round-rect using the specified paint. The roundrect
- * will be filled or framed based on the Style in the paint.
+ * Draw a series of lines. Each line is taken from 4 consecutive values in the pts array. Thus
+ * to draw 1 line, the array must contain at least 4 values. This is logically the same as
+ * drawing the array as follows: drawLine(pts[0], pts[1], pts[2], pts[3]) followed by
+ * drawLine(pts[4], pts[5], pts[6], pts[7]) and so on.
*
- * @param rx The x-radius of the oval used to round the corners
- * @param ry The y-radius of the oval used to round the corners
- * @param paint The paint used to draw the roundRect
+ * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
+ * @param offset Number of values in the array to skip before drawing.
+ * @param count The number of values in the array to process, after skipping "offset" of them.
+ * Since each line uses 4 values, the number of "lines" that are drawn is really
+ * (count >> 2).
+ * @param paint The paint used to draw the points
*/
- public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
+ public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
@NonNull Paint paint) {
- native_drawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, paint.getNativeInstance());
+ super.drawLines(pts, offset, count, paint);
+ }
+
+ public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
+ super.drawLines(pts, paint);
}
/**
- * Draw the specified path using the specified paint. The path will be
- * filled or framed based on the Style in the paint.
+ * Draw the specified oval using the specified paint. The oval will be filled or framed based on
+ * the Style in the paint.
*
- * @param path The path to be drawn
- * @param paint The paint used to draw the path
+ * @param oval The rectangle bounds of the oval to be drawn
*/
- public void drawPath(@NonNull Path path, @NonNull Paint paint) {
- if (path.isSimplePath && path.rects != null) {
- native_drawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
- } else {
- native_drawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
- }
+ public void drawOval(@NonNull RectF oval, @NonNull Paint paint) {
+ super.drawOval(oval, paint);
}
/**
- * @hide
+ * Draw the specified oval using the specified paint. The oval will be filled or framed based on
+ * the Style in the paint.
*/
- protected 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);
- }
+ public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ super.drawOval(left, top, right, bottom, paint);
+ }
+
+ /**
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified paint.
+ * This is equivalent (but faster) to drawing an infinitely large rectangle with the specified
+ * paint.
+ *
+ * @param paint The paint used to draw onto the canvas
+ */
+ public void drawPaint(@NonNull Paint paint) {
+ super.drawPaint(paint);
}
/**
@@ -1283,16 +1465,10 @@ public class Canvas {
* @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(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
- Bitmap bitmap = patch.getBitmap();
- throwIfCannotDraw(bitmap);
- final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
- native_drawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint,
- mDensity, patch.getDensity());
+ super.drawPatch(patch, dst, paint);
}
/**
@@ -1301,445 +1477,270 @@ public class Canvas {
* @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(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
- Bitmap bitmap = patch.getBitmap();
- throwIfCannotDraw(bitmap);
- final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
- native_drawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint,
- mDensity, patch.getDensity());
+ super.drawPatch(patch, dst, paint);
}
/**
- * Draw the specified bitmap, with its top/left corner at (x,y), using
- * the specified paint, transformed by the current matrix.
+ * Draw the specified path using the specified paint. The path will be filled or framed based on
+ * the Style in the paint.
*
- * <p>Note: if the paint contains a maskfilter that generates a mask which
- * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
- * then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
- * Thus the color outside of the original width/height will be the edge
- * color replicated.
- *
- * <p>If the bitmap and canvas have different densities, this function
- * will take care of automatically scaling the bitmap to draw at the
- * same density as the canvas.
- *
- * @param bitmap The bitmap to be drawn
- * @param left The position of the left side of the bitmap being drawn
- * @param top The position of the top side of the bitmap being drawn
- * @param paint The paint used to draw the bitmap (may be null)
+ * @param path The path to be drawn
+ * @param paint The paint used to draw the path
*/
- public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
- throwIfCannotDraw(bitmap);
- native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top,
- paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity, bitmap.mDensity);
+ public void drawPath(@NonNull Path path, @NonNull Paint paint) {
+ super.drawPath(path, paint);
}
/**
- * Draw the specified bitmap, scaling/translating automatically to fill
- * the destination rectangle. If the source rectangle is not null, it
- * specifies the subset of the bitmap to draw.
- *
- * <p>Note: if the paint contains a maskfilter that generates a mask which
- * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
- * then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
- * Thus the color outside of the original width/height will be the edge
- * color replicated.
- *
- * <p>This function <em>ignores the density associated with the bitmap</em>.
- * This is because the source and destination rectangle coordinate
- * spaces are in their respective densities, so must already have the
- * appropriate scaling factor applied.
- *
- * @param bitmap The bitmap to be drawn
- * @param src May be null. The subset of the bitmap to be drawn
- * @param dst The rectangle that the bitmap will be scaled/translated
- * to fit into
- * @param paint May be null. The paint used to draw the bitmap
+ * Helper for drawPoints() for drawing a single point.
*/
- public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
- @Nullable Paint paint) {
- if (dst == null) {
- throw new NullPointerException();
- }
- throwIfCannotDraw(bitmap);
- final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
-
- float left, top, right, bottom;
- if (src == null) {
- left = top = 0;
- right = bitmap.getWidth();
- bottom = bitmap.getHeight();
- } else {
- left = src.left;
- right = src.right;
- top = src.top;
- bottom = src.bottom;
- }
-
- native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
- bitmap.mDensity);
- }
-
- /**
- * Draw the specified bitmap, scaling/translating automatically to fill
- * the destination rectangle. If the source rectangle is not null, it
- * specifies the subset of the bitmap to draw.
- *
- * <p>Note: if the paint contains a maskfilter that generates a mask which
- * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
- * then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
- * Thus the color outside of the original width/height will be the edge
- * color replicated.
- *
- * <p>This function <em>ignores the density associated with the bitmap</em>.
- * This is because the source and destination rectangle coordinate
- * spaces are in their respective densities, so must already have the
- * appropriate scaling factor applied.
+ public void drawPoint(float x, float y, @NonNull Paint paint) {
+ super.drawPoint(x, y, paint);
+ }
+
+ /**
+ * Draw a series of points. Each point is centered at the coordinate specified by pts[], and its
+ * diameter is specified by the paint's stroke width (as transformed by the canvas' CTM), with
+ * special treatment for a stroke width of 0, which always draws exactly 1 pixel (or at most 4
+ * if antialiasing is enabled). The shape of the point is controlled by the paint's Cap type.
+ * The shape is a square, unless the cap type is Round, in which case the shape is a circle.
*
- * @param bitmap The bitmap to be drawn
- * @param src May be null. The subset of the bitmap to be drawn
- * @param dst The rectangle that the bitmap will be scaled/translated
- * to fit into
- * @param paint May be null. The paint used to draw the bitmap
+ * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
+ * @param offset Number of values to skip before starting to draw.
+ * @param count The number of values to process, after skipping offset of them. Since one point
+ * uses two values, the number of "points" that are drawn is really (count >> 1).
+ * @param paint The paint used to draw the points
*/
- public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
- @Nullable Paint paint) {
- if (dst == null) {
- throw new NullPointerException();
- }
- throwIfCannotDraw(bitmap);
- final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
-
- int left, top, right, bottom;
- if (src == null) {
- left = top = 0;
- right = bitmap.getWidth();
- bottom = bitmap.getHeight();
- } else {
- left = src.left;
- right = src.right;
- top = src.top;
- bottom = src.bottom;
- }
+ public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
+ @NonNull Paint paint) {
+ super.drawPoints(pts, offset, count, paint);
+ }
- native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
- bitmap.mDensity);
+ /**
+ * Helper for drawPoints() that assumes you want to draw the entire array
+ */
+ public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
+ super.drawPoints(pts, paint);
}
/**
- * Treat the specified array of colors as a bitmap, and draw it. This gives
- * the same result as first creating a bitmap from the array, and then
- * drawing it, but this method avoids explicitly creating a bitmap object
- * which can be more efficient if the colors are changing often.
- *
- * @param colors Array of colors representing the pixels of the bitmap
- * @param offset Offset into the array of colors for the first pixel
- * @param stride The number of colors in the array between rows (must be
- * >= width or <= -width).
- * @param x The X coordinate for where to draw the bitmap
- * @param y The Y coordinate for where to draw the bitmap
- * @param width The width of the bitmap
- * @param height The height of the bitmap
- * @param hasAlpha True if the alpha channel of the colors contains valid
- * values. If false, the alpha byte is ignored (assumed to
- * be 0xFF for every pixel).
- * @param paint May be null. The paint used to draw the bitmap
+ * Draw the text in the array, with each character's origin specified by the pos array.
*
- * @deprecated Usage with a {@link #isHardwareAccelerated() hardware accelerated} canvas
- * requires an internal copy of color buffer contents every time this method is called. Using a
- * Bitmap avoids this copy, and allows the application to more explicitly control the lifetime
- * and copies of pixel data.
+ * @param text The text to be drawn
+ * @param index The index of the first character to draw
+ * @param count The number of characters to draw, starting from index.
+ * @param pos Array of [x,y] positions, used to position each character
+ * @param paint The paint used for the text (e.g. color, size, style)
+ * @deprecated This method does not support glyph composition and decomposition and should
+ * therefore not be used to render complex scripts. It also doesn't handle
+ * supplementary characters (eg emoji).
*/
@Deprecated
- public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y,
- int width, int height, boolean hasAlpha, @Nullable Paint paint) {
- // check for valid input
- if (width < 0) {
- throw new IllegalArgumentException("width must be >= 0");
- }
- if (height < 0) {
- throw new IllegalArgumentException("height must be >= 0");
- }
- if (Math.abs(stride) < width) {
- throw new IllegalArgumentException("abs(stride) must be >= width");
- }
- int lastScanline = offset + (height - 1) * stride;
- int length = colors.length;
- if (offset < 0 || (offset + width > length) || lastScanline < 0
- || (lastScanline + width > length)) {
- throw new ArrayIndexOutOfBoundsException();
- }
- // quick escape if there's nothing to draw
- if (width == 0 || height == 0) {
- return;
- }
- // punch down to native for the actual draw
- native_drawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha,
- paint != null ? paint.getNativeInstance() : 0);
+ public void drawPosText(@NonNull char[] text, int index, int count,
+ @NonNull @Size(multiple = 2) float[] pos,
+ @NonNull Paint paint) {
+ super.drawPosText(text, index, count, pos, paint);
}
/**
- * Legacy version of drawBitmap(int[] colors, ...) that took ints for x,y
+ * Draw the text in the array, with each character's origin specified by the pos array.
*
- * @deprecated Usage with a {@link #isHardwareAccelerated() hardware accelerated} canvas
- * requires an internal copy of color buffer contents every time this method is called. Using a
- * Bitmap avoids this copy, and allows the application to more explicitly control the lifetime
- * and copies of pixel data.
+ * @param text The text to be drawn
+ * @param pos Array of [x,y] positions, used to position each character
+ * @param paint The paint used for the text (e.g. color, size, style)
+ * @deprecated This method does not support glyph composition and decomposition and should
+ * therefore not be used to render complex scripts. It also doesn't handle
+ * supplementary characters (eg emoji).
*/
@Deprecated
- public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y,
- int width, int height, boolean hasAlpha, @Nullable Paint paint) {
- // call through to the common float version
- drawBitmap(colors, offset, stride, (float)x, (float)y, width, height,
- hasAlpha, paint);
+ public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
+ @NonNull Paint paint) {
+ super.drawPosText(text, pos, paint);
}
/**
- * Draw the bitmap using the specified matrix.
+ * Draw the specified Rect using the specified paint. The rectangle will be filled or framed
+ * based on the Style in the paint.
*
- * @param bitmap The bitmap to draw
- * @param matrix The matrix used to transform the bitmap when it is drawn
- * @param paint May be null. The paint used to draw the bitmap
+ * @param rect The rect to be drawn
+ * @param paint The paint used to draw the rect
*/
- public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
- nativeDrawBitmapMatrix(mNativeCanvasWrapper, bitmap, matrix.ni(),
- paint != null ? paint.getNativeInstance() : 0);
+ public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
+ super.drawRect(rect, paint);
}
/**
- * @hide
+ * Draw the specified Rect using the specified Paint. The rectangle will be filled or framed
+ * based on the Style in the paint.
+ *
+ * @param r The rectangle to be drawn.
+ * @param paint The paint used to draw the rectangle
*/
- protected static void checkRange(int length, int offset, int count) {
- if ((offset | count) < 0 || offset + count > length) {
- throw new ArrayIndexOutOfBoundsException();
- }
+ public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
+ super.drawRect(r, paint);
}
/**
- * Draw the bitmap through the mesh, where mesh vertices are evenly
- * distributed across the bitmap. There are meshWidth+1 vertices across, and
- * meshHeight+1 vertices down. The verts array is accessed in row-major
- * order, so that the first meshWidth+1 vertices are distributed across the
- * top of the bitmap from left to right. A more general version of this
- * method is drawVertices().
+ * Draw the specified Rect using the specified paint. The rectangle will be filled or framed
+ * based on the Style in the paint.
*
- * @param bitmap The bitmap to draw using the mesh
- * @param meshWidth The number of columns in the mesh. Nothing is drawn if
- * this is 0
- * @param meshHeight The number of rows in the mesh. Nothing is drawn if
- * this is 0
- * @param verts Array of x,y pairs, specifying where the mesh should be
- * drawn. There must be at least
- * (meshWidth+1) * (meshHeight+1) * 2 + vertOffset values
- * in the array
- * @param vertOffset Number of verts elements to skip before drawing
- * @param colors May be null. Specifies a color at each vertex, which is
- * interpolated across the cell, and whose values are
- * multiplied by the corresponding bitmap colors. If not null,
- * there must be at least (meshWidth+1) * (meshHeight+1) +
- * colorOffset values in the array.
- * @param colorOffset Number of color elements to skip before drawing
- * @param paint May be null. The paint used to draw the bitmap
+ * @param left The left side of the rectangle to be drawn
+ * @param top The top side of the rectangle to be drawn
+ * @param right The right side of the rectangle to be drawn
+ * @param bottom The bottom side of the rectangle to be drawn
+ * @param paint The paint used to draw the rect
*/
- public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
- @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
- @Nullable Paint paint) {
- if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
- throw new ArrayIndexOutOfBoundsException();
- }
- if (meshWidth == 0 || meshHeight == 0) {
- return;
- }
- int count = (meshWidth + 1) * (meshHeight + 1);
- // we mul by 2 since we need two floats per vertex
- checkRange(verts.length, vertOffset, count * 2);
- if (colors != null) {
- // no mul by 2, since we need only 1 color per vertex
- checkRange(colors.length, colorOffset, count);
- }
- nativeDrawBitmapMesh(mNativeCanvasWrapper, bitmap, meshWidth, meshHeight,
- verts, vertOffset, colors, colorOffset,
- paint != null ? paint.getNativeInstance() : 0);
+ public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ super.drawRect(left, top, right, bottom, paint);
}
- public enum VertexMode {
- TRIANGLES(0),
- TRIANGLE_STRIP(1),
- TRIANGLE_FAN(2);
-
- VertexMode(int nativeInt) {
- this.nativeInt = nativeInt;
- }
+ /**
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified RGB color,
+ * using srcover porterduff mode.
+ *
+ * @param r red component (0..255) of the color to draw onto the canvas
+ * @param g green component (0..255) of the color to draw onto the canvas
+ * @param b blue component (0..255) of the color to draw onto the canvas
+ */
+ public void drawRGB(int r, int g, int b) {
+ super.drawRGB(r, g, b);
+ }
- /**
- * @hide
- */
- public final int nativeInt;
+ /**
+ * Draw the specified round-rect using the specified paint. The roundrect will be filled or
+ * framed based on the Style in the paint.
+ *
+ * @param rect The rectangular bounds of the roundRect to be drawn
+ * @param rx The x-radius of the oval used to round the corners
+ * @param ry The y-radius of the oval used to round the corners
+ * @param paint The paint used to draw the roundRect
+ */
+ public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
+ super.drawRoundRect(rect, rx, ry, paint);
}
/**
- * Draw the array of vertices, interpreted as triangles (based on mode). The
- * verts array is required, and specifies the x,y pairs for each vertex. If
- * texs is non-null, then it is used to specify the coordinate in shader
- * coordinates to use at each vertex (the paint must have a shader in this
- * case). If there is no texs array, but there is a color array, then each
- * color is interpolated across its corresponding triangle in a gradient. If
- * both texs and colors arrays are present, then they behave as before, but
- * the resulting color at each pixels is the result of multiplying the
- * colors from the shader and the color-gradient together. The indices array
- * is optional, but if it is present, then it is used to specify the index
- * of each triangle, rather than just walking through the arrays in order.
+ * Draw the specified round-rect using the specified paint. The roundrect will be filled or
+ * framed based on the Style in the paint.
*
- * @param mode How to interpret the array of vertices
- * @param vertexCount The number of values in the vertices array (and
- * corresponding texs and colors arrays if non-null). Each logical
- * vertex is two values (x, y), vertexCount must be a multiple of 2.
- * @param verts Array of vertices for the mesh
- * @param vertOffset Number of values in the verts to skip before drawing.
- * @param texs May be null. If not null, specifies the coordinates to sample
- * into the current shader (e.g. bitmap tile or gradient)
- * @param texOffset Number of values in texs to skip before drawing.
- * @param colors May be null. If not null, specifies a color for each
- * vertex, to be interpolated across the triangle.
- * @param colorOffset Number of values in colors to skip before drawing.
- * @param indices If not null, array of indices to reference into the
- * vertex (texs, colors) array.
- * @param indexCount number of entries in the indices array (if not null).
- * @param paint Specifies the shader to use if the texs array is non-null.
+ * @param rx The x-radius of the oval used to round the corners
+ * @param ry The y-radius of the oval used to round the corners
+ * @param paint The paint used to draw the roundRect
*/
- public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
- int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
- int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
+ public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@NonNull Paint paint) {
- checkRange(verts.length, vertOffset, vertexCount);
- if (isHardwareAccelerated()) {
- return;
- }
- if (texs != null) {
- checkRange(texs.length, texOffset, vertexCount);
- }
- if (colors != null) {
- checkRange(colors.length, colorOffset, vertexCount / 2);
- }
- if (indices != null) {
- checkRange(indices.length, indexOffset, indexCount);
- }
- nativeDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
- vertOffset, texs, texOffset, colors, colorOffset,
- indices, indexOffset, indexCount, paint.getNativeInstance());
+ super.drawRoundRect(left, top, right, bottom, rx, ry, paint);
}
/**
- * Draw the text, with origin at (x,y), using the specified paint. The
- * origin is interpreted based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
+ * based on the Align setting in the paint.
*
- * @param text The text to be drawn
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
+ * @param text The text to be drawn
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint) {
- if ((index | count | (index + count) |
- (text.length - index - count)) < 0) {
- throw new IndexOutOfBoundsException();
- }
- native_drawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
- paint.getNativeInstance(), paint.mNativeTypeface);
+ super.drawText(text, index, count, x, y, paint);
}
/**
- * Draw the text, with origin at (x,y), using the specified paint. The
- * origin is interpreted based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
+ * based on the Align setting in the paint.
*
- * @param text The text to be drawn
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
+ * @param text The text to be drawn
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
- native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
- paint.getNativeInstance(), paint.mNativeTypeface);
+ super.drawText(text, x, y, paint);
}
/**
- * Draw the text, with origin at (x,y), using the specified paint.
- * The origin is interpreted based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
+ * based on the Align setting in the paint.
*
- * @param text The text to be drawn
+ * @param text The text to be drawn
* @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text to draw
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
+ * @param end (end - 1) is the index of the last character in text to draw
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull String text, int start, int end, float x, float y,
@NonNull Paint paint) {
- if ((start | end | (end - start) | (text.length() - end)) < 0) {
- throw new IndexOutOfBoundsException();
- }
- native_drawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
- paint.getNativeInstance(), paint.mNativeTypeface);
+ super.drawText(text, start, end, x, y, paint);
}
/**
- * Draw the specified range of text, specified by start/end, with its
- * origin at (x,y), in the specified Paint. The origin is interpreted
- * based on the Align setting in the Paint.
+ * Draw the specified range of text, specified by start/end, with its origin at (x,y), in the
+ * specified Paint. The origin is interpreted based on the Align setting in the Paint.
*
- * @param text The text to be drawn
- * @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text
- * to draw
- * @param x The x-coordinate of origin for where to draw the text
- * @param y The y-coordinate of origin for where to draw the text
+ * @param text The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
+ * @param x The x-coordinate of origin for where to draw the text
+ * @param y The y-coordinate of origin for where to draw the text
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
@NonNull Paint paint) {
- if ((start | end | (end - start) | (text.length() - end)) < 0) {
- throw new IndexOutOfBoundsException();
- }
- if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- native_drawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
- } else if (text instanceof GraphicsOperations) {
- ((GraphicsOperations) text).drawText(this, start, end, x, y,
- paint);
- } else {
- char[] buf = TemporaryBuffer.obtain(end - start);
- TextUtils.getChars(text, start, end, buf, 0);
- native_drawText(mNativeCanvasWrapper, buf, 0, end - start, x, y,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
- TemporaryBuffer.recycle(buf);
- }
+ super.drawText(text, start, end, x, y, paint);
+ }
+
+ /**
+ * Draw the text, with origin at (x,y), using the specified paint, along the specified path. The
+ * paint's Align setting determins where along the path to start the text.
+ *
+ * @param text The text to be drawn
+ * @param path The path the text should follow for its baseline
+ * @param hOffset The distance along the path to add to the text's starting position
+ * @param vOffset The distance above(-) or below(+) the path to position the text
+ * @param paint The paint used for the text (e.g. color, size, style)
+ */
+ public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,
+ float hOffset, float vOffset, @NonNull Paint paint) {
+ super.drawTextOnPath(text, index, count, path, hOffset, vOffset, paint);
+ }
+
+ /**
+ * Draw the text, with origin at (x,y), using the specified paint, along the specified path. The
+ * paint's Align setting determins where along the path to start the text.
+ *
+ * @param text The text to be drawn
+ * @param path The path the text should follow for its baseline
+ * @param hOffset The distance along the path to add to the text's starting position
+ * @param vOffset The distance above(-) or below(+) the path to position the text
+ * @param paint The paint used for the text (e.g. color, size, style)
+ */
+ public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
+ float vOffset, @NonNull Paint paint) {
+ super.drawTextOnPath(text, path, hOffset, vOffset, paint);
}
/**
* Draw a run of text, all in a single direction, with optional context for complex text
* shaping.
- *
- * <p>See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}
- * for more details. This method uses a character array rather than CharSequence to
- * represent the string. Also, to be consistent with the pattern established in
- * {@link #drawText}, in this method {@code count} and {@code contextCount} are used rather
- * than offsets of the end position; {@code count = end - start, contextCount = contextEnd -
+ * <p>
+ * See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)} for
+ * more details. This method uses a character array rather than CharSequence to represent the
+ * string. Also, to be consistent with the pattern established in {@link #drawText}, in this
+ * method {@code count} and {@code contextCount} are used rather than offsets of the end
+ * position; {@code count = end - start, contextCount = contextEnd -
* contextStart}.
*
* @param text the text to render
* @param index the start of the text to render
* @param count the count of chars to render
- * @param contextIndex the start of the context for shaping. Must be
- * no greater than index.
- * @param contextCount the number of characters in the context for shaping.
- * contexIndex + contextCount must be no less than index + count.
+ * @param contextIndex the start of the context for shaping. Must be no greater than index.
+ * @param contextCount the number of characters in the context for shaping. contexIndex +
+ * contextCount must be no less than index + count.
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
* @param isRtl whether the run is in RTL direction
@@ -1747,401 +1748,80 @@ public class Canvas {
*/
public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex,
int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) {
-
- if (text == null) {
- throw new NullPointerException("text is null");
- }
- if (paint == null) {
- throw new NullPointerException("paint is null");
- }
- if ((index | count | contextIndex | contextCount | index - contextIndex
- | (contextIndex + contextCount) - (index + count)
- | text.length - (contextIndex + contextCount)) < 0) {
- throw new IndexOutOfBoundsException();
- }
-
- native_drawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
- x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
+ super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, isRtl, paint);
}
/**
* Draw a run of text, all in a single direction, with optional context for complex text
* shaping.
- *
- * <p>The run of text includes the characters from {@code start} to {@code end} in the text. In
+ * <p>
+ * The run of text includes the characters from {@code start} to {@code end} in the text. In
* addition, the range {@code contextStart} to {@code contextEnd} is used as context for the
* purpose of complex text shaping, such as Arabic text potentially shaped differently based on
* the text next to it.
- *
- * <p>All text outside the range {@code contextStart..contextEnd} is ignored. The text between
+ * <p>
+ * All text outside the range {@code contextStart..contextEnd} is ignored. The text between
* {@code start} and {@code end} will be laid out and drawn.
- *
- * <p>The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
+ * <p>
+ * The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
* suitable only for runs of a single direction. Alignment of the text is as determined by the
* Paint's TextAlign value. Further, {@code 0 <= contextStart <= start <= end <= contextEnd
* <= text.length} must hold on entry.
- *
- * <p>Also see {@link android.graphics.Paint#getRunAdvance} for a corresponding method to
- * measure the text; the advance width of the text drawn matches the value obtained from that
- * method.
+ * <p>
+ * Also see {@link android.graphics.Paint#getRunAdvance} for a corresponding method to measure
+ * the text; the advance width of the text drawn matches the value obtained from that method.
*
* @param text the text to render
- * @param start the start of the text to render. Data before this position
- * can be used for shaping context.
- * @param end the end of the text to render. Data at or after this
- * position can be used for shaping context.
+ * @param start the start of the text to render. Data before this position can be used for
+ * shaping context.
+ * @param end the end of the text to render. Data at or after this position can be used for
+ * shaping context.
* @param contextStart the index of the start of the shaping context
* @param contextEnd the index of the end of the shaping context
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
* @param isRtl whether the run is in RTL direction
* @param paint the paint
- *
* @see #drawTextRun(char[], int, int, int, int, float, float, boolean, Paint)
*/
public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) {
-
- if (text == null) {
- throw new NullPointerException("text is null");
- }
- if (paint == null) {
- throw new NullPointerException("paint is null");
- }
- if ((start | end | contextStart | contextEnd | start - contextStart | end - start
- | contextEnd - end | text.length() - contextEnd) < 0) {
- throw new IndexOutOfBoundsException();
- }
-
- if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- native_drawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
- contextEnd, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
- } else if (text instanceof GraphicsOperations) {
- ((GraphicsOperations) text).drawTextRun(this, start, end,
- contextStart, contextEnd, x, y, isRtl, paint);
- } else {
- int contextLen = contextEnd - contextStart;
- int len = end - start;
- char[] buf = TemporaryBuffer.obtain(contextLen);
- TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- native_drawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
- 0, contextLen, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
- TemporaryBuffer.recycle(buf);
- }
- }
-
- /**
- * Draw the text in the array, with each character's origin specified by
- * the pos array.
- *
- * @param text The text to be drawn
- * @param index The index of the first character to draw
- * @param count The number of characters to draw, starting from index.
- * @param pos Array of [x,y] positions, used to position each
- * character
- * @param paint The paint used for the text (e.g. color, size, style)
- *
- * @deprecated This method does not support glyph composition and decomposition and
- * should therefore not be used to render complex scripts. It also doesn't
- * handle supplementary characters (eg emoji).
- */
- @Deprecated
- public void drawPosText(@NonNull char[] text, int index, int count,
- @NonNull @Size(multiple=2) float[] pos,
- @NonNull Paint paint) {
- if (index < 0 || index + count > text.length || count*2 > pos.length) {
- throw new IndexOutOfBoundsException();
- }
- for (int i = 0; i < count; i++) {
- drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
- }
+ super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, isRtl, paint);
}
/**
- * Draw the text in the array, with each character's origin specified by
- * the pos array.
- *
- * @param text The text to be drawn
- * @param pos Array of [x,y] positions, used to position each character
- * @param paint The paint used for the text (e.g. color, size, style)
+ * Draw the array of vertices, interpreted as triangles (based on mode). The verts array is
+ * required, and specifies the x,y pairs for each vertex. If texs is non-null, then it is used
+ * to specify the coordinate in shader coordinates to use at each vertex (the paint must have a
+ * shader in this case). If there is no texs array, but there is a color array, then each color
+ * is interpolated across its corresponding triangle in a gradient. If both texs and colors
+ * arrays are present, then they behave as before, but the resulting color at each pixels is the
+ * result of multiplying the colors from the shader and the color-gradient together. The indices
+ * array is optional, but if it is present, then it is used to specify the index of each
+ * triangle, rather than just walking through the arrays in order.
*
- * @deprecated This method does not support glyph composition and decomposition and
- * should therefore not be used to render complex scripts. It also doesn't
- * handle supplementary characters (eg emoji).
+ * @param mode How to interpret the array of vertices
+ * @param vertexCount The number of values in the vertices array (and corresponding texs and
+ * colors arrays if non-null). Each logical vertex is two values (x, y), vertexCount
+ * must be a multiple of 2.
+ * @param verts Array of vertices for the mesh
+ * @param vertOffset Number of values in the verts to skip before drawing.
+ * @param texs May be null. If not null, specifies the coordinates to sample into the current
+ * shader (e.g. bitmap tile or gradient)
+ * @param texOffset Number of values in texs to skip before drawing.
+ * @param colors May be null. If not null, specifies a color for each vertex, to be interpolated
+ * across the triangle.
+ * @param colorOffset Number of values in colors to skip before drawing.
+ * @param indices If not null, array of indices to reference into the vertex (texs, colors)
+ * array.
+ * @param indexCount number of entries in the indices array (if not null).
+ * @param paint Specifies the shader to use if the texs array is non-null.
*/
- @Deprecated
- public void drawPosText(@NonNull String text, @NonNull @Size(multiple=2) float[] pos,
+ public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
+ int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
+ int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
@NonNull Paint paint) {
- drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
- }
-
- /**
- * Draw the text, with origin at (x,y), using the specified paint, along
- * the specified path. The paint's Align setting determins where along the
- * path to start the text.
- *
- * @param text The text to be drawn
- * @param path The path the text should follow for its baseline
- * @param hOffset The distance along the path to add to the text's
- * starting position
- * @param vOffset The distance above(-) or below(+) the path to position
- * the text
- * @param paint The paint used for the text (e.g. color, size, style)
- */
- public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,
- float hOffset, float vOffset, @NonNull Paint paint) {
- if (index < 0 || index + count > text.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
- native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
- path.readOnlyNI(), hOffset, vOffset,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
+ super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset,
+ colors, colorOffset, indices, indexOffset, indexCount, paint);
}
-
- /**
- * Draw the text, with origin at (x,y), using the specified paint, along
- * the specified path. The paint's Align setting determins where along the
- * path to start the text.
- *
- * @param text The text to be drawn
- * @param path The path the text should follow for its baseline
- * @param hOffset The distance along the path to add to the text's
- * starting position
- * @param vOffset The distance above(-) or below(+) the path to position
- * the text
- * @param paint The paint used for the text (e.g. color, size, style)
- */
- public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
- float vOffset, @NonNull Paint paint) {
- if (text.length() > 0) {
- native_drawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
- paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
- }
- }
-
- /**
- * Save the canvas state, draw the picture, and restore the canvas state.
- * This differs from picture.draw(canvas), which does not perform any
- * save/restore.
- *
- * <p>
- * <strong>Note:</strong> This forces the picture to internally call
- * {@link Picture#endRecording} in order to prepare for playback.
- *
- * @param picture The picture to be drawn
- */
- public void drawPicture(@NonNull Picture picture) {
- picture.endRecording();
- int restoreCount = save();
- picture.draw(this);
- restoreToCount(restoreCount);
- }
-
- /**
- * Draw the picture, stretched to fit into the dst rectangle.
- */
- public void drawPicture(@NonNull Picture picture, @NonNull RectF dst) {
- save();
- translate(dst.left, dst.top);
- if (picture.getWidth() > 0 && picture.getHeight() > 0) {
- scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
- }
- drawPicture(picture);
- restore();
- }
-
- /**
- * Draw the picture, stretched to fit into the dst rectangle.
- */
- public void drawPicture(@NonNull Picture picture, @NonNull Rect dst) {
- save();
- translate(dst.left, dst.top);
- if (picture.getWidth() > 0 && picture.getHeight() > 0) {
- scale((float) dst.width() / picture.getWidth(),
- (float) dst.height() / picture.getHeight());
- }
- drawPicture(picture);
- restore();
- }
-
- /**
- * Releases the resources associated with this canvas.
- *
- * @hide
- */
- public void release() {
- mNativeCanvasWrapper = 0;
- if (mFinalizer != null) {
- mFinalizer.run();
- mFinalizer = null;
- }
- }
-
- /**
- * Free up as much memory as possible from private caches (e.g. fonts, images)
- *
- * @hide
- */
- public static native void freeCaches();
-
- /**
- * Free up text layout caches
- *
- * @hide
- */
- public static native void freeTextLayoutCaches();
-
- private static native long initRaster(Bitmap bitmap);
- private static native void native_setBitmap(long canvasHandle,
- Bitmap bitmap);
- private static native boolean native_isOpaque(long canvasHandle);
- private static native void native_setHighContrastText(long renderer, boolean highContrastText);
- private static native int native_getWidth(long canvasHandle);
- private static native int native_getHeight(long canvasHandle);
-
- private static native int native_save(long canvasHandle, int saveFlags);
- private static native int native_saveLayer(long nativeCanvas, float l,
- float t, float r, float b,
- long nativePaint,
- int layerFlags);
- private static native int native_saveLayerAlpha(long nativeCanvas, float l,
- float t, float r, float b,
- int alpha, int layerFlags);
- private static native void native_restore(long canvasHandle, boolean tolerateUnderflow);
- private static native void native_restoreToCount(long canvasHandle,
- int saveCount,
- boolean tolerateUnderflow);
- private static native int native_getSaveCount(long canvasHandle);
-
- private static native void native_translate(long canvasHandle,
- float dx, float dy);
- private static native void native_scale(long canvasHandle,
- float sx, float sy);
- private static native void native_rotate(long canvasHandle, float degrees);
- private static native void native_skew(long canvasHandle,
- float sx, float sy);
- private static native void native_concat(long nativeCanvas,
- long nativeMatrix);
- private static native void native_setMatrix(long nativeCanvas,
- long nativeMatrix);
- private static native boolean native_clipRect(long nativeCanvas,
- float left, float top,
- float right, float bottom,
- int regionOp);
- private static native boolean native_clipPath(long nativeCanvas,
- long nativePath,
- int regionOp);
- private static native boolean native_clipRegion(long nativeCanvas,
- long nativeRegion,
- int regionOp);
- private static native void nativeSetDrawFilter(long nativeCanvas,
- long nativeFilter);
- private static native boolean native_getClipBounds(long nativeCanvas,
- Rect bounds);
- private static native void native_getCTM(long nativeCanvas,
- long nativeMatrix);
- private static native boolean native_quickReject(long nativeCanvas,
- long nativePath);
- private static native boolean native_quickReject(long nativeCanvas,
- float left, float top,
- float right, float bottom);
- private static native void native_drawColor(long nativeCanvas, int color,
- int mode);
- private static native void native_drawPaint(long nativeCanvas,
- long nativePaint);
- private static native void native_drawPoint(long canvasHandle, float x, float y,
- long paintHandle);
- private static native void native_drawPoints(long canvasHandle, float[] pts,
- int offset, int count,
- long paintHandle);
- private static native void native_drawLine(long nativeCanvas, float startX,
- float startY, float stopX,
- float stopY, long nativePaint);
- private static native void native_drawLines(long canvasHandle, float[] pts,
- int offset, int count,
- long paintHandle);
- private static native void native_drawRect(long nativeCanvas, float left,
- float top, float right,
- float bottom,
- long nativePaint);
- private static native void native_drawOval(long nativeCanvas, float left, float top,
- float right, float bottom, long nativePaint);
- private static native void native_drawCircle(long nativeCanvas, float cx,
- float cy, float radius,
- long nativePaint);
- private static native void native_drawArc(long nativeCanvas, float left, float top,
- float right, float bottom,
- float startAngle, float sweep, boolean useCenter,
- long nativePaint);
- private static native void native_drawRoundRect(long nativeCanvas,
- float left, float top, float right, float bottom,
- float rx, float ry, long nativePaint);
- private static native void native_drawPath(long nativeCanvas,
- long nativePath,
- long nativePaint);
- private static native void native_drawRegion(long nativeCanvas,
- long nativeRegion, long nativePaint);
- private native void native_drawNinePatch(long nativeCanvas, long nativeBitmap,
- long ninePatch, float dstLeft, float dstTop, float dstRight, float dstBottom,
- long nativePaintOrZero, int screenDensity, int bitmapDensity);
- private native void native_drawBitmap(long nativeCanvas, Bitmap bitmap,
- float left, float top,
- long nativePaintOrZero,
- int canvasDensity,
- int screenDensity,
- int bitmapDensity);
- private native void native_drawBitmap(long nativeCanvas, Bitmap bitmap,
- float srcLeft, float srcTop, float srcRight, float srcBottom,
- float dstLeft, float dstTop, float dstRight, float dstBottom,
- long nativePaintOrZero, int screenDensity, int bitmapDensity);
- private static native void native_drawBitmap(long nativeCanvas, int[] colors,
- int offset, int stride, float x,
- float y, int width, int height,
- boolean hasAlpha,
- long nativePaintOrZero);
- private static native void nativeDrawBitmapMatrix(long nativeCanvas,
- Bitmap bitmap,
- long nativeMatrix,
- long nativePaint);
- private static native void nativeDrawBitmapMesh(long nativeCanvas,
- Bitmap bitmap,
- int meshWidth, int meshHeight,
- float[] verts, int vertOffset,
- int[] colors, int colorOffset,
- long nativePaint);
- private static native void nativeDrawVertices(long nativeCanvas, int mode, int n,
- float[] verts, int vertOffset, float[] texs, int texOffset,
- int[] colors, int colorOffset, short[] indices,
- int indexOffset, int indexCount, long nativePaint);
-
- private static native void native_drawText(long nativeCanvas, char[] text,
- int index, int count, float x,
- float y, int flags, long nativePaint,
- long nativeTypeface);
- private static native void native_drawText(long nativeCanvas, String text,
- int start, int end, float x,
- float y, int flags, long nativePaint,
- long nativeTypeface);
-
- private static native void native_drawTextRun(long nativeCanvas, String text,
- int start, int end, int contextStart, int contextEnd,
- float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
-
- private static native void native_drawTextRun(long nativeCanvas, char[] text,
- int start, int count, int contextStart, int contextCount,
- float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
-
- private static native void native_drawTextOnPath(long nativeCanvas,
- char[] text, int index,
- int count, long nativePath,
- float hOffset,
- float vOffset, int bidiFlags,
- long nativePaint, long nativeTypeface);
- private static native void native_drawTextOnPath(long nativeCanvas,
- String text, long nativePath,
- float hOffset,
- float vOffset,
- int flags, long nativePaint, long nativeTypeface);
- private static native long getNativeFinalizer();
}
diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java
index c627297db7c0..a2c104a0b4bb 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -18,11 +18,12 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.Size;
-import android.util.MathUtils;
+
import com.android.internal.util.XmlUtils;
import java.util.HashMap;
import java.util.Locale;
+import java.util.function.DoubleUnaryOperator;
/**
* The Color class defines methods for creating and converting color ints.
@@ -94,7 +95,7 @@ public class Color {
*/
@ColorInt
public static int rgb(int red, int green, int blue) {
- return (0xFF << 24) | (red << 16) | (green << 8) | blue;
+ return 0xff000000 | (red << 16) | (green << 8) | blue;
}
/**
@@ -121,12 +122,11 @@ public class Color {
* @return a value between 0 (darkest black) and 1 (lightest white)
*/
public static float luminance(@ColorInt int color) {
- double red = Color.red(color) / 255.0;
- red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
- double green = Color.green(color) / 255.0;
- green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);
- double blue = Color.blue(color) / 255.0;
- blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);
+ ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
+ DoubleUnaryOperator eotf = cs.getEotf();
+ double red = eotf.applyAsDouble(Color.red(color) / 255.0);
+ double green = eotf.applyAsDouble(Color.green(color) / 255.0);
+ double blue = eotf.applyAsDouble(Color.blue(color) / 255.0);
return (float) ((0.2126 * red) + (0.7152 * green) + (0.0722 * blue));
}
@@ -250,9 +250,8 @@ public class Color {
}
private static final HashMap<String, Integer> sColorNameMap;
-
static {
- sColorNameMap = new HashMap<String, Integer>();
+ sColorNameMap = new HashMap<>();
sColorNameMap.put("black", BLACK);
sColorNameMap.put("darkgray", DKGRAY);
sColorNameMap.put("gray", GRAY);
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
new file mode 100644
index 000000000000..7dc5de3051e6
--- /dev/null
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -0,0 +1,3845 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Size;
+import android.annotation.Nullable;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * {@usesMathJax}
+ *
+ * <p>A {@link ColorSpace} is used to identify a specific organization of colors.
+ * Each color space is characterized by a {@link Model color model} that defines
+ * how a color value is represented (for instance the {@link Model#RGB RGB} color
+ * model defines a color value as a triplet of numbers).</p>
+ *
+ * <p>Each component of a color must fall within a valid range, specific to each
+ * color space, defined by {@link #getMinValue(int)} and {@link #getMaxValue(int)}
+ * This range is commonly \([0..1]\). While it is recommended to use values in the
+ * valid range, a color space always clamps input and output values when performing
+ * operations such as converting to a different color space.</p>
+ *
+ * <h3>Using color spaces</h3>
+ *
+ * <p>This implementation provides a pre-defined set of common color spaces
+ * described in the {@link Named} enum. To obtain an instance of one of the
+ * pre-defined color spaces, simply invoke {@link #get(Named)}:</p>
+ *
+ * <pre class="prettyprint">
+ * ColorSpace sRgb = ColorSpace.get(ColorSpace.Named.SRGB);
+ * </pre>
+ *
+ * <p>The {@link #get(Named)} method always returns the same instance for a given
+ * name. Color spaces with an {@link Model#RGB RGB} color model can be safely
+ * cast to {@link Rgb}. Doing so gives you access to more APIs to query various
+ * properties of RGB color models: color gamut primaries, transfer functions,
+ * conversions to and from linear space, etc. Please refer to {@link Rgb} for
+ * more information.</p>
+ *
+ * <p>The documentation of {@link Named} provides a detailed description of the
+ * various characteristics of each available color space.</p>
+ *
+ * <h3>Color space conversions</h3>
+
+ * <p>To allow conversion between color spaces, this implementation uses the CIE
+ * XYZ profile connection space (PCS). Color values can be converted to and from
+ * this PCS using {@link #toXyz(float[])} and {@link #fromXyz(float[])}.</p>
+ *
+ * <p>For color space with a non-RGB color model, the white point of the PCS
+ * <em>must be</em> the CIE standard illuminant D50. RGB color spaces use their
+ * native white point (D65 for {@link Named#SRGB sRGB} for instance and must
+ * undergo {@link Adaptation chromatic adaptation} as necessary.</p>
+ *
+ * <p>Since the white point of the PCS is not defined for RGB color space, it is
+ * highly recommended to use the variants of the {@link #connect(ColorSpace, ColorSpace)}
+ * method to perform conversions between color spaces. A color space can be
+ * manually adapted to a specific white point using {@link #adapt(ColorSpace, float[])}.
+ * Please refer to the documentation of {@link Rgb RGB color spaces} for more
+ * information. Several common CIE standard illuminants are provided in this
+ * class as reference (see {@link #ILLUMINANT_D65} or {@link #ILLUMINANT_D50}
+ * for instance).</p>
+ *
+ * <p>Here is an example of how to convert from a color space to another:</p>
+ *
+ * <pre class="prettyprint">
+ * // Convert from DCI-P3 to Rec.2020
+ * ColorSpace.Connector connector = ColorSpace.connect(
+ * ColorSpace.get(ColorSpace.Named.DCI_P3),
+ * ColorSpace.get(ColorSpace.Named.BT2020));
+ *
+ * float[] bt2020 = connector.transform(p3r, p3g, p3b);
+ * </pre>
+ *
+ * <p>You can easily convert to {@link Named#SRGB sRGB} by omitting the second
+ * parameter:</p>
+ *
+ * <pre class="prettyprint">
+ * // Convert from DCI-P3 to sRGB
+ * ColorSpace.Connector connector = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.DCI_P3));
+ *
+ * float[] sRGB = connector.transform(p3r, p3g, p3b);
+ * </pre>
+ *
+ * <p>Conversions also work between color spaces with different color models:</p>
+ *
+ * <pre class="prettyprint">
+ * // Convert from CIE L*a*b* (color model Lab) to Rec.709 (color model RGB)
+ * ColorSpace.Connector connector = ColorSpace.connect(
+ * ColorSpace.get(ColorSpace.Named.CIE_LAB),
+ * ColorSpace.get(ColorSpace.Named.BT709));
+ * </pre>
+ *
+ * <h3>Color spaces and multi-threading</h3>
+ *
+ * <p>Color spaces and other related classes ({@link Connector} for instance)
+ * are immutable and stateless. They can be safely used from multiple concurrent
+ * threads.</p>
+ *
+ * <p>Public static methods provided by this class, such as {@link #get(Named)}
+ * and {@link #connect(ColorSpace, ColorSpace)}, are also guaranteed to be
+ * thread-safe.</p>
+ *
+ * <h3>Visualization and debugging</h3>
+ *
+ * <p>To visualize and debug color spaces, you can call {@link #createRenderer()}.
+ * The {@link Renderer} created by calling this method can be used to compare
+ * color spaces and locate specific colors on a CIE 1931 chromaticity diagram.</p>
+ *
+ * <p>The following code snippet shows how to render a bitmap that compares
+ * the color gamuts and white points of {@link Named#DCI_P3} and
+ * {@link Named#PRO_PHOTO_RGB}:</p>
+ *
+ * <pre class="prettyprint">
+ * Bitmap bitmap = ColorSpace.createRenderer()
+ * .size(768)
+ * .clip(true)
+ * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+ * .add(ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), 0xff097ae9)
+ * .render();
+ * </pre>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_renderer.png" />
+ * <figcaption style="text-align: center;">DCI-P3 vs ProPhoto RGB</figcaption>
+ * </p>
+ *
+ * <p>Please refer to the documentation of the {@link Renderer} class for more
+ * information about its options and capabilities.</p>
+ *
+ * @see #get(Named)
+ * @see Named
+ * @see Model
+ * @see Connector
+ * @see Adaptation
+ * @see Renderer
+ */
+@SuppressWarnings("StaticInitializerReferencesSubClass")
+public abstract class ColorSpace {
+ /**
+ * Standard CIE 1931 2° illuminant A, encoded in xyY.
+ * This illuminant has a color temperature of 2856K.
+ */
+ public static final float[] ILLUMINANT_A = { 0.44757f, 0.40745f };
+ /**
+ * Standard CIE 1931 2° illuminant B, encoded in xyY.
+ * This illuminant has a color temperature of 4874K.
+ */
+ public static final float[] ILLUMINANT_B = { 0.34842f, 0.35161f };
+ /**
+ * Standard CIE 1931 2° illuminant C, encoded in xyY.
+ * This illuminant has a color temperature of 6774K.
+ */
+ public static final float[] ILLUMINANT_C = { 0.31006f, 0.31616f };
+ /**
+ * Standard CIE 1931 2° illuminant D50, encoded in xyY.
+ * This illuminant has a color temperature of 5003K. This illuminant
+ * is used by the profile connection space in ICC profiles.
+ */
+ public static final float[] ILLUMINANT_D50 = { 0.34567f, 0.35850f };
+ /**
+ * Standard CIE 1931 2° illuminant D55, encoded in xyY.
+ * This illuminant has a color temperature of 5503K.
+ */
+ public static final float[] ILLUMINANT_D55 = { 0.33242f, 0.34743f };
+ /**
+ * Standard CIE 1931 2° illuminant D60, encoded in xyY.
+ * This illuminant has a color temperature of 6004K.
+ */
+ public static final float[] ILLUMINANT_D60 = { 0.32168f, 0.33767f };
+ /**
+ * Standard CIE 1931 2° illuminant D65, encoded in xyY.
+ * This illuminant has a color temperature of 6504K. This illuminant
+ * is commonly used in RGB color spaces such as sRGB, BT.209, etc.
+ */
+ public static final float[] ILLUMINANT_D65 = { 0.31271f, 0.32902f };
+ /**
+ * Standard CIE 1931 2° illuminant D75, encoded in xyY.
+ * This illuminant has a color temperature of 7504K.
+ */
+ public static final float[] ILLUMINANT_D75 = { 0.29902f, 0.31485f };
+ /**
+ * Standard CIE 1931 2° illuminant E, encoded in xyY.
+ * This illuminant has a color temperature of 5454K.
+ */
+ public static final float[] ILLUMINANT_E = { 0.33333f, 0.33333f };
+
+ /**
+ * The minimum ID value a color space can have.
+ *
+ * @see #getId()
+ */
+ public static final int MIN_ID = -1; // Do not change
+ /**
+ * The maximum ID value a color space can have.
+ *
+ * @see #getId()
+ */
+ public static final int MAX_ID = 64; // Do not change, used to encode in longs
+
+ private static final float[] SRGB_PRIMARIES = { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f };
+ private static final float[] NTSC_1953_PRIMARIES = { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f };
+ private static final float[] ILLUMINANT_D50_XYZ = { 0.964212f, 1.0f, 0.825188f };
+
+ // See static initialization block next to #get(Named)
+ private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
+
+ @NonNull private final String mName;
+ @NonNull private final Model mModel;
+ @IntRange(from = MIN_ID, to = MAX_ID) private final int mId;
+
+ /**
+ * {@usesMathJax}
+ *
+ * <p>List of common, named color spaces. A corresponding instance of
+ * {@link ColorSpace} can be obtained by calling {@link ColorSpace#get(Named)}:</p>
+ *
+ * <pre class="prettyprint">
+ * ColorSpace cs = ColorSpace.get(ColorSpace.Named.DCI_P3);
+ * </pre>
+ *
+ * <p>The properties of each color space are described below (see {@link #SRGB sRGB}
+ * for instance). When applicable, the color gamut of each color space is compared
+ * to the color gamut of sRGB using a CIE 1931 xy chromaticity diagram. This diagram
+ * shows the location of the color space's primaries and white point.</p>
+ *
+ * @see ColorSpace#get(Named)
+ */
+ public enum Named {
+ // NOTE: Do NOT change the order of the enum
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space sRGB standardized as IEC 61966-2.1:1999.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \le 0.0031308 \\
+ * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \gt 0.0031308 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \le 0.04045 \\
+ * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \gt 0.04045 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_srgb.png" />
+ * <figcaption style="text-align: center;">sRGB</figcaption>
+ * </p>
+ */
+ SRGB,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space sRGB standardized as IEC 61966-2.1:1999.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1 (Linear)</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(C_{sRGB} = C_{linear}\)</td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(C_{linear} = C_{sRGB}\)</td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_srgb.png" />
+ * <figcaption style="text-align: center;">sRGB</figcaption>
+ * </p>
+ */
+ LINEAR_SRGB,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space scRGB-nl standardized as IEC 61966-2-2:2003.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">scRGB-nl IEC 61966-2-2:2003</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{scRGB} = \begin{cases} sign(C_{linear}) 12.92 \times \left| C_{linear} \right| &
+ * \left| C_{linear} \right| \le 0.0031308 \\
+ * sign(C_{linear}) 1.055 \times \left| C_{linear} \right| ^{\frac{1}{2.4}} - 0.055 &
+ * \left| C_{linear} \right| \gt 0.0031308 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}sign(C_{scRGB}) \frac{\left| C_{scRGB} \right|}{12.92} &
+ * \left| C_{scRGB} \right| \le 0.04045 \\
+ * sign(C_{scRGB}) \left( \frac{\left| C_{scRGB} \right| + 0.055}{1.055} \right) ^{2.4} &
+ * \left| C_{scRGB} \right| \gt 0.04045 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([-0.5..7.5[\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
+ * <figcaption style="text-align: center;">Extended RGB (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ EXTENDED_SRGB,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space scRGB standardized as IEC 61966-2-2:2003.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">scRGB IEC 61966-2-2:2003</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(C_{scRGB} = C_{linear}\)</td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(C_{linear} = C_{scRGB}\)</td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([-0.5..7.5[\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
+ * <figcaption style="text-align: center;">Extended RGB (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ LINEAR_EXTENDED_SRGB,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space BT.709 standardized as Rec. ITU-R BT.709-5.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.709-5</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
+ * 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
+ * \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_bt709.png" />
+ * <figcaption style="text-align: center;">BT.709</figcaption>
+ * </p>
+ */
+ BT709,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space BT.2020 standardized as Rec. ITU-R BT.2020-1.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.708</td><td>0.170</td><td>0.131</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.292</td><td>0.797</td><td>0.046</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.2020-1</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{BT2020} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.0181 \\
+ * 1.0993 \times C_{linear}^{\frac{1}{2.2}} - 0.0993 & C_{linear} \ge 0.0181 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}\frac{C_{BT2020}}{4.5} & C_{BT2020} \lt 0.08145 \\
+ * \left( \frac{C_{BT2020} + 0.0993}{1.0993} \right) ^{2.2} & C_{BT2020} \ge 0.08145 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_bt2020.png" />
+ * <figcaption style="text-align: center;">BT.2020 (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ BT2020,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space DCI-P3 standardized as SMPTE RP 431-2-2007.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.680</td><td>0.265</td><td>0.150</td><td>0.314</td></tr>
+ * <tr><td>y</td><td>0.320</td><td>0.690</td><td>0.060</td><td>0.351</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">SMPTE RP 431-2-2007 DCI (P3)</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">N/A</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(C_{P3} = C_{linear}^{\frac{1}{2.6}}\)</td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(C_{linear} = C_{P3}^{2.6}\)</td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_dci_p3.png" />
+ * <figcaption style="text-align: center;">DCI-P3 (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ DCI_P3,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space Display P3 based on SMPTE RP 431-2-2007 and IEC 61966-2.1:1999.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.680</td><td>0.265</td><td>0.150</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.320</td><td>0.690</td><td>0.060</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Display P3</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \le 0.0031308 \\
+ * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \gt 0.0031308 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \le 0.04045 \\
+ * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \gt 0.04045 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_display_p3.png" />
+ * <figcaption style="text-align: center;">Display P3 (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ DISPLAY_P3,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space NTSC, 1953 standard.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.67</td><td>0.21</td><td>0.14</td><td>0.310</td></tr>
+ * <tr><td>y</td><td>0.33</td><td>0.71</td><td>0.08</td><td>0.316</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">NTSC (1953)</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">C</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
+ * 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
+ * \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_ntsc_1953.png" />
+ * <figcaption style="text-align: center;">NTSC 1953 (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ NTSC_1953,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space SMPTE C.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.630</td><td>0.310</td><td>0.155</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.340</td><td>0.595</td><td>0.070</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">SMPTE-C RGB</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
+ * 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
+ * \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_smpte_c.png" />
+ * <figcaption style="text-align: center;">SMPTE-C (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ SMPTE_C,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space Adobe RGB (1998).</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.64</td><td>0.21</td><td>0.15</td><td>0.3127</td></tr>
+ * <tr><td>y</td><td>0.33</td><td>0.71</td><td>0.06</td><td>0.3290</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Adobe RGB (1998)</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(C_{RGB} = C_{linear}^{\frac{1}{2.2}}\)</td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(C_{linear} = C_{RGB}^{2.2}\)</td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_adobe_rgb.png" />
+ * <figcaption style="text-align: center;">Adobe RGB (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ ADOBE_RGB,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space ProPhoto RGB standardized as ROMM RGB ISO 22028-2:2013.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.7347</td><td>0.1596</td><td>0.0366</td><td>0.3457</td></tr>
+ * <tr><td>y</td><td>0.2653</td><td>0.8404</td><td>0.0001</td><td>0.3585</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">ROMM RGB ISO 22028-2:2013</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{ROMM} = \begin{cases} 16 \times C_{linear} & C_{linear} \lt 0.001953 \\
+ * C_{linear}^{\frac{1}{1.8}} & C_{linear} \ge 0.001953 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(\begin{equation}
+ * C_{linear} = \begin{cases}\frac{C_{ROMM}}{16} & C_{ROMM} \lt 0.031248 \\
+ * C_{ROMM}^{1.8} & C_{ROMM} \ge 0.031248 \end{cases}
+ * \end{equation}\)
+ * </td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_pro_photo_rgb.png" />
+ * <figcaption style="text-align: center;">ProPhoto RGB (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ PRO_PHOTO_RGB,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space ACES standardized as SMPTE ST 2065-1:2012.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.73470</td><td>0.00000</td><td>0.00010</td><td>0.32168</td></tr>
+ * <tr><td>y</td><td>0.26530</td><td>1.00000</td><td>-0.07700</td><td>0.33767</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">SMPTE ST 2065-1:2012 ACES</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(C_{ACES} = C_{linear}\)</td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(C_{linear} = C_{ACES}\)</td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_aces.png" />
+ * <figcaption style="text-align: center;">ACES (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ ACES,
+ /**
+ * <p>{@link ColorSpace.Rgb RGB} color space ACEScg standardized as Academy S-2014-004.</p>
+ * <table summary="Color space definition">
+ * <tr>
+ * <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+ * </tr>
+ * <tr><td>x</td><td>0.713</td><td>0.165</td><td>0.128</td><td>0.32168</td></tr>
+ * <tr><td>y</td><td>0.293</td><td>0.830</td><td>0.044</td><td>0.33767</td></tr>
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Academy S-2014-004 ACEScg</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr>
+ * <tr>
+ * <td>Opto-electronic transfer function</td>
+ * <td colspan="4">\(C_{ACEScg} = C_{linear}\)</td>
+ * </tr>
+ * <tr>
+ * <td>Electro-optical transfer function</td>
+ * <td colspan="4">\(C_{linear} = C_{ACEScg}\)</td>
+ * </tr>
+ * <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr>
+ * </table>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_acescg.png" />
+ * <figcaption style="text-align: center;">ACEScg (orange) vs sRGB (white)</figcaption>
+ * </p>
+ */
+ ACESCG,
+ /**
+ * <p>{@link Model#XYZ XYZ} color space CIE XYZ. This color space assumes standard
+ * illuminant D50 as its white point.</p>
+ * <table summary="Color space definition">
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Generic XYZ</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr>
+ * <tr><td>Range</td><td colspan="4">\([-2.0, 2.0]\)</td></tr>
+ * </table>
+ */
+ CIE_XYZ,
+ /**
+ * <p>{@link Model#LAB Lab} color space CIE L*a*b*. This color space uses CIE XYZ D50
+ * as a profile conversion space.</p>
+ * <table summary="Color space definition">
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Generic L*a*b*</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr>
+ * <tr><td>Range</td><td colspan="4">\(L: [0.0, 100.0], a: [-128, 128], b: [-128, 128]\)</td></tr>
+ * </table>
+ */
+ CIE_LAB
+ // Update the initialization block next to #get(Named) when adding new values
+ }
+
+ /**
+ * <p>A render intent determines how a {@link ColorSpace.Connector connector}
+ * maps colors from one color space to another. The choice of mapping is
+ * important when the source color space has a larger color gamut than the
+ * destination color space.</p>
+ *
+ * @see ColorSpace#connect(ColorSpace, ColorSpace, RenderIntent)
+ */
+ public enum RenderIntent {
+ /**
+ * <p>Compresses the source gamut into the destination gamut.
+ * This render intent affects all colors, inside and outside
+ * of destination gamut. The goal of this render intent is
+ * to preserve the visual relationship between colors.</p>
+ *
+ * <p class="note">This render intent is currently not
+ * implemented and behaves like {@link #RELATIVE}.</p>
+ */
+ PERCEPTUAL,
+ /**
+ * Similar to the {@link #ABSOLUTE} render intent, this render
+ * intent matches the closest color in the destination gamut
+ * but makes adjustments for the destination white point.
+ */
+ RELATIVE,
+ /**
+ * <p>Attempts to maintain the relative saturation of colors
+ * from the source gamut to the destination gamut, to keep
+ * highly saturated colors as saturated as possible.</p>
+ *
+ * <p class="note">This render intent is currently not
+ * implemented and behaves like {@link #RELATIVE}.</p>
+ */
+ SATURATION,
+ /**
+ * Colors that are in the destination gamut are left unchanged.
+ * Colors that fall outside of the destination gamut are mapped
+ * to the closest possible color within the gamut of the destination
+ * color space (they are clipped).
+ */
+ ABSOLUTE
+ }
+
+ /**
+ * {@usesMathJax}
+ *
+ * <p>List of adaptation matrices that can be used for chromatic adaptation
+ * using the von Kries transform. These matrices are used to convert values
+ * in the CIE XYZ space to values in the LMS space (Long Medium Short).</p>
+ *
+ * <p>Given an adaptation matrix \(A\), the conversion from XYZ to
+ * LMS is straightforward:</p>
+ *
+ * $$\left[ \begin{array}{c} L\\ M\\ S \end{array} \right] =
+ * A \left[ \begin{array}{c} X\\ Y\\ Z \end{array} \right]$$
+ *
+ * <p>The complete von Kries transform \(T\) uses a diagonal matrix
+ * noted \(D\) to perform the adaptation in LMS space. In addition
+ * to \(A\) and \(D\), the source white point \(W1\) and the destination
+ * white point \(W2\) must be specified:</p>
+ *
+ * $$\begin{align*}
+ * \left[ \begin{array}{c} L_1\\ M_1\\ S_1 \end{array} \right] &=
+ * A \left[ \begin{array}{c} W1_X\\ W1_Y\\ W1_Z \end{array} \right] \\
+ * \left[ \begin{array}{c} L_2\\ M_2\\ S_2 \end{array} \right] &=
+ * A \left[ \begin{array}{c} W2_X\\ W2_Y\\ W2_Z \end{array} \right] \\
+ * D &= \left[ \begin{matrix} \frac{L_2}{L_1} & 0 & 0 \\
+ * 0 & \frac{M_2}{M_1} & 0 \\
+ * 0 & 0 & \frac{S_2}{S_1} \end{matrix} \right] \\
+ * T &= A^{-1}.D.A
+ * \end{align*}$$
+ *
+ * <p>As an example, the resulting matrix \(T\) can then be used to
+ * perform the chromatic adaptation of sRGB XYZ transform from D65
+ * to D50:</p>
+ *
+ * $$sRGB_{D50} = T.sRGB_{D65}$$
+ *
+ * @see ColorSpace.Connector
+ * @see ColorSpace#connect(ColorSpace, ColorSpace)
+ */
+ public enum Adaptation {
+ /**
+ * Bradford matrix for the von Kries chromatic adaptation transform.
+ */
+ BRADFORD(new float[] {
+ 0.8951f, -0.7502f, 0.0389f,
+ 0.2664f, 1.7135f, -0.0685f,
+ -0.1614f, 0.0367f, 1.0296f
+ }),
+ /**
+ * von Kries matrix for the von Kries chromatic adaptation transform.
+ */
+ VON_KRIES(new float[] {
+ 0.40024f, -0.22630f, 0.00000f,
+ 0.70760f, 1.16532f, 0.00000f,
+ -0.08081f, 0.04570f, 0.91822f
+ });
+
+ final float[] mTransform;
+
+ Adaptation(@NonNull @Size(9) float[] transform) {
+ mTransform = transform;
+ }
+ }
+
+ /**
+ * A color model is required by a {@link ColorSpace} to describe the
+ * way colors can be represented as tuples of numbers. A common color
+ * model is the {@link #RGB RGB} color model which defines a color
+ * as represented by a tuple of 3 numbers (red, green and blue).
+ */
+ public enum Model {
+ /**
+ * The RGB model is a color model with 3 components that
+ * refer to the three additive primiaries: red, green
+ * andd blue.
+ */
+ RGB(3),
+ /**
+ * The XYZ model is a color model with 3 components that
+ * are used to model human color vision on a basic sensory
+ * level.
+ */
+ XYZ(3),
+ /**
+ * The Lab model is a color model with 3 components used
+ * to describe a color space that is more perceptually
+ * uniform than XYZ.
+ */
+ LAB(3),
+ /**
+ * The CMYK model is a color model with 4 components that
+ * refer to four inks used in color printing: cyan, magenta,
+ * yellow and black (or key). CMYK is a subtractive color
+ * model.
+ */
+ CMYK(4);
+
+ private final int mComponentCount;
+
+ Model(@IntRange(from = 1, to = 4) int componentCount) {
+ mComponentCount = componentCount;
+ }
+
+ /**
+ * Returns the number of components for this color model.
+ *
+ * @return An integer between 1 and 4
+ */
+ @IntRange(from = 1, to = 4)
+ public int getComponentCount() {
+ return mComponentCount;
+ }
+ }
+
+ private ColorSpace(
+ @NonNull String name,
+ @NonNull Model model,
+ @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+
+ if (name == null || name.length() < 1) {
+ throw new IllegalArgumentException("The name of a color space cannot be null and " +
+ "must contain at least 1 character");
+ }
+
+ if (model == null) {
+ throw new IllegalArgumentException("A color space must have a model");
+ }
+
+ if (id < MIN_ID || id > MAX_ID) {
+ throw new IllegalArgumentException("The id must be between " +
+ MIN_ID + " and " + MAX_ID);
+ }
+
+ mName = name;
+ mModel = model;
+ mId = id;
+ }
+
+ /**
+ * Returns the name of this color space. The name is never null
+ * and contains always at least 1 character.
+ *
+ * @return A non-null String of length >= 1
+ */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the ID of this color space. Positive IDs match the color
+ * spaces enumerated in {@link Named}. A negative ID indicates a
+ * color space created by calling one of the public constructors.
+ *
+ * @return An integer between {@link #MIN_ID} and {@link #MAX_ID}
+ */
+ @IntRange(from = MIN_ID, to = MAX_ID)
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Return the color model of this color space.
+ *
+ * @return A non-null {@link Model}
+ *
+ * @see Model
+ * @see #getComponentCount()
+ */
+ @NonNull
+ public Model getModel() {
+ return mModel;
+ }
+
+ /**
+ * Returns the number of components that form a color value according
+ * to this color space's color model.
+ *
+ * @return An integer between 1 and 4
+ *
+ * @see Model
+ * @see #getModel()
+ */
+ @IntRange(from = 1, to = 4)
+ public int getComponentCount() {
+ return mModel.getComponentCount();
+ }
+
+ /**
+ * Returns whether this color space is a wide-gamut color space.
+ * An RGB color space is wide-gamut if its gamut entirely contains
+ * the {@link Named#SRGB sRGB} gamut and if the area of its gamut is
+ * 90% of greater than the area of the {@link Named#NTSC_1953 NTSC}
+ * gamut.
+ *
+ * @return True if this color space is a wide-gamut color space,
+ * false otherwise
+ */
+ public abstract boolean isWideGamut();
+
+ /**
+ * <p>Indicates whether this color space is the sRGB color space or
+ * equivalent to the sRGB color space.</p>
+ * <p>A color space is considered sRGB if it meets all the following
+ * conditions:</p>
+ * <ul>
+ * <li>Its color model is {@link Model#RGB}.</li>
+ * <li>
+ * Its primaries are within 1e-3 of the true
+ * {@link Named#SRGB sRGB} primaries.
+ * </li>
+ * <li>
+ * Its white point is withing 1e-3 of the CIE standard
+ * illuminant {@link #ILLUMINANT_D65 D65}.
+ * </li>
+ * <li>Its opto-electronic transfer function is not linear.</li>
+ * <li>Its electro-optical transfer function is not linear.</li>
+ * <li>Its range is \([0..1]\).</li>
+ * </ul>
+ * <p>This method always returns true for {@link Named#SRGB}.</p>
+ *
+ * @return True if this color space is the sRGB color space (or a
+ * close approximation), false otherwise
+ */
+ public boolean isSrgb() {
+ return false;
+ }
+
+ /**
+ * Returns the minimum valid value for the specified component of this
+ * color space's color model.
+ *
+ * @param component The index of the component
+ * @return A floating point value less than {@link #getMaxValue(int)}
+ *
+ * @see #getMaxValue(int)
+ * @see Model#getComponentCount()
+ */
+ public abstract float getMinValue(@IntRange(from = 0, to = 3) int component);
+
+ /**
+ * Returns the maximum valid value for the specified component of this
+ * color space's color model.
+ *
+ * @param component The index of the component
+ * @return A floating point value greater than {@link #getMinValue(int)}
+ *
+ * @see #getMinValue(int)
+ * @see Model#getComponentCount()
+ */
+ public abstract float getMaxValue(@IntRange(from = 0, to = 3) int component);
+
+ /**
+ * <p>Converts a color value from this color space's model to
+ * tristimulus CIE XYZ values. If the color model of this color
+ * space is not {@link Model#RGB RGB}, it is assumed that the
+ * target CIE XYZ space uses a {@link #ILLUMINANT_D50 D50}
+ * standard illuminant.</p>
+ *
+ * <p>This method is a convenience for color spaces with a model
+ * of 3 components ({@link Model#RGB RGB} or {@link Model#LAB}
+ * for instance). With color spaces using fewer or more components,
+ * use {@link #toXyz(float[])} instead</p>.
+ *
+ * @param r The first component of the value to convert from (typically R in RGB)
+ * @param g The second component of the value to convert from (typically G in RGB)
+ * @param b The third component of the value to convert from (typically B in RGB)
+ * @return A new array of 3 floats, containing tristimulus XYZ values
+ *
+ * @see #toXyz(float[])
+ * @see #fromXyz(float, float, float)
+ */
+ @NonNull
+ @Size(3)
+ public float[] toXyz(float r, float g, float b) {
+ return toXyz(new float[] { r, g, b });
+ }
+
+ /**
+ * <p>Converts a color value from this color space's model to
+ * tristimulus CIE XYZ values. If the color model of this color
+ * space is not {@link Model#RGB RGB}, it is assumed that the
+ * target CIE XYZ space uses a {@link #ILLUMINANT_D50 D50}
+ * standard illuminant.</p>
+ *
+ * <p class="note">The specified array's length must be at least
+ * equal to to the number of color components as returned by
+ * {@link Model#getComponentCount()}.</p>
+ *
+ * @param v An array of color components containing the color space's
+ * color value to convert to XYZ, and large enough to hold
+ * the resulting tristimulus XYZ values
+ * @return The array passed in parameter
+ *
+ * @see #toXyz(float, float, float)
+ * @see #fromXyz(float[])
+ */
+ @NonNull
+ @Size(min = 3)
+ public abstract float[] toXyz(@NonNull @Size(min = 3) float[] v);
+
+ /**
+ * <p>Converts tristimulus values from the CIE XYZ space to this
+ * color space's color model.</p>
+ *
+ * @param x The X component of the color value
+ * @param y The Y component of the color value
+ * @param z The Z component of the color value
+ * @return A new array whose size is equal to the number of color
+ * components as returned by {@link Model#getComponentCount()}
+ *
+ * @see #fromXyz(float[])
+ * @see #toXyz(float, float, float)
+ */
+ @NonNull
+ @Size(min = 3)
+ public float[] fromXyz(float x, float y, float z) {
+ float[] xyz = new float[mModel.getComponentCount()];
+ xyz[0] = x;
+ xyz[1] = y;
+ xyz[2] = z;
+ return fromXyz(xyz);
+ }
+
+ /**
+ * <p>Converts tristimulus values from the CIE XYZ space to this color
+ * space's color model. The resulting value is passed back in the specified
+ * array.</p>
+ *
+ * <p class="note>The specified array's length must be at least equal to
+ * to the number of color components as returned by
+ * {@link Model#getComponentCount()}, and its first 3 values must
+ * be the XYZ components to convert from.</p>
+ *
+ * @param v An array of color components containing the XYZ values
+ * to convert from, and large enough to hold the number
+ * of components of this color space's model
+ * @return The array passed in parameter
+ *
+ * @see #fromXyz(float, float, float)
+ * @see #toXyz(float[])
+ */
+ @NonNull
+ @Size(min = 3)
+ public abstract float[] fromXyz(@NonNull @Size(min = 3) float[] v);
+
+ /**
+ * <p>Returns a string representation of the object. This method returns
+ * a string equal to the value of:</p>
+ *
+ * <pre class="prettyprint">
+ * getName() + "(id=" + getId() + ", model=" + getModel() + ")"
+ * </pre>
+ *
+ * <p>For instance, the string representation of the {@link Named#SRGB sRGB}
+ * color space is equal to the following value:</p>
+ *
+ * <pre>
+ * sRGB IEC61966-2.1 (id=0, model=RGB)
+ * </pre>
+ *
+ * @return A string representation of the object
+ */
+ @Override
+ public String toString() {
+ return mName + " (id=" + mId + ", model=" + mModel + ")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ColorSpace that = (ColorSpace) o;
+
+ if (mId != that.mId) return false;
+ //noinspection SimplifiableIfStatement
+ if (!mName.equals(that.mName)) return false;
+ return mModel == that.mModel;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mName.hashCode();
+ result = 31 * result + mModel.hashCode();
+ result = 31 * result + mId;
+ return result;
+ }
+
+ /**
+ * <p>Connects two color spaces to allow conversion from the source color
+ * space to the destination color space. If the source and destination
+ * color spaces do not have the same profile connection space (CIE XYZ
+ * with the same white point), they are chromatically adapted to use the
+ * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
+ *
+ * <p>If the source and destination are the same, an optimized connector
+ * is returned to avoid unnecessary computations and loss of precision.</p>
+ *
+ * <p>Colors are mapped from the source color space to the destination color
+ * space using the {@link RenderIntent#PERCEPTUAL perceptual} render intent.</p>
+ *
+ * @param source The color space to convert colors from
+ * @param destination The color space to convert colors to
+ * @return A non-null connector between the two specified color spaces
+ *
+ * @see #connect(ColorSpace)
+ * @see #connect(ColorSpace, RenderIntent)
+ * @see #connect(ColorSpace, ColorSpace, RenderIntent)
+ */
+ @NonNull
+ public static Connector connect(@NonNull ColorSpace source, @NonNull ColorSpace destination) {
+ return connect(source, destination, RenderIntent.PERCEPTUAL);
+ }
+
+ /**
+ * <p>Connects two color spaces to allow conversion from the source color
+ * space to the destination color space. If the source and destination
+ * color spaces do not have the same profile connection space (CIE XYZ
+ * with the same white point), they are chromatically adapted to use the
+ * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
+ *
+ * <p>If the source and destination are the same, an optimized connector
+ * is returned to avoid unnecessary computations and loss of precision.</p>
+ *
+ * @param source The color space to convert colors from
+ * @param destination The color space to convert colors to
+ * @param intent The render intent to map colors from the source to the destination
+ * @return A non-null connector between the two specified color spaces
+ *
+ * @see #connect(ColorSpace)
+ * @see #connect(ColorSpace, RenderIntent)
+ * @see #connect(ColorSpace, ColorSpace)
+ */
+ @NonNull
+ @SuppressWarnings("ConstantConditions")
+ public static Connector connect(@NonNull ColorSpace source, @NonNull ColorSpace destination,
+ @NonNull RenderIntent intent) {
+ if (source.equals(destination)) return Connector.identity(source);
+
+ if (source.getModel() == Model.RGB && destination.getModel() == Model.RGB) {
+ return new Connector.Rgb((Rgb) source, (Rgb) destination, intent);
+ }
+
+ return new Connector(source, destination, intent);
+ }
+
+ /**
+ * <p>Connects the specified color spaces to sRGB.
+ * If the source color space does not use CIE XYZ D65 as its profile
+ * connection space, the two spaces are chromatically adapted to use the
+ * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
+ *
+ * <p>If the source is the sRGB color space, an optimized connector
+ * is returned to avoid unnecessary computations and loss of precision.</p>
+ *
+ * <p>Colors are mapped from the source color space to the destination color
+ * space using the {@link RenderIntent#PERCEPTUAL perceptual} render intent.</p>
+ *
+ * @param source The color space to convert colors from
+ * @return A non-null connector between the specified color space and sRGB
+ *
+ * @see #connect(ColorSpace, RenderIntent)
+ * @see #connect(ColorSpace, ColorSpace)
+ * @see #connect(ColorSpace, ColorSpace, RenderIntent)
+ */
+ @NonNull
+ public static Connector connect(@NonNull ColorSpace source) {
+ return connect(source, RenderIntent.PERCEPTUAL);
+ }
+
+ /**
+ * <p>Connects the specified color spaces to sRGB.
+ * If the source color space does not use CIE XYZ D65 as its profile
+ * connection space, the two spaces are chromatically adapted to use the
+ * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
+ *
+ * <p>If the source is the sRGB color space, an optimized connector
+ * is returned to avoid unnecessary computations and loss of precision.</p>
+ *
+ * @param source The color space to convert colors from
+ * @param intent The render intent to map colors from the source to the destination
+ * @return A non-null connector between the specified color space and sRGB
+ *
+ * @see #connect(ColorSpace)
+ * @see #connect(ColorSpace, ColorSpace)
+ * @see #connect(ColorSpace, ColorSpace, RenderIntent)
+ */
+ @NonNull
+ public static Connector connect(@NonNull ColorSpace source, @NonNull RenderIntent intent) {
+ if (source.isSrgb()) return Connector.identity(source);
+
+ if (source.getModel() == Model.RGB) {
+ return new Connector.Rgb((Rgb) source, (Rgb) get(Named.SRGB), intent);
+ }
+
+ return new Connector(source, get(Named.SRGB), intent);
+ }
+
+ /**
+ * <p>Performs the chromatic adaptation of a color space from its native
+ * white point to the specified white point.</p>
+ *
+ * <p>The chromatic adaptation is performed using the
+ * {@link Adaptation#BRADFORD} matrix.</p>
+ *
+ * <p class="note">The color space returned by this method always has
+ * an ID of {@link #MIN_ID}.</p>
+ *
+ * @param colorSpace The color space to chromatically adapt
+ * @param whitePoint The new white point
+ * @return A {@link ColorSpace} instance with the same name, primaries,
+ * transfer functions and range as the specified color space
+ *
+ * @see Adaptation
+ * @see #adapt(ColorSpace, float[], Adaptation)
+ */
+ @NonNull
+ public static ColorSpace adapt(@NonNull ColorSpace colorSpace,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint) {
+ return adapt(colorSpace, whitePoint, Adaptation.BRADFORD);
+ }
+
+ /**
+ * <p>Performs the chromatic adaptation of a color space from its native
+ * white point to the specified white point. If the specified color space
+ * does not have an {@link Model#RGB RGB} color model, or if the color
+ * space already has the target white point, the color space is returned
+ * unmodified.</p>
+ *
+ * <p>The chromatic adaptation is performed using the von Kries method
+ * described in the documentation of {@link Adaptation}.</p>
+ *
+ * <p class="note">The color space returned by this method always has
+ * an ID of {@link #MIN_ID}.</p>
+ *
+ * @param colorSpace The color space to chromatically adapt
+ * @param whitePoint The new white point
+ * @param adaptation The adaptation matrix
+ * @return A new color space if the specified color space has an RGB
+ * model and a white point different from the specified white
+ * point; the specified color space otherwise
+ *
+ * @see Adaptation
+ * @see #adapt(ColorSpace, float[])
+ */
+ @NonNull
+ public static ColorSpace adapt(@NonNull ColorSpace colorSpace,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ @NonNull Adaptation adaptation) {
+ if (colorSpace.getModel() == Model.RGB) {
+ ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace;
+ if (compare(rgb.mWhitePoint, whitePoint)) return colorSpace;
+
+ float[] xyz = whitePoint.length == 3 ?
+ Arrays.copyOf(whitePoint, 3) : xyYToXyz(whitePoint);
+ float[] adaptationTransform = chromaticAdaptation(adaptation.mTransform,
+ xyYToXyz(rgb.getWhitePoint()), xyz);
+ float[] transform = mul3x3(adaptationTransform, rgb.mTransform);
+
+ return new ColorSpace.Rgb(rgb, transform, whitePoint);
+ }
+ return colorSpace;
+ }
+
+ /**
+ * <p>Returns an instance of {@link ColorSpace} whose ID matches the specified
+ * ID. If the ID is < 0 or &gt; {@link #MAX_ID}, calling this method is equivalent
+ * to calling <code>get(Named.SRGB)</code>.</p>
+ *
+ * <p>This method always returns the same instance for a given ID.</p>
+ *
+ * <p>This method is thread-safe.</p>
+ *
+ * @param index An integer ID between {@link #MIN_ID} and {@link #MAX_ID}
+ * @return A non-null {@link ColorSpace} instance
+ */
+ @NonNull
+ static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
+ if (index < 0 || index > Named.values().length) {
+ return get(Named.SRGB);
+ }
+ return sNamedColorSpaces[index];
+ }
+
+ /**
+ * <p>Returns an instance of {@link ColorSpace} identified by the specified
+ * name. The list of names provided in the {@link Named} enum gives access
+ * to a variety of common RGB color spaces.</p>
+ *
+ * <p>This method always returns the same instance for a given name.</p>
+ *
+ * <p>This method is thread-safe.</p>
+ *
+ * @param name The name of the color space to get an instance of
+ * @return A non-null {@link ColorSpace} instance
+ */
+ @NonNull
+ public static ColorSpace get(@NonNull Named name) {
+ return sNamedColorSpaces[name.ordinal()];
+ }
+
+ /**
+ * <p>Creates a new {@link Renderer} that can be used to visualize and
+ * debug color spaces. See the documentation of {@link Renderer} for
+ * more information.</p>
+ *
+ * @return A new non-null {@link Renderer} instance
+ *
+ * @see Renderer
+ */
+ @NonNull
+ public static Renderer createRenderer() {
+ return new Renderer();
+ }
+
+ static {
+ sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb(
+ "sRGB IEC61966-2.1",
+ SRGB_PRIMARIES,
+ ILLUMINANT_D65,
+ x -> rcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
+ x -> response(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
+ 0.0f, 1.0f,
+ Named.SRGB.ordinal()
+ );
+ sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
+ "sRGB IEC61966-2.1 (Linear)",
+ SRGB_PRIMARIES,
+ ILLUMINANT_D65,
+ DoubleUnaryOperator.identity(),
+ DoubleUnaryOperator.identity(),
+ 0.0f, 1.0f,
+ Named.LINEAR_SRGB.ordinal()
+ );
+ sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
+ "scRGB-nl IEC 61966-2-2:2003",
+ SRGB_PRIMARIES,
+ ILLUMINANT_D65,
+ x -> absRcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
+ x -> absResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
+ -0.5f, 7.5f,
+ Named.EXTENDED_SRGB.ordinal()
+ );
+ sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
+ "scRGB- IEC 61966-2-2:2003",
+ SRGB_PRIMARIES,
+ ILLUMINANT_D65,
+ DoubleUnaryOperator.identity(),
+ DoubleUnaryOperator.identity(),
+ -0.5f, 7.5f,
+ Named.LINEAR_EXTENDED_SRGB.ordinal()
+ );
+ sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
+ "Rec. ITU-R BT.709-5",
+ new float[] { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
+ ILLUMINANT_D65,
+ x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
+ x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
+ 0.0f, 1.0f,
+ Named.BT709.ordinal()
+ );
+ sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
+ "Rec. ITU-R BT.2020-1",
+ new float[] { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f },
+ ILLUMINANT_D65,
+ x -> rcpResponse(x, 1 / 0.45, 1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145),
+ x -> response(x, 1 / 0.45, 1 / 1.0993, 0.099 / 1.0993, 1 / 4.5, 0.08145),
+ 0.0f, 1.0f,
+ Named.BT2020.ordinal()
+ );
+ sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
+ "SMPTE RP 431-2-2007 DCI (P3)",
+ new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
+ new float[] { 0.314f, 0.351f },
+ x -> Math.pow(x, 1 / 2.6),
+ x -> Math.pow(x, 2.6),
+ 0.0f, 1.0f,
+ Named.DCI_P3.ordinal()
+ );
+ sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb(
+ "Display P3",
+ new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
+ ILLUMINANT_D65,
+ x -> rcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
+ x -> response(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
+ 0.0f, 1.0f,
+ Named.DISPLAY_P3.ordinal()
+ );
+ sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
+ "NTSC (1953)",
+ NTSC_1953_PRIMARIES,
+ ILLUMINANT_C,
+ x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
+ x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
+ 0.0f, 1.0f,
+ Named.NTSC_1953.ordinal()
+ );
+ sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb(
+ "SMPTE-C RGB",
+ new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
+ ILLUMINANT_D65,
+ x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
+ x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
+ 0.0f, 1.0f,
+ Named.SMPTE_C.ordinal()
+ );
+ sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb(
+ "Adobe RGB (1998)",
+ new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
+ ILLUMINANT_D65,
+ x -> Math.pow(x, 1 / 2.2),
+ x -> Math.pow(x, 2.2),
+ 0.0f, 1.0f,
+ Named.ADOBE_RGB.ordinal()
+ );
+ sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb(
+ "ROMM RGB ISO 22028-2:2013",
+ new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
+ ILLUMINANT_D50,
+ x -> rcpResponse(x, 1.8, 1.0, 0.0, 1 / 16.0, 0.031248),
+ x -> response(x, 1.8, 1.0, 0.0, 1 / 16.0, 0.031248),
+ 0.0f, 1.0f,
+ Named.PRO_PHOTO_RGB.ordinal()
+ );
+ sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb(
+ "SMPTE ST 2065-1:2012 ACES",
+ new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
+ ILLUMINANT_D60,
+ DoubleUnaryOperator.identity(),
+ DoubleUnaryOperator.identity(),
+ -65504.0f, 65504.0f,
+ Named.ACES.ordinal()
+ );
+ sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb(
+ "Academy S-2014-004 ACEScg",
+ new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
+ ILLUMINANT_D60,
+ DoubleUnaryOperator.identity(),
+ DoubleUnaryOperator.identity(),
+ -65504.0f, 65504.0f,
+ Named.ACESCG.ordinal()
+ );
+ sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz(
+ "Generic XYZ",
+ Named.CIE_XYZ.ordinal()
+ );
+ sNamedColorSpaces[Named.CIE_LAB.ordinal()] = new ColorSpace.Lab(
+ "Generic L*a*b*",
+ Named.CIE_LAB.ordinal()
+ );
+ }
+
+ // Reciprocal piecewise gamma response
+ private static double rcpResponse(double x, double g,double a, double b, double c, double d) {
+ return x >= d * c ? (Math.pow(x, 1.0 / g) - b) / a : x / c;
+ }
+
+ // Piecewise gamma response
+ private static double response(double x, double g, double a, double b, double c, double d) {
+ return x >= d ? Math.pow(a * x + b, g) : c * x;
+ }
+
+ // Reciprocal piecewise gamma response, encoded as sign(x).f(abs(x)) for color
+ // spaces that allow negative values
+ @SuppressWarnings("SameParameterValue")
+ private static double absRcpResponse(double x, double g, double a, double b, double c, double d) {
+ return Math.copySign(rcpResponse(x < 0.0 ? -x : x, g, a, b, c, d), x);
+ }
+
+ // Piecewise gamma response, encoded as sign(x).f(abs(x)) for color spaces that
+ // allow negative values
+ @SuppressWarnings("SameParameterValue")
+ private static double absResponse(double x, double g, double a, double b, double c, double d) {
+ return Math.copySign(response(x < 0.0 ? -x : x, g, a, b, c, d), x);
+ }
+
+ /**
+ * Compares two arrays of float with a precision of 1e-3.
+ *
+ * @param a The first array to compare
+ * @param b The second array to compare
+ * @return True if the two arrays are equal, false otherwise
+ */
+ private static boolean compare(@NonNull float[] a, @NonNull float[] b) {
+ if (a == b) return true;
+ for (int i = 0; i < a.length; i++) {
+ if (Float.compare(a[i], b[i]) != 0 && Math.abs(a[i] - b[i]) > 1e-3f) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Inverts a 3x3 matrix. This method assumes the matrix is invertible.
+ *
+ * @param m A 3x3 matrix as a non-null array of 9 floats
+ * @return A new array of 9 floats containing the inverse of the input matrix
+ */
+ @NonNull
+ @Size(9)
+ private static float[] inverse3x3(@NonNull @Size(9) float[] m) {
+ float a = m[0];
+ float b = m[3];
+ float c = m[6];
+ float d = m[1];
+ float e = m[4];
+ float f = m[7];
+ float g = m[2];
+ float h = m[5];
+ float i = m[8];
+
+ float A = e * i - f * h;
+ float B = f * g - d * i;
+ float C = d * h - e * g;
+
+ float det = a * A + b * B + c * C;
+
+ float inverted[] = new float[m.length];
+ inverted[0] = A / det;
+ inverted[1] = B / det;
+ inverted[2] = C / det;
+ inverted[3] = (c * h - b * i) / det;
+ inverted[4] = (a * i - c * g) / det;
+ inverted[5] = (b * g - a * h) / det;
+ inverted[6] = (b * f - c * e) / det;
+ inverted[7] = (c * d - a * f) / det;
+ inverted[8] = (a * e - b * d) / det;
+ return inverted;
+ }
+
+ /**
+ * Multiplies two 3x3 matrices, represented as non-null arrays of 9 floats.
+ *
+ * @param lhs 3x3 matrix, as a non-null array of 9 floats
+ * @param rhs 3x3 matrix, as a non-null array of 9 floats
+ * @return A new array of 9 floats containing the result of the multiplication
+ * of rhs by lhs
+ */
+ @NonNull
+ @Size(9)
+ private static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) {
+ float[] r = new float[9];
+ r[0] = lhs[0] * rhs[0] + lhs[3] * rhs[1] + lhs[6] * rhs[2];
+ r[1] = lhs[1] * rhs[0] + lhs[4] * rhs[1] + lhs[7] * rhs[2];
+ r[2] = lhs[2] * rhs[0] + lhs[5] * rhs[1] + lhs[8] * rhs[2];
+ r[3] = lhs[0] * rhs[3] + lhs[3] * rhs[4] + lhs[6] * rhs[5];
+ r[4] = lhs[1] * rhs[3] + lhs[4] * rhs[4] + lhs[7] * rhs[5];
+ r[5] = lhs[2] * rhs[3] + lhs[5] * rhs[4] + lhs[8] * rhs[5];
+ r[6] = lhs[0] * rhs[6] + lhs[3] * rhs[7] + lhs[6] * rhs[8];
+ r[7] = lhs[1] * rhs[6] + lhs[4] * rhs[7] + lhs[7] * rhs[8];
+ r[8] = lhs[2] * rhs[6] + lhs[5] * rhs[7] + lhs[8] * rhs[8];
+ return r;
+ }
+
+ /**
+ * Multiplies a vector of 3 components by a 3x3 matrix and stores the
+ * result in the input vector.
+ *
+ * @param lhs 3x3 matrix, as a non-null array of 9 floats
+ * @param rhs Vector of 3 components, as a non-null array of 3 floats
+ * @return The array of 3 passed as the rhs parameter
+ */
+ @NonNull
+ @Size(min = 3)
+ private static float[] mul3x3Float3(
+ @NonNull @Size(9) float[] lhs, @NonNull @Size(min = 3) float[] rhs) {
+ float r0 = rhs[0];
+ float r1 = rhs[1];
+ float r2 = rhs[2];
+ rhs[0] = lhs[0] * r0 + lhs[3] * r1 + lhs[6] * r2;
+ rhs[1] = lhs[1] * r0 + lhs[4] * r1 + lhs[7] * r2;
+ rhs[2] = lhs[2] * r0 + lhs[5] * r1 + lhs[8] * r2;
+ return rhs;
+ }
+
+ /**
+ * Multiplies a diagonal 3x3 matrix lhs, represented as an array of 3 floats,
+ * by a 3x3 matrix represented as an array of 9 floats.
+ *
+ * @param lhs Diagonal 3x3 matrix, as a non-null array of 3 floats
+ * @param rhs 3x3 matrix, as a non-null array of 9 floats
+ * @return A new array of 9 floats containing the result of the multiplication
+ * of rhs by lhs
+ */
+ @NonNull
+ @Size(9)
+ private static float[] mul3x3Diag(
+ @NonNull @Size(3) float[] lhs, @NonNull @Size(9) float[] rhs) {
+ return new float[] {
+ lhs[0] * rhs[0], lhs[1] * rhs[1], lhs[2] * rhs[2],
+ lhs[0] * rhs[3], lhs[1] * rhs[4], lhs[2] * rhs[5],
+ lhs[0] * rhs[6], lhs[1] * rhs[7], lhs[2] * rhs[8]
+ };
+ }
+
+ /**
+ * Converts a value from CIE xyY to CIE XYZ. Y is assumed to be 1 so the
+ * input xyY array only contains the x and y components.
+ *
+ * @param xyY The xyY value to convert to XYZ, cannot be null, length must be 2
+ * @return A new float array of length 3 containing XYZ values
+ */
+ @NonNull
+ @Size(3)
+ private static float[] xyYToXyz(@NonNull @Size(2) float[] xyY) {
+ return new float[] { xyY[0] / xyY[1], 1.0f, (1 - xyY[0] - xyY[1]) / xyY[1] };
+ }
+
+ /**
+ * <p>Computes the chromatic adaptation transform from the specified
+ * source white point to the specified destination white point.</p>
+ *
+ * <p>The transform is computed using the von Kris method, described
+ * in more details in the documentation of {@link Adaptation}. The
+ * {@link Adaptation} enum provides different matrices that can be
+ * used to perform the adaptation.</p>
+ *
+ * @param matrix The adaptation matrix
+ * @param srcWhitePoint The white point to adapt from, *will be modified*
+ * @param dstWhitePoint The white point to adapt to, *will be modified*
+ * @return A 3x3 matrix as a non-null array of 9 floats
+ */
+ @NonNull
+ @Size(9)
+ private static float[] chromaticAdaptation(@NonNull @Size(9) float[] matrix,
+ @NonNull @Size(3) float[] srcWhitePoint, @NonNull @Size(3) float[] dstWhitePoint) {
+ float[] srcLMS = mul3x3Float3(matrix, srcWhitePoint);
+ float[] dstLMS = mul3x3Float3(matrix, dstWhitePoint);
+ // LMS is a diagonal matrix stored as a float[3]
+ float[] LMS = { dstLMS[0] / srcLMS[0], dstLMS[1] / srcLMS[1], dstLMS[2] / srcLMS[2] };
+ return mul3x3(inverse3x3(matrix), mul3x3Diag(LMS, matrix));
+ }
+
+ /**
+ * Implementation of the CIE XYZ color space. Assumes the white point is D50.
+ */
+ private static final class Xyz extends ColorSpace {
+ private Xyz(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+ super(name, Model.XYZ, id);
+ }
+
+ @Override
+ public boolean isWideGamut() {
+ return true;
+ }
+
+ @Override
+ public float getMinValue(@IntRange(from = 0, to = 3) int component) {
+ return -2.0f;
+ }
+
+ @Override
+ public float getMaxValue(@IntRange(from = 0, to = 3) int component) {
+ return 2.0f;
+ }
+
+ @Override
+ public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
+ v[0] = clamp(v[0]);
+ v[1] = clamp(v[1]);
+ v[2] = clamp(v[2]);
+ return v;
+ }
+
+ @Override
+ public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
+ v[0] = clamp(v[0]);
+ v[1] = clamp(v[1]);
+ v[2] = clamp(v[2]);
+ return v;
+ }
+
+ private static float clamp(float x) {
+ return x < -2.0f ? -2.0f : x > 2.0f ? 2.0f : x;
+ }
+ }
+
+ /**
+ * Implementation of the CIE L*a*b* color space. Its PCS is CIE XYZ
+ * with a white point of D50.
+ */
+ private static final class Lab extends ColorSpace {
+ private static final float A = 216.0f / 24389.0f;
+ private static final float B = 841.0f / 108.0f;
+ private static final float C = 4.0f / 29.0f;
+ private static final float D = 6.0f / 29.0f;
+
+ private Lab(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+ super(name, Model.LAB, id);
+ }
+
+ @Override
+ public boolean isWideGamut() {
+ return true;
+ }
+
+ @Override
+ public float getMinValue(@IntRange(from = 0, to = 3) int component) {
+ return component == 0 ? 0.0f : -128.0f;
+ }
+
+ @Override
+ public float getMaxValue(@IntRange(from = 0, to = 3) int component) {
+ return component == 0 ? 100.0f : 128.0f;
+ }
+
+ @Override
+ public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
+ v[0] = clamp(v[0], 0.0f, 100.0f);
+ v[1] = clamp(v[1], -128.0f, 128.0f);
+ v[2] = clamp(v[2], -128.0f, 128.0f);
+
+ float fy = (v[0] + 16.0f) / 116.0f;
+ float fx = fy + (v[1] * 0.002f);
+ float fz = fy - (v[2] * 0.005f);
+ float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
+ float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
+ float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
+
+ v[0] = X * ILLUMINANT_D50_XYZ[0];
+ v[1] = Y * ILLUMINANT_D50_XYZ[1];
+ v[2] = Z * ILLUMINANT_D50_XYZ[2];
+
+ return v;
+ }
+
+ @Override
+ public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
+ float X = v[0] / ILLUMINANT_D50_XYZ[0];
+ float Y = v[1] / ILLUMINANT_D50_XYZ[1];
+ float Z = v[2] / ILLUMINANT_D50_XYZ[2];
+
+ float fx = X > A ? (float) Math.pow(X, 1.0 / 3.0) : B * X + C;
+ float fy = Y > A ? (float) Math.pow(Y, 1.0 / 3.0) : B * Y + C;
+ float fz = Z > A ? (float) Math.pow(Z, 1.0 / 3.0) : B * Z + C;
+
+ float L = 116.0f * fy - 16.0f;
+ float a = 500.0f * (fx - fy);
+ float b = 200.0f * (fy - fz);
+
+ v[0] = clamp(L, 0.0f, 100.0f);
+ v[1] = clamp(a, -128.0f, 128.0f);
+ v[2] = clamp(b, -128.0f, 128.0f);
+
+ return v;
+ }
+
+ private static float clamp(float x, float min, float max) {
+ return x < min ? min : x > max ? max : x;
+ }
+ }
+
+ /**
+ * {@usesMathJax}
+ *
+ * <p>An RGB color space is an additive color space using the
+ * {@link Model#RGB RGB} color model (a color is therefore represented
+ * by a tuple of 3 numbers).</p>
+ *
+ * <p>A specific RGB color space is defined by the following properties:</p>
+ * <ul>
+ * <li>Three chromaticities of the red, green and blue primaries, which
+ * define the gamut of the color space.</li>
+ * <li>A white point chromaticity that defines the stimulus to which
+ * color space values are normalized (also just called "white").</li>
+ * <li>An opto-electronic transfer function, also called opto-electronic
+ * conversion function or often, and approximately, gamma function.</li>
+ * <li>An electro-optical transfer function, also called electo-optical
+ * conversion function or often, and approximately, gamma function.</li>
+ * <li>A range of valid RGB values (most commonly \([0..1]\)).</li>
+ * </ul>
+ *
+ * <p>The most commonly used RGB color space is {@link Named#SRGB sRGB}.</p>
+ *
+ * <h3>Primaries and white point chromaticities</h3>
+ * <p>In this implementation, the chromaticity of the primaries and the white
+ * point of an RGB color space is defined in the CIE xyY color space. This
+ * color space separates the chromaticity of a color, the x and y components,
+ * and its luminance, the Y component. Since the primaries and the white
+ * point have full brightness, the Y component is assumed to be 1 and only
+ * the x and y components are needed to encode them.</p>
+ * <p>For convenience, this implementation also allows to define the
+ * primaries and white point in the CIE XYZ space. The tristimulus XYZ values
+ * are internally converted to xyY.</p>
+ *
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_srgb.png" />
+ * <figcaption style="text-align: center;">sRGB primaries and white point</figcaption>
+ * </p>
+ *
+ * <h3>Transfer functions</h3>
+ * <p>A transfer function is a color component conversion function, defined as
+ * a single variable, monotonic mathematical function. It is applied to each
+ * individual component of a color. They are used to perform the mapping
+ * between linear tristimulus values and non-linear electronic signal value.</p>
+ * <p>The <em>opto-electronic transfer function</em> (OETF or OECF) encodes
+ * tristimulus values in a scene to a non-linear electronic signal value.
+ * An OETF is often expressed as a power function with an exponent between
+ * 0.38 and 0.55 (the reciprocal of 1.8 to 2.6).</p>
+ * <p>The <em>electro-optical transfer function</em> (EOTF or EOCF) decodes
+ * a non-linear electronic signal value to a tristimulus value at the display.
+ * An EOTF is often expressed as a power function with an exponent between
+ * 1.8 and 2.6.</p>
+ * <p>Transfer functions are used as a compression scheme. For instance,
+ * linear sRGB values would normally require 11 to 12 bits of precision to
+ * store all values that can be perceived by the human eye. When encoding
+ * sRGB values using the appropriate OETF (see {@link Named#SRGB sRGB} for
+ * an exact mathematical description of that OETF), the values can be
+ * compressed to only 8 bits precision.</p>
+ * <p>When manipulating RGB values, particularly sRGB values, it is safe
+ * to assume that these values have been encoded with the appropriate
+ * OETF (unless noted otherwise). Encoded values are often said to be in
+ * "gamma space". They are therefore defined in a non-linear space. This
+ * in turns means that any linear operation applied to these values is
+ * going to yield mathematically incorrect results (any linear interpolation
+ * such as gradient generation for instance, most image processing functions
+ * such as blurs, etc.).</p>
+ * <p>To properly process encoded RGB values you must first apply the
+ * EOTF to decode the value into linear space. After processing, the RGB
+ * value must be encoded back to non-linear ("gamma") space. Here is a
+ * formal description of the process, where \(f\) is the processing
+ * function to apply:</p>
+ *
+ * $$RGB_{out} = OETF(f(EOTF(RGB_{in})))$$
+ *
+ * <p class="note">Some RGB color spaces, such as {@link Named#ACES} and
+ * {@link Named#LINEAR_EXTENDED_SRGB scRGB}, are said to be linear because
+ * their transfer functions are the identity function: \(f(x) = x\).
+ * If the source and/or destination are known to be linear, it is not
+ * necessary to invoke the transfer functions.</p>
+ *
+ * <h3>Range</h3>
+ * <p>Most RGB color spaces allow RGB values in the range \([0..1]\). There
+ * are however a few RGB color spaces that allow much larger ranges. For
+ * instance, {@link Named#EXTENDED_SRGB scRGB} is used to manipulate the
+ * range \([-0.5..7.5]\) while {@link Named#ACES ACES} can be used throughout
+ * the range \([-65504, 65504]\).</p>
+ *
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
+ * <figcaption style="text-align: center;">Extended sRGB and its large range</figcaption>
+ * </p>
+ *
+ * <h3>Converting between RGB color spaces</h3>
+ * <p>Conversion between two color spaces is achieved by using an intermediate
+ * color space called the profile connection space (PCS). The PCS used by
+ * this implementation is CIE XYZ. The conversion operation is defined
+ * as such:</p>
+ *
+ * $$RGB_{out} = OETF(T_{dst}^{-1} \cdot T_{src} \cdot EOTF(RGB_{in}))$$
+ *
+ * <p>Where \(T_{src}\) is the {@link #getTransform() RGB to XYZ transform}
+ * of the source color space and \(T_{dst}^{-1}\) the {@link #getInverseTransform()
+ * XYZ to RGB transform} of the destination color space.</p>
+ * <p>Many RGB color spaces commonly used with electronic devices use the
+ * standard illuminant {@link #ILLUMINANT_D65 D65}. Care must be take however
+ * when converting between two RGB color spaces if their white points do not
+ * match. This can be achieved by either calling
+ * {@link #adapt(ColorSpace, float[])} to adapt one or both color spaces to
+ * a single common white point. This can be achieved automatically by calling
+ * {@link ColorSpace#connect(ColorSpace, ColorSpace)}, which also handles
+ * non-RGB color spaces.</p>
+ * <p>To learn more about the white point adaptation process, refer to the
+ * documentation of {@link Adaptation}.</p>
+ */
+ public static class Rgb extends ColorSpace {
+ @NonNull private final float[] mWhitePoint;
+ @NonNull private final float[] mPrimaries;
+ @NonNull private final float[] mTransform;
+ @NonNull private final float[] mInverseTransform;
+
+ @NonNull private final boolean mIsWideGamut;
+ @NonNull private final boolean mIsSrgb;
+
+ @NonNull private final DoubleUnaryOperator mOetf;
+ @NonNull private final DoubleUnaryOperator mEotf;
+ @NonNull private final DoubleUnaryOperator mClampedOetf;
+ @NonNull private final DoubleUnaryOperator mClampedEotf;
+
+ private final float mMin;
+ private final float mMax;
+
+ /**
+ * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
+ * The transform matrix must convert from the RGB space to the profile connection
+ * space CIE XYZ.</p>
+ *
+ * <p class="note">The range of the color space is imposed to be \([0..1]\).</p>
+ *
+ * @param name Name of the color space, cannot be null, its length must be >= 1
+ * @param toXYZ 3x3 column-major transform matrix from RGB to the profile
+ * connection space CIE XYZ as an array of 9 floats, cannot be null
+ * @param oetf Opto-electronic transfer function, cannot be null
+ * @param eotf Electro-optical transfer function, cannot be null
+ *
+ * @throws IllegalArgumentException If any of the following conditions is met:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>The OETF is null or the EOTF is null.</li>
+ * <li>The minimum valid value is >= the maximum valid value.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ public Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(9) float[] toXYZ,
+ @NonNull DoubleUnaryOperator oetf,
+ @NonNull DoubleUnaryOperator eotf) {
+ this(name, computePrimaries(toXYZ, eotf), computeWhitePoint(toXYZ, eotf),
+ oetf, eotf, 0.0f, 1.0f, MIN_ID);
+ }
+
+ /**
+ * <p>Creates a new RGB color space using a specified set of primaries
+ * and a specified white point.</p>
+ *
+ * <p>The primaries and white point can be specified in the CIE xyY space
+ * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
+ *
+ * <table summary="Parameters length">
+ * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
+ * <tr><td>xyY</td><td>6</td><td>2</td></tr>
+ * <tr><td>XYZ</td><td>9</td><td>3</td></tr>
+ * </table>
+ *
+ * <p>When the primaries and/or white point are specified in xyY, the Y component
+ * does not need to be specified and is assumed to be 1.0. Only the xy components
+ * are required.</p>
+ *
+ * <p class="note">The ID, areturned by {@link #getId()}, of an object created by
+ * this constructor is always {@link #MIN_ID}.</p>
+ *
+ * @param name Name of the color space, cannot be null, its length must be >= 1
+ * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
+ * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
+ * @param oetf Opto-electronic transfer function, cannot be null
+ * @param eotf Electro-optical transfer function, cannot be null
+ * @param min The minimum valid value in this color space's RGB range
+ * @param max The maximum valid value in this color space's RGB range
+ *
+ * @throws IllegalArgumentException <p>If any of the following conditions is met:</p>
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>The primaries array is null or has a length that is neither 6 or 9.</li>
+ * <li>The white point array is null or has a length that is neither 2 or 3.</li>
+ * <li>The OETF is null or the EOTF is null.</li>
+ * <li>The minimum valid value is >= the maximum valid value.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ public Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(min = 6, max = 9) float[] primaries,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ @NonNull DoubleUnaryOperator oetf,
+ @NonNull DoubleUnaryOperator eotf,
+ float min,
+ float max) {
+ this(name, primaries, whitePoint, oetf, eotf, min, max, MIN_ID);
+ }
+
+ /**
+ * <p>Creates a new RGB color space using a specified set of primaries
+ * and a specified white point.</p>
+ *
+ * <p>The primaries and white point can be specified in the CIE xyY space
+ * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
+ *
+ * <table summary="Parameters length">
+ * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
+ * <tr><td>xyY</td><td>6</td><td>2</td></tr>
+ * <tr><td>XYZ</td><td>9</td><td>3</td></tr>
+ * </table>
+ *
+ * <p>When the primaries and/or white point are specified in xyY, the Y component
+ * does not need to be specified and is assumed to be 1.0. Only the xy components
+ * are required.</p>
+ *
+ * @param name Name of the color space, cannot be null, its length must be >= 1
+ * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
+ * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
+ * @param oetf Opto-electronic transfer function, cannot be null
+ * @param eotf Electro-optical transfer function, cannot be null
+ * @param min The minimum valid value in this color space's RGB range
+ * @param max The maximum valid value in this color space's RGB range
+ * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID}
+ *
+ * @throws IllegalArgumentException If any of the following conditions is met:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>The primaries array is null or has a length that is neither 6 or 9.</li>
+ * <li>The white point array is null or has a length that is neither 2 or 3.</li>
+ * <li>The OETF is null or the EOTF is null.</li>
+ * <li>The minimum valid value is >= the maximum valid value.</li>
+ * <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ private Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(min = 6, max = 9) float[] primaries,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ @NonNull DoubleUnaryOperator oetf,
+ @NonNull DoubleUnaryOperator eotf,
+ float min,
+ float max,
+ @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+
+ super(name, Model.RGB, id);
+
+ if (primaries == null || (primaries.length != 6 && primaries.length != 9)) {
+ throw new IllegalArgumentException("The color space's primaries must be " +
+ "defined as an array of 6 floats in xyY or 9 floats in XYZ");
+ }
+
+ if (whitePoint == null || (whitePoint.length != 2 && whitePoint.length != 3)) {
+ throw new IllegalArgumentException("The color space's white point must be " +
+ "defined as an array of 2 floats in xyY or 3 float in XYZ");
+ }
+
+ if (oetf == null || eotf == null) {
+ throw new IllegalArgumentException("The transfer functions of a color space " +
+ "cannot be null");
+ }
+
+ if (min >= max) {
+ throw new IllegalArgumentException("Invalid range: min=" + min + ", max=" + max +
+ "; min must be strictly < max");
+ }
+
+ mWhitePoint = xyWhitePoint(whitePoint);
+ mPrimaries = xyPrimaries(primaries);
+
+ mTransform = computeXYZMatrix(mPrimaries, mWhitePoint);
+ mInverseTransform = inverse3x3(mTransform);
+
+ mOetf = oetf;
+ mEotf = eotf;
+
+ mMin = min;
+ mMax = max;
+
+ DoubleUnaryOperator clamp = this::clamp;
+ mClampedOetf = oetf.andThen(clamp);
+ mClampedEotf = clamp.andThen(eotf);
+
+ // A color space is wide-gamut if its area is >90% of NTSC 1953 and
+ // if it entirely contains the Color space definition in xyY
+ mIsWideGamut = isWideGamut(mPrimaries, min, max);
+ mIsSrgb = isSrgb(mPrimaries, mWhitePoint, oetf, eotf, min, max, id);
+ }
+
+ /**
+ * Creates a copy of the specified color space with a new transform.
+ *
+ * @param colorSpace The color space to create a copy of
+ */
+ private Rgb(Rgb colorSpace,
+ @NonNull @Size(9) float[] transform,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint) {
+ super(colorSpace.getName(), Model.RGB, -1);
+
+ mWhitePoint = xyWhitePoint(whitePoint);
+ mPrimaries = colorSpace.mPrimaries;
+
+ mTransform = transform;
+ mInverseTransform = inverse3x3(transform);
+
+ mMin = colorSpace.mMin;
+ mMax = colorSpace.mMax;
+
+ mOetf = colorSpace.mOetf;
+ mEotf = colorSpace.mEotf;
+
+ mClampedOetf = colorSpace.mClampedOetf;
+ mClampedEotf = colorSpace.mClampedEotf;
+
+ mIsWideGamut = colorSpace.mIsWideGamut;
+ mIsSrgb = colorSpace.mIsSrgb;
+ }
+
+ /**
+ * Copies the non-adapted CIE xyY white point of this color space in
+ * specified array. The Y component is assumed to be 1 and is therefore
+ * not copied into the destination. The x and y components are written
+ * in the array at positions 0 and 1 respectively.
+ *
+ * @param whitePoint The destination array, cannot be null, its length
+ * must be >= 2
+ *
+ * @return The destination array passed as a parameter
+ *
+ * @see #getWhitePoint(float[])
+ */
+ @NonNull
+ @Size(min = 2)
+ public float[] getWhitePoint(@NonNull @Size(min = 2) float[] whitePoint) {
+ whitePoint[0] = mWhitePoint[0];
+ whitePoint[1] = mWhitePoint[1];
+ return whitePoint;
+ }
+
+ /**
+ * Returns the non-adapted CIE xyY white point of this color space as
+ * a new array of 2 floats. The Y component is assumed to be 1 and is
+ * therefore not copied into the destination. The x and y components
+ * are written in the array at positions 0 and 1 respectively.
+ *
+ * @return A new non-null array of 2 floats
+ *
+ * @see #getWhitePoint()
+ */
+ @NonNull
+ @Size(2)
+ public float[] getWhitePoint() {
+ return Arrays.copyOf(mWhitePoint, mWhitePoint.length);
+ }
+
+ /**
+ * Copies the primaries of this color space in specified array. The Y
+ * component is assumed to be 1 and is therefore not copied into the
+ * destination. The x and y components of the first primary are written
+ * in the array at positions 0 and 1 respectively.
+ *
+ * @param primaries The destination array, cannot be null, its length
+ * must be >= 6
+ *
+ * @return The destination array passed as a parameter
+ *
+ * @see #getPrimaries(float[])
+ */
+ @NonNull
+ @Size(min = 6)
+ public float[] getPrimaries(@NonNull @Size(min = 6) float[] primaries) {
+ System.arraycopy(mPrimaries, 0, primaries, 0, mPrimaries.length);
+ return primaries;
+ }
+
+ /**
+ * Returns the primaries of this color space as a new array of 6 floats.
+ * The Y component is assumed to be 1 and is therefore not copied into
+ * the destination. The x and y components of the first primary are
+ * written in the array at positions 0 and 1 respectively.
+ *
+ * @return A new non-null array of 2 floats
+ *
+ * @see #getWhitePoint()
+ */
+ @NonNull
+ @Size(6)
+ public float[] getPrimaries() {
+ return Arrays.copyOf(mPrimaries, mPrimaries.length);
+ }
+
+ /**
+ * <p>Copies the transform of this color space in specified array. The
+ * transform is used to convert from RGB to XYZ (with the same white
+ * point as this color space). To connect color spaces, you must first
+ * {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them to the
+ * same white point.</p>
+ * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
+ * to convert between color spaces.</p>
+ *
+ * @param transform The destination array, cannot be null, its length
+ * must be >= 9
+ *
+ * @return The destination array passed as a parameter
+ *
+ * @see #getInverseTransform()
+ */
+ @NonNull
+ @Size(min = 9)
+ public float[] getTransform(@NonNull @Size(min = 9) float[] transform) {
+ System.arraycopy(mTransform, 0, transform, 0, mTransform.length);
+ return transform;
+ }
+
+ /**
+ * <p>Returns the transform of this color space as a new array. The
+ * transform is used to convert from RGB to XYZ (with the same white
+ * point as this color space). To connect color spaces, you must first
+ * {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them to the
+ * same white point.</p>
+ * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
+ * to convert between color spaces.</p>
+ *
+ * @return A new array of 9 floats
+ *
+ * @see #getInverseTransform(float[])
+ */
+ @NonNull
+ @Size(9)
+ public float[] getTransform() {
+ return Arrays.copyOf(mTransform, mTransform.length);
+ }
+
+ /**
+ * <p>Copies the inverse transform of this color space in specified array.
+ * The inverse transform is used to convert from XYZ to RGB (with the
+ * same white point as this color space). To connect color spaces, you
+ * must first {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them
+ * to the same white point.</p>
+ * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
+ * to convert between color spaces.</p>
+ *
+ * @param inverseTransform The destination array, cannot be null, its length
+ * must be >= 9
+ *
+ * @return The destination array passed as a parameter
+ *
+ * @see #getTransform()
+ */
+ @NonNull
+ @Size(min = 9)
+ public float[] getInverseTransform(@NonNull @Size(min = 9) float[] inverseTransform) {
+ System.arraycopy(mInverseTransform, 0, inverseTransform, 0, mInverseTransform.length);
+ return inverseTransform;
+ }
+
+ /**
+ * <p>Returns the inverse transform of this color space as a new array.
+ * The inverse transform is used to convert from XYZ to RGB (with the
+ * same white point as this color space). To connect color spaces, you
+ * must first {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them
+ * to the same white point.</p>
+ * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
+ * to convert between color spaces.</p>
+ *
+ * @return A new array of 9 floats
+ *
+ * @see #getTransform(float[])
+ */
+ @NonNull
+ @Size(9)
+ public float[] getInverseTransform() {
+ return Arrays.copyOf(mInverseTransform, mInverseTransform.length);
+ }
+
+ /**
+ * <p>Returns the opto-electronic transfer function (OETF) of this color space.
+ * The inverse function is the electro-optical transfer function (EOTF) returned
+ * by {@link #getEotf()}. These functions are defined to satisfy the following
+ * equality for \(x \in [0..1]\):</p>
+ *
+ * $$OETF(EOTF(x)) = EOTF(OETF(x)) = x$$
+ *
+ * <p>For RGB colors, this function can be used to convert from linear space
+ * to "gamma space" (gamma encoded). The terms gamma space and gamma encoded
+ * are frequently used because many OETFs can be closely approximated using
+ * a simple power function of the form \(x^{\frac{1}{\gamma}}\) (the
+ * approximation of the {@link Named#SRGB sRGB} EOTF uses \(\gamma=2.2\)
+ * for instance).</p>
+ *
+ * @return A transfer function that converts from linear space to "gamma space"
+ *
+ * @see #getEotf()
+ */
+ @NonNull
+ public DoubleUnaryOperator getOetf() {
+ return mOetf;
+ }
+
+ /**
+ * <p>Returns the electro-optical transfer function (EOTF) of this color space.
+ * The inverse function is the opto-electronic transfer function (OETF)
+ * returned by {@link #getOetf()}. These functions are defined to satisfy the
+ * following equality for \(x \in [0..1]\):</p>
+ *
+ * $$OETF(EOTF(x)) = EOTF(OETF(x)) = x$$
+ *
+ * <p>For RGB colors, this function can be used to convert from "gamma space"
+ * (gamma encoded) to linear space. The terms gamma space and gamma encoded
+ * are frequently used because many EOTFs can be closely approximated using
+ * a simple power function of the form \(x^\gamma\) (the approximation of the
+ * {@link Named#SRGB sRGB} EOTF uses \(\gamma=2.2\) for instance).</p>
+ *
+ * @return A transfer function that converts from "gamma space" to linear space
+ *
+ * @see #getOetf()
+ */
+ @NonNull
+ public DoubleUnaryOperator getEotf() {
+ return mEotf;
+ }
+
+ @Override
+ public boolean isSrgb() {
+ return mIsSrgb;
+ }
+
+ @Override
+ public boolean isWideGamut() {
+ return mIsWideGamut;
+ }
+
+ @Override
+ public float getMinValue(int component) {
+ return mMin;
+ }
+
+ @Override
+ public float getMaxValue(int component) {
+ return mMax;
+ }
+
+ /**
+ * <p>Decodes an RGB value to linear space. This is achieved by
+ * applying this color space's electro-optical transfer function
+ * to the supplied values.</p>
+ *
+ * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
+ * more information about transfer functions and their use for
+ * encoding and decoding RGB values.</p>
+ *
+ * @param r The red component to decode to linear space
+ * @param g The green component to decode to linear space
+ * @param b The blue component to decode to linear space
+ * @return A new array of 3 floats containing linear RGB values
+ *
+ * @see #toLinear(float[])
+ * @see #fromLinear(float, float, float)
+ */
+ @NonNull
+ @Size(3)
+ public float[] toLinear(float r, float g, float b) {
+ return toLinear(new float[] { r, g, b });
+ }
+
+ /**
+ * <p>Decodes an RGB value to linear space. This is achieved by
+ * applying this color space's electro-optical transfer function
+ * to the first 3 values of the supplied array. The result is
+ * stored back in the input array.</p>
+ *
+ * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
+ * more information about transfer functions and their use for
+ * encoding and decoding RGB values.</p>
+ *
+ * @param v A non-null array of non-linear RGB values, its length
+ * must be at least 3
+ * @return The specified array
+ *
+ * @see #toLinear(float, float, float)
+ * @see #fromLinear(float[])
+ */
+ @NonNull
+ @Size(min = 3)
+ public float[] toLinear(@NonNull @Size(min = 3) float[] v) {
+ v[0] = (float) mClampedEotf.applyAsDouble(v[0]);
+ v[1] = (float) mClampedEotf.applyAsDouble(v[1]);
+ v[2] = (float) mClampedEotf.applyAsDouble(v[2]);
+ return v;
+ }
+
+ /**
+ * <p>Encodes an RGB value from linear space to this color space's
+ * "gamma space". This is achieved by applying this color space's
+ * opto-electronic transfer function to the supplied values.</p>
+ *
+ * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
+ * more information about transfer functions and their use for
+ * encoding and decoding RGB values.</p>
+ *
+ * @param r The red component to encode from linear space
+ * @param g The green component to encode from linear space
+ * @param b The blue component to encode from linear space
+ * @return A new array of 3 floats containing non-linear RGB values
+ *
+ * @see #fromLinear(float[])
+ * @see #toLinear(float, float, float)
+ */
+ @NonNull
+ @Size(3)
+ public float[] fromLinear(float r, float g, float b) {
+ return fromLinear(new float[] { r, g, b });
+ }
+
+ /**
+ * <p>Encodes an RGB value from linear space to this color space's
+ * "gamma space". This is achieved by applying this color space's
+ * opto-electronic transfer function to the first 3 values of the
+ * supplied array. The result is stored back in the input array.</p>
+ *
+ * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
+ * more information about transfer functions and their use for
+ * encoding and decoding RGB values.</p>
+ *
+ * @param v A non-null array of linear RGB values, its length
+ * must be at least 3
+ * @return A new array of 3 floats containing non-linear RGB values
+ *
+ * @see #fromLinear(float[])
+ * @see #toLinear(float, float, float)
+ */
+ @NonNull
+ @Size(min = 3)
+ public float[] fromLinear(@NonNull @Size(min = 3) float[] v) {
+ v[0] = (float) mClampedOetf.applyAsDouble(v[0]);
+ v[1] = (float) mClampedOetf.applyAsDouble(v[1]);
+ v[2] = (float) mClampedOetf.applyAsDouble(v[2]);
+ return v;
+ }
+
+ @Override
+ @NonNull
+ @Size(min = 3)
+ public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
+ v[0] = (float) mClampedEotf.applyAsDouble(v[0]);
+ v[1] = (float) mClampedEotf.applyAsDouble(v[1]);
+ v[2] = (float) mClampedEotf.applyAsDouble(v[2]);
+ return mul3x3Float3(mTransform, v);
+ }
+
+ @Override
+ @NonNull
+ @Size(min = 3)
+ public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
+ mul3x3Float3(mInverseTransform, v);
+ v[0] = (float) mClampedOetf.applyAsDouble(v[0]);
+ v[1] = (float) mClampedOetf.applyAsDouble(v[1]);
+ v[2] = (float) mClampedOetf.applyAsDouble(v[2]);
+ return v;
+ }
+
+ private double clamp(double x) {
+ return x < mMin ? mMin : x > mMax ? mMax : x;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ Rgb rgb = (Rgb) o;
+
+ if (Float.compare(rgb.mMin, mMin) != 0) return false;
+ if (Float.compare(rgb.mMax, mMax) != 0) return false;
+ if (!Arrays.equals(mWhitePoint, rgb.mWhitePoint)) return false;
+ if (!Arrays.equals(mPrimaries, rgb.mPrimaries)) return false;
+ //noinspection SimplifiableIfStatement
+ if (!mOetf.equals(rgb.mOetf)) return false;
+ return mEotf.equals(rgb.mEotf);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Arrays.hashCode(mWhitePoint);
+ result = 31 * result + Arrays.hashCode(mPrimaries);
+ result = 31 * result + mOetf.hashCode();
+ result = 31 * result + mEotf.hashCode();
+ result = 31 * result + (mMin != +0.0f ? Float.floatToIntBits(mMin) : 0);
+ result = 31 * result + (mMax != +0.0f ? Float.floatToIntBits(mMax) : 0);
+ return result;
+ }
+
+ /**
+ * Computes whether a color space is the sRGB color space or at least
+ * a close approximation.
+ *
+ * @param primaries The set of RGB primaries in xyY as an array of 6 floats
+ * @param whitePoint The white point in xyY as an array of 2 floats
+ * @param OETF The opto-electronic transfer function
+ * @param EOTF The electro-optical transfer function
+ * @param min The minimum value of the color space's range
+ * @param max The minimum value of the color space's range
+ * @param id The ID of the color space
+ * @return True if the color space can be considered as the sRGB color space
+ *
+ * @see #isSrgb()
+ */
+ @SuppressWarnings("RedundantIfStatement")
+ private static boolean isSrgb(
+ @NonNull @Size(6) float[] primaries,
+ @NonNull @Size(2) float[] whitePoint,
+ @NonNull DoubleUnaryOperator OETF,
+ @NonNull DoubleUnaryOperator EOTF,
+ float min,
+ float max,
+ @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+ if (id == 0) return true;
+ if (!compare(primaries, SRGB_PRIMARIES)) {
+ return false;
+ }
+ if (!compare(whitePoint, ILLUMINANT_D65)) {
+ return false;
+ }
+ if (OETF.applyAsDouble(0.5) < 0.5001) return false;
+ if (EOTF.applyAsDouble(0.5) > 0.5001) return false;
+ if (min != 0.0f) return false;
+ if (max != 1.0f) return false;
+ return true;
+ }
+
+ /**
+ * Computes whether the specified CIE xyY or XYZ primaries (with Y set to 1) form
+ * a wide color gamut. A color gamut is considered wide if its area is &gt; 90%
+ * of the area of NTSC 1953 and if it contains the sRGB color gamut entirely.
+ * If the conditions above are not met, the color space is considered as having
+ * a wide color gamut if its range is larger than [0..1].
+ *
+ * @param primaries RGB primaries in CIE xyY as an array of 6 floats
+ * @param min The minimum value of the color space's range
+ * @param max The minimum value of the color space's range
+ * @return True if the color space has a wide gamut, false otherwise
+ *
+ * @see #isWideGamut()
+ * @see #area(float[])
+ */
+ private static boolean isWideGamut(@NonNull @Size(6) float[] primaries,
+ float min, float max) {
+ return (area(primaries) / area(NTSC_1953_PRIMARIES) > 0.9f &&
+ contains(primaries, SRGB_PRIMARIES)) || (min < 0.0f && max > 1.0f);
+ }
+
+ /**
+ * Computes the area of the triangle represented by a set of RGB primaries
+ * in the CIE xyY space.
+ *
+ * @param primaries The triangle's vertices, as RGB primaries in an array of 6 floats
+ * @return The area of the triangle
+ *
+ * @see #isWideGamut(float[], float, float)
+ */
+ private static float area(@NonNull @Size(6) float[] primaries) {
+ float Rx = primaries[0];
+ float Ry = primaries[1];
+ float Gx = primaries[2];
+ float Gy = primaries[3];
+ float Bx = primaries[4];
+ float By = primaries[5];
+ float det = Rx * Gy + Ry * Bx + Gx * By - Gy * Bx - Ry * Gx - Rx * By;
+ float r = 0.5f * det;
+ return r < 0.0f ? -r : r;
+ }
+
+ /**
+ * Computes the cross product of two 2D vectors.
+ *
+ * @param ax The x coordinate of the first vector
+ * @param ay The y coordinate of the first vector
+ * @param bx The x coordinate of the second vector
+ * @param by The y coordinate of the second vector
+ * @return The result of a x b
+ */
+ private static float cross(float ax, float ay, float bx, float by) {
+ return ax * by - ay * bx;
+ }
+
+ /**
+ * Decides whether a 2D triangle, identified by the 6 coordinates of its
+ * 3 vertices, is contained within another 2D triangle, also identified
+ * by the 6 coordinates of its 3 vertices.
+ *
+ * In the illustration below, we want to test whether the RGB triangle
+ * is contained within the triangle XYZ formed by the 3 vertices at
+ * the "+" locations.
+ *
+ * Y .
+ * . + .
+ * . ..
+ * . .
+ * . .
+ * . G
+ * *
+ * * *
+ * ** *
+ * * **
+ * * *
+ * ** *
+ * * *
+ * * *
+ * ** *
+ * * *
+ * * **
+ * ** * R ...
+ * * * .....
+ * * ***** ..
+ * ** ************ . +
+ * B * ************ . X
+ * ......***** .
+ * ...... . .
+ * ..
+ * + .
+ * Z .
+ *
+ * RGB is contained within XYZ if all the following conditions are true
+ * (with "x" the cross product operator):
+ *
+ * --> -->
+ * GR x RX >= 0
+ * --> -->
+ * RX x BR >= 0
+ * --> -->
+ * RG x GY >= 0
+ * --> -->
+ * GY x RG >= 0
+ * --> -->
+ * RB x BZ >= 0
+ * --> -->
+ * BZ x GB >= 0
+ *
+ * @param p1 The enclosing triangle
+ * @param p2 The enclosed triangle
+ * @return True if the triangle p1 contains the triangle p2
+ *
+ * @see #isWideGamut(float[], float, float)
+ */
+ @SuppressWarnings("RedundantIfStatement")
+ private static boolean contains(@NonNull @Size(6) float[] p1, @NonNull @Size(6) float[] p2) {
+ // Translate the vertices p1 in the coordinates system
+ // with the vertices p2 as the origin
+ float[] p0 = new float[] {
+ p1[0] - p2[0], p1[1] - p2[1],
+ p1[2] - p2[2], p1[3] - p2[3],
+ p1[4] - p2[4], p1[5] - p2[5],
+ };
+ // Check the first vertex of p1
+ if (cross(p0[0], p0[1], p2[0] - p2[4], p2[1] - p2[5]) < 0 ||
+ cross(p2[0] - p2[2], p2[1] - p2[3], p0[0], p0[1]) < 0) {
+ return false;
+ }
+ // Check the second vertex of p1
+ if (cross(p0[2], p0[3], p2[2] - p2[0], p2[3] - p2[1]) < 0 ||
+ cross(p2[2] - p2[4], p2[3] - p2[5], p0[2], p0[3]) < 0) {
+ return false;
+ }
+ // Check the third vertex of p1
+ if (cross(p0[4], p0[5], p2[4] - p2[2], p2[5] - p2[3]) < 0 ||
+ cross(p2[4] - p2[0], p2[5] - p2[1], p0[4], p0[5]) < 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Computes the primaries of a color space identified only by
+ * its RGB->XYZ transform matrix. This method assumes that the
+ * range of the color space is [0..1].
+ *
+ * @param toXYZ The color space's 3x3 transform matrix to XYZ
+ * @param EOTF The color space's electro-optical transfer function
+ * @return A new array of 6 floats containing the color space's
+ * primaries in CIE xyY
+ */
+ @NonNull
+ @Size(6)
+ private static float[] computePrimaries(@NonNull @Size(9) float[] toXYZ,
+ DoubleUnaryOperator EOTF) {
+ float one = (float) EOTF.applyAsDouble(1.0);
+ float[] r = mul3x3Float3(toXYZ, new float[] { one, 0.0f, 0.0f });
+ float[] g = mul3x3Float3(toXYZ, new float[] { 0.0f, one, 0.0f });
+ float[] b = mul3x3Float3(toXYZ, new float[] { 0.0f, 0.0f, one });
+
+ float rSum = r[0] + r[1] + r[2];
+ float gSum = g[0] + g[1] + g[2];
+ float bSum = b[0] + b[1] + b[2];
+
+ return new float[] {
+ r[0] / rSum, r[1] / rSum,
+ g[0] / gSum, g[1] / gSum,
+ b[0] / bSum, b[1] / bSum,
+ };
+ }
+
+ /**
+ * Computes the white point of a color space identified only by
+ * its RGB->XYZ transform matrix. This method assumes that the
+ * range of the color space is [0..1].
+ *
+ * @param toXYZ The color space's 3x3 transform matrix to XYZ
+ * @param EOTF The color space's electro-optical transfer function
+ * @return A new array of 2 floats containing the color space's
+ * white point in CIE xyY
+ */
+ @NonNull
+ @Size(2)
+ private static float[] computeWhitePoint(@NonNull @Size(9) float[] toXYZ,
+ @NonNull DoubleUnaryOperator EOTF) {
+ float one = (float) EOTF.applyAsDouble(1.0);
+ float[] w = mul3x3Float3(toXYZ, new float[] { one, one, one });
+ float sum = w[0] + w[1] + w[2];
+ return new float[] { w[0] / sum, w[1] / sum };
+ }
+
+ /**
+ * Converts the specified RGB primaries point to xyY if needed. The primaries
+ * can be specified as an array of 6 floats (in CIE xyY) or 9 floats
+ * (in CIE XYZ). If no conversion is needed, the input array is copied.
+ *
+ * @param primaries The primaries in xyY or XYZ
+ * @return A new array of 6 floats containing the primaries in xyY
+ */
+ @NonNull
+ @Size(6)
+ private static float[] xyPrimaries(@NonNull @Size(min = 6, max = 9) float[] primaries) {
+ float[] xyPrimaries = new float[6];
+
+ // XYZ to xyY
+ if (primaries.length == 9) {
+ float sum;
+
+ sum = primaries[0] + primaries[1] + primaries[2];
+ xyPrimaries[0] = primaries[0] / sum;
+ xyPrimaries[1] = primaries[1] / sum;
+
+ sum = primaries[3] + primaries[4] + primaries[5];
+ xyPrimaries[2] = primaries[3] / sum;
+ xyPrimaries[3] = primaries[4] / sum;
+
+ sum = primaries[6] + primaries[7] + primaries[8];
+ xyPrimaries[4] = primaries[6] / sum;
+ xyPrimaries[5] = primaries[7] / sum;
+ } else {
+ System.arraycopy(primaries, 0, xyPrimaries, 0, 6);
+ }
+
+ return xyPrimaries;
+ }
+
+ /**
+ * Converts the specified white point to xyY if needed. The white point
+ * can be specified as an array of 2 floats (in CIE xyY) or 3 floats
+ * (in CIE XYZ). If no conversion is needed, the input array is copied.
+ *
+ * @param whitePoint The white point in xyY or XYZ
+ * @return A new array of 2 floats containing the white point in xyY
+ */
+ @NonNull
+ @Size(2)
+ private static float[] xyWhitePoint(@Size(min = 2, max = 3) float[] whitePoint) {
+ float[] xyWhitePoint = new float[2];
+
+ // XYZ to xyY
+ if (whitePoint.length == 3) {
+ float sum = whitePoint[0] + whitePoint[1] + whitePoint[2];
+ xyWhitePoint[0] = whitePoint[0] / sum;
+ xyWhitePoint[1] = whitePoint[1] / sum;
+ } else {
+ System.arraycopy(whitePoint, 0, xyWhitePoint, 0, 2);
+ }
+
+ return xyWhitePoint;
+ }
+
+ /**
+ * Computes the matrix that converts from RGB to XYZ based on RGB
+ * primaries and a white point, both specified in the CIE xyY space.
+ * The Y component of the primaries and white point is implied to be 1.
+ *
+ * @param primaries The RGB primaries in xyY, as an array of 6 floats
+ * @param whitePoint The white point in xyY, as an array of 2 floats
+ * @return A 3x3 matrix as a new array of 9 floats
+ */
+ @NonNull
+ @Size(9)
+ private static float[] computeXYZMatrix(
+ @NonNull @Size(6) float[] primaries,
+ @NonNull @Size(2) float[] whitePoint) {
+ float Rx = primaries[0];
+ float Ry = primaries[1];
+ float Gx = primaries[2];
+ float Gy = primaries[3];
+ float Bx = primaries[4];
+ float By = primaries[5];
+ float Wx = whitePoint[0];
+ float Wy = whitePoint[1];
+
+ float oneRxRy = (1 - Rx) / Ry;
+ float oneGxGy = (1 - Gx) / Gy;
+ float oneBxBy = (1 - Bx) / By;
+ float oneWxWy = (1 - Wx) / Wy;
+
+ float RxRy = Rx / Ry;
+ float GxGy = Gx / Gy;
+ float BxBy = Bx / By;
+ float WxWy = Wx / Wy;
+
+ float BY =
+ ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
+ ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
+ float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
+ float RY = 1 - GY - BY;
+
+ float RYRy = RY / Ry;
+ float GYGy = GY / Gy;
+ float BYBy = BY / By;
+
+ return new float[] {
+ RYRy * Rx, RY, RYRy * (1 - Rx - Ry),
+ GYGy * Gx, GY, GYGy * (1 - Gx - Gy),
+ BYBy * Bx, BY, BYBy * (1 - Bx - By)
+ };
+ }
+ }
+
+ /**
+ * {@usesMathJax}
+ *
+ * <p>A connector transforms colors from a source color space to a destination
+ * color space.</p>
+ *
+ * <p>A source color space is connected to a destination color space using the
+ * color transform \(C\) computed from their respective transforms noted
+ * \(T_{src}\) and \(T_{dst}\) in the following equation:</p>
+ *
+ * $$C = T^{-1}_{dst} . T_{src}$$
+ *
+ * <p>The transform \(C\) shown above is only valid when the source and
+ * destination color spaces have the same profile connection space (PCS).
+ * We know that instances of {@link ColorSpace} always use CIE XYZ as their
+ * PCS but their white points might differ. When they do, we must perform
+ * a chromatic adaptation of the color spaces' transforms. To do so, we
+ * use the von Kries method described in the documentation of {@link Adaptation},
+ * using the CIE standard illuminant {@link ColorSpace#ILLUMINANT_D50 D50}
+ * as the target white point.</p>
+ *
+ * <p>Example of conversion from {@link Named#SRGB sRGB} to
+ * {@link Named#DCI_P3 DCI-P3}:</p>
+ *
+ * <pre class="prettyprint">
+ * ColorSpace.Connector connector = ColorSpace.connect(
+ * ColorSpace.get(ColorSpace.Named.SRGB),
+ * ColorSpace.get(ColorSpace.Named.DCI_P3));
+ * float[] p3 = connector.transform(1.0f, 0.0f, 0.0f);
+ * // p3 contains { 0.9473, 0.2740, 0.2076 }
+ * </pre>
+ *
+ * @see Adaptation
+ * @see ColorSpace#adapt(ColorSpace, float[], Adaptation)
+ * @see ColorSpace#adapt(ColorSpace, float[])
+ * @see ColorSpace#connect(ColorSpace, ColorSpace, RenderIntent)
+ * @see ColorSpace#connect(ColorSpace, ColorSpace)
+ * @see ColorSpace#connect(ColorSpace, RenderIntent)
+ * @see ColorSpace#connect(ColorSpace)
+ */
+ public static class Connector {
+ @NonNull private final ColorSpace mSource;
+ @NonNull private final ColorSpace mDestination;
+ @NonNull private final ColorSpace mTransformSource;
+ @NonNull private final ColorSpace mTransformDestination;
+ @NonNull private final RenderIntent mIntent;
+ @NonNull @Size(3) private final float[] mTransform;
+
+ /**
+ * Creates a new connector between a source and a destination color space.
+ *
+ * @param source The source color space, cannot be null
+ * @param destination The destination color space, cannot be null
+ * @param intent The render intent to use when compressing gamuts
+ */
+ Connector(@NonNull ColorSpace source, @NonNull ColorSpace destination,
+ @NonNull RenderIntent intent) {
+ this(source, destination,
+ source.getModel() == Model.RGB ? adapt(source, ILLUMINANT_D50_XYZ) : source,
+ destination.getModel() == Model.RGB ?
+ adapt(destination, ILLUMINANT_D50_XYZ) : destination,
+ intent, computeTransform(source, destination, intent));
+ }
+
+ /**
+ * To connect between color spaces, we might need to use adapted transforms.
+ * This should be transparent to the user so this constructor takes the
+ * original source and destinations (returned by the getters), as well as
+ * possibly adapted color spaces used by transform().
+ */
+ private Connector(
+ @NonNull ColorSpace source, @NonNull ColorSpace destination,
+ @NonNull ColorSpace transformSource, @NonNull ColorSpace transformDestination,
+ @NonNull RenderIntent intent, @Nullable @Size(3) float[] transform) {
+ mSource = source;
+ mDestination = destination;
+ mTransformSource = transformSource;
+ mTransformDestination = transformDestination;
+ mIntent = intent;
+ mTransform = transform;
+ }
+
+ /**
+ * Computes an extra transform to apply in XYZ space depending on the
+ * selected rendering intent.
+ */
+ private static float[] computeTransform(@NonNull ColorSpace source,
+ @NonNull ColorSpace destination, @NonNull RenderIntent intent) {
+ if (intent != RenderIntent.ABSOLUTE) return null;
+
+ boolean srcRGB = source.getModel() == Model.RGB;
+ boolean dstRGB = destination.getModel() == Model.RGB;
+
+ if (srcRGB && dstRGB) return null;
+
+ if (srcRGB || dstRGB) {
+ ColorSpace.Rgb rgb = (ColorSpace.Rgb) (srcRGB ? source : destination);
+ float[] srcXYZ = srcRGB ? xyYToXyz(rgb.mWhitePoint) : ILLUMINANT_D50_XYZ;
+ float[] dstXYZ = dstRGB ? xyYToXyz(rgb.mWhitePoint) : ILLUMINANT_D50_XYZ;
+ return new float[] {
+ srcXYZ[0] / dstXYZ[0],
+ srcXYZ[1] / dstXYZ[1],
+ srcXYZ[2] / dstXYZ[2],
+ };
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the source color space this connector will convert from.
+ *
+ * @return A non-null instance of {@link ColorSpace}
+ *
+ * @see #getDestination()
+ */
+ @NonNull
+ public ColorSpace getSource() {
+ return mSource;
+ }
+
+ /**
+ * Returns the destination color space this connector will convert to.
+ *
+ * @return A non-null instance of {@link ColorSpace}
+ *
+ * @see #getSource()
+ */
+ @NonNull
+ public ColorSpace getDestination() {
+ return mDestination;
+ }
+
+ /**
+ * Returns the render intent this connector will use when mapping the
+ * source color space to the destination color space.
+ *
+ * @return A non-null {@link RenderIntent}
+ *
+ * @see RenderIntent
+ */
+ public RenderIntent getIntent() {
+ return mIntent;
+ }
+
+ /**
+ * <p>Transforms the specified color from the source color space
+ * to a color in the destination color space. This convenience
+ * method assumes a source color model with 3 components
+ * (typically RGB). To transform from color models with more than
+ * 3 components, such as {@link Model#CMYK CMYK}, use
+ * {@link #transform(float[])} instead.</p>
+ *
+ * @param r The red component of the color to transform
+ * @param g The green component of the color to transform
+ * @param b The blue component of the color to transform
+ * @return A new array of 3 floats containing the specified color
+ * transformed from the source space to the destination space
+ *
+ * @see #transform(float[])
+ */
+ @NonNull
+ @Size(3)
+ public float[] transform(float r, float g, float b) {
+ return transform(new float[] { r, g, b });
+ }
+
+ /**
+ * <p>Transforms the specified color from the source color space
+ * to a color in the destination color space.</p>
+ *
+ * @param v A non-null array of 3 floats containing the value to transform
+ * and that will hold the result of the transform
+ * @return The v array passed as a parameter, containing the specified color
+ * transformed from the source space to the destination space
+ *
+ * @see #transform(float, float, float)
+ */
+ @NonNull
+ @Size(min = 3)
+ public float[] transform(@NonNull @Size(min = 3) float[] v) {
+ float[] xyz = mTransformSource.toXyz(v);
+ if (mTransform != null) {
+ xyz[0] *= mTransform[0];
+ xyz[1] *= mTransform[1];
+ xyz[2] *= mTransform[2];
+ }
+ return mTransformDestination.fromXyz(xyz);
+ }
+
+ /**
+ * Optimized connector for RGB->RGB conversions.
+ */
+ private static class Rgb extends Connector {
+ @NonNull private final ColorSpace.Rgb mSource;
+ @NonNull private final ColorSpace.Rgb mDestination;
+ @NonNull private final float[] mTransform;
+
+ Rgb(@NonNull ColorSpace.Rgb source, @NonNull ColorSpace.Rgb destination,
+ @NonNull RenderIntent intent) {
+ super(source, destination, source, destination, intent, null);
+ mSource = source;
+ mDestination = destination;
+ mTransform = computeTransform(source, destination, intent);
+ }
+
+ @Override
+ public float[] transform(@NonNull @Size(min = 3) float[] rgb) {
+ rgb[0] = (float) mSource.mClampedEotf.applyAsDouble(rgb[0]);
+ rgb[1] = (float) mSource.mClampedEotf.applyAsDouble(rgb[1]);
+ rgb[2] = (float) mSource.mClampedEotf.applyAsDouble(rgb[2]);
+ mul3x3Float3(mTransform, rgb);
+ rgb[0] = (float) mDestination.mClampedOetf.applyAsDouble(rgb[0]);
+ rgb[1] = (float) mDestination.mClampedOetf.applyAsDouble(rgb[1]);
+ rgb[2] = (float) mDestination.mClampedOetf.applyAsDouble(rgb[2]);
+ return rgb;
+ }
+
+ /**
+ * <p>Computes the color transform that connects two RGB color spaces.</p>
+ *
+ * <p>We can only connect color spaces if they use the same profile
+ * connection space. We assume the connection space is always
+ * CIE XYZ but we maye need to perform a chromatic adaptation to
+ * match the white points. If an adaptation is needed, we use the
+ * CIE standard illuminant D50. The unmatched color space is adapted
+ * using the von Kries transform and the {@link Adaptation#BRADFORD}
+ * matrix.</p>
+ *
+ * @param source The source color space, cannot be null
+ * @param destination The destination color space, cannot be null
+ * @param intent The render intent to use when compressing gamuts
+ * @return An array of 9 floats containing the 3x3 matrix transform
+ */
+ @NonNull
+ @Size(9)
+ private static float[] computeTransform(
+ @NonNull ColorSpace.Rgb source,
+ @NonNull ColorSpace.Rgb destination,
+ @NonNull RenderIntent intent) {
+ if (compare(source.mWhitePoint, destination.mWhitePoint)) {
+ // RGB->RGB using the PCS of both color spaces since they have the same
+ return mul3x3(destination.mInverseTransform, source.mTransform);
+ } else {
+ // RGB->RGB using CIE XYZ D50 as the PCS
+ float[] transform = source.mTransform;
+ float[] inverseTransform = destination.mInverseTransform;
+
+ float[] srcXYZ = xyYToXyz(source.mWhitePoint);
+ float[] dstXYZ = xyYToXyz(destination.mWhitePoint);
+
+ if (!compare(source.mWhitePoint, ILLUMINANT_D50)) {
+ float[] srcAdaptation = chromaticAdaptation(
+ Adaptation.BRADFORD.mTransform, srcXYZ,
+ Arrays.copyOf(ILLUMINANT_D50_XYZ, 3));
+ transform = mul3x3(srcAdaptation, source.mTransform);
+ }
+
+ if (!compare(destination.mWhitePoint, ILLUMINANT_D50)) {
+ float[] dstAdaptation = chromaticAdaptation(
+ Adaptation.BRADFORD.mTransform, dstXYZ,
+ Arrays.copyOf(ILLUMINANT_D50_XYZ, 3));
+ inverseTransform = inverse3x3(mul3x3(dstAdaptation, destination.mTransform));
+ }
+
+ if (intent == RenderIntent.ABSOLUTE) {
+ transform = mul3x3Diag(
+ new float[] {
+ srcXYZ[0] / dstXYZ[0],
+ srcXYZ[1] / dstXYZ[1],
+ srcXYZ[2] / dstXYZ[2],
+ }, transform);
+ }
+
+ return mul3x3(inverseTransform, transform);
+ }
+ }
+ }
+
+ /**
+ * Returns the identity connector for a given color space.
+ *
+ * @param source The source and destination color space
+ * @return A non-null connector that does not perform any transform
+ *
+ * @see ColorSpace#connect(ColorSpace, ColorSpace)
+ */
+ static Connector identity(ColorSpace source) {
+ return new Connector(source, source, RenderIntent.RELATIVE) {
+ @Override
+ public float[] transform(@NonNull @Size(min = 3) float[] v) {
+ return v;
+ }
+ };
+ }
+ }
+
+ /**
+ * <p>A color space renderer can be used to visualize and compare the gamut and
+ * white point of one or more color spaces. The output is an sRGB {@link Bitmap}
+ * showing a CIE 1931 xyY chromaticity diagram.</p>
+ *
+ * <p>The following code snippet shows how to compare the {@link Named#SRGB}
+ * and {@link Named#DCI_P3} color spaces:</p>
+ *
+ * <pre class="prettyprint">
+ * Bitmap bitmap = ColorSpace.createRenderer()
+ * .size(768)
+ * .clip(true)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+ * .render();
+ * </pre>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_clipped.png" />
+ * <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption>
+ * </p>
+ *
+ * <p>A renderer can also be used to show the location of specific colors,
+ * associated with a color space, in the CIE 1931 xyY chromaticity diagram.
+ * See {@link #add(ColorSpace, float, float, float, int)} for more information.</p>
+ *
+ * @see ColorSpace#createRenderer()
+ */
+ public static class Renderer {
+ private static final int NATIVE_SIZE = 1440;
+
+ @IntRange(from = 128, to = Integer.MAX_VALUE)
+ private int mSize = 1024;
+
+ private boolean mShowWhitePoint = true;
+ private boolean mClip = false;
+
+ private final List<Pair<ColorSpace, Integer>> mColorSpaces = new ArrayList<>(2);
+ private final List<Point> mPoints = new ArrayList<>(0);
+
+ private Renderer() {
+ }
+
+ /**
+ * <p>Defines whether the chromaticity diagram should be clipped by the first
+ * registered color space. The default value is false.</p>
+ *
+ * <p>The following code snippet and image show the default behavior:</p>
+ * <pre class="prettyprint">
+ * Bitmap bitmap = ColorSpace.createRenderer()
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+ * .render();
+ * </pre>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_comparison.png" />
+ * <figcaption style="text-align: center;">Clipping disabled</figcaption>
+ * </p>
+ *
+ * <p>Here is the same example with clipping enabled:</p>
+ * <pre class="prettyprint">
+ * Bitmap bitmap = ColorSpace.createRenderer()
+ * .clip(true)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+ * .render();
+ * </pre>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_clipped.png" />
+ * <figcaption style="text-align: center;">Clipping enabled</figcaption>
+ * </p>
+ *
+ * @param clip True to clip the chromaticity diagram to the first registered color space,
+ * false otherwise
+ * @return This instance of {@link Renderer}
+ */
+ @NonNull
+ public Renderer clip(boolean clip) {
+ mClip = clip;
+ return this;
+ }
+
+ /**
+ * Sets the dimensions (width and height) in pixels of the output bitmap.
+ * The size must be at least 128px and defaults to 1024px.
+ *
+ * @param size The size in pixels of the output bitmap
+ * @return This instance of {@link Renderer}
+ */
+ @NonNull
+ public Renderer size(@IntRange(from = 128, to = Integer.MAX_VALUE) int size) {
+ mSize = Math.max(128, size);
+ return this;
+ }
+
+ /**
+ * Shows or hides the white point of each color space in the output bitmap.
+ * The default is true.
+ *
+ * @param show True to show the white point of each color space, false
+ * otherwise
+ * @return This instance of {@link Renderer}
+ */
+ @NonNull
+ public Renderer showWhitePoint(boolean show) {
+ mShowWhitePoint = show;
+ return this;
+ }
+
+ /**
+ * <p>Adds a color space to represent on the output CIE 1931 chromaticity
+ * diagram. The color space is represented as a triangle showing the
+ * footprint of its color gamut and, optionally, the location of its
+ * white point.</p>
+ *
+ * <p class="note">Color spaces with a color model that is not RGB are
+ * accepted but ignored.</p>
+ *
+ * <p>The following code snippet and image show an example of calling this
+ * method to compare {@link Named#SRGB sRGB} and {@link Named#DCI_P3 DCI-P3}:</p>
+ * <pre class="prettyprint">
+ * Bitmap bitmap = ColorSpace.createRenderer()
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+ * .render();
+ * </pre>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_comparison.png" />
+ * <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption>
+ * </p>
+ *
+ * <p>Adding a color space extending beyond the boundaries of the
+ * spectral locus will alter the size of the diagram within the output
+ * bitmap as shown in this example:</p>
+ * <pre class="prettyprint">
+ * Bitmap bitmap = ColorSpace.createRenderer()
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+ * .add(ColorSpace.get(ColorSpace.Named.ACES), 0xff097ae9)
+ * .add(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), 0xff000000)
+ * .render();
+ * </pre>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_comparison2.png" />
+ * <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption>
+ * </p>
+ *
+ * @param colorSpace The color space whose gamut to render on the diagram
+ * @param color The sRGB color to use to render the color space's gamut and white point
+ * @return This instance of {@link Renderer}
+ *
+ * @see #clip(boolean)
+ * @see #showWhitePoint(boolean)
+ */
+ @NonNull
+ public Renderer add(@NonNull ColorSpace colorSpace, @ColorInt int color) {
+ mColorSpaces.add(new Pair<>(colorSpace, color));
+ return this;
+ }
+
+ /**
+ * <p>Adds a color to represent as a point on the chromaticity diagram.
+ * The color is associated with a color space which will be used to
+ * perform the conversion to CIE XYZ and compute the location of the point
+ * on the diagram. The point is rendered as a colored circle.</p>
+ *
+ * <p>The following code snippet and image show an example of calling this
+ * method to render the location of several sRGB colors as white circles:</p>
+ * <pre class="prettyprint">
+ * Bitmap bitmap = ColorSpace.createRenderer()
+ * .clip(true)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.0f, 0.1f, 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.1f, 0.1f, 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.2f, 0.1f, 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.3f, 0.1f, 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.4f, 0.1f, 0xffffffff)
+ * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.5f, 0.1f, 0xffffffff)
+ * .render();
+ * </pre>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/colorspace_points.png" />
+ * <figcaption style="text-align: center;">
+ * Locating colors on the chromaticity diagram
+ * </figcaption>
+ * </p>
+ *
+ * @param colorSpace The color space of the color to locate on the diagram
+ * @param r The first component of the color to locate on the diagram
+ * @param g The second component of the color to locate on the diagram
+ * @param b The third component of the color to locate on the diagram
+ * @param pointColor The sRGB color to use to render the point on the diagram
+ * @return This instance of {@link Renderer}
+ */
+ @NonNull
+ public Renderer add(@NonNull ColorSpace colorSpace, float r, float g, float b,
+ @ColorInt int pointColor) {
+ mPoints.add(new Point(colorSpace, new float[] { r, g, b }, pointColor));
+ return this;
+ }
+
+ /**
+ * <p>Renders the {@link #add(ColorSpace, int) color spaces} and
+ * {@link #add(ColorSpace, float, float, float, int) points} registered
+ * with this renderer. The output bitmap is an sRGB image with the
+ * dimensions specified by calling {@link #size(int)} (1204x1024px by
+ * default).</p>
+ *
+ * @return A new non-null {@link Bitmap} with the dimensions specified
+ * by {@link #size(int)} (1024x1024 by default)
+ */
+ @NonNull
+ public Bitmap render() {
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ Bitmap bitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ float[] primaries = new float[6];
+ float[] whitePoint = new float[2];
+
+ int width = NATIVE_SIZE;
+ int height = NATIVE_SIZE;
+
+ Path path = new Path();
+
+ setTransform(canvas, width, height, primaries);
+ drawBox(canvas, width, height, paint, path);
+ drawLocus(canvas, width, height, paint, path, primaries);
+ drawGamuts(canvas, width, height, paint, path, primaries, whitePoint);
+ drawPoints(canvas, width, height, paint);
+
+ return bitmap;
+ }
+
+ /**
+ * Draws registered points at their correct position in the xyY coordinates.
+ * Each point is positioned according to its associated color space.
+ *
+ * @param canvas The canvas to transform
+ * @param width Width in pixel of the final image
+ * @param height Height in pixel of the final image
+ * @param paint A pre-allocated paint used to avoid temporary allocations
+ */
+ private void drawPoints(@NonNull Canvas canvas, int width, int height,
+ @NonNull Paint paint) {
+
+ paint.setStyle(Paint.Style.FILL);
+
+ float[] v = new float[3];
+ for (final Point point : mPoints) {
+ v[0] = point.mRgb[0];
+ v[1] = point.mRgb[1];
+ v[2] = point.mRgb[2];
+ point.mColorSpace.toXyz(v);
+
+ paint.setColor(point.mColor);
+
+ // XYZ to xyY, assuming Y=1.0
+ float sum = v[0] + v[1] + v[2];
+ canvas.drawCircle(width * v[0] / sum, height - height * v[1] / sum,
+ 4.0f, paint);
+ }
+ }
+
+ /**
+ * Draws the color gamuts and white points of all the registered color
+ * spaces. Only color spaces with an RGB color model are rendered, the
+ * others are ignored.
+ *
+ * @param canvas The canvas to transform
+ * @param width Width in pixel of the final image
+ * @param height Height in pixel of the final image
+ * @param paint A pre-allocated paint used to avoid temporary allocations
+ * @param path A pre-allocated path used to avoid temporary allocations
+ * @param primaries A pre-allocated array of 6 floats to avoid temporary allocations
+ * @param whitePoint A pre-allocated array of 2 floats to avoid temporary allocations
+ */
+ private void drawGamuts(
+ @NonNull Canvas canvas, int width, int height,
+ @NonNull Paint paint, @NonNull Path path,
+ @NonNull @Size(6) float[] primaries, @NonNull @Size(2) float[] whitePoint) {
+
+ for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
+ ColorSpace colorSpace = item.first;
+ int color = item.second;
+
+ if (colorSpace.getModel() != Model.RGB) continue;
+
+ Rgb rgb = (Rgb) colorSpace;
+ getPrimaries(rgb, primaries);
+
+ path.rewind();
+ path.moveTo(width * primaries[0], height - height * primaries[1]);
+ path.lineTo(width * primaries[2], height - height * primaries[3]);
+ path.lineTo(width * primaries[4], height - height * primaries[5]);
+ path.close();
+
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setColor(color);
+ canvas.drawPath(path, paint);
+
+ // Draw the white point
+ if (mShowWhitePoint) {
+ rgb.getWhitePoint(whitePoint);
+
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(color);
+ canvas.drawCircle(width * whitePoint[0], height - height * whitePoint[1],
+ 4.0f, paint);
+ }
+ }
+ }
+
+ /**
+ * Returns the primaries of the specified RGB color space. This method handles
+ * the special case of the {@link Named#EXTENDED_SRGB} family of color spaces.
+ *
+ * @param rgb The color space whose primaries to extract
+ * @param primaries A pre-allocated array of 6 floats that will hold the result
+ */
+ @NonNull
+ @Size(6)
+ private static float[] getPrimaries(@NonNull Rgb rgb, @NonNull @Size(6) float[] primaries) {
+ // TODO: We should find a better way to handle these cases
+ if (rgb.equals(ColorSpace.get(Named.EXTENDED_SRGB)) ||
+ rgb.equals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB))) {
+ primaries[0] = 1.41f;
+ primaries[1] = 0.33f;
+ primaries[2] = 0.27f;
+ primaries[3] = 1.24f;
+ primaries[4] = -0.23f;
+ primaries[5] = -0.57f;
+ return primaries;
+ }
+ return rgb.getPrimaries(primaries);
+ }
+
+ /**
+ * Draws the CIE 1931 chromaticity diagram: the spectral locus and its inside.
+ * This method respect the clip parameter.
+ *
+ * @param canvas The canvas to transform
+ * @param width Width in pixel of the final image
+ * @param height Height in pixel of the final image
+ * @param paint A pre-allocated paint used to avoid temporary allocations
+ * @param path A pre-allocated path used to avoid temporary allocations
+ * @param primaries A pre-allocated array of 6 floats to avoid temporary allocations
+ */
+ private void drawLocus(
+ @NonNull Canvas canvas, int width, int height, @NonNull Paint paint,
+ @NonNull Path path, @NonNull @Size(6) float[] primaries) {
+
+ int vertexCount = SPECTRUM_LOCUS_X.length * CHROMATICITY_RESOLUTION * 6;
+ float[] vertices = new float[vertexCount * 2];
+ int[] colors = new int[vertices.length];
+ computeChromaticityMesh(NATIVE_SIZE, NATIVE_SIZE, vertices, colors);
+
+ // Draw the spectral locus
+ if (mClip && mColorSpaces.size() > 0) {
+ for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
+ ColorSpace colorSpace = item.first;
+ if (colorSpace.getModel() != Model.RGB) continue;
+
+ Rgb rgb = (Rgb) colorSpace;
+ getPrimaries(rgb, primaries);
+ break;
+ }
+
+ path.rewind();
+ path.moveTo(width * primaries[0], height - height * primaries[1]);
+ path.lineTo(width * primaries[2], height - height * primaries[3]);
+ path.lineTo(width * primaries[4], height - height * primaries[5]);
+ path.close();
+
+ int[] solid = new int[colors.length];
+ Arrays.fill(solid, 0xff6c6c6c);
+ canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0,
+ null, 0, solid, 0, null, 0, 0, paint);
+
+ canvas.save();
+ canvas.clipPath(path);
+
+ canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0,
+ null, 0, colors, 0, null, 0, 0, paint);
+
+ canvas.restore();
+ } else {
+ canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0,
+ null, 0, colors, 0, null, 0, 0, paint);
+ }
+
+ // Draw the non-spectral locus
+ int index = (CHROMATICITY_RESOLUTION - 1) * 12;
+ path.reset();
+ path.moveTo(vertices[index], vertices[index + 1]);
+ for (int x = 2; x < SPECTRUM_LOCUS_X.length; x++) {
+ index += CHROMATICITY_RESOLUTION * 12;
+ path.lineTo(vertices[index], vertices[index + 1]);
+ }
+ path.close();
+
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setColor(0xff000000);
+ canvas.drawPath(path, paint);
+ }
+
+ /**
+ * Draws the diagram box, including borders, tick marks, grid lines
+ * and axis labels.
+ *
+ * @param canvas The canvas to transform
+ * @param width Width in pixel of the final image
+ * @param height Height in pixel of the final image
+ * @param paint A pre-allocated paint used to avoid temporary allocations
+ * @param path A pre-allocated path used to avoid temporary allocations
+ */
+ private void drawBox(@NonNull Canvas canvas, int width, int height, @NonNull Paint paint,
+ @NonNull Path path) {
+ // Draw the unit grid
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(2.0f);
+ paint.setColor(0xffc0c0c0);
+ for (int i = 1; i <= 9; i++) {
+ canvas.drawLine(0.0f, height - (height * i / 10.0f),
+ 0.9f * width, height - (height * i / 10.0f), paint);
+ canvas.drawLine(width * i / 10.0f, height,
+ width * i / 10.0f, 0.1f * height, paint);
+ }
+
+ // Draw tick marks
+ paint.setStrokeWidth(4.0f);
+ paint.setColor(0xff000000);
+ for (int i = 1; i <= 9; i++) {
+ canvas.drawLine(0.0f, height - (height * i / 10.0f),
+ width / 100.0f, height - (height * i / 10.0f), paint);
+ canvas.drawLine(width * i / 10.0f, height,
+ width * i / 10.0f, height - (height / 100.0f), paint);
+ }
+
+ // Draw the axis labels
+ paint.setStyle(Paint.Style.FILL);
+ paint.setTextSize(36.0f);
+ paint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
+
+ Rect bounds = new Rect();
+ for (int i = 1; i < 9; i++) {
+ String text = "0." + i;
+ paint.getTextBounds(text, 0, text.length(), bounds);
+
+ float y = height - (height * i / 10.0f);
+ canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint);
+
+ float x = width * i / 10.0f;
+ canvas.drawText(text, x - bounds.width() / 2.0f,
+ height + bounds.height() + 16, paint);
+ }
+ paint.setStyle(Paint.Style.STROKE);
+
+ // Draw the diagram box
+ path.moveTo(0.0f, height);
+ path.lineTo(0.9f * width, height);
+ path.lineTo(0.9f * width, 0.1f * height);
+ path.lineTo(0.0f, 0.1f * height);
+ path.close();
+ canvas.drawPath(path, paint);
+ }
+
+ /**
+ * Computes and applies the Canvas transforms required to make the color
+ * gamut of each color space visible in the final image.
+ *
+ * @param canvas The canvas to transform
+ * @param width Width in pixel of the final image
+ * @param height Height in pixel of the final image
+ * @param primaries Array of 6 floats used to avoid temporary allocations
+ */
+ private void setTransform(@NonNull Canvas canvas, int width, int height,
+ @NonNull @Size(6) float[] primaries) {
+
+ RectF primariesBounds = new RectF();
+ for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
+ ColorSpace colorSpace = item.first;
+ if (colorSpace.getModel() != Model.RGB) continue;
+
+ Rgb rgb = (Rgb) colorSpace;
+ getPrimaries(rgb, primaries);
+
+ primariesBounds.left = Math.min(primariesBounds.left, primaries[4]);
+ primariesBounds.top = Math.min(primariesBounds.top, primaries[5]);
+ primariesBounds.right = Math.max(primariesBounds.right, primaries[0]);
+ primariesBounds.bottom = Math.max(primariesBounds.bottom, primaries[3]);
+ }
+
+ primariesBounds.left = Math.min(0.0f, primariesBounds.left);
+ primariesBounds.top = Math.min(0.0f, primariesBounds.top);
+ primariesBounds.right = Math.max(0.9f, primariesBounds.right);
+ primariesBounds.bottom = Math.max(0.9f, primariesBounds.bottom);
+
+ float scaleX = 0.9f / primariesBounds.width();
+ float scaleY = 0.9f / primariesBounds.height();
+ float scale = Math.min(scaleX, scaleY);
+
+ canvas.scale(mSize / (float) NATIVE_SIZE, mSize / (float) NATIVE_SIZE);
+ canvas.scale(scale, scale);
+ canvas.translate(
+ (primariesBounds.width() - 0.9f) * width / 2.0f,
+ (primariesBounds.height() - 0.9f) * height / 2.0f);
+
+ // The spectrum extends ~0.85 vertically and ~0.65 horizontally
+ // We shift the canvas a little bit to get nicer margins
+ canvas.translate(0.05f * width, -0.05f * height);
+ }
+
+ // X coordinates of the spectral locus in CIE 1931
+ private static final float[] SPECTRUM_LOCUS_X = {
+ 0.175596f, 0.172787f, 0.170806f, 0.170085f, 0.160343f,
+ 0.146958f, 0.139149f, 0.133536f, 0.126688f, 0.115830f,
+ 0.109616f, 0.099146f, 0.091310f, 0.078130f, 0.068717f,
+ 0.054675f, 0.040763f, 0.027497f, 0.016270f, 0.008169f,
+ 0.004876f, 0.003983f, 0.003859f, 0.004646f, 0.007988f,
+ 0.013870f, 0.022244f, 0.027273f, 0.032820f, 0.038851f,
+ 0.045327f, 0.052175f, 0.059323f, 0.066713f, 0.074299f,
+ 0.089937f, 0.114155f, 0.138695f, 0.154714f, 0.192865f,
+ 0.229607f, 0.265760f, 0.301588f, 0.337346f, 0.373083f,
+ 0.408717f, 0.444043f, 0.478755f, 0.512467f, 0.544767f,
+ 0.575132f, 0.602914f, 0.627018f, 0.648215f, 0.665746f,
+ 0.680061f, 0.691487f, 0.700589f, 0.707901f, 0.714015f,
+ 0.719017f, 0.723016f, 0.734674f, 0.717203f, 0.699732f,
+ 0.682260f, 0.664789f, 0.647318f, 0.629847f, 0.612376f,
+ 0.594905f, 0.577433f, 0.559962f, 0.542491f, 0.525020f,
+ 0.507549f, 0.490077f, 0.472606f, 0.455135f, 0.437664f,
+ 0.420193f, 0.402721f, 0.385250f, 0.367779f, 0.350308f,
+ 0.332837f, 0.315366f, 0.297894f, 0.280423f, 0.262952f,
+ 0.245481f, 0.228010f, 0.210538f, 0.193067f, 0.175596f
+ };
+ // Y coordinates of the spectral locus in CIE 1931
+ private static final float[] SPECTRUM_LOCUS_Y = {
+ 0.005295f, 0.004800f, 0.005472f, 0.005976f, 0.014496f,
+ 0.026643f, 0.035211f, 0.042704f, 0.053441f, 0.073601f,
+ 0.086866f, 0.112037f, 0.132737f, 0.170464f, 0.200773f,
+ 0.254155f, 0.317049f, 0.387997f, 0.463035f, 0.538504f,
+ 0.587196f, 0.610526f, 0.654897f, 0.675970f, 0.715407f,
+ 0.750246f, 0.779682f, 0.792153f, 0.802971f, 0.812059f,
+ 0.819430f, 0.825200f, 0.829460f, 0.832306f, 0.833833f,
+ 0.833316f, 0.826231f, 0.814796f, 0.805884f, 0.781648f,
+ 0.754347f, 0.724342f, 0.692326f, 0.658867f, 0.624470f,
+ 0.589626f, 0.554734f, 0.520222f, 0.486611f, 0.454454f,
+ 0.424252f, 0.396516f, 0.372510f, 0.351413f, 0.334028f,
+ 0.319765f, 0.308359f, 0.299317f, 0.292044f, 0.285945f,
+ 0.280951f, 0.276964f, 0.265326f, 0.257200f, 0.249074f,
+ 0.240948f, 0.232822f, 0.224696f, 0.216570f, 0.208444f,
+ 0.200318f, 0.192192f, 0.184066f, 0.175940f, 0.167814f,
+ 0.159688f, 0.151562f, 0.143436f, 0.135311f, 0.127185f,
+ 0.119059f, 0.110933f, 0.102807f, 0.094681f, 0.086555f,
+ 0.078429f, 0.070303f, 0.062177f, 0.054051f, 0.045925f,
+ 0.037799f, 0.029673f, 0.021547f, 0.013421f, 0.005295f
+ };
+
+ // Number of subdivision of the inside of the spectral locus
+ private static final int CHROMATICITY_RESOLUTION = 32;
+ private static final double ONE_THIRD = 1.0 / 3.0;
+
+ /**
+ * Computes a 2D mesh representation of the CIE 1931 chromaticity
+ * diagram.
+ *
+ * @param width Width in pixels of the mesh
+ * @param height Height in pixels of the mesh
+ * @param vertices Array of floats that will hold the mesh vertices
+ * @param colors Array of floats that will hold the mesh colors
+ */
+ private static void computeChromaticityMesh(int width, int height,
+ @NonNull float[] vertices, @NonNull int[] colors) {
+
+ ColorSpace colorSpace = get(Named.SRGB);
+
+ float[] color = new float[3];
+
+ int vertexIndex = 0;
+ int colorIndex = 0;
+
+ for (int x = 0; x < SPECTRUM_LOCUS_X.length; x++) {
+ int nextX = (x % (SPECTRUM_LOCUS_X.length - 1)) + 1;
+
+ float a1 = (float) Math.atan2(
+ SPECTRUM_LOCUS_Y[x] - ONE_THIRD,
+ SPECTRUM_LOCUS_X[x] - ONE_THIRD);
+ float a2 = (float) Math.atan2(
+ SPECTRUM_LOCUS_Y[nextX] - ONE_THIRD,
+ SPECTRUM_LOCUS_X[nextX] - ONE_THIRD);
+
+ float radius1 = (float) Math.pow(
+ sqr(SPECTRUM_LOCUS_X[x] - ONE_THIRD) +
+ sqr(SPECTRUM_LOCUS_Y[x] - ONE_THIRD),
+ 0.5);
+ float radius2 = (float) Math.pow(
+ sqr(SPECTRUM_LOCUS_X[nextX] - ONE_THIRD) +
+ sqr(SPECTRUM_LOCUS_Y[nextX] - ONE_THIRD),
+ 0.5);
+
+ // Compute patches; each patch is a quad with a different
+ // color associated with each vertex
+ for (int c = 1; c <= CHROMATICITY_RESOLUTION; c++) {
+ float f1 = c / (float) CHROMATICITY_RESOLUTION;
+ float f2 = (c - 1) / (float) CHROMATICITY_RESOLUTION;
+
+ double cr1 = radius1 * Math.cos(a1);
+ double sr1 = radius1 * Math.sin(a1);
+ double cr2 = radius2 * Math.cos(a2);
+ double sr2 = radius2 * Math.sin(a2);
+
+ // Compute the XYZ coordinates of the 4 vertices of the patch
+ float v1x = (float) (ONE_THIRD + cr1 * f1);
+ float v1y = (float) (ONE_THIRD + sr1 * f1);
+ float v1z = 1 - v1x - v1y;
+
+ float v2x = (float) (ONE_THIRD + cr1 * f2);
+ float v2y = (float) (ONE_THIRD + sr1 * f2);
+ float v2z = 1 - v2x - v2y;
+
+ float v3x = (float) (ONE_THIRD + cr2 * f2);
+ float v3y = (float) (ONE_THIRD + sr2 * f2);
+ float v3z = 1 - v3x - v3y;
+
+ float v4x = (float) (ONE_THIRD + cr2 * f1);
+ float v4y = (float) (ONE_THIRD + sr2 * f1);
+ float v4z = 1 - v4x - v4y;
+
+ // Compute the sRGB representation of each XYZ coordinate of the patch
+ colors[colorIndex ] = computeColor(color, v1x, v1y, v1z, colorSpace);
+ colors[colorIndex + 1] = computeColor(color, v2x, v2y, v2z, colorSpace);
+ colors[colorIndex + 2] = computeColor(color, v3x, v3y, v3z, colorSpace);
+ colors[colorIndex + 3] = colors[colorIndex];
+ colors[colorIndex + 4] = colors[colorIndex + 2];
+ colors[colorIndex + 5] = computeColor(color, v4x, v4y, v4z, colorSpace);
+ colorIndex += 6;
+
+ // Flip the mesh upside down to match Canvas' coordinates system
+ vertices[vertexIndex++] = v1x * width;
+ vertices[vertexIndex++] = height - v1y * height;
+ vertices[vertexIndex++] = v2x * width;
+ vertices[vertexIndex++] = height - v2y * height;
+ vertices[vertexIndex++] = v3x * width;
+ vertices[vertexIndex++] = height - v3y * height;
+ vertices[vertexIndex++] = v1x * width;
+ vertices[vertexIndex++] = height - v1y * height;
+ vertices[vertexIndex++] = v3x * width;
+ vertices[vertexIndex++] = height - v3y * height;
+ vertices[vertexIndex++] = v4x * width;
+ vertices[vertexIndex++] = height - v4y * height;
+ }
+ }
+ }
+
+ @ColorInt
+ private static int computeColor(@NonNull @Size(3) float[] color,
+ float x, float y, float z, @NonNull ColorSpace cs) {
+ color[0] = x;
+ color[1] = y;
+ color[2] = z;
+ cs.fromXyz(color);
+ return 0xff000000 |
+ (((int) (color[0] * 255.0f) & 0xff) << 16) |
+ (((int) (color[1] * 255.0f) & 0xff) << 8) |
+ (((int) (color[2] * 255.0f) & 0xff) );
+ }
+
+ private static double sqr(double v) {
+ return v * v;
+ }
+
+ private static class Point {
+ @NonNull final ColorSpace mColorSpace;
+ @NonNull final float[] mRgb;
+ final int mColor;
+
+ Point(@NonNull ColorSpace colorSpace,
+ @NonNull @Size(3) float[] rgb, @ColorInt int color) {
+ mColorSpace = colorSpace;
+ mRgb = rgb;
+ mColor = color;
+ }
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index b2adcf6331df..08a68f4a9b9b 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -21,23 +21,8 @@ package android.graphics;
*/
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.
- */
- @SuppressWarnings({"UnusedDeclaration"})
+ private int mPorterDuffMode;
private final Shader mShaderA;
- @SuppressWarnings({"UnusedDeclaration"})
private final Shader mShaderB;
/** Create a new compose shader, given shaders A, B, and a combining mode.
@@ -49,12 +34,7 @@ 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;
- init(nativeCreate1(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
- (mode != null) ? mode.native_instance : 0));
+ this(shaderA, shaderB, mode.porterDuffMode);
}
/** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
@@ -65,12 +45,15 @@ 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;
+ this(shaderA, shaderB, mode.nativeInt);
+ }
+
+ private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) {
mShaderA = shaderA;
mShaderB = shaderB;
- mPorterDuffMode = mode;
- init(nativeCreate2(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
- mode.nativeInt));
+ mPorterDuffMode = nativeMode;
+ init(nativeCreate(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
+ nativeMode));
}
/**
@@ -78,24 +61,12 @@ public class ComposeShader extends Shader {
*/
@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");
- }
+ final ComposeShader copy = new ComposeShader(
+ mShaderA.copy(), mShaderB.copy(), mPorterDuffMode);
copyLocalMatrix(copy);
return copy;
}
- private static native long nativeCreate1(long native_shaderA, long native_shaderB,
- long native_mode);
- private static native long nativeCreate2(long native_shaderA, long native_shaderB,
+ private static native long nativeCreate(long native_shaderA, long native_shaderB,
int porterDuffMode);
}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 7871aa81dded..9490436d4f04 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -21,6 +21,8 @@ import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -99,6 +101,67 @@ public class FontListParser {
}
}
+ // Note that a well-formed variation contains a four-character tag and a float as styleValue,
+ // with spacers in between. The tag is enclosd either by double quotes or single quotes.
+ @VisibleForTesting
+ public static Axis[] parseFontVariationSettings(String settings) {
+ String[] settingList = settings.split(",");
+ ArrayList<Axis> axisList = new ArrayList<>();
+ settingLoop:
+ for (String setting : settingList) {
+ int pos = 0;
+ while (pos < setting.length()) {
+ char c = setting.charAt(pos);
+ if (c == '\'' || c == '"') {
+ break;
+ } else if (!isSpacer(c)) {
+ continue settingLoop; // Only spacers are allowed before tag appeared.
+ }
+ pos++;
+ }
+ if (pos + 7 > setting.length()) {
+ continue; // 7 is the minimum length of tag-style value pair text.
+ }
+ if (setting.charAt(pos) != setting.charAt(pos + 5)) {
+ continue; // Tag should be wrapped with double or single quote.
+ }
+ String tagString = setting.substring(pos + 1, pos + 5);
+ if (!TAG_PATTERN.matcher(tagString).matches()) {
+ continue; // Skip incorrect format tag.
+ }
+ pos += 6;
+ while (pos < setting.length()) {
+ if (!isSpacer(setting.charAt(pos++))) {
+ break; // Skip spacers between the tag and the styleValue.
+ }
+ }
+ // Skip invalid styleValue
+ float styleValue;
+ String valueString = setting.substring(pos - 1);
+ if (!STYLE_VALUE_PATTERN.matcher(valueString).matches()) {
+ continue; // Skip incorrect format styleValue.
+ }
+ try {
+ styleValue = Float.parseFloat(valueString);
+ } catch (NumberFormatException e) {
+ continue; // ignoreing invalid number format
+ }
+ int tag = makeTag(tagString.charAt(0), tagString.charAt(1), tagString.charAt(2),
+ tagString.charAt(3));
+ axisList.add(new Axis(tag, styleValue));
+ }
+ return axisList.toArray(new Axis[axisList.size()]);
+ }
+
+ @VisibleForTesting
+ public static int makeTag(char c1, char c2, char c3, char c4) {
+ return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
+ }
+
+ private static boolean isSpacer(char c) {
+ return c == ' ' || c == '\r' || c == '\t' || c == '\n';
+ }
+
private static Config readFamilies(XmlPullParser parser)
throws XmlPullParserException, IOException {
Config config = new Config();
@@ -165,8 +228,10 @@ public class FontListParser {
return new Font(fullFilename, index, axes, weight, isItalic);
}
- /** The 'tag' attribute value is read as four character values between 0 and 255 inclusive. */
- private static final Pattern TAG_PATTERN = Pattern.compile("[\\x00-\\xFF]{4}");
+ /** The 'tag' attribute value is read as four character values between U+0020 and U+007E
+ * inclusive.
+ */
+ private static final Pattern TAG_PATTERN = Pattern.compile("[\\x20-\\x7E]{4}");
/** The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
* '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
@@ -179,10 +244,7 @@ public class FontListParser {
int tag = 0;
String tagStr = parser.getAttributeValue(null, "tag");
if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) {
- tag = (tagStr.charAt(0) << 24) +
- (tagStr.charAt(1) << 16) +
- (tagStr.charAt(2) << 8) +
- (tagStr.charAt(3) );
+ tag = makeTag(tagStr.charAt(0), tagStr.charAt(1), tagStr.charAt(2), tagStr.charAt(3));
} else {
throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
}
diff --git a/graphics/java/android/graphics/LayerRasterizer.java b/graphics/java/android/graphics/LayerRasterizer.java
index b692ecf9c7f2..25155ab284fb 100644
--- a/graphics/java/android/graphics/LayerRasterizer.java
+++ b/graphics/java/android/graphics/LayerRasterizer.java
@@ -16,26 +16,20 @@
package android.graphics;
+/**
+ * @removed feature is not supported by hw-accerlerated or PDF backends
+ */
@Deprecated
public class LayerRasterizer extends Rasterizer {
- public LayerRasterizer() {
- native_instance = nativeConstructor();
- }
+ public LayerRasterizer() { }
/** Add a new layer (above any previous layers) to the rasterizer.
The layer will extract those fields that affect the mask from
the specified paint, but will not retain a reference to the paint
object itself, so it may be reused without danger of side-effects.
*/
- public void addLayer(Paint paint, float dx, float dy) {
- nativeAddLayer(native_instance, paint.getNativeInstance(), dx, dy);
- }
-
- public void addLayer(Paint paint) {
- nativeAddLayer(native_instance, paint.getNativeInstance(), 0, 0);
- }
+ public void addLayer(Paint paint, float dx, float dy) { }
- private static native long nativeConstructor();
- private static native void nativeAddLayer(long native_layer, long native_paint, float dx, float dy);
+ public void addLayer(Paint paint) { }
}
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 1e8f11bbe3ed..486070c99e3f 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -16,8 +16,12 @@
package android.graphics;
-import java.io.PrintWriter;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+import java.io.PrintWriter;
/**
* The Matrix class holds a 3x3 matrix for transforming coordinates.
@@ -216,352 +220,345 @@ public class Matrix {
}
};
+ // sizeof(SkMatrix) is 9 * sizeof(float) + uint32_t
+ private static final long NATIVE_ALLOCATION_SIZE = 40;
+
+ private static class NoImagePreloadHolder {
+ public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ Matrix.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+ }
+
/**
* @hide
*/
- public long native_instance;
+ public final long native_instance;
/**
* Create an identity matrix
*/
public Matrix() {
- native_instance = native_create(0);
+ native_instance = nCreate(0);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
/**
* Create a matrix that is a (deep) copy of src
+ *
* @param src The matrix to copy into this matrix
*/
public Matrix(Matrix src) {
- native_instance = native_create(src != null ? src.native_instance : 0);
+ native_instance = nCreate(src != null ? src.native_instance : 0);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
/**
- * Returns true if the matrix is identity.
- * This maybe faster than testing if (getType() == 0)
+ * Returns true if the matrix is identity. This maybe faster than testing if (getType() == 0)
*/
public boolean isIdentity() {
- return native_isIdentity(native_instance);
+ return nIsIdentity(native_instance);
}
/**
- * Gets whether this matrix is affine. An affine matrix preserves
- * straight lines and has no perspective.
+ * Gets whether this matrix is affine. An affine matrix preserves straight lines and has no
+ * perspective.
*
* @return Whether the matrix is affine.
*/
public boolean isAffine() {
- return native_isAffine(native_instance);
+ return nIsAffine(native_instance);
}
/**
- * Returns true if will map a rectangle to another rectangle. This can be
- * true if the matrix is identity, scale-only, or rotates a multiple of 90
- * degrees.
+ * Returns true if will map a rectangle to another rectangle. This can be true if the matrix is
+ * identity, scale-only, or rotates a multiple of 90 degrees.
*/
public boolean rectStaysRect() {
- return native_rectStaysRect(native_instance);
+ return nRectStaysRect(native_instance);
}
/**
- * (deep) copy the src matrix into this matrix. If src is null, reset this
- * matrix to the identity matrix.
+ * (deep) copy the src matrix into this matrix. If src is null, reset this matrix to the
+ * identity matrix.
*/
public void set(Matrix src) {
if (src == null) {
reset();
} else {
- native_set(native_instance, src.native_instance);
+ nSet(native_instance, src.native_instance);
}
}
- /** Returns true iff obj is a Matrix and its values equal our values.
- */
+ /**
+ * Returns true iff obj is a Matrix and its values equal our values.
+ */
@Override
public boolean equals(Object obj) {
- //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);
+ // if (obj == this) return true; -- NaN value would mean matrix != itself
+ if (!(obj instanceof Matrix)) {
+ return false;
+ }
+ return nEquals(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
+ // 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 */
public void reset() {
- native_reset(native_instance);
+ nReset(native_instance);
}
/** Set the matrix to translate by (dx, dy). */
public void setTranslate(float dx, float dy) {
- native_setTranslate(native_instance, dx, dy);
+ nSetTranslate(native_instance, dx, dy);
}
/**
- * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
- * The pivot point is the coordinate that should remain unchanged by the
- * specified transformation.
+ * Set the matrix to scale by sx and sy, with a pivot point at (px, py). The pivot point is the
+ * coordinate that should remain unchanged by the specified transformation.
*/
public void setScale(float sx, float sy, float px, float py) {
- native_setScale(native_instance, sx, sy, px, py);
+ nSetScale(native_instance, sx, sy, px, py);
}
/** Set the matrix to scale by sx and sy. */
public void setScale(float sx, float sy) {
- native_setScale(native_instance, sx, sy);
+ nSetScale(native_instance, sx, sy);
}
/**
- * Set the matrix to rotate by the specified number of degrees, with a pivot
- * point at (px, py). The pivot point is the coordinate that should remain
- * unchanged by the specified transformation.
+ * Set the matrix to rotate by the specified number of degrees, with a pivot point at (px, py).
+ * The pivot point is the coordinate that should remain unchanged by the specified
+ * transformation.
*/
public void setRotate(float degrees, float px, float py) {
- native_setRotate(native_instance, degrees, px, py);
+ nSetRotate(native_instance, degrees, px, py);
}
/**
* Set the matrix to rotate about (0,0) by the specified number of degrees.
*/
public void setRotate(float degrees) {
- native_setRotate(native_instance, degrees);
+ nSetRotate(native_instance, degrees);
}
/**
- * Set the matrix to rotate by the specified sine and cosine values, with a
- * pivot point at (px, py). The pivot point is the coordinate that should
- * remain unchanged by the specified transformation.
+ * Set the matrix to rotate by the specified sine and cosine values, with a pivot point at (px,
+ * py). The pivot point is the coordinate that should remain unchanged by the specified
+ * transformation.
*/
public void setSinCos(float sinValue, float cosValue, float px, float py) {
- native_setSinCos(native_instance, sinValue, cosValue, px, py);
+ nSetSinCos(native_instance, sinValue, cosValue, px, py);
}
/** Set the matrix to rotate by the specified sine and cosine values. */
public void setSinCos(float sinValue, float cosValue) {
- native_setSinCos(native_instance, sinValue, cosValue);
+ nSetSinCos(native_instance, sinValue, cosValue);
}
/**
- * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
- * The pivot point is the coordinate that should remain unchanged by the
- * specified transformation.
+ * Set the matrix to skew by sx and sy, with a pivot point at (px, py). The pivot point is the
+ * coordinate that should remain unchanged by the specified transformation.
*/
public void setSkew(float kx, float ky, float px, float py) {
- native_setSkew(native_instance, kx, ky, px, py);
+ nSetSkew(native_instance, kx, ky, px, py);
}
/** Set the matrix to skew by sx and sy. */
public void setSkew(float kx, float ky) {
- native_setSkew(native_instance, kx, ky);
+ nSetSkew(native_instance, kx, ky);
}
/**
- * Set the matrix to the concatenation of the two specified matrices and
- * return true.
- *
- * <p>Either of the two matrices may also be the target matrix, that is
- * <code>matrixA.setConcat(matrixA, matrixB);</code> is valid.</p>
- *
- * <p class="note">In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this
- * function returns true only if the result can be represented. In
- * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true.</p>
+ * Set the matrix to the concatenation of the two specified matrices and return true.
+ * <p>
+ * Either of the two matrices may also be the target matrix, that is
+ * <code>matrixA.setConcat(matrixA, matrixB);</code> is valid.
+ * </p>
+ * <p class="note">
+ * In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this function returns
+ * true only if the result can be represented. In
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true.
+ * </p>
*/
public boolean setConcat(Matrix a, Matrix b) {
- native_setConcat(native_instance, a.native_instance, b.native_instance);
+ nSetConcat(native_instance, a.native_instance, b.native_instance);
return true;
}
/**
- * Preconcats the matrix with the specified translation.
- * M' = M * T(dx, dy)
+ * Preconcats the matrix with the specified translation. M' = M * T(dx, dy)
*/
public boolean preTranslate(float dx, float dy) {
- native_preTranslate(native_instance, dx, dy);
+ nPreTranslate(native_instance, dx, dy);
return true;
}
/**
- * Preconcats the matrix with the specified scale.
- * M' = M * S(sx, sy, px, py)
+ * Preconcats the matrix with the specified scale. M' = M * S(sx, sy, px, py)
*/
public boolean preScale(float sx, float sy, float px, float py) {
- native_preScale(native_instance, sx, sy, px, py);
+ nPreScale(native_instance, sx, sy, px, py);
return true;
}
/**
- * Preconcats the matrix with the specified scale.
- * M' = M * S(sx, sy)
+ * Preconcats the matrix with the specified scale. M' = M * S(sx, sy)
*/
public boolean preScale(float sx, float sy) {
- native_preScale(native_instance, sx, sy);
+ nPreScale(native_instance, sx, sy);
return true;
}
/**
- * Preconcats the matrix with the specified rotation.
- * M' = M * R(degrees, px, py)
+ * Preconcats the matrix with the specified rotation. M' = M * R(degrees, px, py)
*/
public boolean preRotate(float degrees, float px, float py) {
- native_preRotate(native_instance, degrees, px, py);
+ nPreRotate(native_instance, degrees, px, py);
return true;
}
/**
- * Preconcats the matrix with the specified rotation.
- * M' = M * R(degrees)
+ * Preconcats the matrix with the specified rotation. M' = M * R(degrees)
*/
public boolean preRotate(float degrees) {
- native_preRotate(native_instance, degrees);
+ nPreRotate(native_instance, degrees);
return true;
}
/**
- * Preconcats the matrix with the specified skew.
- * M' = M * K(kx, ky, px, py)
+ * Preconcats the matrix with the specified skew. M' = M * K(kx, ky, px, py)
*/
public boolean preSkew(float kx, float ky, float px, float py) {
- native_preSkew(native_instance, kx, ky, px, py);
+ nPreSkew(native_instance, kx, ky, px, py);
return true;
}
/**
- * Preconcats the matrix with the specified skew.
- * M' = M * K(kx, ky)
+ * Preconcats the matrix with the specified skew. M' = M * K(kx, ky)
*/
public boolean preSkew(float kx, float ky) {
- native_preSkew(native_instance, kx, ky);
+ nPreSkew(native_instance, kx, ky);
return true;
}
/**
- * Preconcats the matrix with the specified matrix.
- * M' = M * other
+ * Preconcats the matrix with the specified matrix. M' = M * other
*/
public boolean preConcat(Matrix other) {
- native_preConcat(native_instance, other.native_instance);
+ nPreConcat(native_instance, other.native_instance);
return true;
}
/**
- * Postconcats the matrix with the specified translation.
- * M' = T(dx, dy) * M
+ * Postconcats the matrix with the specified translation. M' = T(dx, dy) * M
*/
public boolean postTranslate(float dx, float dy) {
- native_postTranslate(native_instance, dx, dy);
+ nPostTranslate(native_instance, dx, dy);
return true;
}
/**
- * Postconcats the matrix with the specified scale.
- * M' = S(sx, sy, px, py) * M
+ * Postconcats the matrix with the specified scale. M' = S(sx, sy, px, py) * M
*/
public boolean postScale(float sx, float sy, float px, float py) {
- native_postScale(native_instance, sx, sy, px, py);
+ nPostScale(native_instance, sx, sy, px, py);
return true;
}
/**
- * Postconcats the matrix with the specified scale.
- * M' = S(sx, sy) * M
+ * Postconcats the matrix with the specified scale. M' = S(sx, sy) * M
*/
public boolean postScale(float sx, float sy) {
- native_postScale(native_instance, sx, sy);
+ nPostScale(native_instance, sx, sy);
return true;
}
/**
- * Postconcats the matrix with the specified rotation.
- * M' = R(degrees, px, py) * M
+ * Postconcats the matrix with the specified rotation. M' = R(degrees, px, py) * M
*/
public boolean postRotate(float degrees, float px, float py) {
- native_postRotate(native_instance, degrees, px, py);
+ nPostRotate(native_instance, degrees, px, py);
return true;
}
/**
- * Postconcats the matrix with the specified rotation.
- * M' = R(degrees) * M
+ * Postconcats the matrix with the specified rotation. M' = R(degrees) * M
*/
public boolean postRotate(float degrees) {
- native_postRotate(native_instance, degrees);
+ nPostRotate(native_instance, degrees);
return true;
}
/**
- * Postconcats the matrix with the specified skew.
- * M' = K(kx, ky, px, py) * M
+ * Postconcats the matrix with the specified skew. M' = K(kx, ky, px, py) * M
*/
public boolean postSkew(float kx, float ky, float px, float py) {
- native_postSkew(native_instance, kx, ky, px, py);
+ nPostSkew(native_instance, kx, ky, px, py);
return true;
}
/**
- * Postconcats the matrix with the specified skew.
- * M' = K(kx, ky) * M
+ * Postconcats the matrix with the specified skew. M' = K(kx, ky) * M
*/
public boolean postSkew(float kx, float ky) {
- native_postSkew(native_instance, kx, ky);
+ nPostSkew(native_instance, kx, ky);
return true;
}
/**
- * Postconcats the matrix with the specified matrix.
- * M' = other * M
+ * Postconcats the matrix with the specified matrix. M' = other * M
*/
public boolean postConcat(Matrix other) {
- native_postConcat(native_instance, other.native_instance);
+ nPostConcat(native_instance, other.native_instance);
return true;
}
- /** Controlls how the src rect should align into the dst rect for
- setRectToRect().
- */
+ /**
+ * Controlls how the src rect should align into the dst rect for setRectToRect().
+ */
public enum ScaleToFit {
/**
- * Scale in X and Y independently, so that src matches dst exactly.
- * This may change the aspect ratio of the src.
+ * Scale in X and Y independently, so that src matches dst exactly. This may change the
+ * aspect ratio of the src.
*/
- FILL (0),
+ FILL(0),
/**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. START aligns the result to the
- * left and top edges of dst.
+ * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+ * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. START
+ * aligns the result to the left and top edges of dst.
*/
- START (1),
+ START(1),
/**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. The result is centered inside dst.
+ * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+ * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. The
+ * result is centered inside dst.
*/
- CENTER (2),
+ CENTER(2),
/**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. END aligns the result to the
- * right and bottom edges of dst.
+ * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+ * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. END
+ * aligns the result to the right and bottom edges of dst.
*/
- END (3);
+ END(3);
// the native values must match those in SkMatrix.h
ScaleToFit(int nativeInt) {
this.nativeInt = nativeInt;
}
+
final int nativeInt;
}
/**
- * Set the matrix to the scale and translate values that map the source
- * rectangle to the destination rectangle, returning true if the the result
- * can be represented.
+ * Set the matrix to the scale and translate values that map the source rectangle to the
+ * destination rectangle, returning true if the the result can be represented.
*
* @param src the source rectangle to map from.
* @param dst the destination rectangle to map to.
@@ -572,13 +569,13 @@ public class Matrix {
if (dst == null || src == null) {
throw new NullPointerException();
}
- return native_setRectToRect(native_instance, src, dst, stf.nativeInt);
+ return nSetRectToRect(native_instance, src, dst, stf.nativeInt);
}
// private helper to perform range checks on arrays of "points"
private static void checkPointArrays(float[] src, int srcIndex,
- float[] dst, int dstIndex,
- int pointCount) {
+ float[] dst, int dstIndex,
+ int pointCount) {
// check for too-small and too-big indices
int srcStop = srcIndex + (pointCount << 1);
int dstStop = dstIndex + (pointCount << 1);
@@ -589,84 +586,81 @@ public class Matrix {
}
/**
- * Set the matrix such that the specified src points would map to the
- * specified dst points. The "points" are represented as an array of floats,
- * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
+ * Set the matrix such that the specified src points would map to the specified dst points. The
+ * "points" are represented as an array of floats, order [x0, y0, x1, y1, ...], where each
+ * "point" is 2 float values.
*
- * @param src The array of src [x,y] pairs (points)
+ * @param src The array of src [x,y] pairs (points)
* @param srcIndex Index of the first pair of src values
- * @param dst The array of dst [x,y] pairs (points)
+ * @param dst The array of dst [x,y] pairs (points)
* @param dstIndex Index of the first pair of dst values
* @param pointCount The number of pairs/points to be used. Must be [0..4]
* @return true if the matrix was set to the specified transformation
*/
public boolean setPolyToPoly(float[] src, int srcIndex,
- float[] dst, int dstIndex,
- int pointCount) {
+ float[] dst, int dstIndex,
+ int pointCount) {
if (pointCount > 4) {
throw new IllegalArgumentException();
}
checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
- return native_setPolyToPoly(native_instance, src, srcIndex,
- dst, dstIndex, pointCount);
+ return nSetPolyToPoly(native_instance, src, srcIndex,
+ dst, dstIndex, pointCount);
}
/**
- * If this matrix can be inverted, return true and if inverse is not null,
- * set inverse to be the inverse of this matrix. If this matrix cannot be
- * inverted, ignore inverse and return false.
+ * If this matrix can be inverted, return true and if inverse is not null, set inverse to be the
+ * inverse of this matrix. If this matrix cannot be inverted, ignore inverse and return false.
*/
public boolean invert(Matrix inverse) {
- return native_invert(native_instance, inverse.native_instance);
+ return nInvert(native_instance, inverse.native_instance);
}
/**
- * Apply this matrix to the array of 2D points specified by src, and write
- * the transformed points into the array of points specified by dst. The
- * two arrays represent their "points" as pairs of floats [x, y].
+ * Apply this matrix to the array of 2D points specified by src, and write the transformed
+ * points into the array of points specified by dst. The two arrays represent their "points" as
+ * pairs of floats [x, y].
*
- * @param dst The array of dst points (x,y pairs)
+ * @param dst The array of dst points (x,y pairs)
* @param dstIndex The index of the first [x,y] pair of dst floats
- * @param src The array of src points (x,y pairs)
+ * @param src The array of src points (x,y pairs)
* @param srcIndex The index of the first [x,y] pair of src floats
* @param pointCount The number of points (x,y pairs) to transform
*/
public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
- int pointCount) {
+ int pointCount) {
checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
- native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
- pointCount, true);
+ nMapPoints(native_instance, dst, dstIndex, src, srcIndex,
+ pointCount, true);
}
/**
- * Apply this matrix to the array of 2D vectors specified by src, and write
- * the transformed vectors into the array of vectors specified by dst. The
- * two arrays represent their "vectors" as pairs of floats [x, y].
- *
- * Note: this method does not apply the translation associated with the matrix. Use
- * {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the translation
- * to be applied.
+ * Apply this matrix to the array of 2D vectors specified by src, and write the transformed
+ * vectors into the array of vectors specified by dst. The two arrays represent their "vectors"
+ * as pairs of floats [x, y]. Note: this method does not apply the translation associated with
+ * the matrix. Use {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the
+ * translation to be applied.
*
- * @param dst The array of dst vectors (x,y pairs)
+ * @param dst The array of dst vectors (x,y pairs)
* @param dstIndex The index of the first [x,y] pair of dst floats
- * @param src The array of src vectors (x,y pairs)
+ * @param src The array of src vectors (x,y pairs)
* @param srcIndex The index of the first [x,y] pair of src floats
* @param vectorCount The number of vectors (x,y pairs) to transform
*/
public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
- int vectorCount) {
+ int vectorCount) {
checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
- native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
- vectorCount, false);
+ nMapPoints(native_instance, dst, dstIndex, src, srcIndex,
+ vectorCount, false);
}
/**
- * Apply this matrix to the array of 2D points specified by src, and write
- * the transformed points into the array of points specified by dst. The
- * two arrays represent their "points" as pairs of floats [x, y].
+ * Apply this matrix to the array of 2D points specified by src, and write the transformed
+ * points into the array of points specified by dst. The two arrays represent their "points" as
+ * pairs of floats [x, y].
*
- * @param dst The array of dst points (x,y pairs)
- * @param src The array of src points (x,y pairs)
+ * @param dst The array of dst points (x,y pairs)
+ * @param src The array of src points (x,y pairs)
*/
public void mapPoints(float[] dst, float[] src) {
if (dst.length != src.length) {
@@ -676,15 +670,14 @@ public class Matrix {
}
/**
- * Apply this matrix to the array of 2D vectors specified by src, and write
- * the transformed vectors into the array of vectors specified by dst. The
- * two arrays represent their "vectors" as pairs of floats [x, y].
+ * Apply this matrix to the array of 2D vectors specified by src, and write the transformed
+ * vectors into the array of vectors specified by dst. The two arrays represent their "vectors"
+ * as pairs of floats [x, y]. Note: this method does not apply the translation associated with
+ * the matrix. Use {@link Matrix#mapPoints(float[], float[])} if you want the translation to be
+ * applied.
*
- * Note: this method does not apply the translation associated with the matrix. Use
- * {@link Matrix#mapPoints(float[], float[])} if you want the translation to be applied.
- *
- * @param dst The array of dst vectors (x,y pairs)
- * @param src The array of src vectors (x,y pairs)
+ * @param dst The array of dst vectors (x,y pairs)
+ * @param src The array of src vectors (x,y pairs)
*/
public void mapVectors(float[] dst, float[] src) {
if (dst.length != src.length) {
@@ -694,8 +687,8 @@ public class Matrix {
}
/**
- * Apply this matrix to the array of 2D points, and write the transformed
- * points back into the array
+ * Apply this matrix to the array of 2D points, and write the transformed points back into the
+ * array
*
* @param pts The array [x0, y0, x1, y1, ...] of points to transform.
*/
@@ -704,10 +697,8 @@ public class Matrix {
}
/**
- * Apply this matrix to the array of 2D vectors, and write the transformed
- * vectors back into the array.
- *
- * Note: this method does not apply the translation associated with the matrix. Use
+ * Apply this matrix to the array of 2D vectors, and write the transformed vectors back into the
+ * array. Note: this method does not apply the translation associated with the matrix. Use
* {@link Matrix#mapPoints(float[])} if you want the translation to be applied.
*
* @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
@@ -717,9 +708,9 @@ public class Matrix {
}
/**
- * Apply this matrix to the src rectangle, and write the transformed
- * rectangle into dst. This is accomplished by transforming the 4 corners of
- * src, and then setting dst to the bounds of those points.
+ * Apply this matrix to the src rectangle, and write the transformed rectangle into dst. This is
+ * accomplished by transforming the 4 corners of src, and then setting dst to the bounds of
+ * those points.
*
* @param dst Where the transformed rectangle is written.
* @param src The original rectangle to be transformed.
@@ -729,13 +720,13 @@ public class Matrix {
if (dst == null || src == null) {
throw new NullPointerException();
}
- return native_mapRect(native_instance, dst, src);
+ return nMapRect(native_instance, dst, src);
}
/**
- * Apply this matrix to the rectangle, and write the transformed rectangle
- * back into it. This is accomplished by transforming the 4 corners of rect,
- * and then setting it to the bounds of those points
+ * Apply this matrix to the rectangle, and write the transformed rectangle back into it. This is
+ * accomplished by transforming the 4 corners of rect, and then setting it to the bounds of
+ * those points
*
* @param rect The rectangle to transform.
* @return the result of calling rectStaysRect()
@@ -745,34 +736,33 @@ public class Matrix {
}
/**
- * Return the mean radius of a circle after it has been mapped by
- * this matrix. NOTE: in perspective this value assumes the circle
- * has its center at the origin.
+ * Return the mean radius of a circle after it has been mapped by this matrix. NOTE: in
+ * perspective this value assumes the circle has its center at the origin.
*/
public float mapRadius(float radius) {
- return native_mapRadius(native_instance, radius);
+ return nMapRadius(native_instance, radius);
}
- /** Copy 9 values from the matrix into the array.
- */
+ /**
+ * Copy 9 values from the matrix into the array.
+ */
public void getValues(float[] values) {
if (values.length < 9) {
throw new ArrayIndexOutOfBoundsException();
}
- native_getValues(native_instance, values);
+ nGetValues(native_instance, values);
}
- /** Copy 9 values from the array into the matrix.
- Depending on the implementation of Matrix, these may be
- transformed into 16.16 integers in the Matrix, such that
- a subsequent call to getValues() will not yield exactly
- the same values.
- */
+ /**
+ * Copy 9 values from the array into the matrix. Depending on the implementation of Matrix,
+ * these may be transformed into 16.16 integers in the Matrix, such that a subsequent call to
+ * getValues() will not yield exactly the same values.
+ */
public void setValues(float[] values) {
if (values.length < 9) {
throw new ArrayIndexOutOfBoundsException();
}
- native_setValues(native_instance, values);
+ nSetValues(native_instance, values);
}
@Override
@@ -798,122 +788,156 @@ public class Matrix {
float[] values = new float[9];
getValues(values);
sb.append('[');
- sb.append(values[0]); sb.append(", "); sb.append(values[1]); sb.append(", ");
- sb.append(values[2]); sb.append("][");
- sb.append(values[3]); sb.append(", "); sb.append(values[4]); sb.append(", ");
- sb.append(values[5]); sb.append("][");
- sb.append(values[6]); sb.append(", "); sb.append(values[7]); sb.append(", ");
- sb.append(values[8]); sb.append(']');
+ sb.append(values[0]);
+ sb.append(", ");
+ sb.append(values[1]);
+ sb.append(", ");
+ sb.append(values[2]);
+ sb.append("][");
+ sb.append(values[3]);
+ sb.append(", ");
+ sb.append(values[4]);
+ sb.append(", ");
+ sb.append(values[5]);
+ sb.append("][");
+ sb.append(values[6]);
+ sb.append(", ");
+ sb.append(values[7]);
+ sb.append(", ");
+ sb.append(values[8]);
+ sb.append(']');
}
/**
* Print short string, to optimize dumping.
+ *
* @hide
*/
public void printShortString(PrintWriter pw) {
float[] values = new float[9];
getValues(values);
pw.print('[');
- pw.print(values[0]); pw.print(", "); pw.print(values[1]); pw.print(", ");
- pw.print(values[2]); pw.print("][");
- pw.print(values[3]); pw.print(", "); pw.print(values[4]); pw.print(", ");
- pw.print(values[5]); pw.print("][");
- pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", ");
- pw.print(values[8]); pw.print(']');
+ pw.print(values[0]);
+ pw.print(", ");
+ pw.print(values[1]);
+ pw.print(", ");
+ pw.print(values[2]);
+ pw.print("][");
+ pw.print(values[3]);
+ pw.print(", ");
+ pw.print(values[4]);
+ pw.print(", ");
+ 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 {
- try {
- finalizer(native_instance);
- native_instance = 0; // Other finalizers can still call us.
- } finally {
- super.finalize();
- }
- }
-
- /*package*/ final long ni() {
+ /** @hide */
+ public final long ni() {
return native_instance;
}
- private static native long native_create(long native_src_or_zero);
- private static native boolean native_isIdentity(long native_object);
- private static native boolean native_isAffine(long native_object);
- private static native boolean native_rectStaysRect(long native_object);
- private static native void native_reset(long native_object);
- private static native void native_set(long native_object,
- long native_other);
- private static native void native_setTranslate(long native_object,
- float dx, float dy);
- private static native void native_setScale(long native_object,
- float sx, float sy, float px, float py);
- private static native void native_setScale(long native_object,
- float sx, float sy);
- private static native void native_setRotate(long native_object,
- float degrees, float px, float py);
- private static native void native_setRotate(long native_object,
- float degrees);
- private static native void native_setSinCos(long native_object,
- float sinValue, float cosValue, float px, float py);
- private static native void native_setSinCos(long native_object,
- float sinValue, float cosValue);
- private static native void native_setSkew(long native_object,
- float kx, float ky, float px, float py);
- private static native void native_setSkew(long native_object,
- float kx, float ky);
- private static native void native_setConcat(long native_object,
- long native_a,
- long native_b);
- private static native void native_preTranslate(long native_object,
- float dx, float dy);
- private static native void native_preScale(long native_object,
- float sx, float sy, float px, float py);
- private static native void native_preScale(long native_object,
- float sx, float sy);
- private static native void native_preRotate(long native_object,
- float degrees, float px, float py);
- private static native void native_preRotate(long native_object,
- float degrees);
- private static native void native_preSkew(long native_object,
- float kx, float ky, float px, float py);
- private static native void native_preSkew(long native_object,
- float kx, float ky);
- private static native void native_preConcat(long native_object,
- long native_other_matrix);
- private static native void native_postTranslate(long native_object,
- float dx, float dy);
- private static native void native_postScale(long native_object,
- float sx, float sy, float px, float py);
- private static native void native_postScale(long native_object,
- float sx, float sy);
- private static native void native_postRotate(long native_object,
- float degrees, float px, float py);
- private static native void native_postRotate(long native_object,
- float degrees);
- private static native void native_postSkew(long native_object,
- float kx, float ky, float px, float py);
- private static native void native_postSkew(long native_object,
- float kx, float ky);
- private static native void native_postConcat(long native_object,
- long native_other_matrix);
- private static native boolean native_setRectToRect(long native_object,
- RectF src, RectF dst, int stf);
- private static native boolean native_setPolyToPoly(long native_object,
- float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);
- private static native boolean native_invert(long native_object,
- long native_inverse);
- private static native void native_mapPoints(long native_object,
- float[] dst, int dstIndex, float[] src, int srcIndex,
- int ptCount, boolean isPts);
- private static native boolean native_mapRect(long native_object,
- RectF dst, RectF src);
- private static native float native_mapRadius(long native_object,
- float radius);
- private static native void native_getValues(long native_object,
- float[] values);
- private static native void native_setValues(long native_object,
- float[] values);
- private static native boolean native_equals(long native_a, long native_b);
- private static native void finalizer(long native_instance);
+ // ------------------ Regular JNI ------------------------
+
+ private static native long nCreate(long nSrc_or_zero);
+ private static native long nGetNativeFinalizer();
+
+
+ // ------------------ Fast JNI ------------------------
+
+ @FastNative
+ private static native boolean nSetRectToRect(long nObject,
+ RectF src, RectF dst, int stf);
+ @FastNative
+ private static native boolean nSetPolyToPoly(long nObject,
+ float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);
+ @FastNative
+ private static native void nMapPoints(long nObject,
+ float[] dst, int dstIndex, float[] src, int srcIndex,
+ int ptCount, boolean isPts);
+ @FastNative
+ private static native boolean nMapRect(long nObject, RectF dst, RectF src);
+ @FastNative
+ private static native void nGetValues(long nObject, float[] values);
+ @FastNative
+ private static native void nSetValues(long nObject, float[] values);
+
+
+ // ------------------ Critical JNI ------------------------
+
+ @CriticalNative
+ private static native boolean nIsIdentity(long nObject);
+ @CriticalNative
+ private static native boolean nIsAffine(long nObject);
+ @CriticalNative
+ private static native boolean nRectStaysRect(long nObject);
+ @CriticalNative
+ private static native void nReset(long nObject);
+ @CriticalNative
+ private static native void nSet(long nObject, long nOther);
+ @CriticalNative
+ private static native void nSetTranslate(long nObject, float dx, float dy);
+ @CriticalNative
+ private static native void nSetScale(long nObject, float sx, float sy, float px, float py);
+ @CriticalNative
+ private static native void nSetScale(long nObject, float sx, float sy);
+ @CriticalNative
+ private static native void nSetRotate(long nObject, float degrees, float px, float py);
+ @CriticalNative
+ private static native void nSetRotate(long nObject, float degrees);
+ @CriticalNative
+ private static native void nSetSinCos(long nObject, float sinValue, float cosValue,
+ float px, float py);
+ @CriticalNative
+ private static native void nSetSinCos(long nObject, float sinValue, float cosValue);
+ @CriticalNative
+ private static native void nSetSkew(long nObject, float kx, float ky, float px, float py);
+ @CriticalNative
+ private static native void nSetSkew(long nObject, float kx, float ky);
+ @CriticalNative
+ private static native void nSetConcat(long nObject, long nA, long nB);
+ @CriticalNative
+ private static native void nPreTranslate(long nObject, float dx, float dy);
+ @CriticalNative
+ private static native void nPreScale(long nObject, float sx, float sy, float px, float py);
+ @CriticalNative
+ private static native void nPreScale(long nObject, float sx, float sy);
+ @CriticalNative
+ private static native void nPreRotate(long nObject, float degrees, float px, float py);
+ @CriticalNative
+ private static native void nPreRotate(long nObject, float degrees);
+ @CriticalNative
+ private static native void nPreSkew(long nObject, float kx, float ky, float px, float py);
+ @CriticalNative
+ private static native void nPreSkew(long nObject, float kx, float ky);
+ @CriticalNative
+ private static native void nPreConcat(long nObject, long nOther_matrix);
+ @CriticalNative
+ private static native void nPostTranslate(long nObject, float dx, float dy);
+ @CriticalNative
+ private static native void nPostScale(long nObject, float sx, float sy, float px, float py);
+ @CriticalNative
+ private static native void nPostScale(long nObject, float sx, float sy);
+ @CriticalNative
+ private static native void nPostRotate(long nObject, float degrees, float px, float py);
+ @CriticalNative
+ private static native void nPostRotate(long nObject, float degrees);
+ @CriticalNative
+ private static native void nPostSkew(long nObject, float kx, float ky, float px, float py);
+ @CriticalNative
+ private static native void nPostSkew(long nObject, float kx, float ky);
+ @CriticalNative
+ private static native void nPostConcat(long nObject, long nOther_matrix);
+ @CriticalNative
+ private static native boolean nInvert(long nObject, long nInverse);
+ @CriticalNative
+ private static native float nMapRadius(long nObject, float radius);
+ @CriticalNative
+ private static native boolean nEquals(long nA, long nB);
}
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index aa81b9196fe1..3e59f34f6bc7 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -58,8 +58,12 @@ public final class Outline {
@Mode
public int mMode = MODE_EMPTY;
- /** @hide */
- public final Path mPath = new Path();
+ /**
+ * Only guaranteed to be non-null when mode == MODE_CONVEX_PATH
+ *
+ * @hide
+ */
+ public Path mPath;
/** @hide */
public final Rect mRect = new Rect();
@@ -87,8 +91,11 @@ public final class Outline {
* @see #isEmpty()
*/
public void setEmpty() {
+ if (mPath != null) {
+ // rewind here to avoid thrashing the allocations, but could alternately clear ref
+ mPath.rewind();
+ }
mMode = MODE_EMPTY;
- mPath.rewind();
mRect.setEmpty();
mRadius = RADIUS_UNDEFINED;
}
@@ -148,7 +155,12 @@ public final class Outline {
*/
public void set(@NonNull Outline src) {
mMode = src.mMode;
- mPath.set(src.mPath);
+ if (src.mMode == MODE_CONVEX_PATH) {
+ if (mPath == null) {
+ mPath = new Path();
+ }
+ mPath.set(src.mPath);
+ }
mRect.set(src.mRect);
mRadius = src.mRadius;
mAlpha = src.mAlpha;
@@ -180,10 +192,13 @@ public final class Outline {
return;
}
+ if (mMode == MODE_CONVEX_PATH) {
+ // rewind here to avoid thrashing the allocations, but could alternately clear ref
+ mPath.rewind();
+ }
mMode = MODE_ROUND_RECT;
mRect.set(left, top, right, bottom);
mRadius = radius;
- mPath.rewind();
}
/**
@@ -236,8 +251,13 @@ public final class Outline {
return;
}
+ if (mPath == null) {
+ mPath = new Path();
+ } else {
+ mPath.rewind();
+ }
+
mMode = MODE_CONVEX_PATH;
- mPath.rewind();
mPath.addOval(left, top, right, bottom, Path.Direction.CW);
mRect.setEmpty();
mRadius = RADIUS_UNDEFINED;
@@ -264,6 +284,10 @@ public final class Outline {
throw new IllegalArgumentException("path must be convex");
}
+ if (mPath == null) {
+ mPath = new Path();
+ }
+
mMode = MODE_CONVEX_PATH;
mPath.set(convexPath);
mRect.setEmpty();
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index cfbe672a4cd6..fc873c40524f 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -27,6 +27,9 @@ import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
import java.util.HashMap;
import java.util.Locale;
@@ -58,7 +61,6 @@ public class Paint {
private ColorFilter mColorFilter;
private MaskFilter mMaskFilter;
private PathEffect mPathEffect;
- private Rasterizer mRasterizer;
private Shader mShader;
private Typeface mTypeface;
private Xfermode mXfermode;
@@ -491,7 +493,6 @@ public class Paint {
mColorFilter = null;
mMaskFilter = null;
mPathEffect = null;
- mRasterizer = null;
mShader = null;
mNativeShader = 0;
mTypeface = null;
@@ -529,7 +530,6 @@ public class Paint {
mColorFilter = paint.mColorFilter;
mMaskFilter = paint.mMaskFilter;
mPathEffect = paint.mPathEffect;
- mRasterizer = paint.mRasterizer;
mShader = paint.mShader;
mNativeShader = paint.mNativeShader;
mTypeface = paint.mTypeface;
@@ -604,8 +604,6 @@ public class Paint {
return nGetFlags(mNativePaint);
}
- private native int nGetFlags(long paintPtr);
-
/**
* Set the paint's flags. Use the Flag enum to specific flag values.
*
@@ -615,8 +613,6 @@ public class Paint {
nSetFlags(mNativePaint, flags);
}
- private native void nSetFlags(long paintPtr, int flags);
-
/**
* Return the paint's hinting mode. Returns either
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
@@ -625,8 +621,6 @@ public class Paint {
return nGetHinting(mNativePaint);
}
- private native int nGetHinting(long paintPtr);
-
/**
* Set the paint's hinting mode. May be either
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
@@ -635,8 +629,6 @@ public class Paint {
nSetHinting(mNativePaint, mode);
}
- private native void nSetHinting(long paintPtr, int mode);
-
/**
* Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
* AntiAliasing smooths out the edges of what is being drawn, but is has
@@ -661,8 +653,6 @@ public class Paint {
nSetAntiAlias(mNativePaint, aa);
}
- private native void nSetAntiAlias(long paintPtr, boolean aa);
-
/**
* Helper for getFlags(), returning true if DITHER_FLAG bit is set
* Dithering affects how colors that are higher precision than the device
@@ -691,8 +681,6 @@ public class Paint {
nSetDither(mNativePaint, dither);
}
- private native void nSetDither(long paintPtr, boolean dither);
-
/**
* Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
*
@@ -712,8 +700,6 @@ public class Paint {
nSetLinearText(mNativePaint, linearText);
}
- private native void nSetLinearText(long paintPtr, boolean linearText);
-
/**
* Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
*
@@ -733,8 +719,6 @@ public class Paint {
nSetSubpixelText(mNativePaint, subpixelText);
}
- private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
-
/**
* Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
*
@@ -754,8 +738,6 @@ public class Paint {
nSetUnderlineText(mNativePaint, underlineText);
}
- private native void nSetUnderlineText(long paintPtr, boolean underlineText);
-
/**
* Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
*
@@ -775,8 +757,6 @@ public class Paint {
nSetStrikeThruText(mNativePaint, strikeThruText);
}
- private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
-
/**
* Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
*
@@ -796,8 +776,6 @@ public class Paint {
nSetFakeBoldText(mNativePaint, fakeBoldText);
}
- private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
-
/**
* Whether or not the bitmap filter is activated.
* Filtering affects the sampling of bitmaps when they are transformed.
@@ -823,8 +801,6 @@ public class Paint {
nSetFilterBitmap(mNativePaint, filter);
}
- private native void nSetFilterBitmap(long paintPtr, boolean filter);
-
/**
* Return the paint's style, used for controlling how primitives'
* geometries are interpreted (except for drawBitmap, which always assumes
@@ -860,8 +836,6 @@ public class Paint {
return nGetColor(mNativePaint);
}
- private native int nGetColor(long paintPtr);
-
/**
* Set the paint's color. Note that the color is an int containing alpha
* as well as r,g,b. This 32bit value is not premultiplied, meaning that
@@ -874,8 +848,6 @@ public class Paint {
nSetColor(mNativePaint, color);
}
- private native void nSetColor(long paintPtr, @ColorInt int color);
-
/**
* Helper to getColor() that just returns the color's alpha value. This is
* the same as calling getColor() >>> 24. It always returns a value between
@@ -887,8 +859,6 @@ public class Paint {
return nGetAlpha(mNativePaint);
}
- private native int nGetAlpha(long paintPtr);
-
/**
* Helper to setColor(), that only assigns the color's alpha value,
* leaving its r,g,b values unchanged. Results are undefined if the alpha
@@ -900,8 +870,6 @@ public class Paint {
nSetAlpha(mNativePaint, a);
}
- private native void nSetAlpha(long paintPtr, int a);
-
/**
* Helper to setColor(), that takes a,r,g,b and constructs the color int
*
@@ -927,8 +895,6 @@ public class Paint {
return nGetStrokeWidth(mNativePaint);
}
- private native float nGetStrokeWidth(long paintPtr);
-
/**
* Set the width for stroking.
* Pass 0 to stroke in hairline mode.
@@ -941,8 +907,6 @@ public class Paint {
nSetStrokeWidth(mNativePaint, width);
}
- private native void nSetStrokeWidth(long paintPtr, float width);
-
/**
* Return the paint's stroke miter value. Used to control the behavior
* of miter joins when the joins angle is sharp.
@@ -954,8 +918,6 @@ public class Paint {
return nGetStrokeMiter(mNativePaint);
}
- private native float nGetStrokeMiter(long paintPtr);
-
/**
* Set the paint's stroke miter value. This is used to control the behavior
* of miter joins when the joins angle is sharp. This value must be >= 0.
@@ -967,8 +929,6 @@ public class Paint {
nSetStrokeMiter(mNativePaint, miter);
}
- private native void nSetStrokeMiter(long paintPtr, float miter);
-
/**
* Return the paint's Cap, controlling how the start and end of stroked
* lines and paths are treated.
@@ -1096,10 +1056,11 @@ public class Paint {
* @return xfermode
*/
public Xfermode setXfermode(Xfermode xfermode) {
- long xfermodeNative = 0;
- if (xfermode != null)
- xfermodeNative = xfermode.native_instance;
- nSetXfermode(mNativePaint, xfermodeNative);
+ int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
+ int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
+ if (newMode != curMode) {
+ nSetXfermode(mNativePaint, newMode);
+ }
mXfermode = xfermode;
return xfermode;
}
@@ -1200,11 +1161,12 @@ public class Paint {
*
* @return the paint's rasterizer (or null)
*
- * @deprecated Rasterizer is not supported by either the HW or PDF backends.
+ * @deprecated Rasterizer is not supported by either the HW or PDF backends.
+ * @removed
*/
@Deprecated
public Rasterizer getRasterizer() {
- return mRasterizer;
+ return null;
}
/**
@@ -1217,16 +1179,11 @@ public class Paint {
* the paint.
* @return rasterizer
*
- * @deprecated Rasterizer is not supported by either the HW or PDF backends.
+ * @deprecated Rasterizer is not supported by either the HW or PDF backends.
+ * @removed
*/
@Deprecated
public Rasterizer setRasterizer(Rasterizer rasterizer) {
- long rasterizerNative = 0;
- if (rasterizer != null) {
- rasterizerNative = rasterizer.native_instance;
- }
- nSetRasterizer(mNativePaint, rasterizerNative);
- mRasterizer = rasterizer;
return rasterizer;
}
@@ -1386,8 +1343,6 @@ public class Paint {
return nIsElegantTextHeight(mNativePaint);
}
- private native boolean nIsElegantTextHeight(long paintPtr);
-
/**
* Set the paint's elegant height metrics flag. This setting selects font
* variants that have not been compacted to fit Latin-based vertical
@@ -1399,8 +1354,6 @@ public class Paint {
nSetElegantTextHeight(mNativePaint, elegant);
}
- private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
-
/**
* Return the paint's text size.
*
@@ -1410,8 +1363,6 @@ public class Paint {
return nGetTextSize(mNativePaint);
}
- private native float nGetTextSize(long paintPtr);
-
/**
* Set the paint's text size. This value must be > 0
*
@@ -1421,8 +1372,6 @@ public class Paint {
nSetTextSize(mNativePaint, textSize);
}
- private native void nSetTextSize(long paintPtr, float textSize);
-
/**
* Return the paint's horizontal scale factor for text. The default value
* is 1.0.
@@ -1433,8 +1382,6 @@ public class Paint {
return nGetTextScaleX(mNativePaint);
}
- private native float nGetTextScaleX(long paintPtr);
-
/**
* Set the paint's horizontal scale factor for text. The default value
* is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
@@ -1446,8 +1393,6 @@ public class Paint {
nSetTextScaleX(mNativePaint, scaleX);
}
- private native void nSetTextScaleX(long paintPtr, float scaleX);
-
/**
* Return the paint's horizontal skew factor for text. The default value
* is 0.
@@ -1458,8 +1403,6 @@ public class Paint {
return nGetTextSkewX(mNativePaint);
}
- private native float nGetTextSkewX(long paintPtr);
-
/**
* Set the paint's horizontal skew factor for text. The default value
* is 0. For approximating oblique text, use values around -0.25.
@@ -1470,8 +1413,6 @@ public class Paint {
nSetTextSkewX(mNativePaint, skewX);
}
- private native void nSetTextSkewX(long paintPtr, float skewX);
-
/**
* Return the paint's letter-spacing for text. The default value
* is 0.
@@ -1494,10 +1435,32 @@ public class Paint {
}
/**
+ * Return the paint's word-spacing for text. The default value is 0.
+ *
+ * @return the paint's word-spacing for drawing text.
+ * @hide
+ */
+ public float getWordSpacing() {
+ return nGetWordSpacing(mNativePaint);
+ }
+
+ /**
+ * Set the paint's word-spacing for text. The default value is 0.
+ * The value is in pixels (note the units are not the same as for
+ * letter-spacing).
+ *
+ * @param wordSpacing set the paint's word-spacing for drawing text.
+ * @hide
+ */
+ public void setWordSpacing(float wordSpacing) {
+ nSetWordSpacing(mNativePaint, wordSpacing);
+ }
+
+ /**
* Returns the font feature settings. The format is the same as the CSS
* font-feature-settings attribute:
- * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
- * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
+ * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
+ * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
*
* @return the paint's currently set font feature settings. Default is null.
*
@@ -1511,8 +1474,8 @@ public class Paint {
* Set font feature settings.
*
* The format is the same as the CSS font-feature-settings attribute:
- * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
- * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
+ * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
+ * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
*
* @see #getFontFeatureSettings()
*
@@ -1564,8 +1527,6 @@ public class Paint {
return nAscent(mNativePaint, mNativeTypeface);
}
- private native float nAscent(long paintPtr, long typefacePtr);
-
/**
* Return the distance below (positive) the baseline (descent) based on the
* current typeface and text size.
@@ -1577,8 +1538,6 @@ public class Paint {
return nDescent(mNativePaint, mNativeTypeface);
}
- private native float nDescent(long paintPtr, long typefacePtr);
-
/**
* Class that describes the various metrics for a font at a given text size.
* Remember, Y values increase going down, so those values will be positive,
@@ -1623,9 +1582,6 @@ public class Paint {
return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
}
- private native float nGetFontMetrics(long paintPtr,
- long typefacePtr, FontMetrics metrics);
-
/**
* Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
* with it, returning the object.
@@ -1641,10 +1597,27 @@ public class Paint {
* integers.
*/
public static class FontMetricsInt {
+ /**
+ * The maximum distance above the baseline for the tallest glyph in
+ * the font at a given text size.
+ */
public int top;
+ /**
+ * The recommended distance above the baseline for singled spaced text.
+ */
public int ascent;
+ /**
+ * The recommended distance below the baseline for singled spaced text.
+ */
public int descent;
+ /**
+ * The maximum distance below the baseline for the lowest glyph in
+ * the font at a given text size.
+ */
public int bottom;
+ /**
+ * The recommended additional space to add between lines of text.
+ */
public int leading;
@Override public String toString() {
@@ -1668,9 +1641,6 @@ public class Paint {
return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
}
- private native int nGetFontMetricsInt(long paintPtr,
- long typefacePtr, FontMetricsInt fmi);
-
public FontMetricsInt getFontMetricsInt() {
FontMetricsInt fm = new FontMetricsInt();
getFontMetricsInt(fm);
@@ -1842,10 +1812,6 @@ public class Paint {
return res;
}
- private static native int nBreakText(long nObject, long nTypeface,
- char[] text, int index, int count,
- float maxWidth, int bidiFlags, float[] measuredWidth);
-
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
* Return the number of chars that were measured, and if measuredWidth is
@@ -1934,10 +1900,6 @@ public class Paint {
return res;
}
- private static native int nBreakText(long nObject, long nTypeface,
- String text, boolean measureForwards,
- float maxWidth, int bidiFlags, float[] measuredWidth);
-
/**
* Return the advance widths for the characters in the string.
*
@@ -2380,13 +2342,12 @@ public class Paint {
* Note: just like Canvas.drawText, this will respect the Align setting in
* the paint.
*
- * @param text The text to retrieve the path from
- * @param index The index of the first character in text
- * @param count The number of characterss starting with index
- * @param x The x coordinate of the text's origin
- * @param y The y coordinate of the text's origin
- * @param path The path to receive the data describing the text. Must
- * be allocated by the caller.
+ * @param text the text to retrieve the path from
+ * @param index the index of the first character in text
+ * @param count the number of characters starting with index
+ * @param x the x coordinate of the text's origin
+ * @param y the y coordinate of the text's origin
+ * @param path the path to receive the data describing the text. Must be allocated by the caller
*/
public void getTextPath(char[] text, int index, int count,
float x, float y, Path path) {
@@ -2402,13 +2363,12 @@ public class Paint {
* Note: just like Canvas.drawText, this will respect the Align setting
* in the paint.
*
- * @param text The text to retrieve the path from
- * @param start The first character in the text
- * @param end 1 past the last charcter in the text
- * @param x The x coordinate of the text's origin
- * @param y The y coordinate of the text's origin
- * @param path The path to receive the data describing the text. Must
- * be allocated by the caller.
+ * @param text the text to retrieve the path from
+ * @param start the first character in the text
+ * @param end 1 past the last character in the text
+ * @param x the x coordinate of the text's origin
+ * @param y the y coordinate of the text's origin
+ * @param path the path to receive the data describing the text. Must be allocated by the caller
*/
public void getTextPath(String text, int start, int end,
float x, float y, Path path) {
@@ -2423,11 +2383,10 @@ public class Paint {
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
*
- * @param text String to measure and return its bounds
- * @param start Index of the first char in the string to measure
- * @param end 1 past the last char in the string measure
- * @param bounds Returns the unioned bounds of all the text. Must be
- * allocated by the caller.
+ * @param text string to measure and return its bounds
+ * @param start index of the first char in the string to measure
+ * @param end 1 past the last char in the string to measure
+ * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
*/
public void getTextBounds(String text, int start, int end, Rect bounds) {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
@@ -2443,11 +2402,33 @@ public class Paint {
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
*
- * @param text Array of chars to measure and return their unioned bounds
- * @param index Index of the first char in the array to measure
- * @param count The number of chars, beginning at index, to measure
- * @param bounds Returns the unioned bounds of all the text. Must be
- * allocated by the caller.
+ * @param text text to measure and return its bounds
+ * @param start index of the first char in the text to measure
+ * @param end 1 past the last char in the text to measure
+ * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
+ * @hide
+ */
+ public void getTextBounds(CharSequence text, int start, int end, Rect bounds) {
+ if ((start | end | (end - start) | (text.length() - end)) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (bounds == null) {
+ throw new NullPointerException("need bounds Rect");
+ }
+ char[] buf = TemporaryBuffer.obtain(end - start);
+ TextUtils.getChars(text, start, end, buf, 0);
+ getTextBounds(buf, 0, end - start, bounds);
+ TemporaryBuffer.recycle(buf);
+ }
+
+ /**
+ * Return in bounds (allocated by the caller) the smallest rectangle that
+ * encloses all of the characters, with an implied origin at (0,0).
+ *
+ * @param text array of chars to measure and return their unioned bounds
+ * @param index index of the first char in the array to measure
+ * @param count the number of chars, beginning at index, to measure
+ * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
*/
public void getTextBounds(char[] text, int index, int count, Rect bounds) {
if ((index | count) < 0 || index + count > text.length) {
@@ -2642,74 +2623,34 @@ public class Paint {
return result;
}
+ // regular JNI
+ private static native long nGetNativeFinalizer();
private static native long nInit();
private static native long nInitWithPaint(long paint);
- private static native void nReset(long paintPtr);
- private static native void nSet(long paintPtrDest, long paintPtrSrc);
- private static native int nGetStyle(long paintPtr);
- private static native void nSetStyle(long paintPtr, int style);
- private static native int nGetStrokeCap(long paintPtr);
- private static native void nSetStrokeCap(long paintPtr, int cap);
- private static native int nGetStrokeJoin(long paintPtr);
- private static native void nSetStrokeJoin(long paintPtr,
- int join);
- private static native boolean nGetFillPath(long paintPtr,
- long src, long dst);
- private static native long nSetShader(long paintPtr, long shader);
- private static native long nSetColorFilter(long paintPtr,
- long filter);
- private static native long nSetXfermode(long paintPtr,
- long xfermode);
- private static native long nSetPathEffect(long paintPtr,
- long effect);
- private static native long nSetMaskFilter(long paintPtr,
- long maskfilter);
- private static native long nSetTypeface(long paintPtr,
- long typeface);
- private static native long nSetRasterizer(long paintPtr,
- long rasterizer);
-
- private static native int nGetTextAlign(long paintPtr);
- private static native void nSetTextAlign(long paintPtr,
- int align);
-
- private static native int nSetTextLocales(long paintPtr, String locales);
- private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
- int mMinikinLangListId);
-
+ private static native int nBreakText(long nObject, long nTypeface,
+ char[] text, int index, int count,
+ float maxWidth, int bidiFlags, float[] measuredWidth);
+ private static native int nBreakText(long nObject, long nTypeface,
+ String text, boolean measureForwards,
+ float maxWidth, int bidiFlags, float[] measuredWidth);
private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
char[] text, int index, int count, int contextIndex, int contextCount,
int bidiFlags, float[] advances, int advancesIndex);
private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
String text, int start, int end, int contextStart, int contextEnd,
int bidiFlags, float[] advances, int advancesIndex);
-
private native int nGetTextRunCursor(long paintPtr, char[] text,
int contextStart, int contextLength, int dir, int offset, int cursorOpt);
private native int nGetTextRunCursor(long paintPtr, String text,
int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
-
private static native void nGetTextPath(long paintPtr, long typefacePtr,
int bidiFlags, char[] text, int index, int count, float x, float y, long path);
private static native void nGetTextPath(long paintPtr, long typefacePtr,
int bidiFlags, String text, int start, int end, float x, float y, long path);
private static native void nGetStringBounds(long nativePaint, long typefacePtr,
- String text, int start, int end, int bidiFlags, Rect bounds);
+ String text, int start, int end, int bidiFlags, Rect bounds);
private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
- char[] text, int index, int count, int bidiFlags, Rect bounds);
- private static native long nGetNativeFinalizer();
-
- private static native void nSetShadowLayer(long paintPtr,
- float radius, float dx, float dy, int color);
- private static native boolean nHasShadowLayer(long paintPtr);
-
- private static native float nGetLetterSpacing(long paintPtr);
- private static native void nSetLetterSpacing(long paintPtr,
- float letterSpacing);
- private static native void nSetFontFeatureSettings(long paintPtr,
- String settings);
- private static native int nGetHyphenEdit(long paintPtr);
- private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+ char[] text, int index, int count, int bidiFlags, Rect bounds);
private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
int bidiFlags, String string);
private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
@@ -2718,4 +2659,136 @@ public class Paint {
private static native int nGetOffsetForAdvance(long paintPtr,
long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
boolean isRtl, float advance);
+
+
+ // ---------------- @FastNative ------------------------
+
+ @FastNative
+ private static native int nSetTextLocales(long paintPtr, String locales);
+ @FastNative
+ private static native void nSetFontFeatureSettings(long paintPtr, String settings);
+ @FastNative
+ private static native float nGetFontMetrics(long paintPtr,
+ long typefacePtr, FontMetrics metrics);
+ @FastNative
+ private static native int nGetFontMetricsInt(long paintPtr,
+ long typefacePtr, FontMetricsInt fmi);
+
+
+ // ---------------- @CriticalNative ------------------------
+
+ @CriticalNative
+ private static native void nReset(long paintPtr);
+ @CriticalNative
+ private static native void nSet(long paintPtrDest, long paintPtrSrc);
+ @CriticalNative
+ private static native int nGetStyle(long paintPtr);
+ @CriticalNative
+ private static native void nSetStyle(long paintPtr, int style);
+ @CriticalNative
+ private static native int nGetStrokeCap(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrokeCap(long paintPtr, int cap);
+ @CriticalNative
+ private static native int nGetStrokeJoin(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrokeJoin(long paintPtr, int join);
+ @CriticalNative
+ private static native boolean nGetFillPath(long paintPtr, long src, long dst);
+ @CriticalNative
+ private static native long nSetShader(long paintPtr, long shader);
+ @CriticalNative
+ private static native long nSetColorFilter(long paintPtr, long filter);
+ @CriticalNative
+ private static native void nSetXfermode(long paintPtr, int xfermode);
+ @CriticalNative
+ private static native long nSetPathEffect(long paintPtr, long effect);
+ @CriticalNative
+ private static native long nSetMaskFilter(long paintPtr, long maskfilter);
+ @CriticalNative
+ private static native long nSetTypeface(long paintPtr, long typeface);
+ @CriticalNative
+ private static native int nGetTextAlign(long paintPtr);
+ @CriticalNative
+ private static native void nSetTextAlign(long paintPtr, int align);
+ @CriticalNative
+ private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
+ int mMinikinLangListId);
+ @CriticalNative
+ private static native void nSetShadowLayer(long paintPtr,
+ float radius, float dx, float dy, int color);
+ @CriticalNative
+ private static native boolean nHasShadowLayer(long paintPtr);
+ @CriticalNative
+ private static native float nGetLetterSpacing(long paintPtr);
+ @CriticalNative
+ private static native void nSetLetterSpacing(long paintPtr, float letterSpacing);
+ @CriticalNative
+ private static native float nGetWordSpacing(long paintPtr);
+ @CriticalNative
+ private static native void nSetWordSpacing(long paintPtr, float wordSpacing);
+ @CriticalNative
+ private static native int nGetHyphenEdit(long paintPtr);
+ @CriticalNative
+ private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+ @CriticalNative
+ private static native void nSetStrokeMiter(long paintPtr, float miter);
+ @CriticalNative
+ private static native float nGetStrokeMiter(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrokeWidth(long paintPtr, float width);
+ @CriticalNative
+ private static native float nGetStrokeWidth(long paintPtr);
+ @CriticalNative
+ private static native void nSetAlpha(long paintPtr, int a);
+ @CriticalNative
+ private static native void nSetDither(long paintPtr, boolean dither);
+ @CriticalNative
+ private static native int nGetFlags(long paintPtr);
+ @CriticalNative
+ private static native void nSetFlags(long paintPtr, int flags);
+ @CriticalNative
+ private static native int nGetHinting(long paintPtr);
+ @CriticalNative
+ private static native void nSetHinting(long paintPtr, int mode);
+ @CriticalNative
+ private static native void nSetAntiAlias(long paintPtr, boolean aa);
+ @CriticalNative
+ private static native void nSetLinearText(long paintPtr, boolean linearText);
+ @CriticalNative
+ private static native void nSetSubpixelText(long paintPtr, boolean subpixelText);
+ @CriticalNative
+ private static native void nSetUnderlineText(long paintPtr, boolean underlineText);
+ @CriticalNative
+ private static native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
+ @CriticalNative
+ private static native void nSetFilterBitmap(long paintPtr, boolean filter);
+ @CriticalNative
+ private static native int nGetColor(long paintPtr);
+ @CriticalNative
+ private static native void nSetColor(long paintPtr, @ColorInt int color);
+ @CriticalNative
+ private static native int nGetAlpha(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
+ @CriticalNative
+ private static native boolean nIsElegantTextHeight(long paintPtr);
+ @CriticalNative
+ private static native void nSetElegantTextHeight(long paintPtr, boolean elegant);
+ @CriticalNative
+ private static native float nGetTextSize(long paintPtr);
+ @CriticalNative
+ private static native float nGetTextScaleX(long paintPtr);
+ @CriticalNative
+ private static native void nSetTextScaleX(long paintPtr, float scaleX);
+ @CriticalNative
+ private static native float nGetTextSkewX(long paintPtr);
+ @CriticalNative
+ private static native void nSetTextSkewX(long paintPtr, float skewX);
+ @CriticalNative
+ private static native float nAscent(long paintPtr, long typefacePtr);
+ @CriticalNative
+ private static native float nDescent(long paintPtr, long typefacePtr);
+ @CriticalNative
+ private static native void nSetTextSize(long paintPtr, float textSize);
}
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index be31bbe0c086..363137321f5f 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -19,6 +19,9 @@ package android.graphics;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
@@ -46,7 +49,7 @@ public class Path {
* Create an empty path
*/
public Path() {
- mNativePath = init1();
+ mNativePath = nInit();
}
/**
@@ -63,7 +66,7 @@ public class Path {
rects = new Region(src.rects);
}
}
- mNativePath = init2(valNative);
+ mNativePath = nInit(valNative);
}
/**
@@ -77,7 +80,7 @@ public class Path {
// 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);
+ nReset(mNativePath);
setFillType(fillType);
}
@@ -89,7 +92,7 @@ public class Path {
isSimplePath = true;
mLastDirection = null;
if (rects != null) rects.setEmpty();
- native_rewind(mNativePath);
+ nRewind(mNativePath);
}
/** Replace the contents of this with the contents of src.
@@ -99,7 +102,7 @@ public class Path {
return;
}
isSimplePath = src.isSimplePath;
- native_set(mNativePath, src.mNativePath);
+ nSet(mNativePath, src.mNativePath);
if (!isSimplePath) {
return;
}
@@ -174,7 +177,7 @@ public class Path {
* @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)) {
+ if (nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
isSimplePath = false;
rects = null;
return true;
@@ -194,7 +197,7 @@ public class Path {
* @return True if the path is convex.
*/
public boolean isConvex() {
- return native_isConvex(mNativePath);
+ return nIsConvex(mNativePath);
}
/**
@@ -243,7 +246,7 @@ public class Path {
* @return the path's fill type
*/
public FillType getFillType() {
- return sFillTypeArray[native_getFillType(mNativePath)];
+ return sFillTypeArray[nGetFillType(mNativePath)];
}
/**
@@ -252,7 +255,7 @@ public class Path {
* @param ft The new fill type for this path
*/
public void setFillType(FillType ft) {
- native_setFillType(mNativePath, ft.nativeInt);
+ nSetFillType(mNativePath, ft.nativeInt);
}
/**
@@ -261,7 +264,7 @@ public class Path {
* @return true if the filltype is one of the INVERSE variants
*/
public boolean isInverseFillType() {
- final int ft = native_getFillType(mNativePath);
+ final int ft = nGetFillType(mNativePath);
return (ft & FillType.INVERSE_WINDING.nativeInt) != 0;
}
@@ -269,9 +272,9 @@ public class Path {
* Toggles the INVERSE state of the filltype
*/
public void toggleInverseFillType() {
- int ft = native_getFillType(mNativePath);
+ int ft = nGetFillType(mNativePath);
ft ^= FillType.INVERSE_WINDING.nativeInt;
- native_setFillType(mNativePath, ft);
+ nSetFillType(mNativePath, ft);
}
/**
@@ -280,7 +283,7 @@ public class Path {
* @return true if the path is empty (contains no lines or curves)
*/
public boolean isEmpty() {
- return native_isEmpty(mNativePath);
+ return nIsEmpty(mNativePath);
}
/**
@@ -293,7 +296,7 @@ public class Path {
* @return true if the path specifies a rectangle
*/
public boolean isRect(RectF rect) {
- return native_isRect(mNativePath, rect);
+ return nIsRect(mNativePath, rect);
}
/**
@@ -306,7 +309,7 @@ public class Path {
*/
@SuppressWarnings({"UnusedDeclaration"})
public void computeBounds(RectF bounds, boolean exact) {
- native_computeBounds(mNativePath, bounds);
+ nComputeBounds(mNativePath, bounds);
}
/**
@@ -317,7 +320,7 @@ public class Path {
* path
*/
public void incReserve(int extraPtCount) {
- native_incReserve(mNativePath, extraPtCount);
+ nIncReserve(mNativePath, extraPtCount);
}
/**
@@ -327,7 +330,7 @@ public class Path {
* @param y The y-coordinate of the start of a new contour
*/
public void moveTo(float x, float y) {
- native_moveTo(mNativePath, x, y);
+ nMoveTo(mNativePath, x, y);
}
/**
@@ -341,7 +344,7 @@ public class Path {
* previous contour, to specify the start of a new contour
*/
public void rMoveTo(float dx, float dy) {
- native_rMoveTo(mNativePath, dx, dy);
+ nRMoveTo(mNativePath, dx, dy);
}
/**
@@ -354,7 +357,7 @@ public class Path {
*/
public void lineTo(float x, float y) {
isSimplePath = false;
- native_lineTo(mNativePath, x, y);
+ nLineTo(mNativePath, x, y);
}
/**
@@ -369,7 +372,7 @@ public class Path {
*/
public void rLineTo(float dx, float dy) {
isSimplePath = false;
- native_rLineTo(mNativePath, dx, dy);
+ nRLineTo(mNativePath, dx, dy);
}
/**
@@ -384,7 +387,7 @@ public class Path {
*/
public void quadTo(float x1, float y1, float x2, float y2) {
isSimplePath = false;
- native_quadTo(mNativePath, x1, y1, x2, y2);
+ nQuadTo(mNativePath, x1, y1, x2, y2);
}
/**
@@ -403,7 +406,7 @@ public class Path {
*/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
isSimplePath = false;
- native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2);
+ nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
/**
@@ -421,7 +424,7 @@ public class Path {
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
isSimplePath = false;
- native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
+ nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
/**
@@ -432,7 +435,7 @@ public class Path {
public void rCubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
isSimplePath = false;
- native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
+ nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
/**
@@ -483,7 +486,7 @@ public class Path {
public void arcTo(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean forceMoveTo) {
isSimplePath = false;
- native_arcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
+ nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
}
/**
@@ -492,7 +495,7 @@ public class Path {
*/
public void close() {
isSimplePath = false;
- native_close(mNativePath);
+ nClose(mNativePath);
}
/**
@@ -544,7 +547,7 @@ public class Path {
*/
public void addRect(float left, float top, float right, float bottom, Direction dir) {
detectSimplePath(left, top, right, bottom, dir);
- native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt);
+ nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
/**
@@ -564,7 +567,7 @@ public class Path {
*/
public void addOval(float left, float top, float right, float bottom, Direction dir) {
isSimplePath = false;
- native_addOval(mNativePath, left, top, right, bottom, dir.nativeInt);
+ nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
}
/**
@@ -577,7 +580,7 @@ public class Path {
*/
public void addCircle(float x, float y, float radius, Direction dir) {
isSimplePath = false;
- native_addCircle(mNativePath, x, y, radius, dir.nativeInt);
+ nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
}
/**
@@ -600,7 +603,7 @@ public class Path {
public void addArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle) {
isSimplePath = false;
- native_addArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
+ nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
}
/**
@@ -625,7 +628,7 @@ public class Path {
public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
Direction dir) {
isSimplePath = false;
- native_addRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
+ nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
}
/**
@@ -658,7 +661,7 @@ public class Path {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
isSimplePath = false;
- native_addRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
+ nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
}
/**
@@ -669,7 +672,7 @@ public class Path {
*/
public void addPath(Path src, float dx, float dy) {
isSimplePath = false;
- native_addPath(mNativePath, src.mNativePath, dx, dy);
+ nAddPath(mNativePath, src.mNativePath, dx, dy);
}
/**
@@ -679,7 +682,7 @@ public class Path {
*/
public void addPath(Path src) {
isSimplePath = false;
- native_addPath(mNativePath, src.mNativePath);
+ nAddPath(mNativePath, src.mNativePath);
}
/**
@@ -689,7 +692,7 @@ public class Path {
*/
public void addPath(Path src, Matrix matrix) {
if (!src.isSimplePath) isSimplePath = false;
- native_addPath(mNativePath, src.mNativePath, matrix.native_instance);
+ nAddPath(mNativePath, src.mNativePath, matrix.native_instance);
}
/**
@@ -725,7 +728,7 @@ public class Path {
} else {
isSimplePath = false;
}
- native_offset(mNativePath, dx, dy);
+ nOffset(mNativePath, dx, dy);
}
/**
@@ -736,7 +739,7 @@ public class Path {
*/
public void setLastPoint(float dx, float dy) {
isSimplePath = false;
- native_setLastPoint(mNativePath, dx, dy);
+ nSetLastPoint(mNativePath, dx, dy);
}
/**
@@ -753,7 +756,7 @@ public class Path {
dst.isSimplePath = false;
dstNative = dst.mNativePath;
}
- native_transform(mNativePath, matrix.native_instance, dstNative);
+ nTransform(mNativePath, matrix.native_instance, dstNative);
}
/**
@@ -763,19 +766,20 @@ public class Path {
*/
public void transform(Matrix matrix) {
isSimplePath = false;
- native_transform(mNativePath, matrix.native_instance);
+ nTransform(mNativePath, matrix.native_instance);
}
protected void finalize() throws Throwable {
try {
- finalizer(mNativePath);
+ nFinalize(mNativePath);
mNativePath = 0; // Other finalizers can still call us.
} finally {
super.finalize();
}
}
- final long readOnlyNI() {
+ /** @hide */
+ public final long readOnlyNI() {
return mNativePath;
}
@@ -800,62 +804,70 @@ public class Path {
* Path. Typically this would be 0.5 so that
* the error is less than half a pixel.
* @return An array of components for points approximating the Path.
- * @hide
*/
public float[] approximate(float acceptableError) {
- return native_approximate(mNativePath, acceptableError);
- }
-
- private static native long init1();
- private static native long init2(long nPath);
- private static native void native_reset(long nPath);
- private static native void native_rewind(long nPath);
- private static native void native_set(long native_dst, long native_src);
- private static native boolean native_isConvex(long nPath);
- private static native int native_getFillType(long nPath);
- private static native void native_setFillType(long nPath, int ft);
- private static native boolean native_isEmpty(long nPath);
- private static native boolean native_isRect(long nPath, RectF rect);
- private static native void native_computeBounds(long nPath, RectF bounds);
- private static native void native_incReserve(long nPath, int extraPtCount);
- private static native void native_moveTo(long nPath, float x, float y);
- private static native void native_rMoveTo(long nPath, float dx, float dy);
- private static native void native_lineTo(long nPath, float x, float y);
- private static native void native_rLineTo(long nPath, float dx, float dy);
- private static native void native_quadTo(long nPath, float x1, float y1,
- float x2, float y2);
- private static native void native_rQuadTo(long nPath, float dx1, float dy1,
- float dx2, float dy2);
- private static native void native_cubicTo(long nPath, float x1, float y1,
- float x2, float y2, float x3, float y3);
- private static native void native_rCubicTo(long nPath, float x1, float y1,
- float x2, float y2, float x3, float y3);
- private static native void native_arcTo(long nPath, float left, float top,
- float right, float bottom, float startAngle,
- float sweepAngle, boolean forceMoveTo);
- private static native void native_close(long nPath);
- private static native void native_addRect(long nPath, float left, float top,
- float right, float bottom, int dir);
- private static native void native_addOval(long nPath, float left, float top,
+ return nApproximate(mNativePath, acceptableError);
+ }
+
+ // ------------------ Regular JNI ------------------------
+
+ private static native long nInit();
+ private static native long nInit(long nPath);
+ private static native void nFinalize(long nPath);
+ private static native void nSet(long native_dst, long nSrc);
+ private static native void nComputeBounds(long nPath, RectF bounds);
+ private static native void nIncReserve(long nPath, int extraPtCount);
+ private static native void nMoveTo(long nPath, float x, float y);
+ private static native void nRMoveTo(long nPath, float dx, float dy);
+ private static native void nLineTo(long nPath, float x, float y);
+ private static native void nRLineTo(long nPath, float dx, float dy);
+ private static native void nQuadTo(long nPath, float x1, float y1, float x2, float y2);
+ private static native void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2);
+ private static native void nCubicTo(long nPath, float x1, float y1, float x2, float y2,
+ float x3, float y3);
+ private static native void nRCubicTo(long nPath, float x1, float y1, float x2, float y2,
+ float x3, float y3);
+ private static native void nArcTo(long nPath, float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, boolean forceMoveTo);
+ private static native void nClose(long nPath);
+ private static native void nAddRect(long nPath, float left, float top,
+ float right, float bottom, int dir);
+ private static native void nAddOval(long nPath, float left, float top,
float right, float bottom, int dir);
- private static native void native_addCircle(long nPath, float x, float y, float radius, int dir);
- private static native void native_addArc(long nPath, float left, float top,
- float right, float bottom,
- float startAngle, float sweepAngle);
- private static native void native_addRoundRect(long nPath, float left, float top,
- float right, float bottom,
- float rx, float ry, int dir);
- private static native void native_addRoundRect(long nPath, float left, float top,
- float right, float bottom,
- float[] radii, int dir);
- private static native void native_addPath(long nPath, long src, float dx, float dy);
- private static native void native_addPath(long nPath, long src);
- private static native void native_addPath(long nPath, long src, long matrix);
- private static native void native_offset(long nPath, float dx, float dy);
- private static native void native_setLastPoint(long nPath, float dx, float dy);
- private static native void native_transform(long nPath, long matrix, long dst_path);
- private static native void native_transform(long nPath, long matrix);
- private static native boolean native_op(long path1, long path2, int op, long result);
- private static native void finalizer(long nPath);
- private static native float[] native_approximate(long nPath, float error);
+ private static native void nAddCircle(long nPath, float x, float y, float radius, int dir);
+ private static native void nAddArc(long nPath, float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle);
+ private static native void nAddRoundRect(long nPath, float left, float top,
+ float right, float bottom, float rx, float ry, int dir);
+ private static native void nAddRoundRect(long nPath, float left, float top,
+ float right, float bottom, float[] radii, int dir);
+ private static native void nAddPath(long nPath, long src, float dx, float dy);
+ private static native void nAddPath(long nPath, long src);
+ private static native void nAddPath(long nPath, long src, long matrix);
+ private static native void nOffset(long nPath, float dx, float dy);
+ private static native void nSetLastPoint(long nPath, float dx, float dy);
+ private static native void nTransform(long nPath, long matrix, long dst_path);
+ private static native void nTransform(long nPath, long matrix);
+ private static native boolean nOp(long path1, long path2, int op, long result);
+ private static native float[] nApproximate(long nPath, float error);
+
+ // ------------------ Fast JNI ------------------------
+
+ @FastNative
+ private static native boolean nIsRect(long nPath, RectF rect);
+
+ // ------------------ Critical JNI ------------------------
+
+ @CriticalNative
+ private static native void nReset(long nPath);
+ @CriticalNative
+ private static native void nRewind(long nPath);
+ @CriticalNative
+ private static native boolean nIsEmpty(long nPath);
+ @CriticalNative
+ private static native boolean nIsConvex(long nPath);
+ @CriticalNative
+ private static native int nGetFillType(long nPath);
+ @CriticalNative
+ private static native void nSetFillType(long nPath, int ft);
}
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 78d892e94cf0..bf79656addc0 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -79,7 +79,7 @@ public class PathMeasure {
* are unchanged.
*
* @param distance The distance along the current contour to sample
- * @param pos If not null, eturns the sampled position (x==[0], y==[1])
+ * @param pos If not null, returns the sampled position (x==[0], y==[1])
* @param tan If not null, returns the sampled tangent (x==[0], y==[1])
* @return false if there was no path associated with this measure object
*/
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index d9d768907c61..51044103003b 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -18,19 +18,11 @@ package android.graphics;
public class PorterDuffXfermode extends Xfermode {
/**
- * @hide
- */
- public final PorterDuff.Mode mode;
-
- /**
* Create an xfermode that uses the specified porter-duff mode.
*
* @param mode The porter-duff mode that is applied
*/
public PorterDuffXfermode(PorterDuff.Mode mode) {
- this.mode = mode;
- native_instance = nativeCreateXfermode(mode.nativeInt);
+ porterDuffMode = mode.nativeInt;
}
-
- private static native long nativeCreateXfermode(int mode);
}
diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java
index f6a38fe89eab..29d82fa1d98b 100644
--- a/graphics/java/android/graphics/Rasterizer.java
+++ b/graphics/java/android/graphics/Rasterizer.java
@@ -21,15 +21,10 @@
package android.graphics;
-@Deprecated
+/**
+ * @removed feature is not supported by hw-accerlerated or PDF backends
+ */
public class Rasterizer {
- protected void finalize() throws Throwable {
- finalizer(native_instance);
- native_instance = 0;
- }
-
- private static native void finalizer(long native_instance);
-
- long native_instance;
+ protected void finalize() throws Throwable { }
}
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index c3861083e2fd..efb46b90e399 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -156,7 +156,6 @@ public class SurfaceTexture {
* @param singleBufferMode whether the SurfaceTexture will be in single buffered mode.
*
* @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
- * @hide
*/
public SurfaceTexture(boolean singleBufferMode) {
mCreatorLooper = Looper.myLooper();
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index c049e41c65ff..a5da5d09ebaf 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -29,17 +29,6 @@ package android.graphics;
* objects drawn with that paint have the xfermode applied.
*/
public class Xfermode {
-
- protected void finalize() throws Throwable {
- try {
- finalizer(native_instance);
- native_instance = 0;
- } finally {
- super.finalize();
- }
- }
-
- private static native void finalizer(long native_instance);
-
- long native_instance;
+ static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
+ int porterDuffMode = DEFAULT;
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 659725ef59f6..00b5eda6d7ff 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -64,6 +64,8 @@ import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import dalvik.annotation.optimization.FastNative;
+
/**
* This class animates properties of a {@link android.graphics.drawable.VectorDrawable} with
* animations defined using {@link android.animation.ObjectAnimator} or
@@ -1791,22 +1793,30 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
long nativeInterpolator, long startDelay, long duration, int repeatCount,
int repeatMode);
+ private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
+ private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
+ private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
+ private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
+
+ // ------------- @FastNative -------------------
+ @FastNative
private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
float startValue, float endValue);
-
+ @FastNative
private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
long endValuePtr);
+ @FastNative
private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
int startValue, int endValue);
+ @FastNative
private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
float startValue, float endValue);
+ @FastNative
private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
float endValue);
- private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
- private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
- private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
- private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
+ @FastNative
private static native void nEnd(long animatorSetPtr);
+ @FastNative
private static native void nReset(long animatorSetPtr);
}
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index df107f5ce3ef..6deeb0d9af9a 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -49,7 +49,6 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.util.Collection;
/**
* A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
@@ -957,14 +956,6 @@ public class BitmapDrawable extends Drawable {
}
@Override
- public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
- if (isAtlasable(mBitmap) && atlasList.add(mBitmap)) {
- return mBitmap.getWidth() * mBitmap.getHeight();
- }
- return 0;
- }
-
- @Override
public Drawable newDrawable() {
return new BitmapDrawable(this, null);
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index c2e302e184c9..6ddc2d7e192f 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -58,7 +58,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Arrays;
-import java.util.Collection;
/**
* A Drawable is a general abstraction for "something that can be drawn." Most
@@ -576,6 +575,12 @@ public abstract class Drawable {
* </p>
*/
public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
+ if (getColorFilter() instanceof PorterDuffColorFilter) {
+ PorterDuffColorFilter existing = (PorterDuffColorFilter) getColorFilter();
+ if (existing.getColor() == color && existing.getMode() == mode) {
+ return;
+ }
+ }
setColorFilter(new PorterDuffColorFilter(color, mode));
}
@@ -1361,19 +1366,6 @@ public abstract class Drawable {
public abstract @Config int getChangingConfigurations();
/**
- * @return Total pixel count
- * @hide
- */
- public int addAtlasableBitmaps(@NonNull Collection<Bitmap> atlasList) {
- return 0;
- }
-
- /** @hide */
- protected final boolean isAtlasable(@Nullable Bitmap bitmap) {
- return bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888;
- }
-
- /**
* Return whether this constant state can have a theme applied.
*/
public boolean canApplyTheme() {
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index c7a3c75f3545..d88aee93fae2 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -21,7 +21,6 @@ import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Insets;
@@ -35,8 +34,6 @@ import android.util.LayoutDirection;
import android.util.SparseArray;
import android.view.View;
-import java.util.Collection;
-
/**
* A helper class that contains several {@link Drawable}s and selects which one to use.
*
@@ -1131,8 +1128,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
createAllFutures();
- mCheckedOpacity = true;
-
final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
int op = (N > 0) ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
@@ -1141,6 +1136,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
mOpacity = op;
+ mCheckedOpacity = true;
return op;
}
@@ -1151,19 +1147,19 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
createAllFutures();
- mCheckedStateful = true;
-
final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
+ boolean isStateful = false;
for (int i = 0; i < N; i++) {
if (drawables[i].isStateful()) {
- mStateful = true;
- return true;
+ isStateful = true;
+ break;
}
}
- mStateful = false;
- return false;
+ mStateful = isStateful;
+ mCheckedStateful = true;
+ return isStateful;
}
public void growArray(int oldSize, int newSize) {
@@ -1194,19 +1190,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return true;
}
- /** @hide */
- @Override
- public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
- final int N = mNumChildren;
- int pixelCount = 0;
- for (int i = 0; i < N; i++) {
- final ConstantState state = getChild(i).getConstantState();
- if (state != null) {
- pixelCount += state.addAtlasableBitmaps(atlasList);
- }
- }
- return pixelCount;
- }
}
protected void setConstantState(DrawableContainerState state) {
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 5abfc54bfce3..5887939b05a4 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -28,7 +28,6 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Insets;
@@ -41,7 +40,6 @@ import android.util.DisplayMetrics;
import android.view.View;
import java.io.IOException;
-import java.util.Collection;
/**
* Drawable container with only one child element.
@@ -508,15 +506,6 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
}
@Override
- public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
- final Drawable.ConstantState state = mDrawableState;
- if (state != null) {
- return state.addAtlasableBitmaps(atlasList);
- }
- return 0;
- }
-
- @Override
public Drawable newDrawable() {
return newDrawable(null);
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 8c633b0da93a..7aad7df05fed 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -282,10 +282,11 @@ public class GradientDrawable extends Drawable {
}
/**
- * Returns the radius for the corners of the gradient.
+ * Returns the radius for the corners of the gradient, that was previously set with
+ * {@link #setCornerRadius(float)}.
* <p>
- * If the radius was previously set with {@link #setCornerRadii(float[])},
- * or if the corners are not rounded, this method will return {@code null}.
+ * If the radius was previously cleared via passing {@code null}
+ * to {@link #setCornerRadii(float[])}, this method will return 0.
*
* @return the radius in pixels of the corners of the rectangle shape, or 0
* @see #setCornerRadius
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 2b950d3d5c5d..977200906d62 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -299,7 +299,7 @@ public final class Icon implements Parcelable {
final PackageManager pm = context.getPackageManager();
try {
ApplicationInfo ai = pm.getApplicationInfo(
- resPackage, PackageManager.GET_UNINSTALLED_PACKAGES);
+ resPackage, PackageManager.MATCH_UNINSTALLED_PACKAGES);
if (ai != null) {
mObj1 = pm.getResourcesForApplication(ai);
} else {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index c30c4c2f02d6..355e45e70a6b 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -23,7 +23,6 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Outline;
@@ -42,7 +41,6 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.util.Collection;
/**
* A Drawable that manages an array of other Drawables. These are drawn in array
@@ -139,7 +137,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
layers[i].setCallback(this);
mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations();
}
- mLayerState.mNum = length;
+ mLayerState.mNumChildren = length;
mLayerState.mChildren = r;
ensurePadding();
@@ -156,7 +154,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
*/
LayerDrawable(@Nullable LayerState state, @Nullable Resources res) {
mLayerState = createConstantState(state, res);
- if (mLayerState.mNum > 0) {
+ if (mLayerState.mNumChildren > 0) {
ensurePadding();
refreshPadding();
}
@@ -187,7 +185,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
a.recycle();
final ChildDrawable[] array = state.mChildren;
- final int N = state.mNum;
+ final int N = state.mNumChildren;
for (int i = 0; i < N; i++) {
final ChildDrawable layer = array[i];
layer.setDensity(density);
@@ -219,7 +217,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
final ChildDrawable[] array = state.mChildren;
- final int N = state.mNum;
+ final int N = state.mNumChildren;
for (int i = 0; i < N; i++) {
final ChildDrawable layer = array[i];
layer.setDensity(density);
@@ -418,7 +416,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
final ChildDrawable[] layers = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
if (layers[i].mDrawable.isProjected()) {
return true;
@@ -437,7 +435,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
int addLayer(@NonNull ChildDrawable layer) {
final LayerState st = mLayerState;
final int N = st.mChildren != null ? st.mChildren.length : 0;
- final int i = st.mNum;
+ final int i = st.mNumChildren;
if (i >= N) {
final ChildDrawable[] nu = new ChildDrawable[N + 10];
if (i > 0) {
@@ -448,7 +446,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
st.mChildren[i] = layer;
- st.mNum++;
+ st.mNumChildren++;
st.invalidateCache();
return i;
}
@@ -516,7 +514,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
*/
public Drawable findDrawableByLayerId(int id) {
final ChildDrawable[] layers = mLayerState.mChildren;
- for (int i = mLayerState.mNum - 1; i >= 0; i--) {
+ for (int i = mLayerState.mNumChildren - 1; i >= 0; i--) {
if (layers[i].mId == id) {
return layers[i].mDrawable;
}
@@ -551,7 +549,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* @attr ref android.R.styleable#LayerDrawableItem_id
*/
public int getId(int index) {
- if (index >= mLayerState.mNum) {
+ if (index >= mLayerState.mNumChildren) {
throw new IndexOutOfBoundsException();
}
return mLayerState.mChildren[index].mId;
@@ -563,7 +561,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* @return The number of layers.
*/
public int getNumberOfLayers() {
- return mLayerState.mNum;
+ return mLayerState.mNumChildren;
}
/**
@@ -595,7 +593,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
*/
public int findIndexByLayerId(int id) {
final ChildDrawable[] layers = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final ChildDrawable childDrawable = layers[i];
if (childDrawable.mId == id) {
@@ -617,7 +615,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* @attr ref android.R.styleable#LayerDrawableItem_drawable
*/
public void setDrawable(int index, Drawable drawable) {
- if (index >= mLayerState.mNum) {
+ if (index >= mLayerState.mNumChildren) {
throw new IndexOutOfBoundsException();
}
@@ -653,7 +651,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* @attr ref android.R.styleable#LayerDrawableItem_drawable
*/
public Drawable getDrawable(int index) {
- if (index >= mLayerState.mNum) {
+ if (index >= mLayerState.mNumChildren) {
throw new IndexOutOfBoundsException();
}
return mLayerState.mChildren[index].mDrawable;
@@ -1005,7 +1003,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void draw(Canvas canvas) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1232,7 +1230,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
// Add all the padding.
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
refreshChildPadding(i, array[i]);
@@ -1251,7 +1249,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
// Take the max padding.
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
refreshChildPadding(i, array[i]);
@@ -1270,7 +1268,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void getOutline(@NonNull Outline outline) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1285,7 +1283,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void setHotspot(float x, float y) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1297,7 +1295,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void setHotspotBounds(int left, int top, int right, int bottom) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1325,7 +1323,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
public boolean setVisible(boolean visible, boolean restart) {
final boolean changed = super.setVisible(visible, restart);
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1339,7 +1337,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void setDither(boolean dither) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1351,7 +1349,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void setAlpha(int alpha) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1373,7 +1371,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void setColorFilter(ColorFilter colorFilter) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1385,7 +1383,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void setTintList(ColorStateList tint) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1397,7 +1395,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void setTintMode(Mode tintMode) {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1408,7 +1406,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
private Drawable getFirstNonNullDrawable() {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1446,7 +1444,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
mLayerState.mAutoMirrored = mirrored;
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1463,7 +1461,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Override
public void jumpToCurrentState() {
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1482,7 +1480,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
boolean changed = false;
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null && dr.isStateful() && dr.setState(state)) {
@@ -1503,7 +1501,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
boolean changed = false;
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null && dr.setLevel(level)) {
@@ -1545,7 +1543,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST;
final ChildDrawable[] array = mLayerState.mChildren;
- for (int i = 0, count = mLayerState.mNum; i < count; i++) {
+ for (int i = 0, count = mLayerState.mNumChildren; i < count; i++) {
final ChildDrawable r = array[i];
final Drawable d = r.mDrawable;
if (d == null) {
@@ -1644,7 +1642,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final ChildDrawable r = array[i];
if (r.mDrawable == null) {
@@ -1686,7 +1684,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final ChildDrawable r = array[i];
if (r.mDrawable == null) {
@@ -1735,7 +1733,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* Ensures the child padding caches are large enough.
*/
void ensurePadding() {
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
if (mPaddingL != null && mPaddingL.length >= N) {
return;
}
@@ -1747,7 +1745,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
void refreshPadding() {
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
final ChildDrawable[] array = mLayerState.mChildren;
for (int i = 0; i < N; i++) {
refreshChildPadding(i, array[i]);
@@ -1768,7 +1766,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
if (!mMutated && super.mutate() == this) {
mLayerState = createConstantState(mLayerState, null);
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1787,7 +1785,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
super.clearMutated();
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1802,7 +1800,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
boolean changed = false;
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
+ final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null) {
@@ -1907,7 +1905,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
static class LayerState extends ConstantState {
private int[] mThemeAttrs;
- int mNum;
+ int mNumChildren;
ChildDrawable[] mChildren;
int mDensity;
@@ -1924,10 +1922,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
@Config int mChangingConfigurations;
@Config int mChildrenChangingConfigurations;
- private boolean mHaveOpacity;
+ private boolean mCheckedOpacity;
private int mOpacity;
- private boolean mHaveIsStateful;
+ private boolean mCheckedStateful;
private boolean mIsStateful;
private boolean mAutoMirrored = false;
@@ -1940,9 +1938,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
if (orig != null) {
final ChildDrawable[] origChildDrawable = orig.mChildren;
- final int N = orig.mNum;
+ final int N = orig.mNumChildren;
- mNum = N;
+ mNumChildren = N;
mChildren = new ChildDrawable[N];
mChangingConfigurations = orig.mChangingConfigurations;
@@ -1953,9 +1951,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
mChildren[i] = new ChildDrawable(or, owner, res);
}
- mHaveOpacity = orig.mHaveOpacity;
+ mCheckedOpacity = orig.mCheckedOpacity;
mOpacity = orig.mOpacity;
- mHaveIsStateful = orig.mHaveIsStateful;
+ mCheckedStateful = orig.mCheckedStateful;
mIsStateful = orig.mIsStateful;
mAutoMirrored = orig.mAutoMirrored;
mPaddingMode = orig.mPaddingMode;
@@ -1972,7 +1970,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
applyDensityScaling(orig.mDensity, mDensity);
}
} else {
- mNum = 0;
+ mNumChildren = 0;
mChildren = null;
}
}
@@ -2024,7 +2022,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
final ChildDrawable[] array = mChildren;
- final int N = mNum;
+ final int N = mNumChildren;
for (int i = 0; i < N; i++) {
final ChildDrawable layer = array[i];
if (layer.canApplyTheme()) {
@@ -2052,12 +2050,12 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
public final int getOpacity() {
- if (mHaveOpacity) {
+ if (mCheckedOpacity) {
return mOpacity;
}
+ final int N = mNumChildren;
final ChildDrawable[] array = mChildren;
- final int N = mNum;
// Seek to the first non-null drawable.
int firstIndex = -1;
@@ -2084,17 +2082,17 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
mOpacity = op;
- mHaveOpacity = true;
+ mCheckedOpacity = true;
return op;
}
public final boolean isStateful() {
- if (mHaveIsStateful) {
+ if (mCheckedStateful) {
return mIsStateful;
}
+ final int N = mNumChildren;
final ChildDrawable[] array = mChildren;
- final int N = mNum;
boolean isStateful = false;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
@@ -2105,13 +2103,13 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
mIsStateful = isStateful;
- mHaveIsStateful = true;
+ mCheckedStateful = true;
return isStateful;
}
public final boolean canConstantState() {
final ChildDrawable[] array = mChildren;
- final int N = mNum;
+ final int N = mNumChildren;
for (int i = 0; i < N; i++) {
final Drawable dr = array[i].mDrawable;
if (dr != null && dr.getConstantState() == null) {
@@ -2124,26 +2122,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
public void invalidateCache() {
- mHaveOpacity = false;
- mHaveIsStateful = false;
+ mCheckedOpacity = false;
+ mCheckedStateful = false;
}
- @Override
- public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
- final ChildDrawable[] array = mChildren;
- final int N = mNum;
- int pixelCount = 0;
- for (int i = 0; i < N; i++) {
- final Drawable dr = array[i].mDrawable;
- if (dr != null) {
- final ConstantState state = dr.getConstantState();
- if (state != null) {
- pixelCount += state.addAtlasableBitmaps(atlasList);
- }
- }
- }
- return pixelCount;
- }
}
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index d96238521f94..c7183d9e8f50 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -49,7 +49,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collection;
/**
*
@@ -633,15 +632,6 @@ public class NinePatchDrawable extends Drawable {
}
@Override
- public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
- final Bitmap bitmap = mNinePatch.getBitmap();
- if (isAtlasable(bitmap) && atlasList.add(bitmap)) {
- return bitmap.getWidth() * bitmap.getHeight();
- }
- return 0;
- }
-
- @Override
public Drawable newDrawable() {
return new NinePatchDrawable(this, null);
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index caf2e7a4cb4c..f83c160b6c20 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -667,7 +667,7 @@ public class RippleDrawable extends LayerDrawable {
public void getOutline(@NonNull Outline outline) {
final LayerState state = mLayerState;
final ChildDrawable[] children = state.mChildren;
- final int N = state.mNum;
+ final int N = state.mNumChildren;
for (int i = 0; i < N; i++) {
if (children[i].mId != R.id.mask) {
children[i].mDrawable.getOutline(outline);
@@ -815,7 +815,7 @@ public class RippleDrawable extends LayerDrawable {
// Check for non-opaque, non-mask content.
final ChildDrawable[] array = mLayerState.mChildren;
- final int count = mLayerState.mNum;
+ final int count = mLayerState.mNumChildren;
for (int i = 0; i < count; i++) {
if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
return MASK_CONTENT;
@@ -829,7 +829,7 @@ public class RippleDrawable extends LayerDrawable {
private void drawContent(Canvas canvas) {
// Draw everything except the mask.
final ChildDrawable[] array = mLayerState.mChildren;
- final int count = mLayerState.mNum;
+ final int count = mLayerState.mNumChildren;
for (int i = 0; i < count; i++) {
if (array[i].mId != R.id.mask) {
array[i].mDrawable.draw(canvas);
@@ -1045,7 +1045,7 @@ public class RippleDrawable extends LayerDrawable {
mLayerState = mState;
mDensity = Drawable.resolveDensity(res, mState.mDensity);
- if (mState.mNum > 0) {
+ if (mState.mNumChildren > 0) {
ensurePadding();
refreshPadding();
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index d644beeb7d37..3a12419c8774 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -55,6 +55,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
+import dalvik.annotation.optimization.FastNative;
import dalvik.system.VMRuntime;
/**
@@ -2119,41 +2120,61 @@ public class VectorDrawable extends Drawable {
abstract Property getProperty(String propertyName);
}
+ private static native int nDraw(long rendererPtr, long canvasWrapperPtr,
+ long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
+ private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
+ int length);
+ private static native void nSetName(long nodePtr, String name);
+ private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
+ int length);
+ private static native void nSetPathString(long pathPtr, String pathString, int length);
+
+ // ------------- @FastNative ------------------
+
+ @FastNative
private static native long nCreateTree(long rootGroupPtr);
+ @FastNative
private static native long nCreateTreeFromCopy(long treeToCopy, long rootGroupPtr);
+ @FastNative
private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
float viewportHeight);
+ @FastNative
private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
+ @FastNative
private static native float nGetRootAlpha(long rendererPtr);
+ @FastNative
private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
- private static native int nDraw(long rendererPtr, long canvasWrapperPtr,
- long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
+ @FastNative
private static native long nCreateFullPath();
+ @FastNative
private static native long nCreateFullPath(long nativeFullPathPtr);
- private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
- int length);
+ @FastNative
private static native void nUpdateFullPathProperties(long pathPtr, float strokeWidth,
int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
int strokeLineJoin, int fillType);
+ @FastNative
private static native void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr);
+ @FastNative
private static native void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr);
+ @FastNative
private static native long nCreateClipPath();
+ @FastNative
private static native long nCreateClipPath(long clipPathPtr);
+ @FastNative
private static native long nCreateGroup();
+ @FastNative
private static native long nCreateGroup(long groupPtr);
- private static native void nSetName(long nodePtr, String name);
- private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
- int length);
+ @FastNative
private static native void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
float pivotY, float scaleX, float scaleY, float translateX, float translateY);
+ @FastNative
private static native void nAddChild(long groupPtr, long nodePtr);
- private static native void nSetPathString(long pathPtr, String pathString, int length);
/**
* The setters and getters below for paths and groups are here temporarily, and will be
@@ -2162,37 +2183,68 @@ public class VectorDrawable extends Drawable {
* for VD during animation, and these setters and getters will be obsolete.
*/
// Setters and getters during animation.
+ @FastNative
private static native float nGetRotation(long groupPtr);
+ @FastNative
private static native void nSetRotation(long groupPtr, float rotation);
+ @FastNative
private static native float nGetPivotX(long groupPtr);
+ @FastNative
private static native void nSetPivotX(long groupPtr, float pivotX);
+ @FastNative
private static native float nGetPivotY(long groupPtr);
+ @FastNative
private static native void nSetPivotY(long groupPtr, float pivotY);
+ @FastNative
private static native float nGetScaleX(long groupPtr);
+ @FastNative
private static native void nSetScaleX(long groupPtr, float scaleX);
+ @FastNative
private static native float nGetScaleY(long groupPtr);
+ @FastNative
private static native void nSetScaleY(long groupPtr, float scaleY);
+ @FastNative
private static native float nGetTranslateX(long groupPtr);
+ @FastNative
private static native void nSetTranslateX(long groupPtr, float translateX);
+ @FastNative
private static native float nGetTranslateY(long groupPtr);
+ @FastNative
private static native void nSetTranslateY(long groupPtr, float translateY);
// Setters and getters for VPath during animation.
+ @FastNative
private static native void nSetPathData(long pathPtr, long pathDataPtr);
+ @FastNative
private static native float nGetStrokeWidth(long pathPtr);
+ @FastNative
private static native void nSetStrokeWidth(long pathPtr, float width);
+ @FastNative
private static native int nGetStrokeColor(long pathPtr);
+ @FastNative
private static native void nSetStrokeColor(long pathPtr, int strokeColor);
+ @FastNative
private static native float nGetStrokeAlpha(long pathPtr);
+ @FastNative
private static native void nSetStrokeAlpha(long pathPtr, float alpha);
+ @FastNative
private static native int nGetFillColor(long pathPtr);
+ @FastNative
private static native void nSetFillColor(long pathPtr, int fillColor);
+ @FastNative
private static native float nGetFillAlpha(long pathPtr);
+ @FastNative
private static native void nSetFillAlpha(long pathPtr, float fillAlpha);
+ @FastNative
private static native float nGetTrimPathStart(long pathPtr);
+ @FastNative
private static native void nSetTrimPathStart(long pathPtr, float trimPathStart);
+ @FastNative
private static native float nGetTrimPathEnd(long pathPtr);
+ @FastNative
private static native void nSetTrimPathEnd(long pathPtr, float trimPathEnd);
+ @FastNative
private static native float nGetTrimPathOffset(long pathPtr);
+ @FastNative
private static native void nSetTrimPathOffset(long pathPtr, float trimPathOffset);
}
diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java
index cfc130990e92..99a042261c4d 100644
--- a/graphics/java/android/graphics/pdf/PdfRenderer.java
+++ b/graphics/java/android/graphics/pdf/PdfRenderer.java
@@ -27,6 +27,7 @@ import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.OsConstants;
+import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import libcore.io.Libcore;
@@ -163,7 +164,12 @@ public final class PdfRenderer implements AutoCloseable {
synchronized (sPdfiumLock) {
mNativeDocument = nativeCreate(mInput.getFd(), size);
- mPageCount = nativeGetPageCount(mNativeDocument);
+ try {
+ mPageCount = nativeGetPageCount(mNativeDocument);
+ } catch (Throwable t) {
+ nativeClose(mNativeDocument);
+ throw t;
+ }
}
mCloseGuard.open("close");
@@ -368,6 +374,12 @@ public final class PdfRenderer implements AutoCloseable {
*/
public void render(@NonNull Bitmap destination, @Nullable Rect destClip,
@Nullable Matrix transform, @RenderMode int renderMode) {
+ if (mNativePage == 0) {
+ throw new NullPointerException();
+ }
+
+ destination = Preconditions.checkNotNull(destination, "bitmap null");
+
if (destination.getConfig() != Config.ARGB_8888) {
throw new IllegalArgumentException("Unsupported pixel format");
}
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 29bf963842da..a14609f3e924 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -18,8 +18,11 @@ package android.view;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.Handler;
+import android.view.ViewTreeObserver.OnDrawListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -105,6 +108,32 @@ public final class PixelCopy {
}
/**
+ * Requests for the display content of a {@link SurfaceView} to be copied
+ * into a provided {@link Bitmap}.
+ *
+ * The contents of the source will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the SurfaceView's Surface will be used as the source of the copy.
+ *
+ * @param source The source from which to copy
+ * @param srcRect The area of the source to copy from. If this is null
+ * the copy area will be the entire surface. The rect will be clamped to
+ * the bounds of the Surface.
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull SurfaceView source, @Nullable Rect srcRect,
+ @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
+ @NonNull Handler listenerThread) {
+ request(source.getHolder().getSurface(), srcRect,
+ dest, listener, listenerThread);
+ }
+
+ /**
* Requests a copy of the pixels from a {@link Surface} to be copied into
* a provided {@link Bitmap}.
*
@@ -122,12 +151,40 @@ public final class PixelCopy {
*/
public static void request(@NonNull Surface source, @NonNull Bitmap dest,
@NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
+ request(source, null, dest, listener, listenerThread);
+ }
+
+ /**
+ * Requests a copy of the pixels at the provided {@link Rect} from
+ * a {@link Surface} to be copied into a provided {@link Bitmap}.
+ *
+ * The contents of the source rect will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the Surface will be used as the source of the copy.
+ *
+ * @param source The source from which to copy
+ * @param srcRect The area of the source to copy from. If this is null
+ * the copy area will be the entire surface. The rect will be clamped to
+ * the bounds of the Surface.
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull Surface source, @Nullable Rect srcRect,
+ @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
+ @NonNull Handler listenerThread) {
validateBitmapDest(dest);
if (!source.isValid()) {
throw new IllegalArgumentException("Surface isn't valid, source.isValid() == false");
}
+ if (srcRect != null && srcRect.isEmpty()) {
+ throw new IllegalArgumentException("sourceRect is empty");
+ }
// TODO: Make this actually async and fast and cool and stuff
- int result = ThreadedRenderer.copySurfaceInto(source, dest);
+ int result = ThreadedRenderer.copySurfaceInto(source, srcRect, dest);
listenerThread.post(new Runnable() {
@Override
public void run() {
@@ -136,6 +193,86 @@ public final class PixelCopy {
});
}
+ /**
+ * Requests a copy of the pixels from a {@link Window} to be copied into
+ * a provided {@link Bitmap}.
+ *
+ * The contents of the source will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the Window's Surface will be used as the source of the copy.
+ *
+ * Note: This is limited to being able to copy from Window's with a non-null
+ * DecorView. If {@link Window#peekDecorView()} is null this throws an
+ * {@link IllegalArgumentException}. It will similarly throw an exception
+ * if the DecorView has not yet acquired a backing surface. It is recommended
+ * that {@link OnDrawListener} is used to ensure that at least one draw
+ * has happened before trying to copy from the window, otherwise either
+ * an {@link IllegalArgumentException} will be thrown or an error will
+ * be returned to the {@link OnPixelCopyFinishedListener}.
+ *
+ * @param source The source from which to copy
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull Window source, @NonNull Bitmap dest,
+ @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
+ request(source, null, dest, listener, listenerThread);
+ }
+
+ /**
+ * Requests a copy of the pixels at the provided {@link Rect} from
+ * a {@link Window} to be copied into a provided {@link Bitmap}.
+ *
+ * The contents of the source rect will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the Window's Surface will be used as the source of the copy.
+ *
+ * Note: This is limited to being able to copy from Window's with a non-null
+ * DecorView. If {@link Window#peekDecorView()} is null this throws an
+ * {@link IllegalArgumentException}. It will similarly throw an exception
+ * if the DecorView has not yet acquired a backing surface. It is recommended
+ * that {@link OnDrawListener} is used to ensure that at least one draw
+ * has happened before trying to copy from the window, otherwise either
+ * an {@link IllegalArgumentException} will be thrown or an error will
+ * be returned to the {@link OnPixelCopyFinishedListener}.
+ *
+ * @param source The source from which to copy
+ * @param srcRect The area of the source to copy from. If this is null
+ * the copy area will be the entire surface. The rect will be clamped to
+ * the bounds of the Surface.
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull Window source, @Nullable Rect srcRect,
+ @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
+ @NonNull Handler listenerThread) {
+ validateBitmapDest(dest);
+ if (source == null) {
+ throw new IllegalArgumentException("source is null");
+ }
+ if (source.peekDecorView() == null) {
+ throw new IllegalArgumentException(
+ "Only able to copy windows with decor views");
+ }
+ Surface surface = null;
+ if (source.peekDecorView().getViewRootImpl() != null) {
+ surface = source.peekDecorView().getViewRootImpl().mSurface;
+ }
+ if (surface == null || !surface.isValid()) {
+ throw new IllegalArgumentException(
+ "Window doesn't have a backing surface!");
+ }
+ request(surface, srcRect, dest, listener, listenerThread);
+ }
+
private static void validateBitmapDest(Bitmap bitmap) {
// TODO: Pre-check max texture dimens if we can
if (bitmap == null) {