aboutsummaryrefslogtreecommitdiff
path: root/src/platform/graphics/quartz/Painter.cpp
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-05-15 14:08:06 +0800
committercrupest <crupest@outlook.com>2022-05-15 14:08:06 +0800
commit8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6 (patch)
tree77e41cc14264060517c0f7ed95837012afb8342e /src/platform/graphics/quartz/Painter.cpp
parent9e0c9d3499bc50c3534b4dc500d8b5d0b5f22752 (diff)
downloadcru-8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6.tar.gz
cru-8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6.tar.bz2
cru-8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6.zip
...
Diffstat (limited to 'src/platform/graphics/quartz/Painter.cpp')
-rw-r--r--src/platform/graphics/quartz/Painter.cpp230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/platform/graphics/quartz/Painter.cpp b/src/platform/graphics/quartz/Painter.cpp
new file mode 100644
index 00000000..69e187c3
--- /dev/null
+++ b/src/platform/graphics/quartz/Painter.cpp
@@ -0,0 +1,230 @@
+#include "cru/platform/graphics/quartz/Painter.h"
+
+#include "cru/platform/graphics/quartz/Brush.h"
+#include "cru/platform/graphics/quartz/Convert.h"
+#include "cru/platform/graphics/quartz/Geometry.h"
+#include "cru/platform/graphics/quartz/Image.h"
+#include "cru/platform/graphics/quartz/TextLayout.h"
+#include "cru/platform/Check.h"
+#include "cru/platform/Color.h"
+#include "cru/platform/Exception.h"
+
+namespace cru::platform::graphics::quartz {
+QuartzCGContextPainter::QuartzCGContextPainter(
+ IGraphicsFactory* graphics_factory, CGContextRef cg_context,
+ bool auto_release, const Size& size,
+ std::function<void(QuartzCGContextPainter*)> on_end_draw)
+ : OsxQuartzResource(graphics_factory),
+ cg_context_(cg_context),
+ auto_release_(auto_release),
+ size_(size),
+ on_end_draw_(std::move(on_end_draw)) {
+ Expects(cg_context);
+
+ CGContextConcatCTM(cg_context_,
+ CGAffineTransformInvert(CGContextGetCTM(cg_context_)));
+
+ transform_ = Matrix::Scale(1, -1) * Matrix::Translation(0, size.height);
+ CGContextConcatCTM(cg_context_, Convert(transform_));
+}
+
+QuartzCGContextPainter::~QuartzCGContextPainter() {
+ DoEndDraw();
+ if (auto_release_) {
+ CGContextRelease(cg_context_);
+ cg_context_ = nullptr;
+ }
+}
+
+Matrix QuartzCGContextPainter::GetTransform() { return transform_; }
+
+void QuartzCGContextPainter::SetTransform(const Matrix& matrix) {
+ CGContextConcatCTM(cg_context_, Convert(*transform_.Inverted()));
+ CGContextConcatCTM(cg_context_, Convert(matrix));
+ transform_ = matrix;
+}
+
+void QuartzCGContextPainter::ConcatTransform(const Matrix& matrix) {
+ CGContextConcatCTM(cg_context_, Convert(matrix));
+ transform_ = matrix * transform_;
+}
+
+void QuartzCGContextPainter::Clear(const Color& color) {
+ Validate();
+
+ CGContextSetRGBFillColor(cg_context_, color.GetFloatRed(),
+ color.GetFloatGreen(), color.GetFloatBlue(),
+ color.GetFloatAlpha());
+ CGContextFillRect(cg_context_, Convert(Rect{Point{}, size_}));
+}
+
+void QuartzCGContextPainter::DrawLine(const Point& start, const Point& end,
+ IBrush* brush, float width) {
+ Validate();
+
+ CGContextBeginPath(cg_context_);
+ CGContextMoveToPoint(cg_context_, start.x, start.y);
+ CGContextAddLineToPoint(cg_context_, end.x, end.y);
+
+ QuartzBrush* b = CheckPlatform<QuartzBrush>(brush, GetPlatformId());
+ b->Select(cg_context_);
+ SetLineWidth(width);
+
+ CGContextStrokePath(cg_context_);
+}
+
+void QuartzCGContextPainter::StrokeRectangle(const Rect& rectangle,
+ IBrush* brush, float width) {
+ Validate();
+
+ QuartzBrush* b = CheckPlatform<QuartzBrush>(brush, GetPlatformId());
+ b->Select(cg_context_);
+ CGContextStrokeRectWithWidth(cg_context_, Convert(rectangle), width);
+}
+
+void QuartzCGContextPainter::FillRectangle(const Rect& rectangle,
+ IBrush* brush) {
+ Validate();
+
+ QuartzBrush* b = CheckPlatform<QuartzBrush>(brush, GetPlatformId());
+ b->Select(cg_context_);
+ CGContextFillRect(cg_context_, Convert(rectangle));
+}
+
+void QuartzCGContextPainter::StrokeEllipse(const Rect& outline_rect,
+ IBrush* brush, float width) {
+ Validate();
+
+ QuartzBrush* b = CheckPlatform<QuartzBrush>(brush, GetPlatformId());
+ b->Select(cg_context_);
+ SetLineWidth(width);
+
+ CGContextStrokeEllipseInRect(cg_context_, Convert(outline_rect));
+}
+
+void QuartzCGContextPainter::FillEllipse(const Rect& outline_rect,
+ IBrush* brush) {
+ Validate();
+
+ QuartzBrush* b = CheckPlatform<QuartzBrush>(brush, GetPlatformId());
+ b->Select(cg_context_);
+ CGContextFillEllipseInRect(cg_context_, Convert(outline_rect));
+}
+
+void QuartzCGContextPainter::StrokeGeometry(IGeometry* geometry, IBrush* brush,
+ float width) {
+ Validate();
+
+ QuartzGeometry* g = CheckPlatform<QuartzGeometry>(geometry, GetPlatformId());
+ QuartzBrush* b = CheckPlatform<QuartzBrush>(brush, GetPlatformId());
+
+ b->Select(cg_context_);
+ SetLineWidth(width);
+
+ CGContextBeginPath(cg_context_);
+ CGContextAddPath(cg_context_, g->GetCGPath());
+ CGContextStrokePath(cg_context_);
+}
+
+void QuartzCGContextPainter::FillGeometry(IGeometry* geometry, IBrush* brush) {
+ Validate();
+
+ QuartzGeometry* g = CheckPlatform<QuartzGeometry>(geometry, GetPlatformId());
+ QuartzBrush* b = CheckPlatform<QuartzBrush>(brush, GetPlatformId());
+
+ b->Select(cg_context_);
+ CGContextBeginPath(cg_context_);
+ CGContextAddPath(cg_context_, g->GetCGPath());
+ CGContextEOFillPath(cg_context_);
+}
+
+void QuartzCGContextPainter::DrawText(const Point& offset,
+ ITextLayout* text_layout, IBrush* brush) {
+ Validate();
+
+ auto tl = CheckPlatform<OsxCTTextLayout>(text_layout, GetPlatformId());
+
+ Color color;
+
+ if (auto b = dynamic_cast<QuartzSolidColorBrush*>(brush)) {
+ color = b->GetColor();
+ } else {
+ color = colors::black;
+ }
+
+ Matrix transform = tl->GetTransform();
+
+ CGContextSaveGState(cg_context_);
+
+ CGContextConcatCTM(cg_context_, Convert(transform * Matrix::Translation(
+ offset.x, offset.y)));
+
+ auto frame = tl->CreateFrameWithColor(color);
+ Ensures(frame);
+ CTFrameDraw(frame, cg_context_);
+ CFRelease(frame);
+
+ CGContextRestoreGState(cg_context_);
+}
+
+void QuartzCGContextPainter::DrawImage(const Point& offset, IImage* image) {
+ Validate();
+ auto i = CheckPlatform<QuartzImage>(image, GetPlatformId());
+
+ auto cg_image = i->GetCGImage();
+
+ auto width = CGImageGetWidth(cg_image);
+ auto height = CGImageGetHeight(cg_image);
+
+ CGContextDrawImage(cg_context_, CGRectMake(offset.x, offset.y, width, height),
+ cg_image);
+}
+
+void QuartzCGContextPainter::PushLayer(const Rect& bounds) {
+ Validate();
+ clip_stack_.push_back(bounds);
+ CGContextClipToRect(cg_context_, Convert(bounds));
+}
+
+void QuartzCGContextPainter::PopLayer() {
+ Validate();
+ clip_stack_.pop_back();
+ if (clip_stack_.empty()) {
+ CGContextResetClip(cg_context_);
+ } else {
+ CGContextClipToRect(cg_context_, Convert(clip_stack_.back()));
+ }
+}
+
+void QuartzCGContextPainter::PushState() {
+ Validate();
+ CGContextSaveGState(cg_context_);
+}
+
+void QuartzCGContextPainter::PopState() {
+ Validate();
+ CGContextRestoreGState(cg_context_);
+}
+
+void QuartzCGContextPainter::EndDraw() { DoEndDraw(); }
+
+void QuartzCGContextPainter::SetLineWidth(float width) {
+ if (cg_context_) {
+ CGContextSetLineWidth(cg_context_, width);
+ }
+}
+
+void QuartzCGContextPainter::DoEndDraw() {
+ if (cg_context_) {
+ CGContextFlush(cg_context_);
+ CGContextSynchronize(cg_context_);
+
+ on_end_draw_(this);
+ }
+}
+
+void QuartzCGContextPainter::Validate() {
+ if (cg_context_ == nullptr)
+ throw ReuseException(u"QuartzCGContextPainter has already be released.");
+}
+} // namespace cru::platform::graphics::quartz