support for more features when rendering to/from JSON
authorethannicholas <ethannicholas@google.com>
Tue, 2 Feb 2016 16:36:58 +0000 (08:36 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 2 Feb 2016 16:36:58 +0000 (08:36 -0800)
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1662503003

Review URL: https://codereview.chromium.org/1662503003

gyp/json.gyp
tools/json/SkJSONCanvas.cpp
tools/json/SkJSONCanvas.h
tools/json/SkJSONRenderer.cpp

index 5b20b29..707e225 100644 (file)
@@ -16,6 +16,7 @@
       'include_dirs': [
         '../include/core',
         '../include/effects',
+        '../include/private',
         '../include/utils',
         '../src/core',
       ],
index 106b59f..efc4acd 100644 (file)
@@ -6,17 +6,20 @@
  */
 
 #include "SkJSONCanvas.h"
+#include "SkImageFilter.h"
 #include "SkMaskFilter.h"
 #include "SkPaintDefaults.h"
 #include "SkPath.h"
 #include "SkPathEffect.h"
 #include "SkRRect.h"
+#include "SkWriteBuffer.h"
 
-SkJSONCanvas::SkJSONCanvas(int width, int height, SkWStream& out) 
+SkJSONCanvas::SkJSONCanvas(int width, int height, SkWStream& out, bool sendBinaries
     : INHERITED(width, height)
     , fOut(out)
     , fRoot(Json::objectValue)
-    , fCommands(Json::arrayValue) {
+    , fCommands(Json::arrayValue) 
+    , fSendBinaries(sendBinaries) {
     fRoot[SKJSONCANVAS_VERSION] = Json::Value(1);
 }
 
@@ -148,8 +151,62 @@ void store_bool(Json::Value* target, const char* key, bool value, bool defaultVa
     }
 }
 
-Json::Value SkJSONCanvas::makePaint(const SkPaint& paint) {
-    Json::Value result(Json::objectValue);
+static void encode_data(const void* data, size_t count, Json::Value* target) {
+    // just use a brain-dead JSON array for now, switch to base64 or something else smarter down the
+    // road
+    for (size_t i = 0; i < count; i++) {
+        target->append(((const uint8_t*)data)[i]);
+    }
+}
+
+static void flatten(const SkFlattenable* flattenable, Json::Value* target, bool sendBinaries) {
+    if (sendBinaries) {
+        SkWriteBuffer buffer;
+        flattenable->flatten(buffer);
+        void* data = sk_malloc_throw(buffer.bytesWritten());
+        buffer.writeToMemory(data);
+        Json::Value bytes;
+        encode_data(data, buffer.bytesWritten(), &bytes);
+        Json::Value jsonFlattenable;
+        jsonFlattenable[SKJSONCANVAS_ATTRIBUTE_NAME] = Json::Value(flattenable->getTypeName());
+        jsonFlattenable[SKJSONCANVAS_ATTRIBUTE_BYTES] = bytes;
+        (*target) = jsonFlattenable;
+        free(data);
+    }
+    else {
+        (*target)[SKJSONCANVAS_ATTRIBUTE_DESCRIPTION] = Json::Value(flattenable->getTypeName());
+    }
+}
+
+static bool SK_WARN_UNUSED_RESULT flatten(const SkImage& image, Json::Value* target, 
+                                          bool sendBinaries) {
+    if (sendBinaries) {
+        SkData* png = image.encode(SkImageEncoder::kPNG_Type, 100);
+        if (png == nullptr) {
+            SkDebugf("could not encode image\n");
+            return false;
+        }
+        Json::Value bytes;
+        encode_data(png->data(), png->size(), &bytes);
+        (*target)[SKJSONCANVAS_ATTRIBUTE_BYTES] = bytes;
+        png->unref();
+    }
+    else {
+        SkString description = SkStringPrintf("%dx%d pixel image", image.width(), image.height());
+        (*target)[SKJSONCANVAS_ATTRIBUTE_DESCRIPTION] = Json::Value(description.c_str());
+    }
+    return true;
+}
+
+static bool SK_WARN_UNUSED_RESULT flatten(const SkBitmap& bitmap, Json::Value* target, 
+                                          bool sendBinaries) {
+    SkImage* image = SkImage::NewFromBitmap(bitmap);
+    bool success = flatten(*image, target, sendBinaries);
+    image->unref();
+    return success;
+}
+
+static void apply_paint_color(const SkPaint& paint, Json::Value* target) {
     SkColor color = paint.getColor();
     if (color != SK_ColorBLACK) {
         Json::Value colorValue(Json::arrayValue);
@@ -157,26 +214,50 @@ Json::Value SkJSONCanvas::makePaint(const SkPaint& paint) {
         colorValue.append(Json::Value(SkColorGetR(color)));
         colorValue.append(Json::Value(SkColorGetG(color)));
         colorValue.append(Json::Value(SkColorGetB(color)));
-        result[SKJSONCANVAS_ATTRIBUTE_COLOR] = colorValue;;
+        (*target)[SKJSONCANVAS_ATTRIBUTE_COLOR] = colorValue;;
     }
+}
+
+static void apply_paint_style(const SkPaint& paint, Json::Value* target) {
     SkPaint::Style style = paint.getStyle();
     if (style != SkPaint::kFill_Style) {
         switch (style) {
             case SkPaint::kStroke_Style: {
                 Json::Value stroke(SKJSONCANVAS_STYLE_STROKE);
-                result[SKJSONCANVAS_ATTRIBUTE_STYLE] = stroke;
+                (*target)[SKJSONCANVAS_ATTRIBUTE_STYLE] = stroke;
                 break;
             }
             case SkPaint::kStrokeAndFill_Style: {
                 Json::Value strokeAndFill(SKJSONCANVAS_STYLE_STROKEANDFILL);
-                result[SKJSONCANVAS_ATTRIBUTE_STYLE] = strokeAndFill;
+                (*target)[SKJSONCANVAS_ATTRIBUTE_STYLE] = strokeAndFill;
                 break;
             }
             default: SkASSERT(false);
         }
     }
-    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_STROKEWIDTH, paint.getStrokeWidth(), 0.0f);
-    store_bool(&result, SKJSONCANVAS_ATTRIBUTE_ANTIALIAS, paint.isAntiAlias(), false);
+}
+
+static void apply_paint_cap(const SkPaint& paint, Json::Value* target) {
+    SkPaint::Cap cap = paint.getStrokeCap();
+    if (cap != SkPaint::kDefault_Cap) {
+        switch (cap) {
+            case SkPaint::kButt_Cap: {
+                (*target)[SKJSONCANVAS_ATTRIBUTE_CAP] = Json::Value(SKJSONCANVAS_CAP_BUTT);
+                break;
+            }
+            case SkPaint::kRound_Cap: {
+                (*target)[SKJSONCANVAS_ATTRIBUTE_CAP] = Json::Value(SKJSONCANVAS_CAP_ROUND);
+                break;
+            }
+            case SkPaint::kSquare_Cap: {
+                (*target)[SKJSONCANVAS_ATTRIBUTE_CAP] = Json::Value(SKJSONCANVAS_CAP_SQUARE);
+                break;
+            }
+            default: SkASSERT(false);
+        }
+    }
+}
+static void apply_paint_maskfilter(const SkPaint& paint, Json::Value* target, bool sendBinaries) {
     SkMaskFilter* maskFilter = paint.getMaskFilter();
     if (maskFilter != nullptr) {
         SkMaskFilter::BlurRec blurRec;
@@ -209,13 +290,17 @@ Json::Value SkJSONCanvas::makePaint(const SkPaint& paint) {
                 default:
                     SkASSERT(false);
             }
-            result[SKJSONCANVAS_ATTRIBUTE_BLUR] = blur;
+            (*target)[SKJSONCANVAS_ATTRIBUTE_BLUR] = blur;
         }
         else {
-            SkDebugf("unimplemented: non-blur maskfilter");
-            SkASSERT(false);
+            Json::Value jsonMaskFilter;
+            flatten(maskFilter, &jsonMaskFilter, sendBinaries);
+            (*target)[SKJSONCANVAS_ATTRIBUTE_MASKFILTER] = jsonMaskFilter;
         }
     }
+}
+
+static void apply_paint_patheffect(const SkPaint& paint, Json::Value* target, bool sendBinaries) {
     SkPathEffect* pathEffect = paint.getPathEffect();
     if (pathEffect != nullptr) {
         SkPathEffect::DashInfo dashInfo;
@@ -231,31 +316,69 @@ Json::Value SkJSONCanvas::makePaint(const SkPaint& paint) {
             free(dashInfo.fIntervals);
             dashing[SKJSONCANVAS_ATTRIBUTE_INTERVALS] = intervals;
             dashing[SKJSONCANVAS_ATTRIBUTE_PHASE] = dashInfo.fPhase;
-            result[SKJSONCANVAS_ATTRIBUTE_DASHING] = dashing;
+            (*target)[SKJSONCANVAS_ATTRIBUTE_DASHING] = dashing;
         }
         else {
-            SkDebugf("unimplemented: non-dash patheffect");
-            SkASSERT(false);
+            Json::Value jsonPathEffect;
+            flatten(pathEffect, &jsonPathEffect, sendBinaries);
+            (*target)[SKJSONCANVAS_ATTRIBUTE_PATHEFFECT] = jsonPathEffect;
         }
     }
-    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_TEXTSIZE, paint.getTextSize(), 
-                 SkPaintDefaults_TextSize);
-    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_TEXTSCALEX, paint.getTextScaleX(), SK_Scalar1);
-    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_TEXTSCALEX, paint.getTextSkewX(), 0.0f);
+}
+    
+static void apply_paint_textalign(const SkPaint& paint, Json::Value* target) {
     SkPaint::Align textAlign = paint.getTextAlign();
     if (textAlign != SkPaint::kLeft_Align) {
         switch (textAlign) {
             case SkPaint::kCenter_Align: {
-                result[SKJSONCANVAS_ATTRIBUTE_TEXTALIGN] = SKJSONCANVAS_ALIGN_CENTER;
+                (*target)[SKJSONCANVAS_ATTRIBUTE_TEXTALIGN] = SKJSONCANVAS_ALIGN_CENTER;
                 break;
             }
             case SkPaint::kRight_Align: {
-                result[SKJSONCANVAS_ATTRIBUTE_TEXTALIGN] = SKJSONCANVAS_ALIGN_RIGHT;
+                (*target)[SKJSONCANVAS_ATTRIBUTE_TEXTALIGN] = SKJSONCANVAS_ALIGN_RIGHT;
                 break;
             }
             default: SkASSERT(false);
         }
     }
+}
+
+static void apply_paint_shader(const SkPaint& paint, Json::Value* target, bool sendBinaries) {
+    SkFlattenable* shader = paint.getShader();
+    if (shader != nullptr) {
+        Json::Value jsonShader;
+        flatten(shader, &jsonShader, sendBinaries);
+        (*target)[SKJSONCANVAS_ATTRIBUTE_SHADER] = jsonShader;
+    }
+}
+
+static void apply_paint_xfermode(const SkPaint& paint, Json::Value* target, bool sendBinaries) {
+    SkFlattenable* xfermode = paint.getXfermode();
+    if (xfermode != nullptr) {
+        Json::Value jsonXfermode;
+        flatten(xfermode, &jsonXfermode, sendBinaries);
+        (*target)[SKJSONCANVAS_ATTRIBUTE_XFERMODE] = jsonXfermode;
+    }
+}
+
+Json::Value SkJSONCanvas::makePaint(const SkPaint& paint) {
+    Json::Value result(Json::objectValue);
+    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_STROKEWIDTH, paint.getStrokeWidth(), 0.0f);
+    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_STROKEMITER, paint.getStrokeMiter(), 
+                 SkPaintDefaults_MiterLimit);
+    store_bool(&result, SKJSONCANVAS_ATTRIBUTE_ANTIALIAS, paint.isAntiAlias(), false);
+    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_TEXTSIZE, paint.getTextSize(), 
+                 SkPaintDefaults_TextSize);
+    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_TEXTSCALEX, paint.getTextScaleX(), SK_Scalar1);
+    store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_TEXTSCALEX, paint.getTextSkewX(), 0.0f);
+    apply_paint_color(paint, &result);
+    apply_paint_style(paint, &result);
+    apply_paint_cap(paint, &result);
+    apply_paint_textalign(paint, &result);
+    apply_paint_patheffect(paint, &result, fSendBinaries);
+    apply_paint_maskfilter(paint, &result, fSendBinaries);
+    apply_paint_shader(paint, &result, fSendBinaries);
+    apply_paint_xfermode(paint, &result, fSendBinaries);
     return result;
 }
 
