1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxPattern.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,488 @@ 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 "gfxTypes.h" 1.10 +#include "gfxPattern.h" 1.11 +#include "gfxASurface.h" 1.12 +#include "gfxPlatform.h" 1.13 +#include "gfx2DGlue.h" 1.14 +#include "gfxGradientCache.h" 1.15 + 1.16 +#include "cairo.h" 1.17 + 1.18 +#include <vector> 1.19 + 1.20 +using namespace mozilla::gfx; 1.21 + 1.22 +gfxPattern::gfxPattern(cairo_pattern_t *aPattern) 1.23 + : mGfxPattern(nullptr) 1.24 +{ 1.25 + mPattern = cairo_pattern_reference(aPattern); 1.26 +} 1.27 + 1.28 +gfxPattern::gfxPattern(const gfxRGBA& aColor) 1.29 + : mGfxPattern(nullptr) 1.30 +{ 1.31 + mPattern = cairo_pattern_create_rgba(aColor.r, aColor.g, aColor.b, aColor.a); 1.32 +} 1.33 + 1.34 +// from another surface 1.35 +gfxPattern::gfxPattern(gfxASurface *surface) 1.36 + : mGfxPattern(nullptr) 1.37 +{ 1.38 + mPattern = cairo_pattern_create_for_surface(surface->CairoSurface()); 1.39 +} 1.40 + 1.41 +// linear 1.42 +gfxPattern::gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1) 1.43 + : mGfxPattern(nullptr) 1.44 +{ 1.45 + mPattern = cairo_pattern_create_linear(x0, y0, x1, y1); 1.46 +} 1.47 + 1.48 +// radial 1.49 +gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0, 1.50 + gfxFloat cx1, gfxFloat cy1, gfxFloat radius1) 1.51 + : mGfxPattern(nullptr) 1.52 +{ 1.53 + mPattern = cairo_pattern_create_radial(cx0, cy0, radius0, 1.54 + cx1, cy1, radius1); 1.55 +} 1.56 + 1.57 +// Azure 1.58 +gfxPattern::gfxPattern(SourceSurface *aSurface, const Matrix &aTransform) 1.59 + : mPattern(nullptr) 1.60 + , mGfxPattern(nullptr) 1.61 + , mSourceSurface(aSurface) 1.62 + , mTransform(aTransform) 1.63 + , mExtend(EXTEND_NONE) 1.64 +{ 1.65 +} 1.66 + 1.67 +gfxPattern::~gfxPattern() 1.68 +{ 1.69 + cairo_pattern_destroy(mPattern); 1.70 + 1.71 + if (mGfxPattern) { 1.72 + mGfxPattern->~Pattern(); 1.73 + } 1.74 +} 1.75 + 1.76 +cairo_pattern_t * 1.77 +gfxPattern::CairoPattern() 1.78 +{ 1.79 + return mPattern; 1.80 +} 1.81 + 1.82 +void 1.83 +gfxPattern::AddColorStop(gfxFloat offset, const gfxRGBA& c) 1.84 +{ 1.85 + if (mPattern) { 1.86 + mStops = nullptr; 1.87 + if (gfxPlatform::GetCMSMode() == eCMSMode_All) { 1.88 + gfxRGBA cms; 1.89 + qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); 1.90 + if (transform) 1.91 + gfxPlatform::TransformPixel(c, cms, transform); 1.92 + 1.93 + // Use the original alpha to avoid unnecessary float->byte->float 1.94 + // conversion errors 1.95 + cairo_pattern_add_color_stop_rgba(mPattern, offset, 1.96 + cms.r, cms.g, cms.b, c.a); 1.97 + } 1.98 + else 1.99 + cairo_pattern_add_color_stop_rgba(mPattern, offset, c.r, c.g, c.b, c.a); 1.100 + } 1.101 +} 1.102 + 1.103 +void 1.104 +gfxPattern::SetColorStops(mozilla::RefPtr<mozilla::gfx::GradientStops> aStops) 1.105 +{ 1.106 + mStops = aStops; 1.107 +} 1.108 + 1.109 +void 1.110 +gfxPattern::CacheColorStops(mozilla::gfx::DrawTarget *aDT) 1.111 +{ 1.112 + if (mPattern) { 1.113 + mStops = nullptr; 1.114 + nsTArray<GradientStop> stops; 1.115 + int count = 0; 1.116 + cairo_pattern_get_color_stop_count(mPattern, &count); 1.117 + stops.SetLength(count); 1.118 + for (int n = 0; n < count; ++n) { 1.119 + double offset, r, g, b, a; 1.120 + cairo_pattern_get_color_stop_rgba(mPattern, n, &offset, &r, &g, &b, &a); 1.121 + stops[n].color = mozilla::gfx::Color(r, g, b, a); 1.122 + stops[n].offset = offset; 1.123 + } 1.124 + mStops = gfxGradientCache::GetOrCreateGradientStops(aDT, 1.125 + stops, 1.126 + (cairo_pattern_get_extend(mPattern) == CAIRO_EXTEND_REPEAT) 1.127 + ? mozilla::gfx::ExtendMode::REPEAT 1.128 + : mozilla::gfx::ExtendMode::CLAMP); 1.129 + } 1.130 +} 1.131 + 1.132 +void 1.133 +gfxPattern::SetMatrix(const gfxMatrix& matrix) 1.134 +{ 1.135 + if (mPattern) { 1.136 + cairo_matrix_t mat = *reinterpret_cast<const cairo_matrix_t*>(&matrix); 1.137 + cairo_pattern_set_matrix(mPattern, &mat); 1.138 + } else { 1.139 + mTransform = ToMatrix(matrix); 1.140 + // Cairo-pattern matrices specify the conversion from DrawTarget to pattern 1.141 + // space. Azure pattern matrices specify the conversion from pattern to 1.142 + // DrawTarget space. 1.143 + mTransform.Invert(); 1.144 + } 1.145 +} 1.146 + 1.147 +gfxMatrix 1.148 +gfxPattern::GetMatrix() const 1.149 +{ 1.150 + if (mPattern) { 1.151 + cairo_matrix_t mat; 1.152 + cairo_pattern_get_matrix(mPattern, &mat); 1.153 + return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)); 1.154 + } else { 1.155 + // invert at the higher precision of gfxMatrix 1.156 + // cause we need to convert at some point anyways 1.157 + gfxMatrix mat = ThebesMatrix(mTransform); 1.158 + mat.Invert(); 1.159 + return mat; 1.160 + } 1.161 +} 1.162 + 1.163 +gfxMatrix 1.164 +gfxPattern::GetInverseMatrix() const 1.165 +{ 1.166 + if (mPattern) { 1.167 + cairo_matrix_t mat; 1.168 + cairo_pattern_get_matrix(mPattern, &mat); 1.169 + cairo_matrix_invert(&mat); 1.170 + return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)); 1.171 + } else { 1.172 + return ThebesMatrix(mTransform); 1.173 + } 1.174 +} 1.175 + 1.176 +Pattern* 1.177 +gfxPattern::GetPattern(DrawTarget *aTarget, Matrix *aPatternTransform) 1.178 +{ 1.179 + if (mGfxPattern) { 1.180 + mGfxPattern->~Pattern(); 1.181 + mGfxPattern = nullptr; 1.182 + } 1.183 + 1.184 + if (!mPattern) { 1.185 + Matrix adjustedMatrix = mTransform; 1.186 + if (aPatternTransform) 1.187 + AdjustTransformForPattern(adjustedMatrix, aTarget->GetTransform(), aPatternTransform); 1.188 + mGfxPattern = new (mSurfacePattern.addr()) 1.189 + SurfacePattern(mSourceSurface, ToExtendMode(mExtend), adjustedMatrix, mFilter); 1.190 + return mGfxPattern; 1.191 + } 1.192 + 1.193 + GraphicsExtend extend = (GraphicsExtend)cairo_pattern_get_extend(mPattern); 1.194 + 1.195 + switch (cairo_pattern_get_type(mPattern)) { 1.196 + case CAIRO_PATTERN_TYPE_SOLID: 1.197 + { 1.198 + double r, g, b, a; 1.199 + cairo_pattern_get_rgba(mPattern, &r, &g, &b, &a); 1.200 + 1.201 + new (mColorPattern.addr()) ColorPattern(Color(r, g, b, a)); 1.202 + return mColorPattern.addr(); 1.203 + } 1.204 + case CAIRO_PATTERN_TYPE_SURFACE: 1.205 + { 1.206 + GraphicsFilter filter = (GraphicsFilter)cairo_pattern_get_filter(mPattern); 1.207 + cairo_matrix_t mat; 1.208 + cairo_pattern_get_matrix(mPattern, &mat); 1.209 + gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat)); 1.210 + 1.211 + cairo_surface_t *surf = nullptr; 1.212 + cairo_pattern_get_surface(mPattern, &surf); 1.213 + 1.214 + if (!mSourceSurface) { 1.215 + nsRefPtr<gfxASurface> gfxSurf = gfxASurface::Wrap(surf); 1.216 + // The underlying surface here will be kept around by the gfxPattern. 1.217 + // This function is intended to be used right away. 1.218 + mSourceSurface = 1.219 + gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTarget, gfxSurf); 1.220 + } 1.221 + 1.222 + if (mSourceSurface) { 1.223 + Matrix newMat = ToMatrix(matrix); 1.224 + 1.225 + AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); 1.226 + 1.227 + double x, y; 1.228 + cairo_surface_get_device_offset(surf, &x, &y); 1.229 + newMat.Translate(-x, -y); 1.230 + mGfxPattern = new (mSurfacePattern.addr()) 1.231 + SurfacePattern(mSourceSurface, ToExtendMode(extend), newMat, ToFilter(filter)); 1.232 + return mGfxPattern; 1.233 + } 1.234 + break; 1.235 + } 1.236 + case CAIRO_PATTERN_TYPE_LINEAR: 1.237 + { 1.238 + double x1, y1, x2, y2; 1.239 + cairo_pattern_get_linear_points(mPattern, &x1, &y1, &x2, &y2); 1.240 + if (!mStops) { 1.241 + int count = 0; 1.242 + cairo_pattern_get_color_stop_count(mPattern, &count); 1.243 + 1.244 + std::vector<GradientStop> stops; 1.245 + 1.246 + for (int i = 0; i < count; i++) { 1.247 + GradientStop stop; 1.248 + double r, g, b, a, offset; 1.249 + cairo_pattern_get_color_stop_rgba(mPattern, i, &offset, &r, &g, &b, &a); 1.250 + 1.251 + stop.offset = offset; 1.252 + stop.color = Color(Float(r), Float(g), Float(b), Float(a)); 1.253 + stops.push_back(stop); 1.254 + } 1.255 + 1.256 + mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend)); 1.257 + } 1.258 + 1.259 + if (mStops) { 1.260 + cairo_matrix_t mat; 1.261 + cairo_pattern_get_matrix(mPattern, &mat); 1.262 + gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat)); 1.263 + 1.264 + Matrix newMat = ToMatrix(matrix); 1.265 + 1.266 + AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); 1.267 + 1.268 + mGfxPattern = new (mLinearGradientPattern.addr()) 1.269 + LinearGradientPattern(Point(x1, y1), Point(x2, y2), mStops, newMat); 1.270 + 1.271 + return mGfxPattern; 1.272 + } 1.273 + break; 1.274 + } 1.275 + case CAIRO_PATTERN_TYPE_RADIAL: 1.276 + { 1.277 + if (!mStops) { 1.278 + int count = 0; 1.279 + cairo_pattern_get_color_stop_count(mPattern, &count); 1.280 + 1.281 + std::vector<GradientStop> stops; 1.282 + 1.283 + for (int i = 0; i < count; i++) { 1.284 + GradientStop stop; 1.285 + double r, g, b, a, offset; 1.286 + cairo_pattern_get_color_stop_rgba(mPattern, i, &offset, &r, &g, &b, &a); 1.287 + 1.288 + stop.offset = offset; 1.289 + stop.color = Color(Float(r), Float(g), Float(b), Float(a)); 1.290 + stops.push_back(stop); 1.291 + } 1.292 + 1.293 + mStops = aTarget->CreateGradientStops(&stops.front(), count, ToExtendMode(extend)); 1.294 + } 1.295 + 1.296 + if (mStops) { 1.297 + cairo_matrix_t mat; 1.298 + cairo_pattern_get_matrix(mPattern, &mat); 1.299 + gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat)); 1.300 + 1.301 + Matrix newMat = ToMatrix(matrix); 1.302 + 1.303 + AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform); 1.304 + 1.305 + double x1, y1, x2, y2, r1, r2; 1.306 + cairo_pattern_get_radial_circles(mPattern, &x1, &y1, &r1, &x2, &y2, &r2); 1.307 + mGfxPattern = new (mRadialGradientPattern.addr()) 1.308 + RadialGradientPattern(Point(x1, y1), Point(x2, y2), r1, r2, mStops, newMat); 1.309 + 1.310 + return mGfxPattern; 1.311 + } 1.312 + break; 1.313 + } 1.314 + default: 1.315 + /* Reassure the compiler we are handling all the enum values. */ 1.316 + break; 1.317 + } 1.318 + 1.319 + new (mColorPattern.addr()) ColorPattern(Color(0, 0, 0, 0)); 1.320 + return mColorPattern.addr(); 1.321 +} 1.322 + 1.323 +void 1.324 +gfxPattern::SetExtend(GraphicsExtend extend) 1.325 +{ 1.326 + if (mPattern) { 1.327 + mStops = nullptr; 1.328 + if (extend == EXTEND_PAD_EDGE) { 1.329 + if (cairo_pattern_get_type(mPattern) == CAIRO_PATTERN_TYPE_SURFACE) { 1.330 + cairo_surface_t *surf = nullptr; 1.331 + 1.332 + cairo_pattern_get_surface (mPattern, &surf); 1.333 + if (surf) { 1.334 + switch (cairo_surface_get_type(surf)) { 1.335 + case CAIRO_SURFACE_TYPE_WIN32_PRINTING: 1.336 + case CAIRO_SURFACE_TYPE_QUARTZ: 1.337 + extend = EXTEND_NONE; 1.338 + break; 1.339 + 1.340 + case CAIRO_SURFACE_TYPE_WIN32: 1.341 + case CAIRO_SURFACE_TYPE_XLIB: 1.342 + default: 1.343 + extend = EXTEND_PAD; 1.344 + break; 1.345 + } 1.346 + } 1.347 + } 1.348 + 1.349 + // if something went wrong, or not a surface pattern, use PAD 1.350 + if (extend == EXTEND_PAD_EDGE) 1.351 + extend = EXTEND_PAD; 1.352 + } 1.353 + 1.354 + cairo_pattern_set_extend(mPattern, (cairo_extend_t)extend); 1.355 + } else { 1.356 + // This is always a surface pattern and will default to EXTEND_PAD 1.357 + // for EXTEND_PAD_EDGE. 1.358 + mExtend = extend; 1.359 + } 1.360 +} 1.361 + 1.362 +bool 1.363 +gfxPattern::IsOpaque() 1.364 +{ 1.365 + if (mPattern) { 1.366 + switch (cairo_pattern_get_type(mPattern)) { 1.367 + case CAIRO_PATTERN_TYPE_SURFACE: 1.368 + { 1.369 + cairo_surface_t *surf = nullptr; 1.370 + cairo_pattern_get_surface(mPattern, &surf); 1.371 + 1.372 + if (cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR) { 1.373 + return true; 1.374 + } 1.375 + } 1.376 + default: 1.377 + return false; 1.378 + } 1.379 + } 1.380 + 1.381 + if (mSourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) { 1.382 + return true; 1.383 + } 1.384 + return false; 1.385 +} 1.386 + 1.387 +gfxPattern::GraphicsExtend 1.388 +gfxPattern::Extend() const 1.389 +{ 1.390 + if (mPattern) { 1.391 + return (GraphicsExtend)cairo_pattern_get_extend(mPattern); 1.392 + } else { 1.393 + return mExtend; 1.394 + } 1.395 +} 1.396 + 1.397 +void 1.398 +gfxPattern::SetFilter(GraphicsFilter filter) 1.399 +{ 1.400 + if (mPattern) { 1.401 + cairo_pattern_set_filter(mPattern, (cairo_filter_t)(int)filter); 1.402 + } else { 1.403 + mFilter = ToFilter(filter); 1.404 + } 1.405 +} 1.406 + 1.407 +GraphicsFilter 1.408 +gfxPattern::Filter() const 1.409 +{ 1.410 + if (mPattern) { 1.411 + return (GraphicsFilter)cairo_pattern_get_filter(mPattern); 1.412 + } else { 1.413 + return ThebesFilter(mFilter); 1.414 + } 1.415 +} 1.416 + 1.417 +bool 1.418 +gfxPattern::GetSolidColor(gfxRGBA& aColor) 1.419 +{ 1.420 + return cairo_pattern_get_rgba(mPattern, 1.421 + &aColor.r, 1.422 + &aColor.g, 1.423 + &aColor.b, 1.424 + &aColor.a) == CAIRO_STATUS_SUCCESS; 1.425 +} 1.426 + 1.427 +already_AddRefed<gfxASurface> 1.428 +gfxPattern::GetSurface() 1.429 +{ 1.430 + if (mPattern) { 1.431 + cairo_surface_t *surf = nullptr; 1.432 + 1.433 + if (cairo_pattern_get_surface (mPattern, &surf) != CAIRO_STATUS_SUCCESS) 1.434 + return nullptr; 1.435 + 1.436 + return gfxASurface::Wrap(surf); 1.437 + } else { 1.438 + // We should never be trying to get the surface off an Azure gfx Pattern. 1.439 + NS_ERROR("Attempt to get surface off an Azure gfxPattern!"); 1.440 + return nullptr; 1.441 + } 1.442 +} 1.443 + 1.444 +gfxPattern::GraphicsPatternType 1.445 +gfxPattern::GetType() const 1.446 +{ 1.447 + if (mPattern) { 1.448 + return (GraphicsPatternType) cairo_pattern_get_type(mPattern); 1.449 + } else { 1.450 + // We should never be trying to get the type off an Azure gfx Pattern. 1.451 + MOZ_ASSERT(0); 1.452 + return PATTERN_SURFACE; 1.453 + } 1.454 +} 1.455 + 1.456 +int 1.457 +gfxPattern::CairoStatus() 1.458 +{ 1.459 + if (mPattern) { 1.460 + return cairo_pattern_status(mPattern); 1.461 + } else { 1.462 + // An Azure pattern as this point is never in error status. 1.463 + return CAIRO_STATUS_SUCCESS; 1.464 + } 1.465 +} 1.466 + 1.467 +void 1.468 +gfxPattern::AdjustTransformForPattern(Matrix &aPatternTransform, 1.469 + const Matrix &aCurrentTransform, 1.470 + const Matrix *aOriginalTransform) 1.471 +{ 1.472 + aPatternTransform.Invert(); 1.473 + if (!aOriginalTransform) { 1.474 + // User space is unchanged, so to get from pattern space to user space, 1.475 + // just invert the cairo matrix. 1.476 + aPatternTransform.NudgeToIntegers(); 1.477 + return; 1.478 + } 1.479 + // aPatternTransform now maps from pattern space to the user space defined 1.480 + // by *aOriginalTransform. 1.481 + 1.482 + Matrix mat = aCurrentTransform; 1.483 + mat.Invert(); 1.484 + // mat maps from device space to current user space 1.485 + 1.486 + // First, transform from pattern space to original user space. Then transform 1.487 + // from original user space to device space. Then transform from 1.488 + // device space to current user space. 1.489 + aPatternTransform = aPatternTransform * *aOriginalTransform * mat; 1.490 + aPatternTransform.NudgeToIntegers(); 1.491 +}