1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/. */
9 #include "gfxContext.h"
10 #include "gfxImageSurface.h"
11 #include "gfxPlatform.h"
12 #include "gfxDrawable.h"
13 #include "imgIEncoder.h"
14 #include "mozilla/Base64.h"
15 #include "mozilla/gfx/2D.h"
16 #include "mozilla/gfx/DataSurfaceHelpers.h"
17 #include "mozilla/Maybe.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/Vector.h"
20 #include "nsComponentManagerUtils.h"
21 #include "nsIClipboardHelper.h"
23 #include "nsIPresShell.h"
24 #include "nsPresContext.h"
26 #include "nsServiceManagerUtils.h"
27 #include "yuv_convert.h"
28 #include "ycbcr_to_rgb565.h"
29 #include "GeckoProfiler.h"
30 #include "ImageContainer.h"
31 #include "ImageRegion.h"
32 #include "gfx2DGlue.h"
36 #include "gfxWindowsPlatform.h"
39 using namespace mozilla
;
40 using namespace mozilla::image
;
41 using namespace mozilla::layers
;
42 using namespace mozilla::gfx
;
44 #include "DeprecatedPremultiplyTables.h"
47 #include "mozilla/Compression.h"
49 using namespace mozilla::Compression
;
53 * Dump a raw image to the default log. This function is exported
54 * from libxul, so it can be called from any library in addition to
55 * (of course) from a debugger.
57 * Note: this helper currently assumes that all 2-bytepp images are
58 * r5g6b5, and that all 4-bytepp images are r8g8b8a8.
61 void mozilla_dump_image(void* bytes
, int width
, int height
, int bytepp
,
64 if (0 == strideBytes
) {
65 strideBytes
= width
* bytepp
;
68 // TODO more flexible; parse string?
71 format
= SurfaceFormat::R5G6B5
;
75 format
= SurfaceFormat::R8G8B8A8
;
79 RefPtr
<DataSourceSurface
> surf
=
80 Factory::CreateWrappingDataSourceSurface((uint8_t*)bytes
, strideBytes
,
81 gfx::IntSize(width
, height
),
83 gfxUtils::DumpAsDataURI(surf
);
88 static const uint8_t PremultiplyValue(uint8_t a
, uint8_t v
) {
89 return gfxUtils::sPremultiplyTable
[a
*256+v
];
92 static const uint8_t UnpremultiplyValue(uint8_t a
, uint8_t v
) {
93 return gfxUtils::sUnpremultiplyTable
[a
*256+v
];
97 PremultiplyData(const uint8_t* srcData
,
98 size_t srcStride
, // row-to-row stride in bytes
100 size_t destStride
, // row-to-row stride in bytes
104 MOZ_ASSERT(srcData
&& destData
);
106 for (size_t y
= 0; y
< rowCount
; ++y
) {
107 const uint8_t* src
= srcData
+ y
* srcStride
;
108 uint8_t* dest
= destData
+ y
* destStride
;
110 for (size_t x
= 0; x
< pixelWidth
; ++x
) {
111 #ifdef IS_LITTLE_ENDIAN
117 *dest
++ = PremultiplyValue(a
, b
);
118 *dest
++ = PremultiplyValue(a
, g
);
119 *dest
++ = PremultiplyValue(a
, r
);
128 *dest
++ = PremultiplyValue(a
, r
);
129 *dest
++ = PremultiplyValue(a
, g
);
130 *dest
++ = PremultiplyValue(a
, b
);
136 UnpremultiplyData(const uint8_t* srcData
,
137 size_t srcStride
, // row-to-row stride in bytes
139 size_t destStride
, // row-to-row stride in bytes
143 MOZ_ASSERT(srcData
&& destData
);
145 for (size_t y
= 0; y
< rowCount
; ++y
) {
146 const uint8_t* src
= srcData
+ y
* srcStride
;
147 uint8_t* dest
= destData
+ y
* destStride
;
149 for (size_t x
= 0; x
< pixelWidth
; ++x
) {
150 #ifdef IS_LITTLE_ENDIAN
156 *dest
++ = UnpremultiplyValue(a
, b
);
157 *dest
++ = UnpremultiplyValue(a
, g
);
158 *dest
++ = UnpremultiplyValue(a
, r
);
167 *dest
++ = UnpremultiplyValue(a
, r
);
168 *dest
++ = UnpremultiplyValue(a
, g
);
169 *dest
++ = UnpremultiplyValue(a
, b
);
176 MapSrcDest(DataSourceSurface
* srcSurf
,
177 DataSourceSurface
* destSurf
,
178 DataSourceSurface::MappedSurface
* out_srcMap
,
179 DataSourceSurface::MappedSurface
* out_destMap
)
181 MOZ_ASSERT(srcSurf
&& destSurf
);
182 MOZ_ASSERT(out_srcMap
&& out_destMap
);
184 if (srcSurf
->GetFormat() != SurfaceFormat::B8G8R8A8
||
185 destSurf
->GetFormat() != SurfaceFormat::B8G8R8A8
)
187 MOZ_ASSERT(false, "Only operate on BGRA8 surfs.");
191 if (srcSurf
->GetSize().width
!= destSurf
->GetSize().width
||
192 srcSurf
->GetSize().height
!= destSurf
->GetSize().height
)
194 MOZ_ASSERT(false, "Width and height must match.");
198 if (srcSurf
== destSurf
) {
199 DataSourceSurface::MappedSurface map
;
200 if (!srcSurf
->Map(DataSourceSurface::MapType::READ_WRITE
, &map
)) {
201 NS_WARNING("Couldn't Map srcSurf/destSurf.");
210 // Map src for reading.
211 DataSourceSurface::MappedSurface srcMap
;
212 if (!srcSurf
->Map(DataSourceSurface::MapType::READ
, &srcMap
)) {
213 NS_WARNING("Couldn't Map srcSurf.");
217 // Map dest for writing.
218 DataSourceSurface::MappedSurface destMap
;
219 if (!destSurf
->Map(DataSourceSurface::MapType::WRITE
, &destMap
)) {
220 NS_WARNING("Couldn't Map aDest.");
225 *out_srcMap
= srcMap
;
226 *out_destMap
= destMap
;
231 UnmapSrcDest(DataSourceSurface
* srcSurf
,
232 DataSourceSurface
* destSurf
)
234 if (srcSurf
== destSurf
) {
243 gfxUtils::PremultiplyDataSurface(DataSourceSurface
* srcSurf
,
244 DataSourceSurface
* destSurf
)
246 MOZ_ASSERT(srcSurf
&& destSurf
);
248 DataSourceSurface::MappedSurface srcMap
;
249 DataSourceSurface::MappedSurface destMap
;
250 if (!MapSrcDest(srcSurf
, destSurf
, &srcMap
, &destMap
))
253 PremultiplyData(srcMap
.mData
, srcMap
.mStride
,
254 destMap
.mData
, destMap
.mStride
,
255 srcSurf
->GetSize().width
,
256 srcSurf
->GetSize().height
);
258 UnmapSrcDest(srcSurf
, destSurf
);
263 gfxUtils::UnpremultiplyDataSurface(DataSourceSurface
* srcSurf
,
264 DataSourceSurface
* destSurf
)
266 MOZ_ASSERT(srcSurf
&& destSurf
);
268 DataSourceSurface::MappedSurface srcMap
;
269 DataSourceSurface::MappedSurface destMap
;
270 if (!MapSrcDest(srcSurf
, destSurf
, &srcMap
, &destMap
))
273 UnpremultiplyData(srcMap
.mData
, srcMap
.mStride
,
274 destMap
.mData
, destMap
.mStride
,
275 srcSurf
->GetSize().width
,
276 srcSurf
->GetSize().height
);
278 UnmapSrcDest(srcSurf
, destSurf
);
283 MapSrcAndCreateMappedDest(DataSourceSurface
* srcSurf
,
284 RefPtr
<DataSourceSurface
>* out_destSurf
,
285 DataSourceSurface::MappedSurface
* out_srcMap
,
286 DataSourceSurface::MappedSurface
* out_destMap
)
289 MOZ_ASSERT(out_destSurf
&& out_srcMap
&& out_destMap
);
291 if (srcSurf
->GetFormat() != SurfaceFormat::B8G8R8A8
) {
292 MOZ_ASSERT(false, "Only operate on BGRA8.");
296 // Ok, map source for reading.
297 DataSourceSurface::MappedSurface srcMap
;
298 if (!srcSurf
->Map(DataSourceSurface::MapType::READ
, &srcMap
)) {
299 MOZ_ASSERT(false, "Couldn't Map srcSurf.");
303 // Make our dest surface based on the src.
304 RefPtr
<DataSourceSurface
> destSurf
=
305 Factory::CreateDataSourceSurfaceWithStride(srcSurf
->GetSize(),
306 srcSurf
->GetFormat(),
308 if (NS_WARN_IF(!destSurf
)) {
312 DataSourceSurface::MappedSurface destMap
;
313 if (!destSurf
->Map(DataSourceSurface::MapType::WRITE
, &destMap
)) {
314 MOZ_ASSERT(false, "Couldn't Map destSurf.");
319 *out_destSurf
= destSurf
;
320 *out_srcMap
= srcMap
;
321 *out_destMap
= destMap
;
325 TemporaryRef
<DataSourceSurface
>
326 gfxUtils::CreatePremultipliedDataSurface(DataSourceSurface
* srcSurf
)
328 RefPtr
<DataSourceSurface
> destSurf
;
329 DataSourceSurface::MappedSurface srcMap
;
330 DataSourceSurface::MappedSurface destMap
;
331 if (!MapSrcAndCreateMappedDest(srcSurf
, &destSurf
, &srcMap
, &destMap
)) {
332 MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
336 PremultiplyData(srcMap
.mData
, srcMap
.mStride
,
337 destMap
.mData
, destMap
.mStride
,
338 srcSurf
->GetSize().width
,
339 srcSurf
->GetSize().height
);
341 UnmapSrcDest(srcSurf
, destSurf
);
345 TemporaryRef
<DataSourceSurface
>
346 gfxUtils::CreateUnpremultipliedDataSurface(DataSourceSurface
* srcSurf
)
348 RefPtr
<DataSourceSurface
> destSurf
;
349 DataSourceSurface::MappedSurface srcMap
;
350 DataSourceSurface::MappedSurface destMap
;
351 if (!MapSrcAndCreateMappedDest(srcSurf
, &destSurf
, &srcMap
, &destMap
)) {
352 MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
356 UnpremultiplyData(srcMap
.mData
, srcMap
.mStride
,
357 destMap
.mData
, destMap
.mStride
,
358 srcSurf
->GetSize().width
,
359 srcSurf
->GetSize().height
);
361 UnmapSrcDest(srcSurf
, destSurf
);
366 gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData
, uint32_t aLength
)
368 MOZ_ASSERT((aLength
% 4) == 0, "Loop below will pass srcEnd!");
370 uint8_t *src
= aData
;
371 uint8_t *srcEnd
= src
+ aLength
;
374 for (; src
!= srcEnd
; src
+= 4) {
386 IsSafeImageTransformComponent(gfxFloat aValue
)
388 return aValue
>= -32768 && aValue
<= 32767;
391 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
393 * This returns the fastest operator to use for solid surfaces which have no
394 * alpha channel or their alpha channel is uniformly opaque.
395 * This differs per render mode.
397 static gfxContext::GraphicsOperator
398 OptimalFillOperator()
401 if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
402 gfxWindowsPlatform::RENDER_DIRECT2D
) {
403 // D2D -really- hates operator source.
404 return gfxContext::OPERATOR_OVER
;
407 return gfxContext::OPERATOR_SOURCE
;
413 // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
414 // the subimage of pixels we're allowed to sample.
415 static already_AddRefed
<gfxDrawable
>
416 CreateSamplingRestrictedDrawable(gfxDrawable
* aDrawable
,
417 gfxContext
* aContext
,
418 const ImageRegion
& aRegion
,
419 const SurfaceFormat aFormat
)
421 PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
422 js::ProfileEntry::Category::GRAPHICS
);
424 gfxRect clipExtents
= aContext
->GetClipExtents();
426 // Inflate by one pixel because bilinear filtering will sample at most
427 // one pixel beyond the computed image pixel coordinate.
428 clipExtents
.Inflate(1.0);
430 gfxRect needed
= aRegion
.IntersectAndRestrict(clipExtents
);
433 // if 'needed' is empty, nothing will be drawn since aFill
434 // must be entirely outside the clip region, so it doesn't
435 // matter what we do here, but we should avoid trying to
436 // create a zero-size surface.
437 if (needed
.IsEmpty())
440 gfxIntSize
size(int32_t(needed
.Width()), int32_t(needed
.Height()));
442 RefPtr
<DrawTarget
> target
=
443 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size
),
449 nsRefPtr
<gfxContext
> tmpCtx
= new gfxContext(target
);
450 tmpCtx
->SetOperator(OptimalFillOperator());
451 aDrawable
->Draw(tmpCtx
, needed
- needed
.TopLeft(), true,
452 GraphicsFilter::FILTER_FAST
, 1.0, gfxMatrix::Translation(needed
.TopLeft()));
453 RefPtr
<SourceSurface
> surface
= target
->Snapshot();
455 nsRefPtr
<gfxDrawable
> drawable
= new gfxSurfaceDrawable(surface
, size
, gfxMatrix::Translation(-needed
.TopLeft()));
456 return drawable
.forget();
458 #endif // !MOZ_GFX_OPTIMIZE_MOBILE
460 // working around cairo/pixman bug (bug 364968)
461 // Our device-space-to-image-space transform may not be acceptable to pixman.
462 struct MOZ_STACK_CLASS AutoCairoPixmanBugWorkaround
464 AutoCairoPixmanBugWorkaround(gfxContext
* aContext
,
465 const gfxMatrix
& aDeviceSpaceToImageSpace
,
466 const gfxRect
& aFill
,
467 const gfxASurface
* aSurface
)
468 : mContext(aContext
), mSucceeded(true), mPushedGroup(false)
470 // Quartz's limits for matrix are much larger than pixman
471 if (!aSurface
|| aSurface
->GetType() == gfxSurfaceType::Quartz
)
474 if (!IsSafeImageTransformComponent(aDeviceSpaceToImageSpace
._11
) ||
475 !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace
._21
) ||
476 !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace
._12
) ||
477 !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace
._22
)) {
478 NS_WARNING("Scaling up too much, bailing out");
483 if (IsSafeImageTransformComponent(aDeviceSpaceToImageSpace
._31
) &&
484 IsSafeImageTransformComponent(aDeviceSpaceToImageSpace
._32
))
487 // We'll push a group, which will hopefully reduce our transform's
488 // translation so it's in bounds.
489 gfxMatrix currentMatrix
= mContext
->CurrentMatrix();
492 // Clip the rounded-out-to-device-pixels bounds of the
493 // transformed fill area. This is the area for the group we
495 mContext
->SetMatrix(gfxMatrix());
496 gfxRect bounds
= currentMatrix
.TransformBounds(aFill
);
498 mContext
->Clip(bounds
);
499 mContext
->SetMatrix(currentMatrix
);
500 mContext
->PushGroup(gfxContentType::COLOR_ALPHA
);
501 mContext
->SetOperator(gfxContext::OPERATOR_OVER
);
506 ~AutoCairoPixmanBugWorkaround()
509 mContext
->PopGroupToSource();
515 bool PushedGroup() { return mPushedGroup
; }
516 bool Succeeded() { return mSucceeded
; }
519 gfxContext
* mContext
;
525 DeviceToImageTransform(gfxContext
* aContext
)
527 gfxFloat deviceX
, deviceY
;
528 nsRefPtr
<gfxASurface
> currentTarget
=
529 aContext
->CurrentSurface(&deviceX
, &deviceY
);
530 gfxMatrix deviceToUser
= aContext
->CurrentMatrix();
531 if (!deviceToUser
.Invert()) {
532 return gfxMatrix(0, 0, 0, 0, 0, 0); // singular
534 deviceToUser
.Translate(-gfxPoint(-deviceX
, -deviceY
));
538 /* These heuristics are based on Source/WebCore/platform/graphics/skia/ImageSkia.cpp:computeResamplingMode() */
539 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
540 static GraphicsFilter
ReduceResamplingFilter(GraphicsFilter aFilter
,
541 int aImgWidth
, int aImgHeight
,
542 float aSourceWidth
, float aSourceHeight
)
544 // Images smaller than this in either direction are considered "small" and
545 // are not resampled ever (see below).
546 const int kSmallImageSizeThreshold
= 8;
548 // The amount an image can be stretched in a single direction before we
549 // say that it is being stretched so much that it must be a line or
550 // background that doesn't need resampling.
551 const float kLargeStretch
= 3.0f
;
553 if (aImgWidth
<= kSmallImageSizeThreshold
554 || aImgHeight
<= kSmallImageSizeThreshold
) {
555 // Never resample small images. These are often used for borders and
556 // rules (think 1x1 images used to make lines).
557 return GraphicsFilter::FILTER_NEAREST
;
560 if (aImgHeight
* kLargeStretch
<= aSourceHeight
|| aImgWidth
* kLargeStretch
<= aSourceWidth
) {
561 // Large image tiling detected.
563 // Don't resample if it is being tiled a lot in only one direction.
564 // This is trying to catch cases where somebody has created a border
565 // (which might be large) and then is stretching it to fill some part
567 if (fabs(aSourceWidth
- aImgWidth
)/aImgWidth
< 0.5 || fabs(aSourceHeight
- aImgHeight
)/aImgHeight
< 0.5)
568 return GraphicsFilter::FILTER_NEAREST
;
570 // The image is growing a lot and in more than one direction. Resampling
571 // is slow and doesn't give us very much when growing a lot.
575 /* Some notes on other heuristics:
576 The Skia backend also uses nearest for backgrounds that are stretched by
577 a large amount. I'm not sure this is common enough for us to worry about
578 now. It also uses nearest for backgrounds/avoids high quality for images
579 that are very slightly scaled. I'm also not sure that very slightly
580 scaled backgrounds are common enough us to worry about.
582 We don't currently have much support for doing high quality interpolation.
583 The only place this currently happens is on Quartz and we don't have as
584 much control over it as would be needed. Webkit avoids using high quality
585 resampling during load. It also avoids high quality if the transformation
586 is not just a scale and translation
588 WebKit bug #40045 added code to avoid resampling different parts
589 of an image with different methods by using a resampling hint size.
590 It currently looks unused in WebKit but it's something to watch out for.
596 static GraphicsFilter
ReduceResamplingFilter(GraphicsFilter aFilter
,
597 int aImgWidth
, int aImgHeight
,
598 int aSourceWidth
, int aSourceHeight
)
600 // Just pass the filter through unchanged
606 gfxUtils::DrawPixelSnapped(gfxContext
* aContext
,
607 gfxDrawable
* aDrawable
,
608 const gfxSize
& aImageSize
,
609 const ImageRegion
& aRegion
,
610 const SurfaceFormat aFormat
,
611 GraphicsFilter aFilter
,
612 uint32_t aImageFlags
,
615 PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
616 js::ProfileEntry::Category::GRAPHICS
);
618 gfxRect
imageRect(gfxPoint(0, 0), aImageSize
);
619 gfxRect
region(aRegion
.Rect());
621 bool doTile
= !imageRect
.Contains(region
) &&
622 !(aImageFlags
& imgIContainer::FLAG_CLAMP
);
624 nsRefPtr
<gfxASurface
> currentTarget
= aContext
->CurrentSurface();
625 gfxMatrix deviceSpaceToImageSpace
= DeviceToImageTransform(aContext
);
627 AutoCairoPixmanBugWorkaround
workaround(aContext
, deviceSpaceToImageSpace
,
628 region
, currentTarget
);
629 if (!workaround
.Succeeded())
632 nsRefPtr
<gfxDrawable
> drawable
= aDrawable
;
634 aFilter
= ReduceResamplingFilter(aFilter
,
635 imageRect
.Width(), imageRect
.Height(),
636 region
.Width(), region
.Height());
638 // OK now, the hard part left is to account for the subimage sampling
639 // restriction. If all the transforms involved are just integer
640 // translations, then we assume no resampling will occur so there's
642 // XXX if only we had source-clipping in cairo!
643 if (aContext
->CurrentMatrix().HasNonIntegerTranslation()) {
644 if (doTile
|| !aRegion
.RestrictionContains(imageRect
)) {
645 if (drawable
->DrawWithSamplingRect(aContext
, aRegion
.Rect(), aRegion
.Restriction(),
646 doTile
, aFilter
, aOpacity
)) {
650 // On Mobile, we don't ever want to do this; it has the potential for
651 // allocating very large temporary surfaces, especially since we'll
652 // do full-page snapshots often (see bug 749426).
653 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
654 nsRefPtr
<gfxDrawable
> restrictedDrawable
=
655 CreateSamplingRestrictedDrawable(aDrawable
, aContext
,
657 if (restrictedDrawable
) {
658 drawable
.swap(restrictedDrawable
);
661 // We no longer need to tile: Either we never needed to, or we already
662 // filled a surface with the tiled pattern; this surface can now be
663 // drawn without tiling.
669 drawable
->Draw(aContext
, aRegion
.Rect(), doTile
, aFilter
, aOpacity
);
673 gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat
)
676 case gfxImageFormat::ARGB32
:
678 case gfxImageFormat::RGB24
:
680 case gfxImageFormat::RGB16_565
:
689 PathFromRegionInternal(gfxContext
* aContext
, const nsIntRegion
& aRegion
)
692 nsIntRegionRectIterator
iter(aRegion
);
694 while ((r
= iter
.Next()) != nullptr) {
695 aContext
->Rectangle(gfxRect(r
->x
, r
->y
, r
->width
, r
->height
));
700 ClipToRegionInternal(gfxContext
* aContext
, const nsIntRegion
& aRegion
)
702 PathFromRegionInternal(aContext
, aRegion
);
706 static TemporaryRef
<Path
>
707 PathFromRegionInternal(DrawTarget
* aTarget
, const nsIntRegion
& aRegion
)
709 RefPtr
<PathBuilder
> pb
= aTarget
->CreatePathBuilder();
710 nsIntRegionRectIterator
iter(aRegion
);
713 while ((r
= iter
.Next()) != nullptr) {
714 pb
->MoveTo(Point(r
->x
, r
->y
));
715 pb
->LineTo(Point(r
->XMost(), r
->y
));
716 pb
->LineTo(Point(r
->XMost(), r
->YMost()));
717 pb
->LineTo(Point(r
->x
, r
->YMost()));
720 RefPtr
<Path
> path
= pb
->Finish();
725 ClipToRegionInternal(DrawTarget
* aTarget
, const nsIntRegion
& aRegion
)
727 if (!aRegion
.IsComplex()) {
728 nsIntRect rect
= aRegion
.GetBounds();
729 aTarget
->PushClipRect(Rect(rect
.x
, rect
.y
, rect
.width
, rect
.height
));
733 RefPtr
<Path
> path
= PathFromRegionInternal(aTarget
, aRegion
);
734 aTarget
->PushClip(path
);
738 gfxUtils::ClipToRegion(gfxContext
* aContext
, const nsIntRegion
& aRegion
)
740 ClipToRegionInternal(aContext
, aRegion
);
744 gfxUtils::ClipToRegion(DrawTarget
* aTarget
, const nsIntRegion
& aRegion
)
746 ClipToRegionInternal(aTarget
, aRegion
);
750 gfxUtils::ClampToScaleFactor(gfxFloat aVal
)
752 // Arbitary scale factor limitation. We can increase this
753 // for better scaling performance at the cost of worse
755 static const gfxFloat kScaleResolution
= 2;
757 // Negative scaling is just a flip and irrelevant to
758 // our resolution calculation.
763 bool inverse
= false;
769 gfxFloat power
= log(aVal
)/log(kScaleResolution
);
771 // If power is within 1e-5 of an integer, round to nearest to
772 // prevent floating point errors, otherwise round up to the
773 // next integer value.
774 if (fabs(power
- NS_round(power
)) < 1e-5) {
775 power
= NS_round(power
);
776 } else if (inverse
) {
777 power
= floor(power
);
782 gfxFloat scale
= pow(kScaleResolution
, power
);
793 gfxUtils::PathFromRegion(gfxContext
* aContext
, const nsIntRegion
& aRegion
)
795 PathFromRegionInternal(aContext
, aRegion
);
799 gfxUtils::TransformRectToRect(const gfxRect
& aFrom
, const gfxPoint
& aToTopLeft
,
800 const gfxPoint
& aToTopRight
, const gfxPoint
& aToBottomRight
)
803 if (aToTopRight
.y
== aToTopLeft
.y
&& aToTopRight
.x
== aToBottomRight
.x
) {
804 // Not a rotation, so xy and yx are zero
806 m
._11
= (aToBottomRight
.x
- aToTopLeft
.x
)/aFrom
.width
;
807 m
._22
= (aToBottomRight
.y
- aToTopLeft
.y
)/aFrom
.height
;
808 m
._31
= aToTopLeft
.x
- m
._11
*aFrom
.x
;
809 m
._32
= aToTopLeft
.y
- m
._22
*aFrom
.y
;
811 NS_ASSERTION(aToTopRight
.y
== aToBottomRight
.y
&& aToTopRight
.x
== aToTopLeft
.x
,
812 "Destination rectangle not axis-aligned");
814 m
._21
= (aToBottomRight
.x
- aToTopLeft
.x
)/aFrom
.height
;
815 m
._12
= (aToBottomRight
.y
- aToTopLeft
.y
)/aFrom
.width
;
816 m
._31
= aToTopLeft
.x
- m
._21
*aFrom
.y
;
817 m
._32
= aToTopLeft
.y
- m
._12
*aFrom
.x
;
823 gfxUtils::TransformRectToRect(const gfxRect
& aFrom
, const IntPoint
& aToTopLeft
,
824 const IntPoint
& aToTopRight
, const IntPoint
& aToBottomRight
)
827 if (aToTopRight
.y
== aToTopLeft
.y
&& aToTopRight
.x
== aToBottomRight
.x
) {
828 // Not a rotation, so xy and yx are zero
830 m
._11
= (aToBottomRight
.x
- aToTopLeft
.x
)/aFrom
.width
;
831 m
._22
= (aToBottomRight
.y
- aToTopLeft
.y
)/aFrom
.height
;
832 m
._31
= aToTopLeft
.x
- m
._11
*aFrom
.x
;
833 m
._32
= aToTopLeft
.y
- m
._22
*aFrom
.y
;
835 NS_ASSERTION(aToTopRight
.y
== aToBottomRight
.y
&& aToTopRight
.x
== aToTopLeft
.x
,
836 "Destination rectangle not axis-aligned");
838 m
._21
= (aToBottomRight
.x
- aToTopLeft
.x
)/aFrom
.height
;
839 m
._12
= (aToBottomRight
.y
- aToTopLeft
.y
)/aFrom
.width
;
840 m
._31
= aToTopLeft
.x
- m
._21
*aFrom
.y
;
841 m
._32
= aToTopLeft
.y
- m
._12
*aFrom
.x
;
846 /* This function is sort of shitty. We truncate doubles
847 * to ints then convert those ints back to doubles to make sure that
848 * they equal the doubles that we got in. */
850 gfxUtils::GfxRectToIntRect(const gfxRect
& aIn
, nsIntRect
* aOut
)
852 *aOut
= nsIntRect(int32_t(aIn
.X()), int32_t(aIn
.Y()),
853 int32_t(aIn
.Width()), int32_t(aIn
.Height()));
854 return gfxRect(aOut
->x
, aOut
->y
, aOut
->width
, aOut
->height
).IsEqualEdges(aIn
);
858 gfxUtils::GetYCbCrToRGBDestFormatAndSize(const PlanarYCbCrData
& aData
,
859 gfxImageFormat
& aSuggestedFormat
,
860 gfxIntSize
& aSuggestedSize
)
863 TypeFromSize(aData
.mYSize
.width
,
865 aData
.mCbCrSize
.width
,
866 aData
.mCbCrSize
.height
);
868 // 'prescale' is true if the scaling is to be done as part of the
869 // YCbCr to RGB conversion rather than on the RGB data when rendered.
870 bool prescale
= aSuggestedSize
.width
> 0 && aSuggestedSize
.height
> 0 &&
871 ToIntSize(aSuggestedSize
) != aData
.mPicSize
;
873 if (aSuggestedFormat
== gfxImageFormat::RGB16_565
) {
874 #if defined(HAVE_YCBCR_TO_RGB565)
876 !IsScaleYCbCrToRGB565Fast(aData
.mPicX
,
878 aData
.mPicSize
.width
,
879 aData
.mPicSize
.height
,
880 aSuggestedSize
.width
,
881 aSuggestedSize
.height
,
884 IsConvertYCbCrToRGB565Fast(aData
.mPicX
,
886 aData
.mPicSize
.width
,
887 aData
.mPicSize
.height
,
892 // yuv2rgb16 function not available
893 aSuggestedFormat
= gfxImageFormat::RGB24
;
896 else if (aSuggestedFormat
!= gfxImageFormat::RGB24
) {
897 // No other formats are currently supported.
898 aSuggestedFormat
= gfxImageFormat::RGB24
;
900 if (aSuggestedFormat
== gfxImageFormat::RGB24
) {
901 /* ScaleYCbCrToRGB32 does not support a picture offset, nor 4:4:4 data.
902 See bugs 639415 and 640073. */
903 if (aData
.mPicX
!= 0 || aData
.mPicY
!= 0 || yuvtype
== YV24
)
907 ToIntSize(aSuggestedSize
) = aData
.mPicSize
;
912 gfxUtils::ConvertYCbCrToRGB(const PlanarYCbCrData
& aData
,
913 const gfxImageFormat
& aDestFormat
,
914 const gfxIntSize
& aDestSize
,
915 unsigned char* aDestBuffer
,
918 // ConvertYCbCrToRGB et al. assume the chroma planes are rounded up if the
919 // luma plane is odd sized.
920 MOZ_ASSERT((aData
.mCbCrSize
.width
== aData
.mYSize
.width
||
921 aData
.mCbCrSize
.width
== (aData
.mYSize
.width
+ 1) >> 1) &&
922 (aData
.mCbCrSize
.height
== aData
.mYSize
.height
||
923 aData
.mCbCrSize
.height
== (aData
.mYSize
.height
+ 1) >> 1));
925 TypeFromSize(aData
.mYSize
.width
,
927 aData
.mCbCrSize
.width
,
928 aData
.mCbCrSize
.height
);
930 // Convert from YCbCr to RGB now, scaling the image if needed.
931 if (ToIntSize(aDestSize
) != aData
.mPicSize
) {
932 #if defined(HAVE_YCBCR_TO_RGB565)
933 if (aDestFormat
== gfxImageFormat::RGB16_565
) {
934 ScaleYCbCrToRGB565(aData
.mYChannel
,
940 aData
.mPicSize
.width
,
941 aData
.mPicSize
.height
,
951 ScaleYCbCrToRGB32(aData
.mYChannel
,
955 aData
.mPicSize
.width
,
956 aData
.mPicSize
.height
,
965 } else { // no prescale
966 #if defined(HAVE_YCBCR_TO_RGB565)
967 if (aDestFormat
== gfxImageFormat::RGB16_565
) {
968 ConvertYCbCrToRGB565(aData
.mYChannel
,
974 aData
.mPicSize
.width
,
975 aData
.mPicSize
.height
,
980 } else // aDestFormat != gfxImageFormat::RGB16_565
982 ConvertYCbCrToRGB32(aData
.mYChannel
,
988 aData
.mPicSize
.width
,
989 aData
.mPicSize
.height
,
997 /* static */ void gfxUtils::ClearThebesSurface(gfxASurface
* aSurface
,
999 const gfxRGBA
& aColor
)
1001 if (aSurface
->CairoStatus()) {
1004 cairo_surface_t
* surf
= aSurface
->CairoSurface();
1005 if (cairo_surface_status(surf
)) {
1008 cairo_t
* ctx
= cairo_create(surf
);
1009 cairo_set_source_rgba(ctx
, aColor
.r
, aColor
.g
, aColor
.b
, aColor
.a
);
1010 cairo_set_operator(ctx
, CAIRO_OPERATOR_SOURCE
);
1015 bounds
= nsIntRect(nsIntPoint(0, 0), aSurface
->GetSize());
1017 cairo_rectangle(ctx
, bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
);
1022 /* static */ TemporaryRef
<DataSourceSurface
>
1023 gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface
* aSurface
,
1024 SurfaceFormat aFormat
)
1026 MOZ_ASSERT(aFormat
!= aSurface
->GetFormat(),
1027 "Unnecessary - and very expersive - surface format conversion");
1029 Rect
bounds(0, 0, aSurface
->GetSize().width
, aSurface
->GetSize().height
);
1031 if (aSurface
->GetType() != SurfaceType::DATA
) {
1032 // If the surface is NOT of type DATA then its data is not mapped into main
1033 // memory. Format conversion is probably faster on the GPU, and by doing it
1034 // there we can avoid any expensive uploads/readbacks except for (possibly)
1035 // a single readback due to the unavoidable GetDataSurface() call. Using
1036 // CreateOffscreenContentDrawTarget ensures the conversion happens on the
1038 RefPtr
<DrawTarget
> dt
= gfxPlatform::GetPlatform()->
1039 CreateOffscreenContentDrawTarget(aSurface
->GetSize(), aFormat
);
1040 // Using DrawSurface() here rather than CopySurface() because CopySurface
1041 // is optimized for memcpy and therefore isn't good for format conversion.
1042 // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
1043 // generally more optimized.
1044 dt
->DrawSurface(aSurface
, bounds
, bounds
, DrawSurfaceOptions(),
1045 DrawOptions(1.0f
, CompositionOp::OP_OVER
));
1046 RefPtr
<SourceSurface
> surface
= dt
->Snapshot();
1047 return surface
->GetDataSurface();
1050 // If the surface IS of type DATA then it may or may not be in main memory
1051 // depending on whether or not it has been mapped yet. We have no way of
1052 // knowing, so we can't be sure if it's best to create a data wrapping
1053 // DrawTarget for the conversion or an offscreen content DrawTarget. We could
1054 // guess it's not mapped and create an offscreen content DrawTarget, but if
1055 // it is then we'll end up uploading the surface data, and most likely the
1056 // caller is going to be accessing the resulting surface data, resulting in a
1057 // readback (both very expensive operations). Alternatively we could guess
1058 // the data is mapped and create a data wrapping DrawTarget and, if the
1059 // surface is not in main memory, then we will incure a readback. The latter
1060 // of these two "wrong choices" is the least costly (a readback, vs an
1061 // upload and a readback), and more than likely the DATA surface that we've
1062 // been passed actually IS in main memory anyway. For these reasons it's most
1063 // likely best to create a data wrapping DrawTarget here to do the format
1065 RefPtr
<DataSourceSurface
> dataSurface
=
1066 Factory::CreateDataSourceSurface(aSurface
->GetSize(), aFormat
);
1067 DataSourceSurface::MappedSurface map
;
1069 !dataSurface
->Map(DataSourceSurface::MapType::READ_WRITE
, &map
)) {
1072 RefPtr
<DrawTarget
> dt
=
1073 Factory::CreateDrawTargetForData(BackendType::CAIRO
,
1075 dataSurface
->GetSize(),
1079 dataSurface
->Unmap();
1082 // Using DrawSurface() here rather than CopySurface() because CopySurface
1083 // is optimized for memcpy and therefore isn't good for format conversion.
1084 // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
1085 // generally more optimized.
1086 dt
->DrawSurface(aSurface
, bounds
, bounds
, DrawSurfaceOptions(),
1087 DrawOptions(1.0f
, CompositionOp::OP_OVER
));
1088 dataSurface
->Unmap();
1089 return dataSurface
.forget();
1092 const uint32_t gfxUtils::sNumFrameColors
= 8;
1094 /* static */ const gfx::Color
&
1095 gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber
)
1097 static bool initialized
= false;
1098 static gfx::Color colors
[sNumFrameColors
];
1102 colors
[i
++] = gfx::Color::FromABGR(0xffff0000);
1103 colors
[i
++] = gfx::Color::FromABGR(0xffcc00ff);
1104 colors
[i
++] = gfx::Color::FromABGR(0xff0066cc);
1105 colors
[i
++] = gfx::Color::FromABGR(0xff00ff00);
1106 colors
[i
++] = gfx::Color::FromABGR(0xff33ffff);
1107 colors
[i
++] = gfx::Color::FromABGR(0xffff0099);
1108 colors
[i
++] = gfx::Color::FromABGR(0xff0000ff);
1109 colors
[i
++] = gfx::Color::FromABGR(0xff999999);
1110 MOZ_ASSERT(i
== sNumFrameColors
);
1114 return colors
[aFrameNumber
% sNumFrameColors
];
1118 EncodeSourceSurfaceInternal(SourceSurface
* aSurface
,
1119 const nsACString
& aMimeType
,
1120 const nsAString
& aOutputOptions
,
1121 gfxUtils::BinaryOrData aBinaryOrData
,
1125 MOZ_ASSERT(aBinaryOrData
== gfxUtils::eDataURIEncode
|| aFile
|| aStrOut
,
1126 "Copying binary encoding to clipboard not currently supported");
1128 const IntSize size
= aSurface
->GetSize();
1129 if (size
.IsEmpty()) {
1130 return NS_ERROR_INVALID_ARG
;
1132 const Size
floatSize(size
.width
, size
.height
);
1134 RefPtr
<DataSourceSurface
> dataSurface
;
1135 if (aSurface
->GetFormat() != SurfaceFormat::B8G8R8A8
) {
1136 // FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5)
1138 gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(aSurface
,
1139 SurfaceFormat::B8G8R8A8
);
1141 dataSurface
= aSurface
->GetDataSurface();
1144 return NS_ERROR_FAILURE
;
1147 DataSourceSurface::MappedSurface map
;
1148 if (!dataSurface
->Map(DataSourceSurface::MapType::READ
, &map
)) {
1149 return NS_ERROR_FAILURE
;
1152 nsAutoCString
encoderCID(
1153 NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType
);
1154 nsCOMPtr
<imgIEncoder
> encoder
= do_CreateInstance(encoderCID
.get());
1157 int32_t w
= std::min(size
.width
, 8);
1158 int32_t h
= std::min(size
.height
, 8);
1159 printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w
, h
);
1160 for (int32_t y
= 0; y
< h
; ++y
) {
1161 for (int32_t x
= 0; x
< w
; ++x
) {
1162 printf("%x ", reinterpret_cast<uint32_t*>(map
.mData
)[y
*map
.mStride
+x
]);
1166 dataSurface
->Unmap();
1167 return NS_ERROR_FAILURE
;
1170 nsresult rv
= encoder
->InitFromData(map
.mData
,
1171 BufferSizeFromStrideAndHeight(map
.mStride
, size
.height
),
1175 imgIEncoder::INPUT_FORMAT_HOSTARGB
,
1177 dataSurface
->Unmap();
1178 NS_ENSURE_SUCCESS(rv
, rv
);
1180 nsCOMPtr
<nsIInputStream
> imgStream
;
1181 CallQueryInterface(encoder
.get(), getter_AddRefs(imgStream
));
1183 return NS_ERROR_FAILURE
;
1187 rv
= imgStream
->Available(&bufSize64
);
1188 NS_ENSURE_SUCCESS(rv
, rv
);
1190 NS_ENSURE_TRUE(bufSize64
< UINT32_MAX
- 16, NS_ERROR_FAILURE
);
1192 uint32_t bufSize
= (uint32_t)bufSize64
;
1194 // ...leave a little extra room so we can call read again and make sure we
1195 // got everything. 16 bytes for better padding (maybe)
1197 uint32_t imgSize
= 0;
1198 Vector
<char> imgData
;
1199 if (!imgData
.initCapacity(bufSize
)) {
1200 return NS_ERROR_OUT_OF_MEMORY
;
1202 uint32_t numReadThisTime
= 0;
1203 while ((rv
= imgStream
->Read(imgData
.begin() + imgSize
,
1205 &numReadThisTime
)) == NS_OK
&& numReadThisTime
> 0)
1207 // Update the length of the vector without overwriting the new data.
1208 imgData
.growByUninitialized(numReadThisTime
);
1210 imgSize
+= numReadThisTime
;
1211 if (imgSize
== bufSize
) {
1212 // need a bigger buffer, just double
1214 if (!imgData
.resizeUninitialized(bufSize
)) {
1215 return NS_ERROR_OUT_OF_MEMORY
;
1219 NS_ENSURE_SUCCESS(rv
, rv
);
1220 NS_ENSURE_TRUE(!imgData
.empty(), NS_ERROR_FAILURE
);
1222 if (aBinaryOrData
== gfxUtils::eBinaryEncode
) {
1224 fwrite(imgData
.begin(), 1, imgSize
, aFile
);
1229 // base 64, result will be null-terminated
1230 nsCString encodedImg
;
1231 rv
= Base64Encode(Substring(imgData
.begin(), imgSize
), encodedImg
);
1232 NS_ENSURE_SUCCESS(rv
, rv
);
1234 nsCString
string("data:");
1235 string
.Append(aMimeType
);
1236 string
.Append(";base64,");
1237 string
.Append(encodedImg
);
1241 if (aFile
== stdout
|| aFile
== stderr
) {
1242 // ADB logcat cuts off long strings so we will break it down
1243 const char* cStr
= string
.BeginReading();
1244 size_t len
= strlen(cStr
);
1246 printf_stderr("IMG: %.140s\n", cStr
);
1254 fprintf(aFile
, "%s", string
.BeginReading());
1255 } else if (aStrOut
) {
1258 nsCOMPtr
<nsIClipboardHelper
> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv
));
1260 clipboard
->CopyString(NS_ConvertASCIItoUTF16(string
), nullptr);
1267 EncodeSourceSurfaceAsPNGURI(SourceSurface
* aSurface
)
1270 EncodeSourceSurfaceInternal(aSurface
, NS_LITERAL_CSTRING("image/png"),
1271 EmptyString(), gfxUtils::eDataURIEncode
,
1276 /* static */ nsresult
1277 gfxUtils::EncodeSourceSurface(SourceSurface
* aSurface
,
1278 const nsACString
& aMimeType
,
1279 const nsAString
& aOutputOptions
,
1280 BinaryOrData aBinaryOrData
,
1283 return EncodeSourceSurfaceInternal(aSurface
, aMimeType
, aOutputOptions
,
1284 aBinaryOrData
, aFile
, nullptr);
1288 gfxUtils::WriteAsPNG(SourceSurface
* aSurface
, const nsAString
& aFile
)
1290 WriteAsPNG(aSurface
, NS_ConvertUTF16toUTF8(aFile
).get());
1294 gfxUtils::WriteAsPNG(SourceSurface
* aSurface
, const char* aFile
)
1296 FILE* file
= fopen(aFile
, "wb");
1299 // Maybe the directory doesn't exist; try creating it, then fopen again.
1300 nsresult rv
= NS_ERROR_FAILURE
;
1301 nsCOMPtr
<nsIFile
> comFile
= do_CreateInstance("@mozilla.org/file/local;1");
1303 NS_ConvertUTF8toUTF16
utf16path((nsDependentCString(aFile
)));
1304 rv
= comFile
->InitWithPath(utf16path
);
1305 if (NS_SUCCEEDED(rv
)) {
1306 nsCOMPtr
<nsIFile
> dirPath
;
1307 comFile
->GetParent(getter_AddRefs(dirPath
));
1309 rv
= dirPath
->Create(nsIFile::DIRECTORY_TYPE
, 0777);
1310 if (NS_SUCCEEDED(rv
) || rv
== NS_ERROR_FILE_ALREADY_EXISTS
) {
1311 file
= fopen(aFile
, "wb");
1317 NS_WARNING("Failed to open file to create PNG!\n");
1322 EncodeSourceSurface(aSurface
, NS_LITERAL_CSTRING("image/png"),
1323 EmptyString(), eBinaryEncode
, file
);
1328 gfxUtils::WriteAsPNG(DrawTarget
* aDT
, const nsAString
& aFile
)
1330 WriteAsPNG(aDT
, NS_ConvertUTF16toUTF8(aFile
).get());
1334 gfxUtils::WriteAsPNG(DrawTarget
* aDT
, const char* aFile
)
1336 RefPtr
<SourceSurface
> surface
= aDT
->Snapshot();
1338 WriteAsPNG(surface
, aFile
);
1340 NS_WARNING("Failed to get surface!");
1345 gfxUtils::WriteAsPNG(nsIPresShell
* aShell
, const char* aFile
)
1347 int32_t width
= 1000, height
= 1000;
1348 nsRect
r(0, 0, aShell
->GetPresContext()->DevPixelsToAppUnits(width
),
1349 aShell
->GetPresContext()->DevPixelsToAppUnits(height
));
1351 RefPtr
<mozilla::gfx::DrawTarget
> dt
= gfxPlatform::GetPlatform()->
1352 CreateOffscreenContentDrawTarget(IntSize(width
, height
),
1353 SurfaceFormat::B8G8R8A8
);
1354 NS_ENSURE_TRUE(dt
, /*void*/);
1356 nsRefPtr
<gfxContext
> context
= new gfxContext(dt
);
1357 aShell
->RenderDocument(r
, 0, NS_RGB(255, 255, 0), context
);
1358 WriteAsPNG(dt
.get(), aFile
);
1362 gfxUtils::DumpAsDataURI(SourceSurface
* aSurface
, FILE* aFile
)
1364 EncodeSourceSurface(aSurface
, NS_LITERAL_CSTRING("image/png"),
1365 EmptyString(), eDataURIEncode
, aFile
);
1368 /* static */ nsCString
1369 gfxUtils::GetAsDataURI(SourceSurface
* aSurface
)
1371 return EncodeSourceSurfaceAsPNGURI(aSurface
);
1375 gfxUtils::DumpAsDataURI(DrawTarget
* aDT
, FILE* aFile
)
1377 RefPtr
<SourceSurface
> surface
= aDT
->Snapshot();
1379 DumpAsDataURI(surface
, aFile
);
1381 NS_WARNING("Failed to get surface!");
1385 /* static */ nsCString
1386 gfxUtils::GetAsLZ4Base64Str(DataSourceSurface
* aSourceSurface
)
1388 int32_t dataSize
= aSourceSurface
->GetSize().height
* aSourceSurface
->Stride();
1389 auto compressedData
= MakeUnique
<char[]>(LZ4::maxCompressedSize(dataSize
));
1390 if (compressedData
) {
1391 int nDataSize
= LZ4::compress((char*)aSourceSurface
->GetData(),
1393 compressedData
.get());
1394 if (nDataSize
> 0) {
1395 nsCString encodedImg
;
1396 nsresult rv
= Base64Encode(Substring(compressedData
.get(), nDataSize
), encodedImg
);
1398 nsCString
string("");
1399 string
.AppendPrintf("data:image/lz4bgra;base64,%i,%i,%i,",
1400 aSourceSurface
->GetSize().width
,
1401 aSourceSurface
->Stride(),
1402 aSourceSurface
->GetSize().height
);
1403 string
.Append(encodedImg
);
1408 return nsCString("");
1411 /* static */ nsCString
1412 gfxUtils::GetAsDataURI(DrawTarget
* aDT
)
1414 RefPtr
<SourceSurface
> surface
= aDT
->Snapshot();
1416 return EncodeSourceSurfaceAsPNGURI(surface
);
1418 NS_WARNING("Failed to get surface!");
1419 return nsCString("");
1424 gfxUtils::CopyAsDataURI(SourceSurface
* aSurface
)
1426 EncodeSourceSurface(aSurface
, NS_LITERAL_CSTRING("image/png"),
1427 EmptyString(), eDataURIEncode
, nullptr);
1431 gfxUtils::CopyAsDataURI(DrawTarget
* aDT
)
1433 RefPtr
<SourceSurface
> surface
= aDT
->Snapshot();
1435 CopyAsDataURI(surface
);
1437 NS_WARNING("Failed to get surface!");
1442 gfxUtils::DumpDisplayList() {
1443 return gfxPrefs::LayoutDumpDisplayList();
1446 FILE *gfxUtils::sDumpPaintFile
= stderr
;
1448 #ifdef MOZ_DUMP_PAINTING
1449 bool gfxUtils::sDumpPainting
= getenv("MOZ_DUMP_PAINT") != 0;
1450 bool gfxUtils::sDumpPaintingToFile
= getenv("MOZ_DUMP_PAINT_TO_FILE") != 0;
1452 bool gfxUtils::sDumpPainting
= false;
1453 bool gfxUtils::sDumpPaintingToFile
= false;
1459 Color
ToDeviceColor(Color aColor
)
1461 // aColor is pass-by-value since to get return value optimization goodness we
1462 // need to return the same object from all return points in this function. We
1463 // could declare a local Color variable and use that, but we might as well
1465 if (gfxPlatform::GetCMSMode() == eCMSMode_All
) {
1466 qcms_transform
*transform
= gfxPlatform::GetCMSRGBTransform();
1468 gfxPlatform::TransformPixel(aColor
, aColor
, transform
);
1469 // Use the original alpha to avoid unnecessary float->byte->float
1470 // conversion errors
1476 Color
ToDeviceColor(nscolor aColor
)
1478 return ToDeviceColor(Color::FromABGR(aColor
));
1481 Color
ToDeviceColor(const gfxRGBA
& aColor
)
1483 return ToDeviceColor(ToColor(aColor
));
1487 } // namespace mozilla