Back to index

lightning-sunbird  0.9+nobinonly
nsImageWin.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   emk <VYV03354@nifty.ne.jp>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 
00040 #include "nsImageWin.h"
00041 #include "nsRenderingContextWin.h"
00042 #include "nsDeviceContextWin.h"
00043 #include "imgScaler.h"
00044 #include "nsComponentManagerUtils.h"
00045 
00046 static PRInt32 GetPlatform()
00047 {
00048   OSVERSIONINFO versionInfo;
00049 
00050 
00051   versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
00052   ::GetVersionEx(&versionInfo);
00053   return versionInfo.dwPlatformId;
00054 }
00055 
00056 static PRInt32 GetOsMajorVersion()
00057 {
00058   OSVERSIONINFO versionInfo;
00059 
00060   versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
00061   ::GetVersionEx(&versionInfo);
00062   return versionInfo.dwMajorVersion;
00063 }
00064 
00065 
00066 PRInt32 nsImageWin::gPlatform = GetPlatform();
00067 PRInt32 nsImageWin::gOsMajorVersion = GetOsMajorVersion();
00068 
00069 
00074 nsImageWin::nsImageWin()
00075   : mImageBits(nsnull)
00076   , mHBitmap(nsnull)
00077   , mAlphaBits(nsnull)
00078   , mColorMap(nsnull)
00079   , mBHead(nsnull)
00080   , mDIBTemp(PR_FALSE)
00081   , mNumBytesPixel(0)
00082   , mNumPaletteColors(0)
00083   , mSizeImage(0)
00084   , mRowBytes(0)
00085   , mIsOptimized(PR_FALSE)
00086   , mDecodedX1(PR_INT32_MAX)
00087   , mDecodedY1(PR_INT32_MAX)
00088   , mDecodedX2(0)
00089   , mDecodedY2(0)
00090   , mIsLocked(PR_FALSE)
00091   , mAlphaDepth(0)
00092   , mARowBytes(0)
00093   , mImageCache(0)
00094   , mInitialized(PR_FALSE)
00095   , mWantsOptimization(PR_FALSE)
00096   , mTimer(nsnull)
00097   , mImagePreMultiplied(PR_FALSE)
00098 {
00099 }
00100 
00101 
00106 nsImageWin :: ~nsImageWin()
00107 {
00108   if (mTimer)
00109     mTimer->Cancel();
00110 
00111   CleanUpDDB();
00112   CleanUpDIB();
00113 
00114   if (mBHead) {
00115     delete[] mBHead;
00116     mBHead = nsnull;
00117   }
00118   if (mAlphaBits) {
00119     delete [] mAlphaBits;
00120     mAlphaBits = nsnull;
00121   }
00122 }
00123 
00124 
00125 NS_IMPL_ISUPPORTS1(nsImageWin, nsIImage)
00126 
00127 
00128 
00132 nsresult nsImageWin::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth,
00133                           nsMaskRequirements aMaskRequirements)
00134 {
00135   if (mInitialized)
00136     return NS_ERROR_FAILURE;
00137 
00138   if (8 == aDepth) {
00139     mNumPaletteColors = 256;
00140     mNumBytesPixel = 1;
00141   } else if (24 == aDepth) {
00142     mNumBytesPixel = 3;
00143   } else {
00144     NS_ASSERTION(PR_FALSE, "unexpected image depth");
00145     return NS_ERROR_UNEXPECTED;
00146   }
00147 
00148   // limit images to 64k pixels on a side (~55 feet on a 100dpi monitor)
00149   const PRInt32 k64KLimit = 0x0000FFFF;
00150   if (aWidth > k64KLimit || aHeight > k64KLimit)
00151     return NS_ERROR_FAILURE;
00152 
00153   if (0 == mNumPaletteColors) {
00154     // space for the header only (no color table)
00155     mBHead = (LPBITMAPINFOHEADER)new char[sizeof(BITMAPINFO)];
00156   } else {
00157     // Space for the header and the palette. Since we'll be using DIB_PAL_COLORS
00158     // the color table is an array of 16-bit unsigned integers that specify an
00159     // index into the currently realized logical palette
00160     mBHead = (LPBITMAPINFOHEADER)new char[sizeof(BITMAPINFOHEADER) +
00161                                           (256 * sizeof(WORD))];
00162   }
00163   if (!mBHead)
00164     return NS_ERROR_OUT_OF_MEMORY;
00165 
00166   mBHead->biSize = sizeof(BITMAPINFOHEADER);
00167   mBHead->biWidth = aWidth;
00168   mBHead->biHeight = aHeight;
00169   mBHead->biPlanes = 1;
00170   mBHead->biBitCount = (WORD)aDepth;
00171   mBHead->biCompression = BI_RGB;
00172   mBHead->biSizeImage = 0;     // not compressed, so we dont need this to be set
00173   mBHead->biXPelsPerMeter = 0;
00174   mBHead->biYPelsPerMeter = 0;
00175   mBHead->biClrUsed = mNumPaletteColors;
00176   mBHead->biClrImportant = mNumPaletteColors;
00177 
00178   // Compute the size of the image
00179   mRowBytes = CalcBytesSpan(mBHead->biWidth);
00180   mSizeImage = mRowBytes * mBHead->biHeight; // no compression
00181 
00182   // Allocate the image bits
00183   mImageBits = new unsigned char[mSizeImage];
00184   if (!mImageBits) {
00185     delete[] mBHead;
00186     mBHead = nsnull;
00187     return NS_ERROR_OUT_OF_MEMORY;
00188   }
00189 
00190   // Need to clear the entire buffer so an incrementally loaded image
00191   // will not have garbage rendered for the unloaded bits.
00192 /* XXX: Since there is a performance hit for doing the clear we need
00193    a different solution. For now, we will let garbage be drawn for
00194    incrementally loaded images. Need a solution where only the portion
00195    of the image that has been loaded is asked to draw.
00196     if (mImageBits != nsnull) {
00197       memset(mImageBits, 128, mSizeImage);
00198     }
00199 */
00200 
00201   if (256 == mNumPaletteColors) {
00202     // Initialize the array of indexes into the logical palette
00203     WORD* palIndx = (WORD*)(((LPBYTE)mBHead) + mBHead->biSize);
00204     for (WORD index = 0; index < 256; index++) {
00205       *palIndx++ = index;
00206     }
00207   }
00208 
00209   // Allocate mask image bits if requested
00210   if (aMaskRequirements != nsMaskRequirements_kNoMask) {
00211     if (nsMaskRequirements_kNeeds1Bit == aMaskRequirements) {
00212       mARowBytes = (aWidth + 7) / 8;
00213       mAlphaDepth = 1;
00214     }else{
00215       //NS_ASSERTION(nsMaskRequirements_kNeeds8Bit == aMaskRequirements,
00216       // "unexpected mask depth");
00217       mARowBytes = aWidth;
00218       mAlphaDepth = 8;
00219     }
00220 
00221     // 32-bit align each row
00222     mARowBytes = (mARowBytes + 3) & ~0x3;
00223     mAlphaBits = new unsigned char[mARowBytes * aHeight];
00224     if (!mAlphaBits) {
00225       delete[] mBHead;
00226       mBHead = nsnull;
00227       delete[] mImageBits;
00228       mImageBits = nsnull;
00229       return NS_ERROR_OUT_OF_MEMORY;
00230     }
00231   }
00232 
00233 
00234   // XXX Let's only do this if we actually have a palette...
00235   mColorMap = new nsColorMap;
00236 
00237   if (mColorMap != nsnull) {
00238     mColorMap->NumColors = mNumPaletteColors;
00239     mColorMap->Index = nsnull;
00240     if (mColorMap->NumColors > 0) {
00241       mColorMap->Index = new PRUint8[3 * mColorMap->NumColors];
00242 
00243       // XXX Note: I added this because purify claims that we make a
00244       // copy of the memory (which we do!). I'm not sure if this
00245       // matters or not, but this shutup purify.
00246       memset(mColorMap->Index, 0, sizeof(PRUint8) * (3 * mColorMap->NumColors));
00247     }
00248   }
00249 
00250   mInitialized = PR_TRUE;
00251   return NS_OK;
00252 }
00253 
00254 
00259 void 
00260 nsImageWin :: ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect)
00261 {
00262   mDecodedX1 = PR_MIN(mDecodedX1, aUpdateRect->x);
00263   mDecodedY1 = PR_MIN(mDecodedY1, aUpdateRect->y);
00264 
00265   if (aUpdateRect->YMost() > mDecodedY2)
00266     mDecodedY2 = aUpdateRect->YMost();
00267   if (aUpdateRect->XMost() > mDecodedX2)
00268     mDecodedX2 = aUpdateRect->XMost();
00269 }
00270 
00274 PRBool nsImageWin::GetIsImageComplete() {
00275   return mInitialized &&
00276          mDecodedX1 == 0 &&
00277          mDecodedY1 == 0 &&
00278          mDecodedX2 == mBHead->biWidth &&
00279          mDecodedY2 == mBHead->biHeight;
00280 }
00281 
00282 //------------------------------------------------------------
00283 
00284 struct MONOBITMAPINFO {
00285   BITMAPINFOHEADER  bmiHeader;
00286   RGBQUAD           bmiColors[2];
00287 
00288 
00289   MONOBITMAPINFO(LONG aWidth, LONG aHeight)
00290   {
00291     memset(&bmiHeader, 0, sizeof(bmiHeader));
00292     bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
00293     bmiHeader.biWidth = aWidth;
00294     bmiHeader.biHeight = aHeight;
00295     bmiHeader.biPlanes = 1;
00296     bmiHeader.biBitCount = 1;
00297 
00298 
00299     // Note that the palette is being set up so the DIB and the DDB have white and
00300     // black reversed. This is because we need the mask to have 0 for the opaque
00301     // pixels of the image, and 1 for the transparent pixels. This way the SRCAND
00302     // operation sets the opaque pixels to 0, and leaves the transparent pixels
00303     // undisturbed
00304     bmiColors[0].rgbBlue = 255;
00305     bmiColors[0].rgbGreen = 255;
00306     bmiColors[0].rgbRed = 255;
00307     bmiColors[0].rgbReserved = 0;
00308     bmiColors[1].rgbBlue = 0;
00309     bmiColors[1].rgbGreen = 0;
00310     bmiColors[1].rgbRed = 0;
00311     bmiColors[1].rgbReserved = 0;
00312   }
00313 };
00314 
00315 
00316 struct ALPHA8BITMAPINFO {
00317   BITMAPINFOHEADER  bmiHeader;
00318   RGBQUAD           bmiColors[256];
00319 
00320 
00321   ALPHA8BITMAPINFO(LONG aWidth, LONG aHeight)
00322   {
00323     memset(&bmiHeader, 0, sizeof(bmiHeader));
00324     bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
00325     bmiHeader.biWidth = aWidth;
00326     bmiHeader.biHeight = aHeight;
00327     bmiHeader.biPlanes = 1;
00328     bmiHeader.biBitCount = 8;
00329 
00330 
00331     /* fill in gray scale palette */
00332      int i;
00333      for(i=0; i < 256; i++){
00334       bmiColors[i].rgbBlue = 255-i;
00335       bmiColors[i].rgbGreen = 255-i;
00336       bmiColors[i].rgbRed = 255-i;
00337       bmiColors[1].rgbReserved = 0;
00338      }
00339   }
00340 };
00341 
00342 
00343 struct ALPHA24BITMAPINFO {
00344   BITMAPINFOHEADER  bmiHeader;
00345 
00346 
00347   ALPHA24BITMAPINFO(LONG aWidth, LONG aHeight)
00348   {
00349     memset(&bmiHeader, 0, sizeof(bmiHeader));
00350     bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
00351     bmiHeader.biWidth = aWidth;
00352     bmiHeader.biHeight = aHeight;
00353     bmiHeader.biPlanes = 1;
00354     bmiHeader.biBitCount = 24;
00355   }
00356 };
00357 
00358 
00359 struct ALPHA32BITMAPINFO {
00360   BITMAPINFOHEADER  bmiHeader;
00361 
00362 
00363   ALPHA32BITMAPINFO(LONG aWidth, LONG aHeight)
00364   {
00365     memset(&bmiHeader, 0, sizeof(bmiHeader));
00366     bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
00367     bmiHeader.biWidth = aWidth;
00368     bmiHeader.biHeight = aHeight;
00369     bmiHeader.biPlanes = 1;
00370     bmiHeader.biBitCount = 32;
00371   }
00372 };
00373 
00374 
00375 
00376 static void CompositeBitsInMemory(HDC aTheHDC,
00377                                   int aDX, int aDY,
00378                                   int aDWidth, int aDHeight,
00379                                   int aSX, int aSY,
00380                                   int aSWidth, int aSHeight,
00381                                   PRInt32 aSrcy,
00382                                   PRUint8 *aAlphaBits,
00383                                   MONOBITMAPINFO *aBMI,
00384                                   PRUint8* aImageBits,
00385                                   LPBITMAPINFOHEADER aBHead,
00386                                   PRInt16 aNumPaletteColors);
00387 // Raster op used with MaskBlt(). Assumes our transparency mask has 0 for the
00388 // opaque pixels and 1 for the transparent pixels. That means we want the
00389 // background raster op (value of 0) to be SRCCOPY, and the foreground raster
00390 // (value of 1) to just use the destination bits
00391 #define MASKBLT_ROP MAKEROP4((DWORD)0x00AA0029, SRCCOPY)
00392 
00393 
00394 void nsImageWin::CreateImageWithAlphaBits(HDC TheHDC)
00395 {
00396   unsigned char *imageWithAlphaBits;
00397   ALPHA32BITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
00398   mHBitmap = ::CreateDIBSection(TheHDC, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS,
00399                                 (LPVOID *)&imageWithAlphaBits, NULL, 0);
00400 
00401 
00402   if (!mHBitmap) {
00403     mIsOptimized = PR_FALSE;
00404     return;
00405   }
00406   
00407   if (256 == mNumPaletteColors) {
00408     for (int y = 0; y < mBHead->biHeight; y++) {
00409       unsigned char *imageWithAlphaRow = imageWithAlphaBits + y * mBHead->biWidth * 4;
00410       unsigned char *imageRow = mImageBits + y * mRowBytes;
00411       unsigned char *alphaRow = mAlphaBits + y * mARowBytes;
00412 
00413 
00414       for (int x = 0; x < mBHead->biWidth;
00415            x++, imageWithAlphaRow += 4, imageRow++, alphaRow++) {
00416         FAST_DIVIDE_BY_255(imageWithAlphaRow[0],
00417                            mColorMap->Index[3 * (*imageRow)] * *alphaRow);
00418         FAST_DIVIDE_BY_255(imageWithAlphaRow[1],
00419                            mColorMap->Index[3 * (*imageRow) + 1] * *alphaRow);
00420         FAST_DIVIDE_BY_255(imageWithAlphaRow[2],
00421                            mColorMap->Index[3 * (*imageRow) + 2] * *alphaRow);
00422         imageWithAlphaRow[3] = *alphaRow;
00423       }
00424     }
00425   } else if (mImagePreMultiplied) {
00426     for (int y = 0; y < mBHead->biHeight; y++) {
00427       unsigned char *imageWithAlphaRow = imageWithAlphaBits + y * mBHead->biWidth * 4;
00428       unsigned char *imageRow = mImageBits + y * mRowBytes;
00429       unsigned char *alphaRow = mAlphaBits + y * mARowBytes;
00430 
00431       for (int x = 0; x < mBHead->biWidth;
00432           x++, imageWithAlphaRow += 4, imageRow += 3, alphaRow++) {
00433         memcpy(imageWithAlphaRow, imageRow, 3);
00434         imageWithAlphaRow[3] = *alphaRow;
00435       }
00436     }
00437   } else {
00438     for (int y = 0; y < mBHead->biHeight; y++) {
00439       unsigned char *imageWithAlphaRow = imageWithAlphaBits + y * mBHead->biWidth * 4;
00440       unsigned char *imageRow = mImageBits + y * mRowBytes;
00441       unsigned char *alphaRow = mAlphaBits + y * mARowBytes;
00442 
00443 
00444       for (int x = 0; x < mBHead->biWidth;
00445           x++, imageWithAlphaRow += 4, imageRow += 3, alphaRow++) {
00446         FAST_DIVIDE_BY_255(imageWithAlphaRow[0], imageRow[0] * *alphaRow);
00447         FAST_DIVIDE_BY_255(imageWithAlphaRow[1], imageRow[1] * *alphaRow);
00448         FAST_DIVIDE_BY_255(imageWithAlphaRow[2], imageRow[2] * *alphaRow);
00449         imageWithAlphaRow[3] = *alphaRow;
00450       }
00451     }
00452   }
00453   mIsOptimized = PR_TRUE;
00454 }
00455 
00460 NS_IMETHODIMP 
00461 nsImageWin::Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface,
00462                  PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight,
00463                  PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight)
00464 {
00465   if (mBHead == nsnull || 
00466       aSWidth < 0 || aDWidth < 0 || aSHeight < 0 || aDHeight < 0)
00467     return NS_ERROR_FAILURE;
00468 
00469   if (0 == aSWidth || 0 == aDWidth || 0 == aSHeight || 0 == aDHeight)
00470     return NS_OK;
00471 
00472   if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
00473     return NS_OK;
00474 
00475   PRInt32 origSHeight = aSHeight, origDHeight = aDHeight;
00476   PRInt32 origSWidth = aSWidth, origDWidth = aDWidth;
00477 
00478   // limit the size of the blit to the amount of the image read in
00479   if (aSX + aSWidth > mDecodedX2) {
00480     aDWidth -= ((aSX + aSWidth - mDecodedX2) * origDWidth) / origSWidth;
00481     aSWidth -= (aSX + aSWidth) - mDecodedX2;
00482   }
00483   if (aSX < mDecodedX1) {
00484     aDX += ((mDecodedX1 - aSX) * origDWidth) / origSWidth;
00485     aSX = mDecodedX1;
00486   }
00487 
00488   if (aSY + aSHeight > mDecodedY2) {
00489     aDHeight -= ((aSY + aSHeight - mDecodedY2) * origDHeight) / origSHeight;
00490     aSHeight -= (aSY + aSHeight) - mDecodedY2;
00491   }
00492   if (aSY < mDecodedY1) {
00493     aDY += ((mDecodedY1 - aSY) * origDHeight) / origSHeight;
00494     aSY = mDecodedY1;
00495   }
00496 
00497   if (aDWidth <= 0 || aDHeight <= 0)
00498     return NS_OK;
00499 
00500   // Translate to bottom-up coordinates for the source bitmap
00501   PRInt32 srcy = mBHead->biHeight - (aSY + aSHeight);
00502 
00503   HDC     TheHDC;
00504   ((nsDrawingSurfaceWin *)aSurface)->GetDC(&TheHDC);
00505   if (!TheHDC)
00506     return NS_ERROR_FAILURE;
00507 
00508   // find out if the surface is a printer.
00509   PRInt32 canRaster;
00510   ((nsDrawingSurfaceWin *)aSurface)->GetTECHNOLOGY(&canRaster);
00511 
00512   CreateDDB();
00513 
00514   PRBool didComposite = PR_FALSE;
00515   if (!mIsOptimized || !mHBitmap) {
00516     DWORD rop = SRCCOPY;
00517 
00518     if (mAlphaBits) {
00519       if (1 == mAlphaDepth) {
00520         MONOBITMAPINFO  bmi(mBHead->biWidth, mBHead->biHeight);
00521 
00522         if (canRaster == DT_RASPRINTER) {
00523           CompositeBitsInMemory(TheHDC, aDX, aDY, aDWidth, aDHeight,
00524                                 aSX, aSY, aSWidth, aSHeight,
00525                                 srcy, mAlphaBits, &bmi, mImageBits, mBHead,
00526                                 mNumPaletteColors);
00527           didComposite = PR_TRUE;
00528         } else {
00529           // Put the mask down
00530           ::StretchDIBits(TheHDC, aDX, aDY, aDWidth, aDHeight,
00531                           aSX, srcy, aSWidth, aSHeight, mAlphaBits,
00532                           (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, SRCAND);
00533           rop = SRCPAINT;
00534         }
00535       } else if (8 == mAlphaDepth) {
00536         nsresult rv = DrawComposited(TheHDC, aDX, aDY, aDWidth, aDHeight,
00537                                      aSX, srcy, aSWidth, aSHeight,
00538                                      origDWidth, origDHeight);
00539         if (NS_FAILED(rv)) {
00540           ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
00541           return rv;
00542         }
00543         didComposite = PR_TRUE;
00544       }
00545     } // mAlphaBits
00546 
00547     // Put the Image down
00548     if (!didComposite) {
00549       ::StretchDIBits(TheHDC, aDX, aDY, aDWidth, aDHeight,
00550                       aSX, srcy, aSWidth, aSHeight, mImageBits,
00551                       (LPBITMAPINFO)mBHead, 256 == mNumPaletteColors ? 
00552                       DIB_PAL_COLORS : DIB_RGB_COLORS, rop);
00553     }
00554   } else {
00555     // Optimized.  mHBitmap contains the DDB
00556     DWORD rop = SRCCOPY;
00557 
00558     if (canRaster == DT_RASPRINTER) {
00559       // To Printer
00560       if (mAlphaBits && mAlphaDepth == 1) {
00561         MONOBITMAPINFO  bmi(mBHead->biWidth, mBHead->biHeight);
00562         if (mImageBits) {
00563           CompositeBitsInMemory(TheHDC, aDX, aDY, aDWidth, aDHeight,
00564                                 aSX, aSY, aSWidth, aSHeight, srcy,
00565                                 mAlphaBits, &bmi, mImageBits, mBHead,
00566                                 mNumPaletteColors);
00567           didComposite = PR_TRUE;  
00568         } else {
00569           ConvertDDBtoDIB(); // Create mImageBits
00570           if (mImageBits) {  
00571             CompositeBitsInMemory(TheHDC, aDX, aDY, aDWidth, aDHeight,
00572                                   aSX, aSY, aSWidth, aSHeight, srcy,
00573                                   mAlphaBits,
00574                                   &bmi, mImageBits, mBHead,
00575                                   mNumPaletteColors);
00576             // Clean up the image bits    
00577             delete [] mImageBits;
00578             mImageBits = nsnull;
00579             didComposite = PR_TRUE;         
00580           } else {
00581             NS_WARNING("Could not composite bits in memory because conversion to DIB failed\n");
00582           }
00583         } // mImageBits
00584       } // mAlphaBits && mAlphaDepth == 1
00585 
00586       if (!didComposite && 
00587           (GetDeviceCaps(TheHDC, RASTERCAPS) & (RC_BITBLT | RC_STRETCHBLT)))
00588         PrintDDB(aSurface, aDX, aDY, aDWidth, aDHeight,
00589                  aSX, srcy, aSWidth, aSHeight, rop);
00590 
00591     } else { 
00592       // we are going to the device that created this DDB
00593 
00594       // Get srcDC from the drawing surface of aContext's (RenderingContext)
00595       // DeviceContext.  Should be the same each time.
00596       nsIDeviceContext    *dx;
00597       aContext.GetDeviceContext(dx);
00598       
00599       nsIDrawingSurface*     ds;
00600       NS_STATIC_CAST(nsDeviceContextWin*, dx)->GetDrawingSurface(aContext, ds);
00601 
00602       nsDrawingSurfaceWin *srcDS = (nsDrawingSurfaceWin *)ds;
00603       if (!srcDS) {
00604         NS_RELEASE(dx);
00605         ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
00606         return NS_ERROR_FAILURE;
00607       }      
00608 
00609       HDC                 srcDC;
00610       srcDS->GetDC(&srcDC);
00611 
00612       // Draw the Alpha/Mask
00613       if (mAlphaBits && mAlphaDepth == 1) {
00614         MONOBITMAPINFO  bmi(mBHead->biWidth, mBHead->biHeight);
00615         ::StretchDIBits(TheHDC, aDX, aDY, aDWidth, aDHeight,
00616                         aSX, srcy, aSWidth, aSHeight, mAlphaBits,
00617                         (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, SRCAND);
00618         rop = SRCPAINT;
00619       }
00620 
00621       // Draw the Image
00622       HBITMAP oldBits = (HBITMAP)::SelectObject(srcDC, mHBitmap);
00623       if (8 == mAlphaDepth) {
00624         BLENDFUNCTION blendFunction;
00625         blendFunction.BlendOp = AC_SRC_OVER;
00626         blendFunction.BlendFlags = 0;
00627         blendFunction.SourceConstantAlpha = 255;
00628         blendFunction.AlphaFormat = 1;  // AC_SRC_ALPHA
00629         gAlphaBlend(TheHDC, aDX, aDY, aDWidth, aDHeight, 
00630                     srcDC, aSX, aSY, aSWidth, aSHeight, blendFunction);
00631       } else { 
00632         ::StretchBlt(TheHDC, aDX, aDY, aDWidth, aDHeight, 
00633                      srcDC, aSX, aSY, aSWidth, aSHeight, rop);
00634       }
00635       ::SelectObject(srcDC, oldBits);
00636       srcDS->ReleaseDC();
00637       NS_RELEASE(dx);
00638     }
00639   }
00640   ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
00641 
00642   return NS_OK;
00643 }
00644 
00649 void nsImageWin::DrawComposited24(unsigned char *aBits,
00650                                   PRUint8 *aImageRGB, PRUint32 aStrideRGB,
00651                                   PRUint8 *aImageAlpha, PRUint32 aStrideAlpha,
00652                                   int aWidth, int aHeight)
00653 {
00654   PRInt32 targetRowBytes = ((aWidth * 3) + 3) & ~3;
00655   for (int y = 0; y < aHeight; y++) {
00656     unsigned char *targetRow = aBits + y * targetRowBytes;
00657     unsigned char *imageRow = aImageRGB + y * aStrideRGB;
00658     unsigned char *alphaRow = aImageAlpha + y * aStrideAlpha;
00659 
00660     for (int x = 0; x < aWidth;
00661          x++, targetRow += 3, imageRow += 3, alphaRow++) {
00662       unsigned alpha = *alphaRow;
00663       MOZ_BLEND(targetRow[0], targetRow[0], imageRow[0], alpha);
00664       MOZ_BLEND(targetRow[1], targetRow[1], imageRow[1], alpha);
00665       MOZ_BLEND(targetRow[2], targetRow[2], imageRow[2], alpha);
00666     }
00667   }
00668 }
00669 
00670 
00675 nsresult nsImageWin::DrawComposited(HDC TheHDC, int aDX, int aDY,
00676                                     int aDWidth, int aDHeight,
00677                                     int aSX, int aSY, int aSWidth, int aSHeight,
00678                                     int aOrigDWidth, int aOrigDHeight)
00679 {
00680   HDC memDC = ::CreateCompatibleDC(TheHDC);
00681   if (!memDC)
00682     return NS_ERROR_OUT_OF_MEMORY;
00683   unsigned char *screenBits;
00684 
00685   PRBool scaling = PR_FALSE;
00686   /* Both scaled and unscaled images come through this code */
00687   if ((aDWidth != aSWidth) || (aDHeight != aSHeight)) {
00688     scaling = PR_TRUE;
00689     aDWidth = aOrigDWidth;
00690     aDHeight = aOrigDHeight;
00691     aSWidth = mBHead->biWidth;
00692     aSHeight = mBHead->biHeight;
00693   }
00694 
00695   ALPHA24BITMAPINFO bmi(aDWidth, aDHeight);
00696   HBITMAP tmpBitmap = ::CreateDIBSection(memDC, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS,
00697                                          (LPVOID *)&screenBits, NULL, 0);
00698   if (!tmpBitmap) {
00699     ::DeleteDC(memDC);
00700     NS_WARNING("nsImageWin::DrawComposited failed to create tmpBitmap\n");
00701     return NS_ERROR_OUT_OF_MEMORY;
00702   }
00703   HBITMAP oldBitmap = (HBITMAP)::SelectObject(memDC, tmpBitmap);
00704   if (!oldBitmap || oldBitmap == (HBITMAP)GDI_ERROR) {
00705     ::DeleteObject(tmpBitmap);
00706     ::DeleteDC(memDC);
00707     return NS_ERROR_OUT_OF_MEMORY;
00708   }
00709 
00710   /* Copy from the HDC */
00711   BOOL retval = ::BitBlt(memDC, 0, 0, aDWidth, aDHeight,
00712                          TheHDC, aDX, aDY, SRCCOPY);
00713   if (!retval || !::GdiFlush()) {
00714     /* select the old object again... */
00715     ::SelectObject(memDC, oldBitmap);
00716     ::DeleteObject(tmpBitmap);
00717     ::DeleteDC(memDC);
00718     return NS_ERROR_FAILURE;
00719   }
00720 
00721   PRUint8 *imageRGB, *imageAlpha;
00722   PRUint32 strideRGB, strideAlpha;
00723 
00724   if (scaling) {
00725     /* Scale our image to match */
00726     imageRGB = (PRUint8 *)nsMemory::Alloc(3*aDWidth*aDHeight);
00727     imageAlpha = (PRUint8 *)nsMemory::Alloc(aDWidth*aDHeight);
00728 
00729     if (!imageRGB || !imageAlpha) {
00730       if (imageRGB)
00731         nsMemory::Free(imageRGB);
00732       if (imageAlpha)
00733         nsMemory::Free(imageAlpha);
00734       ::SelectObject(memDC, oldBitmap);
00735       ::DeleteObject(tmpBitmap);
00736       ::DeleteDC(memDC);
00737       return NS_ERROR_FAILURE;
00738     }
00739 
00740     strideRGB = 3 * aDWidth;
00741     strideAlpha = aDWidth;
00742     RectStretch(aSWidth, aSHeight, aDWidth, aDHeight,
00743                 0, 0, aDWidth-1, aDHeight-1,
00744                 mImageBits, mRowBytes, imageRGB, strideRGB, 24);
00745     RectStretch(aSWidth, aSHeight, aDWidth, aDHeight,
00746                 0, 0, aDWidth-1, aDHeight-1,
00747                 mAlphaBits, mARowBytes, imageAlpha, strideAlpha, 8);
00748   } else {
00749     imageRGB = mImageBits + aSY * mRowBytes + aSX * 3;
00750     imageAlpha = mAlphaBits + aSY * mARowBytes + aSX;
00751     strideRGB = mRowBytes;
00752     strideAlpha = mARowBytes;
00753   }
00754 
00755   /* Do composite */
00756   DrawComposited24(screenBits, imageRGB, strideRGB, imageAlpha, strideAlpha,
00757                    aDWidth, aDHeight);
00758 
00759   if (scaling) {
00760     /* Free scaled images */
00761     nsMemory::Free(imageRGB);
00762     nsMemory::Free(imageAlpha);
00763   }
00764 
00765   /* Copy back to the HDC */
00766   /* Use StretchBlt instead of BitBlt here so that we get proper dithering */
00767   if (scaling) {
00768     /* only copy back the valid portion of the image */
00769     retval = ::StretchBlt(TheHDC, aDX, aDY,
00770                           aDWidth, (mDecodedY2*aDHeight + aSHeight - 1)/aSHeight,
00771                           memDC, 0, 0,
00772                           aDWidth, (mDecodedY2*aDHeight + aSHeight - 1)/aSHeight,
00773                           SRCCOPY);
00774   } else {
00775     retval = ::StretchBlt(TheHDC, aDX, aDY, aDWidth, aDHeight,
00776                           memDC, 0, 0, aDWidth, aDHeight, SRCCOPY);
00777   }
00778 
00779   if (!retval) {
00780     ::SelectObject(memDC, oldBitmap);
00781     ::DeleteObject(tmpBitmap);
00782     ::DeleteDC(memDC);
00783     return NS_ERROR_FAILURE;
00784   }
00785 
00786   /* we're done, ignore possible further errors */
00787   ::SelectObject(memDC, oldBitmap);
00788   ::DeleteObject(tmpBitmap);
00789   ::DeleteDC(memDC);
00790   return NS_OK;
00791 }
00792 
00793 
00798 NS_IMETHODIMP nsImageWin :: Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface,
00799          PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
00800 {
00801   return Draw(aContext, aSurface, 0, 0, mBHead->biWidth, mBHead->biHeight, aX, aY, aWidth, aHeight);
00802 }
00803 
00804 
00808 NS_IMETHODIMP nsImageWin::DrawTile(nsIRenderingContext &aContext,
00809                                    nsIDrawingSurface* aSurface,
00810                                    PRInt32 aSXOffset, PRInt32 aSYOffset,
00811                                    PRInt32 aPadX, PRInt32 aPadY,
00812                                    const nsRect &aDestRect)
00813 {
00814   NS_ASSERTION(!aDestRect.IsEmpty(), "DrawTile doesn't work with empty rects");
00815   if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
00816     return NS_OK;
00817 
00818   float           scale;
00819   unsigned char   *targetRow,*imageRow,*alphaRow;
00820   PRInt32         x0, y0, x1, y1, destScaledWidth, destScaledHeight;
00821   PRInt32         validWidth,validHeight,validX,validY,targetRowBytes;
00822   PRInt32         x,y,width,height,canRaster;
00823   nsCOMPtr<nsIDeviceContext> theDeviceContext;
00824   HDC             theHDC;
00825   nscoord         ScaledTileWidth,ScaledTileHeight;
00826   PRBool          padded = (aPadX || aPadY);
00827 
00828   ((nsDrawingSurfaceWin *)aSurface)->GetTECHNOLOGY(&canRaster);
00829   aContext.GetDeviceContext(*getter_AddRefs(theDeviceContext));
00830 
00831   // We can Progressive Double Blit if we aren't printing to a printer, and
00832   // we aren't a 256 color image, and we don't have an unoptimized 8 bit alpha.
00833   if ((canRaster != DT_RASPRINTER) && (256 != mNumPaletteColors) &&
00834       !(mAlphaDepth == 8 && !mIsOptimized) && !padded)
00835     if (ProgressiveDoubleBlit(theDeviceContext, aSurface,
00836                               aSXOffset, aSYOffset, aDestRect))
00837       return NS_OK;
00838   theDeviceContext->GetCanonicalPixelScale(scale);
00839 
00840   destScaledWidth  = PR_MAX(PRInt32(mBHead->biWidth*scale), 1);
00841   destScaledHeight = PR_MAX(PRInt32(mBHead->biHeight*scale), 1);
00842   
00843   validX = 0;
00844   validY = 0;
00845   validWidth  = mBHead->biWidth;
00846   validHeight = mBHead->biHeight;
00847   
00848   // limit the image rectangle to the size of the image data which
00849   // has been validated.
00850   if (mDecodedY2 < mBHead->biHeight) {
00851     validHeight = mDecodedY2 - mDecodedY1;
00852     destScaledHeight = PR_MAX(PRInt32(validHeight*scale), 1);
00853   }
00854   if (mDecodedX2 < mBHead->biWidth) {
00855     validWidth = mDecodedX2 - mDecodedX1;
00856     destScaledWidth = PR_MAX(PRInt32(validWidth*scale), 1);
00857   }
00858   if (mDecodedY1 > 0) {   
00859     validHeight -= mDecodedY1;
00860     destScaledHeight = PR_MAX(PRInt32(validHeight*scale), 1);
00861     validY = mDecodedY1;
00862   }
00863   if (mDecodedX1 > 0) {
00864     validWidth -= mDecodedX1;
00865     destScaledWidth = PR_MAX(PRInt32(validWidth*scale), 1);
00866     validX = mDecodedX1; 
00867   }
00868 
00869   // put the DestRect into absolute coordintes of the device
00870   y0 = aDestRect.y - aSYOffset;
00871   x0 = aDestRect.x - aSXOffset;
00872   y1 = aDestRect.y + aDestRect.height;
00873   x1 = aDestRect.x + aDestRect.width;
00874 
00875 
00876   // this is the width and height of the image in pixels
00877   // we need to map this to the pixel height of the device
00878   ScaledTileWidth = PR_MAX(PRInt32(mBHead->biWidth*scale), 1);
00879   ScaledTileHeight = PR_MAX(PRInt32(mBHead->biHeight*scale), 1);
00880 
00881   // do alpha depth equal to 8 here.. this needs some special attention
00882   if (mAlphaDepth == 8 && !mIsOptimized && !padded) {
00883     unsigned char *screenBits=nsnull,*adjAlpha,*adjImage,*adjScreen;
00884     HDC           memDC=nsnull;
00885     HBITMAP       tmpBitmap=nsnull,oldBitmap;
00886     unsigned char alpha;
00887     PRInt32       targetBytesPerPixel,imageBytesPerPixel;
00888 
00889     if (!mImageBits) {
00890       ConvertDDBtoDIB();
00891     }
00892 
00893     // draw the alpha and the bitmap to an offscreen buffer.. for the blend.. first 
00894     ((nsDrawingSurfaceWin *)aSurface)->GetDC(&theHDC);
00895     if (theHDC) {
00896     // create a buffer for the blend            
00897       memDC = CreateCompatibleDC(theHDC);
00898       width = aDestRect.width;
00899       height = aDestRect.height;
00900 
00901       ALPHA24BITMAPINFO bmi(width, height);
00902       tmpBitmap = ::CreateDIBSection(memDC, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, (LPVOID *)&screenBits, NULL, 0);
00903       oldBitmap = (HBITMAP)::SelectObject(memDC, tmpBitmap);
00904 
00905       // number of bytes in a row on a 32 bit boundary
00906       targetRowBytes = (bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) >> 5;  // number of 32 bit longs
00907       if (((PRUint32)bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) & 0x1F) { // make sure its a multiple of 32
00908         targetRowBytes++;     // or else there will not be enough bytes per line
00909       }
00910       targetRowBytes <<= 2;   // divide by 4 to get the number of bytes from the number of 32 bit longs
00911 
00912       targetBytesPerPixel = bmi.bmiHeader.biBitCount/8;
00913     }
00914 
00915     if (!tmpBitmap) {
00916       if (memDC) {
00917         ::DeleteDC(memDC);
00918       }
00919       // this failed..and will fall into the slow blitting code
00920       NS_WARNING("The Creation of the tmpBitmap failed \n");
00921     } else {
00922       // Copy from the HDC to the memory DC
00923       // this will be the image on the screen into a buffer for the blend.
00924       ::StretchBlt(memDC, 0, 0, width, height,theHDC, aDestRect.x, aDestRect.y, width, height, SRCCOPY);
00925       ::GdiFlush();
00926   
00927       imageBytesPerPixel = mBHead->biBitCount/8;
00928 
00929       // windows bitmaps start at the bottom.. and go up.  This messes up the offsets for the
00930       // image and tiles.. so I reverse the the direction.. to go (the normal way) from top to bottom.
00931       adjScreen = screenBits + ((height-1) * targetRowBytes);
00932       adjImage = mImageBits + ((validHeight-1) * mRowBytes);
00933       adjAlpha = mAlphaBits + ((validHeight-1) * mARowBytes);
00934 
00935 
00936       for (int y = 0,byw=aSYOffset; y < height; y++,byw++) {
00937 
00938         if (byw >= ScaledTileHeight) {
00939           byw = 0;
00940         }
00941 
00942         targetRow = adjScreen - (y * targetRowBytes);
00943         imageRow = adjImage - (byw * mRowBytes);
00944         alphaRow = adjAlpha - (byw * mARowBytes);
00945 
00946         // we only need this adjustment at the beginning of each row
00947         imageRow += (aSXOffset*imageBytesPerPixel);
00948         alphaRow += aSXOffset;
00949 
00950         for (int x=0,bxw=aSXOffset;x<width;x++,targetRow+=targetBytesPerPixel,imageRow+=imageBytesPerPixel,bxw++, alphaRow++) {
00951           // if we went past the row width of our buffer.. go back and start again
00952           if (bxw>=ScaledTileWidth) {
00953             bxw = 0;
00954             imageRow = adjImage - (byw * mRowBytes);
00955             alphaRow = adjAlpha - (byw * mARowBytes);
00956           }
00957 
00958           alpha = *alphaRow;
00959 
00960           MOZ_BLEND(targetRow[0], targetRow[0], imageRow[0], alpha);
00961           MOZ_BLEND(targetRow[1], targetRow[1], imageRow[1], alpha);
00962           MOZ_BLEND(targetRow[2], targetRow[2], imageRow[2], alpha);
00963         }
00964       }
00965 
00966       // copy the blended image back to the screen
00967       ::StretchBlt(theHDC, aDestRect.x, aDestRect.y, width, height,memDC, 0, 0, width, height, SRCCOPY);
00968 
00969       ::SelectObject(memDC, oldBitmap);
00970       ::DeleteObject(tmpBitmap);
00971       ::DeleteDC(memDC);
00972   
00973     return(NS_OK);
00974     } 
00975   }
00976 
00977   // if we got to this point.. everything else failed.. and the slow blit backstop
00978   // will finish this tiling
00979   for (y=y0;y<y1;y+=ScaledTileHeight+aPadY*scale) {
00980     for (x=x0;x<x1;x+=ScaledTileWidth+aPadX*scale) {
00981     Draw(aContext, aSurface,
00982          0, 0, PR_MIN(validWidth, x1-x), PR_MIN(validHeight, y1-y),
00983          x, y, PR_MIN(destScaledWidth, x1-x), PR_MIN(destScaledHeight, y1-y));
00984     }
00985   } 
00986   return(NS_OK);
00987 }
00988 
00992 PRBool
00993 nsImageWin::ProgressiveDoubleBlit(nsIDeviceContext *aContext,
00994                                   nsIDrawingSurface* aSurface,
00995                                   PRInt32 aSXOffset, PRInt32 aSYOffset,
00996                                   nsRect aDestRect)
00997 {
00998   /*
00999     (aSXOffset, aSYOffset) is the offset into our image that we want to start
01000     drawing from.  We start drawing at (aDestRect.x, aDestRect.y)
01001 
01002     - Find first full tile
01003     - progressivly double blit until end of tile area.  What's left will be at
01004       maximum a strip on the top and a strip on the left side that has not been
01005       blitted.
01006     Then, in no particular order:
01007     - blit the top strip using a row we already put down
01008     - blit the left strip using a column we already put down
01009     - blit the very topleft, since it will be left out
01010   */
01011 
01012   HDC theHDC;
01013   void *screenBits; // We never use the bits, but we need to pass a variable in
01014                     // to create a DIB
01015 
01016   ((nsDrawingSurfaceWin *)aSurface)->GetDC(&theHDC);
01017   if (!theHDC)
01018     return PR_FALSE;
01019 
01020   // create an imageDC
01021   HDC imgDC = ::CreateCompatibleDC(theHDC);
01022   if (!imgDC) {
01023     ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
01024     return PR_FALSE;
01025   }
01026 
01027   CreateDDB();
01028 
01029   nsPaletteInfo palInfo;
01030   aContext->GetPaletteInfo(palInfo);
01031   if (palInfo.isPaletteDevice && palInfo.palette) {
01032 #ifndef WINCE
01033     ::SetStretchBltMode(imgDC, HALFTONE);
01034 #endif
01035     ::SelectPalette(imgDC, (HPALETTE)palInfo.palette, TRUE);
01036     ::RealizePalette(imgDC);
01037   }
01038 
01039   // Create a maskDC, and fill it with mAlphaBits
01040   HDC maskDC = nsnull;
01041   HBITMAP oldImgMaskBits = nsnull;
01042   HBITMAP maskBits;
01043   HBITMAP mTmpHBitmap = nsnull;
01044   if (mAlphaDepth == 1) {
01045     maskDC = ::CreateCompatibleDC(theHDC);
01046     if (!maskDC) {
01047       ::DeleteDC(imgDC);
01048       ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
01049       return PR_FALSE;
01050     }
01051 
01052     MONOBITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
01053     maskBits  = ::CreateDIBSection(theHDC, (LPBITMAPINFO)&bmi,
01054                                    DIB_RGB_COLORS, &screenBits, NULL, 0);
01055     if (!maskBits) {
01056       ::DeleteDC(imgDC);
01057       ::DeleteDC(maskDC);
01058       ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
01059       return PR_FALSE;
01060     }
01061 
01062     oldImgMaskBits = (HBITMAP)::SelectObject(maskDC, maskBits);
01063     ::SetDIBitsToDevice(maskDC, 0, 0, mBHead->biWidth, mBHead->biHeight,
01064                         0, 0, 0, mBHead->biHeight, mAlphaBits,
01065                         (LPBITMAPINFO)&bmi, DIB_RGB_COLORS);
01066   }
01067 
01068 
01069   // Fill imageDC with our image
01070   HBITMAP oldImgBits = nsnull;
01071   if (!mIsOptimized || !mHBitmap) {
01072     // Win 95/98/ME can't do > 0xFF0000 DDBs.
01073     if (gPlatform == VER_PLATFORM_WIN32_WINDOWS) {
01074       int bytesPerPix = ::GetDeviceCaps(imgDC, BITSPIXEL) / 8;
01075       if (mBHead->biWidth * mBHead->biHeight * bytesPerPix > 0xFF0000) {
01076         ::DeleteDC(imgDC);
01077         if (maskDC) {
01078           if (oldImgMaskBits)
01079             ::SelectObject(maskDC, oldImgMaskBits);
01080           ::DeleteObject(maskBits);
01081           ::DeleteDC(maskDC);
01082         }
01083         ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
01084         return PR_FALSE;
01085       }
01086     }
01087     mTmpHBitmap = ::CreateCompatibleBitmap(theHDC, mDecodedX2, mDecodedY2);
01088     if (!mTmpHBitmap) {
01089       ::DeleteDC(imgDC);
01090       if (maskDC) {
01091         if (oldImgMaskBits)
01092           ::SelectObject(maskDC, oldImgMaskBits);
01093         ::DeleteObject(maskBits);
01094         ::DeleteDC(maskDC);
01095       }
01096       ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
01097       return PR_FALSE;
01098     }
01099     oldImgBits = (HBITMAP)::SelectObject(imgDC, mTmpHBitmap);
01100     ::StretchDIBits(imgDC, 0, 0, mBHead->biWidth, mBHead->biHeight,
01101                     0, 0, mBHead->biWidth, mBHead->biHeight,
01102                     mImageBits, (LPBITMAPINFO)mBHead,
01103                     256 == mNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
01104                     SRCCOPY);
01105   } else {
01106     oldImgBits = (HBITMAP)::SelectObject(imgDC, mHBitmap);
01107   }
01108 
01109   PRBool result = PR_TRUE;
01110   PRBool useAlphaBlend = mAlphaDepth == 8 && mIsOptimized;
01111 
01112   PRInt32 firstWidth = mBHead->biWidth - aSXOffset;
01113   PRInt32 firstHeight = mBHead->biHeight - aSYOffset;
01114 
01115   if (aDestRect.width > firstWidth + mBHead->biWidth ||
01116       aDestRect.height > firstHeight + mBHead->biHeight) {
01117     PRInt32 firstPartialWidth = aSXOffset == 0 ? 0 : firstWidth;
01118     PRInt32 firstPartialHeight = aSYOffset == 0 ? 0 : firstHeight;
01119     PRBool hasFullImageWidth = aDestRect.width - firstPartialWidth >= mBHead->biWidth;
01120     PRBool hasFullImageHeight = aDestRect.height - firstPartialHeight >= mBHead->biHeight;
01121 
01122     HDC dstDC = theHDC;               // Defaulting to drawing to theHDC
01123     HDC dstMaskDC = nsnull;           // No Alpha DC by default
01124     HBITMAP dstMaskHBitmap = nsnull;  // No Alpha HBITMAP by default either..
01125     HBITMAP oldDstBits, oldDstMaskBits;
01126     HBITMAP dstHBitmap;
01127 
01128     do {  // Use a do to help with cleanup
01129       nsRect storedDstRect;
01130 
01131       // dstDC will only be used if we have an alpha.  It is not needed for
01132       // images without an alpha because we can directly doubleblit onto theHDC
01133       if (mAlphaDepth != 0) {
01134         // We have an alpha, which means we can't p. doubleblit directly onto
01135         // theHDC.  We must create a temporary DC, p. doubleblit into that, and
01136         // then draw the whole thing to theHDC
01137 
01138         storedDstRect = aDestRect;
01139         aDestRect.x = 0;
01140         aDestRect.y = 0;
01141         // The last p. doubleblit is very slow.. it's faster to draw to theHDC
01142         // twice.  So only progressive doubleblit up to 1/2 of the tiling area
01143         // that requires full tiles.
01144         if (aDestRect.height - firstPartialHeight >= mBHead->biHeight * 2)
01145           aDestRect.height = PR_ROUNDUP(PRInt32(ceil(aDestRect.height / 2.0)),
01146                                         mBHead->biHeight)
01147                              + firstPartialHeight;
01148 
01149         // Win 95/98/ME can't do > 0xFF0000 DDBs.
01150         // Note: This should be extremely rare.  We'd need a tile area (before
01151         //       it was split in 1/2 above) of 2048x4080 @ 4 bytesPerPix before
01152         //       we hit this.
01153         if (gPlatform == VER_PLATFORM_WIN32_WINDOWS) {
01154           int bytesPerPix = (mAlphaDepth == 8) ? 4 :
01155                             ::GetDeviceCaps(theHDC, BITSPIXEL) / 8;
01156           if (aDestRect.width * aDestRect.height * bytesPerPix > 0xFF0000) {
01157             result = PR_FALSE;
01158             break;
01159           }
01160         }
01161 
01162         // make a temporary offscreen DC
01163         dstDC = ::CreateCompatibleDC(theHDC);
01164         if (!dstDC) {
01165           result = PR_FALSE;
01166           break;
01167         }
01168 
01169         // Create HBITMAP for the new DC
01170         if (mAlphaDepth == 8) {
01171           // Create a bitmap of 1 plane, 32 bit color depth
01172           // Can't use ::CreateBitmap, since the resulting HBITMAP will only be
01173           // able to be selected into a compatible HDC (ie. User is @ 24 bit, you
01174           // can't select the 32 HBITMAP into the HDC.)
01175           ALPHA32BITMAPINFO bmi(aDestRect.width, aDestRect.height);
01176           dstHBitmap = ::CreateDIBSection(theHDC, (LPBITMAPINFO)&bmi,
01177                                           DIB_RGB_COLORS, &screenBits, NULL, 0);
01178         } else {
01179           // Create a normal bitmap for the image, and a monobitmap for alpha
01180           dstHBitmap = ::CreateCompatibleBitmap(theHDC, aDestRect.width,
01181                                                 aDestRect.height);
01182           if (dstHBitmap) {
01183             dstMaskDC = ::CreateCompatibleDC(theHDC);
01184             if (dstMaskDC) {
01185               MONOBITMAPINFO bmi(aDestRect.width, aDestRect.height);
01186               dstMaskHBitmap = ::CreateDIBSection(theHDC, (LPBITMAPINFO)&bmi,
01187                                                   DIB_RGB_COLORS, &screenBits,
01188                                                   NULL, 0);
01189               if (dstMaskHBitmap) {
01190                 oldDstMaskBits = (HBITMAP)::SelectObject(dstMaskDC,
01191                                                          dstMaskHBitmap);
01192               } else {
01193                 result = PR_FALSE;
01194                 break;
01195               }
01196             } // dstMaskDC
01197           } // dstHBitmap
01198         }
01199         if (!dstHBitmap) {
01200           result = PR_FALSE;
01201           break;
01202         }
01203 
01204         oldDstBits = (HBITMAP)::SelectObject(dstDC, dstHBitmap);
01205       } // (mAlphaDepth != 0)
01206 
01207 
01208       PRInt32 imgX = hasFullImageWidth ? 0 : aSXOffset;
01209       PRInt32 imgW = PR_MIN(mBHead->biWidth - imgX, aDestRect.width);
01210       PRInt32 imgY = hasFullImageHeight ? 0 : aSYOffset;
01211       PRInt32 imgH = PR_MIN(mBHead->biHeight - imgY, aDestRect.height);
01212 
01213       // surfaceDestPoint is the first position in the destination where we will
01214       // be putting (0,0) of our image down.
01215       nsPoint surfaceDestPoint(aDestRect.x, aDestRect.y);
01216       if (aSXOffset != 0 && hasFullImageWidth)
01217         surfaceDestPoint.x += firstWidth;
01218       if (aSYOffset != 0 && hasFullImageHeight)
01219         surfaceDestPoint.y += firstHeight;
01220 
01221       // Plunk one down
01222       BlitImage(dstDC, dstMaskDC,
01223                 surfaceDestPoint.x, surfaceDestPoint.y, imgW, imgH,
01224                 imgDC, maskDC, imgX, imgY, PR_FALSE);
01225       if (!hasFullImageHeight && imgH != aDestRect.height) {
01226         // Since we aren't drawing a full image in height, there's an area above
01227         // that we can blit to too.
01228         BlitImage(dstDC, dstMaskDC, surfaceDestPoint.x, surfaceDestPoint.y + imgH,
01229                           imgW, aDestRect.height - imgH,
01230                   imgDC, maskDC, imgX, 0, PR_FALSE);
01231         imgH = aDestRect.height;
01232       }
01233       if (!hasFullImageWidth && imgW != aDestRect.width) {
01234         BlitImage(dstDC, dstMaskDC, surfaceDestPoint.x + imgW, surfaceDestPoint.y,
01235                           aDestRect.width - imgW, imgH,
01236                   imgDC, maskDC, 0, imgY, PR_FALSE);
01237         imgW = aDestRect.width;
01238       }
01239 
01240 
01241       nsRect surfaceDestRect;
01242       nsRect surfaceSrcRect(surfaceDestPoint.x, surfaceDestPoint.y, imgW, imgH);
01243 
01244       // Progressively DoubleBlit a Row
01245       if (hasFullImageWidth) {
01246         while (surfaceSrcRect.XMost() < aDestRect.XMost()) {
01247           surfaceDestRect.x = surfaceSrcRect.XMost();
01248           surfaceDestRect.width = surfaceSrcRect.width;
01249           if (surfaceDestRect.XMost() >= aDestRect.XMost())
01250             surfaceDestRect.width = aDestRect.XMost() - surfaceDestRect.x;
01251 
01252           BlitImage(dstDC, dstMaskDC, surfaceDestRect.x, surfaceSrcRect.y,
01253                             surfaceDestRect.width, surfaceSrcRect.height,
01254                     dstDC, dstMaskDC, surfaceSrcRect.x, surfaceSrcRect.y,
01255                     PR_FALSE);
01256           surfaceSrcRect.width += surfaceDestRect.width;
01257         }
01258       }
01259 
01260       // Progressively DoubleBlit a column
01261       if (hasFullImageHeight) {
01262         while (surfaceSrcRect.YMost() < aDestRect.YMost()) {
01263           surfaceDestRect.y = surfaceSrcRect.YMost();
01264           surfaceDestRect.height = surfaceSrcRect.height;
01265           if (surfaceDestRect.YMost() >= aDestRect.YMost())
01266             surfaceDestRect.height = aDestRect.YMost() - surfaceDestRect.y;
01267 
01268           BlitImage(dstDC, dstMaskDC, surfaceSrcRect.x, surfaceDestRect.y,
01269                             surfaceSrcRect.width, surfaceDestRect.height,
01270                     dstDC, dstMaskDC, surfaceSrcRect.x, surfaceSrcRect.y,
01271                     PR_FALSE);
01272           surfaceSrcRect.height += surfaceDestRect.height;
01273         }
01274       }
01275 
01276       // blit to topleft.  This could be done before p doubleblitting
01277       if (surfaceDestPoint.y != aDestRect.y && surfaceDestPoint.x != aDestRect.x)
01278           BlitImage(dstDC, dstMaskDC, aDestRect.x, aDestRect.y,
01279                     firstWidth, firstHeight,
01280                     dstDC, dstMaskDC,
01281                     surfaceDestPoint.x + aSXOffset, surfaceDestPoint.y + aSYOffset,
01282                     PR_FALSE);
01283       // blit top row
01284       if (surfaceDestPoint.y != aDestRect.y)
01285           BlitImage(dstDC, dstMaskDC, surfaceDestPoint.x, aDestRect.y,
01286                     surfaceSrcRect.width, firstHeight,
01287                     dstDC, dstMaskDC, surfaceDestPoint.x, surfaceDestPoint.y + aSYOffset,
01288                     PR_FALSE);
01289 
01290       // blit left column
01291       if (surfaceDestPoint.x != aDestRect.x)
01292         BlitImage(dstDC, dstMaskDC, aDestRect.x, surfaceDestPoint.y,
01293                   firstWidth, surfaceSrcRect.height,
01294                   dstDC, dstMaskDC, surfaceDestPoint.x + aSXOffset, surfaceDestPoint.y,
01295                   PR_FALSE);
01296 
01297       if (mAlphaDepth != 0) {
01298         // blit temporary dstDC (& dstMaskDC, if one exists) to theHDC
01299         BlitImage(theHDC, theHDC, storedDstRect.x, storedDstRect.y,
01300                   aDestRect.width, aDestRect.height,
01301                   dstDC, dstMaskDC, 0, 0, useAlphaBlend);
01302         // blit again to 2nd part of tile area
01303         if (storedDstRect.height > aDestRect.height)
01304           BlitImage(theHDC, theHDC, storedDstRect.x, storedDstRect.y + aDestRect.height,
01305                     aDestRect.width, storedDstRect.height - aDestRect.height,
01306                     dstDC, dstMaskDC, 0, firstPartialHeight, useAlphaBlend);
01307       }
01308     } while (PR_FALSE);
01309 
01310     if (mAlphaDepth != 0) {
01311       if (dstDC) {
01312         if (dstHBitmap) {
01313           ::SelectObject(dstDC, oldDstBits);
01314           ::DeleteObject(dstHBitmap);
01315         }
01316         ::DeleteDC(dstDC);
01317       }
01318       if (dstMaskDC) {
01319         if (dstMaskHBitmap) {
01320           ::SelectObject(dstMaskDC, oldDstMaskBits);
01321           ::DeleteObject(dstMaskHBitmap);
01322         }
01323         ::DeleteDC(dstMaskDC);
01324       }
01325     }
01326   } else {
01327     // up to 4 image parts.
01328     // top-left
01329     BlitImage(theHDC, theHDC,
01330               aDestRect.x, aDestRect.y, firstWidth, firstHeight,
01331               imgDC, maskDC, aSXOffset, aSYOffset, useAlphaBlend);
01332 
01333     // bottom-right
01334     if (aDestRect.width - firstWidth > 0 && aDestRect.height - firstHeight > 0)
01335       BlitImage(theHDC, theHDC,
01336                 aDestRect.x + firstWidth, aDestRect.y + firstHeight,
01337                 aDestRect.width - firstWidth, aDestRect.height - firstHeight,
01338                 imgDC, maskDC, 0, 0, useAlphaBlend);
01339 
01340     // bottom-left
01341     if (aDestRect.height - firstHeight > 0)
01342       BlitImage(theHDC, theHDC, aDestRect.x, aDestRect.y + firstHeight,
01343                 firstWidth, aDestRect.height - firstHeight,
01344                 imgDC, maskDC, aSXOffset, 0, useAlphaBlend);
01345 
01346     // top-right
01347     if (aDestRect.width - firstWidth > 0)
01348       BlitImage(theHDC, theHDC, aDestRect.x + firstWidth, aDestRect.y,
01349                 aDestRect.width - firstWidth, firstHeight,
01350                 imgDC, maskDC, 0, aSYOffset, useAlphaBlend);
01351   }
01352 
01353   if (oldImgBits)
01354     ::SelectObject(imgDC, oldImgBits);
01355   if (mTmpHBitmap)
01356     ::DeleteObject(mTmpHBitmap);
01357   ::DeleteDC(imgDC);
01358   if (maskDC) {
01359     if (oldImgMaskBits)
01360       ::SelectObject(maskDC, oldImgMaskBits);
01361     ::DeleteObject(maskBits);
01362     ::DeleteDC(maskDC);
01363   }
01364   ((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
01365 
01366   return result;
01367 }
01368 
01369 void
01370 nsImageWin::BlitImage(HDC aDstDC, HDC aDstMaskDC, PRInt32 aDstX, PRInt32 aDstY,
01371                       PRInt32 aWidth, PRInt32 aHeight,
01372                       HDC aSrcDC, HDC aSrcMaskDC, PRInt32 aSrcX, PRInt32 aSrcY,
01373                       PRBool aUseAlphaBlend)
01374 {
01375   if (aUseAlphaBlend) {
01376     BLENDFUNCTION blendFunction;
01377     blendFunction.BlendOp = AC_SRC_OVER;
01378     blendFunction.BlendFlags = 0;
01379     blendFunction.SourceConstantAlpha = 255;
01380     blendFunction.AlphaFormat = 1;
01381     gAlphaBlend(aDstDC, aDstX, aDstY, aWidth, aHeight,
01382                 aSrcDC, aSrcX, aSrcY, aWidth, aHeight, blendFunction);
01383   } else {
01384     if (aSrcMaskDC) {
01385       if (aDstMaskDC == aDstDC) {
01386         ::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
01387                  aSrcMaskDC, aSrcX, aSrcY, SRCAND);
01388         ::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
01389                  aSrcDC, aSrcX, aSrcY, SRCPAINT);
01390       } else {
01391         ::BitBlt(aDstMaskDC, aDstX, aDstY, aWidth, aHeight,
01392                  aSrcMaskDC, aSrcX, aSrcY, SRCCOPY);
01393         ::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
01394                   aSrcDC, aSrcX, aSrcY, SRCCOPY);
01395       }
01396     } else {
01397       ::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
01398                aSrcDC, aSrcX, aSrcY, SRCCOPY);
01399     }
01400   }
01401 }
01402 
01403 
01404 ALPHABLENDPROC nsImageWin::gAlphaBlend = NULL;
01405 
01406 
01407 PRBool nsImageWin::CanAlphaBlend(void)
01408 {
01409 #ifdef WINCE
01410   gAlphaBlend = nsnull;
01411   return PR_FALSE;
01412 #else
01413   static PRBool alreadyChecked = PR_FALSE;
01414 
01415   if (!alreadyChecked) {
01416     OSVERSIONINFO os;
01417     
01418     os.dwOSVersionInfoSize = sizeof(os);
01419     ::GetVersionEx(&os);
01420     // If running on Win98, disable using AlphaBlend()
01421     // to avoid Win98 AlphaBlend() bug
01422     if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId ||
01423         os.dwMajorVersion != 4 || os.dwMinorVersion != 10) {
01424       gAlphaBlend = (ALPHABLENDPROC)::GetProcAddress(::LoadLibrary("msimg32"),
01425               "AlphaBlend");
01426     }
01427     alreadyChecked = PR_TRUE;
01428   }
01429 
01430   return gAlphaBlend != NULL;
01431 #endif
01432 }
01433 
01434 
01440 nsresult nsImageWin::Optimize(nsIDeviceContext* aContext)
01441 {
01442   // Do the actual optimizing when we first use the image (draw it)
01443   // This saves on GDI resources.
01444   mWantsOptimization = PR_TRUE;
01445   return NS_OK;
01446 }
01447 
01448 // CreateDDB only if DDB is wanted
01449 NS_IMETHODIMP nsImageWin::CreateDDB()
01450 {
01451   if (!mWantsOptimization || mIsOptimized) {
01452     // Timer only exists when mIsOptimized.  Push timer forwards.
01453     if (mTimer)
01454       mTimer->SetDelay(GFX_MS_REMOVE_DBB);
01455 
01456     return NS_OK;
01457   }
01458 
01459   // we used to set a flag because a valid HDC may not be ready, 
01460   // like at startup, but now we just roll our own HDC for the given screen.
01461 
01462   //
01463   // Some images should not be converted to DDB...
01464   //
01465   // Windows 95/98/Me: DDB size cannot exceed ~16MB in size.
01466   // We also can not optimize empty images, or 8-bit alpha depth images on
01467   // Win98 due to a Windows API bug.
01468   //
01469   // On Windows 95/98/Me, GDI resources are very limited.  Windows NT has more,
01470   // but is still rather limited. Create DDBs on these platforms only for
01471   // large (> 128k) images to minimize GDI handle usage.
01472   // See bug 205893, bug 204374, and bug 216430 for more info on the situation.
01473   // Bottom-line: we need a better accounting mechanism to avoid exceeding the
01474   // system's GDI object limit.  The rather arbitrary size limitation imposed
01475   // here is based on the typical size of the Mozilla memory cache.  This is 
01476   // most certainly the wrong place to impose this policy, but we do it for
01477   // now as a stop-gap measure.
01478   //
01479   if ((gOsMajorVersion <= VER_OSMAJOR_WIN9598MENT &&
01480        (mSizeImage >= 0xFF0000 || mSizeImage < 0x20000)) ||
01481       (mAlphaDepth == 8 && !CanAlphaBlend())) {
01482     return NS_OK;
01483   }
01484 
01485   HDC TheHDC = ::CreateCompatibleDC(NULL);
01486   
01487   if (TheHDC != NULL){
01488     // Temporary fix for bug 135226 until we do better decode-time
01489     // dithering and paletized storage of images. Bail on the 
01490     // optimization to DDB if we're on a paletted device.
01491     int rasterCaps = ::GetDeviceCaps(TheHDC, RASTERCAPS);
01492     if (RC_PALETTE == (rasterCaps & RC_PALETTE)) {
01493       ::DeleteDC(TheHDC);
01494       return NS_OK;
01495     }
01496   
01497     // we have to install the correct bitmap to get a good DC going
01498     int planes = ::GetDeviceCaps(TheHDC, PLANES);
01499     int bpp = ::GetDeviceCaps(TheHDC, BITSPIXEL);
01500 
01501     HBITMAP  tBitmap = ::CreateBitmap(1,1,planes,bpp,NULL);
01502     HBITMAP oldbits = (HBITMAP)::SelectObject(TheHDC,tBitmap);
01503 
01504     if (mAlphaDepth == 8) {
01505       CreateImageWithAlphaBits(TheHDC);
01506     } else {
01507       // Apparently, DIBs use less resources than DDBs.
01508       // On Windows 95/98/Me/NT, use DIBs to save resources.  On remaining
01509       // platforms, use DDBs.  DDBs draw at the same speed or faster when
01510       // drawn to a DDB surface.  This is another temporary solution until
01511       // we manage our resources better.
01512       if (gOsMajorVersion <= VER_OSMAJOR_WIN9598MENT) {
01513         LPVOID bits;
01514         mHBitmap = ::CreateDIBSection(TheHDC, (LPBITMAPINFO)mBHead,
01515           256 == mNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
01516           &bits, NULL, 0);
01517 
01518         if (mHBitmap) {
01519           memcpy(bits, mImageBits, mSizeImage);
01520           mIsOptimized = PR_TRUE;
01521         } else {
01522           mIsOptimized = PR_FALSE;
01523         }
01524       } else {
01525         mHBitmap = ::CreateDIBitmap(TheHDC, mBHead, CBM_INIT, mImageBits,
01526                                     (LPBITMAPINFO)mBHead,
01527                                     256 == mNumPaletteColors ? DIB_PAL_COLORS
01528                                                              : DIB_RGB_COLORS);
01529         mIsOptimized = (mHBitmap != 0);
01530       }
01531     }
01532     if (mIsOptimized) {
01533       ::GdiFlush();
01534       CleanUpDIB();
01535 
01536       if (mTimer) {
01537         mTimer->SetDelay(GFX_MS_REMOVE_DBB);
01538       } else {
01539         mTimer = do_CreateInstance("@mozilla.org/timer;1");
01540         if (mTimer)
01541           mTimer->InitWithFuncCallback(nsImageWin::TimerCallBack, this,
01542                                        GFX_MS_REMOVE_DBB,
01543                                        nsITimer::TYPE_ONE_SHOT);
01544       }
01545     }
01546     ::SelectObject(TheHDC,oldbits);
01547     ::DeleteObject(tBitmap);
01548     ::DeleteDC(TheHDC);
01549   }
01550 
01551   return NS_OK;
01552 }
01553 
01554 // Removes the DBB, restoring the imagebits if necessary
01555 NS_IMETHODIMP nsImageWin::RemoveDDB()
01556 {
01557   if (!mIsOptimized && mHBitmap == nsnull)
01558     return NS_OK;
01559 
01560   if (!mImageBits) {
01561     nsresult rv = ConvertDDBtoDIB();
01562     if (NS_FAILED(rv))
01563       return rv;
01564   }
01565 
01566   CleanUpDDB();
01567   return NS_OK;
01568 }
01569 
01570 
01577 PRInt32  
01578 nsImageWin :: CalcBytesSpan(PRUint32  aWidth)
01579 {
01580   PRInt32 spanBytes;
01581 
01582 
01583   spanBytes = (aWidth * mBHead->biBitCount) >> 5;
01584 
01585 
01586   if (((PRUint32)mBHead->biWidth * mBHead->biBitCount) & 0x1F) 
01587     spanBytes++;
01588 
01589 
01590   spanBytes <<= 2;
01591 
01592 
01593   return(spanBytes);
01594 }
01595 
01596 
01601 void
01602 nsImageWin::CleanUpDIB()
01603 {
01604   if (mImageBits != nsnull) {
01605     delete [] mImageBits;
01606     mImageBits = nsnull;
01607   }
01608 
01609 
01610   // Should be an ISupports, so we can release
01611   if (mColorMap != nsnull){
01612     delete [] mColorMap->Index;
01613     delete mColorMap;
01614     mColorMap = nsnull;
01615   }
01616 }
01617 
01622 void 
01623 nsImageWin :: CleanUpDDB()
01624 {
01625   if (mHBitmap != nsnull) {
01626     if (mTimer) {
01627       mTimer->Cancel();
01628       mTimer = nsnull;
01629     }
01630 
01631     ::DeleteObject(mHBitmap);
01632     mHBitmap = nsnull;
01633   }
01634   mIsOptimized = PR_FALSE;
01635 }
01636 
01637 
01643 nsresult 
01644 nsImageWin::PrintDDB(nsIDrawingSurface* aSurface,
01645                      PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight,
01646                      PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight,
01647                      PRUint32 aROP)
01648 {
01649   HDC   theHDC;
01650   UINT  palType;
01651 
01652 
01653   if (mIsOptimized == PR_TRUE){
01654     if (mHBitmap != nsnull){
01655       ConvertDDBtoDIB();
01656       ((nsDrawingSurfaceWin *)aSurface)->GetDC(&theHDC);
01657 
01658 
01659       if (mBHead->biBitCount == 8) {
01660         palType = DIB_PAL_COLORS;
01661       } else {
01662         palType = DIB_RGB_COLORS;
01663       }
01664 
01665 
01666       ::StretchDIBits(theHDC, aDX, aDY, aDWidth, aDHeight,
01667                       aSX, aSY, aSWidth, aSHeight, mImageBits,
01668                       (LPBITMAPINFO)mBHead, palType, aROP);
01669 
01670 
01671       // we are finished with this, so delete it           
01672       if (mImageBits != nsnull) {
01673         delete [] mImageBits;
01674         mImageBits = nsnull;
01675       }
01676     }
01677   }
01678 
01679 
01680   return NS_OK;
01681 }
01682 
01683 
01690 nsresult nsImageWin::ConvertDDBtoDIB()
01691 {
01692   HDC                 memPrDC;
01693 
01694   if (mImageBits)
01695     return NS_OK;
01696 
01697   if (!mInitialized || mHBitmap == nsnull)
01698     return NS_ERROR_FAILURE;
01699 
01700   memPrDC = ::CreateDC("DISPLAY", NULL, NULL, NULL);
01701   if (!memPrDC)
01702     return NS_ERROR_FAILURE;
01703 
01704   // Allocate the image bits
01705   mImageBits = new unsigned char[mSizeImage];
01706   if (!mImageBits) {
01707     ::DeleteDC(memPrDC);
01708     return NS_ERROR_OUT_OF_MEMORY;
01709   }
01710 
01711   PRInt32 retVal = 
01712     ::GetDIBits(memPrDC, mHBitmap, 0, mBHead->biHeight,
01713                 mImageBits, (LPBITMAPINFO)mBHead,
01714                 256 == mNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS);
01715 
01716   ::GdiFlush();
01717   ::DeleteDC(memPrDC);
01718 
01719   if (retVal == 0) {
01720     delete [] mImageBits;
01721     mImageBits = nsnull;
01722     return NS_ERROR_FAILURE;
01723   }
01724   
01725   // If we converted a 8 bit alpha back to bits, those bits are pre-multiplied
01726   if (mAlphaDepth == 8)
01727     mImagePreMultiplied = PR_TRUE;
01728 
01729   return NS_OK;
01730 }
01731 
01732 
01736 NS_IMETHODIMP
01737 nsImageWin::LockImagePixels(PRBool aMaskPixels)
01738 {
01739   /*  if (!mHBitmap) return NS_ERROR_NOT_INITIALIZED;
01740    ... and do Windows locking of image pixels here, if necessary */
01741 
01742 
01743   mIsLocked = PR_TRUE;
01744 
01745 
01746   return NS_OK;
01747 }
01748 
01749 
01753 NS_IMETHODIMP
01754 nsImageWin::UnlockImagePixels(PRBool aMaskPixels)
01755 {
01756   mIsLocked = PR_FALSE;
01757   // if memory was allocated temporarily by GetBits, it can now be deleted safely
01758   if (mDIBTemp == PR_TRUE) {
01759     // get rid of this memory
01760     if (mImageBits != nsnull) {
01761       delete [] mImageBits;
01762       mImageBits = nsnull;
01763       }
01764 
01765 
01766     mDIBTemp = PR_FALSE;
01767   }
01768 
01769 
01770   /* if (!mHBitmap)
01771     return NS_ERROR_NOT_INITIALIZED;
01772   
01773   if (aMaskPixels && !mAlphamHBitmap)
01774     return NS_ERROR_NOT_INITIALIZED;
01775 
01776 
01777    ... and do Windows unlocking of image pixels here, if necessary */
01778 
01779 
01780   return NS_OK;
01781 }
01782 
01783 
01793 PRUint8*
01794 nsImageWin::GetBits()
01795 {
01796   // if mImageBits did not exist.. then
01797   if (!mImageBits) {
01798     ConvertDDBtoDIB();
01799     mDIBTemp = PR_TRUE;   // only set to true if the DIB is being created here as temporary
01800   }
01801 
01802 
01803   return mImageBits;
01804 
01805 
01806 } // GetBits
01807 
01808 
01819 NS_IMETHODIMP nsImageWin::DrawToImage(nsIImage* aDstImage, nscoord aDX, nscoord aDY, nscoord aDWidth, nscoord aDHeight)
01820 {
01821   NS_ASSERTION(mAlphaDepth <= 1, "nsImageWin::DrawToImage can only handle 0 & 1 bit Alpha");
01822 
01823   if (mAlphaDepth > 1)
01824     return NS_ERROR_UNEXPECTED;
01825 
01826   nsImageWin *dest = NS_STATIC_CAST(nsImageWin *, aDstImage);
01827 
01828   if (!dest)
01829     return NS_ERROR_FAILURE;
01830 
01831   if (aDX >= dest->mBHead->biWidth || aDY >= dest->mBHead->biHeight)
01832     return NS_OK;
01833 
01834   if (!dest->mImageBits)
01835     return NS_ERROR_FAILURE;
01836      
01837   if (!dest->mIsOptimized) {
01838     // set up some local variable to make things run faster in the loop
01839     PRUint8  *rgbPtr = 0, *alphaPtr = 0;
01840     PRUint32 rgbStride, alphaStride;
01841     PRInt32  srcHeight;
01842     PRUint8  *dstRgbPtr = 0, *dstAlphaPtr = 0;
01843     PRUint32 dstRgbStride, dstAlphaStride;
01844     PRInt32  dstHeight;
01845 
01846     rgbPtr = mImageBits;
01847     rgbStride = mRowBytes;
01848     alphaPtr = mAlphaBits;
01849     alphaStride = mARowBytes;
01850     srcHeight = mBHead->biHeight;
01851 
01852     dstRgbPtr = dest->mImageBits;
01853     dstRgbStride = dest->mRowBytes;
01854     dstAlphaPtr = dest->mAlphaBits;
01855     dstAlphaStride = dest->mARowBytes;
01856     dstHeight = dest->mBHead->biHeight;
01857 
01858 
01859     PRInt32 y;
01860     PRInt32 ValidWidth = (aDWidth < (dest->mBHead->biWidth - aDX)) ?
01861                          aDWidth : (dest->mBHead->biWidth - aDX);
01862     PRInt32 ValidHeight = (aDHeight < (dstHeight - aDY)) ?
01863                           aDHeight : (dstHeight - aDY);
01864     PRUint8 *dst;
01865     PRUint8 *src;
01866 
01867     // now composite the two images together
01868     switch (mAlphaDepth) {
01869     case 1:
01870       {
01871         PRUint8 *dstAlpha;
01872         PRUint8 *alpha;
01873         PRUint8 offset = aDX & 0x7; // x starts at 0
01874 
01875 
01876         for (y=0; y<ValidHeight; y++) {
01877           dst = dstRgbPtr +
01878                 (dstHeight - (aDY+y) - 1) * dstRgbStride +
01879                 (3 * aDX);
01880           dstAlpha = dstAlphaPtr + (dstHeight - (aDY+y) - 1) * dstAlphaStride;
01881           src = rgbPtr + (srcHeight - y - 1)*rgbStride;
01882           alpha = alphaPtr + (srcHeight - y - 1)*alphaStride;
01883           for (int x = 0;
01884                x < ValidWidth;
01885                x += 8, dst +=  3 * 8, src +=  3 * 8) {
01886             PRUint8 alphaPixels = *alpha++;
01887             if (alphaPixels == 0) {
01888               // all 8 transparent; jump forward
01889               continue;
01890             }
01891 
01892 
01893             // 1 or more bits are set, handle dstAlpha now - may not be aligned.
01894             // Are all 8 of these alpha pixels used?
01895             if (x+7 >= ValidWidth) {
01896               alphaPixels &= 0xff << (8 - (ValidWidth-x)); // no, mask off unused
01897               if (alphaPixels == 0)
01898                 continue;  // no 1 alpha pixels left
01899             }
01900             if (offset == 0) {
01901               dstAlpha[(aDX+x)>>3] |= alphaPixels; // the cheap aligned case
01902             } else {
01903               dstAlpha[(aDX+x)>>3]       |= alphaPixels >> offset;
01904               // avoid write if no 1's to write - also avoids going past end of array
01905               PRUint8 alphaTemp = alphaPixels << (8U - offset);
01906               if (alphaTemp & 0xff)
01907                 dstAlpha[((aDX+x)>>3) + 1] |= alphaTemp;
01908             }
01909 
01910 
01911             if (alphaPixels == 0xff) {
01912               // fix - could speed up by gathering a run of 0xff's and doing 1 memcpy
01913               // all 8 pixels set; copy and jump forward
01914               memcpy(dst,src,8*3);
01915               continue;
01916             } else {
01917               // else mix of 1's and 0's in alphaPixels, do 1 bit at a time
01918               // Don't go past end of line!
01919               PRUint8 *d = dst, *s = src;
01920               for (PRUint8 aMask = 1<<7, j = 0; aMask && j < ValidWidth-x; aMask >>= 1, j++) {
01921                 // if this pixel is opaque then copy into the destination image
01922                 if (alphaPixels & aMask) {
01923                   // might be faster with *d++ = *s++ 3 times?
01924                   d[0] = s[0];
01925                   d[1] = s[1];
01926                   d[2] = s[2];
01927                   // dstAlpha bit already set
01928                 }
01929                 d += 3;
01930                 s += 3;
01931               }
01932             }
01933           }
01934         }
01935       }
01936       break;
01937     case 0:
01938     default:
01939       dst = dstRgbPtr + (dstHeight - aDY - 1) * dstRgbStride + 3 * aDX;
01940       src = rgbPtr + (srcHeight - 1) * rgbStride;
01941 
01942       for (y = 0; y < ValidHeight; y++) {
01943         memcpy(dst, src,  3 * ValidWidth);
01944         dst -= dstRgbStride;
01945         src -= rgbStride;
01946       }
01947     }
01948     nsRect rect(aDX, aDY, ValidWidth, ValidHeight);
01949     dest->ImageUpdated(nsnull, 0, &rect);
01950   } else {
01951     // dst's Image is optimized (dest->mHBitmap is linked to dest->mImageBits), so just paint on it
01952     if (!dest->mHBitmap)
01953       return NS_ERROR_UNEXPECTED;
01954       
01955     HDC dstMemDC = ::CreateCompatibleDC(nsnull);
01956     HBITMAP oldDstBits;
01957     DWORD rop;
01958 
01959     oldDstBits = (HBITMAP)::SelectObject(dstMemDC, dest->mHBitmap);
01960     rop = SRCCOPY;
01961 
01962     if (mAlphaBits) {
01963       if (1==mAlphaDepth) {
01964         MONOBITMAPINFO  bmi(mBHead->biWidth, mBHead->biHeight);
01965 
01966         ::StretchDIBits(dstMemDC, aDX, aDY, aDWidth, aDHeight,
01967                         0, 0,mBHead->biWidth, mBHead->biHeight, mAlphaBits,
01968                         (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, SRCAND);
01969         rop = SRCPAINT;
01970       }
01971     }
01972 
01973     if (8 == mAlphaDepth) {
01974       nsresult rv = DrawComposited(dstMemDC, aDX, aDY, aDWidth, aDHeight,
01975                                    0, 0, mBHead->biWidth, mBHead->biHeight,
01976                                    aDWidth, aDHeight);
01977       if (NS_FAILED(rv)) {
01978         ::SelectObject(dstMemDC, oldDstBits);
01979         ::DeleteDC(dstMemDC);
01980         return rv;
01981       }
01982     } 
01983     
01984     // Copy/Paint our rgb to dest
01985     if (mIsOptimized && mHBitmap) {
01986       // We are optimized, use Stretchblt
01987       HDC srcMemDC = ::CreateCompatibleDC(nsnull);
01988       HBITMAP oldSrcBits;
01989       oldSrcBits = (HBITMAP)::SelectObject(srcMemDC, mHBitmap);
01990        
01991       ::StretchBlt(dstMemDC, aDX, aDY, aDWidth, aDHeight, srcMemDC, 
01992                    0, 0, mBHead->biWidth, mBHead->biHeight, rop);
01993       
01994       ::SelectObject(srcMemDC, oldSrcBits);
01995       ::DeleteDC(srcMemDC);
01996     } else {
01997       ::StretchDIBits(dstMemDC, aDX, aDY, aDWidth, aDHeight, 
01998                       0, 0, mBHead->biWidth, mBHead->biHeight, mImageBits,
01999                       (LPBITMAPINFO)mBHead, DIB_RGB_COLORS, rop);
02000     }
02001     ::SelectObject(dstMemDC, oldDstBits);
02002     ::DeleteDC(dstMemDC);
02003   }
02004 
02005 
02006   return NS_OK;
02007 }
02008 
02013 void 
02014 CompositeBitsInMemory(HDC aTheHDC, int aDX, int aDY, int aDWidth, int aDHeight,
02015                       int aSX, int aSY, int aSWidth, int aSHeight,PRInt32 aSrcy,
02016                       PRUint8 *aAlphaBits, MONOBITMAPINFO *aBMI,
02017                       PRUint8* aImageBits, LPBITMAPINFOHEADER aBHead,
02018                       PRInt16 aNumPaletteColors)
02019 {
02020   unsigned char *screenBits;
02021 
02022   HDC memDC = ::CreateCompatibleDC(NULL);
02023 
02024   if(0!=memDC){
02025     ALPHA24BITMAPINFO offbmi(aSWidth, aSHeight);
02026     HBITMAP tmpBitmap = ::CreateDIBSection(memDC, (LPBITMAPINFO)&offbmi, DIB_RGB_COLORS,
02027                                            (LPVOID *)&screenBits, NULL, 0);
02028 
02029     if(0 != tmpBitmap){
02030       HBITMAP oldBitmap = (HBITMAP)::SelectObject(memDC, tmpBitmap);
02031 
02032       if(0!=oldBitmap) {
02033         // pop in the alpha channel
02034         ::StretchDIBits(memDC, 0, 0, aSWidth, aSHeight,
02035                         aSX, aSrcy, aSWidth, aSHeight,
02036                         aAlphaBits, (LPBITMAPINFO)aBMI,
02037                         DIB_RGB_COLORS, SRCCOPY); 
02038 
02039         // paint in the image
02040         ::StretchDIBits(memDC, 0, 0, aSWidth, aSHeight,
02041                         aSX, aSrcy, aSWidth, aSHeight,
02042                         aImageBits, (LPBITMAPINFO)aBHead,
02043                         256 == aNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
02044                         SRCPAINT);
02045 
02046         ::GdiFlush();
02047 
02048         // output the composed image
02049 #ifdef _MSC_VER
02050         __try {
02051 #endif
02052            ::StretchDIBits(aTheHDC, aDX, aDY, aDWidth, aDHeight,
02053                           aSX, aSrcy, aSWidth, aSHeight,
02054                           screenBits, (LPBITMAPINFO)&offbmi,
02055                           256 == aNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
02056                           SRCCOPY);
02057 #ifdef _MSC_VER
02058         }  __except (EXCEPTION_EXECUTE_HANDLER) {
02059           /* yeah this is ugly - certain printer drivers crash in the StretchDIBits */
02060           /* workaround is to subtract one from aSrcy */  
02061           ::StretchDIBits(aTheHDC, aDX, aDY, aDWidth, aDHeight,
02062                           aSX, aSrcy-1, aSWidth, aSHeight,
02063                           screenBits, (LPBITMAPINFO)&offbmi,
02064                           256 == aNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
02065                           SRCCOPY);
02066         }
02067 #endif
02068 
02069         ::SelectObject(memDC, oldBitmap);
02070       }
02071       ::DeleteObject(tmpBitmap);
02072     }
02073     ::DeleteDC(memDC);
02074   }
02075 }
02076 
02077 void nsImageWin::TimerCallBack(nsITimer *aTimer, void *aClosure)
02078 {
02079   nsImageWin *entry = NS_STATIC_CAST(nsImageWin*, aClosure);
02080   if (!entry)
02081     return;
02082 
02083   if (NS_FAILED(entry->RemoveDDB())) {
02084     // Try again later.  Can't SetDelay while timer is being called, so
02085     // create a new timer instead
02086     entry->mTimer = do_CreateInstance("@mozilla.org/timer;1");
02087     if (entry->mTimer)
02088       entry->mTimer->InitWithFuncCallback(nsImageWin::TimerCallBack, entry,
02089                                           GFX_MS_REMOVE_DBB,
02090                                           nsITimer::TYPE_ONE_SHOT);
02091   }
02092 }