From 8483326704eff3b6a5fcce3f36d857a9779a4dd2 Mon Sep 17 00:00:00 2001 From: fmalita Date: Fri, 19 Sep 2014 11:40:51 -0700 Subject: [PATCH] SkTextBlob shader space workaround. Blink would like drawTextBlob(x,y) to behave the same as drawText(x,y) WRT shader space. Due to the current generic device base impl, that is not the case. This is a transitional workaround, pending proper drawTextBlob impls in SkDevice classes. R=reed@google.com, robertphillips@google.com Author: fmalita@chromium.org Review URL: https://codereview.chromium.org/586743002 --- gm/textblobshader.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++ gyp/gmslides.gypi | 1 + src/core/SkDevice.cpp | 16 +++++- 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 gm/textblobshader.cpp diff --git a/gm/textblobshader.cpp b/gm/textblobshader.cpp new file mode 100644 index 0000000000..7ec75b504d --- /dev/null +++ b/gm/textblobshader.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" + +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPoint.h" +#include "SkShader.h" +#include "SkTextBlob.h" +#include "SkTDArray.h" +#include "SkTypeface.h" + +// This GM exercises drawTextBlob offset vs. shader space behavior. +class TextBlobShaderGM : public skiagm::GM { +public: + TextBlobShaderGM(const char* txt) { + SkPaint p; + size_t txtLen = strlen(txt); + fGlyphs.append(p.textToGlyphs(txt, txtLen, NULL)); + p.textToGlyphs(txt, txtLen, fGlyphs.begin()); + } + +protected: + + virtual void onOnceBeforeDraw() SK_OVERRIDE { + SkPaint p; + p.setAntiAlias(true); + p.setSubpixelText(true); + p.setTextSize(30); + p.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkTextBlobBuilder builder; + int glyphCount = fGlyphs.count(); + const SkTextBlobBuilder::RunBuffer* run; + + run = &builder.allocRun(p, glyphCount, 10, 10, NULL); + memcpy(run->glyphs, fGlyphs.begin(), glyphCount * sizeof(uint16_t)); + + run = &builder.allocRunPosH(p, glyphCount, 80, NULL); + memcpy(run->glyphs, fGlyphs.begin(), glyphCount * sizeof(uint16_t)); + for (int i = 0; i < glyphCount; ++i) { + run->pos[i] = p.getTextSize() * i * .75f; + } + + run = &builder.allocRunPos(p, glyphCount, NULL); + memcpy(run->glyphs, fGlyphs.begin(), glyphCount * sizeof(uint16_t)); + for (int i = 0; i < glyphCount; ++i) { + run->pos[i * 2] = p.getTextSize() * i * .75f; + run->pos[i * 2 + 1] = 150 + 5 * sinf((float)i * 8 / glyphCount); + } + + fBlob.reset(builder.build()); + + SkColor colors[2]; + colors[0] = SK_ColorRED; + colors[1] = SK_ColorGREEN; + + SkScalar pos[SK_ARRAY_COUNT(colors)]; + for (unsigned i = 0; i < SK_ARRAY_COUNT(pos); ++i) { + pos[i] = (float)i / (SK_ARRAY_COUNT(pos) - 1); + } + + SkISize sz = this->onISize(); + fShader.reset(SkGradientShader::CreateRadial(SkPoint::Make(SkIntToScalar(sz.width() / 2), + SkIntToScalar(sz.height() / 2)), + sz.width() * .66f, colors, pos, + SK_ARRAY_COUNT(colors), + SkShader::kRepeat_TileMode)); + } + + virtual uint32_t onGetFlags() const SK_OVERRIDE { + return kSkip565_Flag; + } + + virtual SkString onShortName() SK_OVERRIDE { + return SkString("textblobshader"); + } + + virtual SkISize onISize() SK_OVERRIDE { + return SkISize::Make(640, 480); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setShader(fShader); + + SkISize sz = this->onISize(); + static const int kXCount = 4; + static const int kYCount = 3; + for (int i = 0; i < kXCount; ++i) { + for (int j = 0; j < kYCount; ++j) { + canvas->drawTextBlob(fBlob, + SkIntToScalar(i * sz.width() / kXCount), + SkIntToScalar(j * sz.height() / kYCount), + p); + } + } + } + +private: + SkTDArray fGlyphs; + SkAutoTUnref fBlob; + SkAutoTUnref fShader; + + typedef skiagm::GM INHERITED; +}; + +DEF_GM( return SkNEW_ARGS(TextBlobShaderGM, ("Blobber")); ) diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 5b1adeb3a1..fe9c5d959a 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -177,6 +177,7 @@ '../gm/texdata.cpp', '../gm/variedtext.cpp', '../gm/textblob.cpp', + '../gm/textblobshader.cpp', '../gm/texturedomaineffect.cpp', '../gm/thinrects.cpp', '../gm/thinstrokedrects.cpp', diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 9b37da1fbd..15d0ab6d2c 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -10,6 +10,7 @@ #include "SkDraw.h" #include "SkMetaData.h" #include "SkPatchUtils.h" +#include "SkShader.h" #include "SkTextBlob.h" SkBaseDevice::SkBaseDevice() @@ -90,6 +91,7 @@ void SkBaseDevice::drawPatch(const SkDraw& draw, const SkPoint cubics[12], const void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint &paint) { + SkPaint runPaint = paint; SkMatrix localMatrix; SkDraw localDraw(draw); @@ -97,9 +99,21 @@ void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkSc localMatrix = *draw.fMatrix; localMatrix.preTranslate(x, y); localDraw.fMatrix = &localMatrix; + + if (paint.getShader()) { + // FIXME: We need to compensate for the translate above. This is suboptimal but + // temporary -- until we get proper derived class drawTextBlob implementations. + + // TODO: pass x,y down to the other methods so they can handle the additional + // translate without needing to allocate a new shader. + SkMatrix shaderMatrix; + shaderMatrix.setTranslate(-x, -y); + SkAutoTUnref wrapper( + SkShader::CreateLocalMatrixShader(paint.getShader(), shaderMatrix)); + runPaint.setShader(wrapper); + } } - SkPaint runPaint = paint; SkTextBlob::RunIterator it(blob); while (!it.done()) { size_t textLen = it.glyphCount() * sizeof(uint16_t);