From 0bd103497a21c2cb30fd5382cedb706cbf4941fa Mon Sep 17 00:00:00 2001 From: ethannicholas Date: Thu, 4 Feb 2016 06:45:25 -0800 Subject: [PATCH] Improved support for images/bitmaps in SkJSONCanvas 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 | 84 +++++++++++++++++++++++++++++++---- tools/json/SkJSONCanvas.h | 14 ++++++ tools/json/SkJSONRenderer.cpp | 70 +++++++++++++++++++++++++++-- 3 files changed, 155 insertions(+), 13 deletions(-) diff --git a/tools/json/SkJSONCanvas.cpp b/tools/json/SkJSONCanvas.cpp index 2272f88152..9361191c0a 100644 --- a/tools/json/SkJSONCanvas.cpp +++ b/tools/json/SkJSONCanvas.cpp @@ -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 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) { diff --git a/tools/json/SkJSONCanvas.h b/tools/json/SkJSONCanvas.h index b919efc66c..2e016df937 100644 --- a/tools/json/SkJSONCanvas.h +++ b/tools/json/SkJSONCanvas.h @@ -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" @@ -136,6 +138,18 @@ #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 diff --git a/tools/json/SkJSONRenderer.cpp b/tools/json/SkJSONRenderer.cpp index 734ce116b4..4049770c6e 100644 --- a/tools/json/SkJSONRenderer.cpp +++ b/tools/json/SkJSONRenderer.cpp @@ -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);