@@ -403,13 +526,42 @@ void SkJSONCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
     fCommands.append(command);
 }
 
-void SkJSONCanvas::onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) {
-    SkDebugf("unsupported: drawImage\n");
+void SkJSONCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, 
+                               const SkPaint* paint) {
+    Json::Value encoded;
+    if (flatten(*image, &encoded, fSendBinaries)) {
+        this->updateMatrix();
+        Json::Value command(Json::objectValue);
+        command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_IMAGE);
+        command[SKJSONCANVAS_ATTRIBUTE_IMAGE] = encoded;
+        command[SKJSONCANVAS_ATTRIBUTE_COORDS] = this->makePoint(dx, dy);
+        if (paint != nullptr) {
+            command[SKJSONCANVAS_ATTRIBUTE_PAINT] = this->makePaint(*paint);
+        }
+        fCommands.append(command);
+    }
 }
 
-void SkJSONCanvas::onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                                   SkCanvas::SrcRectConstraint) {
-    SkDebugf("unsupported: drawImageRect\n");
+void SkJSONCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, 
+                                   const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) {
+    Json::Value encoded;
+    if (flatten(*image, &encoded, fSendBinaries)) {
+        this->updateMatrix();
+        Json::Value command(Json::objectValue);
+        command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_IMAGERECT);
+        command[SKJSONCANVAS_ATTRIBUTE_IMAGE] = encoded;
+        if (src != nullptr) {
+            command[SKJSONCANVAS_ATTRIBUTE_SRC] = this->makeRect(*src);
+        }
+        command[SKJSONCANVAS_ATTRIBUTE_DST] = this->makeRect(dst);
+        if (paint != nullptr) {
+            command[SKJSONCANVAS_ATTRIBUTE_PAINT] = this->makePaint(*paint);
+        }
+        if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
+            command[SKJSONCANVAS_ATTRIBUTE_STRICT] = Json::Value(true);
+        }
+        fCommands.append(command);
+    }
 }
 
 void SkJSONCanvas::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
