1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "gm.h" 9 #include "SkCanvas.h" 10 #include "SkTypeface.h" 11 12 /* This test tries to define the effect of using hairline strokes on text. 13 * Provides non-hairline images for reference and consistency checks. 14 * glyph_pos_(h/n)_(s/f/b) 15 * -> test hairline/non-hairline stroke/fill/stroke+fill. 16 */ 17 static const SkScalar kTextHeight = 14.0f; 18 static const char kText[] = "Proportional Hamburgefons #% fi"; 19 20 namespace skiagm { 21 22 class GlyphPosGM : public GM { 23 public: 24 GlyphPosGM(SkScalar strokeWidth, SkPaint::Style strokeStyle) 25 : fStrokeWidth(strokeWidth) 26 , fStrokeStyle(strokeStyle) { 27 } 28 29 protected: 30 virtual uint32_t onGetFlags() const SK_OVERRIDE { 31 return kSkipTiled_Flag; 32 } 33 34 virtual SkString onShortName() SK_OVERRIDE { 35 SkString str("glyph_pos"); 36 if (fStrokeWidth == 0.0f) { 37 str.append("_h"); // h == Hairline. 38 } else { 39 str.append("_n"); // n == Normal. 40 } 41 if (fStrokeStyle == SkPaint::kStroke_Style) { 42 str.append("_s"); 43 } else if (fStrokeStyle == SkPaint::kFill_Style) { 44 str.append("_f"); 45 } else { 46 str.append("_b"); // b == Both. 47 } 48 return str; 49 } 50 51 virtual SkISize onISize() { return SkISize::Make(800, 600); } 52 53 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 54 if (!fProp) { 55 fProp.reset(sk_tool_utils::create_portable_typeface("Helvetica", SkTypeface::kNormal)); 56 } 57 58 // There's a black pixel at 40, 40 for reference. 59 canvas->drawPoint(40.0f, 40.0f, SK_ColorBLACK); 60 61 // Two reference images. 62 canvas->translate(50.0f, 50.0f); 63 drawTestCase(canvas, 1.0f); 64 65 canvas->translate(0.0f, 50.0f); 66 drawTestCase(canvas, 3.0f); 67 68 // Uniform scaling test. 69 canvas->translate(0.0f, 100.0f); 70 canvas->save(); 71 canvas->scale(3.0f, 3.0f); 72 drawTestCase(canvas, 1.0f); 73 canvas->restore(); 74 75 // Non-uniform scaling test. 76 canvas->translate(0.0f, 100.0f); 77 canvas->save(); 78 canvas->scale(3.0f, 6.0f); 79 drawTestCase(canvas, 1.0f); 80 canvas->restore(); 81 82 // Skew test. 83 canvas->translate(0.0f, 80.0f); 84 canvas->save(); 85 canvas->scale(3.0f, 3.0f); 86 SkMatrix skew; 87 skew.setIdentity(); 88 skew.setSkewX(SkScalarDiv(8.0f, 89 25.0f)); 90 skew.setSkewY(SkScalarDiv(2.0f, 91 25.0f)); 92 canvas->concat(skew); 93 drawTestCase(canvas, 1.0f); 94 canvas->restore(); 95 96 // Perspective test. 97 canvas->translate(0.0f, 80.0f); 98 canvas->save(); 99 SkMatrix perspective; 100 perspective.setIdentity(); 101 perspective.setPerspX(-SkScalarDiv(SK_Scalar1, 340.0f)); 102 perspective.setSkewX(SkScalarDiv(8.0f, 103 25.0f)); 104 perspective.setSkewY(SkScalarDiv(2.0f, 105 25.0f)); 106 107 108 canvas->concat(perspective); 109 drawTestCase(canvas, 1.0f); 110 canvas->restore(); 111 } 112 113 void drawTestCase(SkCanvas* canvas, SkScalar textScale) { 114 SkPaint paint; 115 paint.setColor(SK_ColorBLACK); 116 paint.setAntiAlias(true); 117 paint.setTextSize(kTextHeight * textScale); 118 paint.setTypeface(fProp); 119 paint.setDevKernText(true); 120 paint.setStrokeWidth(fStrokeWidth); 121 paint.setStyle(fStrokeStyle); 122 123 // This demonstrates that we can not measure the text if there's a device transform. The 124 // canvas total matrix will end up being a device transform. 125 bool drawRef = !(canvas->getTotalMatrix().getType() & 126 ~(SkMatrix::kIdentity_Mask | SkMatrix::kTranslate_Mask)); 127 128 SkRect bounds; 129 if (drawRef) { 130 SkScalar advance = paint.measureText(kText, sizeof(kText) - 1, &bounds); 131 132 paint.setStrokeWidth(0.0f); 133 paint.setStyle(SkPaint::kStroke_Style); 134 135 // Green box is the measured text bounds. 136 paint.setColor(SK_ColorGREEN); 137 canvas->drawRect(bounds, paint); 138 139 // Red line is the measured advance from the 0,0 of the text position. 140 paint.setColor(SK_ColorRED); 141 canvas->drawLine(0.0f, 0.0f, advance, 0.0f, paint); 142 } 143 144 // Black text is the testcase, eg. the text. 145 paint.setColor(SK_ColorBLACK); 146 paint.setStrokeWidth(fStrokeWidth); 147 paint.setStyle(fStrokeStyle); 148 canvas->drawText(kText, sizeof(kText) - 1, 0.0f, 0.0f, paint); 149 150 if (drawRef) { 151 SkScalar widths[sizeof(kText) - 1]; 152 paint.getTextWidths(kText, sizeof(kText) - 1, widths, NULL); 153 154 paint.setStrokeWidth(0.0f); 155 paint.setStyle(SkPaint::kStroke_Style); 156 157 // Magenta lines are the positions for the characters. 158 paint.setColor(SK_ColorMAGENTA); 159 SkScalar w = bounds.x(); 160 for (size_t i = 0; i < sizeof(kText) - 1; ++i) { 161 canvas->drawLine(w, 0.0f, w, 5.0f, paint); 162 w += widths[i]; 163 } 164 } 165 } 166 167 private: 168 SkAutoTUnref<SkTypeface> fProp; 169 SkScalar fStrokeWidth; 170 SkPaint::Style fStrokeStyle; 171 172 typedef GM INHERITED; 173 }; 174 175 ////////////////////////////////////////////////////////////////////////////// 176 177 static GM* GlyphPosHairlineStrokeAndFillFactory(void*) { 178 return new GlyphPosGM(0.0f, SkPaint::kStrokeAndFill_Style); 179 } 180 static GM* GlyphPosStrokeAndFillFactory(void*) { 181 return new GlyphPosGM(1.2f, SkPaint::kStrokeAndFill_Style); 182 } 183 static GM* GlyphPosHairlineStrokeFactory(void*) { 184 return new GlyphPosGM(0.0f, SkPaint::kStroke_Style); 185 } 186 static GM* GlyphPosStrokeFactory(void*) { 187 return new GlyphPosGM(1.2f, SkPaint::kStroke_Style); 188 } 189 static GM* GlyphPosHairlineFillFactory(void*) { 190 return new GlyphPosGM(0.0f, SkPaint::kFill_Style); 191 } 192 static GM* GlyphPosFillFactory(void*) { 193 return new GlyphPosGM(1.2f, SkPaint::kFill_Style); 194 } 195 196 static GMRegistry reg1(GlyphPosHairlineStrokeAndFillFactory); 197 static GMRegistry reg2(GlyphPosStrokeAndFillFactory); 198 static GMRegistry reg3(GlyphPosHairlineStrokeFactory); 199 static GMRegistry reg4(GlyphPosStrokeFactory); 200 static GMRegistry reg5(GlyphPosHairlineFillFactory); 201 static GMRegistry reg6(GlyphPosFillFactory); 202 203 204 } 205