summaryrefslogtreecommitdiff
path: root/graphics/java/android
diff options
context:
space:
mode:
author Xin Li <delphij@google.com> 2024-05-24 08:27:25 -0700
committer Xin Li <delphij@google.com> 2024-05-24 11:35:17 -0700
commitfe285426c4f9149537bdd7cb351b02ec102e6082 (patch)
treea50559e0969480b7f613fc086e380a290ee3d6b1 /graphics/java/android
parente2447a31771717fe6259043760ba02484f09d4da (diff)
parented6f98ac9f4049f370e1db86e1b4e141bb83f5cc (diff)
Merge Android 24Q2 Release (ab/11526283) to aosp-main-future
Bug: 337098550 Merged-In: Ie71e752f0224aa239ba1350d50996ce4b510949a Change-Id: Ib25c1abf055b0114e0494088df5585f65df27595
Diffstat (limited to 'graphics/java/android')
-rw-r--r--graphics/java/android/framework_graphics.aconfig2
-rw-r--r--graphics/java/android/graphics/BaseRecordingCanvas.java3
-rw-r--r--graphics/java/android/graphics/Canvas.java60
-rw-r--r--graphics/java/android/graphics/FontListParser.java62
-rw-r--r--graphics/java/android/graphics/Matrix44.java472
-rw-r--r--graphics/java/android/graphics/Paint.java214
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java2
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java58
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java72
-rw-r--r--graphics/java/android/graphics/pdf/TEST_MAPPING7
-rw-r--r--graphics/java/android/graphics/text/LineBreakConfig.java57
-rw-r--r--graphics/java/android/graphics/text/LineBreaker.java10
-rw-r--r--graphics/java/android/graphics/text/MeasuredText.java4
13 files changed, 900 insertions, 123 deletions
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
index f13f3698f9f6..251eade5d4a2 100644
--- a/graphics/java/android/framework_graphics.aconfig
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -13,4 +13,4 @@ flag {
namespace: "core_graphics"
description: "Feature flag for YUV image compress to Ultra HDR."
bug: "308978825"
-} \ No newline at end of file
+}
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index d659ddd75f72..4e88b0efd3e5 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -607,7 +607,8 @@ public class BaseRecordingCanvas extends Canvas {
}
@Override
- public final void drawMesh(@NonNull Mesh mesh, BlendMode blendMode, @NonNull Paint paint) {
+ public final void drawMesh(@NonNull Mesh mesh, @Nullable BlendMode blendMode,
+ @NonNull Paint paint) {
if (blendMode == null) {
blendMode = BlendMode.MODULATE;
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 5291f7b4c16c..b60f84d919f7 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -18,6 +18,7 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -30,6 +31,8 @@ import android.graphics.text.TextRunShaper;
import android.os.Build;
import android.text.TextShaper;
+import com.android.graphics.hwui.flags.Flags;
+
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -150,6 +153,18 @@ public class Canvas extends BaseCanvas {
}
/**
+ * Indicates whether this Canvas is drawing high contrast text.
+ *
+ * @see android.view.accessibility.AccessibilityManager#isHighTextContrastEnabled()
+ * @return True if high contrast text is enabled, false otherwise.
+ *
+ * @hide
+ */
+ public boolean isHighContrastTextEnabled() {
+ return nIsHighContrastText(mNativeCanvasWrapper);
+ }
+
+ /**
* Specify a bitmap for the canvas to draw into. All canvas state such as
* layers, filters, and the save/restore stack are reset. Additionally,
* the canvas' target density is updated to match that of the bitmap.
@@ -766,6 +781,21 @@ public class Canvas extends BaseCanvas {
}
/**
+ * Preconcat the current matrix with the specified matrix. If the specified
+ * matrix is null, this method does nothing. If the canvas's matrix is changed in the z-axis
+ * through this function, the deprecated {@link #getMatrix()} method will return a 3x3 with
+ * z-axis info stripped away.
+ *
+ * @param m The 4x4 matrix to preconcatenate with the current matrix
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public void concat44(@Nullable Matrix44 m) {
+ if (m != null) {
+ nConcat(mNativeCanvasWrapper, m.mBackingArray);
+ }
+ }
+
+ /**
* Completely replace the current matrix with the specified matrix. If the
* matrix parameter is null, then the current matrix is reset to identity.
*
@@ -1110,6 +1140,30 @@ public class Canvas extends BaseCanvas {
return false;
}
+ /**
+ * Intersect the current clip with the specified shader.
+ * The shader will be treated as an alpha mask, taking the intersection of the two.
+ *
+ * @param shader The shader to intersect with the current clip
+ */
+ @FlaggedApi(Flags.FLAG_CLIP_SHADER)
+ public void clipShader(@NonNull Shader shader) {
+ nClipShader(mNativeCanvasWrapper, shader.getNativeInstance(),
+ Region.Op.INTERSECT.nativeInt);
+ }
+
+ /**
+ * Set the clip to the difference of the current clip and the shader.
+ * The shader will be treated as an alpha mask, taking the difference of the two.
+ *
+ * @param shader The shader to intersect with the current clip
+ */
+ @FlaggedApi(Flags.FLAG_CLIP_SHADER)
+ public void clipOutShader(@NonNull Shader shader) {
+ nClipShader(mNativeCanvasWrapper, shader.getNativeInstance(),
+ Region.Op.DIFFERENCE.nativeInt);
+ }
+
public @Nullable DrawFilter getDrawFilter() {
return mDrawFilter;
}
@@ -1410,6 +1464,8 @@ public class Canvas extends BaseCanvas {
@CriticalNative
private static native boolean nIsOpaque(long canvasHandle);
@CriticalNative
+ private static native boolean nIsHighContrastText(long canvasHandle);
+ @CriticalNative
private static native int nGetWidth(long canvasHandle);
@CriticalNative
private static native int nGetHeight(long canvasHandle);
@@ -1444,6 +1500,8 @@ public class Canvas extends BaseCanvas {
private static native void nSkew(long canvasHandle, float sx, float sy);
@CriticalNative
private static native void nConcat(long nativeCanvas, long nativeMatrix);
+ @FastNative
+ private static native void nConcat(long nativeCanvas, float[] mat);
@CriticalNative
private static native void nSetMatrix(long nativeCanvas, long nativeMatrix);
@CriticalNative
@@ -1452,6 +1510,8 @@ public class Canvas extends BaseCanvas {
@CriticalNative
private static native boolean nClipPath(long nativeCanvas, long nativePath, int regionOp);
@CriticalNative
+ private static native void nClipShader(long nativeCanvas, long nativeShader, int regionOp);
+ @CriticalNative
private static native void nSetDrawFilter(long nativeCanvas, long nativeFilter);
@CriticalNative
private static native void nGetMatrix(long nativeCanvas, long nativeMatrix);
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 2d3ffb5d31ff..13c4a94cb9b6 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,10 +16,6 @@
package android.graphics;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT;
import static android.text.FontConfig.NamedFamilyList;
import android.annotation.NonNull;
@@ -32,7 +28,6 @@ import android.os.Build;
import android.os.LocaleList;
import android.text.FontConfig;
import android.util.ArraySet;
-import android.util.Log;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -65,6 +60,7 @@ public class FontListParser {
private static final String VARIANT_ELEGANT = "elegant";
// XML constants for Font.
+ public static final String ATTR_SUPPORTED_AXES = "supportedAxes";
public static final String ATTR_INDEX = "index";
public static final String ATTR_WEIGHT = "weight";
public static final String ATTR_POSTSCRIPT_NAME = "postScriptName";
@@ -78,6 +74,10 @@ public class FontListParser {
public static final String ATTR_TAG = "tag";
public static final String ATTR_STYLEVALUE = "stylevalue";
+ // The tag string for variable font type resolution.
+ private static final String TAG_WGHT = "wght";
+ private static final String TAG_ITAL = "ital";
+
/* Parse fallback list (no names) */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
@@ -263,7 +263,6 @@ public class FontListParser {
final String lang = parser.getAttributeValue("", "lang");
final String variant = parser.getAttributeValue(null, "variant");
final String ignore = parser.getAttributeValue(null, "ignore");
- final String varFamilyTypeStr = parser.getAttributeValue(null, "varFamilyType");
final List<FontConfig.Font> fonts = new ArrayList<>();
while (keepReading(parser)) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
@@ -286,45 +285,12 @@ public class FontListParser {
intVariant = FontConfig.FontFamily.VARIANT_ELEGANT;
}
}
- int varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE;
- if (varFamilyTypeStr != null) {
- varFamilyType = Integer.parseInt(varFamilyTypeStr);
- if (varFamilyType <= -1 || varFamilyType > 3) {
- Log.e(TAG, "Error: unexpected varFamilyType value: " + varFamilyTypeStr);
- varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE;
- }
-
- // validation but don't read font content for performance reasons.
- switch (varFamilyType) {
- case VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY:
- if (fonts.size() != 1) {
- Log.e(TAG, "Error: Single font support wght axis, but two or more fonts are"
- + " included in the font family.");
- varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE;
- }
- break;
- case VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL:
- if (fonts.size() != 1) {
- Log.e(TAG, "Error: Single font support both ital and wght axes, but two or"
- + " more fonts are included in the font family.");
- varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE;
- }
- break;
- case VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT:
- if (fonts.size() != 2) {
- Log.e(TAG, "Error: two fonts that support wght axis, but one or three or"
- + " more fonts are included in the font family.");
- varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE;
- }
- }
- }
boolean skip = (ignore != null && (ignore.equals("true") || ignore.equals("1")));
if (skip || fonts.isEmpty()) {
return null;
}
- return new FontConfig.FontFamily(fonts, LocaleList.forLanguageTags(lang), intVariant,
- varFamilyType);
+ return new FontConfig.FontFamily(fonts, LocaleList.forLanguageTags(lang), intVariant);
}
private static void throwIfAttributeExists(String attrName, XmlPullParser parser) {
@@ -407,6 +373,7 @@ public class FontListParser {
boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
String postScriptName = parser.getAttributeValue(null, ATTR_POSTSCRIPT_NAME);
+ final String supportedAxes = parser.getAttributeValue(null, ATTR_SUPPORTED_AXES);
StringBuilder filename = new StringBuilder();
while (keepReading(parser)) {
if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -422,6 +389,18 @@ public class FontListParser {
}
String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
+ int varTypeAxes = 0;
+ if (supportedAxes != null) {
+ for (String tag : supportedAxes.split(",")) {
+ String strippedTag = tag.strip();
+ if (strippedTag.equals(TAG_WGHT)) {
+ varTypeAxes |= FontConfig.Font.VAR_TYPE_AXES_WGHT;
+ } else if (strippedTag.equals(TAG_ITAL)) {
+ varTypeAxes |= FontConfig.Font.VAR_TYPE_AXES_ITAL;
+ }
+ }
+ }
+
if (postScriptName == null) {
// If post script name was not provided, assume the file name is same to PostScript
// name.
@@ -462,7 +441,8 @@ public class FontListParser {
),
index,
varSettings,
- fallbackFor);
+ fallbackFor,
+ varTypeAxes);
}
private static String findUpdatedFontFile(String psName,
diff --git a/graphics/java/android/graphics/Matrix44.java b/graphics/java/android/graphics/Matrix44.java
new file mode 100644
index 000000000000..7cc0eb7a6728
--- /dev/null
+++ b/graphics/java/android/graphics/Matrix44.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2023 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.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.graphics.hwui.flags.Flags;
+
+import java.util.Arrays;
+
+/**
+ * The Matrix44 class holds a 4x4 matrix for transforming coordinates. It is similar to
+ * {@link Matrix}, and should be used when you want to manipulate the canvas in 3D. Values are kept
+ * in row-major order. The values and operations are treated as column vectors.
+ */
+@FlaggedApi(Flags.FLAG_MATRIX_44)
+public class Matrix44 {
+ final float[] mBackingArray;
+ /**
+ * The default Matrix44 constructor will instantiate an identity matrix.
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public Matrix44() {
+ mBackingArray = new float[]{1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+ }
+
+ /**
+ * Creates and returns a Matrix44 by taking the 3x3 Matrix and placing it on the 0 of the z-axis
+ * by setting row {@code 2} and column {@code 2} to the identity as seen in the following
+ * operation:
+ * <pre class="prettyprint">
+ * [ a b c ] [ a b 0 c ]
+ * [ d e f ] -> [ d e 0 f ]
+ * [ g h i ] [ 0 0 1 0 ]
+ * [ g h 0 i ]
+ * </pre>
+ *
+ * @param mat A 3x3 Matrix to be converted (original Matrix will not be changed)
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public Matrix44(@NonNull Matrix mat) {
+ float[] m = new float[9];
+ mat.getValues(m);
+ mBackingArray = new float[]{m[0], m[1], 0.0f, m[2],
+ m[3], m[4], 0.0f, m[5],
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ m[6], m[7], 0.0f, m[8]};
+ }
+
+ /**
+ * Copies matrix values into the provided array in row-major order.
+ *
+ * @param dst The float array where values will be copied, must be of length 16
+ * @throws IllegalArgumentException if the destination float array is not of length 16
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public void getValues(@NonNull float [] dst) {
+ if (dst.length == 16) {
+ System.arraycopy(mBackingArray, 0, dst, 0, mBackingArray.length);
+ } else {
+ throw new IllegalArgumentException("Dst array must be of length 16");
+ }
+ }
+
+ /**
+ * Replaces the Matrix's values with the values in the provided array.
+ *
+ * @param src A float array of length 16. Floats are treated in row-major order
+ * @throws IllegalArgumentException if the destination float array is not of length 16
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public void setValues(@NonNull float[] src) {
+ if (src.length == 16) {
+ System.arraycopy(src, 0, mBackingArray, 0, mBackingArray.length);
+ } else {
+ throw new IllegalArgumentException("Src array must be of length 16");
+ }
+ }
+
+ /**
+ * Gets the value at the matrix's row and column.
+ *
+ * @param row An integer from 0 to 4 indicating the row of the value to get
+ * @param col An integer from 0 to 4 indicating the column of the value to get
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public float get(int row, int col) {
+ if (row >= 0 && row < 4 && col >= 0 && col < 4) {
+ return mBackingArray[row * 4 + col];
+ }
+ throw new IllegalArgumentException("invalid row and column values");
+ }
+
+ /**
+ * Sets the value at the matrix's row and column to the provided value.
+ *
+ * @param row An integer from 0 to 4 indicating the row of the value to change
+ * @param col An integer from 0 to 4 indicating the column of the value to change
+ * @param val The value the element at the specified index will be set to
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public void set(int row, int col, float val) {
+ if (row >= 0 && row < 4 && col >= 0 && col < 4) {
+ mBackingArray[row * 4 + col] = val;
+ } else {
+ throw new IllegalArgumentException("invalid row and column values");
+ }
+ }
+
+ /**
+ * Sets the Matrix44 to the identity matrix.
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public void reset() {
+ for (int i = 0; i < mBackingArray.length; i++) {
+ mBackingArray[i] = (i % 4 == i / 4) ? 1.0f : 0.0f;
+ }
+ }
+
+ /**
+ * Inverts the Matrix44, then return true if successful, false if unable to invert.
+ *
+ * @return {@code true} on success, {@code false} otherwise
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public boolean invert() {
+ float a00 = mBackingArray[0];
+ float a01 = mBackingArray[1];
+ float a02 = mBackingArray[2];
+ float a03 = mBackingArray[3];
+ float a10 = mBackingArray[4];
+ float a11 = mBackingArray[5];
+ float a12 = mBackingArray[6];
+ float a13 = mBackingArray[7];
+ float a20 = mBackingArray[8];
+ float a21 = mBackingArray[9];
+ float a22 = mBackingArray[10];
+ float a23 = mBackingArray[11];
+ float a30 = mBackingArray[12];
+ float a31 = mBackingArray[13];
+ float a32 = mBackingArray[14];
+ float a33 = mBackingArray[15];
+ float b00 = a00 * a11 - a01 * a10;
+ float b01 = a00 * a12 - a02 * a10;
+ float b02 = a00 * a13 - a03 * a10;
+ float b03 = a01 * a12 - a02 * a11;
+ float b04 = a01 * a13 - a03 * a11;
+ float b05 = a02 * a13 - a03 * a12;
+ float b06 = a20 * a31 - a21 * a30;
+ float b07 = a20 * a32 - a22 * a30;
+ float b08 = a20 * a33 - a23 * a30;
+ float b09 = a21 * a32 - a22 * a31;
+ float b10 = a21 * a33 - a23 * a31;
+ float b11 = a22 * a33 - a23 * a32;
+ float det = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);
+ if (det == 0.0f) {
+ return false;
+ }
+ float invDet = 1.0f / det;
+ mBackingArray[0] = ((a11 * b11 - a12 * b10 + a13 * b09) * invDet);
+ mBackingArray[1] = ((-a01 * b11 + a02 * b10 - a03 * b09) * invDet);
+ mBackingArray[2] = ((a31 * b05 - a32 * b04 + a33 * b03) * invDet);
+ mBackingArray[3] = ((-a21 * b05 + a22 * b04 - a23 * b03) * invDet);
+ mBackingArray[4] = ((-a10 * b11 + a12 * b08 - a13 * b07) * invDet);
+ mBackingArray[5] = ((a00 * b11 - a02 * b08 + a03 * b07) * invDet);
+ mBackingArray[6] = ((-a30 * b05 + a32 * b02 - a33 * b01) * invDet);
+ mBackingArray[7] = ((a20 * b05 - a22 * b02 + a23 * b01) * invDet);
+ mBackingArray[8] = ((a10 * b10 - a11 * b08 + a13 * b06) * invDet);
+ mBackingArray[9] = ((-a00 * b10 + a01 * b08 - a03 * b06) * invDet);
+ mBackingArray[10] = ((a30 * b04 - a31 * b02 + a33 * b00) * invDet);
+ mBackingArray[11] = ((-a20 * b04 + a21 * b02 - a23 * b00) * invDet);
+ mBackingArray[12] = ((-a10 * b09 + a11 * b07 - a12 * b06) * invDet);
+ mBackingArray[13] = ((a00 * b09 - a01 * b07 + a02 * b06) * invDet);
+ mBackingArray[14] = ((-a30 * b03 + a31 * b01 - a32 * b00) * invDet);
+ mBackingArray[15] = ((a20 * b03 - a21 * b01 + a22 * b00) * invDet);
+ return true;
+ }
+
+ /**
+ * Returns true if Matrix44 is equal to identity matrix.
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public boolean isIdentity() {
+ for (int i = 0; i < mBackingArray.length; i++) {
+ float expected = (i % 4 == i / 4) ? 1.0f : 0.0f;
+ if (expected != mBackingArray[i]) return false;
+ }
+ return true;
+ }
+
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ private static float dot(Matrix44 a, Matrix44 b, int row, int col) {
+ return (a.get(row, 0) * b.get(0, col))
+ + (a.get(row, 1) * b.get(1, col))
+ + (a.get(row, 2) * b.get(2, col))
+ + (a.get(row, 3) * b.get(3, col));
+ }
+
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ private static float dot(float r0, float r1, float r2, float r3,
+ float c0, float c1, float c2, float c3) {
+ return (r0 * c0) + (r1 * c1) + (r2 * c2) + (r3 * c3);
+ }
+
+ /**
+ * Multiplies (x, y, z, w) vector by the Matrix44, then returns the new (x, y, z, w). Users
+ * should set {@code w} to 1 to indicate the coordinates are normalized.
+ *
+ * @return An array of length 4 that represents the x, y, z, w (where w is perspective) value
+ * after multiplying x, y, z, 1 by the matrix
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public @NonNull float[] map(float x, float y, float z, float w) {
+ float[] dst = new float[4];
+ this.map(x, y, z, w, dst);
+ return dst;
+ }
+
+ /**
+ * Multiplies (x, y, z, w) vector by the Matrix44, then returns the new (x, y, z, w). Users
+ * should set {@code w} to 1 to indicate the coordinates are normalized.
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public void map(float x, float y, float z, float w, @NonNull float[] dst) {
+ if (dst.length != 4) {
+ throw new IllegalArgumentException("Dst array must be of length 4");
+ }
+ dst[0] = x * mBackingArray[0] + y * mBackingArray[1]
+ + z * mBackingArray[2] + w * mBackingArray[3];
+ dst[1] = x * mBackingArray[4] + y * mBackingArray[5]
+ + z * mBackingArray[6] + w * mBackingArray[7];
+ dst[2] = x * mBackingArray[8] + y * mBackingArray[9]
+ + z * mBackingArray[10] + w * mBackingArray[11];
+ dst[3] = x * mBackingArray[12] + y * mBackingArray[13]
+ + z * mBackingArray[14] + w * mBackingArray[15];
+ }
+
+ /**
+ * Multiplies `this` matrix (A) and provided Matrix (B) in the order of A * B.
+ * The result is saved in `this` Matrix.
+ *
+ * @param b The second Matrix in the concatenation operation
+ * @return A reference to this Matrix, which can be used to chain Matrix operations
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public @NonNull Matrix44 concat(@NonNull Matrix44 b) {
+ float val00 = dot(this, b, 0, 0);
+ float val01 = dot(this, b, 0, 1);
+ float val02 = dot(this, b, 0, 2);
+ float val03 = dot(this, b, 0, 3);
+ float val10 = dot(this, b, 1, 0);
+ float val11 = dot(this, b, 1, 1);
+ float val12 = dot(this, b, 1, 2);
+ float val13 = dot(this, b, 1, 3);
+ float val20 = dot(this, b, 2, 0);
+ float val21 = dot(this, b, 2, 1);
+ float val22 = dot(this, b, 2, 2);
+ float val23 = dot(this, b, 2, 3);
+ float val30 = dot(this, b, 3, 0);
+ float val31 = dot(this, b, 3, 1);
+ float val32 = dot(this, b, 3, 2);
+ float val33 = dot(this, b, 3, 3);
+
+ mBackingArray[0] = val00;
+ mBackingArray[1] = val01;
+ mBackingArray[2] = val02;
+ mBackingArray[3] = val03;
+ mBackingArray[4] = val10;
+ mBackingArray[5] = val11;
+ mBackingArray[6] = val12;
+ mBackingArray[7] = val13;
+ mBackingArray[8] = val20;
+ mBackingArray[9] = val21;
+ mBackingArray[10] = val22;
+ mBackingArray[11] = val23;
+ mBackingArray[12] = val30;
+ mBackingArray[13] = val31;
+ mBackingArray[14] = val32;
+ mBackingArray[15] = val33;
+
+ return this;
+ }
+
+ /**
+ * Applies a rotation around a given axis, then returns self.
+ * {@code x}, {@code y}, {@code z} represent the axis by which to rotate around.
+ * For example, pass in {@code 1, 0, 0} to rotate around the x-axis.
+ * The axis provided will be normalized.
+ *
+ * @param deg Amount in degrees to rotate the matrix about the x-axis
+ * @param xComp X component of the rotation axis
+ * @param yComp Y component of the rotation axis
+ * @param zComp Z component of the rotation axis
+ * @return A reference to this Matrix, which can be used to chain Matrix operations
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public @NonNull Matrix44 rotate(float deg, float xComp, float yComp, float zComp) {
+ float sum = xComp + yComp + zComp;
+ float x = xComp / sum;
+ float y = yComp / sum;
+ float z = zComp / sum;
+
+ float c = (float) Math.cos(deg * Math.PI / 180.0f);
+ float s = (float) Math.sin(deg * Math.PI / 180.0f);
+ float t = 1 - c;
+
+ float rotVals00 = t * x * x + c;
+ float rotVals01 = t * x * y - s * z;
+ float rotVals02 = t * x * z + s * y;
+ float rotVals10 = t * x * y + s * z;
+ float rotVals11 = t * y * y + c;
+ float rotVals12 = t * y * z - s * x;
+ float rotVals20 = t * x * z - s * y;
+ float rotVals21 = t * y * z + s * x;
+ float rotVals22 = t * z * z + c;
+
+ float v00 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
+ rotVals00, rotVals10, rotVals20, 0);
+ float v01 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
+ rotVals01, rotVals11, rotVals21, 0);
+ float v02 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
+ rotVals02, rotVals12, rotVals22, 0);
+ float v03 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
+ 0, 0, 0, 1);
+ float v10 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
+ rotVals00, rotVals10, rotVals20, 0);
+ float v11 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
+ rotVals01, rotVals11, rotVals21, 0);
+ float v12 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
+ rotVals02, rotVals12, rotVals22, 0);
+ float v13 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
+ 0, 0, 0, 1);
+ float v20 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
+ rotVals00, rotVals10, rotVals20, 0);
+ float v21 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
+ rotVals01, rotVals11, rotVals21, 0);
+ float v22 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
+ rotVals02, rotVals12, rotVals22, 0);
+ float v23 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
+ 0, 0, 0, 1);
+ float v30 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
+ rotVals00, rotVals10, rotVals20, 0);
+ float v31 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
+ rotVals01, rotVals11, rotVals21, 0);
+ float v32 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
+ rotVals02, rotVals12, rotVals22, 0);
+ float v33 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
+ 0, 0, 0, 1);
+
+ mBackingArray[0] = v00;
+ mBackingArray[1] = v01;
+ mBackingArray[2] = v02;
+ mBackingArray[3] = v03;
+ mBackingArray[4] = v10;
+ mBackingArray[5] = v11;
+ mBackingArray[6] = v12;
+ mBackingArray[7] = v13;
+ mBackingArray[8] = v20;
+ mBackingArray[9] = v21;
+ mBackingArray[10] = v22;
+ mBackingArray[11] = v23;
+ mBackingArray[12] = v30;
+ mBackingArray[13] = v31;
+ mBackingArray[14] = v32;
+ mBackingArray[15] = v33;
+
+ return this;
+ }
+
+ /**
+ * Applies scaling factors to `this` Matrix44, then returns self. Pass 1s for no change.
+ *
+ * @param x Scaling factor for the x-axis
+ * @param y Scaling factor for the y-axis
+ * @param z Scaling factor for the z-axis
+ * @return A reference to this Matrix, which can be used to chain Matrix operations
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public @NonNull Matrix44 scale(float x, float y, float z) {
+ mBackingArray[0] *= x;
+ mBackingArray[4] *= x;
+ mBackingArray[8] *= x;
+ mBackingArray[12] *= x;
+ mBackingArray[1] *= y;
+ mBackingArray[5] *= y;
+ mBackingArray[9] *= y;
+ mBackingArray[13] *= y;
+ mBackingArray[2] *= z;
+ mBackingArray[6] *= z;
+ mBackingArray[10] *= z;
+ mBackingArray[14] *= z;
+
+ return this;
+ }
+
+ /**
+ * Applies a translation to `this` Matrix44, then returns self.
+ *
+ * @param x Translation for the x-axis
+ * @param y Translation for the y-axis
+ * @param z Translation for the z-axis
+ * @return A reference to this Matrix, which can be used to chain Matrix operations
+ */
+ @FlaggedApi(Flags.FLAG_MATRIX_44)
+ public @NonNull Matrix44 translate(float x, float y, float z) {
+ float newX = x * mBackingArray[0] + y * mBackingArray[1]
+ + z * mBackingArray[2] + mBackingArray[3];
+ float newY = x * mBackingArray[4] + y * mBackingArray[5]
+ + z * mBackingArray[6] + mBackingArray[7];
+ float newZ = x * mBackingArray[8] + y * mBackingArray[9]
+ + z * mBackingArray[10] + mBackingArray[11];
+ float newW = x * mBackingArray[12] + y * mBackingArray[13]
+ + z * mBackingArray[14] + mBackingArray[15];
+
+ mBackingArray[3] = newX;
+ mBackingArray[7] = newY;
+ mBackingArray[11] = newZ;
+ mBackingArray[15] = newW;
+
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("""
+ | %f %f %f %f |
+ | %f %f %f %f |
+ | %f %f %f %f |
+ | %f %f %f %f |
+ """, mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
+ mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
+ mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
+ mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15]);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Matrix44) {
+ return Arrays.equals(mBackingArray, ((Matrix44) obj).mBackingArray);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) mBackingArray[0] + (int) mBackingArray[1] + (int) mBackingArray[2]
+ + (int) mBackingArray[3] + (int) mBackingArray[4] + (int) mBackingArray[5]
+ + (int) mBackingArray[6] + (int) mBackingArray[7] + (int) mBackingArray[8]
+ + (int) mBackingArray[9] + (int) mBackingArray[10] + (int) mBackingArray[11]
+ + (int) mBackingArray[12] + (int) mBackingArray[13] + (int) mBackingArray[14]
+ + (int) mBackingArray[15];
+ }
+
+}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index f10cdb82022e..df95a91d72d7 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,6 +17,7 @@
package android.graphics;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
+import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
@@ -133,7 +134,9 @@ public class Paint {
FAKE_BOLD_TEXT_FLAG,
LINEAR_TEXT_FLAG,
SUBPIXEL_TEXT_FLAG,
- EMBEDDED_BITMAP_TEXT_FLAG
+ EMBEDDED_BITMAP_TEXT_FLAG,
+ TEXT_RUN_FLAG_LEFT_EDGE,
+ TEXT_RUN_FLAG_RIGHT_EDGE
})
public @interface PaintFlag{}
@@ -264,6 +267,66 @@ public class Paint {
/** @hide bit mask for the flag enabling vertical rendering for text */
public static final int VERTICAL_TEXT_FLAG = 0x1000;
+ /**
+ * A text run flag that indicates the run is located the visually most left segment of the line.
+ * <p>
+ * This flag is used for telling the underlying text layout engine that the text is located at
+ * the most left of the line. This flag is used for controlling the amount letter spacing
+ * added. If the text is in the middle of the line, the text layout engine assigns additional
+ * letter spacing to the both side of each letter. On the other hand, the letter spacing should
+ * not be added to the visually most left and right of the line. By setting this flag, text
+ * layout engine calculates the layout as it is located at the most visually left of the line
+ * and doesn't add letter spacing to the left of this run.
+ * <p>
+ * Note that the caller must resolve BiDi runs and reorder them visually and set this flag only
+ * if the target run is located visually most left position. This left does not always mean the
+ * beginning of the text.
+ * <p>
+ * If the run covers entire line, caller should set {@link #TEXT_RUN_FLAG_RIGHT_EDGE} as well.
+ * <p>
+ * Note that this flag is only effective for run based APIs. For example, this flag works for
+ * {@link Canvas#drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}
+ * and
+ * {@link Paint#getRunCharacterAdvance(char[], int, int, int, int, boolean, int, float[], int)}.
+ * However, this doesn't work for
+ * {@link Canvas#drawText(CharSequence, int, int, float, float, Paint)} or
+ * {@link Paint#measureText(CharSequence, int, int)}. The non-run based APIs works as both
+ * {@link #TEXT_RUN_FLAG_LEFT_EDGE} and {@link #TEXT_RUN_FLAG_RIGHT_EDGE} are specified.
+ */
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
+ public static final int TEXT_RUN_FLAG_LEFT_EDGE = 0x2000;
+
+
+ /**
+ * A text run flag that indicates the run is located the visually most right segment of the
+ * line.
+ * <p>
+ * This flag is used for telling the underlying text layout engine that the text is located at
+ * the most right of the line. This flag is used for controlling the amount letter spacing
+ * added. If the text is in the middle of the line, the text layout engine assigns additional
+ * letter spacing to the both side of each letter. On the other hand, the letter spacing should
+ * not be added to the visually most left and right of the line. By setting this flag, text
+ * layout engine calculates the layout as it is located at the most visually left of the line
+ * and doesn't add letter spacing to the left of this run.
+ * <p>
+ * Note that the caller must resolve BiDi runs and reorder them visually and set this flag only
+ * if the target run is located visually most right position. This right does not always mean
+ * the end of the text.
+ * <p>
+ * If the run covers entire line, caller should set {@link #TEXT_RUN_FLAG_LEFT_EDGE} as well.
+ * <p>
+ * Note that this flag is only effective for run based APIs. For example, this flag works for
+ * {@link Canvas#drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}
+ * and
+ * {@link Paint#getRunCharacterAdvance(char[], int, int, int, int, boolean, int, float[], int)}.
+ * However, this doesn't work for
+ * {@link Canvas#drawText(CharSequence, int, int, float, float, Paint)} or
+ * {@link Paint#measureText(CharSequence, int, int)}. The non-run based APIs works as both
+ * {@link #TEXT_RUN_FLAG_LEFT_EDGE} and {@link #TEXT_RUN_FLAG_RIGHT_EDGE} are specified.
+ */
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
+ public static final int TEXT_RUN_FLAG_RIGHT_EDGE = 0x4000;
+
// These flags are always set on a new/reset paint, even if flags 0 is passed.
static final int HIDDEN_DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG
| FILTER_BITMAP_FLAG;
@@ -2474,6 +2537,19 @@ public class Paint {
nGetFontMetricsInt(mNativePaint, metrics, true);
}
+ /** @hide */
+ public static final class RunInfo {
+ private int mClusterCount = 0;
+
+ public int getClusterCount() {
+ return mClusterCount;
+ }
+
+ public void setClusterCount(int clusterCount) {
+ mClusterCount = clusterCount;
+ }
+ }
+
/**
* Return the recommend line spacing based on the current typeface and
* text size.
@@ -2507,17 +2583,24 @@ public class Paint {
if (text.length == 0 || count == 0) {
return 0f;
}
- if (!mHasCompatScaling) {
- return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
- index, count, index, count, mBidiFlags, null, 0));
- }
+ int oldFlag = getFlags();
+ setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));
+ try {
- final float oldSize = getTextSize();
- setTextSize(oldSize * mCompatScaling);
- final float w = nGetTextAdvances(mNativePaint, text, index, count, index, count,
- mBidiFlags, null, 0);
- setTextSize(oldSize);
- return (float) Math.ceil(w*mInvCompatScaling);
+ if (!mHasCompatScaling) {
+ return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
+ index, count, index, count, mBidiFlags, null, 0));
+ }
+
+ final float oldSize = getTextSize();
+ setTextSize(oldSize * mCompatScaling);
+ final float w = nGetTextAdvances(mNativePaint, text, index, count, index, count,
+ mBidiFlags, null, 0);
+ setTextSize(oldSize);
+ return (float) Math.ceil(w * mInvCompatScaling);
+ } finally {
+ setFlags(oldFlag);
+ }
}
/**
@@ -2539,16 +2622,22 @@ public class Paint {
if (text.length() == 0 || start == end) {
return 0f;
}
- if (!mHasCompatScaling) {
- return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
- start, end, start, end, mBidiFlags, null, 0));
+ int oldFlag = getFlags();
+ setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));
+ try {
+ if (!mHasCompatScaling) {
+ return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
+ start, end, start, end, mBidiFlags, null, 0));
+ }
+ final float oldSize = getTextSize();
+ setTextSize(oldSize * mCompatScaling);
+ final float w = nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags,
+ null, 0);
+ setTextSize(oldSize);
+ return (float) Math.ceil(w * mInvCompatScaling);
+ } finally {
+ setFlags(oldFlag);
}
- final float oldSize = getTextSize();
- setTextSize(oldSize * mCompatScaling);
- final float w = nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags,
- null, 0);
- setTextSize(oldSize);
- return (float) Math.ceil(w * mInvCompatScaling);
}
/**
@@ -2753,19 +2842,26 @@ public class Paint {
if (text.length == 0 || count == 0) {
return 0;
}
- if (!mHasCompatScaling) {
+ int oldFlag = getFlags();
+ setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));
+ try {
+ if (!mHasCompatScaling) {
+ nGetTextAdvances(mNativePaint, text, index, count, index, count, mBidiFlags, widths,
+ 0);
+ return count;
+ }
+
+ final float oldSize = getTextSize();
+ setTextSize(oldSize * mCompatScaling);
nGetTextAdvances(mNativePaint, text, index, count, index, count, mBidiFlags, widths, 0);
+ setTextSize(oldSize);
+ for (int i = 0; i < count; i++) {
+ widths[i] *= mInvCompatScaling;
+ }
return count;
+ } finally {
+ setFlags(oldFlag);
}
-
- final float oldSize = getTextSize();
- setTextSize(oldSize * mCompatScaling);
- nGetTextAdvances(mNativePaint, text, index, count, index, count, mBidiFlags, widths, 0);
- setTextSize(oldSize);
- for (int i = 0; i < count; i++) {
- widths[i] *= mInvCompatScaling;
- }
- return count;
}
/**
@@ -2836,19 +2932,25 @@ public class Paint {
if (text.length() == 0 || start == end) {
return 0;
}
- if (!mHasCompatScaling) {
+ int oldFlag = getFlags();
+ setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));
+ try {
+ if (!mHasCompatScaling) {
+ nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags, widths, 0);
+ return end - start;
+ }
+
+ final float oldSize = getTextSize();
+ setTextSize(oldSize * mCompatScaling);
nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags, widths, 0);
+ setTextSize(oldSize);
+ for (int i = 0; i < end - start; i++) {
+ widths[i] *= mInvCompatScaling;
+ }
return end - start;
+ } finally {
+ setFlags(oldFlag);
}
-
- final float oldSize = getTextSize();
- setTextSize(oldSize * mCompatScaling);
- nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags, widths, 0);
- setTextSize(oldSize);
- for (int i = 0; i < end - start; i++) {
- widths[i] *= mInvCompatScaling;
- }
- return end - start;
}
/**
@@ -3320,7 +3422,7 @@ public class Paint {
int contextEnd, boolean isRtl, int offset,
@Nullable float[] advances, int advancesIndex) {
return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset,
- advances, advancesIndex, null);
+ advances, advancesIndex, null, null);
}
/**
@@ -3339,12 +3441,14 @@ public class Paint {
* @param advances the array that receives the computed character advances
* @param advancesIndex the start index from which the advances array is filled
* @param drawBounds the output parameter for the bounding box of drawing text, optional
+ * @param runInfo the output parameter for storing run information.
* @return width measurement between start and offset
- * @hide
+ * @hide TODO: Reorganize APIs
*/
public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, int offset,
- @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) {
+ @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds,
+ @Nullable RunInfo runInfo) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -3370,11 +3474,14 @@ public class Paint {
}
if (end == start) {
+ if (runInfo != null) {
+ runInfo.setClusterCount(0);
+ }
return 0.0f;
}
return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
- isRtl, offset, advances, advancesIndex, drawBounds);
+ isRtl, offset, advances, advancesIndex, drawBounds, runInfo);
}
/**
@@ -3402,7 +3509,7 @@ public class Paint {
int contextStart, int contextEnd, boolean isRtl, int offset,
@Nullable float[] advances, int advancesIndex) {
return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset,
- advances, advancesIndex, null);
+ advances, advancesIndex, null, null);
}
/**
@@ -3418,12 +3525,14 @@ public class Paint {
* @param advances the array that receives the computed character advances
* @param advancesIndex the start index from which the advances array is filled
* @param drawBounds the output parameter for the bounding box of drawing text, optional
+ * @param runInfo an optional output parameter for filling run information.
* @return width measurement between start and offset
- * @hide
+ * @hide TODO: Reorganize APIs
*/
public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, int offset,
- @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) {
+ @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds,
+ @Nullable RunInfo runInfo) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -3456,7 +3565,7 @@ public class Paint {
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart,
0, contextEnd - contextStart, isRtl, offset - contextStart,
- advances, advancesIndex, drawBounds);
+ advances, advancesIndex, drawBounds, runInfo);
TemporaryBuffer.recycle(buf);
return result;
}
@@ -3574,7 +3683,7 @@ public class Paint {
int contextStart, int contextEnd, boolean isRtl, int offset);
private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start,
int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances,
- int advancesIndex, RectF drawingBounds);
+ int advancesIndex, RectF drawingBounds, RunInfo runInfo);
private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, float advance);
private static native void nGetFontMetricsIntForText(long paintPtr, char[] text,
@@ -3729,4 +3838,11 @@ public class Paint {
private static native void nSetTextSize(long paintPtr, float textSize);
@CriticalNative
private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
+
+
+ // Following Native methods are kept for old Robolectric JNI signature used by
+ // SystemUIGoogleRoboRNGTests
+ private static native float nGetRunCharacterAdvance(long paintPtr, char[] text,
+ int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset,
+ float[] advances, int advancesIndex, RectF drawingBounds);
}
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index c1b531fb2d9c..3256f31bdc93 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -489,7 +489,7 @@ public class SurfaceTexture {
@Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "postOnSetFrameRateEventFromNative");
try {
- if (Flags.toolkitSetFrameRate()) {
+ if (Flags.toolkitSetFrameRateReadOnly()) {
SurfaceTexture st = weakSelf.get();
if (st != null) {
Handler handler = st.mOnSetFrameRateHandler;
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 45e29a88c7db..f359025f4b46 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -154,6 +154,12 @@ public final class Icon implements Parcelable {
// TYPE_DATA: data offset
private int mInt2;
+ // TYPE_RESOURCE: use the monochrome drawable from an AdaptiveIconDrawable
+ private boolean mUseMonochrome = false;
+
+ // TYPE_RESOURCE: wrap the monochrome drawable in an InsetDrawable with the specified inset
+ private float mInsetScale = 0.0f;
+
/**
* Gets the type of the icon provided.
* <p>
@@ -368,10 +374,34 @@ public final class Icon implements Parcelable {
result.setTintList(mTintList);
result.setTintBlendMode(mBlendMode);
}
+
+ if (mUseMonochrome) {
+ return crateMonochromeDrawable(result, mInsetScale);
+ }
+
return result;
}
/**
+ * Gets the monochrome drawable from an {@link AdaptiveIconDrawable}.
+ *
+ * @param drawable An {@link AdaptiveIconDrawable}
+ * @return Adjusted (wrapped in {@link InsetDrawable}) monochrome drawable
+ * from an {@link AdaptiveIconDrawable}.
+ * Or the original drawable if no monochrome layer exists.
+ */
+ private static Drawable crateMonochromeDrawable(Drawable drawable, float inset) {
+ if (drawable instanceof AdaptiveIconDrawable) {
+ Drawable monochromeDrawable = ((AdaptiveIconDrawable) drawable).getMonochrome();
+ // wrap with negative inset => scale icon (inspired from BaseIconFactory)
+ if (monochromeDrawable != null) {
+ return new InsetDrawable(monochromeDrawable, inset);
+ }
+ }
+ return drawable;
+ }
+
+ /**
* Resizes image if size too large for Canvas to draw
* @param bitmap Bitmap to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE}
* @return resized bitmap
@@ -693,7 +723,9 @@ public final class Icon implements Parcelable {
&& Arrays.equals(getDataBytes(), otherIcon.getDataBytes());
case TYPE_RESOURCE:
return getResId() == otherIcon.getResId()
- && Objects.equals(getResPackage(), otherIcon.getResPackage());
+ && Objects.equals(getResPackage(), otherIcon.getResPackage())
+ && mUseMonochrome == otherIcon.mUseMonochrome
+ && mInsetScale == otherIcon.mInsetScale;
case TYPE_URI:
case TYPE_URI_ADAPTIVE_BITMAP:
return Objects.equals(getUriString(), otherIcon.getUriString());
@@ -748,6 +780,26 @@ public final class Icon implements Parcelable {
}
/**
+ * Create an Icon pointing to a drawable resource.
+ * @param resPackage Name of the package containing the resource in question
+ * @param resId ID of the drawable resource
+ * @param useMonochrome if this icon should use the monochrome res from the adaptive drawable
+ * @hide
+ */
+ public static @NonNull Icon createWithResourceAdaptiveDrawable(@NonNull String resPackage,
+ @DrawableRes int resId, boolean useMonochrome, float inset) {
+ if (resPackage == null) {
+ throw new IllegalArgumentException("Resource package name must not be null.");
+ }
+ final Icon rep = new Icon(TYPE_RESOURCE);
+ rep.mInt1 = resId;
+ rep.mUseMonochrome = useMonochrome;
+ rep.mInsetScale = inset;
+ rep.mString1 = resPackage;
+ return rep;
+ }
+
+ /**
* Create an Icon pointing to a bitmap in memory.
* @param bits A valid {@link android.graphics.Bitmap} object
*/
@@ -986,6 +1038,8 @@ public final class Icon implements Parcelable {
final int resId = in.readInt();
mString1 = pkg;
mInt1 = resId;
+ mUseMonochrome = in.readBoolean();
+ mInsetScale = in.readFloat();
break;
case TYPE_DATA:
final int len = in.readInt();
@@ -1027,6 +1081,8 @@ public final class Icon implements Parcelable {
case TYPE_RESOURCE:
dest.writeString(getResPackage());
dest.writeInt(getResId());
+ dest.writeBoolean(mUseMonochrome);
+ dest.writeFloat(mInsetScale);
break;
case TYPE_DATA:
dest.writeInt(getDataLength());
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 6e20034c838a..f727f5b076a1 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -100,6 +100,71 @@ public final class SystemFonts {
}
}
+ /** @hide */
+ @VisibleForTesting
+ public static @FontFamily.Builder.VariableFontFamilyType int resolveVarFamilyType(
+ @NonNull FontConfig.FontFamily xmlFamily,
+ @Nullable String familyName) {
+ int wghtCount = 0;
+ int italCount = 0;
+ int targetFonts = 0;
+ boolean hasItalicFont = false;
+
+ List<FontConfig.Font> fonts = xmlFamily.getFontList();
+ for (int i = 0; i < fonts.size(); ++i) {
+ FontConfig.Font font = fonts.get(i);
+
+ if (familyName == null) { // for default family
+ if (font.getFontFamilyName() != null) {
+ continue; // this font is not for the default family.
+ }
+ } else { // for the specific family
+ if (!familyName.equals(font.getFontFamilyName())) {
+ continue; // this font is not for given family.
+ }
+ }
+
+ final int varTypeAxes = font.getVarTypeAxes();
+ if (varTypeAxes == 0) {
+ // If we see static font, we can immediately return as VAR_TYPE_NONE.
+ return FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE;
+ }
+
+ if ((varTypeAxes & FontConfig.Font.VAR_TYPE_AXES_WGHT) != 0) {
+ wghtCount++;
+ }
+
+ if ((varTypeAxes & FontConfig.Font.VAR_TYPE_AXES_ITAL) != 0) {
+ italCount++;
+ }
+
+ if (font.getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC) {
+ hasItalicFont = true;
+ }
+ targetFonts++;
+ }
+
+ if (italCount == 0) { // No ital font.
+ if (targetFonts == 1 && wghtCount == 1) {
+ // If there is only single font that has wght, use it for regular style and
+ // use synthetic bolding for italic.
+ return FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY;
+ } else if (targetFonts == 2 && wghtCount == 2 && hasItalicFont) {
+ // If there are two fonts and italic font is available, use them for regular and
+ // italic separately. (It is impossible to have two italic fonts. It will end up
+ // with Typeface creation failure.)
+ return FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT;
+ }
+ } else if (italCount == 1) {
+ // If ital font is included, a single font should support both wght and ital.
+ if (wghtCount == 1 && targetFonts == 1) {
+ return FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL;
+ }
+ }
+ // Otherwise, unsupported.
+ return FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE;
+ }
+
private static void pushFamilyToFallback(@NonNull FontConfig.FontFamily xmlFamily,
@NonNull ArrayMap<String, NativeFamilyListSet> fallbackMap,
@NonNull Map<String, ByteBuffer> cache) {
@@ -126,7 +191,7 @@ public final class SystemFonts {
}
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
- defaultFonts, languageTags, variant, xmlFamily.getVariableFontFamilyType(), false,
+ defaultFonts, languageTags, variant, resolveVarFamilyType(xmlFamily, null), false,
cache);
// Insert family into fallback map.
for (int i = 0; i < fallbackMap.size(); i++) {
@@ -145,7 +210,7 @@ public final class SystemFonts {
}
} else {
final FontFamily family = createFontFamily(fallback, languageTags, variant,
- xmlFamily.getVariableFontFamilyType(), false, cache);
+ resolveVarFamilyType(xmlFamily, name), false, cache);
if (family != null) {
familyListSet.familyList.add(family);
} else if (defaultFamily != null) {
@@ -217,7 +282,8 @@ public final class SystemFonts {
final FontFamily family = createFontFamily(
xmlFamily.getFontList(),
xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getVariant(),
- xmlFamily.getVariableFontFamilyType(),
+ resolveVarFamilyType(xmlFamily,
+ null /* all fonts under named family should be treated as default */),
true, // named family is always default
bufferCache);
if (family == null) {
diff --git a/graphics/java/android/graphics/pdf/TEST_MAPPING b/graphics/java/android/graphics/pdf/TEST_MAPPING
index d763598f5ba0..afec35c76371 100644
--- a/graphics/java/android/graphics/pdf/TEST_MAPPING
+++ b/graphics/java/android/graphics/pdf/TEST_MAPPING
@@ -1,7 +1,12 @@
{
"presubmit": [
{
- "name": "CtsPdfTestCases"
+ "name": "CtsPdfTestCases",
+ "options": [
+ {
+ "include-filter": "android.graphics.pdf.cts.PdfDocumentTest"
+ }
+ ]
}
]
}
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index c5e451a5ec3a..7d55928aa656 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -23,9 +23,7 @@ import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
+import android.app.ActivityThread;
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
@@ -43,15 +41,6 @@ import java.util.Objects;
* line-break property</a> for more information.
*/
public final class LineBreakConfig implements Parcelable {
-
- /**
- * A feature ID for automatic line break word style.
- * @hide
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- public static final long WORD_STYLE_AUTO = 280005585L;
-
/**
* No hyphenation preference is specified.
*
@@ -187,6 +176,9 @@ public final class LineBreakConfig implements Parcelable {
* - If at least one locale in the locale list contains Japanese script, this option is
* equivalent to {@link #LINE_BREAK_STYLE_STRICT}.
* - Otherwise, this option is equivalent to {@link #LINE_BREAK_STYLE_NONE}.
+ *
+ * <p>
+ * Note: future versions may have special line breaking style rules for other locales.
*/
@FlaggedApi(FLAG_WORD_STYLE_AUTO)
public static final int LINE_BREAK_STYLE_AUTO = 5;
@@ -260,6 +252,9 @@ public final class LineBreakConfig implements Parcelable {
* option is equivalent to {@link #LINE_BREAK_WORD_STYLE_PHRASE} if the result of its line
* count is less than 5 lines.
* - Otherwise, this option is equivalent to {@link #LINE_BREAK_WORD_STYLE_NONE}.
+ *
+ * <p>
+ * Note: future versions may have special line breaking word style rules for other locales.
*/
@FlaggedApi(FLAG_WORD_STYLE_AUTO)
public static final int LINE_BREAK_WORD_STYLE_AUTO = 2;
@@ -354,13 +349,13 @@ public final class LineBreakConfig implements Parcelable {
return this;
}
+ // TODO(316208691): Revive following removed API docs.
+ // Note: different from {@link #merge(LineBreakConfig)} if this function is called with
+ // {@link #LINE_BREAK_STYLE_UNSPECIFIED}, the line break style is reset to
+ // {@link #LINE_BREAK_STYLE_UNSPECIFIED}.
/**
* Sets the line-break style.
*
- * Note: different from {@link #merge(LineBreakConfig)} if this function is called with
- * {@link #LINE_BREAK_STYLE_UNSPECIFIED}, the line break style is reset to
- * {@link #LINE_BREAK_STYLE_UNSPECIFIED}.
- *
* @see <a href="https://unicode.org/reports/tr35/#UnicodeLineBreakStyleIdentifier">
* Unicode Line Break Style Identifier</a>
* @see <a href="https://drafts.csswg.org/css-text/#line-break-property">
@@ -374,13 +369,13 @@ public final class LineBreakConfig implements Parcelable {
return this;
}
+ // TODO(316208691): Revive following removed API docs.
+ // Note: different from {@link #merge(LineBreakConfig)} method, if this function is called
+ // with {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}, the line break style is reset to
+ // {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}.
/**
* Sets the line-break word style.
*
- * Note: different from {@link #merge(LineBreakConfig)} method, if this function is called
- * with {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}, the line break style is reset to
- * {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}.
- *
* @see <a href="https://unicode.org/reports/tr35/#UnicodeLineBreakWordIdentifier">
* Unicode Line Break Word Identifier</a>
* @see <a href="https://drafts.csswg.org/css-text/#word-break-property">
@@ -487,8 +482,15 @@ public final class LineBreakConfig implements Parcelable {
* @hide
*/
public static @LineBreakStyle int getResolvedLineBreakStyle(@Nullable LineBreakConfig config) {
- final int defaultStyle = CompatChanges.isChangeEnabled(WORD_STYLE_AUTO)
- ? LINE_BREAK_STYLE_AUTO : LINE_BREAK_STYLE_NONE;
+ final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
+ .targetSdkVersion;
+ final int defaultStyle;
+ final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+ if (targetSdkVersion >= vicVersion) {
+ defaultStyle = LINE_BREAK_STYLE_AUTO;
+ } else {
+ defaultStyle = LINE_BREAK_STYLE_NONE;
+ }
if (config == null) {
return defaultStyle;
}
@@ -515,8 +517,15 @@ public final class LineBreakConfig implements Parcelable {
*/
public static @LineBreakWordStyle int getResolvedLineBreakWordStyle(
@Nullable LineBreakConfig config) {
- final int defaultWordStyle = CompatChanges.isChangeEnabled(WORD_STYLE_AUTO)
- ? LINE_BREAK_WORD_STYLE_AUTO : LINE_BREAK_WORD_STYLE_NONE;
+ final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
+ .targetSdkVersion;
+ final int defaultWordStyle;
+ final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+ if (targetSdkVersion >= vicVersion) {
+ defaultWordStyle = LINE_BREAK_WORD_STYLE_AUTO;
+ } else {
+ defaultWordStyle = LINE_BREAK_WORD_STYLE_NONE;
+ }
if (config == null) {
return defaultWordStyle;
}
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 06ff4c5c5f36..d471f45e695e 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -17,6 +17,7 @@
package android.graphics.text;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
@@ -163,7 +164,8 @@ public class LineBreaker {
/** @hide */
@IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
JUSTIFICATION_MODE_NONE,
- JUSTIFICATION_MODE_INTER_WORD
+ JUSTIFICATION_MODE_INTER_WORD,
+ JUSTIFICATION_MODE_INTER_CHARACTER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface JustificationMode {}
@@ -179,6 +181,12 @@ public class LineBreaker {
public static final int JUSTIFICATION_MODE_INTER_WORD = 1;
/**
+ * Value for justification mode indicating the text is justified by stretching letter spacing.
+ */
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
+ public static final int JUSTIFICATION_MODE_INTER_CHARACTER = 2;
+
+ /**
* Helper class for creating a {@link LineBreaker}.
*/
public static final class Builder {
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 76e1a8beb890..884268a4b85c 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -279,6 +279,10 @@ public class MeasuredText {
* offset is zero. After the style is applied the internal offset is moved to {@code offset
* + length}, and next call will start from this new position.
*
+ * <p>
+ * {@link Paint#TEXT_RUN_FLAG_RIGHT_EDGE} and {@link Paint#TEXT_RUN_FLAG_LEFT_EDGE} are
+ * ignored and treated as both of them are set.
+ *
* @param paint a paint
* @param length a length to be applied with a given paint, can not exceed the length of the
* text