@@ -417,13 +569,42 @@ void SkJSONCanvas::onDrawImageNine(const SkImage*, const SkIRect& center, const
     SkDebugf("unsupported: drawImageNine\n");
 }
 
-void SkJSONCanvas::onDrawBitmap(const SkBitmap&, SkScalar dx, SkScalar dy, const SkPaint*) {
-    SkDebugf("unsupported: drawBitmap\n");
+void SkJSONCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, 
+                                const SkPaint* paint) {
+    Json::Value encoded;
+    if (flatten(bitmap, &encoded, fSendBinaries)) {
+        this->updateMatrix();
+        Json::Value command(Json::objectValue);
+        command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_BITMAP);
+        command[SKJSONCANVAS_ATTRIBUTE_BITMAP] = encoded;
+        command[SKJSONCANVAS_ATTRIBUTE_COORDS] = this->makePoint(dx, dy);
+        if (paint != nullptr) {
+            command[SKJSONCANVAS_ATTRIBUTE_PAINT] = this->makePaint(*paint);
+        }
+        fCommands.append(command);
+    }
 }
 
-void SkJSONCanvas::onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
-                                    SkCanvas::SrcRectConstraint) {
-    SkDebugf("unsupported: drawBitmapRect\n");
+void SkJSONCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, 
+                                   const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) {
+    Json::Value encoded;
+    if (flatten(bitmap, &encoded, fSendBinaries)) {
+        this->updateMatrix();
+        Json::Value command(Json::objectValue);
+        command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_BITMAPRECT);
+        command[SKJSONCANVAS_ATTRIBUTE_IMAGE] = encoded;
+        if (src != nullptr) {
+            command[SKJSONCANVAS_ATTRIBUTE_SRC] = this->makeRect(*src);
+        }
+        command[SKJSONCANVAS_ATTRIBUTE_DST] = this->makeRect(dst);
+        if (paint != nullptr) {
+            command[SKJSONCANVAS_ATTRIBUTE_PAINT] = this->makePaint(*paint);
+        }
+        if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
+            command[SKJSONCANVAS_ATTRIBUTE_STRICT] = Json::Value(true);
+        }
+        fCommands.append(command);
+    }
 }
 
 void SkJSONCanvas::onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
