Bug 435531 ? problem saving login when form or existing login is password-only. r...
[wine-gecko.git] / gfx / src / thebes / nsThebesImage.cpp
blob95bae70f05a6abb3ede39ea4a2804e87144be281
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
13 * License.
15 * The Original Code is thebes gfx
17 * The Initial Developer of the Original Code is
18 * mozilla.org.
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
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"
47 #include "prenv.h"
49 static PRBool gDisableOptimize = PR_FALSE;
51 #ifdef XP_WIN
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)
58 #endif
60 NS_IMPL_ISUPPORTS1(nsThebesImage, nsIImage)
62 nsThebesImage::nsThebesImage()
63 : mFormat(gfxImageSurface::ImageFormatRGB24),
64 mWidth(0),
65 mHeight(0),
66 mDecoded(0,0,0,0),
67 mImageComplete(PR_FALSE),
68 mSinglePixel(PR_FALSE),
69 mFormatChanged(PR_FALSE),
70 mAlphaDepth(0)
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;
80 #ifdef XP_WIN
81 mIsDDBSurface = PR_FALSE;
82 #endif
85 nsresult
86 nsThebesImage::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequirements aMaskRequirements)
88 mWidth = aWidth;
89 mHeight = aHeight;
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;
100 mAlphaDepth = 1;
101 break;
102 case nsMaskRequirements_kNeeds8Bit:
103 format = gfxImageSurface::ImageFormatARGB32;
104 mAlphaDepth = 8;
105 break;
106 default:
107 format = gfxImageSurface::ImageFormatRGB24;
108 mAlphaDepth = 0;
109 break;
112 mFormat = format;
114 #ifdef XP_WIN
115 if (!ShouldUseImageSurfaces()) {
116 mWinSurface = new gfxWindowsSurface(gfxIntSize(mWidth, mHeight), format);
117 if (mWinSurface && mWinSurface->CairoStatus() == 0) {
118 // no error
119 mImageSurface = mWinSurface->GetImageSurface();
123 if (!mImageSurface)
124 mWinSurface = nsnull;
125 #endif
127 if (!mImageSurface)
128 mImageSurface = new gfxImageSurface(gfxIntSize(mWidth, mHeight), format);
130 if (!mImageSurface || mImageSurface->CairoStatus()) {
131 mImageSurface = nsnull;
132 // guess
133 return NS_ERROR_OUT_OF_MEMORY;
136 #ifdef XP_MACOSX
137 mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
138 #endif
140 mStride = mImageSurface->Stride();
142 return NS_OK;
145 nsThebesImage::~nsThebesImage()
147 #ifdef XP_WIN
148 if (mIsDDBSurface) {
149 gTotalDDBs--;
150 gTotalDDBSize -= mWidth*mHeight*4;
152 #endif
155 PRInt32
156 nsThebesImage::GetBytesPix()
158 return 4;
161 PRBool
162 nsThebesImage::GetIsRowOrderTopToBottom()
164 return PR_TRUE;
167 PRInt32
168 nsThebesImage::GetWidth()
170 return mWidth;
173 PRInt32
174 nsThebesImage::GetHeight()
176 return mHeight;
179 PRUint8 *
180 nsThebesImage::GetBits()
182 if (mImageSurface)
183 return mImageSurface->Data();
184 return nsnull;
187 PRInt32
188 nsThebesImage::GetLineStride()
190 return mStride;
193 PRBool
194 nsThebesImage::GetHasAlphaMask()
196 return mAlphaDepth > 0;
199 PRUint8 *
200 nsThebesImage::GetAlphaBits()
202 return nsnull;
205 PRInt32
206 nsThebesImage::GetAlphaLineStride()
208 return (mAlphaDepth > 0) ? mStride : 0;
211 void
212 nsThebesImage::ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect)
214 mDecoded.UnionRect(mDecoded, *aUpdateRect);
215 #ifdef XP_MACOSX
216 if (mQuartzSurface)
217 mQuartzSurface->Flush();
218 #endif
221 PRBool
222 nsThebesImage::GetIsImageComplete()
224 if (!mImageComplete)
225 mImageComplete = (mDecoded == nsRect(0, 0, mWidth, mHeight));
226 return mImageComplete;
229 nsresult
230 nsThebesImage::Optimize(nsIDeviceContext* aContext)
232 if (gDisableOptimize)
233 return NS_OK;
235 if (mOptSurface || mSinglePixel)
236 return NS_OK;
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
255 (firstPixel,
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;
266 #ifdef XP_WIN
267 mWinSurface = nsnull;
268 #endif
269 #ifdef XP_MACOSX
270 mQuartzSurface = nsnull;
271 #endif
272 return NS_OK;
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())
282 return NS_OK;
284 mOptSurface = nsnull;
286 #ifdef XP_WIN
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
289 // as we can.
290 if (mWinSurface) {
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);
313 if (wsurf) {
314 gTotalDDBs++;
315 gTotalDDBSize += ddbSize;
316 mIsDDBSurface = PR_TRUE;
317 mOptSurface = wsurf;
320 if (!mOptSurface && !mFormatChanged) {
321 // just use the DIB if the format has not changed
322 mOptSurface = mWinSurface;
325 #endif
327 #ifdef XP_MACOSX
328 if (mQuartzSurface) {
329 mQuartzSurface->Flush();
330 mOptSurface = mQuartzSurface;
332 #endif
334 if (mOptSurface == nsnull)
335 mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
337 if (mOptSurface) {
338 mImageSurface = nsnull;
339 #ifdef XP_WIN
340 mWinSurface = nsnull;
341 #endif
342 #ifdef XP_MACOSX
343 mQuartzSurface = nsnull;
344 #endif
347 return NS_OK;
350 nsColorMap *
351 nsThebesImage::GetColorMap()
353 return NULL;
356 PRInt8
357 nsThebesImage::GetAlphaDepth()
359 return mAlphaDepth;
362 void *
363 nsThebesImage::GetBitInfo()
365 return NULL;
368 NS_IMETHODIMP
369 nsThebesImage::LockImagePixels(PRBool aMaskPixels)
371 if (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);
381 if (mSinglePixel)
382 context.SetDeviceColor(mSinglePixelColor);
383 else
384 context.SetSource(mOptSurface);
385 context.Paint();
387 #ifdef XP_WIN
388 mWinSurface = nsnull;
389 #endif
390 #ifdef XP_MACOSX
391 mQuartzSurface = nsnull;
392 #endif
395 return NS_OK;
398 NS_IMETHODIMP
399 nsThebesImage::UnlockImagePixels(PRBool aMaskPixels)
401 if (aMaskPixels)
402 return NS_ERROR_NOT_IMPLEMENTED;
403 mOptSurface = nsnull;
404 #ifdef XP_MACOSX
405 if (mQuartzSurface)
406 mQuartzSurface->Flush();
407 #endif
408 return NS_OK;
411 /* NB: These are pixels, not twips. */
412 NS_IMETHODIMP
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.");
420 return NS_OK;
423 nsThebesRenderingContext *thebesRC = static_cast<nsThebesRenderingContext*>(&aContext);
424 gfxContext *ctx = thebesRC->ThebesContext();
426 #if 0
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);
432 #endif
434 if (mSinglePixel) {
435 // if a == 0, it's a noop
436 if (mSinglePixelColor.a == 0.0)
437 return NS_OK;
439 // otherwise
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);
445 ctx->NewPath();
446 ctx->Rectangle(aDestRect, PR_TRUE);
447 ctx->Fill();
448 ctx->SetOperator(op);
449 return NS_OK;
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))
467 return NS_OK;
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())
478 return NS_OK;
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());
499 } else {
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);
512 tempctx.Paint();
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
520 * coordinate space.
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)
530 return NS_OK;
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);
539 gfxMatrix mat;
540 mat.Translate(srcRect.pos);
541 mat.Scale(1.0 / xscale, 1.0 / yscale);
542 pat->SetMatrix(mat);
544 tempctx.SetPattern(pat);
545 tempctx.SetOperator(gfxContext::OPERATOR_SOURCE);
546 tempctx.NewPath();
547 tempctx.Rectangle(gfxRect(0.0, 0.0, dim.width, dim.height));
548 tempctx.Fill();
550 pat = new gfxPattern(temp);
552 srcRect.pos.x = 0.0;
553 srcRect.pos.y = 0.0;
554 srcRect.size.width = dim.width;
555 srcRect.size.height = dim.height;
557 xscale = 1.0;
558 yscale = 1.0;
561 gfxMatrix mat;
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);
570 pat->SetMatrix(mat);
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
582 // available
584 // This effectively disables smooth upscaling for images.
585 if (xscale > 1.0 || yscale > 1.0 || ctxHasNonTranslation)
586 pat->SetFilter(0);
587 break;
589 case gfxASurface::SurfaceTypeQuartz:
590 case gfxASurface::SurfaceTypeQuartzImage:
591 // Do nothing, Mac seems to be OK. Really?
592 break;
594 default:
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);
600 break;
603 gfxContext::GraphicsOperator op = ctx->CurrentOperator();
604 if (op == gfxContext::OPERATOR_OVER && mFormat == gfxASurface::ImageFormatRGB24)
605 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
607 ctx->NewPath();
608 ctx->SetPattern(pat);
609 ctx->Rectangle(destRect);
610 ctx->Fill();
612 ctx->SetOperator(op);
613 ctx->SetDeviceColor(gfxRGBA(0,0,0,0));
615 return NS_OK;
618 nsresult
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)
630 return NS_OK;
632 // don't do anything if we have a transparent pixel source
633 if (mSinglePixel && mSinglePixelColor.a == 0.0)
634 return NS_OK;
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);
644 } else {
645 nsRefPtr<gfxASurface> surface;
646 PRInt32 width, height;
648 if (hasPadding) {
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);
666 if (mSinglePixel) {
667 tmpContext.SetDeviceColor(mSinglePixelColor);
668 } else {
669 tmpContext.SetSource(ThebesSurface());
671 tmpContext.SetOperator(gfxContext::OPERATOR_SOURCE);
672 tmpContext.Rectangle(gfxRect(0, 0, mWidth, mHeight));
673 tmpContext.Fill();
674 } else {
675 width = mWidth;
676 height = 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
682 // CSS pixels.
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
703 // sides.
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
720 // looking like
721 // P P P ... P P P
722 // P X X ... X X P
723 // P X X ... X X P
724 // ...............
725 // P X X ... X X P
726 // P X X ... X X P
727 // P P P ... P P P
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.
737 PRInt32 destY = 0;
738 for (PRInt32 y = -1; y <= 1; ++y) {
739 PRInt32 stripHeight = y == 0 ? tileHeight : padY;
740 if (stripHeight == 0)
741 continue;
742 PRInt32 srcY = y == 1 ? aSubimageRect.YMost() - padY : aSubimageRect.y;
744 PRInt32 destX = 0;
745 for (PRInt32 x = -1; x <= 1; ++x) {
746 PRInt32 stripWidth = x == 0 ? tileWidth : padX;
747 if (stripWidth == 0)
748 continue;
749 PRInt32 srcX = x == 1 ? aSubimageRect.XMost() - padX : aSubimageRect.x;
751 gfxMatrix patMat;
752 patMat.Translate(gfxPoint(srcX - destX, srcY - destY));
753 pat.SetMatrix(patMat);
754 tmpContext.SetPattern(&pat);
755 tmpContext.Rectangle(gfxRect(destX, destY, stripWidth, stripHeight));
756 tmpContext.Fill();
757 tmpContext.NewPath();
759 destX += stripWidth;
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
767 // CSS pixels.
768 tmpOffset += gfxPoint(aSubimageRect.x - padX, aSubimageRect.y - padY)/scale;
770 surface = tmpSurface;
773 gfxMatrix patMat;
774 gfxPoint p0;
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);
785 #ifndef XP_MACOSX
786 if (scale < 1.0) {
787 // See bug 324698. This is a workaround. See comments
788 // by the earlier SetFilter call.
789 pat.SetFilter(0);
791 #endif
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));
807 return NS_OK;
810 PRBool
811 nsThebesImage::ShouldUseImageSurfaces()
813 #ifdef XP_WIN
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);
820 if (count == 0 ||
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.
826 return PR_TRUE;
828 #endif
830 return PR_FALSE;
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
838 // set to 0xff.
839 void
840 nsThebesImage::SetHasNoAlpha()
842 if (mFormat == gfxASurface::ImageFormatARGB32) {
843 mFormat = gfxASurface::ImageFormatRGB24;
844 mFormatChanged = PR_TRUE;