gfx/2d/PathCairo.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "PathCairo.h"
michael@0 7 #include <math.h>
michael@0 8 #include "DrawTargetCairo.h"
michael@0 9 #include "Logging.h"
michael@0 10 #include "PathHelpers.h"
michael@0 11 #include "HelpersCairo.h"
michael@0 12
michael@0 13 namespace mozilla {
michael@0 14 namespace gfx {
michael@0 15
michael@0 16 PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
michael@0 17 : mFillRule(aFillRule)
michael@0 18 {
michael@0 19 }
michael@0 20
michael@0 21 void
michael@0 22 PathBuilderCairo::MoveTo(const Point &aPoint)
michael@0 23 {
michael@0 24 cairo_path_data_t data;
michael@0 25 data.header.type = CAIRO_PATH_MOVE_TO;
michael@0 26 data.header.length = 2;
michael@0 27 mPathData.push_back(data);
michael@0 28 data.point.x = aPoint.x;
michael@0 29 data.point.y = aPoint.y;
michael@0 30 mPathData.push_back(data);
michael@0 31
michael@0 32 mBeginPoint = mCurrentPoint = aPoint;
michael@0 33 }
michael@0 34
michael@0 35 void
michael@0 36 PathBuilderCairo::LineTo(const Point &aPoint)
michael@0 37 {
michael@0 38 cairo_path_data_t data;
michael@0 39 data.header.type = CAIRO_PATH_LINE_TO;
michael@0 40 data.header.length = 2;
michael@0 41 mPathData.push_back(data);
michael@0 42 data.point.x = aPoint.x;
michael@0 43 data.point.y = aPoint.y;
michael@0 44 mPathData.push_back(data);
michael@0 45
michael@0 46 mCurrentPoint = aPoint;
michael@0 47 }
michael@0 48
michael@0 49 void
michael@0 50 PathBuilderCairo::BezierTo(const Point &aCP1,
michael@0 51 const Point &aCP2,
michael@0 52 const Point &aCP3)
michael@0 53 {
michael@0 54 cairo_path_data_t data;
michael@0 55 data.header.type = CAIRO_PATH_CURVE_TO;
michael@0 56 data.header.length = 4;
michael@0 57 mPathData.push_back(data);
michael@0 58 data.point.x = aCP1.x;
michael@0 59 data.point.y = aCP1.y;
michael@0 60 mPathData.push_back(data);
michael@0 61 data.point.x = aCP2.x;
michael@0 62 data.point.y = aCP2.y;
michael@0 63 mPathData.push_back(data);
michael@0 64 data.point.x = aCP3.x;
michael@0 65 data.point.y = aCP3.y;
michael@0 66 mPathData.push_back(data);
michael@0 67
michael@0 68 mCurrentPoint = aCP3;
michael@0 69 }
michael@0 70
michael@0 71 void
michael@0 72 PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
michael@0 73 const Point &aCP2)
michael@0 74 {
michael@0 75 // We need to elevate the degree of this quadratic Bézier to cubic, so we're
michael@0 76 // going to add an intermediate control point, and recompute control point 1.
michael@0 77 // The first and last control points remain the same.
michael@0 78 // This formula can be found on http://fontforge.sourceforge.net/bezier.html
michael@0 79 Point CP0 = CurrentPoint();
michael@0 80 Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
michael@0 81 Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
michael@0 82 Point CP3 = aCP2;
michael@0 83
michael@0 84 cairo_path_data_t data;
michael@0 85 data.header.type = CAIRO_PATH_CURVE_TO;
michael@0 86 data.header.length = 4;
michael@0 87 mPathData.push_back(data);
michael@0 88 data.point.x = CP1.x;
michael@0 89 data.point.y = CP1.y;
michael@0 90 mPathData.push_back(data);
michael@0 91 data.point.x = CP2.x;
michael@0 92 data.point.y = CP2.y;
michael@0 93 mPathData.push_back(data);
michael@0 94 data.point.x = CP3.x;
michael@0 95 data.point.y = CP3.y;
michael@0 96 mPathData.push_back(data);
michael@0 97
michael@0 98 mCurrentPoint = aCP2;
michael@0 99 }
michael@0 100
michael@0 101 void
michael@0 102 PathBuilderCairo::Close()
michael@0 103 {
michael@0 104 cairo_path_data_t data;
michael@0 105 data.header.type = CAIRO_PATH_CLOSE_PATH;
michael@0 106 data.header.length = 1;
michael@0 107 mPathData.push_back(data);
michael@0 108
michael@0 109 mCurrentPoint = mBeginPoint;
michael@0 110 }
michael@0 111
michael@0 112 void
michael@0 113 PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
michael@0 114 float aEndAngle, bool aAntiClockwise)
michael@0 115 {
michael@0 116 ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
michael@0 117 }
michael@0 118
michael@0 119 Point
michael@0 120 PathBuilderCairo::CurrentPoint() const
michael@0 121 {
michael@0 122 return mCurrentPoint;
michael@0 123 }
michael@0 124
michael@0 125 TemporaryRef<Path>
michael@0 126 PathBuilderCairo::Finish()
michael@0 127 {
michael@0 128 return new PathCairo(mFillRule, mPathData, mCurrentPoint);
michael@0 129 }
michael@0 130
michael@0 131 PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
michael@0 132 : mFillRule(aFillRule)
michael@0 133 , mContainingContext(nullptr)
michael@0 134 , mCurrentPoint(aCurrentPoint)
michael@0 135 {
michael@0 136 mPathData.swap(aPathData);
michael@0 137 }
michael@0 138
michael@0 139 PathCairo::PathCairo(cairo_t *aContext)
michael@0 140 : mFillRule(FillRule::FILL_WINDING)
michael@0 141 , mContainingContext(nullptr)
michael@0 142 {
michael@0 143 cairo_path_t *path = cairo_copy_path(aContext);
michael@0 144
michael@0 145 // XXX - mCurrentPoint is not properly set here, the same is true for the
michael@0 146 // D2D Path code, we never require current point when hitting this codepath
michael@0 147 // but this should be fixed.
michael@0 148 for (int i = 0; i < path->num_data; i++) {
michael@0 149 mPathData.push_back(path->data[i]);
michael@0 150 }
michael@0 151
michael@0 152 cairo_path_destroy(path);
michael@0 153 }
michael@0 154
michael@0 155 PathCairo::~PathCairo()
michael@0 156 {
michael@0 157 if (mContainingContext) {
michael@0 158 cairo_destroy(mContainingContext);
michael@0 159 }
michael@0 160 }
michael@0 161
michael@0 162 TemporaryRef<PathBuilder>
michael@0 163 PathCairo::CopyToBuilder(FillRule aFillRule) const
michael@0 164 {
michael@0 165 RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
michael@0 166
michael@0 167 builder->mPathData = mPathData;
michael@0 168 builder->mCurrentPoint = mCurrentPoint;
michael@0 169
michael@0 170 return builder;
michael@0 171 }
michael@0 172
michael@0 173 TemporaryRef<PathBuilder>
michael@0 174 PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
michael@0 175 {
michael@0 176 RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
michael@0 177
michael@0 178 AppendPathToBuilder(builder, &aTransform);
michael@0 179 builder->mCurrentPoint = aTransform * mCurrentPoint;
michael@0 180
michael@0 181 return builder;
michael@0 182 }
michael@0 183
michael@0 184 bool
michael@0 185 PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
michael@0 186 {
michael@0 187 Matrix inverse = aTransform;
michael@0 188 inverse.Invert();
michael@0 189 Point transformed = inverse * aPoint;
michael@0 190
michael@0 191 EnsureContainingContext();
michael@0 192
michael@0 193 return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
michael@0 194 }
michael@0 195
michael@0 196 bool
michael@0 197 PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
michael@0 198 const Point &aPoint,
michael@0 199 const Matrix &aTransform) const
michael@0 200 {
michael@0 201 Matrix inverse = aTransform;
michael@0 202 inverse.Invert();
michael@0 203 Point transformed = inverse * aPoint;
michael@0 204
michael@0 205 EnsureContainingContext();
michael@0 206
michael@0 207 SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
michael@0 208
michael@0 209 return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
michael@0 210 }
michael@0 211
michael@0 212 Rect
michael@0 213 PathCairo::GetBounds(const Matrix &aTransform) const
michael@0 214 {
michael@0 215 EnsureContainingContext();
michael@0 216
michael@0 217 double x1, y1, x2, y2;
michael@0 218
michael@0 219 cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
michael@0 220 Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
michael@0 221 return aTransform.TransformBounds(bounds);
michael@0 222 }
michael@0 223
michael@0 224 Rect
michael@0 225 PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
michael@0 226 const Matrix &aTransform) const
michael@0 227 {
michael@0 228 EnsureContainingContext();
michael@0 229
michael@0 230 double x1, y1, x2, y2;
michael@0 231
michael@0 232 SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
michael@0 233
michael@0 234 cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
michael@0 235 Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
michael@0 236 return aTransform.TransformBounds(bounds);
michael@0 237 }
michael@0 238
michael@0 239 void
michael@0 240 PathCairo::StreamToSink(PathSink *aSink) const
michael@0 241 {
michael@0 242 for (size_t i = 0; i < mPathData.size(); i++) {
michael@0 243 switch (mPathData[i].header.type) {
michael@0 244 case CAIRO_PATH_MOVE_TO:
michael@0 245 i++;
michael@0 246 aSink->MoveTo(Point(mPathData[i].point.x, mPathData[i].point.y));
michael@0 247 break;
michael@0 248 case CAIRO_PATH_LINE_TO:
michael@0 249 i++;
michael@0 250 aSink->LineTo(Point(mPathData[i].point.x, mPathData[i].point.y));
michael@0 251 break;
michael@0 252 case CAIRO_PATH_CURVE_TO:
michael@0 253 aSink->BezierTo(Point(mPathData[i + 1].point.x, mPathData[i + 1].point.y),
michael@0 254 Point(mPathData[i + 2].point.x, mPathData[i + 2].point.y),
michael@0 255 Point(mPathData[i + 3].point.x, mPathData[i + 3].point.y));
michael@0 256 i += 3;
michael@0 257 break;
michael@0 258 case CAIRO_PATH_CLOSE_PATH:
michael@0 259 aSink->Close();
michael@0 260 break;
michael@0 261 default:
michael@0 262 // Corrupt path data!
michael@0 263 MOZ_ASSERT(false);
michael@0 264 }
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 void
michael@0 269 PathCairo::EnsureContainingContext() const
michael@0 270 {
michael@0 271 if (mContainingContext) {
michael@0 272 return;
michael@0 273 }
michael@0 274
michael@0 275 mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
michael@0 276
michael@0 277 SetPathOnContext(mContainingContext);
michael@0 278 }
michael@0 279
michael@0 280 void
michael@0 281 PathCairo::SetPathOnContext(cairo_t *aContext) const
michael@0 282 {
michael@0 283 // Needs the correct fill rule set.
michael@0 284 cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
michael@0 285
michael@0 286 cairo_new_path(aContext);
michael@0 287
michael@0 288 if (mPathData.size()) {
michael@0 289 cairo_path_t path;
michael@0 290 path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
michael@0 291 path.num_data = mPathData.size();
michael@0 292 path.status = CAIRO_STATUS_SUCCESS;
michael@0 293 cairo_append_path(aContext, &path);
michael@0 294 }
michael@0 295 }
michael@0 296
michael@0 297 void
michael@0 298 PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
michael@0 299 {
michael@0 300 if (aTransform) {
michael@0 301 size_t i = 0;
michael@0 302 while (i < mPathData.size()) {
michael@0 303 uint32_t pointCount = mPathData[i].header.length - 1;
michael@0 304 aBuilder->mPathData.push_back(mPathData[i]);
michael@0 305 i++;
michael@0 306 for (uint32_t c = 0; c < pointCount; c++) {
michael@0 307 cairo_path_data_t data;
michael@0 308 Point newPoint = *aTransform * Point(mPathData[i].point.x, mPathData[i].point.y);
michael@0 309 data.point.x = newPoint.x;
michael@0 310 data.point.y = newPoint.y;
michael@0 311 aBuilder->mPathData.push_back(data);
michael@0 312 i++;
michael@0 313 }
michael@0 314 }
michael@0 315 } else {
michael@0 316 for (size_t i = 0; i < mPathData.size(); i++) {
michael@0 317 aBuilder->mPathData.push_back(mPathData[i]);
michael@0 318 }
michael@0 319 }
michael@0 320 }
michael@0 321
michael@0 322 }
michael@0 323 }

mercurial