@@ -537,3 +718,24 @@ void SkJSONCanvas::willRestore() {
     command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_RESTORE);
     fCommands.append(command);
 }
+
+SkCanvas::SaveLayerStrategy SkJSONCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
+    Json::Value command(Json::objectValue);
+    command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_SAVELAYER);
+    if (rec.fBounds != nullptr) {
+        command[SKJSONCANVAS_ATTRIBUTE_BOUNDS] = this->makeRect(*rec.fBounds);
+    }
+    if (rec.fPaint != nullptr) {
+        command[SKJSONCANVAS_ATTRIBUTE_PAINT] = this->makePaint(*rec.fPaint);
+    }
+    if (rec.fBackdrop != nullptr) {
+        Json::Value backdrop;
+        flatten(rec.fBackdrop, &backdrop, fSendBinaries);
+        command[SKJSONCANVAS_ATTRIBUTE_BACKDROP] = backdrop;
+    }
+    if (rec.fSaveLayerFlags != 0) {
+        SkDebugf("unsupported: saveLayer flags\n");
+    }
+    fCommands.append(command);
+    return this->INHERITED::getSaveLayerStrategy(rec);
+}
index 27cb398..76d1be0 100644 (file)
 #define SKJSONCANVAS_COMMAND_CLIPREGION          "ClipRegion"
 #define SKJSONCANVAS_COMMAND_SAVE                "Save"
 #define SKJSONCANVAS_COMMAND_RESTORE             "Restore"
+#define SKJSONCANVAS_COMMAND_SAVELAYER           "SaveLayer"
 
 #define SKJSONCANVAS_ATTRIBUTE_MATRIX            "matrix"
 #define SKJSONCANVAS_ATTRIBUTE_COORDS            "coords"
+#define SKJSONCANVAS_ATTRIBUTE_BOUNDS            "bounds"
 #define SKJSONCANVAS_ATTRIBUTE_PAINT             "paint"
 #define SKJSONCANVAS_ATTRIBUTE_OUTER             "outer"
 #define SKJSONCANVAS_ATTRIBUTE_INNER             "inner"
@@ -58,6 +60,8 @@
 #define SKJSONCANVAS_ATTRIBUTE_COLOR             "color"
 #define SKJSONCANVAS_ATTRIBUTE_STYLE             "style"
 #define SKJSONCANVAS_ATTRIBUTE_STROKEWIDTH       "strokeWidth"
+#define SKJSONCANVAS_ATTRIBUTE_STROKEMITER       "strokeMiter"
+#define SKJSONCANVAS_ATTRIBUTE_CAP               "cap"
 #define SKJSONCANVAS_ATTRIBUTE_ANTIALIAS         "antiAlias"
 #define SKJSONCANVAS_ATTRIBUTE_REGION            "region"
 #define SKJSONCANVAS_ATTRIBUTE_REGIONOP          "op"
 #define SKJSONCANVAS_ATTRIBUTE_PHASE             "phase"
 #define SKJSONCANVAS_ATTRIBUTE_FILLTYPE          "fillType"
 #define SKJSONCANVAS_ATTRIBUTE_VERBS             "verbs"
+#define SKJSONCANVAS_ATTRIBUTE_NAME              "name"
+#define SKJSONCANVAS_ATTRIBUTE_BYTES             "bytes"
+#define SKJSONCANVAS_ATTRIBUTE_SHADER            "shader"
+#define SKJSONCANVAS_ATTRIBUTE_PATHEFFECT        "pathEffect"
+#define SKJSONCANVAS_ATTRIBUTE_MASKFILTER        "maskFilter"
+#define SKJSONCANVAS_ATTRIBUTE_XFERMODE          "xfermode"
+#define SKJSONCANVAS_ATTRIBUTE_BACKDROP          "backdrop"
+#define SKJSONCANVAS_ATTRIBUTE_IMAGE             "image"
+#define SKJSONCANVAS_ATTRIBUTE_BITMAP            "bitmap"
+#define SKJSONCANVAS_ATTRIBUTE_SRC               "src"
+#define SKJSONCANVAS_ATTRIBUTE_DST               "dst"
+#define SKJSONCANVAS_ATTRIBUTE_STRICT            "strict"
+#define SKJSONCANVAS_ATTRIBUTE_DESCRIPTION       "description"
 
 #define SKJSONCANVAS_VERB_MOVE                   "move"
 #define SKJSONCANVAS_VERB_LINE                   "line"
 #define SKJSONCANVAS_FILLTYPE_INVERSEWINDING     "inverseWinding"
 #define SKJSONCANVAS_FILLTYPE_INVERSEEVENODD     "inverseEvenOdd"
 
