1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1082 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 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 "gfxUtils.h" 1.10 +#include "gfxContext.h" 1.11 +#include "gfxPlatform.h" 1.12 +#include "gfxDrawable.h" 1.13 +#include "mozilla/gfx/2D.h" 1.14 +#include "mozilla/RefPtr.h" 1.15 +#include "nsRegion.h" 1.16 +#include "yuv_convert.h" 1.17 +#include "ycbcr_to_rgb565.h" 1.18 +#include "GeckoProfiler.h" 1.19 +#include "ImageContainer.h" 1.20 +#include "gfx2DGlue.h" 1.21 + 1.22 +#ifdef XP_WIN 1.23 +#include "gfxWindowsPlatform.h" 1.24 +#endif 1.25 + 1.26 +using namespace mozilla; 1.27 +using namespace mozilla::layers; 1.28 +using namespace mozilla::gfx; 1.29 + 1.30 +#include "DeprecatedPremultiplyTables.h" 1.31 + 1.32 +static const uint8_t PremultiplyValue(uint8_t a, uint8_t v) { 1.33 + return gfxUtils::sPremultiplyTable[a*256+v]; 1.34 +} 1.35 + 1.36 +static const uint8_t UnpremultiplyValue(uint8_t a, uint8_t v) { 1.37 + return gfxUtils::sUnpremultiplyTable[a*256+v]; 1.38 +} 1.39 + 1.40 +void 1.41 +gfxUtils::PremultiplyImageSurface(gfxImageSurface *aSourceSurface, 1.42 + gfxImageSurface *aDestSurface) 1.43 +{ 1.44 + if (!aDestSurface) 1.45 + aDestSurface = aSourceSurface; 1.46 + 1.47 + MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() && 1.48 + aSourceSurface->Width() == aDestSurface->Width() && 1.49 + aSourceSurface->Height() == aDestSurface->Height() && 1.50 + aSourceSurface->Stride() == aDestSurface->Stride(), 1.51 + "Source and destination surfaces don't have identical characteristics"); 1.52 + 1.53 + MOZ_ASSERT(aSourceSurface->Stride() == aSourceSurface->Width() * 4, 1.54 + "Source surface stride isn't tightly packed"); 1.55 + 1.56 + // Only premultiply ARGB32 1.57 + if (aSourceSurface->Format() != gfxImageFormat::ARGB32) { 1.58 + if (aDestSurface != aSourceSurface) { 1.59 + memcpy(aDestSurface->Data(), aSourceSurface->Data(), 1.60 + aSourceSurface->Stride() * aSourceSurface->Height()); 1.61 + } 1.62 + return; 1.63 + } 1.64 + 1.65 + uint8_t *src = aSourceSurface->Data(); 1.66 + uint8_t *dst = aDestSurface->Data(); 1.67 + 1.68 + uint32_t dim = aSourceSurface->Width() * aSourceSurface->Height(); 1.69 + for (uint32_t i = 0; i < dim; ++i) { 1.70 +#ifdef IS_LITTLE_ENDIAN 1.71 + uint8_t b = *src++; 1.72 + uint8_t g = *src++; 1.73 + uint8_t r = *src++; 1.74 + uint8_t a = *src++; 1.75 + 1.76 + *dst++ = PremultiplyValue(a, b); 1.77 + *dst++ = PremultiplyValue(a, g); 1.78 + *dst++ = PremultiplyValue(a, r); 1.79 + *dst++ = a; 1.80 +#else 1.81 + uint8_t a = *src++; 1.82 + uint8_t r = *src++; 1.83 + uint8_t g = *src++; 1.84 + uint8_t b = *src++; 1.85 + 1.86 + *dst++ = a; 1.87 + *dst++ = PremultiplyValue(a, r); 1.88 + *dst++ = PremultiplyValue(a, g); 1.89 + *dst++ = PremultiplyValue(a, b); 1.90 +#endif 1.91 + } 1.92 +} 1.93 + 1.94 +void 1.95 +gfxUtils::UnpremultiplyImageSurface(gfxImageSurface *aSourceSurface, 1.96 + gfxImageSurface *aDestSurface) 1.97 +{ 1.98 + if (!aDestSurface) 1.99 + aDestSurface = aSourceSurface; 1.100 + 1.101 + MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() && 1.102 + aSourceSurface->Width() == aDestSurface->Width() && 1.103 + aSourceSurface->Height() == aDestSurface->Height(), 1.104 + "Source and destination surfaces don't have identical characteristics"); 1.105 + 1.106 + // Only premultiply ARGB32 1.107 + if (aSourceSurface->Format() != gfxImageFormat::ARGB32) { 1.108 + if (aDestSurface != aSourceSurface) { 1.109 + aDestSurface->CopyFrom(aSourceSurface); 1.110 + } 1.111 + return; 1.112 + } 1.113 + 1.114 + uint8_t *src = aSourceSurface->Data(); 1.115 + uint8_t *dst = aDestSurface->Data(); 1.116 + 1.117 + for (int32_t i = 0; i < aSourceSurface->Height(); ++i) { 1.118 + uint8_t *srcRow = src + (i * aSourceSurface->Stride()); 1.119 + uint8_t *dstRow = dst + (i * aDestSurface->Stride()); 1.120 + 1.121 + for (int32_t j = 0; j < aSourceSurface->Width(); ++j) { 1.122 +#ifdef IS_LITTLE_ENDIAN 1.123 + uint8_t b = *srcRow++; 1.124 + uint8_t g = *srcRow++; 1.125 + uint8_t r = *srcRow++; 1.126 + uint8_t a = *srcRow++; 1.127 + 1.128 + *dstRow++ = UnpremultiplyValue(a, b); 1.129 + *dstRow++ = UnpremultiplyValue(a, g); 1.130 + *dstRow++ = UnpremultiplyValue(a, r); 1.131 + *dstRow++ = a; 1.132 +#else 1.133 + uint8_t a = *srcRow++; 1.134 + uint8_t r = *srcRow++; 1.135 + uint8_t g = *srcRow++; 1.136 + uint8_t b = *srcRow++; 1.137 + 1.138 + *dstRow++ = a; 1.139 + *dstRow++ = UnpremultiplyValue(a, r); 1.140 + *dstRow++ = UnpremultiplyValue(a, g); 1.141 + *dstRow++ = UnpremultiplyValue(a, b); 1.142 +#endif 1.143 + } 1.144 + } 1.145 +} 1.146 + 1.147 +TemporaryRef<DataSourceSurface> 1.148 +gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* aSurface) 1.149 +{ 1.150 + // Only premultiply ARGB32 1.151 + if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) { 1.152 + return aSurface; 1.153 + } 1.154 + 1.155 + DataSourceSurface::MappedSurface map; 1.156 + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { 1.157 + return nullptr; 1.158 + } 1.159 + 1.160 + RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurfaceWithStride(aSurface->GetSize(), 1.161 + aSurface->GetFormat(), 1.162 + map.mStride); 1.163 + 1.164 + DataSourceSurface::MappedSurface destMap; 1.165 + if (!dest->Map(DataSourceSurface::MapType::WRITE, &destMap)) { 1.166 + aSurface->Unmap(); 1.167 + return nullptr; 1.168 + } 1.169 + 1.170 + uint8_t *src = map.mData; 1.171 + uint8_t *dst = destMap.mData; 1.172 + 1.173 + for (int32_t i = 0; i < aSurface->GetSize().height; ++i) { 1.174 + uint8_t *srcRow = src + (i * map.mStride); 1.175 + uint8_t *dstRow = dst + (i * destMap.mStride); 1.176 + 1.177 + for (int32_t j = 0; j < aSurface->GetSize().width; ++j) { 1.178 +#ifdef IS_LITTLE_ENDIAN 1.179 + uint8_t b = *srcRow++; 1.180 + uint8_t g = *srcRow++; 1.181 + uint8_t r = *srcRow++; 1.182 + uint8_t a = *srcRow++; 1.183 + 1.184 + *dstRow++ = UnpremultiplyValue(a, b); 1.185 + *dstRow++ = UnpremultiplyValue(a, g); 1.186 + *dstRow++ = UnpremultiplyValue(a, r); 1.187 + *dstRow++ = a; 1.188 +#else 1.189 + uint8_t a = *srcRow++; 1.190 + uint8_t r = *srcRow++; 1.191 + uint8_t g = *srcRow++; 1.192 + uint8_t b = *srcRow++; 1.193 + 1.194 + *dstRow++ = a; 1.195 + *dstRow++ = UnpremultiplyValue(a, r); 1.196 + *dstRow++ = UnpremultiplyValue(a, g); 1.197 + *dstRow++ = UnpremultiplyValue(a, b); 1.198 +#endif 1.199 + } 1.200 + } 1.201 + 1.202 + aSurface->Unmap(); 1.203 + dest->Unmap(); 1.204 + return dest; 1.205 +} 1.206 + 1.207 +void 1.208 +gfxUtils::ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface, 1.209 + gfxImageSurface *aDestSurface) { 1.210 + if (!aDestSurface) 1.211 + aDestSurface = aSourceSurface; 1.212 + 1.213 + MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() && 1.214 + aSourceSurface->Width() == aDestSurface->Width() && 1.215 + aSourceSurface->Height() == aDestSurface->Height() && 1.216 + aSourceSurface->Stride() == aDestSurface->Stride(), 1.217 + "Source and destination surfaces don't have identical characteristics"); 1.218 + 1.219 + MOZ_ASSERT(aSourceSurface->Stride() == aSourceSurface->Width() * 4, 1.220 + "Source surface stride isn't tightly packed"); 1.221 + 1.222 + MOZ_ASSERT(aSourceSurface->Format() == gfxImageFormat::ARGB32 || aSourceSurface->Format() == gfxImageFormat::RGB24, 1.223 + "Surfaces must be ARGB32 or RGB24"); 1.224 + 1.225 + uint8_t *src = aSourceSurface->Data(); 1.226 + uint8_t *dst = aDestSurface->Data(); 1.227 + 1.228 + uint32_t dim = aSourceSurface->Width() * aSourceSurface->Height(); 1.229 + uint8_t *srcEnd = src + 4*dim; 1.230 + 1.231 + if (src == dst) { 1.232 + uint8_t buffer[4]; 1.233 + for (; src != srcEnd; src += 4) { 1.234 + buffer[0] = src[2]; 1.235 + buffer[1] = src[1]; 1.236 + buffer[2] = src[0]; 1.237 + 1.238 + src[0] = buffer[0]; 1.239 + src[1] = buffer[1]; 1.240 + src[2] = buffer[2]; 1.241 + } 1.242 + } else { 1.243 + for (; src != srcEnd; src += 4, dst += 4) { 1.244 + dst[0] = src[2]; 1.245 + dst[1] = src[1]; 1.246 + dst[2] = src[0]; 1.247 + dst[3] = src[3]; 1.248 + } 1.249 + } 1.250 +} 1.251 + 1.252 +void 1.253 +gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength) 1.254 +{ 1.255 + uint8_t *src = aData; 1.256 + uint8_t *srcEnd = src + aLength; 1.257 + 1.258 + uint8_t buffer[4]; 1.259 + for (; src != srcEnd; src += 4) { 1.260 + buffer[0] = src[2]; 1.261 + buffer[1] = src[1]; 1.262 + buffer[2] = src[0]; 1.263 + 1.264 + src[0] = buffer[0]; 1.265 + src[1] = buffer[1]; 1.266 + src[2] = buffer[2]; 1.267 + } 1.268 +} 1.269 + 1.270 +static bool 1.271 +IsSafeImageTransformComponent(gfxFloat aValue) 1.272 +{ 1.273 + return aValue >= -32768 && aValue <= 32767; 1.274 +} 1.275 + 1.276 +#ifndef MOZ_GFX_OPTIMIZE_MOBILE 1.277 +/** 1.278 + * This returns the fastest operator to use for solid surfaces which have no 1.279 + * alpha channel or their alpha channel is uniformly opaque. 1.280 + * This differs per render mode. 1.281 + */ 1.282 +static gfxContext::GraphicsOperator 1.283 +OptimalFillOperator() 1.284 +{ 1.285 +#ifdef XP_WIN 1.286 + if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() == 1.287 + gfxWindowsPlatform::RENDER_DIRECT2D) { 1.288 + // D2D -really- hates operator source. 1.289 + return gfxContext::OPERATOR_OVER; 1.290 + } else { 1.291 +#endif 1.292 + return gfxContext::OPERATOR_SOURCE; 1.293 +#ifdef XP_WIN 1.294 + } 1.295 +#endif 1.296 +} 1.297 + 1.298 +// EXTEND_PAD won't help us here; we have to create a temporary surface to hold 1.299 +// the subimage of pixels we're allowed to sample. 1.300 +static already_AddRefed<gfxDrawable> 1.301 +CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable, 1.302 + gfxContext* aContext, 1.303 + const gfxMatrix& aUserSpaceToImageSpace, 1.304 + const gfxRect& aSourceRect, 1.305 + const gfxRect& aSubimage, 1.306 + const gfxImageFormat aFormat) 1.307 +{ 1.308 + PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable"); 1.309 + gfxRect userSpaceClipExtents = aContext->GetClipExtents(); 1.310 + // This isn't optimal --- if aContext has a rotation then GetClipExtents 1.311 + // will have to do a bounding-box computation, and TransformBounds might 1.312 + // too, so we could get a better result if we computed image space clip 1.313 + // extents in one go --- but it doesn't really matter and this is easier 1.314 + // to understand. 1.315 + gfxRect imageSpaceClipExtents = 1.316 + aUserSpaceToImageSpace.TransformBounds(userSpaceClipExtents); 1.317 + // Inflate by one pixel because bilinear filtering will sample at most 1.318 + // one pixel beyond the computed image pixel coordinate. 1.319 + imageSpaceClipExtents.Inflate(1.0); 1.320 + 1.321 + gfxRect needed = imageSpaceClipExtents.Intersect(aSourceRect); 1.322 + needed = needed.Intersect(aSubimage); 1.323 + needed.RoundOut(); 1.324 + 1.325 + // if 'needed' is empty, nothing will be drawn since aFill 1.326 + // must be entirely outside the clip region, so it doesn't 1.327 + // matter what we do here, but we should avoid trying to 1.328 + // create a zero-size surface. 1.329 + if (needed.IsEmpty()) 1.330 + return nullptr; 1.331 + 1.332 + nsRefPtr<gfxDrawable> drawable; 1.333 + gfxIntSize size(int32_t(needed.Width()), int32_t(needed.Height())); 1.334 + 1.335 + nsRefPtr<gfxImageSurface> image = aDrawable->GetAsImageSurface(); 1.336 + if (image && gfxRect(0, 0, image->GetSize().width, image->GetSize().height).Contains(needed)) { 1.337 + nsRefPtr<gfxASurface> temp = image->GetSubimage(needed); 1.338 + drawable = new gfxSurfaceDrawable(temp, size, gfxMatrix().Translate(-needed.TopLeft())); 1.339 + } else { 1.340 + mozilla::RefPtr<mozilla::gfx::DrawTarget> target = 1.341 + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size), 1.342 + ImageFormatToSurfaceFormat(aFormat)); 1.343 + if (!target) { 1.344 + return nullptr; 1.345 + } 1.346 + 1.347 + nsRefPtr<gfxContext> tmpCtx = new gfxContext(target); 1.348 + tmpCtx->SetOperator(OptimalFillOperator()); 1.349 + aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true, 1.350 + GraphicsFilter::FILTER_FAST, gfxMatrix().Translate(needed.TopLeft())); 1.351 + drawable = new gfxSurfaceDrawable(target, size, gfxMatrix().Translate(-needed.TopLeft())); 1.352 + } 1.353 + 1.354 + return drawable.forget(); 1.355 +} 1.356 +#endif // !MOZ_GFX_OPTIMIZE_MOBILE 1.357 + 1.358 +// working around cairo/pixman bug (bug 364968) 1.359 +// Our device-space-to-image-space transform may not be acceptable to pixman. 1.360 +struct MOZ_STACK_CLASS AutoCairoPixmanBugWorkaround 1.361 +{ 1.362 + AutoCairoPixmanBugWorkaround(gfxContext* aContext, 1.363 + const gfxMatrix& aDeviceSpaceToImageSpace, 1.364 + const gfxRect& aFill, 1.365 + const gfxASurface* aSurface) 1.366 + : mContext(aContext), mSucceeded(true), mPushedGroup(false) 1.367 + { 1.368 + // Quartz's limits for matrix are much larger than pixman 1.369 + if (!aSurface || aSurface->GetType() == gfxSurfaceType::Quartz) 1.370 + return; 1.371 + 1.372 + if (!IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.xx) || 1.373 + !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.xy) || 1.374 + !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.yx) || 1.375 + !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.yy)) { 1.376 + NS_WARNING("Scaling up too much, bailing out"); 1.377 + mSucceeded = false; 1.378 + return; 1.379 + } 1.380 + 1.381 + if (IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.x0) && 1.382 + IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.y0)) 1.383 + return; 1.384 + 1.385 + // We'll push a group, which will hopefully reduce our transform's 1.386 + // translation so it's in bounds. 1.387 + gfxMatrix currentMatrix = mContext->CurrentMatrix(); 1.388 + mContext->Save(); 1.389 + 1.390 + // Clip the rounded-out-to-device-pixels bounds of the 1.391 + // transformed fill area. This is the area for the group we 1.392 + // want to push. 1.393 + mContext->IdentityMatrix(); 1.394 + gfxRect bounds = currentMatrix.TransformBounds(aFill); 1.395 + bounds.RoundOut(); 1.396 + mContext->Clip(bounds); 1.397 + mContext->SetMatrix(currentMatrix); 1.398 + mContext->PushGroup(gfxContentType::COLOR_ALPHA); 1.399 + mContext->SetOperator(gfxContext::OPERATOR_OVER); 1.400 + 1.401 + mPushedGroup = true; 1.402 + } 1.403 + 1.404 + ~AutoCairoPixmanBugWorkaround() 1.405 + { 1.406 + if (mPushedGroup) { 1.407 + mContext->PopGroupToSource(); 1.408 + mContext->Paint(); 1.409 + mContext->Restore(); 1.410 + } 1.411 + } 1.412 + 1.413 + bool PushedGroup() { return mPushedGroup; } 1.414 + bool Succeeded() { return mSucceeded; } 1.415 + 1.416 +private: 1.417 + gfxContext* mContext; 1.418 + bool mSucceeded; 1.419 + bool mPushedGroup; 1.420 +}; 1.421 + 1.422 +static gfxMatrix 1.423 +DeviceToImageTransform(gfxContext* aContext, 1.424 + const gfxMatrix& aUserSpaceToImageSpace) 1.425 +{ 1.426 + gfxFloat deviceX, deviceY; 1.427 + nsRefPtr<gfxASurface> currentTarget = 1.428 + aContext->CurrentSurface(&deviceX, &deviceY); 1.429 + gfxMatrix currentMatrix = aContext->CurrentMatrix(); 1.430 + gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert(); 1.431 + deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY)); 1.432 + return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace); 1.433 +} 1.434 + 1.435 +/* These heuristics are based on Source/WebCore/platform/graphics/skia/ImageSkia.cpp:computeResamplingMode() */ 1.436 +#ifdef MOZ_GFX_OPTIMIZE_MOBILE 1.437 +static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter, 1.438 + int aImgWidth, int aImgHeight, 1.439 + float aSourceWidth, float aSourceHeight) 1.440 +{ 1.441 + // Images smaller than this in either direction are considered "small" and 1.442 + // are not resampled ever (see below). 1.443 + const int kSmallImageSizeThreshold = 8; 1.444 + 1.445 + // The amount an image can be stretched in a single direction before we 1.446 + // say that it is being stretched so much that it must be a line or 1.447 + // background that doesn't need resampling. 1.448 + const float kLargeStretch = 3.0f; 1.449 + 1.450 + if (aImgWidth <= kSmallImageSizeThreshold 1.451 + || aImgHeight <= kSmallImageSizeThreshold) { 1.452 + // Never resample small images. These are often used for borders and 1.453 + // rules (think 1x1 images used to make lines). 1.454 + return GraphicsFilter::FILTER_NEAREST; 1.455 + } 1.456 + 1.457 + if (aImgHeight * kLargeStretch <= aSourceHeight || aImgWidth * kLargeStretch <= aSourceWidth) { 1.458 + // Large image tiling detected. 1.459 + 1.460 + // Don't resample if it is being tiled a lot in only one direction. 1.461 + // This is trying to catch cases where somebody has created a border 1.462 + // (which might be large) and then is stretching it to fill some part 1.463 + // of the page. 1.464 + if (fabs(aSourceWidth - aImgWidth)/aImgWidth < 0.5 || fabs(aSourceHeight - aImgHeight)/aImgHeight < 0.5) 1.465 + return GraphicsFilter::FILTER_NEAREST; 1.466 + 1.467 + // The image is growing a lot and in more than one direction. Resampling 1.468 + // is slow and doesn't give us very much when growing a lot. 1.469 + return aFilter; 1.470 + } 1.471 + 1.472 + /* Some notes on other heuristics: 1.473 + The Skia backend also uses nearest for backgrounds that are stretched by 1.474 + a large amount. I'm not sure this is common enough for us to worry about 1.475 + now. It also uses nearest for backgrounds/avoids high quality for images 1.476 + that are very slightly scaled. I'm also not sure that very slightly 1.477 + scaled backgrounds are common enough us to worry about. 1.478 + 1.479 + We don't currently have much support for doing high quality interpolation. 1.480 + The only place this currently happens is on Quartz and we don't have as 1.481 + much control over it as would be needed. Webkit avoids using high quality 1.482 + resampling during load. It also avoids high quality if the transformation 1.483 + is not just a scale and translation 1.484 + 1.485 + WebKit bug #40045 added code to avoid resampling different parts 1.486 + of an image with different methods by using a resampling hint size. 1.487 + It currently looks unused in WebKit but it's something to watch out for. 1.488 + */ 1.489 + 1.490 + return aFilter; 1.491 +} 1.492 +#else 1.493 +static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter, 1.494 + int aImgWidth, int aImgHeight, 1.495 + int aSourceWidth, int aSourceHeight) 1.496 +{ 1.497 + // Just pass the filter through unchanged 1.498 + return aFilter; 1.499 +} 1.500 +#endif 1.501 + 1.502 +/* static */ void 1.503 +gfxUtils::DrawPixelSnapped(gfxContext* aContext, 1.504 + gfxDrawable* aDrawable, 1.505 + const gfxMatrix& aUserSpaceToImageSpace, 1.506 + const gfxRect& aSubimage, 1.507 + const gfxRect& aSourceRect, 1.508 + const gfxRect& aImageRect, 1.509 + const gfxRect& aFill, 1.510 + const gfxImageFormat aFormat, 1.511 + GraphicsFilter aFilter, 1.512 + uint32_t aImageFlags) 1.513 +{ 1.514 + PROFILER_LABEL("gfxUtils", "DrawPixelSnapped"); 1.515 + bool doTile = !aImageRect.Contains(aSourceRect) && 1.516 + !(aImageFlags & imgIContainer::FLAG_CLAMP); 1.517 + 1.518 + nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface(); 1.519 + gfxMatrix deviceSpaceToImageSpace = 1.520 + DeviceToImageTransform(aContext, aUserSpaceToImageSpace); 1.521 + 1.522 + AutoCairoPixmanBugWorkaround workaround(aContext, deviceSpaceToImageSpace, 1.523 + aFill, currentTarget); 1.524 + if (!workaround.Succeeded()) 1.525 + return; 1.526 + 1.527 + nsRefPtr<gfxDrawable> drawable = aDrawable; 1.528 + 1.529 + aFilter = ReduceResamplingFilter(aFilter, aImageRect.Width(), aImageRect.Height(), aSourceRect.Width(), aSourceRect.Height()); 1.530 + 1.531 + gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace; 1.532 + 1.533 + // On Mobile, we don't ever want to do this; it has the potential for 1.534 + // allocating very large temporary surfaces, especially since we'll 1.535 + // do full-page snapshots often (see bug 749426). 1.536 +#ifdef MOZ_GFX_OPTIMIZE_MOBILE 1.537 + // If the pattern translation is large we can get into trouble with pixman's 1.538 + // 16 bit coordinate limits. For now, we only do this on platforms where 1.539 + // we know we have the pixman limits. 16384.0 is a somewhat arbitrary 1.540 + // large number to make sure we avoid the expensive fmod when we can, but 1.541 + // still maintain a safe margin from the actual limit 1.542 + if (doTile && (userSpaceToImageSpace.y0 > 16384.0 || userSpaceToImageSpace.x0 > 16384.0)) { 1.543 + userSpaceToImageSpace.x0 = fmod(userSpaceToImageSpace.x0, aImageRect.width); 1.544 + userSpaceToImageSpace.y0 = fmod(userSpaceToImageSpace.y0, aImageRect.height); 1.545 + } 1.546 +#else 1.547 + // OK now, the hard part left is to account for the subimage sampling 1.548 + // restriction. If all the transforms involved are just integer 1.549 + // translations, then we assume no resampling will occur so there's 1.550 + // nothing to do. 1.551 + // XXX if only we had source-clipping in cairo! 1.552 + if (aContext->CurrentMatrix().HasNonIntegerTranslation() || 1.553 + aUserSpaceToImageSpace.HasNonIntegerTranslation()) { 1.554 + if (doTile || !aSubimage.Contains(aImageRect)) { 1.555 + nsRefPtr<gfxDrawable> restrictedDrawable = 1.556 + CreateSamplingRestrictedDrawable(aDrawable, aContext, 1.557 + aUserSpaceToImageSpace, aSourceRect, 1.558 + aSubimage, aFormat); 1.559 + if (restrictedDrawable) { 1.560 + drawable.swap(restrictedDrawable); 1.561 + } 1.562 + } 1.563 + // We no longer need to tile: Either we never needed to, or we already 1.564 + // filled a surface with the tiled pattern; this surface can now be 1.565 + // drawn without tiling. 1.566 + doTile = false; 1.567 + } 1.568 +#endif 1.569 + 1.570 + drawable->Draw(aContext, aFill, doTile, aFilter, userSpaceToImageSpace); 1.571 +} 1.572 + 1.573 +/* static */ int 1.574 +gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat) 1.575 +{ 1.576 + switch (aFormat) { 1.577 + case gfxImageFormat::ARGB32: 1.578 + return 32; 1.579 + case gfxImageFormat::RGB24: 1.580 + return 24; 1.581 + case gfxImageFormat::RGB16_565: 1.582 + return 16; 1.583 + default: 1.584 + break; 1.585 + } 1.586 + return 0; 1.587 +} 1.588 + 1.589 +static void 1.590 +PathFromRegionInternal(gfxContext* aContext, const nsIntRegion& aRegion, 1.591 + bool aSnap) 1.592 +{ 1.593 + aContext->NewPath(); 1.594 + nsIntRegionRectIterator iter(aRegion); 1.595 + const nsIntRect* r; 1.596 + while ((r = iter.Next()) != nullptr) { 1.597 + aContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height), aSnap); 1.598 + } 1.599 +} 1.600 + 1.601 +static void 1.602 +ClipToRegionInternal(gfxContext* aContext, const nsIntRegion& aRegion, 1.603 + bool aSnap) 1.604 +{ 1.605 + PathFromRegionInternal(aContext, aRegion, aSnap); 1.606 + aContext->Clip(); 1.607 +} 1.608 + 1.609 +static TemporaryRef<Path> 1.610 +PathFromRegionInternal(DrawTarget* aTarget, const nsIntRegion& aRegion, 1.611 + bool aSnap) 1.612 +{ 1.613 + Matrix mat = aTarget->GetTransform(); 1.614 + const gfxFloat epsilon = 0.000001; 1.615 +#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon) 1.616 + // We're essentially duplicating the logic in UserToDevicePixelSnapped here. 1.617 + bool shouldNotSnap = !aSnap || (WITHIN_E(mat._11,1.0) && 1.618 + WITHIN_E(mat._22,1.0) && 1.619 + WITHIN_E(mat._12,0.0) && 1.620 + WITHIN_E(mat._21,0.0)); 1.621 +#undef WITHIN_E 1.622 + 1.623 + RefPtr<PathBuilder> pb = aTarget->CreatePathBuilder(); 1.624 + nsIntRegionRectIterator iter(aRegion); 1.625 + 1.626 + const nsIntRect* r; 1.627 + if (shouldNotSnap) { 1.628 + while ((r = iter.Next()) != nullptr) { 1.629 + pb->MoveTo(Point(r->x, r->y)); 1.630 + pb->LineTo(Point(r->XMost(), r->y)); 1.631 + pb->LineTo(Point(r->XMost(), r->YMost())); 1.632 + pb->LineTo(Point(r->x, r->YMost())); 1.633 + pb->Close(); 1.634 + } 1.635 + } else { 1.636 + while ((r = iter.Next()) != nullptr) { 1.637 + Rect rect(r->x, r->y, r->width, r->height); 1.638 + 1.639 + rect.Round(); 1.640 + pb->MoveTo(rect.TopLeft()); 1.641 + pb->LineTo(rect.TopRight()); 1.642 + pb->LineTo(rect.BottomRight()); 1.643 + pb->LineTo(rect.BottomLeft()); 1.644 + pb->Close(); 1.645 + } 1.646 + } 1.647 + RefPtr<Path> path = pb->Finish(); 1.648 + return path; 1.649 +} 1.650 + 1.651 +static void 1.652 +ClipToRegionInternal(DrawTarget* aTarget, const nsIntRegion& aRegion, 1.653 + bool aSnap) 1.654 +{ 1.655 + RefPtr<Path> path = PathFromRegionInternal(aTarget, aRegion, aSnap); 1.656 + aTarget->PushClip(path); 1.657 +} 1.658 + 1.659 +/*static*/ void 1.660 +gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion) 1.661 +{ 1.662 + ClipToRegionInternal(aContext, aRegion, false); 1.663 +} 1.664 + 1.665 +/*static*/ void 1.666 +gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion) 1.667 +{ 1.668 + ClipToRegionInternal(aTarget, aRegion, false); 1.669 +} 1.670 + 1.671 +/*static*/ void 1.672 +gfxUtils::ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion) 1.673 +{ 1.674 + ClipToRegionInternal(aContext, aRegion, true); 1.675 +} 1.676 + 1.677 +/*static*/ void 1.678 +gfxUtils::ClipToRegionSnapped(DrawTarget* aTarget, const nsIntRegion& aRegion) 1.679 +{ 1.680 + ClipToRegionInternal(aTarget, aRegion, true); 1.681 +} 1.682 + 1.683 +/*static*/ gfxFloat 1.684 +gfxUtils::ClampToScaleFactor(gfxFloat aVal) 1.685 +{ 1.686 + // Arbitary scale factor limitation. We can increase this 1.687 + // for better scaling performance at the cost of worse 1.688 + // quality. 1.689 + static const gfxFloat kScaleResolution = 2; 1.690 + 1.691 + // Negative scaling is just a flip and irrelevant to 1.692 + // our resolution calculation. 1.693 + if (aVal < 0.0) { 1.694 + aVal = -aVal; 1.695 + } 1.696 + 1.697 + bool inverse = false; 1.698 + if (aVal < 1.0) { 1.699 + inverse = true; 1.700 + aVal = 1 / aVal; 1.701 + } 1.702 + 1.703 + gfxFloat power = log(aVal)/log(kScaleResolution); 1.704 + 1.705 + // If power is within 1e-6 of an integer, round to nearest to 1.706 + // prevent floating point errors, otherwise round up to the 1.707 + // next integer value. 1.708 + if (fabs(power - NS_round(power)) < 1e-6) { 1.709 + power = NS_round(power); 1.710 + } else if (inverse) { 1.711 + power = floor(power); 1.712 + } else { 1.713 + power = ceil(power); 1.714 + } 1.715 + 1.716 + gfxFloat scale = pow(kScaleResolution, power); 1.717 + 1.718 + if (inverse) { 1.719 + scale = 1 / scale; 1.720 + } 1.721 + 1.722 + return scale; 1.723 +} 1.724 + 1.725 + 1.726 +/*static*/ void 1.727 +gfxUtils::PathFromRegion(gfxContext* aContext, const nsIntRegion& aRegion) 1.728 +{ 1.729 + PathFromRegionInternal(aContext, aRegion, false); 1.730 +} 1.731 + 1.732 +/*static*/ void 1.733 +gfxUtils::PathFromRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion) 1.734 +{ 1.735 + PathFromRegionInternal(aContext, aRegion, true); 1.736 +} 1.737 + 1.738 +gfxMatrix 1.739 +gfxUtils::TransformRectToRect(const gfxRect& aFrom, const gfxPoint& aToTopLeft, 1.740 + const gfxPoint& aToTopRight, const gfxPoint& aToBottomRight) 1.741 +{ 1.742 + gfxMatrix m; 1.743 + if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) { 1.744 + // Not a rotation, so xy and yx are zero 1.745 + m.xy = m.yx = 0.0; 1.746 + m.xx = (aToBottomRight.x - aToTopLeft.x)/aFrom.width; 1.747 + m.yy = (aToBottomRight.y - aToTopLeft.y)/aFrom.height; 1.748 + m.x0 = aToTopLeft.x - m.xx*aFrom.x; 1.749 + m.y0 = aToTopLeft.y - m.yy*aFrom.y; 1.750 + } else { 1.751 + NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x, 1.752 + "Destination rectangle not axis-aligned"); 1.753 + m.xx = m.yy = 0.0; 1.754 + m.xy = (aToBottomRight.x - aToTopLeft.x)/aFrom.height; 1.755 + m.yx = (aToBottomRight.y - aToTopLeft.y)/aFrom.width; 1.756 + m.x0 = aToTopLeft.x - m.xy*aFrom.y; 1.757 + m.y0 = aToTopLeft.y - m.yx*aFrom.x; 1.758 + } 1.759 + return m; 1.760 +} 1.761 + 1.762 +Matrix 1.763 +gfxUtils::TransformRectToRect(const gfxRect& aFrom, const IntPoint& aToTopLeft, 1.764 + const IntPoint& aToTopRight, const IntPoint& aToBottomRight) 1.765 +{ 1.766 + Matrix m; 1.767 + if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) { 1.768 + // Not a rotation, so xy and yx are zero 1.769 + m._12 = m._21 = 0.0; 1.770 + m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width; 1.771 + m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height; 1.772 + m._31 = aToTopLeft.x - m._11*aFrom.x; 1.773 + m._32 = aToTopLeft.y - m._22*aFrom.y; 1.774 + } else { 1.775 + NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x, 1.776 + "Destination rectangle not axis-aligned"); 1.777 + m._11 = m._22 = 0.0; 1.778 + m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height; 1.779 + m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width; 1.780 + m._31 = aToTopLeft.x - m._21*aFrom.y; 1.781 + m._32 = aToTopLeft.y - m._12*aFrom.x; 1.782 + } 1.783 + return m; 1.784 +} 1.785 + 1.786 +bool 1.787 +gfxUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut) 1.788 +{ 1.789 + *aOut = nsIntRect(int32_t(aIn.X()), int32_t(aIn.Y()), 1.790 + int32_t(aIn.Width()), int32_t(aIn.Height())); 1.791 + return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height).IsEqualEdges(aIn); 1.792 +} 1.793 + 1.794 +void 1.795 +gfxUtils::GetYCbCrToRGBDestFormatAndSize(const PlanarYCbCrData& aData, 1.796 + gfxImageFormat& aSuggestedFormat, 1.797 + gfxIntSize& aSuggestedSize) 1.798 +{ 1.799 + YUVType yuvtype = 1.800 + TypeFromSize(aData.mYSize.width, 1.801 + aData.mYSize.height, 1.802 + aData.mCbCrSize.width, 1.803 + aData.mCbCrSize.height); 1.804 + 1.805 + // 'prescale' is true if the scaling is to be done as part of the 1.806 + // YCbCr to RGB conversion rather than on the RGB data when rendered. 1.807 + bool prescale = aSuggestedSize.width > 0 && aSuggestedSize.height > 0 && 1.808 + ToIntSize(aSuggestedSize) != aData.mPicSize; 1.809 + 1.810 + if (aSuggestedFormat == gfxImageFormat::RGB16_565) { 1.811 +#if defined(HAVE_YCBCR_TO_RGB565) 1.812 + if (prescale && 1.813 + !IsScaleYCbCrToRGB565Fast(aData.mPicX, 1.814 + aData.mPicY, 1.815 + aData.mPicSize.width, 1.816 + aData.mPicSize.height, 1.817 + aSuggestedSize.width, 1.818 + aSuggestedSize.height, 1.819 + yuvtype, 1.820 + FILTER_BILINEAR) && 1.821 + IsConvertYCbCrToRGB565Fast(aData.mPicX, 1.822 + aData.mPicY, 1.823 + aData.mPicSize.width, 1.824 + aData.mPicSize.height, 1.825 + yuvtype)) { 1.826 + prescale = false; 1.827 + } 1.828 +#else 1.829 + // yuv2rgb16 function not available 1.830 + aSuggestedFormat = gfxImageFormat::RGB24; 1.831 +#endif 1.832 + } 1.833 + else if (aSuggestedFormat != gfxImageFormat::RGB24) { 1.834 + // No other formats are currently supported. 1.835 + aSuggestedFormat = gfxImageFormat::RGB24; 1.836 + } 1.837 + if (aSuggestedFormat == gfxImageFormat::RGB24) { 1.838 + /* ScaleYCbCrToRGB32 does not support a picture offset, nor 4:4:4 data. 1.839 + See bugs 639415 and 640073. */ 1.840 + if (aData.mPicX != 0 || aData.mPicY != 0 || yuvtype == YV24) 1.841 + prescale = false; 1.842 + } 1.843 + if (!prescale) { 1.844 + ToIntSize(aSuggestedSize) = aData.mPicSize; 1.845 + } 1.846 +} 1.847 + 1.848 +void 1.849 +gfxUtils::ConvertYCbCrToRGB(const PlanarYCbCrData& aData, 1.850 + const gfxImageFormat& aDestFormat, 1.851 + const gfxIntSize& aDestSize, 1.852 + unsigned char* aDestBuffer, 1.853 + int32_t aStride) 1.854 +{ 1.855 + // ConvertYCbCrToRGB et al. assume the chroma planes are rounded up if the 1.856 + // luma plane is odd sized. 1.857 + MOZ_ASSERT((aData.mCbCrSize.width == aData.mYSize.width || 1.858 + aData.mCbCrSize.width == (aData.mYSize.width + 1) >> 1) && 1.859 + (aData.mCbCrSize.height == aData.mYSize.height || 1.860 + aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1)); 1.861 + YUVType yuvtype = 1.862 + TypeFromSize(aData.mYSize.width, 1.863 + aData.mYSize.height, 1.864 + aData.mCbCrSize.width, 1.865 + aData.mCbCrSize.height); 1.866 + 1.867 + // Convert from YCbCr to RGB now, scaling the image if needed. 1.868 + if (ToIntSize(aDestSize) != aData.mPicSize) { 1.869 +#if defined(HAVE_YCBCR_TO_RGB565) 1.870 + if (aDestFormat == gfxImageFormat::RGB16_565) { 1.871 + ScaleYCbCrToRGB565(aData.mYChannel, 1.872 + aData.mCbChannel, 1.873 + aData.mCrChannel, 1.874 + aDestBuffer, 1.875 + aData.mPicX, 1.876 + aData.mPicY, 1.877 + aData.mPicSize.width, 1.878 + aData.mPicSize.height, 1.879 + aDestSize.width, 1.880 + aDestSize.height, 1.881 + aData.mYStride, 1.882 + aData.mCbCrStride, 1.883 + aStride, 1.884 + yuvtype, 1.885 + FILTER_BILINEAR); 1.886 + } else 1.887 +#endif 1.888 + ScaleYCbCrToRGB32(aData.mYChannel, 1.889 + aData.mCbChannel, 1.890 + aData.mCrChannel, 1.891 + aDestBuffer, 1.892 + aData.mPicSize.width, 1.893 + aData.mPicSize.height, 1.894 + aDestSize.width, 1.895 + aDestSize.height, 1.896 + aData.mYStride, 1.897 + aData.mCbCrStride, 1.898 + aStride, 1.899 + yuvtype, 1.900 + ROTATE_0, 1.901 + FILTER_BILINEAR); 1.902 + } else { // no prescale 1.903 +#if defined(HAVE_YCBCR_TO_RGB565) 1.904 + if (aDestFormat == gfxImageFormat::RGB16_565) { 1.905 + ConvertYCbCrToRGB565(aData.mYChannel, 1.906 + aData.mCbChannel, 1.907 + aData.mCrChannel, 1.908 + aDestBuffer, 1.909 + aData.mPicX, 1.910 + aData.mPicY, 1.911 + aData.mPicSize.width, 1.912 + aData.mPicSize.height, 1.913 + aData.mYStride, 1.914 + aData.mCbCrStride, 1.915 + aStride, 1.916 + yuvtype); 1.917 + } else // aDestFormat != gfxImageFormat::RGB16_565 1.918 +#endif 1.919 + ConvertYCbCrToRGB32(aData.mYChannel, 1.920 + aData.mCbChannel, 1.921 + aData.mCrChannel, 1.922 + aDestBuffer, 1.923 + aData.mPicX, 1.924 + aData.mPicY, 1.925 + aData.mPicSize.width, 1.926 + aData.mPicSize.height, 1.927 + aData.mYStride, 1.928 + aData.mCbCrStride, 1.929 + aStride, 1.930 + yuvtype); 1.931 + } 1.932 +} 1.933 + 1.934 +/* static */ TemporaryRef<DataSourceSurface> 1.935 +gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface, 1.936 + SurfaceFormat aFormat) 1.937 +{ 1.938 + MOZ_ASSERT(aFormat != aSurface->GetFormat(), 1.939 + "Unnecessary - and very expersive - surface format conversion"); 1.940 + 1.941 + Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height); 1.942 + 1.943 + if (aSurface->GetType() != SurfaceType::DATA) { 1.944 + // If the surface is NOT of type DATA then its data is not mapped into main 1.945 + // memory. Format conversion is probably faster on the GPU, and by doing it 1.946 + // there we can avoid any expensive uploads/readbacks except for (possibly) 1.947 + // a single readback due to the unavoidable GetDataSurface() call. Using 1.948 + // CreateOffscreenContentDrawTarget ensures the conversion happens on the 1.949 + // GPU. 1.950 + RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()-> 1.951 + CreateOffscreenContentDrawTarget(aSurface->GetSize(), aFormat); 1.952 + // Using DrawSurface() here rather than CopySurface() because CopySurface 1.953 + // is optimized for memcpy and therefore isn't good for format conversion. 1.954 + // Using OP_OVER since in our case it's equivalent to OP_SOURCE and 1.955 + // generally more optimized. 1.956 + dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(), 1.957 + DrawOptions(1.0f, CompositionOp::OP_OVER)); 1.958 + RefPtr<SourceSurface> surface = dt->Snapshot(); 1.959 + return surface->GetDataSurface(); 1.960 + } 1.961 + 1.962 + // If the surface IS of type DATA then it may or may not be in main memory 1.963 + // depending on whether or not it has been mapped yet. We have no way of 1.964 + // knowing, so we can't be sure if it's best to create a data wrapping 1.965 + // DrawTarget for the conversion or an offscreen content DrawTarget. We could 1.966 + // guess it's not mapped and create an offscreen content DrawTarget, but if 1.967 + // it is then we'll end up uploading the surface data, and most likely the 1.968 + // caller is going to be accessing the resulting surface data, resulting in a 1.969 + // readback (both very expensive operations). Alternatively we could guess 1.970 + // the data is mapped and create a data wrapping DrawTarget and, if the 1.971 + // surface is not in main memory, then we will incure a readback. The latter 1.972 + // of these two "wrong choices" is the least costly (a readback, vs an 1.973 + // upload and a readback), and more than likely the DATA surface that we've 1.974 + // been passed actually IS in main memory anyway. For these reasons it's most 1.975 + // likely best to create a data wrapping DrawTarget here to do the format 1.976 + // conversion. 1.977 + RefPtr<DataSourceSurface> dataSurface = 1.978 + Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat); 1.979 + DataSourceSurface::MappedSurface map; 1.980 + if (!dataSurface || 1.981 + !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { 1.982 + return nullptr; 1.983 + } 1.984 + RefPtr<DrawTarget> dt = 1.985 + Factory::CreateDrawTargetForData(BackendType::CAIRO, 1.986 + map.mData, 1.987 + dataSurface->GetSize(), 1.988 + map.mStride, 1.989 + aFormat); 1.990 + if (!dt) { 1.991 + dataSurface->Unmap(); 1.992 + return nullptr; 1.993 + } 1.994 + // Using DrawSurface() here rather than CopySurface() because CopySurface 1.995 + // is optimized for memcpy and therefore isn't good for format conversion. 1.996 + // Using OP_OVER since in our case it's equivalent to OP_SOURCE and 1.997 + // generally more optimized. 1.998 + dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(), 1.999 + DrawOptions(1.0f, CompositionOp::OP_OVER)); 1.1000 + dataSurface->Unmap(); 1.1001 + return dataSurface.forget(); 1.1002 +} 1.1003 + 1.1004 +#ifdef MOZ_DUMP_PAINTING 1.1005 +/* static */ void 1.1006 +gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile) 1.1007 +{ 1.1008 + aDT->Flush(); 1.1009 + nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT); 1.1010 + if (surf) { 1.1011 + surf->WriteAsPNG(aFile); 1.1012 + } else { 1.1013 + NS_WARNING("Failed to get Thebes surface!"); 1.1014 + } 1.1015 +} 1.1016 + 1.1017 +/* static */ void 1.1018 +gfxUtils::DumpAsDataURL(DrawTarget* aDT) 1.1019 +{ 1.1020 + aDT->Flush(); 1.1021 + nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT); 1.1022 + if (surf) { 1.1023 + surf->DumpAsDataURL(); 1.1024 + } else { 1.1025 + NS_WARNING("Failed to get Thebes surface!"); 1.1026 + } 1.1027 +} 1.1028 + 1.1029 +/* static */ void 1.1030 +gfxUtils::CopyAsDataURL(DrawTarget* aDT) 1.1031 +{ 1.1032 + aDT->Flush(); 1.1033 + nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT); 1.1034 + if (surf) { 1.1035 + surf->CopyAsDataURL(); 1.1036 + } else { 1.1037 + NS_WARNING("Failed to get Thebes surface!"); 1.1038 + } 1.1039 +} 1.1040 + 1.1041 +/* static */ void 1.1042 +gfxUtils::WriteAsPNG(RefPtr<gfx::SourceSurface> aSourceSurface, const char* aFile) 1.1043 +{ 1.1044 + RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface(); 1.1045 + RefPtr<gfx::DrawTarget> dt 1.1046 + = gfxPlatform::GetPlatform() 1.1047 + ->CreateDrawTargetForData(dataSurface->GetData(), 1.1048 + dataSurface->GetSize(), 1.1049 + dataSurface->Stride(), 1.1050 + aSourceSurface->GetFormat()); 1.1051 + gfxUtils::WriteAsPNG(dt.get(), aFile); 1.1052 +} 1.1053 + 1.1054 +/* static */ void 1.1055 +gfxUtils::DumpAsDataURL(RefPtr<gfx::SourceSurface> aSourceSurface) 1.1056 +{ 1.1057 + RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface(); 1.1058 + RefPtr<gfx::DrawTarget> dt 1.1059 + = gfxPlatform::GetPlatform() 1.1060 + ->CreateDrawTargetForData(dataSurface->GetData(), 1.1061 + dataSurface->GetSize(), 1.1062 + dataSurface->Stride(), 1.1063 + aSourceSurface->GetFormat()); 1.1064 + gfxUtils::DumpAsDataURL(dt.get()); 1.1065 +} 1.1066 + 1.1067 +/* static */ void 1.1068 +gfxUtils::CopyAsDataURL(RefPtr<gfx::SourceSurface> aSourceSurface) 1.1069 +{ 1.1070 + RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface(); 1.1071 + RefPtr<gfx::DrawTarget> dt 1.1072 + = gfxPlatform::GetPlatform() 1.1073 + ->CreateDrawTargetForData(dataSurface->GetData(), 1.1074 + dataSurface->GetSize(), 1.1075 + dataSurface->Stride(), 1.1076 + aSourceSurface->GetFormat()); 1.1077 + 1.1078 + gfxUtils::CopyAsDataURL(dt.get()); 1.1079 +} 1.1080 + 1.1081 +bool gfxUtils::sDumpPaintList = getenv("MOZ_DUMP_PAINT_LIST") != 0; 1.1082 +bool gfxUtils::sDumpPainting = getenv("MOZ_DUMP_PAINT") != 0; 1.1083 +bool gfxUtils::sDumpPaintingToFile = getenv("MOZ_DUMP_PAINT_TO_FILE") != 0; 1.1084 +FILE *gfxUtils::sDumpPaintFile = nullptr; 1.1085 +#endif