Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2015 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 
     10 #include "SkAutoMalloc.h"
     11 #include "SkCanvas.h"
     12 #include "SkRSXform.h"
     13 #include "SkSurface.h"
     14 #include "sk_tool_utils.h"
     15 
     16 class DrawAtlasGM : public skiagm::GM {
     17     static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) {
     18         SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
     19         auto surface(sk_tool_utils::makeSurface(caller, info));
     20         SkCanvas* canvas = surface->getCanvas();
     21         // draw red everywhere, but we don't expect to see it in the draw, testing the notion
     22         // that drawAtlas draws a subset-region of the atlas.
     23         canvas->clear(SK_ColorRED);
     24 
     25         SkPaint paint;
     26         paint.setBlendMode(SkBlendMode::kClear);
     27         SkRect r(target);
     28         r.inset(-1, -1);
     29         // zero out a place (with a 1-pixel border) to land our drawing.
     30         canvas->drawRect(r, paint);
     31         paint.setBlendMode(SkBlendMode::kSrcOver);
     32         paint.setColor(SK_ColorBLUE);
     33         paint.setAntiAlias(true);
     34         canvas->drawOval(target, paint);
     35         return surface->makeImageSnapshot();
     36     }
     37 
     38 public:
     39     DrawAtlasGM() {}
     40 
     41 protected:
     42 
     43     SkString onShortName() override {
     44         return SkString("draw-atlas");
     45     }
     46 
     47     SkISize onISize() override {
     48         return SkISize::Make(640, 480);
     49     }
     50 
     51     void onDraw(SkCanvas* canvas) override {
     52         const SkRect target = { 50, 50, 80, 90 };
     53         auto atlas = MakeAtlas(canvas, target);
     54 
     55         const struct {
     56             SkScalar fScale;
     57             SkScalar fDegrees;
     58             SkScalar fTx;
     59             SkScalar fTy;
     60 
     61             void apply(SkRSXform* xform) const {
     62                 const SkScalar rad = SkDegreesToRadians(fDegrees);
     63                 xform->fSCos = fScale * SkScalarCos(rad);
     64                 xform->fSSin = fScale * SkScalarSin(rad);
     65                 xform->fTx   = fTx;
     66                 xform->fTy   = fTy;
     67             }
     68         } rec[] = {
     69             { 1, 0, 10, 10 },       // just translate
     70             { 2, 0, 110, 10 },      // scale + translate
     71             { 1, 30, 210, 10 },     // rotate + translate
     72             { 2, -30, 310, 30 },    // scale + rotate + translate
     73         };
     74 
     75         const int N = SK_ARRAY_COUNT(rec);
     76         SkRSXform xform[N];
     77         SkRect tex[N];
     78         SkColor colors[N];
     79 
     80         for (int i = 0; i < N; ++i) {
     81             rec[i].apply(&xform[i]);
     82             tex[i] = target;
     83             colors[i] = 0x80FF0000 + (i * 40 * 256);
     84         }
     85 
     86         SkPaint paint;
     87         paint.setFilterQuality(kLow_SkFilterQuality);
     88         paint.setAntiAlias(true);
     89 
     90         canvas->drawAtlas(atlas.get(), xform, tex, N, nullptr, &paint);
     91         canvas->translate(0, 100);
     92         canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn, nullptr, &paint);
     93     }
     94 
     95 private:
     96     typedef GM INHERITED;
     97 };
     98 DEF_GM( return new DrawAtlasGM; )
     99 
    100 ///////////////////////////////////////////////////////////////////////////////////////////////////
    101 #include "SkPath.h"
    102 #include "SkPathMeasure.h"
    103 
    104 static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length,
    105                               const SkPoint xy[], const SkPath& path, const SkPaint& paint,
    106                               float baseline_offset, bool useRSX) {
    107     SkPathMeasure meas(path, false);
    108 
    109     int count = paint.countText(text, length);
    110     size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar));
    111     SkAutoSMalloc<512> storage(size);
    112     SkRSXform* xform = (SkRSXform*)storage.get();
    113     SkScalar* widths = (SkScalar*)(xform + count);
    114 
    115     // Compute a conservative bounds so we can cull the draw
    116     const SkRect font = paint.getFontBounds();
    117     const SkScalar max = SkTMax(SkTMax(SkScalarAbs(font.fLeft), SkScalarAbs(font.fRight)),
    118                                 SkTMax(SkScalarAbs(font.fTop), SkScalarAbs(font.fBottom)));
    119     const SkRect bounds = path.getBounds().makeOutset(max, max);
    120 
    121     if (useRSX) {
    122         paint.getTextWidths(text, length, widths);
    123 
    124         for (int i = 0; i < count; ++i) {
    125             // we want to position each character on the center of its advance
    126             const SkScalar offset = SkScalarHalf(widths[i]);
    127             SkPoint pos;
    128             SkVector tan;
    129             if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) {
    130                 pos = xy[i];
    131                 tan.set(1, 0);
    132             }
    133             pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset;
    134 
    135             xform[i].fSCos = tan.x();
    136             xform[i].fSSin = tan.y();
    137             xform[i].fTx   = pos.x() - tan.y() * xy[i].y() - tan.x() * offset;
    138             xform[i].fTy   = pos.y() + tan.x() * xy[i].y() - tan.y() * offset;
    139         }
    140 
    141         canvas->drawTextRSXform(text, length, &xform[0], &bounds, paint);
    142     } else {
    143         canvas->drawTextOnPathHV(text, length, path, 0, baseline_offset, paint);
    144     }
    145 
    146     if (true) {
    147         SkPaint p;
    148         p.setStyle(SkPaint::kStroke_Style);
    149         canvas->drawRect(bounds, p);
    150     }
    151 }
    152 
    153 static void drawTextPath(SkCanvas* canvas, bool useRSX, bool doStroke) {
    154     const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ";
    155     const int N = sizeof(text0) - 1;
    156     SkPoint pos[N];
    157 
    158     SkPaint paint;
    159     paint.setAntiAlias(true);
    160     paint.setTextSize(100);
    161     if (doStroke) {
    162         paint.setStyle(SkPaint::kStroke_Style);
    163         paint.setStrokeWidth(2.25f);
    164         paint.setStrokeJoin(SkPaint::kRound_Join);
    165     }
    166 
    167     SkScalar x = 0;
    168     for (int i = 0; i < N; ++i) {
    169         pos[i].set(x, 0);
    170         x += paint.measureText(&text0[i], 1);
    171     }
    172 
    173     SkPath path;
    174     const float baseline_offset = -5;
    175 
    176     const SkPath::Direction dirs[] = {
    177         SkPath::kCW_Direction, SkPath::kCCW_Direction,
    178     };
    179     for (auto d : dirs) {
    180         path.reset();
    181         path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d);
    182         draw_text_on_path(canvas, text0, N, pos, path, paint, baseline_offset, useRSX);
    183     }
    184 
    185     paint.reset();
    186     paint.setStyle(SkPaint::kStroke_Style);
    187     canvas->drawPath(path, paint);
    188 }
    189 
    190 DEF_SIMPLE_GM(drawTextRSXform, canvas, 860, 860) {
    191     canvas->scale(0.5f, 0.5f);
    192     const bool doStroke[] = { false, true };
    193     for (auto st : doStroke) {
    194         canvas->save();
    195         drawTextPath(canvas, false, st);
    196         canvas->translate(860, 0);
    197         drawTextPath(canvas, true, st);
    198         canvas->restore();
    199         canvas->translate(0, 860);
    200     }
    201 }
    202 
    203 #include "Resources.h"
    204 #include "SkColorFilter.h"
    205 #include "SkVertices.h"
    206 
    207 static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
    208                                        SkColor color) {
    209     SkPoint pos[4];
    210     r.toQuad(pos);
    211     SkColor colors[4] = { color, color, color, color };
    212     return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
    213                                 pos, pos, colors);
    214 }
    215 
    216 /*
    217  *  drawAtlas and drawVertices have several things in common:
    218  *  - can create compound "shaders", combining texture and colors
    219  *      - these are combined via an explicit blendmode
    220  *  - like drawImage, they only respect parts of the paint
    221  *      - colorfilter, imagefilter, blendmode, alpha
    222  *
    223  *  This GM produces a series of pairs of images (atlas | vertices).
    224  *  Each pair should look the same, and each set shows a different combination
    225  *  of alpha | colorFilter | mode
    226  */
    227 DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
    228     const SkRect tex = SkRect::MakeWH(128, 128);
    229     const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
    230     const SkColor color = 0x884488CC;
    231 
    232     auto image = GetResourceAsImage("images/mandrill_128.png");
    233     auto verts = make_vertices(image, tex, color);
    234     const sk_sp<SkColorFilter> filters[] = {
    235         nullptr,
    236         SkColorFilter::MakeModeFilter(0xFF00FF88, SkBlendMode::kModulate),
    237     };
    238     const SkBlendMode modes[] = {
    239         SkBlendMode::kSrcOver,
    240         SkBlendMode::kPlus,
    241     };
    242 
    243     canvas->translate(10, 10);
    244     SkPaint paint;
    245     for (SkBlendMode mode : modes) {
    246         for (int alpha : { 0xFF, 0x7F }) {
    247             paint.setAlpha(alpha);
    248             canvas->save();
    249             for (auto cf : filters) {
    250                 paint.setColorFilter(cf);
    251                 canvas->drawAtlas(image, &xform, &tex, &color, 1,
    252                                   mode, &tex, &paint);
    253                 canvas->translate(128, 0);
    254                 paint.setShader(image->makeShader());
    255                 canvas->drawVertices(verts, mode, paint);
    256                 paint.setShader(nullptr);
    257                 canvas->translate(145, 0);
    258             }
    259             canvas->restore();
    260             canvas->translate(0, 145);
    261         }
    262     }
    263 }
    264