Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd348c67 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Draw a single underline if multiple fonts are used in a single line.

Bug: 297336724
Test: atest hwui_unit_tests (with flag on/off)
Change-Id: I48018abd68c767c9c7ad08722a8974efc1bfe67c
parent 49a91155
Loading
Loading
Loading
Loading
+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"
}
+4 −0
Original line number Diff line number Diff line
@@ -143,6 +143,7 @@ cc_defaults {
                "libcrypto",
                "libsync",
                "libui",
                "aconfig_text_flags_c_lib",
            ],
            static_libs: [
                "libEGL_blobCache",
@@ -712,11 +713,13 @@ cc_test {
    ],

    static_libs: [
        "libflagtest",
        "libgmock",
        "libhwui_static",
    ],
    shared_libs: [
        "libmemunreachable",
        "server_configurable_flags",
    ],
    srcs: [
        "tests/unit/main.cpp",
@@ -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",
    ],
+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
+4 −1
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <utility>

#include "CanvasProperty.h"
#include "FeatureFlags.h"
#include "Mesh.h"
#include "NinePatchUtils.h"
#include "VectorDrawable.h"
@@ -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,
+31 −79
Original line number Diff line number Diff line
@@ -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 {

@@ -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();
@@ -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.
@@ -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