Index: gfx/src/thebes/nsThebesImage.cpp =================================================================== RCS file: /cvsroot/mozilla/gfx/src/thebes/nsThebesImage.cpp,v retrieving revision 1.86 diff -u -8 -p -r1.86 nsThebesImage.cpp --- gfx/src/thebes/nsThebesImage.cpp 28 Apr 2008 21:27:05 -0000 1.86 +++ gfx/src/thebes/nsThebesImage.cpp 15 Jul 2008 18:29:48 -0000 @@ -16,16 +16,17 @@ * * The Initial Developer of the Original Code is * mozilla.org. * Portions created by the Initial Developer are Copyright (C) 2005 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Vladimir Vukicevic + * Francis Robichaud * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -43,16 +44,21 @@ #include "gfxPattern.h" #include "gfxPlatform.h" #include "prenv.h" static PRBool gDisableOptimize = PR_FALSE; +#ifdef XP_UNIX +static gfxFloat gImageThreshold = 0.5; +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#endif #ifdef XP_WIN static PRUint32 gTotalDDBs = 0; static PRUint32 gTotalDDBSize = 0; // only use up a maximum of 64MB in DDBs #define kMaxDDBSize (64*1024*1024) // and don't let anything in that's bigger than 4MB #define kMaxSingleDDBSize (4*1024*1024) #endif @@ -64,27 +70,41 @@ nsThebesImage::nsThebesImage() mWidth(0), mHeight(0), mDecoded(0,0,0,0), mImageComplete(PR_FALSE), mSinglePixel(PR_FALSE), mFormatChanged(PR_FALSE), mAlphaDepth(0) { - static PRBool hasCheckedOptimize = PR_FALSE; - if (!hasCheckedOptimize) { + static PRBool hasLoadedStatic = PR_FALSE; + if (!hasLoadedStatic) { if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) { gDisableOptimize = PR_TRUE; } - hasCheckedOptimize = PR_TRUE; + nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + PRInt32 threshold; + nsresult rv = prefs->GetIntPref("browser.gdk_interpolation_threshold_percent", &threshold); + if (NS_SUCCEEDED(rv)) { + // Store a value between 1 and 100 + threshold = (threshold > 0 || threshold <= 100) ? threshold: gImageThreshold*100; + gImageThreshold = gfxFloat(threshold) / 100; + } + } + hasLoadedStatic = PR_TRUE; } #ifdef XP_WIN mIsDDBSurface = PR_FALSE; #endif +#ifdef XP_UNIX + mTinySurf = nsnull; + mSentFullImage = PR_FALSE; +#endif } nsresult nsThebesImage::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequirements aMaskRequirements) { mWidth = aWidth; mHeight = aHeight; @@ -326,21 +346,26 @@ nsThebesImage::Optimize(nsIDeviceContext #ifdef XP_MACOSX if (mQuartzSurface) { mQuartzSurface->Flush(); mOptSurface = mQuartzSurface; } #endif +#ifndef XP_UNIX if (mOptSurface == nsnull) mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat); +#endif + if (mOptSurface) { +#ifndef XP_UNIX mImageSurface = nsnull; +#endif #ifdef XP_WIN mWinSurface = nsnull; #endif #ifdef XP_MACOSX mQuartzSurface = nsnull; #endif } @@ -481,16 +506,96 @@ nsThebesImage::Draw(nsIRenderingContext if (!AllowedImageSize(destRect.size.width + 1, destRect.size.height + 1)) return NS_ERROR_FAILURE; // Expand the subimageRect to place its edges on integer coordinates. // Basically, if we're allowed to sample part of a pixel we can // sample the whole pixel. subimageRect.RoundOut(); +#ifdef XP_UNIX + // We optimize the image surface to it's visible size and use scaling algorithms + // to reduce X memory consumption, bug 395260. + if (!mSentFullImage && + (subimageRect.size.width > (destRect.size.width / gImageThreshold) || + subimageRect.size.height > (destRect.size.height / gImageThreshold) || + xscale < (1.0 * gImageThreshold) || yscale < (1.0 * gImageThreshold) || + xscale > (1.0 / gImageThreshold) || yscale > (1.0 / gImageThreshold))) { + + gfxIntSize tinySize(NS_lroundf(destRect.size.width), NS_lroundf(destRect.size.height)); + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(mImageSurface->Data(), GDK_COLORSPACE_RGB, + PR_TRUE, 8, mWidth, mHeight, + mStride, nsnull, nsnull); + if (!pixbuf) + return NS_ERROR_FAILURE; + + GdkPixbuf* tinyPix = pixbuf; + if (subimageRect.Height() > destRect.Height() || subimageRect.Width() > destRect.Width()) + tinyPix = gdk_pixbuf_new_subpixbuf(pixbuf, + NS_lroundf(srcRect.pos.x), + NS_lroundf(srcRect.pos.y), + NS_lroundf(srcRect.size.width), + NS_lroundf(srcRect.size.height)); + if (!tinyPix) + return NS_ERROR_FAILURE; + + // XXX Cairo should have it's own scaling algorithm for image surfaces and use Sun's mediaLib + // We can't allow a 1x1 downscaling, causes badalloc with gdk_pixbufs + if ((xscale != 1.0 || yscale != 1.0) && + (tinySize.width != 1 && tinySize.height != 1)) { + // Scales down the pixbuf, R and B channels are inverted but this should not cause any issue. + // XXX we should still verify how GDK_INTERP_BILINEAR reacts to this abstraction. + tinyPix = gdk_pixbuf_scale_simple(tinyPix, tinySize.width, tinySize.height, GDK_INTERP_BILINEAR); + if (!tinyPix) + return NS_ERROR_FAILURE; + + // Disables further scaling operation to avoid bug 364968 explained below. + xscale = 1.0; + yscale = 1.0; + } + + unsigned char* pixels = gdk_pixbuf_get_pixels(tinyPix); + PRInt32 stride = gdk_pixbuf_get_rowstride(tinyPix); + + // We generate a new tiny surface + mTinySurf = new gfxImageSurface(tinySize, mFormat); + + for (int i=0; i < tinySize.height; i++) { + unsigned char* src = pixels + stride*i; + memcpy(mTinySurf->Data() + mTinySurf->Stride()*i, src, mTinySurf->Stride()); + } + + mOptSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(tinySize, mFormat); + if (!mOptSurface || mOptSurface->CairoStatus() != 0) + return NS_ERROR_FAILURE; + + gfxContext tmpCtx(mOptSurface); + tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE); + tmpCtx.SetSource(mTinySurf); + tmpCtx.Paint(); + + // Release the gdk objects without affecting the actual image data + g_object_unref(tinyPix); + g_object_unref(pixbuf); + + // Origin could be different than (0,0) + subimageRect.pos.x -= srcRect.pos.x; + srcRect.pos.x -= srcRect.pos.x; + subimageRect.pos.y -= srcRect.pos.y; + srcRect.pos.y -= srcRect.pos.y; + + } else if (!mSentFullImage && (mTinySurf || !mOptSurface)) { + // We were using a tiny surface and we now see the full image, restore the complete pixmap + mTinySurf = nsnull; + // Send image to X + mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat); + mSentFullImage = GetIsImageComplete(); + } +#endif + nsRefPtr pat; PRBool ctxHasNonTranslation = ctx->CurrentMatrix().HasNonTranslation(); if ((xscale == 1.0 && yscale == 1.0 && !ctxHasNonTranslation) || subimageRect == gfxRect(0, 0, mWidth, mHeight)) { // No need to worry about sampling outside the subimage rectangle, // so no need for a temporary // XXX should we also check for situations where the source rect Index: gfx/src/thebes/nsThebesImage.h =================================================================== RCS file: /cvsroot/mozilla/gfx/src/thebes/nsThebesImage.h,v retrieving revision 1.33 diff -u -8 -p -r1.33 nsThebesImage.h --- gfx/src/thebes/nsThebesImage.h 31 Mar 2008 09:40:54 -0000 1.33 +++ gfx/src/thebes/nsThebesImage.h 15 Jul 2008 18:29:48 -0000 @@ -35,17 +35,21 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef _NSTHEBESIMAGE_H_ #define _NSTHEBESIMAGE_H_ #include "nsIImage.h" - +#if defined(XP_UNIX) +#include +#include +#include "nsIServiceManager.h" +#endif #include "gfxColor.h" #include "gfxASurface.h" #include "gfxImageSurface.h" #include "gfxPattern.h" #if defined(XP_WIN) #include "gfxWindowsSurface.h" #elif defined(XP_MACOSX) #include "gfxQuartzImageSurface.h" @@ -166,16 +170,20 @@ protected: #ifdef XP_WIN PRPackedBool mIsDDBSurface; #endif gfxRGBA mSinglePixelColor; nsRefPtr mImageSurface; nsRefPtr mOptSurface; +#if defined(XP_UNIX) + nsRefPtr mTinySurf; + PRBool mSentFullImage; +#endif #if defined(XP_WIN) nsRefPtr mWinSurface; #elif defined(XP_MACOSX) nsRefPtr mQuartzSurface; #endif PRUint8 mAlphaDepth;