/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Stuart Parmenter * Tim Rowley */ #include #include #include "imgScaler.h" #include "nsImageGTK.h" #include "nsRenderingContextGTK.h" #include "nspr.h" #define IsFlagSet(a,b) ((a) & (b)) #define NS_GET_BIT(rowptr, x) (rowptr[(x)>>3] & (1<<(7-(x)&0x7))) #define NS_SET_BIT(rowptr, x) (rowptr[(x)>>3] |= (1<<(7-(x)&0x7))) #define NS_CLEAR_BIT(rowptr, x) (rowptr[(x)>>3] &= ~(1<<(7-(x)&0x7))) // Defining this will trace the allocation of images. This includes // ctor, dtor and update. // #define TRACE_IMAGE_ALLOCATION //#define CHEAP_PERFORMANCE_MEASURMENT 1 // Define this to see tiling debug output // #define DEBUG_TILING /* XXX we are simply creating a GC and setting its function to Copy. we shouldn't be doing this every time this method is called. this creates way more trips to the server than we should be doing so we are creating a static one. */ static GdkGC *s1bitGC = nsnull; static GdkGC *sXbitGC = nsnull; NS_IMPL_ISUPPORTS1(nsImageGTK, nsIImage) //------------------------------------------------------------ nsImageGTK::nsImageGTK() : mImageBits(nsnull) , mImagePixmap(nsnull) , mTrueAlphaBits(nsnull) , mAlphaBits(nsnull) , mAlphaPixmap(nsnull) , mAlphaXImage(nsnull) , mWidth(0) , mHeight(0) , mRowBytes(0) , mSizeImage(0) , mDecodedX1(PR_INT32_MAX) , mDecodedY1(PR_INT32_MAX) , mDecodedX2(0) , mDecodedY2(0) , mAlphaDepth(0) , mTrueAlphaDepth(0) , mIsSpacer(PR_TRUE) , mPendingUpdate(PR_FALSE) , mDepth(0) , mOptimized(PR_FALSE) { #ifdef TRACE_IMAGE_ALLOCATION printf("nsImageGTK::nsImageGTK(this=%p)\n", this); #endif } //------------------------------------------------------------ nsImageGTK::~nsImageGTK() { if(nsnull != mImageBits) { delete[] mImageBits; mImageBits = nsnull; } if (nsnull != mAlphaBits) { delete[] mAlphaBits; mAlphaBits = nsnull; } if (nsnull != mTrueAlphaBits) { delete[] mTrueAlphaBits; mTrueAlphaBits = nsnull; } if (mAlphaPixmap) { gdk_pixmap_unref(mAlphaPixmap); } if (mImagePixmap) { gdk_pixmap_unref(mImagePixmap); } if (mAlphaXImage) { mAlphaXImage->data = 0; XDestroyImage(mAlphaXImage); } #ifdef TRACE_IMAGE_ALLOCATION printf("nsImageGTK::~nsImageGTK(this=%p)\n", this); #endif } /* static */ void nsImageGTK::Shutdown() { if (s1bitGC) { gdk_gc_unref(s1bitGC); s1bitGC = nsnull; } if (sXbitGC) { gdk_gc_unref(sXbitGC); sXbitGC = nsnull; } } //------------------------------------------------------------ nsresult nsImageGTK::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequirements aMaskRequirements) { // Assumed: Init only gets called once by gfxIImageFrame g_return_val_if_fail ((aWidth != 0) || (aHeight != 0), NS_ERROR_FAILURE); // X Protocol limits us to image dimensions less than 32767 // unless we want to go through lots of pain and suffering. if (aWidth > SHRT_MAX || aHeight > SHRT_MAX) return NS_ERROR_FAILURE; if (24 == aDepth) { mNumBytesPixel = 3; } else { NS_ASSERTION(PR_FALSE, "unexpected image depth"); return NS_ERROR_UNEXPECTED; } mWidth = aWidth; mHeight = aHeight; mDepth = aDepth; #ifdef TRACE_IMAGE_ALLOCATION printf("nsImageGTK::Init(this=%p,%d,%d,%d,%d)\n", this, aWidth, aHeight, aDepth, aMaskRequirements); #endif // create the memory for the image ComputeMetrics(); mImageBits = (PRUint8*) new PRUint8[mSizeImage]; switch(aMaskRequirements) { case nsMaskRequirements_kNeeds8Bit: mTrueAlphaRowBytes = aWidth; mTrueAlphaDepth = 8; // 32-bit align each row mTrueAlphaRowBytes = (mTrueAlphaRowBytes + 3) & ~0x3; mTrueAlphaBits = new PRUint8[mTrueAlphaRowBytes * aHeight]; memset(mTrueAlphaBits, 0, mTrueAlphaRowBytes*aHeight); // FALL THROUGH case nsMaskRequirements_kNeeds1Bit: mAlphaRowBytes = (aWidth + 7) / 8; mAlphaDepth = 1; // 32-bit align each row mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3; mAlphaBits = new PRUint8[mAlphaRowBytes * aHeight]; memset(mAlphaBits, 0, mAlphaRowBytes*aHeight); break; default: break; // avoid compiler warning } if (aMaskRequirements == nsMaskRequirements_kNeeds8Bit) mAlphaDepth = 0; return NS_OK; } //------------------------------------------------------------ PRInt32 nsImageGTK::GetHeight() { return mHeight; } PRInt32 nsImageGTK::GetWidth() { return mWidth; } PRUint8 *nsImageGTK::GetBits() { return mImageBits; } void *nsImageGTK::GetBitInfo() { return nsnull; } PRInt32 nsImageGTK::GetLineStride() { return mRowBytes; } nsColorMap *nsImageGTK::GetColorMap() { return nsnull; } PRUint8 *nsImageGTK::GetAlphaBits() { if (mTrueAlphaBits) return mTrueAlphaBits; else return mAlphaBits; } PRInt32 nsImageGTK::GetAlphaLineStride() { if (mTrueAlphaBits) return mTrueAlphaRowBytes; else return mAlphaRowBytes; } void nsImageGTK::ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect) { mPendingUpdate = PR_TRUE; mUpdateRegion.Or(mUpdateRegion, *aUpdateRect); mDecodedX1 = PR_MIN(mDecodedX1, aUpdateRect->x); mDecodedY1 = PR_MIN(mDecodedY1, aUpdateRect->y); if (aUpdateRect->YMost() > mDecodedY2) mDecodedY2 = aUpdateRect->YMost(); if (aUpdateRect->XMost() > mDecodedX2) mDecodedX2 = aUpdateRect->XMost(); } void nsImageGTK::UpdateCachedImage() { #ifdef TRACE_IMAGE_ALLOCATION printf("nsImageGTK::ImageUpdated(this=%p)\n", this); #endif nsRegionRectIterator ri(mUpdateRegion); const nsRect *rect; while ((rect = ri.Next()) != nsnull) { // fprintf(stderr, "ImageUpdated %p x,y=(%d %d) width,height=(%d %d)\n", // this, rect->x, rect->y, rect->width, rect->height); unsigned bottom, left, right; bottom = rect->y + rect->height; left = rect->x; right = left + rect->width; // check if the image has an all-opaque 8-bit alpha mask if ((mTrueAlphaDepth==8) && (mAlphaDepthy; (y> (left & 0x7); PRUint8 rightmask = 0xff << (7 - ((right-1) & 0x7)); // byte where the first/last bits of the update region are located PRUint32 leftindex = left >> 3; PRUint32 rightindex = (right-1) >> 3; // first/last bits in the same byte - combine mask into leftmask // and fill rightmask so we don't try using it if (leftindex == rightindex) { leftmask &= rightmask; rightmask = 0xff; } // check the leading bits if (leftmask != 0xff) { PRUint8 *ptr = mAlphaBits + mAlphaRowBytes * rect->y + leftindex; for (unsigned y=rect->y; yy + rightindex; for (unsigned y=rect->y; yy; (yx, rect->y, rect->width, rect->height, GDK_RGB_DITHER_MAX, mImageBits + mRowBytes*rect->y + 3*rect->x, mRowBytes, rect->x, rect->y); } if (mAlphaDepth==1) { XPutImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap), GDK_WINDOW_XWINDOW(mAlphaPixmap), GDK_GC_XGC(s1bitGC), mAlphaXImage, rect->x, rect->y, rect->x, rect->y, rect->width, rect->height); } } mUpdateRegion.SetEmpty(); mPendingUpdate = PR_FALSE; mFlags = nsImageUpdateFlags_kBitsChanged; // this should be 0'd out by Draw() } #ifdef CHEAP_PERFORMANCE_MEASURMENT static PRTime gConvertTime, gAlphaTime, gCopyStart, gCopyEnd, gStartTime, gPixmapTime, gEndTime; #endif /* Xlib image scaling... */ #define sign(x) ((x)>0 ? 1:-1) static void XlibStretchHorizontal(long x1,long x2,long y1,long y2, long ymin,long ymax, long startColumn, long endColumn, long offsetX, long offsetY, GdkPixmap *aSrcImage, GdkPixmap *aDstImage, GdkGC *gc); /********************************************************** XlibRectStretch enlarges or diminishes a source rectangle of a bitmap to a destination rectangle. The source rectangle is selected by the two points (xs1,ys1) and (xs2,ys2), and the destination rectangle by (xd1,yd1) and (xd2,yd2). Entry: xs1,ys1 - first point of source rectangle xs2,ys2 - second point of source rectangle xd1,yd1 - first point of destination rectangle xd2,yd2 - second point of destination rectangle offx, offy - offset to target **********************************************************/ void XlibRectStretch(PRInt32 srcWidth, PRInt32 srcHeight, PRInt32 dstWidth, PRInt32 dstHeight, PRInt32 dstOrigX, PRInt32 dstOrigY, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight, GdkPixmap *aSrcImage, GdkPixmap *aDstImage, GdkGC *gc, GdkGC *copygc, PRInt32 aDepth) { long dx,dy,e,d,dx2; short sx,sy; GdkPixmap *aTmpImage = 0; PRBool skipHorizontal=PR_FALSE, skipVertical=PR_FALSE; long startColumn, startRow, endColumn, endRow; long xs1, ys1, xs2, ys2, xd1, yd1, xd2, yd2; xs1 = ys1 = xd1 = yd1 = 0; xs2 = srcWidth-1; ys2 = srcHeight-1; xd2 = dstWidth-1; yd2 = dstHeight-1; // fprintf(stderr, "XRS %p (%ld %ld)-(%ld %ld) (%ld %ld)-(%ld %ld)\n", // (void *)aDstImage, xs1, ys1, xs2, ys2, xd1, yd1, xd2, yd2); startColumn = aDX-dstOrigX; startRow = aDY-dstOrigY; endColumn = aDX+aDWidth-dstOrigX; endRow = aDY+aDHeight-dstOrigY; // fprintf(stderr, "startXY = %d %d endXY = %d %d %d x %d\n", // startColumn, startRow, endColumn, endRow, // endColumn-startColumn, endRow-startRow); long scaleStartY, scaleEndY; scaleStartY = startRow * (ys2-ys1+1) / (yd2-yd1+1); scaleEndY = 1 + endRow * (ys2-ys1+1) / (yd2-yd1+1); if (xd2-xd1 == xs2-xs1) { // fprintf(stderr, "skipping horizontal\n"); skipHorizontal = PR_TRUE; aTmpImage = aSrcImage; scaleStartY = 0; scaleEndY = ys2; } if (yd2-yd1 == ys2-ys1) { // fprintf(stderr, "skipping vertical\n"); skipVertical = PR_TRUE; aTmpImage = aDstImage; } if (skipVertical && skipHorizontal) { gdk_draw_pixmap(aDstImage, gc, aSrcImage, 0, 0, srcWidth, srcHeight, dstOrigX, dstOrigY); return; } // fprintf(stderr, "scaleY Start/End = %d %d\n", scaleStartY, scaleEndY); if (!skipHorizontal && !skipVertical) { aTmpImage = gdk_pixmap_new(nsnull, endColumn-startColumn, scaleEndY-scaleStartY, aDepth); #ifdef MOZ_WIDGET_GTK2 if (aDepth != 1) gdk_drawable_set_colormap(GDK_DRAWABLE(aTmpImage), gdk_rgb_get_colormap()); #endif } dx = abs((int)(yd2-yd1)); dy = abs((int)(ys2-ys1)); sx = sign(yd2-yd1); sy = sign(ys2-ys1); e = dy-dx; dx2 = dx; dy += 1; if (!dx2) dx2=1; if (!skipHorizontal) XlibStretchHorizontal(xd1, xd2, xs1, xs2, scaleStartY, scaleEndY, startColumn, endColumn, (skipVertical?MAX(dstOrigX,0):0),(skipVertical?MAX(dstOrigY,0):0), aSrcImage, aTmpImage, (skipVertical?gc:copygc)); if (!skipVertical) { for (d=0; d<=dx; d++) { if ((yd1 >= startRow) && (yd1 <= endRow)) { gdk_draw_pixmap(aDstImage, gc, aTmpImage, (skipHorizontal?startColumn:0), ys1-scaleStartY, (dstOrigX>0?dstOrigX:0), dstOrigY+yd1, endColumn-startColumn, 1); } while (e>=0) { ys1 += sy; e -= dx2; } yd1 += sx; e += dy; } } if (!skipHorizontal && !skipVertical) gdk_pixmap_unref(aTmpImage); } /********************************************************** Stretches a image horizontally by column replication/deletion. Used by XlibRectStretch. Entry: x1,x2 - x-coordinates of the destination line y1,y2 - x-coordinates of the source line ymin - y-coordinate of top of stretch region ymax - y-coordinate of bottom of stretch region **********************************************************/ static void XlibStretchHorizontal(long x1, long x2, long y1, long y2, long ymin, long ymax, long startColumn, long endColumn, long offsetX, long offsetY, GdkPixmap *aSrcImage, GdkPixmap *aDstImage, GdkGC *gc) { long dx,dy,e,d,dx2; short sx,sy; dx = abs((int)(x2-x1)); dy = abs((int)(y2-y1)); sx = sign(x2-x1); sy = sign(y2-y1); e = dy-dx; dx2 = dx; dy += 1; if (!dx2) dx2=1; for (d=0; d<=dx; d++) { if ((x1 >= startColumn) && (x1 <= endColumn)) { gdk_draw_pixmap(aDstImage, gc, aSrcImage, y1, ymin, x1-startColumn+offsetX, 0+offsetY, 1, ymax-ymin); } while (e>=0) { y1 += sy; e -= dx2; } x1 += sx; e += dy; } } #undef sign // Draw the bitmap, this method has a source and destination coordinates NS_IMETHODIMP nsImageGTK::Draw(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) { g_return_val_if_fail ((aSurface != nsnull), NS_ERROR_FAILURE); if (mPendingUpdate) UpdateCachedImage(); if ((mAlphaDepth==1) && mIsSpacer) return NS_OK; if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1) return NS_OK; #ifdef TRACE_IMAGE_ALLOCATION fprintf(stderr, "nsImageGTK::Draw(%p) s=(%4d %4d %4d %4d) d=(%4d %4d %4d %4d)\n", this, aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight); #endif if (aSWidth <= 0 || aDWidth <= 0 || aSHeight <= 0 || aDHeight <= 0) { return NS_OK; } // store some values we'll need for scaling... PRInt32 srcWidth, srcHeight, dstWidth, dstHeight; PRInt32 dstOrigX, dstOrigY; srcWidth = aSWidth; srcHeight = aSHeight; dstWidth = aDWidth; dstHeight = aDHeight; dstOrigX = aDX; dstOrigY = aDY; // clip to decode region PRInt32 j = aSX + aSWidth; PRInt32 z; if (j > mDecodedX2) { z = j - mDecodedX2; aDWidth -= z*dstWidth/srcWidth; aSWidth -= z; } if (aSX < mDecodedX1) { aDX += (mDecodedX1 - aSX)*dstWidth/srcWidth; aSX = mDecodedX1; } j = aSY + aSHeight; if (j > mDecodedY2) { z = j - mDecodedY2; aDHeight -= z*dstHeight/srcHeight; aSHeight -= z; } if (aSY < mDecodedY1) { aDY += (mDecodedY1 - aSY)*dstHeight/srcHeight; aSY = mDecodedY1; } if (aDWidth <= 0 || aDHeight <= 0 || aSWidth <= 0 || aSHeight <= 0) { return NS_OK; } // clip to drawing surface nsDrawingSurfaceGTK *drawing = (nsDrawingSurfaceGTK*)aSurface; PRUint32 surfaceWidth, surfaceHeight; drawing->GetDimensions(&surfaceWidth, &surfaceHeight); if (aDX + aDWidth > (PRInt32)surfaceWidth) { z = aDX + aDWidth - surfaceWidth; aDWidth -= z; aSWidth -= z*srcWidth/dstWidth; } if (aDX < 0) { aDWidth += aDX; aSWidth += aDX*srcWidth/dstWidth; aSX -= aDX*srcWidth/dstWidth; aDX = 0; } if (aDY + aDHeight > (PRInt32)surfaceHeight) { z = aDY + aDHeight - surfaceHeight; aDHeight -= z; aSHeight -= z*srcHeight/dstHeight; } if (aDY < 0) { aDHeight += aDY; aSHeight += aDY*srcHeight/dstHeight; aSY -= aDY*srcHeight/dstHeight; aDY = 0; } if (aDWidth <= 0 || aDHeight <= 0 || aSWidth <= 0 || aSHeight <= 0) { return NS_OK; } if ((srcWidth != dstWidth) || (srcHeight != dstHeight)) { GdkPixmap *pixmap = 0; GdkGC *gc = 0; nsRegionGTK clipRgn; switch (mAlphaDepth) { case 8: DrawComposited(aContext, aSurface, srcWidth, srcHeight, dstWidth, dstHeight, dstOrigX, dstOrigY, aDX, aDY, aDWidth, aDHeight); break; case 1: pixmap = gdk_pixmap_new(nsnull, dstWidth, dstHeight, 1); if (pixmap) { XlibRectStretch(srcWidth, srcHeight, dstWidth, dstHeight, 0, 0, 0, 0, dstWidth, dstHeight, mAlphaPixmap, pixmap, s1bitGC, s1bitGC, 1); gc = gdk_gc_new(drawing->GetDrawable()); if (gc) { gdk_gc_set_clip_origin(gc, dstOrigX, dstOrigY); gdk_gc_set_clip_mask(gc, pixmap); } } if (gdk_rgb_get_visual()->depth <= 8) { PRUint8 *scaledRGB = (PRUint8 *)nsMemory::Alloc(3*dstWidth*dstHeight); if (!scaledRGB) return NS_ERROR_OUT_OF_MEMORY; RectStretch(mWidth, mHeight, dstWidth, dstHeight, 0, 0, dstWidth-1, dstHeight-1, mImageBits, mRowBytes, scaledRGB, 3*dstWidth, 24); if (NS_SUCCEEDED(((nsRenderingContextGTK&)aContext).CopyClipRegion(clipRgn))) { // we have both a set of rectangles and a bitmap defining the clip // let X11 clip to the bitmap, do the rectangles by hand nsRegionRectSet *rectSet = nsnull; clipRgn.Intersect(aDX, aDY, aDWidth, aDHeight); clipRgn.GetRects(&rectSet); for (PRUint32 i=0; imRectsLen; i++) { nsRegionRect *rect = &(rectSet->mRects[i]); gdk_draw_rgb_image_dithalign(drawing->GetDrawable(), gc, rect->x, rect->y, rect->width, rect->height, GDK_RGB_DITHER_MAX, scaledRGB + 3*((rect->y-dstOrigY)*dstWidth+(rect->x-dstOrigX)), 3*dstWidth, (rect->x-dstOrigX), (rect->y-dstOrigY)); } clipRgn.FreeRects(rectSet); } else { gdk_draw_rgb_image_dithalign(drawing->GetDrawable(), gc, aDX, aDY, aDWidth, aDHeight, GDK_RGB_DITHER_MAX, scaledRGB + 3*((aDY-dstOrigY)*dstWidth+(aDX-dstOrigX)), 3*dstWidth, (aDX-dstOrigX), (aDY-dstOrigY)); } nsMemory::Free(scaledRGB); } else { if (NS_SUCCEEDED(((nsRenderingContextGTK&)aContext).CopyClipRegion(clipRgn))) { // we have both a set of rectangles and a bitmap defining the clip // let X11 clip to the bitmap, do the rectangles by hand nsRegionRectSet *rectSet = nsnull; clipRgn.Intersect(aDX, aDY, aDWidth, aDHeight); clipRgn.GetRects(&rectSet); for (PRUint32 i=0; imRectsLen; i++) { nsRegionRect *rect = &(rectSet->mRects[i]); XlibRectStretch(srcWidth, srcHeight, dstWidth, dstHeight, dstOrigX, dstOrigY, rect->x, rect->y, rect->width, rect->height, mImagePixmap, drawing->GetDrawable(), gc, sXbitGC, gdk_rgb_get_visual()->depth); } clipRgn.FreeRects(rectSet); } else { // only a mask XlibRectStretch(srcWidth, srcHeight, dstWidth, dstHeight, dstOrigX, dstOrigY, aDX, aDY, aDWidth, aDHeight, mImagePixmap, drawing->GetDrawable(), gc, sXbitGC, gdk_rgb_get_visual()->depth); } } break; case 0: if (!gc) gc = ((nsRenderingContextGTK&)aContext).GetGC(); if (gdk_rgb_get_visual()->depth <= 8) { PRUint8 *scaledRGB = (PRUint8 *)nsMemory::Alloc(3*dstWidth*dstHeight); RectStretch(mWidth, mHeight, dstWidth, dstHeight, 0, 0, dstWidth-1, dstHeight-1, mImageBits, mRowBytes, scaledRGB, 3*dstWidth, 24); gdk_draw_rgb_image_dithalign(drawing->GetDrawable(), gc, aDX, aDY, aDWidth, aDHeight, GDK_RGB_DITHER_MAX, scaledRGB + 3*((aDY-dstOrigY)*dstWidth+(aDX-dstOrigX)), 3*dstWidth, (aDX-dstOrigX), (aDY-dstOrigY)); nsMemory::Free(scaledRGB); } else XlibRectStretch(srcWidth, srcHeight, dstWidth, dstHeight, dstOrigX, dstOrigY, aDX, aDY, aDWidth, aDHeight, mImagePixmap, drawing->GetDrawable(), gc, sXbitGC, gdk_rgb_get_visual()->depth); break; } if (gc) gdk_gc_unref(gc); if (pixmap) gdk_pixmap_unref(pixmap); mFlags = 0; return NS_OK; } // now start drawing... if (mAlphaDepth==8) { DrawComposited(aContext, aSurface, srcWidth, srcHeight, dstWidth, dstHeight, aDX-aSX, aDY-aSY, aDX, aDY, aDWidth, aDHeight); return NS_OK; } GdkGC *copyGC; if (mAlphaPixmap) { copyGC = gdk_gc_new(drawing->GetDrawable()); GdkGC *gc = ((nsRenderingContextGTK&)aContext).GetGC(); gdk_gc_copy(copyGC, gc); gdk_gc_unref(gc); // unref the one we got SetupGCForAlpha(copyGC, aDX-aSX, aDY-aSY); } else { // don't make a copy... we promise not to change it copyGC = ((nsRenderingContextGTK&)aContext).GetGC(); } nsRegionGTK clipRgn; if (mAlphaPixmap && NS_SUCCEEDED(((nsRenderingContextGTK&)aContext).CopyClipRegion(clipRgn))) { // we have both a set of rectangles and a bitmap defining the clip // let X11 clip to the bitmap, do the rectangles by hand nsRegionRectSet *rectSet = nsnull; clipRgn.Intersect(aDX, aDY, aSWidth, aSHeight); clipRgn.GetRects(&rectSet); for (PRUint32 i=0; imRectsLen; i++) { nsRegionRect *rect = &(rectSet->mRects[i]); gdk_window_copy_area(drawing->GetDrawable(), // dest window copyGC, // gc rect->x, // xdest rect->y, // ydest mImagePixmap, // source window aSX+(rect->x-aDX), // xsrc aSY+(rect->y-aDY), // ysrc rect->width, // width rect->height); // height } clipRgn.FreeRects(rectSet); } else { // normal case - let X11 take care of all the clipping gdk_window_copy_area(drawing->GetDrawable(), // dest window copyGC, // gc aDX, // xdest aDY, // ydest mImagePixmap, // source window aSX, // xsrc aSY, // ysrc aSWidth, // width aSHeight); // height } gdk_gc_unref(copyGC); mFlags = 0; return NS_OK; } //------------------------------------------------------------ // 8-bit alpha composite drawing... // Most of this will disappear with gtk+-1.4 // Compositing code consists of these functions: // * findIndex32() - helper function to convert mask into bitshift offset // * findIndex24() - helper function to convert mask into bitshift offset // * nsImageGTK::DrawComposited32() - 32-bit (888) truecolor convert/composite // * nsImageGTK::DrawComposited24() - 24-bit (888) truecolor convert/composite // * nsImageGTK::DrawComposited16() - 16-bit ([56][56][56]) truecolor // convert/composite // * nsImageGTK::DrawCompositedGeneral() - convert/composite for any visual // not handled by the above methods // * nsImageGTK::DrawComposited() - compositing master method; does region // clipping, calls one of the above, then // writes out the composited image static unsigned findIndex32(unsigned mask) { switch (mask) { case 0xff: return 3; case 0xff00: return 2; case 0xff0000: return 1; case 0xff000000: return 0; default: return 0; } } static unsigned findIndex24(unsigned mask) { switch (mask) { case 0xff: return 2; case 0xff00: return 1; case 0xff0000: return 0; default: return 0; } } // 32-bit (888) truecolor convert/composite function void nsImageGTK::DrawComposited32(PRBool isLSB, PRBool flipBytes, PRUint8 *imageOrigin, PRUint32 imageStride, PRUint8 *alphaOrigin, PRUint32 alphaStride, unsigned width, unsigned height, XImage *ximage, unsigned char *readData, unsigned char *srcData) { GdkVisual *visual = gdk_rgb_get_visual(); unsigned redIndex = findIndex32(visual->red_mask); unsigned greenIndex = findIndex32(visual->green_mask); unsigned blueIndex = findIndex32(visual->blue_mask); if (flipBytes^isLSB) { redIndex = 3-redIndex; greenIndex = 3-greenIndex; blueIndex = 3-blueIndex; } // fprintf(stderr, "startX=%u startY=%u activeX=%u activeY=%u\n", // startX, startY, activeX, activeY); // fprintf(stderr, "width=%u height=%u\n", ximage->width, ximage->height); for (unsigned y=0; ybytes_per_line; unsigned char *targetRow = readData + 3*(y*ximage->width); unsigned char *imageRow = imageOrigin + y*imageStride; unsigned char *alphaRow = alphaOrigin + y*alphaStride; for (unsigned i=0; ired_mask); unsigned greenIndex = findIndex24(visual->green_mask); unsigned blueIndex = findIndex24(visual->blue_mask); if (flipBytes^isLSB) { redIndex = 2-redIndex; greenIndex = 2-greenIndex; blueIndex = 2-blueIndex; } for (unsigned y=0; ybytes_per_line; unsigned char *targetRow = readData + 3*(y*ximage->width); unsigned char *imageRow = imageOrigin + y*imageStride; unsigned char *alphaRow = alphaOrigin + y*alphaStride; for (unsigned i=0; ired_prec == 5) ? scaled5 : scaled6; unsigned *greenScale = (visual->green_prec == 5) ? scaled5 : scaled6; unsigned *blueScale = (visual->blue_prec == 5) ? scaled5 : scaled6; for (unsigned y=0; ybytes_per_line; unsigned char *targetRow = readData + 3*(y*ximage->width); unsigned char *imageRow = imageOrigin + y*imageStride; unsigned char *alphaRow = alphaOrigin + y*alphaStride; for (unsigned i=0; ired_mask)>>visual->red_shift], imageRow[0], alpha); MOZ_BLEND(targetRow[1], greenScale[(pix&visual->green_mask)>>visual->green_shift], imageRow[1], alpha); MOZ_BLEND(targetRow[2], blueScale[(pix&visual->blue_mask)>>visual->blue_shift], imageRow[2], alpha); } } } // Generic convert/composite function void nsImageGTK::DrawCompositedGeneral(PRBool isLSB, PRBool flipBytes, PRUint8 *imageOrigin, PRUint32 imageStride, PRUint8 *alphaOrigin, PRUint32 alphaStride, unsigned width, unsigned height, XImage *ximage, unsigned char *readData, unsigned char *srcData) { GdkVisual *visual = gdk_rgb_get_visual(); GdkColormap *colormap = gdk_rgb_get_cmap(); // flip bytes if (flipBytes && (ximage->bits_per_pixel>=16)) { for (int row=0; rowheight; row++) { unsigned char *ptr = srcData + row*ximage->bytes_per_line; if (ximage->bits_per_pixel==24) { // Aurgh.... for (int col=0; colbytes_per_line; col+=(ximage->bits_per_pixel/8)) { unsigned char tmp; tmp = *ptr; *ptr = *(ptr+2); *(ptr+2) = tmp; ptr+=3; } continue; } for (int col=0; colbytes_per_line; col+=(ximage->bits_per_pixel/8)) { unsigned char tmp; switch (ximage->bits_per_pixel) { case 16: tmp = *ptr; *ptr = *(ptr+1); *(ptr+1) = tmp; ptr+=2; break; case 32: tmp = *ptr; *ptr = *(ptr+3); *(ptr+3) = tmp; tmp = *(ptr+1); *(ptr+1) = *(ptr+2); *(ptr+2) = tmp; ptr+=4; break; } } } } unsigned redScale, greenScale, blueScale, redFill, greenFill, blueFill; redScale = 8-visual->red_prec; greenScale = 8-visual->green_prec; blueScale = 8-visual->blue_prec; redFill = 0xff>>visual->red_prec; greenFill = 0xff>>visual->green_prec; blueFill = 0xff>>visual->blue_prec; for (unsigned row=0; rowbytes_per_line; unsigned char *target = readData+3*row*ximage->width; for (unsigned col=0; colbits_per_pixel) { case 1: pix = (*ptr>>(col%8))&1; if ((col%8)==7) ptr++; break; case 4: pix = (col&1)?(*ptr>>4):(*ptr&0xf); if (col&1) ptr++; break; case 8: pix = *ptr++; break; case 16: pix = *((short *)ptr); ptr+=2; break; case 24: if (isLSB) pix = (*(ptr+2)<<16) | (*(ptr+1)<<8) | *ptr; else pix = (*ptr<<16) | (*(ptr+1)<<8) | *(ptr+2); ptr+=3; break; case 32: pix = *((unsigned *)ptr); ptr+=4; break; } switch (visual->type) { case GDK_VISUAL_STATIC_GRAY: case GDK_VISUAL_GRAYSCALE: case GDK_VISUAL_STATIC_COLOR: case GDK_VISUAL_PSEUDO_COLOR: *target++ = colormap->colors[pix].red >>8; *target++ = colormap->colors[pix].green >>8; *target++ = colormap->colors[pix].blue >>8; break; case GDK_VISUAL_DIRECT_COLOR: *target++ = colormap->colors[(pix&visual->red_mask)>>visual->red_shift].red >> 8; *target++ = colormap->colors[(pix&visual->green_mask)>>visual->green_shift].green >> 8; *target++ = colormap->colors[(pix&visual->blue_mask)>>visual->blue_shift].blue >> 8; break; case GDK_VISUAL_TRUE_COLOR: *target++ = redFill|((pix&visual->red_mask)>>visual->red_shift)<green_mask)>>visual->green_shift)<blue_mask)>>visual->blue_shift)<width; unsigned char *imageRow = imageOrigin + y*imageStride; unsigned char *alphaRow = alphaOrigin + y*alphaStride; for (unsigned i=0; iGetDrawable()); Drawable drawable = GDK_WINDOW_XWINDOW(drawing->GetDrawable()); int readX, readY; unsigned readWidth, readHeight, destX, destY; destX = aDX-dstOrigX; destY = aDY-dstOrigY; readX = aDX; readY = aDY; readWidth = aDWidth; readHeight = aDHeight; // fprintf(stderr, "dstOrigX=%d dstOrigY=%d, dstWidth=%u dstHeight=%u\n", dstOrigX, dstOrigY, dstWidth, dstHeight); // fprintf(stderr, "srcWidth=%u srcHeight=%u\n", srcWidth, srcHeight); // fprintf(stderr, "readX=%u readY=%u readWidth=%u readHeight=%u destX=%u destY=%u\n\n", // readX, readY, readWidth, readHeight, destX, destY); XImage *ximage = XGetImage(dpy, drawable, readX, readY, readWidth, readHeight, AllPlanes, ZPixmap); NS_ASSERTION((ximage!=NULL), "XGetImage() failed"); if (!ximage) return; unsigned char *readData = (unsigned char *)nsMemory::Alloc(3*readWidth*readHeight); PRUint8 *scaledImage = 0; PRUint8 *scaledAlpha = 0; PRUint8 *imageOrigin, *alphaOrigin; PRUint32 imageStride, alphaStride; if ((srcWidth!=dstWidth) || (srcHeight!=dstHeight)) { PRUint32 x1, y1, x2, y2; x1 = destX*srcWidth/dstWidth; y1 = destY*srcHeight/dstHeight; x2 = (destX+aDWidth)*srcWidth/dstWidth; y2 = (destY+aDHeight)*srcHeight/dstHeight; scaledImage = (PRUint8 *)nsMemory::Alloc(3*aDWidth*aDHeight); scaledAlpha = (PRUint8 *)nsMemory::Alloc(aDWidth*aDHeight); if (!scaledImage || !scaledAlpha) { XDestroyImage(ximage); nsMemory::Free(readData); if (scaledImage) nsMemory::Free(scaledImage); if (scaledAlpha) nsMemory::Free(scaledAlpha); return; } RectStretch(srcWidth, srcHeight, dstWidth, dstHeight, destX, destY, destX+aDWidth-1, destY+aDHeight-1, mImageBits, mRowBytes, scaledImage, 3*readWidth, 24); RectStretch(srcWidth, srcHeight, dstWidth, dstHeight, destX, destY, destX+aDWidth-1, destY+aDHeight-1, mAlphaBits, mAlphaRowBytes, scaledAlpha, readWidth, 8); imageOrigin = scaledImage; imageStride = 3*readWidth; alphaOrigin = scaledAlpha; alphaStride = readWidth; } else { imageOrigin = mImageBits + destY*mRowBytes + 3*destX; imageStride = mRowBytes; alphaOrigin = mAlphaBits + destY*mAlphaRowBytes + destX; alphaStride = mAlphaRowBytes; } PRBool isLSB; unsigned test = 1; isLSB = (((char *)&test)[0]) ? 1 : 0; PRBool flipBytes = ( isLSB && ximage->byte_order != LSBFirst) || (!isLSB && ximage->byte_order == LSBFirst); if ((ximage->bits_per_pixel==32) && (visual->red_prec == 8) && (visual->green_prec == 8) && (visual->blue_prec == 8)) DrawComposited32(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data); else if ((ximage->bits_per_pixel==24) && (visual->red_prec == 8) && (visual->green_prec == 8) && (visual->blue_prec == 8)) DrawComposited24(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data); else if ((ximage->bits_per_pixel==16) && ((visual->red_prec == 5) || (visual->red_prec == 6)) && ((visual->green_prec == 5) || (visual->green_prec == 6)) && ((visual->blue_prec == 5) || (visual->blue_prec == 6))) DrawComposited16(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data); else DrawCompositedGeneral(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data); GdkGC *imageGC = ((nsRenderingContextGTK&)aContext).GetGC(); gdk_draw_rgb_image(drawing->GetDrawable(), imageGC, readX, readY, readWidth, readHeight, GDK_RGB_DITHER_MAX, readData, 3*readWidth); gdk_gc_unref(imageGC); XDestroyImage(ximage); nsMemory::Free(readData); if (scaledImage) nsMemory::Free(scaledImage); if (scaledAlpha) nsMemory::Free(scaledAlpha); mFlags = 0; } void nsImageGTK::DrawCompositeTile(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) { if ((aDWidth==0) || (aDHeight==0)) return; nsDrawingSurfaceGTK* drawing = (nsDrawingSurfaceGTK*) aSurface; GdkVisual *visual = gdk_rgb_get_visual(); Display *dpy = GDK_WINDOW_XDISPLAY(drawing->GetDrawable()); Drawable drawable = GDK_WINDOW_XWINDOW(drawing->GetDrawable()); // I hate clipping... PRUint32 surfaceWidth, surfaceHeight; drawing->GetDimensions(&surfaceWidth, &surfaceHeight); int readX, readY; unsigned readWidth, readHeight; PRInt32 destX, destY; if ((aDY>=(int)surfaceHeight) || (aDX>=(int)surfaceWidth) || (aDY+aDHeight<=0) || (aDX+aDWidth<=0)) { // This should never happen if the layout engine is sane, // as it means we're trying to draw an image which is outside // the drawing surface. Bulletproof gfx for now... return; } if (aDX<0) { readX = 0; readWidth = aDWidth+aDX; destX = aSX-aDX; } else { readX = aDX; readWidth = aDWidth; destX = aSX; } if (aDY<0) { readY = 0; readHeight = aDHeight+aDY; destY = aSY-aDY; } else { readY = aDY; readHeight = aDHeight; destY = aSY; } if (readX+readWidth > surfaceWidth) readWidth = surfaceWidth-readX; if (readY+readHeight > surfaceHeight) readHeight = surfaceHeight-readY; if ((readHeight <= 0) || (readWidth <= 0)) return; XImage *ximage = XGetImage(dpy, drawable, readX, readY, readWidth, readHeight, AllPlanes, ZPixmap); NS_ASSERTION((ximage!=NULL), "XGetImage() failed"); if (!ximage) return; unsigned char *readData = (unsigned char *)nsMemory::Alloc(3*readWidth*readHeight); PRBool isLSB; unsigned test = 1; isLSB = (((char *)&test)[0]) ? 1 : 0; PRBool flipBytes = ( isLSB && ximage->byte_order != LSBFirst) || (!isLSB && ximage->byte_order == LSBFirst); PRUint8 *imageOrigin, *alphaOrigin; PRUint32 imageStride, alphaStride; PRUint32 compX, compY; PRUint8 *compTarget, *compSource; imageStride = mRowBytes; alphaStride = mAlphaRowBytes; if (destX==mWidth) destX = 0; if (destY==mHeight) destY = 0; for (unsigned y=0; ywidth; compSource = (unsigned char *)ximage->data + y*ximage->bytes_per_line; for (unsigned x=0; xbits_per_pixel==32) && (visual->red_prec == 8) && (visual->green_prec == 8) && (visual->blue_prec == 8)) DrawComposited32(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, compX, compY, ximage, compTarget, compSource); else if ((ximage->bits_per_pixel==24) && (visual->red_prec == 8) && (visual->green_prec == 8) && (visual->blue_prec == 8)) DrawComposited24(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, compX, compY, ximage, compTarget, compSource); else if ((ximage->bits_per_pixel==16) && ((visual->red_prec == 5) || (visual->red_prec == 6)) && ((visual->green_prec == 5) || (visual->green_prec == 6)) && ((visual->blue_prec == 5) || (visual->blue_prec == 6))) DrawComposited16(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, compX, compY, ximage, compTarget, compSource); else DrawCompositedGeneral(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, compX, compY, ximage, compTarget, compSource); compTarget += 3*compX; compSource += (ximage->bits_per_pixel*compX)/8; } } GdkGC *imageGC = ((nsRenderingContextGTK&)aContext).GetGC(); gdk_draw_rgb_image(drawing->GetDrawable(), imageGC, readX, readY, readWidth, readHeight, GDK_RGB_DITHER_MAX, readData, 3*readWidth); gdk_gc_unref(imageGC); XDestroyImage(ximage); nsMemory::Free(readData); mFlags = 0; } void nsImageGTK::CreateOffscreenPixmap(PRInt32 aWidth, PRInt32 aHeight) { // Render unique image bits onto an off screen pixmap only once // The image bits can change as a result of ImageUpdated() - for // example: animated GIFs. if (!mImagePixmap) { #ifdef TRACE_IMAGE_ALLOCATION printf("nsImageGTK::Draw(this=%p) gdk_pixmap_new(nsnull,width=%d,height=%d,depth=%d)\n", this, aWidth, aHeight, mDepth); #endif // Create an off screen pixmap to hold the image bits. mImagePixmap = gdk_pixmap_new(nsnull, aWidth, aHeight, gdk_rgb_get_visual()->depth); #ifdef MOZ_WIDGET_GTK2 gdk_drawable_set_colormap(GDK_DRAWABLE(mImagePixmap), gdk_rgb_get_colormap()); #endif } // Ditto for the clipmask if ((!mAlphaPixmap) && (mAlphaDepth==1)) { mAlphaPixmap = gdk_pixmap_new(nsnull, aWidth, aHeight, 1); // Need an XImage for clipmask updates (XPutImage) mAlphaXImage = XCreateImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap), GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()), 1, /* visual depth...1 for bitmaps */ XYPixmap, 0, /* x offset, XXX fix this */ (char *)mAlphaBits, /* cast away our sign. */ aWidth, aHeight, 32,/* bitmap pad */ mAlphaRowBytes); /* bytes per line */ mAlphaXImage->bits_per_pixel=1; /* Image library always places pixels left-to-right MSB to LSB */ mAlphaXImage->bitmap_bit_order = MSBFirst; /* This definition doesn't depend on client byte ordering because the image library ensures that the bytes in bitmask data are arranged left to right on the screen, low to high address in memory. */ mAlphaXImage->byte_order = MSBFirst; if (!s1bitGC) { GdkColor fg = { 1, 0, 0, 0 }; s1bitGC = gdk_gc_new(mAlphaPixmap); gdk_gc_set_foreground(s1bitGC, &fg); } } if (!sXbitGC) sXbitGC = gdk_gc_new(mImagePixmap); } void nsImageGTK::SetupGCForAlpha(GdkGC *aGC, PRInt32 aX, PRInt32 aY) { // XXX should use (different?) GC cache here if (mAlphaPixmap) { // Setup gc to use the given alpha-pixmap for clipping XGCValues xvalues; memset(&xvalues, 0, sizeof(XGCValues)); unsigned long xvalues_mask = 0; xvalues.clip_x_origin = aX; xvalues.clip_y_origin = aY; xvalues_mask = GCClipXOrigin | GCClipYOrigin; xvalues.clip_mask = GDK_WINDOW_XWINDOW(mAlphaPixmap); xvalues_mask |= GCClipMask; XChangeGC(GDK_DISPLAY(), GDK_GC_XGC(aGC), xvalues_mask, &xvalues); } } // Draw the bitmap, this draw just has destination coordinates NS_IMETHODIMP nsImageGTK::Draw(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) { #ifdef TRACE_IMAGE_ALLOCATION printf("nsImageGTK::Draw(this=%p,x=%d,y=%d,width=%d,height=%d)\n", this, aX, aY, aWidth, aHeight); #endif return Draw(aContext, aSurface, 0, 0, mWidth, mHeight, aX, aY, mWidth, mHeight); } /* inline */ void nsImageGTK::TilePixmap(GdkPixmap *src, GdkPixmap *dest, PRInt32 aSXOffset, PRInt32 aSYOffset, const nsRect &destRect, const nsRect &clipRect, PRBool useClip) { GdkGC *gc; GdkGCValues values; GdkGCValuesMask valuesMask; memset(&values, 0, sizeof(GdkGCValues)); values.fill = GDK_TILED; values.tile = src; values.ts_x_origin = destRect.x - aSXOffset; values.ts_y_origin = destRect.y - aSYOffset; valuesMask = GdkGCValuesMask(GDK_GC_FILL | GDK_GC_TILE | GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN); gc = gdk_gc_new_with_values(src, &values, valuesMask); if (useClip) { GdkRectangle gdkrect = {clipRect.x, clipRect.y, clipRect.width, clipRect.height}; gdk_gc_set_clip_rectangle(gc, &gdkrect); } // draw to destination window #ifdef DEBUG_TILING printf("nsImageGTK::TilePixmap(..., %d, %d, %d, %d)\n", destRect.x, destRect.y, destRect.width, destRect.height); #endif gdk_draw_rectangle(dest, gc, PR_TRUE, destRect.x, destRect.y, destRect.width, destRect.height); gdk_gc_unref(gc); } NS_IMETHODIMP nsImageGTK::DrawTile(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aSXOffset, PRInt32 aSYOffset, PRInt32 aPadX, PRInt32 aPadY, const nsRect &aTileRect) { #ifdef DEBUG_TILING printf("nsImageGTK::DrawTile: mWidth=%d, mHeight=%d\n", mWidth, mHeight); printf("nsImageGTK::DrawTile((src: %d, %d), (tile: %d,%d, %d, %d) %p\n", aSXOffset, aSYOffset, aTileRect.x, aTileRect.y, aTileRect.width, aTileRect.height, this); #endif if (mPendingUpdate) UpdateCachedImage(); if ((mAlphaDepth==1) && mIsSpacer) return NS_OK; if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1) return NS_OK; nsDrawingSurfaceGTK *drawing = (nsDrawingSurfaceGTK*)aSurface; PRBool partial = PR_FALSE; PRInt32 validX = 0, validY = 0, validWidth = mWidth, validHeight = mHeight; // limit the image rectangle to the size of the image data which // has been validated. if (mDecodedY2 < mHeight) { validHeight = mDecodedY2 - mDecodedY1; partial = PR_TRUE; } if (mDecodedX2 < mWidth) { validWidth = mDecodedX2 - mDecodedX1; partial = PR_TRUE; } if (mDecodedY1 > 0) { validHeight -= mDecodedY1; validY = mDecodedY1; partial = PR_TRUE; } if (mDecodedX1 > 0) { validWidth -= mDecodedX1; validX = mDecodedX1; partial = PR_TRUE; } if (aTileRect.width == 0 || aTileRect.height == 0 || validWidth == 0 || validHeight == 0) { return NS_OK; } if (partial || (mAlphaDepth == 8) || (aPadX || aPadY)) { PRInt32 aY0 = aTileRect.y - aSYOffset, aX0 = aTileRect.x - aSXOffset, aY1 = aTileRect.y + aTileRect.height, aX1 = aTileRect.x + aTileRect.width; // Set up clipping and call Draw(). PRBool clipState; aContext.PushState(); ((nsRenderingContextGTK&)aContext).SetClipRectInPixels( aTileRect, nsClipCombine_kIntersect, clipState); ((nsRenderingContextGTK&)aContext).UpdateGC(); if (mAlphaDepth==8) { DrawCompositeTile(aContext, aSurface, aSXOffset, aSYOffset, mWidth, mHeight, aTileRect.x, aTileRect.y, aTileRect.width, aTileRect.height); } else { #ifdef DEBUG_TILING printf("Warning: using slow tiling\n"); #endif for (PRInt32 y = aY0; y < aY1; y += mHeight + aPadY) for (PRInt32 x = aX0; x < aX1; x += mWidth + aPadX) Draw(aContext,aSurface, x,y, PR_MIN(validWidth, aX1-x), PR_MIN(validHeight, aY1-y)); } aContext.PopState(clipState); return NS_OK; } if (mAlphaDepth == 1) { GdkPixmap *tileImg; GdkPixmap *tileMask; nsRect tmpRect(0,0,aTileRect.width, aTileRect.height); tileImg = gdk_pixmap_new(nsnull, aTileRect.width, aTileRect.height, drawing->GetDepth()); #ifdef MOZ_WIDGET_GTK2 gdk_drawable_set_colormap(GDK_DRAWABLE(tileImg), gdk_rgb_get_colormap()); #endif TilePixmap(mImagePixmap, tileImg, aSXOffset, aSYOffset, tmpRect, tmpRect, PR_FALSE); // tile alpha mask tileMask = gdk_pixmap_new(nsnull, aTileRect.width, aTileRect.height, mAlphaDepth); TilePixmap(mAlphaPixmap, tileMask, aSXOffset, aSYOffset, tmpRect, tmpRect, PR_FALSE); GdkGC *fgc = gdk_gc_new(drawing->GetDrawable()); gdk_gc_set_clip_mask(fgc, (GdkBitmap*)tileMask); gdk_gc_set_clip_origin(fgc, aTileRect.x, aTileRect.y); // and copy it back gdk_window_copy_area(drawing->GetDrawable(), fgc, aTileRect.x, aTileRect.y, tileImg, 0, 0, aTileRect.width, aTileRect.height); gdk_gc_unref(fgc); gdk_pixmap_unref(tileImg); gdk_pixmap_unref(tileMask); } else { // In the non-alpha case, gdk can tile for us nsRect clipRect; PRBool isValid; aContext.GetClipRect(clipRect, isValid); TilePixmap(mImagePixmap, drawing->GetDrawable(), aSXOffset, aSYOffset, aTileRect, clipRect, PR_FALSE); } mFlags = 0; return NS_OK; } //------------------------------------------------------------ nsresult nsImageGTK::Optimize(nsIDeviceContext* aContext) { if (!mOptimized) UpdateCachedImage(); if ((gdk_rgb_get_visual()->depth > 8) && (mAlphaDepth != 8)) { if(nsnull != mImageBits) { delete[] mImageBits; mImageBits = nsnull; } if (nsnull != mAlphaBits) { delete[] mAlphaBits; mAlphaBits = nsnull; } } if (mTrueAlphaBits) { delete[] mTrueAlphaBits; mTrueAlphaBits = nsnull; } if ((mAlphaDepth==0) && mAlphaPixmap) { gdk_pixmap_unref(mAlphaPixmap); mAlphaPixmap = nsnull; } mOptimized = PR_TRUE; return NS_OK; } //------------------------------------------------------------ // lock the image pixels NS_IMETHODIMP nsImageGTK::LockImagePixels(PRBool aMaskPixels) { if (!mOptimized) return NS_OK; if (aMaskPixels) { if (mAlphaDepth != 1 || !mAlphaPixmap) return NS_OK; XImage *xmask = XGetImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap), GDK_WINDOW_XWINDOW(mAlphaPixmap), 0, 0, mWidth, mHeight, AllPlanes, XYPixmap); mAlphaBits = new PRUint8[mAlphaRowBytes * mHeight]; memset(mAlphaBits, 0, mAlphaRowBytes * mHeight); for (PRInt32 y = 0; y < mHeight; ++y) { PRUint8 *alphaTarget = mAlphaBits + y*mAlphaRowBytes; PRUint32 alphaBitPos = 7; for (PRInt32 x = 0; x < mWidth; ++x) { *alphaTarget |= (XGetPixel(xmask, x, y) << alphaBitPos); if (alphaBitPos-- == 0) { ++alphaTarget; alphaBitPos = 7; } } } XDestroyImage(xmask); return NS_OK; } if (!mImagePixmap) return NS_OK; XImage *ximage, *xmask=0; unsigned pix; ximage = XGetImage(GDK_WINDOW_XDISPLAY(mImagePixmap), GDK_WINDOW_XWINDOW(mImagePixmap), 0, 0, mWidth, mHeight, AllPlanes, ZPixmap); if ((mAlphaDepth==1) && mAlphaPixmap) xmask = XGetImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap), GDK_WINDOW_XWINDOW(mAlphaPixmap), 0, 0, mWidth, mHeight, AllPlanes, XYPixmap); mImageBits = (PRUint8*) new PRUint8[mSizeImage]; GdkVisual *visual = gdk_rgb_get_visual(); GdkColormap *colormap = gdk_rgb_get_cmap(); unsigned redScale, greenScale, blueScale, redFill, greenFill, blueFill; redScale = 8 - visual->red_prec; greenScale = 8 - visual->green_prec; blueScale = 8 - visual->blue_prec; redFill = 0xff >> visual->red_prec; greenFill = 0xff >> visual->green_prec; blueFill = 0xff >> visual->blue_prec; /* read back the image in the slowest (but simplest) way possible... */ for (PRInt32 y=0; ytype) { case GDK_VISUAL_STATIC_GRAY: case GDK_VISUAL_GRAYSCALE: case GDK_VISUAL_STATIC_COLOR: case GDK_VISUAL_PSEUDO_COLOR: *target++ = colormap->colors[pix].red >>8; *target++ = colormap->colors[pix].green >>8; *target++ = colormap->colors[pix].blue >>8; break; case GDK_VISUAL_DIRECT_COLOR: *target++ = colormap->colors[(pix&visual->red_mask)>>visual->red_shift].red >> 8; *target++ = colormap->colors[(pix&visual->green_mask)>>visual->green_shift].green >> 8; *target++ = colormap->colors[(pix&visual->blue_mask)>>visual->blue_shift].blue >> 8; break; case GDK_VISUAL_TRUE_COLOR: *target++ = redFill|((pix&visual->red_mask)>>visual->red_shift)<green_mask)>>visual->green_shift)<blue_mask)>>visual->blue_shift)<= dest->mWidth || aDY >= dest->mHeight) return NS_OK; PRUint8 *rgbPtr=0, *alphaPtr=0; PRUint32 rgbStride, alphaStride; rgbPtr = mImageBits; rgbStride = mRowBytes; alphaPtr = mAlphaBits; alphaStride = mAlphaRowBytes; PRInt32 y; PRInt32 ValidWidth = ( aDWidth < ( dest->mWidth - aDX ) ) ? aDWidth : ( dest->mWidth - aDX ); PRInt32 ValidHeight = ( aDHeight < ( dest->mHeight - aDY ) ) ? aDHeight : ( dest->mHeight - aDY ); // now composite the two images together switch (mAlphaDepth) { case 1: { PRUint8 *dst = dest->mImageBits + aDY*dest->mRowBytes + 3*aDX; PRUint8 *dstAlpha = dest->mAlphaBits + aDY*dest->mAlphaRowBytes; PRUint8 *src = rgbPtr; PRUint8 *alpha = alphaPtr; PRUint8 offset = aDX & 0x7; // x starts at 0 int iterations = (ValidWidth+7)/8; // round up for (y=0; y= ValidWidth) { alphaPixels &= 0xff << (8 - (ValidWidth-x)); // no, mask off unused if (alphaPixels == 0) continue; // no 1 alpha pixels left } if (offset == 0) { dstAlpha[(aDX+x)>>3] |= alphaPixels; // the cheap aligned case } else { dstAlpha[(aDX+x)>>3] |= alphaPixels >> offset; // avoid write if no 1's to write - also avoids going past end of array PRUint8 alphaTemp = alphaPixels << (8U - offset); if (alphaTemp & 0xff) dstAlpha[((aDX+x)>>3) + 1] |= alphaTemp; } if (alphaPixels == 0xff) { // fix - could speed up by gathering a run of 0xff's and doing 1 memcpy // all 8 pixels set; copy and jump forward memcpy(dst,src,8*3); continue; } else { // else mix of 1's and 0's in alphaPixels, do 1 bit at a time // Don't go past end of line! PRUint8 *d = dst, *s = src; for (PRUint8 aMask = 1<<7, j = 0; aMask && j < ValidWidth-x; aMask >>= 1, j++) { // if this pixel is opaque then copy into the destination image if (alphaPixels & aMask) { // might be faster with *d++ = *s++ 3 times? d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; // dstAlpha bit already set } d += 3; s += 3; } } } // at end of each line, bump pointers. Use wordy code because of // bug 127455 to avoid possibility of unsigned underflow dst = (dst - 3*8*iterations) + dest->mRowBytes; src = (src - 3*8*iterations) + rgbStride; alpha = (alpha - iterations) + alphaStride; dstAlpha += dest->mAlphaRowBytes; } } break; case 0: default: for (y=0; ymImageBits + (y+aDY)*dest->mRowBytes + 3*aDX, rgbPtr + y*rgbStride, 3*ValidWidth); } nsRect rect(aDX, aDY, ValidWidth, ValidHeight); dest->ImageUpdated(nsnull, 0, &rect); return NS_OK; }