1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is thebes gfx
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
23 * Vladimir Vukicevic <vladimir@pobox.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsThebesImage.h"
40 #include "nsThebesRenderingContext.h"
42 #include "gfxContext.h"
43 #include "gfxPattern.h"
45 #include "gfxPlatform.h"
49 static PRBool gDisableOptimize
= PR_FALSE
;
52 static PRUint32 gTotalDDBs
= 0;
53 static PRUint32 gTotalDDBSize
= 0;
54 // only use up a maximum of 64MB in DDBs
55 #define kMaxDDBSize (64*1024*1024)
56 // and don't let anything in that's bigger than 4MB
57 #define kMaxSingleDDBSize (4*1024*1024)
60 NS_IMPL_ISUPPORTS1(nsThebesImage
, nsIImage
)
62 nsThebesImage::nsThebesImage()
63 : mFormat(gfxImageSurface::ImageFormatRGB24
),
67 mImageComplete(PR_FALSE
),
68 mSinglePixel(PR_FALSE
),
69 mFormatChanged(PR_FALSE
),
72 static PRBool hasCheckedOptimize
= PR_FALSE
;
73 if (!hasCheckedOptimize
) {
74 if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
75 gDisableOptimize
= PR_TRUE
;
77 hasCheckedOptimize
= PR_TRUE
;
81 mIsDDBSurface
= PR_FALSE
;
86 nsThebesImage::Init(PRInt32 aWidth
, PRInt32 aHeight
, PRInt32 aDepth
, nsMaskRequirements aMaskRequirements
)
91 // Reject over-wide or over-tall images.
92 if (!AllowedImageSize(aWidth
, aHeight
))
93 return NS_ERROR_FAILURE
;
95 gfxImageSurface::gfxImageFormat format
;
96 switch(aMaskRequirements
)
98 case nsMaskRequirements_kNeeds1Bit
:
99 format
= gfxImageSurface::ImageFormatARGB32
;
102 case nsMaskRequirements_kNeeds8Bit
:
103 format
= gfxImageSurface::ImageFormatARGB32
;
107 format
= gfxImageSurface::ImageFormatRGB24
;
115 if (!ShouldUseImageSurfaces()) {
116 mWinSurface
= new gfxWindowsSurface(gfxIntSize(mWidth
, mHeight
), format
);
117 if (mWinSurface
&& mWinSurface
->CairoStatus() == 0) {
119 mImageSurface
= mWinSurface
->GetImageSurface();
124 mWinSurface
= nsnull
;
128 mImageSurface
= new gfxImageSurface(gfxIntSize(mWidth
, mHeight
), format
);
130 if (!mImageSurface
|| mImageSurface
->CairoStatus()) {
131 mImageSurface
= nsnull
;
133 return NS_ERROR_OUT_OF_MEMORY
;
137 mQuartzSurface
= new gfxQuartzImageSurface(mImageSurface
);
140 mStride
= mImageSurface
->Stride();
145 nsThebesImage::~nsThebesImage()
150 gTotalDDBSize
-= mWidth
*mHeight
*4;
156 nsThebesImage::GetBytesPix()
162 nsThebesImage::GetIsRowOrderTopToBottom()
168 nsThebesImage::GetWidth()
174 nsThebesImage::GetHeight()
180 nsThebesImage::GetBits()
183 return mImageSurface
->Data();
188 nsThebesImage::GetLineStride()
194 nsThebesImage::GetHasAlphaMask()
196 return mAlphaDepth
> 0;
200 nsThebesImage::GetAlphaBits()
206 nsThebesImage::GetAlphaLineStride()
208 return (mAlphaDepth
> 0) ? mStride
: 0;
212 nsThebesImage::ImageUpdated(nsIDeviceContext
*aContext
, PRUint8 aFlags
, nsRect
*aUpdateRect
)
214 mDecoded
.UnionRect(mDecoded
, *aUpdateRect
);
217 mQuartzSurface
->Flush();
222 nsThebesImage::GetIsImageComplete()
225 mImageComplete
= (mDecoded
== nsRect(0, 0, mWidth
, mHeight
));
226 return mImageComplete
;
230 nsThebesImage::Optimize(nsIDeviceContext
* aContext
)
232 if (gDisableOptimize
)
235 if (mOptSurface
|| mSinglePixel
)
238 /* Figure out if the entire image is a constant color */
240 // this should always be true
241 if (mStride
== mWidth
* 4) {
242 PRUint32
*imgData
= (PRUint32
*) mImageSurface
->Data();
243 PRUint32 firstPixel
= * (PRUint32
*) imgData
;
244 PRUint32 pixelCount
= mWidth
* mHeight
+ 1;
246 while (--pixelCount
&& *imgData
++ == firstPixel
)
249 if (pixelCount
== 0) {
250 // all pixels were the same
251 if (mFormat
== gfxImageSurface::ImageFormatARGB32
||
252 mFormat
== gfxImageSurface::ImageFormatRGB24
)
254 mSinglePixelColor
= gfxRGBA
256 (mFormat
== gfxImageSurface::ImageFormatRGB24
?
257 gfxRGBA::PACKED_XRGB
:
258 gfxRGBA::PACKED_ARGB_PREMULTIPLIED
));
260 mSinglePixel
= PR_TRUE
;
262 // blow away the older surfaces, to release data
264 mImageSurface
= nsnull
;
265 mOptSurface
= nsnull
;
267 mWinSurface
= nsnull
;
270 mQuartzSurface
= nsnull
;
276 // if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment
279 // if we're being forced to use image surfaces due to
280 // resource constraints, don't try to optimize beyond same-pixel.
281 if (ShouldUseImageSurfaces())
284 mOptSurface
= nsnull
;
287 // we need to special-case windows here, because windows has
288 // a distinction between DIB and DDB and we want to use DDBs as much
291 // Don't do DDBs for large images; see bug 359147
292 // Note that we bother with DDBs at all because they are much faster
293 // on some systems; on others there isn't much of a speed difference
294 // between DIBs and DDBs.
296 // Originally this just limited to 1024x1024; but that still
297 // had us hitting overall total memory usage limits (which was
298 // around 220MB on my intel shared memory system with 2GB RAM
299 // and 16-128mb in use by the video card, so I can't make
300 // heads or tails out of this limit).
302 // So instead, we clamp the max size to 64MB (this limit shuld
303 // be made dynamic based on.. something.. as soon a we figure
304 // out that something) and also limit each individual image to
305 // be less than 4MB to keep very large images out of DDBs.
307 // assume (almost -- we don't quadword-align) worst-case size
308 PRUint32 ddbSize
= mWidth
* mHeight
* 4;
309 if (ddbSize
<= kMaxSingleDDBSize
&&
310 ddbSize
+ gTotalDDBSize
<= kMaxDDBSize
)
312 nsRefPtr
<gfxWindowsSurface
> wsurf
= mWinSurface
->OptimizeToDDB(nsnull
, gfxIntSize(mWidth
, mHeight
), mFormat
);
315 gTotalDDBSize
+= ddbSize
;
316 mIsDDBSurface
= PR_TRUE
;
320 if (!mOptSurface
&& !mFormatChanged
) {
321 // just use the DIB if the format has not changed
322 mOptSurface
= mWinSurface
;
328 if (mQuartzSurface
) {
329 mQuartzSurface
->Flush();
330 mOptSurface
= mQuartzSurface
;
334 if (mOptSurface
== nsnull
)
335 mOptSurface
= gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface
, mFormat
);
338 mImageSurface
= nsnull
;
340 mWinSurface
= nsnull
;
343 mQuartzSurface
= nsnull
;
351 nsThebesImage::GetColorMap()
357 nsThebesImage::GetAlphaDepth()
363 nsThebesImage::GetBitInfo()
369 nsThebesImage::LockImagePixels(PRBool aMaskPixels
)
372 return NS_ERROR_NOT_IMPLEMENTED
;
373 if ((mOptSurface
|| mSinglePixel
) && !mImageSurface
) {
374 // Recover the pixels
375 mImageSurface
= new gfxImageSurface(gfxIntSize(mWidth
, mHeight
),
376 gfxImageSurface::ImageFormatARGB32
);
377 if (!mImageSurface
|| mImageSurface
->CairoStatus())
378 return NS_ERROR_OUT_OF_MEMORY
;
379 gfxContext
context(mImageSurface
);
380 context
.SetOperator(gfxContext::OPERATOR_SOURCE
);
382 context
.SetDeviceColor(mSinglePixelColor
);
384 context
.SetSource(mOptSurface
);
388 mWinSurface
= nsnull
;
391 mQuartzSurface
= nsnull
;
399 nsThebesImage::UnlockImagePixels(PRBool aMaskPixels
)
402 return NS_ERROR_NOT_IMPLEMENTED
;
403 mOptSurface
= nsnull
;
406 mQuartzSurface
->Flush();
411 /* NB: These are pixels, not twips. */
413 nsThebesImage::Draw(nsIRenderingContext
&aContext
,
414 const gfxRect
&aSourceRect
,
415 const gfxRect
&aSubimageRect
,
416 const gfxRect
&aDestRect
)
418 if (NS_UNLIKELY(aDestRect
.IsEmpty())) {
419 NS_ERROR("nsThebesImage::Draw zero dest size - please fix caller.");
423 nsThebesRenderingContext
*thebesRC
= static_cast<nsThebesRenderingContext
*>(&aContext
);
424 gfxContext
*ctx
= thebesRC
->ThebesContext();
427 fprintf (stderr
, "nsThebesImage::Draw src [%f %f %f %f] dest [%f %f %f %f] trans: [%f %f] dec: [%f %f]\n",
428 aSourceRect
.pos
.x
, aSourceRect
.pos
.y
, aSourceRect
.size
.width
, aSourceRect
.size
.height
,
429 aDestRect
.pos
.x
, aDestRect
.pos
.y
, aDestRect
.size
.width
, aDestRect
.size
.height
,
430 ctx
->CurrentMatrix().GetTranslation().x
, ctx
->CurrentMatrix().GetTranslation().y
,
431 mDecoded
.x
, mDecoded
.y
, mDecoded
.width
, mDecoded
.height
);
435 // if a == 0, it's a noop
436 if (mSinglePixelColor
.a
== 0.0)
440 gfxContext::GraphicsOperator op
= ctx
->CurrentOperator();
441 if (op
== gfxContext::OPERATOR_OVER
&& mSinglePixelColor
.a
== 1.0)
442 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
444 ctx
->SetDeviceColor(mSinglePixelColor
);
446 ctx
->Rectangle(aDestRect
, PR_TRUE
);
448 ctx
->SetOperator(op
);
452 gfxFloat xscale
= aDestRect
.size
.width
/ aSourceRect
.size
.width
;
453 gfxFloat yscale
= aDestRect
.size
.height
/ aSourceRect
.size
.height
;
455 gfxRect
srcRect(aSourceRect
);
456 gfxRect
subimageRect(aSubimageRect
);
457 gfxRect
destRect(aDestRect
);
459 if (!GetIsImageComplete()) {
460 gfxRect decoded
= gfxRect(mDecoded
.x
, mDecoded
.y
,
461 mDecoded
.width
, mDecoded
.height
);
462 srcRect
= srcRect
.Intersect(decoded
);
463 subimageRect
= subimageRect
.Intersect(decoded
);
465 // This happens when mDecoded.width or height is zero. bug 368427.
466 if (NS_UNLIKELY(srcRect
.size
.width
== 0 || srcRect
.size
.height
== 0))
469 destRect
.pos
.x
+= (srcRect
.pos
.x
- aSourceRect
.pos
.x
)*xscale
;
470 destRect
.pos
.y
+= (srcRect
.pos
.y
- aSourceRect
.pos
.y
)*yscale
;
472 destRect
.size
.width
= srcRect
.size
.width
* xscale
;
473 destRect
.size
.height
= srcRect
.size
.height
* yscale
;
476 // if either rectangle is empty now (possibly after the image complete check)
477 if (srcRect
.IsEmpty() || destRect
.IsEmpty())
480 // Reject over-wide or over-tall images.
481 if (!AllowedImageSize(destRect
.size
.width
+ 1, destRect
.size
.height
+ 1))
482 return NS_ERROR_FAILURE
;
484 // Expand the subimageRect to place its edges on integer coordinates.
485 // Basically, if we're allowed to sample part of a pixel we can
486 // sample the whole pixel.
487 subimageRect
.RoundOut();
489 nsRefPtr
<gfxPattern
> pat
;
490 PRBool ctxHasNonTranslation
= ctx
->CurrentMatrix().HasNonTranslation();
491 if ((xscale
== 1.0 && yscale
== 1.0 && !ctxHasNonTranslation
) ||
492 subimageRect
== gfxRect(0, 0, mWidth
, mHeight
))
494 // No need to worry about sampling outside the subimage rectangle,
495 // so no need for a temporary
496 // XXX should we also check for situations where the source rect
497 // is well inside the subimage so we can't sample outside?
498 pat
= new gfxPattern(ThebesSurface());
500 // Because of the RoundOut above, the subimageRect has
501 // integer width and height.
502 gfxIntSize
size(PRInt32(subimageRect
.Width()),
503 PRInt32(subimageRect
.Height()));
504 nsRefPtr
<gfxASurface
> temp
=
505 gfxPlatform::GetPlatform()->CreateOffscreenSurface(size
, mFormat
);
506 if (!temp
|| temp
->CairoStatus() != 0)
507 return NS_ERROR_FAILURE
;
509 gfxContext
tempctx(temp
);
510 tempctx
.SetSource(ThebesSurface(), -subimageRect
.pos
);
511 tempctx
.SetOperator(gfxContext::OPERATOR_SOURCE
);
514 pat
= new gfxPattern(temp
);
515 srcRect
.MoveBy(-subimageRect
.pos
);
518 /* See bug 364968 to understand the necessity of this goop; we basically
519 * have to pre-downscale any image that would fall outside of a scaled 16-bit
522 if (aDestRect
.pos
.x
* (1.0 / xscale
) >= 32768.0 ||
523 aDestRect
.pos
.y
* (1.0 / yscale
) >= 32768.0)
525 gfxIntSize
dim(NS_lroundf(destRect
.size
.width
),
526 NS_lroundf(destRect
.size
.height
));
528 // nothing to do in this case
529 if (dim
.width
== 0 || dim
.height
== 0)
532 nsRefPtr
<gfxASurface
> temp
=
533 gfxPlatform::GetPlatform()->CreateOffscreenSurface (dim
, mFormat
);
534 if (!temp
|| temp
->CairoStatus() != 0)
535 return NS_ERROR_FAILURE
;
537 gfxContext
tempctx(temp
);
540 mat
.Translate(srcRect
.pos
);
541 mat
.Scale(1.0 / xscale
, 1.0 / yscale
);
544 tempctx
.SetPattern(pat
);
545 tempctx
.SetOperator(gfxContext::OPERATOR_SOURCE
);
547 tempctx
.Rectangle(gfxRect(0.0, 0.0, dim
.width
, dim
.height
));
550 pat
= new gfxPattern(temp
);
554 srcRect
.size
.width
= dim
.width
;
555 srcRect
.size
.height
= dim
.height
;
562 mat
.Translate(srcRect
.pos
);
563 mat
.Scale(1.0/xscale
, 1.0/yscale
);
565 /* Translate the start point of the image (srcRect.pos)
566 * to coincide with the destination rectangle origin
568 mat
.Translate(-destRect
.pos
);
572 nsRefPtr
<gfxASurface
> target
= ctx
->CurrentSurface();
573 switch (target
->GetType()) {
574 case gfxASurface::SurfaceTypeXlib
:
575 case gfxASurface::SurfaceTypeXcb
:
576 // See bug 324698. This is a workaround for EXTEND_PAD not being
577 // implemented correctly on linux in the X server.
579 // Set the filter to CAIRO_FILTER_FAST if we're scaling up -- otherwise,
580 // pixman's sampling will sample transparency for the outside edges and we'll
581 // get blurry edges. CAIRO_EXTEND_PAD would also work here, if
584 // This effectively disables smooth upscaling for images.
585 if (xscale
> 1.0 || yscale
> 1.0 || ctxHasNonTranslation
)
589 case gfxASurface::SurfaceTypeQuartz
:
590 case gfxASurface::SurfaceTypeQuartzImage
:
591 // Do nothing, Mac seems to be OK. Really?
595 // turn on EXTEND_PAD.
596 // This is what we really want for all surface types, if the
597 // implementation was universally good.
598 if (xscale
!= 1.0 || yscale
!= 1.0 || ctxHasNonTranslation
)
599 pat
->SetExtend(gfxPattern::EXTEND_PAD
);
603 gfxContext::GraphicsOperator op
= ctx
->CurrentOperator();
604 if (op
== gfxContext::OPERATOR_OVER
&& mFormat
== gfxASurface::ImageFormatRGB24
)
605 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
608 ctx
->SetPattern(pat
);
609 ctx
->Rectangle(destRect
);
612 ctx
->SetOperator(op
);
613 ctx
->SetDeviceColor(gfxRGBA(0,0,0,0));
619 nsThebesImage::ThebesDrawTile(gfxContext
*thebesContext
,
620 nsIDeviceContext
* dx
,
621 const gfxPoint
& offset
,
622 const gfxRect
& targetRect
,
623 const nsIntRect
& aSubimageRect
,
624 const PRInt32 xPadding
,
625 const PRInt32 yPadding
)
627 NS_ASSERTION(xPadding
>= 0 && yPadding
>= 0, "negative padding");
629 if (targetRect
.size
.width
<= 0.0 || targetRect
.size
.height
<= 0.0)
632 // don't do anything if we have a transparent pixel source
633 if (mSinglePixel
&& mSinglePixelColor
.a
== 0.0)
636 PRBool doSnap
= !(thebesContext
->CurrentMatrix().HasNonTranslation());
637 PRBool hasPadding
= ((xPadding
!= 0) || (yPadding
!= 0));
638 gfxImageSurface::gfxImageFormat format
= mFormat
;
640 gfxPoint tmpOffset
= offset
;
642 if (mSinglePixel
&& !hasPadding
) {
643 thebesContext
->SetDeviceColor(mSinglePixelColor
);
645 nsRefPtr
<gfxASurface
> surface
;
646 PRInt32 width
, height
;
649 /* Ugh we have padding; create a temporary surface that's the size of the surface + pad area,
650 * and render the image into it first. Then we'll tile that surface. */
651 width
= mWidth
+ xPadding
;
652 height
= mHeight
+ yPadding
;
654 // Reject over-wide or over-tall images.
655 if (!AllowedImageSize(width
, height
))
656 return NS_ERROR_FAILURE
;
658 format
= gfxASurface::ImageFormatARGB32
;
659 surface
= gfxPlatform::GetPlatform()->CreateOffscreenSurface(
660 gfxIntSize(width
, height
), format
);
661 if (!surface
|| surface
->CairoStatus()) {
662 return NS_ERROR_OUT_OF_MEMORY
;
665 gfxContext
tmpContext(surface
);
667 tmpContext
.SetDeviceColor(mSinglePixelColor
);
669 tmpContext
.SetSource(ThebesSurface());
671 tmpContext
.SetOperator(gfxContext::OPERATOR_SOURCE
);
672 tmpContext
.Rectangle(gfxRect(0, 0, mWidth
, mHeight
));
677 surface
= ThebesSurface();
680 // Scale factor to account for CSS pixels; note that the offset (and
681 // therefore p0) is in device pixels, while the width and height are in
683 gfxFloat scale
= gfxFloat(dx
->AppUnitsPerDevPixel()) /
684 gfxFloat(nsIDeviceContext::AppUnitsPerCSSPixel());
686 if ((aSubimageRect
.width
< width
|| aSubimageRect
.height
< height
) &&
687 (thebesContext
->CurrentMatrix().HasNonTranslation() || scale
!= 1.0)) {
688 // Some of the source image should not be drawn, and we're going
689 // to be doing more than just translation, so we might accidentally
690 // sample the non-drawn pixels. Avoid that by creating a
691 // temporary image representing the portion that will be drawn,
692 // with built-in padding since we can't use EXTEND_PAD and
693 // EXTEND_REPEAT at the same time for different axes.
694 PRInt32 padX
= aSubimageRect
.width
< width
? 1 : 0;
695 PRInt32 padY
= aSubimageRect
.height
< height
? 1 : 0;
696 PRInt32 tileWidth
= PR_MIN(aSubimageRect
.width
, width
);
697 PRInt32 tileHeight
= PR_MIN(aSubimageRect
.height
, height
);
699 // This tmpSurface will contain a snapshot of the repeated
700 // tile image at (aSubimageRect.x, aSubimageRect.y,
701 // tileWidth, tileHeight), with padX padding added to the left
702 // and right sides and padY padding added to the top and bottom
704 nsRefPtr
<gfxASurface
> tmpSurface
;
705 tmpSurface
= gfxPlatform::GetPlatform()->CreateOffscreenSurface(
706 gfxIntSize(tileWidth
+ 2*padX
, tileHeight
+ 2*padY
), format
);
707 if (!tmpSurface
|| tmpSurface
->CairoStatus()) {
708 return NS_ERROR_OUT_OF_MEMORY
;
711 gfxContext
tmpContext(tmpSurface
);
712 tmpContext
.SetOperator(gfxContext::OPERATOR_SOURCE
);
713 gfxPattern
pat(surface
);
714 pat
.SetExtend(gfxPattern::EXTEND_REPEAT
);
716 // Copy the needed portion of the source image to the temporary
717 // surface. We also copy over horizontal and/or vertical padding
718 // strips one pixel wide, plus the corner pixels if necessary.
719 // So in the most general case the temporary surface ends up
728 // Where each P pixel has the color of its nearest source X
729 // pixel. We implement this as a loop over all nine possible
730 // areas, [padding, body, padding] x [padding, body, padding].
731 // Note that we will not need padding on both axes unless
732 // we are painting just a single tile, in which case this
733 // will hardly ever get called since nsCSSRendering converts
734 // the single-tile case to nsLayoutUtils::DrawImage. But this
735 // could be called on other paths (XUL trees?) and it's simpler
736 // and clearer to do it the general way.
738 for (PRInt32 y
= -1; y
<= 1; ++y
) {
739 PRInt32 stripHeight
= y
== 0 ? tileHeight
: padY
;
740 if (stripHeight
== 0)
742 PRInt32 srcY
= y
== 1 ? aSubimageRect
.YMost() - padY
: aSubimageRect
.y
;
745 for (PRInt32 x
= -1; x
<= 1; ++x
) {
746 PRInt32 stripWidth
= x
== 0 ? tileWidth
: padX
;
749 PRInt32 srcX
= x
== 1 ? aSubimageRect
.XMost() - padX
: aSubimageRect
.x
;
752 patMat
.Translate(gfxPoint(srcX
- destX
, srcY
- destY
));
753 pat
.SetMatrix(patMat
);
754 tmpContext
.SetPattern(&pat
);
755 tmpContext
.Rectangle(gfxRect(destX
, destY
, stripWidth
, stripHeight
));
757 tmpContext
.NewPath();
761 destY
+= stripHeight
;
764 // tmpOffset was the top-left of the old tile image. Make it
765 // the top-left of the new tile image. Note that tmpOffset is
766 // in destination coordinate space so we have to scale our
768 tmpOffset
+= gfxPoint(aSubimageRect
.x
- padX
, aSubimageRect
.y
- padY
)/scale
;
770 surface
= tmpSurface
;
776 p0
.x
= - floor(tmpOffset
.x
+ 0.5);
777 p0
.y
= - floor(tmpOffset
.y
+ 0.5);
778 patMat
.Scale(scale
, scale
);
779 patMat
.Translate(p0
);
781 gfxPattern
pat(surface
);
782 pat
.SetExtend(gfxPattern::EXTEND_REPEAT
);
783 pat
.SetMatrix(patMat
);
787 // See bug 324698. This is a workaround. See comments
788 // by the earlier SetFilter call.
793 thebesContext
->SetPattern(&pat
);
796 gfxContext::GraphicsOperator op
= thebesContext
->CurrentOperator();
797 if (op
== gfxContext::OPERATOR_OVER
&& format
== gfxASurface::ImageFormatRGB24
)
798 thebesContext
->SetOperator(gfxContext::OPERATOR_SOURCE
);
800 thebesContext
->NewPath();
801 thebesContext
->Rectangle(targetRect
, doSnap
);
802 thebesContext
->Fill();
804 thebesContext
->SetOperator(op
);
805 thebesContext
->SetDeviceColor(gfxRGBA(0,0,0,0));
811 nsThebesImage::ShouldUseImageSurfaces()
814 static const DWORD kGDIObjectsHighWaterMark
= 7000;
816 // at 7000 GDI objects, stop allocating normal images to make sure
817 // we never hit the 10k hard limit.
818 // GetCurrentProcess() just returns (HANDLE)-1, it's inlined afaik
819 DWORD count
= GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS
);
821 count
> kGDIObjectsHighWaterMark
)
823 // either something's broken (count == 0),
824 // or we hit our high water mark; disable
825 // image allocations for a bit.
833 // A hint from the image decoders that this image has no alpha, even
834 // though we created is ARGB32. This changes our format to RGB24,
835 // which in turn will cause us to Optimize() to RGB24. Has no effect
836 // after Optimize() is called, though in all cases it will be just a
837 // performance win -- the pixels are still correct and have the A byte
840 nsThebesImage::SetHasNoAlpha()
842 if (mFormat
== gfxASurface::ImageFormatARGB32
) {
843 mFormat
= gfxASurface::ImageFormatRGB24
;
844 mFormatChanged
= PR_TRUE
;