aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/platform/graphics/Geometry.h59
-rw-r--r--include/cru/platform/graphics/SvgGeometryBuilderMixin.h (renamed from include/cru/platform/graphics/SvgGeometry.h)26
-rw-r--r--include/cru/platform/graphics/web_canvas/WebCanvasGeometry.h25
-rw-r--r--src/platform/graphics/CMakeLists.txt2
-rw-r--r--src/platform/graphics/Geometry.cpp8
-rw-r--r--src/platform/graphics/SvgGeometry.cpp127
-rw-r--r--src/platform/graphics/SvgGeometryBuilderMixin.cpp121
-rw-r--r--src/platform/graphics/web_canvas/Geometry.cpp8
8 files changed, 216 insertions, 160 deletions
diff --git a/include/cru/platform/graphics/Geometry.h b/include/cru/platform/graphics/Geometry.h
index 0e0278d2..71d003f6 100644
--- a/include/cru/platform/graphics/Geometry.h
+++ b/include/cru/platform/graphics/Geometry.h
@@ -4,17 +4,65 @@
#include <memory>
namespace cru::platform::graphics {
+/**
+ * \remarks Geometry implementation is a disaster zone of platform problems.
+ * Here are some notes. For geometry object:
+ * 1. Some platforms just lack the concept of geometry, like web canvas. Even if
+ * there is one, it usually has limited capabilities. They only allow you to
+ * create path right on the context and use it directly. The geometry itself
+ * can't be separated from context and stored independently, not to mention run
+ * some functions. You can't even create a geometry from current state of
+ * context. In one word, context -x-> geometry, geometry is weak.
+ * 2. Doing hit test right on the geometry is not supported on some platform.
+ * The geometry needs to be put on the context first.
+ * 3. Transform a geometry to create a new geometry is not supported on some
+ * platforms. You can transform the context, but no for geometry itself
+ * independently.
+ * 4. Create a geometry of a stroke of another geometry is not suppored more
+ * widely.
+ *
+ * Workarounds:
+ * 1. If the platform didn't support path at all, although not a platform does
+ * so far, record the commands and replay it on context when it is used.
+ * 2. If the geometry can't do hit test itself, always attach a canvas with it,
+ * and when do hit test, put it on context first.
+ * 3. If transform a geometry to create a new geometry is not supported, zip the
+ * original geometry and a transform matrix. When it is used on context, restore
+ * geometry and matrix the same time.
+ * 4. There is no good workaround for 4, so don't use it, maybe.
+ *
+ * Also note for 3, even we can workaround like that, but there are cases it
+ * does not work. Like if we add the support for adding an existing path to a
+ * new path, transform can only be performed on context and we can't construct a
+ * geometry from context, then it fails.
+ *
+ * For geometry builder:
+ * It is hard to conclude the common commands of all platforms. Some have this,
+ * others not. Some don't have this, but others do. There are some common
+ * commands that should be used frequently. If some platforms do not support it,
+ * do it yourself to implement it with existing commands and fancy algorithm.
+ *
+ */
struct CRU_PLATFORM_GRAPHICS_API IGeometry : virtual IGraphicsResource {
/**
- * \remarks Because I designed CreateStrokeGeometry first, which might be not
+ * \remarks Because I designed CreateStrokeGeometry first, which might be not
* that rational. So the default implementation calls that method and do the
* test. New implementation should override this.
*/
virtual bool StrokeContains(float width, const Point& point);
virtual bool FillContains(const Point& point) = 0;
virtual Rect GetBounds() = 0;
+
+ /**
+ * \remarks See class doc for platform limitation.
+ */
virtual std::unique_ptr<IGeometry> Transform(const Matrix& matrix) = 0;
- virtual std::unique_ptr<IGeometry> CreateStrokeGeometry(float width) = 0;
+
+ /**
+ * \remarks This method is a little quirky. It is originated from Direct2D,
+ * but many platforms do not support this, I guess. So don't use this anymore.
+ */
+ virtual std::unique_ptr<IGeometry> CreateStrokeGeometry(float width);
};
/**
@@ -50,12 +98,13 @@ struct CRU_PLATFORM_GRAPHICS_API IGeometryBuilder : virtual IGraphicsResource {
virtual void QuadraticBezierTo(const Point& control_point,
const Point& end_point) = 0;
virtual void RelativeQuadraticBezierTo(const Point& control_offset,
- const Point& end_offset);
+ const Point& end_offset);
virtual void ArcTo(const Point& radius, float angle, bool is_large_arc,
bool is_clockwise, const Point& end_point);
- virtual void RelativeArcTo(const Point& radius, float angle, bool is_large_arc,
- bool is_clockwise, const Point& end_offset);
+ virtual void RelativeArcTo(const Point& radius, float angle,
+ bool is_large_arc, bool is_clockwise,
+ const Point& end_offset);
virtual void CloseFigure(bool close) = 0;
diff --git a/include/cru/platform/graphics/SvgGeometry.h b/include/cru/platform/graphics/SvgGeometryBuilderMixin.h
index 65602578..d516ce6c 100644
--- a/include/cru/platform/graphics/SvgGeometry.h
+++ b/include/cru/platform/graphics/SvgGeometryBuilderMixin.h
@@ -1,28 +1,22 @@
#pragma once
#include "Geometry.h"
-#include "cru/common/Base.h"
#include "cru/common/Format.h"
#include <utility>
namespace cru::platform::graphics {
/**
- * \remarks This class is purely a helper for some platforms, especially web
- * canvas. It constructs a path data of svg of a list of commands. It can't
- * generate a Geometry. Calling Build will throw a PlatformUnsupportedException.
- * Instead, use GetPathData to get svg path data and use it to do other things.
+ * \remarks This class is a helper mixin for some platforms, especially web
+ * canvas. It constructs a svg data by recording commands. Use GetPathData to
+ * get svg path data and use it to build real geometry.
*/
-class CRU_PLATFORM_GRAPHICS_API SvgGeometryBuilder
- : public Object,
- public virtual IGeometryBuilder {
+class CRU_PLATFORM_GRAPHICS_API SvgGeometryBuilderMixin
+ : public virtual IGeometryBuilder {
public:
- SvgGeometryBuilder();
+ SvgGeometryBuilderMixin();
- CRU_DELETE_COPY(SvgGeometryBuilder)
- CRU_DELETE_MOVE(SvgGeometryBuilder)
-
- ~SvgGeometryBuilder() override;
+ ~SvgGeometryBuilderMixin() override;
Point GetCurrentPosition() override;
@@ -51,12 +45,12 @@ class CRU_PLATFORM_GRAPHICS_API SvgGeometryBuilder
void CloseFigure(bool close) override;
- std::unique_ptr<IGeometry> Build() override;
+ void ParseAndApplySvgPathData(StringView path_d) override;
+ protected:
String GetPathData() const { return current_; }
- void ParseAndApplySvgPathData(StringView path_d) override;
-
+ private:
template <typename... Args>
void Append(StringView format, Args&&... args) {
current_ += Format(format, std::forward<Args>(args)...);
diff --git a/include/cru/platform/graphics/web_canvas/WebCanvasGeometry.h b/include/cru/platform/graphics/web_canvas/WebCanvasGeometry.h
index af4cee91..dfb5bbe7 100644
--- a/include/cru/platform/graphics/web_canvas/WebCanvasGeometry.h
+++ b/include/cru/platform/graphics/web_canvas/WebCanvasGeometry.h
@@ -1,27 +1,36 @@
#pragma once
#include "../Geometry.h"
+#include "../SvgGeometryBuilderMixin.h"
#include "WebCanvasResource.h"
#include <emscripten/val.h>
+#include <memory>
namespace cru::platform::graphics::web_canvas {
+class WebCanvasGeometry : public WebCanvasResource, public virtual IGeometry {
+ public:
+ WebCanvasGeometry(WebCanvasGraphicsFactory* factory, emscripten::val path2d);
+ ~WebCanvasGeometry() override;
+
+ bool StrokeContains(float width, const Point& point) override;
+ virtual bool FillContains(const Point& point) = 0;
+ virtual Rect GetBounds() = 0;
+ virtual std::unique_ptr<IGeometry> Transform(const Matrix& matrix) = 0;
+ private:
+ emscripten::val path2d_;
+};
+
/**
* \remarks See IGeometryBuilder for platform limitation.
*/
class WebCanvasGeometryBuilder : public WebCanvasResource,
- public virtual IGeometryBuilder {
+ public SvgGeometryBuilderMixin {
public:
WebCanvasGeometryBuilder(WebCanvasGraphicsFactory* factory);
~WebCanvasGeometryBuilder() override;
- Point GetCurrentPosition() override;
-
- void MoveTo(const Point& point) override;
-
- private:
- Point current_postion_;
- emscripten::val path2d_;
+ std::unique_ptr<IGeometry> Build() override;
};
} // namespace cru::platform::graphics::web_canvas
diff --git a/src/platform/graphics/CMakeLists.txt b/src/platform/graphics/CMakeLists.txt
index 1b24c22b..43ef80e0 100644
--- a/src/platform/graphics/CMakeLists.txt
+++ b/src/platform/graphics/CMakeLists.txt
@@ -3,7 +3,7 @@ add_library(CruPlatformGraphics
Geometry.cpp
Image.cpp
NullPainter.cpp
- SvgGeometry.cpp
+ SvgGeometryBuilderMixin.cpp
)
target_compile_definitions(CruPlatformGraphics PRIVATE CRU_PLATFORM_GRAPHICS_EXPORT_API)
target_link_libraries(CruPlatformGraphics PUBLIC CruPlatformBase)
diff --git a/src/platform/graphics/Geometry.cpp b/src/platform/graphics/Geometry.cpp
index b6f2a724..0c98b456 100644
--- a/src/platform/graphics/Geometry.cpp
+++ b/src/platform/graphics/Geometry.cpp
@@ -1,6 +1,7 @@
#include "cru/platform/graphics/Geometry.h"
#include "cru/common/Exception.h"
+#include "cru/platform/Exception.h"
#include "cru/platform/graphics/Factory.h"
#include <cmath>
@@ -12,6 +13,13 @@ bool IGeometry::StrokeContains(float width, const Point& point) {
return geometry->FillContains(point);
}
+std::unique_ptr<IGeometry> IGeometry::CreateStrokeGeometry(
+ [[maybe_unused]] float width) {
+ throw PlatformUnsupportedException(GetPlatformId(), u"CreateStrokeGeometry",
+ u"Create stroke geometry of a geometry is "
+ u"not supported on this platform.");
+}
+
void IGeometryBuilder::RelativeMoveTo(const Point& offset) {
MoveTo(GetCurrentPosition() + offset);
}
diff --git a/src/platform/graphics/SvgGeometry.cpp b/src/platform/graphics/SvgGeometry.cpp
deleted file mode 100644
index 20ef1b56..00000000
--- a/src/platform/graphics/SvgGeometry.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-#include "cru/platform/graphics/SvgGeometry.h"
-#include "cru/platform/Exception.h"
-#include "cru/platform/graphics/Geometry.h"
-
-namespace cru::platform::graphics {
-SvgGeometryBuilder::SvgGeometryBuilder() {}
-
-SvgGeometryBuilder::~SvgGeometryBuilder() {}
-
-Point SvgGeometryBuilder::GetCurrentPosition() {
- throw PlatformUnsupportedException(
- GetPlatformId(), u"GetCurrentPosition",
- u"Svg geometry does not support get current position.");
-}
-
-void SvgGeometryBuilder::MoveTo(const Point& point) {
- AppendCommand(u"M");
- Append(point);
-}
-
-void SvgGeometryBuilder::RelativeMoveTo(const Point& offset) {
- AppendCommand(u"m");
- Append(offset);
-}
-
-void SvgGeometryBuilder::LineTo(const Point& point) {
- AppendCommand(u"L");
- Append(point);
-}
-
-void SvgGeometryBuilder::RelativeLineTo(const Point& offset) {
- AppendCommand(u"l");
- Append(offset);
-}
-
-void SvgGeometryBuilder::CubicBezierTo(const Point& start_control_point,
- const Point& end_control_point,
- const Point& end_point) {
- AppendCommand(u"C");
- Append(start_control_point);
- Append(end_control_point);
- Append(end_point);
-}
-
-void SvgGeometryBuilder::RelativeCubicBezierTo(
- const Point& start_control_offset, const Point& end_control_offset,
- const Point& end_offset) {
- AppendCommand(u"c");
- Append(start_control_offset);
- Append(end_control_offset);
- Append(end_offset);
-}
-
-void SvgGeometryBuilder::QuadraticBezierTo(const Point& control_point,
- const Point& end_point) {
- AppendCommand(u"Q");
- Append(control_point);
- Append(end_point);
-}
-
-void SvgGeometryBuilder::RelativeQuadraticBezierTo(const Point& control_offset,
- const Point& end_offset) {
- AppendCommand(u"q");
- Append(control_offset);
- Append(end_offset);
-}
-
-void SvgGeometryBuilder::ArcTo(const Point& radius, float angle,
- bool is_large_arc, bool is_clockwise,
- const Point& end_point) {
- AppendCommand(u"A");
- Append(radius.x);
- Append(radius.y);
- Append(angle);
- Append(is_large_arc);
- Append(is_clockwise);
- Append(end_point);
-}
-
-void SvgGeometryBuilder::RelativeArcTo(const Point& radius, float angle,
- bool is_large_arc, bool is_clockwise,
- const Point& end_offset) {
- AppendCommand(u"a");
- Append(radius.x);
- Append(radius.y);
- Append(angle);
- Append(is_large_arc);
- Append(is_clockwise);
- Append(end_offset);
-}
-
-void SvgGeometryBuilder::CloseFigure(bool close) {
- if (close) AppendCommand(u"z");
-}
-
-std::unique_ptr<IGeometry> SvgGeometryBuilder::Build() {
- throw PlatformUnsupportedException(
- u"SvgGeometryBuilder", u"Build",
- u"SvgGeometryBuilder can't build a Geometry. Use GetPathData instead.");
-}
-
-void SvgGeometryBuilder::ParseAndApplySvgPathData(StringView path_d) {
- AppendCommand(path_d);
-}
-
-void SvgGeometryBuilder::AppendCommand(StringView command) {
- current_ += command;
- current_ += u' ';
-}
-
-void SvgGeometryBuilder::Append(bool flag) {
- current_ += flag ? u'1' : u'0';
- current_ += u' ';
-}
-
-void SvgGeometryBuilder::Append(float number) {
- current_ += ToString(number);
- current_ += u' ';
-}
-
-void SvgGeometryBuilder::Append(const Point& point) {
- current_ += ToString(point.x);
- current_ += u',';
- current_ += ToString(point.y);
- current_ += u' ';
-}
-} // namespace cru::platform::graphics
diff --git a/src/platform/graphics/SvgGeometryBuilderMixin.cpp b/src/platform/graphics/SvgGeometryBuilderMixin.cpp
new file mode 100644
index 00000000..1f868f44
--- /dev/null
+++ b/src/platform/graphics/SvgGeometryBuilderMixin.cpp
@@ -0,0 +1,121 @@
+#include "cru/platform/graphics/SvgGeometryBuilderMixin.h"
+#include "cru/platform/Exception.h"
+
+namespace cru::platform::graphics {
+SvgGeometryBuilderMixin::SvgGeometryBuilderMixin() {}
+
+SvgGeometryBuilderMixin::~SvgGeometryBuilderMixin() {}
+
+Point SvgGeometryBuilderMixin::GetCurrentPosition() {
+ throw PlatformUnsupportedException(
+ GetPlatformId(), u"GetCurrentPosition",
+ u"Svg-based geometry does not support get current position.");
+}
+
+void SvgGeometryBuilderMixin::MoveTo(const Point& point) {
+ AppendCommand(u"M");
+ Append(point);
+}
+
+void SvgGeometryBuilderMixin::RelativeMoveTo(const Point& offset) {
+ AppendCommand(u"m");
+ Append(offset);
+}
+
+void SvgGeometryBuilderMixin::LineTo(const Point& point) {
+ AppendCommand(u"L");
+ Append(point);
+}
+
+void SvgGeometryBuilderMixin::RelativeLineTo(const Point& offset) {
+ AppendCommand(u"l");
+ Append(offset);
+}
+
+void SvgGeometryBuilderMixin::CubicBezierTo(const Point& start_control_point,
+ const Point& end_control_point,
+ const Point& end_point) {
+ AppendCommand(u"C");
+ Append(start_control_point);
+ Append(end_control_point);
+ Append(end_point);
+}
+
+void SvgGeometryBuilderMixin::RelativeCubicBezierTo(
+ const Point& start_control_offset, const Point& end_control_offset,
+ const Point& end_offset) {
+ AppendCommand(u"c");
+ Append(start_control_offset);
+ Append(end_control_offset);
+ Append(end_offset);
+}
+
+void SvgGeometryBuilderMixin::QuadraticBezierTo(const Point& control_point,
+ const Point& end_point) {
+ AppendCommand(u"Q");
+ Append(control_point);
+ Append(end_point);
+}
+
+void SvgGeometryBuilderMixin::RelativeQuadraticBezierTo(
+ const Point& control_offset, const Point& end_offset) {
+ AppendCommand(u"q");
+ Append(control_offset);
+ Append(end_offset);
+}
+
+void SvgGeometryBuilderMixin::ArcTo(const Point& radius, float angle,
+ bool is_large_arc, bool is_clockwise,
+ const Point& end_point) {
+ AppendCommand(u"A");
+ Append(radius.x);
+ Append(radius.y);
+ Append(angle);
+ Append(is_large_arc);
+ Append(is_clockwise);
+ Append(end_point);
+}
+
+void SvgGeometryBuilderMixin::RelativeArcTo(const Point& radius, float angle,
+ bool is_large_arc,
+ bool is_clockwise,
+ const Point& end_offset) {
+ AppendCommand(u"a");
+ Append(radius.x);
+ Append(radius.y);
+ Append(angle);
+ Append(is_large_arc);
+ Append(is_clockwise);
+ Append(end_offset);
+}
+
+void SvgGeometryBuilderMixin::CloseFigure(bool close) {
+ if (close) AppendCommand(u"z");
+}
+
+void SvgGeometryBuilderMixin::ParseAndApplySvgPathData(StringView path_d) {
+ AppendCommand(path_d);
+}
+
+void SvgGeometryBuilderMixin::AppendCommand(StringView command) {
+ current_ += command;
+ current_ += u' ';
+}
+
+void SvgGeometryBuilderMixin::Append(bool flag) {
+ current_ += flag ? u'1' : u'0';
+ current_ += u' ';
+}
+
+void SvgGeometryBuilderMixin::Append(float number) {
+ current_ += ToString(number);
+ current_ += u' ';
+}
+
+void SvgGeometryBuilderMixin::Append(const Point& point) {
+ current_ += ToString(point.x);
+ current_ += u',';
+ current_ += ToString(point.y);
+ current_ += u' ';
+}
+} // namespace cru::platform::graphics
diff --git a/src/platform/graphics/web_canvas/Geometry.cpp b/src/platform/graphics/web_canvas/Geometry.cpp
index eb4ce84e..0f2f5a16 100644
--- a/src/platform/graphics/web_canvas/Geometry.cpp
+++ b/src/platform/graphics/web_canvas/Geometry.cpp
@@ -4,9 +4,11 @@
#include "cru/platform/web/Js.h"
namespace cru::platform::graphics::web_canvas {
+bool WebCanvasGeometry::StrokeContains(float width, const Point& point) {}
+
WebCanvasGeometryBuilder::WebCanvasGeometryBuilder(
WebCanvasGraphicsFactory* factory)
- : WebCanvasResource(factory) {
- path2d_ = web::js::Construct("Path2D");
-}
+ : WebCanvasResource(factory) {}
+
+WebCanvasGeometryBuilder::~WebCanvasGeometryBuilder() {}
} // namespace cru::platform::graphics::web_canvas