Back to index

lightning-sunbird  0.9+nobinonly
nsImageGTK.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *   Tim Rowley <tor@cs.brown.edu>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 #include <gtk/gtk.h>
00041 #include <gdk/gdkx.h>
00042 
00043 #include "imgScaler.h"
00044 
00045 #include "nsImageGTK.h"
00046 #include "nsRenderingContextGTK.h"
00047 
00048 #include "nspr.h"
00049 
00050 #define IsFlagSet(a,b) ((a) & (b))
00051 
00052 #define NS_GET_BIT(rowptr, x) (rowptr[(x)>>3] &  (1<<(7-(x)&0x7)))
00053 #define NS_SET_BIT(rowptr, x) (rowptr[(x)>>3] |= (1<<(7-(x)&0x7)))
00054 #define NS_CLEAR_BIT(rowptr, x) (rowptr[(x)>>3] &= ~(1<<(7-(x)&0x7)))
00055 
00056 // Defining this will trace the allocation of images.  This includes
00057 // ctor, dtor and update.
00058 // #define TRACE_IMAGE_ALLOCATION
00059 
00060 //#define CHEAP_PERFORMANCE_MEASURMENT 1
00061 
00062 // Define this to see tiling debug output
00063 // #define DEBUG_TILING
00064 
00065 /* XXX we are simply creating a GC and setting its function to Copy.
00066    we shouldn't be doing this every time this method is called.  this creates
00067    way more trips to the server than we should be doing so we are creating a
00068    static one.
00069 */
00070 static GdkGC *s1bitGC = nsnull;
00071 static GdkGC *sXbitGC = nsnull;
00072 
00073 /* XFree86 <= 4.3 has a bug in their stipple code (fbgc.c < 1.13) that
00074    prevents us from doing fast tiling. */
00075 static PRBool sNeedSlowTile = PR_FALSE;
00076 
00077 #ifdef MOZ_WIDGET_GTK2
00078 NS_IMPL_ISUPPORTS2(nsImageGTK, nsIImage, nsIGdkPixbufImage)
00079 #else
00080 NS_IMPL_ISUPPORTS1(nsImageGTK, nsIImage)
00081 #endif
00082 
00083 //------------------------------------------------------------
00084 
00085 nsImageGTK::nsImageGTK()
00086   : mImageBits(nsnull)
00087   , mImagePixmap(nsnull)
00088   , mTrueAlphaBits(nsnull)
00089   , mAlphaBits(nsnull)
00090   , mAlphaPixmap(nsnull)
00091   , mAlphaXImage(nsnull)
00092   , mWidth(0)
00093   , mHeight(0)
00094   , mRowBytes(0)
00095   , mSizeImage(0)
00096   , mDecodedX1(PR_INT32_MAX)
00097   , mDecodedY1(PR_INT32_MAX)
00098   , mDecodedX2(0)
00099   , mDecodedY2(0)
00100   , mAlphaDepth(0)
00101   , mTrueAlphaDepth(0)
00102   , mIsSpacer(PR_TRUE)
00103   , mPendingUpdate(PR_FALSE)
00104   , mDepth(0)
00105   , mOptimized(PR_FALSE)
00106 {
00107 #ifdef TRACE_IMAGE_ALLOCATION
00108   printf("nsImageGTK::nsImageGTK(this=%p)\n",
00109          this);
00110 #endif
00111 }
00112 
00113 //------------------------------------------------------------
00114 
00115 nsImageGTK::~nsImageGTK()
00116 {
00117   if(nsnull != mImageBits) {
00118     free(mImageBits);
00119     mImageBits = nsnull;
00120   }
00121 
00122   if (nsnull != mAlphaBits) {
00123     free(mAlphaBits);
00124     mAlphaBits = nsnull;
00125   }
00126 
00127   if (nsnull != mTrueAlphaBits) {
00128     free(mTrueAlphaBits);
00129     mTrueAlphaBits = nsnull;
00130   }
00131 
00132   if (mAlphaPixmap) {
00133     gdk_pixmap_unref(mAlphaPixmap);
00134   }
00135 
00136   if (mImagePixmap) {
00137     gdk_pixmap_unref(mImagePixmap);
00138   }
00139 
00140   if (mAlphaXImage) {
00141     mAlphaXImage->data = 0;
00142     XDestroyImage(mAlphaXImage);
00143   }
00144 
00145 #ifdef TRACE_IMAGE_ALLOCATION
00146   printf("nsImageGTK::~nsImageGTK(this=%p)\n",
00147          this);
00148 #endif
00149 }
00150 
00151 /* static */ void
00152 nsImageGTK::Startup()
00153 {
00154   Display *dpy = GDK_DISPLAY();
00155 
00156   if (strstr(ServerVendor(dpy), "XFree86") && VendorRelease(dpy) < 40400000)
00157     sNeedSlowTile = PR_TRUE;
00158 }
00159 
00160 /* static */ void
00161 nsImageGTK::Shutdown()
00162 {
00163   if (s1bitGC) {
00164     gdk_gc_unref(s1bitGC);
00165     s1bitGC = nsnull;
00166   }
00167   if (sXbitGC) {
00168     gdk_gc_unref(sXbitGC);
00169     sXbitGC = nsnull;
00170   }
00171 }
00172 
00173 //------------------------------------------------------------
00174 
00175 nsresult nsImageGTK::Init(PRInt32 aWidth, PRInt32 aHeight,
00176                           PRInt32 aDepth, nsMaskRequirements aMaskRequirements)
00177 {
00178   // Assumed: Init only gets called once by gfxIImageFrame
00179   g_return_val_if_fail ((aWidth != 0) || (aHeight != 0), NS_ERROR_FAILURE);
00180 
00181   // X Protocol limits us to image dimensions less than 32767
00182   // unless we want to go through lots of pain and suffering.
00183   if (aWidth > SHRT_MAX || aHeight > SHRT_MAX)
00184     return NS_ERROR_FAILURE;
00185 
00186   if (24 == aDepth) {
00187     mNumBytesPixel = 3;
00188   } else {
00189     NS_ASSERTION(PR_FALSE, "unexpected image depth");
00190     return NS_ERROR_UNEXPECTED;
00191   }
00192 
00193   mWidth = aWidth;
00194   mHeight = aHeight;
00195   mDepth = aDepth;
00196 
00197 #ifdef TRACE_IMAGE_ALLOCATION
00198   printf("nsImageGTK::Init(this=%p,%d,%d,%d,%d)\n",
00199          this,
00200          aWidth,
00201          aHeight,
00202          aDepth,
00203          aMaskRequirements);
00204 #endif
00205 
00206   // create the memory for the image
00207   ComputeMetrics();
00208 
00209   mImageBits = (PRUint8*)malloc(mSizeImage);
00210   if (!mImageBits)
00211     return NS_ERROR_OUT_OF_MEMORY;
00212 
00213   switch(aMaskRequirements)
00214   {
00215     case nsMaskRequirements_kNeeds8Bit:
00216       mTrueAlphaRowBytes = aWidth;
00217       mTrueAlphaDepth = 8;
00218 
00219       // 32-bit align each row
00220       mTrueAlphaRowBytes = (mTrueAlphaRowBytes + 3) & ~0x3;
00221       mTrueAlphaBits = (PRUint8*)calloc(mTrueAlphaRowBytes * aHeight, 1);
00222       if (!mTrueAlphaBits)
00223         return NS_ERROR_OUT_OF_MEMORY;
00224 
00225       // FALL THROUGH
00226 
00227     case nsMaskRequirements_kNeeds1Bit:
00228       mAlphaRowBytes = (aWidth + 7) / 8;
00229       mAlphaDepth = 1;
00230 
00231       // 32-bit align each row
00232       mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3;
00233 
00234       mAlphaBits = (PRUint8*)calloc(mAlphaRowBytes * aHeight, 1);
00235       if (!mAlphaBits)
00236         return NS_ERROR_OUT_OF_MEMORY;
00237       break;
00238 
00239     default:
00240       break; // avoid compiler warning
00241   }
00242 
00243   if (aMaskRequirements == nsMaskRequirements_kNeeds8Bit)
00244     mAlphaDepth = 0;
00245   
00246   return NS_OK;
00247 }
00248 
00249 //------------------------------------------------------------
00250 
00251 PRInt32 nsImageGTK::GetHeight()
00252 {
00253   return mHeight;
00254 }
00255 
00256 PRInt32 nsImageGTK::GetWidth()
00257 {
00258   return mWidth;
00259 }
00260 
00261 PRUint8 *nsImageGTK::GetBits()
00262 {
00263   return mImageBits;
00264 }
00265 
00266 void *nsImageGTK::GetBitInfo()
00267 {
00268   return nsnull;
00269 }
00270 
00271 PRInt32 nsImageGTK::GetLineStride()
00272 {
00273   return mRowBytes;
00274 }
00275 
00276 nsColorMap *nsImageGTK::GetColorMap()
00277 {
00278   return nsnull;
00279 }
00280 
00281 PRUint8 *nsImageGTK::GetAlphaBits()
00282 {
00283   if (mTrueAlphaBits)
00284     return mTrueAlphaBits;
00285   else
00286     return mAlphaBits;
00287 }
00288 
00289 PRInt32
00290 nsImageGTK::GetAlphaLineStride()
00291 {
00292   if (mTrueAlphaBits)
00293     return mTrueAlphaRowBytes;
00294   else
00295     return mAlphaRowBytes;
00296 }
00297 
00298 void nsImageGTK::ImageUpdated(nsIDeviceContext *aContext,
00299                               PRUint8 aFlags,
00300                               nsRect *aUpdateRect)
00301 {
00302   mPendingUpdate = PR_TRUE;
00303   mUpdateRegion.Or(mUpdateRegion, *aUpdateRect);
00304 
00305   mDecodedX1 = PR_MIN(mDecodedX1, aUpdateRect->x);
00306   mDecodedY1 = PR_MIN(mDecodedY1, aUpdateRect->y);
00307 
00308   if (aUpdateRect->YMost() > mDecodedY2)
00309     mDecodedY2 = aUpdateRect->YMost();
00310   if (aUpdateRect->XMost() > mDecodedX2)
00311     mDecodedX2 = aUpdateRect->XMost();
00312 }
00313 
00317 PRBool nsImageGTK::GetIsImageComplete() {
00318   return mDecodedX1 == 0 &&
00319          mDecodedY1 == 0 &&
00320          mDecodedX2 == mWidth &&
00321          mDecodedY2 == mHeight;
00322 }
00323 
00324 void nsImageGTK::UpdateCachedImage()
00325 {
00326 #ifdef TRACE_IMAGE_ALLOCATION
00327   printf("nsImageGTK::ImageUpdated(this=%p)\n",
00328          this);
00329 #endif
00330 
00331   nsRegionRectIterator ri(mUpdateRegion);
00332   const nsRect *rect;
00333 
00334   while ((rect = ri.Next()) != nsnull) {
00335 
00336 //  fprintf(stderr, "ImageUpdated %p x,y=(%d %d) width,height=(%d %d)\n",
00337 //          this, rect->x, rect->y, rect->width, rect->height);
00338 
00339     unsigned bottom, left, right;
00340     bottom = rect->y + rect->height;
00341     left   = rect->x;
00342     right  = left + rect->width;
00343 
00344     // check if the image has an all-opaque 8-bit alpha mask
00345     if ((mTrueAlphaDepth==8) && (mAlphaDepth<mTrueAlphaDepth)) {
00346       for (unsigned y=rect->y; 
00347            (y<bottom) && (mAlphaDepth<mTrueAlphaDepth); 
00348            y++) {
00349         unsigned char *alpha = mTrueAlphaBits + mTrueAlphaRowBytes*y + left;
00350         unsigned char *mask = mAlphaBits + mAlphaRowBytes*y;
00351         for (unsigned x=left; x<right; x++) {
00352           switch (*(alpha++)) {
00353           case 255:
00354             NS_SET_BIT(mask,x);
00355             break;
00356           case 0:
00357             NS_CLEAR_BIT(mask,x);
00358             if (mAlphaDepth == 0) {
00359               mAlphaDepth=1;
00360 
00361               // promoting an image from no alpha channel to 1-bit, so
00362               // we need to create/clear the alpha pixmap
00363               CreateOffscreenPixmap(mWidth, mHeight);
00364 
00365               XFillRectangle(GDK_WINDOW_XDISPLAY(mAlphaPixmap),
00366                              GDK_WINDOW_XWINDOW(mAlphaPixmap),
00367                              GDK_GC_XGC(s1bitGC),
00368                              mDecodedX1, mDecodedY1,
00369                              mDecodedX2 - mDecodedX1 + 1,
00370                              mDecodedY2 - mDecodedY1 + 1);
00371             }
00372             break;
00373           default:
00374             mAlphaDepth=8;
00375             break;
00376           }
00377         }
00378       }
00379       
00380       if (mAlphaDepth==8) {
00381         if (mImagePixmap) {
00382           gdk_pixmap_unref(mImagePixmap);
00383           mImagePixmap = 0;
00384         }
00385         if (mAlphaPixmap) {
00386           gdk_pixmap_unref(mAlphaPixmap);
00387           mAlphaPixmap = 0;
00388         }
00389         if (mAlphaBits) {
00390           free(mAlphaBits);
00391           mAlphaBits = mTrueAlphaBits;
00392           mAlphaRowBytes = mTrueAlphaRowBytes;
00393           mTrueAlphaBits = 0;
00394         }
00395       }
00396     }
00397 
00398     // check if the image is a spacer
00399     if ((mAlphaDepth==1) && mIsSpacer) {
00400       // mask of the leading/trailing bits in the update region
00401       PRUint8  leftmask   = 0xff  >> (left & 0x7);
00402       PRUint8  rightmask  = 0xff  << (7 - ((right-1) & 0x7));
00403 
00404       // byte where the first/last bits of the update region are located
00405       PRUint32 leftindex  = left      >> 3;
00406       PRUint32 rightindex = (right-1) >> 3;
00407 
00408       // first/last bits in the same byte - combine mask into leftmask
00409       // and fill rightmask so we don't try using it
00410       if (leftindex == rightindex) {
00411         leftmask &= rightmask;
00412         rightmask = 0xff;
00413       }
00414 
00415       // check the leading bits
00416       if (leftmask != 0xff) {
00417         PRUint8 *ptr = mAlphaBits + mAlphaRowBytes * rect->y + leftindex;
00418         for (unsigned y=rect->y; y<bottom; y++, ptr+=mAlphaRowBytes) {
00419           if (*ptr & leftmask) {
00420             mIsSpacer = PR_FALSE;
00421             break;
00422           }
00423         }
00424         // move to first full byte
00425         leftindex++;
00426       }
00427 
00428       // check the trailing bits
00429       if (mIsSpacer && (rightmask != 0xff)) {
00430         PRUint8 *ptr = mAlphaBits + mAlphaRowBytes * rect->y + rightindex;
00431         for (unsigned y=rect->y; y<bottom; y++, ptr+=mAlphaRowBytes) {
00432           if (*ptr & rightmask) {
00433             mIsSpacer = PR_FALSE;
00434             break;
00435           }
00436         }
00437         // move to last full byte
00438         rightindex--;
00439       }
00440     
00441       // check the middle bytes
00442       if (mIsSpacer && (leftindex <= rightindex)) {
00443         for (unsigned y=rect->y; (y<bottom) && mIsSpacer; y++) {
00444           unsigned char *alpha = mAlphaBits + mAlphaRowBytes*y + leftindex;
00445           for (unsigned x=leftindex; x<=rightindex; x++) {
00446             if (*(alpha++)!=0) {
00447               mIsSpacer = PR_FALSE;
00448               break;
00449             }
00450           }
00451         }
00452       }
00453     }
00454 
00455     if (mAlphaDepth != 8) {
00456       CreateOffscreenPixmap(mWidth, mHeight);
00457 
00458       gdk_draw_rgb_image_dithalign(mImagePixmap, sXbitGC, 
00459                                    rect->x, rect->y,
00460                                    rect->width, rect->height,
00461                                    GDK_RGB_DITHER_MAX,
00462                                    mImageBits + mRowBytes*rect->y + 3*rect->x,
00463                                    mRowBytes,
00464                                    0, 0);
00465     }
00466 
00467     if (mAlphaDepth==1) {
00468       XPutImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap),
00469                 GDK_WINDOW_XWINDOW(mAlphaPixmap),
00470                 GDK_GC_XGC(s1bitGC),
00471                 mAlphaXImage,
00472                 rect->x, rect->y, 
00473                 rect->x, rect->y,
00474                 rect->width, rect->height);
00475     }
00476   }
00477   
00478   mUpdateRegion.SetEmpty();
00479   mPendingUpdate = PR_FALSE;
00480   mFlags = nsImageUpdateFlags_kBitsChanged; // this should be 0'd out by Draw()
00481 }
00482 
00483 #ifdef CHEAP_PERFORMANCE_MEASURMENT
00484 static PRTime gConvertTime, gAlphaTime, gCopyStart, gCopyEnd, gStartTime, gPixmapTime, gEndTime;
00485 #endif
00486 
00487 /* Xlib image scaling... */
00488 
00489 #define sign(x) ((x)>0 ? 1:-1)
00490 
00491 static void XlibStretchHorizontal(long x1,long x2,long y1,long y2,
00492                                   long ymin,long ymax,
00493                                   long startColumn, long endColumn,
00494                                   long offsetX, long offsetY,
00495                                   GdkPixmap *aSrcImage, GdkPixmap *aDstImage, GdkGC *gc);
00496 
00497 /**********************************************************
00498  XlibRectStretch enlarges or diminishes a source rectangle of a bitmap to
00499  a destination rectangle. The source rectangle is selected by the two
00500  points (xs1,ys1) and (xs2,ys2), and the destination rectangle by
00501  (xd1,yd1) and (xd2,yd2).
00502 
00503  Entry:
00504        xs1,ys1 - first point of source rectangle
00505        xs2,ys2 - second point of source rectangle
00506        xd1,yd1 - first point of destination rectangle
00507        xd2,yd2 - second point of destination rectangle
00508   offx, offy - offset to target
00509 **********************************************************/
00510 void
00511 XlibRectStretch(PRInt32 srcWidth, PRInt32 srcHeight,
00512                 PRInt32 dstWidth, PRInt32 dstHeight,
00513                 PRInt32 dstOrigX, PRInt32 dstOrigY,
00514                 PRInt32 aDX, PRInt32 aDY,
00515                 PRInt32 aDWidth, PRInt32 aDHeight,
00516                 GdkPixmap *aSrcImage, GdkPixmap *aDstImage,
00517                 GdkGC *gc, GdkGC *copygc, PRInt32 aDepth)
00518 {
00519   long dx,dy,e,d,dx2;
00520   short sx,sy;
00521   GdkPixmap *aTmpImage = 0;
00522   PRBool skipHorizontal=PR_FALSE, skipVertical=PR_FALSE;
00523   long startColumn, startRow, endColumn, endRow;
00524   long xs1, ys1, xs2, ys2, xd1, yd1, xd2, yd2;
00525 
00526   xs1 = ys1 = xd1 = yd1 = 0;
00527   xs2 = srcWidth-1;
00528   ys2 = srcHeight-1;
00529   xd2 = dstWidth-1;
00530   yd2 = dstHeight-1;
00531 
00532 //  fprintf(stderr, "XRS %p (%ld %ld)-(%ld %ld) (%ld %ld)-(%ld %ld)\n",
00533 //          (void *)aDstImage, xs1, ys1, xs2, ys2, xd1, yd1, xd2, yd2);
00534   
00535   startColumn = aDX-dstOrigX;
00536   startRow    = aDY-dstOrigY;
00537   endColumn   = aDX+aDWidth-dstOrigX;
00538   endRow      = aDY+aDHeight-dstOrigY;
00539 
00540 //  fprintf(stderr, "startXY = %d %d  endXY = %d %d   %d x %d\n",
00541 //          startColumn, startRow, endColumn, endRow,
00542 //          endColumn-startColumn, endRow-startRow);
00543 
00544   long scaleStartY, scaleEndY;
00545   scaleStartY = startRow * (ys2-ys1+1) / (yd2-yd1+1);
00546   scaleEndY   = 1 + endRow * (ys2-ys1+1) / (yd2-yd1+1);
00547 
00548   if (xd2-xd1 == xs2-xs1) {
00549 //    fprintf(stderr, "skipping horizontal\n");
00550     skipHorizontal = PR_TRUE;
00551     aTmpImage = aSrcImage;
00552     scaleStartY = 0;
00553     scaleEndY = ys2;
00554   }
00555 
00556   if (yd2-yd1 == ys2-ys1) {
00557 //    fprintf(stderr, "skipping vertical\n");
00558     skipVertical = PR_TRUE;
00559     aTmpImage = aDstImage;
00560   }
00561 
00562   if (skipVertical && skipHorizontal) {
00563     gdk_draw_pixmap(aDstImage, gc, aSrcImage,
00564                     0, 0, srcWidth, srcHeight,
00565                     dstOrigX, dstOrigY);
00566     return;
00567   }
00568 
00569 //  fprintf(stderr, "scaleY Start/End = %d %d\n", scaleStartY, scaleEndY);
00570 
00571   if (!skipHorizontal && !skipVertical) {
00572     aTmpImage = gdk_pixmap_new(nsnull,
00573                                endColumn-startColumn,
00574                                scaleEndY-scaleStartY,
00575                                aDepth);
00576 #ifdef MOZ_WIDGET_GTK2
00577     if (aDepth != 1)
00578       gdk_drawable_set_colormap(GDK_DRAWABLE(aTmpImage),
00579                                 gdk_rgb_get_colormap());
00580 #endif
00581   }
00582  
00583   dx = abs((int)(yd2-yd1));
00584   dy = abs((int)(ys2-ys1));
00585   sx = sign(yd2-yd1);
00586   sy = sign(ys2-ys1);
00587   e = dy-dx;
00588   dx2 = dx;
00589   dy += 1;
00590   if (!dx2) dx2=1;
00591 
00592   if (!skipHorizontal)
00593     XlibStretchHorizontal(xd1, xd2, xs1, xs2, scaleStartY, scaleEndY,
00594                           startColumn, endColumn,
00595                           skipVertical?dstOrigX:-startColumn, skipVertical?dstOrigY:-scaleStartY,
00596                           aSrcImage, aTmpImage, (skipVertical?gc:copygc));
00597   
00598   if (!skipVertical) {
00599     for (d=0; d<=dx; d++) {
00600       if ((yd1 >= startRow) && (yd1 <= endRow)) {
00601         gdk_draw_pixmap(aDstImage, gc, aTmpImage,
00602                         (skipHorizontal?startColumn:0), ys1-scaleStartY,
00603                         aDX, dstOrigY+yd1,
00604                         endColumn-startColumn, 1);
00605       }
00606       while (e>=0) {
00607              ys1 += sy;
00608              e -= dx2;
00609       }
00610       yd1 += sx;
00611       e += dy;
00612     }
00613   }
00614 
00615   if (!skipHorizontal && !skipVertical)
00616     gdk_pixmap_unref(aTmpImage);
00617 }
00618 
00619 /**********************************************************
00620  Stretches a image horizontally by column replication/deletion.
00621  Used by XlibRectStretch.
00622 
00623  Entry:
00624        x1,x2 - x-coordinates of the destination line
00625        y1,y2 - x-coordinates of the source line
00626        ymin  - y-coordinate of top of stretch region
00627        ymax  - y-coordinate of bottom of stretch region
00628 **********************************************************/
00629 static void
00630 XlibStretchHorizontal(long x1, long x2, long y1, long y2,
00631                       long ymin, long ymax,
00632                       long startColumn, long endColumn,
00633                       long offsetX, long offsetY,
00634                       GdkPixmap *aSrcImage, GdkPixmap *aDstImage, GdkGC *gc)
00635 {
00636   long dx,dy,e,d,dx2;
00637   short sx,sy;
00638 
00639   dx = abs((int)(x2-x1));
00640   dy = abs((int)(y2-y1));
00641   sx = sign(x2-x1);
00642   sy = sign(y2-y1);
00643   e = dy-dx;
00644   dx2 = dx;
00645   dy += 1;
00646   if (!dx2) dx2=1;
00647   for (d=0; d<=dx; d++) {
00648     if ((x1 >= startColumn) && (x1 <= endColumn)) {
00649       gdk_draw_pixmap(aDstImage, gc, aSrcImage,
00650                       y1, ymin, x1+offsetX, ymin+offsetY,
00651                       1, ymax-ymin);
00652     }
00653     while (e>=0) {
00654       y1 += sy;
00655       e -= dx2;
00656     }
00657     x1 += sx;
00658     e += dy;
00659   }
00660 }
00661 
00662 #undef sign
00663 
00664 
00665 
00666 // Draw the bitmap, this method has a source and destination coordinates
00667 NS_IMETHODIMP
00668 nsImageGTK::Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface,
00669                  PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight,
00670                  PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight)
00671 {
00672   g_return_val_if_fail ((aSurface != nsnull), NS_ERROR_FAILURE);
00673 
00674   if (mPendingUpdate)
00675     UpdateCachedImage();
00676 
00677   if ((mAlphaDepth==1) && mIsSpacer)
00678     return NS_OK;
00679 
00680   if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
00681     return NS_OK;
00682 
00683 #ifdef TRACE_IMAGE_ALLOCATION
00684   fprintf(stderr, "nsImageGTK::Draw(%p) s=(%4d %4d %4d %4d) d=(%4d %4d %4d %4d)\n",
00685          this,
00686          aSX, aSY, aSWidth, aSHeight,
00687          aDX, aDY, aDWidth, aDHeight);
00688 #endif
00689 
00690   if (aSWidth <= 0 || aDWidth <= 0 || aSHeight <= 0 || aDHeight <= 0) {
00691     return NS_OK;
00692   }
00693 
00694   // store some values we'll need for scaling...
00695 
00696   PRInt32 srcWidth, srcHeight, dstWidth, dstHeight;
00697   PRInt32 dstOrigX, dstOrigY;
00698 
00699   srcWidth = aSWidth;
00700   srcHeight = aSHeight;
00701   dstWidth = aDWidth;
00702   dstHeight = aDHeight;
00703   dstOrigX = aDX;
00704   dstOrigY = aDY;
00705 
00706   // clip to decode region
00707   PRInt32 j = aSX + aSWidth;
00708   PRInt32 z;
00709   if (j > mDecodedX2) {
00710     z = j - mDecodedX2;
00711     aDWidth -= z*dstWidth/srcWidth;
00712     aSWidth -= z;
00713   }
00714   if (aSX < mDecodedX1) {
00715     aDX += (mDecodedX1 - aSX)*dstWidth/srcWidth;
00716     aSX = mDecodedX1;
00717   }
00718 
00719   j = aSY + aSHeight;
00720   if (j > mDecodedY2) {
00721     z = j - mDecodedY2;
00722     aDHeight -= z*dstHeight/srcHeight;
00723     aSHeight -= z;
00724   }
00725   if (aSY < mDecodedY1) {
00726     aDY += (mDecodedY1 - aSY)*dstHeight/srcHeight;
00727     aSY = mDecodedY1;
00728   }
00729 
00730   if (aDWidth <= 0 || aDHeight <= 0 || aSWidth <= 0 || aSHeight <= 0) {
00731     return NS_OK;
00732   }
00733 
00734   // clip to drawing surface
00735   nsDrawingSurfaceGTK *drawing = (nsDrawingSurfaceGTK*)aSurface;
00736   PRUint32 surfaceWidth, surfaceHeight;
00737   drawing->GetDimensions(&surfaceWidth, &surfaceHeight);
00738 
00739   if (aDX + aDWidth > (PRInt32)surfaceWidth) {
00740     z = aDX + aDWidth - surfaceWidth;
00741     aDWidth -= z;
00742     aSWidth -= z*srcWidth/dstWidth;
00743   }
00744 
00745   if (aDX < 0) {
00746     aDWidth += aDX;
00747     aSWidth += aDX*srcWidth/dstWidth;
00748     aSX -= aDX*srcWidth/dstWidth;
00749     aDX = 0;
00750   }
00751 
00752   if (aDY + aDHeight > (PRInt32)surfaceHeight) {
00753     z = aDY + aDHeight - surfaceHeight;
00754     aDHeight -= z;
00755     aSHeight -= z*srcHeight/dstHeight;
00756   }
00757 
00758   if (aDY < 0) {
00759     aDHeight += aDY;
00760     aSHeight += aDY*srcHeight/dstHeight;
00761     aSY -= aDY*srcHeight/dstHeight;
00762     aDY = 0;
00763   }
00764 
00765   if (aDWidth <= 0 || aDHeight <= 0 || aSWidth <= 0 || aSHeight <= 0) {
00766     return NS_OK;
00767   }
00768 
00769   if ((srcWidth != dstWidth) || (srcHeight != dstHeight)) {
00770     GdkPixmap *pixmap = 0;
00771     GdkGC *gc = 0;
00772     nsRegionGTK clipRgn;
00773 
00774     switch (mAlphaDepth) {
00775     case 8:
00776       DrawComposited(aContext, aSurface,
00777                      srcWidth, srcHeight,
00778                      dstWidth, dstHeight,
00779                      dstOrigX, dstOrigY,
00780                      aDX, aDY,
00781                      aDWidth, aDHeight);
00782       break;
00783     case 1:
00784       pixmap = gdk_pixmap_new(nsnull, dstWidth, dstHeight, 1);
00785       if (pixmap) {
00786         XlibRectStretch(srcWidth, srcHeight,
00787                         dstWidth, dstHeight,
00788                         0, 0,
00789                         0, 0,
00790                         dstWidth, dstHeight,
00791                         mAlphaPixmap, pixmap,
00792                         s1bitGC, s1bitGC, 1);
00793         gc = gdk_gc_new(drawing->GetDrawable());
00794         if (gc) {
00795           gdk_gc_set_clip_origin(gc, dstOrigX, dstOrigY);
00796           gdk_gc_set_clip_mask(gc, pixmap);
00797         }
00798       }
00799 
00800       if (gdk_rgb_get_visual()->depth <= 8) {
00801         PRUint8 *scaledRGB = (PRUint8 *)nsMemory::Alloc(3*dstWidth*dstHeight);
00802 
00803         if (!scaledRGB)
00804           return NS_ERROR_OUT_OF_MEMORY;
00805 
00806         RectStretch(mWidth, mHeight,
00807                     dstWidth, dstHeight,
00808                     0, 0, dstWidth-1, dstHeight-1,
00809                     mImageBits, mRowBytes, scaledRGB, 3*dstWidth, 24);
00810 
00811         if (NS_SUCCEEDED(((nsRenderingContextGTK&)aContext).CopyClipRegion(clipRgn))) {
00812           // we have both a set of rectangles and a bitmap defining the clip
00813           // let X11 clip to the bitmap, do the rectangles by hand
00814           nsRegionRectSet *rectSet = nsnull;
00815           clipRgn.Intersect(aDX, aDY, aDWidth, aDHeight);
00816           clipRgn.GetRects(&rectSet);
00817           for (PRUint32 i=0; i<rectSet->mRectsLen; i++) {
00818             nsRegionRect *rect = &(rectSet->mRects[i]);
00819 
00820             gdk_draw_rgb_image_dithalign(drawing->GetDrawable(), gc,
00821                                          rect->x, rect->y, rect->width, rect->height,
00822                                          GDK_RGB_DITHER_MAX, 
00823                                          scaledRGB + 3*((rect->y-dstOrigY)*dstWidth+(rect->x-dstOrigX)),
00824                                          3*dstWidth,
00825                                          (rect->x-dstOrigX), (rect->y-dstOrigY));
00826           }
00827           clipRgn.FreeRects(rectSet);
00828         } else {
00829           gdk_draw_rgb_image_dithalign(drawing->GetDrawable(), gc,
00830                                        aDX, aDY, aDWidth, aDHeight,
00831                                        GDK_RGB_DITHER_MAX, 
00832                                        scaledRGB + 3*((aDY-dstOrigY)*dstWidth+(aDX-dstOrigX)),
00833                                        3*dstWidth,
00834                                        (aDX-dstOrigX), (aDY-dstOrigY));
00835         }
00836         nsMemory::Free(scaledRGB);
00837       } else {
00838         if (NS_SUCCEEDED(((nsRenderingContextGTK&)aContext).CopyClipRegion(clipRgn))) {
00839           // we have both a set of rectangles and a bitmap defining the clip
00840           // let X11 clip to the bitmap, do the rectangles by hand
00841           nsRegionRectSet *rectSet = nsnull;
00842           clipRgn.Intersect(aDX, aDY, aDWidth, aDHeight);
00843           clipRgn.GetRects(&rectSet);
00844           for (PRUint32 i=0; i<rectSet->mRectsLen; i++) {
00845             nsRegionRect *rect = &(rectSet->mRects[i]);
00846             
00847             XlibRectStretch(srcWidth, srcHeight,
00848                             dstWidth, dstHeight,
00849                             dstOrigX, dstOrigY,
00850                             rect->x, rect->y,
00851                             rect->width, rect->height,
00852                             mImagePixmap, drawing->GetDrawable(),
00853                             gc, sXbitGC, gdk_rgb_get_visual()->depth);
00854           }
00855           clipRgn.FreeRects(rectSet);
00856         } else {
00857           // only a mask
00858           XlibRectStretch(srcWidth, srcHeight,
00859                           dstWidth, dstHeight,
00860                           dstOrigX, dstOrigY,
00861                           aDX, aDY,
00862                           aDWidth, aDHeight,
00863                           mImagePixmap, drawing->GetDrawable(),
00864                           gc, sXbitGC, gdk_rgb_get_visual()->depth);
00865         }
00866       }
00867 
00868       break;
00869     case 0:
00870       if (!gc)
00871         gc = ((nsRenderingContextGTK&)aContext).GetGC();
00872 
00873       if (gdk_rgb_get_visual()->depth <= 8) {
00874         PRUint8 *scaledRGB = (PRUint8 *)nsMemory::Alloc(3*dstWidth*dstHeight);
00875         if (!scaledRGB)
00876           break;
00877         RectStretch(mWidth, mHeight,
00878                     dstWidth, dstHeight,
00879                     0, 0, dstWidth-1, dstHeight-1,
00880                     mImageBits, mRowBytes, scaledRGB, 3*dstWidth, 24);
00881     
00882         gdk_draw_rgb_image_dithalign(drawing->GetDrawable(), gc,
00883                                      aDX, aDY, aDWidth, aDHeight,
00884                                      GDK_RGB_DITHER_MAX, 
00885                                      scaledRGB + 3*((aDY-dstOrigY)*dstWidth+(aDX-dstOrigX)),
00886                                      3*dstWidth,
00887                                      (aDX-dstOrigX), (aDY-dstOrigY));
00888 
00889         nsMemory::Free(scaledRGB);
00890       }
00891       else
00892         XlibRectStretch(srcWidth, srcHeight,
00893                         dstWidth, dstHeight,
00894                         dstOrigX, dstOrigY,
00895                         aDX, aDY,
00896                         aDWidth, aDHeight,
00897                         mImagePixmap, drawing->GetDrawable(),
00898                         gc, sXbitGC, gdk_rgb_get_visual()->depth);
00899       break;
00900     }
00901     if (gc)
00902       gdk_gc_unref(gc);
00903     if (pixmap)
00904       gdk_pixmap_unref(pixmap);
00905 
00906     mFlags = 0;
00907     return NS_OK;
00908   }
00909 
00910   // now start drawing...
00911 
00912   if (mAlphaDepth==8) {
00913     DrawComposited(aContext, aSurface, 
00914                    srcWidth, srcHeight,
00915                    dstWidth, dstHeight,
00916                    aDX-aSX, aDY-aSY,
00917                    aDX, aDY,
00918                    aDWidth, aDHeight);
00919     return NS_OK;
00920   }
00921 
00922   GdkGC *copyGC;
00923   if (mAlphaPixmap) {
00924     copyGC = gdk_gc_new(drawing->GetDrawable());
00925     GdkGC *gc = ((nsRenderingContextGTK&)aContext).GetGC();
00926     gdk_gc_copy(copyGC, gc);
00927     gdk_gc_unref(gc); // unref the one we got
00928     
00929     SetupGCForAlpha(copyGC, aDX-aSX, aDY-aSY);
00930   } else {
00931     // don't make a copy... we promise not to change it
00932     copyGC = ((nsRenderingContextGTK&)aContext).GetGC();
00933   }
00934 
00935   nsRegionGTK clipRgn;
00936   if (mAlphaPixmap &&
00937       NS_SUCCEEDED(((nsRenderingContextGTK&)aContext).CopyClipRegion(clipRgn))) {
00938     // we have both a set of rectangles and a bitmap defining the clip
00939     // let X11 clip to the bitmap, do the rectangles by hand
00940     nsRegionRectSet *rectSet = nsnull;
00941     clipRgn.Intersect(aDX, aDY, aSWidth, aSHeight);
00942     clipRgn.GetRects(&rectSet);
00943     for (PRUint32 i=0; i<rectSet->mRectsLen; i++) {
00944       nsRegionRect *rect = &(rectSet->mRects[i]);
00945       gdk_window_copy_area(drawing->GetDrawable(),      // dest window
00946                            copyGC,                      // gc
00947                            rect->x,                     // xdest
00948                            rect->y,                     // ydest
00949                            mImagePixmap,                // source window
00950                            aSX+(rect->x-aDX),           // xsrc
00951                            aSY+(rect->y-aDY),           // ysrc
00952                            rect->width,                 // width
00953                            rect->height);               // height
00954     }
00955     clipRgn.FreeRects(rectSet);
00956   } else {
00957     // normal case - let X11 take care of all the clipping
00958     gdk_window_copy_area(drawing->GetDrawable(),      // dest window
00959                          copyGC,                      // gc
00960                          aDX,                         // xdest
00961                          aDY,                         // ydest
00962                          mImagePixmap,                // source window
00963                          aSX,                         // xsrc
00964                          aSY,                         // ysrc
00965                          aSWidth,                     // width
00966                          aSHeight);                   // height
00967   }
00968  
00969   gdk_gc_unref(copyGC);
00970   mFlags = 0;
00971 
00972   return NS_OK;
00973 }
00974 
00975 //------------------------------------------------------------
00976 // 8-bit alpha composite drawing...
00977 // Most of this will disappear with gtk+-1.4
00978 
00979 // Compositing code consists of these functions:
00980 //  * findIndex32() - helper function to convert mask into bitshift offset
00981 //  * findIndex24() - helper function to convert mask into bitshift offset
00982 //  * nsImageGTK::DrawComposited32() - 32-bit (888) truecolor convert/composite
00983 //  * nsImageGTK::DrawComposited24() - 24-bit (888) truecolor convert/composite
00984 //  * nsImageGTK::DrawComposited16() - 16-bit ([56][56][56]) truecolor 
00985 //                                     convert/composite
00986 //  * nsImageGTK::DrawCompositedGeneral() - convert/composite for any visual
00987 //                                          not handled by the above methods
00988 //  * nsImageGTK::DrawComposited() - compositing master method; does region
00989 //                                   clipping, calls one of the above, then
00990 //                                   writes out the composited image
00991 
00992 static unsigned
00993 findIndex32(unsigned mask)
00994 {
00995   switch (mask) {
00996   case 0xff:
00997     return 3;
00998   case 0xff00:
00999     return 2;
01000   case 0xff0000:
01001     return 1;
01002   case 0xff000000:
01003     return 0;
01004   default:
01005     return 0;
01006   }
01007 }
01008 
01009 static unsigned
01010 findIndex24(unsigned mask)
01011 {
01012   switch (mask) {
01013   case 0xff:
01014     return 2;
01015   case 0xff00:
01016     return 1;
01017   case 0xff0000:
01018     return 0;
01019   default:
01020     return 0;
01021   }
01022 }
01023 
01024 
01025 // 32-bit (888) truecolor convert/composite function
01026 void
01027 nsImageGTK::DrawComposited32(PRBool isLSB, PRBool flipBytes,
01028                              PRUint8 *imageOrigin, PRUint32 imageStride,
01029                              PRUint8 *alphaOrigin, PRUint32 alphaStride,
01030                              unsigned width, unsigned height,
01031                              XImage *ximage, unsigned char *readData, unsigned char *srcData)
01032 {
01033   GdkVisual *visual   = gdk_rgb_get_visual();
01034   unsigned redIndex   = findIndex32(visual->red_mask);
01035   unsigned greenIndex = findIndex32(visual->green_mask);
01036   unsigned blueIndex  = findIndex32(visual->blue_mask);
01037 
01038   if (flipBytes^isLSB) {
01039     redIndex   = 3-redIndex;
01040     greenIndex = 3-greenIndex;
01041     blueIndex  = 3-blueIndex;
01042   }
01043 
01044 //  fprintf(stderr, "startX=%u startY=%u activeX=%u activeY=%u\n",
01045 //          startX, startY, activeX, activeY);
01046 //  fprintf(stderr, "width=%u height=%u\n", ximage->width, ximage->height);
01047 
01048   for (unsigned y=0; y<height; y++) {
01049     unsigned char *baseRow   = srcData     + y*ximage->bytes_per_line;
01050     unsigned char *targetRow = readData    + 3*(y*ximage->width);
01051     unsigned char *imageRow  = imageOrigin + y*imageStride;
01052     unsigned char *alphaRow  = alphaOrigin + y*alphaStride;
01053 
01054     for (unsigned i=0; i<width;
01055          i++, baseRow+=4, targetRow+=3, imageRow+=3, alphaRow++) {
01056       unsigned alpha = *alphaRow;
01057       MOZ_BLEND(targetRow[0], baseRow[redIndex],   imageRow[0], alpha);
01058       MOZ_BLEND(targetRow[1], baseRow[greenIndex], imageRow[1], alpha);
01059       MOZ_BLEND(targetRow[2], baseRow[blueIndex],  imageRow[2], alpha);
01060     }
01061   }
01062 }
01063 
01064 // 24-bit (888) truecolor convert/composite function
01065 void
01066 nsImageGTK::DrawComposited24(PRBool isLSB, PRBool flipBytes,
01067                              PRUint8 *imageOrigin, PRUint32 imageStride,
01068                              PRUint8 *alphaOrigin, PRUint32 alphaStride,
01069                              unsigned width, unsigned height,
01070                              XImage *ximage, unsigned char *readData, unsigned char *srcData)
01071 {
01072   GdkVisual *visual   = gdk_rgb_get_visual();
01073   unsigned redIndex   = findIndex24(visual->red_mask);
01074   unsigned greenIndex = findIndex24(visual->green_mask);
01075   unsigned blueIndex  = findIndex24(visual->blue_mask);
01076 
01077   if (flipBytes^isLSB) {
01078     redIndex   = 2-redIndex;
01079     greenIndex = 2-greenIndex;
01080     blueIndex  = 2-blueIndex;
01081   }
01082 
01083   for (unsigned y=0; y<height; y++) {
01084     unsigned char *baseRow   = srcData     + y*ximage->bytes_per_line;
01085     unsigned char *targetRow = readData    + 3*(y*ximage->width);
01086     unsigned char *imageRow  = imageOrigin + y*imageStride;
01087     unsigned char *alphaRow  = alphaOrigin + y*alphaStride;
01088 
01089     for (unsigned i=0; i<width;
01090          i++, baseRow+=3, targetRow+=3, imageRow+=3, alphaRow++) {
01091       unsigned alpha = *alphaRow;
01092       MOZ_BLEND(targetRow[0], baseRow[redIndex],   imageRow[0], alpha);
01093       MOZ_BLEND(targetRow[1], baseRow[greenIndex], imageRow[1], alpha);
01094       MOZ_BLEND(targetRow[2], baseRow[blueIndex],  imageRow[2], alpha);
01095     }
01096   }
01097 }
01098 
01099 unsigned nsImageGTK::scaled6[1<<6] = {
01100   3,   7,  11,  15,  19,  23,  27,  31,  35,  39,  43,  47,  51,  55,  59,  63,
01101  67,  71,  75,  79,  83,  87,  91,  95,  99, 103, 107, 111, 115, 119, 123, 127,
01102 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 183, 187, 191,
01103 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255
01104 };
01105 
01106 unsigned nsImageGTK::scaled5[1<<5] = {
01107   7,  15,  23,  31,  39,  47,  55,  63,  71,  79,  87,  95, 103, 111, 119, 127,
01108 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, 247, 255
01109 };
01110 
01111 // 16-bit ([56][56][56]) truecolor convert/composite function
01112 void
01113 nsImageGTK::DrawComposited16(PRBool isLSB, PRBool flipBytes,
01114                              PRUint8 *imageOrigin, PRUint32 imageStride,
01115                              PRUint8 *alphaOrigin, PRUint32 alphaStride,
01116                              unsigned width, unsigned height,
01117                              XImage *ximage, unsigned char *readData, unsigned char *srcData)
01118 {
01119   GdkVisual *visual   = gdk_rgb_get_visual();
01120 
01121   unsigned *redScale   = (visual->red_prec   == 5) ? scaled5 : scaled6;
01122   unsigned *greenScale = (visual->green_prec == 5) ? scaled5 : scaled6;
01123   unsigned *blueScale  = (visual->blue_prec  == 5) ? scaled5 : scaled6;
01124 
01125   for (unsigned y=0; y<height; y++) {
01126     unsigned char *baseRow   = srcData     + y*ximage->bytes_per_line;
01127     unsigned char *targetRow = readData    + 3*(y*ximage->width);
01128     unsigned char *imageRow  = imageOrigin + y*imageStride;
01129     unsigned char *alphaRow  = alphaOrigin + y*alphaStride;
01130 
01131     for (unsigned i=0; i<width;
01132          i++, baseRow+=2, targetRow+=3, imageRow+=3, alphaRow++) {
01133       unsigned pix;
01134       if (flipBytes) {
01135         unsigned char tmp[2];
01136         tmp[0] = baseRow[1];
01137         tmp[1] = baseRow[0]; 
01138         pix = *((short *)tmp); 
01139       } else
01140         pix = *((short *)baseRow);
01141       unsigned alpha = *alphaRow;
01142       MOZ_BLEND(targetRow[0],
01143                 redScale[(pix&visual->red_mask)>>visual->red_shift], 
01144                 imageRow[0], alpha);
01145       MOZ_BLEND(targetRow[1],
01146                 greenScale[(pix&visual->green_mask)>>visual->green_shift], 
01147                 imageRow[1], alpha);
01148       MOZ_BLEND(targetRow[2],
01149                 blueScale[(pix&visual->blue_mask)>>visual->blue_shift], 
01150                 imageRow[2], alpha);
01151     }
01152   }
01153 }
01154 
01155 // Generic convert/composite function
01156 void
01157 nsImageGTK::DrawCompositedGeneral(PRBool isLSB, PRBool flipBytes,
01158                                   PRUint8 *imageOrigin, PRUint32 imageStride,
01159                                   PRUint8 *alphaOrigin, PRUint32 alphaStride,
01160                                   unsigned width, unsigned height,
01161                                   XImage *ximage, unsigned char *readData, unsigned char *srcData)
01162 {
01163   GdkVisual *visual     = gdk_rgb_get_visual();
01164   GdkColormap *colormap = gdk_rgb_get_cmap();
01165 
01166   // flip bytes
01167   if (flipBytes && (ximage->bits_per_pixel>=16)) {
01168     for (int row=0; row<ximage->height; row++) {
01169       unsigned char *ptr = srcData + row*ximage->bytes_per_line;
01170       if (ximage->bits_per_pixel==24) {  // Aurgh....
01171         for (int col=0;
01172              col<ximage->bytes_per_line;
01173              col+=(ximage->bits_per_pixel/8)) {
01174           unsigned char tmp;
01175           tmp = *ptr;
01176           *ptr = *(ptr+2);
01177           *(ptr+2) = tmp;
01178           ptr+=3;
01179         }
01180         continue;
01181       }
01182       
01183       for (int col=0; 
01184                col<ximage->bytes_per_line;
01185                col+=(ximage->bits_per_pixel/8)) {
01186         unsigned char tmp;
01187         switch (ximage->bits_per_pixel) {
01188         case 16:
01189           tmp = *ptr;
01190           *ptr = *(ptr+1);
01191           *(ptr+1) = tmp;
01192           ptr+=2;
01193           break; 
01194         case 32:
01195           tmp = *ptr;
01196           *ptr = *(ptr+3);
01197           *(ptr+3) = tmp;
01198           tmp = *(ptr+1);
01199           *(ptr+1) = *(ptr+2);
01200           *(ptr+2) = tmp;
01201           ptr+=4;
01202           break;
01203         }
01204       }
01205     }
01206   }
01207 
01208   unsigned redScale, greenScale, blueScale, redFill, greenFill, blueFill;
01209   redScale =   8-visual->red_prec;
01210   greenScale = 8-visual->green_prec;
01211   blueScale =  8-visual->blue_prec;
01212   redFill =   0xff>>visual->red_prec;
01213   greenFill = 0xff>>visual->green_prec;
01214   blueFill =  0xff>>visual->blue_prec;
01215 
01216   for (unsigned row=0; row<height; row++) {
01217     unsigned char *ptr = srcData + row*ximage->bytes_per_line;
01218     unsigned char *target = readData+3*row*ximage->width;
01219     for (unsigned col=0; col<width; col++) {
01220       unsigned pix;
01221       switch (ximage->bits_per_pixel) {
01222       case 1:
01223         pix = (*ptr>>(col%8))&1;
01224         if ((col%8)==7)
01225           ptr++;
01226         break;
01227       case 4:
01228         pix = (col&1)?(*ptr>>4):(*ptr&0xf);
01229         if (col&1)
01230           ptr++;
01231         break;
01232       case 8:
01233         pix = *ptr++;
01234         break;
01235       case 16:
01236         pix = *((short *)ptr);
01237         ptr+=2;
01238         break;
01239       case 24:
01240         if (isLSB)
01241           pix = (*(ptr+2)<<16) | (*(ptr+1)<<8) | *ptr;
01242         else
01243           pix = (*ptr<<16) | (*(ptr+1)<<8) | *(ptr+2);
01244         ptr+=3;
01245         break;
01246       case 32:
01247         pix = *((unsigned *)ptr);
01248         ptr+=4;
01249         break;
01250       }
01251       switch (visual->type) {
01252       case GDK_VISUAL_STATIC_GRAY:
01253       case GDK_VISUAL_GRAYSCALE:
01254       case GDK_VISUAL_STATIC_COLOR:
01255       case GDK_VISUAL_PSEUDO_COLOR:
01256         *target++ = colormap->colors[pix].red   >>8;
01257         *target++ = colormap->colors[pix].green >>8;
01258         *target++ = colormap->colors[pix].blue  >>8;
01259         break;
01260         
01261       case GDK_VISUAL_DIRECT_COLOR:
01262         *target++ = 
01263           colormap->colors[(pix&visual->red_mask)>>visual->red_shift].red       >> 8;
01264         *target++ = 
01265           colormap->colors[(pix&visual->green_mask)>>visual->green_shift].green >> 8;
01266         *target++ =
01267           colormap->colors[(pix&visual->blue_mask)>>visual->blue_shift].blue    >> 8;
01268         break;
01269         
01270       case GDK_VISUAL_TRUE_COLOR:
01271         *target++ = 
01272           redFill|((pix&visual->red_mask)>>visual->red_shift)<<redScale;
01273         *target++ = 
01274           greenFill|((pix&visual->green_mask)>>visual->green_shift)<<greenScale;
01275         *target++ = 
01276           blueFill|((pix&visual->blue_mask)>>visual->blue_shift)<<blueScale;
01277         break;
01278       }
01279     }
01280   }
01281 
01282   // now composite
01283   for (unsigned y=0; y<height; y++) {
01284     unsigned char *targetRow = readData+3*y*ximage->width;
01285     unsigned char *imageRow  = imageOrigin + y*imageStride;
01286     unsigned char *alphaRow  = alphaOrigin + y*alphaStride;
01287     
01288     for (unsigned i=0; i<width; i++) {
01289       unsigned alpha = alphaRow[i];
01290       MOZ_BLEND(targetRow[3*i],   targetRow[3*i],   imageRow[3*i],   alpha);
01291       MOZ_BLEND(targetRow[3*i+1], targetRow[3*i+1], imageRow[3*i+1], alpha);
01292       MOZ_BLEND(targetRow[3*i+2], targetRow[3*i+2], imageRow[3*i+2], alpha);
01293     }
01294   }
01295 }
01296 
01297 void
01298 nsImageGTK::DrawComposited(nsIRenderingContext &aContext,
01299                            nsIDrawingSurface* aSurface,
01300                            PRInt32 srcWidth, PRInt32 srcHeight,
01301                            PRInt32 dstWidth, PRInt32 dstHeight,
01302                            PRInt32 dstOrigX, PRInt32 dstOrigY,
01303                            PRInt32 aDX, PRInt32 aDY,
01304                            PRInt32 aDWidth, PRInt32 aDHeight)
01305 {
01306   nsDrawingSurfaceGTK* drawing = (nsDrawingSurfaceGTK*) aSurface;
01307   GdkVisual *visual = gdk_rgb_get_visual();
01308     
01309   Display *dpy = GDK_WINDOW_XDISPLAY(drawing->GetDrawable());
01310   Drawable drawable = GDK_WINDOW_XWINDOW(drawing->GetDrawable());
01311 
01312   int readX, readY;
01313   unsigned readWidth, readHeight, destX, destY;
01314 
01315   destX = aDX-dstOrigX;
01316   destY = aDY-dstOrigY;
01317   readX = aDX;
01318   readY = aDY;
01319   readWidth = aDWidth;
01320   readHeight = aDHeight;
01321 
01322 //  fprintf(stderr, "dstOrigX=%d dstOrigY=%d, dstWidth=%u dstHeight=%u\n", dstOrigX, dstOrigY, dstWidth, dstHeight);
01323 //  fprintf(stderr, "srcWidth=%u srcHeight=%u\n", srcWidth, srcHeight);
01324 //  fprintf(stderr, "readX=%u readY=%u readWidth=%u readHeight=%u destX=%u destY=%u\n\n",
01325 //          readX, readY, readWidth, readHeight, destX, destY);
01326 
01327   XImage *ximage = XGetImage(dpy, drawable,
01328                              readX, readY, readWidth, readHeight, 
01329                              AllPlanes, ZPixmap);
01330 
01331   NS_ASSERTION((ximage!=NULL), "XGetImage() failed");
01332   if (!ximage)
01333     return;
01334 
01335   unsigned char *readData = 
01336     (unsigned char *)nsMemory::Alloc(3*readWidth*readHeight);
01337   if (!readData) {
01338     XDestroyImage(ximage);
01339     return;
01340   }
01341 
01342   PRUint8 *scaledImage = 0;
01343   PRUint8 *scaledAlpha = 0;
01344   PRUint8 *imageOrigin, *alphaOrigin;
01345   PRUint32 imageStride, alphaStride;
01346   if ((srcWidth!=dstWidth) || (srcHeight!=dstHeight)) {
01347     PRUint32 x1, y1, x2, y2;
01348     x1 = destX*srcWidth/dstWidth;
01349     y1 = destY*srcHeight/dstHeight;
01350     x2 = (destX+aDWidth)*srcWidth/dstWidth;
01351     y2 = (destY+aDHeight)*srcHeight/dstHeight;
01352 
01353     scaledImage = (PRUint8 *)nsMemory::Alloc(3*aDWidth*aDHeight);
01354     scaledAlpha = (PRUint8 *)nsMemory::Alloc(aDWidth*aDHeight);
01355     if (!scaledImage || !scaledAlpha) {
01356       XDestroyImage(ximage);
01357       nsMemory::Free(readData);
01358       if (scaledImage)
01359         nsMemory::Free(scaledImage);
01360       if (scaledAlpha)
01361         nsMemory::Free(scaledAlpha);
01362       return;
01363     }
01364     RectStretch(srcWidth, srcHeight,
01365                 dstWidth, dstHeight,
01366                 destX, destY,
01367                 destX+aDWidth-1, destY+aDHeight-1,
01368                 mImageBits, mRowBytes, scaledImage, 3*readWidth, 24);
01369     RectStretch(srcWidth, srcHeight,
01370                 dstWidth, dstHeight,
01371                 destX, destY,
01372                 destX+aDWidth-1, destY+aDHeight-1,
01373                 mAlphaBits, mAlphaRowBytes, scaledAlpha, readWidth, 8);
01374     imageOrigin = scaledImage;
01375     imageStride = 3*readWidth;
01376     alphaOrigin = scaledAlpha;
01377     alphaStride = readWidth;
01378   } else {
01379     imageOrigin = mImageBits + destY*mRowBytes + 3*destX;
01380     imageStride = mRowBytes;
01381     alphaOrigin = mAlphaBits + destY*mAlphaRowBytes + destX;
01382     alphaStride = mAlphaRowBytes;
01383   }
01384 
01385   PRBool isLSB;
01386   unsigned test = 1;
01387   isLSB = (((char *)&test)[0]) ? 1 : 0;
01388 
01389   PRBool flipBytes = 
01390     ( isLSB && ximage->byte_order != LSBFirst) ||
01391     (!isLSB && ximage->byte_order == LSBFirst);
01392 
01393   if ((ximage->bits_per_pixel==32) &&
01394       (visual->red_prec == 8) &&
01395       (visual->green_prec == 8) &&
01396       (visual->blue_prec == 8))
01397     DrawComposited32(isLSB, flipBytes, 
01398                      imageOrigin, imageStride,
01399                      alphaOrigin, alphaStride, 
01400                      readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data);
01401   else if ((ximage->bits_per_pixel==24) &&
01402            (visual->red_prec == 8) && 
01403            (visual->green_prec == 8) &&
01404            (visual->blue_prec == 8))
01405     DrawComposited24(isLSB, flipBytes, 
01406                      imageOrigin, imageStride,
01407                      alphaOrigin, alphaStride, 
01408                      readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data);
01409   else if ((ximage->bits_per_pixel==16) &&
01410            ((visual->red_prec == 5)   || (visual->red_prec == 6)) &&
01411            ((visual->green_prec == 5) || (visual->green_prec == 6)) &&
01412            ((visual->blue_prec == 5)  || (visual->blue_prec == 6)))
01413     DrawComposited16(isLSB, flipBytes,
01414                      imageOrigin, imageStride,
01415                      alphaOrigin, alphaStride, 
01416                      readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data);
01417   else
01418     DrawCompositedGeneral(isLSB, flipBytes,
01419                      imageOrigin, imageStride,
01420                      alphaOrigin, alphaStride, 
01421                      readWidth, readHeight, ximage, readData, (unsigned char *)ximage->data);
01422 
01423   GdkGC *imageGC = ((nsRenderingContextGTK&)aContext).GetGC();
01424   gdk_draw_rgb_image(drawing->GetDrawable(), imageGC,
01425                      readX, readY, readWidth, readHeight,
01426                      GDK_RGB_DITHER_MAX,
01427                      readData, 3*readWidth);
01428   gdk_gc_unref(imageGC);
01429 
01430   XDestroyImage(ximage);
01431   nsMemory::Free(readData);
01432   if (scaledImage)
01433     nsMemory::Free(scaledImage);
01434   if (scaledAlpha)
01435     nsMemory::Free(scaledAlpha);
01436   mFlags = 0;
01437 }
01438 
01439 
01440 void
01441 nsImageGTK::DrawCompositeTile(nsIRenderingContext &aContext,
01442                               nsIDrawingSurface* aSurface,
01443                               PRInt32 aSX, PRInt32 aSY,
01444                               PRInt32 aSWidth, PRInt32 aSHeight,
01445                               PRInt32 aDX, PRInt32 aDY,
01446                               PRInt32 aDWidth, PRInt32 aDHeight)
01447 {
01448   if ((aDWidth==0) || (aDHeight==0))
01449     return;
01450 
01451   nsDrawingSurfaceGTK* drawing = (nsDrawingSurfaceGTK*) aSurface;
01452   GdkVisual *visual = gdk_rgb_get_visual();
01453     
01454   Display *dpy = GDK_WINDOW_XDISPLAY(drawing->GetDrawable());
01455   Drawable drawable = GDK_WINDOW_XWINDOW(drawing->GetDrawable());
01456 
01457   // I hate clipping...
01458   PRUint32 surfaceWidth, surfaceHeight;
01459   drawing->GetDimensions(&surfaceWidth, &surfaceHeight);
01460   
01461   int readX, readY;
01462   unsigned readWidth, readHeight;
01463   PRInt32 destX, destY;
01464 
01465   if ((aDY>=(int)surfaceHeight) || (aDX>=(int)surfaceWidth) ||
01466       (aDY+aDHeight<=0) || (aDX+aDWidth<=0)) {
01467     // This should never happen if the layout engine is sane,
01468     // as it means we're trying to draw an image which is outside
01469     // the drawing surface.  Bulletproof gfx for now...
01470     return;
01471   }
01472 
01473   if (aDX<0) {
01474     readX = 0;   readWidth = aDWidth+aDX;    destX = aSX-aDX;
01475   } else {
01476     readX = aDX;  readWidth = aDWidth;       destX = aSX;
01477   }
01478   if (aDY<0) {
01479     readY = 0;   readHeight = aDHeight+aDY;  destY = aSY-aDY;
01480   } else {
01481     readY = aDY;  readHeight = aDHeight;     destY = aSY;
01482   }
01483 
01484   if (readX+readWidth > surfaceWidth)
01485     readWidth = surfaceWidth-readX;
01486   if (readY+readHeight > surfaceHeight)
01487     readHeight = surfaceHeight-readY;
01488 
01489   if ((readHeight <= 0) || (readWidth <= 0))
01490     return;
01491 
01492   XImage *ximage = XGetImage(dpy, drawable,
01493                              readX, readY, readWidth, readHeight, 
01494                              AllPlanes, ZPixmap);
01495 
01496   NS_ASSERTION((ximage!=NULL), "XGetImage() failed");
01497   if (!ximage)
01498     return;
01499 
01500   unsigned char *readData = 
01501     (unsigned char *)nsMemory::Alloc(3*readWidth*readHeight);
01502   if (!readData) {
01503     XDestroyImage(ximage);
01504     return;
01505   }
01506 
01507   PRBool isLSB;
01508   unsigned test = 1;
01509   isLSB = (((char *)&test)[0]) ? 1 : 0;
01510 
01511   PRBool flipBytes = 
01512     ( isLSB && ximage->byte_order != LSBFirst) ||
01513     (!isLSB && ximage->byte_order == LSBFirst);
01514 
01515 
01516   PRUint8 *imageOrigin, *alphaOrigin;
01517   PRUint32 imageStride, alphaStride;
01518   PRUint32 compX, compY;
01519   PRUint8 *compTarget, *compSource;
01520 
01521   imageStride = mRowBytes;
01522   alphaStride = mAlphaRowBytes;
01523 
01524   if (destX==mWidth)
01525     destX = 0;
01526   if (destY==mHeight)
01527     destY = 0;
01528 
01529   for (unsigned y=0; y<readHeight; y+=compY) {
01530     if (y==0) {
01531       compY = PR_MIN(mHeight-destY, readHeight-y);
01532     } else {
01533       destY = 0;
01534       compY = PR_MIN(mHeight, readHeight-y);
01535     }
01536 
01537     compTarget = readData + 3*y*ximage->width;
01538     compSource = (unsigned char *)ximage->data + y*ximage->bytes_per_line;
01539 
01540     for (unsigned x=0; x<readWidth; x+=compX) {
01541       if (x==0) {
01542         compX = PR_MIN(mWidth-destX, readWidth-x);
01543         imageOrigin = mImageBits + destY*mRowBytes + 3*destX;
01544         alphaOrigin = mAlphaBits + destY*mAlphaRowBytes + destX;
01545       } else {
01546         compX = PR_MIN(mWidth, readWidth-x);
01547         imageOrigin = mImageBits + destY*mRowBytes;
01548         alphaOrigin = mAlphaBits + destY*mAlphaRowBytes;
01549       }
01550 
01551       if ((ximage->bits_per_pixel==32) &&
01552           (visual->red_prec == 8) &&
01553           (visual->green_prec == 8) &&
01554           (visual->blue_prec == 8))
01555         DrawComposited32(isLSB, flipBytes, 
01556                          imageOrigin, imageStride,
01557                          alphaOrigin, alphaStride, 
01558                          compX, compY, ximage, compTarget, compSource);
01559       else if ((ximage->bits_per_pixel==24) &&
01560                (visual->red_prec == 8) && 
01561                (visual->green_prec == 8) &&
01562                (visual->blue_prec == 8))
01563         DrawComposited24(isLSB, flipBytes, 
01564                          imageOrigin, imageStride,
01565                          alphaOrigin, alphaStride, 
01566                          compX, compY, ximage, compTarget, compSource);
01567       else if ((ximage->bits_per_pixel==16) &&
01568                ((visual->red_prec == 5)   || (visual->red_prec == 6)) &&
01569                ((visual->green_prec == 5) || (visual->green_prec == 6)) &&
01570                ((visual->blue_prec == 5)  || (visual->blue_prec == 6)))
01571         DrawComposited16(isLSB, flipBytes,
01572                          imageOrigin, imageStride,
01573                          alphaOrigin, alphaStride, 
01574                          compX, compY, ximage, compTarget, compSource);
01575       else
01576         DrawCompositedGeneral(isLSB, flipBytes,
01577                               imageOrigin, imageStride,
01578                               alphaOrigin, alphaStride, 
01579                               compX, compY, ximage, compTarget, compSource);
01580 
01581       compTarget += 3*compX;
01582       compSource += (ximage->bits_per_pixel*compX)/8;
01583     }
01584   }
01585 
01586   GdkGC *imageGC = ((nsRenderingContextGTK&)aContext).GetGC();
01587   gdk_draw_rgb_image(drawing->GetDrawable(), imageGC,
01588                      readX, readY, readWidth, readHeight,
01589                      GDK_RGB_DITHER_MAX,
01590                      readData, 3*readWidth);
01591   gdk_gc_unref(imageGC);
01592 
01593   XDestroyImage(ximage);
01594   nsMemory::Free(readData);
01595   mFlags = 0;
01596 }
01597 
01598 
01599 void nsImageGTK::CreateOffscreenPixmap(PRInt32 aWidth, PRInt32 aHeight)
01600 {
01601   // Render unique image bits onto an off screen pixmap only once
01602   // The image bits can change as a result of ImageUpdated() - for
01603   // example: animated GIFs.
01604   if (!mImagePixmap) {
01605 #ifdef TRACE_IMAGE_ALLOCATION
01606     printf("nsImageGTK::Draw(this=%p) gdk_pixmap_new(nsnull,width=%d,height=%d,depth=%d)\n",
01607            this,
01608            aWidth, aHeight,
01609            mDepth);
01610 #endif
01611 
01612     // Create an off screen pixmap to hold the image bits.
01613     mImagePixmap = gdk_pixmap_new(nsnull, aWidth, aHeight,
01614                                   gdk_rgb_get_visual()->depth);
01615 #ifdef MOZ_WIDGET_GTK2
01616     gdk_drawable_set_colormap(GDK_DRAWABLE(mImagePixmap), gdk_rgb_get_colormap());
01617 #endif
01618   }
01619 
01620     // Ditto for the clipmask
01621   if ((!mAlphaPixmap) && (mAlphaDepth==1)) {
01622     mAlphaPixmap = gdk_pixmap_new(nsnull, aWidth, aHeight, 1);
01623 
01624     // Need an XImage for clipmask updates (XPutImage)
01625     mAlphaXImage = XCreateImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap),
01626                                 GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()),
01627                                 1, /* visual depth...1 for bitmaps */
01628                                 XYPixmap,
01629                                 0, /* x offset, XXX fix this */
01630                                 (char *)mAlphaBits,  /* cast away our sign. */
01631                                 aWidth,
01632                                 aHeight,
01633                                 32,/* bitmap pad */
01634                                 mAlphaRowBytes); /* bytes per line */
01635 
01636     mAlphaXImage->bits_per_pixel=1;
01637 
01638     /* Image library always places pixels left-to-right MSB to LSB */
01639     mAlphaXImage->bitmap_bit_order = MSBFirst;
01640 
01641     /* This definition doesn't depend on client byte ordering
01642        because the image library ensures that the bytes in
01643        bitmask data are arranged left to right on the screen,
01644        low to high address in memory. */
01645     mAlphaXImage->byte_order = MSBFirst;
01646 
01647     if (!s1bitGC) {
01648       GdkColor fg = { 1, 0, 0, 0 };
01649       s1bitGC = gdk_gc_new(mAlphaPixmap);
01650       gdk_gc_set_foreground(s1bitGC, &fg);
01651     }
01652   }
01653 
01654   if (!sXbitGC)
01655     sXbitGC = gdk_gc_new(mImagePixmap);
01656 }
01657 
01658 
01659 void nsImageGTK::SetupGCForAlpha(GdkGC *aGC, PRInt32 aX, PRInt32 aY)
01660 {
01661   // XXX should use (different?) GC cache here
01662   if (mAlphaPixmap) {
01663     // Setup gc to use the given alpha-pixmap for clipping
01664     XGCValues xvalues;
01665     memset(&xvalues, 0, sizeof(XGCValues));
01666     unsigned long xvalues_mask = 0;
01667     xvalues.clip_x_origin = aX;
01668     xvalues.clip_y_origin = aY;
01669     xvalues_mask = GCClipXOrigin | GCClipYOrigin;
01670 
01671     xvalues.clip_mask = GDK_WINDOW_XWINDOW(mAlphaPixmap);
01672     xvalues_mask |= GCClipMask;
01673 
01674     XChangeGC(GDK_DISPLAY(), GDK_GC_XGC(aGC), xvalues_mask, &xvalues);
01675   }
01676 }
01677 
01678 // Draw the bitmap, this draw just has destination coordinates
01679 NS_IMETHODIMP
01680 nsImageGTK::Draw(nsIRenderingContext &aContext,
01681                  nsIDrawingSurface* aSurface,
01682                  PRInt32 aX, PRInt32 aY,
01683                  PRInt32 aWidth, PRInt32 aHeight)
01684 {
01685 #ifdef TRACE_IMAGE_ALLOCATION
01686   printf("nsImageGTK::Draw(this=%p,x=%d,y=%d,width=%d,height=%d)\n",
01687          this,
01688          aX, aY,
01689          aWidth, aHeight);
01690 #endif
01691 
01692   return Draw(aContext, aSurface,
01693               0, 0, mWidth, mHeight,
01694               aX, aY, mWidth, mHeight);
01695 }
01696 
01697 /* inline */
01698 void nsImageGTK::TilePixmap(GdkPixmap *src, GdkPixmap *dest, 
01699                             PRInt32 aSXOffset, PRInt32 aSYOffset,
01700                             const nsRect &destRect, 
01701                             const nsRect &clipRect, PRBool aHaveClip)
01702 {
01703   GdkGC *gc;
01704   GdkGCValues values;
01705   GdkGCValuesMask valuesMask;
01706   memset(&values, 0, sizeof(GdkGCValues));
01707   values.fill = GDK_TILED;
01708   values.tile = src;
01709   values.ts_x_origin = destRect.x - aSXOffset;
01710   values.ts_y_origin = destRect.y - aSYOffset;
01711   valuesMask = GdkGCValuesMask(GDK_GC_FILL | GDK_GC_TILE | GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN);
01712   gc = gdk_gc_new_with_values(src, &values, valuesMask);
01713 
01714   if (aHaveClip) {
01715     GdkRectangle gdkrect = {clipRect.x, clipRect.y, clipRect.width, clipRect.height};
01716     gdk_gc_set_clip_rectangle(gc, &gdkrect);
01717   }
01718 
01719   // draw to destination window
01720   #ifdef DEBUG_TILING
01721   printf("nsImageGTK::TilePixmap(..., %d, %d, %d, %d)\n",
01722          destRect.x, destRect.y, 
01723          destRect.width, destRect.height);
01724   #endif
01725 
01726   gdk_draw_rectangle(dest, gc, PR_TRUE,
01727                      destRect.x, destRect.y,
01728                      destRect.width, destRect.height);
01729 
01730   gdk_gc_unref(gc);
01731 }
01732 
01733 void nsImageGTK::SlowTile(nsDrawingSurfaceGTK *aSurface,
01734                           const nsRect &aTileRect,
01735                           PRInt32 aSXOffset, PRInt32 aSYOffset,
01736                           const nsRect& aClipRect, PRBool aHaveClip)
01737 {
01738   GdkPixmap *tileImg;
01739   GdkPixmap *tileMask;
01740 
01741   nsRect tmpRect(0,0,aTileRect.width, aTileRect.height);
01742 
01743   tileImg = gdk_pixmap_new(nsnull, aTileRect.width, 
01744                            aTileRect.height, aSurface->GetDepth());
01745 #ifdef MOZ_WIDGET_GTK2
01746   gdk_drawable_set_colormap(GDK_DRAWABLE(tileImg), gdk_rgb_get_colormap());
01747 #endif
01748 
01749   TilePixmap(mImagePixmap, tileImg, aSXOffset, aSYOffset, tmpRect,
01750              tmpRect, PR_FALSE);
01751 
01752   // tile alpha mask
01753   tileMask = gdk_pixmap_new(nsnull, aTileRect.width, aTileRect.height,
01754                             mAlphaDepth);
01755   TilePixmap(mAlphaPixmap, tileMask, aSXOffset, aSYOffset, tmpRect,
01756              tmpRect, PR_FALSE);
01757 
01758   GdkGC *fgc = gdk_gc_new(aSurface->GetDrawable());
01759   gdk_gc_set_clip_mask(fgc, (GdkBitmap*)tileMask);
01760   gdk_gc_set_clip_origin(fgc, aTileRect.x, aTileRect.y);
01761 
01762   nsRect drawRect = aTileRect;
01763   if (aHaveClip) {
01764     drawRect.IntersectRect(drawRect, aClipRect);
01765   }
01766 
01767   // and copy it back
01768   gdk_window_copy_area(aSurface->GetDrawable(), fgc, drawRect.x,
01769                        drawRect.y, tileImg,
01770                        drawRect.x - aTileRect.x, drawRect.y - aTileRect.y,
01771                        drawRect.width, drawRect.height);
01772   gdk_gc_unref(fgc);
01773 
01774   gdk_pixmap_unref(tileImg);
01775   gdk_pixmap_unref(tileMask);
01776 }
01777 
01778 
01779 NS_IMETHODIMP nsImageGTK::DrawTile(nsIRenderingContext &aContext,
01780                                    nsIDrawingSurface* aSurface,
01781                                    PRInt32 aSXOffset, PRInt32 aSYOffset,
01782                                    PRInt32 aPadX, PRInt32 aPadY,
01783                                    const nsRect &aTileRect)
01784 {
01785 #ifdef DEBUG_TILING
01786   printf("nsImageGTK::DrawTile: mWidth=%d, mHeight=%d\n", mWidth, mHeight);
01787   printf("nsImageGTK::DrawTile((src: %d, %d), (tile: %d,%d, %d, %d) %p\n", aSXOffset, aSYOffset,
01788          aTileRect.x, aTileRect.y,
01789          aTileRect.width, aTileRect.height, this);
01790 #endif
01791   if (mPendingUpdate)
01792     UpdateCachedImage();
01793 
01794   if ((mAlphaDepth==1) && mIsSpacer)
01795     return NS_OK;
01796 
01797   if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
01798     return NS_OK;
01799 
01800   nsDrawingSurfaceGTK *drawing = (nsDrawingSurfaceGTK*)aSurface;
01801   PRBool partial = PR_FALSE;
01802 
01803   PRInt32
01804     validX = 0,
01805     validY = 0,
01806     validWidth  = mWidth,
01807     validHeight = mHeight;
01808   
01809   // limit the image rectangle to the size of the image data which
01810   // has been validated.
01811   if (mDecodedY2 < mHeight) {
01812     validHeight = mDecodedY2 - mDecodedY1;
01813     partial = PR_TRUE;
01814   }
01815   if (mDecodedX2 < mWidth) {
01816     validWidth = mDecodedX2 - mDecodedX1;
01817     partial = PR_TRUE;
01818   }
01819   if (mDecodedY1 > 0) {   
01820     validHeight -= mDecodedY1;
01821     validY = mDecodedY1;
01822     partial = PR_TRUE;
01823   }
01824   if (mDecodedX1 > 0) {
01825     validWidth -= mDecodedX1;
01826     validX = mDecodedX1; 
01827     partial = PR_TRUE;
01828   }
01829 
01830   if (aTileRect.width == 0 || aTileRect.height == 0 ||
01831       validWidth == 0 || validHeight == 0) {
01832     return NS_OK;
01833   }
01834 
01835   if (partial || (mAlphaDepth == 8) || (aPadX || aPadY)) {
01836     PRInt32 aY0 = aTileRect.y - aSYOffset,
01837             aX0 = aTileRect.x - aSXOffset,
01838             aY1 = aTileRect.y + aTileRect.height,
01839             aX1 = aTileRect.x + aTileRect.width;
01840 
01841     // Set up clipping and call Draw().
01842     aContext.PushState();
01843     ((nsRenderingContextGTK&)aContext).SetClipRectInPixels(
01844       aTileRect, nsClipCombine_kIntersect);
01845     ((nsRenderingContextGTK&)aContext).UpdateGC();
01846 
01847     if (mAlphaDepth==8) {
01848       DrawCompositeTile(aContext, aSurface,
01849                         aSXOffset, aSYOffset, mWidth, mHeight,
01850                         aTileRect.x, aTileRect.y,
01851                         aTileRect.width, aTileRect.height);
01852     } else {
01853 #ifdef DEBUG_TILING
01854       printf("Warning: using slow tiling\n");
01855 #endif
01856       for (PRInt32 y = aY0; y < aY1; y += mHeight + aPadY)
01857         for (PRInt32 x = aX0; x < aX1; x += mWidth + aPadX)
01858           Draw(aContext,aSurface, x,y,
01859                PR_MIN(validWidth, aX1-x),
01860                PR_MIN(validHeight, aY1-y));
01861     }
01862 
01863     aContext.PopState();
01864 
01865     return NS_OK;
01866   }
01867 
01868   nsRect clipRect;
01869   PRBool isNonEmpty;
01870   PRBool haveClip = NS_SUCCEEDED(aContext.GetClipRect(clipRect, isNonEmpty));
01871   if (haveClip && !isNonEmpty) {
01872     return NS_OK;
01873   }
01874     
01875   if (mAlphaDepth == 1) {
01876     if (sNeedSlowTile) {
01877       SlowTile(drawing, aTileRect, aSXOffset, aSYOffset, clipRect, haveClip);
01878       return NS_OK;
01879     }
01880 
01881     GdkGC *tileGC;
01882     GdkGCValues values;
01883     GdkGCValuesMask valuesMask;
01884 
01885     memset(&values, 0, sizeof(GdkGCValues));
01886     values.fill = GDK_STIPPLED;
01887     values.function = GDK_AND;
01888     values.stipple = mAlphaPixmap;
01889     values.ts_x_origin = aTileRect.x - aSXOffset;
01890     values.ts_y_origin = aTileRect.y - aSYOffset;
01891     valuesMask = GdkGCValuesMask(GDK_GC_FOREGROUND | GDK_GC_FUNCTION | 
01892                                  GDK_GC_FILL | GDK_GC_STIPPLE | 
01893                                  GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN);
01894     tileGC = gdk_gc_new_with_values(drawing->GetDrawable(), &values, valuesMask);
01895     
01896     if (haveClip) {
01897       GdkRectangle gdkrect = {clipRect.x, clipRect.y,
01898                               clipRect.width, clipRect.height};
01899       gdk_gc_set_clip_rectangle(tileGC, &gdkrect);
01900     }
01901 
01902     gdk_draw_rectangle(drawing->GetDrawable(), tileGC, PR_TRUE,
01903                        aTileRect.x, aTileRect.y,
01904                        aTileRect.width, aTileRect.height);
01905 
01906     gdk_gc_set_fill(tileGC, GDK_TILED);
01907     gdk_gc_set_function(tileGC, GDK_OR);
01908     gdk_gc_set_tile(tileGC, mImagePixmap);
01909 
01910     gdk_draw_rectangle(drawing->GetDrawable(), tileGC, PR_TRUE,
01911                        aTileRect.x, aTileRect.y,
01912                        aTileRect.width, aTileRect.height);
01913 
01914     gdk_gc_unref(tileGC);
01915   } else {
01916     // In the non-alpha case, gdk can tile for us
01917     TilePixmap(mImagePixmap, drawing->GetDrawable(), aSXOffset, aSYOffset,
01918                aTileRect, clipRect, haveClip);
01919   }
01920 
01921   mFlags = 0;
01922   return NS_OK;
01923 }
01924 
01925 
01926 
01927 //------------------------------------------------------------
01928 
01929 nsresult nsImageGTK::Optimize(nsIDeviceContext* aContext)
01930 {
01931   if (!mOptimized)
01932     UpdateCachedImage();
01933 
01934   if (mAlphaBits && mTrueAlphaBits) {
01935     // 8-bit alpha image turned out to be 1-bit - blacken transparent
01936     // areas so that we can draw it using the fast tile path
01937     for (PRInt32 y = 0; y < mHeight; y++)
01938       for (PRInt32 x = 0; x < mWidth; x++)
01939         if (!mTrueAlphaBits[y * mTrueAlphaRowBytes + x]) {
01940           mImageBits[y * mRowBytes + 3 * x]     = 0;
01941           mImageBits[y * mRowBytes + 3 * x + 1] = 0;
01942           mImageBits[y * mRowBytes + 3 * x + 2] = 0;
01943         }
01944     nsRect rect(0, 0, mWidth, mHeight);
01945     ImageUpdated(nsnull, 0, &rect);
01946     UpdateCachedImage();
01947   }
01948 
01949   if ((gdk_rgb_get_visual()->depth > 8) && (mAlphaDepth != 8)) {
01950     if(nsnull != mImageBits) {
01951       free(mImageBits);
01952       mImageBits = nsnull;
01953     }
01954 
01955     if (nsnull != mAlphaBits) {
01956       free(mAlphaBits);
01957       mAlphaBits = nsnull;
01958     }
01959   }
01960     
01961   if (mTrueAlphaBits) {
01962     free(mTrueAlphaBits);
01963     mTrueAlphaBits = nsnull;
01964   }
01965 
01966   if ((mAlphaDepth==0) && mAlphaPixmap) {
01967     gdk_pixmap_unref(mAlphaPixmap);
01968     mAlphaPixmap = nsnull;
01969   }
01970 
01971   mOptimized = PR_TRUE;
01972 
01973   return NS_OK;
01974 }
01975 
01976 //------------------------------------------------------------
01977 // lock the image pixels
01978 NS_IMETHODIMP
01979 nsImageGTK::LockImagePixels(PRBool aMaskPixels)
01980 {
01981   if (!mOptimized)
01982     return NS_OK;
01983 
01984   if (aMaskPixels) {
01985     if (mAlphaDepth != 1 || !mAlphaPixmap)
01986       return NS_OK;
01987 
01988     XImage *xmask = XGetImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap),
01989                               GDK_WINDOW_XWINDOW(mAlphaPixmap),
01990                               0, 0, mWidth, mHeight,
01991                               AllPlanes, XYPixmap);
01992 
01993     mAlphaBits = (PRUint8*)calloc(mAlphaRowBytes * mHeight, 1);
01994     if (!mAlphaBits)
01995       return NS_ERROR_OUT_OF_MEMORY;
01996 
01997     for (PRInt32 y = 0; y < mHeight; ++y) {
01998       PRUint8 *alphaTarget = mAlphaBits + y*mAlphaRowBytes;
01999       PRUint32 alphaBitPos = 7;
02000 
02001       for (PRInt32 x = 0; x < mWidth; ++x) {
02002         *alphaTarget |= (XGetPixel(xmask, x, y) << alphaBitPos);
02003         if (alphaBitPos-- == 0) {
02004           ++alphaTarget;
02005           alphaBitPos = 7;
02006         }
02007       }
02008     }
02009 
02010     XDestroyImage(xmask);
02011     return NS_OK;
02012   }
02013 
02014   if (!mImagePixmap)
02015     return NS_OK;
02016 
02017   XImage *ximage, *xmask=0;
02018   unsigned pix;
02019 
02020   ximage = XGetImage(GDK_WINDOW_XDISPLAY(mImagePixmap),
02021                      GDK_WINDOW_XWINDOW(mImagePixmap),
02022                      0, 0, mWidth, mHeight,
02023                      AllPlanes, ZPixmap);
02024 
02025   if ((mAlphaDepth==1) && mAlphaPixmap)
02026     xmask = XGetImage(GDK_WINDOW_XDISPLAY(mAlphaPixmap),
02027                       GDK_WINDOW_XWINDOW(mAlphaPixmap),
02028                       0, 0, mWidth, mHeight,
02029                       AllPlanes, XYPixmap);
02030 
02031   mImageBits = (PRUint8*)malloc(mSizeImage);
02032   if (!mImageBits)
02033     return NS_ERROR_OUT_OF_MEMORY;
02034 
02035   GdkVisual *visual = gdk_rgb_get_visual();
02036   GdkColormap *colormap = gdk_rgb_get_cmap();
02037 
02038   unsigned redScale, greenScale, blueScale, redFill, greenFill, blueFill;
02039   redScale   = 8 - visual->red_prec;
02040   greenScale = 8 - visual->green_prec;
02041   blueScale  = 8 - visual->blue_prec;
02042   redFill    = 0xff >> visual->red_prec;
02043   greenFill  = 0xff >> visual->green_prec;
02044   blueFill   = 0xff >> visual->blue_prec;
02045 
02046   /* read back the image in the slowest (but simplest) way possible... */
02047   for (PRInt32 y=0; y<mHeight; y++) {
02048     PRUint8 *target = mImageBits + y*mRowBytes;
02049     for (PRInt32 x=0; x<mWidth; x++) {
02050       if (xmask && !XGetPixel(xmask, x, y)) {
02051         *target++ = 0xFF;
02052         *target++ = 0xFF;
02053         *target++ = 0xFF;
02054       } else {
02055         pix = XGetPixel(ximage, x, y);
02056         switch (visual->type) {
02057         case GDK_VISUAL_STATIC_GRAY:
02058         case GDK_VISUAL_GRAYSCALE:
02059         case GDK_VISUAL_STATIC_COLOR:
02060         case GDK_VISUAL_PSEUDO_COLOR:
02061           *target++ = colormap->colors[pix].red   >>8;
02062           *target++ = colormap->colors[pix].green >>8;
02063           *target++ = colormap->colors[pix].blue  >>8;
02064           break;
02065 
02066         case GDK_VISUAL_DIRECT_COLOR:
02067           *target++ = 
02068             colormap->colors[(pix&visual->red_mask)>>visual->red_shift].red       >> 8;
02069           *target++ = 
02070             colormap->colors[(pix&visual->green_mask)>>visual->green_shift].green >> 8;
02071           *target++ =
02072             colormap->colors[(pix&visual->blue_mask)>>visual->blue_shift].blue    >> 8;
02073           break;
02074 
02075         case GDK_VISUAL_TRUE_COLOR:
02076           *target++ = 
02077             redFill|((pix&visual->red_mask)>>visual->red_shift)<<redScale;
02078           *target++ = 
02079             greenFill|((pix&visual->green_mask)>>visual->green_shift)<<greenScale;
02080           *target++ = 
02081             blueFill|((pix&visual->blue_mask)>>visual->blue_shift)<<blueScale;
02082           break;
02083         }
02084       }
02085     }
02086   }
02087 
02088   XDestroyImage(ximage);
02089   if (xmask)
02090     XDestroyImage(xmask);
02091 
02092   return NS_OK;
02093 }
02094 
02095 //------------------------------------------------------------
02096 // unlock the image pixels. nothing to do on gtk
02097 NS_IMETHODIMP
02098 nsImageGTK::UnlockImagePixels(PRBool aMaskPixels)
02099 {
02100   if (mOptimized)
02101     Optimize(nsnull);
02102 
02103   return NS_OK;
02104 } 
02105 
02106 NS_IMETHODIMP nsImageGTK::DrawToImage(nsIImage* aDstImage,
02107                                       nscoord aDX, nscoord aDY,
02108                                       nscoord aDWidth, nscoord aDHeight)
02109 {
02110   nsImageGTK *dest = NS_STATIC_CAST(nsImageGTK *, aDstImage);
02111 
02112   if (!dest)
02113     return NS_ERROR_FAILURE;
02114     
02115   if (aDX >= dest->mWidth || aDY >= dest->mHeight)
02116     return NS_OK;
02117 
02118   PRUint8 *rgbPtr=0, *alphaPtr=0;
02119   PRUint32 rgbStride, alphaStride;
02120 
02121   rgbPtr = mImageBits;
02122   rgbStride = mRowBytes;
02123   alphaPtr = mAlphaBits;
02124   alphaStride = mAlphaRowBytes;
02125 
02126   PRInt32 y;
02127   PRInt32 ValidWidth = ( aDWidth < ( dest->mWidth - aDX ) ) ? aDWidth : ( dest->mWidth - aDX ); 
02128   PRInt32 ValidHeight = ( aDHeight < ( dest->mHeight - aDY ) ) ? aDHeight : ( dest->mHeight - aDY );
02129 
02130   // now composite the two images together
02131   switch (mAlphaDepth) {
02132   case 1:
02133     {
02134       PRUint8 *dst = dest->mImageBits + aDY*dest->mRowBytes + 3*aDX;
02135       PRUint8 *dstAlpha = dest->mAlphaBits + aDY*dest->mAlphaRowBytes;
02136       PRUint8 *src = rgbPtr;
02137       PRUint8 *alpha = alphaPtr;
02138       PRUint8 offset = aDX & 0x7; // x starts at 0
02139       int iterations = (ValidWidth+7)/8; // round up
02140 
02141       for (y=0; y<ValidHeight; y++) {
02142         for (int x=0; x<ValidWidth; x += 8, dst += 3*8, src += 3*8) {
02143           PRUint8 alphaPixels = *alpha++;
02144           if (alphaPixels == 0) {
02145             // all 8 transparent; jump forward
02146             continue;
02147           }
02148 
02149           // 1 or more bits are set, handle dstAlpha now - may not be aligned.
02150           // Are all 8 of these alpha pixels used?
02151           if (x+7 >= ValidWidth) {
02152             alphaPixels &= 0xff << (8 - (ValidWidth-x)); // no, mask off unused
02153             if (alphaPixels == 0)
02154               continue;  // no 1 alpha pixels left
02155           }
02156           if (offset == 0) {
02157             dstAlpha[(aDX+x)>>3] |= alphaPixels; // the cheap aligned case
02158           }
02159           else {
02160             dstAlpha[(aDX+x)>>3]       |= alphaPixels >> offset;
02161             // avoid write if no 1's to write - also avoids going past end of array
02162             PRUint8 alphaTemp = alphaPixels << (8U - offset);
02163             if (alphaTemp & 0xff)
02164               dstAlpha[((aDX+x)>>3) + 1] |= alphaTemp;
02165           }
02166           
02167           if (alphaPixels == 0xff) {
02168             // fix - could speed up by gathering a run of 0xff's and doing 1 memcpy
02169             // all 8 pixels set; copy and jump forward
02170             memcpy(dst,src,8*3);
02171             continue;
02172           }
02173           else {
02174             // else mix of 1's and 0's in alphaPixels, do 1 bit at a time
02175             // Don't go past end of line!
02176             PRUint8 *d = dst, *s = src;
02177             for (PRUint8 aMask = 1<<7, j = 0; aMask && j < ValidWidth-x; aMask >>= 1, j++) {
02178               // if this pixel is opaque then copy into the destination image
02179               if (alphaPixels & aMask) {
02180                 // might be faster with *d++ = *s++ 3 times?
02181                 d[0] = s[0];
02182                 d[1] = s[1];
02183                 d[2] = s[2];
02184                 // dstAlpha bit already set
02185               }
02186               d += 3;
02187               s += 3;
02188             }
02189           }
02190         }
02191         // at end of each line, bump pointers.  Use wordy code because of
02192         // bug 127455 to avoid possibility of unsigned underflow
02193         dst = (dst - 3*8*iterations) + dest->mRowBytes;
02194         src = (src - 3*8*iterations) + rgbStride;
02195         alpha = (alpha - iterations) + alphaStride;
02196         dstAlpha += dest->mAlphaRowBytes;
02197       }
02198     }
02199     break;
02200   case 0:
02201   default:
02202     for (y=0; y<ValidHeight; y++)
02203       memcpy(dest->mImageBits + (y+aDY)*dest->mRowBytes + 3*aDX, 
02204              rgbPtr + y*rgbStride,
02205              3*ValidWidth);
02206   }
02207 
02208   nsRect rect(aDX, aDY, ValidWidth, ValidHeight);
02209   dest->ImageUpdated(nsnull, 0, &rect);
02210 
02211   return NS_OK;
02212 }
02213 
02214 #ifdef MOZ_WIDGET_GTK2
02215 static void pixbuf_free(guchar* data, gpointer) {
02216   nsMemory::Free(data);
02217 }
02218 
02219 NS_IMETHODIMP_(GdkPixbuf*)
02220 nsImageGTK::GetGdkPixbuf() {
02221   // Init ensures that we only have 24bpp images
02222   NS_ASSERTION(mNumBytesPixel == 3, "Unexpected color depth");
02223 
02224   nsresult rv = LockImagePixels(PR_FALSE);
02225   NS_ENSURE_SUCCESS(rv, nsnull);
02226 
02227   // Since UnlockImagePixels potentially frees the image data (and since the
02228   // buffer might outlive this object anyway), we have to copy the data.
02229   guchar* pixels = NS_STATIC_CAST(guchar*,
02230       nsMemory::Clone(mImageBits, mRowBytes * mHeight));
02231   UnlockImagePixels(PR_FALSE);
02232   if (!pixels)
02233     return nsnull;
02234 
02235   GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(pixels,
02236       GDK_COLORSPACE_RGB,
02237       PR_FALSE,
02238       8,
02239       mWidth,
02240       mHeight,
02241       mRowBytes,
02242       pixbuf_free,
02243       nsnull);
02244   if (!pixbuf)
02245     return nsnull;
02246 
02247   if (!GetHasAlphaMask()) {
02248     // No alpha channel -> we are done
02249     return pixbuf;
02250   }
02251 
02252   GdkPixbuf *alphaPixbuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
02253   g_object_unref(pixbuf);
02254   if (!alphaPixbuf)
02255     return nsnull;
02256 
02257   LockImagePixels(PR_TRUE);
02258 
02259   PRInt32 alphaBytesPerRow = GetAlphaLineStride();
02260   PRUint8 *alphaBits = GetAlphaBits();
02261 
02262   // Run through alphaBits and copy the alpha mask into the pixbuf's
02263   // alpha channel.
02264   PRUint8 *maskRow = alphaBits;
02265   PRUint8 *pixbufRow = gdk_pixbuf_get_pixels(alphaPixbuf);
02266 
02267   gint pixbufRowStride = gdk_pixbuf_get_rowstride(alphaPixbuf);
02268   gint pixbufChannels = gdk_pixbuf_get_n_channels(alphaPixbuf);
02269 
02270   for (PRInt32 y = 0; y < mHeight; ++y) {
02271     PRUint8 *pixbufPixel = pixbufRow;
02272     PRUint8 *maskPixel = maskRow;
02273 
02274     // If using 1-bit alpha, we must expand it to 8-bit
02275     PRUint32 bitPos = 7;
02276 
02277     for (PRInt32 x = 0; x < mWidth; ++x) {
02278       if (mAlphaDepth == 1) {
02279         pixbufPixel[pixbufChannels - 1] = ((*maskPixel >> bitPos) & 1) ? 255 : 0;
02280         if (bitPos-- == 0) { // wrapped around, move forward a byte
02281           ++maskPixel;
02282           bitPos = 7;
02283         }
02284       } else {
02285         pixbufPixel[pixbufChannels - 1] = *maskPixel++;
02286       }
02287 
02288       pixbufPixel += pixbufChannels;
02289     }
02290 
02291     pixbufRow += pixbufRowStride;
02292     maskRow += alphaBytesPerRow;
02293   }
02294 
02295   UnlockImagePixels(PR_TRUE);
02296   return alphaPixbuf;
02297 }
02298 
02299 #endif