Improved support for images/bitmaps in SkJSONCanvas
authorethannicholas <ethannicholas@google.com>
Thu, 4 Feb 2016 14:45:25 +0000 (06:45 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 4 Feb 2016 14:45:25 +0000 (06:45 -0800)
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1662063003

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

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

index 2272f88..9361191 100644 (file)
@@ -181,15 +181,31 @@ static void flatten(const SkFlattenable* flattenable, Json::Value* target, bool
 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;
+        SkData* encoded = image.encode(SkImageEncoder::kPNG_Type, 100);
+        if (encoded == nullptr) {
+            // PNG encode doesn't necessarily support all color formats, convert to a different
+            // format
+            size_t rowBytes = 4 * image.width();
+            void* buffer = sk_malloc_throw(rowBytes * image.height());
+            SkImageInfo dstInfo = SkImageInfo::Make(image.width(), image.height(), 
+                                                    kN32_SkColorType, kPremul_SkAlphaType);
+            if (!image.readPixels(dstInfo, buffer, rowBytes, 0, 0)) {
+                SkDebugf("readPixels failed\n");
+                return false;
+            }
+            SkImage* converted = SkImage::NewRasterCopy(dstInfo, buffer, rowBytes);
+            encoded = converted->encode(SkImageEncoder::kPNG_Type, 100);
+            if (encoded == nullptr) {
+                SkDebugf("image encode failed\n");
+                return false;
+            }
+            free(converted);
+            free(buffer);
         }
         Json::Value bytes;
-        encode_data(png->data(), png->size(), &bytes);
+        encode_data(encoded->data(), encoded->size(), &bytes);
         (*target)[SKJSONCANVAS_ATTRIBUTE_BYTES] = bytes;
-        png->unref();
+        encoded->unref();
     }
     else {
         SkString description = SkStringPrintf("%dx%d pixel image", image.width(), image.height());
@@ -198,11 +214,50 @@ static bool SK_WARN_UNUSED_RESULT flatten(const SkImage& image, Json::Value* tar
     return true;
 }
 
+static const char* color_type_name(SkColorType colorType) {
+    switch (colorType) {
+        case kARGB_4444_SkColorType:
+            return SKJSONCANVAS_COLORTYPE_ARGB4444;
+        case kRGBA_8888_SkColorType:
+            return SKJSONCANVAS_COLORTYPE_RGBA8888;
+        case kBGRA_8888_SkColorType:
+            return SKJSONCANVAS_COLORTYPE_BGRA8888;
+        case kRGB_565_SkColorType:
+            return SKJSONCANVAS_COLORTYPE_565;
+        case kGray_8_SkColorType:
+            return SKJSONCANVAS_COLORTYPE_GRAY8;
+        case kIndex_8_SkColorType:
+            return SKJSONCANVAS_COLORTYPE_INDEX8;
+        case kAlpha_8_SkColorType:
+            return SKJSONCANVAS_COLORTYPE_ALPHA8;
+        default:
+            SkASSERT(false);
+            return SKJSONCANVAS_COLORTYPE_RGBA8888;
+    }
+}
+
+static const char* alpha_type_name(SkAlphaType alphaType) {
+    switch (alphaType) {
+        case kOpaque_SkAlphaType:
+            return SKJSONCANVAS_ALPHATYPE_OPAQUE;
+        case kPremul_SkAlphaType:
+            return SKJSONCANVAS_ALPHATYPE_PREMUL;
+        case kUnpremul_SkAlphaType:
+            return SKJSONCANVAS_ALPHATYPE_UNPREMUL;
+        default:
+            SkASSERT(false);
+            return SKJSONCANVAS_ALPHATYPE_OPAQUE;
+    }
+}
+
 static bool SK_WARN_UNUSED_RESULT flatten(const SkBitmap& bitmap, Json::Value* target, 
                                           bool sendBinaries) {
-    SkImage* image = SkImage::NewFromBitmap(bitmap);
+    bitmap.lockPixels();
+    SkAutoTUnref<SkImage> image(SkImage::NewFromBitmap(bitmap));
+    bitmap.unlockPixels();
+    (*target)[SKJSONCANVAS_ATTRIBUTE_COLOR] = Json::Value(color_type_name(bitmap.colorType()));
+    (*target)[SKJSONCANVAS_ATTRIBUTE_ALPHA] = Json::Value(alpha_type_name(bitmap.alphaType()));
     bool success = flatten(*image, target, sendBinaries);
-    image->unref();
     return success;
 }
 
@@ -361,6 +416,15 @@ static void apply_paint_xfermode(const SkPaint& paint, Json::Value* target, bool
     }
 }
 
+static void apply_paint_imagefilter(const SkPaint& paint, Json::Value* target, bool sendBinaries) {
+    SkFlattenable* imageFilter = paint.getImageFilter();
+    if (imageFilter != nullptr) {
+        Json::Value jsonImageFilter;
+        flatten(imageFilter, &jsonImageFilter, sendBinaries);
+        (*target)[SKJSONCANVAS_ATTRIBUTE_IMAGEFILTER] = jsonImageFilter;
+    }
+}
+
 Json::Value SkJSONCanvas::makePaint(const SkPaint& paint) {
     Json::Value result(Json::objectValue);
     store_scalar(&result, SKJSONCANVAS_ATTRIBUTE_STROKEWIDTH, paint.getStrokeWidth(), 0.0f);
@@ -379,6 +443,7 @@ Json::Value SkJSONCanvas::makePaint(const SkPaint& paint) {
     apply_paint_maskfilter(paint, &result, fSendBinaries);
     apply_paint_shader(paint, &result, fSendBinaries);
     apply_paint_xfermode(paint, &result, fSendBinaries);
+    apply_paint_imagefilter(paint, &result, fSendBinaries);
     return result;
 }
 
@@ -601,7 +666,7 @@ void SkJSONCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, c
         this->updateMatrix();
         Json::Value command(Json::objectValue);
         command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_BITMAPRECT);
-        command[SKJSONCANVAS_ATTRIBUTE_IMAGE] = encoded;
+        command[SKJSONCANVAS_ATTRIBUTE_BITMAP] = encoded;
         if (src != nullptr) {
             command[SKJSONCANVAS_ATTRIBUTE_SRC] = this->makeRect(*src);
         }
@@ -729,6 +794,7 @@ void SkJSONCanvas::willRestore() {
 }
 
 SkCanvas::SaveLayerStrategy SkJSONCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
+    this->updateMatrix();
     Json::Value command(Json::objectValue);
     command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_SAVELAYER);
     if (rec.fBounds != nullptr) {
index b919efc..2e016df 100644 (file)
@@ -58,6 +58,7 @@
 #define SKJSONCANVAS_ATTRIBUTE_PATH              "path"
 #define SKJSONCANVAS_ATTRIBUTE_TEXT              "text"
 #define SKJSONCANVAS_ATTRIBUTE_COLOR             "color"
+#define SKJSONCANVAS_ATTRIBUTE_ALPHA             "alpha"
 #define SKJSONCANVAS_ATTRIBUTE_STYLE             "style"
 #define SKJSONCANVAS_ATTRIBUTE_STROKEWIDTH       "strokeWidth"
 #define SKJSONCANVAS_ATTRIBUTE_STROKEMITER       "strokeMiter"
@@ -86,6 +87,7 @@
 #define SKJSONCANVAS_ATTRIBUTE_MASKFILTER        "maskFilter"
 #define SKJSONCANVAS_ATTRIBUTE_XFERMODE          "xfermode"
 #define SKJSONCANVAS_ATTRIBUTE_BACKDROP          "backdrop"
+#define SKJSONCANVAS_ATTRIBUTE_IMAGEFILTER       "imagefilter"
 #define SKJSONCANVAS_ATTRIBUTE_IMAGE             "image"
 #define SKJSONCANVAS_ATTRIBUTE_BITMAP            "bitmap"
 #define SKJSONCANVAS_ATTRIBUTE_SRC               "src"
 #define SKJSONCANVAS_CAP_ROUND                   "round"
 #define SKJSONCANVAS_CAP_SQUARE                  "square"
 
+#define SKJSONCANVAS_COLORTYPE_ARGB4444          "ARGB4444"
+#define SKJSONCANVAS_COLORTYPE_RGBA8888          "RGBA8888"
+#define SKJSONCANVAS_COLORTYPE_BGRA8888          "BGRA8888"
+#define SKJSONCANVAS_COLORTYPE_565               "565"
+#define SKJSONCANVAS_COLORTYPE_GRAY8             "Gray8"
+#define SKJSONCANVAS_COLORTYPE_INDEX8            "Index8"
+#define SKJSONCANVAS_COLORTYPE_ALPHA8            "Alpha8"
+
+#define SKJSONCANVAS_ALPHATYPE_OPAQUE            "opaque"
+#define SKJSONCANVAS_ALPHATYPE_PREMUL            "premul"
+#define SKJSONCANVAS_ALPHATYPE_UNPREMUL          "unpremul"
+
 /* 
  * 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 
index 734ce11..4049770 100644 (file)
@@ -159,6 +159,7 @@ static SkFlattenable* load_flattenable(Json::Value jsonFlattenable) {
     const char* name = jsonFlattenable[SKJSONCANVAS_ATTRIBUTE_NAME].asCString();
     SkFlattenable::Factory factory = SkFlattenable::NameToFactory(name);
     if (factory == nullptr) {
+        SkDebugf("no factory for loading '%s'\n", name);
         return nullptr;
     }
     void* data;
@@ -167,14 +168,56 @@ static SkFlattenable* load_flattenable(Json::Value jsonFlattenable) {
     SkFlattenable* result = factory(buffer);
     free(data);
     if (!buffer.isValid()) {
+        SkDebugf("invalid buffer loading flattenable\n");
         return nullptr;
     }
     return result;
 }
 
+static SkColorType colortype_from_name(const char* name) {
+    if (!strcmp(name, SKJSONCANVAS_COLORTYPE_ARGB4444)) {
+        return kARGB_4444_SkColorType;
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COLORTYPE_RGBA8888)) {
+        return kRGBA_8888_SkColorType;
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COLORTYPE_BGRA8888)) {
+        return kBGRA_8888_SkColorType;
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COLORTYPE_565)) {
+        return kRGB_565_SkColorType;
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COLORTYPE_GRAY8)) {
+        return kGray_8_SkColorType;
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COLORTYPE_INDEX8)) {
+        return kIndex_8_SkColorType;
+    }
+    else if (!strcmp(name, SKJSONCANVAS_COLORTYPE_ALPHA8)) {
+        return kAlpha_8_SkColorType;
+    }
+    SkASSERT(false);
+    return kN32_SkColorType;
+}
+
+static SkBitmap* convert_colortype(SkBitmap* bitmap, SkColorType colorType) {
+    if (bitmap->colorType() == colorType  ) {
+        return bitmap;
+    }
+    SkBitmap* dst = new SkBitmap();
+    if (bitmap->copyTo(dst, colorType)) {
+        delete bitmap;
+        return dst;
+    }
+    SkASSERT(false);
+    delete dst;
+    return bitmap;
+}
+
 // caller is responsible for freeing return value
-static SkBitmap* load_bitmap(Json::Value jsonBitmap) {
+static SkBitmap* load_bitmap(const Json::Value& jsonBitmap) {
     if (!jsonBitmap.isMember(SKJSONCANVAS_ATTRIBUTE_BYTES)) {
+        SkDebugf("invalid bitmap\n");
         return nullptr;
     }
     void* data;
@@ -187,20 +230,27 @@ static SkBitmap* load_bitmap(Json::Value jsonBitmap) {
     free(decoder);
     if (result != SkImageDecoder::kFailure) {
         free(data);
+        if (jsonBitmap.isMember(SKJSONCANVAS_ATTRIBUTE_COLOR)) {
+            const char* ctName = jsonBitmap[SKJSONCANVAS_ATTRIBUTE_COLOR].asCString();
+            SkColorType ct = colortype_from_name(ctName);
+            if (ct != kIndex_8_SkColorType) {
+                bitmap = convert_colortype(bitmap, ct);
+            }
+        }
         return bitmap;
     }
-    SkDebugf("image decode failed");
+    SkDebugf("image decode failed\n");
     free(data);
     return nullptr;
 }
 
-static SkImage* load_image(Json::Value jsonImage) {
+static SkImage* load_image(const Json::Value& jsonImage) {
     SkBitmap* bitmap = load_bitmap(jsonImage);
     if (bitmap == nullptr) {
         return nullptr;
     }
     SkImage* result = SkImage::NewFromBitmap(*bitmap);
-    free(bitmap);
+    delete bitmap;
     return result;
 }
 
@@ -248,6 +298,17 @@ static void apply_paint_xfermode(Json::Value& jsonPaint, SkPaint* target) {
     }
 }
 
+static void apply_paint_imagefilter(Json::Value& jsonPaint, SkPaint* target) {
+    if (jsonPaint.isMember(SKJSONCANVAS_ATTRIBUTE_IMAGEFILTER)) {
+        Json::Value jsonImageFilter = jsonPaint[SKJSONCANVAS_ATTRIBUTE_IMAGEFILTER];
+        SkImageFilter* imageFilter = (SkImageFilter*) load_flattenable(jsonImageFilter);
+        if (imageFilter != nullptr) {
+            target->setImageFilter(imageFilter);
+            imageFilter->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();
@@ -400,6 +461,7 @@ void Renderer::getPaint(Json::Value& command, SkPaint* result) {
     apply_paint_patheffect(jsonPaint, result);
     apply_paint_maskfilter(jsonPaint, result);
     apply_paint_xfermode(jsonPaint, result);
+    apply_paint_imagefilter(jsonPaint, result);
     apply_paint_style(jsonPaint, result);
     apply_paint_strokewidth(jsonPaint, result);
     apply_paint_strokemiter(jsonPaint, result);