From b227ad469b42d8287d7d56b7d3611cf168c09471 Mon Sep 17 00:00:00 2001 From: djowel Date: Fri, 15 Dec 2023 10:33:56 +0800 Subject: [PATCH] Correct device_to_user and user_to_device implementation for Quartz2D --- lib/impl/macos/quartz2d/canvas.mm | 50 ++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/lib/impl/macos/quartz2d/canvas.mm b/lib/impl/macos/quartz2d/canvas.mm index cf44bfa2..39b648f0 100755 --- a/lib/impl/macos/quartz2d/canvas.mm +++ b/lib/impl/macos/quartz2d/canvas.mm @@ -57,6 +57,11 @@ float scale() const { return _scale; } void scale(float sc) { _scale = sc; } + using cg_affine = CGAffineTransform; + + void get_inv_affine(CGContextRef context); + cg_affine const& get_inv_affine() const; + private: struct state_info @@ -79,11 +84,12 @@ state_info_stack _stack; fill_rule_enum _fill_rule = fill_rule_enum::fill_winding; float _scale; + CGAffineTransform _inv_affine; }; #pragma clang diagnostic ignored "-Wvla-extension" - namespace + namespace { void make_gradient(std::vector const& space, CGGradientRef& gradient) { @@ -196,6 +202,17 @@ void make_gradient(std::vector const& space, CGGradientRef& _stack.pop(); } + void canvas::canvas_state::get_inv_affine(CGContextRef context) + { + _inv_affine = CGAffineTransformInvert(CGContextGetCTM(context)); + } + + CGAffineTransform const& + canvas::canvas_state::get_inv_affine() const + { + return _inv_affine; + } + canvas::canvas(canvas_impl* context_) : _context{context_} , _state{std::make_unique()} @@ -208,6 +225,8 @@ void make_gradient(std::vector const& space, CGGradientRef& CGPoint user = {100, 100}; auto device = CGContextConvertPointToDeviceSpace(ctx, user); _state->scale(device.x / user.x); + + _state->get_inv_affine(ctx); } canvas::~canvas() @@ -251,20 +270,30 @@ void make_gradient(std::vector const& space, CGGradientRef& point canvas::device_to_user(point p) { - auto scale = _state->scale(); - auto up = CGContextConvertPointToUserSpace( - CGContextRef(_context), {p.x * scale, p.y * scale} - ); + // Get the current transform + auto af = CGContextGetCTM(CGContextRef(_context)); + + // Undo the initial transform + auto xaf = CGAffineTransformConcat(af, _state->get_inv_affine()); + + // Map the point to the inverted `xaf` transform + auto up = CGPointApplyAffineTransform({p.x, p.y}, CGAffineTransformInvert(xaf)); + return {float(up.x), float(up.y)}; } point canvas::user_to_device(point p) { - auto scale = _state->scale(); - auto dp = CGContextConvertPointToDeviceSpace( - CGContextRef(_context), {p.x, p.y} - ); - return {float(dp.x / scale), float(dp.y / scale)}; + // Get the current transform + auto af = CGContextGetCTM(CGContextRef(_context)); + + // Undo the initial transform + auto xaf = CGAffineTransformConcat(af, _state->get_inv_affine()); + + // Map the point to the `xaf` transform + auto up = CGPointApplyAffineTransform({p.x, p.y}, xaf); + + return {float(up.x), float(up.y)}; } affine_transform canvas::transform() const @@ -699,6 +728,7 @@ CTLineRef measure_text( auto line = CTLineCreateWithAttributedString(attr_string); width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); CFRelease(attr_string); + CFRelease(font_attributes); return line; }