Roll external/skia 5ba50afeed4c..6a6de65fd933 (24 commits)
https://skia.googlesource.com/skia.git/+log/5ba50afeed4c..6a6de65fd933
If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://skia-autoroll.corp.goog/r/android-master-autoroll
Please CC nifong@google.com on the revert to ensure that a human
is aware of the problem.
To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug
Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
Test: Presubmit checks will test this change.
Exempt-From-Owner-Approval: The autoroll bot does not require owner approval.
Change-Id: I6827ae4c6f44ebd257196ae4930ccebf2e2ff9fc
diff --git a/Android.bp b/Android.bp
index a528fc0..d4f7a0e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -690,6 +690,7 @@
"src/gpu/geometry/GrPathUtils.cpp",
"src/gpu/geometry/GrQuad.cpp",
"src/gpu/geometry/GrQuadUtils.cpp",
+ "src/gpu/geometry/GrShape.cpp",
"src/gpu/geometry/GrStyledShape.cpp",
"src/gpu/gl/GrGLAssembleGLESInterfaceAutogen.cpp",
"src/gpu/gl/GrGLAssembleGLInterfaceAutogen.cpp",
diff --git a/DEPS b/DEPS
index 89049ca..2636277 100644
--- a/DEPS
+++ b/DEPS
@@ -10,11 +10,11 @@
"third_party/externals/angle2" : "https://chromium.googlesource.com/angle/angle.git@957417438089bfc9c263657c5b071dd264d38d50",
# Dawn requires jinja2 and markupsafe for the code generator, and glslang and shaderc for SPIRV compilation.
# When the Dawn revision is updated these should be updated from the Dawn DEPS as well.
- "third_party/externals/dawn" : "https://dawn.googlesource.com/dawn.git@2d79ef264eba950c255f62698b7fcb362b140495",
- "third_party/externals/glslang" : "https://chromium.googlesource.com/external/github.com/KhronosGroup/glslang@3f4e5c4563068f277141f5fb3d96ec02afc7ac95",
+ "third_party/externals/dawn" : "https://dawn.googlesource.com/dawn.git@754c161fd366eb87e9625b9ef4152c34ff9e5ce3",
+ "third_party/externals/glslang" : "https://chromium.googlesource.com/external/github.com/KhronosGroup/glslang@39281fb710c328998759a17132fd5a3dbe46dcf1",
"third_party/externals/jinja2" : "https://chromium.googlesource.com/chromium/src/third_party/jinja2@b41863e42637544c2941b574c7877d3e1f663e25",
"third_party/externals/markupsafe" : "https://chromium.googlesource.com/chromium/src/third_party/markupsafe@8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
- "third_party/externals/shaderc" : "https://chromium.googlesource.com/external/github.com/google/shaderc@ced9c72d005e3002a24683e1b94b7fb978b10144",
+ "third_party/externals/shaderc" : "https://chromium.googlesource.com/external/github.com/google/shaderc@41f271e6139ceb6a54457fb2da14571f66100a9a",
"third_party/externals/dng_sdk" : "https://android.googlesource.com/platform/external/dng_sdk.git@c8d0c9b1d16bfda56f15165d39e0ffa360a11123",
"third_party/externals/egl-registry" : "https://skia.googlesource.com/external/github.com/KhronosGroup/EGL-Registry@a0bca08de07c7d7651047bedc0b653cfaaa4f2ae",
"third_party/externals/expat" : "https://chromium.googlesource.com/external/github.com/libexpat/libexpat.git@e976867fb57a0cd87e3b0fe05d59e0ed63c6febb",
@@ -34,8 +34,8 @@
"third_party/externals/piex" : "https://android.googlesource.com/platform/external/piex.git@bb217acdca1cc0c16b704669dd6f91a1b509c406",
"third_party/externals/sdl" : "https://skia.googlesource.com/third_party/sdl@5d7cfcca344034aff9327f77fc181ae3754e7a90",
"third_party/externals/sfntly" : "https://chromium.googlesource.com/external/github.com/googlei18n/sfntly.git@b55ff303ea2f9e26702b514cf6a3196a2e3e2974",
- "third_party/externals/spirv-cross" : "https://chromium.googlesource.com/external/github.com/KhronosGroup/SPIRV-Cross@6637610b16aacfe43c77ad4060da62008a83cd12",
- "third_party/externals/spirv-headers" : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers.git@f8bf11a0253a32375c32cad92c841237b96696c0",
+ "third_party/externals/spirv-cross" : "https://chromium.googlesource.com/external/github.com/KhronosGroup/SPIRV-Cross@7e0295abf81cc939ecb2644c88592d77407d18d3",
+ "third_party/externals/spirv-headers" : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers.git@c0df742ec0b8178ad58c68cff3437ad4b6a06e26",
"third_party/externals/spirv-tools" : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Tools.git@e95fbfb1f509ad7a7fdfb72ac35fe412d72fc4a4",
"third_party/externals/swiftshader" : "https://swiftshader.googlesource.com/SwiftShader@7d6b5913c98c224b30ccd5a50e4d36226c69a983",
#"third_party/externals/v8" : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",
diff --git a/gm/runtimeshader.cpp b/gm/runtimeshader.cpp
index 930193b..24c5534 100644
--- a/gm/runtimeshader.cpp
+++ b/gm/runtimeshader.cpp
@@ -175,3 +175,65 @@
}
};
DEF_GM(return new ThresholdRT;)
+
+class SpiralRT : public skiagm::GM {
+ sk_sp<SkRuntimeEffect> fEffect;
+ float fSecs = 4; // so we get something interested when we're not animated
+
+ void onOnceBeforeDraw() override {
+ const char code[] = R"(
+ uniform float rad_scale;
+ uniform float2 in_center;
+ uniform float4 in_colors0;
+ uniform float4 in_colors1;
+
+ void main(float2 p, inout half4 color) {
+ float2 pp = p - in_center;
+ float radius = sqrt(dot(pp, pp));
+ radius = sqrt(radius);
+ float angle = atan(pp.y / pp.x);
+ float t = (angle + 3.1415926/2) / (3.1415926);
+ t += radius * rad_scale;
+ t = fract(t);
+ float4 m = in_colors0 * (1-t) + in_colors1 * t;
+ color = half4(m);
+ }
+ )";
+ auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
+ if (!effect) {
+ SkDebugf("runtime error %s\n", error.c_str());
+ }
+ fEffect = effect;
+ }
+
+ bool runAsBench() const override { return true; }
+
+ SkString onShortName() override { return SkString("spiral_rt"); }
+
+ SkISize onISize() override { return {512, 512}; }
+
+ void onDraw(SkCanvas* canvas) override {
+ struct {
+ float rad_scale;
+ SkV2 in_center;
+ SkV4 in_colors0;
+ SkV4 in_colors1;
+ } uni {
+ std::sin(fSecs / 2) / 5,
+ {256, 256}, // center
+ {1, 0, 0, 1}, // color0
+ {0, 1, 0, 1}, // color1
+ };
+
+ SkPaint paint;
+ paint.setShader(fEffect->makeShader(SkData::MakeWithCopy(&uni, sizeof(uni)),
+ nullptr, 0, nullptr, true));
+ canvas->drawRect({0, 0, 512, 512}, paint);
+ }
+
+ bool onAnimate(double nanos) override {
+ fSecs = nanos / (1000 * 1000 * 1000);
+ return true;
+ }
+};
+DEF_GM(return new SpiralRT;)
diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp
index 403405d..bb4f82c 100644
--- a/gm/wacky_yuv_formats.cpp
+++ b/gm/wacky_yuv_formats.cpp
@@ -1058,6 +1058,7 @@
case VK_FORMAT_R8G8B8A8_UNORM: return kRGBA_8888_SkColorType;
case VK_FORMAT_B8G8R8A8_UNORM: return kBGRA_8888_SkColorType;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return kRGBA_1010102_SkColorType;
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return kBGRA_1010102_SkColorType;
case VK_FORMAT_R16_SFLOAT: return kA16_float_SkColorType;
case VK_FORMAT_R16G16_SFLOAT: return kR16G16_float_SkColorType;
case VK_FORMAT_R16_UNORM: return kA16_unorm_SkColorType;
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index fb09b35..04e2f8f 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -355,6 +355,10 @@
# It'd be nice to fix these and turn this on:
"/wd5041", # out-of-line definition for constexpr static data member is
# not needed and is deprecated in C++17
+
+ # warning C4996: 'std::result_of_t': warning STL4014: std::result_of and std::result_of_t are
+ # deprecated in C++17. They are superseded by std::invoke_result and std::invoke_result_t.
+ "/wd4996",
]
} else {
cflags += [
diff --git a/gn/find_msvc.py b/gn/find_msvc.py
index ce44931..108d576 100755
--- a/gn/find_msvc.py
+++ b/gn/find_msvc.py
@@ -15,7 +15,7 @@
if sys.platform.startswith('win'):
default_dir = r'C:\Program Files (x86)\Microsoft Visual Studio'
for release in ['2019', '2017']:
- for version in ['Enterprise', 'Professional', 'Community', 'BuildTools']:
+ for version in ['Enterprise', 'Professional', 'Community', 'BuildTools', 'Preview']:
path = os.path.join(default_dir, release, version, 'VC')
if os.path.isdir(path):
return path
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 4cb2825..fa3472f 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -341,6 +341,8 @@
"$_src/gpu/geometry/GrQuadUtils.cpp",
"$_src/gpu/geometry/GrQuadUtils.h",
"$_src/gpu/geometry/GrRect.h",
+ "$_src/gpu/geometry/GrShape.cpp",
+ "$_src/gpu/geometry/GrShape.h",
"$_src/gpu/geometry/GrStyledShape.cpp",
"$_src/gpu/geometry/GrStyledShape.h",
"$_src/gpu/ops/GrAAConvexPathRenderer.cpp",
@@ -741,6 +743,8 @@
"$_src/gpu/d3d/GrD3DCaps.h",
"$_src/gpu/d3d/GrD3DCommandList.cpp",
"$_src/gpu/d3d/GrD3DCommandList.h",
+ "$_src/gpu/d3d/GrD3DDescriptorHeap.cpp",
+ "$_src/gpu/d3d/GrD3DDescriptorHeap.h",
"$_src/gpu/d3d/GrD3DGpu.cpp",
"$_src/gpu/d3d/GrD3DGpu.h",
"$_src/gpu/d3d/GrD3DOpsRenderPass.cpp",
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 5012568..e9dcb17 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -1493,9 +1493,74 @@
Verb autoClose(SkPoint pts[2]);
};
+private:
+ /** \class SkPath::RangeIter
+ Iterates through a raw range of path verbs, points, and conics. All values are returned
+ unaltered.
+
+ NOTE: This class will be moved into SkPathPriv once RangeIter is removed.
+ */
+ class RangeIter {
+ public:
+ RangeIter() = default;
+ RangeIter(const uint8_t* verbs, const SkPoint* points, const SkScalar* weights)
+ : fVerb(verbs), fPoints(points), fWeights(weights) {
+ SkDEBUGCODE(fInitialPoints = fPoints;)
+ }
+ void operator++() {
+ auto verb = static_cast<SkPathVerb>(*fVerb++);
+ fPoints += pts_advance_after_verb(verb);
+ if (verb == SkPathVerb::kConic) {
+ ++fWeights;
+ }
+ }
+ bool operator!=(const RangeIter& that) const {
+ return fVerb != that.fVerb;
+ }
+ std::tuple<SkPathVerb, const SkPoint*, const SkScalar*> operator*() const {
+ auto verb = static_cast<SkPathVerb>(*fVerb);
+ SkASSERT(verb != SkPathVerb::kDone);
+ // We provide the starting point for beziers by peeking backwards from the current
+ // point, which works fine as long as there is always a kMove before any geometry.
+ // (SkPath::injectMoveToIfNeeded should have guaranteed this to be the case.)
+ int backset = pts_backset_for_verb(verb);
+ SkASSERT(fPoints + backset >= fInitialPoints);
+ return {verb, fPoints + backset, fWeights};
+ }
+ private:
+ constexpr static int pts_advance_after_verb(SkPathVerb verb) {
+ switch (verb) {
+ case SkPathVerb::kMove: return 1;
+ case SkPathVerb::kLine: return 1;
+ case SkPathVerb::kQuad: return 2;
+ case SkPathVerb::kConic: return 2;
+ case SkPathVerb::kCubic: return 3;
+ case SkPathVerb::kClose: return 0;
+ case SkPathVerb::kDone: return 0;
+ }
+ SkUNREACHABLE;
+ }
+ constexpr static int pts_backset_for_verb(SkPathVerb verb) {
+ switch (verb) {
+ case SkPathVerb::kMove: return 0;
+ case SkPathVerb::kLine: return -1;
+ case SkPathVerb::kQuad: return -1;
+ case SkPathVerb::kConic: return -1;
+ case SkPathVerb::kCubic: return -1;
+ case SkPathVerb::kClose: return 0;
+ case SkPathVerb::kDone: return 0;
+ }
+ SkUNREACHABLE;
+ }
+ const uint8_t* fVerb = nullptr;
+ const SkPoint* fPoints = nullptr;
+ const SkScalar* fWeights = nullptr;
+ SkDEBUGCODE(const SkPoint* fInitialPoints = nullptr;)
+ };
+public:
+
/** \class SkPath::RawIter
- Iterates through verb array, and associated SkPoint array and conic weight.
- verb array, SkPoint array, and conic weight are returned unaltered.
+ Use Iter instead. This class will soon be removed and RangeIter will be made private.
*/
class SK_API RawIter {
public:
@@ -1521,9 +1586,7 @@
@param path SkPath to iterate
*/
- void setPath(const SkPath& path) {
- fRawIter.setPathRef(*path.fPathRef.get());
- }
+ void setPath(const SkPath&);
/** Returns next SkPath::Verb in verb array, and advances RawIter.
When verb array is exhausted, returns kDone_Verb.
@@ -1532,16 +1595,14 @@
@param pts storage for SkPoint data describing returned SkPath::Verb
@return next SkPath::Verb from verb array
*/
- Verb next(SkPoint pts[4]) {
- return (Verb) fRawIter.next(pts);
- }
+ Verb next(SkPoint[4]);
/** Returns next SkPath::Verb, but does not advance RawIter.
@return next SkPath::Verb from verb array
*/
Verb peek() const {
- return (Verb) fRawIter.peek();
+ return (fIter != fEnd) ? static_cast<Verb>(std::get<0>(*fIter)) : kDone_Verb;
}
/** Returns conic weight if next() returned kConic_Verb.
@@ -1552,11 +1613,13 @@
@return conic weight for conic SkPoint returned by next()
*/
SkScalar conicWeight() const {
- return fRawIter.conicWeight();
+ return fConicWeight;
}
private:
- SkPathRef::Iter fRawIter;
+ RangeIter fIter;
+ RangeIter fEnd;
+ SkScalar fConicWeight = 0;
friend class SkPath;
};
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index ffab392..5b6a7c9 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -56,7 +56,9 @@
};
enum Flags {
- kArray_Flag = 0x1,
+ kArray_Flag = 0x1,
+ kMarker_Flag = 0x2,
+ kMarkerNormals_Flag = 0x4,
};
SkString fName;
@@ -65,12 +67,15 @@
Type fType;
int fCount;
uint32_t fFlags;
+ uint32_t fMarker;
#if SK_SUPPORT_GPU
GrSLType fGPUType;
#endif
bool isArray() const { return SkToBool(fFlags & kArray_Flag); }
+ bool hasMarker() const { return SkToBool(fFlags & kMarker_Flag); }
+ bool hasNormalsMarker() const { return SkToBool(fFlags & kMarkerNormals_Flag); }
size_t sizeInBytes() const;
};
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index 0523f5d..03ac124 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -799,6 +799,7 @@
kRG_88,
kBGRA_8888,
kRGBA_1010102,
+ kBGRA_1010102,
kGray_8,
kAlpha_F16,
kRGBA_F16,
@@ -843,6 +844,7 @@
case GrColorType::kRG_88: return kR8G8_unorm_SkColorType;
case GrColorType::kBGRA_8888: return kBGRA_8888_SkColorType;
case GrColorType::kRGBA_1010102: return kRGBA_1010102_SkColorType;
+ case GrColorType::kBGRA_1010102: return kBGRA_1010102_SkColorType;
case GrColorType::kGray_8: return kGray_8_SkColorType;
case GrColorType::kAlpha_F16: return kA16_float_SkColorType;
case GrColorType::kRGBA_F16: return kRGBA_F16_SkColorType;
@@ -878,7 +880,7 @@
case kRGBA_F16_SkColorType: return GrColorType::kRGBA_F16;
case kRGBA_1010102_SkColorType: return GrColorType::kRGBA_1010102;
case kRGB_101010x_SkColorType: return GrColorType::kUnknown;
- case kBGRA_1010102_SkColorType: return GrColorType::kUnknown;
+ case kBGRA_1010102_SkColorType: return GrColorType::kBGRA_1010102;
case kBGR_101010x_SkColorType: return GrColorType::kUnknown;
case kRGBA_F32_SkColorType: return GrColorType::kRGBA_F32;
case kR8G8_unorm_SkColorType: return GrColorType::kRG_88;
@@ -909,6 +911,7 @@
case GrColorType::kRG_88: return kRG_SkColorChannelFlags;
case GrColorType::kBGRA_8888: return kRGBA_SkColorChannelFlags;
case GrColorType::kRGBA_1010102: return kRGBA_SkColorChannelFlags;
+ case GrColorType::kBGRA_1010102: return kRGBA_SkColorChannelFlags;
case GrColorType::kGray_8: return kGray_SkColorChannelFlag;
case GrColorType::kAlpha_F16: return kAlpha_SkColorChannelFlag;
case GrColorType::kRGBA_F16: return kRGBA_SkColorChannelFlags;
@@ -1041,6 +1044,8 @@
return GrColorTypeDesc::MakeRGBA(8, GrColorTypeEncoding::kUnorm);
case GrColorType::kRGBA_1010102:
return GrColorTypeDesc::MakeRGBA(10, 2, GrColorTypeEncoding::kUnorm);
+ case GrColorType::kBGRA_1010102:
+ return GrColorTypeDesc::MakeRGBA(10, 2, GrColorTypeEncoding::kUnorm);
case GrColorType::kGray_8:
return GrColorTypeDesc::MakeGray(8, GrColorTypeEncoding::kUnorm);
case GrColorType::kAlpha_F16:
@@ -1119,6 +1124,7 @@
case GrColorType::kRG_88: return 2;
case GrColorType::kBGRA_8888: return 4;
case GrColorType::kRGBA_1010102: return 4;
+ case GrColorType::kBGRA_1010102: return 4;
case GrColorType::kGray_8: return 1;
case GrColorType::kAlpha_F16: return 2;
case GrColorType::kRGBA_F16: return 8;
@@ -1210,6 +1216,7 @@
case GrColorType::kRG_88: return "kRG_88";
case GrColorType::kBGRA_8888: return "kBGRA_8888";
case GrColorType::kRGBA_1010102: return "kRGBA_1010102";
+ case GrColorType::kBGRA_1010102: return "kBGRA_1010102";
case GrColorType::kGray_8: return "kGray_8";
case GrColorType::kAlpha_F16: return "kAlpha_F16";
case GrColorType::kRGBA_F16: return "kRGBA_F16";
diff --git a/include/private/SkBitmaskEnum.h b/include/private/SkBitmaskEnum.h
index 71022b2..ca254a3 100644
--- a/include/private/SkBitmaskEnum.h
+++ b/include/private/SkBitmaskEnum.h
@@ -9,40 +9,40 @@
#include <type_traits>
-namespace skstd {
+namespace sknonstd {
template <typename T> struct is_bitmask_enum : std::false_type {};
template <typename E>
-typename std::enable_if<skstd::is_bitmask_enum<E>::value, bool>::type constexpr Any(E e) {
- return static_cast<typename std::underlying_type<E>::type>(e) != 0;
+std::enable_if_t<sknonstd::is_bitmask_enum<E>::value, bool> constexpr Any(E e) {
+ return static_cast<std::underlying_type_t<E>>(e) != 0;
}
-} // namespace skstd
+} // namespace sknonstd
template <typename E>
-typename std::enable_if<skstd::is_bitmask_enum<E>::value, E>::type constexpr operator|(E l, E r) {
- using U = typename std::underlying_type<E>::type;
+std::enable_if_t<sknonstd::is_bitmask_enum<E>::value, E> constexpr operator|(E l, E r) {
+ using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(l) | static_cast<U>(r));
}
template <typename E>
-typename std::enable_if<skstd::is_bitmask_enum<E>::value, E&>::type constexpr operator|=(E& l, E r) {
+std::enable_if_t<sknonstd::is_bitmask_enum<E>::value, E&> constexpr operator|=(E& l, E r) {
return l = l | r;
}
template <typename E>
-typename std::enable_if<skstd::is_bitmask_enum<E>::value, E>::type constexpr operator&(E l, E r) {
- using U = typename std::underlying_type<E>::type;
+std::enable_if_t<sknonstd::is_bitmask_enum<E>::value, E> constexpr operator&(E l, E r) {
+ using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(l) & static_cast<U>(r));
}
template <typename E>
-typename std::enable_if<skstd::is_bitmask_enum<E>::value, E&>::type constexpr operator&=(E& l, E r) {
+std::enable_if_t<sknonstd::is_bitmask_enum<E>::value, E&> constexpr operator&=(E& l, E r) {
return l = l & r;
}
template <typename E>
-typename std::enable_if<skstd::is_bitmask_enum<E>::value, E>::type constexpr operator~(E e) {
- return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(e));
+std::enable_if_t<sknonstd::is_bitmask_enum<E>::value, E> constexpr operator~(E e) {
+ return static_cast<E>(~static_cast<std::underlying_type_t<E>>(e));
}
#endif // SkEnumOperators_DEFINED
diff --git a/include/private/SkChecksum.h b/include/private/SkChecksum.h
index 553bfcd..8e2a4ba 100644
--- a/include/private/SkChecksum.h
+++ b/include/private/SkChecksum.h
@@ -54,12 +54,12 @@
// It should be both reasonably fast and high quality.
struct SkGoodHash {
template <typename K>
- SK_WHEN(sizeof(K) == 4, uint32_t) operator()(const K& k) const {
+ std::enable_if_t<sizeof(K) == 4, uint32_t> operator()(const K& k) const {
return SkChecksum::Mix(*(const uint32_t*)&k);
}
template <typename K>
- SK_WHEN(sizeof(K) != 4, uint32_t) operator()(const K& k) const {
+ std::enable_if_t<sizeof(K) != 4, uint32_t> operator()(const K& k) const {
return SkOpts::hash_fn(&k, sizeof(K), 0);
}
diff --git a/include/private/SkTArray.h b/include/private/SkTArray.h
index 8cf34d1..78ce51b 100644
--- a/include/private/SkTArray.h
+++ b/include/private/SkTArray.h
@@ -492,18 +492,18 @@
}
}
- template <bool E = MEM_MOVE> SK_WHEN(E, void) move(int dst, int src) {
+ template <bool E = MEM_MOVE> std::enable_if_t<E, void> move(int dst, int src) {
memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T));
}
- template <bool E = MEM_MOVE> SK_WHEN(E, void) move(void* dst) {
+ template <bool E = MEM_MOVE> std::enable_if_t<E, void> move(void* dst) {
sk_careful_memcpy(dst, fItemArray, fCount * sizeof(T));
}
- template <bool E = MEM_MOVE> SK_WHEN(!E, void) move(int dst, int src) {
+ template <bool E = MEM_MOVE> std::enable_if_t<!E, void> move(int dst, int src) {
new (&fItemArray[dst]) T(std::move(fItemArray[src]));
fItemArray[src].~T();
}
- template <bool E = MEM_MOVE> SK_WHEN(!E, void) move(void* dst) {
+ template <bool E = MEM_MOVE> std::enable_if_t<!E, void> move(void* dst) {
for (int i = 0; i < fCount; ++i) {
new (static_cast<char*>(dst) + sizeof(T) * i) T(std::move(fItemArray[i]));
fItemArray[i].~T();
diff --git a/include/private/SkTLogic.h b/include/private/SkTLogic.h
index 8cf8c28..53455c5 100644
--- a/include/private/SkTLogic.h
+++ b/include/private/SkTLogic.h
@@ -5,10 +5,8 @@
* found in the LICENSE file.
*
*
- * This header provides some of the helpers (like std::enable_if_t) which will
- * become available with C++14 in the type_traits header (in the skstd
- * namespace). This header also provides several Skia specific additions such
- * as SK_WHEN and the sknonstd namespace.
+ * This header provides some std:: features early in the skstd namespace
+ * and several Skia-specific additions in the sknonstd namespace.
*/
#ifndef SkTLogic_DEFINED
@@ -16,55 +14,19 @@
#include <cstddef>
#include <type_traits>
+#include <utility>
namespace skstd {
-template <bool B> using bool_constant = std::integral_constant<bool, B>;
-
-template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
-template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
-
-template <typename T> using remove_const_t = typename std::remove_const<T>::type;
-template <typename T> using remove_volatile_t = typename std::remove_volatile<T>::type;
-template <typename T> using remove_cv_t = typename std::remove_cv<T>::type;
-template <typename T> using remove_pointer_t = typename std::remove_pointer<T>::type;
-template <typename T> using remove_reference_t = typename std::remove_reference<T>::type;
-template <typename T> using remove_extent_t = typename std::remove_extent<T>::type;
-
-template <typename T> using add_const_t = typename std::add_const<T>::type;
-template <typename T> using add_volatile_t = typename std::add_volatile<T>::type;
-template <typename T> using add_cv_t = typename std::add_cv<T>::type;
-template <typename T> using add_pointer_t = typename std::add_pointer<T>::type;
-template <typename T> using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;
-
-template <typename T> using result_of_t = typename std::result_of<T>::type;
-
-template <typename... T> using common_type_t = typename std::common_type<T...>::type;
-
-template <std::size_t... Ints> struct index_sequence {
- using type = index_sequence;
- using value_type = std::size_t;
- static constexpr std::size_t size() noexcept { return sizeof...(Ints); }
-};
-
-template <typename S1, typename S2> struct make_index_sequence_combine;
-template <std::size_t... I1, std::size_t... I2>
-struct make_index_sequence_combine<skstd::index_sequence<I1...>, skstd::index_sequence<I2...>>
- : skstd::index_sequence<I1..., (sizeof...(I1)+I2)...>
-{ };
-
-template <std::size_t N> struct make_index_sequence
- : make_index_sequence_combine<typename skstd::make_index_sequence< N/2>::type,
- typename skstd::make_index_sequence<N - N/2>::type>{};
-template<> struct make_index_sequence<0> : skstd::index_sequence< >{};
-template<> struct make_index_sequence<1> : skstd::index_sequence<0>{};
-
+// C++17, <variant>
struct monostate {};
+// C++17, <type_traits>
template<typename...> struct conjunction : std::true_type { };
template<typename T> struct conjunction<T> : T { };
template<typename T, typename... Ts>
struct conjunction<T, Ts...> : std::conditional<bool(T::value), conjunction<Ts...>, T>::type { };
+
} // namespace skstd
// The sknonstd namespace contains things we would like to be proposed and feel std-ish.
@@ -75,12 +37,12 @@
// std::experimental::propagate_const already exists for other purposes in TSv2.
// These also follow the <dest, source> pattern used by boost.
template <typename D, typename S> struct copy_const {
- using type = skstd::conditional_t<std::is_const<S>::value, skstd::add_const_t<D>, D>;
+ using type = std::conditional_t<std::is_const<S>::value, std::add_const_t<D>, D>;
};
template <typename D, typename S> using copy_const_t = typename copy_const<D, S>::type;
template <typename D, typename S> struct copy_volatile {
- using type = skstd::conditional_t<std::is_volatile<S>::value, skstd::add_volatile_t<D>, D>;
+ using type = std::conditional_t<std::is_volatile<S>::value, std::add_volatile_t<D>, D>;
};
template <typename D, typename S> using copy_volatile_t = typename copy_volatile<D, S>::type;
@@ -92,16 +54,13 @@
// The name 'same' here means 'overwrite'.
// Alternate proposed names are 'replace', 'transfer', or 'qualify_from'.
// same_xxx<D, S> can be written as copy_xxx<remove_xxx_t<D>, S>
-template <typename D, typename S> using same_const = copy_const<skstd::remove_const_t<D>, S>;
+template <typename D, typename S> using same_const = copy_const<std::remove_const_t<D>, S>;
template <typename D, typename S> using same_const_t = typename same_const<D, S>::type;
-template <typename D, typename S> using same_volatile =copy_volatile<skstd::remove_volatile_t<D>,S>;
+template <typename D, typename S> using same_volatile =copy_volatile<std::remove_volatile_t<D>,S>;
template <typename D, typename S> using same_volatile_t = typename same_volatile<D, S>::type;
-template <typename D, typename S> using same_cv = copy_cv<skstd::remove_cv_t<D>, S>;
+template <typename D, typename S> using same_cv = copy_cv<std::remove_cv_t<D>, S>;
template <typename D, typename S> using same_cv_t = typename same_cv<D, S>::type;
} // namespace sknonstd
-// Just a pithier wrapper for enable_if_t.
-#define SK_WHEN(condition, T) skstd::enable_if_t<!!(condition), T>
-
#endif
diff --git a/include/private/SkTemplates.h b/include/private/SkTemplates.h
index 94b335c..477bbcb 100644
--- a/include/private/SkTemplates.h
+++ b/include/private/SkTemplates.h
@@ -17,6 +17,7 @@
#include <cstddef>
#include <memory>
#include <new>
+#include <type_traits>
#include <utility>
/** \file SkTemplates.h
@@ -64,10 +65,10 @@
function.
*/
template <typename T, void (*P)(T*)> class SkAutoTCallVProc
- : public std::unique_ptr<T, SkFunctionWrapper<skstd::remove_pointer_t<decltype(P)>, P>> {
+ : public std::unique_ptr<T, SkFunctionWrapper<std::remove_pointer_t<decltype(P)>, P>> {
public:
SkAutoTCallVProc(T* obj)
- : std::unique_ptr<T, SkFunctionWrapper<skstd::remove_pointer_t<decltype(P)>, P>>(obj) {}
+ : std::unique_ptr<T, SkFunctionWrapper<std::remove_pointer_t<decltype(P)>, P>>(obj) {}
operator T*() const { return this->get(); }
};
@@ -442,14 +443,14 @@
using SkAutoFree = std::unique_ptr<void, SkFunctionWrapper<void(void*), sk_free>>;
template<typename C, std::size_t... Is>
-constexpr auto SkMakeArrayFromIndexSequence(C c, skstd::index_sequence<Is...>)
--> std::array<skstd::result_of_t<C(std::size_t)>, sizeof...(Is)> {
+constexpr auto SkMakeArrayFromIndexSequence(C c, std::index_sequence<Is...>)
+-> std::array<std::result_of_t<C(std::size_t)>, sizeof...(Is)> {
return {{ c(Is)... }};
}
template<size_t N, typename C> constexpr auto SkMakeArray(C c)
--> std::array<skstd::result_of_t<C(std::size_t)>, N> {
- return SkMakeArrayFromIndexSequence(c, skstd::make_index_sequence<N>{});
+-> std::array<std::result_of_t<C(std::size_t)>, N> {
+ return SkMakeArrayFromIndexSequence(c, std::make_index_sequence<N>{});
}
#endif
diff --git a/include/private/SkVx.h b/include/private/SkVx.h
index 81cd680..cd73ab4 100644
--- a/include/private/SkVx.h
+++ b/include/private/SkVx.h
@@ -281,6 +281,7 @@
SIT Vec<1,T> min(const Vec<1,T>& x, const Vec<1,T>& y) { return std::min(x.val, y.val); }
SIT Vec<1,T> max(const Vec<1,T>& x, const Vec<1,T>& y) { return std::max(x.val, y.val); }
+SIT Vec<1,T> atan(const Vec<1,T>& x) { return std:: atan(x.val); }
SIT Vec<1,T> ceil(const Vec<1,T>& x) { return std:: ceil(x.val); }
SIT Vec<1,T> floor(const Vec<1,T>& x) { return std::floor(x.val); }
SIT Vec<1,T> trunc(const Vec<1,T>& x) { return std::trunc(x.val); }
@@ -314,6 +315,7 @@
SINT Vec<N,T> min(const Vec<N,T>& x, const Vec<N,T>& y) { return join(min(x.lo, y.lo), min(x.hi, y.hi)); }
SINT Vec<N,T> max(const Vec<N,T>& x, const Vec<N,T>& y) { return join(max(x.lo, y.lo), max(x.hi, y.hi)); }
+SINT Vec<N,T> atan(const Vec<N,T>& x) { return join( atan(x.lo), atan(x.hi)); }
SINT Vec<N,T> ceil(const Vec<N,T>& x) { return join( ceil(x.lo), ceil(x.hi)); }
SINT Vec<N,T> floor(const Vec<N,T>& x) { return join(floor(x.lo), floor(x.hi)); }
SINT Vec<N,T> trunc(const Vec<N,T>& x) { return join(trunc(x.lo), trunc(x.hi)); }
@@ -440,6 +442,12 @@
fma(x.hi, y.hi, z.hi));
}
+template <int N>
+static inline Vec<N,float> fract(const Vec<N,float>& x) {
+ return x - floor(x);
+}
+
+
// div255(x) = (x + 127) / 255 is a bit-exact rounding divide-by-255, packing down to 8-bit.
template <int N>
static inline Vec<N,uint8_t> div255(const Vec<N,uint16_t>& x) {
diff --git a/infra/bots/gen_tasks_logic/gen_tasks_logic.go b/infra/bots/gen_tasks_logic/gen_tasks_logic.go
index 0f90c7e..13331e6 100644
--- a/infra/bots/gen_tasks_logic/gen_tasks_logic.go
+++ b/infra/bots/gen_tasks_logic/gen_tasks_logic.go
@@ -433,7 +433,7 @@
// deriveCompileTaskName returns the name of a compile task based on the given
// job name.
func (b *jobBuilder) deriveCompileTaskName() string {
- if b.role("Test", "Perf") {
+ if b.role("Test", "Perf", "FM") {
task_os := b.parts["os"]
ec := []string{}
if val := b.parts["extra_config"]; val != "" {
@@ -1272,7 +1272,7 @@
}
// test generates a Test task.
-func (b *jobBuilder) test() {
+func (b *jobBuilder) dm() {
compileTaskName := ""
// LottieWeb doesn't require anything in Skia to be compiled.
if !b.extraConfig("LottieWeb") {
@@ -1373,6 +1373,24 @@
}
}
+func (b *jobBuilder) fm() {
+ b.addTask(b.Name, func(b *taskBuilder) {
+ b.isolate("test_skia_bundled.isolate")
+ b.dep(b.buildTaskDrivers(), b.compile())
+ b.cmd("./fm_driver",
+ "--local=false",
+ "--resources=skia/resources",
+ "--project_id", "skia-swarming-bots",
+ "--task_id", specs.PLACEHOLDER_TASK_ID,
+ "--task_name", b.Name,
+ "build/fm")
+ b.serviceAccount(b.cfg.ServiceAccountCompile)
+ b.swarmDimensions()
+ b.expiration(15 * time.Minute)
+ b.attempts(1)
+ })
+}
+
// perf generates a Perf task.
func (b *jobBuilder) perf() {
compileTaskName := ""
diff --git a/infra/bots/gen_tasks_logic/job_builder.go b/infra/bots/gen_tasks_logic/job_builder.go
index 90d71e8..e0d78b9 100644
--- a/infra/bots/gen_tasks_logic/job_builder.go
+++ b/infra/bots/gen_tasks_logic/job_builder.go
@@ -180,7 +180,11 @@
// Test bots.
if b.role("Test") {
- b.test()
+ b.dm()
+ return
+ }
+ if b.role("FM") {
+ b.fm()
return
}
diff --git a/infra/bots/jobs.json b/infra/bots/jobs.json
index 267f2f5..f3dfe53 100644
--- a/infra/bots/jobs.json
+++ b/infra/bots/jobs.json
@@ -143,6 +143,7 @@
"BuildStats-Debian10-EMCC-wasm-Release-CanvasKit",
"BuildStats-Debian10-EMCC-wasm-Release-CanvasKit_CPU",
"BuildStats-Debian10-EMCC-wasm-Release-PathKit",
+ "FM-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All",
"Housekeeper-Nightly-RecreateSKPs_Canary",
"Housekeeper-Nightly-UpdateGoDeps",
"Housekeeper-OnDemand-Presubmit",
diff --git a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json
index 6958b16..14d8b51 100644
--- a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json
+++ b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json
@@ -68,6 +68,21 @@
"extra_config"
]
},
+ "FM": {
+ "keys": [
+ "os",
+ "compiler",
+ "model",
+ "cpu_or_gpu",
+ "cpu_or_gpu_value",
+ "arch",
+ "configuration",
+ "test_filter"
+ ],
+ "optional_keys": [
+ "extra_config"
+ ]
+ },
"Upload": {
"recurse_roles": [
"Build",
diff --git a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py
index fff45e9..3a77c7d 100644
--- a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py
+++ b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py
@@ -26,6 +26,7 @@
BUILDER_ROLE_INFRA = 'Infra'
BUILDER_ROLE_PERF = 'Perf'
BUILDER_ROLE_TEST = 'Test'
+BUILDER_ROLE_FM = 'FM'
BUILDER_ROLE_UPLOAD = 'Upload'
BUILDER_ROLES = (BUILDER_ROLE_BUILD,
BUILDER_ROLE_BUILDSTATS,
@@ -33,6 +34,7 @@
BUILDER_ROLE_INFRA,
BUILDER_ROLE_PERF,
BUILDER_ROLE_TEST,
+ BUILDER_ROLE_FM,
BUILDER_ROLE_UPLOAD)
diff --git a/infra/bots/task_drivers/fm_driver/fm_driver.go b/infra/bots/task_drivers/fm_driver/fm_driver.go
new file mode 100644
index 0000000..1976784
--- /dev/null
+++ b/infra/bots/task_drivers/fm_driver/fm_driver.go
@@ -0,0 +1,181 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "flag"
+ "math/rand"
+ "runtime"
+ "strings"
+ "sync"
+ "sync/atomic"
+
+ "go.skia.org/infra/go/exec"
+ "go.skia.org/infra/go/util"
+ "go.skia.org/infra/task_driver/go/td"
+)
+
+type work struct {
+ Sources []string
+ Flags []string
+}
+
+func main() {
+ var (
+ projectId = flag.String("project_id", "", "ID of the Google Cloud project.")
+ taskId = flag.String("task_id", "", "ID of this task.")
+ taskName = flag.String("task_name", "", "Name of the task.")
+ local = flag.Bool("local", true, "True if running locally (as opposed to on the bots)")
+ output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.")
+
+ resources = flag.String("resources", "resources", "Passed to fm -i.")
+ )
+ ctx := td.StartRun(projectId, taskId, taskName, output, local)
+ defer td.EndRun(ctx)
+
+ if flag.NArg() < 1 {
+ td.Fatalf(ctx, "Please pass an fm binary.")
+ }
+ fm := flag.Arg(0)
+
+ // Run fm --flag to find the names of all linked GMs or tests.
+ query := func(flag string) []string {
+ stdout, err := exec.RunCwd(ctx, ".", fm, flag, "-i", *resources)
+ if err != nil {
+ td.Fatal(ctx, err)
+ }
+
+ lines := []string{}
+ scanner := bufio.NewScanner(strings.NewReader(stdout))
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ }
+ if err := scanner.Err(); err != nil {
+ td.Fatal(ctx, err)
+ }
+ return lines
+ }
+ gms := query("--listGMs")
+ tests := query("--listTests")
+
+ parse := func(job []string) *work {
+ w := &work{}
+
+ for _, token := range job {
+ // Everything after # is a comment.
+ if strings.HasPrefix(token, "#") {
+ break
+ }
+
+ // Treat "gm" or "gms" as a shortcut for all known GMs.
+ if token == "gm" || token == "gms" {
+ w.Sources = append(w.Sources, gms...)
+ continue
+ }
+ // Same for tests.
+ if token == "test" || token == "tests" {
+ w.Sources = append(w.Sources, tests...)
+ continue
+ }
+
+ // Is this a flag to pass through to FM?
+ if parts := strings.Split(token, "="); len(parts) == 2 {
+ f := "-"
+ if len(parts[0]) > 1 {
+ f += "-"
+ }
+ f += parts[0]
+
+ w.Flags = append(w.Flags, f, parts[1])
+ continue
+ }
+
+ // Anything else must be the name of a source for FM to run.
+ w.Sources = append(w.Sources, token)
+ }
+
+ return w
+ }
+
+ // TODO: this doesn't have to be hard coded, of course.
+ // TODO: add some .skps or images to demo that.
+ script := `
+ b=cpu tests
+ b=cpu gms
+ b=cpu gms skvm=true
+
+ #b=cpu gms skvm=true gamut=p3
+ #b=cpu gms skvm=true ct=565
+ `
+ jobs := [][]string{}
+ scanner := bufio.NewScanner(strings.NewReader(script))
+ for scanner.Scan() {
+ jobs = append(jobs, strings.Fields(scanner.Text()))
+ }
+ if err := scanner.Err(); err != nil {
+ td.Fatal(ctx, err)
+ }
+
+ var failures int32 = 0
+ wg := &sync.WaitGroup{}
+
+ worker := func(queue chan work) {
+ for w := range queue {
+ cmd := []string{}
+ cmd = append(cmd, fm)
+ cmd = append(cmd, "-i", *resources)
+ cmd = append(cmd, w.Flags...)
+ cmd = append(cmd, "-s")
+ cmd = append(cmd, w.Sources...)
+
+ if _, err := exec.RunCwd(ctx, ".", cmd...); err != nil {
+ if len(w.Sources) == 1 {
+ // If a source ran alone and failed, that's just a failure.
+ atomic.AddInt32(&failures, 1)
+ td.FailStep(ctx, err)
+ } else {
+ // If a batch of sources ran and failed, split them up and try again.
+ for _, source := range w.Sources {
+ wg.Add(1)
+ queue <- work{[]string{source}, w.Flags}
+ }
+ }
+ }
+ wg.Done()
+ }
+ }
+
+ workers := runtime.NumCPU()
+ queue := make(chan work, 1<<20)
+ for i := 0; i < workers; i++ {
+ go worker(queue)
+ }
+
+ for _, job := range jobs {
+ w := parse(job)
+ if len(w.Sources) == 0 {
+ continue
+ }
+
+ // Shuffle the sources randomly as a cheap way to approximate evenly expensive batches.
+ rand.Shuffle(len(w.Sources), func(i, j int) {
+ w.Sources[i], w.Sources[j] = w.Sources[j], w.Sources[i]
+ })
+
+ // Round up so there's at least one source per batch.
+ batch := (len(w.Sources) + workers - 1) / workers
+ util.ChunkIter(len(w.Sources), batch, func(start, end int) error {
+ wg.Add(1)
+ queue <- work{w.Sources[start:end], w.Flags}
+ return nil
+ })
+ }
+ wg.Wait()
+
+ if failures > 0 {
+ td.Fatalf(ctx, "%v runs of %v failed after retries.", failures, fm)
+ }
+}
diff --git a/infra/bots/tasks.json b/infra/bots/tasks.json
index 7f44a35..c17929f 100755
--- a/infra/bots/tasks.json
+++ b/infra/bots/tasks.json
@@ -727,6 +727,11 @@
],
"trigger": "master"
},
+ "FM-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All": {
+ "tasks": [
+ "FM-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All"
+ ]
+ },
"Housekeeper-Nightly-RecreateSKPs_Canary": {
"tasks": [
"Housekeeper-Nightly-RecreateSKPs_Canary"
@@ -14619,6 +14624,35 @@
"perf"
]
},
+ "FM-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All": {
+ "command": [
+ "./fm_driver",
+ "--local=false",
+ "--resources=skia/resources",
+ "--project_id",
+ "skia-swarming-bots",
+ "--task_id",
+ "<(TASK_ID)",
+ "--task_name",
+ "FM-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All",
+ "build/fm"
+ ],
+ "dependencies": [
+ "Build-Debian10-Clang-x86_64-Debug",
+ "Housekeeper-PerCommit-BuildTaskDrivers"
+ ],
+ "dimensions": [
+ "cpu:x86-64-Haswell_GCE",
+ "gpu:none",
+ "machine_type:n1-highcpu-64",
+ "os:Debian-10.3",
+ "pool:Skia"
+ ],
+ "expiration_ns": 900000000000,
+ "isolate": "test_skia_bundled.isolate",
+ "max_attempts": 1,
+ "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+ },
"Housekeeper-Nightly-RecreateSKPs_Canary": {
"caches": [
{
diff --git a/modules/skottie/src/effects/BrightnessContrastEffect.cpp b/modules/skottie/src/effects/BrightnessContrastEffect.cpp
index e480cfe..1c3e828 100644
--- a/modules/skottie/src/effects/BrightnessContrastEffect.cpp
+++ b/modules/skottie/src/effects/BrightnessContrastEffect.cpp
@@ -56,6 +56,8 @@
// [3] https://www.desmos.com/calculator/ehem0vy3ft
// [4] https://www.desmos.com/calculator/5t4xi10q4v
//
+
+#ifndef SKOTTIE_ACCURATE_CONTRAST_APPROXIMATION
static sk_sp<SkData> make_contrast_coeffs(float contrast) {
struct { float a, b, c; } coeffs;
@@ -76,6 +78,28 @@
color.rgb = ((a*color.rgb + b)*color.rgb + c)*color.rgb;
}
)";
+#else
+// More accurate (but slower) approximation:
+//
+// f(x) = x + a * sin(2πx)
+//
+// a = -contrast/3π
+//
+static sk_sp<SkData> make_contrast_coeffs(float contrast) {
+ const auto coeff_a = -contrast / (3 * SK_ScalarPI);
+
+ return SkData::MakeWithCopy(&coeff_a, sizeof(coeff_a));
+}
+
+static constexpr char CONTRAST_EFFECT[] = R"(
+ uniform half a;
+
+ void main(inout half4 color) {
+ color.rgb += a * sin(color.rgb * 6.283185);
+ }
+)";
+
+#endif
class BrightnessContrastAdapter final : public DiscardableAdapterBase<BrightnessContrastAdapter,
sksg::ExternalColorFilter> {
diff --git a/modules/skplaintexteditor/app/editor_application.cpp b/modules/skplaintexteditor/app/editor_application.cpp
index 214cbab..cf37eb6 100644
--- a/modules/skplaintexteditor/app/editor_application.cpp
+++ b/modules/skplaintexteditor/app/editor_application.cpp
@@ -181,7 +181,7 @@
} else if (skui::InputState::kUp == state) {
fMouseDown = false;
}
- bool shiftOrDrag = skstd::Any(modifiers & skui::ModifierKey::kShift) || !mouseDown;
+ bool shiftOrDrag = sknonstd::Any(modifiers & skui::ModifierKey::kShift) || !mouseDown;
if (fMouseDown) {
return this->move(fEditor.getPosition({x - fMargin, y + fPos - fMargin}), shiftOrDrag);
}
@@ -189,7 +189,7 @@
}
bool onChar(SkUnichar c, skui::ModifierKey modi) override {
- using skstd::Any;
+ using sknonstd::Any;
modi &= ~skui::ModifierKey::kFirstPress;
if (!Any(modi & (skui::ModifierKey::kControl |
skui::ModifierKey::kOption |
@@ -306,7 +306,7 @@
return false; // ignore keyup
}
// ignore other modifiers.
- using skstd::Any;
+ using sknonstd::Any;
skui::ModifierKey ctrlAltCmd = modifiers & (skui::ModifierKey::kControl |
skui::ModifierKey::kOption |
skui::ModifierKey::kCommand);
@@ -348,7 +348,8 @@
default:
break;
}
- } else if (skstd::Any(ctrlAltCmd & (skui::ModifierKey::kControl | skui::ModifierKey::kCommand))) {
+ } else if (sknonstd::Any(ctrlAltCmd & (skui::ModifierKey::kControl |
+ skui::ModifierKey::kCommand))) {
switch (key) {
case skui::Key::kLeft:
return this->moveCursor(Editor::Movement::kWordLeft, shift);
diff --git a/modules/skshaper/src/SkShaper_harfbuzz.cpp b/modules/skshaper/src/SkShaper_harfbuzz.cpp
index 3386f7c..29d9fe9 100644
--- a/modules/skshaper/src/SkShaper_harfbuzz.cpp
+++ b/modules/skshaper/src/SkShaper_harfbuzz.cpp
@@ -59,7 +59,7 @@
# define HB_FEATURE_GLOBAL_END ((unsigned int) -1)
#endif
-namespace skstd {
+namespace sknonstd {
template <> struct is_bitmask_enum<hb_buffer_flags_t> : std::true_type {};
}
diff --git a/samplecode/Sample3D.cpp b/samplecode/Sample3D.cpp
index 402ec68..7525c48 100644
--- a/samplecode/Sample3D.cpp
+++ b/samplecode/Sample3D.cpp
@@ -377,8 +377,8 @@
in fragmentProcessor color_map;
in fragmentProcessor normal_map;
- uniform float4x4 localToWorld;
- uniform float4x4 localToWorldAdjInv;
+ layout (marker=local_to_world) uniform float4x4 localToWorld;
+ layout (marker=normals(local_to_world)) uniform float4x4 localToWorldAdjInv;
uniform float3 lightPos;
float3 convert_normal_sample(half4 c) {
@@ -413,27 +413,11 @@
return;
}
- auto adj_inv = [](const SkM44& m) {
- // Normals need to be transformed by the inverse-transpose of the upper-left 3x3 portion
- // (scale + rotate) of the local to world matrix. (If the local to world only has
- // uniform scale, we can use its upper-left 3x3 directly, but we don't know if that's
- // the case here, so go the extra mile.)
- SkM44 rot_scale(m.rc(0, 0), m.rc(0, 1), m.rc(0, 2), 0,
- m.rc(1, 0), m.rc(1, 1), m.rc(1, 2), 0,
- m.rc(2, 0), m.rc(2, 1), m.rc(2, 2), 0,
- 0, 0, 0, 1);
- SkM44 inv(SkM44::kUninitialized_Constructor);
- SkAssertResult(rot_scale.invert(&inv));
- return inv.transpose();
- };
-
struct Uniforms {
- SkM44 fLocalToWorld;
- SkM44 fLocalToWorldAdjInv;
+ SkM44 fLocalToWorld; // Automatically populated, via layout(marker)
+ SkM44 fLocalToWorldAdjInv; // Ditto
SkV3 fLightPos;
} uni;
- uni.fLocalToWorld = this->localToWorld(canvas);
- uni.fLocalToWorldAdjInv = adj_inv(uni.fLocalToWorld);
uni.fLightPos = fLight.computeWorldPos(fSphere);
sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
diff --git a/src/core/SkAdvancedTypefaceMetrics.h b/src/core/SkAdvancedTypefaceMetrics.h
index 84fd020..0ccf562 100644
--- a/src/core/SkAdvancedTypefaceMetrics.h
+++ b/src/core/SkAdvancedTypefaceMetrics.h
@@ -65,7 +65,7 @@
SkIRect fBBox = {0, 0, 0, 0}; // The bounding box of all glyphs (in font units).
};
-namespace skstd {
+namespace sknonstd {
template <> struct is_bitmask_enum<SkAdvancedTypefaceMetrics::FontFlags> : std::true_type {};
template <> struct is_bitmask_enum<SkAdvancedTypefaceMetrics::StyleFlags> : std::true_type {};
}
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 7352117..3b6abc0 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1542,7 +1542,7 @@
}
void SkCanvas::markCTM(const char* name) {
- if (name && name[0]) {
+ if (SkCanvasPriv::ValidateMarker(name)) {
fMarkerStack->setMarker(SkOpts::hash_fn(name, strlen(name), 0),
this->getLocalToDevice(), fMCRec);
this->onMarkCTM(name);
@@ -1550,7 +1550,8 @@
}
bool SkCanvas::findMarkedCTM(const char* name, SkM44* mx) const {
- return name && name[0] && fMarkerStack->findMarker(SkOpts::hash_fn(name, strlen(name), 0), mx);
+ return SkCanvasPriv::ValidateMarker(name) &&
+ fMarkerStack->findMarker(SkOpts::hash_fn(name, strlen(name), 0), mx);
}
//////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkCanvasPriv.cpp b/src/core/SkCanvasPriv.cpp
index cf90419..dc05d6c 100644
--- a/src/core/SkCanvasPriv.cpp
+++ b/src/core/SkCanvasPriv.cpp
@@ -10,6 +10,8 @@
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriter32.h"
+#include <locale>
+
SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
const SkPaint* paint, const SkRect& bounds)
: fCanvas(canvas)
@@ -97,3 +99,20 @@
*totalDstClipCount = dstClipCount;
*totalMatrixCount = maxMatrixIndex + 1;
}
+
+bool SkCanvasPriv::ValidateMarker(const char* name) {
+ if (!name) {
+ return false;
+ }
+
+ std::locale loc(std::locale::classic());
+ if (!std::isalpha(*name, loc)) {
+ return false;
+ }
+ while (*(++name)) {
+ if (!std::isalnum(*name, loc) && *name != '_') {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/src/core/SkCanvasPriv.h b/src/core/SkCanvasPriv.h
index 5bef882..a245d87 100644
--- a/src/core/SkCanvasPriv.h
+++ b/src/core/SkCanvasPriv.h
@@ -53,6 +53,10 @@
// computes the minimum length for these arrays that would provide index access errors.
static void GetDstClipAndMatrixCounts(const SkCanvas::ImageSetEntry set[], int count,
int* totalDstClipCount, int* totalMatrixCount);
+
+ // Checks that the marker name is an identifier ([a-zA-Z][a-zA-Z0-9_]*)
+ // Identifiers with leading underscores are reserved (not allowed).
+ static bool ValidateMarker(const char*);
};
#endif
diff --git a/src/core/SkGlyphRunPainter.cpp b/src/core/SkGlyphRunPainter.cpp
index d0071f3..8ef928d 100644
--- a/src/core/SkGlyphRunPainter.cpp
+++ b/src/core/SkGlyphRunPainter.cpp
@@ -402,7 +402,7 @@
textContext->fOptions, blob.get());
}
- return blob->test_makeOp(textLen, mtxProvider, drawOrigin, skPaint, filteredColor, surfaceProps,
+ return blob->test_makeOp(mtxProvider, drawOrigin, skPaint, filteredColor, surfaceProps,
rtc->textTarget());
}
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 3974cf1..c162670 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1847,6 +1847,35 @@
return (Verb)verb;
}
+void SkPath::RawIter::setPath(const SkPath& path) {
+ SkPathPriv::Iterate iterate(path);
+ fIter = iterate.begin();
+ fEnd = iterate.end();
+}
+
+SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
+ if (!(fIter != fEnd)) {
+ return kDone_Verb;
+ }
+ auto [verb, iterPts, weights] = *fIter;
+ int numPts;
+ switch (verb) {
+ case SkPathVerb::kMove: numPts = 1; break;
+ case SkPathVerb::kLine: numPts = 2; break;
+ case SkPathVerb::kQuad: numPts = 3; break;
+ case SkPathVerb::kConic:
+ numPts = 3;
+ fConicWeight = *weights;
+ break;
+ case SkPathVerb::kCubic: numPts = 4; break;
+ case SkPathVerb::kClose: numPts = 0; break;
+ case SkPathVerb::kDone: SkUNREACHABLE;
+ }
+ memcpy(pts, iterPts, sizeof(SkPoint) * numPts);
+ ++fIter;
+ return (Verb) verb;
+}
+
///////////////////////////////////////////////////////////////////////////////
#include "include/core/SkStream.h"
diff --git a/src/core/SkPathPriv.h b/src/core/SkPathPriv.h
index 874d174..f924907 100644
--- a/src/core/SkPathPriv.h
+++ b/src/core/SkPathPriv.h
@@ -144,6 +144,35 @@
};
/**
+ * Iterable object for traversing verbs, points, and conic weights in a path:
+ *
+ * for (auto [verb, pts, weights] : SkPathPriv::Iterate(skPath)) {
+ * ...
+ * }
+ */
+ struct Iterate {
+ public:
+ Iterate(const SkPath& path)
+ : Iterate(path.fPathRef->verbsBegin(),
+ // Don't allow iteration through non-finite points.
+ (!path.isFinite()) ? path.fPathRef->verbsBegin()
+ : path.fPathRef->verbsEnd(),
+ path.fPathRef->points(), path.fPathRef->conicWeights()) {
+ }
+ Iterate(const uint8_t* verbsBegin, const uint8_t* verbsEnd, const SkPoint* points,
+ const SkScalar* weights)
+ : fVerbsBegin(verbsBegin), fVerbsEnd(verbsEnd), fPoints(points), fWeights(weights) {
+ }
+ SkPath::RangeIter begin() { return {fVerbsBegin, fPoints, fWeights}; }
+ SkPath::RangeIter end() { return {fVerbsEnd, nullptr, nullptr}; }
+ private:
+ const uint8_t* fVerbsBegin;
+ const uint8_t* fVerbsEnd;
+ const SkPoint* fPoints;
+ const SkScalar* fWeights;
+ };
+
+ /**
* Returns a pointer to the verb data.
*/
static const uint8_t* VerbData(const SkPath& path) {
diff --git a/src/core/SkPictureCommon.h b/src/core/SkPictureCommon.h
index 2a10699..bd4823d 100644
--- a/src/core/SkPictureCommon.h
+++ b/src/core/SkPictureCommon.h
@@ -74,12 +74,12 @@
}
template <typename T>
- SK_WHEN(T::kTags & SkRecords::kHasPaint_Tag, void) operator()(const T& op) {
+ std::enable_if_t<T::kTags & SkRecords::kHasPaint_Tag, void> operator()(const T& op) {
this->checkPaint(AsPtr(op.paint));
}
template <typename T>
- SK_WHEN(!(T::kTags & SkRecords::kHasPaint_Tag), void)
+ std::enable_if_t<!(T::kTags & SkRecords::kHasPaint_Tag), void>
operator()(const T& op) { /* do nothing */ }
int fNumSlowPathsAndDashEffects;
diff --git a/src/core/SkRecord.h b/src/core/SkRecord.h
index 68c63492..37cc058 100644
--- a/src/core/SkRecord.h
+++ b/src/core/SkRecord.h
@@ -129,13 +129,13 @@
};
template <typename T>
- SK_WHEN(std::is_empty<T>::value, T*) allocCommand() {
+ std::enable_if_t<std::is_empty<T>::value, T*> allocCommand() {
static T singleton = {};
return &singleton;
}
template <typename T>
- SK_WHEN(!std::is_empty<T>::value, T*) allocCommand() { return this->alloc<T>(); }
+ std::enable_if_t<!std::is_empty<T>::value, T*> allocCommand() { return this->alloc<T>(); }
void grow();
diff --git a/src/core/SkRecordPattern.h b/src/core/SkRecordPattern.h
index 7e96592..96a3407 100644
--- a/src/core/SkRecordPattern.h
+++ b/src/core/SkRecordPattern.h
@@ -49,19 +49,20 @@
type* get() { return fPaint; }
template <typename T>
- SK_WHEN((T::kTags & kDrawWithPaint_Tag) == kDrawWithPaint_Tag, bool) operator()(T* draw) {
+ std::enable_if_t<(T::kTags & kDrawWithPaint_Tag) == kDrawWithPaint_Tag, bool>
+ operator()(T* draw) {
fPaint = AsPtr(draw->paint);
return true;
}
template <typename T>
- SK_WHEN((T::kTags & kDrawWithPaint_Tag) == kDraw_Tag, bool) operator()(T* draw) {
+ std::enable_if_t<(T::kTags & kDrawWithPaint_Tag) == kDraw_Tag, bool> operator()(T* draw) {
fPaint = nullptr;
return true;
}
template <typename T>
- SK_WHEN(!(T::kTags & kDraw_Tag), bool) operator()(T* draw) {
+ std::enable_if_t<!(T::kTags & kDraw_Tag), bool> operator()(T* draw) {
fPaint = nullptr;
return false;
}
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index b51bffb..387b31c 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -10,6 +10,8 @@
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkChecksum.h"
#include "include/private/SkMutex.h"
+#include "src/core/SkCanvasPriv.h"
+#include "src/core/SkMatrixProvider.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkUtils.h"
@@ -52,6 +54,20 @@
SkSL::Compiler* SharedCompiler::gCompiler = nullptr;
}
+// Accepts a valid marker, or "normals(<marker>)"
+static bool parse_marker(const SkSL::StringFragment& marker, uint32_t* id, uint32_t* flags) {
+ SkString s = marker;
+ if (s.startsWith("normals(") && s.endsWith(')')) {
+ *flags |= SkRuntimeEffect::Variable::kMarkerNormals_Flag;
+ s.set(marker.fChars + 8, marker.fLength - 9);
+ }
+ if (!SkCanvasPriv::ValidateMarker(s.c_str())) {
+ return false;
+ }
+ *id = SkOpts::hash_fn(s.c_str(), s.size(), 0);
+ return true;
+}
+
SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
SkSL::SharedCompiler compiler;
auto program = compiler->convertProgram(SkSL::Program::kPipelineStage_Kind,
@@ -202,6 +218,18 @@
break;
}
+ const SkSL::StringFragment& marker(var.fModifiers.fLayout.fMarker);
+ if (marker.fLength) {
+ // Rules that should be enforced by the IR generator:
+ SkASSERT(v.fQualifier == Variable::Qualifier::kUniform);
+ SkASSERT(v.fType == Variable::Type::kFloat4x4);
+ v.fFlags |= Variable::kMarker_Flag;
+ if (!parse_marker(marker, &v.fMarker, &v.fFlags)) {
+ RETURN_FAILURE("Invalid 'marker' string: '%.*s'",
+ (int)marker.fLength, marker.fChars);
+ }
+ }
+
if (v.fType != Variable::Type::kBool) {
offset = SkAlign4(offset);
}
@@ -380,6 +408,18 @@
//auto u16 = [&]{ auto x = sk_unaligned_load<uint16_t>(ip); ip += sizeof(x); return x; };
auto u32 = [&]{ auto x = sk_unaligned_load<uint32_t>(ip); ip += sizeof(x); return x; };
+ auto apply = [&](SkSL::ByteCodeInstruction instBase, skvm::F32 (*func)(skvm::F32)) {
+ const int N = (int)inst - (int)instBase + 1;
+ SkASSERT(N <= 4);
+ skvm::F32 args[4];
+ for (int i = 0; i < N; ++i) {
+ args[i] = pop();
+ }
+ for (int i = N; i --> 0;) {
+ push(func(args[i]));
+ }
+ };
+
switch (inst) {
default:
#if 0
@@ -420,6 +460,19 @@
push(uniform[ix]);
} break;
+ case Inst::kLoadUniform2: {
+ int ix = u8();
+ push(uniform[ix + 0]);
+ push(uniform[ix + 1]);
+ } break;
+
+ case Inst::kLoadUniform3: {
+ int ix = u8();
+ push(uniform[ix + 0]);
+ push(uniform[ix + 1]);
+ push(uniform[ix + 2]);
+ } break;
+
case Inst::kLoadUniform4: {
int ix = u8();
push(uniform[ix + 0]);
@@ -493,6 +546,36 @@
push(a+x);
} break;
+ case Inst::kSubtractF: {
+ skvm::F32 x = pop(),
+ a = pop();
+ push(a-x);
+ } break;
+
+ case Inst::kSubtractF2: {
+ skvm::F32 x = pop(), y = pop(),
+ a = pop(), b = pop();
+ push(b-y);
+ push(a-x);
+ } break;
+
+ case Inst::kSubtractF3: {
+ skvm::F32 x = pop(), y = pop(), z = pop(),
+ a = pop(), b = pop(), c = pop();
+ push(c-z);
+ push(b-y);
+ push(a-x);
+ } break;
+
+ case Inst::kSubtractF4: {
+ skvm::F32 x = pop(), y = pop(), z = pop(), w = pop(),
+ a = pop(), b = pop(), c = pop(), d = pop();
+ push(d-w);
+ push(c-z);
+ push(b-y);
+ push(a-x);
+ } break;
+
case Inst::kMultiplyF: {
skvm::F32 x = pop(),
a = pop();
@@ -523,32 +606,56 @@
push(a*x);
} break;
- case Inst::kSin: {
- skvm::F32 x = pop();
- push(approx_sin(x));
+ case Inst::kDivideF: {
+ skvm::F32 x = pop(),
+ a = pop();
+ push(a/x);
} break;
- case Inst::kSin2: {
- skvm::F32 x = pop(), y = pop();
- push(approx_sin(y));
- push(approx_sin(x));
+ case Inst::kDivideF2: {
+ skvm::F32 x = pop(), y = pop(),
+ a = pop(), b = pop();
+ push(b/y);
+ push(a/x);
} break;
- case Inst::kSin3: {
- skvm::F32 x = pop(), y = pop(), z = pop();
- push(approx_sin(z));
- push(approx_sin(y));
- push(approx_sin(x));
+ case Inst::kDivideF3: {
+ skvm::F32 x = pop(), y = pop(), z = pop(),
+ a = pop(), b = pop(), c = pop();
+ push(c/z);
+ push(b/y);
+ push(a/x);
} break;
- case Inst::kSin4: {
- skvm::F32 x = pop(), y = pop(), z = pop(), w = pop();
- push(approx_sin(w));
- push(approx_sin(z));
- push(approx_sin(y));
- push(approx_sin(x));
+ case Inst::kDivideF4: {
+ skvm::F32 x = pop(), y = pop(), z = pop(), w = pop(),
+ a = pop(), b = pop(), c = pop(), d = pop();
+ push(d/w);
+ push(c/z);
+ push(b/y);
+ push(a/x);
} break;
+ case Inst::kATan:
+ case Inst::kATan2:
+ case Inst::kATan3:
+ case Inst::kATan4: apply(Inst::kATan, skvm::approx_atan); break;
+
+ case Inst::kFract:
+ case Inst::kFract2:
+ case Inst::kFract3:
+ case Inst::kFract4: apply(Inst::kFract, skvm::fract); break;
+
+ case Inst::kSqrt:
+ case Inst::kSqrt2:
+ case Inst::kSqrt3:
+ case Inst::kSqrt4: apply(Inst::kSqrt, skvm::sqrt); break;
+
+ case Inst::kSin:
+ case Inst::kSin2:
+ case Inst::kSin3:
+ case Inst::kSin4: apply(Inst::kSin, skvm::approx_sin); break;
+
// Baby steps... just leaving test conditions on the stack for now.
case Inst::kMaskPush: break;
case Inst::kMaskNegate: break;
@@ -738,7 +845,35 @@
if (!this->totalLocalMatrix(args.fPreLocalMatrix)->invert(&matrix)) {
return nullptr;
}
- auto fp = GrSkSLFP::Make(args.fContext, fEffect, "runtime_shader", fInputs);
+ // If any of our uniforms are late-bound (eg, layout(marker)), we need to clone the blob
+ sk_sp<SkData> inputs = fInputs;
+
+ for (const auto& v : fEffect->inputs()) {
+ if (v.hasMarker()) {
+ if (inputs == fInputs) {
+ inputs = SkData::MakeWithCopy(fInputs->data(), fInputs->size());
+ }
+ SkASSERT(v.fType == SkRuntimeEffect::Variable::Type::kFloat4x4);
+ SkM44* localToMarker = SkTAddOffset<SkM44>(inputs->writable_data(), v.fOffset);
+ if (!args.fMatrixProvider.getLocalToMarker(v.fMarker, localToMarker)) {
+ // We couldn't provide a matrix that was requested by the SkSL
+ SkDebugf("Failed to get marked matrix %u\n", v.fMarker);
+ return nullptr;
+ }
+ if (v.hasNormalsMarker()) {
+ // Normals need to be transformed by the inverse-transpose of the upper-left
+ // 3x3 portion (scale + rotate) of the matrix.
+ localToMarker->setRow(3, {0, 0, 0, 1});
+ localToMarker->setCol(3, {0, 0, 0, 1});
+ if (!localToMarker->invert(localToMarker)) {
+ return nullptr;
+ }
+ *localToMarker = localToMarker->transpose();
+ }
+ }
+ }
+
+ auto fp = GrSkSLFP::Make(args.fContext, fEffect, "runtime_shader", std::move(inputs));
for (const auto& child : fChildren) {
auto childFP = child ? as_SB(child)->asFragmentProcessor(args) : nullptr;
if (!childFP) {
diff --git a/src/core/SkVertices.cpp b/src/core/SkVertices.cpp
index 2d6992c..532bb4f 100644
--- a/src/core/SkVertices.cpp
+++ b/src/core/SkVertices.cpp
@@ -9,6 +9,7 @@
#include "include/core/SkData.h"
#include "include/private/SkTo.h"
+#include "src/core/SkCanvasPriv.h"
#include "src/core/SkOpts.h"
#include "src/core/SkReader32.h"
#include "src/core/SkSafeMath.h"
@@ -31,7 +32,7 @@
SkVertices::Attribute::Attribute(Type t, Usage u, const char* markerName)
: fType(t)
, fUsage(u)
- , fMarkerName((markerName && markerName[0]) ? markerName : nullptr) {
+ , fMarkerName(markerName) {
fMarkerID = fMarkerName ? SkOpts::hash_fn(fMarkerName, strlen(fMarkerName), 0) : 0;
SkASSERT(!fMarkerName || fMarkerID != 0);
}
@@ -67,6 +68,9 @@
}
bool SkVertices::Attribute::isValid() const {
+ if (fMarkerName && !SkCanvasPriv::ValidateMarker(fMarkerName)) {
+ return false;
+ }
switch (fUsage) {
case Usage::kRaw:
return fMarkerID == 0;
diff --git a/src/core/SkZip.h b/src/core/SkZip.h
index d13ecaf..5cf6120 100644
--- a/src/core/SkZip.h
+++ b/src/core/SkZip.h
@@ -11,6 +11,7 @@
#include <iterator>
#include <tuple>
#include <type_traits>
+#include <utility>
#include "include/core/SkTypes.h"
#include "include/private/SkTemplates.h"
@@ -105,22 +106,22 @@
constexpr ReturnTuple index(size_t i) const {
SkASSERT(this->size() > 0);
SkASSERT(i < this->size());
- return indexDetail(i, skstd::make_index_sequence<sizeof...(Ts)>{});
+ return indexDetail(i, std::make_index_sequence<sizeof...(Ts)>{});
}
template<std::size_t... Is>
- constexpr ReturnTuple indexDetail(size_t i, skstd::index_sequence<Is...>) const {
+ constexpr ReturnTuple indexDetail(size_t i, std::index_sequence<Is...>) const {
return ReturnTuple((std::get<Is>(fPointers))[i]...);
}
std::tuple<Ts*...> pointersAt(size_t i) const {
SkASSERT(this->size() > 0);
SkASSERT(i < this->size());
- return pointersAtDetail(i, skstd::make_index_sequence<sizeof...(Ts)>{});
+ return pointersAtDetail(i, std::make_index_sequence<sizeof...(Ts)>{});
}
template<std::size_t... Is>
- constexpr std::tuple<Ts*...> pointersAtDetail(size_t i, skstd::index_sequence<Is...>) const {
+ constexpr std::tuple<Ts*...> pointersAtDetail(size_t i, std::index_sequence<Is...>) const {
return std::tuple<Ts*...>{&(std::get<Is>(fPointers))[i]...};
}
diff --git a/src/gpu/GrDataUtils.cpp b/src/gpu/GrDataUtils.cpp
index 6efa24d..a6102fd 100644
--- a/src/gpu/GrDataUtils.cpp
+++ b/src/gpu/GrDataUtils.cpp
@@ -334,6 +334,9 @@
case GrColorType::kRGBA_8888: *load = SkRasterPipeline::load_8888; break;
case GrColorType::kRG_88: *load = SkRasterPipeline::load_rg88; break;
case GrColorType::kRGBA_1010102: *load = SkRasterPipeline::load_1010102; break;
+ case GrColorType::kBGRA_1010102: *load = SkRasterPipeline::load_1010102;
+ swizzle = GrSwizzle("bgra");
+ break;
case GrColorType::kAlpha_F16: *load = SkRasterPipeline::load_af16; break;
case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipeline::load_f16; break;
case GrColorType::kRG_1616: *load = SkRasterPipeline::load_rg1616; break;
@@ -396,6 +399,9 @@
case GrColorType::kRGBA_8888: *store = SkRasterPipeline::store_8888; break;
case GrColorType::kRG_88: *store = SkRasterPipeline::store_rg88; break;
case GrColorType::kRGBA_1010102: *store = SkRasterPipeline::store_1010102; break;
+ case GrColorType::kBGRA_1010102: swizzle = GrSwizzle("bgra");
+ *store = SkRasterPipeline::store_1010102;
+ break;
case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16; break;
case GrColorType::kRG_1616: *store = SkRasterPipeline::store_rg1616; break;
case GrColorType::kRGBA_16161616: *store = SkRasterPipeline::store_16161616; break;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 694ff4f..e1da02f 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -240,6 +240,7 @@
case GrColorType::kABGR_4444:
case GrColorType::kBGRA_8888:
case GrColorType::kRGBA_1010102:
+ case GrColorType::kBGRA_1010102:
case GrColorType::kRGBA_F16:
case GrColorType::kRGBA_F16_Clamped:
return GrColorType::kRGBA_8888;
@@ -884,41 +885,11 @@
// Fills the rect, using rect as its own local coordinates
this->fillRectToRect(clip, std::move(paint), aa, viewMatrix, rect, rect);
return;
- } else if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
- stroke.getStyle() == SkStrokeRec::kHairline_Style) {
- if ((!rect.width() || !rect.height()) &&
- SkStrokeRec::kHairline_Style != stroke.getStyle()) {
- SkScalar r = stroke.getWidth() / 2;
- // TODO: Move these stroke->fill fallbacks to GrStyledShape?
- switch (stroke.getJoin()) {
- case SkPaint::kMiter_Join:
- this->drawRect(
- clip, std::move(paint), aa, viewMatrix,
- {rect.fLeft - r, rect.fTop - r, rect.fRight + r, rect.fBottom + r},
- &GrStyle::SimpleFill());
- return;
- case SkPaint::kRound_Join:
- // Raster draws nothing when both dimensions are empty.
- if (rect.width() || rect.height()){
- SkRRect rrect = SkRRect::MakeRectXY(rect.makeOutset(r, r), r, r);
- this->drawRRect(clip, std::move(paint), aa, viewMatrix, rrect,
- GrStyle::SimpleFill());
- return;
- }
- case SkPaint::kBevel_Join:
- if (!rect.width()) {
- this->drawRect(clip, std::move(paint), aa, viewMatrix,
- {rect.fLeft - r, rect.fTop, rect.fRight + r, rect.fBottom},
- &GrStyle::SimpleFill());
- } else {
- this->drawRect(clip, std::move(paint), aa, viewMatrix,
- {rect.fLeft, rect.fTop - r, rect.fRight, rect.fBottom + r},
- &GrStyle::SimpleFill());
- }
- return;
- }
- }
-
+ } else if ((stroke.getStyle() == SkStrokeRec::kStroke_Style ||
+ stroke.getStyle() == SkStrokeRec::kHairline_Style) &&
+ (rect.width() && rect.height())) {
+ // Only use the StrokeRectOp for non-empty rectangles. Empty rectangles will be processed by
+ // GrStyledShape to handle stroke caps and dashing properly.
std::unique_ptr<GrDrawOp> op;
GrAAType aaType = this->chooseAAType(aa);
@@ -2297,7 +2268,9 @@
}
}
- this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, shape);
+ // If we get here in drawShape(), we definitely need to use path rendering
+ this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, shape,
+ /* attempt fallback */ false);
}
bool GrRenderTargetContextPriv::drawAndStencilPath(const GrHardClip& clip,
@@ -2388,7 +2361,8 @@
GrPaint&& paint,
GrAA aa,
const SkMatrix& viewMatrix,
- const GrStyledShape& originalShape) {
+ const GrStyledShape& originalShape,
+ bool attemptShapeFallback) {
ASSERT_SINGLE_OWNER
RETURN_IF_ABANDONED
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "internalDrawPath", fContext);
@@ -2397,6 +2371,14 @@
return;
}
+ if (attemptShapeFallback && originalShape.simplified()) {
+ // Usually we enter drawShapeUsingPathRenderer() because the shape+style was too
+ // complex for dedicated draw ops. However, if GrStyledShape was able to reduce something
+ // we ought to try again instead of going right to path rendering.
+ this->drawShape(clip, std::move(paint), aa, viewMatrix, originalShape);
+ return;
+ }
+
SkIRect clipConservativeBounds;
clip.getConservativeBounds(this->width(), this->height(), &clipConservativeBounds, nullptr);
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index eb10b3f..ca0ac26 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -671,8 +671,10 @@
DrawQuad* quad,
const SkRect* subset = nullptr);
+ // If 'attemptShapeFallback' is true, and the original shape had been simplfied, this
+ // will re-route through drawShape() to see if we can avoid path rendering one more time.
void drawShapeUsingPathRenderer(const GrClip&, GrPaint&&, GrAA, const SkMatrix&,
- const GrStyledShape&);
+ const GrStyledShape&, bool attemptShapeFallback = true);
void addOp(std::unique_ptr<GrOp>);
diff --git a/src/gpu/GrTRecorder.h b/src/gpu/GrTRecorder.h
index 7287d62..e3f41a7 100644
--- a/src/gpu/GrTRecorder.h
+++ b/src/gpu/GrTRecorder.h
@@ -83,7 +83,7 @@
* operating on TItem*. Multiple inheritance may make this not true. It is runtime asserted.
*/
template <typename TItem, typename... Args>
- SK_WHEN((std::is_base_of<TBase, TItem>::value), TItem&)
+ std::enable_if_t<(std::is_base_of<TBase, TItem>::value), TItem&>
emplaceWithData(size_t extraDataSize, Args... args);
private:
@@ -103,7 +103,7 @@
template <typename TBase>
template <typename TItem, typename... Args>
-inline SK_WHEN((std::is_base_of<TBase, TItem>::value), TItem&)
+inline std::enable_if_t<(std::is_base_of<TBase, TItem>::value), TItem&>
GrTRecorder<TBase>::emplaceWithData(size_t extraDataSize, Args... args) {
static constexpr size_t kTAlign = alignof(TItem);
static constexpr size_t kHeaderAlign = alignof(Header);
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index f2cbbaa..9c66d39 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -211,6 +211,7 @@
case GrColorType::kRG_F16:
case GrColorType::kRGBA_8888_SRGB:
case GrColorType::kRGBA_1010102:
+ case GrColorType::kBGRA_1010102:
case GrColorType::kAlpha_F16:
case GrColorType::kRGBA_F32:
case GrColorType::kRGBA_F16:
diff --git a/src/gpu/d3d/GrD3DDescriptorHeap.cpp b/src/gpu/d3d/GrD3DDescriptorHeap.cpp
new file mode 100644
index 0000000..81c1ae6
--- /dev/null
+++ b/src/gpu/d3d/GrD3DDescriptorHeap.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/d3d/GrD3DDescriptorHeap.h"
+#include "src/gpu/d3d/GrD3DGpu.h"
+
+sk_sp<GrD3DDescriptorHeap> GrD3DDescriptorHeap::Make(GrD3DGpu* gpu, D3D12_DESCRIPTOR_HEAP_TYPE type,
+ unsigned int numDescriptors,
+ D3D12_DESCRIPTOR_HEAP_FLAGS flags) {
+ D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+ heapDesc.Type = type;
+ heapDesc.NumDescriptors = numDescriptors;
+ heapDesc.Flags = flags;
+
+ ID3D12DescriptorHeap* heap;
+ gpu->device()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&heap));
+
+ return sk_sp<GrD3DDescriptorHeap>(
+ new GrD3DDescriptorHeap(std::move(gr_cp<ID3D12DescriptorHeap>(heap)), heapDesc,
+ gpu->device()->GetDescriptorHandleIncrementSize(type)));
+}
+
+GrD3DDescriptorHeap::GrD3DDescriptorHeap(const gr_cp<ID3D12DescriptorHeap>& heap,
+ const D3D12_DESCRIPTOR_HEAP_DESC& descriptor,
+ unsigned int handleIncrementSize)
+ : fHeap(heap)
+ , fHandleIncrementSize(handleIncrementSize)
+ , fFreeBlocks(descriptor.NumDescriptors) {
+ // set all the bits in freeBlocks
+ for (UINT i = 0; i < descriptor.NumDescriptors; ++i) {
+ fFreeBlocks.set(i);
+ }
+}
+
+D3D12_CPU_DESCRIPTOR_HANDLE GrD3DDescriptorHeap::allocateCPUHandle() {
+ // valid only for non-shader-visible heaps
+ SkASSERT(!SkToBool(fHeap->GetDesc().Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
+ D3D12_CPU_DESCRIPTOR_HANDLE handle = fHeap->GetCPUDescriptorHandleForHeapStart();
+ int freeIndex = fFreeBlocks.leadingBitIndex();
+ SkASSERT(freeIndex >= 0);
+ handle.ptr += freeIndex * fHandleIncrementSize;
+ fFreeBlocks.clear(freeIndex);
+
+ return handle;
+}
+
+D3D12_GPU_DESCRIPTOR_HANDLE GrD3DDescriptorHeap::allocateGPUHandle() {
+ D3D12_GPU_DESCRIPTOR_HANDLE handle = fHeap->GetGPUDescriptorHandleForHeapStart();
+ int freeIndex = fFreeBlocks.leadingBitIndex();
+ SkASSERT(freeIndex >= 0);
+ handle.ptr += freeIndex * fHandleIncrementSize;
+ fFreeBlocks.clear(freeIndex);
+
+ return handle;
+}
+
+void GrD3DDescriptorHeap::freeCPUHandle(D3D12_CPU_DESCRIPTOR_HANDLE* handle) {
+ // valid only for non-shader-visible heaps
+ SkASSERT(!SkToBool(fHeap->GetDesc().Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
+ D3D12_CPU_DESCRIPTOR_HANDLE heapStart = fHeap->GetCPUDescriptorHandleForHeapStart();
+ // Make sure this handle belongs to this heap
+ SkASSERT(handle->ptr >= heapStart.ptr);
+ SIZE_T index = (handle->ptr - heapStart.ptr) / fHandleIncrementSize;
+ SkASSERT(index < fHeap->GetDesc().NumDescriptors);
+ fFreeBlocks.set(index);
+ handle->ptr = 0;
+}
+
+void GrD3DDescriptorHeap::freeGPUHandle(D3D12_GPU_DESCRIPTOR_HANDLE* handle) {
+ D3D12_GPU_DESCRIPTOR_HANDLE heapStart = fHeap->GetGPUDescriptorHandleForHeapStart();
+ // Make sure this handle belongs to this heap
+ SkASSERT(handle->ptr >= heapStart.ptr);
+ SIZE_T index = (handle->ptr - heapStart.ptr) / fHandleIncrementSize;
+ SkASSERT(index < fHeap->GetDesc().NumDescriptors);
+ fFreeBlocks.set(index);
+ handle->ptr = 0;
+}
+
+#ifdef SK_TRACE_MANAGED_RESOURCES
+ /** Output a human-readable dump of this resource's information
+ */
+void GrD3DDescriptorHeap::dumpInfo() const {
+ SkDebugf("GrD3DDescriptorHeap: %d (%d refs)\n", fHeap.get(), this->getRefCnt());
+}
+#endif
+
+
diff --git a/src/gpu/d3d/GrD3DDescriptorHeap.h b/src/gpu/d3d/GrD3DDescriptorHeap.h
new file mode 100644
index 0000000..c9c8558
--- /dev/null
+++ b/src/gpu/d3d/GrD3DDescriptorHeap.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrD3DDescriptorHeap_DEFINED
+#define GrD3DDescriptorHeap_DEFINED
+
+#include "include/gpu/d3d/GrD3DTypes.h"
+#include "src/gpu/GrManagedResource.h"
+#include "src/utils/SkBitSet.h"
+
+class GrD3DGpu;
+
+class GrD3DDescriptorHeap : public GrManagedResource {
+public:
+ static sk_sp<GrD3DDescriptorHeap> Make(GrD3DGpu* gpu, D3D12_DESCRIPTOR_HEAP_TYPE,
+ unsigned int numDescriptors,
+ D3D12_DESCRIPTOR_HEAP_FLAGS);
+
+ ~GrD3DDescriptorHeap() override = default;
+
+ D3D12_CPU_DESCRIPTOR_HANDLE allocateCPUHandle(); // valid only for non-shader-visible heaps
+ D3D12_GPU_DESCRIPTOR_HANDLE allocateGPUHandle();
+ void freeCPUHandle(D3D12_CPU_DESCRIPTOR_HANDLE*);
+ void freeGPUHandle(D3D12_GPU_DESCRIPTOR_HANDLE*);
+
+#ifdef SK_TRACE_MANAGED_RESOURCES
+ /** Output a human-readable dump of this resource's information
+ */
+ void dumpInfo() const override;
+#endif
+
+ void freeGPUData() const override {}
+
+private:
+ GrD3DDescriptorHeap(const gr_cp<ID3D12DescriptorHeap>&, const D3D12_DESCRIPTOR_HEAP_DESC&,
+ unsigned int handleIncrementSize);
+
+ gr_cp<ID3D12DescriptorHeap> fHeap;
+ size_t fHandleIncrementSize;
+ SkBitSet fFreeBlocks;
+};
+
+#endif
diff --git a/src/gpu/dawn/GrDawnGpu.cpp b/src/gpu/dawn/GrDawnGpu.cpp
index 68fc496..d8a1ac0 100644
--- a/src/gpu/dawn/GrDawnGpu.cpp
+++ b/src/gpu/dawn/GrDawnGpu.cpp
@@ -348,7 +348,7 @@
memset(defaultStorage.get(), 0, baseLayerSize);
}
wgpu::Device device = this->device();
- wgpu::CommandEncoder copyEncoder = fDevice.CreateCommandEncoder();
+ wgpu::CommandEncoder copyEncoder = this->getCopyEncoder();
int w = dimensions.width(), h = dimensions.height();
for (uint32_t i = 0; i < desc.mipLevelCount; i++) {
size_t origRowBytes = bpp * w;
@@ -369,8 +369,8 @@
wgpu::BufferCopyView srcBuffer;
srcBuffer.buffer = static_cast<GrDawnStagingBuffer*>(stagingBuffer.fBuffer)->buffer();
srcBuffer.offset = stagingBuffer.fOffset;
- srcBuffer.rowPitch = rowBytes;
- srcBuffer.imageHeight = h;
+ srcBuffer.bytesPerRow = rowBytes;
+ srcBuffer.rowsPerImage = h;
wgpu::TextureCopyView dstTexture;
dstTexture.texture = tex;
dstTexture.mipLevel = i;
@@ -380,8 +380,6 @@
w = std::max(1, w / 2);
h = std::max(1, h / 2);
}
- wgpu::CommandBuffer cmdBuf = copyEncoder.Finish();
- fQueue.Submit(1, &cmdBuf);
GrDawnTextureInfo info;
info.fTexture = tex;
info.fFormat = desc.format;
@@ -559,8 +557,8 @@
wgpu::BufferCopyView dstBuffer;
dstBuffer.buffer = buf;
dstBuffer.offset = 0;
- dstBuffer.rowPitch = rowBytes;
- dstBuffer.imageHeight = height;
+ dstBuffer.bytesPerRow = rowBytes;
+ dstBuffer.rowsPerImage = height;
wgpu::Extent3D copySize = {(uint32_t) width, (uint32_t) height, 1};
this->getCopyEncoder().CopyTextureToBuffer(&srcTexture, &dstBuffer, ©Size);
diff --git a/src/gpu/dawn/GrDawnTexture.cpp b/src/gpu/dawn/GrDawnTexture.cpp
index 5acc963..50b1c62 100644
--- a/src/gpu/dawn/GrDawnTexture.cpp
+++ b/src/gpu/dawn/GrDawnTexture.cpp
@@ -158,8 +158,8 @@
wgpu::BufferCopyView srcBuffer;
srcBuffer.buffer = static_cast<GrDawnStagingBuffer*>(slice.fBuffer)->buffer();
srcBuffer.offset = slice.fOffset;
- srcBuffer.rowPitch = dstRowBytes;
- srcBuffer.imageHeight = height;
+ srcBuffer.bytesPerRow = dstRowBytes;
+ srcBuffer.rowsPerImage = height;
wgpu::TextureCopyView dstTexture;
dstTexture.texture = fInfo.fTexture;
diff --git a/src/gpu/geometry/GrShape.cpp b/src/gpu/geometry/GrShape.cpp
new file mode 100644
index 0000000..d656021
--- /dev/null
+++ b/src/gpu/geometry/GrShape.cpp
@@ -0,0 +1,453 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/geometry/GrShape.h"
+
+#include "src/core/SkPathPriv.h"
+
+GrShape& GrShape::operator=(const GrShape& shape) {
+ switch(shape.type()) {
+ case Type::kEmpty:
+ this->reset();
+ break;
+ case Type::kPoint:
+ this->setPoint(shape.fPoint);
+ break;
+ case Type::kRect:
+ this->setRect(shape.fRect);
+ break;
+ case Type::kRRect:
+ this->setRRect(shape.fRRect);
+ break;
+ case Type::kPath:
+ this->setPath(shape.fPath);
+ break;
+ case Type::kArc:
+ this->setArc(shape.fArc);
+ break;
+ case Type::kLine:
+ this->setLine(shape.fLine);
+ break;
+ default:
+ SkUNREACHABLE;
+ }
+
+ fStart = shape.fStart;
+ fCW = shape.fCW;
+ fInverted = shape.fInverted;
+
+ return *this;
+}
+
+uint32_t GrShape::stateKey() const {
+ // Use the path's full fill type instead of just whether or not it's inverted.
+ uint32_t key = this->isPath() ? static_cast<uint32_t>(fPath.getFillType())
+ : (fInverted ? 1 : 0);
+ key |= ((uint32_t) fType) << 2; // fill type was 2 bits
+ key |= fStart << 5; // type was 3 bits, total 5 bits so far
+ key |= (fCW ? 1 : 0) << 8; // start was 3 bits, total 8 bits so far
+ return key;
+}
+
+bool GrShape::simplifyPath(unsigned flags) {
+ SkASSERT(this->isPath());
+
+ SkRect rect;
+ SkRRect rrect;
+ SkPoint pts[2];
+
+ SkPathDirection dir;
+ unsigned start;
+
+ if (fPath.isEmpty()) {
+ this->setType(Type::kEmpty);
+ return false;
+ } else if (fPath.isLine(pts)) {
+ this->simplifyLine(pts[0], pts[1], flags);
+ return false;
+ } else if (SkPathPriv::IsRRect(fPath, &rrect, &dir, &start)) {
+ this->simplifyRRect(rrect, dir, start, flags);
+ return true;
+ } else if (SkPathPriv::IsOval(fPath, &rect, &dir, &start)) {
+ // Convert to rrect indexing since oval is not represented explicitly
+ this->simplifyRRect(SkRRect::MakeOval(rect), dir, start * 2, flags);
+ return true;
+ } else if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) {
+ // When there is a path effect we restrict rect detection to the narrower API that
+ // gives us the starting position. Otherwise, we will retry with the more aggressive
+ // isRect().
+ this->simplifyRect(rect, dir, start, flags);
+ return true;
+ } else if (flags & kIgnoreWinding_Flag) {
+ // Attempt isRect() since we don't have to preserve any winding info
+ bool closed;
+ if (fPath.isRect(&rect, &closed) && (closed || (flags & kSimpleFill_Flag))) {
+ this->simplifyRect(rect, kDefaultDir, kDefaultStart, flags);
+ return true;
+ }
+ }
+ // No further simplification for a path. For performance reasons, we don't query the path to
+ // determine it was closed, as whether or not it was closed when it remains a path type is not
+ // important for styling.
+ return false;
+}
+
+bool GrShape::simplifyArc(unsigned flags) {
+ SkASSERT(this->isArc());
+
+ // Arcs can simplify to rrects, lines, points, or empty; regardless of what it simplifies to
+ // it was closed if went through the center point.
+ bool wasClosed = fArc.fUseCenter;
+ if (fArc.fOval.isEmpty() || !fArc.fSweepAngle) {
+ if (flags & kSimpleFill_Flag) {
+ // Go straight to empty, since the other degenerate shapes all have 0 area anyway.
+ this->setType(Type::kEmpty);
+ } else if (!fArc.fSweepAngle) {
+ SkPoint center = {fArc.fOval.centerX(), fArc.fOval.centerY()};
+ SkScalar startRad = SkDegreesToRadians(fArc.fStartAngle);
+ SkPoint start = {center.fX + 0.5f * fArc.fOval.width() * SkScalarCos(startRad),
+ center.fY + 0.5f * fArc.fOval.height() * SkScalarSin(startRad)};
+ // Either just the starting point, or a line from the center to the start
+ if (fArc.fUseCenter) {
+ this->simplifyLine(center, start, flags);
+ } else {
+ this->simplifyPoint(start, flags);
+ }
+ } else {
+ // TODO: Theoretically, we could analyze the arc projected into the empty bounds to
+ // determine a line, but that is somewhat complex for little value (since the arc
+ // can backtrack on itself if the sweep angle is large enough).
+ this->setType(Type::kEmpty);
+ }
+ } else {
+ if ((flags & kSimpleFill_Flag) || ((flags & kIgnoreWinding_Flag) && !fArc.fUseCenter)) {
+ // Eligible to turn into an oval if it sweeps a full circle
+ if (fArc.fSweepAngle <= -360.f || fArc.fSweepAngle >= 360.f) {
+ this->simplifyRRect(SkRRect::MakeOval(fArc.fOval),
+ kDefaultDir, kDefaultStart, flags);
+ return true;
+ }
+ }
+
+ if (flags & kMakeCanonical_Flag) {
+ // Map start to 0 to 360, sweep is always positive
+ if (fArc.fSweepAngle < 0) {
+ fArc.fStartAngle = fArc.fStartAngle + fArc.fSweepAngle;
+ fArc.fSweepAngle = -fArc.fSweepAngle;
+ }
+
+ if (fArc.fStartAngle < 0 || fArc.fStartAngle >= 360.f) {
+ fArc.fStartAngle = SkScalarMod(fArc.fStartAngle, 360.f);
+ }
+ }
+ }
+
+ return wasClosed;
+}
+
+void GrShape::simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start,
+ unsigned flags) {
+ if (rrect.isEmpty() || rrect.isRect()) {
+ // Change index from rrect to rect
+ start = ((start + 1) / 2) % 4;
+ this->simplifyRect(rrect.rect(), dir, start, flags);
+ } else if (!this->isRRect()) {
+ this->setType(Type::kRRect);
+ fRRect = rrect;
+ this->setPathWindingParams(dir, start);
+ // A round rect is already canonical, so there's nothing more to do
+ } else {
+ // If starting as a round rect, the provided rrect/winding params should be already set
+ SkASSERT(fRRect == rrect && this->dir() == dir && this->startIndex() == start);
+ }
+}
+
+void GrShape::simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start,
+ unsigned flags) {
+ if (!rect.width() || !rect.height()) {
+ if (flags & kSimpleFill_Flag) {
+ // A zero area, filled shape so go straight to empty
+ this->setType(Type::kEmpty);
+ } else if (!fRect.width() ^ !fRect.height()) {
+ // A line, choose the first point that best matches the starting index
+ SkPoint p1 = {rect.fLeft, rect.fTop};
+ SkPoint p2 = {rect.fRight, rect.fBottom};
+ if (start >= 2 && !(flags & kIgnoreWinding_Flag)) {
+ using std::swap;
+ swap(p1, p2);
+ }
+ this->simplifyLine(p1, p2, flags);
+ } else {
+ // A point (all edges are equal, so start+dir doesn't affect choice)
+ this->simplifyPoint({rect.fLeft, rect.fTop}, flags);
+ }
+ } else {
+ if (!this->isRect()) {
+ this->setType(Type::kRect);
+ fRect = rect;
+ this->setPathWindingParams(dir, start);
+ } else {
+ // If starting as a rect, the provided rect/winding params should already be set
+ SkASSERT(fRect == rect && this->dir() == dir && this->startIndex() == start);
+ }
+ if (flags & kMakeCanonical_Flag) {
+ fRect.sort();
+ }
+ }
+}
+
+void GrShape::simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags) {
+ if (flags & kSimpleFill_Flag) {
+ this->setType(Type::kEmpty);
+ } else if (p1 == p2) {
+ this->simplifyPoint(p1, false);
+ } else {
+ if (!this->isLine()) {
+ this->setType(Type::kLine);
+ fLine.fP1 = p1;
+ fLine.fP2 = p2;
+ } else {
+ // If starting as a line, the provided points should already be set
+ SkASSERT(fLine.fP1 == p1 && fLine.fP2 == p2);
+ }
+ if (flags & kMakeCanonical_Flag) {
+ // Sort the end points
+ if (fLine.fP2.fY < fLine.fP1.fY ||
+ (fLine.fP2.fY == fLine.fP1.fY && fLine.fP2.fX < fLine.fP1.fX)) {
+ using std::swap;
+ swap(fLine.fP1, fLine.fP2);
+ }
+ }
+ }
+}
+
+void GrShape::simplifyPoint(const SkPoint& point, unsigned flags) {
+ if (flags & kSimpleFill_Flag) {
+ this->setType(Type::kEmpty);
+ } else if (!this->isPoint()) {
+ this->setType(Type::kPoint);
+ fPoint = point;
+ } else {
+ // If starting as a point, the provided position should already be set
+ SkASSERT(point == fPoint);
+ }
+}
+
+bool GrShape::simplify(unsigned flags) {
+ // Sanity check that winding parameters are valid for the current type.
+ SkASSERT((fType == Type::kRect || fType == Type::kRRect) ||
+ (this->dir() == kDefaultDir && this->startIndex() == kDefaultStart));
+
+ // The type specific functions automatically fall through to the simpler shapes, so
+ // we only need to start in the right place.
+ bool wasClosed = false;
+ switch(fType) {
+ case Type::kEmpty:
+ // do nothing
+ break;
+ case Type::kPoint:
+ this->simplifyPoint(fPoint, flags);
+ break;
+ case Type::kLine:
+ this->simplifyLine(fLine.fP1, fLine.fP2, flags);
+ break;
+ case Type::kRect:
+ this->simplifyRect(fRect, this->dir(), this->startIndex(), flags);
+ wasClosed = true;
+ break;
+ case Type::kRRect:
+ this->simplifyRRect(fRRect, this->dir(), this->startIndex(), flags);
+ wasClosed = true;
+ break;
+ case Type::kPath:
+ wasClosed = this->simplifyPath(flags);
+ break;
+ case Type::kArc:
+ wasClosed = this->simplifyArc(flags);
+ break;
+
+ default:
+ SkUNREACHABLE;
+ }
+
+ if (((flags & kIgnoreWinding_Flag) || (fType != Type::kRect && fType != Type::kRRect))) {
+ // Reset winding parameters if we don't need them anymore
+ this->setPathWindingParams(kDefaultDir, kDefaultStart);
+ }
+
+ return wasClosed;
+}
+
+bool GrShape::contains(const SkRect& rect) const {
+ switch(this->type()) {
+ case Type::kEmpty:
+ case Type::kPoint: // fall through since a point has 0 area
+ case Type::kLine: // fall through, "" (currently choosing not to test if 'rect' == line)
+ return false;
+ case Type::kRect:
+ return fRect.contains(rect);
+ case Type::kRRect:
+ return fRRect.contains(rect);
+ case Type::kPath:
+ return fPath.conservativelyContainsRect(rect);
+ case Type::kArc:
+ if (fArc.fUseCenter) {
+ SkPath arc;
+ this->asPath(&arc);
+ return arc.conservativelyContainsRect(rect);
+ } else {
+ return false;
+ }
+ default:
+ SkUNREACHABLE;
+ }
+}
+
+bool GrShape::closed() const {
+ switch(this->type()) {
+ case Type::kEmpty: // fall through
+ case Type::kRect: // fall through
+ case Type::kRRect:
+ return true;
+ case Type::kPath:
+ // SkPath doesn't keep track of the closed status of each contour.
+ return SkPathPriv::IsClosedSingleContour(fPath);
+ case Type::kArc:
+ return fArc.fUseCenter;
+ case Type::kPoint: // fall through
+ case Type::kLine:
+ return false;
+ default:
+ SkUNREACHABLE;
+ }
+}
+
+bool GrShape::convex(bool simpleFill) const {
+ switch(this->type()) {
+ case Type::kEmpty: // fall through
+ case Type::kRect: // fall through
+ case Type::kRRect:
+ return true;
+ case Type::kPath:
+ // SkPath.isConvex() really means "is this path convex were it to be closed".
+ // Convex paths may only have one contour hence isLastContourClosed() is sufficient.
+ return (simpleFill || fPath.isLastContourClosed()) && fPath.isConvex();
+ case Type::kArc:
+ return SkPathPriv::DrawArcIsConvex(fArc.fSweepAngle, fArc.fUseCenter, simpleFill);
+ case Type::kPoint: // fall through
+ case Type::kLine:
+ return false;
+ default:
+ SkUNREACHABLE;
+ }
+}
+
+SkRect GrShape::bounds() const {
+ // Bounds where left == bottom or top == right can indicate a line or point shape. We return
+ // inverted bounds for a truly empty shape.
+ static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
+ switch(this->type()) {
+ case Type::kEmpty:
+ return kInverted;
+ case Type::kPoint:
+ return {fPoint.fX, fPoint.fY, fPoint.fX, fPoint.fY};
+ case Type::kRect:
+ return fRect.makeSorted();
+ case Type::kRRect:
+ return fRRect.getBounds();
+ case Type::kPath:
+ return fPath.getBounds();
+ case Type::kArc:
+ return fArc.fOval;
+ case Type::kLine: {
+ SkRect b = SkRect::MakeLTRB(fLine.fP1.fX, fLine.fP1.fY,
+ fLine.fP2.fX, fLine.fP2.fY);
+ b.sort();
+ return b; }
+ default:
+ SkUNREACHABLE;
+ }
+}
+
+uint32_t GrShape::segmentMask() const {
+ // In order to match what a path would report, this has to inspect the shapes slightly
+ // to reflect what they might simplify to.
+ switch(this->type()) {
+ case Type::kEmpty:
+ return 0;
+ case Type::kRRect:
+ if (fRRect.isEmpty() || fRRect.isRect()) {
+ return SkPath::kLine_SegmentMask;
+ } else if (fRRect.isOval()) {
+ return SkPath::kConic_SegmentMask;
+ } else {
+ return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
+ }
+ case Type::kPath:
+ return fPath.getSegmentMasks();
+ case Type::kArc:
+ if (fArc.fUseCenter) {
+ return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
+ } else {
+ return SkPath::kConic_SegmentMask;
+ }
+ case Type::kPoint: // fall through
+ case Type::kLine: // ""
+ case Type::kRect:
+ return SkPath::kLine_SegmentMask;
+ default:
+ SkUNREACHABLE;
+ }
+}
+
+void GrShape::asPath(SkPath* out, bool simpleFill) const {
+ if (!this->isPath() && !this->isArc()) {
+ // When not a path, we need to set fill type on the path to match invertedness.
+ // All the non-path geometries produce equivalent shapes with either even-odd or winding
+ // so we can use the default fill type.
+ out->reset();
+ out->setFillType(kDefaultFillType);
+ if (fInverted) {
+ out->toggleInverseFillType();
+ }
+ } // Else when we're already a path, that will assign the fill type directly to 'out'.
+
+ switch(this->type()) {
+ case Type::kEmpty:
+ return;
+ case Type::kPoint:
+ // A plain moveTo() or moveTo+close() does not match the expected path for a
+ // point that is being dashed (see SkDashPath's handling of zero-length segments).
+ out->moveTo(fPoint);
+ out->lineTo(fPoint);
+ return;
+ case Type::kRect:
+ out->addRect(fRect, this->dir(), this->startIndex());
+ return;
+ case Type::kRRect:
+ out->addRRect(fRRect, this->dir(), this->startIndex());
+ return;
+ case Type::kPath:
+ *out = fPath;
+ return;
+ case Type::kArc:
+ SkPathPriv::CreateDrawArcPath(out, fArc.fOval, fArc.fStartAngle, fArc.fSweepAngle,
+ fArc.fUseCenter, simpleFill);
+ // CreateDrawArcPath resets the output path and configures its fill type, so we just
+ // have to ensure invertedness is correct.
+ if (fInverted) {
+ out->toggleInverseFillType();
+ }
+ return;
+ case Type::kLine:
+ out->moveTo(fLine.fP1);
+ out->lineTo(fLine.fP2);
+ return;
+ default:
+ SkUNREACHABLE;
+ }
+}
diff --git a/src/gpu/geometry/GrShape.h b/src/gpu/geometry/GrShape.h
new file mode 100644
index 0000000..857af35
--- /dev/null
+++ b/src/gpu/geometry/GrShape.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrShape_DEFINED
+#define GrShape_DEFINED
+
+#include "include/core/SkPath.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRRect.h"
+#include "include/core/SkRect.h"
+
+// Represents an arc along an oval boundary, or a closed wedge of the oval.
+struct GrArc {
+ SkRect fOval; // The sorted, bounding box defining the oval the arc is traced along
+ SkScalar fStartAngle; // In degrees
+ SkScalar fSweepAngle; // In degrees
+ bool fUseCenter; // True if the arc includes the center point of the oval
+};
+
+// Represents a line segment between two points.
+struct GrLineSegment {
+ SkPoint fP1;
+ SkPoint fP2;
+};
+
+/**
+ * GrShape is a convenience class to represent the many different specialized geometries that
+ * Ganesh can handle, including rects, round rects, lines, as well as paths. It is intended as
+ * a data-only class where any additional complex behavior is handled by an owning type (e.g.
+ * GrStyledShape). However, it does include some basic utilities that unify common functionality
+ * (such as contains()) from the underlying shape types.
+ *
+ * In order to have lossless simplification of the geometry, it also tracks winding direction, start
+ * index, and fill inversion. The direction and index are match the SkPath indexing scheme for
+ * the shape's type (e.g. rect, rrect, or oval).
+ *
+ * Regarding GrShape's empty shape:
+ * - GrShape uses empty to refer to the absence of any geometric data
+ * - SkRect::isEmpty() returns true if the rect is not sorted, even if it has area. GrShape will not
+ * simplify these shapes to an empty GrShape. Rects with actual 0 width and height will simplify
+ * to a point or line, not empty. This is to preserve geometric data for path effects and strokes.
+ * - SkRRect::isEmpty() is true when the bounds have 0 width or height, so GrShape will simplify it
+ * to a point or line, just like a rect. SkRRect does not have the concept of unsorted edges.
+ */
+class GrShape {
+public:
+ // The current set of types GrShape can represent directly
+ enum class Type : uint8_t {
+ kEmpty, kPoint, kRect, kRRect, kPath, kArc, kLine, kLast = kLine
+ };
+ static constexpr int kTypeCount = static_cast<int>(Type::kLast) + 1;
+
+ // The direction and start index used when a shape does not have a representable winding,
+ // or when that information was discarded during simplification (kIgnoreWinding_Flag).
+ static constexpr SkPathDirection kDefaultDir = SkPathDirection::kCW;
+ static constexpr unsigned kDefaultStart = 0;
+ // The fill rule that is used by asPath() for shapes that aren't already a path.
+ static constexpr SkPathFillType kDefaultFillType = SkPathFillType::kEvenOdd;
+
+ GrShape() {}
+ explicit GrShape(const SkPoint& point) { this->setPoint(point); }
+ explicit GrShape(const SkRect& rect) { this->setRect(rect); }
+ explicit GrShape(const SkRRect& rrect) { this->setRRect(rrect); }
+ explicit GrShape(const SkPath& path) { this->setPath(path); }
+ explicit GrShape(const GrArc& arc) { this->setArc(arc); }
+ explicit GrShape(const GrLineSegment& line){ this->setLine(line); }
+
+ explicit GrShape(const GrShape& shape) { *this = shape; }
+
+ ~GrShape() { this->reset(); }
+
+ // NOTE: None of the geometry types benefit from move semantics, so we don't bother
+ // defining a move assignment operator for GrShape.
+ GrShape& operator=(const GrShape& shape);
+
+ // These type queries reflect the shape type provided when assigned, it does not incorporate
+ // any potential simplification (e.g. if isRRect() is true and rrect().isRect() is true,
+ // isRect() will still be false, until simplify() is called).
+ bool isEmpty() const { return this->type() == Type::kEmpty; }
+ bool isPoint() const { return this->type() == Type::kPoint; }
+ bool isRect() const { return this->type() == Type::kRect; }
+ bool isRRect() const { return this->type() == Type::kRRect; }
+ bool isPath() const { return this->type() == Type::kPath; }
+ bool isArc() const { return this->type() == Type::kArc; }
+ bool isLine() const { return this->type() == Type::kLine; }
+
+ Type type() const { return fType; }
+
+ // Report the shape type, winding direction, start index, and invertedness as a value suitable
+ // for use in a resource key. This does not include any geometry coordinates into the key value.
+ uint32_t stateKey() const;
+
+ // Whether or not the shape is meant to be the inverse of its geometry (i.e. its exterior).
+ bool inverted() const {
+ return this->isPath() ? fPath.isInverseFillType() : SkToBool(fInverted);
+ }
+
+ // Returns the path direction extracted from the path during simplification, if the shape's
+ // type represents a rrect, rect, or oval.
+ SkPathDirection dir() const { return fCW ? SkPathDirection::kCW : SkPathDirection::kCCW; }
+ // Returns the start index extracted from the path during simplification, if the shape's
+ // type represents a rrect, rect, or oval.
+ unsigned startIndex() const { return fStart; }
+
+ // Override the direction and start parameters for the simplified contour. These are only
+ // meaningful for rects, rrects, and ovals.
+ void setPathWindingParams(SkPathDirection dir, unsigned start) {
+ SkASSERT((this->isRect() && start < 4) || (this->isRRect() && start < 8) ||
+ (dir == kDefaultDir && start == kDefaultStart));
+ fCW = dir == SkPathDirection::kCW;
+ fStart = static_cast<uint8_t>(start);
+ }
+
+ void setInverted(bool inverted) {
+ if (this->isPath()) {
+ if (inverted != fPath.isInverseFillType()) {
+ fPath.toggleInverseFillType();
+ }
+ } else {
+ fInverted = static_cast<uint16_t>(inverted);
+ }
+ }
+
+ // Access the actual geometric description of the shape. May only access the appropriate type
+ // based on what was last set. The type may change after simplify() is called.
+ SkPoint& point() { SkASSERT(this->isPoint()); return fPoint; }
+ const SkPoint& point() const { SkASSERT(this->isPoint()); return fPoint; }
+
+ SkRect& rect() { SkASSERT(this->isRect()); return fRect; }
+ const SkRect& rect() const { SkASSERT(this->isRect()); return fRect; }
+
+ SkRRect& rrect() { SkASSERT(this->isRRect()); return fRRect; }
+ const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; }
+
+ SkPath& path() { SkASSERT(this->isPath()); return fPath; }
+ const SkPath& path() const { SkASSERT(this->isPath()); return fPath; }
+
+ GrArc& arc() { SkASSERT(this->isArc()); return fArc; }
+ const GrArc& arc() const { SkASSERT(this->isArc()); return fArc; }
+
+ GrLineSegment& line() { SkASSERT(this->isLine()); return fLine; }
+ const GrLineSegment& line() const { SkASSERT(this->isLine()); return fLine; }
+
+ // Update the geometry stored in the GrShape and update its associated type to match. This
+ // performs no simplification, so calling setRRect() with a round rect that has isRect() return
+ // true will still be considered an rrect by this shape until simplify() is called.
+ //
+ // These also reset any extracted direction, start, and inverted state from a prior simplified
+ // path, since these functions ared used to describe a new geometry.
+ void setPoint(const SkPoint& point) {
+ this->reset(Type::kPoint);
+ fPoint = point;
+ }
+ void setRect(const SkRect& rect) {
+ this->reset(Type::kRect);
+ fRect = rect;
+ }
+ void setRRect(const SkRRect& rrect) {
+ this->reset(Type::kRRect);
+ fRRect = rrect;
+ }
+ void setArc(const GrArc& arc) {
+ this->reset(Type::kArc);
+ fArc = arc;
+ }
+ void setLine(const GrLineSegment& line) {
+ this->reset(Type::kLine);
+ fLine = line;
+ }
+ void setPath(const SkPath& path) {
+ if (this->isPath()) {
+ // Assign directly
+ fPath = path;
+ } else {
+ // In-place initialize
+ this->setType(Type::kPath);
+ new (&fPath) SkPath(path);
+ }
+ // Must also set these since we didn't call reset() like other setX functions.
+ this->setPathWindingParams(kDefaultDir, kDefaultStart);
+ fInverted = path.isInverseFillType();
+ }
+ void reset() {
+ this->setType(Type::kEmpty);
+ }
+
+ // Flags that enable more aggressive, "destructive" simplifications to the geometry
+ enum SimplifyFlags : unsigned {
+ // If set, it is assumed the original shape would have been implicitly filled when drawn or
+ // clipped, so simpler shape types that are closed can still be considered. Shapes with
+ // 0 area (i.e. points and lines) can be turned into empty.
+ kSimpleFill_Flag = 0b001,
+ // If set, simplifications that would impact a directional stroke or path effect can still
+ // be taken (e.g. dir and start are not required, arcs can be converted to ovals).
+ kIgnoreWinding_Flag = 0b010,
+ // If set, the geometry will be updated to have sorted coordinates (rects, lines), modulated
+ // sweep angles (arcs).
+ kMakeCanonical_Flag = 0b100,
+
+ kAll_Flags = 0b111
+ };
+ // Returns true if the shape was originally closed based on type (or detected type within a
+ // path), even if the final simplification results in a point, line, or empty.
+ bool simplify(unsigned flags = kAll_Flags);
+
+ // True if the given bounding box is completely inside the shape.
+ bool contains(const SkRect& rect) const;
+
+ // True if the underlying geometry represents a closed shape, without the need for an
+ // implicit close (note that if simplified earlier with 'simpleFill' = true, a shape that was
+ // not closed may become closed).
+ bool closed() const;
+
+ // True if the underlying shape is known to be convex, assuming no other styles. If 'simpleFill'
+ // is true, it is assumed the contours will be implicitly closed when drawn or used.
+ bool convex(bool simpleFill = true) const;
+
+ // The bounding box of the shape.
+ SkRect bounds() const;
+
+ // The segment masks that describe the shape, were it to be converted to an SkPath
+ uint32_t segmentMask() const;
+
+ // Convert the shape into a path that describes the same geometry.
+ void asPath(SkPath* out, bool simpleFill = true) const;
+
+private:
+
+ void setType(Type type) {
+ if (this->isPath() && type != Type::kPath) {
+ fPath.~SkPath();
+ }
+ fType = type;
+ }
+
+ void reset(Type type) {
+ this->setType(type);
+ this->setPathWindingParams(kDefaultDir, kDefaultStart);
+ this->setInverted(false);
+ }
+
+ // Paths and arcs are root shapes, another type will never simplify to them, so they do
+ // not take the geometry to simplify as an argument. Since they are root shapes, they also
+ // return whether or not they were originally closed before being simplified.
+ bool simplifyPath(unsigned flags);
+ bool simplifyArc(unsigned flags);
+
+ // The simpler type classes do take the geometry because it may represent an in-progress
+ // simplification that hasn't been set on the GrShape yet. The simpler types do not report
+ // whether or not they were closed because it's implicit in their type.
+ void simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags);
+ void simplifyPoint(const SkPoint& point, unsigned flags);
+
+ // RRects and rects care about winding for path effects and will set the path winding state
+ // of the shape as well.
+ void simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start, unsigned flags);
+ void simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start, unsigned flags);
+
+ union {
+ SkPoint fPoint;
+ SkRect fRect;
+ SkRRect fRRect;
+ SkPath fPath;
+ GrArc fArc;
+ GrLineSegment fLine;
+ };
+
+ Type fType = Type::kEmpty;
+ uint8_t fStart; // Restricted to rrects and simpler, so this will be < 8
+ bool fCW;
+ bool fInverted;
+};
+
+#endif
diff --git a/src/gpu/geometry/GrStyledShape.cpp b/src/gpu/geometry/GrStyledShape.cpp
index d81b99e..0e87857 100644
--- a/src/gpu/geometry/GrStyledShape.cpp
+++ b/src/gpu/geometry/GrStyledShape.cpp
@@ -12,26 +12,11 @@
#include <utility>
GrStyledShape& GrStyledShape::operator=(const GrStyledShape& that) {
- fStyle = that.fStyle;
- this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
- switch (fType) {
- case Type::kEmpty:
- break;
- case Type::kInvertedEmpty:
- break;
- case Type::kRRect:
- fRRectData = that.fRRectData;
- break;
- case Type::kArc:
- fArcData = that.fArcData;
- break;
- case Type::kLine:
- fLineData = that.fLineData;
- break;
- case Type::kPath:
- fPathData.fGenID = that.fPathData.fGenID;
- break;
- }
+ fShape = that.fShape;
+ fStyle = that.fStyle;
+ fGenID = that.fGenID;
+ fSimplified = that.fSimplified;
+
fInheritedKey.reset(that.fInheritedKey.count());
sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
sizeof(uint32_t) * fInheritedKey.count());
@@ -43,20 +28,6 @@
return *this;
}
-static bool flip_inversion(bool originalIsInverted, GrStyledShape::FillInversion inversion) {
- switch (inversion) {
- case GrStyledShape::FillInversion::kPreserve:
- return false;
- case GrStyledShape::FillInversion::kFlip:
- return true;
- case GrStyledShape::FillInversion::kForceInverted:
- return !originalIsInverted;
- case GrStyledShape::FillInversion::kForceNoninverted:
- return originalIsInverted;
- }
- return false;
-}
-
static bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion inversion) {
switch (inversion) {
case GrStyledShape::FillInversion::kPreserve:
@@ -72,102 +43,41 @@
}
GrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInversion inversion) {
- if (original.style().isSimpleFill() && !flip_inversion(original.inverseFilled(), inversion)) {
+ bool newIsInverted = is_inverted(original.fShape.inverted(), inversion);
+ if (original.style().isSimpleFill() && newIsInverted == original.fShape.inverted()) {
// By returning the original rather than falling through we can preserve any inherited style
// key. Otherwise, we wipe it out below since the style change invalidates it.
return original;
}
GrStyledShape result;
+ SkASSERT(result.fStyle.isSimpleFill());
if (original.fInheritedPathForListeners.isValid()) {
result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get());
}
- switch (original.fType) {
- case Type::kRRect:
- result.fType = original.fType;
- result.fRRectData.fRRect = original.fRRectData.fRRect;
- result.fRRectData.fDir = kDefaultRRectDir;
- result.fRRectData.fStart = kDefaultRRectStart;
- result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
- break;
- case Type::kArc:
- result.fType = original.fType;
- result.fArcData.fOval = original.fArcData.fOval;
- result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees;
- result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees;
- result.fArcData.fUseCenter = original.fArcData.fUseCenter;
- result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion);
- break;
- case Type::kLine:
- // Lines don't fill.
- if (is_inverted(original.fLineData.fInverted, inversion)) {
- result.fType = Type::kInvertedEmpty;
- } else {
- result.fType = Type::kEmpty;
- }
- break;
- case Type::kEmpty:
- result.fType = is_inverted(false, inversion) ? Type::kInvertedEmpty : Type::kEmpty;
- break;
- case Type::kInvertedEmpty:
- result.fType = is_inverted(true, inversion) ? Type::kInvertedEmpty : Type::kEmpty;
- break;
- case Type::kPath:
- result.initType(Type::kPath, &original.fPathData.fPath);
- result.fPathData.fGenID = original.fPathData.fGenID;
- if (flip_inversion(original.fPathData.fPath.isInverseFillType(), inversion)) {
- result.fPathData.fPath.toggleInverseFillType();
- }
- if (!original.style().isSimpleFill()) {
- // Going from a non-filled style to fill may allow additional simplifications (e.g.
- // closing an open rect that wasn't closed in the original shape because it had
- // stroke style).
- result.attemptToSimplifyPath();
- }
- break;
+
+ result.fShape = original.fShape;
+ result.fGenID = original.fGenID;
+ result.fShape.setInverted(newIsInverted);
+
+ if (!original.style().isSimpleFill()) {
+ // Going from a non-filled style to fill may allow additional simplifications (e.g.
+ // closing an open rect that wasn't closed in the original shape because it had
+ // stroke style).
+ result.simplify();
+ // The above simplify() call only sets simplified to true if its geometry was changed,
+ // since it already sees its style as a simple fill. Since the original style was not a
+ // simple fill, MakeFilled always simplifies.
+ result.fSimplified = true;
}
+
+ // Sanity check that lines/points were converted to empty by the style change
+ SkASSERT((!original.fShape.isLine() && !original.fShape.isPoint()) || result.fShape.isEmpty());
+
// We don't copy the inherited key since it can contain path effect information that we just
// stripped.
return result;
}
-SkRect GrStyledShape::bounds() const {
- // Bounds where left == bottom or top == right can indicate a line or point shape. We return
- // inverted bounds for a truly empty shape.
- static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
- switch (fType) {
- case Type::kEmpty:
- return kInverted;
- case Type::kInvertedEmpty:
- return kInverted;
- case Type::kLine: {
- SkRect bounds;
- if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) {
- bounds.fLeft = fLineData.fPts[0].fX;
- bounds.fRight = fLineData.fPts[1].fX;
- } else {
- bounds.fLeft = fLineData.fPts[1].fX;
- bounds.fRight = fLineData.fPts[0].fX;
- }
- if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) {
- bounds.fTop = fLineData.fPts[0].fY;
- bounds.fBottom = fLineData.fPts[1].fY;
- } else {
- bounds.fTop = fLineData.fPts[1].fY;
- bounds.fBottom = fLineData.fPts[0].fY;
- }
- return bounds;
- }
- case Type::kRRect:
- return fRRectData.fRRect.getBounds();
- case Type::kArc:
- // Could make this less conservative by looking at angles.
- return fArcData.fOval;
- case Type::kPath:
- return this->path().getBounds();
- }
- SK_ABORT("Unknown shape type");
-}
-
SkRect GrStyledShape::styledBounds() const {
if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
return SkRect::MakeEmpty();
@@ -189,9 +99,9 @@
static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
static_assert(sizeof(SkScalar) == sizeof(uint32_t));
- // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to
+ // 1 is for the verb count. Each verb is a byte but we'll pad the verb data out to
// a uint32_t length.
- return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
+ return 1 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
}
// Writes the path data key into the passed pointer.
@@ -203,7 +113,6 @@
const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
SkASSERT(verbCnt <= GrStyledShape::kMaxKeyFromDataVerbCnt);
SkASSERT(pointCnt && verbCnt);
- *key++ = (uint32_t)path.getFillType();
*key++ = verbCnt;
memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
int verbKeySize = SkAlign4(verbCnt);
@@ -225,37 +134,45 @@
if (fInheritedKey.count()) {
return fInheritedKey.count();
}
- switch (fType) {
- case Type::kEmpty:
- return 1;
- case Type::kInvertedEmpty:
- return 1;
- case Type::kRRect:
- SkASSERT(!fInheritedKey.count());
+
+ int count = 1; // Every key has the state flags from the GrShape
+ switch(fShape.type()) {
+ case GrShape::Type::kPoint:
+ static_assert(0 == sizeof(SkPoint) % sizeof(uint32_t));
+ count += sizeof(SkPoint) / sizeof(uint32_t);
+ break;
+ case GrShape::Type::kRect:
+ static_assert(0 == sizeof(SkRect) % sizeof(uint32_t));
+ count += sizeof(SkRect) / sizeof(uint32_t);
+ break;
+ case GrShape::Type::kRRect:
static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
- // + 1 for the direction, start index, and inverseness.
- return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
- case Type::kArc:
- SkASSERT(!fInheritedKey.count());
- static_assert(0 == sizeof(fArcData) % sizeof(uint32_t));
- return sizeof(fArcData) / sizeof(uint32_t);
- case Type::kLine:
- static_assert(2 * sizeof(uint32_t) == sizeof(SkPoint));
- // 4 for the end points and 1 for the inverseness
- return 5;
- case Type::kPath: {
- if (0 == fPathData.fGenID) {
- return -1;
+ count += SkRRect::kSizeInMemory / sizeof(uint32_t);
+ break;
+ case GrShape::Type::kArc:
+ static_assert(0 == sizeof(GrArc) % sizeof(uint32_t));
+ count += sizeof(GrArc) / sizeof(uint32_t);
+ break;
+ case GrShape::Type::kLine:
+ static_assert(0 == sizeof(GrLineSegment) % sizeof(uint32_t));
+ count += sizeof(GrLineSegment) / sizeof(uint32_t);
+ break;
+ case GrShape::Type::kPath: {
+ if (0 == fGenID) {
+ return -1; // volatile, so won't be keyed
}
- int dataKeySize = path_key_from_data_size(fPathData.fPath);
+ int dataKeySize = path_key_from_data_size(fShape.path());
if (dataKeySize >= 0) {
- return dataKeySize;
+ count += dataKeySize;
+ } else {
+ count++; // Just adds the gen ID.
}
- // The key is the path ID and fill type.
- return 2;
- }
+ break; }
+ default:
+ // else it's empty, which just needs the state flags for its key
+ SkASSERT(fShape.isEmpty());
}
- SK_ABORT("Should never get here.");
+ return count;
}
void GrStyledShape::writeUnstyledKey(uint32_t* key) const {
@@ -265,43 +182,57 @@
memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
SkDEBUGCODE(key += fInheritedKey.count();)
} else {
- switch (fType) {
- case Type::kEmpty:
- *key++ = 1;
- break;
- case Type::kInvertedEmpty:
- *key++ = 2;
- break;
- case Type::kRRect:
- fRRectData.fRRect.writeToMemory(key);
- key += SkRRect::kSizeInMemory / sizeof(uint32_t);
- *key = (fRRectData.fDir == SkPathDirection::kCCW) ? (1 << 31) : 0;
- *key |= fRRectData.fInverted ? (1 << 30) : 0;
- *key++ |= fRRectData.fStart;
- SkASSERT(fRRectData.fStart < 8);
- break;
- case Type::kArc:
- memcpy(key, &fArcData, sizeof(fArcData));
- key += sizeof(fArcData) / sizeof(uint32_t);
- break;
- case Type::kLine:
- memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
- key += 4;
- *key++ = fLineData.fInverted ? 1 : 0;
- break;
- case Type::kPath: {
- SkASSERT(fPathData.fGenID);
- int dataKeySize = path_key_from_data_size(fPathData.fPath);
+ // Dir and start are only used for rect and rrect shapes, so are not included in other
+ // shape type keys. Make sure that they are the defaults for other shapes so it doesn't
+ // matter that we universally include them in the flag key value.
+ SkASSERT((fShape.isRect() || fShape.isRRect()) ||
+ (fShape.dir() == GrShape::kDefaultDir &&
+ fShape.startIndex() == GrShape::kDefaultStart));
+
+ // Every key starts with the state from the GrShape (this includes path fill type,
+ // and any tracked winding, start, inversion, as well as the class of geometry).
+ *key++ = fShape.stateKey();
+
+ switch(fShape.type()) {
+ case GrShape::Type::kPath: {
+ SkASSERT(fGenID != 0);
+ // Ensure that the path's inversion matches our state so that the path's key suffices.
+ SkASSERT(fShape.inverted() == fShape.path().isInverseFillType());
+
+ int dataKeySize = path_key_from_data_size(fShape.path());
if (dataKeySize >= 0) {
- write_path_key_from_data(fPathData.fPath, key);
+ write_path_key_from_data(fShape.path(), key);
return;
+ } else {
+ *key++ = fGenID;
}
- *key++ = fPathData.fGenID;
- // We could canonicalize the fill rule for paths that don't differentiate between
- // even/odd or winding fill (e.g. convex).
- *key++ = (uint32_t)this->path().getFillType();
+ break; }
+ case GrShape::Type::kPoint:
+ memcpy(key, &fShape.point(), sizeof(SkPoint));
+ key += sizeof(SkPoint) / sizeof(uint32_t);
break;
- }
+ case GrShape::Type::kRect:
+ memcpy(key, &fShape.rect(), sizeof(SkRect));
+ key += sizeof(SkRect) / sizeof(uint32_t);
+ break;
+ case GrShape::Type::kRRect:
+ fShape.rrect().writeToMemory(key);
+ key += SkRRect::kSizeInMemory / sizeof(uint32_t);
+ break;
+ case GrShape::Type::kArc:
+ // Write dense floats first
+ memcpy(key, &fShape.arc(), sizeof(SkRect) + 2 * sizeof(float));
+ key += (sizeof(GrArc) / sizeof(uint32_t) - 1);
+ // Then write the final bool as an int, to make sure upper bits are set
+ *key++ = fShape.arc().fUseCenter ? 1 : 0;
+ break;
+ case GrShape::Type::kLine:
+ memcpy(key, &fShape.line(), sizeof(GrLineSegment));
+ key += sizeof(GrLineSegment) / sizeof(uint32_t);
+ break;
+ default:
+ // Nothing other than the flag state is needed in the key for an empty shape
+ SkASSERT(fShape.isEmpty());
}
}
SkASSERT(key - origKey == this->unstyledKeySize());
@@ -311,7 +242,7 @@
SkScalar scale) {
SkASSERT(!fInheritedKey.count());
// If the output shape turns out to be simple, then we will just use its geometric key
- if (Type::kPath == fType) {
+ if (fShape.isPath()) {
// We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
// ApplyFullStyle(shape).
// The full key is structured as (geo,path_effect,stroke).
@@ -324,7 +255,7 @@
parentCnt = parent.unstyledKeySize();
if (parentCnt < 0) {
// The parent's geometry has no key so we will have no key.
- fPathData.fGenID = 0;
+ fGenID = 0;
return;
}
}
@@ -339,7 +270,7 @@
if (styleCnt < 0) {
// The style doesn't allow a key, set the path gen ID to 0 so that we fail when
// we try to get a key for the shape.
- fPathData.fGenID = 0;
+ fGenID = 0;
return;
}
fInheritedKey.reset(parentCnt + styleCnt);
@@ -360,8 +291,8 @@
const SkPath* GrStyledShape::originalPathForListeners() const {
if (fInheritedPathForListeners.isValid()) {
return fInheritedPathForListeners.get();
- } else if (Type::kPath == fType && !fPathData.fPath.isVolatile()) {
- return &fPathData.fPath;
+ } else if (fShape.isPath() && !fShape.path().isVolatile()) {
+ return &fShape.path();
}
return nullptr;
}
@@ -376,38 +307,17 @@
SkScalar sweepAngleDegrees, bool useCenter,
const GrStyle& style) {
GrStyledShape result;
- result.changeType(Type::kArc);
- result.fArcData.fOval = oval;
- result.fArcData.fStartAngleDegrees = startAngleDegrees;
- result.fArcData.fSweepAngleDegrees = sweepAngleDegrees;
- result.fArcData.fUseCenter = useCenter;
- result.fArcData.fInverted = false;
+ result.fShape.setArc({oval.makeSorted(), startAngleDegrees, sweepAngleDegrees, useCenter});
result.fStyle = style;
- result.attemptToSimplifyArc();
+ result.simplify();
return result;
}
-GrStyledShape::GrStyledShape(const GrStyledShape& that) : fStyle(that.fStyle) {
- const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
- this->initType(that.fType, thatPath);
- switch (fType) {
- case Type::kEmpty:
- break;
- case Type::kInvertedEmpty:
- break;
- case Type::kRRect:
- fRRectData = that.fRRectData;
- break;
- case Type::kArc:
- fArcData = that.fArcData;
- break;
- case Type::kLine:
- fLineData = that.fLineData;
- break;
- case Type::kPath:
- fPathData.fGenID = that.fPathData.fGenID;
- break;
- }
+GrStyledShape::GrStyledShape(const GrStyledShape& that)
+ : fShape(that.fShape)
+ , fStyle(that.fStyle)
+ , fGenID(that.fGenID)
+ , fSimplified(that.fSimplified) {
fInheritedKey.reset(that.fInheritedKey.count());
sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
sizeof(uint32_t) * fInheritedKey.count());
@@ -423,7 +333,6 @@
// stroke of a rect).
if (!parent.style().applies() ||
(GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
- this->initType(Type::kEmpty);
*this = parent;
return;
}
@@ -432,12 +341,14 @@
SkTLazy<SkPath> tmpPath;
const GrStyledShape* parentForKey = &parent;
SkTLazy<GrStyledShape> tmpParent;
- this->initType(Type::kPath);
- fPathData.fGenID = 0;
+
+ // Start out as an empty path that is filled in by the applied style
+ fShape.setPath(SkPath());
+
if (pe) {
const SkPath* srcForPathEffect;
- if (parent.fType == Type::kPath) {
- srcForPathEffect = &parent.path();
+ if (parent.fShape.isPath()) {
+ srcForPathEffect = &parent.fShape.path();
} else {
srcForPathEffect = tmpPath.init();
parent.asPath(tmpPath.get());
@@ -445,7 +356,7 @@
// Should we consider bounds? Would have to include in key, but it'd be nice to know
// if the bounds actually modified anything before including in key.
SkStrokeRec strokeRec = parent.fStyle.strokeRec();
- if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
+ if (!parent.fStyle.applyPathEffectToPath(&fShape.path(), &strokeRec, *srcForPathEffect,
scale)) {
tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
*this = tmpParent.get()->applyStyle(apply, scale);
@@ -462,7 +373,7 @@
// We detect that case here and change parentForKey to a temporary that represents
// the simpler shape so that applying both path effect and the strokerec all at
// once produces the same key.
- tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
+ tmpParent.init(fShape.path(), GrStyle(strokeRec, nullptr));
tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
if (!tmpPath.isValid()) {
tmpPath.init();
@@ -471,7 +382,7 @@
SkStrokeRec::InitStyle fillOrHairline;
// The parent shape may have simplified away the strokeRec, check for that here.
if (tmpParent.get()->style().applies()) {
- SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
+ SkAssertResult(tmpParent.get()->style().applyToPath(&fShape.path(), &fillOrHairline,
*tmpPath.get(), scale));
} else if (tmpParent.get()->style().isSimpleFill()) {
fillOrHairline = SkStrokeRec::kFill_InitStyle;
@@ -486,8 +397,8 @@
}
} else {
const SkPath* srcForParentStyle;
- if (parent.fType == Type::kPath) {
- srcForParentStyle = &parent.path();
+ if (parent.fShape.isPath()) {
+ srcForParentStyle = &parent.fShape.path();
} else {
srcForParentStyle = tmpPath.init();
parent.asPath(tmpPath.get());
@@ -495,275 +406,374 @@
SkStrokeRec::InitStyle fillOrHairline;
SkASSERT(parent.fStyle.applies());
SkASSERT(!parent.fStyle.pathEffect());
- SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
- scale));
+ SkAssertResult(parent.fStyle.applyToPath(&fShape.path(), &fillOrHairline,
+ *srcForParentStyle, scale));
fStyle.resetToInitStyle(fillOrHairline);
}
+
if (parent.fInheritedPathForListeners.isValid()) {
fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get());
- } else if (Type::kPath == parent.fType && !parent.fPathData.fPath.isVolatile()) {
- fInheritedPathForListeners.set(parent.fPathData.fPath);
+ } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) {
+ fInheritedPathForListeners.set(parent.fShape.path());
}
- this->attemptToSimplifyPath();
+ this->simplify();
this->setInheritedKey(*parentForKey, apply, scale);
}
-void GrStyledShape::attemptToSimplifyPath() {
- SkRect rect;
- SkRRect rrect;
- SkPathDirection rrectDir;
- unsigned rrectStart;
- bool inverted = this->path().isInverseFillType();
- SkPoint pts[2];
- if (this->path().isEmpty()) {
- // Dashing ignores inverseness skbug.com/5421.
- this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty
- : Type::kEmpty);
- } else if (this->path().isLine(pts)) {
- this->changeType(Type::kLine);
- fLineData.fPts[0] = pts[0];
- fLineData.fPts[1] = pts[1];
- fLineData.fInverted = inverted;
- } else if (SkPathPriv::IsRRect(this->path(), &rrect, &rrectDir, &rrectStart)) {
- this->changeType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fDir = rrectDir;
- fRRectData.fStart = rrectStart;
- fRRectData.fInverted = inverted;
- SkASSERT(!fRRectData.fRRect.isEmpty());
- } else if (SkPathPriv::IsOval(this->path(), &rect, &rrectDir, &rrectStart)) {
- this->changeType(Type::kRRect);
- fRRectData.fRRect.setOval(rect);
- fRRectData.fDir = rrectDir;
- fRRectData.fInverted = inverted;
- // convert from oval indexing to rrect indexiing.
- fRRectData.fStart = 2 * rrectStart;
- } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
- this->changeType(Type::kRRect);
- // When there is a path effect we restrict rect detection to the narrower API that
- // gives us the starting position. Otherwise, we will retry with the more aggressive
- // isRect().
- fRRectData.fRRect.setRect(rect);
- fRRectData.fInverted = inverted;
- fRRectData.fDir = rrectDir;
- // convert from rect indexing to rrect indexiing.
- fRRectData.fStart = 2 * rrectStart;
- } else if (!this->style().hasPathEffect()) {
- bool closed;
- if (this->path().isRect(&rect, &closed, nullptr)) {
- if (closed || this->style().isSimpleFill()) {
- this->changeType(Type::kRRect);
- fRRectData.fRRect.setRect(rect);
- // Since there is no path effect the dir and start index is immaterial.
- fRRectData.fDir = kDefaultRRectDir;
- fRRectData.fStart = kDefaultRRectStart;
- // There isn't dashing so we will have to preserver inverseness.
- fRRectData.fInverted = inverted;
+bool GrStyledShape::asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start,
+ bool* inverted) const {
+ if (!fShape.isRRect() && !fShape.isRect()) {
+ return false;
+ }
+
+ // Sanity check here, if we don't have a path effect on the style, we should have passed
+ // appropriate flags to GrShape::simplify() to have reset these parameters.
+ SkASSERT(fStyle.hasPathEffect() || (fShape.dir() == GrShape::kDefaultDir &&
+ fShape.startIndex() == GrShape::kDefaultStart));
+
+ // If the shape is a regular rect, map to round rect winding parameters, including accounting
+ // for the automatic sorting of edges that SkRRect::MakeRect() performs.
+ if (fShape.isRect()) {
+ if (rrect) {
+ *rrect = SkRRect::MakeRect(fShape.rect());
+ }
+ // Don't bother mapping these if we don't have a path effect, however.
+ if (!fStyle.hasPathEffect()) {
+ if (dir) {
+ *dir = GrShape::kDefaultDir;
+ }
+ if (start) {
+ *start = GrShape::kDefaultStart;
+ }
+ } else {
+ // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
+ // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
+ // rect edges. Thus, we may need to modify the rrect's start index and direction.
+ SkPathDirection rectDir = fShape.dir();
+ unsigned rectStart = fShape.startIndex();
+
+ if (fShape.rect().fLeft > fShape.rect().fRight) {
+ // Toggle direction, and modify index by mapping through the array
+ static const unsigned kMapping[] = {1, 0, 3, 2};
+ rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
+ : SkPathDirection::kCCW;
+ rectStart = kMapping[rectStart];
+ }
+ if (fShape.rect().fTop > fShape.rect().fBottom) {
+ // Toggle direction and map index by 3 - start
+ // NOTE: if we earlier flipped for X as well, this results in no net direction
+ // change and effectively flipping the start index to the diagonal corners of the
+ // rect (matching what we'd expect for a rect with both X and Y flipped).
+ rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
+ : SkPathDirection::kCCW;
+ rectStart = 3 - rectStart;
+ }
+
+ if (dir) {
+ *dir = rectDir;
+ }
+ if (start) {
+ // Convert to round rect indexing
+ *start = 2 * rectStart;
+ }
+ }
+ } else {
+ // Straight forward export
+ if (rrect) {
+ *rrect = fShape.rrect();
+ }
+ if (dir) {
+ *dir = fShape.dir();
+ }
+ if (start) {
+ *start = fShape.startIndex();
+ // Canonicalize the index if the rrect is an oval, which GrShape doesn't treat special
+ // but we do for dashing placement
+ if (fShape.rrect().isOval()) {
+ *start &= 0b110;
}
}
}
- if (Type::kPath != fType) {
+
+ if (inverted) {
+ *inverted = fShape.inverted();
+ }
+
+ return true;
+}
+
+bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
+ if (!fShape.isLine()) {
+ return false;
+ }
+
+ if (pts) {
+ pts[0] = fShape.line().fP1;
+ pts[1] = fShape.line().fP2;
+ }
+ if (inverted) {
+ *inverted = fShape.inverted();
+ }
+ return true;
+}
+
+bool GrStyledShape::asNestedRects(SkRect rects[2]) const {
+ if (!fShape.isPath()) {
+ return false;
+ }
+
+ // TODO: it would be better two store DRRects natively in the shape rather than converting
+ // them to a path and then reextracting the nested rects
+ if (fShape.path().isInverseFillType()) {
+ return false;
+ }
+
+ SkPathDirection dirs[2];
+ if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
+ return false;
+ }
+
+ if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
+ // The two rects need to be wound opposite to each other
+ return false;
+ }
+
+ // Right now, nested rects where the margin is not the same width
+ // all around do not render correctly
+ const SkScalar* outer = rects[0].asScalars();
+ const SkScalar* inner = rects[1].asScalars();
+
+ bool allEq = true;
+
+ SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
+ bool allGoE1 = margin >= SK_Scalar1;
+
+ for (int i = 1; i < 4; ++i) {
+ SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
+ if (temp < SK_Scalar1) {
+ allGoE1 = false;
+ }
+ if (!SkScalarNearlyEqual(margin, temp)) {
+ allEq = false;
+ }
+ }
+
+ return allEq || allGoE1;
+}
+
+void GrStyledShape::simplify() {
+ // Dashing ignores inverseness skbug.com/5421.
+ bool inverted = !fStyle.isDashed() && fShape.inverted();
+
+ unsigned simplifyFlags = 0;
+ if (fStyle.isSimpleFill()) {
+ simplifyFlags = GrShape::kAll_Flags;
+ } else if (!fStyle.hasPathEffect()) {
+ // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
+ if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
+ simplifyFlags |= GrShape::kIgnoreWinding_Flag;
+ }
+ simplifyFlags |= GrShape::kMakeCanonical_Flag;
+ } // else if there's a path effect, every destructive simplification is disabledd
+
+ // Remember if the original shape was closed; in the event we simplify to a point or line
+ // because of degenerate geometry, we need to update joins and caps.
+ GrShape::Type oldType = fShape.type();
+ bool wasClosed = fShape.simplify(simplifyFlags);
+ fSimplified = oldType != fShape.type();
+
+ if (fShape.isPath()) {
+ // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
+ if (fInheritedKey.count() || fShape.path().isVolatile()) {
+ fGenID = 0;
+ } else {
+ fGenID = fShape.path().getGenerationID();
+ }
+ if (!fStyle.hasNonDashPathEffect() &&
+ (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
+ fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style ||
+ fShape.path().isConvex())) {
+ // Stroke styles don't differentiate between winding and even/odd. There is no
+ // distinction between even/odd and non-zero winding count for convex paths.
+ // Moreover, dashing ignores inverseness (skbug.com/5421)
+ fShape.path().setFillType(GrShape::kDefaultFillType);
+ }
+ } else {
fInheritedKey.reset(0);
// Whenever we simplify to a non-path, break the chain so we no longer refer to the
// original path. This prevents attaching genID listeners to temporary paths created when
// drawing simple shapes.
fInheritedPathForListeners.reset();
- if (Type::kRRect == fType) {
- this->attemptToSimplifyRRect();
- } else if (Type::kLine == fType) {
- this->attemptToSimplifyLine();
- }
- } else {
- if (fInheritedKey.count() || this->path().isVolatile()) {
- fPathData.fGenID = 0;
- } else {
- fPathData.fGenID = this->path().getGenerationID();
- }
- if (!this->style().hasNonDashPathEffect()) {
- if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
- this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) {
- // Stroke styles don't differentiate between winding and even/odd.
- // Moreover, dashing ignores inverseness (skbug.com/5421)
- bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
- if (inverse) {
- this->path().setFillType(kDefaultPathInverseFillType);
- } else {
- this->path().setFillType(kDefaultPathFillType);
- }
- } else if (this->path().isConvex()) {
- // There is no distinction between even/odd and non-zero winding count for convex
- // paths.
- if (this->path().isInverseFillType()) {
- this->path().setFillType(kDefaultPathInverseFillType);
- } else {
- this->path().setFillType(kDefaultPathFillType);
- }
- }
- }
+
+ // Further simplifications to the shape based on the style
+ fSimplified |= this->simplifyStroke(wasClosed);
}
+
+ // Restore invertedness after any modifications were made to the shape type
+ fShape.setInverted(inverted);
+ SkASSERT(!fShape.isPath() || inverted == fShape.path().isInverseFillType());
}
-void GrStyledShape::attemptToSimplifyRRect() {
- SkASSERT(Type::kRRect == fType);
- SkASSERT(!fInheritedKey.count());
- if (fRRectData.fRRect.isEmpty()) {
- // An empty filled rrect is equivalent to a filled empty path with inversion preserved.
- if (fStyle.isSimpleFill()) {
- fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty;
- fStyle = GrStyle::SimpleFill();
- return;
+bool GrStyledShape::simplifyStroke(bool originallyClosed) {
+ // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
+ // becomes a round rect.
+ if (!fStyle.hasPathEffect() && fShape.isRect() &&
+ fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
+ if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
+ (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
+ fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
+ // Bevel-stroked rect needs path rendering
+ return false;
}
- // Dashing a rrect with no width or height is equivalent to filling an emtpy path.
- // When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length
- // line will produce cap geometry if the effect begins in an "on" interval.
- if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) {
- // Dashing ignores the inverseness (currently). skbug.com/5421.
- fType = Type::kEmpty;
- fStyle = GrStyle::SimpleFill();
- return;
- }
- }
- if (!this->style().hasPathEffect()) {
- fRRectData.fDir = kDefaultRRectDir;
- fRRectData.fStart = kDefaultRRectStart;
- } else if (fStyle.isDashed()) {
- // Dashing ignores the inverseness (currently). skbug.com/5421
- fRRectData.fInverted = false;
- // Possible TODO here: Check whether the dash results in a single arc or line.
- }
- // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
- if (!fStyle.hasPathEffect() &&
- fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style &&
- fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
- fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 &&
- fRRectData.fRRect.isRect()) {
+
SkScalar r = fStyle.strokeRec().getWidth() / 2;
- fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r));
- fStyle = GrStyle::SimpleFill();
- }
-}
-
-void GrStyledShape::attemptToSimplifyLine() {
- SkASSERT(Type::kLine == fType);
- SkASSERT(!fInheritedKey.count());
- if (fStyle.isDashed()) {
- bool allOffsZero = true;
- for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) {
- allOffsZero = !fStyle.dashIntervals()[i];
+ fShape.rect().outset(r, r);
+ if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
+ // There's no dashing to worry about if we got here, so it's okay that this resets
+ // winding parameters
+ fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
}
- if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) {
- return;
- }
- // Dashing ignores inverseness.
- fLineData.fInverted = false;
- return;
- } else if (fStyle.hasPathEffect()) {
- return;
- }
- if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
- // Make stroke + fill be stroke since the fill is empty.
- SkStrokeRec rec = fStyle.strokeRec();
- rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
- fStyle = GrStyle(rec, nullptr);
- }
- if (fStyle.isSimpleFill()) {
- this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty);
- return;
- }
- if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style &&
- this->attemptToSimplifyStrokedLineToRRect()) {
- return;
- }
- // Only path effects could care about the order of the points. Otherwise canonicalize
- // the point order.
- SkPoint* pts = fLineData.fPts;
- if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) {
- using std::swap;
- swap(pts[0], pts[1]);
- }
-}
-
-void GrStyledShape::attemptToSimplifyArc() {
- SkASSERT(fType == Type::kArc);
- SkASSERT(!fArcData.fInverted);
- if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) {
- this->changeType(Type::kEmpty);
- return;
- }
-
- // Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses
- // the full circle and doesn't use the center point is an oval. Unless it has square or round
- // caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're
- // ignoring that for now.
- if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter &&
- fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) {
- if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) {
- auto oval = fArcData.fOval;
- this->changeType(Type::kRRect);
- this->fRRectData.fRRect.setOval(oval);
- this->fRRectData.fDir = kDefaultRRectDir;
- this->fRRectData.fStart = kDefaultRRectStart;
- this->fRRectData.fInverted = false;
- return;
- }
- }
- if (!fStyle.pathEffect()) {
- // Canonicalize the arc such that the start is always in [0, 360) and the sweep is always
- // positive.
- if (fArcData.fSweepAngleDegrees < 0) {
- fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees;
- fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees;
- }
- }
- if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) {
- this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f);
- }
- // Possible TODOs here: Look at whether dash pattern results in a single dash and convert to
- // non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke
- // could as well if the stroke fills the center.
-}
-
-bool GrStyledShape::attemptToSimplifyStrokedLineToRRect() {
- SkASSERT(Type::kLine == fType);
- SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
-
- SkRect rect;
- SkVector outset;
- // If we allowed a rotation angle for rrects we could capture all cases here.
- if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) {
- rect.fLeft = std::min(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
- rect.fRight = std::max(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
- rect.fTop = rect.fBottom = fLineData.fPts[0].fY;
- outset.fY = fStyle.strokeRec().getWidth() / 2.f;
- outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
- } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) {
- rect.fTop = std::min(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
- rect.fBottom = std::max(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
- rect.fLeft = rect.fRight = fLineData.fPts[0].fX;
- outset.fX = fStyle.strokeRec().getWidth() / 2.f;
- outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
- } else {
- return false;
- }
- rect.outset(outset.fX, outset.fY);
- if (rect.isEmpty()) {
- this->changeType(Type::kEmpty);
fStyle = GrStyle::SimpleFill();
return true;
}
- SkRRect rrect;
- if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
- SkASSERT(outset.fX == outset.fY);
- rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY);
- } else {
- rrect = SkRRect::MakeRect(rect);
+
+ // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
+ // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
+ if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
+ fStyle.strokeRec().isHairlineStyle()) {
+ return false;
}
- bool inverted = fLineData.fInverted && !fStyle.hasPathEffect();
- this->changeType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = inverted;
- fRRectData.fDir = kDefaultRRectDir;
- fRRectData.fStart = kDefaultRRectStart;
+
+ // Tracks style simplifications, even if the geometry can't be further simplified.
+ bool styleSimplified = false;
+ if (fStyle.isDashed()) {
+ // For dashing a point, if the first interval is on, we can drop the dash and just draw
+ // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
+ bool dropDash = false;
+ if (fShape.isPoint()) {
+ dropDash = fStyle.dashIntervalCnt() > 0 &&
+ SkToBool(fStyle.dashIntervals()[0]);
+ } else {
+ dropDash = true;
+ for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
+ if (SkToBool(fStyle.dashIntervals()[i])) {
+ // An off interval has non-zero length so this won't convert to a simple line
+ dropDash = false;
+ break;
+ }
+ }
+ }
+
+ if (!dropDash) {
+ return false;
+ }
+ // Fall through to modifying the shape to respect the new stroke geometry
+ fStyle = GrStyle(fStyle.strokeRec(), nullptr);
+ // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
+ // we reset to be unclosed so we don't override the style based on joins later.
+ originallyClosed = false;
+ styleSimplified = true;
+ }
+
+ // At this point, we're a line or point with no path effects. Any fill portion of the style
+ // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
+ bool strokeAndFilled = false;
+ if (fStyle.isSimpleFill()) {
+ fShape.reset();
+ return true;
+ } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
+ // Stroke only
+ SkStrokeRec rec = fStyle.strokeRec();
+ rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
+ fStyle = GrStyle(rec, nullptr);
+ styleSimplified = true;
+ strokeAndFilled = true;
+ }
+
+ // A point or line that was formed by a degenerate closed shape needs its style updated to
+ // reflect the fact that it doesn't actually produce caps.
+ if (originallyClosed) {
+ SkPaint::Cap cap;
+ if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
+ // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
+ // turn. With round joins, this would make a semi-circle at each end, which is visually
+ // identical to a round cap on the reduced line geometry.
+ cap = SkPaint::kRound_Cap;
+ } else if (fShape.isPoint() && fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
+ !strokeAndFilled) {
+ // Use a square cap for miter join + stroked points, which matches raster's behavior and
+ // expectations from Chrome masking tests, although it could be argued to just always
+ // use a butt cap. This behavior, though, ensures that the default stroked paint draws
+ // something with empty geometry.
+ cap = SkPaint::kSquare_Cap;
+ } else {
+ // If this were a closed line, the 180 degree turn either is a miter join that exceeds
+ // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
+ // of a 180 degreen corner is equivalent to a butt cap.
+ // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
+ // it fits this closed line description (it is not two 90 degree turns that could
+ // produce miter geometry).
+ cap = SkPaint::kButt_Cap;
+ }
+
+ if (cap != fStyle.strokeRec().getCap() ||
+ SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) {
+ SkStrokeRec rec = fStyle.strokeRec();
+ rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter());
+ fStyle = GrStyle(rec, nullptr);
+ styleSimplified = true;
+ }
+ }
+
+ if (fShape.isPoint()) {
+ // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
+ // doesn't draw anything, a round cap is an oval and a square cap is a square.
+ if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
+ fShape.reset();
+ } else {
+ SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
+ SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
+ r.outset(w, w);
+
+ if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
+ fShape.setRRect(SkRRect::MakeOval(r));
+ } else {
+ fShape.setRect(r);
+ }
+ }
+ } else {
+ // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
+ // allowed rotation angle, this would work for any lines.
+ SkRect rect;
+ SkVector outset;
+ if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
+ rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
+ rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
+ rect.fTop = rect.fBottom = fShape.line().fP1.fY;
+ outset.fY = fStyle.strokeRec().getWidth() / 2.f;
+ outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
+ } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
+ rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
+ rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
+ rect.fLeft = rect.fRight = fShape.line().fP1.fX;
+ outset.fX = fStyle.strokeRec().getWidth() / 2.f;
+ outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
+ } else {
+ // Geometrically can't apply the style and turn into a fill, but might still be simpler
+ // than before based solely on changes to fStyle.
+ return styleSimplified;
+ }
+ rect.outset(outset.fX, outset.fY);
+ if (rect.isEmpty()) {
+ fShape.reset();
+ } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
+ SkASSERT(outset.fX == outset.fY);
+ fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY));
+ } else {
+ fShape.setRect(rect);
+ }
+ }
+ // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
fStyle = GrStyle::SimpleFill();
return true;
}
diff --git a/src/gpu/geometry/GrStyledShape.h b/src/gpu/geometry/GrStyledShape.h
index 0fc7d1c..39adad2 100644
--- a/src/gpu/geometry/GrStyledShape.h
+++ b/src/gpu/geometry/GrStyledShape.h
@@ -14,6 +14,7 @@
#include "src/core/SkPathPriv.h"
#include "src/core/SkTLazy.h"
#include "src/gpu/GrStyle.h"
+#include "src/gpu/geometry/GrShape.h"
#include <new>
class SkIDChangeListener;
@@ -41,7 +42,7 @@
// to have to worry about this. This value is exposed for unit tests.
static constexpr int kMaxKeyFromDataVerbCnt = 10;
- GrStyledShape() { this->initType(Type::kEmpty); }
+ GrStyledShape() {}
explicit GrStyledShape(const SkPath& path) : GrStyledShape(path, GrStyle::SimpleFill()) {}
@@ -49,79 +50,40 @@
explicit GrStyledShape(const SkRect& rect) : GrStyledShape(rect, GrStyle::SimpleFill()) {}
- GrStyledShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
- this->initType(Type::kPath, &path);
- this->attemptToSimplifyPath();
+ GrStyledShape(const SkPath& path, const SkPaint& paint) : GrStyledShape(path, GrStyle(paint)) {}
+
+ GrStyledShape(const SkRRect& rrect, const SkPaint& paint)
+ : GrStyledShape(rrect, GrStyle(paint)) {}
+
+ GrStyledShape(const SkRect& rect, const SkPaint& paint) : GrStyledShape(rect, GrStyle(paint)) {}
+
+ GrStyledShape(const SkPath& path, const GrStyle& style) : fShape(path), fStyle(style) {
+ this->simplify();
}
- GrStyledShape(const SkRRect& rrect, const GrStyle& style) : fStyle(style) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
+ GrStyledShape(const SkRRect& rrect, const GrStyle& style) : fShape(rrect), fStyle(style) {
+ this->simplify();
}
GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted,
- const GrStyle& style)
- : fStyle(style) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = inverted;
- if (style.pathEffect()) {
- fRRectData.fDir = dir;
- fRRectData.fStart = start;
- if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
- fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
- } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
- fRRectData.fStart &= 0b110;
- }
- } else {
- fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
- }
- this->attemptToSimplifyRRect();
+ const GrStyle& style)
+ : fShape(rrect)
+ , fStyle(style) {
+ fShape.setPathWindingParams(dir, start);
+ fShape.setInverted(inverted);
+ this->simplify();
}
- GrStyledShape(const SkRect& rect, const GrStyle& style) : fStyle(style) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = SkRRect::MakeRect(rect);
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
+ GrStyledShape(const SkRect& rect, const GrStyle& style) : fShape(rect), fStyle(style) {
+ this->simplify();
}
- GrStyledShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
- this->initType(Type::kPath, &path);
- this->attemptToSimplifyPath();
- }
-
- GrStyledShape(const SkRRect& rrect, const SkPaint& paint) : fStyle(paint) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
- }
-
- GrStyledShape(const SkRect& rect, const SkPaint& paint) : fStyle(paint) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = SkRRect::MakeRect(rect);
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
- }
-
- static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
- SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
-
GrStyledShape(const GrStyledShape&);
- GrStyledShape& operator=(const GrStyledShape& that);
- ~GrStyledShape() { this->changeType(Type::kEmpty); }
+ static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
+ SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
+
+ GrStyledShape& operator=(const GrStyledShape& that);
/**
* Informs MakeFilled on how to modify that shape's fill rule when making a simple filled
@@ -145,6 +107,9 @@
const GrStyle& style() const { return fStyle; }
+ // True if the shape and/or style were modified into a simpler, equivalent pairing
+ bool simplified() const { return fSimplified; }
+
/**
* Returns a shape that has either applied the path effect or path effect and stroking
* information from this shape's style to its geometry. Scale is used when approximating the
@@ -155,153 +120,39 @@
}
bool isRect() const {
- if (Type::kRRect != fType) {
- return false;
- }
-
- return fRRectData.fRRect.isRect();
+ // Should have simplified a rrect to a rect if possible already.
+ SkASSERT(!fShape.isRRect() || !fShape.rrect().isRect());
+ return fShape.isRect();
}
/** Returns the unstyled geometry as a rrect if possible. */
- bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const {
- if (Type::kRRect != fType) {
- return false;
- }
- if (rrect) {
- *rrect = fRRectData.fRRect;
- }
- if (dir) {
- *dir = fRRectData.fDir;
- }
- if (start) {
- *start = fRRectData.fStart;
- }
- if (inverted) {
- *inverted = fRRectData.fInverted;
- }
- return true;
- }
+ bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const;
/**
* If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
* An inverse filled line path is still considered a line.
*/
- bool asLine(SkPoint pts[2], bool* inverted) const {
- if (fType != Type::kLine) {
- return false;
- }
- if (pts) {
- pts[0] = fLineData.fPts[0];
- pts[1] = fLineData.fPts[1];
- }
- if (inverted) {
- *inverted = fLineData.fInverted;
- }
- return true;
- }
+ bool asLine(SkPoint pts[2], bool* inverted) const;
+
+ // Can this shape be drawn as a pair of filled nested rectangles?
+ bool asNestedRects(SkRect rects[2]) const;
/** Returns the unstyled geometry as a path. */
void asPath(SkPath* out) const {
- switch (fType) {
- case Type::kEmpty:
- out->reset();
- break;
- case Type::kInvertedEmpty:
- out->reset();
- out->setFillType(kDefaultPathInverseFillType);
- break;
- case Type::kRRect:
- out->reset();
- out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
- // Below matches the fill type that attemptToSimplifyPath uses.
- if (fRRectData.fInverted) {
- out->setFillType(kDefaultPathInverseFillType);
- } else {
- out->setFillType(kDefaultPathFillType);
- }
- break;
- case Type::kArc:
- SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
- fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
- fStyle.isSimpleFill());
- if (fArcData.fInverted) {
- out->setFillType(kDefaultPathInverseFillType);
- } else {
- out->setFillType(kDefaultPathFillType);
- }
- break;
- case Type::kLine:
- out->reset();
- out->moveTo(fLineData.fPts[0]);
- out->lineTo(fLineData.fPts[1]);
- if (fLineData.fInverted) {
- out->setFillType(kDefaultPathInverseFillType);
- } else {
- out->setFillType(kDefaultPathFillType);
- }
- break;
- case Type::kPath:
- *out = this->path();
- break;
- }
- }
-
- // Can this shape be drawn as a pair of filled nested rectangles?
- bool asNestedRects(SkRect rects[2]) const {
- if (Type::kPath != fType) {
- return false;
- }
-
- // TODO: it would be better two store DRRects natively in the shape rather than converting
- // them to a path and then reextracting the nested rects
- if (this->path().isInverseFillType()) {
- return false;
- }
-
- SkPathDirection dirs[2];
- if (!SkPathPriv::IsNestedFillRects(this->path(), rects, dirs)) {
- return false;
- }
-
- if (SkPathFillType::kWinding == this->path().getFillType() && dirs[0] == dirs[1]) {
- // The two rects need to be wound opposite to each other
- return false;
- }
-
- // Right now, nested rects where the margin is not the same width
- // all around do not render correctly
- const SkScalar* outer = rects[0].asScalars();
- const SkScalar* inner = rects[1].asScalars();
-
- bool allEq = true;
-
- SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
- bool allGoE1 = margin >= SK_Scalar1;
-
- for (int i = 1; i < 4; ++i) {
- SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
- if (temp < SK_Scalar1) {
- allGoE1 = false;
- }
- if (!SkScalarNearlyEqual(margin, temp)) {
- allEq = false;
- }
- }
-
- return allEq || allGoE1;
+ fShape.asPath(out, fStyle.isSimpleFill());
}
/**
* Returns whether the geometry is empty. Note that applying the style could produce a
* non-empty shape. It also may have an inverse fill.
*/
- bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
+ bool isEmpty() const { return fShape.isEmpty(); }
/**
* Gets the bounds of the geometry without reflecting the shape's styling. This ignores
* the inverse fill nature of the geometry.
*/
- SkRect bounds() const;
+ SkRect bounds() const { return fShape.bounds(); }
/**
* Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
@@ -315,28 +166,7 @@
* This is because filling closes all contours in the path.
*/
bool knownToBeConvex() const {
- switch (fType) {
- case Type::kEmpty:
- return true;
- case Type::kInvertedEmpty:
- return true;
- case Type::kRRect:
- return true;
- case Type::kArc:
- return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
- SkToBool(fArcData.fUseCenter),
- fStyle.isSimpleFill());
- case Type::kLine:
- return true;
- case Type::kPath:
- // SkPath.isConvex() really means "is this path convex were it to be closed" and
- // thus doesn't give the correct answer for stroked paths, hence we also check
- // whether the path is either filled or closed. Convex paths may only have one
- // contour hence isLastContourClosed() is a sufficient for a convex path.
- return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
- this->path().isConvex();
- }
- return false;
+ return fShape.convex(fStyle.isSimpleFill());
}
/**
@@ -345,52 +175,20 @@
* kept separate from knownToBeConvex().
*/
bool knownDirection() const {
- switch (fType) {
- case Type::kEmpty:
- return true;
- case Type::kInvertedEmpty:
- return true;
- case Type::kRRect:
- return true;
- case Type::kArc:
- return true;
- case Type::kLine:
- return true;
- case Type::kPath:
- // Assuming this is called after knownToBeConvex(), this should just be relying on
- // cached convexity and direction and will be cheap.
- return !SkPathPriv::CheapIsFirstDirection(this->path(),
- SkPathPriv::kUnknown_FirstDirection);
- }
- return false;
+ // Assuming this is called after knownToBeConvex(), this should just be relying on
+ // cached convexity and direction and will be cheap.
+ return !fShape.isPath() ||
+ !SkPathPriv::CheapIsFirstDirection(fShape.path(),
+ SkPathPriv::kUnknown_FirstDirection);
}
/** Is the pre-styled geometry inverse filled? */
bool inverseFilled() const {
- bool ret = false;
- switch (fType) {
- case Type::kEmpty:
- ret = false;
- break;
- case Type::kInvertedEmpty:
- ret = true;
- break;
- case Type::kRRect:
- ret = fRRectData.fInverted;
- break;
- case Type::kArc:
- ret = fArcData.fInverted;
- break;
- case Type::kLine:
- ret = fLineData.fInverted;
- break;
- case Type::kPath:
- ret = this->path().isInverseFillType();
- break;
- }
+ // Since the path tracks inverted-fillness itself, it should match what was recorded.
+ SkASSERT(!fShape.isPath() || fShape.inverted() == fShape.path().isInverseFillType());
// Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
- SkASSERT(!(ret && this->style().isDashed()));
- return ret;
+ SkASSERT(!(fShape.inverted() && this->style().isDashed()));
+ return fShape.inverted();
}
/**
@@ -412,49 +210,13 @@
* not have any caps if stroked (modulo the effect of any path effect).
*/
bool knownToBeClosed() const {
- switch (fType) {
- case Type::kEmpty:
- return true;
- case Type::kInvertedEmpty:
- return true;
- case Type::kRRect:
- return true;
- case Type::kArc:
- return fArcData.fUseCenter;
- case Type::kLine:
- return false;
- case Type::kPath:
- // SkPath doesn't keep track of the closed status of each contour.
- return SkPathPriv::IsClosedSingleContour(this->path());
- }
- return false;
+ // This refers to the base shape and does not depend on invertedness.
+ return fShape.closed();
}
uint32_t segmentMask() const {
- switch (fType) {
- case Type::kEmpty:
- return 0;
- case Type::kInvertedEmpty:
- return 0;
- case Type::kRRect:
- if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
- return SkPath::kConic_SegmentMask;
- } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
- fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
- return SkPath::kLine_SegmentMask;
- }
- return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
- case Type::kArc:
- if (fArcData.fUseCenter) {
- return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
- }
- return SkPath::kConic_SegmentMask;
- case Type::kLine:
- return SkPath::kLine_SegmentMask;
- case Type::kPath:
- return this->path().getSegmentMasks();
- }
- return 0;
+ // This refers to the base shape and does not depend on invertedness.
+ return fShape.segmentMask();
}
/**
@@ -489,50 +251,6 @@
bool testingOnly_isNonVolatilePath() const;
private:
- enum class Type {
- kEmpty,
- kInvertedEmpty,
- kRRect,
- kArc,
- kLine,
- kPath,
- };
-
- void initType(Type type, const SkPath* path = nullptr) {
- fType = Type::kEmpty;
- this->changeType(type, path);
- }
-
- void changeType(Type type, const SkPath* path = nullptr) {
- bool wasPath = Type::kPath == fType;
- fType = type;
- bool isPath = Type::kPath == type;
- SkASSERT(!path || isPath);
- if (wasPath && !isPath) {
- fPathData.fPath.~SkPath();
- } else if (!wasPath && isPath) {
- if (path) {
- new (&fPathData.fPath) SkPath(*path);
- } else {
- new (&fPathData.fPath) SkPath();
- }
- } else if (isPath && path) {
- fPathData.fPath = *path;
- }
- // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
- fPathData.fGenID = 0;
- }
-
- SkPath& path() {
- SkASSERT(Type::kPath == fType);
- return fPathData.fPath;
- }
-
- const SkPath& path() const {
- SkASSERT(Type::kPath == fType);
- return fPathData.fPath;
- }
-
/** Constructor used by the applyStyle() function */
GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale);
@@ -542,92 +260,23 @@
*/
void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale);
- void attemptToSimplifyPath();
- void attemptToSimplifyRRect();
- void attemptToSimplifyLine();
- void attemptToSimplifyArc();
-
- bool attemptToSimplifyStrokedLineToRRect();
+ // Similar to GrShape::simplify but also takes into account style and stroking, possibly
+ // applying the style explicitly to produce a new analytic shape with a simpler style.
+ void simplify();
+ // As part of the simplification process, some shapes can have stroking trivially evaluated
+ // and form a new geometry with just a fill.
+ bool simplifyStroke(bool originallyClosed);
/** Gets the path that gen id listeners should be added to. */
const SkPath* originalPathForListeners() const;
- // Defaults to use when there is no distinction between even/odd and winding fills.
- static constexpr SkPathFillType kDefaultPathFillType = SkPathFillType::kEvenOdd;
- static constexpr SkPathFillType kDefaultPathInverseFillType = SkPathFillType::kInverseEvenOdd;
-
- static constexpr SkPathDirection kDefaultRRectDir = SkPathDirection::kCW;
- static constexpr unsigned kDefaultRRectStart = 0;
-
- static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
- SkPathDirection* dir) {
- *dir = kDefaultRRectDir;
- // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
- // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
- if (!hasPathEffect) {
- // It doesn't matter what start we use, just be consistent to avoid redundant keys.
- return kDefaultRRectStart;
- }
- // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
- // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
- // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
- bool swapX = rect.fLeft > rect.fRight;
- bool swapY = rect.fTop > rect.fBottom;
- if (swapX && swapY) {
- // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
- return 2 * 2;
- } else if (swapX) {
- *dir = SkPathDirection::kCCW;
- // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
- return 2 * 1;
- } else if (swapY) {
- *dir = SkPathDirection::kCCW;
- // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
- return 2 * 3;
- }
- return 0;
- }
-
- static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
- SkPathDirection* dir) {
- // This comes from SkPath's interface. The default for adding a SkRRect to a path is
- // clockwise beginning at starting index 6.
- static constexpr unsigned kPathRRectStartIdx = 6;
- *dir = kDefaultRRectDir;
- if (!hasPathEffect) {
- // It doesn't matter what start we use, just be consistent to avoid redundant keys.
- return kDefaultRRectStart;
- }
- return kPathRRectStartIdx;
- }
-
- union {
- struct {
- SkRRect fRRect;
- SkPathDirection fDir;
- unsigned fStart;
- bool fInverted;
- } fRRectData;
- struct {
- SkRect fOval;
- SkScalar fStartAngleDegrees;
- SkScalar fSweepAngleDegrees;
- int16_t fUseCenter;
- int16_t fInverted;
- } fArcData;
- struct {
- SkPath fPath;
- // Gen ID of the original path (fPath may be modified)
- int32_t fGenID;
- } fPathData;
- struct {
- SkPoint fPts[2];
- bool fInverted;
- } fLineData;
- };
+ GrShape fShape;
GrStyle fStyle;
- SkTLazy<SkPath> fInheritedPathForListeners;
- SkAutoSTArray<8, uint32_t> fInheritedKey;
- Type fType;
+ // Gen ID of the original path (path may be modified or simplified away).
+ int32_t fGenID = 0;
+ bool fSimplified = false;
+
+ SkTLazy<SkPath> fInheritedPathForListeners;
+ SkAutoSTArray<8, uint32_t> fInheritedKey;
};
#endif
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index fa54fcd..588e852 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -1856,7 +1856,7 @@
info.fInternalFormatForTexImageOrStorage = bgraTexImageFormat;
}
- if (SkToBool(info.fFlags &FormatInfo::kTexturable_Flag)) {
+ if (SkToBool(info.fFlags & FormatInfo::kTexturable_Flag)) {
info.fColorTypeInfoCount = 1;
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
int ctIdx = 0;
@@ -2450,6 +2450,7 @@
ctxInfo.hasExtension("GL_EXT_texture_type_2_10_10_10_REV")) {
info.fFlags = FormatInfo::kTexturable_Flag;
} // No WebGL support
+
if (texStorageSupported) {
info.fFlags |= FormatInfo::kUseTexStorage_Flag;
info.fInternalFormatForTexImageOrStorage = GR_GL_RGB10_A2;
@@ -2458,8 +2459,11 @@
texImageSupportsSizedInternalFormat ? GR_GL_RGB10_A2 : GR_GL_RGBA;
}
- if (SkToBool(info.fFlags &FormatInfo::kTexturable_Flag)) {
- info.fColorTypeInfoCount = 1;
+ if (SkToBool(info.fFlags & FormatInfo::kTexturable_Flag)) {
+ bool supportsBGRAColorType = GR_IS_GR_GL(standard) &&
+ (version >= GR_GL_VER(1, 2) || ctxInfo.hasExtension("GL_EXT_bgra"));
+
+ info.fColorTypeInfoCount = supportsBGRAColorType ? 2 : 1;
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
int ctIdx = 0;
// Format: RGB10_A2, Surface: kRGBA_1010102
@@ -2494,6 +2498,40 @@
ioFormat.fExternalReadFormat = GR_GL_RGBA;
}
}
+ //------------------------------------------------------------------
+ // Format: RGB10_A2, Surface: kBGRA_1010102
+ if (supportsBGRAColorType) {
+ auto& ctInfo = info.fColorTypeInfos[ctIdx++];
+ ctInfo.fColorType = GrColorType::kBGRA_1010102;
+ ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
+ this->setColorTypeFormat(GrColorType::kBGRA_1010102, GrGLFormat::kRGB10_A2);
+
+ // External IO ColorTypes:
+ ctInfo.fExternalIOFormatCount = 2;
+ ctInfo.fExternalIOFormats.reset(
+ new ColorTypeInfo::ExternalIOFormats[ctInfo.fExternalIOFormatCount]());
+ int ioIdx = 0;
+ // Format: RGB10_A2, Surface: kBGRA_1010102, Data: kBGRA_1010102
+ {
+ auto& ioFormat = ctInfo.fExternalIOFormats[ioIdx++];
+ ioFormat.fColorType = GrColorType::kBGRA_1010102;
+ ioFormat.fExternalType = GR_GL_UNSIGNED_INT_2_10_10_10_REV;
+ ioFormat.fExternalTexImageFormat = GR_GL_BGRA;
+ ioFormat.fExternalReadFormat =
+ formatWorkarounds.fDisallowBGRA8ReadPixels ? 0 : GR_GL_BGRA;
+ // Not guaranteed by ES/WebGL.
+ ioFormat.fRequiresImplementationReadQuery = !GR_IS_GR_GL(standard);
+ }
+
+ // Format: RGB10_A2, Surface: kBGRA_1010102, Data: kRGBA_8888
+ {
+ auto& ioFormat = ctInfo.fExternalIOFormats[ioIdx++];
+ ioFormat.fColorType = GrColorType::kRGBA_8888;
+ ioFormat.fExternalType = GR_GL_UNSIGNED_BYTE;
+ ioFormat.fExternalTexImageFormat = 0;
+ ioFormat.fExternalReadFormat = GR_GL_RGBA;
+ }
+ }
}
}
@@ -4424,6 +4462,8 @@
if (GR_IS_GR_GL(fStandard)) {
combos.push_back({ GrColorType::kBGRA_8888,
GrBackendFormat::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_2D) });
+ combos.push_back({ GrColorType::kBGRA_1010102,
+ GrBackendFormat::MakeGL(GR_GL_RGB10_A2, GR_GL_TEXTURE_2D) });
} else {
SkASSERT(GR_IS_GR_GL_ES(fStandard) || GR_IS_GR_WEBGL(fStandard));
diff --git a/src/gpu/mock/GrMockCaps.cpp b/src/gpu/mock/GrMockCaps.cpp
index e0764dc..dbaf644 100644
--- a/src/gpu/mock/GrMockCaps.cpp
+++ b/src/gpu/mock/GrMockCaps.cpp
@@ -48,6 +48,8 @@
SkImage::CompressionType::kNone)},
{ GrColorType::kRGBA_1010102, GrBackendFormat::MakeMock(GrColorType::kRGBA_1010102,
SkImage::CompressionType::kNone)},
+ { GrColorType::kBGRA_1010102, GrBackendFormat::MakeMock(GrColorType::kBGRA_1010102,
+ SkImage::CompressionType::kNone)},
{ GrColorType::kGray_8, GrBackendFormat::MakeMock(GrColorType::kGray_8,
SkImage::CompressionType::kNone)},
{ GrColorType::kAlpha_F16, GrBackendFormat::MakeMock(GrColorType::kAlpha_F16,
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index 50dac59..2285a70 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -156,9 +156,9 @@
int fColorTypeInfoCount = 0;
};
#ifdef SK_BUILD_FOR_IOS
- static constexpr size_t kNumMtlFormats = 17;
+ static constexpr size_t kNumMtlFormats = 18;
#else
- static constexpr size_t kNumMtlFormats = 15;
+ static constexpr size_t kNumMtlFormats = 16;
#endif
static size_t GetFormatIndex(MTLPixelFormat);
FormatInfo fFormatTable[kNumMtlFormats];
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index 51f7a26..864d539 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -469,6 +469,7 @@
MTLPixelFormatR16Float,
MTLPixelFormatRG8Unorm,
MTLPixelFormatRGB10A2Unorm,
+ MTLPixelFormatBGR10A2Unorm,
#ifdef SK_BUILD_FOR_IOS
MTLPixelFormatABGR4Unorm,
#endif
@@ -697,6 +698,26 @@
}
}
+ // Format: BGR10A2Unorm
+ {
+ info = &fFormatTable[GetFormatIndex(MTLPixelFormatBGR10A2Unorm)];
+ if (this->isMac() && fFamilyGroup == 1) {
+ info->fFlags = FormatInfo::kTexturable_Flag;
+ } else {
+ info->fFlags = FormatInfo::kAllFlags;
+ }
+ info->fBytesPerPixel = 4;
+ info->fColorTypeInfoCount = 1;
+ info->fColorTypeInfos.reset(new ColorTypeInfo[info->fColorTypeInfoCount]());
+ int ctIdx = 0;
+ // Format: BGR10A2Unorm, Surface: kBGRA_1010102
+ {
+ auto& ctInfo = info->fColorTypeInfos[ctIdx++];
+ ctInfo.fColorType = GrColorType::kBGRA_1010102;
+ ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
+ }
+ }
+
// Format: R16Float
{
info = &fFormatTable[GetFormatIndex(MTLPixelFormatR16Float)];
@@ -846,6 +867,7 @@
this->setColorType(GrColorType::kRG_88, { MTLPixelFormatRG8Unorm });
this->setColorType(GrColorType::kBGRA_8888, { MTLPixelFormatBGRA8Unorm });
this->setColorType(GrColorType::kRGBA_1010102, { MTLPixelFormatRGB10A2Unorm });
+ this->setColorType(GrColorType::kBGRA_1010102, { MTLPixelFormatBGR10A2Unorm });
this->setColorType(GrColorType::kGray_8, { MTLPixelFormatR8Unorm });
this->setColorType(GrColorType::kAlpha_F16, { MTLPixelFormatR16Float });
this->setColorType(GrColorType::kRGBA_F16, { MTLPixelFormatRGBA16Float });
@@ -1072,6 +1094,7 @@
{ GrColorType::kRG_88, GrBackendFormat::MakeMtl(MTLPixelFormatRG8Unorm) },
{ GrColorType::kBGRA_8888, GrBackendFormat::MakeMtl(MTLPixelFormatBGRA8Unorm) },
{ GrColorType::kRGBA_1010102, GrBackendFormat::MakeMtl(MTLPixelFormatRGB10A2Unorm) },
+ { GrColorType::kBGRA_1010102, GrBackendFormat::MakeMtl(MTLPixelFormatBGR10A2Unorm) },
{ GrColorType::kGray_8, GrBackendFormat::MakeMtl(MTLPixelFormatR8Unorm) },
{ GrColorType::kAlpha_F16, GrBackendFormat::MakeMtl(MTLPixelFormatR16Float) },
{ GrColorType::kRGBA_F16, GrBackendFormat::MakeMtl(MTLPixelFormatRGBA16Float) },
diff --git a/src/gpu/mtl/GrMtlCppUtil.h b/src/gpu/mtl/GrMtlCppUtil.h
index f78058a..f431ec8 100644
--- a/src/gpu/mtl/GrMtlCppUtil.h
+++ b/src/gpu/mtl/GrMtlCppUtil.h
@@ -18,7 +18,7 @@
#if GR_TEST_UTILS
const char* GrMtlFormatToStr(GrMTLPixelFormat mtlFormat);
-bool GrMtlFormatIsBGRA(GrMTLPixelFormat mtlFormat);
+bool GrMtlFormatIsBGRA8(GrMTLPixelFormat mtlFormat);
#endif
#endif
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 16ab069..3b89047 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -741,6 +741,7 @@
case MTLPixelFormatRG8Unorm: return GrColorType::kRG_88;
case MTLPixelFormatBGRA8Unorm: return GrColorType::kBGRA_8888;
case MTLPixelFormatRGB10A2Unorm: return GrColorType::kRGBA_1010102;
+ case MTLPixelFormatBGR10A2Unorm: return GrColorType::kBGRA_1010102;
case MTLPixelFormatR16Float: return GrColorType::kR_F16;
case MTLPixelFormatRGBA16Float: return GrColorType::kRGBA_F16;
case MTLPixelFormatR16Unorm: return GrColorType::kR_16;
diff --git a/src/gpu/mtl/GrMtlUtil.mm b/src/gpu/mtl/GrMtlUtil.mm
index 57ecfd9..29eac72 100644
--- a/src/gpu/mtl/GrMtlUtil.mm
+++ b/src/gpu/mtl/GrMtlUtil.mm
@@ -248,6 +248,7 @@
case MTLPixelFormatR16Float: return kRed_SkColorChannelFlag;
case MTLPixelFormatRG8Unorm: return kRG_SkColorChannelFlags;
case MTLPixelFormatRGB10A2Unorm: return kRGBA_SkColorChannelFlags;
+ case MTLPixelFormatBGR10A2Unorm: return kRGBA_SkColorChannelFlags;
#if defined(SK_BUILD_FOR_IOS) && !TARGET_OS_SIMULATOR
case MTLPixelFormatABGR4Unorm: return kRGBA_SkColorChannelFlags;
#endif
@@ -294,7 +295,7 @@
}
#if GR_TEST_UTILS
-bool GrMtlFormatIsBGRA(GrMTLPixelFormat mtlFormat) {
+bool GrMtlFormatIsBGRA8(GrMTLPixelFormat mtlFormat) {
return mtlFormat == MTLPixelFormatBGRA8Unorm;
}
@@ -312,6 +313,7 @@
case MTLPixelFormatR16Float: return "R16Float";
case MTLPixelFormatRG8Unorm: return "RG8Unorm";
case MTLPixelFormatRGB10A2Unorm: return "RGB10A2Unorm";
+ case MTLPixelFormatBGR10A2Unorm: return "BGR10A2Unorm";
#ifdef SK_BUILD_FOR_IOS
case MTLPixelFormatABGR4Unorm: return "ABGR4Unorm";
#endif
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index bc8b38d..80a34c6 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -25,88 +25,114 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
+GrAtlasTextOp::GrAtlasTextOp(MaskType maskType,
+ GrPaint&& paint,
+ GrTextBlob::SubRun* subrun,
+ const SkMatrix& drawMatrix,
+ SkPoint drawOrigin,
+ const SkIRect& clipRect,
+ const SkPMColor4f& filteredColor,
+ SkColor luminanceColor,
+ bool useGammaCorrectDistanceTable,
+ uint32_t DFGPFlags)
+ : INHERITED(ClassID())
+ , fMaskType{maskType}
+ , fNeedsGlyphTransform{subrun->needsTransform()}
+ , fLuminanceColor{luminanceColor}
+ , fUseGammaCorrectDistanceTable{useGammaCorrectDistanceTable}
+ , fDFGPFlags{DFGPFlags}
+ , fGeoDataAllocSize{kMinGeometryAllocated}
+ , fProcessors{std::move(paint)}
+ , fNumGlyphs{SkTo<int>(subrun->fGlyphs.size())} {
+ GrAtlasTextOp::Geometry& geometry = fGeoData[0];
+
+ // Unref handled in ~GrAtlasTextOp().
+ geometry.fBlob = SkRef(subrun->fBlob);
+ geometry.fSubRunPtr = subrun;
+ geometry.fDrawMatrix = drawMatrix;
+ geometry.fDrawOrigin = drawOrigin;
+ geometry.fClipRect = clipRect;
+ geometry.fColor = subrun->maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE
+ : filteredColor;
+ fGeoCount = 1;
+
+ SkRect bounds = subrun->deviceRect(drawMatrix, drawOrigin);
+ // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
+ // we treat this as a set of non-AA rects rendered with a texture.
+ this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
+}
+
std::unique_ptr<GrAtlasTextOp> GrAtlasTextOp::MakeBitmap(GrRecordingContext* context,
GrPaint&& paint,
- GrMaskFormat maskFormat,
- int glyphCount,
- bool needsTransform) {
- GrOpMemoryPool* pool = context->priv().opMemoryPool();
+ GrTextBlob::SubRun* subrun,
+ const SkMatrix& drawMatrix,
+ SkPoint drawOrigin,
+ const SkIRect& clipRect,
+ const SkPMColor4f& filteredColor) {
+ GrOpMemoryPool* pool = context->priv().opMemoryPool();
- std::unique_ptr<GrAtlasTextOp> op = pool->allocate<GrAtlasTextOp>(std::move(paint));
-
- switch (maskFormat) {
- case kA8_GrMaskFormat:
- op->fMaskType = kGrayscaleCoverageMask_MaskType;
- break;
- case kA565_GrMaskFormat:
- op->fMaskType = kLCDCoverageMask_MaskType;
- break;
- case kARGB_GrMaskFormat:
- op->fMaskType = kColorBitmapMask_MaskType;
- break;
+ MaskType maskType = [&]() {
+ switch (subrun->maskFormat()) {
+ case kA8_GrMaskFormat: return kGrayscaleCoverageMask_MaskType;
+ case kA565_GrMaskFormat: return kLCDCoverageMask_MaskType;
+ case kARGB_GrMaskFormat: return kColorBitmapMask_MaskType;
+ // Needed to placate some compilers.
+ default: return kGrayscaleCoverageMask_MaskType;
}
- op->fNumGlyphs = glyphCount;
- op->fGeoCount = 1;
- op->fLuminanceColor = 0;
- op->fNeedsGlyphTransform = needsTransform;
- return op;
- }
+ }();
+
+ return pool->allocate<GrAtlasTextOp>(maskType,
+ std::move(paint),
+ subrun,
+ drawMatrix,
+ drawOrigin,
+ clipRect,
+ filteredColor,
+ 0,
+ false,
+ 0);
+}
std::unique_ptr<GrAtlasTextOp> GrAtlasTextOp::MakeDistanceField(
GrRecordingContext* context,
GrPaint&& paint,
- int glyphCount,
+ GrTextBlob::SubRun* subrun,
+ const SkMatrix& drawMatrix,
+ SkPoint drawOrigin,
+ const SkIRect& clipRect,
+ const SkPMColor4f& filteredColor,
bool useGammaCorrectDistanceTable,
SkColor luminanceColor,
- const SkSurfaceProps& props,
- bool isAntiAliased,
- bool useLCD) {
- GrOpMemoryPool* pool = context->priv().opMemoryPool();
+ const SkSurfaceProps& props) {
+ GrOpMemoryPool* pool = context->priv().opMemoryPool();
+ bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
+ bool isLCD = subrun->hasUseLCDText() && SkPixelGeometryIsH(props.pixelGeometry());
+ MaskType maskType = !subrun->isAntiAliased() ? kAliasedDistanceField_MaskType
+ : isLCD ? (isBGR ? kLCDBGRDistanceField_MaskType
+ : kLCDDistanceField_MaskType)
+ : kGrayscaleDistanceField_MaskType;
- std::unique_ptr<GrAtlasTextOp> op = pool->allocate<GrAtlasTextOp>(std::move(paint));
+ uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+ DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
+ DFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
+ DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
+ DFGPFlags |= kAliasedDistanceField_MaskType == maskType ? kAliased_DistanceFieldEffectFlag : 0;
- bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
- bool isLCD = useLCD && SkPixelGeometryIsH(props.pixelGeometry());
- op->fMaskType = !isAntiAliased ? kAliasedDistanceField_MaskType
- : isLCD ? (isBGR ? kLCDBGRDistanceField_MaskType
- : kLCDDistanceField_MaskType)
- : kGrayscaleDistanceField_MaskType;
- op->fUseGammaCorrectDistanceTable = useGammaCorrectDistanceTable;
- op->fLuminanceColor = luminanceColor;
- op->fNeedsGlyphTransform = true;
- op->fNumGlyphs = glyphCount;
- op->fGeoCount = 1;
- return op;
+ if (isLCD) {
+ DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
+ DFGPFlags |= kLCDBGRDistanceField_MaskType == maskType ? kBGR_DistanceFieldEffectFlag : 0;
}
-static const int kDistanceAdjustLumShift = 5;
-
-void GrAtlasTextOp::init() {
- const Geometry& geo = fGeoData[0];
- if (this->usesDistanceFields()) {
- bool isLCD = this->isLCD();
-
- const SkMatrix& drawMatrix = geo.fDrawMatrix;
-
- fDFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
- fDFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
- fDFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
- fDFGPFlags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
- fDFGPFlags |= (kAliasedDistanceField_MaskType == fMaskType)
- ? kAliased_DistanceFieldEffectFlag
- : 0;
-
- if (isLCD) {
- fDFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
- fDFGPFlags |=
- (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0;
- }
- }
-
- SkRect bounds = geo.fSubRunPtr->deviceRect(geo.fDrawMatrix, geo.fDrawOrigin);
- // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
- // we treat this as a set of non-AA rects rendered with a texture.
- this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
+ return pool->allocate<GrAtlasTextOp>(maskType,
+ std::move(paint),
+ subrun,
+ drawMatrix,
+ drawOrigin,
+ clipRect,
+ filteredColor,
+ luminanceColor,
+ useGammaCorrectDistanceTable,
+ DFGPFlags);
}
void GrAtlasTextOp::visitProxies(const VisitProxyFunc& func) const {
@@ -590,6 +616,8 @@
return CombineResult::kMerged;
}
+static const int kDistanceAdjustLumShift = 5;
+
// TODO trying to figure out why lcd is so whack
// (see comments in GrTextContext::ComputeCanonicalColor)
GrGeometryProcessor* GrAtlasTextOp::setupDfProcessor(SkArenaAlloc* arena,
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 757ed1f..451de49 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -36,29 +36,25 @@
SkPMColor4f fColor;
};
- static std::unique_ptr<GrAtlasTextOp> MakeBitmap(GrRecordingContext*,
- GrPaint&&,
- GrMaskFormat,
- int glyphCount,
- bool needsTransform);
+ static std::unique_ptr<GrAtlasTextOp> MakeBitmap(GrRecordingContext* context,
+ GrPaint&& paint,
+ GrTextBlob::SubRun* subrun,
+ const SkMatrix& drawMatrix,
+ SkPoint drawOrigin,
+ const SkIRect& clipRect,
+ const SkPMColor4f& filteredColor);
static std::unique_ptr<GrAtlasTextOp> MakeDistanceField(
GrRecordingContext*,
GrPaint&&,
- int glyphCount,
+ GrTextBlob::SubRun*,
+ const SkMatrix& drawMatrix,
+ SkPoint drawOrigin,
+ const SkIRect& clipRect,
+ const SkPMColor4f& filteredColor,
bool useGammaCorrectDistanceTable,
SkColor luminanceColor,
- const SkSurfaceProps&,
- bool isAntiAliased,
- bool useLCD);
-
- // To avoid even the initial copy of the struct, we have a getter for the first item which
- // is used to seed the op with its initial geometry. After seeding, the client should call
- // init() so the op can initialize itself
- Geometry& geometry() { return fGeoData[0]; }
-
- /** Called after this->geometry() has been configured. */
- void init();
+ const SkSurfaceProps&);
const char* name() const override { return "AtlasTextOp"; }
@@ -94,10 +90,16 @@
// The minimum number of Geometry we will try to allocate.
static constexpr auto kMinGeometryAllocated = 12;
- GrAtlasTextOp(GrPaint&& paint)
- : INHERITED(ClassID())
- , fGeoDataAllocSize(kMinGeometryAllocated)
- , fProcessors(std::move(paint)) {}
+ GrAtlasTextOp(MaskType maskType,
+ GrPaint&& paint,
+ GrTextBlob::SubRun* subrun,
+ const SkMatrix& drawMatrix,
+ SkPoint drawOrigin,
+ const SkIRect& clipRect,
+ const SkPMColor4f& filteredColor,
+ SkColor luminanceColor,
+ bool useGammaCorrectDistanceTable,
+ uint32_t DFGPFlags);
struct FlushInfo {
sk_sp<const GrBuffer> fVertexBuffer;
@@ -176,20 +178,18 @@
const GrSurfaceProxyView* views,
unsigned int numActiveViews) const;
+ const MaskType fMaskType;
+ const bool fNeedsGlyphTransform;
+ const SkColor fLuminanceColor{0};
+ const bool fUseGammaCorrectDistanceTable{false};
+ // Distance field properties
+ const uint32_t fDFGPFlags;
SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
int fGeoDataAllocSize;
GrProcessorSet fProcessors;
- struct {
- uint32_t fUsesLocalCoords : 1;
- uint32_t fUseGammaCorrectDistanceTable : 1;
- uint32_t fNeedsGlyphTransform : 1;
- };
+ bool fUsesLocalCoords;
int fGeoCount;
int fNumGlyphs;
- MaskType fMaskType;
- // Distance field properties
- SkColor fLuminanceColor;
- uint32_t fDFGPFlags = 0;
typedef GrMeshDrawOp INHERITED;
};
diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp
index e62805c..e73868b 100644
--- a/src/gpu/text/GrTextBlob.cpp
+++ b/src/gpu/text/GrTextBlob.cpp
@@ -583,7 +583,7 @@
}
}
- auto op = this->makeOp(*subRun, glyphCount, deviceMatrix, drawOrigin, clipRect,
+ auto op = this->makeOp(*subRun, deviceMatrix, drawOrigin, clipRect,
paint, filteredColor, props, target);
if (op) {
if (skipClip) {
@@ -600,8 +600,7 @@
const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
size_t GrTextBlob::size() const { return fSize; }
-std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(int glyphCount,
- const SkMatrixProvider& matrixProvider,
+std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(const SkMatrixProvider& matrixProvider,
SkPoint drawOrigin,
const SkPaint& paint,
const SkPMColor4f& filteredColor,
@@ -609,7 +608,7 @@
GrTextTarget* target) {
SubRun* info = fFirstSubRun;
SkIRect emptyRect = SkIRect::MakeEmpty();
- return this->makeOp(*info, glyphCount, matrixProvider, drawOrigin, emptyRect, paint,
+ return this->makeOp(*info, matrixProvider, drawOrigin, emptyRect, paint,
filteredColor, props, target);
}
@@ -716,7 +715,6 @@
}
std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(SubRun& info,
- int glyphCount,
const SkMatrixProvider& matrixProvider,
SkPoint drawOrigin,
const SkIRect& clipRect,
@@ -724,30 +722,29 @@
const SkPMColor4f& filteredColor,
const SkSurfaceProps& props,
GrTextTarget* target) {
- GrMaskFormat format = info.maskFormat();
-
GrPaint grPaint;
target->makeGrPaint(info.maskFormat(), paint, matrixProvider, &grPaint);
- std::unique_ptr<GrAtlasTextOp> op;
if (info.drawAsDistanceFields()) {
// TODO: Can we be even smarter based on the dest transfer function?
- op = GrAtlasTextOp::MakeDistanceField(
- target->getContext(), std::move(grPaint), glyphCount,
- target->colorInfo().isLinearlyBlended(), SkPaintPriv::ComputeLuminanceColor(paint),
- props, info.isAntiAliased(), info.hasUseLCDText());
+ return GrAtlasTextOp::MakeDistanceField(target->getContext(),
+ std::move(grPaint),
+ &info,
+ matrixProvider.localToDevice(),
+ drawOrigin,
+ clipRect,
+ filteredColor,
+ target->colorInfo().isLinearlyBlended(),
+ SkPaintPriv::ComputeLuminanceColor(paint),
+ props);
} else {
- op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount,
- info.needsTransform());
+ return GrAtlasTextOp::MakeBitmap(target->getContext(),
+ std::move(grPaint),
+ &info,
+ matrixProvider.localToDevice(),
+ drawOrigin,
+ clipRect,
+ filteredColor);
}
- GrAtlasTextOp::Geometry& geometry = op->geometry();
- geometry.fDrawMatrix = matrixProvider.localToDevice();
- geometry.fClipRect = clipRect;
- geometry.fBlob = SkRef(this);
- geometry.fSubRunPtr = &info;
- geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
- geometry.fDrawOrigin = drawOrigin;
- op->init();
- return op;
}
void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index 7b27644..98f813a 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -166,8 +166,7 @@
size_t size() const;
// Internal test methods
- std::unique_ptr<GrDrawOp> test_makeOp(int glyphCount,
- const SkMatrixProvider& matrixProvider,
+ std::unique_ptr<GrDrawOp> test_makeOp(const SkMatrixProvider& matrixProvider,
SkPoint drawOrigin,
const SkPaint& paint,
const SkPMColor4f& filteredColor,
@@ -220,7 +219,6 @@
void insertSubRun(SubRun* subRun);
std::unique_ptr<GrAtlasTextOp> makeOp(SubRun& info,
- int glyphCount,
const SkMatrixProvider& matrixProvider,
SkPoint drawOrigin,
const SkIRect& clipRect,
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 6e97db3..3ca9787 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -89,6 +89,7 @@
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_R16G16_UNORM:
case VK_FORMAT_R16G16_SFLOAT:
@@ -680,6 +681,7 @@
VK_FORMAT_R8G8B8_UNORM,
VK_FORMAT_R8G8_UNORM,
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_FORMAT_A2R10G10B10_UNORM_PACK32,
VK_FORMAT_B4G4R4A4_UNORM_PACK16,
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
VK_FORMAT_R8G8B8A8_SRGB,
@@ -949,6 +951,25 @@
}
}
}
+ // Format: VK_FORMAT_A2R10G10B10_UNORM_PACK32
+ {
+ constexpr VkFormat format = VK_FORMAT_A2R10G10B10_UNORM_PACK32;
+ auto& info = this->getFormatInfo(format);
+ info.init(interface, physDev, properties, format);
+ info.fBytesPerPixel = 4;
+ if (SkToBool(info.fOptimalFlags & FormatInfo::kTexturable_Flag)) {
+ info.fColorTypeInfoCount = 1;
+ info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
+ int ctIdx = 0;
+ // Format: VK_FORMAT_A2R10G10B10_UNORM_PACK32, Surface: kBGRA_1010102
+ {
+ constexpr GrColorType ct = GrColorType::kBGRA_1010102;
+ auto& ctInfo = info.fColorTypeInfos[ctIdx++];
+ ctInfo.fColorType = ct;
+ ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
+ }
+ }
+ }
// Format: VK_FORMAT_B4G4R4A4_UNORM_PACK16
{
constexpr VkFormat format = VK_FORMAT_B4G4R4A4_UNORM_PACK16;
@@ -1177,6 +1198,7 @@
this->setColorType(GrColorType::kRG_88, { VK_FORMAT_R8G8_UNORM });
this->setColorType(GrColorType::kBGRA_8888, { VK_FORMAT_B8G8R8A8_UNORM });
this->setColorType(GrColorType::kRGBA_1010102, { VK_FORMAT_A2B10G10R10_UNORM_PACK32 });
+ this->setColorType(GrColorType::kBGRA_1010102, { VK_FORMAT_A2R10G10B10_UNORM_PACK32 });
this->setColorType(GrColorType::kGray_8, { VK_FORMAT_R8_UNORM });
this->setColorType(GrColorType::kAlpha_F16, { VK_FORMAT_R16_SFLOAT });
this->setColorType(GrColorType::kRGBA_F16, { VK_FORMAT_R16G16B16A16_SFLOAT });
@@ -1729,7 +1751,8 @@
{ GrColorType::kRGB_888x, GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8_UNORM) },
{ GrColorType::kRG_88, GrBackendFormat::MakeVk(VK_FORMAT_R8G8_UNORM) },
{ GrColorType::kBGRA_8888, GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM) },
- { GrColorType::kRGBA_1010102, GrBackendFormat::MakeVk(VK_FORMAT_A2B10G10R10_UNORM_PACK32)},
+ { GrColorType::kRGBA_1010102, GrBackendFormat::MakeVk(VK_FORMAT_A2B10G10R10_UNORM_PACK32)},
+ { GrColorType::kBGRA_1010102, GrBackendFormat::MakeVk(VK_FORMAT_A2R10G10B10_UNORM_PACK32)},
{ GrColorType::kGray_8, GrBackendFormat::MakeVk(VK_FORMAT_R8_UNORM) },
{ GrColorType::kAlpha_F16, GrBackendFormat::MakeVk(VK_FORMAT_R16_SFLOAT) },
{ GrColorType::kRGBA_F16, GrBackendFormat::MakeVk(VK_FORMAT_R16G16B16A16_SFLOAT) },
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 57ab258..f136817 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -294,7 +294,7 @@
std::unique_ptr<ColorTypeInfo[]> fColorTypeInfos;
int fColorTypeInfoCount = 0;
};
- static const size_t kNumVkFormats = 21;
+ static const size_t kNumVkFormats = 22;
FormatInfo fFormatTable[kNumVkFormats];
FormatInfo& getFormatInfo(VkFormat);
diff --git a/src/gpu/vk/GrVkUtil.cpp b/src/gpu/vk/GrVkUtil.cpp
index 507acf3..bd73858 100644
--- a/src/gpu/vk/GrVkUtil.cpp
+++ b/src/gpu/vk/GrVkUtil.cpp
@@ -20,6 +20,7 @@
case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_R8G8_UNORM:
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
case VK_FORMAT_R5G6B5_UNORM_PACK16:
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
diff --git a/src/gpu/vk/GrVkUtil.h b/src/gpu/vk/GrVkUtil.h
index 8aed18c..e66cd81 100644
--- a/src/gpu/vk/GrVkUtil.h
+++ b/src/gpu/vk/GrVkUtil.h
@@ -60,6 +60,7 @@
case VK_FORMAT_R8G8B8_UNORM: return kRGB_SkColorChannelFlags;
case VK_FORMAT_R8G8_UNORM: return kRG_SkColorChannelFlags;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return kRGBA_SkColorChannelFlags;
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return kRGBA_SkColorChannelFlags;
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return kRGBA_SkColorChannelFlags;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return kRGBA_SkColorChannelFlags;
case VK_FORMAT_R32G32B32A32_SFLOAT: return kRGBA_SkColorChannelFlags;
@@ -117,6 +118,7 @@
case VK_FORMAT_R8G8B8_UNORM: return "R8G8B8_UNORM";
case VK_FORMAT_R8G8_UNORM: return "R8G8_UNORM";
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return "A2B10G10R10_UNORM_PACK32";
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return "A2R10G10B10_UNORM_PACK32";
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return "B4G4R4A4_UNORM_PACK16";
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return "R4G4B4A4_UNORM_PACK16";
case VK_FORMAT_R32G32B32A32_SFLOAT: return "R32G32B32A32_SFLOAT";
diff --git a/src/pdf/SkPDFSubsetFont.cpp b/src/pdf/SkPDFSubsetFont.cpp
index df2999e..81c37ee 100644
--- a/src/pdf/SkPDFSubsetFont.cpp
+++ b/src/pdf/SkPDFSubsetFont.cpp
@@ -17,7 +17,7 @@
#include "hb-subset.h"
template <class T, void(*P)(T*)> using resource =
- std::unique_ptr<T, SkFunctionWrapper<skstd::remove_pointer_t<decltype(P)>, P>>;
+ std::unique_ptr<T, SkFunctionWrapper<std::remove_pointer_t<decltype(P)>, P>>;
using HBBlob = resource<hb_blob_t, &hb_blob_destroy>;
using HBFace = resource<hb_face_t, &hb_face_destroy>;
using HBSubsetInput = resource<hb_subset_input_t, &hb_subset_input_destroy>;
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index e18b286..35b87bb 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -933,7 +933,7 @@
}
using DoneFTSize = SkFunctionWrapper<decltype(FT_Done_Size), FT_Done_Size>;
- std::unique_ptr<skstd::remove_pointer_t<FT_Size>, DoneFTSize> ftSize([this]() -> FT_Size {
+ std::unique_ptr<std::remove_pointer_t<FT_Size>, DoneFTSize> ftSize([this]() -> FT_Size {
FT_Size size;
FT_Error err = FT_New_Size(fFaceRec->fFace.get(), &size);
if (err != 0) {
diff --git a/src/ports/SkFontMgr_android_parser.cpp b/src/ports/SkFontMgr_android_parser.cpp
index 6f4ec76..60b94bd 100644
--- a/src/ports/SkFontMgr_android_parser.cpp
+++ b/src/ports/SkFontMgr_android_parser.cpp
@@ -644,7 +644,7 @@
return -1;
}
- SkAutoTCallVProc<skstd::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
+ SkAutoTCallVProc<std::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
if (!parser) {
SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n");
diff --git a/src/sksl/SkSLByteCode.cpp b/src/sksl/SkSLByteCode.cpp
index c2a1ab6..3af0f6f 100644
--- a/src/sksl/SkSLByteCode.cpp
+++ b/src/sksl/SkSLByteCode.cpp
@@ -51,6 +51,7 @@
VECTOR_MATRIX_DISASSEMBLE(kAddF, "addf")
VECTOR_DISASSEMBLE(kAddI, "addi")
case ByteCodeInstruction::kAndB: printf("andb"); break;
+ VECTOR_DISASSEMBLE(kATan, "atan")
case ByteCodeInstruction::kBranch: printf("branch %d", READ16()); break;
case ByteCodeInstruction::kCall: printf("call %d", READ8()); break;
case ByteCodeInstruction::kCallExternal: {
@@ -85,6 +86,7 @@
VECTOR_DISASSEMBLE(kDivideS, "divideS")
VECTOR_DISASSEMBLE(kDivideU, "divideu")
VECTOR_MATRIX_DISASSEMBLE(kDup, "dup")
+ VECTOR_DISASSEMBLE(kFract, "fract")
case ByteCodeInstruction::kInverse2x2: printf("inverse2x2"); break;
case ByteCodeInstruction::kInverse3x3: printf("inverse3x3"); break;
case ByteCodeInstruction::kInverse4x4: printf("inverse4x4"); break;
@@ -654,6 +656,8 @@
continue;
}
+ VECTOR_UNARY_FN(kFract, skvx::fract, fFloat)
+
case ByteCodeInstruction::kInverse2x2:
Inverse2x2(sp);
continue;
@@ -1062,6 +1066,7 @@
continue;
}
+ VECTOR_UNARY_FN(kATan, skvx::atan, fFloat)
VECTOR_UNARY_FN(kTan, skvx::tan, fFloat)
case ByteCodeInstruction::kWriteExternal4:
diff --git a/src/sksl/SkSLByteCode.h b/src/sksl/SkSLByteCode.h
index f2acdf7..7c1f1ec 100644
--- a/src/sksl/SkSLByteCode.h
+++ b/src/sksl/SkSLByteCode.h
@@ -27,6 +27,7 @@
VECTOR_MATRIX(kAddF),
VECTOR(kAddI),
kAndB,
+ VECTOR(kATan),
kBranch,
// Followed by a byte indicating the index of the function to call
kCall,
@@ -60,6 +61,7 @@
VECTOR(kDivideU),
// Duplicates the top stack value
VECTOR_MATRIX(kDup),
+ VECTOR(kFract),
kInverse2x2,
kInverse3x3,
kInverse4x4,
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 210b54e..60bff34 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
@@ -43,8 +43,10 @@
, fContext(*context)
, fOutput(output)
, fIntrinsics {
+ { "atan", ByteCodeInstruction::kATan },
{ "cos", ByteCodeInstruction::kCos },
{ "dot", SpecialIntrinsic::kDot },
+ { "fract", ByteCodeInstruction::kFract },
{ "inverse", ByteCodeInstruction::kInverse2x2 },
{ "sin", ByteCodeInstruction::kSin },
{ "sqrt", ByteCodeInstruction::kSqrt },
@@ -217,7 +219,9 @@
VECTOR_UNARY_OP(kConvertStoF)
VECTOR_UNARY_OP(kConvertUtoF)
+ VECTOR_UNARY_OP(kATan)
VECTOR_UNARY_OP(kCos)
+ VECTOR_UNARY_OP(kFract)
VECTOR_UNARY_OP(kSin)
VECTOR_UNARY_OP(kSqrt)
VECTOR_UNARY_OP(kTan)
@@ -994,7 +998,9 @@
}
} else {
switch (found->second.fValue.fInstruction) {
+ case ByteCodeInstruction::kATan:
case ByteCodeInstruction::kCos:
+ case ByteCodeInstruction::kFract:
case ByteCodeInstruction::kSin:
case ByteCodeInstruction::kSqrt:
case ByteCodeInstruction::kTan:
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index f8eb00d..8061f21 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -288,6 +288,17 @@
if (modifiers.fLayout.fKey && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
fErrors.error(decls.fOffset, "'key' is not permitted on 'uniform' variables");
}
+ if (modifiers.fLayout.fMarker.fLength) {
+ if (fKind != Program::kPipelineStage_Kind) {
+ fErrors.error(decls.fOffset, "'marker' is only permitted in runtime effects");
+ }
+ if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
+ fErrors.error(decls.fOffset, "'marker' is only permitted on 'uniform' variables");
+ }
+ if (*baseType != *fContext.fFloat4x4_Type) {
+ fErrors.error(decls.fOffset, "'marker' is only permitted on float4x4 variables");
+ }
+ }
if (modifiers.fFlags & Modifiers::kVarying_Flag) {
if (fKind != Program::kPipelineStage_Kind) {
fErrors.error(decls.fOffset, "'varying' is only permitted in runtime effects");
@@ -1552,21 +1563,29 @@
int64_t leftVal = ((IntLiteral&) left).fValue;
int64_t rightVal = ((IntLiteral&) right).fValue;
switch (op) {
- case Token::Kind::TK_PLUS: return RESULT(Int, +);
- case Token::Kind::TK_MINUS: return RESULT(Int, -);
- case Token::Kind::TK_STAR: return RESULT(Int, *);
+ case Token::Kind::TK_PLUS: return URESULT(Int, +);
+ case Token::Kind::TK_MINUS: return URESULT(Int, -);
+ case Token::Kind::TK_STAR: return URESULT(Int, *);
case Token::Kind::TK_SLASH:
- if (rightVal) {
- return RESULT(Int, /);
+ if (leftVal == std::numeric_limits<int64_t>::min() && rightVal == -1) {
+ fErrors.error(right.fOffset, "arithmetic overflow");
+ return nullptr;
}
- fErrors.error(right.fOffset, "division by zero");
- return nullptr;
+ if (!rightVal) {
+ fErrors.error(right.fOffset, "division by zero");
+ return nullptr;
+ }
+ return RESULT(Int, /);
case Token::Kind::TK_PERCENT:
- if (rightVal) {
- return RESULT(Int, %);
+ if (leftVal == std::numeric_limits<int64_t>::min() && rightVal == -1) {
+ fErrors.error(right.fOffset, "arithmetic overflow");
+ return nullptr;
}
- fErrors.error(right.fOffset, "division by zero");
- return nullptr;
+ if (!rightVal) {
+ fErrors.error(right.fOffset, "division by zero");
+ return nullptr;
+ }
+ return RESULT(Int, %);
case Token::Kind::TK_BITWISEAND: return RESULT(Int, &);
case Token::Kind::TK_BITWISEOR: return RESULT(Int, |);
case Token::Kind::TK_BITWISEXOR: return RESULT(Int, ^);
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index 697edc7..0037870 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -85,6 +85,7 @@
TOKEN(TRIANGLES_ADJACENCY, "triangles_adjacency");
TOKEN(MAX_VERTICES, "max_vertices");
TOKEN(INVOCATIONS, "invocations");
+ TOKEN(MARKER, "marker");
TOKEN(WHEN, "when");
TOKEN(KEY, "key");
TOKEN(TRACKED, "tracked");
@@ -771,14 +772,15 @@
Layout::Primitive primitive = Layout::kUnspecified_Primitive;
int maxVertices = -1;
int invocations = -1;
+ StringFragment marker;
StringFragment when;
Layout::Key key = Layout::kNo_Key;
Layout::CType ctype = Layout::CType::kDefault;
if (this->checkNext(Token::Kind::TK_LAYOUT)) {
if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
return Layout(flags, location, offset, binding, index, set, builtin,
- inputAttachmentIndex, format, primitive, maxVertices, invocations, when,
- key, ctype);
+ inputAttachmentIndex, format, primitive, maxVertices, invocations, marker,
+ when, key, ctype);
}
for (;;) {
Token t = this->nextToken();
@@ -894,6 +896,9 @@
case LayoutToken::INVOCATIONS:
invocations = this->layoutInt();
break;
+ case LayoutToken::MARKER:
+ marker = this->layoutCode();
+ break;
case LayoutToken::WHEN:
when = this->layoutCode();
break;
@@ -921,7 +926,7 @@
}
}
return Layout(flags, location, offset, binding, index, set, builtin, inputAttachmentIndex,
- format, primitive, maxVertices, invocations, when, key, ctype);
+ format, primitive, maxVertices, invocations, marker, when, key, ctype);
}
/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE |
@@ -1508,7 +1513,11 @@
return ASTNode::ID::Invalid();
}
Token t;
+ AutoDepth depth(this);
while (this->checkNext(Token::Kind::TK_COMMA, &t)) {
+ if (!depth.increase()) {
+ return ASTNode::ID::Invalid();
+ }
ASTNode::ID right = this->assignmentExpression();
if (!right) {
return ASTNode::ID::Invalid();
diff --git a/src/sksl/SkSLParser.h b/src/sksl/SkSLParser.h
index e66b950..980133f 100644
--- a/src/sksl/SkSLParser.h
+++ b/src/sksl/SkSLParser.h
@@ -68,6 +68,7 @@
TRIANGLES_ADJACENCY,
MAX_VERTICES,
INVOCATIONS,
+ MARKER,
WHEN,
KEY,
TRACKED,
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 50619cb..b01bd95 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -1823,7 +1823,7 @@
SkASSERT(fProgram.fSettings.fRTHeightOffset >= 0);
fields.emplace_back(Modifiers(Layout(0, -1, fProgram.fSettings.fRTHeightOffset, -1,
-1, -1, -1, -1, Layout::Format::kUnspecified,
- Layout::kUnspecified_Primitive, -1, -1, "",
+ Layout::kUnspecified_Primitive, -1, -1, "", "",
Layout::kNo_Key, Layout::CType::kDefault), 0),
SKSL_RTHEIGHT_NAME, fContext.fFloat_Type.get());
StringFragment name("sksl_synthetic_uniforms");
@@ -1834,7 +1834,7 @@
SkASSERT(binding != -1 && set != -1);
Layout layout(0, -1, -1, binding, -1, set, -1, -1, Layout::Format::kUnspecified,
- Layout::kUnspecified_Primitive, -1, -1, "", Layout::kNo_Key,
+ Layout::kUnspecified_Primitive, -1, -1, "", "", Layout::kNo_Key,
Layout::CType::kDefault);
Variable* intfVar = (Variable*) fSynthetics.takeOwnership(std::unique_ptr<Symbol>(
new Variable(-1,
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
index fd85cc5..8ec3d77 100644
--- a/src/sksl/ir/SkSLLayout.h
+++ b/src/sksl/ir/SkSLLayout.h
@@ -189,7 +189,7 @@
Layout(int flags, int location, int offset, int binding, int index, int set, int builtin,
int inputAttachmentIndex, Format format, Primitive primitive, int maxVertices,
- int invocations, StringFragment when, Key key, CType ctype)
+ int invocations, StringFragment marker, StringFragment when, Key key, CType ctype)
: fFlags(flags)
, fLocation(location)
, fOffset(offset)
@@ -202,6 +202,7 @@
, fPrimitive(primitive)
, fMaxVertices(maxVertices)
, fInvocations(invocations)
+ , fMarker(marker)
, fWhen(when)
, fKey(key)
, fCType(ctype) {}
@@ -377,6 +378,10 @@
result += separator + "invocations = " + to_string(fInvocations);
separator = ", ";
}
+ if (fMarker.fLength) {
+ result += separator + "marker = " + fMarker;
+ separator = ", ";
+ }
if (fWhen.fLength) {
result += separator + "when = " + fWhen;
separator = ", ";
@@ -403,6 +408,7 @@
fPrimitive == other.fPrimitive &&
fMaxVertices == other.fMaxVertices &&
fInvocations == other.fInvocations &&
+ fMarker == other.fMarker &&
fWhen == other.fWhen &&
fKey == other.fKey &&
fCType == other.fCType;
@@ -428,6 +434,8 @@
Primitive fPrimitive;
int fMaxVertices;
int fInvocations;
+ // marker refers to matrices tagged on the SkCanvas with markCTM
+ StringFragment fMarker;
StringFragment fWhen;
Key fKey;
CType fCType;
diff --git a/src/utils/SkBitSet.h b/src/utils/SkBitSet.h
index 770b0d8..b5c7cb2 100644
--- a/src/utils/SkBitSet.h
+++ b/src/utils/SkBitSet.h
@@ -9,6 +9,7 @@
#define SkBitSet_DEFINED
#include "include/private/SkTemplates.h"
+#include "src/core/SkMathPriv.h"
class SkBitSet {
public:
@@ -28,6 +29,14 @@
*chunk |= mask;
}
+ /** Set the value of the index-th bit to false. */
+ void clear(int index) {
+ uint32_t mask = ~(1 << (index & 31));
+ uint32_t* chunk = this->internalGet(index);
+ SkASSERT(chunk);
+ *chunk &= mask;
+ }
+
bool has(int index) const {
const uint32_t* chunk = this->internalGet(index);
uint32_t mask = 1 << (index & 31);
@@ -50,6 +59,19 @@
}
}
+ // Returns the index of the first set bit
+ // Returns -1 if no bits are set
+ int leadingBitIndex() {
+ const uint32_t* data = fBitData.get();
+ for (unsigned i = 0; i < fDwordCount; ++i) {
+ if (uint32_t value = data[i]) { // There are set bits
+ int index = SkPrevLog2(value);
+ return index + i * 32;
+ }
+ }
+ return -1;
+ }
+
private:
std::unique_ptr<uint32_t, SkFunctionWrapper<void(void*), sk_free>> fBitData;
size_t fDwordCount; // Dword (32-bit) count of the bitset.
diff --git a/src/utils/mac/SkUniqueCFRef.h b/src/utils/mac/SkUniqueCFRef.h
index 80640de..f5086ac 100644
--- a/src/utils/mac/SkUniqueCFRef.h
+++ b/src/utils/mac/SkUniqueCFRef.h
@@ -11,14 +11,14 @@
#include "include/core/SkTypes.h"
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
-#include "include/private/SkTLogic.h"
#include "include/private/SkTemplates.h"
#include <CoreFoundation/CoreFoundation.h>
#include <memory>
+#include <type_traits>
template <typename CFRef> using SkUniqueCFRef =
- std::unique_ptr<skstd::remove_pointer_t<CFRef>,
+ std::unique_ptr<std::remove_pointer_t<CFRef>,
SkFunctionWrapper<decltype(CFRelease), CFRelease>>;
#endif
diff --git a/src/xml/SkXMLParser.cpp b/src/xml/SkXMLParser.cpp
index 2e258da..5cf2ed8 100644
--- a/src/xml/SkXMLParser.cpp
+++ b/src/xml/SkXMLParser.cpp
@@ -80,7 +80,7 @@
}
SkXMLParser* fParser;
- SkAutoTCallVProc<skstd::remove_pointer_t<XML_Parser>, XML_ParserFree> fXMLParser;
+ SkAutoTCallVProc<std::remove_pointer_t<XML_Parser>, XML_ParserFree> fXMLParser;
private:
SkString fBufferedText;
diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp
index 2b8a906..ca98152 100644
--- a/tests/BackendAllocationTest.cpp
+++ b/tests/BackendAllocationTest.cpp
@@ -65,8 +65,8 @@
skColorType,
nullptr, nullptr);
if (!surf) {
- ERRORF(reporter, "Couldn't make surface from backendTexture for colorType %d\n",
- skColorType);
+ ERRORF(reporter, "Couldn't make surface from backendTexture for %s\n",
+ ToolUtils::colortype_name(skColorType));
} else {
REPORTER_ASSERT(reporter, initialCount+1 == cache->getResourceCount());
}
@@ -80,8 +80,8 @@
kPremul_SkAlphaType,
nullptr);
if (!img) {
- ERRORF(reporter, "Couldn't make image from backendTexture for skColorType %d\n",
- skColorType);
+ ERRORF(reporter, "Couldn't make image from backendTexture for %s\n",
+ ToolUtils::colortype_name(skColorType));
} else {
SkImage_Base* ib = as_IB(img);
@@ -101,7 +101,7 @@
context->deleteBackendTexture(backendTex);
}
-static bool isBGRA(const GrBackendFormat& format) {
+static bool isBGRA8(const GrBackendFormat& format) {
switch (format.backend()) {
case GrBackendApi::kOpenGL:
#ifdef SK_GL
@@ -120,7 +120,7 @@
}
case GrBackendApi::kMetal:
#ifdef SK_METAL
- return GrMtlFormatIsBGRA(format.asMtlFormat());
+ return GrMtlFormatIsBGRA8(format.asMtlFormat());
#else
return false;
#endif
@@ -440,7 +440,7 @@
return;
}
- if (skColorType == kBGRA_8888_SkColorType && !isBGRA(backendTex.getBackendFormat())) {
+ if (skColorType == kBGRA_8888_SkColorType && !isBGRA8(backendTex.getBackendFormat())) {
// When kBGRA is backed by an RGBA something goes wrong in the swizzling
return;
}
@@ -601,11 +601,11 @@
{ kRGB_888x_SkColorType, SkColors::kCyan },
// TODO: readback is busted when alpha = 0.5f (perhaps premul vs. unpremul)
{ kBGRA_8888_SkColorType, { 1, 0, 0, 1.0f } },
- // TODO: readback is busted when alpha = 0.5f (perhaps premul vs. unpremul)
- { kRGBA_1010102_SkColorType, { .25f, .5f, .75f, 1.0f }},
- // RGB/BGR 101010x and BGRA 1010102 have no Ganesh correlate
+ // TODO: readback is busted for *10A2 when alpha = 0.5f (perhaps premul vs. unpremul)
+ { kRGBA_1010102_SkColorType, { 0.25f, 0.5f, 0.75f, 1.0f }},
+ { kBGRA_1010102_SkColorType, { 0.25f, 0.5f, 0.75f, 1.0f }},
+ // RGB/BGR 101010x have no Ganesh correlate
{ kRGB_101010x_SkColorType, { 0, 0.5f, 0, 0.5f } },
- { kBGRA_1010102_SkColorType, { 0, 0.5f, 0, 0.5f } },
{ kBGR_101010x_SkColorType, { 0, 0.5f, 0, 0.5f } },
{ kGray_8_SkColorType, kGrayCol },
{ kRGBA_F16Norm_SkColorType, SkColors::kLtGray },
@@ -713,26 +713,28 @@
renderable);
}
- auto createWithSrcDataMtd = [](GrContext* context,
- const SkPixmap srcData[],
- int numLevels,
- GrRenderable renderable) {
- SkASSERT(srcData && numLevels);
- auto result = context->createBackendTexture(srcData, numLevels, renderable,
- GrProtected::kNo);
- check_vk_layout(result, VkLayout::kReadOnlyOptimal);
+ {
+ auto createWithSrcDataMtd = [](GrContext* context,
+ const SkPixmap srcData[],
+ int numLevels,
+ GrRenderable renderable) {
+ SkASSERT(srcData && numLevels);
+ auto result = context->createBackendTexture(srcData, numLevels, renderable,
+ GrProtected::kNo);
+ check_vk_layout(result, VkLayout::kReadOnlyOptimal);
#ifdef SK_DEBUG
- {
- auto format =
- context->defaultBackendFormat(srcData[0].colorType(), renderable);
- SkASSERT(format == result.getBackendFormat());
- }
+ {
+ auto format = context->defaultBackendFormat(srcData[0].colorType(),
+ renderable);
+ SkASSERT(format == result.getBackendFormat());
+ }
#endif
- return result;
- };
+ return result;
+ };
- test_pixmap_init(context, reporter, createWithSrcDataMtd, colorType, mipMapped,
- renderable);
+ test_pixmap_init(context, reporter, createWithSrcDataMtd, colorType, mipMapped,
+ renderable);
+ }
}
}
}
@@ -764,7 +766,8 @@
{ GrColorType::kBGRA_8888, GR_GL_RGBA8, SkColors::kBlue },
{ GrColorType::kBGRA_8888, GR_GL_BGRA8, SkColors::kBlue },
// TODO: readback is busted when alpha = 0.5f (perhaps premul vs. unpremul)
- { GrColorType::kRGBA_1010102, GR_GL_RGB10_A2, { 0.5f, 0, 0, 1.0f } },
+ { GrColorType::kRGBA_1010102, GR_GL_RGB10_A2, { 0.25f, 0.5f, 0.75f, 1.f }},
+ { GrColorType::kBGRA_1010102, GR_GL_RGB10_A2, { 0.25f, 0.5f, 0.75f, 1.f }},
{ GrColorType::kBGR_565, GR_GL_RGB565, SkColors::kRed },
{ GrColorType::kABGR_4444, GR_GL_RGBA4, SkColors::kGreen },
@@ -797,9 +800,11 @@
continue;
}
- if (GrColorType::kBGRA_8888 == combo.fColorType) {
- // We allow using a GL_RGBA8 texture as BGRA on desktop GL but not ES.
- if (GR_GL_RGBA8 == combo.fFormat && kGL_GrGLStandard != standard) {
+ if (GrColorType::kBGRA_8888 == combo.fColorType ||
+ GrColorType::kBGRA_1010102 == combo.fColorType) {
+ // We allow using a GL_RGBA8 or GR_GL_RGB10_A2 texture as BGRA on desktop GL but not ES
+ if (kGL_GrGLStandard != standard &&
+ (GR_GL_RGBA8 == combo.fFormat || GR_GL_RGB10_A2 == combo.fFormat)) {
continue;
}
}
@@ -915,7 +920,10 @@
{ GrColorType::kBGRA_8888, VK_FORMAT_B8G8R8A8_UNORM, SkColors::kBlue },
- { GrColorType::kRGBA_1010102, VK_FORMAT_A2B10G10R10_UNORM_PACK32, { 0.5f, 0, 0, 1.0f }},
+ { GrColorType::kRGBA_1010102, VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ { 0.25f, 0.5f, 0.75f, 1.0f }},
+ { GrColorType::kBGRA_1010102, VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+ { 0.25f, 0.5f, 0.75f, 1.0f }},
{ GrColorType::kBGR_565, VK_FORMAT_R5G6B5_UNORM_PACK16, SkColors::kRed },
{ GrColorType::kABGR_4444, VK_FORMAT_R4G4B4A4_UNORM_PACK16, SkColors::kCyan },
diff --git a/tests/GrStyledShapeTest.cpp b/tests/GrStyledShapeTest.cpp
index 712f351..b5a7cf2 100644
--- a/tests/GrStyledShapeTest.cpp
+++ b/tests/GrStyledShapeTest.cpp
@@ -27,11 +27,11 @@
}
bool GrStyledShape::testingOnly_isPath() const {
- return Type::kPath == fType;
+ return fShape.isPath();
}
bool GrStyledShape::testingOnly_isNonVolatilePath() const {
- return Type::kPath == fType && !fPathData.fPath.isVolatile();
+ return fShape.isPath() && !fShape.path().isVolatile();
}
using Key = SkTArray<uint32_t>;
@@ -360,9 +360,10 @@
bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
- // Converted to an outset rectangle.
- return paint.getStrokeJoin() == SkPaint::kMiter_Join &&
- paint.getStrokeMiter() >= SK_ScalarSqrt2;
+ // Converted to an outset rectangle or round rect
+ return (paint.getStrokeJoin() == SkPaint::kMiter_Join &&
+ paint.getStrokeMiter() >= SK_ScalarSqrt2) ||
+ paint.getStrokeJoin() == SkPaint::kRound_Join;
}
private:
@@ -1569,9 +1570,9 @@
dashAndStrokeInvertexEmptyCase.compare(reporter, fillEmptyCase,
TestCase::kAllSame_ComparisonExpecation);
- // A shape made from an empty rrect should behave the same as an empty path when filled but not
- // when stroked. However, dashing an empty rrect produces an empty path leaving nothing to
- // stroke - so equivalent to filling an empty path.
+ // A shape made from an empty rrect should behave the same as an empty path when filled and
+ // when stroked. The shape is closed so it does not produce caps when stroked. When dashed there
+ // is no path to dash along, making it equivalent as well.
SkRRect emptyRRect = SkRRect::MakeEmpty();
REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
@@ -1580,7 +1581,7 @@
TestCase strokeEmptyRRectCase(reporter, emptyRRect, stroke);
strokeEmptyRRectCase.compare(reporter, strokeEmptyCase,
- TestCase::kAllDifferent_ComparisonExpecation);
+ TestCase::kAllSame_ComparisonExpecation);
TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke);
dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
@@ -1596,7 +1597,7 @@
TestCase strokeInvertedEmptyRRectCase(reporter, emptyRRect, kDir, kStart, true,
GrStyle(stroke));
strokeInvertedEmptyRRectCase.compare(reporter, strokeInvertedEmptyCase,
- TestCase::kAllDifferent_ComparisonExpecation);
+ TestCase::kAllSame_ComparisonExpecation);
TestCase dashAndStrokeEmptyInvertedRRectCase(reporter, emptyRRect, kDir, kStart, true,
GrStyle(dashAndStroke));
diff --git a/tests/MtlBackendAllocationTest.mm b/tests/MtlBackendAllocationTest.mm
index f7ea5b5..68b8e0c 100644
--- a/tests/MtlBackendAllocationTest.mm
+++ b/tests/MtlBackendAllocationTest.mm
@@ -49,7 +49,10 @@
{ GrColorType::kBGRA_8888, MTLPixelFormatBGRA8Unorm, SkColors::kBlue },
- { GrColorType::kRGBA_1010102, MTLPixelFormatRGB10A2Unorm, { 0.5f, 0, 0, 1.0f } },
+ { GrColorType::kRGBA_1010102, MTLPixelFormatRGB10A2Unorm,
+ { 0.25f, 0.5f, 0.75f, 1.0f } },
+ { GrColorType::kBGRA_1010102, MTLPixelFormatBGR10A2Unorm,
+ { 0.25f, 0.5f, 0.75f, 1.0f } },
#ifdef SK_BUILD_FOR_IOS
{ GrColorType::kBGR_565, MTLPixelFormatB5G6R5Unorm, SkColors::kRed },
{ GrColorType::kABGR_4444, MTLPixelFormatABGR4Unorm, SkColors::kGreen },
diff --git a/tests/ReadWriteAlphaTest.cpp b/tests/ReadWriteAlphaTest.cpp
index 3c7fac3..df18ab9 100644
--- a/tests/ReadWriteAlphaTest.cpp
+++ b/tests/ReadWriteAlphaTest.cpp
@@ -31,8 +31,9 @@
for (int x = 0; x < w; ++x) {
uint8_t a = actual[y * actualRowBytes + x];
uint8_t e = expected[y * w + x];
- if (GrColorType::kRGBA_1010102 == colorType) {
- // This config only preserves two bits of alpha
+ if (GrColorType::kRGBA_1010102 == colorType ||
+ GrColorType::kBGRA_1010102 == colorType) {
+ // These configs only preserves two bits of alpha
a >>= 6;
e >>= 6;
}
diff --git a/tests/TransferPixelsTest.cpp b/tests/TransferPixelsTest.cpp
index 9a6539f..91f8206 100644
--- a/tests/TransferPixelsTest.cpp
+++ b/tests/TransferPixelsTest.cpp
@@ -182,8 +182,9 @@
auto error = std::function<ComparePixmapsErrorReporter>(
[reporter, colorType](int x, int y, const float diffs[4]) {
ERRORF(reporter,
- "Error at (%d %d) in transfer, color type: %d, diffs: (%f, %f, %f, %f)", x,
- y, colorType, diffs[0], diffs[1], diffs[2], diffs[3]);
+ "Error at (%d %d) in transfer, color type: %s, diffs: (%f, %f, %f, %f)",
+ x, y, GrColorTypeToStr(colorType),
+ diffs[0], diffs[1], diffs[2], diffs[3]);
});
GrImageInfo srcInfo(allowedSrc.fColorType, kUnpremul_SkAlphaType, nullptr, tex->width(),
tex->height());
@@ -352,8 +353,9 @@
auto error = std::function<ComparePixmapsErrorReporter>(
[reporter, colorType](int x, int y, const float diffs[4]) {
ERRORF(reporter,
- "Error at (%d %d) in transfer, color type: %d, diffs: (%f, %f, %f, %f)", x,
- y, colorType, diffs[0], diffs[1], diffs[2], diffs[3]);
+ "Error at (%d %d) in transfer, color type: %s, diffs: (%f, %f, %f, %f)",
+ x, y, GrColorTypeToStr(colorType),
+ diffs[0], diffs[1], diffs[2], diffs[3]);
});
GrImageInfo textureDataInfo(colorType, kUnpremul_SkAlphaType, nullptr, kTexDims);
ComparePixels(textureDataInfo, textureData.get(), textureDataRowBytes, transferInfo,
@@ -412,6 +414,7 @@
GrColorType::kRG_88,
GrColorType::kBGRA_8888,
GrColorType::kRGBA_1010102,
+ GrColorType::kBGRA_1010102,
GrColorType::kGray_8,
GrColorType::kAlpha_F16,
GrColorType::kRGBA_F16,
@@ -444,6 +447,7 @@
GrColorType::kRG_88,
GrColorType::kBGRA_8888,
GrColorType::kRGBA_1010102,
+ GrColorType::kBGRA_1010102,
GrColorType::kGray_8,
GrColorType::kAlpha_F16,
GrColorType::kRGBA_F16,
diff --git a/tools/ToolUtils.cpp b/tools/ToolUtils.cpp
index b41e985..027f8f8 100644
--- a/tools/ToolUtils.cpp
+++ b/tools/ToolUtils.cpp
@@ -58,8 +58,8 @@
case kRGB_888x_SkColorType: return "RGB_888x";
case kBGRA_8888_SkColorType: return "BGRA_8888";
case kRGBA_1010102_SkColorType: return "RGBA_1010102";
- case kRGB_101010x_SkColorType: return "RGB_101010x";
case kBGRA_1010102_SkColorType: return "BGRA_1010102";
+ case kRGB_101010x_SkColorType: return "RGB_101010x";
case kBGR_101010x_SkColorType: return "BGR_101010x";
case kGray_8_SkColorType: return "Gray_8";
case kRGBA_F16Norm_SkColorType: return "RGBA_F16Norm";
@@ -86,8 +86,8 @@
case kRGB_888x_SkColorType: return "888";
case kBGRA_8888_SkColorType: return "8888";
case kRGBA_1010102_SkColorType: return "1010102";
- case kRGB_101010x_SkColorType: return "101010";
case kBGRA_1010102_SkColorType: return "1010102";
+ case kRGB_101010x_SkColorType: return "101010";
case kBGR_101010x_SkColorType: return "101010";
case kGray_8_SkColorType: return "G8";
case kRGBA_F16Norm_SkColorType: return "F16Norm"; // TODO: "F16"?
diff --git a/tools/skui/ModifierKey.h b/tools/skui/ModifierKey.h
index 09c5536..ffb358c 100644
--- a/tools/skui/ModifierKey.h
+++ b/tools/skui/ModifierKey.h
@@ -16,7 +16,7 @@
};
}
-namespace skstd {
+namespace sknonstd {
template <> struct is_bitmask_enum<skui::ModifierKey> : std::true_type {};
}
#endif // skui_modifierkey_defined
diff --git a/tools/viewer/SlideDir.cpp b/tools/viewer/SlideDir.cpp
index 95b1f4a..017febe 100644
--- a/tools/viewer/SlideDir.cpp
+++ b/tools/viewer/SlideDir.cpp
@@ -396,7 +396,7 @@
bool SlideDir::onMouse(SkScalar x, SkScalar y, skui::InputState state,
skui::ModifierKey modifiers) {
modifiers &= ~skui::ModifierKey::kFirstPress;
- if (state == skui::InputState::kMove || skstd::Any(modifiers))
+ if (state == skui::InputState::kMove || sknonstd::Any(modifiers))
return false;
if (fFocusController->hasFocus()) {