18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* 28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright 2014 Google Inc. 3c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * 48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Use of this source code is governed by a BSD-style license that can be 5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * found in the LICENSE file. 6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt */ 78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkRecordOpts.h" 98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkRecordPattern.h" 118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkRecords.h" 128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkTDArray.h" 138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtusing namespace SkRecords; 158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordOptimize(SkRecord* record) { 178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // TODO(mtklein): fuse independent optimizations to reduce number of passes? 188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkRecordNoopCulls(record); 198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkRecordNoopSaveRestores(record); 208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // TODO(mtklein): figure out why we draw differently and reenable 218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt //SkRecordNoopSaveLayerDrawRestores(record); 228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 23b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt SkRecordAnnotateCullingPairs(record); 248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before BoundDrawPosTextH. 258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkRecordBoundDrawPosTextH(record); 268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Most of the optimizations in this file are pattern-based. These are all defined as structs with: 298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// - a Pattern typedef 308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method, 318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// which returns true if it made changes and false if not. 328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 33ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt// Run a pattern-based optimization once across the SkRecord, returning true if it made any changes. 34ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt// It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern, 35ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt// record, and [begin,end) span of the commands that matched. 36ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidttemplate <typename Pass> 37ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidtstatic bool apply(Pass* pass, SkRecord* record) { 38ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt typename Pass::Pattern pattern; 398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool changed = false; 408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt unsigned begin, end = 0; 418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt while (pattern.search(record, &begin, &end)) { 438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt changed |= pass->onMatch(record, &pattern, begin, end); 448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return changed; 468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct CullNooper { 498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt typedef Pattern3<Is<PushCull>, Star<Is<NoOp> >, Is<PopCull> > Pattern; 508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 51d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt record->replace<NoOp>(begin); // PushCull 538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt record->replace<NoOp>(end-1); // PopCull 548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return true; 558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}; 578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordNoopCulls(SkRecord* record) { 59b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt CullNooper pass; 608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt while (apply(&pass, record)); 618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 62b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt 63b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt// Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps. 64b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidtstruct SaveOnlyDrawsRestoreNooper { 65b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt typedef Pattern3<Is<Save>, 66b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt Star<Or<Is<NoOp>, IsDraw> >, 67b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt Is<Restore> > 68b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt Pattern; 69b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt 70b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 71b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt record->replace<NoOp>(begin); // Save 72b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt record->replace<NoOp>(end-1); // Restore 738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return true; 748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 751f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}; 76c2ebb4b85d69b65f552fee71ac68f44e8d87b39eDmitry Shmidt// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops. 771f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtstruct SaveNoDrawsRestoreNooper { 788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Star matches greedily, so we also have to exclude Save and Restore. 798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt typedef Pattern3<Is<Save>, 808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Star<Not<Or3<Is<Save>, 818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Is<Restore>, 828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt IsDraw> > >, 838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Is<Restore> > 848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Pattern; 858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // If restore doesn't revert both matrix and clip, this isn't safe to noop away. 888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (pattern->first<Save>()->flags != SkCanvas::kMatrixClip_SaveFlag) { 898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return false; 908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // The entire span between Save and Restore (inclusively) does nothing. 938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt for (unsigned i = begin; i < end; i++) { 948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt record->replace<NoOp>(i); 958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return true; 978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}; 998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordNoopSaveRestores(SkRecord* record) { 1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SaveOnlyDrawsRestoreNooper onlyDraws; 1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SaveNoDrawsRestoreNooper noDraws; 1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Run until they stop changing things. 1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt while (apply(&onlyDraws, record) || apply(&noDraws, record)); 1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the 1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// draw, and no-op the SaveLayer and Restore. 1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct SaveLayerDrawRestoreNooper { 1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern; 1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SaveLayer* saveLayer = pattern->first<SaveLayer>(); 1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (saveLayer->bounds != NULL) { 1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // SaveLayer with bounds is too tricky for us. 1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return false; 1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkPaint* layerPaint = saveLayer->paint; 1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (NULL == layerPaint) { 1211f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt // There wasn't really any point to this SaveLayer at all. 12204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return KillSaveLayerAndRestore(record, begin); 12304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt } 12404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt 12504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt SkPaint* drawPaint = pattern->second<SkPaint>(); 12604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (drawPaint == NULL) { 12704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt // We can just give the draw the SaveLayer's paint. 12804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt // TODO(mtklein): figure out how to do this clearly 12904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return false; 13004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt } 13104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt 1321f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt const uint32_t layerColor = layerPaint->getColor(); 1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt const uint32_t drawColor = drawPaint->getColor(); 1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!IsOnlyAlpha(layerColor) || !IsOpaque(drawColor) || 1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt HasAnyEffect(*layerPaint) || HasAnyEffect(*drawPaint)) { 1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Too fancy for us. Actually, as long as layerColor is just an alpha 1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // we can blend it into drawColor's alpha; drawColor doesn't strictly have to be opaque. 1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return false; 1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt drawPaint->setColor(SkColorSetA(drawColor, SkColorGetA(layerColor))); 1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return KillSaveLayerAndRestore(record, begin); 1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 144f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt 145f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerIndex) { 146f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt record->replace<NoOp>(saveLayerIndex); // SaveLayer 147f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt record->replace<NoOp>(saveLayerIndex+2); // Restore 148f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt return true; 149f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt } 150f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt 1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt static bool HasAnyEffect(const SkPaint& paint) { 1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return paint.getPathEffect() || 1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt paint.getShader() || 1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt paint.getXfermode() || 1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt paint.getMaskFilter() || 1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt paint.getColorFilter() || 1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt paint.getRasterizer() || 1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt paint.getLooper() || 1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt paint.getImageFilter(); 1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt static bool IsOpaque(SkColor color) { 1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return SkColorGetA(color) == SK_AlphaOPAQUE; 1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt static bool IsOnlyAlpha(SkColor color) { 1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT); 1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}; 1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordNoopSaveLayerDrawRestores(SkRecord* record) { 1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SaveLayerDrawRestoreNooper pass; 1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt apply(&pass, record); 1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal. 1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct StrengthReducer { 1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt typedef Pattern1<Is<DrawPosText> > Pattern; 1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkASSERT(end == begin + 1); 1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt DrawPosText* draw = pattern->first<DrawPosText>(); 1828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt const unsigned points = draw->paint.countText(draw->text, draw->byteLength); 1848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (points == 0) { 1858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return false; // No point (ha!). 1868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt const SkScalar firstY = draw->pos[0].fY; 1898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt for (unsigned i = 1; i < points; i++) { 1908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (draw->pos[i].fY != firstY) { 1918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return false; // Needs full power of DrawPosText. 1928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // All ys are the same. We can replace DrawPosText with DrawPosTextH. 1958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // draw->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ]. 1978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,y,x,y, ...]. 1988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Then we'll rearrange things so all the xs are in order up front, clobbering the ys. 1998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSafe); 2008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkScalar* scalars = &draw->pos[0].fX; 2018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt for (unsigned i = 0; i < 2*points; i += 2) { 2028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt scalars[i/2] = scalars[i]; 2038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Extend lifetime of draw to the end of the loop so we can copy its paint. 2068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Adopted<DrawPosText> adopted(draw); 2078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkNEW_PLACEMENT_ARGS(record->replace<DrawPosTextH>(begin, adopted), 2088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt DrawPosTextH, 209f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt (draw->paint, draw->text, draw->byteLength, scalars, firstY)); 2108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return true; 2118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}; 2138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordReduceDrawPosTextStrength(SkRecord* record) { 214f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt StrengthReducer pass; 2158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt apply(&pass, record); 2168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 2178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservative upper and lower 2198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// bounds to use with SkCanvas::quickRejectY. 2208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct TextBounder { 2218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt typedef Pattern1<Is<DrawPosTextH> > Pattern; 2228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 2248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkASSERT(end == begin + 1); 2258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt DrawPosTextH* draw = pattern->first<DrawPosTextH>(); 2268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // If we're drawing vertical text, none of the checks we're about to do make any sense. 2288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // We'll need to call SkPaint::computeFastBounds() later, so bail if that's not possible. 2298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (draw->paint.isVerticalText() || !draw->paint.canComputeFastBounds()) { 2308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return false; 2318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Rather than checking the top and bottom font metrics, we guess. Actually looking up the 2341f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt // top and bottom metrics is slow, and this overapproximation should be good enough. 2358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt const SkScalar buffer = draw->paint.getTextSize() * 1.5f; 2368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkDEBUGCODE(SkPaint::FontMetrics metrics;) 2378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkDEBUGCODE(draw->paint.getFontMetrics(&metrics);) 2388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkASSERT(-buffer <= metrics.fTop); 2398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkASSERT(+buffer >= metrics.fBottom); 2408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Let the paint adjust the text bounds. We don't care about left and right here, so we use 2428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // 0 and 1 respectively just so the bounds rectangle isn't empty. 2438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkRect bounds; 2448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bounds.set(0, draw->y - buffer, SK_Scalar1, draw->y + buffer); 2458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkRect adjusted = draw->paint.computeFastBounds(bounds, &bounds); 2468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Adopted<DrawPosTextH> adopted(draw); 2488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkNEW_PLACEMENT_ARGS(record->replace<BoundedDrawPosTextH>(begin, adopted), 2498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BoundedDrawPosTextH, 2508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt (&adopted, adjusted.fTop, adjusted.fBottom)); 2518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return true; 2528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}; 2548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordBoundDrawPosTextH(SkRecord* record) { 2558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt TextBounder pass; 2568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt apply(&pass, record); 2578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 2588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Replaces PushCull with PairedPushCull, which lets us skip to the paired PopCull when the canvas 2608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// can quickReject the cull rect. 2618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// There's no efficient way (yet?) to express this one as a pattern, so we write a custom pass. 2628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtclass CullAnnotator { 2638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtpublic: 2648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Do nothing to most ops. 2658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt template <typename T> void operator()(T*) {} 2668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt void operator()(PushCull* push) { 2688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Pair pair = { fIndex, push }; 2698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt fPushStack.push(pair); 2708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt void operator()(PopCull* pop) { 2738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Pair push = fPushStack.top(); 2748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt fPushStack.pop(); 2758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkASSERT(fIndex > push.index); 2778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt unsigned skip = fIndex - push.index; 2788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Adopted<PushCull> adopted(push.command); 2808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkNEW_PLACEMENT_ARGS(fRecord->replace<PairedPushCull>(push.index, adopted), 2818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt PairedPushCull, (&adopted, skip)); 2828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt void apply(SkRecord* record) { 2858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt for (fRecord = record, fIndex = 0; fIndex < record->count(); fIndex++) { 2868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt fRecord->mutate<void>(fIndex, *this); 2878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtprivate: 2918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct Pair { 2928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt unsigned index; 2938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt PushCull* command; 2948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt }; 2958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkTDArray<Pair> fPushStack; 2978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt SkRecord* fRecord; 2988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt unsigned fIndex; 2998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}; 3008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordAnnotateCullingPairs(SkRecord* record) { 3018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt CullAnnotator pass; 3028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt pass.apply(record); 3031f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt} 3041f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt