1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/PathCairo.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,323 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "PathCairo.h" 1.10 +#include <math.h> 1.11 +#include "DrawTargetCairo.h" 1.12 +#include "Logging.h" 1.13 +#include "PathHelpers.h" 1.14 +#include "HelpersCairo.h" 1.15 + 1.16 +namespace mozilla { 1.17 +namespace gfx { 1.18 + 1.19 +PathBuilderCairo::PathBuilderCairo(FillRule aFillRule) 1.20 + : mFillRule(aFillRule) 1.21 +{ 1.22 +} 1.23 + 1.24 +void 1.25 +PathBuilderCairo::MoveTo(const Point &aPoint) 1.26 +{ 1.27 + cairo_path_data_t data; 1.28 + data.header.type = CAIRO_PATH_MOVE_TO; 1.29 + data.header.length = 2; 1.30 + mPathData.push_back(data); 1.31 + data.point.x = aPoint.x; 1.32 + data.point.y = aPoint.y; 1.33 + mPathData.push_back(data); 1.34 + 1.35 + mBeginPoint = mCurrentPoint = aPoint; 1.36 +} 1.37 + 1.38 +void 1.39 +PathBuilderCairo::LineTo(const Point &aPoint) 1.40 +{ 1.41 + cairo_path_data_t data; 1.42 + data.header.type = CAIRO_PATH_LINE_TO; 1.43 + data.header.length = 2; 1.44 + mPathData.push_back(data); 1.45 + data.point.x = aPoint.x; 1.46 + data.point.y = aPoint.y; 1.47 + mPathData.push_back(data); 1.48 + 1.49 + mCurrentPoint = aPoint; 1.50 +} 1.51 + 1.52 +void 1.53 +PathBuilderCairo::BezierTo(const Point &aCP1, 1.54 + const Point &aCP2, 1.55 + const Point &aCP3) 1.56 +{ 1.57 + cairo_path_data_t data; 1.58 + data.header.type = CAIRO_PATH_CURVE_TO; 1.59 + data.header.length = 4; 1.60 + mPathData.push_back(data); 1.61 + data.point.x = aCP1.x; 1.62 + data.point.y = aCP1.y; 1.63 + mPathData.push_back(data); 1.64 + data.point.x = aCP2.x; 1.65 + data.point.y = aCP2.y; 1.66 + mPathData.push_back(data); 1.67 + data.point.x = aCP3.x; 1.68 + data.point.y = aCP3.y; 1.69 + mPathData.push_back(data); 1.70 + 1.71 + mCurrentPoint = aCP3; 1.72 +} 1.73 + 1.74 +void 1.75 +PathBuilderCairo::QuadraticBezierTo(const Point &aCP1, 1.76 + const Point &aCP2) 1.77 +{ 1.78 + // We need to elevate the degree of this quadratic Bézier to cubic, so we're 1.79 + // going to add an intermediate control point, and recompute control point 1. 1.80 + // The first and last control points remain the same. 1.81 + // This formula can be found on http://fontforge.sourceforge.net/bezier.html 1.82 + Point CP0 = CurrentPoint(); 1.83 + Point CP1 = (CP0 + aCP1 * 2.0) / 3.0; 1.84 + Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0; 1.85 + Point CP3 = aCP2; 1.86 + 1.87 + cairo_path_data_t data; 1.88 + data.header.type = CAIRO_PATH_CURVE_TO; 1.89 + data.header.length = 4; 1.90 + mPathData.push_back(data); 1.91 + data.point.x = CP1.x; 1.92 + data.point.y = CP1.y; 1.93 + mPathData.push_back(data); 1.94 + data.point.x = CP2.x; 1.95 + data.point.y = CP2.y; 1.96 + mPathData.push_back(data); 1.97 + data.point.x = CP3.x; 1.98 + data.point.y = CP3.y; 1.99 + mPathData.push_back(data); 1.100 + 1.101 + mCurrentPoint = aCP2; 1.102 +} 1.103 + 1.104 +void 1.105 +PathBuilderCairo::Close() 1.106 +{ 1.107 + cairo_path_data_t data; 1.108 + data.header.type = CAIRO_PATH_CLOSE_PATH; 1.109 + data.header.length = 1; 1.110 + mPathData.push_back(data); 1.111 + 1.112 + mCurrentPoint = mBeginPoint; 1.113 +} 1.114 + 1.115 +void 1.116 +PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle, 1.117 + float aEndAngle, bool aAntiClockwise) 1.118 +{ 1.119 + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); 1.120 +} 1.121 + 1.122 +Point 1.123 +PathBuilderCairo::CurrentPoint() const 1.124 +{ 1.125 + return mCurrentPoint; 1.126 +} 1.127 + 1.128 +TemporaryRef<Path> 1.129 +PathBuilderCairo::Finish() 1.130 +{ 1.131 + return new PathCairo(mFillRule, mPathData, mCurrentPoint); 1.132 +} 1.133 + 1.134 +PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint) 1.135 + : mFillRule(aFillRule) 1.136 + , mContainingContext(nullptr) 1.137 + , mCurrentPoint(aCurrentPoint) 1.138 +{ 1.139 + mPathData.swap(aPathData); 1.140 +} 1.141 + 1.142 +PathCairo::PathCairo(cairo_t *aContext) 1.143 + : mFillRule(FillRule::FILL_WINDING) 1.144 + , mContainingContext(nullptr) 1.145 +{ 1.146 + cairo_path_t *path = cairo_copy_path(aContext); 1.147 + 1.148 + // XXX - mCurrentPoint is not properly set here, the same is true for the 1.149 + // D2D Path code, we never require current point when hitting this codepath 1.150 + // but this should be fixed. 1.151 + for (int i = 0; i < path->num_data; i++) { 1.152 + mPathData.push_back(path->data[i]); 1.153 + } 1.154 + 1.155 + cairo_path_destroy(path); 1.156 +} 1.157 + 1.158 +PathCairo::~PathCairo() 1.159 +{ 1.160 + if (mContainingContext) { 1.161 + cairo_destroy(mContainingContext); 1.162 + } 1.163 +} 1.164 + 1.165 +TemporaryRef<PathBuilder> 1.166 +PathCairo::CopyToBuilder(FillRule aFillRule) const 1.167 +{ 1.168 + RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule); 1.169 + 1.170 + builder->mPathData = mPathData; 1.171 + builder->mCurrentPoint = mCurrentPoint; 1.172 + 1.173 + return builder; 1.174 +} 1.175 + 1.176 +TemporaryRef<PathBuilder> 1.177 +PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const 1.178 +{ 1.179 + RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule); 1.180 + 1.181 + AppendPathToBuilder(builder, &aTransform); 1.182 + builder->mCurrentPoint = aTransform * mCurrentPoint; 1.183 + 1.184 + return builder; 1.185 +} 1.186 + 1.187 +bool 1.188 +PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const 1.189 +{ 1.190 + Matrix inverse = aTransform; 1.191 + inverse.Invert(); 1.192 + Point transformed = inverse * aPoint; 1.193 + 1.194 + EnsureContainingContext(); 1.195 + 1.196 + return cairo_in_fill(mContainingContext, transformed.x, transformed.y); 1.197 +} 1.198 + 1.199 +bool 1.200 +PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions, 1.201 + const Point &aPoint, 1.202 + const Matrix &aTransform) const 1.203 +{ 1.204 + Matrix inverse = aTransform; 1.205 + inverse.Invert(); 1.206 + Point transformed = inverse * aPoint; 1.207 + 1.208 + EnsureContainingContext(); 1.209 + 1.210 + SetCairoStrokeOptions(mContainingContext, aStrokeOptions); 1.211 + 1.212 + return cairo_in_stroke(mContainingContext, transformed.x, transformed.y); 1.213 +} 1.214 + 1.215 +Rect 1.216 +PathCairo::GetBounds(const Matrix &aTransform) const 1.217 +{ 1.218 + EnsureContainingContext(); 1.219 + 1.220 + double x1, y1, x2, y2; 1.221 + 1.222 + cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2); 1.223 + Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1)); 1.224 + return aTransform.TransformBounds(bounds); 1.225 +} 1.226 + 1.227 +Rect 1.228 +PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions, 1.229 + const Matrix &aTransform) const 1.230 +{ 1.231 + EnsureContainingContext(); 1.232 + 1.233 + double x1, y1, x2, y2; 1.234 + 1.235 + SetCairoStrokeOptions(mContainingContext, aStrokeOptions); 1.236 + 1.237 + cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2); 1.238 + Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1)); 1.239 + return aTransform.TransformBounds(bounds); 1.240 +} 1.241 + 1.242 +void 1.243 +PathCairo::StreamToSink(PathSink *aSink) const 1.244 +{ 1.245 + for (size_t i = 0; i < mPathData.size(); i++) { 1.246 + switch (mPathData[i].header.type) { 1.247 + case CAIRO_PATH_MOVE_TO: 1.248 + i++; 1.249 + aSink->MoveTo(Point(mPathData[i].point.x, mPathData[i].point.y)); 1.250 + break; 1.251 + case CAIRO_PATH_LINE_TO: 1.252 + i++; 1.253 + aSink->LineTo(Point(mPathData[i].point.x, mPathData[i].point.y)); 1.254 + break; 1.255 + case CAIRO_PATH_CURVE_TO: 1.256 + aSink->BezierTo(Point(mPathData[i + 1].point.x, mPathData[i + 1].point.y), 1.257 + Point(mPathData[i + 2].point.x, mPathData[i + 2].point.y), 1.258 + Point(mPathData[i + 3].point.x, mPathData[i + 3].point.y)); 1.259 + i += 3; 1.260 + break; 1.261 + case CAIRO_PATH_CLOSE_PATH: 1.262 + aSink->Close(); 1.263 + break; 1.264 + default: 1.265 + // Corrupt path data! 1.266 + MOZ_ASSERT(false); 1.267 + } 1.268 + } 1.269 +} 1.270 + 1.271 +void 1.272 +PathCairo::EnsureContainingContext() const 1.273 +{ 1.274 + if (mContainingContext) { 1.275 + return; 1.276 + } 1.277 + 1.278 + mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface()); 1.279 + 1.280 + SetPathOnContext(mContainingContext); 1.281 +} 1.282 + 1.283 +void 1.284 +PathCairo::SetPathOnContext(cairo_t *aContext) const 1.285 +{ 1.286 + // Needs the correct fill rule set. 1.287 + cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule)); 1.288 + 1.289 + cairo_new_path(aContext); 1.290 + 1.291 + if (mPathData.size()) { 1.292 + cairo_path_t path; 1.293 + path.data = const_cast<cairo_path_data_t*>(&mPathData.front()); 1.294 + path.num_data = mPathData.size(); 1.295 + path.status = CAIRO_STATUS_SUCCESS; 1.296 + cairo_append_path(aContext, &path); 1.297 + } 1.298 +} 1.299 + 1.300 +void 1.301 +PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const 1.302 +{ 1.303 + if (aTransform) { 1.304 + size_t i = 0; 1.305 + while (i < mPathData.size()) { 1.306 + uint32_t pointCount = mPathData[i].header.length - 1; 1.307 + aBuilder->mPathData.push_back(mPathData[i]); 1.308 + i++; 1.309 + for (uint32_t c = 0; c < pointCount; c++) { 1.310 + cairo_path_data_t data; 1.311 + Point newPoint = *aTransform * Point(mPathData[i].point.x, mPathData[i].point.y); 1.312 + data.point.x = newPoint.x; 1.313 + data.point.y = newPoint.y; 1.314 + aBuilder->mPathData.push_back(data); 1.315 + i++; 1.316 + } 1.317 + } 1.318 + } else { 1.319 + for (size_t i = 0; i < mPathData.size(); i++) { 1.320 + aBuilder->mPathData.push_back(mPathData[i]); 1.321 + } 1.322 + } 1.323 +} 1.324 + 1.325 +} 1.326 +}