1/* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "platform/graphics/GraphicsContextRecorder.h" 33 34#include "platform/graphics/ImageBuffer.h" 35#include "platform/graphics/ImageSource.h" 36#include "platform/image-decoders/ImageDecoder.h" 37#include "platform/image-decoders/ImageFrame.h" 38#include "platform/image-encoders/skia/PNGImageEncoder.h" 39#include "third_party/skia/include/core/SkBitmapDevice.h" 40#include "third_party/skia/include/core/SkPictureRecorder.h" 41#include "third_party/skia/include/core/SkStream.h" 42#include "wtf/HexNumber.h" 43#include "wtf/text/Base64.h" 44#include "wtf/text/TextEncoding.h" 45 46namespace WebCore { 47 48GraphicsContext* GraphicsContextRecorder::record(const IntSize& size, bool isCertainlyOpaque) 49{ 50 ASSERT(!m_picture); 51 ASSERT(!m_recorder); 52 ASSERT(!m_context); 53 m_isCertainlyOpaque = isCertainlyOpaque; 54 m_recorder = adoptPtr(new SkPictureRecorder); 55 SkCanvas* canvas = m_recorder->beginRecording(size.width(), size.height(), 0, 0); 56 m_context = adoptPtr(new GraphicsContext(canvas)); 57 m_context->setTrackOpaqueRegion(isCertainlyOpaque); 58 m_context->setCertainlyOpaque(isCertainlyOpaque); 59 return m_context.get(); 60} 61 62PassRefPtr<GraphicsContextSnapshot> GraphicsContextRecorder::stop() 63{ 64 m_context.clear(); 65 m_picture = adoptRef(m_recorder->endRecording()); 66 m_recorder.clear(); 67 return adoptRef(new GraphicsContextSnapshot(m_picture.release(), m_isCertainlyOpaque)); 68} 69 70GraphicsContextSnapshot::GraphicsContextSnapshot(PassRefPtr<SkPicture> picture, bool isCertainlyOpaque) 71 : m_picture(picture) 72 , m_isCertainlyOpaque(isCertainlyOpaque) 73{ 74} 75 76 77class SnapshotPlayer : public SkDrawPictureCallback { 78 WTF_MAKE_NONCOPYABLE(SnapshotPlayer); 79public: 80 explicit SnapshotPlayer(PassRefPtr<SkPicture> picture, SkCanvas* canvas) 81 : m_picture(picture) 82 , m_canvas(canvas) 83 { 84 } 85 86 SkCanvas* canvas() { return m_canvas; } 87 88 void play() 89 { 90 m_picture->draw(m_canvas, this); 91 } 92 93private: 94 RefPtr<SkPicture> m_picture; 95 SkCanvas* m_canvas; 96}; 97 98class FragmentSnapshotPlayer : public SnapshotPlayer { 99public: 100 FragmentSnapshotPlayer(PassRefPtr<SkPicture> picture, SkCanvas* canvas) 101 : SnapshotPlayer(picture, canvas) 102 { 103 } 104 105 void play(unsigned fromStep, unsigned toStep) 106 { 107 m_fromStep = fromStep; 108 m_toStep = toStep; 109 m_stepCount = 0; 110 SnapshotPlayer::play(); 111 } 112 113 virtual bool abortDrawing() OVERRIDE 114 { 115 ++m_stepCount; 116 if (m_stepCount == m_fromStep) { 117 const SkBitmap& bitmap = canvas()->getDevice()->accessBitmap(true); 118 bitmap.eraseARGB(0, 0, 0, 0); // FIXME: get layers background color, it might make resulting image a bit more plausable. 119 } 120 return m_toStep && m_stepCount > m_toStep; 121 } 122 123private: 124 unsigned m_fromStep; 125 unsigned m_toStep; 126 unsigned m_stepCount; 127}; 128 129class ProfilingSnapshotPlayer : public SnapshotPlayer { 130public: 131 ProfilingSnapshotPlayer(PassRefPtr<SkPicture> picture, SkCanvas* canvas) 132 : SnapshotPlayer(picture, canvas) 133 { 134 } 135 136 void play(GraphicsContextSnapshot::Timings* timingsVector, unsigned minRepeatCount, double minDuration) 137 { 138 m_timingsVector = timingsVector; 139 m_timingsVector->reserveCapacity(minRepeatCount); 140 141 double now = WTF::monotonicallyIncreasingTime(); 142 double stopTime = now + minDuration; 143 for (unsigned step = 0; step < minRepeatCount || now < stopTime; ++step) { 144 m_timingsVector->append(Vector<double>()); 145 m_currentTimings = &m_timingsVector->last(); 146 if (m_timingsVector->size() > 1) 147 m_currentTimings->reserveCapacity(m_timingsVector->begin()->size()); 148 SnapshotPlayer::play(); 149 now = WTF::monotonicallyIncreasingTime(); 150 m_currentTimings->append(now); 151 } 152 } 153 154 virtual bool abortDrawing() OVERRIDE 155 { 156 m_currentTimings->append(WTF::monotonicallyIncreasingTime()); 157 return false; 158 } 159 160 const GraphicsContextSnapshot::Timings& timingsVector() const { return *m_timingsVector; } 161 162private: 163 GraphicsContextSnapshot::Timings* m_timingsVector; 164 Vector<double>* m_currentTimings; 165}; 166 167class LoggingCanvas : public SkCanvas { 168public: 169 LoggingCanvas(int width, int height) : SkCanvas(width, height) 170 { 171 m_log = JSONArray::create(); 172 } 173 174 void clear(SkColor color) OVERRIDE 175 { 176 addItemWithParams("clear")->setString("color", stringForSkColor(color)); 177 } 178 179 void drawPaint(const SkPaint& paint) OVERRIDE 180 { 181 addItemWithParams("drawPaint")->setObject("paint", objectForSkPaint(paint)); 182 } 183 184 void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) OVERRIDE 185 { 186 RefPtr<JSONObject> params = addItemWithParams("drawPoints"); 187 params->setString("pointMode", pointModeName(mode)); 188 params->setArray("points", arrayForSkPoints(count, pts)); 189 params->setObject("paint", objectForSkPaint(paint)); 190 } 191 192 void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE 193 { 194 RefPtr<JSONObject> params = addItemWithParams("drawRect"); 195 params->setObject("rect", objectForSkRect(rect)); 196 params->setObject("paint", objectForSkPaint(paint)); 197 } 198 199 void drawOval(const SkRect& oval, const SkPaint& paint) OVERRIDE 200 { 201 RefPtr<JSONObject> params = addItemWithParams("drawOval"); 202 params->setObject("oval", objectForSkRect(oval)); 203 params->setObject("paint", objectForSkPaint(paint)); 204 } 205 206 void drawRRect(const SkRRect& rrect, const SkPaint& paint) OVERRIDE 207 { 208 RefPtr<JSONObject> params = addItemWithParams("drawRRect"); 209 params->setObject("rrect", objectForSkRRect(rrect)); 210 params->setObject("paint", objectForSkPaint(paint)); 211 } 212 213 void drawPath(const SkPath& path, const SkPaint& paint) OVERRIDE 214 { 215 RefPtr<JSONObject> params = addItemWithParams("drawPath"); 216 params->setObject("path", objectForSkPath(path)); 217 params->setObject("paint", objectForSkPaint(paint)); 218 } 219 220 void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint) OVERRIDE 221 { 222 RefPtr<JSONObject> params = addItemWithParams("drawBitmap"); 223 params->setNumber("left", left); 224 params->setNumber("top", top); 225 params->setObject("bitmap", objectForSkBitmap(bitmap)); 226 params->setObject("paint", objectForSkPaint(*paint)); 227 } 228 229 void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags flags) OVERRIDE 230 { 231 RefPtr<JSONObject> params = addItemWithParams("drawBitmapRectToRect"); 232 params->setObject("bitmap", objectForSkBitmap(bitmap)); 233 params->setObject("src", objectForSkRect(*src)); 234 params->setObject("dst", objectForSkRect(dst)); 235 params->setObject("paint", objectForSkPaint(*paint)); 236 params->setNumber("flags", flags); 237 } 238 239 void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m, const SkPaint* paint) OVERRIDE 240 { 241 RefPtr<JSONObject> params = addItemWithParams("drawBitmapMatrix"); 242 params->setObject("bitmap", objectForSkBitmap(bitmap)); 243 params->setArray("matrix", arrayForSkMatrix(m)); 244 params->setObject("paint", objectForSkPaint(*paint)); 245 } 246 247 void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint) OVERRIDE 248 { 249 RefPtr<JSONObject> params = addItemWithParams("drawBitmapNine"); 250 params->setObject("bitmap", objectForSkBitmap(bitmap)); 251 params->setObject("center", objectForSkIRect(center)); 252 params->setObject("dst", objectForSkRect(dst)); 253 params->setObject("paint", objectForSkPaint(*paint)); 254 } 255 256 void drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) OVERRIDE 257 { 258 RefPtr<JSONObject> params = addItemWithParams("drawSprite"); 259 params->setObject("bitmap", objectForSkBitmap(bitmap)); 260 params->setNumber("left", left); 261 params->setNumber("top", top); 262 params->setObject("paint", objectForSkPaint(*paint)); 263 } 264 265 void drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, 266 const uint16_t indices[], int indexCount, const SkPaint& paint) OVERRIDE 267 { 268 RefPtr<JSONObject> params = addItemWithParams("drawVertices"); 269 params->setObject("paint", objectForSkPaint(paint)); 270 } 271 272 void drawData(const void* data, size_t length) OVERRIDE 273 { 274 RefPtr<JSONObject> params = addItemWithParams("drawData"); 275 params->setNumber("length", length); 276 } 277 278 void beginCommentGroup(const char* description) OVERRIDE 279 { 280 RefPtr<JSONObject> params = addItemWithParams("beginCommentGroup"); 281 params->setString("description", description); 282 } 283 284 void addComment(const char* keyword, const char* value) OVERRIDE 285 { 286 RefPtr<JSONObject> params = addItemWithParams("addComment"); 287 params->setString("key", keyword); 288 params->setString("value", value); 289 } 290 291 void endCommentGroup() OVERRIDE 292 { 293 addItem("endCommentGroup"); 294 } 295 296 void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) OVERRIDE 297 { 298 RefPtr<JSONObject> params = addItemWithParams("drawDRRect"); 299 params->setObject("outer", objectForSkRRect(outer)); 300 params->setObject("inner", objectForSkRRect(inner)); 301 params->setObject("paint", objectForSkPaint(paint)); 302 } 303 304 void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) OVERRIDE 305 { 306 RefPtr<JSONObject> params = addItemWithParams("drawText"); 307 params->setString("text", stringForText(text, byteLength, paint)); 308 params->setNumber("x", x); 309 params->setNumber("y", y); 310 params->setObject("paint", objectForSkPaint(paint)); 311 } 312 313 void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) OVERRIDE 314 { 315 RefPtr<JSONObject> params = addItemWithParams("drawPosText"); 316 params->setString("text", stringForText(text, byteLength, paint)); 317 size_t pointsCount = paint.countText(text, byteLength); 318 params->setArray("pos", arrayForSkPoints(pointsCount, pos)); 319 params->setObject("paint", objectForSkPaint(paint)); 320 } 321 322 void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) OVERRIDE 323 { 324 RefPtr<JSONObject> params = addItemWithParams("drawPosTextH"); 325 params->setString("text", stringForText(text, byteLength, paint)); 326 size_t pointsCount = paint.countText(text, byteLength); 327 params->setArray("xpos", arrayForSkScalars(pointsCount, xpos)); 328 params->setNumber("constY", constY); 329 params->setObject("paint", objectForSkPaint(paint)); 330 } 331 332 void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) OVERRIDE 333 { 334 RefPtr<JSONObject> params = addItemWithParams("drawTextOnPath"); 335 params->setString("text", stringForText(text, byteLength, paint)); 336 params->setObject("path", objectForSkPath(path)); 337 params->setArray("matrix", arrayForSkMatrix(*matrix)); 338 params->setObject("paint", objectForSkPaint(paint)); 339 } 340 341 void onPushCull(const SkRect& cullRect) OVERRIDE 342 { 343 RefPtr<JSONObject> params = addItemWithParams("pushCull"); 344 params->setObject("cullRect", objectForSkRect(cullRect)); 345 } 346 347 void onPopCull() OVERRIDE 348 { 349 addItem("popCull"); 350 } 351 352 void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle style) OVERRIDE 353 { 354 RefPtr<JSONObject> params = addItemWithParams("clipRect"); 355 params->setObject("rect", objectForSkRect(rect)); 356 params->setString("SkRegion::Op", regionOpName(op)); 357 params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style); 358 } 359 360 void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle style) OVERRIDE 361 { 362 RefPtr<JSONObject> params = addItemWithParams("clipRRect"); 363 params->setObject("rrect", objectForSkRRect(rrect)); 364 params->setString("SkRegion::Op", regionOpName(op)); 365 params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style); 366 } 367 368 void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle style) OVERRIDE 369 { 370 RefPtr<JSONObject> params = addItemWithParams("clipPath"); 371 params->setObject("path", objectForSkPath(path)); 372 params->setString("SkRegion::Op", regionOpName(op)); 373 params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style); 374 } 375 376 void onClipRegion(const SkRegion& region, SkRegion::Op op) OVERRIDE 377 { 378 RefPtr<JSONObject> params = addItemWithParams("clipRegion"); 379 params->setString("op", regionOpName(op)); 380 } 381 382 void onDrawPicture(const SkPicture* picture) OVERRIDE 383 { 384 addItemWithParams("drawPicture")->setObject("picture", objectForSkPicture(*picture)); 385 } 386 387 void didSetMatrix(const SkMatrix& matrix) OVERRIDE 388 { 389 RefPtr<JSONObject> params = addItemWithParams("setMatrix"); 390 params->setArray("matrix", arrayForSkMatrix(matrix)); 391 this->SkCanvas::didSetMatrix(matrix); 392 } 393 394 void didConcat(const SkMatrix& matrix) OVERRIDE 395 { 396 switch (matrix.getType()) { 397 case SkMatrix::kTranslate_Mask: 398 translate(matrix.getTranslateX(), matrix.getTranslateY()); 399 break; 400 case SkMatrix::kScale_Mask: 401 scale(matrix.getScaleX(), matrix.getScaleY()); 402 break; 403 default: 404 concat(matrix); 405 } 406 this->SkCanvas::didConcat(matrix); 407 } 408 409 void willRestore() OVERRIDE 410 { 411 addItem("restore"); 412 this->SkCanvas::willRestore(); 413 } 414 415 SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) OVERRIDE 416 { 417 RefPtr<JSONObject> params = addItemWithParams("saveLayer"); 418 if (bounds) 419 params->setObject("bounds", objectForSkRect(*bounds)); 420 params->setObject("paint", objectForSkPaint(*paint)); 421 params->setString("saveFlags", saveFlagsToString(flags)); 422 this->SkCanvas::willSaveLayer(bounds, paint, flags); 423 return kNoLayer_SaveLayerStrategy; 424 } 425 426 void willSave() OVERRIDE 427 { 428 RefPtr<JSONObject> params = addItemWithParams("save"); 429 this->SkCanvas::willSave(); 430 } 431 432 bool isClipEmpty() const OVERRIDE 433 { 434 return false; 435 } 436 437 bool isClipRect() const OVERRIDE 438 { 439 return true; 440 } 441 442#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE 443 ClipType getClipType() const OVERRIDE 444 { 445 return kRect_ClipType; 446 } 447#endif 448 449 bool getClipBounds(SkRect* bounds) const OVERRIDE 450 { 451 if (bounds) 452 bounds->setXYWH(0, 0, SkIntToScalar(this->imageInfo().fWidth), SkIntToScalar(this->imageInfo().fHeight)); 453 return true; 454 } 455 456 bool getClipDeviceBounds(SkIRect* bounds) const OVERRIDE 457 { 458 if (bounds) 459 bounds->setLargest(); 460 return true; 461 } 462 463 PassRefPtr<JSONArray> log() 464 { 465 return m_log; 466 } 467 468private: 469 RefPtr<JSONArray> m_log; 470 471 PassRefPtr<JSONObject> addItem(const String& name) 472 { 473 RefPtr<JSONObject> item = JSONObject::create(); 474 item->setString("method", name); 475 m_log->pushObject(item); 476 return item.release(); 477 } 478 479 PassRefPtr<JSONObject> addItemWithParams(const String& name) 480 { 481 RefPtr<JSONObject> item = addItem(name); 482 RefPtr<JSONObject> params = JSONObject::create(); 483 item->setObject("params", params); 484 return params.release(); 485 } 486 487 PassRefPtr<JSONObject> objectForSkRect(const SkRect& rect) 488 { 489 RefPtr<JSONObject> rectItem = JSONObject::create(); 490 rectItem->setNumber("left", rect.left()); 491 rectItem->setNumber("top", rect.top()); 492 rectItem->setNumber("right", rect.right()); 493 rectItem->setNumber("bottom", rect.bottom()); 494 return rectItem.release(); 495 } 496 497 PassRefPtr<JSONObject> objectForSkIRect(const SkIRect& rect) 498 { 499 RefPtr<JSONObject> rectItem = JSONObject::create(); 500 rectItem->setNumber("left", rect.left()); 501 rectItem->setNumber("top", rect.top()); 502 rectItem->setNumber("right", rect.right()); 503 rectItem->setNumber("bottom", rect.bottom()); 504 return rectItem.release(); 505 } 506 507 String pointModeName(PointMode mode) 508 { 509 switch (mode) { 510 case SkCanvas::kPoints_PointMode: return "Points"; 511 case SkCanvas::kLines_PointMode: return "Lines"; 512 case SkCanvas::kPolygon_PointMode: return "Polygon"; 513 default: 514 ASSERT_NOT_REACHED(); 515 return "?"; 516 }; 517 } 518 519 PassRefPtr<JSONObject> objectForSkPoint(const SkPoint& point) 520 { 521 RefPtr<JSONObject> pointItem = JSONObject::create(); 522 pointItem->setNumber("x", point.x()); 523 pointItem->setNumber("y", point.y()); 524 return pointItem.release(); 525 } 526 527 PassRefPtr<JSONArray> arrayForSkPoints(size_t count, const SkPoint points[]) 528 { 529 RefPtr<JSONArray> pointsArrayItem = JSONArray::create(); 530 for (size_t i = 0; i < count; ++i) 531 pointsArrayItem->pushObject(objectForSkPoint(points[i])); 532 return pointsArrayItem.release(); 533 } 534 535 PassRefPtr<JSONObject> objectForSkPicture(const SkPicture& picture) 536 { 537 RefPtr<JSONObject> pictureItem = JSONObject::create(); 538 pictureItem->setNumber("width", picture.width()); 539 pictureItem->setNumber("height", picture.height()); 540 return pictureItem.release(); 541 } 542 543 PassRefPtr<JSONObject> objectForRadius(const SkRRect& rrect, SkRRect::Corner corner) 544 { 545 RefPtr<JSONObject> radiusItem = JSONObject::create(); 546 SkVector radius = rrect.radii(corner); 547 radiusItem->setNumber("xRadius", radius.x()); 548 radiusItem->setNumber("yRadius", radius.y()); 549 return radiusItem.release(); 550 } 551 552 String rrectTypeName(SkRRect::Type type) 553 { 554 switch (type) { 555 case SkRRect::kEmpty_Type: return "Empty"; 556 case SkRRect::kRect_Type: return "Rect"; 557 case SkRRect::kOval_Type: return "Oval"; 558 case SkRRect::kSimple_Type: return "Simple"; 559 case SkRRect::kNinePatch_Type: return "Nine-patch"; 560 case SkRRect::kComplex_Type: return "Complex"; 561 default: 562 ASSERT_NOT_REACHED(); 563 return "?"; 564 }; 565 } 566 567 String radiusName(SkRRect::Corner corner) 568 { 569 switch (corner) { 570 case SkRRect::kUpperLeft_Corner: return "upperLeftRadius"; 571 case SkRRect::kUpperRight_Corner: return "upperRightRadius"; 572 case SkRRect::kLowerRight_Corner: return "lowerRightRadius"; 573 case SkRRect::kLowerLeft_Corner: return "lowerLeftRadius"; 574 default: 575 ASSERT_NOT_REACHED(); 576 return "?"; 577 } 578 } 579 580 PassRefPtr<JSONObject> objectForSkRRect(const SkRRect& rrect) 581 { 582 RefPtr<JSONObject> rrectItem = JSONObject::create(); 583 rrectItem->setString("type", rrectTypeName(rrect.type())); 584 rrectItem->setNumber("left", rrect.rect().left()); 585 rrectItem->setNumber("top", rrect.rect().top()); 586 rrectItem->setNumber("right", rrect.rect().right()); 587 rrectItem->setNumber("bottom", rrect.rect().bottom()); 588 for (int i = 0; i < 4; ++i) 589 rrectItem->setObject(radiusName((SkRRect::Corner) i), objectForRadius(rrect, (SkRRect::Corner) i)); 590 return rrectItem.release(); 591 } 592 593 String fillTypeName(SkPath::FillType type) 594 { 595 switch (type) { 596 case SkPath::kWinding_FillType: return "Winding"; 597 case SkPath::kEvenOdd_FillType: return "EvenOdd"; 598 case SkPath::kInverseWinding_FillType: return "InverseWinding"; 599 case SkPath::kInverseEvenOdd_FillType: return "InverseEvenOdd"; 600 default: 601 ASSERT_NOT_REACHED(); 602 return "?"; 603 }; 604 } 605 606 String convexityName(SkPath::Convexity convexity) 607 { 608 switch (convexity) { 609 case SkPath::kUnknown_Convexity: return "Unknown"; 610 case SkPath::kConvex_Convexity: return "Convex"; 611 case SkPath::kConcave_Convexity: return "Concave"; 612 default: 613 ASSERT_NOT_REACHED(); 614 return "?"; 615 }; 616 } 617 618 String verbName(SkPath::Verb verb) 619 { 620 switch (verb) { 621 case SkPath::kMove_Verb: return "Move"; 622 case SkPath::kLine_Verb: return "Line"; 623 case SkPath::kQuad_Verb: return "Quad"; 624 case SkPath::kConic_Verb: return "Conic"; 625 case SkPath::kCubic_Verb: return "Cubic"; 626 case SkPath::kClose_Verb: return "Close"; 627 case SkPath::kDone_Verb: return "Done"; 628 default: 629 ASSERT_NOT_REACHED(); 630 return "?"; 631 }; 632 } 633 634 struct VerbParams { 635 String name; 636 unsigned pointCount; 637 unsigned pointOffset; 638 639 VerbParams(const String& name, unsigned pointCount, unsigned pointOffset) 640 : name(name) 641 , pointCount(pointCount) 642 , pointOffset(pointOffset) { } 643 }; 644 645 VerbParams segmentParams(SkPath::Verb verb) 646 { 647 switch (verb) { 648 case SkPath::kMove_Verb: return VerbParams("Move", 1, 0); 649 case SkPath::kLine_Verb: return VerbParams("Line", 1, 1); 650 case SkPath::kQuad_Verb: return VerbParams("Quad", 2, 1); 651 case SkPath::kConic_Verb: return VerbParams("Conic", 2, 1); 652 case SkPath::kCubic_Verb: return VerbParams("Cubic", 3, 1); 653 case SkPath::kClose_Verb: return VerbParams("Close", 0, 0); 654 case SkPath::kDone_Verb: return VerbParams("Done", 0, 0); 655 default: 656 ASSERT_NOT_REACHED(); 657 return VerbParams("?", 0, 0); 658 }; 659 } 660 661 PassRefPtr<JSONObject> objectForSkPath(const SkPath& path) 662 { 663 RefPtr<JSONObject> pathItem = JSONObject::create(); 664 pathItem->setString("fillType", fillTypeName(path.getFillType())); 665 pathItem->setString("convexity", convexityName(path.getConvexity())); 666 pathItem->setBoolean("isRect", path.isRect(0)); 667 SkPath::Iter iter(path, false); 668 SkPoint points[4]; 669 RefPtr<JSONArray> pathPointsArray = JSONArray::create(); 670 for (SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Verb; verb = iter.next(points, false)) { 671 VerbParams verbParams = segmentParams(verb); 672 RefPtr<JSONObject> pathPointItem = JSONObject::create(); 673 pathPointItem->setString("verb", verbParams.name); 674 ASSERT(verbParams.pointCount + verbParams.pointOffset <= WTF_ARRAY_LENGTH(points)); 675 pathPointItem->setArray("points", arrayForSkPoints(verbParams.pointCount, points + verbParams.pointOffset)); 676 if (SkPath::kConic_Verb == verb) 677 pathPointItem->setNumber("conicWeight", iter.conicWeight()); 678 pathPointsArray->pushObject(pathPointItem); 679 } 680 pathItem->setArray("pathPoints", pathPointsArray); 681 pathItem->setObject("bounds", objectForSkRect(path.getBounds())); 682 return pathItem.release(); 683 } 684 685 String configName(SkBitmap::Config config) 686 { 687 switch (config) { 688 case SkBitmap::kNo_Config: return "None"; 689 case SkBitmap::kA8_Config: return "A8"; 690 case SkBitmap::kIndex8_Config: return "Index8"; 691 case SkBitmap::kRGB_565_Config: return "RGB565"; 692 case SkBitmap::kARGB_4444_Config: return "ARGB4444"; 693 case SkBitmap::kARGB_8888_Config: return "ARGB8888"; 694 default: 695 ASSERT_NOT_REACHED(); 696 return "?"; 697 }; 698 } 699 700 PassRefPtr<JSONObject> objectForBitmapData(const SkBitmap& bitmap) 701 { 702 RefPtr<JSONObject> dataItem = JSONObject::create(); 703 Vector<unsigned char> output; 704 WebCore::PNGImageEncoder::encode(bitmap, &output); 705 dataItem->setString("base64", WTF::base64Encode(reinterpret_cast<char*>(output.data()), output.size())); 706 dataItem->setString("mimeType", "image/png"); 707 return dataItem.release(); 708 } 709 710 PassRefPtr<JSONObject> objectForSkBitmap(const SkBitmap& bitmap) 711 { 712 RefPtr<JSONObject> bitmapItem = JSONObject::create(); 713 bitmapItem->setNumber("width", bitmap.width()); 714 bitmapItem->setNumber("height", bitmap.height()); 715 bitmapItem->setString("config", configName(bitmap.config())); 716 bitmapItem->setBoolean("opaque", bitmap.isOpaque()); 717 bitmapItem->setBoolean("immutable", bitmap.isImmutable()); 718 bitmapItem->setBoolean("volatile", bitmap.isVolatile()); 719 bitmapItem->setNumber("genID", bitmap.getGenerationID()); 720 bitmapItem->setObject("data", objectForBitmapData(bitmap)); 721 return bitmapItem.release(); 722 } 723 724 PassRefPtr<JSONObject> objectForSkShader(const SkShader& shader) 725 { 726 RefPtr<JSONObject> shaderItem = JSONObject::create(); 727 const SkMatrix localMatrix = shader.getLocalMatrix(); 728 if (!localMatrix.isIdentity()) 729 shaderItem->setArray("localMatrix", arrayForSkMatrix(localMatrix)); 730 return shaderItem.release(); 731 } 732 733 String stringForSkColor(const SkColor& color) 734 { 735 String colorString = "#"; 736 appendUnsignedAsHex(color, colorString); 737 return colorString; 738 } 739 740 void appendFlagToString(String* flagsString, bool isSet, const String& name) 741 { 742 if (!isSet) 743 return; 744 if (flagsString->length()) 745 flagsString->append("|"); 746 flagsString->append(name); 747 } 748 749 String stringForSkPaintFlags(const SkPaint& paint) 750 { 751 if (!paint.getFlags()) 752 return "none"; 753 String flagsString = ""; 754 appendFlagToString(&flagsString, paint.isAntiAlias(), "AntiAlias"); 755 appendFlagToString(&flagsString, paint.isDither(), "Dither"); 756 appendFlagToString(&flagsString, paint.isUnderlineText(), "UnderlinText"); 757 appendFlagToString(&flagsString, paint.isStrikeThruText(), "StrikeThruText"); 758 appendFlagToString(&flagsString, paint.isFakeBoldText(), "FakeBoldText"); 759 appendFlagToString(&flagsString, paint.isLinearText(), "LinearText"); 760 appendFlagToString(&flagsString, paint.isSubpixelText(), "SubpixelText"); 761 appendFlagToString(&flagsString, paint.isDevKernText(), "DevKernText"); 762 appendFlagToString(&flagsString, paint.isLCDRenderText(), "LCDRenderText"); 763 appendFlagToString(&flagsString, paint.isEmbeddedBitmapText(), "EmbeddedBitmapText"); 764 appendFlagToString(&flagsString, paint.isAutohinted(), "Autohinted"); 765 appendFlagToString(&flagsString, paint.isVerticalText(), "VerticalText"); 766 appendFlagToString(&flagsString, paint.getFlags() & SkPaint::kGenA8FromLCD_Flag, "GenA8FromLCD"); 767 return flagsString; 768 } 769 770 String filterLevelName(SkPaint::FilterLevel filterLevel) 771 { 772 switch (filterLevel) { 773 case SkPaint::kNone_FilterLevel: return "None"; 774 case SkPaint::kLow_FilterLevel: return "Low"; 775 case SkPaint::kMedium_FilterLevel: return "Medium"; 776 case SkPaint::kHigh_FilterLevel: return "High"; 777 default: 778 ASSERT_NOT_REACHED(); 779 return "?"; 780 }; 781 } 782 783 String textAlignName(SkPaint::Align align) 784 { 785 switch (align) { 786 case SkPaint::kLeft_Align: return "Left"; 787 case SkPaint::kCenter_Align: return "Center"; 788 case SkPaint::kRight_Align: return "Right"; 789 default: 790 ASSERT_NOT_REACHED(); 791 return "?"; 792 }; 793 } 794 795 String strokeCapName(SkPaint::Cap cap) 796 { 797 switch (cap) { 798 case SkPaint::kButt_Cap: return "Butt"; 799 case SkPaint::kRound_Cap: return "Round"; 800 case SkPaint::kSquare_Cap: return "Square"; 801 default: 802 ASSERT_NOT_REACHED(); 803 return "?"; 804 }; 805 } 806 807 String strokeJoinName(SkPaint::Join join) 808 { 809 switch (join) { 810 case SkPaint::kMiter_Join: return "Miter"; 811 case SkPaint::kRound_Join: return "Round"; 812 case SkPaint::kBevel_Join: return "Bevel"; 813 default: 814 ASSERT_NOT_REACHED(); 815 return "?"; 816 }; 817 } 818 819 String styleName(SkPaint::Style style) 820 { 821 switch (style) { 822 case SkPaint::kFill_Style: return "Fill"; 823 case SkPaint::kStroke_Style: return "Stroke"; 824 case SkPaint::kStrokeAndFill_Style: return "StrokeAndFill"; 825 default: 826 ASSERT_NOT_REACHED(); 827 return "?"; 828 }; 829 } 830 831 String textEncodingName(SkPaint::TextEncoding encoding) 832 { 833 switch (encoding) { 834 case SkPaint::kUTF8_TextEncoding: return "UTF-8"; 835 case SkPaint::kUTF16_TextEncoding: return "UTF-16"; 836 case SkPaint::kUTF32_TextEncoding: return "UTF-32"; 837 case SkPaint::kGlyphID_TextEncoding: return "GlyphID"; 838 default: 839 ASSERT_NOT_REACHED(); 840 return "?"; 841 }; 842 } 843 844 String hintingName(SkPaint::Hinting hinting) 845 { 846 switch (hinting) { 847 case SkPaint::kNo_Hinting: return "None"; 848 case SkPaint::kSlight_Hinting: return "Slight"; 849 case SkPaint::kNormal_Hinting: return "Normal"; 850 case SkPaint::kFull_Hinting: return "Full"; 851 default: 852 ASSERT_NOT_REACHED(); 853 return "?"; 854 }; 855 } 856 857 PassRefPtr<JSONObject> objectForSkPaint(const SkPaint& paint) 858 { 859 RefPtr<JSONObject> paintItem = JSONObject::create(); 860 paintItem->setNumber("textSize", paint.getTextSize()); 861 paintItem->setNumber("textScaleX", paint.getTextScaleX()); 862 paintItem->setNumber("textSkewX", paint.getTextSkewX()); 863 if (SkShader* shader = paint.getShader()) 864 paintItem->setObject("shader", objectForSkShader(*shader)); 865 paintItem->setString("color", stringForSkColor(paint.getColor())); 866 paintItem->setNumber("strokeWidth", paint.getStrokeWidth()); 867 paintItem->setNumber("strokeMiter", paint.getStrokeMiter()); 868 paintItem->setString("flags", stringForSkPaintFlags(paint)); 869 paintItem->setString("filterLevel", filterLevelName(paint.getFilterLevel())); 870 paintItem->setString("textAlign", textAlignName(paint.getTextAlign())); 871 paintItem->setString("strokeCap", strokeCapName(paint.getStrokeCap())); 872 paintItem->setString("strokeJoin", strokeJoinName(paint.getStrokeJoin())); 873 paintItem->setString("styleName", styleName(paint.getStyle())); 874 paintItem->setString("textEncoding", textEncodingName(paint.getTextEncoding())); 875 paintItem->setString("hinting", hintingName(paint.getHinting())); 876 return paintItem.release(); 877 } 878 879 PassRefPtr<JSONArray> arrayForSkMatrix(const SkMatrix& matrix) 880 { 881 RefPtr<JSONArray> matrixArray = JSONArray::create(); 882 for (int i = 0; i < 9; ++i) 883 matrixArray->pushNumber(matrix[i]); 884 return matrixArray.release(); 885 } 886 887 PassRefPtr<JSONArray> arrayForSkScalars(size_t n, const SkScalar scalars[]) 888 { 889 RefPtr<JSONArray> scalarsArray = JSONArray::create(); 890 for (size_t i = 0; i < n; ++i) 891 scalarsArray->pushNumber(scalars[i]); 892 return scalarsArray.release(); 893 } 894 895 String regionOpName(SkRegion::Op op) 896 { 897 switch (op) { 898 case SkRegion::kDifference_Op: return "kDifference_Op"; 899 case SkRegion::kIntersect_Op: return "kIntersect_Op"; 900 case SkRegion::kUnion_Op: return "kUnion_Op"; 901 case SkRegion::kXOR_Op: return "kXOR_Op"; 902 case SkRegion::kReverseDifference_Op: return "kReverseDifference_Op"; 903 case SkRegion::kReplace_Op: return "kReplace_Op"; 904 default: return "Unknown type"; 905 }; 906 } 907 908 void translate(SkScalar dx, SkScalar dy) 909 { 910 RefPtr<JSONObject> params = addItemWithParams("translate"); 911 params->setNumber("dx", dx); 912 params->setNumber("dy", dy); 913 } 914 915 void scale(SkScalar scaleX, SkScalar scaleY) 916 { 917 RefPtr<JSONObject> params = addItemWithParams("scale"); 918 params->setNumber("scaleX", scaleX); 919 params->setNumber("scaleY", scaleY); 920 } 921 922 void concat(const SkMatrix& matrix) 923 { 924 RefPtr<JSONObject> params = addItemWithParams("concat"); 925 params->setArray("matrix", arrayForSkMatrix(matrix)); 926 } 927 928 String saveFlagsToString(SkCanvas::SaveFlags flags) 929 { 930 String flagsString = ""; 931 if (flags & SkCanvas::kHasAlphaLayer_SaveFlag) 932 flagsString.append("kHasAlphaLayer_SaveFlag "); 933 if (flags & SkCanvas::kFullColorLayer_SaveFlag) 934 flagsString.append("kFullColorLayer_SaveFlag "); 935 if (flags & SkCanvas::kClipToLayer_SaveFlag) 936 flagsString.append("kClipToLayer_SaveFlag "); 937 return flagsString; 938 } 939 940 String textEncodingCanonicalName(SkPaint::TextEncoding encoding) 941 { 942 String name = textEncodingName(encoding); 943 if (encoding == SkPaint::kUTF16_TextEncoding || encoding == SkPaint::kUTF32_TextEncoding) 944 name.append("LE"); 945 return name; 946 } 947 948 String stringForUTFText(const void* text, size_t length, SkPaint::TextEncoding encoding) 949 { 950 return WTF::TextEncoding(textEncodingCanonicalName(encoding)).decode((const char*)text, length); 951 } 952 953 String stringForText(const void* text, size_t byteLength, const SkPaint& paint) 954 { 955 SkPaint::TextEncoding encoding = paint.getTextEncoding(); 956 switch (encoding) { 957 case SkPaint::kUTF8_TextEncoding: 958 case SkPaint::kUTF16_TextEncoding: 959 case SkPaint::kUTF32_TextEncoding: 960 return stringForUTFText(text, byteLength, encoding); 961 case SkPaint::kGlyphID_TextEncoding: { 962 WTF::Vector<SkUnichar> dataVector(byteLength / 2); 963 SkUnichar* textData = dataVector.data(); 964 paint.glyphsToUnichars(static_cast<const uint16_t*>(text), byteLength / 2, textData); 965 return WTF::UTF32LittleEndianEncoding().decode(reinterpret_cast<const char*>(textData), byteLength * 2); 966 } 967 default: 968 ASSERT_NOT_REACHED(); 969 return "?"; 970 } 971 } 972}; 973 974static bool decodeBitmap(const void* data, size_t length, SkBitmap* result) 975{ 976 RefPtr<SharedBuffer> buffer = SharedBuffer::create(static_cast<const char*>(data), length); 977 OwnPtr<ImageDecoder> imageDecoder = ImageDecoder::create(*buffer, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnored); 978 if (!imageDecoder) 979 return false; 980 imageDecoder->setData(buffer.get(), true); 981 ImageFrame* frame = imageDecoder->frameBufferAtIndex(0); 982 if (!frame) 983 return true; 984 *result = frame->getSkBitmap(); 985 return true; 986} 987 988PassRefPtr<GraphicsContextSnapshot> GraphicsContextSnapshot::load(const char* data, size_t size) 989{ 990 SkMemoryStream stream(data, size); 991 RefPtr<SkPicture> picture = adoptRef(SkPicture::CreateFromStream(&stream, decodeBitmap)); 992 if (!picture) 993 return nullptr; 994 return adoptRef(new GraphicsContextSnapshot(picture, false)); 995} 996 997PassOwnPtr<ImageBuffer> GraphicsContextSnapshot::replay(unsigned fromStep, unsigned toStep) const 998{ 999 OwnPtr<ImageBuffer> imageBuffer = createImageBuffer(); 1000 FragmentSnapshotPlayer player(m_picture, imageBuffer->context()->canvas()); 1001 player.play(fromStep, toStep); 1002 return imageBuffer.release(); 1003} 1004 1005PassOwnPtr<GraphicsContextSnapshot::Timings> GraphicsContextSnapshot::profile(unsigned minRepeatCount, double minDuration) const 1006{ 1007 OwnPtr<GraphicsContextSnapshot::Timings> timings = adoptPtr(new GraphicsContextSnapshot::Timings()); 1008 OwnPtr<ImageBuffer> imageBuffer = createImageBuffer(); 1009 ProfilingSnapshotPlayer player(m_picture, imageBuffer->context()->canvas()); 1010 player.play(timings.get(), minRepeatCount, minDuration); 1011 return timings.release(); 1012} 1013 1014PassOwnPtr<ImageBuffer> GraphicsContextSnapshot::createImageBuffer() const 1015{ 1016 return ImageBuffer::create(IntSize(m_picture->width(), m_picture->height()), m_isCertainlyOpaque ? Opaque : NonOpaque); 1017} 1018 1019PassRefPtr<JSONArray> GraphicsContextSnapshot::snapshotCommandLog() const 1020{ 1021 LoggingCanvas canvas(m_picture->width(), m_picture->height()); 1022 FragmentSnapshotPlayer player(m_picture, &canvas); 1023 player.play(0, 0); 1024 return canvas.log(); 1025} 1026 1027} 1028