Back to index

lightning-sunbird  0.9+nobinonly
nsIconChannel.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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  * Brian Ryner.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Scott MacGregor <mscott@netscape.com>
00025  *   Neil Rashbrook <neil@parkwaycc.co.uk>
00026  *   IBM Corp.
00027  *   Rich Walsh <dragtext@e-vertise.com>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either the GNU General Public License Version 2 or later (the "GPL"), or
00031  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 //------------------------------------------------------------------------
00044 
00045 #include "nsIconChannel.h"
00046 #include "nsIIconURI.h"
00047 #include "nsReadableUtils.h"
00048 #include "nsMemory.h"
00049 #include "nsNetUtil.h"
00050 #include "nsInt64.h"
00051 #include "nsIFile.h"
00052 #include "nsIFileURL.h"
00053 #include "nsDirectoryServiceDefs.h"
00054 
00055 #define INCL_PM
00056 #include <os2.h>
00057 
00058 //------------------------------------------------------------------------
00059 
00060 // Due to byte swap, the second nibble is the first pixel of the pair
00061 #define FIRSTPEL(x)  (0xF & (x >> 4))
00062 #define SECONDPEL(x) (0xF & x)
00063 
00064 // nbr of bytes per row, rounded up to the nearest dword boundary
00065 #define ALIGNEDBPR(cx,bits) ((( ((cx)*(bits)) + 31) / 32) * 4)
00066 
00067 // nbr of bytes per row, rounded up to the nearest byte boundary
00068 #define UNALIGNEDBPR(cx,bits) (( ((cx)*(bits)) + 7) / 8)
00069 
00070 // native icon functions
00071 HPOINTER GetFileIcon(nsCString& file, PRBool fExists);
00072 
00073 void    ConvertColorBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf);
00074 void    ShrinkColorBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf);
00075 void    ConvertMaskBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf);
00076 void    ShrinkMaskBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf);
00077 
00078 //------------------------------------------------------------------------
00079 // nsIconChannel methods
00080 
00081 nsIconChannel::nsIconChannel()
00082 {
00083 }
00084 
00085 nsIconChannel::~nsIconChannel()
00086 {}
00087 
00088 NS_IMPL_THREADSAFE_ISUPPORTS4(nsIconChannel, 
00089                               nsIChannel,
00090                               nsIRequest,
00091                               nsIRequestObserver,
00092                               nsIStreamListener)
00093 
00094 nsresult nsIconChannel::Init(nsIURI* uri)
00095 {
00096   NS_ASSERTION(uri, "no uri");
00097   mUrl = uri;
00098 
00099   nsresult rv;
00100   mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
00101   return rv;
00102 }
00103 
00104 //------------------------------------------------------------------------
00105 // nsIRequest methods:
00106 
00107 NS_IMETHODIMP nsIconChannel::GetName(nsACString &result)
00108 {
00109   return mUrl->GetSpec(result);
00110 }
00111 
00112 NS_IMETHODIMP nsIconChannel::IsPending(PRBool *result)
00113 {
00114   return mPump->IsPending(result);
00115 }
00116 
00117 NS_IMETHODIMP nsIconChannel::GetStatus(nsresult *status)
00118 {
00119   return mPump->GetStatus(status);
00120 }
00121 
00122 NS_IMETHODIMP nsIconChannel::Cancel(nsresult status)
00123 {
00124   return mPump->Cancel(status);
00125 }
00126 
00127 NS_IMETHODIMP nsIconChannel::Suspend(void)
00128 {
00129   return mPump->Suspend();
00130 }
00131 
00132 NS_IMETHODIMP nsIconChannel::Resume(void)
00133 {
00134   return mPump->Resume();
00135 }
00136 
00137 NS_IMETHODIMP nsIconChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00138 {
00139   *aLoadGroup = mLoadGroup;
00140   NS_IF_ADDREF(*aLoadGroup);
00141   return NS_OK;
00142 }
00143 
00144 NS_IMETHODIMP nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00145 {
00146   mLoadGroup = aLoadGroup;
00147   return NS_OK;
00148 }
00149 
00150 NS_IMETHODIMP nsIconChannel::GetLoadFlags(PRUint32 *aLoadAttributes)
00151 {
00152   return mPump->GetLoadFlags(aLoadAttributes);
00153 }
00154 
00155 NS_IMETHODIMP nsIconChannel::SetLoadFlags(PRUint32 aLoadAttributes)
00156 {
00157   return mPump->SetLoadFlags(aLoadAttributes);
00158 }
00159 
00160 //------------------------------------------------------------------------
00161 // nsIChannel methods:
00162 
00163 NS_IMETHODIMP nsIconChannel::GetOriginalURI(nsIURI* *aURI)
00164 {
00165   *aURI = mOriginalURI ? mOriginalURI : mUrl;
00166   NS_ADDREF(*aURI);
00167   return NS_OK;
00168 }
00169 
00170 NS_IMETHODIMP nsIconChannel::SetOriginalURI(nsIURI* aURI)
00171 {
00172   mOriginalURI = aURI;
00173   return NS_OK;
00174 }
00175 
00176 NS_IMETHODIMP nsIconChannel::GetURI(nsIURI* *aURI)
00177 {
00178   *aURI = mUrl;
00179   NS_IF_ADDREF(*aURI);
00180   return NS_OK;
00181 }
00182 
00183 NS_IMETHODIMP
00184 nsIconChannel::Open(nsIInputStream **_retval)
00185 {
00186   return MakeInputStream(_retval, PR_FALSE);
00187 }
00188 
00189 NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
00190 {
00191   nsCOMPtr<nsIInputStream> inStream;
00192   nsresult rv = MakeInputStream(getter_AddRefs(inStream), PR_TRUE);
00193   if (NS_FAILED(rv))
00194     return rv;
00195 
00196   // Init our streampump
00197   rv = mPump->Init(inStream, nsInt64(-1), nsInt64(-1), 0, 0, PR_FALSE);
00198   if (NS_FAILED(rv))
00199     return rv;
00200 
00201   rv = mPump->AsyncRead(this, ctxt);
00202   if (NS_SUCCEEDED(rv)) {
00203     // Store our real listener
00204     mListener = aListener;
00205     // Add ourself to the load group, if available
00206     if (mLoadGroup)
00207       mLoadGroup->AddRequest(this, nsnull);
00208   }
00209   return rv;
00210 }
00211 
00212 nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile ** aLocalFile, PRUint32 * aDesiredImageSize, nsACString &aContentType, nsACString &aFileExtension)
00213 {
00214   nsresult rv = NS_OK;
00215   nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
00216   NS_ENSURE_SUCCESS(rv, rv);
00217 
00218   iconURI->GetImageSize(aDesiredImageSize);
00219   iconURI->GetContentType(aContentType);
00220   iconURI->GetFileExtension(aFileExtension);
00221 
00222   nsCOMPtr<nsIURI> fileURI;
00223   rv = iconURI->GetIconFile(getter_AddRefs(fileURI));
00224   if (NS_FAILED(rv) || !fileURI) return NS_OK;
00225 
00226   nsCOMPtr<nsIFileURL>    fileURL = do_QueryInterface(fileURI, &rv);
00227   if (NS_FAILED(rv) || !fileURL) return NS_OK;
00228 
00229   nsCOMPtr<nsIFile> file;
00230   rv = fileURL->GetFile(getter_AddRefs(file));
00231   if (NS_FAILED(rv) || !file) return NS_OK;
00232   
00233   *aLocalFile = file;
00234   NS_IF_ADDREF(*aLocalFile);
00235   return NS_OK;
00236 }
00237 
00238 //------------------------------------------------------------------------
00239 
00240 // retrieves a native icon with 16, 256, or 16M colors and converts it
00241 // to 24-bit BGR with 1-bit alpha data (BGR_A1 format);  Note:  this
00242 // implementation ignores the file's MIME-type because using it virtually
00243 // guarantees we'll end up with an inappropriate icon (i.e. an .exe icon)
00244 
00245 nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval, PRBool nonBlocking)
00246 {
00247   // get some details about this icon
00248   nsCOMPtr<nsIFile> localFile;
00249   PRUint32 desiredImageSize;
00250   nsXPIDLCString contentType;
00251   nsCAutoString filePath;
00252   nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(localFile), &desiredImageSize, contentType, filePath);
00253   NS_ENSURE_SUCCESS(rv, rv);
00254 
00255   // if the file exists, get its path
00256   PRBool fileExists = PR_FALSE;
00257   if (localFile) {
00258     localFile->GetNativePath(filePath);
00259     localFile->Exists(&fileExists);
00260   }
00261 
00262   // get the icon from the file
00263   HPOINTER hIcon = GetFileIcon(filePath, fileExists);
00264   if (hIcon == NULLHANDLE)
00265     return NS_ERROR_FAILURE;
00266 
00267   // get the color & mask bitmaps used by the icon
00268   POINTERINFO IconInfo;
00269   if (!WinQueryPointerInfo(hIcon, &IconInfo)) {
00270     WinFreeFileIcon( hIcon);
00271     return NS_ERROR_FAILURE;
00272   }
00273 
00274   // if we need a mini-icon, use those bitmaps if present;
00275   // otherwise, signal that the icon needs to be shrunk
00276   PRBool fShrink = FALSE;
00277   if (desiredImageSize <= 16) {
00278     if (IconInfo.hbmMiniPointer) {
00279       IconInfo.hbmColor = IconInfo.hbmMiniColor;
00280       IconInfo.hbmPointer = IconInfo.hbmMiniPointer;
00281     }
00282     else
00283       fShrink = TRUE;
00284   }
00285 
00286   // various resources to be allocated
00287   PBITMAPINFO2  pBMInfo = 0;
00288   PRUint8*      pInBuf  = 0;
00289   PRUint8*      pOutBuf = 0;
00290   HDC           hdc     = 0;
00291   HPS           hps     = 0;
00292 
00293   // using this dummy do{...}while(0) "loop" guarantees resources will
00294   // be deallocated, eliminates the need for nesting, and generally makes
00295   // testing for & dealing with errors pretty painless (just 'break')
00296   do {
00297     rv = NS_ERROR_FAILURE;
00298   
00299     // get the details for the color bitmap;  if there isn't one
00300     // or this is 1-bit color, exit
00301     BITMAPINFOHEADER2  BMHeader;
00302     BMHeader.cbFix = sizeof(BMHeader);
00303     if (!IconInfo.hbmColor ||
00304         !GpiQueryBitmapInfoHeader(IconInfo.hbmColor, &BMHeader) ||
00305         BMHeader.cBitCount == 1)
00306       break;
00307   
00308     // alloc space for the color bitmap's info, including its color table
00309     PRUint32 cbBMInfo = sizeof(BITMAPINFO2) + (sizeof(RGB2) * 255);
00310     pBMInfo = (PBITMAPINFO2)nsMemory::Alloc(cbBMInfo);
00311     if (!pBMInfo)
00312       break;
00313   
00314     // alloc space for the color bitmap data
00315     PRUint32 cbInRow = ALIGNEDBPR( BMHeader.cx, BMHeader.cBitCount);
00316     PRUint32 cbInBuf = cbInRow * BMHeader.cy;
00317     pInBuf = (PRUint8*)nsMemory::Alloc(cbInBuf);
00318     if (!pInBuf)
00319       break;
00320     memset( pInBuf, 0, cbInBuf);
00321   
00322     // alloc space for the 24-bit BGR_A1 bitmap we're creating
00323     PRUint32 cxOut    = (fShrink ? BMHeader.cx / 2 : BMHeader.cx);
00324     PRUint32 cyOut    = (fShrink ? BMHeader.cy / 2 : BMHeader.cy);
00325     PRUint32 cbColor  = ALIGNEDBPR( cxOut, 24) * cyOut;
00326     PRUint32 cbMask   = ALIGNEDBPR( cxOut,  1) * cyOut;
00327     PRUint32 cbOutBuf = 3 + cbColor + cbMask;
00328     pOutBuf = (PRUint8*)nsMemory::Alloc(cbOutBuf);
00329     if (!pOutBuf)
00330       break;
00331     memset( pOutBuf, 0, cbOutBuf);
00332   
00333     // create a DC and PS
00334     DEVOPENSTRUC  dop = {NULL, "DISPLAY", NULL, NULL, NULL, NULL, NULL, NULL, NULL};
00335     hdc = DevOpenDC( (HAB)0, OD_MEMORY, "*", 5L, (PDEVOPENDATA)&dop, NULLHANDLE);
00336     if (!hdc)
00337       break;
00338   
00339     SIZEL sizel = {0,0};
00340     hps = GpiCreatePS((HAB)0, hdc, &sizel, GPIA_ASSOC | PU_PELS | GPIT_MICRO);
00341     if (!hps)
00342       break;
00343   
00344     // get the color bits
00345     memset( pBMInfo, 0, cbBMInfo);
00346     *((PBITMAPINFOHEADER2)pBMInfo ) = BMHeader;
00347     GpiSetBitmap(hps, IconInfo.hbmColor);
00348     if (GpiQueryBitmapBits( hps, 0L, (LONG)BMHeader.cy,
00349                             (BYTE*)pInBuf, pBMInfo) <= 0)
00350       break;
00351   
00352     // The first 2 bytes are the width & height of the icon in pixels
00353     PRUint8* outPtr = pOutBuf;
00354     *outPtr++ = (PRUint8)cxOut;
00355     *outPtr++ = (PRUint8)cyOut;
00356     // then the number of bits of alpha per pixel
00357     *outPtr++ = (PRUint8)1;
00358   
00359     // convert the color bitmap
00360     pBMInfo->cbImage = cbInBuf;
00361     if (fShrink)
00362       ShrinkColorBitMap( pInBuf, pBMInfo, outPtr);
00363     else
00364       ConvertColorBitMap( pInBuf, pBMInfo, outPtr);
00365     outPtr += cbColor;
00366   
00367     // now we need to tack on the alpha data
00368   
00369     // Get the mask info
00370     BMHeader.cbFix = sizeof(BMHeader);
00371     if (!GpiQueryBitmapInfoHeader(IconInfo.hbmPointer, &BMHeader))
00372       break;
00373   
00374     // if the existing input buffer isn't large enough, reallocate it
00375     cbInRow  = ALIGNEDBPR( BMHeader.cx, BMHeader.cBitCount);
00376     if ((cbInRow * BMHeader.cy) > cbInBuf)  // Need more for mask
00377     {
00378       cbInBuf = cbInRow * BMHeader.cy;
00379       nsMemory::Free(pInBuf);
00380       pInBuf  = (PRUint8*)nsMemory::Alloc(cbInBuf);
00381       memset( pInBuf, 0, cbInBuf);
00382     }
00383   
00384     // get the mask/alpha bits
00385     memset( pBMInfo, 0, cbBMInfo);
00386     *((PBITMAPINFOHEADER2)pBMInfo ) = BMHeader;
00387     GpiSetBitmap(hps, IconInfo.hbmPointer);
00388     if (GpiQueryBitmapBits( hps, 0L, (LONG)BMHeader.cy,
00389                             (BYTE*)pInBuf, pBMInfo) <= 0)
00390       break;
00391 
00392     // convert the mask/alpha bitmap
00393     pBMInfo->cbImage = cbInBuf;
00394     if (fShrink)
00395       ShrinkMaskBitMap( pInBuf, pBMInfo, outPtr);
00396     else
00397       ConvertMaskBitMap( pInBuf, pBMInfo, outPtr);
00398   
00399     // create a pipe
00400     nsCOMPtr<nsIInputStream> inStream;
00401     nsCOMPtr<nsIOutputStream> outStream;
00402     rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream),
00403                     cbOutBuf, cbOutBuf, nonBlocking);
00404     if (NS_FAILED(rv))
00405       break;
00406   
00407     // put our data into the pipe
00408     PRUint32 written;
00409     rv = outStream->Write( NS_REINTERPRET_CAST(const char*, pOutBuf),
00410                            cbOutBuf, &written);
00411     if (NS_FAILED(rv))
00412       break;
00413   
00414     // success! so addref the pipe
00415     NS_ADDREF(*_retval = inStream);
00416   
00417   } while (0);
00418 
00419   // free all the resources we allocated
00420   if (pOutBuf)
00421     nsMemory::Free( pOutBuf);
00422   if (pInBuf)
00423     nsMemory::Free( pInBuf);
00424   if (pBMInfo)
00425     nsMemory::Free( pBMInfo);
00426   if (hps) {
00427     GpiAssociate(hps, NULLHANDLE);
00428     GpiDestroyPS(hps);
00429   }
00430   if (hdc)
00431     DevCloseDC(hdc);
00432   if (hIcon)
00433     WinFreeFileIcon( hIcon);
00434 
00435   return rv;
00436 }
00437 
00438 //------------------------------------------------------------------------
00439 
00440 // if the file exists, get its icon;  if it doesn't, create a dummy file
00441 // with the same extension, then use whatever icon the system assigns it
00442 
00443 HPOINTER    GetFileIcon(nsCString& file, PRBool fExists)
00444 {
00445 
00446   if (fExists)
00447     return WinLoadFileIcon( file.get(), FALSE);
00448 
00449   nsCOMPtr<nsIFile> dummyPath;
00450   if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
00451                                        getter_AddRefs(dummyPath))))
00452     return 0;
00453 
00454   if (file.First() == '.')
00455     file.Insert("moztmp", 0);
00456 
00457   if (NS_FAILED(dummyPath->AppendNative(file)))
00458     return 0;
00459 
00460   nsCAutoString dummyFile;
00461   dummyPath->GetNativePath(dummyFile);
00462 
00463   HPOINTER  hRtn = 0;
00464   FILE*     fp = fopen( dummyFile.get(), "wb+");
00465   if (fp) {
00466     fclose( fp);
00467     hRtn = WinLoadFileIcon(dummyFile.get(), FALSE);
00468     remove(dummyFile.get());
00469   }
00470 
00471   return hRtn;
00472 }
00473 
00474 //------------------------------------------------------------------------
00475 
00476 // converts 16, 256, & 16M color bitmaps to 24-bit BGR format;  since the
00477 // scanlines in OS/2 bitmaps run bottom-to-top, it starts at the end of the
00478 // input buffer & works its way back;  Note:  no output padding is needed
00479 // for any expected color-depth or size;  only 4-bit, 20x20 icons contain
00480 // input padding that has to be ignored
00481 
00482 void    ConvertColorBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf)
00483 {
00484 
00485   PRUint32  bprIn = ALIGNEDBPR(pBMInfo->cx, pBMInfo->cBitCount);
00486   PRUint8*  pIn   = inBuf + (pBMInfo->cy - 1) * bprIn;
00487   PRUint8*  pOut  = outBuf;
00488   PRGB2       pColorTable = &pBMInfo->argbColor[0];
00489 
00490   if (pBMInfo->cBitCount == 4) {
00491     PRUint32  ubprIn = UNALIGNEDBPR(pBMInfo->cx, pBMInfo->cBitCount);
00492     PRUint32  padIn  = bprIn - ubprIn;
00493 
00494     for (PRUint32 row = pBMInfo->cy; row > 0; row--) {
00495       for (PRUint32 ndx = 0; ndx < ubprIn; ndx++, pIn++) {
00496         pOut = 3 + (PRUint8*)memcpy( pOut, &pColorTable[FIRSTPEL(*pIn)], 3);
00497         pOut = 3 + (PRUint8*)memcpy( pOut, &pColorTable[SECONDPEL(*pIn)], 3);
00498       }
00499       pIn -= (2 * bprIn) - padIn;
00500     }
00501   }
00502   else
00503   if (pBMInfo->cBitCount == 8) {
00504     for (PRUint32 row = pBMInfo->cy; row > 0; row--) {
00505       for (PRUint32 ndx = 0; ndx < bprIn; ndx++, pIn++) {
00506         pOut = 3 + (PRUint8*)memcpy( pOut, &pColorTable[*pIn], 3);
00507       }
00508       pIn -= 2 * bprIn;
00509     }
00510   }
00511   else
00512   if (pBMInfo->cBitCount == 24) {
00513     for (PRUint32 row = pBMInfo->cy; row > 0; row--) {
00514       pOut = bprIn + (PRUint8*)memcpy( pOut, pIn, bprIn);
00515       pIn -= bprIn;
00516     }
00517   }
00518 
00519   return;
00520 }
00521 
00522 //------------------------------------------------------------------------
00523 
00524 // similar to ConvertColorBitMap() except that it skips every other pixel
00525 // horizontally, & every other line vertically;  this is the exact reverse
00526 // of what GPI does when it expands a mini-icon to full-size
00527 
00528 void    ShrinkColorBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf)
00529 {
00530 
00531   PRUint32  bprIn = ALIGNEDBPR(pBMInfo->cx, pBMInfo->cBitCount);
00532   PRUint8*  pIn   = inBuf + (pBMInfo->cy - 1) * bprIn;
00533   PRUint8*  pOut  = outBuf;
00534   PRGB2       pColorTable = &pBMInfo->argbColor[0];
00535 
00536   if (pBMInfo->cBitCount == 4) {
00537     PRUint32  ubprIn = UNALIGNEDBPR(pBMInfo->cx, pBMInfo->cBitCount);
00538     PRUint32  padIn  = bprIn - ubprIn;
00539 
00540     for (PRUint32 row = pBMInfo->cy; row > 0; row -= 2) {
00541       for (PRUint32 ndx = 0; ndx < ubprIn; ndx++, pIn++) {
00542         pOut = 3 + (PRUint8*)memcpy( pOut, &pColorTable[FIRSTPEL(*pIn)], 3);
00543       }
00544       pIn -= (3 * bprIn) - padIn;
00545     }
00546   }
00547   else
00548   if (pBMInfo->cBitCount == 8) {
00549     for (PRUint32 row = pBMInfo->cy; row > 0; row -= 2) {
00550       for (PRUint32 ndx = 0; ndx < bprIn; ndx += 2, pIn += 2) {
00551         pOut = 3 + (PRUint8*)memcpy( pOut, &pColorTable[*pIn], 3);
00552       }
00553       pIn -= 3 * bprIn;
00554     }
00555   }
00556   else
00557   if (pBMInfo->cBitCount == 24) {
00558     for (PRUint32 row = pBMInfo->cy; row > 0; row -= 2) {
00559       for (PRUint32 ndx = 0; ndx < bprIn; ndx += 6, pIn += 3) {
00560         pOut = 3 + (PRUint8*)memcpy( pOut, pIn, 3);
00561         pIn += 3;
00562       }
00563       pIn -= 3 * bprIn;
00564     }
00565   }
00566 
00567   return;
00568 }
00569 
00570 //------------------------------------------------------------------------
00571 
00572 // converts an icon's AND mask into 1-bit alpha data;  since the AND mask
00573 // is the 2nd half of a pair of bitmaps & the scanlines run bottom-to-top,
00574 // starting at the end & working back to the midpoint converts the entire
00575 // bitmap;  Note: because the input is already padded to a dword boundary,
00576 // the output will be padded automatically
00577 
00578 void    ConvertMaskBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf)
00579 {
00580 
00581   PRUint32  bprIn  = ALIGNEDBPR(pBMInfo->cx, pBMInfo->cBitCount);
00582   PRUint32  lprIn  = bprIn / 4;
00583   PRUint32* pOut32 = (PRUint32*)outBuf;
00584   PRUint32* pIn32  = (PRUint32*)(inBuf + (pBMInfo->cy - 1) * bprIn);
00585 
00586   for (PRUint32 row = pBMInfo->cy/2; row > 0; row--) {
00587     for (PRUint32 ndx = 0; ndx < lprIn; ndx++) {
00588         *pOut32++ = ~(*pIn32++);
00589     }
00590     pIn32 -= 2 * lprIn;
00591   }
00592 
00593   return;
00594 }
00595 
00596 //------------------------------------------------------------------------
00597 
00598 // similar to ConvertMaskBitMap() except that it skips every other pixel
00599 // horizontally, & every other line vertically;  Note:  this is the only
00600 // one of these functions that may have to add padding to its output
00601 
00602 void    ShrinkMaskBitMap(PRUint8* inBuf, PBITMAPINFO2 pBMInfo, PRUint8* outBuf)
00603 {
00604 
00605   PRUint32  bprIn  = ALIGNEDBPR(pBMInfo->cx, pBMInfo->cBitCount);
00606   PRUint32  padOut = (bprIn / 2) & 3;
00607   PRUint8*  pOut   = outBuf;
00608   PRUint8*  pIn    = inBuf + (pBMInfo->cy - 1) * bprIn;
00609 
00610   // for every other row
00611   for (PRUint32 row = pBMInfo->cy/2; row > 0; row -= 2) {
00612     PRUint8 dest = 0;
00613     PRUint8 destMask = 0x80;
00614 
00615     // for every byte in the row
00616     for (PRUint32 ndx = 0; ndx < bprIn; ndx++) {
00617       PRUint8 src = ~(*pIn++);
00618       PRUint8 srcMask = 0x80;
00619 
00620       // for every other bit in the current byte
00621       for (PRUint32 bitNdx = 0; bitNdx < 8; bitNdx += 2) {
00622         if (src & srcMask)
00623           dest |= destMask;
00624         srcMask >>= 2;
00625         destMask >>= 1;
00626       }
00627 
00628       // if we've filled an output byte from two input bytes, save it
00629       if (!destMask) {
00630         *pOut++ = dest;
00631         dest = 0;
00632         destMask = 0x80;
00633       }
00634     }
00635 
00636     // after we've processed every input byte in the row, 
00637     // does the output row need padding?
00638     if (padOut) {
00639       memset( pOut, 0, padOut);
00640       pOut += padOut;
00641     }
00642 
00643     pIn -= 3 * bprIn;
00644   }
00645 
00646   return;
00647 }
00648 
00649 //------------------------------------------------------------------------
00650 
00651 NS_IMETHODIMP nsIconChannel::GetContentType(nsACString &aContentType) 
00652 {
00653   aContentType.AssignLiteral("image/icon");
00654   return NS_OK;
00655 }
00656 
00657 NS_IMETHODIMP
00658 nsIconChannel::SetContentType(const nsACString &aContentType)
00659 {
00660   // It doesn't make sense to set the content-type on this type
00661   // of channel...
00662   return NS_ERROR_FAILURE;
00663 }
00664 
00665 NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString &aContentCharset) 
00666 {
00667   aContentCharset.Truncate();
00668   return NS_OK;
00669 }
00670 
00671 NS_IMETHODIMP
00672 nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
00673 {
00674   // It doesn't make sense to set the content-charset on this type
00675   // of channel...
00676   return NS_ERROR_FAILURE;
00677 }
00678 
00679 NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt32 *aContentLength)
00680 {
00681   *aContentLength = mContentLength;
00682   return NS_OK;
00683 }
00684 
00685 NS_IMETHODIMP nsIconChannel::SetContentLength(PRInt32 aContentLength)
00686 {
00687   NS_NOTREACHED("nsIconChannel::SetContentLength");
00688   return NS_ERROR_NOT_IMPLEMENTED;
00689 }
00690 
00691 NS_IMETHODIMP nsIconChannel::GetOwner(nsISupports* *aOwner)
00692 {
00693   *aOwner = mOwner.get();
00694   NS_IF_ADDREF(*aOwner);
00695   return NS_OK;
00696 }
00697 
00698 NS_IMETHODIMP nsIconChannel::SetOwner(nsISupports* aOwner)
00699 {
00700   mOwner = aOwner;
00701   return NS_OK;
00702 }
00703 
00704 NS_IMETHODIMP nsIconChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
00705 {
00706   *aNotificationCallbacks = mCallbacks.get();
00707   NS_IF_ADDREF(*aNotificationCallbacks);
00708   return NS_OK;
00709 }
00710 
00711 NS_IMETHODIMP nsIconChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
00712 {
00713   mCallbacks = aNotificationCallbacks;
00714   return NS_OK;
00715 }
00716 
00717 NS_IMETHODIMP nsIconChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
00718 {
00719   *aSecurityInfo = nsnull;
00720   return NS_OK;
00721 }
00722 
00723 // nsIRequestObserver methods
00724 NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
00725 {
00726   if (mListener)
00727     return mListener->OnStartRequest(this, aContext);
00728   return NS_OK;
00729 }
00730 
00731 NS_IMETHODIMP nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
00732 {
00733   if (mListener) {
00734     mListener->OnStopRequest(this, aContext, aStatus);
00735     mListener = nsnull;
00736   }
00737 
00738   // Remove from load group
00739   if (mLoadGroup)
00740     mLoadGroup->RemoveRequest(this, nsnull, aStatus);
00741 
00742   // Drop notification callbacks to prevent cycles.
00743   mCallbacks = nsnull;
00744 
00745   return NS_OK;
00746 }
00747 
00748 //------------------------------------------------------------------------
00749 // nsIStreamListener methods
00750 NS_IMETHODIMP nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
00751                                              nsISupports* aContext,
00752                                              nsIInputStream* aStream,
00753                                              PRUint32 aOffset,
00754                                              PRUint32 aCount)
00755 {
00756   if (mListener)
00757     return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
00758   return NS_OK;
00759 }
00760 
00761 //------------------------------------------------------------------------
00762