SkRecordDraw.cpp revision 937c9c7eb4e06d4d3bc495e129c7b8103a5d6c0f
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 "SkRecordDraw.h" 9#include "SkPatchUtils.h" 10 11void SkRecordDraw(const SkRecord& record, 12 SkCanvas* canvas, 13 const SkBBoxHierarchy* bbh, 14 SkDrawPictureCallback* callback) { 15 SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); 16 17 if (NULL != bbh) { 18 // Draw only ops that affect pixels in the canvas's current clip. 19 // The SkRecord and BBH were recorded in identity space. This canvas 20 // is not necessarily in that same space. getClipBounds() returns us 21 // this canvas' clip bounds transformed back into identity space, which 22 // lets us query the BBH. 23 SkRect query = { 0, 0, 0, 0 }; 24 (void)canvas->getClipBounds(&query); 25 26 SkTDArray<void*> ops; 27 bbh->search(query, &ops); 28 29 SkRecords::Draw draw(canvas); 30 for (int i = 0; i < ops.count(); i++) { 31 if (NULL != callback && callback->abortDrawing()) { 32 return; 33 } 34 record.visit<void>((uintptr_t)ops[i], draw); // See FillBounds below. 35 } 36 } else { 37 // Draw all ops. 38 SkRecords::Draw draw(canvas); 39 for (unsigned i = 0; i < record.count(); i++) { 40 if (NULL != callback && callback->abortDrawing()) { 41 return; 42 } 43 record.visit<void>(i, draw); 44 } 45 } 46} 47 48void SkRecordPartialDraw(const SkRecord& record, 49 SkCanvas* canvas, 50 const SkRect& clearRect, 51 unsigned start, unsigned stop) { 52 SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); 53 54 stop = SkTMin(stop, record.count()); 55 SkRecords::PartialDraw draw(canvas, clearRect); 56 for (unsigned i = start; i < stop; i++) { 57 record.visit<void>(i, draw); 58 } 59} 60 61namespace SkRecords { 62 63// FIXME: SkBitmaps are stateful, so we need to copy them to play back in multiple threads. 64static SkBitmap shallow_copy(const SkBitmap& bitmap) { 65 return bitmap; 66} 67 68// NoOps draw nothing. 69template <> void Draw::draw(const NoOp&) {} 70 71#define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; } 72DRAW(Restore, restore()); 73DRAW(Save, save()); 74DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags)); 75DRAW(PopCull, popCull()); 76DRAW(PushCull, pushCull(r.rect)); 77DRAW(Clear, clear(r.color)); 78DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix))); 79 80DRAW(ClipPath, clipPath(r.path, r.op, r.doAA)); 81DRAW(ClipRRect, clipRRect(r.rrect, r.op, r.doAA)); 82DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA)); 83DRAW(ClipRegion, clipRegion(r.region, r.op)); 84 85DRAW(BeginCommentGroup, beginCommentGroup(r.description)); 86DRAW(AddComment, addComment(r.key, r.value)); 87DRAW(EndCommentGroup, endCommentGroup()); 88 89DRAW(DrawBitmap, drawBitmap(shallow_copy(r.bitmap), r.left, r.top, r.paint)); 90DRAW(DrawBitmapMatrix, drawBitmapMatrix(shallow_copy(r.bitmap), r.matrix, r.paint)); 91DRAW(DrawBitmapNine, drawBitmapNine(shallow_copy(r.bitmap), r.center, r.dst, r.paint)); 92DRAW(DrawBitmapRectToRect, 93 drawBitmapRectToRect(shallow_copy(r.bitmap), r.src, r.dst, r.paint, r.flags)); 94DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); 95DRAW(DrawOval, drawOval(r.oval, r.paint)); 96DRAW(DrawPaint, drawPaint(r.paint)); 97DRAW(DrawPath, drawPath(r.path, r.paint)); 98DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.xmode.get(), r.paint)); 99DRAW(DrawPicture, drawPicture(r.picture, r.matrix, r.paint)); 100DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); 101DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); 102DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); 103DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); 104DRAW(DrawRect, drawRect(r.rect, r.paint)); 105DRAW(DrawSprite, drawSprite(shallow_copy(r.bitmap), r.left, r.top, r.paint)); 106DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); 107DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); 108DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint)); 109DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors, 110 r.xmode.get(), r.indices, r.indexCount, r.paint)); 111#undef DRAW 112 113 114// This is an SkRecord visitor that fills an SkBBoxHierarchy. 115// 116// The interesting part here is how to calculate bounds for ops which don't 117// have intrinsic bounds. What is the bounds of a Save or a Translate? 118// 119// We answer this by thinking about a particular definition of bounds: if I 120// don't execute this op, pixels in this rectangle might draw incorrectly. So 121// the bounds of a Save, a Translate, a Restore, etc. are the union of the 122// bounds of Draw* ops that they might have an effect on. For any given 123// Save/Restore block, the bounds of the Save, the Restore, and any other 124// non-drawing ("control") ops inside are exactly the union of the bounds of 125// the drawing ops inside that block. 126// 127// To implement this, we keep a stack of active Save blocks. As we consume ops 128// inside the Save/Restore block, drawing ops are unioned with the bounds of 129// the block, and control ops are stashed away for later. When we finish the 130// block with a Restore, our bounds are complete, and we go back and fill them 131// in for all the control ops we stashed away. 132class FillBounds : SkNoncopyable { 133public: 134 FillBounds(const SkRecord& record, SkBBoxHierarchy* bbh) : fBounds(record.count()) { 135 // Calculate bounds for all ops. This won't go quite in order, so we'll need 136 // to store the bounds separately then feed them in to the BBH later in order. 137 const Bounds largest = Bounds::MakeLargest(); 138 fCTM = &SkMatrix::I(); 139 fCurrentClipBounds = largest; 140 for (fCurrentOp = 0; fCurrentOp < record.count(); fCurrentOp++) { 141 record.visit<void>(fCurrentOp, *this); 142 } 143 144 // If we have any lingering unpaired Saves, simulate restores to make 145 // sure all ops in those Save blocks have their bounds calculated. 146 while (!fSaveStack.isEmpty()) { 147 this->popSaveBlock(); 148 } 149 150 // Any control ops not part of any Save/Restore block draw everywhere. 151 while (!fControlIndices.isEmpty()) { 152 this->popControl(largest); 153 } 154 155 // Finally feed all stored bounds into the BBH. They'll be returned in this order. 156 SkASSERT(NULL != bbh); 157 for (uintptr_t i = 0; i < record.count(); i++) { 158 if (!fBounds[i].isEmpty()) { 159 bbh->insert((void*)i, fBounds[i], true/*ok to defer*/); 160 } 161 } 162 bbh->flushDeferredInserts(); 163 } 164 165 template <typename T> void operator()(const T& op) { 166 this->updateCTM(op); 167 this->updateClipBounds(op); 168 this->trackBounds(op); 169 } 170 171private: 172 // In this file, SkRect are in local coordinates, Bounds are translated back to identity space. 173 typedef SkRect Bounds; 174 175 struct SaveBounds { 176 int controlOps; // Number of control ops in this Save block, including the Save. 177 Bounds bounds; // Bounds of everything in the block. 178 const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block. 179 }; 180 181 template <typename T> void updateCTM(const T&) { /* most ops don't change the CTM */ } 182 void updateCTM(const Restore& op) { fCTM = &op.matrix; } 183 void updateCTM(const SetMatrix& op) { fCTM = &op.matrix; } 184 185 template <typename T> void updateClipBounds(const T&) { /* most ops don't change the clip */ } 186 // Each of these devBounds fields is the state of the device bounds after the op. 187 // So Restore's devBounds are those bounds saved by its paired Save or SaveLayer. 188 void updateClipBounds(const Restore& op) { fCurrentClipBounds = Bounds::Make(op.devBounds); } 189 void updateClipBounds(const ClipPath& op) { fCurrentClipBounds = Bounds::Make(op.devBounds); } 190 void updateClipBounds(const ClipRRect& op) { fCurrentClipBounds = Bounds::Make(op.devBounds); } 191 void updateClipBounds(const ClipRect& op) { fCurrentClipBounds = Bounds::Make(op.devBounds); } 192 void updateClipBounds(const ClipRegion& op) { fCurrentClipBounds = Bounds::Make(op.devBounds); } 193 void updateClipBounds(const SaveLayer& op) { 194 if (op.bounds) { 195 fCurrentClipBounds.intersect(this->adjustAndMap(*op.bounds, op.paint)); 196 } 197 } 198 199 // The bounds of these ops must be calculated when we hit the Restore 200 // from the bounds of the ops in the same Save block. 201 void trackBounds(const Save&) { this->pushSaveBlock(NULL); } 202 void trackBounds(const SaveLayer& op) { this->pushSaveBlock(op.paint); } 203 void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); } 204 205 void trackBounds(const SetMatrix&) { this->pushControl(); } 206 void trackBounds(const ClipRect&) { this->pushControl(); } 207 void trackBounds(const ClipRRect&) { this->pushControl(); } 208 void trackBounds(const ClipPath&) { this->pushControl(); } 209 void trackBounds(const ClipRegion&) { this->pushControl(); } 210 void trackBounds(const PushCull&) { this->pushControl(); } 211 void trackBounds(const PopCull&) { this->pushControl(); } 212 void trackBounds(const BeginCommentGroup&) { this->pushControl(); } 213 void trackBounds(const AddComment&) { this->pushControl(); } 214 void trackBounds(const EndCommentGroup&) { this->pushControl(); } 215 216 // For all other ops, we can calculate and store the bounds directly now. 217 template <typename T> void trackBounds(const T& op) { 218 fBounds[fCurrentOp] = this->bounds(op); 219 this->updateSaveBounds(fBounds[fCurrentOp]); 220 } 221 222 void pushSaveBlock(const SkPaint* paint) { 223 // Starting a new Save block. Push a new entry to represent that. 224 SaveBounds sb = { 0, Bounds::MakeEmpty(), paint }; 225 fSaveStack.push(sb); 226 this->pushControl(); 227 } 228 229 static bool PaintMayAffectTransparentBlack(const SkPaint* paint) { 230 // FIXME: this is very conservative 231 return paint && (paint->getImageFilter() || paint->getColorFilter()); 232 } 233 234 Bounds popSaveBlock() { 235 // We're done the Save block. Apply the block's bounds to all control ops inside it. 236 SaveBounds sb; 237 fSaveStack.pop(&sb); 238 239 // If the paint affects transparent black, we can't trust any of our calculated bounds. 240 const Bounds& bounds = 241 PaintMayAffectTransparentBlack(sb.paint) ? fCurrentClipBounds : sb.bounds; 242 243 while (sb.controlOps --> 0) { 244 this->popControl(bounds); 245 } 246 247 // This whole Save block may be part another Save block. 248 this->updateSaveBounds(bounds); 249 250 // If called from a real Restore (not a phony one for balance), it'll need the bounds. 251 return bounds; 252 } 253 254 void pushControl() { 255 fControlIndices.push(fCurrentOp); 256 if (!fSaveStack.isEmpty()) { 257 fSaveStack.top().controlOps++; 258 } 259 } 260 261 void popControl(const Bounds& bounds) { 262 fBounds[fControlIndices.top()] = bounds; 263 fControlIndices.pop(); 264 } 265 266 void updateSaveBounds(const Bounds& bounds) { 267 // If we're in a Save block, expand its bounds to cover these bounds too. 268 if (!fSaveStack.isEmpty()) { 269 fSaveStack.top().bounds.join(bounds); 270 } 271 } 272 273 // FIXME: this method could use better bounds 274 Bounds bounds(const DrawText&) const { return fCurrentClipBounds; } 275 276 Bounds bounds(const Clear&) const { return Bounds::MakeLargest(); } // Ignores the clip. 277 Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; } 278 Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOps don't draw. 279 280 Bounds bounds(const DrawSprite& op) const { 281 const SkBitmap& bm = op.bitmap; 282 return Bounds::MakeXYWH(op.left, op.top, bm.width(), bm.height()); // Ignores the matrix. 283 } 284 285 Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); } 286 Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); } 287 Bounds bounds(const DrawRRect& op) const { 288 return this->adjustAndMap(op.rrect.rect(), &op.paint); 289 } 290 Bounds bounds(const DrawDRRect& op) const { 291 return this->adjustAndMap(op.outer.rect(), &op.paint); 292 } 293 294 Bounds bounds(const DrawBitmapRectToRect& op) const { 295 return this->adjustAndMap(op.dst, op.paint); 296 } 297 Bounds bounds(const DrawBitmapNine& op) const { 298 return this->adjustAndMap(op.dst, op.paint); 299 } 300 Bounds bounds(const DrawBitmap& op) const { 301 const SkBitmap& bm = op.bitmap; 302 return this->adjustAndMap(SkRect::MakeXYWH(op.left, op.top, bm.width(), bm.height()), 303 op.paint); 304 } 305 Bounds bounds(const DrawBitmapMatrix& op) const { 306 const SkBitmap& bm = op.bitmap; 307 SkRect dst = SkRect::MakeWH(bm.width(), bm.height()); 308 op.matrix.mapRect(&dst); 309 return this->adjustAndMap(dst, op.paint); 310 } 311 312 Bounds bounds(const DrawPath& op) const { 313 return op.path.isInverseFillType() ? fCurrentClipBounds 314 : this->adjustAndMap(op.path.getBounds(), &op.paint); 315 } 316 Bounds bounds(const DrawPoints& op) const { 317 SkRect dst; 318 dst.set(op.pts, op.count); 319 320 // Pad the bounding box a little to make sure hairline points' bounds aren't empty. 321 SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f); 322 dst.outset(stroke/2, stroke/2); 323 324 return this->adjustAndMap(dst, &op.paint); 325 } 326 Bounds bounds(const DrawPatch& op) const { 327 SkRect dst; 328 dst.set(op.cubics, SkPatchUtils::kNumCtrlPts); 329 return this->adjustAndMap(dst, &op.paint); 330 } 331 Bounds bounds(const DrawVertices& op) const { 332 SkRect dst; 333 dst.set(op.vertices, op.vertexCount); 334 return this->adjustAndMap(dst, &op.paint); 335 } 336 337 Bounds bounds(const DrawPicture& op) const { 338 SkRect dst = op.picture->cullRect(); 339 if (op.matrix) { 340 op.matrix->mapRect(&dst); 341 } 342 return this->adjustAndMap(dst, op.paint); 343 } 344 345 Bounds bounds(const DrawPosText& op) const { 346 const int N = op.paint.countText(op.text, op.byteLength); 347 if (N == 0) { 348 return Bounds::MakeEmpty(); 349 } 350 351 SkRect dst; 352 dst.set(op.pos, N); 353 AdjustTextForFontMetrics(&dst, op.paint); 354 return this->adjustAndMap(dst, &op.paint); 355 } 356 Bounds bounds(const DrawPosTextH& op) const { 357 const int N = op.paint.countText(op.text, op.byteLength); 358 if (N == 0) { 359 return Bounds::MakeEmpty(); 360 } 361 362 SkScalar left = op.xpos[0], right = op.xpos[0]; 363 for (int i = 1; i < N; i++) { 364 left = SkMinScalar(left, op.xpos[i]); 365 right = SkMaxScalar(right, op.xpos[i]); 366 } 367 SkRect dst = { left, op.y, right, op.y }; 368 AdjustTextForFontMetrics(&dst, op.paint); 369 return this->adjustAndMap(dst, &op.paint); 370 } 371 Bounds bounds(const DrawTextOnPath& op) const { 372 SkRect dst = op.path.getBounds(); 373 374 // Pad all sides by the maximum padding in any direction we'd normally apply. 375 SkRect pad = { 0, 0, 0, 0}; 376 AdjustTextForFontMetrics(&pad, op.paint); 377 378 // That maximum padding happens to always be the right pad today. 379 SkASSERT(pad.fLeft == -pad.fRight); 380 SkASSERT(pad.fTop == -pad.fBottom); 381 SkASSERT(pad.fRight > pad.fBottom); 382 dst.outset(pad.fRight, pad.fRight); 383 384 return this->adjustAndMap(dst, &op.paint); 385 } 386 387 Bounds bounds(const DrawTextBlob& op) const { 388 SkRect dst = op.blob->bounds(); 389 dst.offset(op.x, op.y); 390 // TODO: remove when implicit bounds are plumbed through 391 if (dst.isEmpty()) { 392 return fCurrentClipBounds; 393 } 394 return this->adjustAndMap(dst, &op.paint); 395 } 396 397 static void AdjustTextForFontMetrics(SkRect* rect, const SkPaint& paint) { 398#ifdef SK_DEBUG 399 SkRect correct = *rect; 400#endif 401 const SkScalar yPad = 2.0f * paint.getTextSize(), // In practice, this seems to be enough. 402 xPad = 4.0f * yPad; // Hack for very wide Github logo font. 403 rect->outset(xPad, yPad); 404#ifdef SK_DEBUG 405 SkPaint::FontMetrics metrics; 406 paint.getFontMetrics(&metrics); 407 correct.fLeft += metrics.fXMin; 408 correct.fTop += metrics.fTop; 409 correct.fRight += metrics.fXMax; 410 correct.fBottom += metrics.fBottom; 411 // See skia:2862 for why we ignore small text sizes. 412 SkASSERTF(paint.getTextSize() < 0.001f || rect->contains(correct), 413 "%f %f %f %f vs. %f %f %f %f\n", 414 -xPad, -yPad, +xPad, +yPad, 415 metrics.fXMin, metrics.fTop, metrics.fXMax, metrics.fBottom); 416#endif 417 } 418 419 // Returns true if rect was meaningfully adjusted for the effects of paint, 420 // false if the paint could affect the rect in unknown ways. 421 static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) { 422 if (paint) { 423 if (paint->canComputeFastBounds()) { 424 *rect = paint->computeFastBounds(*rect, rect); 425 return true; 426 } 427 return false; 428 } 429 return true; 430 } 431 432 // Adjust rect for all paints that may affect its geometry, then map it to identity space. 433 Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const { 434 // Inverted rectangles really confuse our BBHs. 435 rect.sort(); 436 437 // Adjust the rect for its own paint. 438 if (!AdjustForPaint(paint, &rect)) { 439 // The paint could do anything to our bounds. The only safe answer is the current clip. 440 return fCurrentClipBounds; 441 } 442 443 // Adjust rect for all the paints from the SaveLayers we're inside. 444 for (int i = fSaveStack.count() - 1; i >= 0; i--) { 445 if (!AdjustForPaint(fSaveStack[i].paint, &rect)) { 446 // Same deal as above. 447 return fCurrentClipBounds; 448 } 449 } 450 451 // Map the rect back to identity space. 452 fCTM->mapRect(&rect); 453 454 // Nothing can draw outside the current clip. 455 // (Only bounded ops call into this method, so oddballs like Clear don't matter here.) 456 rect.intersect(fCurrentClipBounds); 457 return rect; 458 } 459 460 // Conservative identity-space bounds for each op in the SkRecord. 461 SkAutoTMalloc<Bounds> fBounds; 462 463 // We walk fCurrentOp through the SkRecord, as we go using updateCTM() 464 // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative 465 // identity-space bounds of the current clip (fCurrentClipBounds). 466 unsigned fCurrentOp; 467 const SkMatrix* fCTM; 468 Bounds fCurrentClipBounds; 469 470 // Used to track the bounds of Save/Restore blocks and the control ops inside them. 471 SkTDArray<SaveBounds> fSaveStack; 472 SkTDArray<unsigned> fControlIndices; 473}; 474 475} // namespace SkRecords 476 477void SkRecordFillBounds(const SkRecord& record, SkBBoxHierarchy* bbh) { 478 SkRecords::FillBounds(record, bbh); 479} 480