diff --git a/gyp/canvas_state_lib.gyp b/gyp/canvas_state_lib.gyp new file mode 100644 index 0000000000..1bb2fe4a25 --- /dev/null +++ b/gyp/canvas_state_lib.gyp @@ -0,0 +1,37 @@ +# Building test for running CanvasState + +# HOW TO USE: +# This target is not included in normal Skia builds. In order to build it, +# you need to run gyp_skia on this file. This target also requires the +# variable skia_pic to be used during building: +# +# GYP_DEFINES=skia_pic=1 ./gyp_skia gyp/canvas_state_lib.gyp +# ninja -C out/Debug canvas_state_lib +# +# This will create the shared library libcanvas_state_lib.so. That can +# be passed to tests to test passing an SkCanvas between versions of +# Skia. See tests/CanvasStateTest.cpp for more info. +{ + 'targets' : [ + { + 'target_name' : 'canvas_state_lib', + 'type' : 'shared_library', + # FIXME: Is there a way to ensure that -fPIC was used for skia_lib? + 'dependencies' : [ 'skia_lib.gyp:skia_lib'], + 'sources' : [ + '../tests/CanvasStateHelpers.cpp', + ], + 'cflags' : [ + '-fPIC', + ], + }, + { + # Dummy 'most' target, since gyp_skia sets 'most' to be the default. + 'target_name' : 'most', + 'type' : 'none', + 'dependencies' : [ + 'canvas_state_lib', + ], + } + ], +} diff --git a/gyp/tests.gypi b/gyp/tests.gypi index 2d316c7f98..e574d16b87 100644 --- a/gyp/tests.gypi +++ b/gyp/tests.gypi @@ -46,6 +46,7 @@ '../tests/BlitRowTest.cpp', '../tests/BlurTest.cpp', '../tests/CachedDecodingPixelRefTest.cpp', + '../tests/CanvasStateHelpers.cpp', '../tests/CanvasStateTest.cpp', '../tests/CanvasTest.cpp', '../tests/ChecksumTest.cpp', diff --git a/src/utils/SkCanvasStateUtils.cpp b/src/utils/SkCanvasStateUtils.cpp index e286c9138f..a6806ddc7c 100644 --- a/src/utils/SkCanvasStateUtils.cpp +++ b/src/utils/SkCanvasStateUtils.cpp @@ -20,6 +20,11 @@ * ANY CHANGES TO THE STRUCTS BELOW THAT IMPACT THE ABI SHOULD RESULT IN A NEW * NEW SUBCLASS OF SkCanvasState. SUCH CHANGES SHOULD ONLY BE MADE IF ABSOLUTELY * NECESSARY! + * + * In order to test changes, run the CanvasState tests. gyp/canvas_state_lib.gyp + * describes how to create a library to pass to the CanvasState tests. The tests + * should succeed when building the library with your changes and passing that to + * the tests running in the unchanged Skia. */ enum RasterConfigs { kUnknown_RasterConfig = 0, diff --git a/tests/CanvasStateHelpers.cpp b/tests/CanvasStateHelpers.cpp new file mode 100644 index 0000000000..96972b8a1e --- /dev/null +++ b/tests/CanvasStateHelpers.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "CanvasStateHelpers.h" +#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG +#include "SkCanvas.h" +#include "SkCanvasStateUtils.h" +#include "SkPaint.h" +#include "SkRect.h" +#include "SkRegion.h" + +void complex_layers_draw(SkCanvas* canvas, float left, float top, + float right, float bottom, int32_t spacer) { + SkPaint bluePaint; + bluePaint.setColor(SK_ColorBLUE); + bluePaint.setStyle(SkPaint::kFill_Style); + + SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + canvas->drawRect(rect, bluePaint); + canvas->translate(0, rect.height() + spacer); + canvas->drawRect(rect, bluePaint); +} + +extern "C" bool complex_layers_draw_from_canvas_state(SkCanvasState* state, + float left, float top, float right, float bottom, int32_t spacer) { + SkCanvas* canvas = SkCanvasStateUtils::CreateFromCanvasState(state); + if (!canvas) { + return false; + } + complex_layers_draw(canvas, left, top, right, bottom, spacer); + canvas->unref(); + return true; +} + +void complex_clips_draw(SkCanvas* canvas, int32_t left, int32_t top, + int32_t right, int32_t bottom, int32_t clipOp, const SkRegion& localRegion) { + canvas->save(); + SkRect clipRect = SkRect::MakeLTRB(SkIntToScalar(left), SkIntToScalar(top), + SkIntToScalar(right), SkIntToScalar(bottom)); + canvas->clipRect(clipRect, (SkRegion::Op) clipOp); + canvas->drawColor(SK_ColorBLUE); + canvas->restore(); + + canvas->clipRegion(localRegion, (SkRegion::Op) clipOp); + canvas->drawColor(SK_ColorBLUE); +} + +extern "C" bool complex_clips_draw_from_canvas_state(SkCanvasState* state, + int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t clipOp, + int32_t regionRects, int32_t* rectCoords) { + SkCanvas* canvas = SkCanvasStateUtils::CreateFromCanvasState(state); + if (!canvas) { + return false; + } + + SkRegion localRegion; + for (int32_t i = 0; i < regionRects; ++i) { + localRegion.op(rectCoords[0], rectCoords[1], rectCoords[2], rectCoords[3], + SkRegion::kUnion_Op); + rectCoords += 4; + } + + complex_clips_draw(canvas, left, top, right, bottom, clipOp, localRegion); + canvas->unref(); + return true; +} +#endif // SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG diff --git a/tests/CanvasStateHelpers.h b/tests/CanvasStateHelpers.h new file mode 100644 index 0000000000..aa3c1781aa --- /dev/null +++ b/tests/CanvasStateHelpers.h @@ -0,0 +1,52 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef CanvasStateHelpers_DEFINED +#define CanvasStateHelpers_DEFINED + +#include "SkTypes.h" + +#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG +class SkCanvas; +class SkCanvasState; +class SkRegion; + +/* + * Helper function to perform drawing to an SkCanvas. Used by both + * test_complex_layers and complex_layers_draw_from_canvas_state. + */ +void complex_layers_draw(SkCanvas* canvas, float left, float top, + float right, float bottom, int32_t spacer); + +/* + * Create an SkCanvas from state and draw to it. Return true on success. + * + * Used by test_complex_layers test in CanvasStateTest. Marked as extern + * so it can be called from a separate library. + */ +extern "C" bool complex_layers_draw_from_canvas_state(SkCanvasState* state, + float left, float top, float right, float bottom, int32_t spacer); + +/* + * Helper function to perform drawing to an SkCanvas. Used both by test_complex_clips + * and complex_clips_draw_from_canvas_state. + */ +void complex_clips_draw(SkCanvas* canvas, int32_t left, int32_t top, + int32_t right, int32_t bottom, int32_t clipOp, const SkRegion& localRegion); + +/* + * Create an SkCanvas from state and draw to it. Return true on success. + * + * Used by test_complex_clips test in CanvasStateTest. Marked as extern + * so it can be called from a separate library. + */ +extern "C" bool complex_clips_draw_from_canvas_state(SkCanvasState* state, + int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t clipOp, + int32_t regionRects, int32_t* rectCoords); + +#endif // SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG +#endif // CanvasStateHelpers_DEFINED diff --git a/tests/CanvasStateTest.cpp b/tests/CanvasStateTest.cpp index eb84c11c7d..787f9afaec 100644 --- a/tests/CanvasStateTest.cpp +++ b/tests/CanvasStateTest.cpp @@ -5,8 +5,10 @@ * found in the LICENSE file. */ +#include "CanvasStateHelpers.h" #include "SkCanvas.h" #include "SkCanvasStateUtils.h" +#include "SkCommandLineFlags.h" #include "SkDrawFilter.h" #include "SkError.h" #include "SkPaint.h" @@ -14,8 +16,48 @@ #include "SkRect.h" #include "Test.h" -static void test_complex_layers(skiatest::Reporter* reporter) { +// dlopen and the library flag are only used for tests which require this flag. #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG +#include + +DEFINE_string(library, "", "Support library to use for CanvasState test. If empty (the default), " + "the test will be run without crossing a library boundary. Otherwise, " + "it is expected to be a full path to a shared library file, which will" + " be dynamically loaded. Functions from the library will be called to " + "test SkCanvasState. Instructions for generating the library are in " + "gyp/canvas_state_lib.gyp"); + + +// This class calls dlopen on the library passed in to the command line flag library, and handles +// calling dlclose when it goes out of scope. +class OpenLibResult { +public: + // If the flag library was passed to this run of the test, attempt to open it using dlopen and + // report whether it succeeded. + OpenLibResult(skiatest::Reporter* reporter) { + if (FLAGS_library.count() == 1) { + fHandle = dlopen(FLAGS_library[0], RTLD_LAZY | RTLD_LOCAL); + REPORTER_ASSERT_MESSAGE(reporter, fHandle != NULL, "Failed to open library!"); + } else { + fHandle = NULL; + } + } + + // Automatically call dlclose when going out of scope. + ~OpenLibResult() { + if (fHandle) { + dlclose(fHandle); + } + } + + // Pointer to the shared library object. + void* handle() { return fHandle; } + +private: + void* fHandle; +}; + +DEF_TEST(CanvasState_test_complex_layers, reporter) { const int WIDTH = 400; const int HEIGHT = 400; const int SPACER = 10; @@ -34,7 +76,22 @@ static void test_complex_layers(skiatest::Reporter* reporter) { SkCanvas::kARGB_NoClipLayer_SaveFlag }; REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags)); - const int layerCombinations = sizeof(layerAlpha) / sizeof(int); + + bool (*drawFn)(SkCanvasState* state, float l, float t, + float r, float b, int32_t s); + + OpenLibResult openLibResult(reporter); + if (openLibResult.handle() != NULL) { + *(void**) (&drawFn) = dlsym(openLibResult.handle(), + "complex_layers_draw_from_canvas_state"); + } else { + drawFn = complex_layers_draw_from_canvas_state; + } + + REPORTER_ASSERT(reporter, drawFn); + if (!drawFn) { + return; + } for (size_t i = 0; i < SK_ARRAY_COUNT(colorTypes); ++i) { SkBitmap bitmaps[2]; @@ -47,32 +104,28 @@ static void test_complex_layers(skiatest::Reporter* reporter) { canvas.drawColor(SK_ColorRED); - for (int k = 0; k < layerCombinations; ++k) { + for (size_t k = 0; k < SK_ARRAY_COUNT(layerAlpha); ++k) { // draw a rect within the layer's bounds and again outside the layer's bounds canvas.saveLayerAlpha(&rect, layerAlpha[k], flags[k]); - SkCanvasState* state = NULL; - SkCanvas* tmpCanvas = NULL; if (j) { - state = SkCanvasStateUtils::CaptureCanvasState(&canvas); + // Capture from the first Skia. + SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas); REPORTER_ASSERT(reporter, state); - tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state); - REPORTER_ASSERT(reporter, tmpCanvas); + + // And draw to it in the second Skia. + bool success = complex_layers_draw_from_canvas_state(state, + rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, SPACER); + REPORTER_ASSERT(reporter, success); + + // And release it in the *first* Skia. + SkCanvasStateUtils::ReleaseCanvasState(state); } else { - tmpCanvas = SkRef(&canvas); + // Draw in the first Skia. + complex_layers_draw(&canvas, rect.fLeft, rect.fTop, + rect.fRight, rect.fBottom, SPACER); } - SkPaint bluePaint; - bluePaint.setColor(SK_ColorBLUE); - bluePaint.setStyle(SkPaint::kFill_Style); - - tmpCanvas->drawRect(rect, bluePaint); - tmpCanvas->translate(0, rect.height() + SPACER); - tmpCanvas->drawRect(rect, bluePaint); - - tmpCanvas->unref(); - SkCanvasStateUtils::ReleaseCanvasState(state); - canvas.restore(); // translate the canvas for the next iteration @@ -86,13 +139,13 @@ static void test_complex_layers(skiatest::Reporter* reporter) { bitmaps[1].getPixels(), bitmaps[0].getSize())); } -#endif } +#endif //////////////////////////////////////////////////////////////////////////////// -static void test_complex_clips(skiatest::Reporter* reporter) { #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG +DEF_TEST(CanvasState_test_complex_clips, reporter) { const int WIDTH = 400; const int HEIGHT = 400; const int SPACER = 10; @@ -124,7 +177,23 @@ static void test_complex_clips(skiatest::Reporter* reporter) { SkCanvas::kARGB_NoClipLayer_SaveFlag, }; REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags)); - const int layerCombinations = sizeof(flags) / sizeof(SkCanvas::SaveFlags); + + bool (*drawFn)(SkCanvasState* state, int32_t l, int32_t t, + int32_t r, int32_t b, int32_t clipOp, + int32_t regionRects, int32_t* rectCoords); + + OpenLibResult openLibResult(reporter); + if (openLibResult.handle() != NULL) { + *(void**) (&drawFn) = dlsym(openLibResult.handle(), + "complex_clips_draw_from_canvas_state"); + } else { + drawFn = complex_clips_draw_from_canvas_state; + } + + REPORTER_ASSERT(reporter, drawFn); + if (!drawFn) { + return; + } SkBitmap bitmaps[2]; for (int i = 0; i < 2; ++i) { @@ -136,32 +205,35 @@ static void test_complex_clips(skiatest::Reporter* reporter) { SkRegion localRegion = clipRegion; - for (int j = 0; j < layerCombinations; ++j) { + for (size_t j = 0; j < SK_ARRAY_COUNT(flags); ++j) { SkRect layerBounds = SkRect::Make(layerRect); canvas.saveLayerAlpha(&layerBounds, 128, flags[j]); - SkCanvasState* state = NULL; - SkCanvas* tmpCanvas = NULL; if (i) { - state = SkCanvasStateUtils::CaptureCanvasState(&canvas); + SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas); REPORTER_ASSERT(reporter, state); - tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state); - REPORTER_ASSERT(reporter, tmpCanvas); + + SkRegion::Iterator iter(localRegion); + SkTDArray rectCoords; + for (; !iter.done(); iter.next()) { + const SkIRect& rect = iter.rect(); + *rectCoords.append() = rect.fLeft; + *rectCoords.append() = rect.fTop; + *rectCoords.append() = rect.fRight; + *rectCoords.append() = rect.fBottom; + } + bool success = drawFn(state, clipRect.fLeft, clipRect.fTop, + clipRect.fRight, clipRect.fBottom, clipOps[j], + rectCoords.count() / 4, rectCoords.begin()); + REPORTER_ASSERT(reporter, success); + + SkCanvasStateUtils::ReleaseCanvasState(state); } else { - tmpCanvas = SkRef(&canvas); + complex_clips_draw(&canvas, clipRect.fLeft, clipRect.fTop, + clipRect.fRight, clipRect.fBottom, clipOps[j], + localRegion); } - tmpCanvas->save(); - tmpCanvas->clipRect(SkRect::Make(clipRect), clipOps[j]); - tmpCanvas->drawColor(SK_ColorBLUE); - tmpCanvas->restore(); - - tmpCanvas->clipRegion(localRegion, clipOps[j]); - tmpCanvas->drawColor(SK_ColorBLUE); - - tmpCanvas->unref(); - SkCanvasStateUtils::ReleaseCanvasState(state); - canvas.restore(); // translate the canvas and region for the next iteration @@ -175,8 +247,8 @@ static void test_complex_clips(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(), bitmaps[1].getPixels(), bitmaps[0].getSize())); -#endif } +#endif //////////////////////////////////////////////////////////////////////////////// @@ -185,7 +257,7 @@ public: virtual bool filter(SkPaint*, Type) SK_OVERRIDE { return true; } }; -static void test_draw_filters(skiatest::Reporter* reporter) { +DEF_TEST(CanvasState_test_draw_filters, reporter) { TestDrawFilter drawFilter; SkBitmap bitmap; bitmap.allocN32Pixels(10, 10); @@ -210,7 +282,7 @@ static void test_draw_filters(skiatest::Reporter* reporter) { // we need this function to prevent SkError from printing to stdout static void error_callback(SkError code, void* ctx) {} -static void test_soft_clips(skiatest::Reporter* reporter) { +DEF_TEST(CanvasState_test_soft_clips, reporter) { SkBitmap bitmap; bitmap.allocN32Pixels(10, 10); SkCanvas canvas(bitmap); @@ -229,8 +301,8 @@ static void test_soft_clips(skiatest::Reporter* reporter) { SkClearLastError(); } -static void test_saveLayer_clip(skiatest::Reporter* reporter) { #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG +DEF_TEST(CanvasState_test_saveLayer_clip, reporter) { const int WIDTH = 100; const int HEIGHT = 100; const int LAYER_WIDTH = 50; @@ -261,13 +333,5 @@ static void test_saveLayer_clip(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, clipStackBounds.height() == LAYER_HEIGHT); canvas.restore(); +} #endif -} - -DEF_TEST(CanvasState, reporter) { - test_complex_layers(reporter); - test_complex_clips(reporter); - test_draw_filters(reporter); - test_soft_clips(reporter); - test_saveLayer_clip(reporter); -}