|
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/. */ |
|
5 |
|
6 #include "gfxTypes.h" |
|
7 #include "gfxPattern.h" |
|
8 #include "gfxASurface.h" |
|
9 #include "gfxPlatform.h" |
|
10 #include "gfx2DGlue.h" |
|
11 #include "gfxGradientCache.h" |
|
12 |
|
13 #include "cairo.h" |
|
14 |
|
15 #include <vector> |
|
16 |
|
17 using namespace mozilla::gfx; |
|
18 |
|
19 gfxPattern::gfxPattern(cairo_pattern_t *aPattern) |
|
20 : mGfxPattern(nullptr) |
|
21 { |
|
22 mPattern = cairo_pattern_reference(aPattern); |
|
23 } |
|
24 |
|
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 } |
|
30 |
|
31 // from another surface |
|
32 gfxPattern::gfxPattern(gfxASurface *surface) |
|
33 : mGfxPattern(nullptr) |
|
34 { |
|
35 mPattern = cairo_pattern_create_for_surface(surface->CairoSurface()); |
|
36 } |
|
37 |
|
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 } |
|
44 |
|
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 } |
|
53 |
|
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 } |
|
63 |
|
64 gfxPattern::~gfxPattern() |
|
65 { |
|
66 cairo_pattern_destroy(mPattern); |
|
67 |
|
68 if (mGfxPattern) { |
|
69 mGfxPattern->~Pattern(); |
|
70 } |
|
71 } |
|
72 |
|
73 cairo_pattern_t * |
|
74 gfxPattern::CairoPattern() |
|
75 { |
|
76 return mPattern; |
|
77 } |
|
78 |
|
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); |
|
89 |
|
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 } |
|
99 |
|
100 void |
|
101 gfxPattern::SetColorStops(mozilla::RefPtr<mozilla::gfx::GradientStops> aStops) |
|
102 { |
|
103 mStops = aStops; |
|
104 } |
|
105 |
|
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 } |
|
128 |
|
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 } |
|
143 |
|
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 } |
|
159 |
|
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 } |
|
172 |
|
173 Pattern* |
|
174 gfxPattern::GetPattern(DrawTarget *aTarget, Matrix *aPatternTransform) |
|
175 { |
|
176 if (mGfxPattern) { |
|
177 mGfxPattern->~Pattern(); |
|
178 mGfxPattern = nullptr; |
|
179 } |
|
180 |
|
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 } |
|
189 |
|
190 GraphicsExtend extend = (GraphicsExtend)cairo_pattern_get_extend(mPattern); |
|
191 |
|
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); |
|
197 |
|
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)); |
|
207 |
|
208 cairo_surface_t *surf = nullptr; |
|
209 cairo_pattern_get_surface(mPattern, &surf); |
|
210 |
|
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 } |
|
218 |
|
219 if (mSourceSurface) { |
|
220 Matrix newMat = ToMatrix(matrix); |
|
221 |
|
222 AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); |
|
223 |
|
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); |
|
240 |
|
241 std::vector<GradientStop> stops; |
|
242 |
|
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); |
|
247 |
|
248 stop.offset = offset; |
|
249 stop.color = Color(Float(r), Float(g), Float(b), Float(a)); |
|
250 stops.push_back(stop); |
|
251 } |
|
252 |
|
253 mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend)); |
|
254 } |
|
255 |
|
256 if (mStops) { |
|
257 cairo_matrix_t mat; |
|
258 cairo_pattern_get_matrix(mPattern, &mat); |
|
259 gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat)); |
|
260 |
|
261 Matrix newMat = ToMatrix(matrix); |
|
262 |
|
263 AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); |
|
264 |
|
265 mGfxPattern = new (mLinearGradientPattern.addr()) |
|
266 LinearGradientPattern(Point(x1, y1), Point(x2, y2), mStops, newMat); |
|
267 |
|
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); |
|
277 |
|
278 std::vector<GradientStop> stops; |
|
279 |
|
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); |
|
284 |
|
285 stop.offset = offset; |
|
286 stop.color = Color(Float(r), Float(g), Float(b), Float(a)); |
|
287 stops.push_back(stop); |
|
288 } |
|
289 |
|
290 mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend)); |
|
291 } |
|
292 |
|
293 if (mStops) { |
|
294 cairo_matrix_t mat; |
|
295 cairo_pattern_get_matrix(mPattern, &mat); |
|
296 gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat)); |
|
297 |
|
298 Matrix newMat = ToMatrix(matrix); |
|
299 |
|
300 AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); |
|
301 |
|
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); |
|
306 |
|
307 return mGfxPattern; |
|
308 } |
|
309 break; |
|
310 } |
|
311 default: |
|
312 /* Reassure the compiler we are handling all the enum values. */ |
|
313 break; |
|
314 } |
|
315 |
|
316 new (mColorPattern.addr()) ColorPattern(Color(0, 0, 0, 0)); |
|
317 return mColorPattern.addr(); |
|
318 } |
|
319 |
|
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; |
|
328 |
|
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; |
|
336 |
|
337 case CAIRO_SURFACE_TYPE_WIN32: |
|
338 case CAIRO_SURFACE_TYPE_XLIB: |
|
339 default: |
|
340 extend = EXTEND_PAD; |
|
341 break; |
|
342 } |
|
343 } |
|
344 } |
|
345 |
|
346 // if something went wrong, or not a surface pattern, use PAD |
|
347 if (extend == EXTEND_PAD_EDGE) |
|
348 extend = EXTEND_PAD; |
|
349 } |
|
350 |
|
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 } |
|
358 |
|
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); |
|
368 |
|
369 if (cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR) { |
|
370 return true; |
|
371 } |
|
372 } |
|
373 default: |
|
374 return false; |
|
375 } |
|
376 } |
|
377 |
|
378 if (mSourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) { |
|
379 return true; |
|
380 } |
|
381 return false; |
|
382 } |
|
383 |
|
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 } |
|
393 |
|
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 } |
|
403 |
|
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 } |
|
413 |
|
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 } |
|
423 |
|
424 already_AddRefed<gfxASurface> |
|
425 gfxPattern::GetSurface() |
|
426 { |
|
427 if (mPattern) { |
|
428 cairo_surface_t *surf = nullptr; |
|
429 |
|
430 if (cairo_pattern_get_surface (mPattern, &surf) != CAIRO_STATUS_SUCCESS) |
|
431 return nullptr; |
|
432 |
|
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 } |
|
440 |
|
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 } |
|
452 |
|
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 } |
|
463 |
|
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. |
|
478 |
|
479 Matrix mat = aCurrentTransform; |
|
480 mat.Invert(); |
|
481 // mat maps from device space to current user space |
|
482 |
|
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 } |