+#define SKJSONCANVAS_CAP_BUTT                    "butt"
+#define SKJSONCANVAS_CAP_ROUND                   "round"
+#define SKJSONCANVAS_CAP_SQUARE                  "square"
+
 /* 
  * Implementation of SkCanvas which writes JSON when drawn to. The JSON describes all of the draw
  * commands issued to the canvas, and can later be turned back into draw commands using 
 class SkJSONCanvas : public SkCanvas {
 public:
     /* Create a canvas which writes to the specified output stream. */
-    SkJSONCanvas(int width, int height, SkWStream& out);
+    SkJSONCanvas(int width, int height, SkWStream& out, bool sendBinaries = false);
 
     /* Complete the JSON document. */
     void finish();
@@ -202,6 +223,8 @@ public:
 
     void willRestore() override;
 
+    SkCanvas::SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override;
+
 private:
     Json::Value makePoint(const SkPoint& point);
 
@@ -231,6 +254,7 @@ private:
     SkMatrix    fLastMatrix;
     Json::Value fRoot;
     Json::Value fCommands;
+    bool        fSendBinaries;
 
     typedef SkCanvas INHERITED;
 };
index a830fe0..734ce11 100644 (file)
@@ -12,6 +12,7 @@
 #include "SkJSONCanvas.h"
 #include "SkJSONCPP.h"
 #include "SkPath.h"
+#include "SkValidatingReadBuffer.h"
 
 namespace SkJSONRenderer {
 
@@ -35,6 +36,8 @@ public:
 
     void processRestore(Json::Value& command, SkCanvas* target);
 
+    void processSaveLayer(Json::Value& command, SkCanvas* target);
+
     void processPaint(Json::Value& command, SkCanvas* target);
 
     void processRect(Json::Value& command, SkCanvas* target);
@@ -51,6 +54,14 @@ public:
 
     void processPoints(Json::Value& command, SkCanvas* target);
 
+    void processImage(Json::Value& command, SkCanvas* target);
+
+    void processImageRect(Json::Value& command, SkCanvas* target);
+
+    void processBitmap(Json::Value& command, SkCanvas* target);
+
+    void processBitmapRect(Json::Value& command, SkCanvas* target);
+
     void processClipRect(Json::Value& command, SkCanvas* target);
     
     void processClipRRect(Json::Value& command, SkCanvas* target);
@@ -70,6 +81,9 @@ void Renderer::processCommand(Json::Value& command, SkCanvas* target) {
     else if (!strcmp(name, SKJSONCANVAS_COMMAND_RESTORE)) {
         this->processRestore(command, target);
     }
+    else if (!strcmp(name, SKJSONCANVAS_COMMAND_SAVELAYER)) {
+        this->processSaveLayer(command, target);
+    }
     else if (!strcmp(name, SKJSONCANVAS_COMMAND_PAINT)) {
         this->processPaint(command, target);
     }
@@ -94,6 +108,18 @@ void Renderer::processCommand(Json::Value& command, SkCanvas* target) {
     else if (!strcmp(name, SKJSONCANVAS_COMMAND_POINTS)) {
         this->processPoints(command, target);
     }
+    else if (!strcmp(name, SKJSONCANVAS_COMMAND_IMAGE)) {
+        this->processImage(command, target);
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COMMAND_IMAGERECT)) {
+        this->processImageRect(command, target);
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COMMAND_BITMAP)) {
+        this->processBitmap(command, target);
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COMMAND_BITMAPRECT)) {
+        this->processBitmapRect(command, target);
+    }
     else if (!strcmp(name, SKJSONCANVAS_COMMAND_CLIPRECT)) {
         this->processClipRect(command, target);
     }
@@ -108,32 +134,171 @@ void Renderer::processCommand(Json::Value& command, SkCanvas* target) {
     }
 }
 
