Loading core/java/android/text/flags/fix_double_underline.aconfig 0 → 100644 +8 −0 Original line number Diff line number Diff line package: "com.android.text.flags" flag { name: "fix_double_underline" namespace: "text" description: "Feature flag for fixing double underline because of the multiple font used in the single line." bug: "297336724" } libs/hwui/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,7 @@ cc_defaults { "libcrypto", "libsync", "libui", "aconfig_text_flags_c_lib", ], static_libs: [ "libEGL_blobCache", Loading Loading @@ -712,11 +713,13 @@ cc_test { ], static_libs: [ "libflagtest", "libgmock", "libhwui_static", ], shared_libs: [ "libmemunreachable", "server_configurable_flags", ], srcs: [ "tests/unit/main.cpp", Loading Loading @@ -756,6 +759,7 @@ cc_test { "tests/unit/TestUtilsTests.cpp", "tests/unit/ThreadBaseTests.cpp", "tests/unit/TypefaceTests.cpp", "tests/unit/UnderlineTest.cpp", "tests/unit/VectorDrawableTests.cpp", "tests/unit/WebViewFunctorManagerTests.cpp", ], Loading libs/hwui/FeatureFlags.h 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * Copyright (C) 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. */ #ifndef ANDROID_HWUI_FEATURE_FLAGS_H #define ANDROID_HWUI_FEATURE_FLAGS_H #ifdef __ANDROID__ #include <com_android_text_flags.h> #endif // __ANDROID__ namespace android { namespace text_feature { inline bool fix_double_underline() { #ifdef __ANDROID__ return com_android_text_flags_fix_double_underline(); #else return true; #endif // __ANDROID__ } } // namespace text_feature } // namespace android #endif // ANDROID_HWUI_FEATURE_FLAGS_H libs/hwui/SkiaCanvas.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ #include <utility> #include "CanvasProperty.h" #include "FeatureFlags.h" #include "Mesh.h" #include "NinePatchUtils.h" #include "VectorDrawable.h" Loading Loading @@ -795,8 +796,10 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai sk_sp<SkTextBlob> textBlob(builder.make()); applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); if (!text_feature::fix_double_underline()) { drawTextDecorations(x, y, totalAdvance, paintCopy); } } void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, const Paint& paint, const SkPath& path, size_t start, Loading libs/hwui/hwui/Canvas.cpp +31 −79 Original line number Diff line number Diff line Loading @@ -16,17 +16,18 @@ #include "Canvas.h" #include <SkFontMetrics.h> #include <SkRRect.h> #include "FeatureFlags.h" #include "MinikinUtils.h" #include "Paint.h" #include "Properties.h" #include "RenderNode.h" #include "Typeface.h" #include "pipeline/skia/SkiaRecordingCanvas.h" #include "hwui/DrawTextFunctor.h" #include "hwui/PaintFilter.h" #include <SkFontMetrics.h> #include <SkRRect.h> #include "pipeline/skia/SkiaRecordingCanvas.h" namespace android { Loading @@ -34,13 +35,6 @@ Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::Rende return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height); } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, const Paint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); const SkScalar bottom = top + strokeWidth; canvas->drawRect(left, top, right, bottom, paint); } void Canvas::drawTextDecorations(float x, float y, float length, const Paint& paint) { // paint has already been filtered by our caller, so we can ignore any filter const bool strikeThru = paint.isStrikeThru(); Loading Loading @@ -72,73 +66,6 @@ void Canvas::drawTextDecorations(float x, float y, float length, const Paint& pa } } static void simplifyPaint(int color, Paint* paint) { paint->setColor(color); paint->setShader(nullptr); paint->setColorFilter(nullptr); paint->setLooper(nullptr); paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); paint->setStrokeJoin(SkPaint::kRound_Join); paint->setLooper(nullptr); } class DrawTextFunctor { public: DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x, float y, float totalAdvance) : layout(layout) , canvas(canvas) , paint(paint) , x(x) , y(y) , totalAdvance(totalAdvance) {} void operator()(size_t start, size_t end) { auto glyphFunc = [&](uint16_t* text, float* positions) { for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { text[textIndex++] = layout.getGlyphId(i); positions[posIndex++] = x + layout.getX(i); positions[posIndex++] = y + layout.getY(i); } }; size_t glyphCount = end - start; if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { // high contrast draw path int color = paint.getColor(); int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); bool darken = channelSum < (128 * 3); // outline gDrawTextBlobMode = DrawTextBlobMode::HctOutline; Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); // inner gDrawTextBlobMode = DrawTextBlobMode::HctInner; Paint innerPaint(paint); simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); innerPaint.setStyle(SkPaint::kFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance); gDrawTextBlobMode = DrawTextBlobMode::Normal; } else { // standard draw path canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); } } private: const minikin::Layout& layout; Canvas* canvas; const Paint& paint; float x; float y; float totalAdvance; }; void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions, int glyphCount, const Paint& paint) { // Minikin modify skFont for auto-fakebold/auto-fakeitalic. Loading Loading @@ -182,6 +109,31 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); if (text_feature::fix_double_underline()) { Paint copied(paint); PaintFilter* filter = getPaintFilter(); if (filter != nullptr) { filter->filterFullPaint(&copied); } const bool isUnderline = copied.isUnderline(); const bool isStrikeThru = copied.isStrikeThru(); if (isUnderline || isStrikeThru) { const SkScalar left = x; const SkScalar right = x + layout.getAdvance(); if (isUnderline) { const SkScalar top = y + f.getUnderlinePosition(); drawStroke(left, right, top, f.getUnderlineThickness(), copied, this); } if (isStrikeThru) { float textSize = paint.getSkFont().getSize(); const float position = textSize * Paint::kStdStrikeThru_Top; const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness; const SkScalar top = y + position; drawStroke(left, right, top, thickness, copied, this); } } } } void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, Loading Loading
core/java/android/text/flags/fix_double_underline.aconfig 0 → 100644 +8 −0 Original line number Diff line number Diff line package: "com.android.text.flags" flag { name: "fix_double_underline" namespace: "text" description: "Feature flag for fixing double underline because of the multiple font used in the single line." bug: "297336724" }
libs/hwui/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,7 @@ cc_defaults { "libcrypto", "libsync", "libui", "aconfig_text_flags_c_lib", ], static_libs: [ "libEGL_blobCache", Loading Loading @@ -712,11 +713,13 @@ cc_test { ], static_libs: [ "libflagtest", "libgmock", "libhwui_static", ], shared_libs: [ "libmemunreachable", "server_configurable_flags", ], srcs: [ "tests/unit/main.cpp", Loading Loading @@ -756,6 +759,7 @@ cc_test { "tests/unit/TestUtilsTests.cpp", "tests/unit/ThreadBaseTests.cpp", "tests/unit/TypefaceTests.cpp", "tests/unit/UnderlineTest.cpp", "tests/unit/VectorDrawableTests.cpp", "tests/unit/WebViewFunctorManagerTests.cpp", ], Loading
libs/hwui/FeatureFlags.h 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * Copyright (C) 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. */ #ifndef ANDROID_HWUI_FEATURE_FLAGS_H #define ANDROID_HWUI_FEATURE_FLAGS_H #ifdef __ANDROID__ #include <com_android_text_flags.h> #endif // __ANDROID__ namespace android { namespace text_feature { inline bool fix_double_underline() { #ifdef __ANDROID__ return com_android_text_flags_fix_double_underline(); #else return true; #endif // __ANDROID__ } } // namespace text_feature } // namespace android #endif // ANDROID_HWUI_FEATURE_FLAGS_H
libs/hwui/SkiaCanvas.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ #include <utility> #include "CanvasProperty.h" #include "FeatureFlags.h" #include "Mesh.h" #include "NinePatchUtils.h" #include "VectorDrawable.h" Loading Loading @@ -795,8 +796,10 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai sk_sp<SkTextBlob> textBlob(builder.make()); applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); if (!text_feature::fix_double_underline()) { drawTextDecorations(x, y, totalAdvance, paintCopy); } } void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, const Paint& paint, const SkPath& path, size_t start, Loading
libs/hwui/hwui/Canvas.cpp +31 −79 Original line number Diff line number Diff line Loading @@ -16,17 +16,18 @@ #include "Canvas.h" #include <SkFontMetrics.h> #include <SkRRect.h> #include "FeatureFlags.h" #include "MinikinUtils.h" #include "Paint.h" #include "Properties.h" #include "RenderNode.h" #include "Typeface.h" #include "pipeline/skia/SkiaRecordingCanvas.h" #include "hwui/DrawTextFunctor.h" #include "hwui/PaintFilter.h" #include <SkFontMetrics.h> #include <SkRRect.h> #include "pipeline/skia/SkiaRecordingCanvas.h" namespace android { Loading @@ -34,13 +35,6 @@ Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::Rende return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height); } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, const Paint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); const SkScalar bottom = top + strokeWidth; canvas->drawRect(left, top, right, bottom, paint); } void Canvas::drawTextDecorations(float x, float y, float length, const Paint& paint) { // paint has already been filtered by our caller, so we can ignore any filter const bool strikeThru = paint.isStrikeThru(); Loading Loading @@ -72,73 +66,6 @@ void Canvas::drawTextDecorations(float x, float y, float length, const Paint& pa } } static void simplifyPaint(int color, Paint* paint) { paint->setColor(color); paint->setShader(nullptr); paint->setColorFilter(nullptr); paint->setLooper(nullptr); paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); paint->setStrokeJoin(SkPaint::kRound_Join); paint->setLooper(nullptr); } class DrawTextFunctor { public: DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x, float y, float totalAdvance) : layout(layout) , canvas(canvas) , paint(paint) , x(x) , y(y) , totalAdvance(totalAdvance) {} void operator()(size_t start, size_t end) { auto glyphFunc = [&](uint16_t* text, float* positions) { for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { text[textIndex++] = layout.getGlyphId(i); positions[posIndex++] = x + layout.getX(i); positions[posIndex++] = y + layout.getY(i); } }; size_t glyphCount = end - start; if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { // high contrast draw path int color = paint.getColor(); int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); bool darken = channelSum < (128 * 3); // outline gDrawTextBlobMode = DrawTextBlobMode::HctOutline; Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); // inner gDrawTextBlobMode = DrawTextBlobMode::HctInner; Paint innerPaint(paint); simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); innerPaint.setStyle(SkPaint::kFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance); gDrawTextBlobMode = DrawTextBlobMode::Normal; } else { // standard draw path canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); } } private: const minikin::Layout& layout; Canvas* canvas; const Paint& paint; float x; float y; float totalAdvance; }; void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions, int glyphCount, const Paint& paint) { // Minikin modify skFont for auto-fakebold/auto-fakeitalic. Loading Loading @@ -182,6 +109,31 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); if (text_feature::fix_double_underline()) { Paint copied(paint); PaintFilter* filter = getPaintFilter(); if (filter != nullptr) { filter->filterFullPaint(&copied); } const bool isUnderline = copied.isUnderline(); const bool isStrikeThru = copied.isStrikeThru(); if (isUnderline || isStrikeThru) { const SkScalar left = x; const SkScalar right = x + layout.getAdvance(); if (isUnderline) { const SkScalar top = y + f.getUnderlinePosition(); drawStroke(left, right, top, f.getUnderlineThickness(), copied, this); } if (isStrikeThru) { float textSize = paint.getSkFont().getSize(); const float position = textSize * Paint::kStdStrikeThru_Top; const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness; const SkScalar top = y + position; drawStroke(left, right, top, thickness, copied, this); } } } } void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, Loading