Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "gfxTypes.h"
7 #include "gfxPattern.h"
8 #include "gfxASurface.h"
9 #include "gfxPlatform.h"
10 #include "gfx2DGlue.h"
11 #include "gfxGradientCache.h"
13 #include "cairo.h"
15 #include <vector>
17 using namespace mozilla::gfx;
19 gfxPattern::gfxPattern(cairo_pattern_t *aPattern)
20 : mGfxPattern(nullptr)
21 {
22 mPattern = cairo_pattern_reference(aPattern);
23 }
25 gfxPattern::gfxPattern(const gfxRGBA& aColor)
26 : mGfxPattern(nullptr)
27 {
28 mPattern = cairo_pattern_create_rgba(aColor.r, aColor.g, aColor.b, aColor.a);
29 }
31 // from another surface
32 gfxPattern::gfxPattern(gfxASurface *surface)
33 : mGfxPattern(nullptr)
34 {
35 mPattern = cairo_pattern_create_for_surface(surface->CairoSurface());
36 }
38 // linear
39 gfxPattern::gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1)
40 : mGfxPattern(nullptr)
41 {
42 mPattern = cairo_pattern_create_linear(x0, y0, x1, y1);
43 }
45 // radial
46 gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0,
47 gfxFloat cx1, gfxFloat cy1, gfxFloat radius1)
48 : mGfxPattern(nullptr)
49 {
50 mPattern = cairo_pattern_create_radial(cx0, cy0, radius0,
51 cx1, cy1, radius1);
52 }
54 // Azure
55 gfxPattern::gfxPattern(SourceSurface *aSurface, const Matrix &aTransform)
56 : mPattern(nullptr)
57 , mGfxPattern(nullptr)
58 , mSourceSurface(aSurface)
59 , mTransform(aTransform)
60 , mExtend(EXTEND_NONE)
61 {
62 }
64 gfxPattern::~gfxPattern()
65 {
66 cairo_pattern_destroy(mPattern);
68 if (mGfxPattern) {
69 mGfxPattern->~Pattern();
70 }
71 }
73 cairo_pattern_t *
74 gfxPattern::CairoPattern()
75 {
76 return mPattern;
77 }
79 void
80 gfxPattern::AddColorStop(gfxFloat offset, const gfxRGBA& c)
81 {
82 if (mPattern) {
83 mStops = nullptr;
84 if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
85 gfxRGBA cms;
86 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
87 if (transform)
88 gfxPlatform::TransformPixel(c, cms, transform);
90 // Use the original alpha to avoid unnecessary float->byte->float
91 // conversion errors
92 cairo_pattern_add_color_stop_rgba(mPattern, offset,
93 cms.r, cms.g, cms.b, c.a);
94 }
95 else
96 cairo_pattern_add_color_stop_rgba(mPattern, offset, c.r, c.g, c.b, c.a);
97 }
98 }
100 void
101 gfxPattern::SetColorStops(mozilla::RefPtr<mozilla::gfx::GradientStops> aStops)
102 {
103 mStops = aStops;
104 }
106 void
107 gfxPattern::CacheColorStops(mozilla::gfx::DrawTarget *aDT)
108 {
109 if (mPattern) {
110 mStops = nullptr;
111 nsTArray<GradientStop> stops;
112 int count = 0;
113 cairo_pattern_get_color_stop_count(mPattern, &count);
114 stops.SetLength(count);
115 for (int n = 0; n < count; ++n) {
116 double offset, r, g, b, a;
117 cairo_pattern_get_color_stop_rgba(mPattern, n, &offset, &r, &g, &b, &a);
118 stops[n].color = mozilla::gfx::Color(r, g, b, a);
119 stops[n].offset = offset;
120 }
121 mStops = gfxGradientCache::GetOrCreateGradientStops(aDT,
122 stops,
123 (cairo_pattern_get_extend(mPattern) == CAIRO_EXTEND_REPEAT)
124 ? mozilla::gfx::ExtendMode::REPEAT
125 : mozilla::gfx::ExtendMode::CLAMP);
126 }
127 }
129 void
130 gfxPattern::SetMatrix(const gfxMatrix& matrix)
131 {
132 if (mPattern) {
133 cairo_matrix_t mat = *reinterpret_cast<const cairo_matrix_t*>(&matrix);
134 cairo_pattern_set_matrix(mPattern, &mat);
135 } else {
136 mTransform = ToMatrix(matrix);
137 // Cairo-pattern matrices specify the conversion from DrawTarget to pattern
138 // space. Azure pattern matrices specify the conversion from pattern to
139 // DrawTarget space.
140 mTransform.Invert();
141 }
142 }
144 gfxMatrix
145 gfxPattern::GetMatrix() const
146 {
147 if (mPattern) {
148 cairo_matrix_t mat;
149 cairo_pattern_get_matrix(mPattern, &mat);
150 return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat));
151 } else {
152 // invert at the higher precision of gfxMatrix
153 // cause we need to convert at some point anyways
154 gfxMatrix mat = ThebesMatrix(mTransform);
155 mat.Invert();
156 return mat;
157 }
158 }
160 gfxMatrix
161 gfxPattern::GetInverseMatrix() const
162 {
163 if (mPattern) {
164 cairo_matrix_t mat;
165 cairo_pattern_get_matrix(mPattern, &mat);
166 cairo_matrix_invert(&mat);
167 return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat));
168 } else {
169 return ThebesMatrix(mTransform);
170 }
171 }
173 Pattern*
174 gfxPattern::GetPattern(DrawTarget *aTarget, Matrix *aPatternTransform)
175 {
176 if (mGfxPattern) {
177 mGfxPattern->~Pattern();
178 mGfxPattern = nullptr;
179 }
181 if (!mPattern) {
182 Matrix adjustedMatrix = mTransform;
183 if (aPatternTransform)
184 AdjustTransformForPattern(adjustedMatrix, aTarget->GetTransform(), aPatternTransform);
185 mGfxPattern = new (mSurfacePattern.addr())
186 SurfacePattern(mSourceSurface, ToExtendMode(mExtend), adjustedMatrix, mFilter);
187 return mGfxPattern;
188 }
190 GraphicsExtend extend = (GraphicsExtend)cairo_pattern_get_extend(mPattern);
192 switch (cairo_pattern_get_type(mPattern)) {
193 case CAIRO_PATTERN_TYPE_SOLID:
194 {
195 double r, g, b, a;
196 cairo_pattern_get_rgba(mPattern, &r, &g, &b, &a);
198 new (mColorPattern.addr()) ColorPattern(Color(r, g, b, a));
199 return mColorPattern.addr();
200 }
201 case CAIRO_PATTERN_TYPE_SURFACE:
202 {
203 GraphicsFilter filter = (GraphicsFilter)cairo_pattern_get_filter(mPattern);
204 cairo_matrix_t mat;
205 cairo_pattern_get_matrix(mPattern, &mat);
206 gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat));
208 cairo_surface_t *surf = nullptr;
209 cairo_pattern_get_surface(mPattern, &surf);
211 if (!mSourceSurface) {
212 nsRefPtr<gfxASurface> gfxSurf = gfxASurface::Wrap(surf);
213 // The underlying surface here will be kept around by the gfxPattern.
214 // This function is intended to be used right away.
215 mSourceSurface =
216 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTarget, gfxSurf);
217 }
219 if (mSourceSurface) {
220 Matrix newMat = ToMatrix(matrix);
222 AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform);
224 double x, y;
225 cairo_surface_get_device_offset(surf, &x, &y);
226 newMat.Translate(-x, -y);
227 mGfxPattern = new (mSurfacePattern.addr())
228 SurfacePattern(mSourceSurface, ToExtendMode(extend), newMat, ToFilter(filter));
229 return mGfxPattern;
230 }
231 break;
232 }
233 case CAIRO_PATTERN_TYPE_LINEAR:
234 {
235 double x1, y1, x2, y2;
236 cairo_pattern_get_linear_points(mPattern, &x1, &y1, &x2, &y2);
237 if (!mStops) {
238 int count = 0;
239 cairo_pattern_get_color_stop_count(mPattern, &count);
241 std::vector<GradientStop> stops;
243 for (int i = 0; i < count; i++) {
244 GradientStop stop;
245 double r, g, b, a, offset;
246 cairo_pattern_get_color_stop_rgba(mPattern, i, &offset, &r, &g, &b, &a);
248 stop.offset = offset;
249 stop.color = Color(Float(r), Float(g), Float(b), Float(a));
250 stops.push_back(stop);
251 }
253 mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend));
254 }
256 if (mStops) {
257 cairo_matrix_t mat;
258 cairo_pattern_get_matrix(mPattern, &mat);
259 gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat));
261 Matrix newMat = ToMatrix(matrix);
263 AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform);
265 mGfxPattern = new (mLinearGradientPattern.addr())
266 LinearGradientPattern(Point(x1, y1), Point(x2, y2), mStops, newMat);
268 return mGfxPattern;
269 }
270 break;
271 }
272 case CAIRO_PATTERN_TYPE_RADIAL:
273 {
274 if (!mStops) {
275 int count = 0;
276 cairo_pattern_get_color_stop_count(mPattern, &count);
278 std::vector<GradientStop> stops;
280 for (int i = 0; i < count; i++) {
281 GradientStop stop;
282 double r, g, b, a, offset;
283 cairo_pattern_get_color_stop_rgba(mPattern, i, &offset, &r, &g, &b, &a);
285 stop.offset = offset;
286 stop.color = Color(Float(r), Float(g), Float(b), Float(a));
287 stops.push_back(stop);
288 }
290 mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend));
291 }
293 if (mStops) {
294 cairo_matrix_t mat;
295 cairo_pattern_get_matrix(mPattern, &mat);
296 gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat));
298 Matrix newMat = ToMatrix(matrix);
300 AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform);
302 double x1, y1, x2, y2, r1, r2;
303 cairo_pattern_get_radial_circles(mPattern, &x1, &y1, &r1, &x2, &y2, &r2);
304 mGfxPattern = new (mRadialGradientPattern.addr())
305 RadialGradientPattern(Point(x1, y1), Point(x2, y2), r1, r2, mStops, newMat);
307 return mGfxPattern;
308 }
309 break;
310 }
311 default:
312 /* Reassure the compiler we are handling all the enum values. */
313 break;
314 }
316 new (mColorPattern.addr()) ColorPattern(Color(0, 0, 0, 0));
317 return mColorPattern.addr();
318 }
320 void
321 gfxPattern::SetExtend(GraphicsExtend extend)
322 {
323 if (mPattern) {
324 mStops = nullptr;
325 if (extend == EXTEND_PAD_EDGE) {
326 if (cairo_pattern_get_type(mPattern) == CAIRO_PATTERN_TYPE_SURFACE) {
327 cairo_surface_t *surf = nullptr;
329 cairo_pattern_get_surface (mPattern, &surf);
330 if (surf) {
331 switch (cairo_surface_get_type(surf)) {
332 case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
333 case CAIRO_SURFACE_TYPE_QUARTZ:
334 extend = EXTEND_NONE;
335 break;
337 case CAIRO_SURFACE_TYPE_WIN32:
338 case CAIRO_SURFACE_TYPE_XLIB:
339 default:
340 extend = EXTEND_PAD;
341 break;
342 }
343 }
344 }
346 // if something went wrong, or not a surface pattern, use PAD
347 if (extend == EXTEND_PAD_EDGE)
348 extend = EXTEND_PAD;
349 }
351 cairo_pattern_set_extend(mPattern, (cairo_extend_t)extend);
352 } else {
353 // This is always a surface pattern and will default to EXTEND_PAD
354 // for EXTEND_PAD_EDGE.
355 mExtend = extend;
356 }
357 }
359 bool
360 gfxPattern::IsOpaque()
361 {
362 if (mPattern) {
363 switch (cairo_pattern_get_type(mPattern)) {
364 case CAIRO_PATTERN_TYPE_SURFACE:
365 {
366 cairo_surface_t *surf = nullptr;
367 cairo_pattern_get_surface(mPattern, &surf);
369 if (cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR) {
370 return true;
371 }
372 }
373 default:
374 return false;
375 }
376 }
378 if (mSourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
379 return true;
380 }
381 return false;
382 }
384 gfxPattern::GraphicsExtend
385 gfxPattern::Extend() const
386 {
387 if (mPattern) {
388 return (GraphicsExtend)cairo_pattern_get_extend(mPattern);
389 } else {
390 return mExtend;
391 }
392 }
394 void
395 gfxPattern::SetFilter(GraphicsFilter filter)
396 {
397 if (mPattern) {
398 cairo_pattern_set_filter(mPattern, (cairo_filter_t)(int)filter);
399 } else {
400 mFilter = ToFilter(filter);
401 }
402 }
404 GraphicsFilter
405 gfxPattern::Filter() const
406 {
407 if (mPattern) {
408 return (GraphicsFilter)cairo_pattern_get_filter(mPattern);
409 } else {
410 return ThebesFilter(mFilter);
411 }
412 }
414 bool
415 gfxPattern::GetSolidColor(gfxRGBA& aColor)
416 {
417 return cairo_pattern_get_rgba(mPattern,
418 &aColor.r,
419 &aColor.g,
420 &aColor.b,
421 &aColor.a) == CAIRO_STATUS_SUCCESS;
422 }
424 already_AddRefed<gfxASurface>
425 gfxPattern::GetSurface()
426 {
427 if (mPattern) {
428 cairo_surface_t *surf = nullptr;
430 if (cairo_pattern_get_surface (mPattern, &surf) != CAIRO_STATUS_SUCCESS)
431 return nullptr;
433 return gfxASurface::Wrap(surf);
434 } else {
435 // We should never be trying to get the surface off an Azure gfx Pattern.
436 NS_ERROR("Attempt to get surface off an Azure gfxPattern!");
437 return nullptr;
438 }
439 }
441 gfxPattern::GraphicsPatternType
442 gfxPattern::GetType() const
443 {
444 if (mPattern) {
445 return (GraphicsPatternType) cairo_pattern_get_type(mPattern);
446 } else {
447 // We should never be trying to get the type off an Azure gfx Pattern.
448 MOZ_ASSERT(0);
449 return PATTERN_SURFACE;
450 }
451 }
453 int
454 gfxPattern::CairoStatus()
455 {
456 if (mPattern) {
457 return cairo_pattern_status(mPattern);
458 } else {
459 // An Azure pattern as this point is never in error status.
460 return CAIRO_STATUS_SUCCESS;
461 }
462 }
464 void
465 gfxPattern::AdjustTransformForPattern(Matrix &aPatternTransform,
466 const Matrix &aCurrentTransform,
467 const Matrix *aOriginalTransform)
468 {
469 aPatternTransform.Invert();
470 if (!aOriginalTransform) {
471 // User space is unchanged, so to get from pattern space to user space,
472 // just invert the cairo matrix.
473 aPatternTransform.NudgeToIntegers();
474 return;
475 }
476 // aPatternTransform now maps from pattern space to the user space defined
477 // by *aOriginalTransform.
479 Matrix mat = aCurrentTransform;
480 mat.Invert();
481 // mat maps from device space to current user space
483 // First, transform from pattern space to original user space. Then transform
484 // from original user space to device space. Then transform from
485 // device space to current user space.
486 aPatternTransform = aPatternTransform * *aOriginalTransform * mat;
487 aPatternTransform.NudgeToIntegers();
488 }