-void Renderer::getPaint(Json::Value& command, SkPaint* result) {
-    Json::Value jsonPaint = command[SKJSONCANVAS_ATTRIBUTE_PAINT];
+static void apply_paint_color(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_COLOR)) {
         Json::Value color = jsonPaint[SKJSONCANVAS_ATTRIBUTE_COLOR];
-        result->setColor(SkColorSetARGB(color[0].asInt(), color[1].asInt(), color[2].asInt(),
+        target->setColor(SkColorSetARGB(color[0].asInt(), color[1].asInt(), color[2].asInt(),
                          color[3].asInt()));
     }
+}
+
+// note that the caller is responsible for freeing the pointer
+static Json::ArrayIndex decode_data(Json::Value bytes, void** target) {
+    Json::ArrayIndex size = bytes.size();
+    *target = sk_malloc_throw(size);
+    for (Json::ArrayIndex i = 0; i < size; i++) {
+        ((uint8_t*) *target)[i] = bytes[i].asInt();
+    }
+    return size;
+}
+
+static SkFlattenable* load_flattenable(Json::Value jsonFlattenable) {
+    if (!jsonFlattenable.isMember(SKJSONCANVAS_ATTRIBUTE_NAME)) {
+        return nullptr;
+    }
+    const char* name = jsonFlattenable[SKJSONCANVAS_ATTRIBUTE_NAME].asCString();
+    SkFlattenable::Factory factory = SkFlattenable::NameToFactory(name);
+    if (factory == nullptr) {
+        return nullptr;
+    }
+    void* data;
+    int size = decode_data(jsonFlattenable[SKJSONCANVAS_ATTRIBUTE_BYTES], &data);
+    SkValidatingReadBuffer buffer(data, size);
+    SkFlattenable* result = factory(buffer);
+    free(data);
+    if (!buffer.isValid()) {
+        return nullptr;
+    }
+    return result;
+}
+
+// caller is responsible for freeing return value
+static SkBitmap* load_bitmap(Json::Value jsonBitmap) {
+    if (!jsonBitmap.isMember(SKJSONCANVAS_ATTRIBUTE_BYTES)) {
+        return nullptr;
+    }
+    void* data;
+    int size = decode_data(jsonBitmap[SKJSONCANVAS_ATTRIBUTE_BYTES], &data);
+    SkMemoryStream stream(data, size);
+    SkImageDecoder* decoder = SkImageDecoder::Factory(&stream);
+    SkBitmap* bitmap = new SkBitmap();
+    SkImageDecoder::Result result = decoder->decode(&stream, bitmap, 
+                                                    SkImageDecoder::kDecodePixels_Mode);
+    free(decoder);
+    if (result != SkImageDecoder::kFailure) {
+        free(data);
+        return bitmap;
+    }
+    SkDebugf("image decode failed");
+    free(data);
+    return nullptr;
+}
+
+static SkImage* load_image(Json::Value jsonImage) {
+    SkBitmap* bitmap = load_bitmap(jsonImage);
+    if (bitmap == nullptr) {
+        return nullptr;
+    }
+    SkImage* result = SkImage::NewFromBitmap(*bitmap);
+    free(bitmap);
+    return result;
+}
+
+static void apply_paint_shader(Json::Value& jsonPaint, SkPaint* target) {
+    if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_SHADER)) {
+        Json::Value jsonShader = jsonPaint[SKJSONCANVAS_ATTRIBUTE_SHADER];
+        SkShader* shader = (SkShader*) load_flattenable(jsonShader);
+        if (shader != nullptr) {
+            target->setShader(shader);
+            shader->unref();
+        }
+    }
+}
+
+static void apply_paint_patheffect(Json::Value& jsonPaint, SkPaint* target) {
+    if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_PATHEFFECT)) {
+        Json::Value jsonPathEffect = jsonPaint[SKJSONCANVAS_ATTRIBUTE_PATHEFFECT];
+        SkPathEffect* pathEffect = (SkPathEffect*) load_flattenable(jsonPathEffect);
+        if (pathEffect != nullptr) {
+            target->setPathEffect(pathEffect);
+            pathEffect->unref();
+        }
+    }
+}
+
+static void apply_paint_maskfilter(Json::Value& jsonPaint, SkPaint* target) {
+    if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_MASKFILTER)) {
+        Json::Value jsonMaskFilter = jsonPaint[SKJSONCANVAS_ATTRIBUTE_MASKFILTER];
+        SkMaskFilter* maskFilter = (SkMaskFilter*) load_flattenable(jsonMaskFilter);
+        if (maskFilter != nullptr) {
+            target->setMaskFilter(maskFilter);
+            maskFilter->unref();
+        }
+    }
+}
+
+static void apply_paint_xfermode(Json::Value& jsonPaint, SkPaint* target) {
+    if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_XFERMODE)) {
+        Json::Value jsonXfermode = jsonPaint[SKJSONCANVAS_ATTRIBUTE_XFERMODE];
+        SkXfermode* xfermode = (SkXfermode*) load_flattenable(jsonXfermode);
+        if (xfermode != nullptr) {
+            target->setXfermode(xfermode);
+            xfermode->unref();
+        }
+    }
+}
+
+static void apply_paint_style(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_STYLE)) {
         const char* style = jsonPaint[SKJSONCANVAS_ATTRIBUTE_STYLE].asCString();
         if (!strcmp(style, SKJSONCANVAS_STYLE_FILL)) {
-            result->setStyle(SkPaint::kFill_Style);
+            target->setStyle(SkPaint::kFill_Style);
         }
         else if (!strcmp(style, SKJSONCANVAS_STYLE_STROKE)) {
-            result->setStyle(SkPaint::kStroke_Style);
+            target->setStyle(SkPaint::kStroke_Style);
         }
         else if (!strcmp(style, SKJSONCANVAS_STYLE_STROKEANDFILL)) {
-            result->setStyle(SkPaint::kStrokeAndFill_Style);
+            target->setStyle(SkPaint::kStrokeAndFill_Style);
         }
     }
+}
+
+static void apply_paint_strokewidth(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_STROKEWIDTH)) {
         float strokeWidth = jsonPaint[SKJSONCANVAS_ATTRIBUTE_STROKEWIDTH].asFloat();
-        result->setStrokeWidth(strokeWidth);
+        target->setStrokeWidth(strokeWidth);
+    }    
+}
+
+static void apply_paint_strokemiter(Json::Value& jsonPaint, SkPaint* target) {
+    if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_STROKEMITER)) {
+        float strokeMiter = jsonPaint[SKJSONCANVAS_ATTRIBUTE_STROKEMITER].asFloat();
+        target->setStrokeMiter(strokeMiter);
+    }    
+}
+
+static void apply_paint_cap(Json::Value& jsonPaint, SkPaint* target) {
+    if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_CAP)) {
+        const char* cap = jsonPaint[SKJSONCANVAS_ATTRIBUTE_CAP].asCString();
+        if (!strcmp(cap, SKJSONCANVAS_CAP_BUTT)) {
+            target->setStrokeCap(SkPaint::kButt_Cap);
+        }
+        else if (!strcmp(cap, SKJSONCANVAS_CAP_ROUND)) {
+            target->setStrokeCap(SkPaint::kRound_Cap);
+        }
+        else if (!strcmp(cap, SKJSONCANVAS_CAP_SQUARE)) {
+            target->setStrokeCap(SkPaint::kSquare_Cap);
+        }
     }
