GrTextBlob.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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. #include "include/core/SkColorFilter.h"
  8. #include "include/gpu/GrContext.h"
  9. #include "src/core/SkMaskFilterBase.h"
  10. #include "src/core/SkPaintPriv.h"
  11. #include "src/gpu/GrBlurUtils.h"
  12. #include "src/gpu/GrClip.h"
  13. #include "src/gpu/GrStyle.h"
  14. #include "src/gpu/geometry/GrShape.h"
  15. #include "src/gpu/ops/GrAtlasTextOp.h"
  16. #include "src/gpu/text/GrTextBlob.h"
  17. #include "src/gpu/text/GrTextTarget.h"
  18. #include <new>
  19. template <size_t N> static size_t sk_align(size_t s) {
  20. return ((s + (N-1)) / N) * N;
  21. }
  22. sk_sp<GrTextBlob> GrTextBlob::Make(int glyphCount,
  23. int runCount,
  24. GrColor color,
  25. GrStrikeCache* strikeCache) {
  26. // We allocate size for the GrTextBlob itself, plus size for the vertices array,
  27. // and size for the glyphIds array.
  28. size_t verticesCount = glyphCount * kVerticesPerGlyph * kMaxVASize;
  29. size_t blobStart = 0;
  30. size_t vertex = sk_align<alignof(char)> (blobStart + sizeof(GrTextBlob) * 1);
  31. size_t glyphs = sk_align<alignof(GrGlyph*)> (vertex + sizeof(char) * verticesCount);
  32. size_t runs = sk_align<alignof(GrTextBlob::Run)>(glyphs + sizeof(GrGlyph*) * glyphCount);
  33. size_t size = (runs + sizeof(GrTextBlob::Run) * runCount);
  34. void* allocation = ::operator new (size);
  35. if (CACHE_SANITY_CHECK) {
  36. sk_bzero(allocation, size);
  37. }
  38. sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{strikeCache}};
  39. blob->fSize = size;
  40. // setup offsets for vertices / glyphs
  41. blob->fVertices = SkTAddOffset<char>(blob.get(), vertex);
  42. blob->fGlyphs = SkTAddOffset<GrGlyph*>(blob.get(), glyphs);
  43. blob->fRuns = SkTAddOffset<GrTextBlob::Run>(blob.get(), runs);
  44. // Initialize runs
  45. for (int i = 0; i < runCount; i++) {
  46. new (&blob->fRuns[i]) GrTextBlob::Run{blob.get(), color};
  47. }
  48. blob->fRunCountLimit = runCount;
  49. return blob;
  50. }
  51. void GrTextBlob::Run::setupFont(const SkStrikeSpec& strikeSpec) {
  52. if (fFallbackStrikeSpec != nullptr) {
  53. *fFallbackStrikeSpec = strikeSpec;
  54. } else {
  55. fStrikeSpec = strikeSpec;
  56. }
  57. }
  58. void GrTextBlob::Run::appendPathGlyph(const SkPath& path, SkPoint position,
  59. SkScalar scale, bool preTransformed) {
  60. fPathGlyphs.push_back(PathGlyph(path, position.x(), position.y(), scale, preTransformed));
  61. }
  62. bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
  63. const SkMaskFilterBase::BlurRec& blurRec,
  64. const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
  65. // If we have LCD text then our canonical color will be set to transparent, in this case we have
  66. // to regenerate the blob on any color change
  67. // We use the grPaint to get any color filter effects
  68. if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
  69. fLuminanceColor != SkPaintPriv::ComputeLuminanceColor(paint)) {
  70. return true;
  71. }
  72. if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
  73. return true;
  74. }
  75. /** This could be relaxed for blobs with only distance field glyphs. */
  76. if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) {
  77. return true;
  78. }
  79. // We only cache one masked version
  80. if (fKey.fHasBlur &&
  81. (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) {
  82. return true;
  83. }
  84. // Similarly, we only cache one version for each style
  85. if (fKey.fStyle != SkPaint::kFill_Style &&
  86. (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
  87. fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
  88. fStrokeInfo.fJoin != paint.getStrokeJoin())) {
  89. return true;
  90. }
  91. // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
  92. // for mixed blobs if this becomes an issue.
  93. if (this->hasBitmap() && this->hasDistanceField()) {
  94. // Identical viewmatrices and we can reuse in all cases
  95. if (fInitialViewMatrix.cheapEqualTo(viewMatrix) && x == fInitialX && y == fInitialY) {
  96. return false;
  97. }
  98. return true;
  99. }
  100. if (this->hasBitmap()) {
  101. if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
  102. fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
  103. fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
  104. fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
  105. return true;
  106. }
  107. // If the text blob only has full pixel glyphs, then fractional part of the position does
  108. // not affect the SkGlyphs used.
  109. if (anyRunHasSubpixelPosition) {
  110. // We can update the positions in the text blob without regenerating the whole
  111. // blob, but only for integer translations.
  112. // This cool bit of math will determine the necessary translation to apply to the
  113. // already generated vertex coordinates to move them to the correct position.
  114. SkScalar transX = viewMatrix.getTranslateX() +
  115. viewMatrix.getScaleX() * (x - fInitialX) +
  116. viewMatrix.getSkewX() * (y - fInitialY) -
  117. fInitialViewMatrix.getTranslateX();
  118. SkScalar transY = viewMatrix.getTranslateY() +
  119. viewMatrix.getSkewY() * (x - fInitialX) +
  120. viewMatrix.getScaleY() * (y - fInitialY) -
  121. fInitialViewMatrix.getTranslateY();
  122. if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
  123. return true;
  124. }
  125. }
  126. } else if (this->hasDistanceField()) {
  127. // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
  128. // distance field being generated, so we have to regenerate in those cases
  129. SkScalar newMaxScale = viewMatrix.getMaxScale();
  130. SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale();
  131. SkScalar scaleAdjust = newMaxScale / oldMaxScale;
  132. if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
  133. return true;
  134. }
  135. }
  136. // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
  137. // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
  138. // the blob anyways at flush time, so no need to regenerate explicitly
  139. return false;
  140. }
  141. inline std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
  142. const SubRun& info, int glyphCount, uint16_t run, uint16_t subRun,
  143. const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
  144. const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
  145. const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) {
  146. GrMaskFormat format = info.maskFormat();
  147. GrPaint grPaint;
  148. target->makeGrPaint(info.maskFormat(), paint, viewMatrix, &grPaint);
  149. std::unique_ptr<GrAtlasTextOp> op;
  150. if (info.drawAsDistanceFields()) {
  151. // TODO: Can we be even smarter based on the dest transfer function?
  152. op = GrAtlasTextOp::MakeDistanceField(
  153. target->getContext(), std::move(grPaint), glyphCount, distanceAdjustTable,
  154. target->colorSpaceInfo().isLinearlyBlended(),
  155. SkPaintPriv::ComputeLuminanceColor(paint),
  156. props, info.isAntiAliased(), info.hasUseLCDText());
  157. } else {
  158. op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount,
  159. info.needsTransform());
  160. }
  161. GrAtlasTextOp::Geometry& geometry = op->geometry();
  162. geometry.fViewMatrix = viewMatrix;
  163. geometry.fClipRect = clipRect;
  164. geometry.fBlob = SkRef(this);
  165. geometry.fRun = run;
  166. geometry.fSubRun = subRun;
  167. geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
  168. geometry.fX = x;
  169. geometry.fY = y;
  170. op->init();
  171. return op;
  172. }
  173. static void calculate_translation(bool applyVM,
  174. const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY,
  175. const SkMatrix& currentViewMatrix, SkScalar currentX,
  176. SkScalar currentY, SkScalar* transX, SkScalar* transY) {
  177. if (applyVM) {
  178. *transX = newViewMatrix.getTranslateX() +
  179. newViewMatrix.getScaleX() * (newX - currentX) +
  180. newViewMatrix.getSkewX() * (newY - currentY) -
  181. currentViewMatrix.getTranslateX();
  182. *transY = newViewMatrix.getTranslateY() +
  183. newViewMatrix.getSkewY() * (newX - currentX) +
  184. newViewMatrix.getScaleY() * (newY - currentY) -
  185. currentViewMatrix.getTranslateY();
  186. } else {
  187. *transX = newX - currentX;
  188. *transY = newY - currentY;
  189. }
  190. }
  191. void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
  192. const GrDistanceFieldAdjustTable* distanceAdjustTable,
  193. const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
  194. const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
  195. // GrTextBlob::makeOp only takes uint16_t values for run and subRun indices.
  196. // Encountering something larger than this is highly unlikely, so we'll just not draw it.
  197. int lastRun = SkTMin(fRunCountLimit, (1 << 16)) - 1;
  198. // For each run in the GrTextBlob we're going to churn through all the glyphs.
  199. // Each run is broken into a path part and a Mask / DFT / ARGB part.
  200. for (int runIndex = 0; runIndex <= lastRun; runIndex++) {
  201. Run& run = fRuns[runIndex];
  202. // first flush any path glyphs
  203. if (run.fPathGlyphs.count()) {
  204. SkPaint runPaint{paint};
  205. runPaint.setAntiAlias(run.fAntiAlias);
  206. for (int i = 0; i < run.fPathGlyphs.count(); i++) {
  207. GrTextBlob::Run::PathGlyph& pathGlyph = run.fPathGlyphs[i];
  208. SkMatrix ctm;
  209. const SkPath* path = &pathGlyph.fPath;
  210. // TmpPath must be in the same scope as GrShape shape below.
  211. SkTLazy<SkPath> tmpPath;
  212. // The glyph positions and glyph outlines are either in device space or in source
  213. // space based on fPreTransformed.
  214. if (!pathGlyph.fPreTransformed) {
  215. // Positions and outlines are in source space.
  216. ctm = viewMatrix;
  217. SkMatrix pathMatrix = SkMatrix::MakeScale(pathGlyph.fScale, pathGlyph.fScale);
  218. // The origin for the blob may have changed, so figure out the delta.
  219. SkVector originShift = SkPoint{x, y} - SkPoint{fInitialX, fInitialY};
  220. // Shift the original glyph location in source space to the position of the new
  221. // blob.
  222. pathMatrix.postTranslate(originShift.x() + pathGlyph.fX,
  223. originShift.y() + pathGlyph.fY);
  224. // If there are shaders, blurs or styles, the path must be scaled into source
  225. // space independently of the CTM. This allows the CTM to be correct for the
  226. // different effects.
  227. GrStyle style(runPaint);
  228. bool scalePath = runPaint.getShader()
  229. || style.applies()
  230. || runPaint.getMaskFilter();
  231. if (!scalePath) {
  232. // Scale can be applied to CTM -- no effects.
  233. ctm.preConcat(pathMatrix);
  234. } else {
  235. // Scale the outline into source space.
  236. // Transform the path form the normalized outline to source space. This
  237. // way the CTM will remain the same so it can be used by the effects.
  238. SkPath* sourceOutline = tmpPath.init();
  239. path->transform(pathMatrix, sourceOutline);
  240. sourceOutline->setIsVolatile(true);
  241. path = sourceOutline;
  242. }
  243. } else {
  244. // Positions and outlines are in device space.
  245. SkPoint originalOrigin = {fInitialX, fInitialY};
  246. fInitialViewMatrix.mapPoints(&originalOrigin, 1);
  247. SkPoint newOrigin = {x, y};
  248. viewMatrix.mapPoints(&newOrigin, 1);
  249. // The origin shift in device space.
  250. SkPoint originShift = newOrigin - originalOrigin;
  251. // Shift the original glyph location in device space to the position of the
  252. // new blob.
  253. ctm = SkMatrix::MakeTrans(originShift.x() + pathGlyph.fX,
  254. originShift.y() + pathGlyph.fY);
  255. }
  256. // TODO: we are losing the mutability of the path here
  257. GrShape shape(*path, paint);
  258. target->drawShape(clip, runPaint, ctm, shape);
  259. }
  260. }
  261. // then flush each subrun, if any
  262. if (!run.fInitialized) {
  263. continue;
  264. }
  265. int lastSubRun = SkTMin(run.fSubRunInfo.count(), 1 << 16) - 1;
  266. for (int subRun = 0; subRun <= lastSubRun; subRun++) {
  267. const SubRun& info = run.fSubRunInfo[subRun];
  268. int glyphCount = info.glyphCount();
  269. if (0 == glyphCount) {
  270. continue;
  271. }
  272. bool skipClip = false;
  273. bool submitOp = true;
  274. SkIRect clipRect = SkIRect::MakeEmpty();
  275. SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
  276. SkRRect clipRRect;
  277. GrAA aa;
  278. // We can clip geometrically if we're not using SDFs or transformed glyphs,
  279. // and we have an axis-aligned rectangular non-AA clip
  280. if (!info.drawAsDistanceFields() && !info.needsTransform() &&
  281. clip.isRRect(rtBounds, &clipRRect, &aa) &&
  282. clipRRect.isRect() && GrAA::kNo == aa) {
  283. skipClip = true;
  284. // We only need to do clipping work if the subrun isn't contained by the clip
  285. SkRect subRunBounds;
  286. this->computeSubRunBounds(&subRunBounds, runIndex, subRun, viewMatrix, x, y,
  287. false);
  288. if (!clipRRect.getBounds().contains(subRunBounds)) {
  289. // If the subrun is completely outside, don't add an op for it
  290. if (!clipRRect.getBounds().intersects(subRunBounds)) {
  291. submitOp = false;
  292. }
  293. else {
  294. clipRRect.getBounds().round(&clipRect);
  295. }
  296. }
  297. }
  298. if (submitOp) {
  299. auto op = this->makeOp(info, glyphCount, runIndex, subRun, viewMatrix, x, y,
  300. clipRect, paint, filteredColor, props, distanceAdjustTable,
  301. target);
  302. if (op) {
  303. if (skipClip) {
  304. target->addDrawOp(GrNoClip(), std::move(op));
  305. }
  306. else {
  307. target->addDrawOp(clip, std::move(op));
  308. }
  309. }
  310. }
  311. }
  312. }
  313. }
  314. std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
  315. int glyphCount, uint16_t run, uint16_t subRun, const SkMatrix& viewMatrix,
  316. SkScalar x, SkScalar y, const SkPaint& paint, const SkPMColor4f& filteredColor,
  317. const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
  318. GrTextTarget* target) {
  319. const GrTextBlob::SubRun& info = fRuns[run].fSubRunInfo[subRun];
  320. SkIRect emptyRect = SkIRect::MakeEmpty();
  321. return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, emptyRect,
  322. paint, filteredColor, props, distanceAdjustTable, target);
  323. }
  324. void GrTextBlob::AssertEqual(const GrTextBlob& l, const GrTextBlob& r) {
  325. SkASSERT_RELEASE(l.fSize == r.fSize);
  326. SkASSERT_RELEASE(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
  327. SkASSERT_RELEASE(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
  328. SkASSERT_RELEASE(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
  329. SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
  330. SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
  331. SkASSERT_RELEASE(l.fKey == r.fKey);
  332. //SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical
  333. SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale);
  334. SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale);
  335. SkASSERT_RELEASE(l.fTextType == r.fTextType);
  336. SkASSERT_RELEASE(l.fRunCountLimit == r.fRunCountLimit);
  337. for (int i = 0; i < l.fRunCountLimit; i++) {
  338. const Run& lRun = l.fRuns[i];
  339. const Run& rRun = r.fRuns[i];
  340. SkASSERT_RELEASE(lRun.fStrikeSpec.descriptor() == rRun.fStrikeSpec.descriptor());
  341. // color can be changed
  342. //SkASSERT(lRun.fColor == rRun.fColor);
  343. SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized);
  344. SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
  345. for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
  346. const SubRun& lSubRun = lRun.fSubRunInfo[j];
  347. const SubRun& rSubRun = rRun.fSubRunInfo[j];
  348. // TODO we can do this check, but we have to apply the VM to the old vertex bounds
  349. //SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds());
  350. if (lSubRun.strike()) {
  351. SkASSERT_RELEASE(rSubRun.strike());
  352. SkASSERT_RELEASE(GrTextStrike::GetKey(*lSubRun.strike()) ==
  353. GrTextStrike::GetKey(*rSubRun.strike()));
  354. } else {
  355. SkASSERT_RELEASE(!rSubRun.strike());
  356. }
  357. SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex());
  358. SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex());
  359. SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex());
  360. SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex());
  361. SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat());
  362. SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields());
  363. SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText());
  364. }
  365. SkASSERT_RELEASE(lRun.fPathGlyphs.count() == rRun.fPathGlyphs.count());
  366. for (int i = 0; i < lRun.fPathGlyphs.count(); i++) {
  367. const Run::PathGlyph& lPathGlyph = lRun.fPathGlyphs[i];
  368. const Run::PathGlyph& rPathGlyph = rRun.fPathGlyphs[i];
  369. SkASSERT_RELEASE(lPathGlyph.fPath == rPathGlyph.fPath);
  370. // We can't assert that these have the same translations
  371. }
  372. }
  373. }
  374. void GrTextBlob::SubRun::computeTranslation(const SkMatrix& viewMatrix,
  375. SkScalar x, SkScalar y, SkScalar* transX,
  376. SkScalar* transY) {
  377. // Don't use the matrix to translate on distance field for fallback subruns.
  378. calculate_translation(!this->drawAsDistanceFields() && !this->isFallback(), viewMatrix,
  379. x, y, fCurrentViewMatrix, fX, fY, transX, transY);
  380. fCurrentViewMatrix = viewMatrix;
  381. fX = x;
  382. fY = y;
  383. }