1 /* 2 * Copyright 2011 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 "SkData.h" 9 #include "SkDumpCanvas.h" 10 #include "SkImage.h" 11 #include "SkPatchUtils.h" 12 #include "SkPicture.h" 13 #include "SkPixelRef.h" 14 #include "SkRegion.h" 15 #include "SkRRect.h" 16 #include "SkString.h" 17 #include "SkTextBlob.h" 18 #include <stdarg.h> 19 #include <stdio.h> 20 21 // needed just to know that these are all subclassed from SkFlattenable 22 #include "SkShader.h" 23 #include "SkPathEffect.h" 24 #include "SkColorFilter.h" 25 #include "SkPathEffect.h" 26 #include "SkMaskFilter.h" 27 28 static void toString(const SkRect& r, SkString* str) { 29 str->appendf("[%g,%g %g:%g]", 30 SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), 31 SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); 32 } 33 34 static void toString(const SkIRect& r, SkString* str) { 35 str->appendf("[%d,%d %d:%d]", r.fLeft, r.fTop, r.width(), r.height()); 36 } 37 38 static void toString(const SkRRect& rrect, SkString* str) { 39 SkRect r = rrect.getBounds(); 40 str->appendf("[%g,%g %g:%g]", 41 SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), 42 SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); 43 if (rrect.isOval()) { 44 str->append("()"); 45 } else if (rrect.isSimple()) { 46 const SkVector& rad = rrect.getSimpleRadii(); 47 str->appendf("(%g,%g)", rad.x(), rad.y()); 48 } else if (rrect.isComplex()) { 49 SkVector radii[4] = { 50 rrect.radii(SkRRect::kUpperLeft_Corner), 51 rrect.radii(SkRRect::kUpperRight_Corner), 52 rrect.radii(SkRRect::kLowerRight_Corner), 53 rrect.radii(SkRRect::kLowerLeft_Corner), 54 }; 55 str->appendf("(%g,%g %g,%g %g,%g %g,%g)", 56 radii[0].x(), radii[0].y(), 57 radii[1].x(), radii[1].y(), 58 radii[2].x(), radii[2].y(), 59 radii[3].x(), radii[3].y()); 60 } 61 } 62 63 static void dumpVerbs(const SkPath& path, SkString* str) { 64 SkPath::Iter iter(path, false); 65 SkPoint pts[4]; 66 for (;;) { 67 switch (iter.next(pts, false)) { 68 case SkPath::kMove_Verb: 69 str->appendf(" M%g,%g", pts[0].fX, pts[0].fY); 70 break; 71 case SkPath::kLine_Verb: 72 str->appendf(" L%g,%g", pts[0].fX, pts[0].fY); 73 break; 74 case SkPath::kQuad_Verb: 75 str->appendf(" Q%g,%g,%g,%g", pts[1].fX, pts[1].fY, 76 pts[2].fX, pts[2].fY); 77 break; 78 case SkPath::kCubic_Verb: 79 str->appendf(" C%g,%g,%g,%g,%g,%g", pts[1].fX, pts[1].fY, 80 pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY); 81 break; 82 case SkPath::kClose_Verb: 83 str->append("X"); 84 break; 85 case SkPath::kDone_Verb: 86 return; 87 case SkPath::kConic_Verb: 88 SkASSERT(0); 89 break; 90 } 91 } 92 } 93 94 static void toString(const SkPath& path, SkString* str) { 95 if (path.isEmpty()) { 96 str->append("path:empty"); 97 } else { 98 toString(path.getBounds(), str); 99 #if 1 100 SkString s; 101 dumpVerbs(path, &s); 102 str->append(s.c_str()); 103 #endif 104 str->append("]"); 105 str->prepend("path:["); 106 } 107 } 108 109 static const char* toString(SkClipOp op) { 110 static const char* gOpNames[] = { 111 "DIFF", "SECT", "UNION", "XOR", "RDIFF", "REPLACE" 112 }; 113 return gOpNames[static_cast<int>(op)]; 114 } 115 116 static void toString(const SkRegion& rgn, SkString* str) { 117 str->append("Region:["); 118 toString(rgn.getBounds(), str); 119 str->append("]"); 120 if (rgn.isComplex()) { 121 str->append(".complex"); 122 } 123 } 124 125 static const char* toString(SkVertices::VertexMode vm) { 126 static const char* gVMNames[] = { 127 "TRIANGLES", "STRIP", "FAN" 128 }; 129 return gVMNames[vm]; 130 } 131 132 static const char* toString(SkCanvas::PointMode pm) { 133 static const char* gPMNames[] = { 134 "POINTS", "LINES", "POLYGON" 135 }; 136 return gPMNames[pm]; 137 } 138 139 static void toString(const void* text, size_t byteLen, SkPaint::TextEncoding enc, 140 SkString* str) { 141 // FIXME: this code appears to be untested - and probably unused - and probably wrong 142 switch (enc) { 143 case SkPaint::kUTF8_TextEncoding: 144 str->appendf("\"%.*s\"%s", (int)SkTMax<size_t>(byteLen, 32), (const char*) text, 145 byteLen > 32 ? "..." : ""); 146 break; 147 case SkPaint::kUTF16_TextEncoding: 148 str->appendf("\"%.*ls\"%s", (int)SkTMax<size_t>(byteLen, 32), (const wchar_t*) text, 149 byteLen > 64 ? "..." : ""); 150 break; 151 case SkPaint::kUTF32_TextEncoding: 152 str->appendf("\"%.*ls\"%s", (int)SkTMax<size_t>(byteLen, 32), (const wchar_t*) text, 153 byteLen > 128 ? "..." : ""); 154 break; 155 case SkPaint::kGlyphID_TextEncoding: 156 str->append("<glyphs>"); 157 break; 158 159 default: 160 SkASSERT(false); 161 break; 162 } 163 } 164 165 /////////////////////////////////////////////////////////////////////////////// 166 167 #define WIDE_OPEN 16384 168 169 SkDumpCanvas::SkDumpCanvas(Dumper* dumper) : INHERITED(WIDE_OPEN, WIDE_OPEN) { 170 fNestLevel = 0; 171 SkSafeRef(dumper); 172 fDumper = dumper; 173 } 174 175 SkDumpCanvas::~SkDumpCanvas() { 176 SkSafeUnref(fDumper); 177 } 178 179 void SkDumpCanvas::dump(Verb verb, const SkPaint* paint, 180 const char format[], ...) { 181 static const size_t BUFFER_SIZE = 1024; 182 183 char buffer[BUFFER_SIZE]; 184 va_list args; 185 va_start(args, format); 186 vsnprintf(buffer, BUFFER_SIZE, format, args); 187 va_end(args); 188 189 if (fDumper) { 190 fDumper->dump(this, verb, buffer, paint); 191 } 192 } 193 194 /////////////////////////////////////////////////////////////////////////////// 195 196 void SkDumpCanvas::willSave() { 197 this->dump(kSave_Verb, nullptr, "save()"); 198 this->INHERITED::willSave(); 199 } 200 201 SkCanvas::SaveLayerStrategy SkDumpCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { 202 SkString str; 203 str.printf("saveLayer(0x%X)", rec.fSaveLayerFlags); 204 if (rec.fBounds) { 205 str.append(" bounds"); 206 toString(*rec.fBounds, &str); 207 } 208 const SkPaint* paint = rec.fPaint; 209 if (paint) { 210 if (paint->getAlpha() != 0xFF) { 211 str.appendf(" alpha:0x%02X", paint->getAlpha()); 212 } 213 if (!paint->isSrcOver()) { 214 str.appendf(" blendmode:%d", (int)paint->getBlendMode()); 215 } 216 } 217 this->dump(kSave_Verb, paint, str.c_str()); 218 return this->INHERITED::getSaveLayerStrategy(rec); 219 } 220 221 void SkDumpCanvas::willRestore() { 222 this->dump(kRestore_Verb, nullptr, "restore"); 223 this->INHERITED::willRestore(); 224 } 225 226 void SkDumpCanvas::didConcat(const SkMatrix& matrix) { 227 SkString str; 228 229 switch (matrix.getType()) { 230 case SkMatrix::kTranslate_Mask: 231 this->dump(kMatrix_Verb, nullptr, "translate(%g %g)", 232 SkScalarToFloat(matrix.getTranslateX()), 233 SkScalarToFloat(matrix.getTranslateY())); 234 break; 235 case SkMatrix::kScale_Mask: 236 this->dump(kMatrix_Verb, nullptr, "scale(%g %g)", 237 SkScalarToFloat(matrix.getScaleX()), 238 SkScalarToFloat(matrix.getScaleY())); 239 break; 240 default: 241 matrix.toString(&str); 242 this->dump(kMatrix_Verb, nullptr, "concat(%s)", str.c_str()); 243 break; 244 } 245 246 this->INHERITED::didConcat(matrix); 247 } 248 249 void SkDumpCanvas::didSetMatrix(const SkMatrix& matrix) { 250 SkString str; 251 matrix.toString(&str); 252 this->dump(kMatrix_Verb, nullptr, "setMatrix(%s)", str.c_str()); 253 this->INHERITED::didSetMatrix(matrix); 254 } 255 256 /////////////////////////////////////////////////////////////////////////////// 257 258 const char* SkDumpCanvas::EdgeStyleToAAString(ClipEdgeStyle edgeStyle) { 259 return kSoft_ClipEdgeStyle == edgeStyle ? "AA" : "BW"; 260 } 261 262 void SkDumpCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) { 263 SkString str; 264 toString(rect, &str); 265 this->dump(kClip_Verb, nullptr, "clipRect(%s %s %s)", str.c_str(), toString(op), 266 EdgeStyleToAAString(edgeStyle)); 267 this->INHERITED::onClipRect(rect, op, edgeStyle); 268 } 269 270 void SkDumpCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) { 271 SkString str; 272 toString(rrect, &str); 273 this->dump(kClip_Verb, nullptr, "clipRRect(%s %s %s)", str.c_str(), toString(op), 274 EdgeStyleToAAString(edgeStyle)); 275 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 276 } 277 278 void SkDumpCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) { 279 SkString str; 280 toString(path, &str); 281 this->dump(kClip_Verb, nullptr, "clipPath(%s %s %s)", str.c_str(), toString(op), 282 EdgeStyleToAAString(edgeStyle)); 283 this->INHERITED::onClipPath(path, op, edgeStyle); 284 } 285 286 void SkDumpCanvas::onClipRegion(const SkRegion& deviceRgn, SkClipOp op) { 287 SkString str; 288 toString(deviceRgn, &str); 289 this->dump(kClip_Verb, nullptr, "clipRegion(%s %s)", str.c_str(), toString(op)); 290 this->INHERITED::onClipRegion(deviceRgn, op); 291 } 292 293 /////////////////////////////////////////////////////////////////////////////// 294 295 void SkDumpCanvas::onDrawPaint(const SkPaint& paint) { 296 this->dump(kDrawPaint_Verb, &paint, "drawPaint()"); 297 } 298 299 void SkDumpCanvas::onDrawPoints(PointMode mode, size_t count, 300 const SkPoint pts[], const SkPaint& paint) { 301 this->dump(kDrawPoints_Verb, &paint, "drawPoints(%s, %d)", toString(mode), 302 count); 303 } 304 305 void SkDumpCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) { 306 SkString str; 307 toString(rect, &str); 308 this->dump(kDrawOval_Verb, &paint, "drawOval(%s)", str.c_str()); 309 } 310 311 void SkDumpCanvas::onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle, 312 bool useCenter, const SkPaint& paint) { 313 SkString str; 314 toString(rect, &str); 315 this->dump(kDrawArc_Verb, &paint, "drawArc(%s, %g, %g, %d)", str.c_str(), startAngle, 316 sweepAngle, useCenter); 317 } 318 319 void SkDumpCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { 320 SkString str; 321 toString(rect, &str); 322 this->dump(kDrawRect_Verb, &paint, "drawRect(%s)", str.c_str()); 323 } 324 325 void SkDumpCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { 326 SkString str; 327 toString(rrect, &str); 328 this->dump(kDrawDRRect_Verb, &paint, "drawRRect(%s)", str.c_str()); 329 } 330 331 void SkDumpCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, 332 const SkPaint& paint) { 333 SkString str0, str1; 334 toString(outer, &str0); 335 toString(inner, &str0); 336 this->dump(kDrawRRect_Verb, &paint, "drawDRRect(%s,%s)", 337 str0.c_str(), str1.c_str()); 338 } 339 340 void SkDumpCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { 341 SkString str; 342 toString(path, &str); 343 this->dump(kDrawPath_Verb, &paint, "drawPath(%s)", str.c_str()); 344 } 345 346 void SkDumpCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, 347 const SkPaint* paint) { 348 SkString str; 349 bitmap.toString(&str); 350 this->dump(kDrawBitmap_Verb, paint, "drawBitmap(%s %g %g)", str.c_str(), 351 SkScalarToFloat(x), SkScalarToFloat(y)); 352 } 353 354 void SkDumpCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, 355 const SkPaint* paint, SrcRectConstraint) { 356 SkString bs, rs; 357 bitmap.toString(&bs); 358 toString(dst, &rs); 359 // show the src-rect only if its not everything 360 if (src && (src->fLeft > 0 || src->fTop > 0 || 361 src->fRight < SkIntToScalar(bitmap.width()) || 362 src->fBottom < SkIntToScalar(bitmap.height()))) { 363 SkString ss; 364 toString(*src, &ss); 365 rs.prependf("%s ", ss.c_str()); 366 } 367 368 this->dump(kDrawBitmap_Verb, paint, "drawBitmapRect(%s %s)", bs.c_str(), rs.c_str()); 369 } 370 371 void SkDumpCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, 372 const SkRect& dst, const SkPaint* paint) { 373 SkString str, centerStr, dstStr; 374 bitmap.toString(&str); 375 toString(center, ¢erStr); 376 toString(dst, &dstStr); 377 this->dump(kDrawBitmap_Verb, paint, "drawBitmapNine(%s %s %s)", str.c_str(), 378 centerStr.c_str(), dstStr.c_str()); 379 } 380 381 void SkDumpCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) { 382 SkString str; 383 image->toString(&str); 384 this->dump(kDrawBitmap_Verb, paint, "drawImage(%s %g %g)", str.c_str(), 385 SkScalarToFloat(x), SkScalarToFloat(y)); 386 } 387 388 void SkDumpCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, 389 const SkPaint* paint, SrcRectConstraint) { 390 SkString bs, rs; 391 image->toString(&bs); 392 toString(dst, &rs); 393 // show the src-rect only if its not everything 394 if (src && (src->fLeft > 0 || src->fTop > 0 || 395 src->fRight < SkIntToScalar(image->width()) || 396 src->fBottom < SkIntToScalar(image->height()))) { 397 SkString ss; 398 toString(*src, &ss); 399 rs.prependf("%s ", ss.c_str()); 400 } 401 402 this->dump(kDrawBitmap_Verb, paint, "drawImageRectToRect(%s %s)", 403 bs.c_str(), rs.c_str()); 404 } 405 406 void SkDumpCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, 407 const SkPaint& paint) { 408 SkString str; 409 toString(text, byteLength, paint.getTextEncoding(), &str); 410 this->dump(kDrawText_Verb, &paint, "drawText(%s [%d] %g %g)", str.c_str(), 411 byteLength, SkScalarToFloat(x), SkScalarToFloat(y)); 412 } 413 414 void SkDumpCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], 415 const SkPaint& paint) { 416 SkString str; 417 toString(text, byteLength, paint.getTextEncoding(), &str); 418 this->dump(kDrawText_Verb, &paint, "drawPosText(%s [%d] %g %g ...)", 419 str.c_str(), byteLength, SkScalarToFloat(pos[0].fX), 420 SkScalarToFloat(pos[0].fY)); 421 } 422 423 void SkDumpCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], 424 SkScalar constY, const SkPaint& paint) { 425 SkString str; 426 toString(text, byteLength, paint.getTextEncoding(), &str); 427 this->dump(kDrawText_Verb, &paint, "drawPosTextH(%s [%d] %g %g ...)", 428 str.c_str(), byteLength, SkScalarToFloat(xpos[0]), 429 SkScalarToFloat(constY)); 430 } 431 432 void SkDumpCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, 433 const SkMatrix* matrix, const SkPaint& paint) { 434 SkString str; 435 toString(text, byteLength, paint.getTextEncoding(), &str); 436 this->dump(kDrawText_Verb, &paint, "drawTextOnPath(%s [%d])", 437 str.c_str(), byteLength); 438 } 439 440 void SkDumpCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[], 441 const SkRect* cull, const SkPaint& paint) { 442 SkString str; 443 toString(text, byteLength, paint.getTextEncoding(), &str); 444 this->dump(kDrawText_Verb, &paint, "drawTextRSXform(%s [%d])", 445 str.c_str(), byteLength); 446 } 447 448 void SkDumpCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, 449 const SkPaint& paint) { 450 SkString str; 451 toString(blob->bounds(), &str); 452 this->dump(kDrawText_Verb, &paint, "drawTextBlob(%p) [%s]", blob, str.c_str()); 453 // FIXME: dump the actual blob content? 454 } 455 456 void SkDumpCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, 457 const SkPaint* paint) { 458 this->dump(kDrawPicture_Verb, nullptr, "drawPicture(%p) %f:%f:%f:%f", picture, 459 picture->cullRect().fLeft, picture->cullRect().fTop, 460 picture->cullRect().fRight, picture->cullRect().fBottom); 461 fNestLevel += 1; 462 this->INHERITED::onDrawPicture(picture, matrix, paint); 463 fNestLevel -= 1; 464 this->dump(kDrawPicture_Verb, nullptr, "endPicture(%p) %f:%f:%f:%f", &picture, 465 picture->cullRect().fLeft, picture->cullRect().fTop, 466 picture->cullRect().fRight, picture->cullRect().fBottom); 467 } 468 469 void SkDumpCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode, 470 const SkPaint& paint) { 471 this->dump(kDrawVertices_Verb, &paint, "drawVertices(%s [%d] ...)", 472 toString(vertices->mode()), vertices->vertexCount()); 473 } 474 475 void SkDumpCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], 476 const SkPoint texCoords[4], SkBlendMode, 477 const SkPaint& paint) { 478 //dumps corner points and colors in clockwise order starting on upper-left corner 479 this->dump(kDrawPatch_Verb, &paint, "drawPatch(Vertices{[%f, %f], [%f, %f], [%f, %f], [%f, %f]}\ 480 | Colors{[0x%x], [0x%x], [0x%x], [0x%x]} | TexCoords{[%f,%f], [%f,%f], [%f,%f], \ 481 [%f,%f]})", 482 cubics[0].fX, cubics[0].fY, 483 cubics[3].fX, cubics[3].fY, 484 cubics[6].fX, cubics[6].fY, 485 cubics[9].fX, cubics[9].fY, 486 colors[0], colors[1], colors[2], colors[3], 487 texCoords[0].x(), texCoords[0].y(), texCoords[1].x(), texCoords[1].y(), 488 texCoords[2].x(), texCoords[2].y(), texCoords[3].x(), texCoords[3].y()); 489 } 490 491 void SkDumpCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { 492 SkString str; 493 toString(rect, &str); 494 this->dump(kDrawAnnotation_Verb, nullptr, "drawAnnotation(%s \"%s\" (%zu))", 495 str.c_str(), key, value ? value->size() : 0); 496 } 497 498 /////////////////////////////////////////////////////////////////////////////// 499 /////////////////////////////////////////////////////////////////////////////// 500 501 SkFormatDumper::SkFormatDumper(void (*proc)(const char*, void*), void* refcon) { 502 fProc = proc; 503 fRefcon = refcon; 504 } 505 506 static void appendPtr(SkString* str, const void* ptr, const char name[]) { 507 if (ptr) { 508 str->appendf(" %s:%p", name, ptr); 509 } 510 } 511 512 static void appendFlattenable(SkString* str, const SkFlattenable* ptr, 513 const char name[]) { 514 if (ptr) { 515 str->appendf(" %s:%p", name, ptr); 516 } 517 } 518 519 void SkFormatDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb, 520 const char str[], const SkPaint* p) { 521 SkString msg, tab; 522 const int level = canvas->getNestLevel() + canvas->getSaveCount() - 1; 523 SkASSERT(level >= 0); 524 for (int i = 0; i < level; i++) { 525 #if 0 526 tab.append("\t"); 527 #else 528 tab.append(" "); // tabs are often too wide to be useful 529 #endif 530 } 531 msg.printf("%s%s", tab.c_str(), str); 532 533 if (p) { 534 msg.appendf(" color:0x%08X flags:%X", p->getColor(), p->getFlags()); 535 if (!p->isSrcOver()) { 536 msg.appendf(" blendmode:%d", (int)p->getBlendMode()); 537 } 538 appendFlattenable(&msg, p->getShader(), "shader"); 539 appendFlattenable(&msg, p->getPathEffect(), "pathEffect"); 540 appendFlattenable(&msg, p->getMaskFilter(), "maskFilter"); 541 appendFlattenable(&msg, p->getPathEffect(), "pathEffect"); 542 appendFlattenable(&msg, p->getColorFilter(), "filter"); 543 544 if (SkDumpCanvas::kDrawText_Verb == verb) { 545 msg.appendf(" textSize:%g", SkScalarToFloat(p->getTextSize())); 546 appendPtr(&msg, p->getTypeface(), "typeface"); 547 } 548 549 if (p->getStyle() != SkPaint::kFill_Style) { 550 msg.appendf(" strokeWidth:%g", SkScalarToFloat(p->getStrokeWidth())); 551 } 552 } 553 554 fProc(msg.c_str(), fRefcon); 555 } 556 557 /////////////////////////////////////////////////////////////////////////////// 558 559 static void dumpToDebugf(const char text[], void*) { 560 SkDebugf("%s\n", text); 561 } 562 563 SkDebugfDumper::SkDebugfDumper() : INHERITED(dumpToDebugf, nullptr) {} 564