|  | /* | 
|  | * Copyright 2022 Google LLC | 
|  | * | 
|  | * 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/SkPath.h" | 
|  | #include "include/core/SkPathBuilder.h" | 
|  | #include "include/core/SkPathUtils.h" | 
|  | #include "src/base/SkFloatBits.h" | 
|  |  | 
|  | static SkPath get_path() { | 
|  | SkPathBuilder builder(SkPathFillType::kWinding); | 
|  | builder.moveTo(SkBits2Float(0x45034ec4), SkBits2Float(0x42e7fb80));  // 2100.92f, 115.991f | 
|  | builder.quadTo(SkBits2Float(0x4500f46c), | 
|  | SkBits2Float(0x43333300), | 
|  | SkBits2Float(0x4500f46c), | 
|  | SkBits2Float(0x431f0ec0));  // 2063.28f, 179.199f, 2063.28f, 159.058f | 
|  | builder.quadTo(SkBits2Float(0x4500f46c), | 
|  | SkBits2Float(0x430ad7c0), | 
|  | SkBits2Float(0x45019462), | 
|  | SkBits2Float(0x42fed580));  // 2063.28f, 138.843f, 2073.27f, 127.417f | 
|  | builder.quadTo(SkBits2Float(0x45023458), | 
|  | SkBits2Float(0x42e7fb80), | 
|  | SkBits2Float(0x45034ec4), | 
|  | SkBits2Float(0x42e7fb80));  // 2083.27f, 115.991f, 2100.92f, 115.991f | 
|  | builder.close(); | 
|  | return builder.detach(); | 
|  | } | 
|  |  | 
|  | // Reproduces the underlying problem from skbug.com/40043963. | 
|  | // The path (part of a glyph) was being drawn stroked, and with a perspective matrix. | 
|  | // The perspective matrix forces a very large resScale when stroking the path. | 
|  | // The resulting filled path is incorrect. Note that stroking with a smaller resScale works fine. | 
|  | DEF_SIMPLE_GM(bug12866, canvas, 128, 64) { | 
|  | SkPaint strokePaint; | 
|  | strokePaint.setAntiAlias(true); | 
|  | strokePaint.setStyle(SkPaint::kStroke_Style); | 
|  | strokePaint.setStrokeWidth(3); | 
|  |  | 
|  | SkPaint fillPaint; | 
|  | fillPaint.setAntiAlias(true); | 
|  |  | 
|  | SkPath strokePath = get_path(); | 
|  | SkPathBuilder builder; | 
|  | skpathutils::FillPathWithPaint(strokePath, strokePaint, &builder, nullptr, | 
|  | SkMatrix::Scale(1200.0f, 1200.0f)); | 
|  | SkPath fillPath = builder.detach(); | 
|  |  | 
|  | SkRect strokeBounds = strokePath.getBounds(); | 
|  | SkRect fillBounds = fillPath.getBounds(); | 
|  |  | 
|  | // Draw the stroked path. This (internally) uses a resScale of 1.0, and looks good. | 
|  | canvas->save(); | 
|  | canvas->translate(10 - strokeBounds.fLeft, 10 - strokeBounds.fTop); | 
|  | canvas->drawPath(strokePath, strokePaint); | 
|  | canvas->restore(); | 
|  |  | 
|  | // With a perspective CTM, it's possible for resScale to become large. Draw the filled | 
|  | // path produced by the stroker in that situation, which ends up being incorrect. | 
|  | canvas->save(); | 
|  | canvas->translate(74 - fillBounds.fLeft, 10 - fillBounds.fTop); | 
|  | canvas->drawPath(fillPath, fillPaint); | 
|  | canvas->restore(); | 
|  | } | 
|  |  | 
|  | // This is another example of the same underlying bug (recursion limit in the stroker), | 
|  | // but with cubics, rather than quads. | 
|  | DEF_SIMPLE_GM(bug40810065, canvas, 256, 512) { | 
|  | canvas->scale(2.f, 2.f); | 
|  |  | 
|  | SkPathBuilder path1; | 
|  | path1.moveTo(108.87f, 3.78f); | 
|  | path1.cubicTo(201.1f, -128.61f, 34.21f, 82.54f, 134.14f, 126.01f); | 
|  | SkPathBuilder path2; | 
|  | path2.moveTo(108.87f, 3.78f); | 
|  | path2.cubicTo(201.f, -128.61f, 34.21f, 82.54f, 134.14f, 126.f); | 
|  |  | 
|  | SkPaint stroke; | 
|  | stroke.setColor(SK_ColorBLACK); | 
|  | stroke.setAntiAlias(true); | 
|  | stroke.setStyle(SkPaint::kStroke_Style); | 
|  | stroke.setStrokeWidth(1.f); | 
|  | stroke.setStrokeCap(SkPaint::kRound_Cap); | 
|  |  | 
|  | canvas->save(); | 
|  | canvas->translate(-75.f, 50.f); | 
|  | canvas->drawPath(path1.detach(), stroke); | 
|  | canvas->restore(); | 
|  |  | 
|  | canvas->save(); | 
|  | canvas->translate(-20.f, 100.f); | 
|  | canvas->drawPath(path2.detach(), stroke); | 
|  | canvas->restore(); | 
|  | } | 
|  |  | 
|  | // Finally: A repro case that involves conics. This should draw NOTHING. When incorrect, it drew | 
|  | // a large black rectangle over half of the slide. | 
|  | DEF_SIMPLE_GM_BG(bug41422450, canvas, 863, 473, SK_ColorWHITE) { | 
|  | SkM44 mat{1, -0.00000139566271f, 0, -2321738, | 
|  | 0.000113059919f, 0.0123444516f, 0, -353, | 
|  | 0, 0, 1, 0, | 
|  | 0, 0, 0, 1}; | 
|  | canvas->concat(mat); | 
|  |  | 
|  | SkRect circle = SkRect::MakeLTRB(-3299135.5f, -12312541.0f, 9897407.0f, 884000.812f); | 
|  |  | 
|  | SkPath strokePath = SkPathBuilder().arcTo(circle, 59.9999962f, 59.9999962f, true).detach(); | 
|  |  | 
|  | SkPaint strokePaint; | 
|  | strokePaint.setStyle(SkPaint::kStroke_Style); | 
|  | strokePaint.setStrokeWidth(2); | 
|  | canvas->drawPath(strokePath, strokePaint); | 
|  | } |