+}
+
+static void apply_paint_antialias(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_ANTIALIAS)) {
-        result->setAntiAlias(jsonPaint[SKJSONCANVAS_ATTRIBUTE_ANTIALIAS].asBool());
+        target->setAntiAlias(jsonPaint[SKJSONCANVAS_ATTRIBUTE_ANTIALIAS].asBool());
     }
+}
+
+static void apply_paint_blur(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_BLUR)) {
         Json::Value blur = jsonPaint[SKJSONCANVAS_ATTRIBUTE_BLUR];
         SkScalar sigma = blur[SKJSONCANVAS_ATTRIBUTE_SIGMA].asFloat();
@@ -167,8 +332,11 @@ void Renderer::getPaint(Json::Value& command, SkPaint* result) {
             SkASSERT(false);
             flags = SkBlurMaskFilter::BlurFlags::kNone_BlurFlag;
         }
-        result->setMaskFilter(SkBlurMaskFilter::Create(style, sigma, flags));
+        target->setMaskFilter(SkBlurMaskFilter::Create(style, sigma, flags));
     }
+}
+
+static void apply_paint_dashing(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_DASHING)) {
         Json::Value dash = jsonPaint[SKJSONCANVAS_ATTRIBUTE_DASHING];
         Json::Value jsonIntervals = dash[SKJSONCANVAS_ATTRIBUTE_INTERVALS];
@@ -178,9 +346,12 @@ void Renderer::getPaint(Json::Value& command, SkPaint* result) {
             intervals[i] = jsonIntervals[i].asFloat();
         }
         SkScalar phase = dash[SKJSONCANVAS_ATTRIBUTE_PHASE].asFloat();
-        result->setPathEffect(SkDashPathEffect::Create(intervals, count, phase));
+        target->setPathEffect(SkDashPathEffect::Create(intervals, count, phase));
         free(intervals);
     }
+}
+
+static void apply_paint_textalign(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_TEXTALIGN)) {
         SkPaint::Align textAlign;
         const char* jsonAlign = jsonPaint[SKJSONCANVAS_ATTRIBUTE_TEXTALIGN].asCString();
@@ -197,22 +368,51 @@ void Renderer::getPaint(Json::Value& command, SkPaint* result) {
             SkASSERT(false);
             textAlign = SkPaint::kLeft_Align;
         }
-        result->setTextAlign(textAlign);
+        target->setTextAlign(textAlign);
     }
+}
+
+static void apply_paint_textsize(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_TEXTSIZE)) {
         float textSize = jsonPaint[SKJSONCANVAS_ATTRIBUTE_TEXTSIZE].asFloat();
-        result->setTextSize(textSize);
+        target->setTextSize(textSize);
     }
+}
+
+static void apply_paint_textscalex(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_TEXTSCALEX)) {
         float textScaleX = jsonPaint[SKJSONCANVAS_ATTRIBUTE_TEXTSCALEX].asFloat();
-        result->setTextScaleX(textScaleX);
+        target->setTextScaleX(textScaleX);
     }
+}
+
+static void apply_paint_textskewx(Json::Value& jsonPaint, SkPaint* target) {
     if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_TEXTSKEWX)) {
         float textSkewX = jsonPaint[SKJSONCANVAS_ATTRIBUTE_TEXTSKEWX].asFloat();
-        result->setTextSkewX(textSkewX);
+        target->setTextSkewX(textSkewX);
     }
 }
 
+void Renderer::getPaint(Json::Value& command, SkPaint* result) {
+    Json::Value jsonPaint = command[SKJSONCANVAS_ATTRIBUTE_PAINT];
+    apply_paint_color(jsonPaint, result);
+    apply_paint_shader(jsonPaint, result);
+    apply_paint_patheffect(jsonPaint, result);
+    apply_paint_maskfilter(jsonPaint, result);
+    apply_paint_xfermode(jsonPaint, result);
+    apply_paint_style(jsonPaint, result);
+    apply_paint_strokewidth(jsonPaint, result);
+    apply_paint_strokemiter(jsonPaint, result);
+    apply_paint_cap(jsonPaint, result);
+    apply_paint_antialias(jsonPaint, result);
+    apply_paint_blur(jsonPaint, result);
+    apply_paint_dashing(jsonPaint, result);
+    apply_paint_textalign(jsonPaint, result);
+    apply_paint_textsize(jsonPaint, result);
+    apply_paint_textscalex(jsonPaint, result);
+    apply_paint_textskewx(jsonPaint, result);
+}
+
 void Renderer::getRect(Json::Value& command, const char* name, SkRect* result) {
     Json::Value rect = command[name];
     result->set(rect[0].asFloat(), rect[1].asFloat(), rect[2].asFloat(), rect[3].asFloat());
@@ -330,6 +530,27 @@ void Renderer::processRestore(Json::Value& command, SkCanvas* target) {
     target->restore();
 }
 
+void Renderer::processSaveLayer(Json::Value& command, SkCanvas* target) {
+    SkCanvas::SaveLayerRec rec;
+    SkRect bounds;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_BOUNDS)) {
+        this->getRect(command, SKJSONCANVAS_ATTRIBUTE_BOUNDS, &bounds);
+        rec.fBounds = &bounds;
+    }
+    SkPaint paint;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_PAINT)) {
+        this->getPaint(command, &paint);
+        rec.fPaint = &paint;
+    }
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_BACKDROP)) {
+        rec.fBackdrop = (SkImageFilter*) load_flattenable(command[SKJSONCANVAS_ATTRIBUTE_BACKDROP]);
+    }
+    target->saveLayer(rec);
+    if (rec.fBackdrop != nullptr) {
+        rec.fBackdrop->unref();
+    }
+}
+
 void Renderer::processPaint(Json::Value& command, SkCanvas* target) {
     SkPaint paint;
     this->getPaint(command, &paint);
@@ -440,6 +661,114 @@ void Renderer::processClipPath(Json::Value& command, SkCanvas* target) {
                      command[SKJSONCANVAS_ATTRIBUTE_ANTIALIAS].asBool());
 }
 
