|  | /* | 
|  | * Copyright 2021 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "gm/gm.h" | 
|  | #include "include/core/SkCanvas.h" | 
|  | #include "include/core/SkColor.h" | 
|  | #include "include/core/SkFont.h" | 
|  | #include "include/core/SkFontMetrics.h" | 
|  | #include "include/core/SkGraphics.h" | 
|  | #include "include/core/SkPaint.h" | 
|  | #include "include/core/SkRefCnt.h" | 
|  | #include "include/core/SkScalar.h" | 
|  | #include "include/core/SkSize.h" | 
|  | #include "include/core/SkString.h" | 
|  | #include "include/core/SkTypeface.h" | 
|  | #include "tools/Resources.h" | 
|  | #include "tools/ToolUtils.h" | 
|  |  | 
|  | #include <string.h> | 
|  | #include <initializer_list> | 
|  |  | 
|  | namespace skiagm { | 
|  |  | 
|  | namespace { | 
|  | const SkScalar kTextSizes[] = {12, 18, 30, 120}; | 
|  | const char kTestFontName[] = "fonts/test_glyphs-glyf_colr_1.ttf"; | 
|  | const char kTestFontNameVariable[] = "fonts/test_glyphs-glyf_colr_1_variable.ttf"; | 
|  | const SkScalar xWidth = 1200; | 
|  | const SkScalar xTranslate = 200; | 
|  | } | 
|  |  | 
|  | class ColrV1GM : public GM { | 
|  | public: | 
|  | ColrV1GM(const char* testName, | 
|  | SkSpan<const uint32_t> codepoints, | 
|  | SkScalar skewX, | 
|  | SkScalar rotateDeg, | 
|  | std::initializer_list<SkFontArguments::VariationPosition::Coordinate> | 
|  | specifiedVariations) | 
|  | : fTestName(testName) | 
|  | , fCodepoints(codepoints) | 
|  | , fSkewX(skewX) | 
|  | , fRotateDeg(rotateDeg) { | 
|  | fVariationPosition.coordinateCount = specifiedVariations.size(); | 
|  | fCoordinates = std::make_unique<SkFontArguments::VariationPosition::Coordinate[]>( | 
|  | specifiedVariations.size()); | 
|  | for (size_t i = 0; i < specifiedVariations.size(); ++i) { | 
|  | fCoordinates[i] = std::data(specifiedVariations)[i]; | 
|  | } | 
|  |  | 
|  | fVariationPosition.coordinates = fCoordinates.get(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void onOnceBeforeDraw() override { | 
|  | if (fVariationPosition.coordinateCount) { | 
|  | fTypeface = MakeResourceAsTypeface(kTestFontNameVariable); | 
|  | } else { | 
|  | fTypeface = MakeResourceAsTypeface(kTestFontName); | 
|  | } | 
|  | fVariationSliders = ToolUtils::VariationSliders(fTypeface.get(), fVariationPosition); | 
|  | } | 
|  |  | 
|  | SkString onShortName() override { | 
|  | SkASSERT(!fTestName.isEmpty()); | 
|  | SkString gm_name = SkStringPrintf("colrv1_%s", fTestName.c_str()); | 
|  |  | 
|  | if (fSkewX) { | 
|  | gm_name.append(SkStringPrintf("_skew_%.2f", fSkewX)); | 
|  | } | 
|  |  | 
|  | if (fRotateDeg) { | 
|  | gm_name.append(SkStringPrintf("_rotate_%.2f", fRotateDeg)); | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < fVariationPosition.coordinateCount; ++i) { | 
|  | SkString tagName = ToolUtils::VariationSliders::tagToString( | 
|  | fVariationPosition.coordinates[i].axis); | 
|  | gm_name.append(SkStringPrintf( | 
|  | "_%s_%.2f", tagName.c_str(), fVariationPosition.coordinates[i].value)); | 
|  | } | 
|  |  | 
|  | return gm_name; | 
|  | } | 
|  |  | 
|  | bool onGetControls(SkMetaData* controls) override { | 
|  | return fVariationSliders.writeControls(controls); | 
|  | } | 
|  |  | 
|  | void onSetControls(const SkMetaData& controls) override { | 
|  | return fVariationSliders.readControls(controls); | 
|  | } | 
|  |  | 
|  | SkISize onISize() override { | 
|  | // Sweep tests get a slightly wider canvas so that glyphs from one group fit in one row. | 
|  | if (fTestName.equals("sweep_varsweep")) { | 
|  | return SkISize::Make(xWidth + 500, xWidth); | 
|  | } | 
|  | return SkISize::Make(xWidth, xWidth); | 
|  | } | 
|  |  | 
|  | sk_sp<SkTypeface> makeVariedTypeface() { | 
|  | if (!fTypeface) { | 
|  | return nullptr; | 
|  | } | 
|  | SkSpan<const SkFontArguments::VariationPosition::Coordinate> coords = | 
|  | fVariationSliders.getCoordinates(); | 
|  | SkFontArguments::VariationPosition varPos = {coords.data(), | 
|  | static_cast<int>(coords.size())}; | 
|  | SkFontArguments args; | 
|  | args.setVariationDesignPosition(varPos); | 
|  | return fTypeface->makeClone(args); | 
|  | } | 
|  |  | 
|  | DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { | 
|  | canvas->drawColor(SK_ColorWHITE); | 
|  | SkPaint paint; | 
|  |  | 
|  | canvas->translate(xTranslate, 20); | 
|  |  | 
|  | if (!fTypeface) { | 
|  | *errorMsg = "Did not recognize COLR v1 font format."; | 
|  | return DrawResult::kSkip; | 
|  | } | 
|  |  | 
|  | canvas->rotate(fRotateDeg); | 
|  | canvas->skew(fSkewX, 0); | 
|  |  | 
|  | SkFont font(makeVariedTypeface()); | 
|  |  | 
|  | SkFontMetrics metrics; | 
|  | SkScalar y = 0; | 
|  | std::vector<SkColor> paint_colors = { | 
|  | SK_ColorBLACK, SK_ColorGREEN, SK_ColorRED, SK_ColorBLUE}; | 
|  | auto paint_color_iterator = paint_colors.begin(); | 
|  | for (SkScalar textSize : kTextSizes) { | 
|  | font.setSize(textSize); | 
|  | font.getMetrics(&metrics); | 
|  | SkScalar y_shift = -(metrics.fAscent + metrics.fDescent + metrics.fLeading) * 1.2; | 
|  | y += y_shift; | 
|  | paint.setColor(*paint_color_iterator); | 
|  | int x = 0; | 
|  | // Perform simple line breaking to fit more glyphs into the GM canvas. | 
|  | for (size_t i = 0; i < fCodepoints.size(); ++i) { | 
|  | canvas->drawSimpleText(&fCodepoints[i], | 
|  | sizeof(uint32_t), | 
|  | SkTextEncoding::kUTF32, | 
|  | x, | 
|  | y, | 
|  | font, | 
|  | paint); | 
|  | SkScalar glyphAdvance = font.measureText( | 
|  | &fCodepoints[i], sizeof(uint32_t), SkTextEncoding::kUTF32, nullptr); | 
|  | if (x + glyphAdvance < onISize().width() - xTranslate) { | 
|  | x += glyphAdvance + glyphAdvance * 0.05f; | 
|  | } else { | 
|  | y += y_shift; | 
|  | x = 0; | 
|  | } | 
|  | } | 
|  | paint_color_iterator++; | 
|  | } | 
|  | return DrawResult::kOk; | 
|  | } | 
|  |  | 
|  | private: | 
|  | using INHERITED = GM; | 
|  |  | 
|  | SkString fTestName; | 
|  | sk_sp<SkTypeface> fTypeface; | 
|  | SkSpan<const uint32_t> fCodepoints; | 
|  | SkScalar fSkewX; | 
|  | SkScalar fRotateDeg; | 
|  | std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> fCoordinates; | 
|  | SkFontArguments::VariationPosition fVariationPosition; | 
|  | ToolUtils::VariationSliders fVariationSliders; | 
|  | }; | 
|  |  | 
|  | // Generated using test glyphs generator script from https://github.com/googlefonts/color-fonts: | 
|  | // $ python3 config/test_glyphs-glyf_colr_1.py -vvv  --generate-descriptions fonts/ | 
|  | // Regenerate descriptions and paste the generated arrays here when updating the test font. | 
|  | namespace ColrV1TestDefinitions { | 
|  | const uint32_t gradient_stops_repeat[] = {0xf0100, 0xf0101, 0xf0102, 0xf0103}; | 
|  | const uint32_t sweep_varsweep[] = { | 
|  | 0xf0200, 0xf0201, 0xf0202, 0xf0203, 0xf0204, 0xf0205, 0xf0206, 0xf0207, 0xf0208, | 
|  | 0xf0209, 0xf020a, 0xf020b, 0xf020c, 0xf020d, 0xf020e, 0xf020f, 0xf0210, 0xf0211, | 
|  | 0xf0212, 0xf0213, 0xf0214, 0xf0215, 0xf0216, 0xf0217, 0xf0218, 0xf0219, 0xf021a, | 
|  | 0xf021b, 0xf021c, 0xf021d, 0xf021e, 0xf021f, 0xf0220, 0xf0221, 0xf0222, 0xf0223, | 
|  | 0xf0224, 0xf0225, 0xf0226, 0xf0227, 0xf0228, 0xf0229, 0xf022a, 0xf022b, 0xf022c, | 
|  | 0xf022d, 0xf022e, 0xf022f, 0xf0230, 0xf0231, 0xf0232, 0xf0233, 0xf0234, 0xf0235, | 
|  | 0xf0236, 0xf0237, 0xf0238, 0xf0239, 0xf023a, 0xf023b, 0xf023c, 0xf023d, 0xf023e, | 
|  | 0xf023f, 0xf0240, 0xf0241, 0xf0242, 0xf0243, 0xf0244, 0xf0245, 0xf0246, 0xf0247}; | 
|  | const uint32_t paint_scale[] = {0xf0300, 0xf0301, 0xf0302, 0xf0303, 0xf0304, 0xf0305}; | 
|  | const uint32_t extend_mode[] = { | 
|  | 0xf0500, 0xf0501, 0xf0502, 0xf0503, 0xf0504, 0xf0505, 0xf0506, 0xf0507, 0xf0508}; | 
|  | const uint32_t paint_rotate[] = {0xf0600, 0xf0601, 0xf0602, 0xf0603}; | 
|  | const uint32_t paint_skew[] = {0xf0700, 0xf0701, 0xf0702, 0xf0703, 0xf0704, 0xf0705}; | 
|  | const uint32_t paint_transform[] = {0xf0800, 0xf0801, 0xf0802, 0xf0803}; | 
|  | const uint32_t paint_translate[] = {0xf0900, 0xf0901, 0xf0902, 0xf0903, 0xf0904, 0xf0905, 0xf0906}; | 
|  | const uint32_t composite_mode[] = {0xf0a00, 0xf0a01, 0xf0a02, 0xf0a03, 0xf0a04, 0xf0a05, 0xf0a06, | 
|  | 0xf0a07, 0xf0a08, 0xf0a09, 0xf0a0a, 0xf0a0b, 0xf0a0c, 0xf0a0d, | 
|  | 0xf0a0e, 0xf0a0f, 0xf0a10, 0xf0a11, 0xf0a12, 0xf0a13, 0xf0a14, | 
|  | 0xf0a15, 0xf0a16, 0xf0a17, 0xf0a18, 0xf0a19, 0xf0a1a, 0xf0a1b}; | 
|  | const uint32_t foreground_color[] = { | 
|  | 0xf0b00, 0xf0b01, 0xf0b02, 0xf0b03, 0xf0b04, 0xf0b05, 0xf0b06, 0xf0b07}; | 
|  | const uint32_t clipbox[] = {0xf0c00, 0xf0c01, 0xf0c02, 0xf0c03, 0xf0c04}; | 
|  | const uint32_t gradient_p2_skewed[] = {0xf0d00}; | 
|  | const uint32_t variable_alpha[] = {0xf1000}; | 
|  | };  // namespace ColrV1TestDefinitions | 
|  |  | 
|  | namespace { | 
|  | std::unique_ptr<ColrV1GM> F( | 
|  | const char* name, | 
|  | SkSpan<const uint32_t> codepoints, | 
|  | SkScalar skewX, | 
|  | SkScalar rotateDeg, | 
|  | std::initializer_list<SkFontArguments::VariationPosition::Coordinate> variations) | 
|  | { | 
|  | return std::make_unique<ColrV1GM>(name, codepoints, skewX, rotateDeg, variations); | 
|  | } | 
|  |  | 
|  | SkFourByteTag constexpr operator "" _t(const char* tagName, size_t size) { | 
|  | SkASSERT(size == 4); | 
|  | return SkSetFourByteTag(tagName[0], tagName[1], tagName[2], tagName[3]); | 
|  | } | 
|  | } | 
|  | #define C(TEST_CATEGORY) #TEST_CATEGORY, ColrV1TestDefinitions::TEST_CATEGORY | 
|  | DEF_GM(return F(C(clipbox),                0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(clipbox),                0.0f,  0.0f, {{"CLIO"_t, 200.f}})) | 
|  | DEF_GM(return F(C(composite_mode),         0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(composite_mode),        -0.5f,  0.0f, {})) | 
|  | DEF_GM(return F(C(composite_mode),        -0.5f, 20.0f, {})) | 
|  | DEF_GM(return F(C(composite_mode),         0.0f, 20.0f, {})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL1"_t, -0.25f}, {"COL3"_t, 0.25f}})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL1"_t, 0.5f}, {"COL3"_t, -0.5f}})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL3"_t, 0.5f}})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL3"_t, 1.f}})) | 
|  | // Radial gradient tests where radii become negative | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL1"_t, -1.5f}})) | 
|  | // Both radii negative and equal, nothing should render. | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, -200.f}, {"GRR1"_t, -300.f}})) | 
|  | // Small cones opening to the right. | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRX0"_t, -1000.f}, {"GRX1"_t, -1000.f}, {"GRR0"_t, -1000.f}, {"GRR1"_t, -900.f}})) | 
|  | // Small cones opening to the left. | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRX0"_t, 1000.f}, {"GRX1"_t, -1000.f}, {"GRR0"_t, -1000.f}, {"GRR1"_t, 200.f}})) | 
|  | // Pad cone should appear green. | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, -50.f}, {"COL3"_t, -2.f}, {"COL2"_t, -2.f}, {"COL1"_t, -0.9f}})) | 
|  | // Pad cone should appear red. | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, -50.f}, {"COL3"_t, -2.f}, {"COL2"_t, -2.f}, {"COL1"_t, -1.1f}})) | 
|  | // Hard boundary for pad mode, should appear on the right inside the glyph for linear and radial. | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL3"_t, 1.f}, {"COL2"_t, 1.5f}, {"COL1"_t, 2.f}})) | 
|  | // Extend mode with rotation or skew below. | 
|  | DEF_GM(return F(C(extend_mode),           -0.5f,  0.0f, {})) | 
|  | DEF_GM(return F(C(extend_mode),           -0.5f, 20.0f, {})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f, 20.0f, {})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL2"_t, -0.3f}})) | 
|  | DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, 430.f}, {"GRR1"_t, 40.f}})) | 
|  | DEF_GM(return F(C(foreground_color),       0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(gradient_p2_skewed),     0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(gradient_stops_repeat),  0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(gradient_stops_repeat), -0.5f,  0.0f, {})) | 
|  | DEF_GM(return F(C(gradient_stops_repeat), -0.5f, 20.0f, {})) | 
|  | DEF_GM(return F(C(gradient_stops_repeat),  0.0f, 20.0f, {})) | 
|  | DEF_GM(return F(C(paint_rotate),           0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(paint_rotate),           0.0f,  0.0f, {{"ROTA"_t, 40.f}})) | 
|  | DEF_GM(return F(C(paint_rotate),           0.0f,  0.0f, {{"ROTX"_t, -250.f}, {"ROTY"_t, -250.f}})) | 
|  | DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {{"SCOX"_t, 200.f}, {"SCOY"_t, 200.f}})) | 
|  | DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {{"SCSX"_t, 0.25f}, {"SCOY"_t, 0.25f}})) | 
|  | DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {{"SCSX"_t, -1.f}, {"SCOY"_t, -1.f}})) | 
|  | DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {{"SKXA"_t, 20.f}})) | 
|  | DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {{"SKYA"_t, 20.f}})) | 
|  | DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {{"SKCX"_t, 200.f},{"SKCY"_t, 200.f}})) | 
|  | DEF_GM(return F(C(paint_transform),        0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(paint_translate),        0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(paint_translate),        0.0f,  0.0f, {{"TLDX"_t, 100.f}, {"TLDY"_t, 100.f}})) | 
|  | DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(sweep_varsweep),        -0.5f,  0.0f, {})) | 
|  | DEF_GM(return F(C(sweep_varsweep),        -0.5f, 20.0f, {})) | 
|  | DEF_GM(return F(C(sweep_varsweep),         0.0f, 20.0f, {})) | 
|  | DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPS"_t, 0.f}})) | 
|  | DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPS"_t, 90.f}})) | 
|  | DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPE"_t, -90.f}})) | 
|  | DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPE"_t, -45.f}})) | 
|  | DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPS"_t, -45.f},{"SWPE"_t, 45.f}})) | 
|  | DEF_GM(return F(C(sweep_varsweep), | 
|  | 0.0f, | 
|  | 0.0f, | 
|  | {{"SWC1"_t, -0.25f}, | 
|  | {"SWC2"_t, 0.083333333f}, | 
|  | {"SWC3"_t, 0.083333333f}, | 
|  | {"SWC4"_t, +0.25f}})) | 
|  | DEF_GM(return F(C(variable_alpha),         0.0f,  0.0f, {})) | 
|  | DEF_GM(return F(C(variable_alpha),         0.0f,  0.0f, {{"APH1"_t, -0.7f}})) | 
|  | DEF_GM(return F(C(variable_alpha),         0.0f,  0.0f, {{"APH2"_t, -0.7f}, {"APH3"_t, -0.2f}})) | 
|  |  | 
|  | }  // namespace skiagm |