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/LoggingCanvas.h"
33
34#include "platform/image-encoders/skia/PNGImageEncoder.h"
35#include "third_party/skia/include/core/SkPicture.h"
36#include "wtf/HexNumber.h"
37#include "wtf/text/Base64.h"
38#include "wtf/text/TextEncoding.h"
39
40namespace blink {
41
42class AutoLogger {
43public:
44    explicit AutoLogger(LoggingCanvas*);
45    PassRefPtr<JSONObject> logItem(const String& name);
46    PassRefPtr<JSONObject> logItemWithParams(const String& name);
47    ~AutoLogger();
48
49private:
50    LoggingCanvas* m_canvas;
51    RefPtr<JSONObject> m_logItem;
52};
53
54AutoLogger::AutoLogger(LoggingCanvas* loggingCanvas) : m_canvas(loggingCanvas)
55{
56    loggingCanvas->m_depthCount++;
57}
58
59PassRefPtr<JSONObject> AutoLogger::logItem(const String& name)
60{
61    RefPtr<JSONObject> item = JSONObject::create();
62    item->setString("method", name);
63    m_logItem = item;
64    return item.release();
65}
66
67PassRefPtr<JSONObject> AutoLogger::logItemWithParams(const String& name)
68{
69    RefPtr<JSONObject> item = logItem(name);
70    RefPtr<JSONObject> params = JSONObject::create();
71    item->setObject("params", params);
72    return params.release();
73}
74
75AutoLogger::~AutoLogger()
76{
77    m_canvas->m_depthCount--;
78    if (!m_canvas->m_depthCount)
79        m_canvas->m_log->pushObject(m_logItem);
80}
81
82LoggingCanvas::LoggingCanvas(int width, int height) : InterceptingCanvas(width, height)
83{
84    m_log = JSONArray::create();
85}
86
87void LoggingCanvas::clear(SkColor color)
88{
89    AutoLogger logger(this);
90    logger.logItemWithParams("clear")->setString("color", stringForSkColor(color));
91    this->SkCanvas::clear(color);
92}
93
94void LoggingCanvas::drawPaint(const SkPaint& paint)
95{
96    AutoLogger logger(this);
97    logger.logItemWithParams("drawPaint")->setObject("paint", objectForSkPaint(paint));
98    this->SkCanvas::drawPaint(paint);
99}
100
101void LoggingCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint)
102{
103    AutoLogger logger(this);
104    RefPtr<JSONObject> params = logger.logItemWithParams("drawPoints");
105    params->setString("pointMode", pointModeName(mode));
106    params->setArray("points", arrayForSkPoints(count, pts));
107    params->setObject("paint", objectForSkPaint(paint));
108    this->SkCanvas::drawPoints(mode, count, pts, paint);
109}
110
111void LoggingCanvas::drawRect(const SkRect& rect, const SkPaint& paint)
112{
113    AutoLogger logger(this);
114    RefPtr<JSONObject> params = logger.logItemWithParams("drawRect");
115    params->setObject("rect", objectForSkRect(rect));
116    params->setObject("paint", objectForSkPaint(paint));
117    this->SkCanvas::drawRect(rect, paint);
118}
119
120void LoggingCanvas::drawOval(const SkRect& oval, const SkPaint& paint)
121{
122    AutoLogger logger(this);
123    RefPtr<JSONObject> params = logger.logItemWithParams("drawOval");
124    params->setObject("oval", objectForSkRect(oval));
125    params->setObject("paint", objectForSkPaint(paint));
126    this->SkCanvas::drawOval(oval, paint);
127}
128
129void LoggingCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint)
130{
131    AutoLogger logger(this);
132    RefPtr<JSONObject> params = logger.logItemWithParams("drawRRect");
133    params->setObject("rrect", objectForSkRRect(rrect));
134    params->setObject("paint", objectForSkPaint(paint));
135    this->SkCanvas::drawRRect(rrect, paint);
136}
137
138void LoggingCanvas::drawPath(const SkPath& path, const SkPaint& paint)
139{
140    AutoLogger logger(this);
141    RefPtr<JSONObject> params = logger.logItemWithParams("drawPath");
142    params->setObject("path", objectForSkPath(path));
143    params->setObject("paint", objectForSkPaint(paint));
144    this->SkCanvas::drawPath(path, paint);
145}
146
147void LoggingCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint)
148{
149    AutoLogger logger(this);
150    RefPtr<JSONObject> params = logger.logItemWithParams("drawBitmap");
151    params->setNumber("left", left);
152    params->setNumber("top", top);
153    params->setObject("bitmap", objectForSkBitmap(bitmap));
154    params->setObject("paint", objectForSkPaint(*paint));
155    this->SkCanvas::drawBitmap(bitmap, left, top, paint);
156}
157
158void LoggingCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags flags)
159{
160    AutoLogger logger(this);
161    RefPtr<JSONObject> params = logger.logItemWithParams("drawBitmapRectToRect");
162    params->setObject("bitmap", objectForSkBitmap(bitmap));
163    params->setObject("src", objectForSkRect(*src));
164    params->setObject("dst", objectForSkRect(dst));
165    params->setObject("paint", objectForSkPaint(*paint));
166    params->setNumber("flags", flags);
167    this->SkCanvas::drawBitmapRectToRect(bitmap, src, dst, paint, flags);
168}
169
170void LoggingCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m, const SkPaint* paint)
171{
172    AutoLogger logger(this);
173    RefPtr<JSONObject> params = logger.logItemWithParams("drawBitmapMatrix");
174    params->setObject("bitmap", objectForSkBitmap(bitmap));
175    params->setArray("matrix", arrayForSkMatrix(m));
176    params->setObject("paint", objectForSkPaint(*paint));
177    this->SkCanvas::drawBitmapMatrix(bitmap, m, paint);
178}
179
180void LoggingCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint)
181{
182    AutoLogger logger(this);
183    RefPtr<JSONObject> params = logger.logItemWithParams("drawBitmapNine");
184    params->setObject("bitmap", objectForSkBitmap(bitmap));
185    params->setObject("center", objectForSkIRect(center));
186    params->setObject("dst", objectForSkRect(dst));
187    params->setObject("paint", objectForSkPaint(*paint));
188    this->SkCanvas::drawBitmapNine(bitmap, center, dst, paint);
189}
190
191void LoggingCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint)
192{
193    AutoLogger logger(this);
194    RefPtr<JSONObject> params = logger.logItemWithParams("drawSprite");
195    params->setObject("bitmap", objectForSkBitmap(bitmap));
196    params->setNumber("left", left);
197    params->setNumber("top", top);
198    params->setObject("paint", objectForSkPaint(*paint));
199    this->SkCanvas::drawSprite(bitmap, left, top, paint);
200}
201
202void LoggingCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
203    const uint16_t indices[], int indexCount, const SkPaint& paint)
204{
205    AutoLogger logger(this);
206    RefPtr<JSONObject> params = logger.logItemWithParams("drawVertices");
207    params->setObject("paint", objectForSkPaint(paint));
208    this->SkCanvas::drawVertices(vmode, vertexCount, vertices, texs, colors, xmode, indices, indexCount, paint);
209}
210
211void LoggingCanvas::drawData(const void* data, size_t length)
212{
213    AutoLogger logger(this);
214    RefPtr<JSONObject> params = logger.logItemWithParams("drawData");
215    params->setNumber("length", length);
216    this->SkCanvas::drawData(data, length);
217}
218
219void LoggingCanvas::beginCommentGroup(const char* description)
220{
221    AutoLogger logger(this);
222    RefPtr<JSONObject> params = logger.logItemWithParams("beginCommentGroup");
223    params->setString("description", description);
224    this->SkCanvas::beginCommentGroup(description);
225}
226
227void LoggingCanvas::addComment(const char* keyword, const char* value)
228{
229    AutoLogger logger(this);
230    RefPtr<JSONObject> params = logger.logItemWithParams("addComment");
231    params->setString("key", keyword);
232    params->setString("value", value);
233    this->SkCanvas::addComment(keyword, value);
234}
235
236void LoggingCanvas::endCommentGroup()
237{
238    AutoLogger logger(this);
239    logger.logItem("endCommentGroup");
240    this->SkCanvas::endCommentGroup();
241}
242
243void LoggingCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint)
244{
245    AutoLogger logger(this);
246    RefPtr<JSONObject> params = logger.logItemWithParams("drawDRRect");
247    params->setObject("outer", objectForSkRRect(outer));
248    params->setObject("inner", objectForSkRRect(inner));
249    params->setObject("paint", objectForSkPaint(paint));
250    this->SkCanvas::onDrawDRRect(outer, inner, paint);
251}
252
253void LoggingCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint)
254{
255    AutoLogger logger(this);
256    RefPtr<JSONObject> params = logger.logItemWithParams("drawText");
257    params->setString("text", stringForText(text, byteLength, paint));
258    params->setNumber("x", x);
259    params->setNumber("y", y);
260    params->setObject("paint", objectForSkPaint(paint));
261    this->SkCanvas::onDrawText(text, byteLength, x, y, paint);
262}
263
264void LoggingCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint)
265{
266    AutoLogger logger(this);
267    RefPtr<JSONObject> params = logger.logItemWithParams("drawPosText");
268    params->setString("text", stringForText(text, byteLength, paint));
269    size_t pointsCount = paint.countText(text, byteLength);
270    params->setArray("pos", arrayForSkPoints(pointsCount, pos));
271    params->setObject("paint", objectForSkPaint(paint));
272    this->SkCanvas::onDrawPosText(text, byteLength, pos, paint);
273}
274
275void LoggingCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint)
276{
277    AutoLogger logger(this);
278    RefPtr<JSONObject> params = logger.logItemWithParams("drawPosTextH");
279    params->setString("text", stringForText(text, byteLength, paint));
280    size_t pointsCount = paint.countText(text, byteLength);
281    params->setArray("xpos", arrayForSkScalars(pointsCount, xpos));
282    params->setNumber("constY", constY);
283    params->setObject("paint", objectForSkPaint(paint));
284    this->SkCanvas::onDrawPosTextH(text, byteLength, xpos, constY, paint);
285}
286
287void LoggingCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint)
288{
289    AutoLogger logger(this);
290    RefPtr<JSONObject> params = logger.logItemWithParams("drawTextOnPath");
291    params->setString("text", stringForText(text, byteLength, paint));
292    params->setObject("path", objectForSkPath(path));
293    params->setArray("matrix", arrayForSkMatrix(*matrix));
294    params->setObject("paint", objectForSkPaint(paint));
295    this->SkCanvas::onDrawTextOnPath(text, byteLength, path, matrix, paint);
296}
297
298void LoggingCanvas::onPushCull(const SkRect& cullRect)
299{
300    AutoLogger logger(this);
301    RefPtr<JSONObject> params = logger.logItemWithParams("pushCull");
302    params->setObject("cullRect", objectForSkRect(cullRect));
303    this->SkCanvas::onPushCull(cullRect);
304}
305
306void LoggingCanvas::onPopCull()
307{
308    AutoLogger logger(this);
309    logger.logItem("popCull");
310    this->SkCanvas::onPopCull();
311}
312
313void LoggingCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle style)
314{
315    AutoLogger logger(this);
316    RefPtr<JSONObject> params = logger.logItemWithParams("clipRect");
317    params->setObject("rect", objectForSkRect(rect));
318    params->setString("SkRegion::Op", regionOpName(op));
319    params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
320    this->SkCanvas::onClipRect(rect, op, style);
321}
322
323void LoggingCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle style)
324{
325    AutoLogger logger(this);
326    RefPtr<JSONObject> params = logger.logItemWithParams("clipRRect");
327    params->setObject("rrect", objectForSkRRect(rrect));
328    params->setString("SkRegion::Op", regionOpName(op));
329    params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
330    this->SkCanvas::onClipRRect(rrect, op, style);
331}
332
333void LoggingCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle style)
334{
335    AutoLogger logger(this);
336    RefPtr<JSONObject> params = logger.logItemWithParams("clipPath");
337    params->setObject("path", objectForSkPath(path));
338    params->setString("SkRegion::Op", regionOpName(op));
339    params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
340    this->SkCanvas::onClipPath(path, op, style);
341}
342
343void LoggingCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op)
344{
345    AutoLogger logger(this);
346    RefPtr<JSONObject> params = logger.logItemWithParams("clipRegion");
347    params->setString("op", regionOpName(op));
348    this->SkCanvas::onClipRegion(region, op);
349}
350
351void LoggingCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint)
352{
353    AutoLogger logger(this);
354    logger.logItemWithParams("drawPicture")->setObject("picture", objectForSkPicture(*picture));
355    this->SkCanvas::onDrawPicture(picture, matrix, paint);
356}
357
358void LoggingCanvas::didSetMatrix(const SkMatrix& matrix)
359{
360    AutoLogger logger(this);
361    RefPtr<JSONObject> params = logger.logItemWithParams("setMatrix");
362    params->setArray("matrix", arrayForSkMatrix(matrix));
363    this->SkCanvas::didSetMatrix(matrix);
364}
365
366void LoggingCanvas::didConcat(const SkMatrix& matrix)
367{
368    AutoLogger logger(this);
369    RefPtr<JSONObject> params;
370
371    switch (matrix.getType()) {
372    case SkMatrix::kTranslate_Mask:
373        params = logger.logItemWithParams("translate");
374        params->setNumber("dx", matrix.getTranslateX());
375        params->setNumber("dy", matrix.getTranslateY());
376        break;
377
378    case SkMatrix::kScale_Mask:
379        params = logger.logItemWithParams("scale");
380        params->setNumber("scaleX", matrix.getScaleX());
381        params->setNumber("scaleY", matrix.getScaleY());
382        break;
383
384    default:
385        params = logger.logItemWithParams("concat");
386        params->setArray("matrix", arrayForSkMatrix(matrix));
387    }
388    this->SkCanvas::didConcat(matrix);
389}
390
391void LoggingCanvas::willSave()
392{
393    AutoLogger logger(this);
394    RefPtr<JSONObject> params = logger.logItem("save");
395    this->SkCanvas::willSave();
396}
397
398SkCanvas::SaveLayerStrategy LoggingCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags)
399{
400    AutoLogger logger(this);
401    RefPtr<JSONObject> params = logger.logItemWithParams("saveLayer");
402    if (bounds)
403        params->setObject("bounds", objectForSkRect(*bounds));
404    params->setObject("paint", objectForSkPaint(*paint));
405    params->setString("saveFlags", saveFlagsToString(flags));
406    return this->SkCanvas::willSaveLayer(bounds, paint, flags);
407}
408
409void LoggingCanvas::willRestore()
410{
411    AutoLogger logger(this);
412    logger.logItem("restore");
413    this->SkCanvas::willRestore();
414}
415
416PassRefPtr<JSONArray> LoggingCanvas::log()
417{
418    return m_log;
419}
420
421PassRefPtr<JSONObject> LoggingCanvas::objectForSkRect(const SkRect& rect)
422{
423    RefPtr<JSONObject> rectItem = JSONObject::create();
424    rectItem->setNumber("left", rect.left());
425    rectItem->setNumber("top", rect.top());
426    rectItem->setNumber("right", rect.right());
427    rectItem->setNumber("bottom", rect.bottom());
428    return rectItem.release();
429}
430
431PassRefPtr<JSONObject> LoggingCanvas::objectForSkIRect(const SkIRect& rect)
432{
433    RefPtr<JSONObject> rectItem = JSONObject::create();
434    rectItem->setNumber("left", rect.left());
435    rectItem->setNumber("top", rect.top());
436    rectItem->setNumber("right", rect.right());
437    rectItem->setNumber("bottom", rect.bottom());
438    return rectItem.release();
439}
440
441String LoggingCanvas::pointModeName(PointMode mode)
442{
443    switch (mode) {
444    case SkCanvas::kPoints_PointMode: return "Points";
445    case SkCanvas::kLines_PointMode: return "Lines";
446    case SkCanvas::kPolygon_PointMode: return "Polygon";
447    default:
448        ASSERT_NOT_REACHED();
449        return "?";
450    };
451}
452
453PassRefPtr<JSONObject> LoggingCanvas::objectForSkPoint(const SkPoint& point)
454{
455    RefPtr<JSONObject> pointItem = JSONObject::create();
456    pointItem->setNumber("x", point.x());
457    pointItem->setNumber("y", point.y());
458    return pointItem.release();
459}
460
461PassRefPtr<JSONArray> LoggingCanvas::arrayForSkPoints(size_t count, const SkPoint points[])
462{
463    RefPtr<JSONArray> pointsArrayItem = JSONArray::create();
464    for (size_t i = 0; i < count; ++i)
465        pointsArrayItem->pushObject(objectForSkPoint(points[i]));
466    return pointsArrayItem.release();
467}
468
469PassRefPtr<JSONObject> LoggingCanvas::objectForSkPicture(const SkPicture& picture)
470{
471    RefPtr<JSONObject> pictureItem = JSONObject::create();
472    pictureItem->setNumber("width", picture.width());
473    pictureItem->setNumber("height", picture.height());
474    return pictureItem.release();
475}
476
477PassRefPtr<JSONObject> LoggingCanvas::objectForRadius(const SkRRect& rrect, SkRRect::Corner corner)
478{
479    RefPtr<JSONObject> radiusItem = JSONObject::create();
480    SkVector radius = rrect.radii(corner);
481    radiusItem->setNumber("xRadius", radius.x());
482    radiusItem->setNumber("yRadius", radius.y());
483    return radiusItem.release();
484}
485
486String LoggingCanvas::rrectTypeName(SkRRect::Type type)
487{
488    switch (type) {
489    case SkRRect::kEmpty_Type: return "Empty";
490    case SkRRect::kRect_Type: return "Rect";
491    case SkRRect::kOval_Type: return "Oval";
492    case SkRRect::kSimple_Type: return "Simple";
493    case SkRRect::kNinePatch_Type: return "Nine-patch";
494    case SkRRect::kComplex_Type: return "Complex";
495    default:
496        ASSERT_NOT_REACHED();
497        return "?";
498    };
499}
500
501String LoggingCanvas::radiusName(SkRRect::Corner corner)
502{
503    switch (corner) {
504    case SkRRect::kUpperLeft_Corner: return "upperLeftRadius";
505    case SkRRect::kUpperRight_Corner: return "upperRightRadius";
506    case SkRRect::kLowerRight_Corner: return "lowerRightRadius";
507    case SkRRect::kLowerLeft_Corner: return "lowerLeftRadius";
508    default:
509        ASSERT_NOT_REACHED();
510        return "?";
511    }
512}
513
514PassRefPtr<JSONObject> LoggingCanvas::objectForSkRRect(const SkRRect& rrect)
515{
516    RefPtr<JSONObject> rrectItem = JSONObject::create();
517    rrectItem->setString("type", rrectTypeName(rrect.type()));
518    rrectItem->setNumber("left", rrect.rect().left());
519    rrectItem->setNumber("top", rrect.rect().top());
520    rrectItem->setNumber("right", rrect.rect().right());
521    rrectItem->setNumber("bottom", rrect.rect().bottom());
522    for (int i = 0; i < 4; ++i)
523        rrectItem->setObject(radiusName((SkRRect::Corner) i), objectForRadius(rrect, (SkRRect::Corner) i));
524    return rrectItem.release();
525}
526
527String LoggingCanvas::fillTypeName(SkPath::FillType type)
528{
529    switch (type) {
530    case SkPath::kWinding_FillType: return "Winding";
531    case SkPath::kEvenOdd_FillType: return "EvenOdd";
532    case SkPath::kInverseWinding_FillType: return "InverseWinding";
533    case SkPath::kInverseEvenOdd_FillType: return "InverseEvenOdd";
534    default:
535        ASSERT_NOT_REACHED();
536        return "?";
537    };
538}
539
540String LoggingCanvas::convexityName(SkPath::Convexity convexity)
541{
542    switch (convexity) {
543    case SkPath::kUnknown_Convexity: return "Unknown";
544    case SkPath::kConvex_Convexity: return "Convex";
545    case SkPath::kConcave_Convexity: return "Concave";
546    default:
547        ASSERT_NOT_REACHED();
548        return "?";
549    };
550}
551
552String LoggingCanvas::verbName(SkPath::Verb verb)
553{
554    switch (verb) {
555    case SkPath::kMove_Verb: return "Move";
556    case SkPath::kLine_Verb: return "Line";
557    case SkPath::kQuad_Verb: return "Quad";
558    case SkPath::kConic_Verb: return "Conic";
559    case SkPath::kCubic_Verb: return "Cubic";
560    case SkPath::kClose_Verb: return "Close";
561    case SkPath::kDone_Verb: return "Done";
562    default:
563        ASSERT_NOT_REACHED();
564        return "?";
565    };
566}
567
568LoggingCanvas::VerbParams LoggingCanvas::segmentParams(SkPath::Verb verb)
569{
570    switch (verb) {
571    case SkPath::kMove_Verb: return VerbParams("Move", 1, 0);
572    case SkPath::kLine_Verb: return VerbParams("Line", 1, 1);
573    case SkPath::kQuad_Verb: return VerbParams("Quad", 2, 1);
574    case SkPath::kConic_Verb: return VerbParams("Conic", 2, 1);
575    case SkPath::kCubic_Verb: return VerbParams("Cubic", 3, 1);
576    case SkPath::kClose_Verb: return VerbParams("Close", 0, 0);
577    case SkPath::kDone_Verb: return VerbParams("Done", 0, 0);
578    default:
579        ASSERT_NOT_REACHED();
580        return VerbParams("?", 0, 0);
581    };
582}
583
584PassRefPtr<JSONObject> LoggingCanvas::objectForSkPath(const SkPath& path)
585{
586    RefPtr<JSONObject> pathItem = JSONObject::create();
587    pathItem->setString("fillType", fillTypeName(path.getFillType()));
588    pathItem->setString("convexity", convexityName(path.getConvexity()));
589    pathItem->setBoolean("isRect", path.isRect(0));
590    SkPath::Iter iter(path, false);
591    SkPoint points[4];
592    RefPtr<JSONArray> pathPointsArray = JSONArray::create();
593    for (SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Verb; verb = iter.next(points, false)) {
594        VerbParams verbParams = segmentParams(verb);
595        RefPtr<JSONObject> pathPointItem = JSONObject::create();
596        pathPointItem->setString("verb", verbParams.name);
597        ASSERT(verbParams.pointCount + verbParams.pointOffset <= WTF_ARRAY_LENGTH(points));
598        pathPointItem->setArray("points", arrayForSkPoints(verbParams.pointCount, points + verbParams.pointOffset));
599        if (SkPath::kConic_Verb == verb)
600            pathPointItem->setNumber("conicWeight", iter.conicWeight());
601        pathPointsArray->pushObject(pathPointItem);
602    }
603    pathItem->setArray("pathPoints", pathPointsArray);
604    pathItem->setObject("bounds", objectForSkRect(path.getBounds()));
605    return pathItem.release();
606}
607
608String LoggingCanvas::colorTypeName(SkColorType colorType)
609{
610    switch (colorType) {
611    case kUnknown_SkColorType: return "None";
612    case kAlpha_8_SkColorType: return "A8";
613    case kIndex_8_SkColorType: return "Index8";
614    case kRGB_565_SkColorType: return "RGB565";
615    case kARGB_4444_SkColorType: return "ARGB4444";
616    case kN32_SkColorType: return "ARGB8888";
617    default:
618        ASSERT_NOT_REACHED();
619        return "?";
620    };
621}
622
623PassRefPtr<JSONObject> LoggingCanvas::objectForBitmapData(const SkBitmap& bitmap)
624{
625    RefPtr<JSONObject> dataItem = JSONObject::create();
626    Vector<unsigned char> output;
627    PNGImageEncoder::encode(bitmap, &output);
628    dataItem->setString("base64", WTF::base64Encode(reinterpret_cast<char*>(output.data()), output.size()));
629    dataItem->setString("mimeType", "image/png");
630    return dataItem.release();
631}
632
633PassRefPtr<JSONObject> LoggingCanvas::objectForSkBitmap(const SkBitmap& bitmap)
634{
635    RefPtr<JSONObject> bitmapItem = JSONObject::create();
636    bitmapItem->setNumber("width", bitmap.width());
637    bitmapItem->setNumber("height", bitmap.height());
638    bitmapItem->setString("config", colorTypeName(bitmap.colorType()));
639    bitmapItem->setBoolean("opaque", bitmap.isOpaque());
640    bitmapItem->setBoolean("immutable", bitmap.isImmutable());
641    bitmapItem->setBoolean("volatile", bitmap.isVolatile());
642    bitmapItem->setNumber("genID", bitmap.getGenerationID());
643    bitmapItem->setObject("data", objectForBitmapData(bitmap));
644    return bitmapItem.release();
645}
646
647PassRefPtr<JSONObject> LoggingCanvas::objectForSkShader(const SkShader& shader)
648{
649    RefPtr<JSONObject> shaderItem = JSONObject::create();
650    const SkMatrix localMatrix = shader.getLocalMatrix();
651    if (!localMatrix.isIdentity())
652        shaderItem->setArray("localMatrix", arrayForSkMatrix(localMatrix));
653    return shaderItem.release();
654}
655
656String LoggingCanvas::stringForSkColor(const SkColor& color)
657{
658    String colorString = "#";
659    appendUnsignedAsHex(color, colorString);
660    return colorString;
661}
662
663void LoggingCanvas::appendFlagToString(String* flagsString, bool isSet, const String& name)
664{
665    if (!isSet)
666        return;
667    if (flagsString->length())
668        flagsString->append("|");
669    flagsString->append(name);
670}
671
672String LoggingCanvas::stringForSkPaintFlags(const SkPaint& paint)
673{
674    if (!paint.getFlags())
675        return "none";
676    String flagsString = "";
677    appendFlagToString(&flagsString, paint.isAntiAlias(), "AntiAlias");
678    appendFlagToString(&flagsString, paint.isDither(), "Dither");
679    appendFlagToString(&flagsString, paint.isUnderlineText(), "UnderlinText");
680    appendFlagToString(&flagsString, paint.isStrikeThruText(), "StrikeThruText");
681    appendFlagToString(&flagsString, paint.isFakeBoldText(), "FakeBoldText");
682    appendFlagToString(&flagsString, paint.isLinearText(), "LinearText");
683    appendFlagToString(&flagsString, paint.isSubpixelText(), "SubpixelText");
684    appendFlagToString(&flagsString, paint.isDevKernText(), "DevKernText");
685    appendFlagToString(&flagsString, paint.isLCDRenderText(), "LCDRenderText");
686    appendFlagToString(&flagsString, paint.isEmbeddedBitmapText(), "EmbeddedBitmapText");
687    appendFlagToString(&flagsString, paint.isAutohinted(), "Autohinted");
688    appendFlagToString(&flagsString, paint.isVerticalText(), "VerticalText");
689    appendFlagToString(&flagsString, paint.getFlags() & SkPaint::kGenA8FromLCD_Flag, "GenA8FromLCD");
690    return flagsString;
691}
692
693String LoggingCanvas::filterLevelName(SkPaint::FilterLevel filterLevel)
694{
695    switch (filterLevel) {
696    case SkPaint::kNone_FilterLevel: return "None";
697    case SkPaint::kLow_FilterLevel: return "Low";
698    case SkPaint::kMedium_FilterLevel: return "Medium";
699    case SkPaint::kHigh_FilterLevel: return "High";
700    default:
701        ASSERT_NOT_REACHED();
702        return "?";
703    };
704}
705
706String LoggingCanvas::textAlignName(SkPaint::Align align)
707{
708    switch (align) {
709    case SkPaint::kLeft_Align: return "Left";
710    case SkPaint::kCenter_Align: return "Center";
711    case SkPaint::kRight_Align: return "Right";
712    default:
713        ASSERT_NOT_REACHED();
714        return "?";
715    };
716}
717
718String LoggingCanvas::strokeCapName(SkPaint::Cap cap)
719{
720    switch (cap) {
721    case SkPaint::kButt_Cap: return "Butt";
722    case SkPaint::kRound_Cap: return "Round";
723    case SkPaint::kSquare_Cap: return "Square";
724    default:
725        ASSERT_NOT_REACHED();
726        return "?";
727    };
728}
729
730String LoggingCanvas::strokeJoinName(SkPaint::Join join)
731{
732    switch (join) {
733    case SkPaint::kMiter_Join: return "Miter";
734    case SkPaint::kRound_Join: return "Round";
735    case SkPaint::kBevel_Join: return "Bevel";
736    default:
737        ASSERT_NOT_REACHED();
738        return "?";
739    };
740}
741
742String LoggingCanvas::styleName(SkPaint::Style style)
743{
744    switch (style) {
745    case SkPaint::kFill_Style: return "Fill";
746    case SkPaint::kStroke_Style: return "Stroke";
747    case SkPaint::kStrokeAndFill_Style: return "StrokeAndFill";
748    default:
749        ASSERT_NOT_REACHED();
750        return "?";
751    };
752}
753
754String LoggingCanvas::textEncodingName(SkPaint::TextEncoding encoding)
755{
756    switch (encoding) {
757    case SkPaint::kUTF8_TextEncoding: return "UTF-8";
758    case SkPaint::kUTF16_TextEncoding: return "UTF-16";
759    case SkPaint::kUTF32_TextEncoding: return "UTF-32";
760    case SkPaint::kGlyphID_TextEncoding: return "GlyphID";
761    default:
762        ASSERT_NOT_REACHED();
763        return "?";
764    };
765}
766
767String LoggingCanvas::hintingName(SkPaint::Hinting hinting)
768{
769    switch (hinting) {
770    case SkPaint::kNo_Hinting: return "None";
771    case SkPaint::kSlight_Hinting: return "Slight";
772    case SkPaint::kNormal_Hinting: return "Normal";
773    case SkPaint::kFull_Hinting: return "Full";
774    default:
775        ASSERT_NOT_REACHED();
776        return "?";
777    };
778}
779
780PassRefPtr<JSONObject> LoggingCanvas::objectForSkPaint(const SkPaint& paint)
781{
782    RefPtr<JSONObject> paintItem = JSONObject::create();
783    paintItem->setNumber("textSize", paint.getTextSize());
784    paintItem->setNumber("textScaleX", paint.getTextScaleX());
785    paintItem->setNumber("textSkewX", paint.getTextSkewX());
786    if (SkShader* shader = paint.getShader())
787        paintItem->setObject("shader", objectForSkShader(*shader));
788    paintItem->setString("color", stringForSkColor(paint.getColor()));
789    paintItem->setNumber("strokeWidth", paint.getStrokeWidth());
790    paintItem->setNumber("strokeMiter", paint.getStrokeMiter());
791    paintItem->setString("flags", stringForSkPaintFlags(paint));
792    paintItem->setString("filterLevel", filterLevelName(paint.getFilterLevel()));
793    paintItem->setString("textAlign", textAlignName(paint.getTextAlign()));
794    paintItem->setString("strokeCap", strokeCapName(paint.getStrokeCap()));
795    paintItem->setString("strokeJoin", strokeJoinName(paint.getStrokeJoin()));
796    paintItem->setString("styleName", styleName(paint.getStyle()));
797    paintItem->setString("textEncoding", textEncodingName(paint.getTextEncoding()));
798    paintItem->setString("hinting", hintingName(paint.getHinting()));
799    return paintItem.release();
800}
801
802PassRefPtr<JSONArray> LoggingCanvas::arrayForSkMatrix(const SkMatrix& matrix)
803{
804    RefPtr<JSONArray> matrixArray = JSONArray::create();
805    for (int i = 0; i < 9; ++i)
806        matrixArray->pushNumber(matrix[i]);
807    return matrixArray.release();
808}
809
810PassRefPtr<JSONArray> LoggingCanvas::arrayForSkScalars(size_t n, const SkScalar scalars[])
811{
812    RefPtr<JSONArray> scalarsArray = JSONArray::create();
813    for (size_t i = 0; i < n; ++i)
814        scalarsArray->pushNumber(scalars[i]);
815    return scalarsArray.release();
816}
817
818String LoggingCanvas::regionOpName(SkRegion::Op op)
819{
820    switch (op) {
821    case SkRegion::kDifference_Op: return "kDifference_Op";
822    case SkRegion::kIntersect_Op: return "kIntersect_Op";
823    case SkRegion::kUnion_Op: return "kUnion_Op";
824    case SkRegion::kXOR_Op: return "kXOR_Op";
825    case SkRegion::kReverseDifference_Op: return "kReverseDifference_Op";
826    case SkRegion::kReplace_Op: return "kReplace_Op";
827    default: return "Unknown type";
828    };
829}
830
831String LoggingCanvas::saveFlagsToString(SkCanvas::SaveFlags flags)
832{
833    String flagsString = "";
834    if (flags & SkCanvas::kHasAlphaLayer_SaveFlag)
835        flagsString.append("kHasAlphaLayer_SaveFlag ");
836    if (flags & SkCanvas::kFullColorLayer_SaveFlag)
837        flagsString.append("kFullColorLayer_SaveFlag ");
838    if (flags & SkCanvas::kClipToLayer_SaveFlag)
839        flagsString.append("kClipToLayer_SaveFlag ");
840    return flagsString;
841}
842
843String LoggingCanvas::textEncodingCanonicalName(SkPaint::TextEncoding encoding)
844{
845    String name = textEncodingName(encoding);
846    if (encoding == SkPaint::kUTF16_TextEncoding || encoding == SkPaint::kUTF32_TextEncoding)
847        name.append("LE");
848    return name;
849}
850
851String LoggingCanvas::stringForUTFText(const void* text, size_t length, SkPaint::TextEncoding encoding)
852{
853    return WTF::TextEncoding(textEncodingCanonicalName(encoding)).decode((const char*)text, length);
854}
855
856String LoggingCanvas::stringForText(const void* text, size_t byteLength, const SkPaint& paint)
857{
858    SkPaint::TextEncoding encoding = paint.getTextEncoding();
859    switch (encoding) {
860    case SkPaint::kUTF8_TextEncoding:
861    case SkPaint::kUTF16_TextEncoding:
862    case SkPaint::kUTF32_TextEncoding:
863        return stringForUTFText(text, byteLength, encoding);
864    case SkPaint::kGlyphID_TextEncoding: {
865        WTF::Vector<SkUnichar> dataVector(byteLength / 2);
866        SkUnichar* textData = dataVector.data();
867        paint.glyphsToUnichars(static_cast<const uint16_t*>(text), byteLength / 2, textData);
868        return WTF::UTF32LittleEndianEncoding().decode(reinterpret_cast<const char*>(textData), byteLength * 2);
869    }
870    default:
871        ASSERT_NOT_REACHED();
872        return "?";
873    }
874}
875
876} // namespace blink
877