9150 lines
327 KiB
C++
9150 lines
327 KiB
C++
|
|
/*
|
|
* Copyright 2012 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
/* THIS IS A SUBSET OF THE SKIA LIBRARY'S PATHOPS MODULE WITH ONLY THE FOLLOWING FUNCTIONALITY:
|
|
* - Path simplification
|
|
* - Path boolean operations
|
|
* - Path parser from SVG representation
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <climits>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <new>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#include <intrin.h>
|
|
#endif
|
|
|
|
#define TArray SKIA_TArray
|
|
|
|
#define SKNX_NO_SIMD
|
|
|
|
#ifdef SKIA_SIMPLIFY_NAMESPACE
|
|
namespace SKIA_SIMPLIFY_NAMESPACE {
|
|
#endif
|
|
|
|
#if !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_WIN) && \
|
|
!defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC)
|
|
|
|
#ifdef __APPLE__
|
|
#endif
|
|
|
|
#if defined(_WIN32) || defined(__SYMBIAN32__)
|
|
#define SK_BUILD_FOR_WIN
|
|
#elif defined(ANDROID) || defined(__ANDROID__)
|
|
#define SK_BUILD_FOR_ANDROID
|
|
#elif defined(linux) || defined(__linux) || defined(__FreeBSD__) || \
|
|
defined(__OpenBSD__) || defined(__sun) || defined(__NetBSD__) || \
|
|
defined(__DragonFly__) || defined(__Fuchsia__) || \
|
|
defined(__GLIBC__) || defined(__GNU__) || defined(__unix__)
|
|
#define SK_BUILD_FOR_UNIX
|
|
#elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
|
#define SK_BUILD_FOR_IOS
|
|
#else
|
|
#define SK_BUILD_FOR_MAC
|
|
#endif
|
|
#endif // end SK_BUILD_FOR_*
|
|
|
|
|
|
#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__)
|
|
#if !defined(SK_RESTRICT)
|
|
#define SK_RESTRICT __restrict
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(SK_RESTRICT)
|
|
#define SK_RESTRICT __restrict__
|
|
#endif
|
|
|
|
#if !defined(SK_CPU_BENDIAN) && !defined(SK_CPU_LENDIAN)
|
|
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
|
#define SK_CPU_BENDIAN
|
|
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
#define SK_CPU_LENDIAN
|
|
#elif defined(__sparc) || defined(__sparc__) || \
|
|
defined(_POWER) || defined(__powerpc__) || \
|
|
defined(__ppc__) || defined(__hppa) || \
|
|
defined(__PPC__) || defined(__PPC64__) || \
|
|
defined(_MIPSEB) || defined(__ARMEB__) || \
|
|
defined(__s390__) || \
|
|
(defined(__sh__) && defined(__BIG_ENDIAN__)) || \
|
|
(defined(__ia64) && defined(__BIG_ENDIAN__))
|
|
#define SK_CPU_BENDIAN
|
|
#else
|
|
#define SK_CPU_LENDIAN
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
|
|
#define SK_CPU_X86 1
|
|
#endif
|
|
|
|
/**
|
|
* SK_CPU_SSE_LEVEL
|
|
*
|
|
* If defined, SK_CPU_SSE_LEVEL should be set to the highest supported level.
|
|
* On non-intel CPU this should be undefined.
|
|
*/
|
|
#define SK_CPU_SSE_LEVEL_SSE1 10
|
|
#define SK_CPU_SSE_LEVEL_SSE2 20
|
|
#define SK_CPU_SSE_LEVEL_SSE3 30
|
|
#define SK_CPU_SSE_LEVEL_SSSE3 31
|
|
#define SK_CPU_SSE_LEVEL_SSE41 41
|
|
#define SK_CPU_SSE_LEVEL_SSE42 42
|
|
#define SK_CPU_SSE_LEVEL_AVX 51
|
|
#define SK_CPU_SSE_LEVEL_AVX2 52
|
|
#define SK_CPU_SSE_LEVEL_SKX 60
|
|
|
|
// TODO(brianosman,kjlubick) clean up these checks
|
|
|
|
// Are we in GCC/Clang?
|
|
#ifndef SK_CPU_SSE_LEVEL
|
|
// These checks must be done in descending order to ensure we set the highest
|
|
// available SSE level.
|
|
#if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \
|
|
defined(__AVX512BW__) && defined(__AVX512VL__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX
|
|
#elif defined(__AVX2__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2
|
|
#elif defined(__AVX__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX
|
|
#elif defined(__SSE4_2__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE42
|
|
#elif defined(__SSE4_1__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE41
|
|
#elif defined(__SSSE3__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSSE3
|
|
#elif defined(__SSE3__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE3
|
|
#elif defined(__SSE2__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2
|
|
#endif
|
|
#endif
|
|
|
|
// Are we in VisualStudio?
|
|
#ifndef SK_CPU_SSE_LEVEL
|
|
// These checks must be done in descending order to ensure we set the highest
|
|
// available SSE level. 64-bit intel guarantees at least SSE2 support.
|
|
#if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \
|
|
defined(__AVX512BW__) && defined(__AVX512VL__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX
|
|
#elif defined(__AVX2__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2
|
|
#elif defined(__AVX__)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX
|
|
#elif defined(_M_X64) || defined(_M_AMD64)
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2
|
|
#elif defined(_M_IX86_FP)
|
|
#if _M_IX86_FP >= 2
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2
|
|
#elif _M_IX86_FP == 1
|
|
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE1
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
// ARM defines
|
|
#if defined(__arm__) && (!defined(__APPLE__) || !TARGET_IPHONE_SIMULATOR)
|
|
#define SK_CPU_ARM32
|
|
#elif defined(__aarch64__)
|
|
#define SK_CPU_ARM64
|
|
#endif
|
|
|
|
// All 64-bit ARM chips have NEON. Many 32-bit ARM chips do too.
|
|
#if !defined(SK_ARM_HAS_NEON) && defined(__ARM_NEON)
|
|
#define SK_ARM_HAS_NEON
|
|
#endif
|
|
|
|
/* SkTypes.h, the root of the public header files, includes this file
|
|
SkUserConfig.h after first initializing certain Skia defines, letting
|
|
this file change or augment those flags.
|
|
|
|
Below are optional defines that add, subtract, or change default behavior
|
|
in Skia. Your port can locally edit this file to enable/disable flags as
|
|
you choose, or these can be declared on your command line (i.e. -Dfoo).
|
|
|
|
By default, this #include file will always default to having all the flags
|
|
commented out, so including it will have no effect.
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/* Skia has lots of debug-only code. Often this is just null checks or other
|
|
parameter checking, but sometimes it can be quite intrusive (e.g. check that
|
|
each 32bit pixel is in premultiplied form). This code can be very useful
|
|
during development, but will slow things down in a shipping product.
|
|
|
|
By default, these mutually exclusive flags are defined in SkTypes.h,
|
|
based on the presence or absence of NDEBUG, but that decision can be changed
|
|
here.
|
|
*/
|
|
//#define SK_DEBUG
|
|
//#define SK_RELEASE
|
|
|
|
/* To write debug messages to a console, skia will call SkDebugf(...) following
|
|
printf conventions (e.g. const char* format, ...). If you want to redirect
|
|
this to something other than printf, define yours here
|
|
*/
|
|
#define SkDebugf(...) ((void) 0)
|
|
|
|
/* Skia has both debug and release asserts. When an assert fails SK_ABORT will
|
|
be used to report an abort message. SK_ABORT is expected not to return. Skia
|
|
provides a default implementation which will print the message with SkDebugf
|
|
and then call sk_abort_no_print.
|
|
*/
|
|
//#define SK_ABORT(message, ...)
|
|
|
|
/* To specify a different default font strike cache memory limit, define this. If this is
|
|
undefined, skia will use a built-in value.
|
|
*/
|
|
//#define SK_DEFAULT_FONT_CACHE_LIMIT (1024 * 1024)
|
|
|
|
/* To specify a different default font strike cache count limit, define this. If this is
|
|
undefined, skia will use a built-in value.
|
|
*/
|
|
// #define SK_DEFAULT_FONT_CACHE_COUNT_LIMIT 2048
|
|
|
|
/* To specify the default size of the image cache, undefine this and set it to
|
|
the desired value (in bytes). SkGraphics.h as a runtime API to set this
|
|
value as well. If this is undefined, a built-in value will be used.
|
|
*/
|
|
//#define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024)
|
|
|
|
/* Define this to set the upper limit for text to support LCD. Values that
|
|
are very large increase the cost in the font cache and draw slower, without
|
|
improving readability. If this is undefined, Skia will use its default
|
|
value (e.g. 48)
|
|
*/
|
|
//#define SK_MAX_SIZE_FOR_LCDTEXT 48
|
|
|
|
/* Change the kN32_SkColorType ordering to BGRA to work in X windows.
|
|
*/
|
|
//#define SK_R32_SHIFT 16
|
|
|
|
/* Determines whether to build code that supports the Ganesh GPU backend. Some classes
|
|
that are not GPU-specific, such as SkShader subclasses, have optional code
|
|
that is used allows them to interact with this GPU backend. If you'd like to
|
|
include this code, include -DSK_GANESH in your cflags or uncomment below.
|
|
Defaults to not set (No Ganesh GPU backend).
|
|
This define affects the ABI of Skia, so make sure it matches the client which uses
|
|
the compiled version of Skia.
|
|
*/
|
|
//#define SK_GANESH
|
|
|
|
/* Skia makes use of histogram logging macros to trace the frequency of
|
|
events. By default, Skia provides no-op versions of these macros.
|
|
Skia consumers can provide their own definitions of these macros to
|
|
integrate with their histogram collection backend.
|
|
*/
|
|
//#define SK_HISTOGRAM_BOOLEAN(name, sample)
|
|
//#define SK_HISTOGRAM_ENUMERATION(name, sample, enum_size)
|
|
//#define SK_HISTOGRAM_EXACT_LINEAR(name, sample, value_max)
|
|
//#define SK_HISTOGRAM_MEMORY_KB(name, sample)
|
|
|
|
/* Skia tries to make use of some non-standard C++ language extensions.
|
|
By default, Skia provides msvc and clang/gcc versions of these macros.
|
|
Skia consumers can provide their own definitions of these macros to
|
|
integrate with their own compilers and build system.
|
|
*/
|
|
//#define SK_ALWAYS_INLINE inline __attribute__((always_inline))
|
|
//#define SK_NEVER_INLINE __attribute__((noinline))
|
|
//#define SK_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
|
|
//#define SK_NO_SANITIZE(A) __attribute__((no_sanitize(A)))
|
|
//#define SK_TRIVIAL_ABI [[clang::trivial_abi]]
|
|
|
|
/*
|
|
* If compiling Skia as a DLL, public APIs should be exported. Skia will set
|
|
* SK_API to something sensible for Clang and MSVC, but if clients need to
|
|
* customize it for their build system or compiler, they may.
|
|
* If a client needs to use SK_API (e.g. overriding SK_ABORT), then they
|
|
* *must* define their own, the default will not be defined prior to loading
|
|
* this file.
|
|
*/
|
|
//#define SK_API __declspec(dllexport)
|
|
|
|
#ifndef SK_USER_CONFIG_WAS_LOADED
|
|
|
|
// Include this to set reasonable defaults (e.g. for SK_CPU_LENDIAN)
|
|
|
|
// Allows embedders that want to disable macros that take arguments to just
|
|
// define that symbol to be one of these
|
|
#define SK_NOTHING_ARG1(arg1)
|
|
#define SK_NOTHING_ARG2(arg1, arg2)
|
|
#define SK_NOTHING_ARG3(arg1, arg2, arg3)
|
|
|
|
// IWYU pragma: begin_exports
|
|
|
|
// Note: SK_USER_CONFIG_HEADER will not work with Bazel builds and some C++ compilers.
|
|
#if defined(SK_USER_CONFIG_HEADER)
|
|
#elif defined(SK_USE_BAZEL_CONFIG_HEADER)
|
|
// The Bazel config file is presumed to be in the root directory of its Bazel Workspace.
|
|
// This is achieved in Skia by having a nested WORKSPACE in include/config and a cc_library
|
|
// defined in that folder. As a result, we do not try to include SkUserConfig.h from the
|
|
// top of Skia because Bazel sandboxing will move it to a different location.
|
|
#else
|
|
#endif
|
|
// IWYU pragma: end_exports
|
|
|
|
// Checks to make sure the SkUserConfig options do not conflict.
|
|
#if !defined(SK_DEBUG) && !defined(SK_RELEASE)
|
|
#ifdef NDEBUG
|
|
#define SK_RELEASE
|
|
#else
|
|
#define SK_DEBUG
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(SK_DEBUG) && defined(SK_RELEASE)
|
|
# error "cannot define both SK_DEBUG and SK_RELEASE"
|
|
#elif !defined(SK_DEBUG) && !defined(SK_RELEASE)
|
|
# error "must define either SK_DEBUG or SK_RELEASE"
|
|
#endif
|
|
|
|
#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN)
|
|
# error "cannot define both SK_CPU_LENDIAN and SK_CPU_BENDIAN"
|
|
#elif !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN)
|
|
# error "must define either SK_CPU_LENDIAN or SK_CPU_BENDIAN"
|
|
#endif
|
|
|
|
#if defined(SK_CPU_BENDIAN) && !defined(I_ACKNOWLEDGE_SKIA_DOES_NOT_SUPPORT_BIG_ENDIAN)
|
|
#error "The Skia team is not endian-savvy enough to support big-endian CPUs."
|
|
#error "If you still want to use Skia,"
|
|
#error "please define I_ACKNOWLEDGE_SKIA_DOES_NOT_SUPPORT_BIG_ENDIAN."
|
|
#endif
|
|
|
|
#define SK_USER_CONFIG_WAS_LOADED
|
|
#endif // SK_USER_CONFIG_WAS_LOADED
|
|
|
|
// If SKIA_IMPLEMENTATION is defined as 1, that signals we are building Skia and should
|
|
// export our symbols. If it is not set (or set to 0), then Skia is being used by a client
|
|
// and we should not export our symbols.
|
|
#if !defined(SKIA_IMPLEMENTATION)
|
|
#define SKIA_IMPLEMENTATION 0
|
|
#endif
|
|
|
|
// If we are compiling Skia is being as a DLL, we need to be sure to export all of our public
|
|
// APIs to that DLL. If a client is using Skia which was compiled as a DLL, we need to instruct
|
|
// the linker to use the symbols from that DLL. This is the goal of the SK_API define.
|
|
#if !defined(SK_API)
|
|
#if defined(SKIA_DLL)
|
|
#if defined(_MSC_VER)
|
|
#if SKIA_IMPLEMENTATION
|
|
#define SK_API __declspec(dllexport)
|
|
#else
|
|
#define SK_API __declspec(dllimport)
|
|
#endif
|
|
#else
|
|
#define SK_API __attribute__((visibility("default")))
|
|
#endif
|
|
#else
|
|
#define SK_API
|
|
#endif
|
|
#endif
|
|
|
|
// SK_SPI is functionally identical to SK_API, but used within src to clarify that it's less stable
|
|
#if !defined(SK_SPI)
|
|
#define SK_SPI SK_API
|
|
#endif
|
|
|
|
// See https://clang.llvm.org/docs/AttributeReference.html#availability
|
|
// The API_AVAILABLE macro comes from <os/availability.h> on MacOS
|
|
#if defined(SK_ENABLE_API_AVAILABLE)
|
|
# define SK_API_AVAILABLE API_AVAILABLE
|
|
#else
|
|
# define SK_API_AVAILABLE(...)
|
|
#endif
|
|
|
|
#if defined(__clang__) || defined(__GNUC__)
|
|
# define SK_ATTRIBUTE(attr) __attribute__((attr))
|
|
#else
|
|
# define SK_ATTRIBUTE(attr)
|
|
#endif
|
|
|
|
/**
|
|
* If your judgment is better than the compiler's (i.e. you've profiled it),
|
|
* you can use SK_ALWAYS_INLINE to force inlining. E.g.
|
|
* inline void someMethod() { ... } // may not be inlined
|
|
* SK_ALWAYS_INLINE void someMethod() { ... } // should always be inlined
|
|
*/
|
|
#if !defined(SK_ALWAYS_INLINE)
|
|
# if defined(SK_BUILD_FOR_WIN)
|
|
# define SK_ALWAYS_INLINE __forceinline
|
|
# else
|
|
# define SK_ALWAYS_INLINE SK_ATTRIBUTE(always_inline) inline
|
|
# endif
|
|
#endif
|
|
|
|
/**
|
|
* If your judgment is better than the compiler's (i.e. you've profiled it),
|
|
* you can use SK_NEVER_INLINE to prevent inlining.
|
|
*/
|
|
#if !defined(SK_NEVER_INLINE)
|
|
# if defined(SK_BUILD_FOR_WIN)
|
|
# define SK_NEVER_INLINE __declspec(noinline)
|
|
# else
|
|
# define SK_NEVER_INLINE SK_ATTRIBUTE(noinline)
|
|
# endif
|
|
#endif
|
|
|
|
/**
|
|
* Used to annotate a function as taking printf style arguments.
|
|
* `A` is the (1 based) index of the format string argument.
|
|
* `B` is the (1 based) index of the first argument used by the format string.
|
|
*/
|
|
#if !defined(SK_PRINTF_LIKE)
|
|
# define SK_PRINTF_LIKE(A, B) SK_ATTRIBUTE(format(printf, (A), (B)))
|
|
#endif
|
|
|
|
/**
|
|
* Used to ignore sanitizer warnings.
|
|
*/
|
|
#if !defined(SK_NO_SANITIZE)
|
|
# define SK_NO_SANITIZE(A) SK_ATTRIBUTE(no_sanitize(A))
|
|
#endif
|
|
|
|
/**
|
|
* Helper macro to define no_sanitize attributes only with clang.
|
|
*/
|
|
#if defined(__clang__) && defined(__has_attribute)
|
|
#if __has_attribute(no_sanitize)
|
|
#define SK_CLANG_NO_SANITIZE(A) SK_NO_SANITIZE(A)
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(SK_CLANG_NO_SANITIZE)
|
|
#define SK_CLANG_NO_SANITIZE(A)
|
|
#endif
|
|
|
|
/**
|
|
* Annotates a class' non-trivial special functions as trivial for the purposes of calls.
|
|
* Allows a class with a non-trivial destructor to be __is_trivially_relocatable.
|
|
* Use of this attribute on a public API breaks platform ABI.
|
|
* Annotated classes may not hold pointers derived from `this`.
|
|
* Annotated classes must implement move+delete as equivalent to memcpy+free.
|
|
* Use may require more complete types, as callee destroys.
|
|
*
|
|
* https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
|
|
* https://libcxx.llvm.org/DesignDocs/UniquePtrTrivialAbi.html
|
|
*/
|
|
#if !defined(SK_TRIVIAL_ABI)
|
|
# define SK_TRIVIAL_ABI
|
|
#endif
|
|
|
|
#if !defined(SkDebugf)
|
|
void SK_SPI SkDebugf(const char format[], ...) SK_PRINTF_LIKE(1, 2);
|
|
#endif
|
|
|
|
#if defined(SK_DEBUG)
|
|
#define SkDEBUGCODE(...) __VA_ARGS__
|
|
#define SkDEBUGF(...) SkDebugf(__VA_ARGS__)
|
|
#else
|
|
#define SkDEBUGCODE(...)
|
|
#define SkDEBUGF(...)
|
|
#endif
|
|
|
|
#if defined(__clang__) && defined(__has_attribute)
|
|
#if __has_attribute(likely)
|
|
#define SK_LIKELY [[likely]]
|
|
#define SK_UNLIKELY [[unlikely]]
|
|
#else
|
|
#define SK_LIKELY
|
|
#define SK_UNLIKELY
|
|
#endif
|
|
#else
|
|
#define SK_LIKELY
|
|
#define SK_UNLIKELY
|
|
#endif
|
|
|
|
/** Called internally if we hit an unrecoverable error.
|
|
The platform implementation must not return, but should either throw
|
|
an exception or otherwise exit.
|
|
*/
|
|
[[noreturn]] SK_API extern void sk_abort_no_print(void);
|
|
|
|
#if defined(SK_BUILD_FOR_GOOGLE3)
|
|
void SkDebugfForDumpStackTrace(const char* data, void* unused);
|
|
namespace base {
|
|
void DumpStackTrace(int skip_count, void w(const char*, void*), void* arg);
|
|
}
|
|
# define SK_DUMP_GOOGLE3_STACK() ::base::DumpStackTrace(0, SkDebugfForDumpStackTrace, nullptr)
|
|
#else
|
|
# define SK_DUMP_GOOGLE3_STACK()
|
|
#endif
|
|
|
|
#if !defined(SK_ABORT)
|
|
# if defined(SK_BUILD_FOR_WIN)
|
|
// This style lets Visual Studio follow errors back to the source file.
|
|
# define SK_DUMP_LINE_FORMAT "%s(%d)"
|
|
# else
|
|
# define SK_DUMP_LINE_FORMAT "%s:%d"
|
|
# endif
|
|
# define SK_ABORT(message, ...) \
|
|
do { \
|
|
SkDebugf(SK_DUMP_LINE_FORMAT ": fatal error: \"" message "\"\n", \
|
|
__FILE__, __LINE__, ##__VA_ARGS__); \
|
|
SK_DUMP_GOOGLE3_STACK(); \
|
|
sk_abort_no_print(); \
|
|
} while (false)
|
|
#endif
|
|
|
|
// SkASSERT, SkASSERTF and SkASSERT_RELEASE can be used as standalone assertion expressions, e.g.
|
|
// uint32_t foo(int x) {
|
|
// SkASSERT(x > 4);
|
|
// return x - 4;
|
|
// }
|
|
// and are also written to be compatible with constexpr functions:
|
|
// constexpr uint32_t foo(int x) {
|
|
// return SkASSERT(x > 4),
|
|
// x - 4;
|
|
// }
|
|
#if defined(__clang__)
|
|
#define SkASSERT_RELEASE(cond) \
|
|
static_cast<void>( __builtin_expect(static_cast<bool>(cond), 1) \
|
|
? static_cast<void>(0) \
|
|
: []{ SK_ABORT("check(%s)", #cond); }() )
|
|
|
|
#define SkASSERTF_RELEASE(cond, fmt, ...) \
|
|
static_cast<void>( __builtin_expect(static_cast<bool>(cond), 1) \
|
|
? static_cast<void>(0) \
|
|
: [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() )
|
|
#else
|
|
#define SkASSERT_RELEASE(cond) \
|
|
static_cast<void>( (cond) ? static_cast<void>(0) : []{ SK_ABORT("check(%s)", #cond); }() )
|
|
|
|
#define SkASSERTF_RELEASE(cond, fmt, ...) \
|
|
static_cast<void>( (cond) \
|
|
? static_cast<void>(0) \
|
|
: [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() )
|
|
#endif
|
|
|
|
#if defined(SK_DEBUG)
|
|
#define SkASSERT(cond) SkASSERT_RELEASE(cond)
|
|
#define SkASSERTF(cond, fmt, ...) SkASSERTF_RELEASE(cond, fmt, ##__VA_ARGS__)
|
|
#define SkDEBUGFAIL(message) SK_ABORT("%s", message)
|
|
#define SkDEBUGFAILF(fmt, ...) SK_ABORT(fmt, ##__VA_ARGS__)
|
|
#define SkAssertResult(cond) SkASSERT(cond)
|
|
#else
|
|
#define SkASSERT(cond) static_cast<void>(0)
|
|
#define SkASSERTF(cond, fmt, ...) static_cast<void>(0)
|
|
#define SkDEBUGFAIL(message)
|
|
#define SkDEBUGFAILF(fmt, ...)
|
|
|
|
// unlike SkASSERT, this macro executes its condition in the non-debug build.
|
|
// The if is present so that this can be used with functions marked [[nodiscard]].
|
|
#define SkAssertResult(cond) if (cond) {} do {} while(false)
|
|
#endif
|
|
|
|
#if !defined(SkUNREACHABLE)
|
|
# if defined(_MSC_VER) && !defined(__clang__)
|
|
# define FAST_FAIL_INVALID_ARG 5
|
|
// See https://developercommunity.visualstudio.com/content/problem/1128631/code-flow-doesnt-see-noreturn-with-extern-c.html
|
|
// for why this is wrapped. Hopefully removable after msvc++ 19.27 is no longer supported.
|
|
[[noreturn]] static inline void sk_fast_fail() { __fastfail(FAST_FAIL_INVALID_ARG); }
|
|
# define SkUNREACHABLE sk_fast_fail()
|
|
# else
|
|
# define SkUNREACHABLE __builtin_trap()
|
|
# endif
|
|
#endif
|
|
|
|
[[noreturn]] SK_API inline void sk_print_index_out_of_bounds(size_t i, size_t size) {
|
|
SK_ABORT("Index (%zu) out of bounds for size %zu.\n", i, size);
|
|
}
|
|
|
|
template <typename T> SK_API inline T sk_collection_check_bounds(T i, T size) {
|
|
if (0 <= i && i < size) SK_LIKELY {
|
|
return i;
|
|
}
|
|
|
|
SK_UNLIKELY {
|
|
#if defined(SK_DEBUG)
|
|
sk_print_index_out_of_bounds(static_cast<size_t>(i), static_cast<size_t>(size));
|
|
#else
|
|
SkUNREACHABLE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
[[noreturn]] SK_API inline void sk_print_length_too_big(size_t i, size_t size) {
|
|
SK_ABORT("Length (%zu) is too big for size %zu.\n", i, size);
|
|
}
|
|
|
|
template <typename T> SK_API inline T sk_collection_check_length(T i, T size) {
|
|
if (0 <= i && i <= size) SK_LIKELY {
|
|
return i;
|
|
}
|
|
|
|
SK_UNLIKELY {
|
|
#if defined(SK_DEBUG)
|
|
sk_print_length_too_big(static_cast<size_t>(i), static_cast<size_t>(size));
|
|
#else
|
|
SkUNREACHABLE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
SK_API inline void sk_collection_not_empty(bool empty) {
|
|
if (empty) SK_UNLIKELY {
|
|
#if defined(SK_DEBUG)
|
|
SK_ABORT("Collection is empty.\n");
|
|
#else
|
|
SkUNREACHABLE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
[[noreturn]] SK_API inline void sk_print_size_too_big(size_t size, size_t maxSize) {
|
|
SK_ABORT("Size (%zu) can't be represented in bytes. Max size is %zu.\n", size, maxSize);
|
|
}
|
|
|
|
template <typename T>
|
|
SK_ALWAYS_INLINE size_t check_size_bytes_too_big(size_t size) {
|
|
const size_t kMaxSize = std::numeric_limits<size_t>::max() / sizeof(T);
|
|
if (size > kMaxSize) {
|
|
#if defined(SK_DEBUG)
|
|
sk_print_size_too_big(size, kMaxSize);
|
|
#else
|
|
SkUNREACHABLE;
|
|
#endif
|
|
}
|
|
return size;
|
|
}
|
|
|
|
// TODO(bungeman,kjlubick) There are a lot of assumptions throughout the codebase that
|
|
// these types are 32 bits, when they could be more or less. Public APIs should stop
|
|
// using these. Internally, we could use uint_fast8_t and uint_fast16_t, but not in
|
|
// public APIs due to ABI incompatibilities.
|
|
|
|
/** Fast type for unsigned 8 bits. Use for parameter passing and local
|
|
variables, not for storage
|
|
*/
|
|
typedef unsigned U8CPU;
|
|
|
|
/** Fast type for unsigned 16 bits. Use for parameter passing and local
|
|
variables, not for storage
|
|
*/
|
|
typedef unsigned U16CPU;
|
|
|
|
// Max Signed 16 bit value
|
|
static constexpr int16_t SK_MaxS16 = INT16_MAX;
|
|
static constexpr int16_t SK_MinS16 = -SK_MaxS16;
|
|
|
|
static constexpr int32_t SK_MaxS32 = INT32_MAX;
|
|
static constexpr int32_t SK_MinS32 = -SK_MaxS32;
|
|
static constexpr int32_t SK_NaN32 = INT32_MIN;
|
|
|
|
static constexpr int64_t SK_MaxS64 = INT64_MAX;
|
|
static constexpr int64_t SK_MinS64 = -SK_MaxS64;
|
|
|
|
// 64bit -> 32bit utilities
|
|
|
|
// Handy util that can be passed two ints, and will automatically promote to
|
|
// 64bits before the multiply, so the caller doesn't have to remember to cast
|
|
// e.g. (int64_t)a * b;
|
|
static inline int64_t sk_64_mul(int64_t a, int64_t b) {
|
|
return a * b;
|
|
}
|
|
|
|
static inline constexpr int32_t SkLeftShift(int32_t value, int32_t shift) {
|
|
return (int32_t) ((uint32_t) value << shift);
|
|
}
|
|
|
|
static inline constexpr int64_t SkLeftShift(int64_t value, int32_t shift) {
|
|
return (int64_t) ((uint64_t) value << shift);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Returns true if value is a power of 2. Does not explicitly check for
|
|
* value <= 0.
|
|
*/
|
|
template <typename T> constexpr inline bool SkIsPow2(T value) {
|
|
return (value & (value - 1)) == 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Return a*b/((1 << shift) - 1), rounding any fractional bits.
|
|
* Only valid if a and b are unsigned and <= 32767 and shift is > 0 and <= 8
|
|
*/
|
|
static inline unsigned SkMul16ShiftRound(U16CPU a, U16CPU b, int shift) {
|
|
SkASSERT(a <= 32767);
|
|
SkASSERT(b <= 32767);
|
|
SkASSERT(shift > 0 && shift <= 8);
|
|
unsigned prod = a*b + (1 << (shift - 1));
|
|
return (prod + (prod >> shift)) >> shift;
|
|
}
|
|
|
|
/**
|
|
* Return a*b/255, rounding any fractional bits.
|
|
* Only valid if a and b are unsigned and <= 32767.
|
|
*/
|
|
static inline U8CPU SkMulDiv255Round(U16CPU a, U16CPU b) {
|
|
return SkMul16ShiftRound(a, b, 8);
|
|
}
|
|
|
|
static constexpr int32_t Sk64_pin_to_s32(int64_t x) {
|
|
return x < SK_MinS32 ? SK_MinS32 : (x > SK_MaxS32 ? SK_MaxS32 : (int32_t)x);
|
|
}
|
|
|
|
static constexpr int32_t Sk32_sat_add(int32_t a, int32_t b) {
|
|
return Sk64_pin_to_s32((int64_t)a + (int64_t)b);
|
|
}
|
|
|
|
static constexpr int32_t Sk32_sat_sub(int32_t a, int32_t b) {
|
|
return Sk64_pin_to_s32((int64_t)a - (int64_t)b);
|
|
}
|
|
|
|
// To avoid UBSAN complaints about 2's compliment overflows
|
|
//
|
|
static constexpr int32_t Sk32_can_overflow_add(int32_t a, int32_t b) {
|
|
return (int32_t)((uint32_t)a + (uint32_t)b);
|
|
}
|
|
static constexpr int32_t Sk32_can_overflow_sub(int32_t a, int32_t b) {
|
|
return (int32_t)((uint32_t)a - (uint32_t)b);
|
|
}
|
|
|
|
/**
|
|
* This is a 'safe' abs for 32-bit integers that asserts when undefined behavior would occur.
|
|
* SkTAbs (in SkTemplates.h) is a general purpose absolute-value function.
|
|
*/
|
|
static inline int32_t SkAbs32(int32_t value) {
|
|
SkASSERT(value != SK_NaN32); // The most negative int32_t can't be negated.
|
|
if (value < 0) {
|
|
value = -value;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
struct SkIPoint;
|
|
|
|
/** SkIVector provides an alternative name for SkIPoint. SkIVector and SkIPoint
|
|
can be used interchangeably for all purposes.
|
|
*/
|
|
typedef SkIPoint SkIVector;
|
|
|
|
/** \struct SkIPoint
|
|
SkIPoint holds two 32-bit integer coordinates.
|
|
*/
|
|
struct SkIPoint {
|
|
int32_t fX; //!< x-axis value
|
|
int32_t fY; //!< y-axis value
|
|
|
|
/** Sets fX to x, fY to y.
|
|
|
|
@param x integer x-axis value of constructed SkIPoint
|
|
@param y integer y-axis value of constructed SkIPoint
|
|
@return SkIPoint (x, y)
|
|
*/
|
|
static constexpr SkIPoint Make(int32_t x, int32_t y) {
|
|
return {x, y};
|
|
}
|
|
|
|
/** Returns x-axis value of SkIPoint.
|
|
|
|
@return fX
|
|
*/
|
|
constexpr int32_t x() const { return fX; }
|
|
|
|
/** Returns y-axis value of SkIPoint.
|
|
|
|
@return fY
|
|
*/
|
|
constexpr int32_t y() const { return fY; }
|
|
|
|
/** Returns true if fX and fY are both zero.
|
|
|
|
@return true if fX is zero and fY is zero
|
|
*/
|
|
bool isZero() const { return (fX | fY) == 0; }
|
|
|
|
/** Sets fX to x and fY to y.
|
|
|
|
@param x new value for fX
|
|
@param y new value for fY
|
|
*/
|
|
void set(int32_t x, int32_t y) {
|
|
fX = x;
|
|
fY = y;
|
|
}
|
|
|
|
/** Returns SkIPoint changing the signs of fX and fY.
|
|
|
|
@return SkIPoint as (-fX, -fY)
|
|
*/
|
|
SkIPoint operator-() const {
|
|
return {-fX, -fY};
|
|
}
|
|
|
|
/** Offsets SkIPoint by ivector v. Sets SkIPoint to (fX + v.fX, fY + v.fY).
|
|
|
|
@param v ivector to add
|
|
*/
|
|
void operator+=(const SkIVector& v) {
|
|
fX = Sk32_sat_add(fX, v.fX);
|
|
fY = Sk32_sat_add(fY, v.fY);
|
|
}
|
|
|
|
/** Subtracts ivector v from SkIPoint. Sets SkIPoint to: (fX - v.fX, fY - v.fY).
|
|
|
|
@param v ivector to subtract
|
|
*/
|
|
void operator-=(const SkIVector& v) {
|
|
fX = Sk32_sat_sub(fX, v.fX);
|
|
fY = Sk32_sat_sub(fY, v.fY);
|
|
}
|
|
|
|
/** Returns true if SkIPoint is equivalent to SkIPoint constructed from (x, y).
|
|
|
|
@param x value compared with fX
|
|
@param y value compared with fY
|
|
@return true if SkIPoint equals (x, y)
|
|
*/
|
|
bool equals(int32_t x, int32_t y) const {
|
|
return fX == x && fY == y;
|
|
}
|
|
|
|
/** Returns true if a is equivalent to b.
|
|
|
|
@param a SkIPoint to compare
|
|
@param b SkIPoint to compare
|
|
@return true if a.fX == b.fX and a.fY == b.fY
|
|
*/
|
|
friend bool operator==(const SkIPoint& a, const SkIPoint& b) {
|
|
return a.fX == b.fX && a.fY == b.fY;
|
|
}
|
|
|
|
/** Returns true if a is not equivalent to b.
|
|
|
|
@param a SkIPoint to compare
|
|
@param b SkIPoint to compare
|
|
@return true if a.fX != b.fX or a.fY != b.fY
|
|
*/
|
|
friend bool operator!=(const SkIPoint& a, const SkIPoint& b) {
|
|
return a.fX != b.fX || a.fY != b.fY;
|
|
}
|
|
|
|
/** Returns ivector from b to a; computed as (a.fX - b.fX, a.fY - b.fY).
|
|
|
|
Can also be used to subtract ivector from ivector, returning ivector.
|
|
|
|
@param a SkIPoint or ivector to subtract from
|
|
@param b ivector to subtract
|
|
@return ivector from b to a
|
|
*/
|
|
friend SkIVector operator-(const SkIPoint& a, const SkIPoint& b) {
|
|
return { Sk32_sat_sub(a.fX, b.fX), Sk32_sat_sub(a.fY, b.fY) };
|
|
}
|
|
|
|
/** Returns SkIPoint resulting from SkIPoint a offset by ivector b, computed as:
|
|
(a.fX + b.fX, a.fY + b.fY).
|
|
|
|
Can also be used to offset SkIPoint b by ivector a, returning SkIPoint.
|
|
Can also be used to add ivector to ivector, returning ivector.
|
|
|
|
@param a SkIPoint or ivector to add to
|
|
@param b SkIPoint or ivector to add
|
|
@return SkIPoint equal to a offset by b
|
|
*/
|
|
friend SkIPoint operator+(const SkIPoint& a, const SkIVector& b) {
|
|
return { Sk32_sat_add(a.fX, b.fX), Sk32_sat_add(a.fY, b.fY) };
|
|
}
|
|
};
|
|
|
|
struct SkPoint;
|
|
|
|
/** SkVector provides an alternative name for SkPoint. SkVector and SkPoint can
|
|
be used interchangeably for all purposes.
|
|
*/
|
|
typedef SkPoint SkVector;
|
|
|
|
/** \struct SkPoint
|
|
SkPoint holds two 32-bit floating point coordinates.
|
|
*/
|
|
struct SK_API SkPoint {
|
|
float fX; //!< x-axis value
|
|
float fY; //!< y-axis value
|
|
|
|
/** Sets fX to x, fY to y. Used both to set SkPoint and vector.
|
|
|
|
@param x float x-axis value of constructed SkPoint or vector
|
|
@param y float y-axis value of constructed SkPoint or vector
|
|
@return SkPoint (x, y)
|
|
*/
|
|
static constexpr SkPoint Make(float x, float y) {
|
|
return {x, y};
|
|
}
|
|
|
|
/** Returns x-axis value of SkPoint or vector.
|
|
|
|
@return fX
|
|
*/
|
|
constexpr float x() const { return fX; }
|
|
|
|
/** Returns y-axis value of SkPoint or vector.
|
|
|
|
@return fY
|
|
*/
|
|
constexpr float y() const { return fY; }
|
|
|
|
/** Returns true if fX and fY are both zero.
|
|
|
|
@return true if fX is zero and fY is zero
|
|
*/
|
|
bool isZero() const { return (0 == fX) & (0 == fY); }
|
|
|
|
/** Sets fX to x and fY to y.
|
|
|
|
@param x new value for fX
|
|
@param y new value for fY
|
|
*/
|
|
void set(float x, float y) {
|
|
fX = x;
|
|
fY = y;
|
|
}
|
|
|
|
/** Sets fX to x and fY to y, promoting integers to float values.
|
|
|
|
Assigning a large integer value directly to fX or fY may cause a compiler
|
|
error, triggered by narrowing conversion of int to float. This safely
|
|
casts x and y to avoid the error.
|
|
|
|
@param x new value for fX
|
|
@param y new value for fY
|
|
*/
|
|
void iset(int32_t x, int32_t y) {
|
|
fX = static_cast<float>(x);
|
|
fY = static_cast<float>(y);
|
|
}
|
|
|
|
/** Sets fX to p.fX and fY to p.fY, promoting integers to float values.
|
|
|
|
Assigning an SkIPoint containing a large integer value directly to fX or fY may
|
|
cause a compiler error, triggered by narrowing conversion of int to float.
|
|
This safely casts p.fX and p.fY to avoid the error.
|
|
|
|
@param p SkIPoint members promoted to float
|
|
*/
|
|
void iset(const SkIPoint& p) {
|
|
fX = static_cast<float>(p.fX);
|
|
fY = static_cast<float>(p.fY);
|
|
}
|
|
|
|
/** Sets fX to absolute value of pt.fX; and fY to absolute value of pt.fY.
|
|
|
|
@param pt members providing magnitude for fX and fY
|
|
*/
|
|
void setAbs(const SkPoint& pt) {
|
|
fX = std::abs(pt.fX);
|
|
fY = std::abs(pt.fY);
|
|
}
|
|
|
|
/** Adds offset to each SkPoint in points array with count entries.
|
|
|
|
@param points SkPoint array
|
|
@param count entries in array
|
|
@param offset vector added to points
|
|
*/
|
|
static void Offset(SkPoint points[], int count, const SkVector& offset) {
|
|
Offset(points, count, offset.fX, offset.fY);
|
|
}
|
|
|
|
/** Adds offset (dx, dy) to each SkPoint in points array of length count.
|
|
|
|
@param points SkPoint array
|
|
@param count entries in array
|
|
@param dx added to fX in points
|
|
@param dy added to fY in points
|
|
*/
|
|
static void Offset(SkPoint points[], int count, float dx, float dy) {
|
|
for (int i = 0; i < count; ++i) {
|
|
points[i].offset(dx, dy);
|
|
}
|
|
}
|
|
|
|
/** Adds offset (dx, dy) to SkPoint.
|
|
|
|
@param dx added to fX
|
|
@param dy added to fY
|
|
*/
|
|
void offset(float dx, float dy) {
|
|
fX += dx;
|
|
fY += dy;
|
|
}
|
|
|
|
/** Returns the Euclidean distance from origin, computed as:
|
|
|
|
sqrt(fX * fX + fY * fY)
|
|
|
|
.
|
|
|
|
@return straight-line distance to origin
|
|
*/
|
|
float length() const { return SkPoint::Length(fX, fY); }
|
|
|
|
/** Returns the Euclidean distance from origin, computed as:
|
|
|
|
sqrt(fX * fX + fY * fY)
|
|
|
|
.
|
|
|
|
@return straight-line distance to origin
|
|
*/
|
|
float distanceToOrigin() const { return this->length(); }
|
|
|
|
/** Scales (fX, fY) so that length() returns one, while preserving ratio of fX to fY,
|
|
if possible. If prior length is nearly zero, sets vector to (0, 0) and returns
|
|
false; otherwise returns true.
|
|
|
|
@return true if former length is not zero or nearly zero
|
|
|
|
example: https://fiddle.skia.org/c/@Point_normalize_2
|
|
*/
|
|
bool normalize();
|
|
|
|
/** Sets vector to (x, y) scaled so length() returns one, and so that
|
|
(fX, fY) is proportional to (x, y). If (x, y) length is nearly zero,
|
|
sets vector to (0, 0) and returns false; otherwise returns true.
|
|
|
|
@param x proportional value for fX
|
|
@param y proportional value for fY
|
|
@return true if (x, y) length is not zero or nearly zero
|
|
|
|
example: https://fiddle.skia.org/c/@Point_setNormalize
|
|
*/
|
|
bool setNormalize(float x, float y);
|
|
|
|
/** Scales vector so that distanceToOrigin() returns length, if possible. If former
|
|
length is nearly zero, sets vector to (0, 0) and return false; otherwise returns
|
|
true.
|
|
|
|
@param length straight-line distance to origin
|
|
@return true if former length is not zero or nearly zero
|
|
|
|
example: https://fiddle.skia.org/c/@Point_setLength
|
|
*/
|
|
bool setLength(float length);
|
|
|
|
/** Sets vector to (x, y) scaled to length, if possible. If former
|
|
length is nearly zero, sets vector to (0, 0) and return false; otherwise returns
|
|
true.
|
|
|
|
@param x proportional value for fX
|
|
@param y proportional value for fY
|
|
@param length straight-line distance to origin
|
|
@return true if (x, y) length is not zero or nearly zero
|
|
|
|
example: https://fiddle.skia.org/c/@Point_setLength_2
|
|
*/
|
|
bool setLength(float x, float y, float length);
|
|
|
|
/** Sets dst to SkPoint times scale. dst may be SkPoint to modify SkPoint in place.
|
|
|
|
@param scale factor to multiply SkPoint by
|
|
@param dst storage for scaled SkPoint
|
|
|
|
example: https://fiddle.skia.org/c/@Point_scale
|
|
*/
|
|
void scale(float scale, SkPoint* dst) const;
|
|
|
|
/** Scales SkPoint in place by scale.
|
|
|
|
@param value factor to multiply SkPoint by
|
|
*/
|
|
void scale(float value) { this->scale(value, this); }
|
|
|
|
/** Changes the sign of fX and fY.
|
|
*/
|
|
void negate() {
|
|
fX = -fX;
|
|
fY = -fY;
|
|
}
|
|
|
|
/** Returns SkPoint changing the signs of fX and fY.
|
|
|
|
@return SkPoint as (-fX, -fY)
|
|
*/
|
|
SkPoint operator-() const {
|
|
return {-fX, -fY};
|
|
}
|
|
|
|
/** Adds vector v to SkPoint. Sets SkPoint to: (fX + v.fX, fY + v.fY).
|
|
|
|
@param v vector to add
|
|
*/
|
|
void operator+=(const SkVector& v) {
|
|
fX += v.fX;
|
|
fY += v.fY;
|
|
}
|
|
|
|
/** Subtracts vector v from SkPoint. Sets SkPoint to: (fX - v.fX, fY - v.fY).
|
|
|
|
@param v vector to subtract
|
|
*/
|
|
void operator-=(const SkVector& v) {
|
|
fX -= v.fX;
|
|
fY -= v.fY;
|
|
}
|
|
|
|
/** Returns SkPoint multiplied by scale.
|
|
|
|
@param scale float to multiply by
|
|
@return SkPoint as (fX * scale, fY * scale)
|
|
*/
|
|
SkPoint operator*(float scale) const {
|
|
return {fX * scale, fY * scale};
|
|
}
|
|
|
|
/** Multiplies SkPoint by scale. Sets SkPoint to: (fX * scale, fY * scale).
|
|
|
|
@param scale float to multiply by
|
|
@return reference to SkPoint
|
|
*/
|
|
SkPoint& operator*=(float scale) {
|
|
fX *= scale;
|
|
fY *= scale;
|
|
return *this;
|
|
}
|
|
|
|
/** Returns true if both fX and fY are measurable values.
|
|
|
|
@return true for values other than infinities and NaN
|
|
*/
|
|
bool isFinite() const {
|
|
float accum = 0;
|
|
accum *= fX;
|
|
accum *= fY;
|
|
|
|
// accum is either NaN or it is finite (zero).
|
|
SkASSERT(0 == accum || std::isnan(accum));
|
|
|
|
// value==value will be true iff value is not NaN
|
|
// TODO: is it faster to say !accum or accum==accum?
|
|
return !std::isnan(accum);
|
|
}
|
|
|
|
/** Returns true if SkPoint is equivalent to SkPoint constructed from (x, y).
|
|
|
|
@param x value compared with fX
|
|
@param y value compared with fY
|
|
@return true if SkPoint equals (x, y)
|
|
*/
|
|
bool equals(float x, float y) const {
|
|
return fX == x && fY == y;
|
|
}
|
|
|
|
/** Returns true if a is equivalent to b.
|
|
|
|
@param a SkPoint to compare
|
|
@param b SkPoint to compare
|
|
@return true if a.fX == b.fX and a.fY == b.fY
|
|
*/
|
|
friend bool operator==(const SkPoint& a, const SkPoint& b) {
|
|
return a.fX == b.fX && a.fY == b.fY;
|
|
}
|
|
|
|
/** Returns true if a is not equivalent to b.
|
|
|
|
@param a SkPoint to compare
|
|
@param b SkPoint to compare
|
|
@return true if a.fX != b.fX or a.fY != b.fY
|
|
*/
|
|
friend bool operator!=(const SkPoint& a, const SkPoint& b) {
|
|
return a.fX != b.fX || a.fY != b.fY;
|
|
}
|
|
|
|
/** Returns vector from b to a, computed as (a.fX - b.fX, a.fY - b.fY).
|
|
|
|
Can also be used to subtract vector from SkPoint, returning SkPoint.
|
|
Can also be used to subtract vector from vector, returning vector.
|
|
|
|
@param a SkPoint to subtract from
|
|
@param b SkPoint to subtract
|
|
@return vector from b to a
|
|
*/
|
|
friend SkVector operator-(const SkPoint& a, const SkPoint& b) {
|
|
return {a.fX - b.fX, a.fY - b.fY};
|
|
}
|
|
|
|
/** Returns SkPoint resulting from SkPoint a offset by vector b, computed as:
|
|
(a.fX + b.fX, a.fY + b.fY).
|
|
|
|
Can also be used to offset SkPoint b by vector a, returning SkPoint.
|
|
Can also be used to add vector to vector, returning vector.
|
|
|
|
@param a SkPoint or vector to add to
|
|
@param b SkPoint or vector to add
|
|
@return SkPoint equal to a offset by b
|
|
*/
|
|
friend SkPoint operator+(const SkPoint& a, const SkVector& b) {
|
|
return {a.fX + b.fX, a.fY + b.fY};
|
|
}
|
|
|
|
/** Returns the Euclidean distance from origin, computed as:
|
|
|
|
sqrt(x * x + y * y)
|
|
|
|
.
|
|
|
|
@param x component of length
|
|
@param y component of length
|
|
@return straight-line distance to origin
|
|
|
|
example: https://fiddle.skia.org/c/@Point_Length
|
|
*/
|
|
static float Length(float x, float y);
|
|
|
|
/** Scales (vec->fX, vec->fY) so that length() returns one, while preserving ratio of vec->fX
|
|
to vec->fY, if possible. If original length is nearly zero, sets vec to (0, 0) and returns
|
|
zero; otherwise, returns length of vec before vec is scaled.
|
|
|
|
Returned prior length may be INFINITY if it can not be represented by float.
|
|
|
|
Note that normalize() is faster if prior length is not required.
|
|
|
|
@param vec normalized to unit length
|
|
@return original vec length
|
|
|
|
example: https://fiddle.skia.org/c/@Point_Normalize
|
|
*/
|
|
static float Normalize(SkVector* vec);
|
|
|
|
/** Returns the Euclidean distance between a and b.
|
|
|
|
@param a line end point
|
|
@param b line end point
|
|
@return straight-line distance from a to b
|
|
*/
|
|
static float Distance(const SkPoint& a, const SkPoint& b) {
|
|
return Length(a.fX - b.fX, a.fY - b.fY);
|
|
}
|
|
|
|
/** Returns the dot product of vector a and vector b.
|
|
|
|
@param a left side of dot product
|
|
@param b right side of dot product
|
|
@return product of input magnitudes and cosine of the angle between them
|
|
*/
|
|
static float DotProduct(const SkVector& a, const SkVector& b) {
|
|
return a.fX * b.fX + a.fY * b.fY;
|
|
}
|
|
|
|
/** Returns the cross product of vector a and vector b.
|
|
|
|
a and b form three-dimensional vectors with z-axis value equal to zero. The
|
|
cross product is a three-dimensional vector with x-axis and y-axis values equal
|
|
to zero. The cross product z-axis component is returned.
|
|
|
|
@param a left side of cross product
|
|
@param b right side of cross product
|
|
@return area spanned by vectors signed by angle direction
|
|
*/
|
|
static float CrossProduct(const SkVector& a, const SkVector& b) {
|
|
return a.fX * b.fY - a.fY * b.fX;
|
|
}
|
|
|
|
/** Returns the cross product of vector and vec.
|
|
|
|
Vector and vec form three-dimensional vectors with z-axis value equal to zero.
|
|
The cross product is a three-dimensional vector with x-axis and y-axis values
|
|
equal to zero. The cross product z-axis component is returned.
|
|
|
|
@param vec right side of cross product
|
|
@return area spanned by vectors signed by angle direction
|
|
*/
|
|
float cross(const SkVector& vec) const {
|
|
return CrossProduct(*this, vec);
|
|
}
|
|
|
|
/** Returns the dot product of vector and vector vec.
|
|
|
|
@param vec right side of dot product
|
|
@return product of input magnitudes and cosine of the angle between them
|
|
*/
|
|
float dot(const SkVector& vec) const {
|
|
return DotProduct(*this, vec);
|
|
}
|
|
|
|
};
|
|
|
|
// SkPoint is part of the public API, but is also required by code in base. The following include
|
|
// forwarding allows SkPoint to participate in the API and for use by code in base.
|
|
|
|
/** Convert a sign-bit int (i.e. float interpreted as int) into a 2s compliement
|
|
int. This also converts -0 (0x80000000) to 0. Doing this to a float allows
|
|
it to be compared using normal C operators (<, <=, etc.)
|
|
*/
|
|
static inline int32_t SkSignBitTo2sCompliment(int32_t x) {
|
|
if (x < 0) {
|
|
x &= 0x7FFFFFFF;
|
|
x = -x;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/** Convert a 2s compliment int to a sign-bit (i.e. int interpreted as float).
|
|
This undoes the result of SkSignBitTo2sCompliment().
|
|
*/
|
|
static inline int32_t Sk2sComplimentToSignBit(int32_t x) {
|
|
int sign = x >> 31;
|
|
// make x positive
|
|
x = (x ^ sign) - sign;
|
|
// set the sign bit as needed
|
|
x |= SkLeftShift(sign, 31);
|
|
return x;
|
|
}
|
|
|
|
union SkFloatIntUnion {
|
|
float fFloat;
|
|
int32_t fSignBitInt;
|
|
};
|
|
|
|
// Helper to see a float as its bit pattern (w/o aliasing warnings)
|
|
static inline int32_t SkFloat2Bits(float x) {
|
|
SkFloatIntUnion data;
|
|
data.fFloat = x;
|
|
return data.fSignBitInt;
|
|
}
|
|
|
|
// Helper to see a bit pattern as a float (w/o aliasing warnings)
|
|
static inline float SkBits2Float(int32_t floatAsBits) {
|
|
SkFloatIntUnion data;
|
|
data.fSignBitInt = floatAsBits;
|
|
return data.fFloat;
|
|
}
|
|
|
|
constexpr int32_t gFloatBits_exponent_mask = 0x7F800000;
|
|
constexpr int32_t gFloatBits_matissa_mask = 0x007FFFFF;
|
|
|
|
static inline bool SkFloatBits_IsFinite(int32_t bits) {
|
|
return (bits & gFloatBits_exponent_mask) != gFloatBits_exponent_mask;
|
|
}
|
|
|
|
static inline bool SkFloatBits_IsInf(int32_t bits) {
|
|
return ((bits & gFloatBits_exponent_mask) == gFloatBits_exponent_mask) &&
|
|
(bits & gFloatBits_matissa_mask) == 0;
|
|
}
|
|
|
|
/** Return the float as a 2s compliment int. Just to be used to compare floats
|
|
to each other or against positive float-bit-constants (like 0). This does
|
|
not return the int equivalent of the float, just something cheaper for
|
|
compares-only.
|
|
*/
|
|
static inline int32_t SkFloatAs2sCompliment(float x) {
|
|
return SkSignBitTo2sCompliment(SkFloat2Bits(x));
|
|
}
|
|
|
|
/** Return the 2s compliment int as a float. This undos the result of
|
|
SkFloatAs2sCompliment
|
|
*/
|
|
static inline float Sk2sComplimentAsFloat(int32_t x) {
|
|
return SkBits2Float(Sk2sComplimentToSignBit(x));
|
|
}
|
|
|
|
// Scalar wrappers for float-bit routines
|
|
|
|
#define SkScalarAs2sCompliment(x) SkFloatAs2sCompliment(x)
|
|
|
|
inline constexpr float SK_FloatSqrt2 = 1.41421356f;
|
|
inline constexpr float SK_FloatPI = 3.14159265f;
|
|
inline constexpr double SK_DoublePI = 3.14159265358979323846264338327950288;
|
|
|
|
static inline float sk_float_sqrt(float x) { return std::sqrt(x); }
|
|
static inline float sk_float_sin(float x) { return std::sin(x); }
|
|
static inline float sk_float_cos(float x) { return std::cos(x); }
|
|
static inline float sk_float_tan(float x) { return std::tan(x); }
|
|
static inline float sk_float_floor(float x) { return std::floor(x); }
|
|
static inline float sk_float_ceil(float x) { return std::ceil(x); }
|
|
static inline float sk_float_trunc(float x) { return std::trunc(x); }
|
|
static inline float sk_float_acos(float x) { return std::acos(x); }
|
|
static inline float sk_float_asin(float x) { return std::asin(x); }
|
|
static inline float sk_float_atan2(float y, float x) { return std::atan2(y,x); }
|
|
static inline float sk_float_abs(float x) { return std::fabs(x); }
|
|
static inline float sk_float_copysign(float x, float y) { return std::copysign(x, y); }
|
|
static inline float sk_float_mod(float x, float y) { return std::fmod(x,y); }
|
|
static inline float sk_float_pow(float x, float y) { return std::pow(x, y); }
|
|
static inline float sk_float_exp(float x) { return std::exp(x); }
|
|
static inline float sk_float_log(float x) { return std::log(x); }
|
|
static inline float sk_float_log2(float x) { return std::log2(x); }
|
|
|
|
static constexpr int sk_float_sgn(float x) {
|
|
return (0.0f < x) - (x < 0.0f);
|
|
}
|
|
|
|
static constexpr float sk_float_degrees_to_radians(float degrees) {
|
|
return degrees * (SK_FloatPI / 180);
|
|
}
|
|
|
|
static constexpr float sk_float_radians_to_degrees(float radians) {
|
|
return radians * (180 / SK_FloatPI);
|
|
}
|
|
|
|
// floor(double+0.5) vs. floorf(float+0.5f) give comparable performance, but upcasting to double
|
|
// means tricky values like 0.49999997 and 2^24 get rounded correctly. If these were rounded
|
|
// as floatf(x + .5f), they would be 1 higher than expected.
|
|
#define sk_float_round(x) (float)sk_double_round((double)(x))
|
|
|
|
static inline bool sk_float_isfinite(float x) {
|
|
return SkFloatBits_IsFinite(SkFloat2Bits(x));
|
|
}
|
|
|
|
static inline bool sk_floats_are_finite(float a, float b) {
|
|
return sk_float_isfinite(a) && sk_float_isfinite(b);
|
|
}
|
|
|
|
static inline bool sk_floats_are_finite(const float array[], int count) {
|
|
float prod = 0;
|
|
for (int i = 0; i < count; ++i) {
|
|
prod *= array[i];
|
|
}
|
|
// At this point, prod will either be NaN or 0
|
|
return prod == 0; // if prod is NaN, this check will return false
|
|
}
|
|
|
|
static inline bool sk_float_isinf(float x) {
|
|
return SkFloatBits_IsInf(SkFloat2Bits(x));
|
|
}
|
|
|
|
static constexpr bool sk_float_isnan(float x) { return x != x; }
|
|
static constexpr bool sk_double_isnan(double x) { return x != x; }
|
|
|
|
inline constexpr int SK_MaxS32FitsInFloat = 2147483520;
|
|
inline constexpr int SK_MinS32FitsInFloat = -SK_MaxS32FitsInFloat;
|
|
|
|
// 0x7fffff8000000000
|
|
inline constexpr int64_t SK_MaxS64FitsInFloat = SK_MaxS64 >> (63-24) << (63-24);
|
|
inline constexpr int64_t SK_MinS64FitsInFloat = -SK_MaxS64FitsInFloat;
|
|
|
|
/**
|
|
* Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN.
|
|
*/
|
|
static constexpr int sk_float_saturate2int(float x) {
|
|
x = x < SK_MaxS32FitsInFloat ? x : SK_MaxS32FitsInFloat;
|
|
x = x > SK_MinS32FitsInFloat ? x : SK_MinS32FitsInFloat;
|
|
return (int)x;
|
|
}
|
|
|
|
/**
|
|
* Return the closest int for the given double. Returns SK_MaxS32 for NaN.
|
|
*/
|
|
static constexpr int sk_double_saturate2int(double x) {
|
|
x = x < SK_MaxS32 ? x : SK_MaxS32;
|
|
x = x > SK_MinS32 ? x : SK_MinS32;
|
|
return (int)x;
|
|
}
|
|
|
|
/**
|
|
* Return the closest int64_t for the given float. Returns SK_MaxS64FitsInFloat for NaN.
|
|
*/
|
|
static constexpr int64_t sk_float_saturate2int64(float x) {
|
|
x = x < SK_MaxS64FitsInFloat ? x : SK_MaxS64FitsInFloat;
|
|
x = x > SK_MinS64FitsInFloat ? x : SK_MinS64FitsInFloat;
|
|
return (int64_t)x;
|
|
}
|
|
|
|
#define sk_float_floor2int(x) sk_float_saturate2int(sk_float_floor(x))
|
|
#define sk_float_round2int(x) sk_float_saturate2int(sk_float_round(x))
|
|
#define sk_float_ceil2int(x) sk_float_saturate2int(sk_float_ceil(x))
|
|
|
|
#define sk_float_floor2int_no_saturate(x) (int)sk_float_floor(x)
|
|
#define sk_float_round2int_no_saturate(x) (int)sk_float_round(x)
|
|
#define sk_float_ceil2int_no_saturate(x) (int)sk_float_ceil(x)
|
|
|
|
#define sk_double_floor(x) floor(x)
|
|
#define sk_double_round(x) floor((x) + 0.5)
|
|
#define sk_double_ceil(x) ceil(x)
|
|
#define sk_double_floor2int(x) (int)sk_double_floor(x)
|
|
#define sk_double_round2int(x) (int)sk_double_round(x)
|
|
#define sk_double_ceil2int(x) (int)sk_double_ceil(x)
|
|
|
|
// Cast double to float, ignoring any warning about too-large finite values being cast to float.
|
|
// Clang thinks this is undefined, but it's actually implementation defined to return either
|
|
// the largest float or infinity (one of the two bracketing representable floats). Good enough!
|
|
SK_NO_SANITIZE("float-cast-overflow")
|
|
static constexpr float sk_double_to_float(double x) {
|
|
return static_cast<float>(x);
|
|
}
|
|
|
|
inline constexpr float SK_FloatNaN = std::numeric_limits<float>::quiet_NaN();
|
|
inline constexpr float SK_FloatInfinity = std::numeric_limits<float>::infinity();
|
|
inline constexpr float SK_FloatNegativeInfinity = -SK_FloatInfinity;
|
|
|
|
inline constexpr double SK_DoubleNaN = std::numeric_limits<double>::quiet_NaN();
|
|
|
|
// Calculate the midpoint between a and b. Similar to std::midpoint in c++20.
|
|
static constexpr float sk_float_midpoint(float a, float b) {
|
|
// Use double math to avoid underflow and overflow.
|
|
return static_cast<float>(0.5 * (static_cast<double>(a) + b));
|
|
}
|
|
|
|
// Returns false if any of the floats are outside the range [0...1].
|
|
// Returns true if count is 0.
|
|
bool sk_floats_are_unit(const float array[], size_t count);
|
|
|
|
static inline float sk_float_rsqrt_portable(float x) { return 1.0f / sk_float_sqrt(x); }
|
|
static inline float sk_float_rsqrt (float x) { return 1.0f / sk_float_sqrt(x); }
|
|
|
|
// The number of significant digits to print.
|
|
inline constexpr int SK_FLT_DECIMAL_DIG = std::numeric_limits<float>::max_digits10;
|
|
|
|
// IEEE defines how float divide behaves for non-finite values and zero-denoms, but C does not,
|
|
// so we have a helper that suppresses the possible undefined-behavior warnings.
|
|
SK_NO_SANITIZE("float-divide-by-zero")
|
|
static constexpr float sk_ieee_float_divide(float numer, float denom) {
|
|
return numer / denom;
|
|
}
|
|
|
|
SK_NO_SANITIZE("float-divide-by-zero")
|
|
static constexpr double sk_ieee_double_divide(double numer, double denom) {
|
|
return numer / denom;
|
|
}
|
|
|
|
// Return a*b + c.
|
|
static inline float sk_fmaf(float a, float b, float c) {
|
|
return std::fma(a, b, c);
|
|
}
|
|
|
|
// Returns true iff the provided number is within a small epsilon of 0.
|
|
bool sk_double_nearly_zero(double a);
|
|
|
|
// Compare two doubles and return true if they are within maxUlpsDiff of each other.
|
|
// * nan as a or b - returns false.
|
|
// * infinity, infinity or -infinity, -infinity - returns true.
|
|
// * infinity and any other number - returns false.
|
|
//
|
|
// ulp is an initialism for Units in the Last Place.
|
|
bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t maxUlpsDiff=16);
|
|
|
|
typedef float SkScalar;
|
|
|
|
#define SK_Scalar1 1.0f
|
|
#define SK_ScalarHalf 0.5f
|
|
#define SK_ScalarSqrt2 SK_FloatSqrt2
|
|
#define SK_ScalarPI SK_FloatPI
|
|
#define SK_ScalarTanPIOver8 0.414213562f
|
|
#define SK_ScalarRoot2Over2 0.707106781f
|
|
#define SK_ScalarMax 3.402823466e+38f
|
|
#define SK_ScalarMin (-SK_ScalarMax)
|
|
#define SK_ScalarInfinity SK_FloatInfinity
|
|
#define SK_ScalarNegativeInfinity SK_FloatNegativeInfinity
|
|
#define SK_ScalarNaN SK_FloatNaN
|
|
|
|
#define SkScalarFloorToScalar(x) sk_float_floor(x)
|
|
#define SkScalarCeilToScalar(x) sk_float_ceil(x)
|
|
#define SkScalarRoundToScalar(x) sk_float_round(x)
|
|
#define SkScalarTruncToScalar(x) sk_float_trunc(x)
|
|
|
|
#define SkScalarFloorToInt(x) sk_float_floor2int(x)
|
|
#define SkScalarCeilToInt(x) sk_float_ceil2int(x)
|
|
#define SkScalarRoundToInt(x) sk_float_round2int(x)
|
|
|
|
#define SkScalarAbs(x) sk_float_abs(x)
|
|
#define SkScalarCopySign(x, y) sk_float_copysign(x, y)
|
|
#define SkScalarMod(x, y) sk_float_mod(x,y)
|
|
#define SkScalarSqrt(x) sk_float_sqrt(x)
|
|
#define SkScalarPow(b, e) sk_float_pow(b, e)
|
|
|
|
#define SkScalarSin(radians) (float)sk_float_sin(radians)
|
|
#define SkScalarCos(radians) (float)sk_float_cos(radians)
|
|
#define SkScalarTan(radians) (float)sk_float_tan(radians)
|
|
#define SkScalarASin(val) (float)sk_float_asin(val)
|
|
#define SkScalarACos(val) (float)sk_float_acos(val)
|
|
#define SkScalarATan2(y, x) (float)sk_float_atan2(y,x)
|
|
#define SkScalarExp(x) (float)sk_float_exp(x)
|
|
#define SkScalarLog(x) (float)sk_float_log(x)
|
|
#define SkScalarLog2(x) (float)sk_float_log2(x)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define SkIntToScalar(x) static_cast<SkScalar>(x)
|
|
#define SkIntToFloat(x) static_cast<float>(x)
|
|
#define SkScalarTruncToInt(x) sk_float_saturate2int(x)
|
|
|
|
#define SkScalarToFloat(x) static_cast<float>(x)
|
|
#define SkFloatToScalar(x) static_cast<SkScalar>(x)
|
|
#define SkScalarToDouble(x) static_cast<double>(x)
|
|
#define SkDoubleToScalar(x) sk_double_to_float(x)
|
|
|
|
static inline bool SkScalarIsNaN(SkScalar x) { return x != x; }
|
|
|
|
/** Returns true if x is not NaN and not infinite
|
|
*/
|
|
static inline bool SkScalarIsFinite(SkScalar x) { return sk_float_isfinite(x); }
|
|
|
|
static inline bool SkScalarsAreFinite(SkScalar a, SkScalar b) {
|
|
return sk_floats_are_finite(a, b);
|
|
}
|
|
|
|
static inline bool SkScalarsAreFinite(const SkScalar array[], int count) {
|
|
return sk_floats_are_finite(array, count);
|
|
}
|
|
|
|
/** Returns the fractional part of the scalar. */
|
|
static inline SkScalar SkScalarFraction(SkScalar x) {
|
|
return x - SkScalarTruncToScalar(x);
|
|
}
|
|
|
|
static inline SkScalar SkScalarSquare(SkScalar x) { return x * x; }
|
|
|
|
#define SkScalarInvert(x) (SK_Scalar1 / (x))
|
|
#define SkScalarAve(a, b) (((a) + (b)) * SK_ScalarHalf)
|
|
#define SkScalarHalf(a) ((a) * SK_ScalarHalf)
|
|
|
|
#define SkDegreesToRadians(degrees) ((degrees) * (SK_ScalarPI / 180))
|
|
#define SkRadiansToDegrees(radians) ((radians) * (180 / SK_ScalarPI))
|
|
|
|
static inline bool SkScalarIsInt(SkScalar x) {
|
|
return x == SkScalarFloorToScalar(x);
|
|
}
|
|
|
|
/**
|
|
* Returns -1 || 0 || 1 depending on the sign of value:
|
|
* -1 if x < 0
|
|
* 0 if x == 0
|
|
* 1 if x > 0
|
|
*/
|
|
static inline int SkScalarSignAsInt(SkScalar x) {
|
|
return x < 0 ? -1 : (x > 0);
|
|
}
|
|
|
|
// Scalar result version of above
|
|
static inline SkScalar SkScalarSignAsScalar(SkScalar x) {
|
|
return x < 0 ? -SK_Scalar1 : ((x > 0) ? SK_Scalar1 : 0);
|
|
}
|
|
|
|
#define SK_ScalarNearlyZero (SK_Scalar1 / (1 << 12))
|
|
|
|
static inline bool SkScalarNearlyZero(SkScalar x,
|
|
SkScalar tolerance = SK_ScalarNearlyZero) {
|
|
SkASSERT(tolerance >= 0);
|
|
return SkScalarAbs(x) <= tolerance;
|
|
}
|
|
|
|
static inline bool SkScalarNearlyEqual(SkScalar x, SkScalar y,
|
|
SkScalar tolerance = SK_ScalarNearlyZero) {
|
|
SkASSERT(tolerance >= 0);
|
|
return SkScalarAbs(x-y) <= tolerance;
|
|
}
|
|
|
|
#define SK_ScalarSinCosNearlyZero (SK_Scalar1 / (1 << 16))
|
|
|
|
static inline float SkScalarSinSnapToZero(SkScalar radians) {
|
|
float v = SkScalarSin(radians);
|
|
return SkScalarNearlyZero(v, SK_ScalarSinCosNearlyZero) ? 0.0f : v;
|
|
}
|
|
|
|
static inline float SkScalarCosSnapToZero(SkScalar radians) {
|
|
float v = SkScalarCos(radians);
|
|
return SkScalarNearlyZero(v, SK_ScalarSinCosNearlyZero) ? 0.0f : v;
|
|
}
|
|
|
|
/** Linearly interpolate between A and B, based on t.
|
|
If t is 0, return A
|
|
If t is 1, return B
|
|
else interpolate.
|
|
t must be [0..SK_Scalar1]
|
|
*/
|
|
static inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) {
|
|
SkASSERT(t >= 0 && t <= SK_Scalar1);
|
|
return A + (B - A) * t;
|
|
}
|
|
|
|
/** Interpolate along the function described by (keys[length], values[length])
|
|
for the passed searchKey. SearchKeys outside the range keys[0]-keys[Length]
|
|
clamp to the min or max value. This function assumes the number of pairs
|
|
(length) will be small and a linear search is used.
|
|
|
|
Repeated keys are allowed for discontinuous functions (so long as keys is
|
|
monotonically increasing). If key is the value of a repeated scalar in
|
|
keys the first one will be used.
|
|
*/
|
|
SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[],
|
|
const SkScalar values[], int length);
|
|
|
|
/*
|
|
* Helper to compare an array of scalars.
|
|
*/
|
|
static inline bool SkScalarsEqual(const SkScalar a[], const SkScalar b[], int n) {
|
|
SkASSERT(n >= 0);
|
|
for (int i = 0; i < n; ++i) {
|
|
if (a[i] != b[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
struct SkISize {
|
|
int32_t fWidth;
|
|
int32_t fHeight;
|
|
|
|
static constexpr SkISize Make(int32_t w, int32_t h) { return {w, h}; }
|
|
|
|
static constexpr SkISize MakeEmpty() { return {0, 0}; }
|
|
|
|
void set(int32_t w, int32_t h) { *this = SkISize{w, h}; }
|
|
|
|
/** Returns true iff fWidth == 0 && fHeight == 0
|
|
*/
|
|
bool isZero() const { return 0 == fWidth && 0 == fHeight; }
|
|
|
|
/** Returns true if either width or height are <= 0 */
|
|
bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; }
|
|
|
|
/** Set the width and height to 0 */
|
|
void setEmpty() { fWidth = fHeight = 0; }
|
|
|
|
constexpr int32_t width() const { return fWidth; }
|
|
constexpr int32_t height() const { return fHeight; }
|
|
|
|
constexpr int64_t area() const { return fWidth * fHeight; }
|
|
|
|
bool equals(int32_t w, int32_t h) const { return fWidth == w && fHeight == h; }
|
|
};
|
|
|
|
static inline bool operator==(const SkISize& a, const SkISize& b) {
|
|
return a.fWidth == b.fWidth && a.fHeight == b.fHeight;
|
|
}
|
|
|
|
static inline bool operator!=(const SkISize& a, const SkISize& b) { return !(a == b); }
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct SkSize {
|
|
SkScalar fWidth;
|
|
SkScalar fHeight;
|
|
|
|
static constexpr SkSize Make(SkScalar w, SkScalar h) { return {w, h}; }
|
|
|
|
static constexpr SkSize Make(const SkISize& src) {
|
|
return {SkIntToScalar(src.width()), SkIntToScalar(src.height())};
|
|
}
|
|
|
|
static constexpr SkSize MakeEmpty() { return {0, 0}; }
|
|
|
|
void set(SkScalar w, SkScalar h) { *this = SkSize{w, h}; }
|
|
|
|
/** Returns true iff fWidth == 0 && fHeight == 0
|
|
*/
|
|
bool isZero() const { return 0 == fWidth && 0 == fHeight; }
|
|
|
|
/** Returns true if either width or height are <= 0 */
|
|
bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; }
|
|
|
|
/** Set the width and height to 0 */
|
|
void setEmpty() { *this = SkSize{0, 0}; }
|
|
|
|
SkScalar width() const { return fWidth; }
|
|
SkScalar height() const { return fHeight; }
|
|
|
|
bool equals(SkScalar w, SkScalar h) const { return fWidth == w && fHeight == h; }
|
|
|
|
SkISize toRound() const { return {SkScalarRoundToInt(fWidth), SkScalarRoundToInt(fHeight)}; }
|
|
|
|
SkISize toCeil() const { return {SkScalarCeilToInt(fWidth), SkScalarCeilToInt(fHeight)}; }
|
|
|
|
SkISize toFloor() const { return {SkScalarFloorToInt(fWidth), SkScalarFloorToInt(fHeight)}; }
|
|
};
|
|
|
|
static inline bool operator==(const SkSize& a, const SkSize& b) {
|
|
return a.fWidth == b.fWidth && a.fHeight == b.fHeight;
|
|
}
|
|
|
|
static inline bool operator!=(const SkSize& a, const SkSize& b) { return !(a == b); }
|
|
|
|
// All of these files should be independent of things users can set via the user config file.
|
|
// They should also be able to be included in any order.
|
|
// IWYU pragma: begin_exports
|
|
|
|
// Load and verify defines from the user config file.
|
|
|
|
// Any includes or defines below can be configured by the user config file.
|
|
// IWYU pragma: end_exports
|
|
|
|
|
|
#if !defined(SK_GANESH) && !defined(SK_GRAPHITE)
|
|
# undef SK_GL
|
|
# undef SK_VULKAN
|
|
# undef SK_METAL
|
|
# undef SK_DAWN
|
|
# undef SK_DIRECT3D
|
|
#endif
|
|
|
|
// If SK_R32_SHIFT is set, we'll use that to choose RGBA or BGRA.
|
|
// If not, we'll default to RGBA everywhere except BGRA on Windows.
|
|
#if defined(SK_R32_SHIFT)
|
|
static_assert(SK_R32_SHIFT == 0 || SK_R32_SHIFT == 16, "");
|
|
#elif defined(SK_BUILD_FOR_WIN)
|
|
#define SK_R32_SHIFT 16
|
|
#else
|
|
#define SK_R32_SHIFT 0
|
|
#endif
|
|
|
|
#if defined(SK_B32_SHIFT)
|
|
static_assert(SK_B32_SHIFT == (16-SK_R32_SHIFT), "");
|
|
#else
|
|
#define SK_B32_SHIFT (16-SK_R32_SHIFT)
|
|
#endif
|
|
|
|
#define SK_G32_SHIFT 8
|
|
#define SK_A32_SHIFT 24
|
|
|
|
/**
|
|
* SK_PMCOLOR_BYTE_ORDER can be used to query the byte order of SkPMColor at compile time.
|
|
*/
|
|
#ifdef SK_CPU_BENDIAN
|
|
# define SK_PMCOLOR_BYTE_ORDER(C0, C1, C2, C3) \
|
|
(SK_ ## C3 ## 32_SHIFT == 0 && \
|
|
SK_ ## C2 ## 32_SHIFT == 8 && \
|
|
SK_ ## C1 ## 32_SHIFT == 16 && \
|
|
SK_ ## C0 ## 32_SHIFT == 24)
|
|
#else
|
|
# define SK_PMCOLOR_BYTE_ORDER(C0, C1, C2, C3) \
|
|
(SK_ ## C0 ## 32_SHIFT == 0 && \
|
|
SK_ ## C1 ## 32_SHIFT == 8 && \
|
|
SK_ ## C2 ## 32_SHIFT == 16 && \
|
|
SK_ ## C3 ## 32_SHIFT == 24)
|
|
#endif
|
|
|
|
#if defined SK_DEBUG && defined SK_BUILD_FOR_WIN
|
|
#ifdef free
|
|
#undef free
|
|
#endif
|
|
#undef free
|
|
#endif
|
|
|
|
#ifndef SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
|
|
#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0
|
|
#endif
|
|
|
|
#if !defined(SK_GAMMA_EXPONENT)
|
|
#define SK_GAMMA_EXPONENT (0.0f) // SRGB
|
|
#endif
|
|
|
|
#if defined(SK_HISTOGRAM_ENUMERATION) || \
|
|
defined(SK_HISTOGRAM_BOOLEAN) || \
|
|
defined(SK_HISTOGRAM_EXACT_LINEAR) || \
|
|
defined(SK_HISTOGRAM_MEMORY_KB)
|
|
# define SK_HISTOGRAMS_ENABLED 1
|
|
#else
|
|
# define SK_HISTOGRAMS_ENABLED 0
|
|
#endif
|
|
|
|
#ifndef SK_HISTOGRAM_BOOLEAN
|
|
# define SK_HISTOGRAM_BOOLEAN(name, sample)
|
|
#endif
|
|
|
|
#ifndef SK_HISTOGRAM_ENUMERATION
|
|
# define SK_HISTOGRAM_ENUMERATION(name, sample, enum_size)
|
|
#endif
|
|
|
|
#ifndef SK_HISTOGRAM_EXACT_LINEAR
|
|
# define SK_HISTOGRAM_EXACT_LINEAR(name, sample, value_max)
|
|
#endif
|
|
|
|
#ifndef SK_HISTOGRAM_MEMORY_KB
|
|
# define SK_HISTOGRAM_MEMORY_KB(name, sample)
|
|
#endif
|
|
|
|
#define SK_HISTOGRAM_PERCENTAGE(name, percent_as_int) \
|
|
SK_HISTOGRAM_EXACT_LINEAR(name, percent_as_int, 101)
|
|
|
|
// The top-level define SK_ENABLE_OPTIMIZE_SIZE can be used to remove several large features at once
|
|
#if defined(SK_ENABLE_OPTIMIZE_SIZE)
|
|
#if !defined(SK_FORCE_RASTER_PIPELINE_BLITTER)
|
|
#define SK_FORCE_RASTER_PIPELINE_BLITTER
|
|
#endif
|
|
#define SK_DISABLE_SDF_TEXT
|
|
#endif
|
|
|
|
#ifndef SK_DISABLE_LEGACY_SHADERCONTEXT
|
|
# define SK_ENABLE_LEGACY_SHADERCONTEXT
|
|
#endif
|
|
|
|
#if defined(SK_BUILD_FOR_LIBFUZZER) || defined(SK_BUILD_FOR_AFL_FUZZ)
|
|
#if !defined(SK_BUILD_FOR_FUZZER)
|
|
#define SK_BUILD_FOR_FUZZER
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* These defines are set to 0 or 1, rather than being undefined or defined
|
|
* TODO: consider updating these for consistency
|
|
*/
|
|
|
|
#if !defined(GR_CACHE_STATS)
|
|
#if defined(SK_DEBUG) || defined(SK_DUMP_STATS)
|
|
#define GR_CACHE_STATS 1
|
|
#else
|
|
#define GR_CACHE_STATS 0
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(GR_GPU_STATS)
|
|
#if defined(SK_DEBUG) || defined(SK_DUMP_STATS) || defined(GR_TEST_UTILS)
|
|
#define GR_GPU_STATS 1
|
|
#else
|
|
#define GR_GPU_STATS 0
|
|
#endif
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef uint32_t SkFourByteTag;
|
|
static inline constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d) {
|
|
return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** 32 bit integer to hold a unicode value
|
|
*/
|
|
typedef int32_t SkUnichar;
|
|
|
|
/** 16 bit unsigned integer to hold a glyph index
|
|
*/
|
|
typedef uint16_t SkGlyphID;
|
|
|
|
/** 32 bit value to hold a millisecond duration
|
|
Note that SK_MSecMax is about 25 days.
|
|
*/
|
|
typedef uint32_t SkMSec;
|
|
|
|
/** Maximum representable milliseconds; 24d 20h 31m 23.647s.
|
|
*/
|
|
static constexpr SkMSec SK_MSecMax = INT32_MAX;
|
|
|
|
/** The generation IDs in Skia reserve 0 has an invalid marker.
|
|
*/
|
|
static constexpr uint32_t SK_InvalidGenID = 0;
|
|
|
|
/** The unique IDs in Skia reserve 0 has an invalid marker.
|
|
*/
|
|
static constexpr uint32_t SK_InvalidUniqueID = 0;
|
|
|
|
/**
|
|
* std::underlying_type is only defined for enums. For integral types, we just want the type.
|
|
*/
|
|
template <typename T, class Enable = void>
|
|
struct sk_strip_enum {
|
|
typedef T type;
|
|
};
|
|
|
|
template <typename T>
|
|
struct sk_strip_enum<T, typename std::enable_if<std::is_enum<T>::value>::type> {
|
|
typedef typename std::underlying_type<T>::type type;
|
|
};
|
|
|
|
|
|
/**
|
|
* In C++ an unsigned to signed cast where the source value cannot be represented in the destination
|
|
* type results in an implementation defined destination value. Unlike C, C++ does not allow a trap.
|
|
* This makes "(S)(D)s == s" a possibly useful test. However, there are two cases where this is
|
|
* incorrect:
|
|
*
|
|
* when testing if a value of a smaller signed type can be represented in a larger unsigned type
|
|
* (int8_t)(uint16_t)-1 == -1 => (int8_t)0xFFFF == -1 => [implementation defined] == -1
|
|
*
|
|
* when testing if a value of a larger unsigned type can be represented in a smaller signed type
|
|
* (uint16_t)(int8_t)0xFFFF == 0xFFFF => (uint16_t)-1 == 0xFFFF => 0xFFFF == 0xFFFF => true.
|
|
*
|
|
* Consider the cases:
|
|
* u = unsigned, less digits
|
|
* U = unsigned, more digits
|
|
* s = signed, less digits
|
|
* S = signed, more digits
|
|
* v is the value we're considering.
|
|
*
|
|
* u -> U: (u)(U)v == v, trivially true
|
|
* U -> u: (U)(u)v == v, both casts well defined, test works
|
|
* s -> S: (s)(S)v == v, trivially true
|
|
* S -> s: (S)(s)v == v, first cast implementation value, second cast defined, test works
|
|
* s -> U: (s)(U)v == v, *this is bad*, the second cast results in implementation defined value
|
|
* S -> u: (S)(u)v == v, the second cast is required to prevent promotion of rhs to unsigned
|
|
* u -> S: (u)(S)v == v, trivially true
|
|
* U -> s: (U)(s)v == v, *this is bad*,
|
|
* first cast results in implementation defined value,
|
|
* second cast is defined. However, this creates false positives
|
|
* uint16_t x = 0xFFFF
|
|
* (uint16_t)(int8_t)x == x
|
|
* => (uint16_t)-1 == x
|
|
* => 0xFFFF == x
|
|
* => true
|
|
*
|
|
* So for the eight cases three are trivially true, three more are valid casts, and two are special.
|
|
* The two 'full' checks which otherwise require two comparisons are valid cast checks.
|
|
* The two remaining checks s -> U [v >= 0] and U -> s [v <= max(s)] can be done with one op.
|
|
*/
|
|
|
|
template <typename D, typename S>
|
|
static constexpr inline
|
|
typename std::enable_if<(std::is_integral<S>::value || std::is_enum<S>::value) &&
|
|
(std::is_integral<D>::value || std::is_enum<D>::value), bool>::type
|
|
/*bool*/ SkTFitsIn(S src) {
|
|
// Ensure that is_signed and is_unsigned are passed the arithmetic underlyng types of enums.
|
|
using Sa = typename sk_strip_enum<S>::type;
|
|
using Da = typename sk_strip_enum<D>::type;
|
|
|
|
// SkTFitsIn() is used in public headers, so needs to be written targeting at most C++11.
|
|
return
|
|
|
|
// E.g. (int8_t)(uint8_t) int8_t(-1) == -1, but the uint8_t == 255, not -1.
|
|
(std::is_signed<Sa>::value && std::is_unsigned<Da>::value && sizeof(Sa) <= sizeof(Da)) ?
|
|
(S)0 <= src :
|
|
|
|
// E.g. (uint8_t)(int8_t) uint8_t(255) == 255, but the int8_t == -1.
|
|
(std::is_signed<Da>::value && std::is_unsigned<Sa>::value && sizeof(Da) <= sizeof(Sa)) ?
|
|
src <= (S)std::numeric_limits<Da>::max() :
|
|
|
|
#if !defined(SK_DEBUG) && !defined(__MSVC_RUNTIME_CHECKS )
|
|
// Correct (simple) version. This trips up MSVC's /RTCc run-time checking.
|
|
(S)(D)src == src;
|
|
#else
|
|
// More complex version that's safe with /RTCc. Used in all debug builds, for coverage.
|
|
(std::is_signed<Sa>::value) ?
|
|
(intmax_t)src >= (intmax_t)std::numeric_limits<Da>::min() &&
|
|
(intmax_t)src <= (intmax_t)std::numeric_limits<Da>::max() :
|
|
|
|
// std::is_unsigned<S> ?
|
|
(uintmax_t)src <= (uintmax_t)std::numeric_limits<Da>::max();
|
|
#endif
|
|
}
|
|
|
|
struct SkRect;
|
|
|
|
/** \struct SkIRect
|
|
SkIRect holds four 32-bit integer coordinates describing the upper and
|
|
lower bounds of a rectangle. SkIRect may be created from outer bounds or
|
|
from position, width, and height. SkIRect describes an area; if its right
|
|
is less than or equal to its left, or if its bottom is less than or equal to
|
|
its top, it is considered empty.
|
|
*/
|
|
struct SK_API SkIRect {
|
|
int32_t fLeft = 0; //!< smaller x-axis bounds
|
|
int32_t fTop = 0; //!< smaller y-axis bounds
|
|
int32_t fRight = 0; //!< larger x-axis bounds
|
|
int32_t fBottom = 0; //!< larger y-axis bounds
|
|
|
|
/** Returns constructed SkIRect set to (0, 0, 0, 0).
|
|
Many other rectangles are empty; if left is equal to or greater than right,
|
|
or if top is equal to or greater than bottom. Setting all members to zero
|
|
is a convenience, but does not designate a special empty rectangle.
|
|
|
|
@return bounds (0, 0, 0, 0)
|
|
*/
|
|
[[nodiscard]] static constexpr SkIRect MakeEmpty() {
|
|
return SkIRect{0, 0, 0, 0};
|
|
}
|
|
|
|
/** Returns constructed SkIRect set to (0, 0, w, h). Does not validate input; w or h
|
|
may be negative.
|
|
|
|
@param w width of constructed SkIRect
|
|
@param h height of constructed SkIRect
|
|
@return bounds (0, 0, w, h)
|
|
*/
|
|
[[nodiscard]] static constexpr SkIRect MakeWH(int32_t w, int32_t h) {
|
|
return SkIRect{0, 0, w, h};
|
|
}
|
|
|
|
/** Returns constructed SkIRect set to (0, 0, size.width(), size.height()).
|
|
Does not validate input; size.width() or size.height() may be negative.
|
|
|
|
@param size values for SkIRect width and height
|
|
@return bounds (0, 0, size.width(), size.height())
|
|
*/
|
|
[[nodiscard]] static constexpr SkIRect MakeSize(const SkISize& size) {
|
|
return SkIRect{0, 0, size.fWidth, size.fHeight};
|
|
}
|
|
|
|
/** Returns constructed SkIRect set to (pt.x(), pt.y(), pt.x() + size.width(),
|
|
pt.y() + size.height()). Does not validate input; size.width() or size.height() may be
|
|
negative.
|
|
|
|
@param pt values for SkIRect fLeft and fTop
|
|
@param size values for SkIRect width and height
|
|
@return bounds at pt with width and height of size
|
|
*/
|
|
[[nodiscard]] static constexpr SkIRect MakePtSize(SkIPoint pt, SkISize size) {
|
|
return MakeXYWH(pt.x(), pt.y(), size.width(), size.height());
|
|
}
|
|
|
|
/** Returns constructed SkIRect set to (l, t, r, b). Does not sort input; SkIRect may
|
|
result in fLeft greater than fRight, or fTop greater than fBottom.
|
|
|
|
@param l integer stored in fLeft
|
|
@param t integer stored in fTop
|
|
@param r integer stored in fRight
|
|
@param b integer stored in fBottom
|
|
@return bounds (l, t, r, b)
|
|
*/
|
|
[[nodiscard]] static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
|
|
return SkIRect{l, t, r, b};
|
|
}
|
|
|
|
/** Returns constructed SkIRect set to: (x, y, x + w, y + h).
|
|
Does not validate input; w or h may be negative.
|
|
|
|
@param x stored in fLeft
|
|
@param y stored in fTop
|
|
@param w added to x and stored in fRight
|
|
@param h added to y and stored in fBottom
|
|
@return bounds at (x, y) with width w and height h
|
|
*/
|
|
[[nodiscard]] static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
|
|
return { x, y, Sk32_sat_add(x, w), Sk32_sat_add(y, h) };
|
|
}
|
|
|
|
/** Returns left edge of SkIRect, if sorted.
|
|
Call sort() to reverse fLeft and fRight if needed.
|
|
|
|
@return fLeft
|
|
*/
|
|
constexpr int32_t left() const { return fLeft; }
|
|
|
|
/** Returns top edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid,
|
|
and sort() to reverse fTop and fBottom if needed.
|
|
|
|
@return fTop
|
|
*/
|
|
constexpr int32_t top() const { return fTop; }
|
|
|
|
/** Returns right edge of SkIRect, if sorted.
|
|
Call sort() to reverse fLeft and fRight if needed.
|
|
|
|
@return fRight
|
|
*/
|
|
constexpr int32_t right() const { return fRight; }
|
|
|
|
/** Returns bottom edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid,
|
|
and sort() to reverse fTop and fBottom if needed.
|
|
|
|
@return fBottom
|
|
*/
|
|
constexpr int32_t bottom() const { return fBottom; }
|
|
|
|
/** Returns left edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid,
|
|
and sort() to reverse fLeft and fRight if needed.
|
|
|
|
@return fLeft
|
|
*/
|
|
constexpr int32_t x() const { return fLeft; }
|
|
|
|
/** Returns top edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid,
|
|
and sort() to reverse fTop and fBottom if needed.
|
|
|
|
@return fTop
|
|
*/
|
|
constexpr int32_t y() const { return fTop; }
|
|
|
|
// Experimental
|
|
constexpr SkIPoint topLeft() const { return {fLeft, fTop}; }
|
|
|
|
/** Returns span on the x-axis. This does not check if SkIRect is sorted, or if
|
|
result fits in 32-bit signed integer; result may be negative.
|
|
|
|
@return fRight minus fLeft
|
|
*/
|
|
constexpr int32_t width() const { return Sk32_can_overflow_sub(fRight, fLeft); }
|
|
|
|
/** Returns span on the y-axis. This does not check if SkIRect is sorted, or if
|
|
result fits in 32-bit signed integer; result may be negative.
|
|
|
|
@return fBottom minus fTop
|
|
*/
|
|
constexpr int32_t height() const { return Sk32_can_overflow_sub(fBottom, fTop); }
|
|
|
|
/** Returns spans on the x-axis and y-axis. This does not check if SkIRect is sorted,
|
|
or if result fits in 32-bit signed integer; result may be negative.
|
|
|
|
@return SkISize (width, height)
|
|
*/
|
|
constexpr SkISize size() const { return SkISize::Make(this->width(), this->height()); }
|
|
|
|
/** Returns span on the x-axis. This does not check if SkIRect is sorted, so the
|
|
result may be negative. This is safer than calling width() since width() might
|
|
overflow in its calculation.
|
|
|
|
@return fRight minus fLeft cast to int64_t
|
|
*/
|
|
constexpr int64_t width64() const { return (int64_t)fRight - (int64_t)fLeft; }
|
|
|
|
/** Returns span on the y-axis. This does not check if SkIRect is sorted, so the
|
|
result may be negative. This is safer than calling height() since height() might
|
|
overflow in its calculation.
|
|
|
|
@return fBottom minus fTop cast to int64_t
|
|
*/
|
|
constexpr int64_t height64() const { return (int64_t)fBottom - (int64_t)fTop; }
|
|
|
|
/** Returns true if fLeft is equal to or greater than fRight, or if fTop is equal
|
|
to or greater than fBottom. Call sort() to reverse rectangles with negative
|
|
width64() or height64().
|
|
|
|
@return true if width64() or height64() are zero or negative
|
|
*/
|
|
bool isEmpty64() const { return fRight <= fLeft || fBottom <= fTop; }
|
|
|
|
/** Returns true if width() or height() are zero or negative.
|
|
|
|
@return true if width() or height() are zero or negative
|
|
*/
|
|
bool isEmpty() const {
|
|
int64_t w = this->width64();
|
|
int64_t h = this->height64();
|
|
if (w <= 0 || h <= 0) {
|
|
return true;
|
|
}
|
|
// Return true if either exceeds int32_t
|
|
return !SkTFitsIn<int32_t>(w | h);
|
|
}
|
|
|
|
/** Returns true if all members in a: fLeft, fTop, fRight, and fBottom; are
|
|
identical to corresponding members in b.
|
|
|
|
@param a SkIRect to compare
|
|
@param b SkIRect to compare
|
|
@return true if members are equal
|
|
*/
|
|
friend bool operator==(const SkIRect& a, const SkIRect& b) {
|
|
return a.fLeft == b.fLeft && a.fTop == b.fTop &&
|
|
a.fRight == b.fRight && a.fBottom == b.fBottom;
|
|
}
|
|
|
|
/** Returns true if any member in a: fLeft, fTop, fRight, and fBottom; is not
|
|
identical to the corresponding member in b.
|
|
|
|
@param a SkIRect to compare
|
|
@param b SkIRect to compare
|
|
@return true if members are not equal
|
|
*/
|
|
friend bool operator!=(const SkIRect& a, const SkIRect& b) {
|
|
return a.fLeft != b.fLeft || a.fTop != b.fTop ||
|
|
a.fRight != b.fRight || a.fBottom != b.fBottom;
|
|
}
|
|
|
|
/** Sets SkIRect to (0, 0, 0, 0).
|
|
|
|
Many other rectangles are empty; if left is equal to or greater than right,
|
|
or if top is equal to or greater than bottom. Setting all members to zero
|
|
is a convenience, but does not designate a special empty rectangle.
|
|
*/
|
|
void setEmpty() { memset(this, 0, sizeof(*this)); }
|
|
|
|
/** Sets SkIRect to (left, top, right, bottom).
|
|
left and right are not sorted; left is not necessarily less than right.
|
|
top and bottom are not sorted; top is not necessarily less than bottom.
|
|
|
|
@param left stored in fLeft
|
|
@param top stored in fTop
|
|
@param right stored in fRight
|
|
@param bottom stored in fBottom
|
|
*/
|
|
void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
|
|
fLeft = left;
|
|
fTop = top;
|
|
fRight = right;
|
|
fBottom = bottom;
|
|
}
|
|
|
|
/** Sets SkIRect to: (x, y, x + width, y + height).
|
|
Does not validate input; width or height may be negative.
|
|
|
|
@param x stored in fLeft
|
|
@param y stored in fTop
|
|
@param width added to x and stored in fRight
|
|
@param height added to y and stored in fBottom
|
|
*/
|
|
void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
|
|
fLeft = x;
|
|
fTop = y;
|
|
fRight = Sk32_sat_add(x, width);
|
|
fBottom = Sk32_sat_add(y, height);
|
|
}
|
|
|
|
void setWH(int32_t width, int32_t height) {
|
|
fLeft = 0;
|
|
fTop = 0;
|
|
fRight = width;
|
|
fBottom = height;
|
|
}
|
|
|
|
void setSize(SkISize size) {
|
|
fLeft = 0;
|
|
fTop = 0;
|
|
fRight = size.width();
|
|
fBottom = size.height();
|
|
}
|
|
|
|
/** Returns SkIRect offset by (dx, dy).
|
|
|
|
If dx is negative, SkIRect returned is moved to the left.
|
|
If dx is positive, SkIRect returned is moved to the right.
|
|
If dy is negative, SkIRect returned is moved upward.
|
|
If dy is positive, SkIRect returned is moved downward.
|
|
|
|
@param dx offset added to fLeft and fRight
|
|
@param dy offset added to fTop and fBottom
|
|
@return SkIRect offset by dx and dy, with original width and height
|
|
*/
|
|
constexpr SkIRect makeOffset(int32_t dx, int32_t dy) const {
|
|
return {
|
|
Sk32_sat_add(fLeft, dx), Sk32_sat_add(fTop, dy),
|
|
Sk32_sat_add(fRight, dx), Sk32_sat_add(fBottom, dy),
|
|
};
|
|
}
|
|
|
|
/** Returns SkIRect offset by (offset.x(), offset.y()).
|
|
|
|
If offset.x() is negative, SkIRect returned is moved to the left.
|
|
If offset.x() is positive, SkIRect returned is moved to the right.
|
|
If offset.y() is negative, SkIRect returned is moved upward.
|
|
If offset.y() is positive, SkIRect returned is moved downward.
|
|
|
|
@param offset translation vector
|
|
@return SkIRect translated by offset, with original width and height
|
|
*/
|
|
constexpr SkIRect makeOffset(SkIVector offset) const {
|
|
return this->makeOffset(offset.x(), offset.y());
|
|
}
|
|
|
|
/** Returns SkIRect, inset by (dx, dy).
|
|
|
|
If dx is negative, SkIRect returned is wider.
|
|
If dx is positive, SkIRect returned is narrower.
|
|
If dy is negative, SkIRect returned is taller.
|
|
If dy is positive, SkIRect returned is shorter.
|
|
|
|
@param dx offset added to fLeft and subtracted from fRight
|
|
@param dy offset added to fTop and subtracted from fBottom
|
|
@return SkIRect inset symmetrically left and right, top and bottom
|
|
*/
|
|
SkIRect makeInset(int32_t dx, int32_t dy) const {
|
|
return {
|
|
Sk32_sat_add(fLeft, dx), Sk32_sat_add(fTop, dy),
|
|
Sk32_sat_sub(fRight, dx), Sk32_sat_sub(fBottom, dy),
|
|
};
|
|
}
|
|
|
|
/** Returns SkIRect, outset by (dx, dy).
|
|
|
|
If dx is negative, SkIRect returned is narrower.
|
|
If dx is positive, SkIRect returned is wider.
|
|
If dy is negative, SkIRect returned is shorter.
|
|
If dy is positive, SkIRect returned is taller.
|
|
|
|
@param dx offset subtracted to fLeft and added from fRight
|
|
@param dy offset subtracted to fTop and added from fBottom
|
|
@return SkIRect outset symmetrically left and right, top and bottom
|
|
*/
|
|
SkIRect makeOutset(int32_t dx, int32_t dy) const {
|
|
return {
|
|
Sk32_sat_sub(fLeft, dx), Sk32_sat_sub(fTop, dy),
|
|
Sk32_sat_add(fRight, dx), Sk32_sat_add(fBottom, dy),
|
|
};
|
|
}
|
|
|
|
/** Offsets SkIRect by adding dx to fLeft, fRight; and by adding dy to fTop, fBottom.
|
|
|
|
If dx is negative, moves SkIRect returned to the left.
|
|
If dx is positive, moves SkIRect returned to the right.
|
|
If dy is negative, moves SkIRect returned upward.
|
|
If dy is positive, moves SkIRect returned downward.
|
|
|
|
@param dx offset added to fLeft and fRight
|
|
@param dy offset added to fTop and fBottom
|
|
*/
|
|
void offset(int32_t dx, int32_t dy) {
|
|
fLeft = Sk32_sat_add(fLeft, dx);
|
|
fTop = Sk32_sat_add(fTop, dy);
|
|
fRight = Sk32_sat_add(fRight, dx);
|
|
fBottom = Sk32_sat_add(fBottom, dy);
|
|
}
|
|
|
|
/** Offsets SkIRect by adding delta.fX to fLeft, fRight; and by adding delta.fY to
|
|
fTop, fBottom.
|
|
|
|
If delta.fX is negative, moves SkIRect returned to the left.
|
|
If delta.fX is positive, moves SkIRect returned to the right.
|
|
If delta.fY is negative, moves SkIRect returned upward.
|
|
If delta.fY is positive, moves SkIRect returned downward.
|
|
|
|
@param delta offset added to SkIRect
|
|
*/
|
|
void offset(const SkIPoint& delta) {
|
|
this->offset(delta.fX, delta.fY);
|
|
}
|
|
|
|
/** Offsets SkIRect so that fLeft equals newX, and fTop equals newY. width and height
|
|
are unchanged.
|
|
|
|
@param newX stored in fLeft, preserving width()
|
|
@param newY stored in fTop, preserving height()
|
|
*/
|
|
void offsetTo(int32_t newX, int32_t newY) {
|
|
fRight = Sk64_pin_to_s32((int64_t)fRight + newX - fLeft);
|
|
fBottom = Sk64_pin_to_s32((int64_t)fBottom + newY - fTop);
|
|
fLeft = newX;
|
|
fTop = newY;
|
|
}
|
|
|
|
/** Insets SkIRect by (dx,dy).
|
|
|
|
If dx is positive, makes SkIRect narrower.
|
|
If dx is negative, makes SkIRect wider.
|
|
If dy is positive, makes SkIRect shorter.
|
|
If dy is negative, makes SkIRect taller.
|
|
|
|
@param dx offset added to fLeft and subtracted from fRight
|
|
@param dy offset added to fTop and subtracted from fBottom
|
|
*/
|
|
void inset(int32_t dx, int32_t dy) {
|
|
fLeft = Sk32_sat_add(fLeft, dx);
|
|
fTop = Sk32_sat_add(fTop, dy);
|
|
fRight = Sk32_sat_sub(fRight, dx);
|
|
fBottom = Sk32_sat_sub(fBottom, dy);
|
|
}
|
|
|
|
/** Outsets SkIRect by (dx, dy).
|
|
|
|
If dx is positive, makes SkIRect wider.
|
|
If dx is negative, makes SkIRect narrower.
|
|
If dy is positive, makes SkIRect taller.
|
|
If dy is negative, makes SkIRect shorter.
|
|
|
|
@param dx subtracted to fLeft and added from fRight
|
|
@param dy subtracted to fTop and added from fBottom
|
|
*/
|
|
void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); }
|
|
|
|
/** Adjusts SkIRect by adding dL to fLeft, dT to fTop, dR to fRight, and dB to fBottom.
|
|
|
|
If dL is positive, narrows SkIRect on the left. If negative, widens it on the left.
|
|
If dT is positive, shrinks SkIRect on the top. If negative, lengthens it on the top.
|
|
If dR is positive, narrows SkIRect on the right. If negative, widens it on the right.
|
|
If dB is positive, shrinks SkIRect on the bottom. If negative, lengthens it on the bottom.
|
|
|
|
The resulting SkIRect is not checked for validity. Thus, if the resulting SkIRect left is
|
|
greater than right, the SkIRect will be considered empty. Call sort() after this call
|
|
if that is not the desired behavior.
|
|
|
|
@param dL offset added to fLeft
|
|
@param dT offset added to fTop
|
|
@param dR offset added to fRight
|
|
@param dB offset added to fBottom
|
|
*/
|
|
void adjust(int32_t dL, int32_t dT, int32_t dR, int32_t dB) {
|
|
fLeft = Sk32_sat_add(fLeft, dL);
|
|
fTop = Sk32_sat_add(fTop, dT);
|
|
fRight = Sk32_sat_add(fRight, dR);
|
|
fBottom = Sk32_sat_add(fBottom, dB);
|
|
}
|
|
|
|
/** Returns true if: fLeft <= x < fRight && fTop <= y < fBottom.
|
|
Returns false if SkIRect is empty.
|
|
|
|
Considers input to describe constructed SkIRect: (x, y, x + 1, y + 1) and
|
|
returns true if constructed area is completely enclosed by SkIRect area.
|
|
|
|
@param x test SkIPoint x-coordinate
|
|
@param y test SkIPoint y-coordinate
|
|
@return true if (x, y) is inside SkIRect
|
|
*/
|
|
bool contains(int32_t x, int32_t y) const {
|
|
return x >= fLeft && x < fRight && y >= fTop && y < fBottom;
|
|
}
|
|
|
|
/** Returns true if SkIRect contains r.
|
|
Returns false if SkIRect is empty or r is empty.
|
|
|
|
SkIRect contains r when SkIRect area completely includes r area.
|
|
|
|
@param r SkIRect contained
|
|
@return true if all sides of SkIRect are outside r
|
|
*/
|
|
bool contains(const SkIRect& r) const {
|
|
return !r.isEmpty() && !this->isEmpty() && // check for empties
|
|
fLeft <= r.fLeft && fTop <= r.fTop &&
|
|
fRight >= r.fRight && fBottom >= r.fBottom;
|
|
}
|
|
|
|
/** Returns true if SkIRect contains r.
|
|
Returns false if SkIRect is empty or r is empty.
|
|
|
|
SkIRect contains r when SkIRect area completely includes r area.
|
|
|
|
@param r SkRect contained
|
|
@return true if all sides of SkIRect are outside r
|
|
*/
|
|
inline bool contains(const SkRect& r) const;
|
|
|
|
/** Returns true if SkIRect contains construction.
|
|
Asserts if SkIRect is empty or construction is empty, and if SK_DEBUG is defined.
|
|
|
|
Return is undefined if SkIRect is empty or construction is empty.
|
|
|
|
@param r SkIRect contained
|
|
@return true if all sides of SkIRect are outside r
|
|
*/
|
|
bool containsNoEmptyCheck(const SkIRect& r) const {
|
|
SkASSERT(fLeft < fRight && fTop < fBottom);
|
|
SkASSERT(r.fLeft < r.fRight && r.fTop < r.fBottom);
|
|
return fLeft <= r.fLeft && fTop <= r.fTop && fRight >= r.fRight && fBottom >= r.fBottom;
|
|
}
|
|
|
|
/** Returns true if SkIRect intersects r, and sets SkIRect to intersection.
|
|
Returns false if SkIRect does not intersect r, and leaves SkIRect unchanged.
|
|
|
|
Returns false if either r or SkIRect is empty, leaving SkIRect unchanged.
|
|
|
|
@param r limit of result
|
|
@return true if r and SkIRect have area in common
|
|
*/
|
|
bool intersect(const SkIRect& r) {
|
|
return this->intersect(*this, r);
|
|
}
|
|
|
|
/** Returns true if a intersects b, and sets SkIRect to intersection.
|
|
Returns false if a does not intersect b, and leaves SkIRect unchanged.
|
|
|
|
Returns false if either a or b is empty, leaving SkIRect unchanged.
|
|
|
|
@param a SkIRect to intersect
|
|
@param b SkIRect to intersect
|
|
@return true if a and b have area in common
|
|
*/
|
|
[[nodiscard]] bool intersect(const SkIRect& a, const SkIRect& b);
|
|
|
|
/** Returns true if a intersects b.
|
|
Returns false if either a or b is empty, or do not intersect.
|
|
|
|
@param a SkIRect to intersect
|
|
@param b SkIRect to intersect
|
|
@return true if a and b have area in common
|
|
*/
|
|
static bool Intersects(const SkIRect& a, const SkIRect& b) {
|
|
return SkIRect{}.intersect(a, b);
|
|
}
|
|
|
|
/** Sets SkIRect to the union of itself and r.
|
|
|
|
Has no effect if r is empty. Otherwise, if SkIRect is empty, sets SkIRect to r.
|
|
|
|
@param r expansion SkIRect
|
|
|
|
example: https://fiddle.skia.org/c/@IRect_join_2
|
|
*/
|
|
void join(const SkIRect& r);
|
|
|
|
/** Swaps fLeft and fRight if fLeft is greater than fRight; and swaps
|
|
fTop and fBottom if fTop is greater than fBottom. Result may be empty,
|
|
and width() and height() will be zero or positive.
|
|
*/
|
|
void sort() {
|
|
using std::swap;
|
|
if (fLeft > fRight) {
|
|
swap(fLeft, fRight);
|
|
}
|
|
if (fTop > fBottom) {
|
|
swap(fTop, fBottom);
|
|
}
|
|
}
|
|
|
|
/** Returns SkIRect with fLeft and fRight swapped if fLeft is greater than fRight; and
|
|
with fTop and fBottom swapped if fTop is greater than fBottom. Result may be empty;
|
|
and width() and height() will be zero or positive.
|
|
|
|
@return sorted SkIRect
|
|
*/
|
|
SkIRect makeSorted() const {
|
|
return MakeLTRB(std::min(fLeft, fRight), std::min(fTop, fBottom),
|
|
std::max(fLeft, fRight), std::max(fTop, fBottom));
|
|
}
|
|
};
|
|
|
|
/** \struct SkRect
|
|
SkRect holds four float coordinates describing the upper and
|
|
lower bounds of a rectangle. SkRect may be created from outer bounds or
|
|
from position, width, and height. SkRect describes an area; if its right
|
|
is less than or equal to its left, or if its bottom is less than or equal to
|
|
its top, it is considered empty.
|
|
*/
|
|
struct SK_API SkRect {
|
|
float fLeft = 0; //!< smaller x-axis bounds
|
|
float fTop = 0; //!< smaller y-axis bounds
|
|
float fRight = 0; //!< larger x-axis bounds
|
|
float fBottom = 0; //!< larger y-axis bounds
|
|
|
|
/** Returns constructed SkRect set to (0, 0, 0, 0).
|
|
Many other rectangles are empty; if left is equal to or greater than right,
|
|
or if top is equal to or greater than bottom. Setting all members to zero
|
|
is a convenience, but does not designate a special empty rectangle.
|
|
|
|
@return bounds (0, 0, 0, 0)
|
|
*/
|
|
[[nodiscard]] static constexpr SkRect MakeEmpty() {
|
|
return SkRect{0, 0, 0, 0};
|
|
}
|
|
|
|
/** Returns constructed SkRect set to float values (0, 0, w, h). Does not
|
|
validate input; w or h may be negative.
|
|
|
|
Passing integer values may generate a compiler warning since SkRect cannot
|
|
represent 32-bit integers exactly. Use SkIRect for an exact integer rectangle.
|
|
|
|
@param w float width of constructed SkRect
|
|
@param h float height of constructed SkRect
|
|
@return bounds (0, 0, w, h)
|
|
*/
|
|
[[nodiscard]] static constexpr SkRect MakeWH(float w, float h) {
|
|
return SkRect{0, 0, w, h};
|
|
}
|
|
|
|
/** Returns constructed SkRect set to integer values (0, 0, w, h). Does not validate
|
|
input; w or h may be negative.
|
|
|
|
Use to avoid a compiler warning that input may lose precision when stored.
|
|
Use SkIRect for an exact integer rectangle.
|
|
|
|
@param w integer width of constructed SkRect
|
|
@param h integer height of constructed SkRect
|
|
@return bounds (0, 0, w, h)
|
|
*/
|
|
[[nodiscard]] static SkRect MakeIWH(int w, int h) {
|
|
return {0, 0, static_cast<float>(w), static_cast<float>(h)};
|
|
}
|
|
|
|
/** Returns constructed SkRect set to (0, 0, size.width(), size.height()). Does not
|
|
validate input; size.width() or size.height() may be negative.
|
|
|
|
@param size float values for SkRect width and height
|
|
@return bounds (0, 0, size.width(), size.height())
|
|
*/
|
|
[[nodiscard]] static constexpr SkRect MakeSize(const SkSize& size) {
|
|
return SkRect{0, 0, size.fWidth, size.fHeight};
|
|
}
|
|
|
|
/** Returns constructed SkRect set to (l, t, r, b). Does not sort input; SkRect may
|
|
result in fLeft greater than fRight, or fTop greater than fBottom.
|
|
|
|
@param l float stored in fLeft
|
|
@param t float stored in fTop
|
|
@param r float stored in fRight
|
|
@param b float stored in fBottom
|
|
@return bounds (l, t, r, b)
|
|
*/
|
|
[[nodiscard]] static constexpr SkRect MakeLTRB(float l, float t, float r, float b) {
|
|
return SkRect {l, t, r, b};
|
|
}
|
|
|
|
/** Returns constructed SkRect set to (x, y, x + w, y + h).
|
|
Does not validate input; w or h may be negative.
|
|
|
|
@param x stored in fLeft
|
|
@param y stored in fTop
|
|
@param w added to x and stored in fRight
|
|
@param h added to y and stored in fBottom
|
|
@return bounds at (x, y) with width w and height h
|
|
*/
|
|
[[nodiscard]] static constexpr SkRect MakeXYWH(float x, float y, float w, float h) {
|
|
return SkRect {x, y, x + w, y + h};
|
|
}
|
|
|
|
/** Returns constructed SkIRect set to (0, 0, size.width(), size.height()).
|
|
Does not validate input; size.width() or size.height() may be negative.
|
|
|
|
@param size integer values for SkRect width and height
|
|
@return bounds (0, 0, size.width(), size.height())
|
|
*/
|
|
static SkRect Make(const SkISize& size) {
|
|
return MakeIWH(size.width(), size.height());
|
|
}
|
|
|
|
/** Returns constructed SkIRect set to irect, promoting integers to float.
|
|
Does not validate input; fLeft may be greater than fRight, fTop may be greater
|
|
than fBottom.
|
|
|
|
@param irect integer unsorted bounds
|
|
@return irect members converted to float
|
|
*/
|
|
[[nodiscard]] static SkRect Make(const SkIRect& irect) {
|
|
return {
|
|
static_cast<float>(irect.fLeft), static_cast<float>(irect.fTop),
|
|
static_cast<float>(irect.fRight), static_cast<float>(irect.fBottom)
|
|
};
|
|
}
|
|
|
|
/** Returns true if fLeft is equal to or greater than fRight, or if fTop is equal
|
|
to or greater than fBottom. Call sort() to reverse rectangles with negative
|
|
width() or height().
|
|
|
|
@return true if width() or height() are zero or negative
|
|
*/
|
|
bool isEmpty() const {
|
|
// We write it as the NOT of a non-empty rect, so we will return true if any values
|
|
// are NaN.
|
|
return !(fLeft < fRight && fTop < fBottom);
|
|
}
|
|
|
|
/** Returns true if fLeft is equal to or less than fRight, or if fTop is equal
|
|
to or less than fBottom. Call sort() to reverse rectangles with negative
|
|
width() or height().
|
|
|
|
@return true if width() or height() are zero or positive
|
|
*/
|
|
bool isSorted() const { return fLeft <= fRight && fTop <= fBottom; }
|
|
|
|
/** Returns true if all values in the rectangle are finite.
|
|
|
|
@return true if no member is infinite or NaN
|
|
*/
|
|
bool isFinite() const {
|
|
float accum = 0;
|
|
accum *= fLeft;
|
|
accum *= fTop;
|
|
accum *= fRight;
|
|
accum *= fBottom;
|
|
|
|
// accum is either NaN or it is finite (zero).
|
|
SkASSERT(0 == accum || std::isnan(accum));
|
|
|
|
// value==value will be true iff value is not NaN
|
|
// TODO: is it faster to say !accum or accum==accum?
|
|
return !std::isnan(accum);
|
|
}
|
|
|
|
/** Returns left edge of SkRect, if sorted. Call isSorted() to see if SkRect is valid.
|
|
Call sort() to reverse fLeft and fRight if needed.
|
|
|
|
@return fLeft
|
|
*/
|
|
constexpr float x() const { return fLeft; }
|
|
|
|
/** Returns top edge of SkRect, if sorted. Call isEmpty() to see if SkRect may be invalid,
|
|
and sort() to reverse fTop and fBottom if needed.
|
|
|
|
@return fTop
|
|
*/
|
|
constexpr float y() const { return fTop; }
|
|
|
|
/** Returns left edge of SkRect, if sorted. Call isSorted() to see if SkRect is valid.
|
|
Call sort() to reverse fLeft and fRight if needed.
|
|
|
|
@return fLeft
|
|
*/
|
|
constexpr float left() const { return fLeft; }
|
|
|
|
/** Returns top edge of SkRect, if sorted. Call isEmpty() to see if SkRect may be invalid,
|
|
and sort() to reverse fTop and fBottom if needed.
|
|
|
|
@return fTop
|
|
*/
|
|
constexpr float top() const { return fTop; }
|
|
|
|
/** Returns right edge of SkRect, if sorted. Call isSorted() to see if SkRect is valid.
|
|
Call sort() to reverse fLeft and fRight if needed.
|
|
|
|
@return fRight
|
|
*/
|
|
constexpr float right() const { return fRight; }
|
|
|
|
/** Returns bottom edge of SkRect, if sorted. Call isEmpty() to see if SkRect may be invalid,
|
|
and sort() to reverse fTop and fBottom if needed.
|
|
|
|
@return fBottom
|
|
*/
|
|
constexpr float bottom() const { return fBottom; }
|
|
|
|
/** Returns span on the x-axis. This does not check if SkRect is sorted, or if
|
|
result fits in 32-bit float; result may be negative or infinity.
|
|
|
|
@return fRight minus fLeft
|
|
*/
|
|
constexpr float width() const { return fRight - fLeft; }
|
|
|
|
/** Returns span on the y-axis. This does not check if SkRect is sorted, or if
|
|
result fits in 32-bit float; result may be negative or infinity.
|
|
|
|
@return fBottom minus fTop
|
|
*/
|
|
constexpr float height() const { return fBottom - fTop; }
|
|
|
|
/** Returns average of left edge and right edge. Result does not change if SkRect
|
|
is sorted. Result may overflow to infinity if SkRect is far from the origin.
|
|
|
|
@return midpoint on x-axis
|
|
*/
|
|
constexpr float centerX() const {
|
|
return sk_float_midpoint(fLeft, fRight);
|
|
}
|
|
|
|
/** Returns average of top edge and bottom edge. Result does not change if SkRect
|
|
is sorted.
|
|
|
|
@return midpoint on y-axis
|
|
*/
|
|
constexpr float centerY() const {
|
|
return sk_float_midpoint(fTop, fBottom);
|
|
}
|
|
|
|
/** Returns the point this->centerX(), this->centerY().
|
|
@return rectangle center
|
|
*/
|
|
constexpr SkPoint center() const { return {this->centerX(), this->centerY()}; }
|
|
|
|
/** Returns true if all members in a: fLeft, fTop, fRight, and fBottom; are
|
|
equal to the corresponding members in b.
|
|
|
|
a and b are not equal if either contain NaN. a and b are equal if members
|
|
contain zeroes with different signs.
|
|
|
|
@param a SkRect to compare
|
|
@param b SkRect to compare
|
|
@return true if members are equal
|
|
*/
|
|
friend bool operator==(const SkRect& a, const SkRect& b) {
|
|
return a.fLeft == b.fLeft &&
|
|
a.fTop == b.fTop &&
|
|
a.fRight == b.fRight &&
|
|
a.fBottom == b.fBottom;
|
|
}
|
|
|
|
/** Returns true if any in a: fLeft, fTop, fRight, and fBottom; does not
|
|
equal the corresponding members in b.
|
|
|
|
a and b are not equal if either contain NaN. a and b are equal if members
|
|
contain zeroes with different signs.
|
|
|
|
@param a SkRect to compare
|
|
@param b SkRect to compare
|
|
@return true if members are not equal
|
|
*/
|
|
friend bool operator!=(const SkRect& a, const SkRect& b) {
|
|
return !(a == b);
|
|
}
|
|
|
|
/** Returns four points in quad that enclose SkRect ordered as: top-left, top-right,
|
|
bottom-right, bottom-left.
|
|
|
|
TODO: Consider adding parameter to control whether quad is clockwise or counterclockwise.
|
|
|
|
@param quad storage for corners of SkRect
|
|
|
|
example: https://fiddle.skia.org/c/@Rect_toQuad
|
|
*/
|
|
void toQuad(SkPoint quad[4]) const;
|
|
|
|
/** Sets SkRect to (0, 0, 0, 0).
|
|
|
|
Many other rectangles are empty; if left is equal to or greater than right,
|
|
or if top is equal to or greater than bottom. Setting all members to zero
|
|
is a convenience, but does not designate a special empty rectangle.
|
|
*/
|
|
void setEmpty() { *this = MakeEmpty(); }
|
|
|
|
/** Sets SkRect to src, promoting src members from integer to float.
|
|
Very large values in src may lose precision.
|
|
|
|
@param src integer SkRect
|
|
*/
|
|
void set(const SkIRect& src) {
|
|
fLeft = src.fLeft;
|
|
fTop = src.fTop;
|
|
fRight = src.fRight;
|
|
fBottom = src.fBottom;
|
|
}
|
|
|
|
/** Sets SkRect to (left, top, right, bottom).
|
|
left and right are not sorted; left is not necessarily less than right.
|
|
top and bottom are not sorted; top is not necessarily less than bottom.
|
|
|
|
@param left stored in fLeft
|
|
@param top stored in fTop
|
|
@param right stored in fRight
|
|
@param bottom stored in fBottom
|
|
*/
|
|
void setLTRB(float left, float top, float right, float bottom) {
|
|
fLeft = left;
|
|
fTop = top;
|
|
fRight = right;
|
|
fBottom = bottom;
|
|
}
|
|
|
|
/** Sets to bounds of SkPoint array with count entries. If count is zero or smaller,
|
|
or if SkPoint array contains an infinity or NaN, sets to (0, 0, 0, 0).
|
|
|
|
Result is either empty or sorted: fLeft is less than or equal to fRight, and
|
|
fTop is less than or equal to fBottom.
|
|
|
|
@param pts SkPoint array
|
|
@param count entries in array
|
|
*/
|
|
void setBounds(const SkPoint pts[], int count) {
|
|
(void)this->setBoundsCheck(pts, count);
|
|
}
|
|
|
|
/** Sets to bounds of SkPoint array with count entries. Returns false if count is
|
|
zero or smaller, or if SkPoint array contains an infinity or NaN; in these cases
|
|
sets SkRect to (0, 0, 0, 0).
|
|
|
|
Result is either empty or sorted: fLeft is less than or equal to fRight, and
|
|
fTop is less than or equal to fBottom.
|
|
|
|
@param pts SkPoint array
|
|
@param count entries in array
|
|
@return true if all SkPoint values are finite
|
|
|
|
example: https://fiddle.skia.org/c/@Rect_setBoundsCheck
|
|
*/
|
|
bool setBoundsCheck(const SkPoint pts[], int count);
|
|
|
|
/** Sets to bounds of SkPoint pts array with count entries. If any SkPoint in pts
|
|
contains infinity or NaN, all SkRect dimensions are set to NaN.
|
|
|
|
@param pts SkPoint array
|
|
@param count entries in array
|
|
|
|
example: https://fiddle.skia.org/c/@Rect_setBoundsNoCheck
|
|
*/
|
|
void setBoundsNoCheck(const SkPoint pts[], int count);
|
|
|
|
/** Sets bounds to the smallest SkRect enclosing SkPoint p0 and p1. The result is
|
|
sorted and may be empty. Does not check to see if values are finite.
|
|
|
|
@param p0 corner to include
|
|
@param p1 corner to include
|
|
*/
|
|
void set(const SkPoint& p0, const SkPoint& p1) {
|
|
fLeft = std::min(p0.fX, p1.fX);
|
|
fRight = std::max(p0.fX, p1.fX);
|
|
fTop = std::min(p0.fY, p1.fY);
|
|
fBottom = std::max(p0.fY, p1.fY);
|
|
}
|
|
|
|
/** Sets SkRect to (x, y, x + width, y + height).
|
|
Does not validate input; width or height may be negative.
|
|
|
|
@param x stored in fLeft
|
|
@param y stored in fTop
|
|
@param width added to x and stored in fRight
|
|
@param height added to y and stored in fBottom
|
|
*/
|
|
void setXYWH(float x, float y, float width, float height) {
|
|
fLeft = x;
|
|
fTop = y;
|
|
fRight = x + width;
|
|
fBottom = y + height;
|
|
}
|
|
|
|
/** Sets SkRect to (0, 0, width, height). Does not validate input;
|
|
width or height may be negative.
|
|
|
|
@param width stored in fRight
|
|
@param height stored in fBottom
|
|
*/
|
|
void setWH(float width, float height) {
|
|
fLeft = 0;
|
|
fTop = 0;
|
|
fRight = width;
|
|
fBottom = height;
|
|
}
|
|
void setIWH(int32_t width, int32_t height) {
|
|
this->setWH(width, height);
|
|
}
|
|
|
|
/** Returns SkRect offset by (dx, dy).
|
|
|
|
If dx is negative, SkRect returned is moved to the left.
|
|
If dx is positive, SkRect returned is moved to the right.
|
|
If dy is negative, SkRect returned is moved upward.
|
|
If dy is positive, SkRect returned is moved downward.
|
|
|
|
@param dx added to fLeft and fRight
|
|
@param dy added to fTop and fBottom
|
|
@return SkRect offset on axes, with original width and height
|
|
*/
|
|
constexpr SkRect makeOffset(float dx, float dy) const {
|
|
return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
|
|
}
|
|
|
|
/** Returns SkRect offset by v.
|
|
|
|
@param v added to rect
|
|
@return SkRect offset on axes, with original width and height
|
|
*/
|
|
constexpr SkRect makeOffset(SkVector v) const { return this->makeOffset(v.x(), v.y()); }
|
|
|
|
/** Returns SkRect, inset by (dx, dy).
|
|
|
|
If dx is negative, SkRect returned is wider.
|
|
If dx is positive, SkRect returned is narrower.
|
|
If dy is negative, SkRect returned is taller.
|
|
If dy is positive, SkRect returned is shorter.
|
|
|
|
@param dx added to fLeft and subtracted from fRight
|
|
@param dy added to fTop and subtracted from fBottom
|
|
@return SkRect inset symmetrically left and right, top and bottom
|
|
*/
|
|
SkRect makeInset(float dx, float dy) const {
|
|
return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy);
|
|
}
|
|
|
|
/** Returns SkRect, outset by (dx, dy).
|
|
|
|
If dx is negative, SkRect returned is narrower.
|
|
If dx is positive, SkRect returned is wider.
|
|
If dy is negative, SkRect returned is shorter.
|
|
If dy is positive, SkRect returned is taller.
|
|
|
|
@param dx subtracted to fLeft and added from fRight
|
|
@param dy subtracted to fTop and added from fBottom
|
|
@return SkRect outset symmetrically left and right, top and bottom
|
|
*/
|
|
SkRect makeOutset(float dx, float dy) const {
|
|
return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy);
|
|
}
|
|
|
|
/** Offsets SkRect by adding dx to fLeft, fRight; and by adding dy to fTop, fBottom.
|
|
|
|
If dx is negative, moves SkRect to the left.
|
|
If dx is positive, moves SkRect to the right.
|
|
If dy is negative, moves SkRect upward.
|
|
If dy is positive, moves SkRect downward.
|
|
|
|
@param dx offset added to fLeft and fRight
|
|
@param dy offset added to fTop and fBottom
|
|
*/
|
|
void offset(float dx, float dy) {
|
|
fLeft += dx;
|
|
fTop += dy;
|
|
fRight += dx;
|
|
fBottom += dy;
|
|
}
|
|
|
|
/** Offsets SkRect by adding delta.fX to fLeft, fRight; and by adding delta.fY to
|
|
fTop, fBottom.
|
|
|
|
If delta.fX is negative, moves SkRect to the left.
|
|
If delta.fX is positive, moves SkRect to the right.
|
|
If delta.fY is negative, moves SkRect upward.
|
|
If delta.fY is positive, moves SkRect downward.
|
|
|
|
@param delta added to SkRect
|
|
*/
|
|
void offset(const SkPoint& delta) {
|
|
this->offset(delta.fX, delta.fY);
|
|
}
|
|
|
|
/** Offsets SkRect so that fLeft equals newX, and fTop equals newY. width and height
|
|
are unchanged.
|
|
|
|
@param newX stored in fLeft, preserving width()
|
|
@param newY stored in fTop, preserving height()
|
|
*/
|
|
void offsetTo(float newX, float newY) {
|
|
fRight += newX - fLeft;
|
|
fBottom += newY - fTop;
|
|
fLeft = newX;
|
|
fTop = newY;
|
|
}
|
|
|
|
/** Insets SkRect by (dx, dy).
|
|
|
|
If dx is positive, makes SkRect narrower.
|
|
If dx is negative, makes SkRect wider.
|
|
If dy is positive, makes SkRect shorter.
|
|
If dy is negative, makes SkRect taller.
|
|
|
|
@param dx added to fLeft and subtracted from fRight
|
|
@param dy added to fTop and subtracted from fBottom
|
|
*/
|
|
void inset(float dx, float dy) {
|
|
fLeft += dx;
|
|
fTop += dy;
|
|
fRight -= dx;
|
|
fBottom -= dy;
|
|
}
|
|
|
|
/** Outsets SkRect by (dx, dy).
|
|
|
|
If dx is positive, makes SkRect wider.
|
|
If dx is negative, makes SkRect narrower.
|
|
If dy is positive, makes SkRect taller.
|
|
If dy is negative, makes SkRect shorter.
|
|
|
|
@param dx subtracted to fLeft and added from fRight
|
|
@param dy subtracted to fTop and added from fBottom
|
|
*/
|
|
void outset(float dx, float dy) { this->inset(-dx, -dy); }
|
|
|
|
/** Returns true if SkRect intersects r, and sets SkRect to intersection.
|
|
Returns false if SkRect does not intersect r, and leaves SkRect unchanged.
|
|
|
|
Returns false if either r or SkRect is empty, leaving SkRect unchanged.
|
|
|
|
@param r limit of result
|
|
@return true if r and SkRect have area in common
|
|
|
|
example: https://fiddle.skia.org/c/@Rect_intersect
|
|
*/
|
|
bool intersect(const SkRect& r);
|
|
|
|
/** Returns true if a intersects b, and sets SkRect to intersection.
|
|
Returns false if a does not intersect b, and leaves SkRect unchanged.
|
|
|
|
Returns false if either a or b is empty, leaving SkRect unchanged.
|
|
|
|
@param a SkRect to intersect
|
|
@param b SkRect to intersect
|
|
@return true if a and b have area in common
|
|
*/
|
|
[[nodiscard]] bool intersect(const SkRect& a, const SkRect& b);
|
|
|
|
|
|
private:
|
|
static bool Intersects(float al, float at, float ar, float ab,
|
|
float bl, float bt, float br, float bb) {
|
|
float L = std::max(al, bl);
|
|
float R = std::min(ar, br);
|
|
float T = std::max(at, bt);
|
|
float B = std::min(ab, bb);
|
|
return L < R && T < B;
|
|
}
|
|
|
|
public:
|
|
|
|
/** Returns true if SkRect intersects r.
|
|
Returns false if either r or SkRect is empty, or do not intersect.
|
|
|
|
@param r SkRect to intersect
|
|
@return true if r and SkRect have area in common
|
|
*/
|
|
bool intersects(const SkRect& r) const {
|
|
return Intersects(fLeft, fTop, fRight, fBottom,
|
|
r.fLeft, r.fTop, r.fRight, r.fBottom);
|
|
}
|
|
|
|
/** Returns true if a intersects b.
|
|
Returns false if either a or b is empty, or do not intersect.
|
|
|
|
@param a SkRect to intersect
|
|
@param b SkRect to intersect
|
|
@return true if a and b have area in common
|
|
*/
|
|
static bool Intersects(const SkRect& a, const SkRect& b) {
|
|
return Intersects(a.fLeft, a.fTop, a.fRight, a.fBottom,
|
|
b.fLeft, b.fTop, b.fRight, b.fBottom);
|
|
}
|
|
|
|
/** Sets SkRect to the union of itself and r.
|
|
|
|
Has no effect if r is empty. Otherwise, if SkRect is empty, sets
|
|
SkRect to r.
|
|
|
|
@param r expansion SkRect
|
|
|
|
example: https://fiddle.skia.org/c/@Rect_join_2
|
|
*/
|
|
void join(const SkRect& r);
|
|
|
|
/** Sets SkRect to the union of itself and r.
|
|
|
|
Asserts if r is empty and SK_DEBUG is defined.
|
|
If SkRect is empty, sets SkRect to r.
|
|
|
|
May produce incorrect results if r is empty.
|
|
|
|
@param r expansion SkRect
|
|
*/
|
|
void joinNonEmptyArg(const SkRect& r) {
|
|
SkASSERT(!r.isEmpty());
|
|
// if we are empty, just assign
|
|
if (fLeft >= fRight || fTop >= fBottom) {
|
|
*this = r;
|
|
} else {
|
|
this->joinPossiblyEmptyRect(r);
|
|
}
|
|
}
|
|
|
|
/** Sets SkRect to the union of itself and the construction.
|
|
|
|
May produce incorrect results if SkRect or r is empty.
|
|
|
|
@param r expansion SkRect
|
|
*/
|
|
void joinPossiblyEmptyRect(const SkRect& r) {
|
|
fLeft = std::min(fLeft, r.left());
|
|
fTop = std::min(fTop, r.top());
|
|
fRight = std::max(fRight, r.right());
|
|
fBottom = std::max(fBottom, r.bottom());
|
|
}
|
|
|
|
/** Returns true if: fLeft <= x < fRight && fTop <= y < fBottom.
|
|
Returns false if SkRect is empty.
|
|
|
|
@param x test SkPoint x-coordinate
|
|
@param y test SkPoint y-coordinate
|
|
@return true if (x, y) is inside SkRect
|
|
*/
|
|
bool contains(float x, float y) const {
|
|
return x >= fLeft && x < fRight && y >= fTop && y < fBottom;
|
|
}
|
|
|
|
/** Returns true if SkRect contains r.
|
|
Returns false if SkRect is empty or r is empty.
|
|
|
|
SkRect contains r when SkRect area completely includes r area.
|
|
|
|
@param r SkRect contained
|
|
@return true if all sides of SkRect are outside r
|
|
*/
|
|
bool contains(const SkRect& r) const {
|
|
// todo: can we eliminate the this->isEmpty check?
|
|
return !r.isEmpty() && !this->isEmpty() &&
|
|
fLeft <= r.fLeft && fTop <= r.fTop &&
|
|
fRight >= r.fRight && fBottom >= r.fBottom;
|
|
}
|
|
|
|
/** Returns true if SkRect contains r.
|
|
Returns false if SkRect is empty or r is empty.
|
|
|
|
SkRect contains r when SkRect area completely includes r area.
|
|
|
|
@param r SkIRect contained
|
|
@return true if all sides of SkRect are outside r
|
|
*/
|
|
bool contains(const SkIRect& r) const {
|
|
// todo: can we eliminate the this->isEmpty check?
|
|
return !r.isEmpty() && !this->isEmpty() &&
|
|
fLeft <= r.fLeft && fTop <= r.fTop &&
|
|
fRight >= r.fRight && fBottom >= r.fBottom;
|
|
}
|
|
|
|
/** Sets SkIRect by adding 0.5 and discarding the fractional portion of SkRect
|
|
members, using (sk_float_round2int(fLeft), sk_float_round2int(fTop),
|
|
sk_float_round2int(fRight), sk_float_round2int(fBottom)).
|
|
|
|
@param dst storage for SkIRect
|
|
*/
|
|
void round(SkIRect* dst) const {
|
|
SkASSERT(dst);
|
|
dst->setLTRB(sk_float_round2int(fLeft), sk_float_round2int(fTop),
|
|
sk_float_round2int(fRight), sk_float_round2int(fBottom));
|
|
}
|
|
|
|
/** Sets SkIRect by discarding the fractional portion of fLeft and fTop; and rounding
|
|
up fRight and fBottom, using
|
|
(sk_float_floor2int(fLeft), sk_float_floor2int(fTop),
|
|
sk_float_ceil2int(fRight), sk_float_ceil2int(fBottom)).
|
|
|
|
@param dst storage for SkIRect
|
|
*/
|
|
void roundOut(SkIRect* dst) const {
|
|
SkASSERT(dst);
|
|
dst->setLTRB(sk_float_floor2int(fLeft), sk_float_floor2int(fTop),
|
|
sk_float_ceil2int(fRight), sk_float_ceil2int(fBottom));
|
|
}
|
|
|
|
/** Sets SkRect by discarding the fractional portion of fLeft and fTop; and rounding
|
|
up fRight and fBottom, using
|
|
(sk_float_floor(fLeft), sk_float_floor(fTop),
|
|
sk_float_ceil(fRight), sk_float_ceil(fBottom)).
|
|
|
|
@param dst storage for SkRect
|
|
*/
|
|
void roundOut(SkRect* dst) const {
|
|
dst->setLTRB(sk_float_floor(fLeft), sk_float_floor(fTop),
|
|
sk_float_ceil(fRight), sk_float_ceil(fBottom));
|
|
}
|
|
|
|
/** Sets SkRect by rounding up fLeft and fTop; and discarding the fractional portion
|
|
of fRight and fBottom, using
|
|
(sk_float_ceil2int(fLeft), sk_float_ceil2int(fTop),
|
|
sk_float_floor2int(fRight), sk_float_floor2int(fBottom)).
|
|
|
|
@param dst storage for SkIRect
|
|
*/
|
|
void roundIn(SkIRect* dst) const {
|
|
SkASSERT(dst);
|
|
dst->setLTRB(sk_float_ceil2int(fLeft), sk_float_ceil2int(fTop),
|
|
sk_float_floor2int(fRight), sk_float_floor2int(fBottom));
|
|
}
|
|
|
|
/** Returns SkIRect by adding 0.5 and discarding the fractional portion of SkRect
|
|
members, using (sk_float_round2int(fLeft), sk_float_round2int(fTop),
|
|
sk_float_round2int(fRight), sk_float_round2int(fBottom)).
|
|
|
|
@return rounded SkIRect
|
|
*/
|
|
SkIRect round() const {
|
|
SkIRect ir;
|
|
this->round(&ir);
|
|
return ir;
|
|
}
|
|
|
|
/** Sets SkIRect by discarding the fractional portion of fLeft and fTop; and rounding
|
|
up fRight and fBottom, using
|
|
(sk_float_floor2int(fLeft), sk_float_floor2int(fTop),
|
|
sk_float_ceil2int(fRight), sk_float_ceil2int(fBottom)).
|
|
|
|
@return rounded SkIRect
|
|
*/
|
|
SkIRect roundOut() const {
|
|
SkIRect ir;
|
|
this->roundOut(&ir);
|
|
return ir;
|
|
}
|
|
/** Sets SkIRect by rounding up fLeft and fTop; and discarding the fractional portion
|
|
of fRight and fBottom, using
|
|
(sk_float_ceil2int(fLeft), sk_float_ceil2int(fTop),
|
|
sk_float_floor2int(fRight), sk_float_floor2int(fBottom)).
|
|
|
|
@return rounded SkIRect
|
|
*/
|
|
SkIRect roundIn() const {
|
|
SkIRect ir;
|
|
this->roundIn(&ir);
|
|
return ir;
|
|
}
|
|
|
|
/** Swaps fLeft and fRight if fLeft is greater than fRight; and swaps
|
|
fTop and fBottom if fTop is greater than fBottom. Result may be empty;
|
|
and width() and height() will be zero or positive.
|
|
*/
|
|
void sort() {
|
|
using std::swap;
|
|
if (fLeft > fRight) {
|
|
swap(fLeft, fRight);
|
|
}
|
|
|
|
if (fTop > fBottom) {
|
|
swap(fTop, fBottom);
|
|
}
|
|
}
|
|
|
|
/** Returns SkRect with fLeft and fRight swapped if fLeft is greater than fRight; and
|
|
with fTop and fBottom swapped if fTop is greater than fBottom. Result may be empty;
|
|
and width() and height() will be zero or positive.
|
|
|
|
@return sorted SkRect
|
|
*/
|
|
SkRect makeSorted() const {
|
|
return MakeLTRB(std::min(fLeft, fRight), std::min(fTop, fBottom),
|
|
std::max(fLeft, fRight), std::max(fTop, fBottom));
|
|
}
|
|
|
|
/** Returns pointer to first float in SkRect, to treat it as an array with four
|
|
entries.
|
|
|
|
@return pointer to fLeft
|
|
*/
|
|
const float* asScalars() const { return &fLeft; }
|
|
|
|
/** Writes text representation of SkRect to standard output. Set asHex to true to
|
|
generate exact binary representations of floating point numbers.
|
|
|
|
@param asHex true if SkScalar values are written as hexadecimal
|
|
|
|
example: https://fiddle.skia.org/c/@Rect_dump
|
|
*/
|
|
void dump(bool asHex) const;
|
|
|
|
/** Writes text representation of SkRect to standard output. The representation may be
|
|
directly compiled as C++ code. Floating point values are written
|
|
with limited precision; it may not be possible to reconstruct original SkRect
|
|
from output.
|
|
*/
|
|
void dump() const { this->dump(false); }
|
|
|
|
/** Writes text representation of SkRect to standard output. The representation may be
|
|
directly compiled as C++ code. Floating point values are written
|
|
in hexadecimal to preserve their exact bit pattern. The output reconstructs the
|
|
original SkRect.
|
|
|
|
Use instead of dump() when submitting
|
|
*/
|
|
void dumpHex() const { this->dump(true); }
|
|
};
|
|
|
|
inline bool SkIRect::contains(const SkRect& r) const {
|
|
return !r.isEmpty() && !this->isEmpty() && // check for empties
|
|
fLeft <= r.fLeft && fTop <= r.fTop &&
|
|
fRight >= r.fRight && fBottom >= r.fBottom;
|
|
}
|
|
|
|
/*
|
|
* Usage: SK_MACRO_CONCAT(a, b) to construct the symbol ab
|
|
*
|
|
* SK_MACRO_CONCAT_IMPL_PRIV just exists to make this work. Do not use directly
|
|
*
|
|
*/
|
|
#define SK_MACRO_CONCAT(X, Y) SK_MACRO_CONCAT_IMPL_PRIV(X, Y)
|
|
#define SK_MACRO_CONCAT_IMPL_PRIV(X, Y) X ## Y
|
|
|
|
/*
|
|
* Usage: SK_MACRO_APPEND_LINE(foo) to make foo123, where 123 is the current
|
|
* line number. Easy way to construct
|
|
* unique names for local functions or
|
|
* variables.
|
|
*/
|
|
#define SK_MACRO_APPEND_LINE(name) SK_MACRO_CONCAT(name, __LINE__)
|
|
|
|
#define SK_MACRO_APPEND_COUNTER(name) SK_MACRO_CONCAT(name, __COUNTER__)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Can be used to bracket data types that must be dense/packed, e.g. hash keys.
|
|
#if defined(__clang__) // This should work on GCC too, but GCC diagnostic pop didn't seem to work!
|
|
#define SK_BEGIN_REQUIRE_DENSE _Pragma("GCC diagnostic push") \
|
|
_Pragma("GCC diagnostic error \"-Wpadded\"")
|
|
#define SK_END_REQUIRE_DENSE _Pragma("GCC diagnostic pop")
|
|
#else
|
|
#define SK_BEGIN_REQUIRE_DENSE
|
|
#define SK_END_REQUIRE_DENSE
|
|
#endif
|
|
|
|
#if defined(__clang__) && defined(__has_feature)
|
|
// Some compilers have a preprocessor that does not appear to do short-circuit
|
|
// evaluation as expected
|
|
#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
|
|
// Chrome had issues if we tried to include lsan_interface.h ourselves.
|
|
// https://github.com/llvm/llvm-project/blob/10a35632d55bb05004fe3d0c2d4432bb74897ee7/compiler-rt/include/sanitizer/lsan_interface.h#L26
|
|
extern "C" {
|
|
void __lsan_ignore_object(const void *p);
|
|
}
|
|
#define SK_INTENTIONALLY_LEAKED(X) __lsan_ignore_object(X)
|
|
#else
|
|
#define SK_INTENTIONALLY_LEAKED(X) ((void)0)
|
|
#endif
|
|
#else
|
|
#define SK_INTENTIONALLY_LEAKED(X) ((void)0)
|
|
#endif
|
|
|
|
#define SK_INIT_TO_AVOID_WARNING = 0
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Defines overloaded bitwise operators to make it easier to use an enum as a
|
|
* bitfield.
|
|
*/
|
|
#define SK_MAKE_BITFIELD_OPS(X) \
|
|
inline X operator ~(X a) { \
|
|
using U = std::underlying_type_t<X>; \
|
|
return (X) (~static_cast<U>(a)); \
|
|
} \
|
|
inline X operator |(X a, X b) { \
|
|
using U = std::underlying_type_t<X>; \
|
|
return (X) (static_cast<U>(a) | static_cast<U>(b)); \
|
|
} \
|
|
inline X& operator |=(X& a, X b) { \
|
|
return (a = a | b); \
|
|
} \
|
|
inline X operator &(X a, X b) { \
|
|
using U = std::underlying_type_t<X>; \
|
|
return (X) (static_cast<U>(a) & static_cast<U>(b)); \
|
|
} \
|
|
inline X& operator &=(X& a, X b) { \
|
|
return (a = a & b); \
|
|
}
|
|
|
|
#define SK_DECL_BITFIELD_OPS_FRIENDS(X) \
|
|
friend X operator ~(X a); \
|
|
friend X operator |(X a, X b); \
|
|
friend X& operator |=(X& a, X b); \
|
|
\
|
|
friend X operator &(X a, X b); \
|
|
friend X& operator &=(X& a, X b);
|
|
|
|
template <typename D, typename S> constexpr D SkTo(S s) {
|
|
return SkASSERT(SkTFitsIn<D>(s)),
|
|
static_cast<D>(s);
|
|
}
|
|
|
|
template <typename S> constexpr int8_t SkToS8(S x) { return SkTo<int8_t>(x); }
|
|
template <typename S> constexpr uint8_t SkToU8(S x) { return SkTo<uint8_t>(x); }
|
|
template <typename S> constexpr int16_t SkToS16(S x) { return SkTo<int16_t>(x); }
|
|
template <typename S> constexpr uint16_t SkToU16(S x) { return SkTo<uint16_t>(x); }
|
|
template <typename S> constexpr int32_t SkToS32(S x) { return SkTo<int32_t>(x); }
|
|
template <typename S> constexpr uint32_t SkToU32(S x) { return SkTo<uint32_t>(x); }
|
|
template <typename S> constexpr int64_t SkToS64(S x) { return SkTo<int64_t>(x); }
|
|
template <typename S> constexpr uint64_t SkToU64(S x) { return SkTo<uint64_t>(x); }
|
|
template <typename S> constexpr int SkToInt(S x) { return SkTo<int>(x); }
|
|
template <typename S> constexpr unsigned SkToUInt(S x) { return SkTo<unsigned>(x); }
|
|
template <typename S> constexpr size_t SkToSizeT(S x) { return SkTo<size_t>(x); }
|
|
|
|
/** @return false or true based on the condition
|
|
*/
|
|
template <typename T> static constexpr bool SkToBool(const T& x) {
|
|
return (bool)x;
|
|
}
|
|
|
|
struct SkPoint3;
|
|
struct SkRSXform;
|
|
struct SkSize;
|
|
|
|
// Remove when clients are updated to live without this
|
|
#define SK_SUPPORT_LEGACY_MATRIX_RECTTORECT
|
|
|
|
/**
|
|
* When we transform points through a matrix containing perspective (the bottom row is something
|
|
* other than 0,0,1), the bruteforce math can produce confusing results (since we might divide
|
|
* by 0, or a negative w value). By default, methods that map rects and paths will apply
|
|
* perspective clipping, but this can be changed by specifying kYes to those methods.
|
|
*/
|
|
enum class SkApplyPerspectiveClip {
|
|
kNo, //!< Don't pre-clip the geometry before applying the (perspective) matrix
|
|
kYes, //!< Do pre-clip the geometry before applying the (perspective) matrix
|
|
};
|
|
|
|
/** \class SkMatrix
|
|
SkMatrix holds a 3x3 matrix for transforming coordinates. This allows mapping
|
|
SkPoint and vectors with translation, scaling, skewing, rotation, and
|
|
perspective.
|
|
|
|
SkMatrix elements are in row major order.
|
|
SkMatrix constexpr default constructs to identity.
|
|
|
|
SkMatrix includes a hidden variable that classifies the type of matrix to
|
|
improve performance. SkMatrix is not thread safe unless getType() is called first.
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_063
|
|
*/
|
|
SK_BEGIN_REQUIRE_DENSE
|
|
class SK_API SkMatrix {
|
|
public:
|
|
|
|
/** Creates an identity SkMatrix:
|
|
|
|
| 1 0 0 |
|
|
| 0 1 0 |
|
|
| 0 0 1 |
|
|
*/
|
|
constexpr SkMatrix() : SkMatrix(1,0,0, 0,1,0, 0,0,1, kIdentity_Mask | kRectStaysRect_Mask) {}
|
|
|
|
/** Sets SkMatrix to scale by (sx, sy). Returned matrix is:
|
|
|
|
| sx 0 0 |
|
|
| 0 sy 0 |
|
|
| 0 0 1 |
|
|
|
|
@param sx horizontal scale factor
|
|
@param sy vertical scale factor
|
|
@return SkMatrix with scale
|
|
*/
|
|
[[nodiscard]] static SkMatrix Scale(SkScalar sx, SkScalar sy) {
|
|
SkMatrix m;
|
|
m.setScale(sx, sy);
|
|
return m;
|
|
}
|
|
|
|
/** Sets SkMatrix to translate by (dx, dy). Returned matrix is:
|
|
|
|
| 1 0 dx |
|
|
| 0 1 dy |
|
|
| 0 0 1 |
|
|
|
|
@param dx horizontal translation
|
|
@param dy vertical translation
|
|
@return SkMatrix with translation
|
|
*/
|
|
[[nodiscard]] static SkMatrix Translate(SkScalar dx, SkScalar dy) {
|
|
SkMatrix m;
|
|
m.setTranslate(dx, dy);
|
|
return m;
|
|
}
|
|
[[nodiscard]] static SkMatrix Translate(SkVector t) { return Translate(t.x(), t.y()); }
|
|
[[nodiscard]] static SkMatrix Translate(SkIVector t) { return Translate(t.x(), t.y()); }
|
|
|
|
/** Sets SkMatrix to rotate by |deg| about a pivot point at (0, 0).
|
|
|
|
@param deg rotation angle in degrees (positive rotates clockwise)
|
|
@return SkMatrix with rotation
|
|
*/
|
|
[[nodiscard]] static SkMatrix RotateDeg(SkScalar deg) {
|
|
SkMatrix m;
|
|
m.setRotate(deg);
|
|
return m;
|
|
}
|
|
[[nodiscard]] static SkMatrix RotateDeg(SkScalar deg, SkPoint pt) {
|
|
SkMatrix m;
|
|
m.setRotate(deg, pt.x(), pt.y());
|
|
return m;
|
|
}
|
|
[[nodiscard]] static SkMatrix RotateRad(SkScalar rad) {
|
|
return RotateDeg(SkRadiansToDegrees(rad));
|
|
}
|
|
|
|
/** Sets SkMatrix to skew by (kx, ky) about pivot point (0, 0).
|
|
|
|
@param kx horizontal skew factor
|
|
@param ky vertical skew factor
|
|
@return SkMatrix with skew
|
|
*/
|
|
[[nodiscard]] static SkMatrix Skew(SkScalar kx, SkScalar ky) {
|
|
SkMatrix m;
|
|
m.setSkew(kx, ky);
|
|
return m;
|
|
}
|
|
|
|
/** \enum SkMatrix::ScaleToFit
|
|
ScaleToFit describes how SkMatrix is constructed to map one SkRect to another.
|
|
ScaleToFit may allow SkMatrix to have unequal horizontal and vertical scaling,
|
|
or may restrict SkMatrix to square scaling. If restricted, ScaleToFit specifies
|
|
how SkMatrix maps to the side or center of the destination SkRect.
|
|
*/
|
|
enum ScaleToFit {
|
|
kFill_ScaleToFit, //!< scales in x and y to fill destination SkRect
|
|
kStart_ScaleToFit, //!< scales and aligns to left and top
|
|
kCenter_ScaleToFit, //!< scales and aligns to center
|
|
kEnd_ScaleToFit, //!< scales and aligns to right and bottom
|
|
};
|
|
|
|
/** Returns SkMatrix set to scale and translate src to dst. ScaleToFit selects
|
|
whether mapping completely fills dst or preserves the aspect ratio, and how to
|
|
align src within dst. Returns the identity SkMatrix if src is empty. If dst is
|
|
empty, returns SkMatrix set to:
|
|
|
|
| 0 0 0 |
|
|
| 0 0 0 |
|
|
| 0 0 1 |
|
|
|
|
@param src SkRect to map from
|
|
@param dst SkRect to map to
|
|
@param mode How to handle the mapping
|
|
@return SkMatrix mapping src to dst
|
|
*/
|
|
[[nodiscard]] static SkMatrix RectToRect(const SkRect& src, const SkRect& dst,
|
|
ScaleToFit mode = kFill_ScaleToFit) {
|
|
return MakeRectToRect(src, dst, mode);
|
|
}
|
|
|
|
/** Sets SkMatrix to:
|
|
|
|
| scaleX skewX transX |
|
|
| skewY scaleY transY |
|
|
| pers0 pers1 pers2 |
|
|
|
|
@param scaleX horizontal scale factor
|
|
@param skewX horizontal skew factor
|
|
@param transX horizontal translation
|
|
@param skewY vertical skew factor
|
|
@param scaleY vertical scale factor
|
|
@param transY vertical translation
|
|
@param pers0 input x-axis perspective factor
|
|
@param pers1 input y-axis perspective factor
|
|
@param pers2 perspective scale factor
|
|
@return SkMatrix constructed from parameters
|
|
*/
|
|
[[nodiscard]] static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
|
|
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
|
SkScalar pers0, SkScalar pers1, SkScalar pers2) {
|
|
SkMatrix m;
|
|
m.setAll(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2);
|
|
return m;
|
|
}
|
|
|
|
/** \enum SkMatrix::TypeMask
|
|
Enum of bit fields for mask returned by getType().
|
|
Used to identify the complexity of SkMatrix, to optimize performance.
|
|
*/
|
|
enum TypeMask {
|
|
kIdentity_Mask = 0, //!< identity SkMatrix; all bits clear
|
|
kTranslate_Mask = 0x01, //!< translation SkMatrix
|
|
kScale_Mask = 0x02, //!< scale SkMatrix
|
|
kAffine_Mask = 0x04, //!< skew or rotate SkMatrix
|
|
kPerspective_Mask = 0x08, //!< perspective SkMatrix
|
|
};
|
|
|
|
/** Returns a bit field describing the transformations the matrix may
|
|
perform. The bit field is computed conservatively, so it may include
|
|
false positives. For example, when kPerspective_Mask is set, all
|
|
other bits are set.
|
|
|
|
@return kIdentity_Mask, or combinations of: kTranslate_Mask, kScale_Mask,
|
|
kAffine_Mask, kPerspective_Mask
|
|
*/
|
|
TypeMask getType() const {
|
|
if (fTypeMask & kUnknown_Mask) {
|
|
fTypeMask = this->computeTypeMask();
|
|
}
|
|
// only return the public masks
|
|
return (TypeMask)(fTypeMask & 0xF);
|
|
}
|
|
|
|
/** Returns true if SkMatrix is identity. Identity matrix is:
|
|
|
|
| 1 0 0 |
|
|
| 0 1 0 |
|
|
| 0 0 1 |
|
|
|
|
@return true if SkMatrix has no effect
|
|
*/
|
|
bool isIdentity() const {
|
|
return this->getType() == 0;
|
|
}
|
|
|
|
/** Returns true if SkMatrix at most scales and translates. SkMatrix may be identity,
|
|
contain only scale elements, only translate elements, or both. SkMatrix form is:
|
|
|
|
| scale-x 0 translate-x |
|
|
| 0 scale-y translate-y |
|
|
| 0 0 1 |
|
|
|
|
@return true if SkMatrix is identity; or scales, translates, or both
|
|
*/
|
|
bool isScaleTranslate() const {
|
|
return !(this->getType() & ~(kScale_Mask | kTranslate_Mask));
|
|
}
|
|
|
|
/** Returns true if SkMatrix is identity, or translates. SkMatrix form is:
|
|
|
|
| 1 0 translate-x |
|
|
| 0 1 translate-y |
|
|
| 0 0 1 |
|
|
|
|
@return true if SkMatrix is identity, or translates
|
|
*/
|
|
bool isTranslate() const { return !(this->getType() & ~(kTranslate_Mask)); }
|
|
|
|
/** Returns true SkMatrix maps SkRect to another SkRect. If true, SkMatrix is identity,
|
|
or scales, or rotates a multiple of 90 degrees, or mirrors on axes. In all
|
|
cases, SkMatrix may also have translation. SkMatrix form is either:
|
|
|
|
| scale-x 0 translate-x |
|
|
| 0 scale-y translate-y |
|
|
| 0 0 1 |
|
|
|
|
or
|
|
|
|
| 0 rotate-x translate-x |
|
|
| rotate-y 0 translate-y |
|
|
| 0 0 1 |
|
|
|
|
for non-zero values of scale-x, scale-y, rotate-x, and rotate-y.
|
|
|
|
Also called preservesAxisAlignment(); use the one that provides better inline
|
|
documentation.
|
|
|
|
@return true if SkMatrix maps one SkRect into another
|
|
*/
|
|
bool rectStaysRect() const {
|
|
if (fTypeMask & kUnknown_Mask) {
|
|
fTypeMask = this->computeTypeMask();
|
|
}
|
|
return (fTypeMask & kRectStaysRect_Mask) != 0;
|
|
}
|
|
|
|
/** Returns true SkMatrix maps SkRect to another SkRect. If true, SkMatrix is identity,
|
|
or scales, or rotates a multiple of 90 degrees, or mirrors on axes. In all
|
|
cases, SkMatrix may also have translation. SkMatrix form is either:
|
|
|
|
| scale-x 0 translate-x |
|
|
| 0 scale-y translate-y |
|
|
| 0 0 1 |
|
|
|
|
or
|
|
|
|
| 0 rotate-x translate-x |
|
|
| rotate-y 0 translate-y |
|
|
| 0 0 1 |
|
|
|
|
for non-zero values of scale-x, scale-y, rotate-x, and rotate-y.
|
|
|
|
Also called rectStaysRect(); use the one that provides better inline
|
|
documentation.
|
|
|
|
@return true if SkMatrix maps one SkRect into another
|
|
*/
|
|
bool preservesAxisAlignment() const { return this->rectStaysRect(); }
|
|
|
|
/** Returns true if the matrix contains perspective elements. SkMatrix form is:
|
|
|
|
| -- -- -- |
|
|
| -- -- -- |
|
|
| perspective-x perspective-y perspective-scale |
|
|
|
|
where perspective-x or perspective-y is non-zero, or perspective-scale is
|
|
not one. All other elements may have any value.
|
|
|
|
@return true if SkMatrix is in most general form
|
|
*/
|
|
bool hasPerspective() const {
|
|
return SkToBool(this->getPerspectiveTypeMaskOnly() &
|
|
kPerspective_Mask);
|
|
}
|
|
|
|
/** Returns true if SkMatrix contains only translation, rotation, reflection, and
|
|
uniform scale.
|
|
Returns false if SkMatrix contains different scales, skewing, perspective, or
|
|
degenerate forms that collapse to a line or point.
|
|
|
|
Describes that the SkMatrix makes rendering with and without the matrix are
|
|
visually alike; a transformed circle remains a circle. Mathematically, this is
|
|
referred to as similarity of a Euclidean space, or a similarity transformation.
|
|
|
|
Preserves right angles, keeping the arms of the angle equal lengths.
|
|
|
|
@param tol to be deprecated
|
|
@return true if SkMatrix only rotates, uniformly scales, translates
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_isSimilarity
|
|
*/
|
|
bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const;
|
|
|
|
/** Returns true if SkMatrix contains only translation, rotation, reflection, and
|
|
scale. Scale may differ along rotated axes.
|
|
Returns false if SkMatrix skewing, perspective, or degenerate forms that collapse
|
|
to a line or point.
|
|
|
|
Preserves right angles, but not requiring that the arms of the angle
|
|
retain equal lengths.
|
|
|
|
@param tol to be deprecated
|
|
@return true if SkMatrix only rotates, scales, translates
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_preservesRightAngles
|
|
*/
|
|
bool preservesRightAngles(SkScalar tol = SK_ScalarNearlyZero) const;
|
|
|
|
/** SkMatrix organizes its values in row-major order. These members correspond to
|
|
each value in SkMatrix.
|
|
*/
|
|
static constexpr int kMScaleX = 0; //!< horizontal scale factor
|
|
static constexpr int kMSkewX = 1; //!< horizontal skew factor
|
|
static constexpr int kMTransX = 2; //!< horizontal translation
|
|
static constexpr int kMSkewY = 3; //!< vertical skew factor
|
|
static constexpr int kMScaleY = 4; //!< vertical scale factor
|
|
static constexpr int kMTransY = 5; //!< vertical translation
|
|
static constexpr int kMPersp0 = 6; //!< input x perspective factor
|
|
static constexpr int kMPersp1 = 7; //!< input y perspective factor
|
|
static constexpr int kMPersp2 = 8; //!< perspective bias
|
|
|
|
/** Affine arrays are in column-major order to match the matrix used by
|
|
PDF and XPS.
|
|
*/
|
|
static constexpr int kAScaleX = 0; //!< horizontal scale factor
|
|
static constexpr int kASkewY = 1; //!< vertical skew factor
|
|
static constexpr int kASkewX = 2; //!< horizontal skew factor
|
|
static constexpr int kAScaleY = 3; //!< vertical scale factor
|
|
static constexpr int kATransX = 4; //!< horizontal translation
|
|
static constexpr int kATransY = 5; //!< vertical translation
|
|
|
|
/** Returns one matrix value. Asserts if index is out of range and SK_DEBUG is
|
|
defined.
|
|
|
|
@param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
|
|
kMPersp0, kMPersp1, kMPersp2
|
|
@return value corresponding to index
|
|
*/
|
|
SkScalar operator[](int index) const {
|
|
SkASSERT((unsigned)index < 9);
|
|
return fMat[index];
|
|
}
|
|
|
|
/** Returns one matrix value. Asserts if index is out of range and SK_DEBUG is
|
|
defined.
|
|
|
|
@param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
|
|
kMPersp0, kMPersp1, kMPersp2
|
|
@return value corresponding to index
|
|
*/
|
|
SkScalar get(int index) const {
|
|
SkASSERT((unsigned)index < 9);
|
|
return fMat[index];
|
|
}
|
|
|
|
/** Returns one matrix value from a particular row/column. Asserts if index is out
|
|
of range and SK_DEBUG is defined.
|
|
|
|
@param r matrix row to fetch
|
|
@param c matrix column to fetch
|
|
@return value at the given matrix position
|
|
*/
|
|
SkScalar rc(int r, int c) const {
|
|
SkASSERT(r >= 0 && r <= 2);
|
|
SkASSERT(c >= 0 && c <= 2);
|
|
return fMat[r*3 + c];
|
|
}
|
|
|
|
/** Returns scale factor multiplied by x-axis input, contributing to x-axis output.
|
|
With mapPoints(), scales SkPoint along the x-axis.
|
|
|
|
@return horizontal scale factor
|
|
*/
|
|
SkScalar getScaleX() const { return fMat[kMScaleX]; }
|
|
|
|
/** Returns scale factor multiplied by y-axis input, contributing to y-axis output.
|
|
With mapPoints(), scales SkPoint along the y-axis.
|
|
|
|
@return vertical scale factor
|
|
*/
|
|
SkScalar getScaleY() const { return fMat[kMScaleY]; }
|
|
|
|
/** Returns scale factor multiplied by x-axis input, contributing to y-axis output.
|
|
With mapPoints(), skews SkPoint along the y-axis.
|
|
Skewing both axes can rotate SkPoint.
|
|
|
|
@return vertical skew factor
|
|
*/
|
|
SkScalar getSkewY() const { return fMat[kMSkewY]; }
|
|
|
|
/** Returns scale factor multiplied by y-axis input, contributing to x-axis output.
|
|
With mapPoints(), skews SkPoint along the x-axis.
|
|
Skewing both axes can rotate SkPoint.
|
|
|
|
@return horizontal scale factor
|
|
*/
|
|
SkScalar getSkewX() const { return fMat[kMSkewX]; }
|
|
|
|
/** Returns translation contributing to x-axis output.
|
|
With mapPoints(), moves SkPoint along the x-axis.
|
|
|
|
@return horizontal translation factor
|
|
*/
|
|
SkScalar getTranslateX() const { return fMat[kMTransX]; }
|
|
|
|
/** Returns translation contributing to y-axis output.
|
|
With mapPoints(), moves SkPoint along the y-axis.
|
|
|
|
@return vertical translation factor
|
|
*/
|
|
SkScalar getTranslateY() const { return fMat[kMTransY]; }
|
|
|
|
/** Returns factor scaling input x-axis relative to input y-axis.
|
|
|
|
@return input x-axis perspective factor
|
|
*/
|
|
SkScalar getPerspX() const { return fMat[kMPersp0]; }
|
|
|
|
/** Returns factor scaling input y-axis relative to input x-axis.
|
|
|
|
@return input y-axis perspective factor
|
|
*/
|
|
SkScalar getPerspY() const { return fMat[kMPersp1]; }
|
|
|
|
/** Returns writable SkMatrix value. Asserts if index is out of range and SK_DEBUG is
|
|
defined. Clears internal cache anticipating that caller will change SkMatrix value.
|
|
|
|
Next call to read SkMatrix state may recompute cache; subsequent writes to SkMatrix
|
|
value must be followed by dirtyMatrixTypeCache().
|
|
|
|
@param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
|
|
kMPersp0, kMPersp1, kMPersp2
|
|
@return writable value corresponding to index
|
|
*/
|
|
SkScalar& operator[](int index) {
|
|
SkASSERT((unsigned)index < 9);
|
|
this->setTypeMask(kUnknown_Mask);
|
|
return fMat[index];
|
|
}
|
|
|
|
/** Sets SkMatrix value. Asserts if index is out of range and SK_DEBUG is
|
|
defined. Safer than operator[]; internal cache is always maintained.
|
|
|
|
@param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
|
|
kMPersp0, kMPersp1, kMPersp2
|
|
@param value scalar to store in SkMatrix
|
|
*/
|
|
SkMatrix& set(int index, SkScalar value) {
|
|
SkASSERT((unsigned)index < 9);
|
|
fMat[index] = value;
|
|
this->setTypeMask(kUnknown_Mask);
|
|
return *this;
|
|
}
|
|
|
|
/** Sets horizontal scale factor.
|
|
|
|
@param v horizontal scale factor to store
|
|
*/
|
|
SkMatrix& setScaleX(SkScalar v) { return this->set(kMScaleX, v); }
|
|
|
|
/** Sets vertical scale factor.
|
|
|
|
@param v vertical scale factor to store
|
|
*/
|
|
SkMatrix& setScaleY(SkScalar v) { return this->set(kMScaleY, v); }
|
|
|
|
/** Sets vertical skew factor.
|
|
|
|
@param v vertical skew factor to store
|
|
*/
|
|
SkMatrix& setSkewY(SkScalar v) { return this->set(kMSkewY, v); }
|
|
|
|
/** Sets horizontal skew factor.
|
|
|
|
@param v horizontal skew factor to store
|
|
*/
|
|
SkMatrix& setSkewX(SkScalar v) { return this->set(kMSkewX, v); }
|
|
|
|
/** Sets horizontal translation.
|
|
|
|
@param v horizontal translation to store
|
|
*/
|
|
SkMatrix& setTranslateX(SkScalar v) { return this->set(kMTransX, v); }
|
|
|
|
/** Sets vertical translation.
|
|
|
|
@param v vertical translation to store
|
|
*/
|
|
SkMatrix& setTranslateY(SkScalar v) { return this->set(kMTransY, v); }
|
|
|
|
/** Sets input x-axis perspective factor, which causes mapXY() to vary input x-axis values
|
|
inversely proportional to input y-axis values.
|
|
|
|
@param v perspective factor
|
|
*/
|
|
SkMatrix& setPerspX(SkScalar v) { return this->set(kMPersp0, v); }
|
|
|
|
/** Sets input y-axis perspective factor, which causes mapXY() to vary input y-axis values
|
|
inversely proportional to input x-axis values.
|
|
|
|
@param v perspective factor
|
|
*/
|
|
SkMatrix& setPerspY(SkScalar v) { return this->set(kMPersp1, v); }
|
|
|
|
/** Sets all values from parameters. Sets matrix to:
|
|
|
|
| scaleX skewX transX |
|
|
| skewY scaleY transY |
|
|
| persp0 persp1 persp2 |
|
|
|
|
@param scaleX horizontal scale factor to store
|
|
@param skewX horizontal skew factor to store
|
|
@param transX horizontal translation to store
|
|
@param skewY vertical skew factor to store
|
|
@param scaleY vertical scale factor to store
|
|
@param transY vertical translation to store
|
|
@param persp0 input x-axis values perspective factor to store
|
|
@param persp1 input y-axis values perspective factor to store
|
|
@param persp2 perspective scale factor to store
|
|
*/
|
|
SkMatrix& setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
|
|
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
|
SkScalar persp0, SkScalar persp1, SkScalar persp2) {
|
|
fMat[kMScaleX] = scaleX;
|
|
fMat[kMSkewX] = skewX;
|
|
fMat[kMTransX] = transX;
|
|
fMat[kMSkewY] = skewY;
|
|
fMat[kMScaleY] = scaleY;
|
|
fMat[kMTransY] = transY;
|
|
fMat[kMPersp0] = persp0;
|
|
fMat[kMPersp1] = persp1;
|
|
fMat[kMPersp2] = persp2;
|
|
this->setTypeMask(kUnknown_Mask);
|
|
return *this;
|
|
}
|
|
|
|
/** Copies nine scalar values contained by SkMatrix into buffer, in member value
|
|
ascending order: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
|
|
kMPersp0, kMPersp1, kMPersp2.
|
|
|
|
@param buffer storage for nine scalar values
|
|
*/
|
|
void get9(SkScalar buffer[9]) const {
|
|
memcpy(buffer, fMat, 9 * sizeof(SkScalar));
|
|
}
|
|
|
|
/** Sets SkMatrix to nine scalar values in buffer, in member value ascending order:
|
|
kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1,
|
|
kMPersp2.
|
|
|
|
Sets matrix to:
|
|
|
|
| buffer[0] buffer[1] buffer[2] |
|
|
| buffer[3] buffer[4] buffer[5] |
|
|
| buffer[6] buffer[7] buffer[8] |
|
|
|
|
In the future, set9 followed by get9 may not return the same values. Since SkMatrix
|
|
maps non-homogeneous coordinates, scaling all nine values produces an equivalent
|
|
transformation, possibly improving precision.
|
|
|
|
@param buffer nine scalar values
|
|
*/
|
|
SkMatrix& set9(const SkScalar buffer[9]);
|
|
|
|
/** Sets SkMatrix to identity; which has no effect on mapped SkPoint. Sets SkMatrix to:
|
|
|
|
| 1 0 0 |
|
|
| 0 1 0 |
|
|
| 0 0 1 |
|
|
|
|
Also called setIdentity(); use the one that provides better inline
|
|
documentation.
|
|
*/
|
|
SkMatrix& reset();
|
|
|
|
/** Sets SkMatrix to identity; which has no effect on mapped SkPoint. Sets SkMatrix to:
|
|
|
|
| 1 0 0 |
|
|
| 0 1 0 |
|
|
| 0 0 1 |
|
|
|
|
Also called reset(); use the one that provides better inline
|
|
documentation.
|
|
*/
|
|
SkMatrix& setIdentity() { return this->reset(); }
|
|
|
|
/** Sets SkMatrix to translate by (dx, dy).
|
|
|
|
@param dx horizontal translation
|
|
@param dy vertical translation
|
|
*/
|
|
SkMatrix& setTranslate(SkScalar dx, SkScalar dy);
|
|
|
|
/** Sets SkMatrix to translate by (v.fX, v.fY).
|
|
|
|
@param v vector containing horizontal and vertical translation
|
|
*/
|
|
SkMatrix& setTranslate(const SkVector& v) { return this->setTranslate(v.fX, v.fY); }
|
|
|
|
/** Sets SkMatrix to scale by sx and sy, about a pivot point at (px, py).
|
|
The pivot point is unchanged when mapped with SkMatrix.
|
|
|
|
@param sx horizontal scale factor
|
|
@param sy vertical scale factor
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to scale by sx and sy about at pivot point at (0, 0).
|
|
|
|
@param sx horizontal scale factor
|
|
@param sy vertical scale factor
|
|
*/
|
|
SkMatrix& setScale(SkScalar sx, SkScalar sy);
|
|
|
|
/** Sets SkMatrix to rotate by degrees about a pivot point at (px, py).
|
|
The pivot point is unchanged when mapped with SkMatrix.
|
|
|
|
Positive degrees rotates clockwise.
|
|
|
|
@param degrees angle of axes relative to upright axes
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& setRotate(SkScalar degrees, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to rotate by degrees about a pivot point at (0, 0).
|
|
Positive degrees rotates clockwise.
|
|
|
|
@param degrees angle of axes relative to upright axes
|
|
*/
|
|
SkMatrix& setRotate(SkScalar degrees);
|
|
|
|
/** Sets SkMatrix to rotate by sinValue and cosValue, about a pivot point at (px, py).
|
|
The pivot point is unchanged when mapped with SkMatrix.
|
|
|
|
Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1).
|
|
Vector length specifies scale.
|
|
|
|
@param sinValue rotation vector x-axis component
|
|
@param cosValue rotation vector y-axis component
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& setSinCos(SkScalar sinValue, SkScalar cosValue,
|
|
SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to rotate by sinValue and cosValue, about a pivot point at (0, 0).
|
|
|
|
Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1).
|
|
Vector length specifies scale.
|
|
|
|
@param sinValue rotation vector x-axis component
|
|
@param cosValue rotation vector y-axis component
|
|
*/
|
|
SkMatrix& setSinCos(SkScalar sinValue, SkScalar cosValue);
|
|
|
|
/** Sets SkMatrix to rotate, scale, and translate using a compressed matrix form.
|
|
|
|
Vector (rsxForm.fSSin, rsxForm.fSCos) describes the angle of rotation relative
|
|
to (0, 1). Vector length specifies scale. Mapped point is rotated and scaled
|
|
by vector, then translated by (rsxForm.fTx, rsxForm.fTy).
|
|
|
|
@param rsxForm compressed SkRSXform matrix
|
|
@return reference to SkMatrix
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_setRSXform
|
|
*/
|
|
SkMatrix& setRSXform(const SkRSXform& rsxForm);
|
|
|
|
/** Sets SkMatrix to skew by kx and ky, about a pivot point at (px, py).
|
|
The pivot point is unchanged when mapped with SkMatrix.
|
|
|
|
@param kx horizontal skew factor
|
|
@param ky vertical skew factor
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to skew by kx and ky, about a pivot point at (0, 0).
|
|
|
|
@param kx horizontal skew factor
|
|
@param ky vertical skew factor
|
|
*/
|
|
SkMatrix& setSkew(SkScalar kx, SkScalar ky);
|
|
|
|
/** Sets SkMatrix to SkMatrix a multiplied by SkMatrix b. Either a or b may be this.
|
|
|
|
Given:
|
|
|
|
| A B C | | J K L |
|
|
a = | D E F |, b = | M N O |
|
|
| G H I | | P Q R |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR |
|
|
a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
|
|
| G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR |
|
|
|
|
@param a SkMatrix on left side of multiply expression
|
|
@param b SkMatrix on right side of multiply expression
|
|
*/
|
|
SkMatrix& setConcat(const SkMatrix& a, const SkMatrix& b);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from translation (dx, dy).
|
|
This can be thought of as moving the point to be mapped before applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| A B C | | 1 0 dx |
|
|
Matrix = | D E F |, T(dx, dy) = | 0 1 dy |
|
|
| G H I | | 0 0 1 |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | 1 0 dx | | A B A*dx+B*dy+C |
|
|
Matrix * T(dx, dy) = | D E F | | 0 1 dy | = | D E D*dx+E*dy+F |
|
|
| G H I | | 0 0 1 | | G H G*dx+H*dy+I |
|
|
|
|
@param dx x-axis translation before applying SkMatrix
|
|
@param dy y-axis translation before applying SkMatrix
|
|
*/
|
|
SkMatrix& preTranslate(SkScalar dx, SkScalar dy);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from scaling by (sx, sy)
|
|
about pivot point (px, py).
|
|
This can be thought of as scaling about a pivot point before applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| A B C | | sx 0 dx |
|
|
Matrix = | D E F |, S(sx, sy, px, py) = | 0 sy dy |
|
|
| G H I | | 0 0 1 |
|
|
|
|
where
|
|
|
|
dx = px - sx * px
|
|
dy = py - sy * py
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | sx 0 dx | | A*sx B*sy A*dx+B*dy+C |
|
|
Matrix * S(sx, sy, px, py) = | D E F | | 0 sy dy | = | D*sx E*sy D*dx+E*dy+F |
|
|
| G H I | | 0 0 1 | | G*sx H*sy G*dx+H*dy+I |
|
|
|
|
@param sx horizontal scale factor
|
|
@param sy vertical scale factor
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from scaling by (sx, sy)
|
|
about pivot point (0, 0).
|
|
This can be thought of as scaling about the origin before applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| A B C | | sx 0 0 |
|
|
Matrix = | D E F |, S(sx, sy) = | 0 sy 0 |
|
|
| G H I | | 0 0 1 |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | sx 0 0 | | A*sx B*sy C |
|
|
Matrix * S(sx, sy) = | D E F | | 0 sy 0 | = | D*sx E*sy F |
|
|
| G H I | | 0 0 1 | | G*sx H*sy I |
|
|
|
|
@param sx horizontal scale factor
|
|
@param sy vertical scale factor
|
|
*/
|
|
SkMatrix& preScale(SkScalar sx, SkScalar sy);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from rotating by degrees
|
|
about pivot point (px, py).
|
|
This can be thought of as rotating about a pivot point before applying SkMatrix.
|
|
|
|
Positive degrees rotates clockwise.
|
|
|
|
Given:
|
|
|
|
| A B C | | c -s dx |
|
|
Matrix = | D E F |, R(degrees, px, py) = | s c dy |
|
|
| G H I | | 0 0 1 |
|
|
|
|
where
|
|
|
|
c = cos(degrees)
|
|
s = sin(degrees)
|
|
dx = s * py + (1 - c) * px
|
|
dy = -s * px + (1 - c) * py
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | c -s dx | | Ac+Bs -As+Bc A*dx+B*dy+C |
|
|
Matrix * R(degrees, px, py) = | D E F | | s c dy | = | Dc+Es -Ds+Ec D*dx+E*dy+F |
|
|
| G H I | | 0 0 1 | | Gc+Hs -Gs+Hc G*dx+H*dy+I |
|
|
|
|
@param degrees angle of axes relative to upright axes
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& preRotate(SkScalar degrees, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from rotating by degrees
|
|
about pivot point (0, 0).
|
|
This can be thought of as rotating about the origin before applying SkMatrix.
|
|
|
|
Positive degrees rotates clockwise.
|
|
|
|
Given:
|
|
|
|
| A B C | | c -s 0 |
|
|
Matrix = | D E F |, R(degrees, px, py) = | s c 0 |
|
|
| G H I | | 0 0 1 |
|
|
|
|
where
|
|
|
|
c = cos(degrees)
|
|
s = sin(degrees)
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | c -s 0 | | Ac+Bs -As+Bc C |
|
|
Matrix * R(degrees, px, py) = | D E F | | s c 0 | = | Dc+Es -Ds+Ec F |
|
|
| G H I | | 0 0 1 | | Gc+Hs -Gs+Hc I |
|
|
|
|
@param degrees angle of axes relative to upright axes
|
|
*/
|
|
SkMatrix& preRotate(SkScalar degrees);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from skewing by (kx, ky)
|
|
about pivot point (px, py).
|
|
This can be thought of as skewing about a pivot point before applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| A B C | | 1 kx dx |
|
|
Matrix = | D E F |, K(kx, ky, px, py) = | ky 1 dy |
|
|
| G H I | | 0 0 1 |
|
|
|
|
where
|
|
|
|
dx = -kx * py
|
|
dy = -ky * px
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | 1 kx dx | | A+B*ky A*kx+B A*dx+B*dy+C |
|
|
Matrix * K(kx, ky, px, py) = | D E F | | ky 1 dy | = | D+E*ky D*kx+E D*dx+E*dy+F |
|
|
| G H I | | 0 0 1 | | G+H*ky G*kx+H G*dx+H*dy+I |
|
|
|
|
@param kx horizontal skew factor
|
|
@param ky vertical skew factor
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from skewing by (kx, ky)
|
|
about pivot point (0, 0).
|
|
This can be thought of as skewing about the origin before applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| A B C | | 1 kx 0 |
|
|
Matrix = | D E F |, K(kx, ky) = | ky 1 0 |
|
|
| G H I | | 0 0 1 |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | 1 kx 0 | | A+B*ky A*kx+B C |
|
|
Matrix * K(kx, ky) = | D E F | | ky 1 0 | = | D+E*ky D*kx+E F |
|
|
| G H I | | 0 0 1 | | G+H*ky G*kx+H I |
|
|
|
|
@param kx horizontal skew factor
|
|
@param ky vertical skew factor
|
|
*/
|
|
SkMatrix& preSkew(SkScalar kx, SkScalar ky);
|
|
|
|
/** Sets SkMatrix to SkMatrix multiplied by SkMatrix other.
|
|
This can be thought of mapping by other before applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| A B C | | J K L |
|
|
Matrix = | D E F |, other = | M N O |
|
|
| G H I | | P Q R |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR |
|
|
Matrix * other = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
|
|
| G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR |
|
|
|
|
@param other SkMatrix on right side of multiply expression
|
|
*/
|
|
SkMatrix& preConcat(const SkMatrix& other);
|
|
|
|
/** Sets SkMatrix to SkMatrix constructed from translation (dx, dy) multiplied by SkMatrix.
|
|
This can be thought of as moving the point to be mapped after applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| J K L | | 1 0 dx |
|
|
Matrix = | M N O |, T(dx, dy) = | 0 1 dy |
|
|
| P Q R | | 0 0 1 |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| 1 0 dx | | J K L | | J+dx*P K+dx*Q L+dx*R |
|
|
T(dx, dy) * Matrix = | 0 1 dy | | M N O | = | M+dy*P N+dy*Q O+dy*R |
|
|
| 0 0 1 | | P Q R | | P Q R |
|
|
|
|
@param dx x-axis translation after applying SkMatrix
|
|
@param dy y-axis translation after applying SkMatrix
|
|
*/
|
|
SkMatrix& postTranslate(SkScalar dx, SkScalar dy);
|
|
|
|
/** Sets SkMatrix to SkMatrix constructed from scaling by (sx, sy) about pivot point
|
|
(px, py), multiplied by SkMatrix.
|
|
This can be thought of as scaling about a pivot point after applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| J K L | | sx 0 dx |
|
|
Matrix = | M N O |, S(sx, sy, px, py) = | 0 sy dy |
|
|
| P Q R | | 0 0 1 |
|
|
|
|
where
|
|
|
|
dx = px - sx * px
|
|
dy = py - sy * py
|
|
|
|
sets SkMatrix to:
|
|
|
|
| sx 0 dx | | J K L | | sx*J+dx*P sx*K+dx*Q sx*L+dx+R |
|
|
S(sx, sy, px, py) * Matrix = | 0 sy dy | | M N O | = | sy*M+dy*P sy*N+dy*Q sy*O+dy*R |
|
|
| 0 0 1 | | P Q R | | P Q R |
|
|
|
|
@param sx horizontal scale factor
|
|
@param sy vertical scale factor
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to SkMatrix constructed from scaling by (sx, sy) about pivot point
|
|
(0, 0), multiplied by SkMatrix.
|
|
This can be thought of as scaling about the origin after applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| J K L | | sx 0 0 |
|
|
Matrix = | M N O |, S(sx, sy) = | 0 sy 0 |
|
|
| P Q R | | 0 0 1 |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| sx 0 0 | | J K L | | sx*J sx*K sx*L |
|
|
S(sx, sy) * Matrix = | 0 sy 0 | | M N O | = | sy*M sy*N sy*O |
|
|
| 0 0 1 | | P Q R | | P Q R |
|
|
|
|
@param sx horizontal scale factor
|
|
@param sy vertical scale factor
|
|
*/
|
|
SkMatrix& postScale(SkScalar sx, SkScalar sy);
|
|
|
|
/** Sets SkMatrix to SkMatrix constructed from rotating by degrees about pivot point
|
|
(px, py), multiplied by SkMatrix.
|
|
This can be thought of as rotating about a pivot point after applying SkMatrix.
|
|
|
|
Positive degrees rotates clockwise.
|
|
|
|
Given:
|
|
|
|
| J K L | | c -s dx |
|
|
Matrix = | M N O |, R(degrees, px, py) = | s c dy |
|
|
| P Q R | | 0 0 1 |
|
|
|
|
where
|
|
|
|
c = cos(degrees)
|
|
s = sin(degrees)
|
|
dx = s * py + (1 - c) * px
|
|
dy = -s * px + (1 - c) * py
|
|
|
|
sets SkMatrix to:
|
|
|
|
|c -s dx| |J K L| |cJ-sM+dx*P cK-sN+dx*Q cL-sO+dx+R|
|
|
R(degrees, px, py) * Matrix = |s c dy| |M N O| = |sJ+cM+dy*P sK+cN+dy*Q sL+cO+dy*R|
|
|
|0 0 1| |P Q R| | P Q R|
|
|
|
|
@param degrees angle of axes relative to upright axes
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& postRotate(SkScalar degrees, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to SkMatrix constructed from rotating by degrees about pivot point
|
|
(0, 0), multiplied by SkMatrix.
|
|
This can be thought of as rotating about the origin after applying SkMatrix.
|
|
|
|
Positive degrees rotates clockwise.
|
|
|
|
Given:
|
|
|
|
| J K L | | c -s 0 |
|
|
Matrix = | M N O |, R(degrees, px, py) = | s c 0 |
|
|
| P Q R | | 0 0 1 |
|
|
|
|
where
|
|
|
|
c = cos(degrees)
|
|
s = sin(degrees)
|
|
|
|
sets SkMatrix to:
|
|
|
|
| c -s dx | | J K L | | cJ-sM cK-sN cL-sO |
|
|
R(degrees, px, py) * Matrix = | s c dy | | M N O | = | sJ+cM sK+cN sL+cO |
|
|
| 0 0 1 | | P Q R | | P Q R |
|
|
|
|
@param degrees angle of axes relative to upright axes
|
|
*/
|
|
SkMatrix& postRotate(SkScalar degrees);
|
|
|
|
/** Sets SkMatrix to SkMatrix constructed from skewing by (kx, ky) about pivot point
|
|
(px, py), multiplied by SkMatrix.
|
|
This can be thought of as skewing about a pivot point after applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| J K L | | 1 kx dx |
|
|
Matrix = | M N O |, K(kx, ky, px, py) = | ky 1 dy |
|
|
| P Q R | | 0 0 1 |
|
|
|
|
where
|
|
|
|
dx = -kx * py
|
|
dy = -ky * px
|
|
|
|
sets SkMatrix to:
|
|
|
|
| 1 kx dx| |J K L| |J+kx*M+dx*P K+kx*N+dx*Q L+kx*O+dx+R|
|
|
K(kx, ky, px, py) * Matrix = |ky 1 dy| |M N O| = |ky*J+M+dy*P ky*K+N+dy*Q ky*L+O+dy*R|
|
|
| 0 0 1| |P Q R| | P Q R|
|
|
|
|
@param kx horizontal skew factor
|
|
@param ky vertical skew factor
|
|
@param px pivot on x-axis
|
|
@param py pivot on y-axis
|
|
*/
|
|
SkMatrix& postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
|
|
|
|
/** Sets SkMatrix to SkMatrix constructed from skewing by (kx, ky) about pivot point
|
|
(0, 0), multiplied by SkMatrix.
|
|
This can be thought of as skewing about the origin after applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| J K L | | 1 kx 0 |
|
|
Matrix = | M N O |, K(kx, ky) = | ky 1 0 |
|
|
| P Q R | | 0 0 1 |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| 1 kx 0 | | J K L | | J+kx*M K+kx*N L+kx*O |
|
|
K(kx, ky) * Matrix = | ky 1 0 | | M N O | = | ky*J+M ky*K+N ky*L+O |
|
|
| 0 0 1 | | P Q R | | P Q R |
|
|
|
|
@param kx horizontal skew factor
|
|
@param ky vertical skew factor
|
|
*/
|
|
SkMatrix& postSkew(SkScalar kx, SkScalar ky);
|
|
|
|
/** Sets SkMatrix to SkMatrix other multiplied by SkMatrix.
|
|
This can be thought of mapping by other after applying SkMatrix.
|
|
|
|
Given:
|
|
|
|
| J K L | | A B C |
|
|
Matrix = | M N O |, other = | D E F |
|
|
| P Q R | | G H I |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR |
|
|
other * Matrix = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
|
|
| G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR |
|
|
|
|
@param other SkMatrix on left side of multiply expression
|
|
*/
|
|
SkMatrix& postConcat(const SkMatrix& other);
|
|
|
|
#ifndef SK_SUPPORT_LEGACY_MATRIX_RECTTORECT
|
|
private:
|
|
#endif
|
|
/** Sets SkMatrix to scale and translate src SkRect to dst SkRect. stf selects whether
|
|
mapping completely fills dst or preserves the aspect ratio, and how to align
|
|
src within dst. Returns false if src is empty, and sets SkMatrix to identity.
|
|
Returns true if dst is empty, and sets SkMatrix to:
|
|
|
|
| 0 0 0 |
|
|
| 0 0 0 |
|
|
| 0 0 1 |
|
|
|
|
@param src SkRect to map from
|
|
@param dst SkRect to map to
|
|
@return true if SkMatrix can represent SkRect mapping
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_setRectToRect
|
|
*/
|
|
bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf);
|
|
|
|
/** Returns SkMatrix set to scale and translate src SkRect to dst SkRect. stf selects
|
|
whether mapping completely fills dst or preserves the aspect ratio, and how to
|
|
align src within dst. Returns the identity SkMatrix if src is empty. If dst is
|
|
empty, returns SkMatrix set to:
|
|
|
|
| 0 0 0 |
|
|
| 0 0 0 |
|
|
| 0 0 1 |
|
|
|
|
@param src SkRect to map from
|
|
@param dst SkRect to map to
|
|
@return SkMatrix mapping src to dst
|
|
*/
|
|
static SkMatrix MakeRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf) {
|
|
SkMatrix m;
|
|
m.setRectToRect(src, dst, stf);
|
|
return m;
|
|
}
|
|
#ifndef SK_SUPPORT_LEGACY_MATRIX_RECTTORECT
|
|
public:
|
|
#endif
|
|
|
|
/** Sets SkMatrix to map src to dst. count must be zero or greater, and four or less.
|
|
|
|
If count is zero, sets SkMatrix to identity and returns true.
|
|
If count is one, sets SkMatrix to translate and returns true.
|
|
If count is two or more, sets SkMatrix to map SkPoint if possible; returns false
|
|
if SkMatrix cannot be constructed. If count is four, SkMatrix may include
|
|
perspective.
|
|
|
|
@param src SkPoint to map from
|
|
@param dst SkPoint to map to
|
|
@param count number of SkPoint in src and dst
|
|
@return true if SkMatrix was constructed successfully
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_setPolyToPoly
|
|
*/
|
|
bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count);
|
|
|
|
/** Sets inverse to reciprocal matrix, returning true if SkMatrix can be inverted.
|
|
Geometrically, if SkMatrix maps from source to destination, inverse SkMatrix
|
|
maps from destination to source. If SkMatrix can not be inverted, inverse is
|
|
unchanged.
|
|
|
|
@param inverse storage for inverted SkMatrix; may be nullptr
|
|
@return true if SkMatrix can be inverted
|
|
*/
|
|
[[nodiscard]] bool invert(SkMatrix* inverse) const {
|
|
// Allow the trivial case to be inlined.
|
|
if (this->isIdentity()) {
|
|
if (inverse) {
|
|
inverse->reset();
|
|
}
|
|
return true;
|
|
}
|
|
return this->invertNonIdentity(inverse);
|
|
}
|
|
|
|
/** Fills affine with identity values in column major order.
|
|
Sets affine to:
|
|
|
|
| 1 0 0 |
|
|
| 0 1 0 |
|
|
|
|
Affine 3 by 2 matrices in column major order are used by OpenGL and XPS.
|
|
|
|
@param affine storage for 3 by 2 affine matrix
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_SetAffineIdentity
|
|
*/
|
|
static void SetAffineIdentity(SkScalar affine[6]);
|
|
|
|
/** Fills affine in column major order. Sets affine to:
|
|
|
|
| scale-x skew-x translate-x |
|
|
| skew-y scale-y translate-y |
|
|
|
|
If SkMatrix contains perspective, returns false and leaves affine unchanged.
|
|
|
|
@param affine storage for 3 by 2 affine matrix; may be nullptr
|
|
@return true if SkMatrix does not contain perspective
|
|
*/
|
|
[[nodiscard]] bool asAffine(SkScalar affine[6]) const;
|
|
|
|
/** Sets SkMatrix to affine values, passed in column major order. Given affine,
|
|
column, then row, as:
|
|
|
|
| scale-x skew-x translate-x |
|
|
| skew-y scale-y translate-y |
|
|
|
|
SkMatrix is set, row, then column, to:
|
|
|
|
| scale-x skew-x translate-x |
|
|
| skew-y scale-y translate-y |
|
|
| 0 0 1 |
|
|
|
|
@param affine 3 by 2 affine matrix
|
|
*/
|
|
SkMatrix& setAffine(const SkScalar affine[6]);
|
|
|
|
/**
|
|
* A matrix is categorized as 'perspective' if the bottom row is not [0, 0, 1].
|
|
* However, for most uses (e.g. mapPoints) a bottom row of [0, 0, X] behaves like a
|
|
* non-perspective matrix, though it will be categorized as perspective. Calling
|
|
* normalizePerspective() will change the matrix such that, if its bottom row was [0, 0, X],
|
|
* it will be changed to [0, 0, 1] by scaling the rest of the matrix by 1/X.
|
|
*
|
|
* | A B C | | A/X B/X C/X |
|
|
* | D E F | -> | D/X E/X F/X | for X != 0
|
|
* | 0 0 X | | 0 0 1 |
|
|
*/
|
|
void normalizePerspective() {
|
|
if (fMat[8] != 1) {
|
|
this->doNormalizePerspective();
|
|
}
|
|
}
|
|
|
|
/** Maps src SkPoint array of length count to dst SkPoint array of equal or greater
|
|
length. SkPoint are mapped by multiplying each SkPoint by SkMatrix. Given:
|
|
|
|
| A B C | | x |
|
|
Matrix = | D E F |, pt = | y |
|
|
| G H I | | 1 |
|
|
|
|
where
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
x = src[i].fX
|
|
y = src[i].fY
|
|
}
|
|
|
|
each dst SkPoint is computed as:
|
|
|
|
|A B C| |x| Ax+By+C Dx+Ey+F
|
|
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
src and dst may point to the same storage.
|
|
|
|
@param dst storage for mapped SkPoint
|
|
@param src SkPoint to transform
|
|
@param count number of SkPoint to transform
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_mapPoints
|
|
*/
|
|
void mapPoints(SkPoint dst[], const SkPoint src[], int count) const;
|
|
|
|
/** Maps pts SkPoint array of length count in place. SkPoint are mapped by multiplying
|
|
each SkPoint by SkMatrix. Given:
|
|
|
|
| A B C | | x |
|
|
Matrix = | D E F |, pt = | y |
|
|
| G H I | | 1 |
|
|
|
|
where
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
x = pts[i].fX
|
|
y = pts[i].fY
|
|
}
|
|
|
|
each resulting pts SkPoint is computed as:
|
|
|
|
|A B C| |x| Ax+By+C Dx+Ey+F
|
|
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
@param pts storage for mapped SkPoint
|
|
@param count number of SkPoint to transform
|
|
*/
|
|
void mapPoints(SkPoint pts[], int count) const {
|
|
this->mapPoints(pts, pts, count);
|
|
}
|
|
|
|
/** Maps src SkPoint3 array of length count to dst SkPoint3 array, which must of length count or
|
|
greater. SkPoint3 array is mapped by multiplying each SkPoint3 by SkMatrix. Given:
|
|
|
|
| A B C | | x |
|
|
Matrix = | D E F |, src = | y |
|
|
| G H I | | z |
|
|
|
|
each resulting dst SkPoint is computed as:
|
|
|
|
|A B C| |x|
|
|
Matrix * src = |D E F| |y| = |Ax+By+Cz Dx+Ey+Fz Gx+Hy+Iz|
|
|
|G H I| |z|
|
|
|
|
@param dst storage for mapped SkPoint3 array
|
|
@param src SkPoint3 array to transform
|
|
@param count items in SkPoint3 array to transform
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_mapHomogeneousPoints
|
|
*/
|
|
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const;
|
|
|
|
/**
|
|
* Returns homogeneous points, starting with 2D src points (with implied w = 1).
|
|
*/
|
|
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint src[], int count) const;
|
|
|
|
/** Returns SkPoint pt multiplied by SkMatrix. Given:
|
|
|
|
| A B C | | x |
|
|
Matrix = | D E F |, pt = | y |
|
|
| G H I | | 1 |
|
|
|
|
result is computed as:
|
|
|
|
|A B C| |x| Ax+By+C Dx+Ey+F
|
|
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
@param p SkPoint to map
|
|
@return mapped SkPoint
|
|
*/
|
|
SkPoint mapPoint(SkPoint pt) const {
|
|
SkPoint result;
|
|
this->mapXY(pt.x(), pt.y(), &result);
|
|
return result;
|
|
}
|
|
|
|
/** Maps SkPoint (x, y) to result. SkPoint is mapped by multiplying by SkMatrix. Given:
|
|
|
|
| A B C | | x |
|
|
Matrix = | D E F |, pt = | y |
|
|
| G H I | | 1 |
|
|
|
|
result is computed as:
|
|
|
|
|A B C| |x| Ax+By+C Dx+Ey+F
|
|
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
@param x x-axis value of SkPoint to map
|
|
@param y y-axis value of SkPoint to map
|
|
@param result storage for mapped SkPoint
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_mapXY
|
|
*/
|
|
void mapXY(SkScalar x, SkScalar y, SkPoint* result) const;
|
|
|
|
/** Returns SkPoint (x, y) multiplied by SkMatrix. Given:
|
|
|
|
| A B C | | x |
|
|
Matrix = | D E F |, pt = | y |
|
|
| G H I | | 1 |
|
|
|
|
result is computed as:
|
|
|
|
|A B C| |x| Ax+By+C Dx+Ey+F
|
|
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
@param x x-axis value of SkPoint to map
|
|
@param y y-axis value of SkPoint to map
|
|
@return mapped SkPoint
|
|
*/
|
|
SkPoint mapXY(SkScalar x, SkScalar y) const {
|
|
SkPoint result;
|
|
this->mapXY(x,y, &result);
|
|
return result;
|
|
}
|
|
|
|
|
|
/** Returns (0, 0) multiplied by SkMatrix. Given:
|
|
|
|
| A B C | | 0 |
|
|
Matrix = | D E F |, pt = | 0 |
|
|
| G H I | | 1 |
|
|
|
|
result is computed as:
|
|
|
|
|A B C| |0| C F
|
|
Matrix * pt = |D E F| |0| = |C F I| = - , -
|
|
|G H I| |1| I I
|
|
|
|
@return mapped (0, 0)
|
|
*/
|
|
SkPoint mapOrigin() const {
|
|
SkScalar x = this->getTranslateX(),
|
|
y = this->getTranslateY();
|
|
if (this->hasPerspective()) {
|
|
SkScalar w = fMat[kMPersp2];
|
|
if (w) { w = 1 / w; }
|
|
x *= w;
|
|
y *= w;
|
|
}
|
|
return {x, y};
|
|
}
|
|
|
|
/** Maps src vector array of length count to vector SkPoint array of equal or greater
|
|
length. Vectors are mapped by multiplying each vector by SkMatrix, treating
|
|
SkMatrix translation as zero. Given:
|
|
|
|
| A B 0 | | x |
|
|
Matrix = | D E 0 |, src = | y |
|
|
| G H I | | 1 |
|
|
|
|
where
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
x = src[i].fX
|
|
y = src[i].fY
|
|
}
|
|
|
|
each dst vector is computed as:
|
|
|
|
|A B 0| |x| Ax+By Dx+Ey
|
|
Matrix * src = |D E 0| |y| = |Ax+By Dx+Ey Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
src and dst may point to the same storage.
|
|
|
|
@param dst storage for mapped vectors
|
|
@param src vectors to transform
|
|
@param count number of vectors to transform
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_mapVectors
|
|
*/
|
|
void mapVectors(SkVector dst[], const SkVector src[], int count) const;
|
|
|
|
/** Maps vecs vector array of length count in place, multiplying each vector by
|
|
SkMatrix, treating SkMatrix translation as zero. Given:
|
|
|
|
| A B 0 | | x |
|
|
Matrix = | D E 0 |, vec = | y |
|
|
| G H I | | 1 |
|
|
|
|
where
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
x = vecs[i].fX
|
|
y = vecs[i].fY
|
|
}
|
|
|
|
each result vector is computed as:
|
|
|
|
|A B 0| |x| Ax+By Dx+Ey
|
|
Matrix * vec = |D E 0| |y| = |Ax+By Dx+Ey Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
@param vecs vectors to transform, and storage for mapped vectors
|
|
@param count number of vectors to transform
|
|
*/
|
|
void mapVectors(SkVector vecs[], int count) const {
|
|
this->mapVectors(vecs, vecs, count);
|
|
}
|
|
|
|
/** Maps vector (dx, dy) to result. Vector is mapped by multiplying by SkMatrix,
|
|
treating SkMatrix translation as zero. Given:
|
|
|
|
| A B 0 | | dx |
|
|
Matrix = | D E 0 |, vec = | dy |
|
|
| G H I | | 1 |
|
|
|
|
each result vector is computed as:
|
|
|
|
|A B 0| |dx| A*dx+B*dy D*dx+E*dy
|
|
Matrix * vec = |D E 0| |dy| = |A*dx+B*dy D*dx+E*dy G*dx+H*dy+I| = ----------- , -----------
|
|
|G H I| | 1| G*dx+H*dy+I G*dx+*dHy+I
|
|
|
|
@param dx x-axis value of vector to map
|
|
@param dy y-axis value of vector to map
|
|
@param result storage for mapped vector
|
|
*/
|
|
void mapVector(SkScalar dx, SkScalar dy, SkVector* result) const {
|
|
SkVector vec = { dx, dy };
|
|
this->mapVectors(result, &vec, 1);
|
|
}
|
|
|
|
/** Returns vector (dx, dy) multiplied by SkMatrix, treating SkMatrix translation as zero.
|
|
Given:
|
|
|
|
| A B 0 | | dx |
|
|
Matrix = | D E 0 |, vec = | dy |
|
|
| G H I | | 1 |
|
|
|
|
each result vector is computed as:
|
|
|
|
|A B 0| |dx| A*dx+B*dy D*dx+E*dy
|
|
Matrix * vec = |D E 0| |dy| = |A*dx+B*dy D*dx+E*dy G*dx+H*dy+I| = ----------- , -----------
|
|
|G H I| | 1| G*dx+H*dy+I G*dx+*dHy+I
|
|
|
|
@param dx x-axis value of vector to map
|
|
@param dy y-axis value of vector to map
|
|
@return mapped vector
|
|
*/
|
|
SkVector mapVector(SkScalar dx, SkScalar dy) const {
|
|
SkVector vec = { dx, dy };
|
|
this->mapVectors(&vec, &vec, 1);
|
|
return vec;
|
|
}
|
|
|
|
/** Sets dst to bounds of src corners mapped by SkMatrix.
|
|
Returns true if mapped corners are dst corners.
|
|
|
|
Returned value is the same as calling rectStaysRect().
|
|
|
|
@param dst storage for bounds of mapped SkPoint
|
|
@param src SkRect to map
|
|
@param pc whether to apply perspective clipping
|
|
@return true if dst is equivalent to mapped src
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_mapRect
|
|
*/
|
|
bool mapRect(SkRect* dst, const SkRect& src,
|
|
SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const;
|
|
|
|
/** Sets rect to bounds of rect corners mapped by SkMatrix.
|
|
Returns true if mapped corners are computed rect corners.
|
|
|
|
Returned value is the same as calling rectStaysRect().
|
|
|
|
@param rect rectangle to map, and storage for bounds of mapped corners
|
|
@param pc whether to apply perspective clipping
|
|
@return true if result is equivalent to mapped rect
|
|
*/
|
|
bool mapRect(SkRect* rect, SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const {
|
|
return this->mapRect(rect, *rect, pc);
|
|
}
|
|
|
|
/** Returns bounds of src corners mapped by SkMatrix.
|
|
|
|
@param src rectangle to map
|
|
@return mapped bounds
|
|
*/
|
|
SkRect mapRect(const SkRect& src,
|
|
SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const {
|
|
SkRect dst;
|
|
(void)this->mapRect(&dst, src, pc);
|
|
return dst;
|
|
}
|
|
|
|
/** Maps four corners of rect to dst. SkPoint are mapped by multiplying each
|
|
rect corner by SkMatrix. rect corner is processed in this order:
|
|
(rect.fLeft, rect.fTop), (rect.fRight, rect.fTop), (rect.fRight, rect.fBottom),
|
|
(rect.fLeft, rect.fBottom).
|
|
|
|
rect may be empty: rect.fLeft may be greater than or equal to rect.fRight;
|
|
rect.fTop may be greater than or equal to rect.fBottom.
|
|
|
|
Given:
|
|
|
|
| A B C | | x |
|
|
Matrix = | D E F |, pt = | y |
|
|
| G H I | | 1 |
|
|
|
|
where pt is initialized from each of (rect.fLeft, rect.fTop),
|
|
(rect.fRight, rect.fTop), (rect.fRight, rect.fBottom), (rect.fLeft, rect.fBottom),
|
|
each dst SkPoint is computed as:
|
|
|
|
|A B C| |x| Ax+By+C Dx+Ey+F
|
|
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
|
|
|G H I| |1| Gx+Hy+I Gx+Hy+I
|
|
|
|
@param dst storage for mapped corner SkPoint
|
|
@param rect SkRect to map
|
|
|
|
Note: this does not perform perspective clipping (as that might result in more than
|
|
4 points, so results are suspect if the matrix contains perspective.
|
|
*/
|
|
void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const {
|
|
// This could potentially be faster if we only transformed each x and y of the rect once.
|
|
rect.toQuad(dst);
|
|
this->mapPoints(dst, 4);
|
|
}
|
|
|
|
/** Sets dst to bounds of src corners mapped by SkMatrix. If matrix contains
|
|
elements other than scale or translate: asserts if SK_DEBUG is defined;
|
|
otherwise, results are undefined.
|
|
|
|
@param dst storage for bounds of mapped SkPoint
|
|
@param src SkRect to map
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_mapRectScaleTranslate
|
|
*/
|
|
void mapRectScaleTranslate(SkRect* dst, const SkRect& src) const;
|
|
|
|
/** Returns geometric mean radius of ellipse formed by constructing circle of
|
|
size radius, and mapping constructed circle with SkMatrix. The result squared is
|
|
equal to the major axis length times the minor axis length.
|
|
Result is not meaningful if SkMatrix contains perspective elements.
|
|
|
|
@param radius circle size to map
|
|
@return average mapped radius
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_mapRadius
|
|
*/
|
|
SkScalar mapRadius(SkScalar radius) const;
|
|
|
|
/** Compares a and b; returns true if a and b are numerically equal. Returns true
|
|
even if sign of zero values are different. Returns false if either SkMatrix
|
|
contains NaN, even if the other SkMatrix also contains NaN.
|
|
|
|
@param a SkMatrix to compare
|
|
@param b SkMatrix to compare
|
|
@return true if SkMatrix a and SkMatrix b are numerically equal
|
|
*/
|
|
friend SK_API bool operator==(const SkMatrix& a, const SkMatrix& b);
|
|
|
|
/** Compares a and b; returns true if a and b are not numerically equal. Returns false
|
|
even if sign of zero values are different. Returns true if either SkMatrix
|
|
contains NaN, even if the other SkMatrix also contains NaN.
|
|
|
|
@param a SkMatrix to compare
|
|
@param b SkMatrix to compare
|
|
@return true if SkMatrix a and SkMatrix b are numerically not equal
|
|
*/
|
|
friend SK_API bool operator!=(const SkMatrix& a, const SkMatrix& b) {
|
|
return !(a == b);
|
|
}
|
|
|
|
/** Writes text representation of SkMatrix to standard output. Floating point values
|
|
are written with limited precision; it may not be possible to reconstruct
|
|
original SkMatrix from output.
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_dump
|
|
*/
|
|
void dump() const;
|
|
|
|
/** Returns the minimum scaling factor of SkMatrix by decomposing the scaling and
|
|
skewing elements.
|
|
Returns -1 if scale factor overflows or SkMatrix contains perspective.
|
|
|
|
@return minimum scale factor
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_getMinScale
|
|
*/
|
|
SkScalar getMinScale() const;
|
|
|
|
/** Returns the maximum scaling factor of SkMatrix by decomposing the scaling and
|
|
skewing elements.
|
|
Returns -1 if scale factor overflows or SkMatrix contains perspective.
|
|
|
|
@return maximum scale factor
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_getMaxScale
|
|
*/
|
|
SkScalar getMaxScale() const;
|
|
|
|
/** Sets scaleFactors[0] to the minimum scaling factor, and scaleFactors[1] to the
|
|
maximum scaling factor. Scaling factors are computed by decomposing
|
|
the SkMatrix scaling and skewing elements.
|
|
|
|
Returns true if scaleFactors are found; otherwise, returns false and sets
|
|
scaleFactors to undefined values.
|
|
|
|
@param scaleFactors storage for minimum and maximum scale factors
|
|
@return true if scale factors were computed correctly
|
|
*/
|
|
[[nodiscard]] bool getMinMaxScales(SkScalar scaleFactors[2]) const;
|
|
|
|
/** Decomposes SkMatrix into scale components and whatever remains. Returns false if
|
|
SkMatrix could not be decomposed.
|
|
|
|
Sets scale to portion of SkMatrix that scale axes. Sets remaining to SkMatrix
|
|
with scaling factored out. remaining may be passed as nullptr
|
|
to determine if SkMatrix can be decomposed without computing remainder.
|
|
|
|
Returns true if scale components are found. scale and remaining are
|
|
unchanged if SkMatrix contains perspective; scale factors are not finite, or
|
|
are nearly zero.
|
|
|
|
On success: Matrix = Remaining * scale.
|
|
|
|
@param scale axes scaling factors; may be nullptr
|
|
@param remaining SkMatrix without scaling; may be nullptr
|
|
@return true if scale can be computed
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_decomposeScale
|
|
*/
|
|
bool decomposeScale(SkSize* scale, SkMatrix* remaining = nullptr) const;
|
|
|
|
/** Returns reference to const identity SkMatrix. Returned SkMatrix is set to:
|
|
|
|
| 1 0 0 |
|
|
| 0 1 0 |
|
|
| 0 0 1 |
|
|
|
|
@return const identity SkMatrix
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_I
|
|
*/
|
|
static const SkMatrix& I();
|
|
|
|
/** Returns reference to a const SkMatrix with invalid values. Returned SkMatrix is set
|
|
to:
|
|
|
|
| SK_ScalarMax SK_ScalarMax SK_ScalarMax |
|
|
| SK_ScalarMax SK_ScalarMax SK_ScalarMax |
|
|
| SK_ScalarMax SK_ScalarMax SK_ScalarMax |
|
|
|
|
@return const invalid SkMatrix
|
|
|
|
example: https://fiddle.skia.org/c/@Matrix_InvalidMatrix
|
|
*/
|
|
static const SkMatrix& InvalidMatrix();
|
|
|
|
/** Returns SkMatrix a multiplied by SkMatrix b.
|
|
|
|
Given:
|
|
|
|
| A B C | | J K L |
|
|
a = | D E F |, b = | M N O |
|
|
| G H I | | P Q R |
|
|
|
|
sets SkMatrix to:
|
|
|
|
| A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR |
|
|
a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
|
|
| G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR |
|
|
|
|
@param a SkMatrix on left side of multiply expression
|
|
@param b SkMatrix on right side of multiply expression
|
|
@return SkMatrix computed from a times b
|
|
*/
|
|
static SkMatrix Concat(const SkMatrix& a, const SkMatrix& b) {
|
|
SkMatrix result;
|
|
result.setConcat(a, b);
|
|
return result;
|
|
}
|
|
|
|
friend SkMatrix operator*(const SkMatrix& a, const SkMatrix& b) {
|
|
return Concat(a, b);
|
|
}
|
|
|
|
/** Sets internal cache to unknown state. Use to force update after repeated
|
|
modifications to SkMatrix element reference returned by operator[](int index).
|
|
*/
|
|
void dirtyMatrixTypeCache() {
|
|
this->setTypeMask(kUnknown_Mask);
|
|
}
|
|
|
|
/** Initializes SkMatrix with scale and translate elements.
|
|
|
|
| sx 0 tx |
|
|
| 0 sy ty |
|
|
| 0 0 1 |
|
|
|
|
@param sx horizontal scale factor to store
|
|
@param sy vertical scale factor to store
|
|
@param tx horizontal translation to store
|
|
@param ty vertical translation to store
|
|
*/
|
|
void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty) {
|
|
fMat[kMScaleX] = sx;
|
|
fMat[kMSkewX] = 0;
|
|
fMat[kMTransX] = tx;
|
|
|
|
fMat[kMSkewY] = 0;
|
|
fMat[kMScaleY] = sy;
|
|
fMat[kMTransY] = ty;
|
|
|
|
fMat[kMPersp0] = 0;
|
|
fMat[kMPersp1] = 0;
|
|
fMat[kMPersp2] = 1;
|
|
|
|
int mask = 0;
|
|
if (sx != 1 || sy != 1) {
|
|
mask |= kScale_Mask;
|
|
}
|
|
if (tx != 0.0f || ty != 0.0f) {
|
|
mask |= kTranslate_Mask;
|
|
}
|
|
if (sx != 0 && sy != 0) {
|
|
mask |= kRectStaysRect_Mask;
|
|
}
|
|
this->setTypeMask(mask);
|
|
}
|
|
|
|
/** Returns true if all elements of the matrix are finite. Returns false if any
|
|
element is infinity, or NaN.
|
|
|
|
@return true if matrix has only finite elements
|
|
*/
|
|
bool isFinite() const { return SkScalarsAreFinite(fMat, 9); }
|
|
|
|
private:
|
|
/** Set if the matrix will map a rectangle to another rectangle. This
|
|
can be true if the matrix is scale-only, or rotates a multiple of
|
|
90 degrees.
|
|
|
|
This bit will be set on identity matrices
|
|
*/
|
|
static constexpr int kRectStaysRect_Mask = 0x10;
|
|
|
|
/** Set if the perspective bit is valid even though the rest of
|
|
the matrix is Unknown.
|
|
*/
|
|
static constexpr int kOnlyPerspectiveValid_Mask = 0x40;
|
|
|
|
static constexpr int kUnknown_Mask = 0x80;
|
|
|
|
static constexpr int kORableMasks = kTranslate_Mask |
|
|
kScale_Mask |
|
|
kAffine_Mask |
|
|
kPerspective_Mask;
|
|
|
|
static constexpr int kAllMasks = kTranslate_Mask |
|
|
kScale_Mask |
|
|
kAffine_Mask |
|
|
kPerspective_Mask |
|
|
kRectStaysRect_Mask;
|
|
|
|
SkScalar fMat[9];
|
|
mutable int32_t fTypeMask;
|
|
|
|
constexpr SkMatrix(SkScalar sx, SkScalar kx, SkScalar tx,
|
|
SkScalar ky, SkScalar sy, SkScalar ty,
|
|
SkScalar p0, SkScalar p1, SkScalar p2, int typeMask)
|
|
: fMat{sx, kx, tx,
|
|
ky, sy, ty,
|
|
p0, p1, p2}
|
|
, fTypeMask(typeMask) {}
|
|
|
|
static void ComputeInv(SkScalar dst[9], const SkScalar src[9], double invDet, bool isPersp);
|
|
|
|
uint8_t computeTypeMask() const;
|
|
uint8_t computePerspectiveTypeMask() const;
|
|
|
|
void setTypeMask(int mask) {
|
|
// allow kUnknown or a valid mask
|
|
SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask ||
|
|
((kUnknown_Mask | kOnlyPerspectiveValid_Mask) & mask)
|
|
== (kUnknown_Mask | kOnlyPerspectiveValid_Mask));
|
|
fTypeMask = mask;
|
|
}
|
|
|
|
void orTypeMask(int mask) {
|
|
SkASSERT((mask & kORableMasks) == mask);
|
|
fTypeMask |= mask;
|
|
}
|
|
|
|
void clearTypeMask(int mask) {
|
|
// only allow a valid mask
|
|
SkASSERT((mask & kAllMasks) == mask);
|
|
fTypeMask &= ~mask;
|
|
}
|
|
|
|
TypeMask getPerspectiveTypeMaskOnly() const {
|
|
if ((fTypeMask & kUnknown_Mask) &&
|
|
!(fTypeMask & kOnlyPerspectiveValid_Mask)) {
|
|
fTypeMask = this->computePerspectiveTypeMask();
|
|
}
|
|
return (TypeMask)(fTypeMask & 0xF);
|
|
}
|
|
|
|
/** Returns true if we already know that the matrix is identity;
|
|
false otherwise.
|
|
*/
|
|
bool isTriviallyIdentity() const {
|
|
if (fTypeMask & kUnknown_Mask) {
|
|
return false;
|
|
}
|
|
return ((fTypeMask & 0xF) == 0);
|
|
}
|
|
|
|
inline void updateTranslateMask() {
|
|
if ((fMat[kMTransX] != 0) | (fMat[kMTransY] != 0)) {
|
|
fTypeMask |= kTranslate_Mask;
|
|
} else {
|
|
fTypeMask &= ~kTranslate_Mask;
|
|
}
|
|
}
|
|
|
|
typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y,
|
|
SkPoint* result);
|
|
|
|
static MapXYProc GetMapXYProc(TypeMask mask) {
|
|
SkASSERT((mask & ~kAllMasks) == 0);
|
|
return gMapXYProcs[mask & kAllMasks];
|
|
}
|
|
|
|
MapXYProc getMapXYProc() const {
|
|
return GetMapXYProc(this->getType());
|
|
}
|
|
|
|
typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[],
|
|
const SkPoint src[], int count);
|
|
|
|
static MapPtsProc GetMapPtsProc(TypeMask mask) {
|
|
SkASSERT((mask & ~kAllMasks) == 0);
|
|
return gMapPtsProcs[mask & kAllMasks];
|
|
}
|
|
|
|
MapPtsProc getMapPtsProc() const {
|
|
return GetMapPtsProc(this->getType());
|
|
}
|
|
|
|
[[nodiscard]] bool invertNonIdentity(SkMatrix* inverse) const;
|
|
|
|
static bool Poly2Proc(const SkPoint[], SkMatrix*);
|
|
static bool Poly3Proc(const SkPoint[], SkMatrix*);
|
|
static bool Poly4Proc(const SkPoint[], SkMatrix*);
|
|
|
|
static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
|
|
static const MapXYProc gMapXYProcs[];
|
|
|
|
static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int);
|
|
static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
|
|
int count);
|
|
static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
|
|
static void Affine_vpts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
|
|
static const MapPtsProc gMapPtsProcs[];
|
|
|
|
// return the number of bytes written, whether or not buffer is null
|
|
size_t writeToMemory(void* buffer) const;
|
|
/**
|
|
* Reads data from the buffer parameter
|
|
*
|
|
* @param buffer Memory to read from
|
|
* @param length Amount of memory available in the buffer
|
|
* @return number of bytes read (must be a multiple of 4) or
|
|
* 0 if there was not enough memory available
|
|
*/
|
|
size_t readFromMemory(const void* buffer, size_t length);
|
|
|
|
// legacy method -- still needed? why not just postScale(1/divx, ...)?
|
|
bool postIDiv(int divx, int divy);
|
|
void doNormalizePerspective();
|
|
|
|
friend class SkPerspIter;
|
|
friend class SkMatrixPriv;
|
|
friend class SerializationTest;
|
|
};
|
|
SK_END_REQUIRE_DENSE
|
|
|
|
enum class SkPathFillType {
|
|
/** Specifies that "inside" is computed by a non-zero sum of signed edge crossings */
|
|
kWinding,
|
|
/** Specifies that "inside" is computed by an odd number of edge crossings */
|
|
kEvenOdd,
|
|
/** Same as Winding, but draws outside of the path, rather than inside */
|
|
kInverseWinding,
|
|
/** Same as EvenOdd, but draws outside of the path, rather than inside */
|
|
kInverseEvenOdd
|
|
};
|
|
|
|
static inline bool SkPathFillType_IsEvenOdd(SkPathFillType ft) {
|
|
return (static_cast<int>(ft) & 1) != 0;
|
|
}
|
|
|
|
static inline bool SkPathFillType_IsInverse(SkPathFillType ft) {
|
|
return (static_cast<int>(ft) & 2) != 0;
|
|
}
|
|
|
|
static inline SkPathFillType SkPathFillType_ConvertToNonInverse(SkPathFillType ft) {
|
|
return static_cast<SkPathFillType>(static_cast<int>(ft) & 1);
|
|
}
|
|
|
|
enum class SkPathDirection {
|
|
/** clockwise direction for adding closed contours */
|
|
kCW,
|
|
/** counter-clockwise direction for adding closed contours */
|
|
kCCW,
|
|
};
|
|
|
|
enum SkPathSegmentMask {
|
|
kLine_SkPathSegmentMask = 1 << 0,
|
|
kQuad_SkPathSegmentMask = 1 << 1,
|
|
kConic_SkPathSegmentMask = 1 << 2,
|
|
kCubic_SkPathSegmentMask = 1 << 3,
|
|
};
|
|
|
|
enum class SkPathVerb {
|
|
kMove, //!< SkPath::RawIter returns 1 point
|
|
kLine, //!< SkPath::RawIter returns 2 points
|
|
kQuad, //!< SkPath::RawIter returns 3 points
|
|
kConic, //!< SkPath::RawIter returns 3 points + 1 weight
|
|
kCubic, //!< SkPath::RawIter returns 4 points
|
|
kClose //!< SkPath::RawIter returns 0 points
|
|
};
|
|
|
|
/** \class SkRefCntBase
|
|
|
|
SkRefCntBase is the base class for objects that may be shared by multiple
|
|
objects. When an existing owner wants to share a reference, it calls ref().
|
|
When an owner wants to release its reference, it calls unref(). When the
|
|
shared object's reference count goes to zero as the result of an unref()
|
|
call, its (virtual) destructor is called. It is an error for the
|
|
destructor to be called explicitly (or via the object going out of scope on
|
|
the stack or calling delete) if getRefCnt() > 1.
|
|
*/
|
|
class SK_API SkRefCntBase {
|
|
public:
|
|
/** Default construct, initializing the reference count to 1.
|
|
*/
|
|
SkRefCntBase() : fRefCnt(1) {}
|
|
|
|
/** Destruct, asserting that the reference count is 1.
|
|
*/
|
|
virtual ~SkRefCntBase() {
|
|
#ifdef SK_DEBUG
|
|
SkASSERTF(this->getRefCnt() == 1, "fRefCnt was %d", this->getRefCnt());
|
|
// illegal value, to catch us if we reuse after delete
|
|
fRefCnt.store(0, std::memory_order_relaxed);
|
|
#endif
|
|
}
|
|
|
|
/** May return true if the caller is the only owner.
|
|
* Ensures that all previous owner's actions are complete.
|
|
*/
|
|
bool unique() const {
|
|
if (1 == fRefCnt.load(std::memory_order_acquire)) {
|
|
// The acquire barrier is only really needed if we return true. It
|
|
// prevents code conditioned on the result of unique() from running
|
|
// until previous owners are all totally done calling unref().
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Increment the reference count. Must be balanced by a call to unref().
|
|
*/
|
|
void ref() const {
|
|
SkASSERT(this->getRefCnt() > 0);
|
|
// No barrier required.
|
|
(void)fRefCnt.fetch_add(+1, std::memory_order_relaxed);
|
|
}
|
|
|
|
/** Decrement the reference count. If the reference count is 1 before the
|
|
decrement, then delete the object. Note that if this is the case, then
|
|
the object needs to have been allocated via new, and not on the stack.
|
|
*/
|
|
void unref() const {
|
|
SkASSERT(this->getRefCnt() > 0);
|
|
// A release here acts in place of all releases we "should" have been doing in ref().
|
|
if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
|
|
// Like unique(), the acquire is only needed on success, to make sure
|
|
// code in internal_dispose() doesn't happen before the decrement.
|
|
this->internal_dispose();
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
#ifdef SK_DEBUG
|
|
/** Return the reference count. Use only for debugging. */
|
|
int32_t getRefCnt() const {
|
|
return fRefCnt.load(std::memory_order_relaxed);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Called when the ref count goes to 0.
|
|
*/
|
|
virtual void internal_dispose() const {
|
|
#ifdef SK_DEBUG
|
|
SkASSERT(0 == this->getRefCnt());
|
|
fRefCnt.store(1, std::memory_order_relaxed);
|
|
#endif
|
|
delete this;
|
|
}
|
|
|
|
// The following friends are those which override internal_dispose()
|
|
// and conditionally call SkRefCnt::internal_dispose().
|
|
friend class SkWeakRefCnt;
|
|
|
|
mutable std::atomic<int32_t> fRefCnt;
|
|
|
|
SkRefCntBase(SkRefCntBase&&) = delete;
|
|
SkRefCntBase(const SkRefCntBase&) = delete;
|
|
SkRefCntBase& operator=(SkRefCntBase&&) = delete;
|
|
SkRefCntBase& operator=(const SkRefCntBase&) = delete;
|
|
};
|
|
|
|
#ifdef SK_REF_CNT_MIXIN_INCLUDE
|
|
// It is the responsibility of the following include to define the type SkRefCnt.
|
|
// This SkRefCnt should normally derive from SkRefCntBase.
|
|
#else
|
|
class SK_API SkRefCnt : public SkRefCntBase {
|
|
// "#include SK_REF_CNT_MIXIN_INCLUDE" doesn't work with this build system.
|
|
#if defined(SK_BUILD_FOR_GOOGLE3)
|
|
public:
|
|
void deref() const { this->unref(); }
|
|
#endif
|
|
};
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Call obj->ref() and return obj. The obj must not be nullptr.
|
|
*/
|
|
template <typename T> static inline T* SkRef(T* obj) {
|
|
SkASSERT(obj);
|
|
obj->ref();
|
|
return obj;
|
|
}
|
|
|
|
/** Check if the argument is non-null, and if so, call obj->ref() and return obj.
|
|
*/
|
|
template <typename T> static inline T* SkSafeRef(T* obj) {
|
|
if (obj) {
|
|
obj->ref();
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/** Check if the argument is non-null, and if so, call obj->unref()
|
|
*/
|
|
template <typename T> static inline void SkSafeUnref(T* obj) {
|
|
if (obj) {
|
|
obj->unref();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// This is a variant of SkRefCnt that's Not Virtual, so weighs 4 bytes instead of 8 or 16.
|
|
// There's only benefit to using this if the deriving class does not otherwise need a vtable.
|
|
template <typename Derived>
|
|
class SkNVRefCnt {
|
|
public:
|
|
SkNVRefCnt() : fRefCnt(1) {}
|
|
~SkNVRefCnt() {
|
|
#ifdef SK_DEBUG
|
|
int rc = fRefCnt.load(std::memory_order_relaxed);
|
|
SkASSERTF(rc == 1, "NVRefCnt was %d", rc);
|
|
#endif
|
|
}
|
|
|
|
// Implementation is pretty much the same as SkRefCntBase. All required barriers are the same:
|
|
// - unique() needs acquire when it returns true, and no barrier if it returns false;
|
|
// - ref() doesn't need any barrier;
|
|
// - unref() needs a release barrier, and an acquire if it's going to call delete.
|
|
|
|
bool unique() const { return 1 == fRefCnt.load(std::memory_order_acquire); }
|
|
void ref() const { (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed); }
|
|
void unref() const {
|
|
if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
|
|
// restore the 1 for our destructor's assert
|
|
SkDEBUGCODE(fRefCnt.store(1, std::memory_order_relaxed));
|
|
delete (const Derived*)this;
|
|
}
|
|
}
|
|
void deref() const { this->unref(); }
|
|
|
|
// This must be used with caution. It is only valid to call this when 'threadIsolatedTestCnt'
|
|
// refs are known to be isolated to the current thread. That is, it is known that there are at
|
|
// least 'threadIsolatedTestCnt' refs for which no other thread may make a balancing unref()
|
|
// call. Assuming the contract is followed, if this returns false then no other thread has
|
|
// ownership of this. If it returns true then another thread *may* have ownership.
|
|
bool refCntGreaterThan(int32_t threadIsolatedTestCnt) const {
|
|
int cnt = fRefCnt.load(std::memory_order_acquire);
|
|
// If this fails then the above contract has been violated.
|
|
SkASSERT(cnt >= threadIsolatedTestCnt);
|
|
return cnt > threadIsolatedTestCnt;
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic<int32_t> fRefCnt;
|
|
|
|
SkNVRefCnt(SkNVRefCnt&&) = delete;
|
|
SkNVRefCnt(const SkNVRefCnt&) = delete;
|
|
SkNVRefCnt& operator=(SkNVRefCnt&&) = delete;
|
|
SkNVRefCnt& operator=(const SkNVRefCnt&) = delete;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Shared pointer class to wrap classes that support a ref()/unref() interface.
|
|
*
|
|
* This can be used for classes inheriting from SkRefCnt, but it also works for other
|
|
* classes that match the interface, but have different internal choices: e.g. the hosted class
|
|
* may have its ref/unref be thread-safe, but that is not assumed/imposed by sk_sp.
|
|
*
|
|
* Declared with the trivial_abi attribute where supported so that sk_sp and types containing it
|
|
* may be considered as trivially relocatable by the compiler so that destroying-move operations
|
|
* i.e. move constructor followed by destructor can be optimized to memcpy.
|
|
*/
|
|
template <typename T> class SK_TRIVIAL_ABI sk_sp {
|
|
public:
|
|
using element_type = T;
|
|
|
|
constexpr sk_sp() : fPtr(nullptr) {}
|
|
constexpr sk_sp(std::nullptr_t) : fPtr(nullptr) {}
|
|
|
|
/**
|
|
* Shares the underlying object by calling ref(), so that both the argument and the newly
|
|
* created sk_sp both have a reference to it.
|
|
*/
|
|
sk_sp(const sk_sp<T>& that) : fPtr(SkSafeRef(that.get())) {}
|
|
template <typename U,
|
|
typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type>
|
|
sk_sp(const sk_sp<U>& that) : fPtr(SkSafeRef(that.get())) {}
|
|
|
|
/**
|
|
* Move the underlying object from the argument to the newly created sk_sp. Afterwards only
|
|
* the new sk_sp will have a reference to the object, and the argument will point to null.
|
|
* No call to ref() or unref() will be made.
|
|
*/
|
|
sk_sp(sk_sp<T>&& that) : fPtr(that.release()) {}
|
|
template <typename U,
|
|
typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type>
|
|
sk_sp(sk_sp<U>&& that) : fPtr(that.release()) {}
|
|
|
|
/**
|
|
* Adopt the bare pointer into the newly created sk_sp.
|
|
* No call to ref() or unref() will be made.
|
|
*/
|
|
explicit sk_sp(T* obj) : fPtr(obj) {}
|
|
|
|
/**
|
|
* Calls unref() on the underlying object pointer.
|
|
*/
|
|
~sk_sp() {
|
|
SkSafeUnref(fPtr);
|
|
SkDEBUGCODE(fPtr = nullptr);
|
|
}
|
|
|
|
sk_sp<T>& operator=(std::nullptr_t) { this->reset(); return *this; }
|
|
|
|
/**
|
|
* Shares the underlying object referenced by the argument by calling ref() on it. If this
|
|
* sk_sp previously had a reference to an object (i.e. not null) it will call unref() on that
|
|
* object.
|
|
*/
|
|
sk_sp<T>& operator=(const sk_sp<T>& that) {
|
|
if (this != &that) {
|
|
this->reset(SkSafeRef(that.get()));
|
|
}
|
|
return *this;
|
|
}
|
|
template <typename U,
|
|
typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type>
|
|
sk_sp<T>& operator=(const sk_sp<U>& that) {
|
|
this->reset(SkSafeRef(that.get()));
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Move the underlying object from the argument to the sk_sp. If the sk_sp previously held
|
|
* a reference to another object, unref() will be called on that object. No call to ref()
|
|
* will be made.
|
|
*/
|
|
sk_sp<T>& operator=(sk_sp<T>&& that) {
|
|
this->reset(that.release());
|
|
return *this;
|
|
}
|
|
template <typename U,
|
|
typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type>
|
|
sk_sp<T>& operator=(sk_sp<U>&& that) {
|
|
this->reset(that.release());
|
|
return *this;
|
|
}
|
|
|
|
T& operator*() const {
|
|
SkASSERT(this->get() != nullptr);
|
|
return *this->get();
|
|
}
|
|
|
|
explicit operator bool() const { return this->get() != nullptr; }
|
|
|
|
T* get() const { return fPtr; }
|
|
T* operator->() const { return fPtr; }
|
|
|
|
/**
|
|
* Adopt the new bare pointer, and call unref() on any previously held object (if not null).
|
|
* No call to ref() will be made.
|
|
*/
|
|
void reset(T* ptr = nullptr) {
|
|
// Calling fPtr->unref() may call this->~() or this->reset(T*).
|
|
// http://wg21.cmeerw.net/lwg/issue998
|
|
// http://wg21.cmeerw.net/lwg/issue2262
|
|
T* oldPtr = fPtr;
|
|
fPtr = ptr;
|
|
SkSafeUnref(oldPtr);
|
|
}
|
|
|
|
/**
|
|
* Return the bare pointer, and set the internal object pointer to nullptr.
|
|
* The caller must assume ownership of the object, and manage its reference count directly.
|
|
* No call to unref() will be made.
|
|
*/
|
|
[[nodiscard]] T* release() {
|
|
T* ptr = fPtr;
|
|
fPtr = nullptr;
|
|
return ptr;
|
|
}
|
|
|
|
void swap(sk_sp<T>& that) /*noexcept*/ {
|
|
using std::swap;
|
|
swap(fPtr, that.fPtr);
|
|
}
|
|
|
|
using sk_is_trivially_relocatable = std::true_type;
|
|
|
|
private:
|
|
T* fPtr;
|
|
};
|
|
|
|
template <typename T> inline void swap(sk_sp<T>& a, sk_sp<T>& b) /*noexcept*/ {
|
|
a.swap(b);
|
|
}
|
|
|
|
template <typename T, typename U> inline bool operator==(const sk_sp<T>& a, const sk_sp<U>& b) {
|
|
return a.get() == b.get();
|
|
}
|
|
template <typename T> inline bool operator==(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ {
|
|
return !a;
|
|
}
|
|
template <typename T> inline bool operator==(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ {
|
|
return !b;
|
|
}
|
|
|
|
template <typename T, typename U> inline bool operator!=(const sk_sp<T>& a, const sk_sp<U>& b) {
|
|
return a.get() != b.get();
|
|
}
|
|
template <typename T> inline bool operator!=(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ {
|
|
return static_cast<bool>(a);
|
|
}
|
|
template <typename T> inline bool operator!=(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ {
|
|
return static_cast<bool>(b);
|
|
}
|
|
|
|
template <typename C, typename CT, typename T>
|
|
auto operator<<(std::basic_ostream<C, CT>& os, const sk_sp<T>& sp) -> decltype(os << sp.get()) {
|
|
return os << sp.get();
|
|
}
|
|
|
|
template <typename T, typename... Args>
|
|
sk_sp<T> sk_make_sp(Args&&... args) {
|
|
return sk_sp<T>(new T(std::forward<Args>(args)...));
|
|
}
|
|
|
|
/*
|
|
* Returns a sk_sp wrapping the provided ptr AND calls ref on it (if not null).
|
|
*
|
|
* This is different than the semantics of the constructor for sk_sp, which just wraps the ptr,
|
|
* effectively "adopting" it.
|
|
*/
|
|
template <typename T> sk_sp<T> sk_ref_sp(T* obj) {
|
|
return sk_sp<T>(SkSafeRef(obj));
|
|
}
|
|
|
|
template <typename T> sk_sp<T> sk_ref_sp(const T* obj) {
|
|
return sk_sp<T>(const_cast<T*>(SkSafeRef(obj)));
|
|
}
|
|
|
|
// Copyright 2022 Google LLC
|
|
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef SkTypeTraits_DEFINED
|
|
#define SkTypeTraits_DEFINED
|
|
|
|
|
|
// Trait for identifying types which are relocatable via memcpy, for container optimizations.
|
|
template<typename, typename = void>
|
|
struct sk_has_trivially_relocatable_member : std::false_type {};
|
|
|
|
// Types can declare themselves trivially relocatable with a public
|
|
// using sk_is_trivially_relocatable = std::true_type;
|
|
template<typename T>
|
|
struct sk_has_trivially_relocatable_member<T, std::void_t<typename T::sk_is_trivially_relocatable>>
|
|
: T::sk_is_trivially_relocatable {};
|
|
|
|
// By default, all trivially copyable types are trivially relocatable.
|
|
template <typename T>
|
|
struct sk_is_trivially_relocatable
|
|
: std::disjunction<std::is_trivially_copyable<T>, sk_has_trivially_relocatable_member<T>>{};
|
|
|
|
// Here be some dragons: while technically not guaranteed, we count on all sane unique_ptr
|
|
// implementations to be trivially relocatable.
|
|
template <typename T>
|
|
struct sk_is_trivially_relocatable<std::unique_ptr<T>> : std::true_type {};
|
|
|
|
template <typename T>
|
|
inline constexpr bool sk_is_trivially_relocatable_v = sk_is_trivially_relocatable<T>::value;
|
|
|
|
#endif // SkTypeTraits_DEFINED
|
|
|
|
class SkData;
|
|
class SkPathRef;
|
|
class SkRRect;
|
|
class SkWStream;
|
|
enum class SkPathConvexity;
|
|
enum class SkPathFirstDirection;
|
|
struct SkPathVerbAnalysis;
|
|
|
|
// WIP -- define this locally, and fix call-sites to use SkPathBuilder (skbug.com/9000)
|
|
//#define SK_HIDE_PATH_EDIT_METHODS
|
|
|
|
/** \class SkPath
|
|
SkPath contain geometry. SkPath may be empty, or contain one or more verbs that
|
|
outline a figure. SkPath always starts with a move verb to a Cartesian coordinate,
|
|
and may be followed by additional verbs that add lines or curves.
|
|
Adding a close verb makes the geometry into a continuous loop, a closed contour.
|
|
SkPath may contain any number of contours, each beginning with a move verb.
|
|
|
|
SkPath contours may contain only a move verb, or may also contain lines,
|
|
quadratic beziers, conics, and cubic beziers. SkPath contours may be open or
|
|
closed.
|
|
|
|
When used to draw a filled area, SkPath describes whether the fill is inside or
|
|
outside the geometry. SkPath also describes the winding rule used to fill
|
|
overlapping contours.
|
|
|
|
Internally, SkPath lazily computes metrics likes bounds and convexity. Call
|
|
SkPath::updateBoundsCache to make SkPath thread safe.
|
|
*/
|
|
class SK_API SkPath {
|
|
public:
|
|
/**
|
|
* Create a new path with the specified segments.
|
|
*
|
|
* The points and weights arrays are read in order, based on the sequence of verbs.
|
|
*
|
|
* Move 1 point
|
|
* Line 1 point
|
|
* Quad 2 points
|
|
* Conic 2 points and 1 weight
|
|
* Cubic 3 points
|
|
* Close 0 points
|
|
*
|
|
* If an illegal sequence of verbs is encountered, or the specified number of points
|
|
* or weights is not sufficient given the verbs, an empty Path is returned.
|
|
*
|
|
* A legal sequence of verbs consists of any number of Contours. A contour always begins
|
|
* with a Move verb, followed by 0 or more segments: Line, Quad, Conic, Cubic, followed
|
|
* by an optional Close.
|
|
*/
|
|
static SkPath Make(const SkPoint[], int pointCount,
|
|
const uint8_t[], int verbCount,
|
|
const SkScalar[], int conicWeightCount,
|
|
SkPathFillType, bool isVolatile = false);
|
|
|
|
static SkPath Rect(const SkRect&, SkPathDirection = SkPathDirection::kCW,
|
|
unsigned startIndex = 0);
|
|
static SkPath Oval(const SkRect&, SkPathDirection = SkPathDirection::kCW);
|
|
static SkPath Oval(const SkRect&, SkPathDirection, unsigned startIndex);
|
|
static SkPath Circle(SkScalar center_x, SkScalar center_y, SkScalar radius,
|
|
SkPathDirection dir = SkPathDirection::kCW);
|
|
static SkPath RRect(const SkRRect&, SkPathDirection dir = SkPathDirection::kCW);
|
|
static SkPath RRect(const SkRRect&, SkPathDirection, unsigned startIndex);
|
|
static SkPath RRect(const SkRect& bounds, SkScalar rx, SkScalar ry,
|
|
SkPathDirection dir = SkPathDirection::kCW);
|
|
|
|
static SkPath Polygon(const SkPoint pts[], int count, bool isClosed,
|
|
SkPathFillType = SkPathFillType::kWinding,
|
|
bool isVolatile = false);
|
|
|
|
static SkPath Polygon(const std::initializer_list<SkPoint>& list, bool isClosed,
|
|
SkPathFillType fillType = SkPathFillType::kWinding,
|
|
bool isVolatile = false) {
|
|
return Polygon(list.begin(), SkToInt(list.size()), isClosed, fillType, isVolatile);
|
|
}
|
|
|
|
static SkPath Line(const SkPoint a, const SkPoint b) {
|
|
return Polygon({a, b}, false);
|
|
}
|
|
|
|
/** Constructs an empty SkPath. By default, SkPath has no verbs, no SkPoint, and no weights.
|
|
FillType is set to kWinding.
|
|
|
|
@return empty SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_empty_constructor
|
|
*/
|
|
SkPath();
|
|
|
|
/** Constructs a copy of an existing path.
|
|
Copy constructor makes two paths identical by value. Internally, path and
|
|
the returned result share pointer values. The underlying verb array, SkPoint array
|
|
and weights are copied when modified.
|
|
|
|
Creating a SkPath copy is very efficient and never allocates memory.
|
|
SkPath are always copied by value from the interface; the underlying shared
|
|
pointers are not exposed.
|
|
|
|
@param path SkPath to copy by value
|
|
@return copy of SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_copy_const_SkPath
|
|
*/
|
|
SkPath(const SkPath& path);
|
|
|
|
/** Releases ownership of any shared data and deletes data if SkPath is sole owner.
|
|
|
|
example: https://fiddle.skia.org/c/@Path_destructor
|
|
*/
|
|
~SkPath();
|
|
|
|
/** Constructs a copy of an existing path.
|
|
SkPath assignment makes two paths identical by value. Internally, assignment
|
|
shares pointer values. The underlying verb array, SkPoint array and weights
|
|
are copied when modified.
|
|
|
|
Copying SkPath by assignment is very efficient and never allocates memory.
|
|
SkPath are always copied by value from the interface; the underlying shared
|
|
pointers are not exposed.
|
|
|
|
@param path verb array, SkPoint array, weights, and SkPath::FillType to copy
|
|
@return SkPath copied by value
|
|
|
|
example: https://fiddle.skia.org/c/@Path_copy_operator
|
|
*/
|
|
SkPath& operator=(const SkPath& path);
|
|
|
|
/** Compares a and b; returns true if SkPath::FillType, verb array, SkPoint array, and weights
|
|
are equivalent.
|
|
|
|
@param a SkPath to compare
|
|
@param b SkPath to compare
|
|
@return true if SkPath pair are equivalent
|
|
*/
|
|
friend SK_API bool operator==(const SkPath& a, const SkPath& b);
|
|
|
|
/** Compares a and b; returns true if SkPath::FillType, verb array, SkPoint array, and weights
|
|
are not equivalent.
|
|
|
|
@param a SkPath to compare
|
|
@param b SkPath to compare
|
|
@return true if SkPath pair are not equivalent
|
|
*/
|
|
friend bool operator!=(const SkPath& a, const SkPath& b) {
|
|
return !(a == b);
|
|
}
|
|
|
|
/** Returns true if SkPath contain equal verbs and equal weights.
|
|
If SkPath contain one or more conics, the weights must match.
|
|
|
|
conicTo() may add different verbs depending on conic weight, so it is not
|
|
trivial to interpolate a pair of SkPath containing conics with different
|
|
conic weight values.
|
|
|
|
@param compare SkPath to compare
|
|
@return true if SkPath verb array and weights are equivalent
|
|
|
|
example: https://fiddle.skia.org/c/@Path_isInterpolatable
|
|
*/
|
|
bool isInterpolatable(const SkPath& compare) const;
|
|
|
|
/** Interpolates between SkPath with SkPoint array of equal size.
|
|
Copy verb array and weights to out, and set out SkPoint array to a weighted
|
|
average of this SkPoint array and ending SkPoint array, using the formula:
|
|
(Path Point * weight) + ending Point * (1 - weight).
|
|
|
|
weight is most useful when between zero (ending SkPoint array) and
|
|
one (this Point_Array); will work with values outside of this
|
|
range.
|
|
|
|
interpolate() returns false and leaves out unchanged if SkPoint array is not
|
|
the same size as ending SkPoint array. Call isInterpolatable() to check SkPath
|
|
compatibility prior to calling interpolate().
|
|
|
|
@param ending SkPoint array averaged with this SkPoint array
|
|
@param weight contribution of this SkPoint array, and
|
|
one minus contribution of ending SkPoint array
|
|
@param out SkPath replaced by interpolated averages
|
|
@return true if SkPath contain same number of SkPoint
|
|
|
|
example: https://fiddle.skia.org/c/@Path_interpolate
|
|
*/
|
|
bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const;
|
|
|
|
/** Returns SkPathFillType, the rule used to fill SkPath.
|
|
|
|
@return current SkPathFillType setting
|
|
*/
|
|
SkPathFillType getFillType() const { return (SkPathFillType)fFillType; }
|
|
|
|
/** Sets FillType, the rule used to fill SkPath. While there is no check
|
|
that ft is legal, values outside of FillType are not supported.
|
|
*/
|
|
void setFillType(SkPathFillType ft) {
|
|
fFillType = SkToU8(ft);
|
|
}
|
|
|
|
/** Returns if FillType describes area outside SkPath geometry. The inverse fill area
|
|
extends indefinitely.
|
|
|
|
@return true if FillType is kInverseWinding or kInverseEvenOdd
|
|
*/
|
|
bool isInverseFillType() const { return SkPathFillType_IsInverse(this->getFillType()); }
|
|
|
|
/** Replaces FillType with its inverse. The inverse of FillType describes the area
|
|
unmodified by the original FillType.
|
|
*/
|
|
void toggleInverseFillType() {
|
|
fFillType ^= 2;
|
|
}
|
|
|
|
/** Returns true if the path is convex. If necessary, it will first compute the convexity.
|
|
*/
|
|
bool isConvex() const;
|
|
|
|
/** Returns true if this path is recognized as an oval or circle.
|
|
|
|
bounds receives bounds of oval.
|
|
|
|
bounds is unmodified if oval is not found.
|
|
|
|
@param bounds storage for bounding SkRect of oval; may be nullptr
|
|
@return true if SkPath is recognized as an oval or circle
|
|
|
|
example: https://fiddle.skia.org/c/@Path_isOval
|
|
*/
|
|
bool isOval(SkRect* bounds) const;
|
|
|
|
/** Returns true if path is representable as SkRRect.
|
|
Returns false if path is representable as oval, circle, or SkRect.
|
|
|
|
rrect receives bounds of SkRRect.
|
|
|
|
rrect is unmodified if SkRRect is not found.
|
|
|
|
@param rrect storage for bounding SkRect of SkRRect; may be nullptr
|
|
@return true if SkPath contains only SkRRect
|
|
|
|
example: https://fiddle.skia.org/c/@Path_isRRect
|
|
*/
|
|
bool isRRect(SkRRect* rrect) const;
|
|
|
|
/** Sets SkPath to its initial state.
|
|
Removes verb array, SkPoint array, and weights, and sets FillType to kWinding.
|
|
Internal storage associated with SkPath is released.
|
|
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_reset
|
|
*/
|
|
SkPath& reset();
|
|
|
|
/** Sets SkPath to its initial state, preserving internal storage.
|
|
Removes verb array, SkPoint array, and weights, and sets FillType to kWinding.
|
|
Internal storage associated with SkPath is retained.
|
|
|
|
Use rewind() instead of reset() if SkPath storage will be reused and performance
|
|
is critical.
|
|
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_rewind
|
|
*/
|
|
SkPath& rewind();
|
|
|
|
/** Returns if SkPath is empty.
|
|
Empty SkPath may have FillType but has no SkPoint, SkPath::Verb, or conic weight.
|
|
SkPath() constructs empty SkPath; reset() and rewind() make SkPath empty.
|
|
|
|
@return true if the path contains no SkPath::Verb array
|
|
*/
|
|
bool isEmpty() const;
|
|
|
|
/** Returns if contour is closed.
|
|
Contour is closed if SkPath SkPath::Verb array was last modified by close(). When stroked,
|
|
closed contour draws SkPaint::Join instead of SkPaint::Cap at first and last SkPoint.
|
|
|
|
@return true if the last contour ends with a kClose_Verb
|
|
|
|
example: https://fiddle.skia.org/c/@Path_isLastContourClosed
|
|
*/
|
|
bool isLastContourClosed() const;
|
|
|
|
/** Returns true for finite SkPoint array values between negative SK_ScalarMax and
|
|
positive SK_ScalarMax. Returns false for any SkPoint array value of
|
|
SK_ScalarInfinity, SK_ScalarNegativeInfinity, or SK_ScalarNaN.
|
|
|
|
@return true if all SkPoint values are finite
|
|
*/
|
|
bool isFinite() const;
|
|
|
|
/** Returns true if the path is volatile; it will not be altered or discarded
|
|
by the caller after it is drawn. SkPath by default have volatile set false, allowing
|
|
SkSurface to attach a cache of data which speeds repeated drawing. If true, SkSurface
|
|
may not speed repeated drawing.
|
|
|
|
@return true if caller will alter SkPath after drawing
|
|
*/
|
|
bool isVolatile() const {
|
|
return SkToBool(fIsVolatile);
|
|
}
|
|
|
|
/** Specifies whether SkPath is volatile; whether it will be altered or discarded
|
|
by the caller after it is drawn. SkPath by default have volatile set false, allowing
|
|
Skia to attach a cache of data which speeds repeated drawing.
|
|
|
|
Mark temporary paths, discarded or modified after use, as volatile
|
|
to inform Skia that the path need not be cached.
|
|
|
|
Mark animating SkPath volatile to improve performance.
|
|
Mark unchanging SkPath non-volatile to improve repeated rendering.
|
|
|
|
raster surface SkPath draws are affected by volatile for some shadows.
|
|
GPU surface SkPath draws are affected by volatile for some shadows and concave geometries.
|
|
|
|
@param isVolatile true if caller will alter SkPath after drawing
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& setIsVolatile(bool isVolatile) {
|
|
fIsVolatile = isVolatile;
|
|
return *this;
|
|
}
|
|
|
|
/** Tests if line between SkPoint pair is degenerate.
|
|
Line with no length or that moves a very short distance is degenerate; it is
|
|
treated as a point.
|
|
|
|
exact changes the equality test. If true, returns true only if p1 equals p2.
|
|
If false, returns true if p1 equals or nearly equals p2.
|
|
|
|
@param p1 line start point
|
|
@param p2 line end point
|
|
@param exact if false, allow nearly equals
|
|
@return true if line is degenerate; its length is effectively zero
|
|
|
|
example: https://fiddle.skia.org/c/@Path_IsLineDegenerate
|
|
*/
|
|
static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact);
|
|
|
|
/** Tests if quad is degenerate.
|
|
Quad with no length or that moves a very short distance is degenerate; it is
|
|
treated as a point.
|
|
|
|
@param p1 quad start point
|
|
@param p2 quad control point
|
|
@param p3 quad end point
|
|
@param exact if true, returns true only if p1, p2, and p3 are equal;
|
|
if false, returns true if p1, p2, and p3 are equal or nearly equal
|
|
@return true if quad is degenerate; its length is effectively zero
|
|
*/
|
|
static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
|
|
const SkPoint& p3, bool exact);
|
|
|
|
/** Tests if cubic is degenerate.
|
|
Cubic with no length or that moves a very short distance is degenerate; it is
|
|
treated as a point.
|
|
|
|
@param p1 cubic start point
|
|
@param p2 cubic control point 1
|
|
@param p3 cubic control point 2
|
|
@param p4 cubic end point
|
|
@param exact if true, returns true only if p1, p2, p3, and p4 are equal;
|
|
if false, returns true if p1, p2, p3, and p4 are equal or nearly equal
|
|
@return true if cubic is degenerate; its length is effectively zero
|
|
*/
|
|
static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
|
|
const SkPoint& p3, const SkPoint& p4, bool exact);
|
|
|
|
/** Returns true if SkPath contains only one line;
|
|
SkPath::Verb array has two entries: kMove_Verb, kLine_Verb.
|
|
If SkPath contains one line and line is not nullptr, line is set to
|
|
line start point and line end point.
|
|
Returns false if SkPath is not one line; line is unaltered.
|
|
|
|
@param line storage for line. May be nullptr
|
|
@return true if SkPath contains exactly one line
|
|
|
|
example: https://fiddle.skia.org/c/@Path_isLine
|
|
*/
|
|
bool isLine(SkPoint line[2]) const;
|
|
|
|
/** Returns the number of points in SkPath.
|
|
SkPoint count is initially zero.
|
|
|
|
@return SkPath SkPoint array length
|
|
|
|
example: https://fiddle.skia.org/c/@Path_countPoints
|
|
*/
|
|
int countPoints() const;
|
|
|
|
/** Returns SkPoint at index in SkPoint array. Valid range for index is
|
|
0 to countPoints() - 1.
|
|
Returns (0, 0) if index is out of range.
|
|
|
|
@param index SkPoint array element selector
|
|
@return SkPoint array value or (0, 0)
|
|
|
|
example: https://fiddle.skia.org/c/@Path_getPoint
|
|
*/
|
|
SkPoint getPoint(int index) const;
|
|
|
|
/** Returns number of points in SkPath. Up to max points are copied.
|
|
points may be nullptr; then, max must be zero.
|
|
If max is greater than number of points, excess points storage is unaltered.
|
|
|
|
@param points storage for SkPath SkPoint array. May be nullptr
|
|
@param max maximum to copy; must be greater than or equal to zero
|
|
@return SkPath SkPoint array length
|
|
|
|
example: https://fiddle.skia.org/c/@Path_getPoints
|
|
*/
|
|
int getPoints(SkPoint points[], int max) const;
|
|
|
|
/** Returns the number of verbs: kMove_Verb, kLine_Verb, kQuad_Verb, kConic_Verb,
|
|
kCubic_Verb, and kClose_Verb; added to SkPath.
|
|
|
|
@return length of verb array
|
|
|
|
example: https://fiddle.skia.org/c/@Path_countVerbs
|
|
*/
|
|
int countVerbs() const;
|
|
|
|
/** Returns the number of verbs in the path. Up to max verbs are copied. The
|
|
verbs are copied as one byte per verb.
|
|
|
|
@param verbs storage for verbs, may be nullptr
|
|
@param max maximum number to copy into verbs
|
|
@return the actual number of verbs in the path
|
|
|
|
example: https://fiddle.skia.org/c/@Path_getVerbs
|
|
*/
|
|
int getVerbs(uint8_t verbs[], int max) const;
|
|
|
|
/** Returns the approximate byte size of the SkPath in memory.
|
|
|
|
@return approximate size
|
|
*/
|
|
size_t approximateBytesUsed() const;
|
|
|
|
/** Exchanges the verb array, SkPoint array, weights, and SkPath::FillType with other.
|
|
Cached state is also exchanged. swap() internally exchanges pointers, so
|
|
it is lightweight and does not allocate memory.
|
|
|
|
swap() usage has largely been replaced by operator=(const SkPath& path).
|
|
SkPath do not copy their content on assignment until they are written to,
|
|
making assignment as efficient as swap().
|
|
|
|
@param other SkPath exchanged by value
|
|
|
|
example: https://fiddle.skia.org/c/@Path_swap
|
|
*/
|
|
void swap(SkPath& other);
|
|
|
|
/** Returns minimum and maximum axes values of SkPoint array.
|
|
Returns (0, 0, 0, 0) if SkPath contains no points. Returned bounds width and height may
|
|
be larger or smaller than area affected when SkPath is drawn.
|
|
|
|
SkRect returned includes all SkPoint added to SkPath, including SkPoint associated with
|
|
kMove_Verb that define empty contours.
|
|
|
|
@return bounds of all SkPoint in SkPoint array
|
|
*/
|
|
const SkRect& getBounds() const;
|
|
|
|
/** Updates internal bounds so that subsequent calls to getBounds() are instantaneous.
|
|
Unaltered copies of SkPath may also access cached bounds through getBounds().
|
|
|
|
For now, identical to calling getBounds() and ignoring the returned value.
|
|
|
|
Call to prepare SkPath subsequently drawn from multiple threads,
|
|
to avoid a race condition where each draw separately computes the bounds.
|
|
*/
|
|
void updateBoundsCache() const {
|
|
// for now, just calling getBounds() is sufficient
|
|
this->getBounds();
|
|
}
|
|
|
|
/** Returns minimum and maximum axes values of the lines and curves in SkPath.
|
|
Returns (0, 0, 0, 0) if SkPath contains no points.
|
|
Returned bounds width and height may be larger or smaller than area affected
|
|
when SkPath is drawn.
|
|
|
|
Includes SkPoint associated with kMove_Verb that define empty
|
|
contours.
|
|
|
|
Behaves identically to getBounds() when SkPath contains
|
|
only lines. If SkPath contains curves, computed bounds includes
|
|
the maximum extent of the quad, conic, or cubic; is slower than getBounds();
|
|
and unlike getBounds(), does not cache the result.
|
|
|
|
@return tight bounds of curves in SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_computeTightBounds
|
|
*/
|
|
SkRect computeTightBounds() const;
|
|
|
|
/** Returns true if rect is contained by SkPath.
|
|
May return false when rect is contained by SkPath.
|
|
|
|
For now, only returns true if SkPath has one contour and is convex.
|
|
rect may share points and edges with SkPath and be contained.
|
|
Returns true if rect is empty, that is, it has zero width or height; and
|
|
the SkPoint or line described by rect is contained by SkPath.
|
|
|
|
@param rect SkRect, line, or SkPoint checked for containment
|
|
@return true if rect is contained
|
|
|
|
example: https://fiddle.skia.org/c/@Path_conservativelyContainsRect
|
|
*/
|
|
bool conservativelyContainsRect(const SkRect& rect) const;
|
|
|
|
/** Grows SkPath verb array and SkPoint array to contain extraPtCount additional SkPoint.
|
|
May improve performance and use less memory by
|
|
reducing the number and size of allocations when creating SkPath.
|
|
|
|
@param extraPtCount number of additional SkPoint to allocate
|
|
|
|
example: https://fiddle.skia.org/c/@Path_incReserve
|
|
*/
|
|
void incReserve(int extraPtCount);
|
|
|
|
#ifdef SK_HIDE_PATH_EDIT_METHODS
|
|
private:
|
|
#endif
|
|
|
|
/** Adds beginning of contour at SkPoint (x, y).
|
|
|
|
@param x x-axis value of contour start
|
|
@param y y-axis value of contour start
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_moveTo
|
|
*/
|
|
SkPath& moveTo(SkScalar x, SkScalar y);
|
|
|
|
/** Adds beginning of contour at SkPoint p.
|
|
|
|
@param p contour start
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& moveTo(const SkPoint& p) {
|
|
return this->moveTo(p.fX, p.fY);
|
|
}
|
|
|
|
/** Adds beginning of contour relative to last point.
|
|
If SkPath is empty, starts contour at (dx, dy).
|
|
Otherwise, start contour at last point offset by (dx, dy).
|
|
Function name stands for "relative move to".
|
|
|
|
@param dx offset from last point to contour start on x-axis
|
|
@param dy offset from last point to contour start on y-axis
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_rMoveTo
|
|
*/
|
|
SkPath& rMoveTo(SkScalar dx, SkScalar dy);
|
|
|
|
/** Adds line from last point to (x, y). If SkPath is empty, or last SkPath::Verb is
|
|
kClose_Verb, last point is set to (0, 0) before adding line.
|
|
|
|
lineTo() appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
|
|
lineTo() then appends kLine_Verb to verb array and (x, y) to SkPoint array.
|
|
|
|
@param x end of added line on x-axis
|
|
@param y end of added line on y-axis
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_lineTo
|
|
*/
|
|
SkPath& lineTo(SkScalar x, SkScalar y);
|
|
|
|
/** Adds line from last point to SkPoint p. If SkPath is empty, or last SkPath::Verb is
|
|
kClose_Verb, last point is set to (0, 0) before adding line.
|
|
|
|
lineTo() first appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
|
|
lineTo() then appends kLine_Verb to verb array and SkPoint p to SkPoint array.
|
|
|
|
@param p end SkPoint of added line
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& lineTo(const SkPoint& p) {
|
|
return this->lineTo(p.fX, p.fY);
|
|
}
|
|
|
|
/** Adds line from last point to vector (dx, dy). If SkPath is empty, or last SkPath::Verb is
|
|
kClose_Verb, last point is set to (0, 0) before adding line.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
|
|
then appends kLine_Verb to verb array and line end to SkPoint array.
|
|
Line end is last point plus vector (dx, dy).
|
|
Function name stands for "relative line to".
|
|
|
|
@param dx offset from last point to line end on x-axis
|
|
@param dy offset from last point to line end on y-axis
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_rLineTo
|
|
example: https://fiddle.skia.org/c/@Quad_a
|
|
example: https://fiddle.skia.org/c/@Quad_b
|
|
*/
|
|
SkPath& rLineTo(SkScalar dx, SkScalar dy);
|
|
|
|
/** Adds quad from last point towards (x1, y1), to (x2, y2).
|
|
If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
|
|
before adding quad.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
|
|
then appends kQuad_Verb to verb array; and (x1, y1), (x2, y2)
|
|
to SkPoint array.
|
|
|
|
@param x1 control SkPoint of quad on x-axis
|
|
@param y1 control SkPoint of quad on y-axis
|
|
@param x2 end SkPoint of quad on x-axis
|
|
@param y2 end SkPoint of quad on y-axis
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_quadTo
|
|
*/
|
|
SkPath& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
|
|
|
|
/** Adds quad from last point towards SkPoint p1, to SkPoint p2.
|
|
If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
|
|
before adding quad.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
|
|
then appends kQuad_Verb to verb array; and SkPoint p1, p2
|
|
to SkPoint array.
|
|
|
|
@param p1 control SkPoint of added quad
|
|
@param p2 end SkPoint of added quad
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& quadTo(const SkPoint& p1, const SkPoint& p2) {
|
|
return this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
|
|
}
|
|
|
|
/** Adds quad from last point towards vector (dx1, dy1), to vector (dx2, dy2).
|
|
If SkPath is empty, or last SkPath::Verb
|
|
is kClose_Verb, last point is set to (0, 0) before adding quad.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array,
|
|
if needed; then appends kQuad_Verb to verb array; and appends quad
|
|
control and quad end to SkPoint array.
|
|
Quad control is last point plus vector (dx1, dy1).
|
|
Quad end is last point plus vector (dx2, dy2).
|
|
Function name stands for "relative quad to".
|
|
|
|
@param dx1 offset from last point to quad control on x-axis
|
|
@param dy1 offset from last point to quad control on y-axis
|
|
@param dx2 offset from last point to quad end on x-axis
|
|
@param dy2 offset from last point to quad end on y-axis
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Conic_Weight_a
|
|
example: https://fiddle.skia.org/c/@Conic_Weight_b
|
|
example: https://fiddle.skia.org/c/@Conic_Weight_c
|
|
example: https://fiddle.skia.org/c/@Path_rQuadTo
|
|
*/
|
|
SkPath& rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
|
|
|
|
/** Adds conic from last point towards (x1, y1), to (x2, y2), weighted by w.
|
|
If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
|
|
before adding conic.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
|
|
|
|
If w is finite and not one, appends kConic_Verb to verb array;
|
|
and (x1, y1), (x2, y2) to SkPoint array; and w to conic weights.
|
|
|
|
If w is one, appends kQuad_Verb to verb array, and
|
|
(x1, y1), (x2, y2) to SkPoint array.
|
|
|
|
If w is not finite, appends kLine_Verb twice to verb array, and
|
|
(x1, y1), (x2, y2) to SkPoint array.
|
|
|
|
@param x1 control SkPoint of conic on x-axis
|
|
@param y1 control SkPoint of conic on y-axis
|
|
@param x2 end SkPoint of conic on x-axis
|
|
@param y2 end SkPoint of conic on y-axis
|
|
@param w weight of added conic
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
|
SkScalar w);
|
|
|
|
/** Adds conic from last point towards SkPoint p1, to SkPoint p2, weighted by w.
|
|
If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
|
|
before adding conic.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
|
|
|
|
If w is finite and not one, appends kConic_Verb to verb array;
|
|
and SkPoint p1, p2 to SkPoint array; and w to conic weights.
|
|
|
|
If w is one, appends kQuad_Verb to verb array, and SkPoint p1, p2
|
|
to SkPoint array.
|
|
|
|
If w is not finite, appends kLine_Verb twice to verb array, and
|
|
SkPoint p1, p2 to SkPoint array.
|
|
|
|
@param p1 control SkPoint of added conic
|
|
@param p2 end SkPoint of added conic
|
|
@param w weight of added conic
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) {
|
|
return this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w);
|
|
}
|
|
|
|
/** Adds conic from last point towards vector (dx1, dy1), to vector (dx2, dy2),
|
|
weighted by w. If SkPath is empty, or last SkPath::Verb
|
|
is kClose_Verb, last point is set to (0, 0) before adding conic.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array,
|
|
if needed.
|
|
|
|
If w is finite and not one, next appends kConic_Verb to verb array,
|
|
and w is recorded as conic weight; otherwise, if w is one, appends
|
|
kQuad_Verb to verb array; or if w is not finite, appends kLine_Verb
|
|
twice to verb array.
|
|
|
|
In all cases appends SkPoint control and end to SkPoint array.
|
|
control is last point plus vector (dx1, dy1).
|
|
end is last point plus vector (dx2, dy2).
|
|
|
|
Function name stands for "relative conic to".
|
|
|
|
@param dx1 offset from last point to conic control on x-axis
|
|
@param dy1 offset from last point to conic control on y-axis
|
|
@param dx2 offset from last point to conic end on x-axis
|
|
@param dy2 offset from last point to conic end on y-axis
|
|
@param w weight of added conic
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
|
|
SkScalar w);
|
|
|
|
/** Adds cubic from last point towards (x1, y1), then towards (x2, y2), ending at
|
|
(x3, y3). If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to
|
|
(0, 0) before adding cubic.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
|
|
then appends kCubic_Verb to verb array; and (x1, y1), (x2, y2), (x3, y3)
|
|
to SkPoint array.
|
|
|
|
@param x1 first control SkPoint of cubic on x-axis
|
|
@param y1 first control SkPoint of cubic on y-axis
|
|
@param x2 second control SkPoint of cubic on x-axis
|
|
@param y2 second control SkPoint of cubic on y-axis
|
|
@param x3 end SkPoint of cubic on x-axis
|
|
@param y3 end SkPoint of cubic on y-axis
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
|
SkScalar x3, SkScalar y3);
|
|
|
|
/** Adds cubic from last point towards SkPoint p1, then towards SkPoint p2, ending at
|
|
SkPoint p3. If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to
|
|
(0, 0) before adding cubic.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
|
|
then appends kCubic_Verb to verb array; and SkPoint p1, p2, p3
|
|
to SkPoint array.
|
|
|
|
@param p1 first control SkPoint of cubic
|
|
@param p2 second control SkPoint of cubic
|
|
@param p3 end SkPoint of cubic
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
|
|
return this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
|
|
}
|
|
|
|
/** Adds cubic from last point towards vector (dx1, dy1), then towards
|
|
vector (dx2, dy2), to vector (dx3, dy3).
|
|
If SkPath is empty, or last SkPath::Verb
|
|
is kClose_Verb, last point is set to (0, 0) before adding cubic.
|
|
|
|
Appends kMove_Verb to verb array and (0, 0) to SkPoint array,
|
|
if needed; then appends kCubic_Verb to verb array; and appends cubic
|
|
control and cubic end to SkPoint array.
|
|
Cubic control is last point plus vector (dx1, dy1).
|
|
Cubic end is last point plus vector (dx2, dy2).
|
|
Function name stands for "relative cubic to".
|
|
|
|
@param dx1 offset from last point to first cubic control on x-axis
|
|
@param dy1 offset from last point to first cubic control on y-axis
|
|
@param dx2 offset from last point to second cubic control on x-axis
|
|
@param dy2 offset from last point to second cubic control on y-axis
|
|
@param dx3 offset from last point to cubic end on x-axis
|
|
@param dy3 offset from last point to cubic end on y-axis
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& rCubicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
|
|
SkScalar dx3, SkScalar dy3);
|
|
|
|
/** Appends arc to SkPath. Arc added is part of ellipse
|
|
bounded by oval, from startAngle through sweepAngle. Both startAngle and
|
|
sweepAngle are measured in degrees, where zero degrees is aligned with the
|
|
positive x-axis, and positive sweeps extends arc clockwise.
|
|
|
|
arcTo() adds line connecting SkPath last SkPoint to initial arc SkPoint if forceMoveTo
|
|
is false and SkPath is not empty. Otherwise, added contour begins with first point
|
|
of arc. Angles greater than -360 and less than 360 are treated modulo 360.
|
|
|
|
@param oval bounds of ellipse containing arc
|
|
@param startAngle starting angle of arc in degrees
|
|
@param sweepAngle sweep, in degrees. Positive is clockwise; treated modulo 360
|
|
@param forceMoveTo true to start a new contour with arc
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_arcTo
|
|
*/
|
|
SkPath& arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo);
|
|
|
|
/** Appends arc to SkPath, after appending line if needed. Arc is implemented by conic
|
|
weighted to describe part of circle. Arc is contained by tangent from
|
|
last SkPath point to (x1, y1), and tangent from (x1, y1) to (x2, y2). Arc
|
|
is part of circle sized to radius, positioned so it touches both tangent lines.
|
|
|
|
If last Path Point does not start Arc, arcTo appends connecting Line to Path.
|
|
The length of Vector from (x1, y1) to (x2, y2) does not affect Arc.
|
|
|
|
Arc sweep is always less than 180 degrees. If radius is zero, or if
|
|
tangents are nearly parallel, arcTo appends Line from last Path Point to (x1, y1).
|
|
|
|
arcTo appends at most one Line and one conic.
|
|
arcTo implements the functionality of PostScript arct and HTML Canvas arcTo.
|
|
|
|
@param x1 x-axis value common to pair of tangents
|
|
@param y1 y-axis value common to pair of tangents
|
|
@param x2 x-axis value end of second tangent
|
|
@param y2 y-axis value end of second tangent
|
|
@param radius distance from arc to circle center
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_arcTo_2_a
|
|
example: https://fiddle.skia.org/c/@Path_arcTo_2_b
|
|
example: https://fiddle.skia.org/c/@Path_arcTo_2_c
|
|
*/
|
|
SkPath& arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius);
|
|
|
|
/** Appends arc to SkPath, after appending line if needed. Arc is implemented by conic
|
|
weighted to describe part of circle. Arc is contained by tangent from
|
|
last SkPath point to p1, and tangent from p1 to p2. Arc
|
|
is part of circle sized to radius, positioned so it touches both tangent lines.
|
|
|
|
If last SkPath SkPoint does not start arc, arcTo() appends connecting line to SkPath.
|
|
The length of vector from p1 to p2 does not affect arc.
|
|
|
|
Arc sweep is always less than 180 degrees. If radius is zero, or if
|
|
tangents are nearly parallel, arcTo() appends line from last SkPath SkPoint to p1.
|
|
|
|
arcTo() appends at most one line and one conic.
|
|
arcTo() implements the functionality of PostScript arct and HTML Canvas arcTo.
|
|
|
|
@param p1 SkPoint common to pair of tangents
|
|
@param p2 end of second tangent
|
|
@param radius distance from arc to circle center
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
|
|
return this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
|
|
}
|
|
|
|
/** \enum SkPath::ArcSize
|
|
Four oval parts with radii (rx, ry) start at last SkPath SkPoint and ends at (x, y).
|
|
ArcSize and Direction select one of the four oval parts.
|
|
*/
|
|
enum ArcSize {
|
|
kSmall_ArcSize, //!< smaller of arc pair
|
|
kLarge_ArcSize, //!< larger of arc pair
|
|
};
|
|
|
|
/** Appends arc to SkPath. Arc is implemented by one or more conics weighted to
|
|
describe part of oval with radii (rx, ry) rotated by xAxisRotate degrees. Arc
|
|
curves from last SkPath SkPoint to (x, y), choosing one of four possible routes:
|
|
clockwise or counterclockwise, and smaller or larger.
|
|
|
|
Arc sweep is always less than 360 degrees. arcTo() appends line to (x, y) if
|
|
either radii are zero, or if last SkPath SkPoint equals (x, y). arcTo() scales radii
|
|
(rx, ry) to fit last SkPath SkPoint and (x, y) if both are greater than zero but
|
|
too small.
|
|
|
|
arcTo() appends up to four conic curves.
|
|
arcTo() implements the functionality of SVG arc, although SVG sweep-flag value
|
|
is opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise,
|
|
while kCW_Direction cast to int is zero.
|
|
|
|
@param rx radius on x-axis before x-axis rotation
|
|
@param ry radius on y-axis before x-axis rotation
|
|
@param xAxisRotate x-axis rotation in degrees; positive values are clockwise
|
|
@param largeArc chooses smaller or larger arc
|
|
@param sweep chooses clockwise or counterclockwise arc
|
|
@param x end of arc
|
|
@param y end of arc
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
|
|
SkPathDirection sweep, SkScalar x, SkScalar y);
|
|
|
|
/** Appends arc to SkPath. Arc is implemented by one or more conic weighted to describe
|
|
part of oval with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves
|
|
from last SkPath SkPoint to (xy.fX, xy.fY), choosing one of four possible routes:
|
|
clockwise or counterclockwise,
|
|
and smaller or larger.
|
|
|
|
Arc sweep is always less than 360 degrees. arcTo() appends line to xy if either
|
|
radii are zero, or if last SkPath SkPoint equals (xy.fX, xy.fY). arcTo() scales radii r to
|
|
fit last SkPath SkPoint and xy if both are greater than zero but too small to describe
|
|
an arc.
|
|
|
|
arcTo() appends up to four conic curves.
|
|
arcTo() implements the functionality of SVG arc, although SVG sweep-flag value is
|
|
opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise, while
|
|
kCW_Direction cast to int is zero.
|
|
|
|
@param r radii on axes before x-axis rotation
|
|
@param xAxisRotate x-axis rotation in degrees; positive values are clockwise
|
|
@param largeArc chooses smaller or larger arc
|
|
@param sweep chooses clockwise or counterclockwise arc
|
|
@param xy end of arc
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, SkPathDirection sweep,
|
|
const SkPoint xy) {
|
|
return this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY);
|
|
}
|
|
|
|
/** Appends arc to SkPath, relative to last SkPath SkPoint. Arc is implemented by one or
|
|
more conic, weighted to describe part of oval with radii (rx, ry) rotated by
|
|
xAxisRotate degrees. Arc curves from last SkPath SkPoint to relative end SkPoint:
|
|
(dx, dy), choosing one of four possible routes: clockwise or
|
|
counterclockwise, and smaller or larger. If SkPath is empty, the start arc SkPoint
|
|
is (0, 0).
|
|
|
|
Arc sweep is always less than 360 degrees. arcTo() appends line to end SkPoint
|
|
if either radii are zero, or if last SkPath SkPoint equals end SkPoint.
|
|
arcTo() scales radii (rx, ry) to fit last SkPath SkPoint and end SkPoint if both are
|
|
greater than zero but too small to describe an arc.
|
|
|
|
arcTo() appends up to four conic curves.
|
|
arcTo() implements the functionality of svg arc, although SVG "sweep-flag" value is
|
|
opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while
|
|
kCW_Direction cast to int is zero.
|
|
|
|
@param rx radius before x-axis rotation
|
|
@param ry radius before x-axis rotation
|
|
@param xAxisRotate x-axis rotation in degrees; positive values are clockwise
|
|
@param largeArc chooses smaller or larger arc
|
|
@param sweep chooses clockwise or counterclockwise arc
|
|
@param dx x-axis offset end of arc from last SkPath SkPoint
|
|
@param dy y-axis offset end of arc from last SkPath SkPoint
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
|
|
SkPathDirection sweep, SkScalar dx, SkScalar dy);
|
|
|
|
/** Appends kClose_Verb to SkPath. A closed contour connects the first and last SkPoint
|
|
with line, forming a continuous loop. Open and closed contour draw the same
|
|
with SkPaint::kFill_Style. With SkPaint::kStroke_Style, open contour draws
|
|
SkPaint::Cap at contour start and end; closed contour draws
|
|
SkPaint::Join at contour start and end.
|
|
|
|
close() has no effect if SkPath is empty or last SkPath SkPath::Verb is kClose_Verb.
|
|
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_close
|
|
*/
|
|
SkPath& close();
|
|
|
|
#ifdef SK_HIDE_PATH_EDIT_METHODS
|
|
public:
|
|
#endif
|
|
|
|
/** Approximates conic with quad array. Conic is constructed from start SkPoint p0,
|
|
control SkPoint p1, end SkPoint p2, and weight w.
|
|
Quad array is stored in pts; this storage is supplied by caller.
|
|
Maximum quad count is 2 to the pow2.
|
|
Every third point in array shares last SkPoint of previous quad and first SkPoint of
|
|
next quad. Maximum pts storage size is given by:
|
|
(1 + 2 * (1 << pow2)) * sizeof(SkPoint).
|
|
|
|
Returns quad count used the approximation, which may be smaller
|
|
than the number requested.
|
|
|
|
conic weight determines the amount of influence conic control point has on the curve.
|
|
w less than one represents an elliptical section. w greater than one represents
|
|
a hyperbolic section. w equal to one represents a parabolic section.
|
|
|
|
Two quad curves are sufficient to approximate an elliptical conic with a sweep
|
|
of up to 90 degrees; in this case, set pow2 to one.
|
|
|
|
@param p0 conic start SkPoint
|
|
@param p1 conic control SkPoint
|
|
@param p2 conic end SkPoint
|
|
@param w conic weight
|
|
@param pts storage for quad array
|
|
@param pow2 quad count, as power of two, normally 0 to 5 (1 to 32 quad curves)
|
|
@return number of quad curves written to pts
|
|
*/
|
|
static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
|
|
SkScalar w, SkPoint pts[], int pow2);
|
|
|
|
/** Returns true if SkPath is equivalent to SkRect when filled.
|
|
If false: rect, isClosed, and direction are unchanged.
|
|
If true: rect, isClosed, and direction are written to if not nullptr.
|
|
|
|
rect may be smaller than the SkPath bounds. SkPath bounds may include kMove_Verb points
|
|
that do not alter the area drawn by the returned rect.
|
|
|
|
@param rect storage for bounds of SkRect; may be nullptr
|
|
@param isClosed storage set to true if SkPath is closed; may be nullptr
|
|
@param direction storage set to SkRect direction; may be nullptr
|
|
@return true if SkPath contains SkRect
|
|
|
|
example: https://fiddle.skia.org/c/@Path_isRect
|
|
*/
|
|
bool isRect(SkRect* rect, bool* isClosed = nullptr, SkPathDirection* direction = nullptr) const;
|
|
|
|
#ifdef SK_HIDE_PATH_EDIT_METHODS
|
|
private:
|
|
#endif
|
|
|
|
/** Adds a new contour to the path, defined by the rect, and wound in the
|
|
specified direction. The verbs added to the path will be:
|
|
|
|
kMove, kLine, kLine, kLine, kClose
|
|
|
|
start specifies which corner to begin the contour:
|
|
0: upper-left corner
|
|
1: upper-right corner
|
|
2: lower-right corner
|
|
3: lower-left corner
|
|
|
|
This start point also acts as the implied beginning of the subsequent,
|
|
contour, if it does not have an explicit moveTo(). e.g.
|
|
|
|
path.addRect(...)
|
|
// if we don't say moveTo() here, we will use the rect's start point
|
|
path.lineTo(...)
|
|
|
|
@param rect SkRect to add as a closed contour
|
|
@param dir SkPath::Direction to orient the new contour
|
|
@param start initial corner of SkRect to add
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_addRect_2
|
|
*/
|
|
SkPath& addRect(const SkRect& rect, SkPathDirection dir, unsigned start);
|
|
|
|
SkPath& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) {
|
|
return this->addRect(rect, dir, 0);
|
|
}
|
|
|
|
SkPath& addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
|
|
SkPathDirection dir = SkPathDirection::kCW) {
|
|
return this->addRect({left, top, right, bottom}, dir, 0);
|
|
}
|
|
|
|
/** Adds oval to path, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
|
|
Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width
|
|
and half oval height. Oval begins at (oval.fRight, oval.centerY()) and continues
|
|
clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction.
|
|
|
|
@param oval bounds of ellipse added
|
|
@param dir SkPath::Direction to wind ellipse
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_addOval
|
|
*/
|
|
SkPath& addOval(const SkRect& oval, SkPathDirection dir = SkPathDirection::kCW);
|
|
|
|
/** Adds oval to SkPath, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
|
|
Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width
|
|
and half oval height. Oval begins at start and continues
|
|
clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction.
|
|
|
|
@param oval bounds of ellipse added
|
|
@param dir SkPath::Direction to wind ellipse
|
|
@param start index of initial point of ellipse
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_addOval_2
|
|
*/
|
|
SkPath& addOval(const SkRect& oval, SkPathDirection dir, unsigned start);
|
|
|
|
/** Adds circle centered at (x, y) of size radius to SkPath, appending kMove_Verb,
|
|
four kConic_Verb, and kClose_Verb. Circle begins at: (x + radius, y), continuing
|
|
clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction.
|
|
|
|
Has no effect if radius is zero or negative.
|
|
|
|
@param x center of circle
|
|
@param y center of circle
|
|
@param radius distance from center to edge
|
|
@param dir SkPath::Direction to wind circle
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius,
|
|
SkPathDirection dir = SkPathDirection::kCW);
|
|
|
|
/** Appends arc to SkPath, as the start of new contour. Arc added is part of ellipse
|
|
bounded by oval, from startAngle through sweepAngle. Both startAngle and
|
|
sweepAngle are measured in degrees, where zero degrees is aligned with the
|
|
positive x-axis, and positive sweeps extends arc clockwise.
|
|
|
|
If sweepAngle <= -360, or sweepAngle >= 360; and startAngle modulo 90 is nearly
|
|
zero, append oval instead of arc. Otherwise, sweepAngle values are treated
|
|
modulo 360, and arc may or may not draw depending on numeric rounding.
|
|
|
|
@param oval bounds of ellipse containing arc
|
|
@param startAngle starting angle of arc in degrees
|
|
@param sweepAngle sweep, in degrees. Positive is clockwise; treated modulo 360
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_addArc
|
|
*/
|
|
SkPath& addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
|
|
|
|
/** Appends SkRRect to SkPath, creating a new closed contour. SkRRect has bounds
|
|
equal to rect; each corner is 90 degrees of an ellipse with radii (rx, ry). If
|
|
dir is kCW_Direction, SkRRect starts at top-left of the lower-left corner and
|
|
winds clockwise. If dir is kCCW_Direction, SkRRect starts at the bottom-left
|
|
of the upper-left corner and winds counterclockwise.
|
|
|
|
If either rx or ry is too large, rx and ry are scaled uniformly until the
|
|
corners fit. If rx or ry is less than or equal to zero, addRoundRect() appends
|
|
SkRect rect to SkPath.
|
|
|
|
After appending, SkPath may be empty, or may contain: SkRect, oval, or SkRRect.
|
|
|
|
@param rect bounds of SkRRect
|
|
@param rx x-axis radius of rounded corners on the SkRRect
|
|
@param ry y-axis radius of rounded corners on the SkRRect
|
|
@param dir SkPath::Direction to wind SkRRect
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
|
|
SkPathDirection dir = SkPathDirection::kCW);
|
|
|
|
/** Appends SkRRect to SkPath, creating a new closed contour. SkRRect has bounds
|
|
equal to rect; each corner is 90 degrees of an ellipse with radii from the
|
|
array.
|
|
|
|
@param rect bounds of SkRRect
|
|
@param radii array of 8 SkScalar values, a radius pair for each corner
|
|
@param dir SkPath::Direction to wind SkRRect
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[],
|
|
SkPathDirection dir = SkPathDirection::kCW);
|
|
|
|
/** Adds rrect to SkPath, creating a new closed contour. If
|
|
dir is kCW_Direction, rrect starts at top-left of the lower-left corner and
|
|
winds clockwise. If dir is kCCW_Direction, rrect starts at the bottom-left
|
|
of the upper-left corner and winds counterclockwise.
|
|
|
|
After appending, SkPath may be empty, or may contain: SkRect, oval, or SkRRect.
|
|
|
|
@param rrect bounds and radii of rounded rectangle
|
|
@param dir SkPath::Direction to wind SkRRect
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_addRRect
|
|
*/
|
|
SkPath& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kCW);
|
|
|
|
/** Adds rrect to SkPath, creating a new closed contour. If dir is kCW_Direction, rrect
|
|
winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise.
|
|
start determines the first point of rrect to add.
|
|
|
|
@param rrect bounds and radii of rounded rectangle
|
|
@param dir SkPath::Direction to wind SkRRect
|
|
@param start index of initial point of SkRRect
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_addRRect_2
|
|
*/
|
|
SkPath& addRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start);
|
|
|
|
/** Adds contour created from line array, adding (count - 1) line segments.
|
|
Contour added starts at pts[0], then adds a line for every additional SkPoint
|
|
in pts array. If close is true, appends kClose_Verb to SkPath, connecting
|
|
pts[count - 1] and pts[0].
|
|
|
|
If count is zero, append kMove_Verb to path.
|
|
Has no effect if count is less than one.
|
|
|
|
@param pts array of line sharing end and start SkPoint
|
|
@param count length of SkPoint array
|
|
@param close true to add line connecting contour end and start
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_addPoly
|
|
*/
|
|
SkPath& addPoly(const SkPoint pts[], int count, bool close);
|
|
|
|
/** Adds contour created from list. Contour added starts at list[0], then adds a line
|
|
for every additional SkPoint in list. If close is true, appends kClose_Verb to SkPath,
|
|
connecting last and first SkPoint in list.
|
|
|
|
If list is empty, append kMove_Verb to path.
|
|
|
|
@param list array of SkPoint
|
|
@param close true to add line connecting contour end and start
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& addPoly(const std::initializer_list<SkPoint>& list, bool close) {
|
|
return this->addPoly(list.begin(), SkToInt(list.size()), close);
|
|
}
|
|
|
|
#ifdef SK_HIDE_PATH_EDIT_METHODS
|
|
public:
|
|
#endif
|
|
|
|
/** \enum SkPath::AddPathMode
|
|
AddPathMode chooses how addPath() appends. Adding one SkPath to another can extend
|
|
the last contour or start a new contour.
|
|
*/
|
|
enum AddPathMode {
|
|
/** Contours are appended to the destination path as new contours.
|
|
*/
|
|
kAppend_AddPathMode,
|
|
/** Extends the last contour of the destination path with the first countour
|
|
of the source path, connecting them with a line. If the last contour is
|
|
closed, a new empty contour starting at its start point is extended instead.
|
|
If the destination path is empty, the result is the source path.
|
|
The last path of the result is closed only if the last path of the source is.
|
|
*/
|
|
kExtend_AddPathMode,
|
|
};
|
|
|
|
/** Appends src to SkPath, offset by (dx, dy).
|
|
|
|
If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are
|
|
added unaltered. If mode is kExtend_AddPathMode, add line before appending
|
|
verbs, SkPoint, and conic weights.
|
|
|
|
@param src SkPath verbs, SkPoint, and conic weights to add
|
|
@param dx offset added to src SkPoint array x-axis coordinates
|
|
@param dy offset added to src SkPoint array y-axis coordinates
|
|
@param mode kAppend_AddPathMode or kExtend_AddPathMode
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& addPath(const SkPath& src, SkScalar dx, SkScalar dy,
|
|
AddPathMode mode = kAppend_AddPathMode);
|
|
|
|
/** Appends src to SkPath.
|
|
|
|
If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are
|
|
added unaltered. If mode is kExtend_AddPathMode, add line before appending
|
|
verbs, SkPoint, and conic weights.
|
|
|
|
@param src SkPath verbs, SkPoint, and conic weights to add
|
|
@param mode kAppend_AddPathMode or kExtend_AddPathMode
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) {
|
|
SkMatrix m;
|
|
m.reset();
|
|
return this->addPath(src, m, mode);
|
|
}
|
|
|
|
/** Appends src to SkPath, transformed by matrix. Transformed curves may have different
|
|
verbs, SkPoint, and conic weights.
|
|
|
|
If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are
|
|
added unaltered. If mode is kExtend_AddPathMode, add line before appending
|
|
verbs, SkPoint, and conic weights.
|
|
|
|
@param src SkPath verbs, SkPoint, and conic weights to add
|
|
@param matrix transform applied to src
|
|
@param mode kAppend_AddPathMode or kExtend_AddPathMode
|
|
@return reference to SkPath
|
|
*/
|
|
SkPath& addPath(const SkPath& src, const SkMatrix& matrix,
|
|
AddPathMode mode = kAppend_AddPathMode);
|
|
|
|
/** Appends src to SkPath, from back to front.
|
|
Reversed src always appends a new contour to SkPath.
|
|
|
|
@param src SkPath verbs, SkPoint, and conic weights to add
|
|
@return reference to SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_reverseAddPath
|
|
*/
|
|
SkPath& reverseAddPath(const SkPath& src);
|
|
|
|
/** Offsets SkPoint array by (dx, dy). Offset SkPath replaces dst.
|
|
If dst is nullptr, SkPath is replaced by offset data.
|
|
|
|
@param dx offset added to SkPoint array x-axis coordinates
|
|
@param dy offset added to SkPoint array y-axis coordinates
|
|
@param dst overwritten, translated copy of SkPath; may be nullptr
|
|
|
|
example: https://fiddle.skia.org/c/@Path_offset
|
|
*/
|
|
void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
|
|
|
|
/** Offsets SkPoint array by (dx, dy). SkPath is replaced by offset data.
|
|
|
|
@param dx offset added to SkPoint array x-axis coordinates
|
|
@param dy offset added to SkPoint array y-axis coordinates
|
|
*/
|
|
void offset(SkScalar dx, SkScalar dy) {
|
|
this->offset(dx, dy, this);
|
|
}
|
|
|
|
/** Transforms verb array, SkPoint array, and weight by matrix.
|
|
transform may change verbs and increase their number.
|
|
Transformed SkPath replaces dst; if dst is nullptr, original data
|
|
is replaced.
|
|
|
|
@param matrix SkMatrix to apply to SkPath
|
|
@param dst overwritten, transformed copy of SkPath; may be nullptr
|
|
@param pc whether to apply perspective clipping
|
|
|
|
example: https://fiddle.skia.org/c/@Path_transform
|
|
*/
|
|
void transform(const SkMatrix& matrix, SkPath* dst,
|
|
SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const;
|
|
|
|
/** Transforms verb array, SkPoint array, and weight by matrix.
|
|
transform may change verbs and increase their number.
|
|
SkPath is replaced by transformed data.
|
|
|
|
@param matrix SkMatrix to apply to SkPath
|
|
@param pc whether to apply perspective clipping
|
|
*/
|
|
void transform(const SkMatrix& matrix,
|
|
SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) {
|
|
this->transform(matrix, this, pc);
|
|
}
|
|
|
|
SkPath makeTransform(const SkMatrix& m,
|
|
SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const {
|
|
SkPath dst;
|
|
this->transform(m, &dst, pc);
|
|
return dst;
|
|
}
|
|
|
|
SkPath makeScale(SkScalar sx, SkScalar sy) {
|
|
return this->makeTransform(SkMatrix::Scale(sx, sy), SkApplyPerspectiveClip::kNo);
|
|
}
|
|
|
|
/** Returns last point on SkPath in lastPt. Returns false if SkPoint array is empty,
|
|
storing (0, 0) if lastPt is not nullptr.
|
|
|
|
@param lastPt storage for final SkPoint in SkPoint array; may be nullptr
|
|
@return true if SkPoint array contains one or more SkPoint
|
|
|
|
example: https://fiddle.skia.org/c/@Path_getLastPt
|
|
*/
|
|
bool getLastPt(SkPoint* lastPt) const;
|
|
|
|
/** Sets last point to (x, y). If SkPoint array is empty, append kMove_Verb to
|
|
verb array and append (x, y) to SkPoint array.
|
|
|
|
@param x set x-axis value of last point
|
|
@param y set y-axis value of last point
|
|
|
|
example: https://fiddle.skia.org/c/@Path_setLastPt
|
|
*/
|
|
void setLastPt(SkScalar x, SkScalar y);
|
|
|
|
/** Sets the last point on the path. If SkPoint array is empty, append kMove_Verb to
|
|
verb array and append p to SkPoint array.
|
|
|
|
@param p set value of last point
|
|
*/
|
|
void setLastPt(const SkPoint& p) {
|
|
this->setLastPt(p.fX, p.fY);
|
|
}
|
|
|
|
/** \enum SkPath::SegmentMask
|
|
SegmentMask constants correspond to each drawing Verb type in SkPath; for
|
|
instance, if SkPath only contains lines, only the kLine_SegmentMask bit is set.
|
|
*/
|
|
enum SegmentMask {
|
|
kLine_SegmentMask = kLine_SkPathSegmentMask,
|
|
kQuad_SegmentMask = kQuad_SkPathSegmentMask,
|
|
kConic_SegmentMask = kConic_SkPathSegmentMask,
|
|
kCubic_SegmentMask = kCubic_SkPathSegmentMask,
|
|
};
|
|
|
|
/** Returns a mask, where each set bit corresponds to a SegmentMask constant
|
|
if SkPath contains one or more verbs of that type.
|
|
Returns zero if SkPath contains no lines, or curves: quads, conics, or cubics.
|
|
|
|
getSegmentMasks() returns a cached result; it is very fast.
|
|
|
|
@return SegmentMask bits or zero
|
|
*/
|
|
uint32_t getSegmentMasks() const;
|
|
|
|
/** \enum SkPath::Verb
|
|
Verb instructs SkPath how to interpret one or more SkPoint and optional conic weight;
|
|
manage contour, and terminate SkPath.
|
|
*/
|
|
enum Verb {
|
|
kMove_Verb = static_cast<int>(SkPathVerb::kMove),
|
|
kLine_Verb = static_cast<int>(SkPathVerb::kLine),
|
|
kQuad_Verb = static_cast<int>(SkPathVerb::kQuad),
|
|
kConic_Verb = static_cast<int>(SkPathVerb::kConic),
|
|
kCubic_Verb = static_cast<int>(SkPathVerb::kCubic),
|
|
kClose_Verb = static_cast<int>(SkPathVerb::kClose),
|
|
kDone_Verb = kClose_Verb + 1
|
|
};
|
|
|
|
/** \class SkPath::Iter
|
|
Iterates through verb array, and associated SkPoint array and conic weight.
|
|
Provides options to treat open contours as closed, and to ignore
|
|
degenerate data.
|
|
*/
|
|
class SK_API Iter {
|
|
public:
|
|
|
|
/** Initializes SkPath::Iter with an empty SkPath. next() on SkPath::Iter returns
|
|
kDone_Verb.
|
|
Call setPath to initialize SkPath::Iter at a later time.
|
|
|
|
@return SkPath::Iter of empty SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_Iter_Iter
|
|
*/
|
|
Iter();
|
|
|
|
/** Sets SkPath::Iter to return elements of verb array, SkPoint array, and conic weight in
|
|
path. If forceClose is true, SkPath::Iter will add kLine_Verb and kClose_Verb after each
|
|
open contour. path is not altered.
|
|
|
|
@param path SkPath to iterate
|
|
@param forceClose true if open contours generate kClose_Verb
|
|
@return SkPath::Iter of path
|
|
|
|
example: https://fiddle.skia.org/c/@Path_Iter_const_SkPath
|
|
*/
|
|
Iter(const SkPath& path, bool forceClose);
|
|
|
|
/** Sets SkPath::Iter to return elements of verb array, SkPoint array, and conic weight in
|
|
path. If forceClose is true, SkPath::Iter will add kLine_Verb and kClose_Verb after each
|
|
open contour. path is not altered.
|
|
|
|
@param path SkPath to iterate
|
|
@param forceClose true if open contours generate kClose_Verb
|
|
|
|
example: https://fiddle.skia.org/c/@Path_Iter_setPath
|
|
*/
|
|
void setPath(const SkPath& path, bool forceClose);
|
|
|
|
/** Returns next SkPath::Verb in verb array, and advances SkPath::Iter.
|
|
When verb array is exhausted, returns kDone_Verb.
|
|
|
|
Zero to four SkPoint are stored in pts, depending on the returned SkPath::Verb.
|
|
|
|
@param pts storage for SkPoint data describing returned SkPath::Verb
|
|
@return next SkPath::Verb from verb array
|
|
|
|
example: https://fiddle.skia.org/c/@Path_RawIter_next
|
|
*/
|
|
Verb next(SkPoint pts[4]);
|
|
|
|
/** Returns conic weight if next() returned kConic_Verb.
|
|
|
|
If next() has not been called, or next() did not return kConic_Verb,
|
|
result is undefined.
|
|
|
|
@return conic weight for conic SkPoint returned by next()
|
|
*/
|
|
SkScalar conicWeight() const { return *fConicWeights; }
|
|
|
|
/** Returns true if last kLine_Verb returned by next() was generated
|
|
by kClose_Verb. When true, the end point returned by next() is
|
|
also the start point of contour.
|
|
|
|
If next() has not been called, or next() did not return kLine_Verb,
|
|
result is undefined.
|
|
|
|
@return true if last kLine_Verb was generated by kClose_Verb
|
|
*/
|
|
bool isCloseLine() const { return SkToBool(fCloseLine); }
|
|
|
|
/** Returns true if subsequent calls to next() return kClose_Verb before returning
|
|
kMove_Verb. if true, contour SkPath::Iter is processing may end with kClose_Verb, or
|
|
SkPath::Iter may have been initialized with force close set to true.
|
|
|
|
@return true if contour is closed
|
|
|
|
example: https://fiddle.skia.org/c/@Path_Iter_isClosedContour
|
|
*/
|
|
bool isClosedContour() const;
|
|
|
|
private:
|
|
const SkPoint* fPts;
|
|
const uint8_t* fVerbs;
|
|
const uint8_t* fVerbStop;
|
|
const SkScalar* fConicWeights;
|
|
SkPoint fMoveTo;
|
|
SkPoint fLastPt;
|
|
bool fForceClose;
|
|
bool fNeedClose;
|
|
bool fCloseLine;
|
|
|
|
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;)
|
|
}
|
|
bool operator!=(const RangeIter& that) const {
|
|
return fVerb != that.fVerb;
|
|
}
|
|
bool operator==(const RangeIter& that) const {
|
|
return fVerb == that.fVerb;
|
|
}
|
|
RangeIter& operator++() {
|
|
auto verb = static_cast<SkPathVerb>(*fVerb++);
|
|
fPoints += pts_advance_after_verb(verb);
|
|
if (verb == SkPathVerb::kConic) {
|
|
++fWeights;
|
|
}
|
|
return *this;
|
|
}
|
|
RangeIter operator++(int) {
|
|
RangeIter copy = *this;
|
|
this->operator++();
|
|
return copy;
|
|
}
|
|
SkPathVerb peekVerb() const {
|
|
return static_cast<SkPathVerb>(*fVerb);
|
|
}
|
|
std::tuple<SkPathVerb, const SkPoint*, const SkScalar*> operator*() const {
|
|
SkPathVerb verb = this->peekVerb();
|
|
// 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;
|
|
}
|
|
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 -1;
|
|
}
|
|
SkUNREACHABLE;
|
|
}
|
|
const uint8_t* fVerb = nullptr;
|
|
const SkPoint* fPoints = nullptr;
|
|
const SkScalar* fWeights = nullptr;
|
|
SkDEBUGCODE(const SkPoint* fInitialPoints = nullptr;)
|
|
};
|
|
public:
|
|
|
|
/** \class SkPath::RawIter
|
|
Use Iter instead. This class will soon be removed and RangeIter will be made private.
|
|
*/
|
|
class SK_API RawIter {
|
|
public:
|
|
|
|
/** Initializes RawIter with an empty SkPath. next() on RawIter returns kDone_Verb.
|
|
Call setPath to initialize SkPath::Iter at a later time.
|
|
|
|
@return RawIter of empty SkPath
|
|
*/
|
|
RawIter() {}
|
|
|
|
/** Sets RawIter to return elements of verb array, SkPoint array, and conic weight in path.
|
|
|
|
@param path SkPath to iterate
|
|
@return RawIter of path
|
|
*/
|
|
RawIter(const SkPath& path) {
|
|
setPath(path);
|
|
}
|
|
|
|
/** Sets SkPath::Iter to return elements of verb array, SkPoint array, and conic weight in
|
|
path.
|
|
|
|
@param path SkPath to iterate
|
|
*/
|
|
void setPath(const SkPath&);
|
|
|
|
/** Returns next SkPath::Verb in verb array, and advances RawIter.
|
|
When verb array is exhausted, returns kDone_Verb.
|
|
Zero to four SkPoint are stored in pts, depending on the returned SkPath::Verb.
|
|
|
|
@param pts storage for SkPoint data describing returned SkPath::Verb
|
|
@return next SkPath::Verb from verb array
|
|
*/
|
|
Verb next(SkPoint[4]);
|
|
|
|
/** Returns next SkPath::Verb, but does not advance RawIter.
|
|
|
|
@return next SkPath::Verb from verb array
|
|
*/
|
|
Verb peek() const {
|
|
return (fIter != fEnd) ? static_cast<Verb>(std::get<0>(*fIter)) : kDone_Verb;
|
|
}
|
|
|
|
/** Returns conic weight if next() returned kConic_Verb.
|
|
|
|
If next() has not been called, or next() did not return kConic_Verb,
|
|
result is undefined.
|
|
|
|
@return conic weight for conic SkPoint returned by next()
|
|
*/
|
|
SkScalar conicWeight() const {
|
|
return fConicWeight;
|
|
}
|
|
|
|
private:
|
|
RangeIter fIter;
|
|
RangeIter fEnd;
|
|
SkScalar fConicWeight = 0;
|
|
friend class SkPath;
|
|
|
|
};
|
|
|
|
/** Returns true if the point (x, y) is contained by SkPath, taking into
|
|
account FillType.
|
|
|
|
@param x x-axis value of containment test
|
|
@param y y-axis value of containment test
|
|
@return true if SkPoint is in SkPath
|
|
|
|
example: https://fiddle.skia.org/c/@Path_contains
|
|
*/
|
|
bool contains(SkScalar x, SkScalar y) const;
|
|
|
|
/** Writes text representation of SkPath to stream. If stream is nullptr, writes to
|
|
standard output. Set dumpAsHex true to generate exact binary representations
|
|
of floating point numbers used in SkPoint array and conic weights.
|
|
|
|
@param stream writable SkWStream receiving SkPath text representation; may be nullptr
|
|
@param dumpAsHex true if SkScalar values are written as hexadecimal
|
|
|
|
example: https://fiddle.skia.org/c/@Path_dump
|
|
*/
|
|
void dump(SkWStream* stream, bool dumpAsHex) const;
|
|
|
|
void dump() const { this->dump(nullptr, false); }
|
|
void dumpHex() const { this->dump(nullptr, true); }
|
|
|
|
// Like dump(), but outputs for the SkPath::Make() factory
|
|
void dumpArrays(SkWStream* stream, bool dumpAsHex) const;
|
|
void dumpArrays() const { this->dumpArrays(nullptr, false); }
|
|
|
|
/** Writes SkPath to buffer, returning the number of bytes written.
|
|
Pass nullptr to obtain the storage size.
|
|
|
|
Writes SkPath::FillType, verb array, SkPoint array, conic weight, and
|
|
additionally writes computed information like SkPath::Convexity and bounds.
|
|
|
|
Use only be used in concert with readFromMemory();
|
|
the format used for SkPath in memory is not guaranteed.
|
|
|
|
@param buffer storage for SkPath; may be nullptr
|
|
@return size of storage required for SkPath; always a multiple of 4
|
|
|
|
example: https://fiddle.skia.org/c/@Path_writeToMemory
|
|
*/
|
|
size_t writeToMemory(void* buffer) const;
|
|
|
|
/** Writes SkPath to buffer, returning the buffer written to, wrapped in SkData.
|
|
|
|
serialize() writes SkPath::FillType, verb array, SkPoint array, conic weight, and
|
|
additionally writes computed information like SkPath::Convexity and bounds.
|
|
|
|
serialize() should only be used in concert with readFromMemory().
|
|
The format used for SkPath in memory is not guaranteed.
|
|
|
|
@return SkPath data wrapped in SkData buffer
|
|
|
|
example: https://fiddle.skia.org/c/@Path_serialize
|
|
*/
|
|
sk_sp<SkData> serialize() const;
|
|
|
|
/** Initializes SkPath from buffer of size length. Returns zero if the buffer is
|
|
data is inconsistent, or the length is too small.
|
|
|
|
Reads SkPath::FillType, verb array, SkPoint array, conic weight, and
|
|
additionally reads computed information like SkPath::Convexity and bounds.
|
|
|
|
Used only in concert with writeToMemory();
|
|
the format used for SkPath in memory is not guaranteed.
|
|
|
|
@param buffer storage for SkPath
|
|
@param length buffer size in bytes; must be multiple of 4
|
|
@return number of bytes read, or zero on failure
|
|
|
|
example: https://fiddle.skia.org/c/@Path_readFromMemory
|
|
*/
|
|
size_t readFromMemory(const void* buffer, size_t length);
|
|
|
|
/** (See Skia bug 1762.)
|
|
Returns a non-zero, globally unique value. A different value is returned
|
|
if verb array, SkPoint array, or conic weight changes.
|
|
|
|
Setting SkPath::FillType does not change generation identifier.
|
|
|
|
Each time the path is modified, a different generation identifier will be returned.
|
|
SkPath::FillType does affect generation identifier on Android framework.
|
|
|
|
@return non-zero, globally unique value
|
|
|
|
example: https://fiddle.skia.org/c/@Path_getGenerationID
|
|
*/
|
|
uint32_t getGenerationID() const;
|
|
|
|
/** Returns if SkPath data is consistent. Corrupt SkPath data is detected if
|
|
internal values are out of range or internal storage does not match
|
|
array dimensions.
|
|
|
|
@return true if SkPath data is consistent
|
|
*/
|
|
bool isValid() const;
|
|
|
|
using sk_is_trivially_relocatable = std::true_type;
|
|
|
|
private:
|
|
SkPath(sk_sp<SkPathRef>, SkPathFillType, bool isVolatile, SkPathConvexity,
|
|
SkPathFirstDirection firstDirection);
|
|
|
|
sk_sp<SkPathRef> fPathRef;
|
|
int fLastMoveToIndex;
|
|
mutable std::atomic<uint8_t> fConvexity; // SkPathConvexity
|
|
mutable std::atomic<uint8_t> fFirstDirection; // SkPathFirstDirection
|
|
uint8_t fFillType : 2;
|
|
uint8_t fIsVolatile : 1;
|
|
|
|
static_assert(::sk_is_trivially_relocatable<decltype(fPathRef)>::value);
|
|
|
|
/** Resets all fields other than fPathRef to their initial 'empty' values.
|
|
* Assumes the caller has already emptied fPathRef.
|
|
* On Android increments fGenerationID without reseting it.
|
|
*/
|
|
void resetFields();
|
|
|
|
/** Sets all fields other than fPathRef to the values in 'that'.
|
|
* Assumes the caller has already set fPathRef.
|
|
* Doesn't change fGenerationID or fSourcePath on Android.
|
|
*/
|
|
void copyFields(const SkPath& that);
|
|
|
|
size_t writeToMemoryAsRRect(void* buffer) const;
|
|
size_t readAsRRect(const void*, size_t);
|
|
size_t readFromMemory_EQ4Or5(const void*, size_t);
|
|
|
|
friend class Iter;
|
|
friend class SkPathPriv;
|
|
friend class SkPathStroker;
|
|
|
|
/* Append, in reverse order, the first contour of path, ignoring path's
|
|
last point. If no moveTo() call has been made for this contour, the
|
|
first point is automatically set to (0,0).
|
|
*/
|
|
SkPath& reversePathTo(const SkPath&);
|
|
|
|
// called before we add points for lineTo, quadTo, cubicTo, checking to see
|
|
// if we need to inject a leading moveTo first
|
|
//
|
|
// SkPath path; path.lineTo(...); <--- need a leading moveTo(0, 0)
|
|
// SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
|
|
//
|
|
inline void injectMoveToIfNeeded();
|
|
|
|
inline bool hasOnlyMoveTos() const;
|
|
|
|
SkPathConvexity computeConvexity() const;
|
|
|
|
bool isValidImpl() const;
|
|
/** Asserts if SkPath data is inconsistent.
|
|
Debugging check intended for internal use only.
|
|
*/
|
|
#ifdef SK_DEBUG
|
|
void validate() const;
|
|
void validateRef() const;
|
|
#endif
|
|
|
|
// called by stroker to see if all points (in the last contour) are equal and worthy of a cap
|
|
bool isZeroLengthSincePoint(int startPtIndex) const;
|
|
|
|
/** Returns if the path can return a bound at no cost (true) or will have to
|
|
perform some computation (false).
|
|
*/
|
|
bool hasComputedBounds() const;
|
|
|
|
// 'rect' needs to be sorted
|
|
void setBounds(const SkRect& rect);
|
|
|
|
void setPt(int index, SkScalar x, SkScalar y);
|
|
|
|
SkPath& dirtyAfterEdit();
|
|
|
|
// Bottlenecks for working with fConvexity and fFirstDirection.
|
|
// Notice the setters are const... these are mutable atomic fields.
|
|
void setConvexity(SkPathConvexity) const;
|
|
|
|
void setFirstDirection(SkPathFirstDirection) const;
|
|
SkPathFirstDirection getFirstDirection() const;
|
|
|
|
/** Returns the comvexity type, computing if needed. Never returns kUnknown.
|
|
@return path's convexity type (convex or concave)
|
|
*/
|
|
SkPathConvexity getConvexity() const;
|
|
|
|
SkPathConvexity getConvexityOrUnknown() const;
|
|
|
|
// Compares the cached value with a freshly computed one (computeConvexity())
|
|
bool isConvexityAccurate() const;
|
|
|
|
/** Stores a convexity type for this path. This is what will be returned if
|
|
* getConvexityOrUnknown() is called. If you pass kUnknown, then if getContexityType()
|
|
* is called, the real convexity will be computed.
|
|
*
|
|
* example: https://fiddle.skia.org/c/@Path_setConvexity
|
|
*/
|
|
void setConvexity(SkPathConvexity convexity);
|
|
|
|
/** Shrinks SkPath verb array and SkPoint array storage to discard unused capacity.
|
|
* May reduce the heap overhead for SkPath known to be fully constructed.
|
|
*
|
|
* NOTE: This may relocate the underlying buffers, and thus any Iterators referencing
|
|
* this path should be discarded after calling shrinkToFit().
|
|
*/
|
|
void shrinkToFit();
|
|
|
|
// Creates a new Path after the supplied arguments have been validated by
|
|
// sk_path_analyze_verbs().
|
|
static SkPath MakeInternal(const SkPathVerbAnalysis& analsis,
|
|
const SkPoint points[],
|
|
const uint8_t verbs[],
|
|
int verbCount,
|
|
const SkScalar conics[],
|
|
SkPathFillType fillType,
|
|
bool isVolatile);
|
|
|
|
friend class SkAutoPathBoundsUpdate;
|
|
friend class SkAutoDisableOvalCheck;
|
|
friend class SkAutoDisableDirectionCheck;
|
|
friend class SkPathBuilder;
|
|
friend class SkPathEdgeIter;
|
|
friend class SkPathWriter;
|
|
friend class SkOpBuilder;
|
|
friend class SkBench_AddPathTest; // perf test reversePathTo
|
|
friend class PathTest_Private; // unit test reversePathTo
|
|
friend class ForceIsRRect_Private; // unit test isRRect
|
|
friend class FuzzPath; // for legacy access to validateRef
|
|
};
|
|
|
|
// Copyright 2022 Google LLC
|
|
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef SkAlignedStorage_DEFINED
|
|
#define SkAlignedStorage_DEFINED
|
|
|
|
|
|
template <int N, typename T> class SkAlignedSTStorage {
|
|
public:
|
|
SkAlignedSTStorage() {}
|
|
SkAlignedSTStorage(SkAlignedSTStorage&&) = delete;
|
|
SkAlignedSTStorage(const SkAlignedSTStorage&) = delete;
|
|
SkAlignedSTStorage& operator=(SkAlignedSTStorage&&) = delete;
|
|
SkAlignedSTStorage& operator=(const SkAlignedSTStorage&) = delete;
|
|
|
|
// Returns void* because this object does not initialize the
|
|
// memory. Use placement new for types that require a constructor.
|
|
void* get() { return fStorage; }
|
|
const void* get() const { return fStorage; }
|
|
|
|
// Act as a container of bytes because the storage is uninitialized.
|
|
std::byte* data() { return fStorage; }
|
|
const std::byte* data() const { return fStorage; }
|
|
size_t size() const { return std::size(fStorage); }
|
|
|
|
private:
|
|
alignas(T) std::byte fStorage[sizeof(T) * N];
|
|
};
|
|
|
|
#endif // SkAlignedStorage_DEFINED
|
|
|
|
// Having this be an export works around IWYU churn related to
|
|
// https://github.com/include-what-you-use/include-what-you-use/issues/1121
|
|
|
|
// Add macro to check the lifetime of initializer_list arguments. initializer_list has a very
|
|
// short life span, and can only be used as a parameter, and not as a variable.
|
|
#if defined(__clang__) && defined(__has_cpp_attribute) && __has_cpp_attribute(clang::lifetimebound)
|
|
#define SK_CHECK_IL_LIFETIME [[clang::lifetimebound]]
|
|
#else
|
|
#define SK_CHECK_IL_LIFETIME
|
|
#endif
|
|
|
|
/**
|
|
* SkSpan holds a reference to contiguous data of type T along with a count. SkSpan does not own
|
|
* the data itself but is merely a reference, therefore you must take care with the lifetime of
|
|
* the underlying data.
|
|
*
|
|
* SkSpan is a count and a pointer into existing array or data type that stores its data in
|
|
* contiguous memory like std::vector. Any container that works with std::size() and std::data()
|
|
* can be used.
|
|
*
|
|
* SkSpan makes a convenient parameter for a routine to accept array like things. This allows you to
|
|
* write the routine without overloads for all different container types.
|
|
*
|
|
* Example:
|
|
* void routine(SkSpan<const int> a) { ... }
|
|
*
|
|
* std::vector v = {1, 2, 3, 4, 5};
|
|
*
|
|
* routine(a);
|
|
*
|
|
* A word of caution when working with initializer_list, initializer_lists have a lifetime that is
|
|
* limited to the current statement. The following is correct and safe:
|
|
*
|
|
* Example:
|
|
* routine({1,2,3,4,5});
|
|
*
|
|
* The following is undefined, and will result in erratic execution:
|
|
*
|
|
* Bad Example:
|
|
* initializer_list l = {1, 2, 3, 4, 5}; // The data behind l dies at the ;.
|
|
* routine(l);
|
|
*/
|
|
template <typename T>
|
|
class SkSpan {
|
|
public:
|
|
constexpr SkSpan() : fPtr{nullptr}, fSize{0} {}
|
|
|
|
template <typename Integer, std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
|
|
constexpr SkSpan(T* ptr, Integer size) : fPtr{ptr}, fSize{SkToSizeT(size)} {
|
|
SkASSERT(ptr || fSize == 0); // disallow nullptr + a nonzero size
|
|
SkASSERT(fSize < kMaxSize);
|
|
}
|
|
template <typename U, typename = std::enable_if_t<std::is_same_v<const U, T>>>
|
|
constexpr SkSpan(const SkSpan<U>& that) : fPtr(std::data(that)), fSize(std::size(that)) {}
|
|
constexpr SkSpan(const SkSpan& o) = default;
|
|
template<size_t N> constexpr SkSpan(T(&a)[N]) : SkSpan(a, N) { }
|
|
template<typename Container>
|
|
constexpr SkSpan(Container&& c) : SkSpan(std::data(c), std::size(c)) { }
|
|
SkSpan(std::initializer_list<T> il SK_CHECK_IL_LIFETIME)
|
|
: SkSpan(std::data(il), std::size(il)) {}
|
|
|
|
constexpr SkSpan& operator=(const SkSpan& that) = default;
|
|
|
|
constexpr T& operator [] (size_t i) const {
|
|
return fPtr[sk_collection_check_bounds(i, this->size())];
|
|
}
|
|
constexpr T& front() const { sk_collection_not_empty(this->empty()); return fPtr[0]; }
|
|
constexpr T& back() const { sk_collection_not_empty(this->empty()); return fPtr[fSize - 1]; }
|
|
constexpr T* begin() const { return fPtr; }
|
|
constexpr T* end() const { return fPtr + fSize; }
|
|
constexpr auto rbegin() const { return std::make_reverse_iterator(this->end()); }
|
|
constexpr auto rend() const { return std::make_reverse_iterator(this->begin()); }
|
|
constexpr T* data() const { return this->begin(); }
|
|
constexpr size_t size() const { return fSize; }
|
|
constexpr bool empty() const { return fSize == 0; }
|
|
constexpr size_t size_bytes() const { return fSize * sizeof(T); }
|
|
constexpr SkSpan<T> first(size_t prefixLen) const {
|
|
return SkSpan{fPtr, sk_collection_check_length(prefixLen, fSize)};
|
|
}
|
|
constexpr SkSpan<T> last(size_t postfixLen) const {
|
|
return SkSpan{fPtr + (this->size() - postfixLen),
|
|
sk_collection_check_length(postfixLen, fSize)};
|
|
}
|
|
constexpr SkSpan<T> subspan(size_t offset) const {
|
|
return this->subspan(offset, this->size() - offset);
|
|
}
|
|
constexpr SkSpan<T> subspan(size_t offset, size_t count) const {
|
|
const size_t safeOffset = sk_collection_check_length(offset, fSize);
|
|
|
|
// Should read offset + count > size(), but that could overflow. We know that safeOffset
|
|
// is <= size, therefore the subtraction will not overflow.
|
|
if (count > this->size() - safeOffset) SK_UNLIKELY {
|
|
// The count is too large.
|
|
SkUNREACHABLE;
|
|
}
|
|
return SkSpan{fPtr + safeOffset, count};
|
|
}
|
|
|
|
private:
|
|
static constexpr size_t kMaxSize = std::numeric_limits<size_t>::max() / sizeof(T);
|
|
|
|
T* fPtr;
|
|
size_t fSize;
|
|
};
|
|
|
|
template <typename Container>
|
|
SkSpan(Container&&) ->
|
|
SkSpan<std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>>;
|
|
|
|
// Copyright 2022 Google LLC.
|
|
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef SkContainers_DEFINED
|
|
#define SkContainers_DEFINED
|
|
|
|
|
|
|
|
class SK_SPI SkContainerAllocator {
|
|
public:
|
|
SkContainerAllocator(size_t sizeOfT, int maxCapacity)
|
|
: fSizeOfT{sizeOfT}
|
|
, fMaxCapacity{maxCapacity} {}
|
|
|
|
// allocate will abort on failure. Given a capacity of 0, it will return the empty span.
|
|
// The bytes allocated are freed using sk_free().
|
|
SkSpan<std::byte> allocate(int capacity, double growthFactor = 1.0);
|
|
|
|
private:
|
|
friend struct SkContainerAllocatorTestingPeer;
|
|
// All capacity counts will be rounded up to kCapacityMultiple.
|
|
// TODO: this is a constant from the original SkTArray code. This should be checked some how.
|
|
static constexpr int64_t kCapacityMultiple = 8;
|
|
|
|
// Rounds up capacity to next multiple of kCapacityMultiple and pin to fMaxCapacity.
|
|
size_t roundUpCapacity(int64_t capacity) const;
|
|
|
|
// Grows the capacity by growthFactor being sure to stay with in kMinBytes and fMaxCapacity.
|
|
size_t growthFactorCapacity(int capacity, double growthFactor) const;
|
|
|
|
const size_t fSizeOfT;
|
|
const int64_t fMaxCapacity;
|
|
};
|
|
|
|
// sk_allocate_canfail returns the empty span on failure. Parameter size must be > 0.
|
|
SkSpan<std::byte> sk_allocate_canfail(size_t size);
|
|
|
|
// Returns the empty span if size is 0. sk_allocate_throw aborts on failure.
|
|
SkSpan<std::byte> sk_allocate_throw(size_t size);
|
|
|
|
SK_SPI void sk_report_container_overflow_and_die();
|
|
#endif // SkContainers_DEFINED
|
|
|
|
/*
|
|
memory wrappers to be implemented by the porting layer (platform)
|
|
*/
|
|
|
|
|
|
/** Free memory returned by sk_malloc(). It is safe to pass null. */
|
|
SK_API extern void sk_free(void*);
|
|
|
|
/**
|
|
* Called internally if we run out of memory. The platform implementation must
|
|
* not return, but should either throw an exception or otherwise exit.
|
|
*/
|
|
SK_API extern void sk_out_of_memory(void);
|
|
|
|
enum {
|
|
/**
|
|
* If this bit is set, the returned buffer must be zero-initialized. If this bit is not set
|
|
* the buffer can be uninitialized.
|
|
*/
|
|
SK_MALLOC_ZERO_INITIALIZE = 1 << 0,
|
|
|
|
/**
|
|
* If this bit is set, the implementation must throw/crash/quit if the request cannot
|
|
* be fulfilled. If this bit is not set, then it should return nullptr on failure.
|
|
*/
|
|
SK_MALLOC_THROW = 1 << 1,
|
|
};
|
|
/**
|
|
* Return a block of memory (at least 4-byte aligned) of at least the specified size.
|
|
* If the requested memory cannot be returned, either return nullptr or throw/exit, depending
|
|
* on the SK_MALLOC_THROW bit. If the allocation succeeds, the memory will be zero-initialized
|
|
* if the SK_MALLOC_ZERO_INITIALIZE bit was set.
|
|
*
|
|
* To free the memory, call sk_free()
|
|
*/
|
|
SK_API extern void* sk_malloc_flags(size_t size, unsigned flags);
|
|
|
|
/** Same as standard realloc(), but this one never returns null on failure. It will throw
|
|
* if it fails.
|
|
* If size is 0, it will call sk_free on buffer and return null. (This behavior is implementation-
|
|
* defined for normal realloc. We follow what glibc does.)
|
|
*/
|
|
SK_API extern void* sk_realloc_throw(void* buffer, size_t size);
|
|
|
|
static inline void* sk_malloc_throw(size_t size) {
|
|
return sk_malloc_flags(size, SK_MALLOC_THROW);
|
|
}
|
|
|
|
static inline void* sk_calloc_throw(size_t size) {
|
|
return sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_ZERO_INITIALIZE);
|
|
}
|
|
|
|
static inline void* sk_calloc_canfail(size_t size) {
|
|
#if defined(SK_BUILD_FOR_FUZZER)
|
|
// To reduce the chance of OOM, pretend we can't allocate more than 200kb.
|
|
if (size > 200000) {
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
return sk_malloc_flags(size, SK_MALLOC_ZERO_INITIALIZE);
|
|
}
|
|
|
|
// Performs a safe multiply count * elemSize, checking for overflow
|
|
SK_API extern void* sk_calloc_throw(size_t count, size_t elemSize);
|
|
SK_API extern void* sk_malloc_throw(size_t count, size_t elemSize);
|
|
SK_API extern void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize);
|
|
|
|
/**
|
|
* These variants return nullptr on failure
|
|
*/
|
|
static inline void* sk_malloc_canfail(size_t size) {
|
|
#if defined(SK_BUILD_FOR_FUZZER)
|
|
// To reduce the chance of OOM, pretend we can't allocate more than 200kb.
|
|
if (size > 200000) {
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
return sk_malloc_flags(size, 0);
|
|
}
|
|
SK_API extern void* sk_malloc_canfail(size_t count, size_t elemSize);
|
|
|
|
// bzero is safer than memset, but we can't rely on it, so... sk_bzero()
|
|
static inline void sk_bzero(void* buffer, size_t size) {
|
|
// Please c.f. sk_careful_memcpy. It's undefined behavior to call memset(null, 0, 0).
|
|
if (size) {
|
|
memset(buffer, 0, size);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* sk_careful_memcpy() is just like memcpy(), but guards against undefined behavior.
|
|
*
|
|
* It is undefined behavior to call memcpy() with null dst or src, even if len is 0.
|
|
* If an optimizer is "smart" enough, it can exploit this to do unexpected things.
|
|
* memcpy(dst, src, 0);
|
|
* if (src) {
|
|
* printf("%x\n", *src);
|
|
* }
|
|
* In this code the compiler can assume src is not null and omit the if (src) {...} check,
|
|
* unconditionally running the printf, crashing the program if src really is null.
|
|
* Of the compilers we pay attention to only GCC performs this optimization in practice.
|
|
*/
|
|
static inline void* sk_careful_memcpy(void* dst, const void* src, size_t len) {
|
|
// When we pass >0 len we had better already be passing valid pointers.
|
|
// So we just need to skip calling memcpy when len == 0.
|
|
if (len) {
|
|
memcpy(dst,src,len);
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static inline void* sk_careful_memmove(void* dst, const void* src, size_t len) {
|
|
// When we pass >0 len we had better already be passing valid pointers.
|
|
// So we just need to skip calling memcpy when len == 0.
|
|
if (len) {
|
|
memmove(dst,src,len);
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static inline int sk_careful_memcmp(const void* a, const void* b, size_t len) {
|
|
// When we pass >0 len we had better already be passing valid pointers.
|
|
// So we just need to skip calling memcmp when len == 0.
|
|
if (len == 0) {
|
|
return 0; // we treat zero-length buffers as "equal"
|
|
}
|
|
return memcmp(a, b, len);
|
|
}
|
|
|
|
namespace skia_private {
|
|
/** TArray<T> implements a typical, mostly std::vector-like array.
|
|
Each T will be default-initialized on allocation, and ~T will be called on destruction.
|
|
|
|
MEM_MOVE controls the behavior when a T needs to be moved (e.g. when the array is resized)
|
|
- true: T will be bit-copied via memcpy.
|
|
- false: T will be moved via move-constructors.
|
|
*/
|
|
template <typename T, bool MEM_MOVE = sk_is_trivially_relocatable_v<T>> class TArray {
|
|
public:
|
|
using value_type = T;
|
|
|
|
/**
|
|
* Creates an empty array with no initial storage
|
|
*/
|
|
TArray() : fOwnMemory(true), fCapacity{0} {}
|
|
|
|
/**
|
|
* Creates an empty array that will preallocate space for reserveCount elements.
|
|
*/
|
|
explicit TArray(int reserveCount) : TArray() { this->reserve_exact(reserveCount); }
|
|
|
|
/**
|
|
* Copies one array to another. The new array will be heap allocated.
|
|
*/
|
|
TArray(const TArray& that) : TArray(that.fData, that.fSize) {}
|
|
|
|
TArray(TArray&& that) {
|
|
if (that.fOwnMemory) {
|
|
this->setData(that);
|
|
that.setData({});
|
|
} else {
|
|
this->initData(that.fSize);
|
|
that.move(fData);
|
|
}
|
|
fSize = std::exchange(that.fSize, 0);
|
|
}
|
|
|
|
/**
|
|
* Creates a TArray by copying contents of a standard C array. The new
|
|
* array will be heap allocated. Be careful not to use this constructor
|
|
* when you really want the (void*, int) version.
|
|
*/
|
|
TArray(const T* array, int count) {
|
|
this->initData(count);
|
|
this->copy(array);
|
|
}
|
|
|
|
/**
|
|
* Creates a TArray by copying contents of an initializer list.
|
|
*/
|
|
TArray(std::initializer_list<T> data) : TArray(data.begin(), data.size()) {}
|
|
|
|
TArray& operator=(const TArray& that) {
|
|
if (this == &that) {
|
|
return *this;
|
|
}
|
|
this->clear();
|
|
this->checkRealloc(that.size(), kExactFit);
|
|
fSize = that.fSize;
|
|
this->copy(that.fData);
|
|
return *this;
|
|
}
|
|
TArray& operator=(TArray&& that) {
|
|
if (this != &that) {
|
|
this->clear();
|
|
if (that.fOwnMemory) {
|
|
// The storage is on the heap, so move the data pointer.
|
|
if (fOwnMemory) {
|
|
sk_free(fData);
|
|
}
|
|
|
|
fData = std::exchange(that.fData, nullptr);
|
|
|
|
// Can't use exchange with bitfields.
|
|
fCapacity = that.fCapacity;
|
|
that.fCapacity = 0;
|
|
|
|
fOwnMemory = true;
|
|
} else {
|
|
// The data is stored inline in that, so move it element-by-element.
|
|
this->checkRealloc(that.size(), kExactFit);
|
|
that.move(fData);
|
|
}
|
|
fSize = std::exchange(that.fSize, 0);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
~TArray() {
|
|
this->destroyAll();
|
|
if (fOwnMemory) {
|
|
sk_free(fData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets to size() = n newly constructed T objects and resets any reserve count.
|
|
*/
|
|
void reset(int n) {
|
|
SkASSERT(n >= 0);
|
|
this->clear();
|
|
this->checkRealloc(n, kExactFit);
|
|
fSize = n;
|
|
for (int i = 0; i < this->size(); ++i) {
|
|
new (fData + i) T;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets to a copy of a C array and resets any reserve count.
|
|
*/
|
|
void reset(const T* array, int count) {
|
|
SkASSERT(count >= 0);
|
|
this->clear();
|
|
this->checkRealloc(count, kExactFit);
|
|
fSize = count;
|
|
this->copy(array);
|
|
}
|
|
|
|
/**
|
|
* Ensures there is enough reserved space for at least n elements. This is guaranteed at least
|
|
* until the array size grows above n and subsequently shrinks below n, any version of reset()
|
|
* is called, or reserve() is called again.
|
|
*/
|
|
void reserve(int n) {
|
|
SkASSERT(n >= 0);
|
|
if (n > this->size()) {
|
|
this->checkRealloc(n - this->size(), kGrowing);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensures there is enough reserved space for exactly n elements. The same capacity guarantees
|
|
* as above apply.
|
|
*/
|
|
void reserve_exact(int n) {
|
|
SkASSERT(n >= 0);
|
|
if (n > this->size()) {
|
|
this->checkRealloc(n - this->size(), kExactFit);
|
|
}
|
|
}
|
|
|
|
void removeShuffle(int n) {
|
|
SkASSERT(n < this->size());
|
|
int newCount = fSize - 1;
|
|
fSize = newCount;
|
|
fData[n].~T();
|
|
if (n != newCount) {
|
|
this->move(n, newCount);
|
|
}
|
|
}
|
|
|
|
// Is the array empty.
|
|
bool empty() const { return fSize == 0; }
|
|
|
|
/**
|
|
* Adds one new default-initialized T value and returns it by reference. Note that the reference
|
|
* only remains valid until the next call that adds or removes elements.
|
|
*/
|
|
T& push_back() {
|
|
void* newT = this->push_back_raw(1);
|
|
return *new (newT) T;
|
|
}
|
|
|
|
/**
|
|
* Adds one new T value which is copy-constructed, returning it by reference. As always,
|
|
* the reference only remains valid until the next call that adds or removes elements.
|
|
*/
|
|
T& push_back(const T& t) {
|
|
T* newT;
|
|
if (this->capacity() > fSize) SK_LIKELY {
|
|
// Copy over the element directly.
|
|
newT = new (fData + fSize) T(t);
|
|
} else {
|
|
newT = this->growAndConstructAtEnd(t);
|
|
}
|
|
|
|
fSize += 1;
|
|
return *newT;
|
|
}
|
|
|
|
/**
|
|
* Adds one new T value which is copy-constructed, returning it by reference.
|
|
*/
|
|
T& push_back(T&& t) {
|
|
T* newT;
|
|
if (this->capacity() > fSize) SK_LIKELY {
|
|
// Move over the element directly.
|
|
newT = new (fData + fSize) T(std::move(t));
|
|
} else {
|
|
newT = this->growAndConstructAtEnd(std::move(t));
|
|
}
|
|
|
|
fSize += 1;
|
|
return *newT;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new T at the back of this array, returning it by reference.
|
|
*/
|
|
template <typename... Args> T& emplace_back(Args&&... args) {
|
|
T* newT;
|
|
if (this->capacity() > fSize) SK_LIKELY {
|
|
// Emplace the new element in directly.
|
|
newT = new (fData + fSize) T(std::forward<Args>(args)...);
|
|
} else {
|
|
newT = this->growAndConstructAtEnd(std::forward<Args>(args)...);
|
|
}
|
|
|
|
fSize += 1;
|
|
return *newT;
|
|
}
|
|
|
|
/**
|
|
* Allocates n more default-initialized T values, and returns the address of
|
|
* the start of that new range. Note: this address is only valid until the
|
|
* next API call made on the array that might add or remove elements.
|
|
*/
|
|
T* push_back_n(int n) {
|
|
SkASSERT(n >= 0);
|
|
T* newTs = TCast(this->push_back_raw(n));
|
|
for (int i = 0; i < n; ++i) {
|
|
new (&newTs[i]) T;
|
|
}
|
|
return newTs;
|
|
}
|
|
|
|
/**
|
|
* Version of above that uses a copy constructor to initialize all n items
|
|
* to the same T.
|
|
*/
|
|
T* push_back_n(int n, const T& t) {
|
|
SkASSERT(n >= 0);
|
|
T* newTs = TCast(this->push_back_raw(n));
|
|
for (int i = 0; i < n; ++i) {
|
|
new (&newTs[i]) T(t);
|
|
}
|
|
return static_cast<T*>(newTs);
|
|
}
|
|
|
|
/**
|
|
* Version of above that uses a copy constructor to initialize the n items
|
|
* to separate T values.
|
|
*/
|
|
T* push_back_n(int n, const T t[]) {
|
|
SkASSERT(n >= 0);
|
|
this->checkRealloc(n, kGrowing);
|
|
T* end = this->end();
|
|
for (int i = 0; i < n; ++i) {
|
|
new (end + i) T(t[i]);
|
|
}
|
|
fSize += n;
|
|
return end;
|
|
}
|
|
|
|
/**
|
|
* Version of above that uses the move constructor to set n items.
|
|
*/
|
|
T* move_back_n(int n, T* t) {
|
|
SkASSERT(n >= 0);
|
|
this->checkRealloc(n, kGrowing);
|
|
T* end = this->end();
|
|
for (int i = 0; i < n; ++i) {
|
|
new (end + i) T(std::move(t[i]));
|
|
}
|
|
fSize += n;
|
|
return end;
|
|
}
|
|
|
|
/**
|
|
* Removes the last element. Not safe to call when size() == 0.
|
|
*/
|
|
void pop_back() {
|
|
sk_collection_not_empty(this->empty());
|
|
--fSize;
|
|
fData[fSize].~T();
|
|
}
|
|
|
|
/**
|
|
* Removes the last n elements. Not safe to call when size() < n.
|
|
*/
|
|
void pop_back_n(int n) {
|
|
SkASSERT(n >= 0);
|
|
SkASSERT(this->size() >= n);
|
|
int i = fSize;
|
|
while (i-- > fSize - n) {
|
|
(*this)[i].~T();
|
|
}
|
|
fSize -= n;
|
|
}
|
|
|
|
/**
|
|
* Pushes or pops from the back to resize. Pushes will be default
|
|
* initialized.
|
|
*/
|
|
void resize_back(int newCount) {
|
|
SkASSERT(newCount >= 0);
|
|
|
|
if (newCount > this->size()) {
|
|
this->push_back_n(newCount - fSize);
|
|
} else if (newCount < this->size()) {
|
|
this->pop_back_n(fSize - newCount);
|
|
}
|
|
}
|
|
|
|
/** Swaps the contents of this array with that array. Does a pointer swap if possible,
|
|
otherwise copies the T values. */
|
|
void swap(TArray& that) {
|
|
using std::swap;
|
|
if (this == &that) {
|
|
return;
|
|
}
|
|
if (fOwnMemory && that.fOwnMemory) {
|
|
swap(fData, that.fData);
|
|
swap(fSize, that.fSize);
|
|
|
|
// Can't use swap because fCapacity is a bit field.
|
|
auto allocCount = fCapacity;
|
|
fCapacity = that.fCapacity;
|
|
that.fCapacity = allocCount;
|
|
} else {
|
|
// This could be more optimal...
|
|
TArray copy(std::move(that));
|
|
that = std::move(*this);
|
|
*this = std::move(copy);
|
|
}
|
|
}
|
|
|
|
T* begin() {
|
|
return fData;
|
|
}
|
|
const T* begin() const {
|
|
return fData;
|
|
}
|
|
|
|
// It's safe to use fItemArray + fSize because if fItemArray is nullptr then adding 0 is
|
|
// valid and returns nullptr. See [expr.add] in the C++ standard.
|
|
T* end() {
|
|
if (fData == nullptr) {
|
|
SkASSERT(fSize == 0);
|
|
}
|
|
return fData + fSize;
|
|
}
|
|
const T* end() const {
|
|
if (fData == nullptr) {
|
|
SkASSERT(fSize == 0);
|
|
}
|
|
return fData + fSize;
|
|
}
|
|
T* data() { return fData; }
|
|
const T* data() const { return fData; }
|
|
int size() const { return fSize; }
|
|
size_t size_bytes() const { return Bytes(fSize); }
|
|
void resize(size_t count) { this->resize_back((int)count); }
|
|
|
|
void clear() {
|
|
this->destroyAll();
|
|
fSize = 0;
|
|
}
|
|
|
|
void shrink_to_fit() {
|
|
if (!fOwnMemory || fSize == fCapacity) {
|
|
return;
|
|
}
|
|
if (fSize == 0) {
|
|
sk_free(fData);
|
|
fData = nullptr;
|
|
fCapacity = 0;
|
|
} else {
|
|
SkSpan<std::byte> allocation = Allocate(fSize);
|
|
this->move(TCast(allocation.data()));
|
|
if (fOwnMemory) {
|
|
sk_free(fData);
|
|
}
|
|
this->setDataFromBytes(allocation);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the i^th element.
|
|
*/
|
|
T& operator[] (int i) {
|
|
return fData[sk_collection_check_bounds(i, this->size())];
|
|
}
|
|
|
|
const T& operator[] (int i) const {
|
|
return fData[sk_collection_check_bounds(i, this->size())];
|
|
}
|
|
|
|
T& at(int i) { return (*this)[i]; }
|
|
const T& at(int i) const { return (*this)[i]; }
|
|
|
|
/**
|
|
* equivalent to operator[](0)
|
|
*/
|
|
T& front() {
|
|
sk_collection_not_empty(this->empty());
|
|
return fData[0];
|
|
}
|
|
|
|
const T& front() const {
|
|
sk_collection_not_empty(this->empty());
|
|
return fData[0];
|
|
}
|
|
|
|
/**
|
|
* equivalent to operator[](size() - 1)
|
|
*/
|
|
T& back() {
|
|
sk_collection_not_empty(this->empty());
|
|
return fData[fSize - 1];
|
|
}
|
|
|
|
const T& back() const {
|
|
sk_collection_not_empty(this->empty());
|
|
return fData[fSize - 1];
|
|
}
|
|
|
|
/**
|
|
* equivalent to operator[](size()-1-i)
|
|
*/
|
|
T& fromBack(int i) {
|
|
return (*this)[fSize - i - 1];
|
|
}
|
|
|
|
const T& fromBack(int i) const {
|
|
return (*this)[fSize - i - 1];
|
|
}
|
|
|
|
bool operator==(const TArray<T, MEM_MOVE>& right) const {
|
|
int leftCount = this->size();
|
|
if (leftCount != right.size()) {
|
|
return false;
|
|
}
|
|
for (int index = 0; index < leftCount; ++index) {
|
|
if (fData[index] != right.fData[index]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool operator!=(const TArray<T, MEM_MOVE>& right) const {
|
|
return !(*this == right);
|
|
}
|
|
|
|
int capacity() const {
|
|
return fCapacity;
|
|
}
|
|
|
|
protected:
|
|
// Creates an empty array that will use the passed storage block until it is insufficiently
|
|
// large to hold the entire array.
|
|
template <int InitialCapacity>
|
|
TArray(SkAlignedSTStorage<InitialCapacity, T>* storage, int size = 0) {
|
|
static_assert(InitialCapacity >= 0);
|
|
SkASSERT(size >= 0);
|
|
SkASSERT(storage->get() != nullptr);
|
|
if (size > InitialCapacity) {
|
|
this->initData(size);
|
|
} else {
|
|
this->setDataFromBytes(*storage);
|
|
fSize = size;
|
|
|
|
// setDataFromBytes always sets fOwnMemory to true, but we are actually using static
|
|
// storage here, which shouldn't ever be freed.
|
|
fOwnMemory = false;
|
|
}
|
|
}
|
|
|
|
// Copy a C array, using pre-allocated storage if preAllocCount >= count. Otherwise, storage
|
|
// will only be used when array shrinks to fit.
|
|
template <int InitialCapacity>
|
|
TArray(const T* array, int size, SkAlignedSTStorage<InitialCapacity, T>* storage)
|
|
: TArray{storage, size}
|
|
{
|
|
this->copy(array);
|
|
}
|
|
|
|
private:
|
|
// Growth factors for checkRealloc.
|
|
static constexpr double kExactFit = 1.0;
|
|
static constexpr double kGrowing = 1.5;
|
|
|
|
static constexpr int kMinHeapAllocCount = 8;
|
|
static_assert(SkIsPow2(kMinHeapAllocCount), "min alloc count not power of two.");
|
|
|
|
// Note for 32-bit machines kMaxCapacity will be <= SIZE_MAX. For 64-bit machines it will
|
|
// just be INT_MAX if the sizeof(T) < 2^32.
|
|
static constexpr int kMaxCapacity = SkToInt(std::min(SIZE_MAX / sizeof(T), (size_t)INT_MAX));
|
|
|
|
void setDataFromBytes(SkSpan<std::byte> allocation) {
|
|
T* data = TCast(allocation.data());
|
|
// We have gotten extra bytes back from the allocation limit, pin to kMaxCapacity. It
|
|
// would seem like the SkContainerAllocator should handle the divide, but it would have
|
|
// to a full divide instruction. If done here the size is known at compile, and usually
|
|
// can be implemented by a right shift. The full divide takes ~50X longer than the shift.
|
|
size_t size = std::min(allocation.size() / sizeof(T), SkToSizeT(kMaxCapacity));
|
|
this->setData(SkSpan<T>(data, size));
|
|
}
|
|
|
|
void setData(SkSpan<T> array) {
|
|
fData = array.data();
|
|
fCapacity = SkToU32(array.size());
|
|
fOwnMemory = true;
|
|
}
|
|
|
|
// We disable Control-Flow Integrity sanitization (go/cfi) when casting item-array buffers.
|
|
// CFI flags this code as dangerous because we are casting `buffer` to a T* while the buffer's
|
|
// contents might still be uninitialized memory. When T has a vtable, this is especially risky
|
|
// because we could hypothetically access a virtual method on fItemArray and jump to an
|
|
// unpredictable location in memory. Of course, TArray won't actually use fItemArray in this
|
|
// way, and we don't want to construct a T before the user requests one. There's no real risk
|
|
// here, so disable CFI when doing these casts.
|
|
SK_CLANG_NO_SANITIZE("cfi")
|
|
static T* TCast(void* buffer) {
|
|
return (T*)buffer;
|
|
}
|
|
|
|
static size_t Bytes(int n) {
|
|
SkASSERT(n <= kMaxCapacity);
|
|
return SkToSizeT(n) * sizeof(T);
|
|
}
|
|
|
|
static SkSpan<std::byte> Allocate(int capacity, double growthFactor = 1.0) {
|
|
return SkContainerAllocator{sizeof(T), kMaxCapacity}.allocate(capacity, growthFactor);
|
|
}
|
|
|
|
void initData(int count) {
|
|
this->setDataFromBytes(Allocate(count));
|
|
fSize = count;
|
|
}
|
|
|
|
void destroyAll() {
|
|
if (!this->empty()) {
|
|
T* cursor = this->begin();
|
|
T* const end = this->end();
|
|
do {
|
|
cursor->~T();
|
|
cursor++;
|
|
} while (cursor < end);
|
|
}
|
|
}
|
|
|
|
/** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage.
|
|
* In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage.
|
|
*/
|
|
void copy(const T* src) {
|
|
if constexpr (std::is_trivially_copyable_v<T>) {
|
|
if (!this->empty() && src != nullptr) {
|
|
sk_careful_memcpy(fData, src, this->size_bytes());
|
|
}
|
|
} else {
|
|
for (int i = 0; i < this->size(); ++i) {
|
|
new (fData + i) T(src[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void move(int dst, int src) {
|
|
if constexpr (MEM_MOVE) {
|
|
memcpy(static_cast<void*>(&fData[dst]),
|
|
static_cast<const void*>(&fData[src]),
|
|
sizeof(T));
|
|
} else {
|
|
new (&fData[dst]) T(std::move(fData[src]));
|
|
fData[src].~T();
|
|
}
|
|
}
|
|
|
|
void move(void* dst) {
|
|
if constexpr (MEM_MOVE) {
|
|
sk_careful_memcpy(dst, fData, Bytes(fSize));
|
|
} else {
|
|
for (int i = 0; i < this->size(); ++i) {
|
|
new (static_cast<char*>(dst) + Bytes(i)) T(std::move(fData[i]));
|
|
fData[i].~T();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function that makes space for n objects, adjusts the count, but does not initialize
|
|
// the new objects.
|
|
void* push_back_raw(int n) {
|
|
this->checkRealloc(n, kGrowing);
|
|
void* ptr = fData + fSize;
|
|
fSize += n;
|
|
return ptr;
|
|
}
|
|
|
|
template <typename... Args>
|
|
SK_ALWAYS_INLINE T* growAndConstructAtEnd(Args&&... args) {
|
|
SkSpan<std::byte> buffer = this->preallocateNewData(/*delta=*/1, kGrowing);
|
|
T* newT = new (TCast(buffer.data()) + fSize) T(std::forward<Args>(args)...);
|
|
this->installDataAndUpdateCapacity(buffer);
|
|
|
|
return newT;
|
|
}
|
|
|
|
void checkRealloc(int delta, double growthFactor) {
|
|
SkASSERT(delta >= 0);
|
|
SkASSERT(fSize >= 0);
|
|
SkASSERT(fCapacity >= 0);
|
|
|
|
// Check if there are enough remaining allocated elements to satisfy the request.
|
|
if (this->capacity() - fSize < delta) {
|
|
// Looks like we need to reallocate.
|
|
this->installDataAndUpdateCapacity(this->preallocateNewData(delta, growthFactor));
|
|
}
|
|
}
|
|
|
|
SkSpan<std::byte> preallocateNewData(int delta, double growthFactor) {
|
|
SkASSERT(delta >= 0);
|
|
SkASSERT(fSize >= 0);
|
|
SkASSERT(fCapacity >= 0);
|
|
|
|
// Don't overflow fSize or size_t later in the memory allocation. Overflowing memory
|
|
// allocation really only applies to fSizes on 32-bit machines; on 64-bit machines this
|
|
// will probably never produce a check. Since kMaxCapacity is bounded above by INT_MAX,
|
|
// this also checks the bounds of fSize.
|
|
if (delta > kMaxCapacity - fSize) {
|
|
sk_report_container_overflow_and_die();
|
|
}
|
|
const int newCount = fSize + delta;
|
|
|
|
return Allocate(newCount, growthFactor);
|
|
}
|
|
|
|
void installDataAndUpdateCapacity(SkSpan<std::byte> allocation) {
|
|
this->move(TCast(allocation.data()));
|
|
if (fOwnMemory) {
|
|
sk_free(fData);
|
|
}
|
|
this->setDataFromBytes(allocation);
|
|
SkASSERT(fData != nullptr);
|
|
}
|
|
|
|
T* fData{nullptr};
|
|
int fSize{0};
|
|
uint32_t fOwnMemory : 1;
|
|
uint32_t fCapacity : 31;
|
|
};
|
|
|
|
template <typename T, bool M> static inline void swap(TArray<T, M>& a, TArray<T, M>& b) {
|
|
a.swap(b);
|
|
}
|
|
|
|
// Subclass of TArray that contains a pre-allocated memory block for the array.
|
|
template <int N, typename T, bool MEM_MOVE = sk_is_trivially_relocatable_v<T>>
|
|
class STArray : private SkAlignedSTStorage<N,T>, public TArray<T, MEM_MOVE> {
|
|
static_assert(N > 0);
|
|
using Storage = SkAlignedSTStorage<N,T>;
|
|
|
|
public:
|
|
STArray()
|
|
: Storage{}
|
|
, TArray<T, MEM_MOVE>(this) {} // Must use () to avoid confusion with initializer_list
|
|
// when T=bool because * are convertable to bool.
|
|
|
|
STArray(const T* array, int count)
|
|
: Storage{}
|
|
, TArray<T, MEM_MOVE>{array, count, this} {}
|
|
|
|
STArray(std::initializer_list<T> data)
|
|
: STArray{data.begin(), SkToInt(data.size())} {}
|
|
|
|
explicit STArray(int reserveCount)
|
|
: STArray() { this->reserve_exact(reserveCount); }
|
|
|
|
STArray(const STArray& that)
|
|
: STArray() { *this = that; }
|
|
|
|
explicit STArray(const TArray<T, MEM_MOVE>& that)
|
|
: STArray() { *this = that; }
|
|
|
|
STArray(STArray&& that)
|
|
: STArray() { *this = std::move(that); }
|
|
|
|
explicit STArray(TArray<T, MEM_MOVE>&& that)
|
|
: STArray() { *this = std::move(that); }
|
|
|
|
STArray& operator=(const STArray& that) {
|
|
TArray<T, MEM_MOVE>::operator=(that);
|
|
return *this;
|
|
}
|
|
|
|
STArray& operator=(const TArray<T, MEM_MOVE>& that) {
|
|
TArray<T, MEM_MOVE>::operator=(that);
|
|
return *this;
|
|
}
|
|
|
|
STArray& operator=(STArray&& that) {
|
|
TArray<T, MEM_MOVE>::operator=(std::move(that));
|
|
return *this;
|
|
}
|
|
|
|
STArray& operator=(TArray<T, MEM_MOVE>&& that) {
|
|
TArray<T, MEM_MOVE>::operator=(std::move(that));
|
|
return *this;
|
|
}
|
|
|
|
// Force the use of TArray for data() and size().
|
|
using TArray<T, MEM_MOVE>::data;
|
|
using TArray<T, MEM_MOVE>::size;
|
|
};
|
|
} // namespace skia_private
|
|
|
|
class SK_SPI SkTDStorage {
|
|
public:
|
|
explicit SkTDStorage(int sizeOfT);
|
|
SkTDStorage(const void* src, int size, int sizeOfT);
|
|
|
|
// Copy
|
|
SkTDStorage(const SkTDStorage& that);
|
|
SkTDStorage& operator= (const SkTDStorage& that);
|
|
|
|
// Move
|
|
SkTDStorage(SkTDStorage&& that);
|
|
SkTDStorage& operator= (SkTDStorage&& that);
|
|
|
|
~SkTDStorage();
|
|
|
|
void reset();
|
|
void swap(SkTDStorage& that);
|
|
|
|
// Size routines
|
|
bool empty() const { return fSize == 0; }
|
|
void clear() { fSize = 0; }
|
|
int size() const { return fSize; }
|
|
void resize(int newSize);
|
|
size_t size_bytes() const { return this->bytes(fSize); }
|
|
|
|
// Capacity routines
|
|
int capacity() const { return fCapacity; }
|
|
void reserve(int newCapacity);
|
|
void shrink_to_fit();
|
|
|
|
void* data() { return fStorage; }
|
|
const void* data() const { return fStorage; }
|
|
|
|
// Deletion routines
|
|
void erase(int index, int count);
|
|
// Removes the entry at 'index' and replaces it with the last array element
|
|
void removeShuffle(int index);
|
|
|
|
// Insertion routines
|
|
void* prepend();
|
|
|
|
void append();
|
|
void append(int count);
|
|
void* append(const void* src, int count);
|
|
|
|
void* insert(int index);
|
|
void* insert(int index, int count, const void* src);
|
|
|
|
void pop_back() {
|
|
SkASSERT(fSize > 0);
|
|
fSize--;
|
|
}
|
|
|
|
friend bool operator==(const SkTDStorage& a, const SkTDStorage& b);
|
|
friend bool operator!=(const SkTDStorage& a, const SkTDStorage& b) {
|
|
return !(a == b);
|
|
}
|
|
|
|
private:
|
|
size_t bytes(int n) const { return SkToSizeT(n * fSizeOfT); }
|
|
void* address(int n) { return fStorage + this->bytes(n); }
|
|
|
|
// Adds delta to fSize. Crash if outside [0, INT_MAX]
|
|
int calculateSizeOrDie(int delta);
|
|
|
|
// Move the tail of the array defined by the indexes tailStart and tailEnd to dstIndex. The
|
|
// elements at dstIndex are overwritten by the tail.
|
|
void moveTail(int dstIndex, int tailStart, int tailEnd);
|
|
|
|
// Copy src into the array at dstIndex.
|
|
void copySrc(int dstIndex, const void* src, int count);
|
|
|
|
const int fSizeOfT;
|
|
std::byte* fStorage{nullptr};
|
|
int fCapacity{0}; // size of the allocation in fArray (#elements)
|
|
int fSize{0}; // logical number of elements (fSize <= fCapacity)
|
|
};
|
|
|
|
static inline void swap(SkTDStorage& a, SkTDStorage& b) {
|
|
a.swap(b);
|
|
}
|
|
|
|
// SkTDArray<T> implements a std::vector-like array for raw data-only objects that do not require
|
|
// construction or destruction. The constructor and destructor for T will not be called; T objects
|
|
// will always be moved via raw memcpy. Newly created T objects will contain uninitialized memory.
|
|
template <typename T> class SkTDArray {
|
|
public:
|
|
SkTDArray() : fStorage{sizeof(T)} {}
|
|
SkTDArray(const T src[], int count) : fStorage{src, count, sizeof(T)} { }
|
|
SkTDArray(const std::initializer_list<T>& list) : SkTDArray(list.begin(), list.size()) {}
|
|
|
|
// Copy
|
|
SkTDArray(const SkTDArray<T>& src) : SkTDArray(src.data(), src.size()) {}
|
|
SkTDArray<T>& operator=(const SkTDArray<T>& src) {
|
|
fStorage = src.fStorage;
|
|
return *this;
|
|
}
|
|
|
|
// Move
|
|
SkTDArray(SkTDArray<T>&& src) : fStorage{std::move(src.fStorage)} {}
|
|
SkTDArray<T>& operator=(SkTDArray<T>&& src) {
|
|
fStorage = std::move(src.fStorage);
|
|
return *this;
|
|
}
|
|
|
|
friend bool operator==(const SkTDArray<T>& a, const SkTDArray<T>& b) {
|
|
return a.fStorage == b.fStorage;
|
|
}
|
|
friend bool operator!=(const SkTDArray<T>& a, const SkTDArray<T>& b) { return !(a == b); }
|
|
|
|
void swap(SkTDArray<T>& that) {
|
|
using std::swap;
|
|
swap(fStorage, that.fStorage);
|
|
}
|
|
|
|
bool empty() const { return fStorage.empty(); }
|
|
|
|
// Return the number of elements in the array
|
|
int size() const { return fStorage.size(); }
|
|
|
|
// Return the total number of elements allocated.
|
|
// Note: capacity() - size() gives you the number of elements you can add without causing an
|
|
// allocation.
|
|
int capacity() const { return fStorage.capacity(); }
|
|
|
|
// return the number of bytes in the array: count * sizeof(T)
|
|
size_t size_bytes() const { return fStorage.size_bytes(); }
|
|
|
|
T* data() { return static_cast<T*>(fStorage.data()); }
|
|
const T* data() const { return static_cast<const T*>(fStorage.data()); }
|
|
T* begin() { return this->data(); }
|
|
const T* begin() const { return this->data(); }
|
|
T* end() { return this->data() + this->size(); }
|
|
const T* end() const { return this->data() + this->size(); }
|
|
|
|
T& operator[](int index) {
|
|
return this->data()[sk_collection_check_bounds(index, this->size())];
|
|
}
|
|
const T& operator[](int index) const {
|
|
return this->data()[sk_collection_check_bounds(index, this->size())];
|
|
}
|
|
|
|
const T& back() const {
|
|
sk_collection_not_empty(this->empty());
|
|
return this->data()[this->size() - 1];
|
|
}
|
|
T& back() {
|
|
sk_collection_not_empty(this->empty());
|
|
return this->data()[this->size() - 1];
|
|
}
|
|
|
|
void reset() {
|
|
fStorage.reset();
|
|
}
|
|
|
|
void clear() {
|
|
fStorage.clear();
|
|
}
|
|
|
|
// Sets the number of elements in the array.
|
|
// If the array does not have space for count elements, it will increase
|
|
// the storage allocated to some amount greater than that required.
|
|
// It will never shrink the storage.
|
|
void resize(int count) {
|
|
fStorage.resize(count);
|
|
}
|
|
|
|
void reserve(int n) {
|
|
fStorage.reserve(n);
|
|
}
|
|
|
|
T* append() {
|
|
fStorage.append();
|
|
return this->end() - 1;
|
|
}
|
|
T* append(int count) {
|
|
fStorage.append(count);
|
|
return this->end() - count;
|
|
}
|
|
T* append(int count, const T* src) {
|
|
return static_cast<T*>(fStorage.append(src, count));
|
|
}
|
|
|
|
T* insert(int index) {
|
|
return static_cast<T*>(fStorage.insert(index));
|
|
}
|
|
T* insert(int index, int count, const T* src = nullptr) {
|
|
return static_cast<T*>(fStorage.insert(index, count, src));
|
|
}
|
|
|
|
void remove(int index, int count = 1) {
|
|
fStorage.erase(index, count);
|
|
}
|
|
|
|
void removeShuffle(int index) {
|
|
fStorage.removeShuffle(index);
|
|
}
|
|
|
|
// routines to treat the array like a stack
|
|
void push_back(const T& v) {
|
|
this->append();
|
|
this->back() = v;
|
|
}
|
|
void pop_back() { fStorage.pop_back(); }
|
|
|
|
void shrink_to_fit() {
|
|
fStorage.shrink_to_fit();
|
|
}
|
|
|
|
private:
|
|
SkTDStorage fStorage;
|
|
};
|
|
|
|
template <typename T> static inline void swap(SkTDArray<T>& a, SkTDArray<T>& b) { a.swap(b); }
|
|
|
|
struct SkRect;
|
|
|
|
|
|
// FIXME: move everything below into the SkPath class
|
|
/**
|
|
* The logical operations that can be performed when combining two paths.
|
|
*/
|
|
enum SkPathOp {
|
|
kDifference_SkPathOp, //!< subtract the op path from the first path
|
|
kIntersect_SkPathOp, //!< intersect the two paths
|
|
kUnion_SkPathOp, //!< union (inclusive-or) the two paths
|
|
kXOR_SkPathOp, //!< exclusive-or the two paths
|
|
kReverseDifference_SkPathOp, //!< subtract the first path from the op path
|
|
};
|
|
|
|
/** Set this path to the result of applying the Op to this path and the
|
|
specified path: this = (this op operand).
|
|
The resulting path will be constructed from non-overlapping contours.
|
|
The curve order is reduced where possible so that cubics may be turned
|
|
into quadratics, and quadratics maybe turned into lines.
|
|
|
|
Returns true if operation was able to produce a result;
|
|
otherwise, result is unmodified.
|
|
|
|
@param one The first operand (for difference, the minuend)
|
|
@param two The second operand (for difference, the subtrahend)
|
|
@param op The operator to apply.
|
|
@param result The product of the operands. The result may be one of the
|
|
inputs.
|
|
@return True if the operation succeeded.
|
|
*/
|
|
bool SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result);
|
|
|
|
/** Set this path to a set of non-overlapping contours that describe the
|
|
same area as the original path.
|
|
The curve order is reduced where possible so that cubics may
|
|
be turned into quadratics, and quadratics maybe turned into lines.
|
|
|
|
Returns true if operation was able to produce a result;
|
|
otherwise, result is unmodified.
|
|
|
|
@param path The path to simplify.
|
|
@param result The simplified path. The result may be the input.
|
|
@return True if simplification succeeded.
|
|
*/
|
|
bool SK_API Simplify(const SkPath& path, SkPath* result);
|
|
|
|
/** Set the resulting rectangle to the tight bounds of the path.
|
|
|
|
@param path The path measured.
|
|
@param result The tight bounds of the path.
|
|
@return True if the bounds could be computed.
|
|
*/
|
|
bool SK_API TightBounds(const SkPath& path, SkRect* result);
|
|
|
|
/** Set the result with fill type winding to area equivalent to path.
|
|
Returns true if successful. Does not detect if path contains contours which
|
|
contain self-crossings or cross other contours; in these cases, may return
|
|
true even though result does not fill same area as path.
|
|
|
|
Returns true if operation was able to produce a result;
|
|
otherwise, result is unmodified. The result may be the input.
|
|
|
|
@param path The path typically with fill type set to even odd.
|
|
@param result The equivalent path with fill type set to winding.
|
|
@return True if winding path was set.
|
|
*/
|
|
bool SK_API AsWinding(const SkPath& path, SkPath* result);
|
|
|
|
/** Perform a series of path operations, optimized for unioning many paths together.
|
|
*/
|
|
class SK_API SkOpBuilder {
|
|
public:
|
|
/** Add one or more paths and their operand. The builder is empty before the first
|
|
path is added, so the result of a single add is (emptyPath OP path).
|
|
|
|
@param path The second operand.
|
|
@param _operator The operator to apply to the existing and supplied paths.
|
|
*/
|
|
void add(const SkPath& path, SkPathOp _operator);
|
|
|
|
/** Computes the sum of all paths and operands, and resets the builder to its
|
|
initial state.
|
|
|
|
@param result The product of the operands.
|
|
@return True if the operation succeeded.
|
|
*/
|
|
bool resolve(SkPath* result);
|
|
|
|
private:
|
|
skia_private::TArray<SkPath> fPathRefs;
|
|
SkTDArray<SkPathOp> fOps;
|
|
|
|
static bool FixWinding(SkPath* path);
|
|
static void ReversePath(SkPath* path);
|
|
void reset();
|
|
};
|
|
|
|
class SkString;
|
|
|
|
class SK_API SkParsePath {
|
|
public:
|
|
static bool FromSVGString(const char str[], SkPath*);
|
|
|
|
enum class PathEncoding { Absolute, Relative };
|
|
static SkString ToSVGString(const SkPath&, PathEncoding = PathEncoding::Absolute);
|
|
};
|
|
|
|
#ifdef SKIA_SIMPLIFY_NAMESPACE
|
|
} // namespace SKIA_SIMPLIFY_NAMESPACE
|
|
#endif
|
|
|
|
#undef TArray
|