add a system for building SkCanvas wrappers for testing

This adds a way to build a wrapping canvas for testing that is allowed
to manipulate the internal state of the canvas. It provides a way
to add friends to SkCanvas without having to change SkCanvas.

Change-Id: I40de8b236ba5acff45b3a8f7e440dcf6fa196fcf
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/524316
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
Herb Derby 2022-03-24 10:46:35 -04:00 committed by SkCQ
parent f3539b6c47
commit 3b238ceae9
6 changed files with 86 additions and 1 deletions

View File

@ -78,6 +78,7 @@ skia_utils_sources = [
"$_src/utils/SkShadowUtils.cpp", "$_src/utils/SkShadowUtils.cpp",
"$_src/utils/SkShaperJSONWriter.cpp", "$_src/utils/SkShaperJSONWriter.cpp",
"$_src/utils/SkShaperJSONWriter.h", "$_src/utils/SkShaperJSONWriter.h",
"$_src/utils/SkTestCanvas.h",
"$_src/utils/SkTextUtils.cpp", "$_src/utils/SkTextUtils.cpp",
"$_src/utils/SkThreadUtils_pthread.cpp", "$_src/utils/SkThreadUtils_pthread.cpp",
"$_src/utils/SkThreadUtils_win.cpp", "$_src/utils/SkThreadUtils_win.cpp",

View File

@ -2402,6 +2402,9 @@ private:
friend class SkPictureRecord; // predrawNotify (why does it need it? <reed>) friend class SkPictureRecord; // predrawNotify (why does it need it? <reed>)
friend class SkOverdrawCanvas; friend class SkOverdrawCanvas;
friend class SkRasterHandleAllocator; friend class SkRasterHandleAllocator;
template <typename Key>
friend class SkTestCanvas;
protected: protected:
// For use by SkNoDrawCanvas (via SkCanvasVirtualEnforcer, which can't be a friend) // For use by SkNoDrawCanvas (via SkCanvasVirtualEnforcer, which can't be a friend)
SkCanvas(const SkIRect& bounds); SkCanvas(const SkIRect& bounds);

View File

@ -16,6 +16,12 @@
class SK_API SkNWayCanvas : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> { class SK_API SkNWayCanvas : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {
public: public:
SkNWayCanvas(int width, int height); SkNWayCanvas(int width, int height);
#if SK_SUPPORT_GPU && GR_TEST_UTILS
// You can turn NWay canvas into a canvas a wrapper for a single canvas by passing the
// canvas.
SkNWayCanvas(SkCanvas*);
#endif
~SkNWayCanvas() override; ~SkNWayCanvas() override;
virtual void addCanvas(SkCanvas*); virtual void addCanvas(SkCanvas*);

View File

@ -56,6 +56,7 @@
#include "include/private/chromium/GrSlug.h" #include "include/private/chromium/GrSlug.h"
#include "src/gpu/BaseDevice.h" #include "src/gpu/BaseDevice.h"
#include "src/gpu/SkGr.h" #include "src/gpu/SkGr.h"
#include "src/utils/SkTestCanvas.h"
#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
# include "src/gpu/GrRenderTarget.h" # include "src/gpu/GrRenderTarget.h"
# include "src/gpu/GrRenderTargetProxy.h" # include "src/gpu/GrRenderTargetProxy.h"
@ -2874,4 +2875,28 @@ SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> all
return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr; return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
} }
/////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
#if SK_SUPPORT_GPU && GR_TEST_UTILS
SkTestCanvas<SkSlugTestKey>::SkTestCanvas(SkCanvas* convertCanvas)
: SkNWayCanvas(convertCanvas)
, fGPUCanvas(convertCanvas) { }
void SkTestCanvas<SkSlugTestKey>::onDrawGlyphRunList(
const SkGlyphRunList& glyphRunList, const SkPaint& paint) {
SkRect bounds = glyphRunList.sourceBounds();
if (this->internalQuickReject(bounds, paint)) {
return;
}
auto layer = this->aboutToDraw(this, paint, &bounds);
if (layer) {
if (glyphRunList.hasRSXForm()) {
fGPUCanvas->onDrawGlyphRunList(glyphRunList, layer->paint());
} else {
auto slug = fGPUCanvas->onConvertGlyphRunListToSlug(glyphRunList, layer->paint());
fGPUCanvas->drawSlug(slug.get());
}
}
}
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -8,14 +8,26 @@
#include "include/core/SkShader.h" #include "include/core/SkShader.h"
#include "include/utils/SkNWayCanvas.h" #include "include/utils/SkNWayCanvas.h"
#include "src/core/SkCanvasPriv.h" #include "src/core/SkCanvasPriv.h"
#include "src/core/SkDevice.h"
SkNWayCanvas::SkNWayCanvas(int width, int height) : INHERITED(width, height) {} SkNWayCanvas::SkNWayCanvas(int width, int height) : INHERITED(width, height) {}
#if SK_SUPPORT_GPU && GR_TEST_UTILS
SkNWayCanvas::SkNWayCanvas(SkCanvas* canvas) : INHERITED(sk_ref_sp(canvas->baseDevice())) {
this->addCanvas(canvas);
}
#endif
SkNWayCanvas::~SkNWayCanvas() { SkNWayCanvas::~SkNWayCanvas() {
this->removeAll(); this->removeAll();
} }
void SkNWayCanvas::addCanvas(SkCanvas* canvas) { void SkNWayCanvas::addCanvas(SkCanvas* canvas) {
if (!fList.isEmpty()) {
// We are using the nway canvas as a wrapper for the originally added canvas, and the device
// on the nway may contradict calls for the device on this canvas. So, to add a second
// canvas, the devices on the first canvas, and the nway base device must be different.
SkASSERT(fList[0]->baseDevice() != this->baseDevice());
}
if (canvas) { if (canvas) {
*fList.append() = canvas; *fList.append() = canvas;
} }

38
src/utils/SkTestCanvas.h Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// SkTestCanvas is a simple way to make a testing canvas which is allowed to use private
// facilities of SkCanvas without having to add a friend to SkCanvas.h.
//
// You create a Key (a simple empty struct) to make a template specialization class. You need to
// make a key for each of the different Canvases you need. The implementations of the canvases
// are in SkCanvas.cpp, which allows the use of helper classes.
#ifndef SkTestCanvas_DEFINED
#define SkTestCanvas_DEFINED
#include "include/core/SkSize.h"
#include "include/utils/SkNWayCanvas.h"
#include "src/core/SkDevice.h"
#include "src/core/SkGlyphRun.h"
// You can only make template specializations of SkTestCanvas.
template <typename Key> class SkTestCanvas;
// A test canvas to test using slug rendering instead of text blob rendering.
struct SkSlugTestKey {};
template <>
class SkTestCanvas<SkSlugTestKey> : public SkNWayCanvas {
public:
SkTestCanvas(SkCanvas* convertCanvas);
void onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) override;
private:
SkCanvas* fGPUCanvas;
};
#endif // SkTestCanvas_DEFINED