diff options
| -rw-r--r-- | core/java/android/text/Layout.java | 92 | ||||
| -rw-r--r-- | core/java/android/widget/Editor.java | 18 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/LayoutTest.java | 275 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Canvas.java | 14 | ||||
| -rw-r--r-- | libs/hwui/hwui/DrawTextFunctor.h | 1 | ||||
| -rw-r--r-- | libs/hwui/jni/android_graphics_Canvas.cpp | 5 |
6 files changed, 394 insertions, 11 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 8e52af3fb57b..8dee4b19c6d3 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -16,6 +16,7 @@ package android.text; +import static com.android.graphics.hwui.flags.Flags.highContrastTextLuminance; import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION; @@ -28,7 +29,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.BlendMode; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; @@ -46,7 +49,9 @@ import android.text.style.ReplacementSpan; import android.text.style.TabStopSpan; import android.widget.TextView; +import com.android.graphics.hwui.flags.Flags; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; @@ -480,9 +485,23 @@ public abstract class Layout { int lastLine = TextUtils.unpackRangeEndFromLong(lineRange); if (lastLine < 0) return; - drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, - cursorOffsetVertical, firstLine, lastLine); + if (shouldDrawHighlightsOnTop(canvas)) { + drawBackground(canvas, firstLine, lastLine); + } else { + drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, + cursorOffsetVertical, firstLine, lastLine); + } + drawText(canvas, firstLine, lastLine); + + // Since high contrast text draws a solid rectangle background behind the text, it covers up + // the highlights and selections. In this case we draw over the top of the text with a + // blend mode that ensures the text stays high-contrast. + if (shouldDrawHighlightsOnTop(canvas)) { + drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, + cursorOffsetVertical, firstLine, lastLine); + } + if (leftShift != 0) { // Manually translate back to the original position because of b/324498002, using // save/restore disappears the toggle switch drawables. @@ -490,6 +509,19 @@ public abstract class Layout { } } + private static boolean shouldDrawHighlightsOnTop(Canvas canvas) { + return Flags.highContrastTextSmallTextRect() && canvas.isHighContrastTextEnabled(); + } + + private static Paint setToHighlightPaint(Paint p, BlendMode blendMode, Paint outPaint) { + if (p == null) return null; + outPaint.set(p); + outPaint.setBlendMode(blendMode); + // Yellow for maximum contrast + outPaint.setColor(Color.YELLOW); + return outPaint; + } + /** * Draw text part of this layout. * @@ -542,11 +574,28 @@ public abstract class Layout { int firstLine, int lastLine) { drawBackground(canvas, firstLine, lastLine); + drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, + cursorOffsetVertical, firstLine, lastLine); + } + + /** + * @hide public for Editor.java + */ + public void drawHighlights( + @NonNull Canvas canvas, + @Nullable List<Path> highlightPaths, + @Nullable List<Paint> highlightPaints, + @Nullable Path selectionPath, + @Nullable Paint selectionPaint, + int cursorOffsetVertical, + int firstLine, + int lastLine) { if (highlightPaths == null && highlightPaints == null) { return; } if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical); try { + BlendMode blendMode = determineHighContrastHighlightBlendMode(canvas); if (highlightPaths != null) { if (highlightPaints == null) { throw new IllegalArgumentException( @@ -559,7 +608,12 @@ public abstract class Layout { } for (int i = 0; i < highlightPaths.size(); ++i) { final Path highlight = highlightPaths.get(i); - final Paint highlightPaint = highlightPaints.get(i); + Paint highlightPaint = highlightPaints.get(i); + if (shouldDrawHighlightsOnTop(canvas)) { + highlightPaint = setToHighlightPaint(highlightPaint, blendMode, + mWorkPlainPaint); + } + if (highlight != null) { canvas.drawPath(highlight, highlightPaint); } @@ -567,6 +621,10 @@ public abstract class Layout { } if (selectionPath != null) { + if (shouldDrawHighlightsOnTop(canvas)) { + selectionPaint = setToHighlightPaint(selectionPaint, blendMode, + mWorkPlainPaint); + } canvas.drawPath(selectionPath, selectionPaint); } } finally { @@ -574,6 +632,31 @@ public abstract class Layout { } } + @Nullable + private BlendMode determineHighContrastHighlightBlendMode(Canvas canvas) { + if (!shouldDrawHighlightsOnTop(canvas)) { + return null; + } + + return isHighContrastTextDark() ? BlendMode.MULTIPLY : BlendMode.DIFFERENCE; + } + + private boolean isHighContrastTextDark() { + // High-contrast text mode + // Determine if the text is black-on-white or white-on-black, so we know what blendmode will + // give the highest contrast and most realistic text color. + // This equation should match the one in libs/hwui/hwui/DrawTextFunctor.h + if (highContrastTextLuminance()) { + var lab = new double[3]; + ColorUtils.colorToLAB(mPaint.getColor(), lab); + return lab[0] < 0.5; + } else { + var color = mPaint.getColor(); + int channelSum = Color.red(color) + Color.green(color) + Color.blue(color); + return channelSum < (128 * 3); + } + } + private boolean isJustificationRequired(int lineNum) { if (mJustificationMode == JUSTIFICATION_MODE_NONE) return false; final int lineEnd = getLineEnd(lineNum); @@ -3396,7 +3479,8 @@ public abstract class Layout { private CharSequence mText; @UnsupportedAppUsage private TextPaint mPaint; - private TextPaint mWorkPaint = new TextPaint(); + private final TextPaint mWorkPaint = new TextPaint(); + private final Paint mWorkPlainPaint = new Paint(); private int mWidth; private Alignment mAlignment = Alignment.ALIGN_NORMAL; private float mSpacingMult; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 57d268ced6f4..139ebc38706e 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -19,6 +19,8 @@ package android.widget; import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID; +import static com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect; + import android.R; import android.animation.ValueAnimator; import android.annotation.IntDef; @@ -2151,8 +2153,15 @@ public class Editor { int lastLine = TextUtils.unpackRangeEndFromLong(lineRange); if (lastLine < 0) return; - layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight, - selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine); + boolean shouldDrawHighlightsOnTop = highContrastTextSmallTextRect() + && canvas.isHighContrastTextEnabled(); + + if (!shouldDrawHighlightsOnTop) { + layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight, + selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine); + } else { + layout.drawBackground(canvas, firstLine, lastLine); + } if (layout instanceof DynamicLayout) { if (mTextRenderNodes == null) { @@ -2226,6 +2235,11 @@ public class Editor { // Boring layout is used for empty and hint text layout.drawText(canvas, firstLine, lastLine); } + + if (shouldDrawHighlightsOnTop) { + layout.drawHighlights(canvas, highlightPaths, highlightPaints, selectionHighlight, + selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine); + } } private int drawHardwareAcceleratedInner(Canvas canvas, Layout layout, Path highlight, diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java index 5649e7118fd5..f60eff69690a 100644 --- a/core/tests/coretests/src/android/text/LayoutTest.java +++ b/core/tests/coretests/src/android/text/LayoutTest.java @@ -16,6 +16,11 @@ package android.text; +import static com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -26,11 +31,16 @@ import static org.junit.Assert.fail; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.text.Layout.Alignment; import android.text.style.StrikethroughSpan; @@ -38,6 +48,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +60,9 @@ import java.util.Locale; @SmallTest @RunWith(AndroidJUnit4.class) public class LayoutTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final int LINE_COUNT = 5; private static final int LINE_HEIGHT = 12; private static final int LINE_DESCENT = 4; @@ -638,22 +652,268 @@ public class LayoutTest { } } - private final class MockCanvas extends Canvas { + @Test + @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) + public void highContrastTextEnabled_testDrawSelectionAndHighlight_drawsHighContrastSelectionAndHighlight() { + Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, + mAlign, mSpacingMult, mSpacingAdd); + + List<Path> highlightPaths = new ArrayList<>(); + List<Paint> highlightPaints = new ArrayList<>(); + + Path selectionPath = new Path(); + RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT); + selectionPath.addRect(selectionRect, Path.Direction.CW); + highlightPaths.add(selectionPath); + + Paint selectionPaint = new Paint(); + selectionPaint.setColor(Color.CYAN); + highlightPaints.add(selectionPaint); + + final int width = 256; + final int height = 256; + MockCanvas c = new MockCanvas(width, height); + c.setHighContrastTextEnabled(true); + layout.draw(c, highlightPaths, highlightPaints, selectionPath, selectionPaint, + /* cursorOffsetVertical= */ 0); + List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands(); + var textsDrawn = LINE_COUNT; + var highlightsDrawn = 2; + assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn); + + var highlightsFound = 0; + var curLineIndex = 0; + for (int i = 0; i < drawCommands.size(); i++) { + MockCanvas.DrawCommand drawCommand = drawCommands.get(i); + + if (drawCommand.path != null) { + assertThat(drawCommand.path).isEqualTo(selectionPath); + assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW); + assertThat(drawCommand.paint.getBlendMode()).isNotNull(); + highlightsFound++; + } else if (drawCommand.text != null) { + int start = layout.getLineStart(curLineIndex); + int end = layout.getLineEnd(curLineIndex); + assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text); + curLineIndex++; + + assertWithMessage("highlight is drawn on top of text") + .that(highlightsFound).isEqualTo(0); + } + } + + assertThat(highlightsFound).isEqualTo(2); + } + + @Test + @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) + public void highContrastTextEnabled_testDrawHighlight_drawsHighContrastHighlight() { + Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, + mAlign, mSpacingMult, mSpacingAdd); + + List<Path> highlightPaths = new ArrayList<>(); + List<Paint> highlightPaints = new ArrayList<>(); + + Path selectionPath = new Path(); + RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT); + selectionPath.addRect(selectionRect, Path.Direction.CW); + highlightPaths.add(selectionPath); + + Paint selectionPaint = new Paint(); + selectionPaint.setColor(Color.CYAN); + highlightPaints.add(selectionPaint); + + final int width = 256; + final int height = 256; + MockCanvas c = new MockCanvas(width, height); + c.setHighContrastTextEnabled(true); + layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null, + /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0); + List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands(); + var textsDrawn = LINE_COUNT; + var highlightsDrawn = 1; + assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn); + + var highlightsFound = 0; + var curLineIndex = 0; + for (int i = 0; i < drawCommands.size(); i++) { + MockCanvas.DrawCommand drawCommand = drawCommands.get(i); + + if (drawCommand.path != null) { + assertThat(drawCommand.path).isEqualTo(selectionPath); + assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW); + assertThat(drawCommand.paint.getBlendMode()).isNotNull(); + highlightsFound++; + } else if (drawCommand.text != null) { + int start = layout.getLineStart(curLineIndex); + int end = layout.getLineEnd(curLineIndex); + assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text); + curLineIndex++; + + assertWithMessage("highlight is drawn on top of text") + .that(highlightsFound).isEqualTo(0); + } + } + + assertThat(highlightsFound).isEqualTo(1); + } + + @Test + @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) + public void highContrastTextDisabledByDefault_testDrawHighlight_drawsNormalHighlightBehind() { + Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, + mAlign, mSpacingMult, mSpacingAdd); + + List<Path> highlightPaths = new ArrayList<>(); + List<Paint> highlightPaints = new ArrayList<>(); + + Path selectionPath = new Path(); + RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT); + selectionPath.addRect(selectionRect, Path.Direction.CW); + highlightPaths.add(selectionPath); + + Paint selectionPaint = new Paint(); + selectionPaint.setColor(Color.CYAN); + highlightPaints.add(selectionPaint); + + final int width = 256; + final int height = 256; + MockCanvas c = new MockCanvas(width, height); + layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null, + /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0); + List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands(); + var textsDrawn = LINE_COUNT; + var highlightsDrawn = 1; + assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn); + + var highlightsFound = 0; + var curLineIndex = 0; + for (int i = 0; i < drawCommands.size(); i++) { + MockCanvas.DrawCommand drawCommand = drawCommands.get(i); + + if (drawCommand.path != null) { + assertThat(drawCommand.path).isEqualTo(selectionPath); + assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN); + assertThat(drawCommand.paint.getBlendMode()).isNull(); + highlightsFound++; + } else if (drawCommand.text != null) { + int start = layout.getLineStart(curLineIndex); + int end = layout.getLineEnd(curLineIndex); + assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text); + curLineIndex++; + + assertWithMessage("highlight is drawn behind text") + .that(highlightsFound).isGreaterThan(0); + } + } + + assertThat(highlightsFound).isEqualTo(1); + } + + @Test + @RequiresFlagsDisabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) + public void highContrastTextEnabledButFlagOff_testDrawHighlight_drawsNormalHighlightBehind() { + Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, + mAlign, mSpacingMult, mSpacingAdd); + + List<Path> highlightPaths = new ArrayList<>(); + List<Paint> highlightPaints = new ArrayList<>(); + + Path selectionPath = new Path(); + RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT); + selectionPath.addRect(selectionRect, Path.Direction.CW); + highlightPaths.add(selectionPath); + + Paint selectionPaint = new Paint(); + selectionPaint.setColor(Color.CYAN); + highlightPaints.add(selectionPaint); + + final int width = 256; + final int height = 256; + MockCanvas c = new MockCanvas(width, height); + c.setHighContrastTextEnabled(true); + layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null, + /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0); + List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands(); + var textsDrawn = LINE_COUNT; + var highlightsDrawn = 1; + assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn); - class DrawCommand { + var highlightsFound = 0; + var curLineIndex = 0; + for (int i = 0; i < drawCommands.size(); i++) { + MockCanvas.DrawCommand drawCommand = drawCommands.get(i); + + if (drawCommand.path != null) { + assertThat(drawCommand.path).isEqualTo(selectionPath); + assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN); + assertThat(drawCommand.paint.getBlendMode()).isNull(); + highlightsFound++; + } else if (drawCommand.text != null) { + int start = layout.getLineStart(curLineIndex); + int end = layout.getLineEnd(curLineIndex); + assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text); + curLineIndex++; + + assertWithMessage("highlight is drawn behind text") + .that(highlightsFound).isGreaterThan(0); + } + } + + assertThat(highlightsFound).isEqualTo(1); + } + + @Test + public void mockCanvasHighContrastOverridesCorrectly() { + var canvas = new MockCanvas(100, 100); + + assertThat(canvas.isHighContrastTextEnabled()).isFalse(); + canvas.setHighContrastTextEnabled(true); + assertThat(canvas.isHighContrastTextEnabled()).isTrue(); + canvas.setHighContrastTextEnabled(false); + assertThat(canvas.isHighContrastTextEnabled()).isFalse(); + } + + private static final class MockCanvas extends Canvas { + + static class DrawCommand { public final String text; public final float x; public final float y; + public final Path path; + public final Paint paint; - DrawCommand(String text, float x, float y) { + DrawCommand(String text, float x, float y, Paint paint) { this.text = text; this.x = x; this.y = y; + this.paint = paint; + path = null; + } + + DrawCommand(Path path, Paint paint) { + this.path = path; + this.paint = paint; + y = 0; + x = 0; + text = null; } } List<DrawCommand> mDrawCommands; + private Boolean mIsHighContrastTextOverride = null; + + public void setHighContrastTextEnabled(boolean enabled) { + mIsHighContrastTextOverride = enabled; + } + + @Override + public boolean isHighContrastTextEnabled() { + return mIsHighContrastTextOverride == null ? super.isHighContrastTextEnabled() + : mIsHighContrastTextOverride; + } + MockCanvas(int width, int height) { super(); mDrawCommands = new ArrayList<>(); @@ -666,7 +926,7 @@ public class LayoutTest { @Override public void drawText(String text, int start, int end, float x, float y, Paint p) { - mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y)); + mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y, p)); } @Override @@ -676,7 +936,7 @@ public class LayoutTest { @Override public void drawText(char[] text, int index, int count, float x, float y, Paint p) { - mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y)); + mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y, p)); } @Override @@ -691,6 +951,11 @@ public class LayoutTest { drawText(text, index, count, x, y, paint); } + @Override + public void drawPath(Path path, Paint p) { + mDrawCommands.add(new DrawCommand(path, p)); + } + List<DrawCommand> getDrawCommands() { return mDrawCommands; } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index f3bb21719890..b6ce9b64323b 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -153,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. @@ -1452,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); diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h index 02bf0d8d5e95..1fcb6920db14 100644 --- a/libs/hwui/hwui/DrawTextFunctor.h +++ b/libs/hwui/hwui/DrawTextFunctor.h @@ -92,6 +92,7 @@ public: // high contrast draw path int color = paint.getColor(); bool darken; + // This equation should match the one in core/java/android/text/Layout.java if (flags::high_contrast_text_luminance()) { uirenderer::Lab lab = uirenderer::sRGBToLab(color); darken = lab.L <= 50; diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 1fc34d633370..9b63a46822ac 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -88,6 +88,10 @@ static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHand get_canvas(canvasHandle)->setBitmap(bitmap); } +static jboolean isHighContrastText(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return get_canvas(canvasHandle)->isHighContrastText() ? JNI_TRUE : JNI_FALSE; +} + static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE; } @@ -792,6 +796,7 @@ static const JNINativeMethod gMethods[] = { // ------------ @CriticalNative ---------------- {"nIsOpaque", "(J)Z", (void*)CanvasJNI::isOpaque}, + {"nIsHighContrastText", "(J)Z", (void*)CanvasJNI::isHighContrastText}, {"nGetWidth", "(J)I", (void*)CanvasJNI::getWidth}, {"nGetHeight", "(J)I", (void*)CanvasJNI::getHeight}, {"nSave", "(JI)I", (void*)CanvasJNI::save}, |