diff --git a/BUILD.gn b/BUILD.gn index ccfe13e94d4f..4ac1e9db14a8 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1548,6 +1548,16 @@ skia_component("skia") { defines += [ "SK_BUILD_FOR_DEBUGGER" ] } + if (skia_use_no_jpeg_encode) { + sources += skia_no_encode_jpeg_srcs + } + if (skia_use_no_png_encode) { + sources += skia_no_encode_png_srcs + } + if (skia_use_no_webp_encode) { + sources += skia_no_encode_webp_srcs + } + if (is_win) { sources += [ "src/ports/SkDebug_win.cpp", diff --git a/gn/skia.gni b/gn/skia.gni index e1d8678bfd88..b529317aff8c 100644 --- a/gn/skia.gni +++ b/gn/skia.gni @@ -59,11 +59,14 @@ declare_args() { skia_use_jpeg_gainmaps = is_skia_dev_build skia_use_libjpeg_turbo_decode = true skia_use_libjpeg_turbo_encode = true + skia_use_no_jpeg_encode = false skia_use_libjxl_decode = false skia_use_libpng_decode = true skia_use_libpng_encode = true + skia_use_no_png_encode = false skia_use_libwebp_decode = true skia_use_libwebp_encode = !is_wasm + skia_use_no_webp_encode = false skia_use_lua = is_skia_dev_build && !is_ios skia_use_metal = false skia_use_ndk_images = is_android && defined(ndk_api) && ndk_api >= 30 diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp index 60acba7c975b..b4d791fc022f 100644 --- a/modules/canvaskit/canvaskit_bindings.cpp +++ b/modules/canvaskit/canvaskit_bindings.cpp @@ -53,6 +53,9 @@ #include "include/effects/SkPerlinNoiseShader.h" #include "include/effects/SkRuntimeEffect.h" #include "include/effects/SkTrimPathEffect.h" +#include "include/encode/SkJpegEncoder.h" +#include "include/encode/SkPngEncoder.h" +#include "include/encode/SkWebpEncoder.h" #include "include/private/SkShadowFlags.h" #include "include/utils/SkParsePath.h" #include "include/utils/SkShadowUtils.h" @@ -917,6 +920,35 @@ sk_sp MakeImageFromGenerator(SimpleImageInfo ii, JSObject callbackObj) } #endif // CK_ENABLE_WEBGL + +static Uint8Array encodeImage(GrDirectContext* dContext, + sk_sp img, + SkEncodedImageFormat fmt, + int quality) { + sk_sp data = nullptr; + if (fmt == SkEncodedImageFormat::kJPEG) { + SkJpegEncoder::Options opts; + opts.fQuality = quality; + data = SkJpegEncoder::Encode(dContext, img.get(), opts); + } else if (fmt == SkEncodedImageFormat::kPNG) { + data = SkPngEncoder::Encode(dContext, img.get(), {}); + } else { + SkWebpEncoder::Options opts; + if (quality >= 100) { + opts.fCompression = SkWebpEncoder::Compression::kLossless; + opts.fQuality = 75; // This is effort to compress + } else { + opts.fCompression = SkWebpEncoder::Compression::kLossy; + opts.fQuality = quality; + } + data = SkWebpEncoder::Encode(dContext, img.get(), opts); + } + if (!data) { + return emscripten::val::null(); + } + return toBytes(data); +} + EMSCRIPTEN_BINDINGS(Skia) { #ifdef ENABLE_GPU constant("gpu", true); @@ -1502,22 +1534,14 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("_encodeToBytes", optional_override([](sk_sp self, SkEncodedImageFormat fmt, int quality) -> Uint8Array { - sk_sp data = self->encodeToData(fmt, quality); - if (!data) { - return emscripten::val::null(); - } - return toBytes(data); + return encodeImage(nullptr, self, fmt, quality); })) #if defined(ENABLE_GPU) .function("_encodeToBytes", optional_override([](sk_sp self, SkEncodedImageFormat fmt, int quality, GrDirectContext* dContext) -> Uint8Array { - sk_sp data = self->encodeToData(dContext, fmt, quality); - if (!data) { - return emscripten::val::null(); - } - return toBytes(data); + return encodeImage(dContext, self, fmt, quality); }), allow_raw_pointers()) #endif .function("makeCopyWithDefaultMipmaps", optional_override([](sk_sp self)->sk_sp { diff --git a/modules/canvaskit/compile.sh b/modules/canvaskit/compile.sh index a7df8c0fa026..df3cb42b5a3b 100755 --- a/modules/canvaskit/compile.sh +++ b/modules/canvaskit/compile.sh @@ -168,24 +168,33 @@ DO_DECODE="true" if [[ $@ == *no_codecs* ]]; then echo "Omitting codecs" DO_DECODE="false" - ENCODE_PNG="false" ENCODE_JPEG="false" + ENCODE_PNG="false" ENCODE_WEBP="false" + NO_ENCODE_JPEG="true" + NO_ENCODE_PNG="true" + NO_ENCODE_WEBP="true" else ENCODE_PNG="true" + NO_ENCODE_PNG="false" if [[ $@ == *no_encode_png* ]]; then ENCODE_PNG="false" + NO_ENCODE_PNG="true" fi ENCODE_JPEG="true" + NO_ENCODE_JPEG="false" if [[ $@ == *no_encode_jpeg* ]]; then ENCODE_JPEG="false" + NO_ENCODE_JPEG="true" fi ENCODE_WEBP="true" + NO_ENCODE_WEBP="false" if [[ $@ == *no_encode_webp* ]]; then ENCODE_WEBP="false" + NO_ENCODE_WEBP="true" fi fi # no_codecs @@ -213,10 +222,13 @@ echo "Compiling" skia_use_libheif=false \ skia_use_libjpeg_turbo_decode=${DO_DECODE} \ skia_use_libjpeg_turbo_encode=${ENCODE_JPEG} \ + skia_use_no_jpeg_encode=${NO_ENCODE_JPEG} \ skia_use_libpng_decode=${DO_DECODE} \ skia_use_libpng_encode=${ENCODE_PNG} \ + skia_use_no_png_encode=${NO_ENCODE_PNG} \ skia_use_libwebp_decode=${DO_DECODE} \ skia_use_libwebp_encode=${ENCODE_WEBP} \ + skia_use_no_webp_encode=${NO_ENCODE_WEBP} \ skia_use_lua=false \ skia_use_piex=false \ skia_use_system_freetype2=false \ diff --git a/modules/canvaskit/debugger_bindings.cpp b/modules/canvaskit/debugger_bindings.cpp index 243e9907f2b6..5024515f0344 100644 --- a/modules/canvaskit/debugger_bindings.cpp +++ b/modules/canvaskit/debugger_bindings.cpp @@ -14,6 +14,7 @@ #include "include/core/SkPicture.h" #include "include/core/SkString.h" #include "include/core/SkSurface.h" +#include "include/encode/SkPngEncoder.h" #include "include/utils/SkBase64.h" #include "src/core/SkPicturePriv.h" #include "src/utils/SkJSONWriter.h" @@ -255,7 +256,7 @@ class SkpDebugPlayer { // filenames like "\\1" in DrawImage commands. // Return type is the PNG data as a base64 encoded string with prepended URI. std::string getImageResource(int index) { - sk_sp pngData = fImages[index]->encodeToData(); + sk_sp pngData = SkPngEncoder::Encode(nullptr, fImages[index], {}); size_t len = SkBase64::Encode(pngData->data(), pngData->size(), nullptr); SkString dst; dst.resize(len); diff --git a/site/docs/user/api/skcanvas_creation.md b/site/docs/user/api/skcanvas_creation.md index 34f9812ab778..7831a7e71562 100644 --- a/site/docs/user/api/skcanvas_creation.md +++ b/site/docs/user/api/skcanvas_creation.md @@ -36,7 +36,7 @@ canvas commands are drawn. draw(rasterCanvas); sk_sp img(rasterSurface->makeImageSnapshot()); if (!img) { return; } - sk_sp png(img->encodeToData()); + sk_sp png = SkPngEncoder::Encode(nullptr, img, {}); if (!png) { return; } SkFILEWStream out(path); (void)out.write(png->data(), png->size()); @@ -88,9 +88,9 @@ current thread when Skia calls are made. // You've already created your OpenGL context and bound it. sk_sp interface = nullptr; // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current - // context in a platform-specific way. Alternatively, you may create your own GrGLInterface and - // initialize it however you like to attach to an alternate OpenGL implementation or intercept - // Skia's OpenGL calls. + // context in a platform-specific way. Alternatively, you may create your own GrGLInterface + // and initialize it however you like to attach to an alternate OpenGL implementation or + // intercept Skia's OpenGL calls. sk_sp context = GrDirectContext::MakeGL(interface); SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height); sk_sp gpuSurface( @@ -103,7 +103,8 @@ current thread when Skia calls are made. draw(gpuCanvas); sk_sp img(gpuSurface->makeImageSnapshot()); if (!img) { return; } - sk_sp png(img->encodeToData()); + // Must pass non-null context so the pixels can be read back and encoded. + sk_sp png = SkPngEncoder::Encode(context.get(), img, {}); if (!png) { return; } SkFILEWStream out(path); (void)out.write(png->data(), png->size());