gfxUtils.cpp (60828B)
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 "gfxUtils.h" 7 8 #include "cairo.h" 9 #include "gfxContext.h" 10 #include "gfxEnv.h" 11 #include "gfxImageSurface.h" 12 #include "gfxPlatform.h" 13 #include "gfxDrawable.h" 14 #include "gfxQuad.h" 15 #include "imgIEncoder.h" 16 #include "mozilla/Base64.h" 17 #include "mozilla/StyleColorInlines.h" 18 #include "mozilla/Components.h" 19 #include "mozilla/dom/Document.h" 20 #include "mozilla/dom/ImageEncoder.h" 21 #include "mozilla/dom/WorkerPrivate.h" 22 #include "mozilla/dom/WorkerRunnable.h" 23 #include "mozilla/ipc/CrossProcessSemaphore.h" 24 #include "mozilla/gfx/2D.h" 25 #include "mozilla/gfx/DataSurfaceHelpers.h" 26 #include "mozilla/gfx/Logging.h" 27 #include "mozilla/gfx/PathHelpers.h" 28 #include "mozilla/gfx/Swizzle.h" 29 #include "mozilla/gfx/gfxVars.h" 30 #include "mozilla/image/nsBMPEncoder.h" 31 #include "mozilla/image/nsICOEncoder.h" 32 #include "mozilla/image/nsJPEGEncoder.h" 33 #include "mozilla/image/nsPNGEncoder.h" 34 #include "mozilla/layers/SynchronousTask.h" 35 #include "mozilla/Maybe.h" 36 #include "mozilla/Preferences.h" 37 #include "mozilla/ProfilerLabels.h" 38 #include "mozilla/RefPtr.h" 39 #include "mozilla/ServoStyleConsts.h" 40 #include "mozilla/StaticPrefs_gfx.h" 41 #include "mozilla/StaticPrefs_layout.h" 42 #include "mozilla/UniquePtrExtensions.h" 43 #include "mozilla/webrender/webrender_ffi.h" 44 #include "nsAppRunner.h" 45 #include "nsComponentManagerUtils.h" 46 #include "nsIClipboardHelper.h" 47 #include "nsIFile.h" 48 #include "nsIGfxInfo.h" 49 #include "nsMimeTypes.h" 50 #include "nsPresContext.h" 51 #include "nsRegion.h" 52 #include "nsServiceManagerUtils.h" 53 #include "nsRFPService.h" 54 #include "ImageContainer.h" 55 #include "ImageRegion.h" 56 #include "gfx2DGlue.h" 57 58 #ifdef XP_WIN 59 # include "gfxWindowsPlatform.h" 60 #endif 61 62 using namespace mozilla; 63 using namespace mozilla::image; 64 using namespace mozilla::layers; 65 using namespace mozilla::gfx; 66 67 #undef compress 68 #include "mozilla/Compression.h" 69 70 using namespace mozilla::Compression; 71 extern "C" { 72 73 /** 74 * Dump a raw image to the default log. This function is exported 75 * from libxul, so it can be called from any library in addition to 76 * (of course) from a debugger. 77 * 78 * Note: this helper currently assumes that all 2-bytepp images are 79 * r5g6b5, and that all 4-bytepp images are r8g8b8a8. 80 */ 81 NS_EXPORT 82 void mozilla_dump_image(void* bytes, int width, int height, int bytepp, 83 int strideBytes) { 84 if (0 == strideBytes) { 85 strideBytes = width * bytepp; 86 } 87 SurfaceFormat format; 88 // TODO more flexible; parse string? 89 switch (bytepp) { 90 case 2: 91 format = SurfaceFormat::R5G6B5_UINT16; 92 break; 93 case 4: 94 default: 95 format = SurfaceFormat::R8G8B8A8; 96 break; 97 } 98 99 RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface( 100 (uint8_t*)bytes, strideBytes, IntSize(width, height), format); 101 gfxUtils::DumpAsDataURI(surf); 102 } 103 } 104 105 static bool MapSrcDest(DataSourceSurface* srcSurf, DataSourceSurface* destSurf, 106 DataSourceSurface::MappedSurface* out_srcMap, 107 DataSourceSurface::MappedSurface* out_destMap) { 108 MOZ_ASSERT(srcSurf && destSurf); 109 MOZ_ASSERT(out_srcMap && out_destMap); 110 111 if (srcSurf->GetSize() != destSurf->GetSize()) { 112 MOZ_ASSERT(false, "Width and height must match."); 113 return false; 114 } 115 116 if (srcSurf == destSurf) { 117 DataSourceSurface::MappedSurface map; 118 if (!srcSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { 119 NS_WARNING("Couldn't Map srcSurf/destSurf."); 120 return false; 121 } 122 123 *out_srcMap = map; 124 *out_destMap = map; 125 return true; 126 } 127 128 // Map src for reading. 129 DataSourceSurface::MappedSurface srcMap; 130 if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) { 131 NS_WARNING("Couldn't Map srcSurf."); 132 return false; 133 } 134 135 // Map dest for writing. 136 DataSourceSurface::MappedSurface destMap; 137 if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) { 138 NS_WARNING("Couldn't Map aDest."); 139 srcSurf->Unmap(); 140 return false; 141 } 142 143 *out_srcMap = srcMap; 144 *out_destMap = destMap; 145 return true; 146 } 147 148 static void UnmapSrcDest(DataSourceSurface* srcSurf, 149 DataSourceSurface* destSurf) { 150 if (srcSurf == destSurf) { 151 srcSurf->Unmap(); 152 } else { 153 srcSurf->Unmap(); 154 destSurf->Unmap(); 155 } 156 } 157 158 bool gfxUtils::PremultiplyDataSurface(DataSourceSurface* srcSurf, 159 DataSourceSurface* destSurf) { 160 MOZ_ASSERT(srcSurf && destSurf); 161 162 DataSourceSurface::MappedSurface srcMap; 163 DataSourceSurface::MappedSurface destMap; 164 if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap)) return false; 165 166 PremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(), 167 destMap.mData, destMap.mStride, destSurf->GetFormat(), 168 srcSurf->GetSize()); 169 170 UnmapSrcDest(srcSurf, destSurf); 171 return true; 172 } 173 174 bool gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* srcSurf, 175 DataSourceSurface* destSurf) { 176 MOZ_ASSERT(srcSurf && destSurf); 177 178 DataSourceSurface::MappedSurface srcMap; 179 DataSourceSurface::MappedSurface destMap; 180 if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap)) return false; 181 182 UnpremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(), 183 destMap.mData, destMap.mStride, destSurf->GetFormat(), 184 srcSurf->GetSize()); 185 186 UnmapSrcDest(srcSurf, destSurf); 187 return true; 188 } 189 190 static bool MapSrcAndCreateMappedDest( 191 DataSourceSurface* srcSurf, RefPtr<DataSourceSurface>* out_destSurf, 192 DataSourceSurface::MappedSurface* out_srcMap, 193 DataSourceSurface::MappedSurface* out_destMap) { 194 MOZ_ASSERT(srcSurf); 195 MOZ_ASSERT(out_destSurf && out_srcMap && out_destMap); 196 197 // Ok, map source for reading. 198 DataSourceSurface::MappedSurface srcMap; 199 if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) { 200 MOZ_ASSERT(false, "Couldn't Map srcSurf."); 201 return false; 202 } 203 204 // Make our dest surface based on the src. 205 RefPtr<DataSourceSurface> destSurf = 206 Factory::CreateDataSourceSurfaceWithStride( 207 srcSurf->GetSize(), srcSurf->GetFormat(), srcMap.mStride); 208 if (NS_WARN_IF(!destSurf)) { 209 return false; 210 } 211 212 DataSourceSurface::MappedSurface destMap; 213 if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) { 214 MOZ_ASSERT(false, "Couldn't Map destSurf."); 215 srcSurf->Unmap(); 216 return false; 217 } 218 219 *out_destSurf = destSurf; 220 *out_srcMap = srcMap; 221 *out_destMap = destMap; 222 return true; 223 } 224 225 already_AddRefed<DataSourceSurface> gfxUtils::CreatePremultipliedDataSurface( 226 DataSourceSurface* srcSurf) { 227 RefPtr<DataSourceSurface> destSurf; 228 DataSourceSurface::MappedSurface srcMap; 229 DataSourceSurface::MappedSurface destMap; 230 if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) { 231 MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed."); 232 RefPtr<DataSourceSurface> surface(srcSurf); 233 return surface.forget(); 234 } 235 236 PremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(), 237 destMap.mData, destMap.mStride, destSurf->GetFormat(), 238 srcSurf->GetSize()); 239 240 UnmapSrcDest(srcSurf, destSurf); 241 return destSurf.forget(); 242 } 243 244 already_AddRefed<DataSourceSurface> gfxUtils::CreateUnpremultipliedDataSurface( 245 DataSourceSurface* srcSurf) { 246 RefPtr<DataSourceSurface> destSurf; 247 DataSourceSurface::MappedSurface srcMap; 248 DataSourceSurface::MappedSurface destMap; 249 if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) { 250 MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed."); 251 RefPtr<DataSourceSurface> surface(srcSurf); 252 return surface.forget(); 253 } 254 255 UnpremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(), 256 destMap.mData, destMap.mStride, destSurf->GetFormat(), 257 srcSurf->GetSize()); 258 259 UnmapSrcDest(srcSurf, destSurf); 260 return destSurf.forget(); 261 } 262 263 void gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength) { 264 MOZ_ASSERT((aLength % 4) == 0, "Loop below will pass srcEnd!"); 265 SwizzleData(aData, aLength, SurfaceFormat::B8G8R8A8, aData, aLength, 266 SurfaceFormat::R8G8B8A8, IntSize(aLength / 4, 1)); 267 } 268 269 /* These heuristics are based on 270 * Source/WebCore/platform/graphics/skia/ImageSkia.cpp:computeResamplingMode() 271 */ 272 #ifdef MOZ_GFX_OPTIMIZE_MOBILE 273 static SamplingFilter ReduceResamplingFilter(SamplingFilter aSamplingFilter, 274 int aImgWidth, int aImgHeight, 275 float aSourceWidth, 276 float aSourceHeight) { 277 // Images smaller than this in either direction are considered "small" and 278 // are not resampled ever (see below). 279 const int kSmallImageSizeThreshold = 8; 280 281 // The amount an image can be stretched in a single direction before we 282 // say that it is being stretched so much that it must be a line or 283 // background that doesn't need resampling. 284 const float kLargeStretch = 3.0f; 285 286 if (aImgWidth <= kSmallImageSizeThreshold || 287 aImgHeight <= kSmallImageSizeThreshold) { 288 // Never resample small images. These are often used for borders and 289 // rules (think 1x1 images used to make lines). 290 return SamplingFilter::POINT; 291 } 292 293 if (aImgHeight * kLargeStretch <= aSourceHeight || 294 aImgWidth * kLargeStretch <= aSourceWidth) { 295 // Large image tiling detected. 296 297 // Don't resample if it is being tiled a lot in only one direction. 298 // This is trying to catch cases where somebody has created a border 299 // (which might be large) and then is stretching it to fill some part 300 // of the page. 301 if (fabs(aSourceWidth - aImgWidth) / aImgWidth < 0.5 || 302 fabs(aSourceHeight - aImgHeight) / aImgHeight < 0.5) 303 return SamplingFilter::POINT; 304 305 // The image is growing a lot and in more than one direction. Resampling 306 // is slow and doesn't give us very much when growing a lot. 307 return aSamplingFilter; 308 } 309 310 /* Some notes on other heuristics: 311 The Skia backend also uses nearest for backgrounds that are stretched by 312 a large amount. I'm not sure this is common enough for us to worry about 313 now. It also uses nearest for backgrounds/avoids high quality for images 314 that are very slightly scaled. I'm also not sure that very slightly 315 scaled backgrounds are common enough us to worry about. 316 317 We don't currently have much support for doing high quality interpolation. 318 The only place this currently happens is on Quartz and we don't have as 319 much control over it as would be needed. Webkit avoids using high quality 320 resampling during load. It also avoids high quality if the transformation 321 is not just a scale and translation 322 323 WebKit bug #40045 added code to avoid resampling different parts 324 of an image with different methods by using a resampling hint size. 325 It currently looks unused in WebKit but it's something to watch out for. 326 */ 327 328 return aSamplingFilter; 329 } 330 #else 331 static SamplingFilter ReduceResamplingFilter(SamplingFilter aSamplingFilter, 332 int aImgWidth, int aImgHeight, 333 int aSourceWidth, 334 int aSourceHeight) { 335 // Just pass the filter through unchanged 336 return aSamplingFilter; 337 } 338 #endif 339 340 #ifdef MOZ_WIDGET_COCOA 341 // Only prescale a temporary surface if we're going to repeat it often. 342 // Scaling is expensive on OS X and without prescaling, we'd scale 343 // every tile of the repeated rect. However, using a temp surface also 344 // potentially uses more memory if the scaled image is large. So only prescale 345 // on a temp surface if we know we're going to repeat the image in either the X 346 // or Y axis multiple times. 347 static bool ShouldUseTempSurface(Rect aImageRect, Rect aNeededRect) { 348 int repeatX = aNeededRect.width / aImageRect.width; 349 int repeatY = aNeededRect.height / aImageRect.height; 350 return (repeatX >= 5) || (repeatY >= 5); 351 } 352 353 static bool PrescaleAndTileDrawable(gfxDrawable* aDrawable, 354 gfxContext* aContext, 355 const ImageRegion& aRegion, Rect aImageRect, 356 const SamplingFilter aSamplingFilter, 357 const SurfaceFormat aFormat, 358 gfxFloat aOpacity, ExtendMode aExtendMode) { 359 MatrixScales scaleFactor = 360 aContext->CurrentMatrix().ScaleFactors().ConvertTo<float>(); 361 Matrix scaleMatrix = Matrix::Scaling(scaleFactor.xScale, scaleFactor.yScale); 362 const float fuzzFactor = 0.01; 363 364 // If we aren't scaling or translating, don't go down this path 365 if ((FuzzyEqual(scaleFactor.xScale, 1.0f, fuzzFactor) && 366 FuzzyEqual(scaleFactor.yScale, 1.0f, fuzzFactor)) || 367 aContext->CurrentMatrix().HasNonAxisAlignedTransform()) { 368 return false; 369 } 370 371 gfxRect clipExtents = aContext->GetClipExtents(); 372 373 // Inflate by one pixel because bilinear filtering will sample at most 374 // one pixel beyond the computed image pixel coordinate. 375 clipExtents.Inflate(1.0); 376 377 gfxRect needed = aRegion.IntersectAndRestrict(clipExtents); 378 Rect scaledNeededRect = scaleMatrix.TransformBounds(ToRect(needed)); 379 scaledNeededRect.RoundOut(); 380 if (scaledNeededRect.IsEmpty()) { 381 return false; 382 } 383 384 Rect scaledImageRect = scaleMatrix.TransformBounds(aImageRect); 385 if (!ShouldUseTempSurface(scaledImageRect, scaledNeededRect)) { 386 return false; 387 } 388 389 IntSize scaledImageSize((int32_t)scaledImageRect.width, 390 (int32_t)scaledImageRect.height); 391 if (scaledImageSize.width != scaledImageRect.width || 392 scaledImageSize.height != scaledImageRect.height) { 393 // If the scaled image isn't pixel aligned, we'll get artifacts 394 // so we have to take the slow path. 395 return false; 396 } 397 398 RefPtr<DrawTarget> scaledDT = 399 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 400 scaledImageSize, aFormat); 401 if (!scaledDT || !scaledDT->IsValid()) { 402 return false; 403 } 404 405 gfxContext tmpCtx(scaledDT); 406 407 scaledDT->SetTransform(scaleMatrix); 408 gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, 409 aImageRect.height); 410 411 // Since this is just the scaled image, we don't want to repeat anything yet. 412 aDrawable->Draw(&tmpCtx, gfxImageRect, ExtendMode::CLAMP, aSamplingFilter, 413 1.0, gfxMatrix()); 414 415 RefPtr<SourceSurface> scaledImage = scaledDT->Snapshot(); 416 417 { 418 gfxContextMatrixAutoSaveRestore autoSR(aContext); 419 Matrix withoutScale = aContext->CurrentMatrix(); 420 DrawTarget* destDrawTarget = aContext->GetDrawTarget(); 421 422 // The translation still is in scaled units 423 withoutScale.PreScale(1.0f / scaleFactor.xScale, 1.0f / scaleFactor.yScale); 424 aContext->SetMatrix(withoutScale); 425 426 DrawOptions drawOptions(aOpacity, aContext->CurrentOp(), 427 aContext->CurrentAntialiasMode()); 428 429 SurfacePattern scaledImagePattern(scaledImage, aExtendMode, Matrix(), 430 aSamplingFilter); 431 destDrawTarget->FillRect(scaledNeededRect, scaledImagePattern, drawOptions); 432 } 433 return true; 434 } 435 #endif // MOZ_WIDGET_COCOA 436 437 /* static */ 438 void gfxUtils::DrawPixelSnapped(gfxContext* aContext, gfxDrawable* aDrawable, 439 const gfxSize& aImageSize, 440 const ImageRegion& aRegion, 441 const SurfaceFormat aFormat, 442 SamplingFilter aSamplingFilter, 443 uint32_t aImageFlags, gfxFloat aOpacity) { 444 AUTO_PROFILER_LABEL("gfxUtils::DrawPixelSnapped", GRAPHICS); 445 446 gfxRect imageRect(gfxPoint(0, 0), aImageSize); 447 gfxRect region(aRegion.Rect()); 448 ExtendMode extendMode = aRegion.GetExtendMode(); 449 450 RefPtr<gfxDrawable> drawable = aDrawable; 451 452 aSamplingFilter = ReduceResamplingFilter(aSamplingFilter, imageRect.Width(), 453 imageRect.Height(), region.Width(), 454 region.Height()); 455 456 // OK now, the hard part left is to account for the subimage sampling 457 // restriction. If all the transforms involved are just integer 458 // translations, then we assume no resampling will occur so there's 459 // nothing to do. 460 // XXX if only we had source-clipping in cairo! 461 462 if (aContext->CurrentMatrix().HasNonIntegerTranslation()) { 463 if ((extendMode != ExtendMode::CLAMP) || 464 !aRegion.RestrictionContains(imageRect)) { 465 if (drawable->DrawWithSamplingRect( 466 aContext->GetDrawTarget(), aContext->CurrentOp(), 467 aContext->CurrentAntialiasMode(), aRegion.Rect(), 468 aRegion.Restriction(), extendMode, aSamplingFilter, aOpacity)) { 469 return; 470 } 471 472 #ifdef MOZ_WIDGET_COCOA 473 if (PrescaleAndTileDrawable(aDrawable, aContext, aRegion, 474 ToRect(imageRect), aSamplingFilter, aFormat, 475 aOpacity, extendMode)) { 476 return; 477 } 478 #endif 479 } 480 } 481 482 drawable->Draw(aContext, aRegion.Rect(), extendMode, aSamplingFilter, 483 aOpacity, gfxMatrix()); 484 } 485 486 /* static */ 487 int gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat) { 488 switch (aFormat) { 489 case SurfaceFormat::A8R8G8B8_UINT32: 490 return 32; 491 case SurfaceFormat::X8R8G8B8_UINT32: 492 return 24; 493 case SurfaceFormat::R5G6B5_UINT16: 494 return 16; 495 default: 496 break; 497 } 498 return 0; 499 } 500 501 /*static*/ 502 void gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion) { 503 aContext->NewPath(); 504 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { 505 const IntRect& r = iter.Get(); 506 aContext->Rectangle(gfxRect(r.X(), r.Y(), r.Width(), r.Height())); 507 } 508 aContext->Clip(); 509 } 510 511 /*static*/ 512 void gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion) { 513 uint32_t numRects = aRegion.GetNumRects(); 514 // If there is only one rect, then the region bounds are equivalent to the 515 // contents. So just use push a single clip rect with the bounds. 516 if (numRects == 1) { 517 aTarget->PushClipRect(Rect(aRegion.GetBounds())); 518 return; 519 } 520 521 // Check if the target's transform will preserve axis-alignment and 522 // pixel-alignment for each rect. For now, just handle the common case 523 // of integer translations. 524 Matrix transform = aTarget->GetTransform(); 525 if (transform.IsIntegerTranslation()) { 526 IntPoint translation = RoundedToInt(transform.GetTranslation()); 527 AutoTArray<IntRect, 16> rects; 528 rects.SetLength(numRects); 529 uint32_t i = 0; 530 // Build the list of transformed rects by adding in the translation. 531 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { 532 IntRect rect = iter.Get(); 533 rect.MoveBy(translation); 534 rects[i++] = rect; 535 } 536 aTarget->PushDeviceSpaceClipRects(rects.Elements(), rects.Length()); 537 } else { 538 // The transform does not produce axis-aligned rects or a rect was not 539 // pixel-aligned. So just build a path with all the rects and clip to it 540 // instead. 541 RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder(); 542 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { 543 AppendRectToPath(pathBuilder, Rect(iter.Get())); 544 } 545 RefPtr<Path> path = pathBuilder->Finish(); 546 aTarget->PushClip(path); 547 } 548 } 549 550 /*static*/ 551 float gfxUtils::ClampToScaleFactor(float aVal, bool aRoundDown) { 552 // Arbitary scale factor limitation. We can increase this 553 // for better scaling performance at the cost of worse 554 // quality. 555 static const float kScaleResolution = 2; 556 557 // Negative scaling is just a flip and irrelevant to 558 // our resolution calculation. 559 if (aVal < 0.0) { 560 aVal = -aVal; 561 } 562 563 bool inverse = false; 564 if (aVal < 1.0) { 565 inverse = true; 566 aVal = 1 / aVal; 567 } 568 569 float power = logf(aVal) / logf(kScaleResolution); 570 571 // If power is within 1e-5 of an integer, round to nearest to 572 // prevent floating point errors, otherwise round up to the 573 // next integer value. 574 if (fabs(power - NS_round(power)) < 1e-5) { 575 power = NS_round(power); 576 // Use floor when we are either inverted or rounding down, but 577 // not both. 578 } else if (inverse != aRoundDown) { 579 power = floor(power); 580 // Otherwise, ceil when we are not inverted and not rounding 581 // down, or we are inverted and rounding down. 582 } else { 583 power = ceil(power); 584 } 585 586 float scale = powf(kScaleResolution, power); 587 588 if (inverse) { 589 scale = 1 / scale; 590 } 591 592 return scale; 593 } 594 595 gfxMatrix gfxUtils::TransformRectToRect(const gfxRect& aFrom, 596 const gfxPoint& aToTopLeft, 597 const gfxPoint& aToTopRight, 598 const gfxPoint& aToBottomRight) { 599 gfxMatrix m; 600 if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) { 601 // Not a rotation, so xy and yx are zero 602 m._21 = m._12 = 0.0; 603 m._11 = (aToBottomRight.x - aToTopLeft.x) / aFrom.Width(); 604 m._22 = (aToBottomRight.y - aToTopLeft.y) / aFrom.Height(); 605 m._31 = aToTopLeft.x - m._11 * aFrom.X(); 606 m._32 = aToTopLeft.y - m._22 * aFrom.Y(); 607 } else { 608 NS_ASSERTION( 609 aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x, 610 "Destination rectangle not axis-aligned"); 611 m._11 = m._22 = 0.0; 612 m._21 = (aToBottomRight.x - aToTopLeft.x) / aFrom.Height(); 613 m._12 = (aToBottomRight.y - aToTopLeft.y) / aFrom.Width(); 614 m._31 = aToTopLeft.x - m._21 * aFrom.Y(); 615 m._32 = aToTopLeft.y - m._12 * aFrom.X(); 616 } 617 return m; 618 } 619 620 Matrix gfxUtils::TransformRectToRect(const gfxRect& aFrom, 621 const IntPoint& aToTopLeft, 622 const IntPoint& aToTopRight, 623 const IntPoint& aToBottomRight) { 624 Matrix m; 625 if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) { 626 // Not a rotation, so xy and yx are zero 627 m._12 = m._21 = 0.0; 628 m._11 = (aToBottomRight.x - aToTopLeft.x) / aFrom.Width(); 629 m._22 = (aToBottomRight.y - aToTopLeft.y) / aFrom.Height(); 630 m._31 = aToTopLeft.x - m._11 * aFrom.X(); 631 m._32 = aToTopLeft.y - m._22 * aFrom.Y(); 632 } else { 633 NS_ASSERTION( 634 aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x, 635 "Destination rectangle not axis-aligned"); 636 m._11 = m._22 = 0.0; 637 m._21 = (aToBottomRight.x - aToTopLeft.x) / aFrom.Height(); 638 m._12 = (aToBottomRight.y - aToTopLeft.y) / aFrom.Width(); 639 m._31 = aToTopLeft.x - m._21 * aFrom.Y(); 640 m._32 = aToTopLeft.y - m._12 * aFrom.X(); 641 } 642 return m; 643 } 644 645 /* This function is sort of shitty. We truncate doubles 646 * to ints then convert those ints back to doubles to make sure that 647 * they equal the doubles that we got in. */ 648 bool gfxUtils::GfxRectToIntRect(const gfxRect& aIn, IntRect* aOut) { 649 *aOut = IntRect(int32_t(aIn.X()), int32_t(aIn.Y()), int32_t(aIn.Width()), 650 int32_t(aIn.Height())); 651 return gfxRect(aOut->X(), aOut->Y(), aOut->Width(), aOut->Height()) 652 .IsEqualEdges(aIn); 653 } 654 655 /* Clamp r to CAIRO_COORD_MIN .. CAIRO_COORD_MAX 656 * these are to be device coordinates. 657 * 658 * Cairo is currently using 24.8 fixed point, 659 * so -2^24 .. 2^24-1 is our valid 660 */ 661 /*static*/ 662 void gfxUtils::ConditionRect(gfxRect& aRect) { 663 #define CAIRO_COORD_MAX (16777215.0) 664 #define CAIRO_COORD_MIN (-16777216.0) 665 // if either x or y is way out of bounds; 666 // note that we don't handle negative w/h here 667 if (aRect.X() > CAIRO_COORD_MAX) { 668 aRect.SetRectX(CAIRO_COORD_MAX, 0.0); 669 } 670 671 if (aRect.Y() > CAIRO_COORD_MAX) { 672 aRect.SetRectY(CAIRO_COORD_MAX, 0.0); 673 } 674 675 if (aRect.X() < CAIRO_COORD_MIN) { 676 aRect.SetWidth(aRect.XMost() - CAIRO_COORD_MIN); 677 if (aRect.Width() < 0.0) { 678 aRect.SetWidth(0.0); 679 } 680 aRect.MoveToX(CAIRO_COORD_MIN); 681 } 682 683 if (aRect.Y() < CAIRO_COORD_MIN) { 684 aRect.SetHeight(aRect.YMost() - CAIRO_COORD_MIN); 685 if (aRect.Height() < 0.0) { 686 aRect.SetHeight(0.0); 687 } 688 aRect.MoveToY(CAIRO_COORD_MIN); 689 } 690 691 if (aRect.XMost() > CAIRO_COORD_MAX) { 692 aRect.SetRightEdge(CAIRO_COORD_MAX); 693 } 694 695 if (aRect.YMost() > CAIRO_COORD_MAX) { 696 aRect.SetBottomEdge(CAIRO_COORD_MAX); 697 } 698 #undef CAIRO_COORD_MAX 699 #undef CAIRO_COORD_MIN 700 } 701 702 /*static*/ 703 gfxQuad gfxUtils::TransformToQuad(const gfxRect& aRect, 704 const mozilla::gfx::Matrix4x4& aMatrix) { 705 gfxPoint points[4]; 706 707 points[0] = aMatrix.TransformPoint(aRect.TopLeft()); 708 points[1] = aMatrix.TransformPoint(aRect.TopRight()); 709 points[2] = aMatrix.TransformPoint(aRect.BottomRight()); 710 points[3] = aMatrix.TransformPoint(aRect.BottomLeft()); 711 712 // Could this ever result in lines that intersect? I don't think so. 713 return gfxQuad(points[0], points[1], points[2], points[3]); 714 } 715 716 Matrix4x4 gfxUtils::SnapTransformTranslation(const Matrix4x4& aTransform, 717 Matrix* aResidualTransform) { 718 if (aResidualTransform) { 719 *aResidualTransform = Matrix(); 720 } 721 722 Matrix matrix2D; 723 if (aTransform.CanDraw2D(&matrix2D) && !matrix2D.HasNonTranslation() && 724 matrix2D.HasNonIntegerTranslation()) { 725 return Matrix4x4::From2D( 726 SnapTransformTranslation(matrix2D, aResidualTransform)); 727 } 728 729 return SnapTransformTranslation3D(aTransform, aResidualTransform); 730 } 731 732 Matrix gfxUtils::SnapTransformTranslation(const Matrix& aTransform, 733 Matrix* aResidualTransform) { 734 if (aResidualTransform) { 735 *aResidualTransform = Matrix(); 736 } 737 738 if (!aTransform.HasNonTranslation() && 739 aTransform.HasNonIntegerTranslation()) { 740 auto snappedTranslation = IntPoint::Round(aTransform.GetTranslation()); 741 Matrix snappedMatrix = 742 Matrix::Translation(snappedTranslation.x, snappedTranslation.y); 743 if (aResidualTransform) { 744 // set aResidualTransform so that aResidual * snappedMatrix == matrix2D. 745 // (I.e., appying snappedMatrix after aResidualTransform gives the 746 // ideal transform.) 747 *aResidualTransform = 748 Matrix::Translation(aTransform._31 - snappedTranslation.x, 749 aTransform._32 - snappedTranslation.y); 750 } 751 return snappedMatrix; 752 } 753 754 return aTransform; 755 } 756 757 Matrix4x4 gfxUtils::SnapTransformTranslation3D(const Matrix4x4& aTransform, 758 Matrix* aResidualTransform) { 759 if (aTransform.IsSingular() || aTransform.HasPerspectiveComponent() || 760 aTransform.HasNonTranslation() || 761 !aTransform.HasNonIntegerTranslation()) { 762 // For a singular transform, there is no reversed matrix, so we 763 // don't snap it. 764 // For a perspective transform, the content is transformed in 765 // non-linear, so we don't snap it too. 766 return aTransform; 767 } 768 769 // Snap for 3D Transforms 770 771 Point3D transformedOrigin = aTransform.TransformPoint(Point3D()); 772 773 // Compute the transformed snap by rounding the values of 774 // transformed origin. 775 auto transformedSnapXY = 776 IntPoint::Round(transformedOrigin.x, transformedOrigin.y); 777 Matrix4x4 inverse = aTransform; 778 inverse.Invert(); 779 // see Matrix4x4::ProjectPoint() 780 Float transformedSnapZ = 781 inverse._33 == 0 ? 0 782 : (-(transformedSnapXY.x * inverse._13 + 783 transformedSnapXY.y * inverse._23 + inverse._43) / 784 inverse._33); 785 Point3D transformedSnap = 786 Point3D(transformedSnapXY.x, transformedSnapXY.y, transformedSnapZ); 787 if (transformedOrigin == transformedSnap) { 788 return aTransform; 789 } 790 791 // Compute the snap from the transformed snap. 792 Point3D snap = inverse.TransformPoint(transformedSnap); 793 if (snap.z > 0.001 || snap.z < -0.001) { 794 // Allow some level of accumulated computation error. 795 MOZ_ASSERT(inverse._33 == 0.0); 796 return aTransform; 797 } 798 799 // The difference between the origin and snap is the residual transform. 800 if (aResidualTransform) { 801 // The residual transform is to translate the snap to the origin 802 // of the content buffer. 803 *aResidualTransform = Matrix::Translation(-snap.x, -snap.y); 804 } 805 806 // Translate transformed origin to transformed snap since the 807 // residual transform would trnslate the snap to the origin. 808 Point3D transformedShift = transformedSnap - transformedOrigin; 809 Matrix4x4 result = aTransform; 810 result.PostTranslate(transformedShift.x, transformedShift.y, 811 transformedShift.z); 812 813 // For non-2d transform, residual translation could be more than 814 // 0.5 pixels for every axis. 815 816 return result; 817 } 818 819 Matrix4x4 gfxUtils::SnapTransform(const Matrix4x4& aTransform, 820 const gfxRect& aSnapRect, 821 Matrix* aResidualTransform) { 822 if (aResidualTransform) { 823 *aResidualTransform = Matrix(); 824 } 825 826 Matrix matrix2D; 827 if (aTransform.Is2D(&matrix2D)) { 828 return Matrix4x4::From2D( 829 SnapTransform(matrix2D, aSnapRect, aResidualTransform)); 830 } 831 return aTransform; 832 } 833 834 Matrix gfxUtils::SnapTransform(const Matrix& aTransform, 835 const gfxRect& aSnapRect, 836 Matrix* aResidualTransform) { 837 if (aResidualTransform) { 838 *aResidualTransform = Matrix(); 839 } 840 841 if (gfxSize(1.0, 1.0) <= aSnapRect.Size() && 842 aTransform.PreservesAxisAlignedRectangles()) { 843 auto transformedTopLeft = IntPoint::Round( 844 aTransform.TransformPoint(ToPoint(aSnapRect.TopLeft()))); 845 auto transformedTopRight = IntPoint::Round( 846 aTransform.TransformPoint(ToPoint(aSnapRect.TopRight()))); 847 auto transformedBottomRight = IntPoint::Round( 848 aTransform.TransformPoint(ToPoint(aSnapRect.BottomRight()))); 849 850 Matrix snappedMatrix = gfxUtils::TransformRectToRect( 851 aSnapRect, transformedTopLeft, transformedTopRight, 852 transformedBottomRight); 853 854 if (aResidualTransform && !snappedMatrix.IsSingular()) { 855 // set aResidualTransform so that aResidual * snappedMatrix == matrix2D. 856 // (i.e., appying snappedMatrix after aResidualTransform gives the 857 // ideal transform. 858 Matrix snappedMatrixInverse = snappedMatrix; 859 snappedMatrixInverse.Invert(); 860 *aResidualTransform = aTransform * snappedMatrixInverse; 861 } 862 return snappedMatrix; 863 } 864 return aTransform; 865 } 866 867 /* static */ 868 void gfxUtils::ClearThebesSurface(gfxASurface* aSurface) { 869 if (aSurface->CairoStatus()) { 870 return; 871 } 872 cairo_surface_t* surf = aSurface->CairoSurface(); 873 if (cairo_surface_status(surf)) { 874 return; 875 } 876 cairo_t* ctx = cairo_create(surf); 877 cairo_set_source_rgba(ctx, 0.0, 0.0, 0.0, 0.0); 878 cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE); 879 IntRect bounds(nsIntPoint(0, 0), aSurface->GetSize()); 880 cairo_rectangle(ctx, bounds.X(), bounds.Y(), bounds.Width(), bounds.Height()); 881 cairo_fill(ctx); 882 cairo_destroy(ctx); 883 } 884 885 /* static */ 886 already_AddRefed<DataSourceSurface> 887 gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface, 888 SurfaceFormat aFormat) { 889 MOZ_ASSERT(aFormat != aSurface->GetFormat(), 890 "Unnecessary - and very expersive - surface format conversion"); 891 892 Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height); 893 894 if (!aSurface->IsDataSourceSurface()) { 895 // If the surface is NOT of type DATA then its data is not mapped into main 896 // memory. Format conversion is probably faster on the GPU, and by doing it 897 // there we can avoid any expensive uploads/readbacks except for (possibly) 898 // a single readback due to the unavoidable GetDataSurface() call. Using 899 // CreateOffscreenContentDrawTarget ensures the conversion happens on the 900 // GPU. 901 RefPtr<DrawTarget> dt = 902 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 903 aSurface->GetSize(), aFormat); 904 if (!dt) { 905 gfxWarning() << "gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat " 906 "failed in CreateOffscreenContentDrawTarget"; 907 return nullptr; 908 } 909 910 // Using DrawSurface() here rather than CopySurface() because CopySurface 911 // is optimized for memcpy and therefore isn't good for format conversion. 912 // Using OP_OVER since in our case it's equivalent to OP_SOURCE and 913 // generally more optimized. 914 dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(), 915 DrawOptions(1.0f, CompositionOp::OP_OVER)); 916 RefPtr<SourceSurface> surface = dt->Snapshot(); 917 return surface->GetDataSurface(); 918 } 919 920 // If the surface IS of type DATA then it may or may not be in main memory 921 // depending on whether or not it has been mapped yet. We have no way of 922 // knowing, so we can't be sure if it's best to create a data wrapping 923 // DrawTarget for the conversion or an offscreen content DrawTarget. We could 924 // guess it's not mapped and create an offscreen content DrawTarget, but if 925 // it is then we'll end up uploading the surface data, and most likely the 926 // caller is going to be accessing the resulting surface data, resulting in a 927 // readback (both very expensive operations). Alternatively we could guess 928 // the data is mapped and create a data wrapping DrawTarget and, if the 929 // surface is not in main memory, then we will incure a readback. The latter 930 // of these two "wrong choices" is the least costly (a readback, vs an 931 // upload and a readback), and more than likely the DATA surface that we've 932 // been passed actually IS in main memory anyway. For these reasons it's most 933 // likely best to create a data wrapping DrawTarget here to do the format 934 // conversion. 935 RefPtr<DataSourceSurface> dataSurface = 936 Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat); 937 DataSourceSurface::MappedSurface map; 938 if (!dataSurface || 939 !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { 940 return nullptr; 941 } 942 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData( 943 BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride, 944 aFormat); 945 if (!dt) { 946 dataSurface->Unmap(); 947 return nullptr; 948 } 949 // Using DrawSurface() here rather than CopySurface() because CopySurface 950 // is optimized for memcpy and therefore isn't good for format conversion. 951 // Using OP_OVER since in our case it's equivalent to OP_SOURCE and 952 // generally more optimized. 953 dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(), 954 DrawOptions(1.0f, CompositionOp::OP_OVER)); 955 dataSurface->Unmap(); 956 return dataSurface.forget(); 957 } 958 959 /* static */ 960 already_AddRefed<SourceSurface> gfxUtils::ScaleSourceSurface( 961 SourceSurface& aSurface, const IntSize& aTargetSize) { 962 const IntSize surfaceSize = aSurface.GetSize(); 963 964 MOZ_ASSERT(surfaceSize != aTargetSize); 965 MOZ_ASSERT(!surfaceSize.IsEmpty()); 966 MOZ_ASSERT(!aTargetSize.IsEmpty()); 967 968 RefPtr<DrawTarget> dt = Factory::CreateDrawTarget( 969 gfxVars::ContentBackend(), aTargetSize, aSurface.GetFormat()); 970 971 if (!dt || !dt->IsValid()) { 972 return nullptr; 973 } 974 975 dt->DrawSurface(&aSurface, Rect(Point(), Size(aTargetSize)), 976 Rect(Point(), Size(surfaceSize))); 977 return dt->GetBackingSurface(); 978 } 979 980 const uint32_t gfxUtils::sNumFrameColors = 8; 981 982 /* static */ 983 const gfx::DeviceColor& gfxUtils::GetColorForFrameNumber( 984 uint64_t aFrameNumber) { 985 static bool initialized = false; 986 static gfx::DeviceColor colors[sNumFrameColors]; 987 988 if (!initialized) { 989 // This isn't truly device color, but it is just for debug. 990 uint32_t i = 0; 991 colors[i++] = gfx::DeviceColor::FromABGR(0xffff0000); 992 colors[i++] = gfx::DeviceColor::FromABGR(0xffcc00ff); 993 colors[i++] = gfx::DeviceColor::FromABGR(0xff0000ee); 994 colors[i++] = gfx::DeviceColor::FromABGR(0xff00ff00); 995 colors[i++] = gfx::DeviceColor::FromABGR(0xff33ffff); 996 colors[i++] = gfx::DeviceColor::FromABGR(0xffff0099); 997 colors[i++] = gfx::DeviceColor::FromABGR(0xff0000ff); 998 colors[i++] = gfx::DeviceColor::FromABGR(0xff999999); 999 MOZ_ASSERT(i == sNumFrameColors); 1000 initialized = true; 1001 } 1002 1003 return colors[aFrameNumber % sNumFrameColors]; 1004 } 1005 1006 // static 1007 nsresult gfxUtils::EncodeSourceSurfaceAsStream(SourceSurface* aSurface, 1008 const ImageType aImageType, 1009 const nsAString& aOutputOptions, 1010 nsIInputStream** aOutStream) { 1011 const IntSize size = aSurface->GetSize(); 1012 if (size.IsEmpty()) { 1013 return NS_ERROR_FAILURE; 1014 } 1015 1016 RefPtr<DataSourceSurface> dataSurface; 1017 if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) { 1018 // FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5) 1019 dataSurface = gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat( 1020 aSurface, SurfaceFormat::B8G8R8A8); 1021 } else { 1022 dataSurface = aSurface->GetDataSurface(); 1023 } 1024 if (!dataSurface) { 1025 return NS_ERROR_FAILURE; 1026 } 1027 1028 DataSourceSurface::MappedSurface map; 1029 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { 1030 return NS_ERROR_FAILURE; 1031 } 1032 1033 RefPtr<imgIEncoder> encoder = nullptr; 1034 1035 switch (aImageType) { 1036 case ImageType::BMP: 1037 encoder = MakeRefPtr<nsBMPEncoder>(); 1038 break; 1039 1040 case ImageType::ICO: 1041 encoder = MakeRefPtr<nsICOEncoder>(); 1042 break; 1043 1044 case ImageType::JPEG: 1045 encoder = MakeRefPtr<nsJPEGEncoder>(); 1046 break; 1047 1048 case ImageType::PNG: 1049 encoder = MakeRefPtr<nsPNGEncoder>(); 1050 break; 1051 1052 default: 1053 break; 1054 } 1055 1056 MOZ_RELEASE_ASSERT(encoder != nullptr); 1057 1058 nsresult rv = encoder->InitFromData( 1059 map.mData, BufferSizeFromStrideAndHeight(map.mStride, size.height), 1060 size.width, size.height, map.mStride, imgIEncoder::INPUT_FORMAT_HOSTARGB, 1061 aOutputOptions, VoidCString()); 1062 dataSurface->Unmap(); 1063 if (NS_FAILED(rv)) { 1064 return NS_ERROR_FAILURE; 1065 } 1066 1067 nsCOMPtr<nsIInputStream> imgStream(encoder); 1068 if (!imgStream) { 1069 return NS_ERROR_FAILURE; 1070 } 1071 1072 imgStream.forget(aOutStream); 1073 1074 return NS_OK; 1075 } 1076 1077 // static 1078 Maybe<nsTArray<uint8_t>> gfxUtils::EncodeSourceSurfaceAsBytes( 1079 SourceSurface* aSurface, const ImageType aImageType, 1080 const nsAString& aOutputOptions) { 1081 nsCOMPtr<nsIInputStream> imgStream; 1082 nsresult rv = EncodeSourceSurfaceAsStream( 1083 aSurface, aImageType, aOutputOptions, getter_AddRefs(imgStream)); 1084 if (NS_FAILED(rv)) { 1085 return Nothing(); 1086 } 1087 1088 uint64_t bufSize64; 1089 rv = imgStream->Available(&bufSize64); 1090 if (NS_FAILED(rv) || bufSize64 > UINT32_MAX) { 1091 return Nothing(); 1092 } 1093 1094 uint32_t bytesLeft = static_cast<uint32_t>(bufSize64); 1095 1096 nsTArray<uint8_t> imgData; 1097 imgData.SetLength(bytesLeft); 1098 uint8_t* bytePtr = imgData.Elements(); 1099 1100 while (bytesLeft > 0) { 1101 uint32_t bytesRead = 0; 1102 rv = imgStream->Read(reinterpret_cast<char*>(bytePtr), bytesLeft, 1103 &bytesRead); 1104 if (NS_FAILED(rv) || bytesRead == 0) { 1105 return Nothing(); 1106 } 1107 1108 bytePtr += bytesRead; 1109 bytesLeft -= bytesRead; 1110 } 1111 1112 #ifdef DEBUG 1113 1114 // Currently, all implementers of imgIEncoder report their exact size through 1115 // nsIInputStream::Available(), but let's explicitly state that we rely on 1116 // that behavior for the algorithm above. 1117 1118 char dummy = 0; 1119 uint32_t bytesRead = 0; 1120 rv = imgStream->Read(&dummy, 1, &bytesRead); 1121 MOZ_ASSERT(NS_SUCCEEDED(rv) && bytesRead == 0); 1122 1123 #endif 1124 1125 return Some(std::move(imgData)); 1126 } 1127 1128 /* static */ 1129 nsresult gfxUtils::EncodeSourceSurface(SourceSurface* aSurface, 1130 const ImageType aImageType, 1131 const nsAString& aOutputOptions, 1132 BinaryOrData aBinaryOrData, FILE* aFile, 1133 nsACString* aStrOut) { 1134 MOZ_ASSERT(aBinaryOrData == gfxUtils::eDataURIEncode || aFile || aStrOut, 1135 "Copying binary encoding to clipboard not currently supported"); 1136 1137 auto maybeImgData = 1138 EncodeSourceSurfaceAsBytes(aSurface, aImageType, aOutputOptions); 1139 if (!maybeImgData) { 1140 return NS_ERROR_FAILURE; 1141 } 1142 1143 nsTArray<uint8_t>& imgData = *maybeImgData; 1144 1145 if (aBinaryOrData == gfxUtils::eBinaryEncode) { 1146 if (aFile) { 1147 (void)fwrite(imgData.Elements(), 1, imgData.Length(), aFile); 1148 } 1149 return NS_OK; 1150 } 1151 1152 nsCString stringBuf; 1153 nsACString& dataURI = aStrOut ? *aStrOut : stringBuf; 1154 dataURI.AppendLiteral("data:"); 1155 1156 switch (aImageType) { 1157 case ImageType::BMP: 1158 dataURI.AppendLiteral(IMAGE_BMP); 1159 break; 1160 1161 case ImageType::ICO: 1162 dataURI.AppendLiteral(IMAGE_ICO_MS); 1163 break; 1164 case ImageType::JPEG: 1165 dataURI.AppendLiteral(IMAGE_JPEG); 1166 break; 1167 1168 case ImageType::PNG: 1169 dataURI.AppendLiteral(IMAGE_PNG); 1170 break; 1171 1172 default: 1173 break; 1174 } 1175 1176 dataURI.AppendLiteral(";base64,"); 1177 nsresult rv = Base64EncodeAppend(reinterpret_cast<char*>(imgData.Elements()), 1178 imgData.Length(), dataURI); 1179 NS_ENSURE_SUCCESS(rv, rv); 1180 1181 if (aFile) { 1182 #ifdef ANDROID 1183 if (aFile == stdout || aFile == stderr) { 1184 // ADB logcat cuts off long strings so we will break it down 1185 const char* cStr = dataURI.BeginReading(); 1186 size_t len = strlen(cStr); 1187 while (true) { 1188 printf_stderr("IMG: %.140s\n", cStr); 1189 if (len <= 140) break; 1190 len -= 140; 1191 cStr += 140; 1192 } 1193 } 1194 #endif 1195 fprintf(aFile, "%s", dataURI.BeginReading()); 1196 } else if (!aStrOut) { 1197 nsCOMPtr<nsIClipboardHelper> clipboard( 1198 do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv)); 1199 if (clipboard) { 1200 clipboard->CopyString(NS_ConvertASCIItoUTF16(dataURI), nullptr); 1201 } 1202 } 1203 return NS_OK; 1204 } 1205 1206 static nsCString EncodeSourceSurfaceAsPNGURI(SourceSurface* aSurface) { 1207 nsCString string; 1208 gfxUtils::EncodeSourceSurface(aSurface, ImageType::PNG, u""_ns, 1209 gfxUtils::eDataURIEncode, nullptr, &string); 1210 return string; 1211 } 1212 1213 // https://jdashg.github.io/misc/colors/from-coeffs.html 1214 const float kBT601NarrowYCbCrToRGB_RowMajor[16] = { 1215 1.16438f, 0.00000f, 1.59603f, -0.87420f, 1.16438f, -0.39176f, 1216 -0.81297f, 0.53167f, 1.16438f, 2.01723f, 0.00000f, -1.08563f, 1217 0.00000f, 0.00000f, 0.00000f, 1.00000f}; 1218 const float kBT709NarrowYCbCrToRGB_RowMajor[16] = { 1219 1.16438f, 0.00000f, 1.79274f, -0.97295f, 1.16438f, -0.21325f, 1220 -0.53291f, 0.30148f, 1.16438f, 2.11240f, 0.00000f, -1.13340f, 1221 0.00000f, 0.00000f, 0.00000f, 1.00000f}; 1222 const float kBT2020NarrowYCbCrToRGB_RowMajor[16] = { 1223 1.16438f, 0.00000f, 1.67867f, -0.91569f, 1.16438f, -0.18733f, 1224 -0.65042f, 0.34746f, 1.16438f, 2.14177f, 0.00000f, -1.14815f, 1225 0.00000f, 0.00000f, 0.00000f, 1.00000f}; 1226 const float kIdentityNarrowYCbCrToRGB_RowMajor[16] = { 1227 0.00000f, 0.00000f, 1.00000f, 0.00000f, 1.00000f, 0.00000f, 1228 0.00000f, 0.00000f, 0.00000f, 1.00000f, 0.00000f, 0.00000f, 1229 0.00000f, 0.00000f, 0.00000f, 1.00000f}; 1230 1231 /* static */ const float* gfxUtils::YuvToRgbMatrix4x3RowMajor( 1232 gfx::YUVColorSpace aYUVColorSpace) { 1233 #define X(x) \ 1234 {x[0], x[1], x[2], 0.0f, x[4], x[5], x[6], 0.0f, x[8], x[9], x[10], 0.0f} 1235 1236 static const float rec601[12] = X(kBT601NarrowYCbCrToRGB_RowMajor); 1237 static const float rec709[12] = X(kBT709NarrowYCbCrToRGB_RowMajor); 1238 static const float rec2020[12] = X(kBT2020NarrowYCbCrToRGB_RowMajor); 1239 static const float identity[12] = X(kIdentityNarrowYCbCrToRGB_RowMajor); 1240 1241 #undef X 1242 1243 switch (aYUVColorSpace) { 1244 case gfx::YUVColorSpace::BT601: 1245 return rec601; 1246 case gfx::YUVColorSpace::BT709: 1247 return rec709; 1248 case gfx::YUVColorSpace::BT2020: 1249 return rec2020; 1250 case gfx::YUVColorSpace::Identity: 1251 return identity; 1252 default: 1253 MOZ_CRASH("Bad YUVColorSpace"); 1254 } 1255 } 1256 1257 /* static */ const float* gfxUtils::YuvToRgbMatrix3x3ColumnMajor( 1258 gfx::YUVColorSpace aYUVColorSpace) { 1259 #define X(x) {x[0], x[4], x[8], x[1], x[5], x[9], x[2], x[6], x[10]} 1260 1261 static const float rec601[9] = X(kBT601NarrowYCbCrToRGB_RowMajor); 1262 static const float rec709[9] = X(kBT709NarrowYCbCrToRGB_RowMajor); 1263 static const float rec2020[9] = X(kBT2020NarrowYCbCrToRGB_RowMajor); 1264 static const float identity[9] = X(kIdentityNarrowYCbCrToRGB_RowMajor); 1265 1266 #undef X 1267 1268 switch (aYUVColorSpace) { 1269 case gfx::YUVColorSpace::BT601: 1270 return rec601; 1271 case YUVColorSpace::BT709: 1272 return rec709; 1273 case YUVColorSpace::BT2020: 1274 return rec2020; 1275 case YUVColorSpace::Identity: 1276 return identity; 1277 default: 1278 MOZ_CRASH("Bad YUVColorSpace"); 1279 } 1280 } 1281 1282 /* static */ const float* gfxUtils::YuvToRgbMatrix4x4ColumnMajor( 1283 YUVColorSpace aYUVColorSpace) { 1284 #define X(x) \ 1285 {x[0], x[4], x[8], x[12], x[1], x[5], x[9], x[13], \ 1286 x[2], x[6], x[10], x[14], x[3], x[7], x[11], x[15]} 1287 1288 static const float rec601[16] = X(kBT601NarrowYCbCrToRGB_RowMajor); 1289 static const float rec709[16] = X(kBT709NarrowYCbCrToRGB_RowMajor); 1290 static const float rec2020[16] = X(kBT2020NarrowYCbCrToRGB_RowMajor); 1291 static const float identity[16] = X(kIdentityNarrowYCbCrToRGB_RowMajor); 1292 1293 #undef X 1294 1295 switch (aYUVColorSpace) { 1296 case YUVColorSpace::BT601: 1297 return rec601; 1298 case YUVColorSpace::BT709: 1299 return rec709; 1300 case YUVColorSpace::BT2020: 1301 return rec2020; 1302 case YUVColorSpace::Identity: 1303 return identity; 1304 default: 1305 MOZ_CRASH("Bad YUVColorSpace"); 1306 } 1307 } 1308 1309 // Translate from CICP values to the color spaces we support, or return 1310 // Nothing() if there is no appropriate match to let the caller choose 1311 // a default or generate an error. 1312 // 1313 // See Rec. ITU-T H.273 (12/2016) for details on CICP 1314 /* static */ Maybe<gfx::YUVColorSpace> gfxUtils::CicpToColorSpace( 1315 const CICP::MatrixCoefficients aMatrixCoefficients, 1316 const CICP::ColourPrimaries aColourPrimaries, LazyLogModule& aLogger) { 1317 switch (aMatrixCoefficients) { 1318 case CICP::MatrixCoefficients::MC_BT2020_NCL: 1319 case CICP::MatrixCoefficients::MC_BT2020_CL: 1320 return Some(gfx::YUVColorSpace::BT2020); 1321 case CICP::MatrixCoefficients::MC_BT601: 1322 return Some(gfx::YUVColorSpace::BT601); 1323 case CICP::MatrixCoefficients::MC_BT709: 1324 return Some(gfx::YUVColorSpace::BT709); 1325 case CICP::MatrixCoefficients::MC_IDENTITY: 1326 return Some(gfx::YUVColorSpace::Identity); 1327 case CICP::MatrixCoefficients::MC_CHROMAT_NCL: 1328 case CICP::MatrixCoefficients::MC_CHROMAT_CL: 1329 case CICP::MatrixCoefficients::MC_UNSPECIFIED: 1330 switch (aColourPrimaries) { 1331 case CICP::ColourPrimaries::CP_BT601: 1332 return Some(gfx::YUVColorSpace::BT601); 1333 case CICP::ColourPrimaries::CP_BT709: 1334 return Some(gfx::YUVColorSpace::BT709); 1335 case CICP::ColourPrimaries::CP_BT2020: 1336 return Some(gfx::YUVColorSpace::BT2020); 1337 default: 1338 MOZ_LOG(aLogger, LogLevel::Debug, 1339 ("Couldn't infer color matrix from primaries: %hhu", 1340 aColourPrimaries)); 1341 return {}; 1342 } 1343 default: 1344 MOZ_LOG(aLogger, LogLevel::Debug, 1345 ("Unsupported color matrix value: %hhu", aMatrixCoefficients)); 1346 return {}; 1347 } 1348 } 1349 1350 // Translate from CICP values to the color primaries we support, or return 1351 // Nothing() if there is no appropriate match to let the caller choose 1352 // a default or generate an error. 1353 // 1354 // See Rec. ITU-T H.273 (12/2016) for details on CICP 1355 /* static */ Maybe<gfx::ColorSpace2> gfxUtils::CicpToColorPrimaries( 1356 const CICP::ColourPrimaries aColourPrimaries, LazyLogModule& aLogger) { 1357 switch (aColourPrimaries) { 1358 case CICP::ColourPrimaries::CP_BT709: 1359 return Some(gfx::ColorSpace2::BT709); 1360 case CICP::ColourPrimaries::CP_BT2020: 1361 return Some(gfx::ColorSpace2::BT2020); 1362 default: 1363 MOZ_LOG(aLogger, LogLevel::Debug, 1364 ("Unsupported color primaries value: %hhu", aColourPrimaries)); 1365 return {}; 1366 } 1367 } 1368 1369 // Translate from CICP values to the transfer functions we support, or return 1370 // Nothing() if there is no appropriate match. 1371 // 1372 /* static */ Maybe<gfx::TransferFunction> gfxUtils::CicpToTransferFunction( 1373 const CICP::TransferCharacteristics aTransferCharacteristics) { 1374 switch (aTransferCharacteristics) { 1375 case CICP::TransferCharacteristics::TC_BT709: 1376 return Some(gfx::TransferFunction::BT709); 1377 1378 case CICP::TransferCharacteristics::TC_SRGB: 1379 return Some(gfx::TransferFunction::SRGB); 1380 1381 case CICP::TransferCharacteristics::TC_SMPTE2084: 1382 return Some(gfx::TransferFunction::PQ); 1383 1384 case CICP::TransferCharacteristics::TC_HLG: 1385 return Some(gfx::TransferFunction::HLG); 1386 1387 default: 1388 return {}; 1389 } 1390 } 1391 1392 /* static */ 1393 void gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile) { 1394 WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get()); 1395 } 1396 1397 /* static */ 1398 void gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile) { 1399 FILE* file = fopen(aFile, "wb"); 1400 1401 if (!file) { 1402 // Maybe the directory doesn't exist; try creating it, then fopen again. 1403 nsCOMPtr<nsIFile> comFile; 1404 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(aFile), 1405 getter_AddRefs(comFile)); 1406 if (NS_SUCCEEDED(rv)) { 1407 nsCOMPtr<nsIFile> dirPath; 1408 comFile->GetParent(getter_AddRefs(dirPath)); 1409 if (dirPath) { 1410 rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777); 1411 if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) { 1412 file = fopen(aFile, "wb"); 1413 } 1414 } 1415 } 1416 if (!file) { 1417 NS_WARNING("Failed to open file to create PNG!"); 1418 return; 1419 } 1420 } 1421 1422 EncodeSourceSurface(aSurface, ImageType::PNG, u""_ns, eBinaryEncode, file); 1423 fclose(file); 1424 } 1425 1426 /* static */ 1427 void gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile) { 1428 WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get()); 1429 } 1430 1431 /* static */ 1432 void gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile) { 1433 RefPtr<SourceSurface> surface = aDT->Snapshot(); 1434 if (surface) { 1435 WriteAsPNG(surface, aFile); 1436 } else { 1437 NS_WARNING("Failed to get surface!"); 1438 } 1439 } 1440 1441 /* static */ 1442 void gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile) { 1443 EncodeSourceSurface(aSurface, ImageType::PNG, u""_ns, eDataURIEncode, aFile); 1444 } 1445 1446 /* static */ 1447 nsCString gfxUtils::GetAsDataURI(SourceSurface* aSurface) { 1448 return EncodeSourceSurfaceAsPNGURI(aSurface); 1449 } 1450 1451 /* static */ 1452 void gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile) { 1453 RefPtr<SourceSurface> surface = aDT->Snapshot(); 1454 if (surface) { 1455 DumpAsDataURI(surface, aFile); 1456 } else { 1457 NS_WARNING("Failed to get surface!"); 1458 } 1459 } 1460 1461 /* static */ 1462 nsCString gfxUtils::GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface) { 1463 DataSourceSurface::ScopedMap map(aSourceSurface, DataSourceSurface::READ); 1464 int32_t dataSize = aSourceSurface->GetSize().height * map.GetStride(); 1465 auto compressedData = MakeUnique<char[]>(LZ4::maxCompressedSize(dataSize)); 1466 if (compressedData) { 1467 int nDataSize = 1468 LZ4::compress((char*)map.GetData(), dataSize, compressedData.get()); 1469 if (nDataSize > 0) { 1470 nsCString string; 1471 string.AppendPrintf("data:image/lz4bgra;base64,%i,%i,%i,", 1472 aSourceSurface->GetSize().width, map.GetStride(), 1473 aSourceSurface->GetSize().height); 1474 nsresult rv = Base64EncodeAppend(compressedData.get(), nDataSize, string); 1475 if (rv == NS_OK) { 1476 return string; 1477 } 1478 } 1479 } 1480 return {}; 1481 } 1482 1483 /* static */ 1484 nsCString gfxUtils::GetAsDataURI(DrawTarget* aDT) { 1485 RefPtr<SourceSurface> surface = aDT->Snapshot(); 1486 if (surface) { 1487 return EncodeSourceSurfaceAsPNGURI(surface); 1488 } else { 1489 NS_WARNING("Failed to get surface!"); 1490 return nsCString(""); 1491 } 1492 } 1493 1494 /* static */ 1495 void gfxUtils::CopyAsDataURI(SourceSurface* aSurface) { 1496 EncodeSourceSurface(aSurface, ImageType::PNG, u""_ns, eDataURIEncode, 1497 nullptr); 1498 } 1499 1500 /* static */ 1501 void gfxUtils::CopyAsDataURI(DrawTarget* aDT) { 1502 RefPtr<SourceSurface> surface = aDT->Snapshot(); 1503 if (surface) { 1504 CopyAsDataURI(surface); 1505 } else { 1506 NS_WARNING("Failed to get surface!"); 1507 } 1508 } 1509 1510 /* static */ 1511 UniquePtr<uint8_t[]> gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface, 1512 bool aIsAlphaPremultiplied, 1513 int32_t* outFormat) { 1514 *outFormat = 0; 1515 1516 DataSourceSurface::MappedSurface map; 1517 if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) return nullptr; 1518 1519 uint32_t bufferSize = 1520 aSurface->GetSize().width * aSurface->GetSize().height * 4; 1521 auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize); 1522 if (!imageBuffer) { 1523 aSurface->Unmap(); 1524 return nullptr; 1525 } 1526 memcpy(imageBuffer.get(), map.mData, bufferSize); 1527 1528 aSurface->Unmap(); 1529 1530 int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB; 1531 if (!aIsAlphaPremultiplied) { 1532 // We need to convert to INPUT_FORMAT_RGBA, otherwise 1533 // we are automatically considered premult, and unpremult'd. 1534 // Yes, it is THAT silly. 1535 // Except for different lossy conversions by color, 1536 // we could probably just change the label, and not change the data. 1537 gfxUtils::ConvertBGRAtoRGBA(imageBuffer.get(), bufferSize); 1538 format = imgIEncoder::INPUT_FORMAT_RGBA; 1539 } 1540 1541 *outFormat = format; 1542 return imageBuffer; 1543 } 1544 1545 /* static */ 1546 UniquePtr<uint8_t[]> gfxUtils::GetImageBufferWithRandomNoise( 1547 gfx::DataSourceSurface* aSurface, bool aIsAlphaPremultiplied, 1548 nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal, 1549 int32_t* outFormat) { 1550 UniquePtr<uint8_t[]> imageBuffer = 1551 GetImageBuffer(aSurface, aIsAlphaPremultiplied, outFormat); 1552 1553 nsRFPService::RandomizePixels( 1554 aCookieJarSettings, aPrincipal, imageBuffer.get(), 1555 aSurface->GetSize().width, aSurface->GetSize().height, 1556 aSurface->GetSize().width * aSurface->GetSize().height * 4, 1557 SurfaceFormat::A8R8G8B8_UINT32); 1558 1559 return imageBuffer; 1560 } 1561 1562 /* static */ 1563 nsresult gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface, 1564 bool aIsAlphaPremultiplied, 1565 const char* aMimeType, 1566 const nsAString& aEncoderOptions, 1567 nsIInputStream** outStream) { 1568 nsCString enccid("@mozilla.org/image/encoder;2?type="); 1569 enccid += aMimeType; 1570 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get()); 1571 if (!encoder) return NS_ERROR_FAILURE; 1572 1573 int32_t format = 0; 1574 UniquePtr<uint8_t[]> imageBuffer = 1575 GetImageBuffer(aSurface, aIsAlphaPremultiplied, &format); 1576 if (!imageBuffer) return NS_ERROR_FAILURE; 1577 1578 return dom::ImageEncoder::GetInputStream( 1579 aSurface->GetSize().width, aSurface->GetSize().height, imageBuffer.get(), 1580 format, encoder, aEncoderOptions, VoidCString(), outStream); 1581 } 1582 1583 /* static */ 1584 nsresult gfxUtils::GetInputStreamWithRandomNoise( 1585 gfx::DataSourceSurface* aSurface, bool aIsAlphaPremultiplied, 1586 const char* aMimeType, const nsAString& aEncoderOptions, 1587 nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal, 1588 nsIInputStream** outStream) { 1589 nsCString enccid("@mozilla.org/image/encoder;2?type="); 1590 enccid += aMimeType; 1591 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get()); 1592 if (!encoder) { 1593 return NS_ERROR_FAILURE; 1594 } 1595 1596 int32_t format = 0; 1597 UniquePtr<uint8_t[]> imageBuffer = 1598 GetImageBuffer(aSurface, aIsAlphaPremultiplied, &format); 1599 if (!imageBuffer) { 1600 return NS_ERROR_FAILURE; 1601 } 1602 1603 nsRFPService::RandomizePixels( 1604 aCookieJarSettings, aPrincipal, imageBuffer.get(), 1605 aSurface->GetSize().width, aSurface->GetSize().height, 1606 aSurface->GetSize().width * aSurface->GetSize().height * 4, 1607 SurfaceFormat::A8R8G8B8_UINT32); 1608 1609 return dom::ImageEncoder::GetInputStream( 1610 aSurface->GetSize().width, aSurface->GetSize().height, imageBuffer.get(), 1611 format, encoder, aEncoderOptions, VoidCString(), outStream); 1612 } 1613 1614 class GetFeatureStatusWorkerRunnable final 1615 : public dom::WorkerMainThreadRunnable { 1616 public: 1617 GetFeatureStatusWorkerRunnable(dom::WorkerPrivate* workerPrivate, 1618 const nsCOMPtr<nsIGfxInfo>& gfxInfo, 1619 int32_t feature, nsACString& failureId, 1620 int32_t* status) 1621 : WorkerMainThreadRunnable(workerPrivate, "GFX :: GetFeatureStatus"_ns), 1622 mGfxInfo(gfxInfo), 1623 mFeature(feature), 1624 mStatus(status), 1625 mFailureId(failureId), 1626 mNSResult(NS_OK) {} 1627 1628 bool MainThreadRun() override { 1629 if (mGfxInfo) { 1630 mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mFailureId, mStatus); 1631 } 1632 return true; 1633 } 1634 1635 nsresult GetNSResult() const { return mNSResult; } 1636 1637 protected: 1638 ~GetFeatureStatusWorkerRunnable() = default; 1639 1640 private: 1641 nsCOMPtr<nsIGfxInfo> mGfxInfo; 1642 int32_t mFeature; 1643 int32_t* mStatus; 1644 nsACString& mFailureId; 1645 nsresult mNSResult; 1646 }; 1647 1648 #define GFX_SHADER_CHECK_BUILD_VERSION_PREF "gfx-shader-check.build-version" 1649 #define GFX_SHADER_CHECK_PTR_SIZE_PREF "gfx-shader-check.ptr-size" 1650 #define GFX_SHADER_CHECK_DEVICE_ID_PREF "gfx-shader-check.device-id" 1651 #define GFX_SHADER_CHECK_DRIVER_VERSION_PREF "gfx-shader-check.driver-version" 1652 1653 /* static */ 1654 void gfxUtils::RemoveShaderCacheFromDiskIfNecessary() { 1655 if (!gfxVars::UseWebRenderProgramBinaryDisk()) { 1656 return; 1657 } 1658 1659 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); 1660 1661 // Get current values 1662 nsCString buildID(mozilla::PlatformBuildID()); 1663 int ptrSize = sizeof(void*); 1664 nsString deviceID, driverVersion; 1665 gfxInfo->GetAdapterDeviceID(deviceID); 1666 gfxInfo->GetAdapterDriverVersion(driverVersion); 1667 1668 // Get pref stored values 1669 nsAutoCString buildIDChecked; 1670 Preferences::GetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildIDChecked); 1671 int ptrSizeChecked = Preferences::GetInt(GFX_SHADER_CHECK_PTR_SIZE_PREF, 0); 1672 nsAutoString deviceIDChecked, driverVersionChecked; 1673 Preferences::GetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceIDChecked); 1674 Preferences::GetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, 1675 driverVersionChecked); 1676 1677 if (buildID == buildIDChecked && ptrSize == ptrSizeChecked && 1678 deviceID == deviceIDChecked && driverVersion == driverVersionChecked) { 1679 return; 1680 } 1681 1682 nsAutoString path(gfx::gfxVars::ProfDirectory()); 1683 1684 if (!wr::remove_program_binary_disk_cache(&path)) { 1685 // Failed to remove program binary disk cache. The disk cache might have 1686 // invalid data. Disable program binary disk cache usage. 1687 gfxVars::SetUseWebRenderProgramBinaryDisk(false); 1688 return; 1689 } 1690 1691 Preferences::SetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildID); 1692 Preferences::SetInt(GFX_SHADER_CHECK_PTR_SIZE_PREF, ptrSize); 1693 Preferences::SetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceID); 1694 Preferences::SetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, driverVersion); 1695 } 1696 1697 /* static */ 1698 bool gfxUtils::DumpDisplayList() { 1699 return StaticPrefs::layout_display_list_dump() || 1700 (StaticPrefs::layout_display_list_dump_parent() && 1701 XRE_IsParentProcess()) || 1702 (StaticPrefs::layout_display_list_dump_content() && 1703 XRE_IsContentProcess()); 1704 } 1705 1706 MOZ_GLOBINIT FILE* gfxUtils::sDumpPaintFile = stderr; 1707 1708 namespace mozilla { 1709 namespace gfx { 1710 1711 DeviceColor ToDeviceColor(const sRGBColor& aColor) { 1712 // aColor is pass-by-value since to get return value optimization goodness we 1713 // need to return the same object from all return points in this function. We 1714 // could declare a local Color variable and use that, but we might as well 1715 // just use aColor. 1716 if (gfxPlatform::GetCMSMode() == CMSMode::All) { 1717 qcms_transform* transform = gfxPlatform::GetCMSRGBTransform(); 1718 if (transform) { 1719 return gfxPlatform::TransformPixel(aColor, transform); 1720 // Use the original alpha to avoid unnecessary float->byte->float 1721 // conversion errors 1722 } 1723 } 1724 return DeviceColor(aColor.r, aColor.g, aColor.b, aColor.a); 1725 } 1726 1727 DeviceColor ToDeviceColor(nscolor aColor) { 1728 return ToDeviceColor(sRGBColor::FromABGR(aColor)); 1729 } 1730 1731 DeviceColor ToDeviceColor(const StyleAbsoluteColor& aColor) { 1732 return ToDeviceColor(aColor.ToColor()); 1733 } 1734 1735 sRGBColor ToSRGBColor(const StyleAbsoluteColor& aColor) { 1736 auto srgb = aColor.ToColorSpace(StyleColorSpace::Srgb); 1737 1738 const auto ToComponent = [](float aF) -> float { 1739 float component = std::clamp(aF, 0.0f, 1.0f); 1740 if (MOZ_UNLIKELY(!std::isfinite(component))) { 1741 return 0.0f; 1742 } 1743 return component; 1744 }; 1745 return {ToComponent(srgb.components._0), ToComponent(srgb.components._1), 1746 ToComponent(srgb.components._2), ToComponent(srgb.alpha)}; 1747 } 1748 1749 } // namespace gfx 1750 } // namespace mozilla