support for more features when rendering to/from JSON
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
diff --git a/tools/json/SkJSONRenderer.cpp b/tools/json/SkJSONRenderer.cpp
index a830fe0..734ce11 100644
--- a/tools/json/SkJSONRenderer.cpp
+++ b/tools/json/SkJSONRenderer.cpp
@@ -12,6 +12,7 @@
 #include "SkJSONCanvas.h"
 #include "SkJSONCPP.h"
 #include "SkPath.h"
+#include "SkValidatingReadBuffer.h"
 
 namespace SkJSONRenderer {
 
@@ -35,6 +36,8 @@
 
     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 @@
 
     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 @@
     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 @@
     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::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 @@
             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 @@
             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 @@
             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 @@
     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 @@
                      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;