+void Renderer::processImage(Json::Value& command, SkCanvas* target) {
+    SkImage* image = load_image(command[SKJSONCANVAS_ATTRIBUTE_IMAGE]);
+    if (image == nullptr) {
+        return;
+    }
+    Json::Value point = command[SKJSONCANVAS_ATTRIBUTE_COORDS];
+    SkPaint* paintPtr;
+    SkPaint paint;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_PAINT)) {
+        this->getPaint(command, &paint);
+        paintPtr = &paint;
+    }
+    else {
+        paintPtr = nullptr;
+    }
+    target->drawImage(image, point[0].asFloat(), point[1].asFloat(), paintPtr);
+    image->unref();
+}
+
+void Renderer::processImageRect(Json::Value& command, SkCanvas* target) {
+    SkImage* image = load_image(command[SKJSONCANVAS_ATTRIBUTE_IMAGE]);
+    if (image == nullptr) {
+        return;
+    }
+    SkRect dst;
+    this->getRect(command, SKJSONCANVAS_ATTRIBUTE_DST, &dst);
+    SkPaint* paintPtr;
+    SkPaint paint;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_PAINT)) {
+        this->getPaint(command, &paint);
+        paintPtr = &paint;
+    }
+    else {
+        paintPtr = nullptr;
+    }
+    SkCanvas::SrcRectConstraint constraint;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_STRICT) && 
+        command[SKJSONCANVAS_ATTRIBUTE_STRICT].asBool()) {
+        constraint = SkCanvas::kStrict_SrcRectConstraint;
+    }
+    else {
+        constraint = SkCanvas::kFast_SrcRectConstraint;
+    }
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_SRC)) {
+        SkRect src;
+        this->getRect(command, SKJSONCANVAS_ATTRIBUTE_SRC, &src);
+        target->drawImageRect(image, src, dst, paintPtr, constraint);
+    }
+    else {
+        target->drawImageRect(image, dst, paintPtr, constraint);
+    }
+    image->unref();
+}
+
+void Renderer::processBitmap(Json::Value& command, SkCanvas* target) {
+    SkImage* image = load_image(command[SKJSONCANVAS_ATTRIBUTE_BITMAP]);
+    if (image == nullptr) {
+        return;
+    }
+    Json::Value point = command[SKJSONCANVAS_ATTRIBUTE_COORDS];
+    SkPaint* paintPtr;
+    SkPaint paint;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_PAINT)) {
+        this->getPaint(command, &paint);
+        paintPtr = &paint;
+    }
+    else {
+        paintPtr = nullptr;
+    }
+    target->drawImage(image, point[0].asFloat(), point[1].asFloat(), paintPtr);
+    image->unref();
+}
+
+void Renderer::processBitmapRect(Json::Value& command, SkCanvas* target) {
+    SkBitmap* bitmap = load_bitmap(command[SKJSONCANVAS_ATTRIBUTE_BITMAP]);
+    if (bitmap == nullptr) {
+        return;
+    }
+    SkRect dst;
+    this->getRect(command, SKJSONCANVAS_ATTRIBUTE_DST, &dst);
+    SkPaint* paintPtr;
+    SkPaint paint;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_PAINT)) {
+        this->getPaint(command, &paint);
+        paintPtr = &paint;
+    }
+    else {
+        paintPtr = nullptr;
+    }
+    SkCanvas::SrcRectConstraint constraint;
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_STRICT) && 
+        command[SKJSONCANVAS_ATTRIBUTE_STRICT].asBool()) {
+        constraint = SkCanvas::kStrict_SrcRectConstraint;
+    }
+    else {
+        constraint = SkCanvas::kFast_SrcRectConstraint;
+    }
+    if (command.isMember(SKJSONCANVAS_ATTRIBUTE_SRC)) {
+        SkRect src;
+        this->getRect(command, SKJSONCANVAS_ATTRIBUTE_SRC, &src);
+        target->drawBitmapRect(*bitmap, src, dst, paintPtr, constraint);
+    }
+    else {
+        target->drawBitmapRect(*bitmap, dst, paintPtr, constraint);
+    }
+    free(bitmap);
+}
+
 void render(const char* json, SkCanvas* target) {
     Renderer renderer;
     Json::Reader reader;