Back to index

lightning-sunbird  0.9+nobinonly
nsCairoImage.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  * mozilla.org.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Stuart Parmenter <pavlov@pavlov.net>
00024  *   Vladimir Vukicevic <vladimir@pobox.com>
00025  *   Joe Hewitt <hewitt@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsMemory.h"
00042 #include "nsCairoImage.h"
00043 #include "nsCairoRenderingContext.h"
00044 #include "nsCairoDrawingSurface.h"
00045 
00046 #include "nsIServiceManager.h"
00047 
00048 static void ARGBToThreeChannel(PRUint32* aARGB, PRUint8* aData) {
00049     PRUint8 r = (PRUint8)(*aARGB >> 16);
00050     PRUint8 g = (PRUint8)(*aARGB >> 8);
00051     PRUint8 b = (PRUint8)*aARGB;
00052 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
00053     // BGR format; assume little-endian system
00054 #ifndef IS_LITTLE_ENDIAN
00055 #error Strange big-endian/OS combination
00056 #endif
00057     // BGR, blue byte first
00058     aData[0] = b; aData[1] = g; aData[2] = r;
00059 #else
00060     // RGB, red byte first
00061     aData[0] = r; aData[1] = g; aData[2] = b;
00062 #endif
00063 }
00064 
00065 static PRUint32 ThreeChannelToARGB(PRUint8* aData, PRUint8 aAlpha) {
00066     PRUint8 r, g, b;
00067 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
00068     // BGR format; assume little-endian system
00069 #ifndef IS_LITTLE_ENDIAN
00070 #error Strange big-endian/OS combination
00071 #endif
00072     // BGR, blue byte first
00073     b = aData[0]; g = aData[1]; r = aData[2];
00074 #else
00075     // RGB, red byte first
00076     r = aData[0]; g = aData[1]; b = aData[2];
00077 #endif
00078     return (aAlpha << 24) | (r << 16) | (g << 8) | b;
00079 }
00080 
00081 nsCairoImage::nsCairoImage()
00082  : mWidth(0),
00083    mHeight(0),
00084    mDecoded(0,0,0,0),
00085    mImageSurface(nsnull),
00086    mImageSurfaceBuf(nsnull),
00087    mImageSurfaceData(nsnull),
00088    mImageSurfaceAlpha(nsnull),
00089    mAlphaDepth(0),
00090    mHadAnyData(PR_FALSE)
00091 {
00092 }
00093 
00094 nsCairoImage::~nsCairoImage()
00095 {
00096     if (mImageSurface)
00097         cairo_surface_destroy(mImageSurface);
00098     // XXXX - totally not safe. mImageSurface should own data,
00099     // but it doesn't.
00100     if (mImageSurfaceBuf)
00101         nsMemory::Free(mImageSurfaceBuf);
00102     if (mImageSurfaceData)
00103         nsMemory::Free(mImageSurfaceData);
00104     if (mImageSurfaceAlpha)
00105         nsMemory::Free(mImageSurfaceAlpha);
00106 }
00107 
00108 NS_IMPL_ISUPPORTS1(nsCairoImage, nsIImage)
00109 
00110 
00111 
00112 nsresult
00113 nsCairoImage::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequirements aMaskRequirements)
00114 {
00115     mWidth = aWidth;
00116     mHeight = aHeight;
00117 
00118     switch(aMaskRequirements)
00119     {
00120     case nsMaskRequirements_kNeeds1Bit:
00121         mAlphaDepth = 1;
00122         mCairoFormat = CAIRO_FORMAT_ARGB32;
00123         break;
00124     case nsMaskRequirements_kNeeds8Bit:
00125         mAlphaDepth = 8;
00126         mCairoFormat = CAIRO_FORMAT_ARGB32;
00127         break;
00128     default:
00129         mAlphaDepth = 0;
00130         // this should be able to be RGB24, but that doesn't seem to work
00131         mCairoFormat = CAIRO_FORMAT_ARGB32;
00132         break;
00133     }
00134 
00135     PRInt32 size = aWidth*aHeight*4;
00136     mImageSurfaceBuf = (PRUint32*)nsMemory::Alloc(size);
00137     NS_ENSURE_TRUE(mImageSurfaceBuf, NS_ERROR_OUT_OF_MEMORY);
00138     // Fill it in. Setting alpha to FF is important for images with no
00139     // alpha of their own
00140     memset(mImageSurfaceBuf, 0xFF, size);
00141 
00142     mImageSurface = cairo_image_surface_create_for_data
00143         ((char*)mImageSurfaceBuf, mCairoFormat, mWidth, mHeight, aWidth * 4);
00144     return NS_OK;
00145 }
00146 
00147 PRInt32
00148 nsCairoImage::GetBytesPix()
00149 {
00150     // not including alpha, I guess
00151     return 3;
00152 }
00153 
00154 PRBool
00155 nsCairoImage::GetIsRowOrderTopToBottom()
00156 {
00157     return PR_FALSE;
00158 }
00159 
00160 PRInt32
00161 nsCairoImage::GetWidth()
00162 {
00163     return mWidth;
00164 }
00165 
00166 PRInt32
00167 nsCairoImage::GetHeight()
00168 {
00169     return mHeight;
00170 }
00171 
00172 PRUint8 *
00173 nsCairoImage::GetBits()
00174 {
00175     return (PRUint8 *) mImageSurfaceData;
00176 }
00177 
00178 PRInt32
00179 nsCairoImage::GetLineStride()
00180 {
00181     return mWidth*3;
00182 }
00183 
00184 PRBool
00185 nsCairoImage::GetHasAlphaMask()
00186 {
00187     return mAlphaDepth > 0;
00188 }
00189 
00190 PRUint8 *
00191 nsCairoImage::GetAlphaBits()
00192 {
00193     return (PRUint8 *) mImageSurfaceAlpha;
00194 }
00195 
00196 PRInt32
00197 nsCairoImage::GetAlphaLineStride()
00198 {
00199     return mAlphaDepth == 1 ? (mWidth+7)/8 : mWidth;
00200 }
00201 
00202 void
00203 nsCairoImage::ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect)
00204 {
00205     mDecoded.UnionRect(mDecoded, *aUpdateRect);
00206 }
00207 
00208 PRBool
00209 nsCairoImage::GetIsImageComplete()
00210 {
00211     return mDecoded == nsRect(0, 0, mWidth, mHeight);
00212 }
00213 
00214 nsresult
00215 nsCairoImage::Optimize(nsIDeviceContext* aContext)
00216 {
00217     return NS_OK;
00218 }
00219 
00220 nsColorMap *
00221 nsCairoImage::GetColorMap()
00222 {
00223     return NULL;
00224 }
00225 
00226 /* WHY ARE THESE HERE?!?!?!?!?!? */
00227 /* INSTEAD OF RENDERINGCONTEXT, WHICH ONLY TAKES IMGICONTAINER?!?!?!? */
00228 NS_IMETHODIMP
00229 nsCairoImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface,
00230                    PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
00231 {
00232     return Draw(aContext, aSurface, 0, 0, mWidth, mHeight, aX, aY, aWidth, aHeight);
00233 }
00234 
00235 void
00236 nsCairoImage::UpdateFromImageData()
00237 {
00238     if (!mImageSurfaceData)
00239         return;
00240     
00241     NS_ASSERTION(mAlphaDepth == 0 || mAlphaDepth == 1 || mAlphaDepth == 8,
00242                  "Bad alpha depth");
00243 
00244     PRInt32 alphaIndex = 0;
00245     PRInt32 bufIndex = 0;
00246     for (PRInt32 row = 0; row < mHeight; ++row) {
00247         for (PRInt32 col = 0; col < mWidth; ++col) {
00248             PRUint8 alpha = 0xFF;
00249             if (mAlphaDepth == 1) {
00250                 PRUint8 mask = 1 << (7 - (col&7));
00251                 if (!(mImageSurfaceAlpha[alphaIndex] & mask)) {
00252                     alpha = 0;
00253                 }
00254                 if (mask == 0x01) {
00255                     ++alphaIndex;
00256                 }
00257             } else if (mAlphaDepth == 8) {
00258                 alpha = mImageSurfaceAlpha[bufIndex];
00259             }
00260             mImageSurfaceBuf[bufIndex] = ThreeChannelToARGB(&mImageSurfaceData[bufIndex*3], alpha);
00261             ++bufIndex;
00262         }
00263         if (mAlphaDepth == 1) {
00264             if (mWidth & 7) {
00265                 ++alphaIndex;
00266             }
00267         }
00268     }
00269 
00270     if (PR_FALSE) { // Enabling this saves memory but can lead to pathological
00271         // behaviour
00272         nsMemory::Free(mImageSurfaceData);
00273         mImageSurfaceData = nsnull;
00274         if (mImageSurfaceAlpha) {
00275             nsMemory::Free(mImageSurfaceAlpha);
00276             mImageSurfaceAlpha = nsnull;
00277         }
00278 
00279         mHadAnyData = PR_TRUE;
00280     }
00281 }
00282 
00283 NS_IMETHODIMP
00284 nsCairoImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface,
00285                    PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight,
00286                    PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight)
00287 {
00288 #if 1
00289     fprintf (stderr, "****** nsCairoImage::Draw: (%d,%d,%d,%d) -> (%d,%d,%d,%d)\n",
00290              aSX, aSY, aSWidth, aSHeight,
00291              aDX, aDY, aDWidth, aDHeight);
00292 #endif
00293     UpdateFromImageData();
00294 
00295     nsCairoDrawingSurface *dstSurf = NS_STATIC_CAST(nsCairoDrawingSurface*, aSurface);
00296     nsCairoRenderingContext *cairoContext = NS_STATIC_CAST(nsCairoRenderingContext*, &aContext);
00297 
00298     cairo_t *dstCairo = cairoContext->GetCairo();
00299 
00300     cairo_translate(dstCairo, aDX, aDY);
00301 
00302     cairo_pattern_t *pat = cairo_pattern_create_for_surface (mImageSurface);
00303     cairo_matrix_t* matrix = cairo_matrix_create();
00304     if (matrix) {
00305         cairo_matrix_set_affine(matrix, double(aSWidth)/aDWidth, 0,
00306                                 0, double(aSHeight)/aDHeight, aSX, aSY);
00307         cairo_pattern_set_matrix(pat, matrix);
00308         cairo_matrix_destroy(matrix);
00309     }
00310     cairo_set_pattern (dstCairo, pat);
00311     cairo_pattern_destroy (pat);
00312 
00313     cairo_new_path(dstCairo);
00314     cairo_rectangle (dstCairo, 0, 0, aDWidth, aDHeight);
00315     cairo_fill (dstCairo);
00316     cairo_translate(dstCairo, -aDX, -aDY);
00317     
00318     return NS_OK;
00319 }
00320 
00321 NS_IMETHODIMP
00322 nsCairoImage::DrawTile(nsIRenderingContext &aContext,
00323                        nsIDrawingSurface *aSurface,
00324                        PRInt32 aSXOffset, PRInt32 aSYOffset,
00325                        PRInt32 aPadX, PRInt32 aPadY,
00326                        const nsRect &aTileRect)
00327 {
00328     UpdateFromImageData();
00329 
00330     if (aPadX || aPadY)
00331         fprintf (stderr, "Warning: nsCairoImage::DrawTile given padX(%d)/padY(%d), ignoring\n", aPadX, aPadY);
00332 
00333     nsCairoDrawingSurface *dstSurf = NS_STATIC_CAST(nsCairoDrawingSurface*, aSurface);
00334     nsCairoRenderingContext *cairoContext = NS_STATIC_CAST(nsCairoRenderingContext*, &aContext);
00335 
00336     cairo_t *dstCairo = cairoContext->GetCairo();
00337 
00338     cairo_translate(dstCairo, aTileRect.x, aTileRect.y);
00339 
00340     cairo_pattern_t *pat = cairo_pattern_create_for_surface (mImageSurface);
00341     cairo_matrix_t* matrix = cairo_matrix_create();
00342     if (matrix) {
00343         nsCOMPtr<nsIDeviceContext> dc;
00344         aContext.GetDeviceContext(*getter_AddRefs(dc));
00345         float app2dev = dc->AppUnitsToDevUnits();
00346         cairo_matrix_set_affine(matrix, app2dev, 0, 0, app2dev, aSXOffset, aSYOffset);
00347         cairo_pattern_set_matrix(pat, matrix);
00348         cairo_matrix_destroy(matrix);
00349     }
00350     cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT);
00351     cairo_set_pattern (dstCairo, pat);
00352     cairo_pattern_destroy (pat);
00353 
00354     cairo_new_path(dstCairo);
00355     cairo_rectangle (dstCairo, 0, 0, aTileRect.width, aTileRect.height);
00356     cairo_fill (dstCairo);
00357     cairo_translate(dstCairo, -aTileRect.x, -aTileRect.y);
00358 
00359     return NS_OK;
00360 }
00361 
00362 NS_IMETHODIMP
00363 nsCairoImage::DrawToImage(nsIImage* aDstImage, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight)
00364 {
00365     UpdateFromImageData();
00366 
00367     nsCairoImage *dstCairoImage = NS_STATIC_CAST(nsCairoImage*, aDstImage);
00368 
00369     cairo_t *dstCairo = cairo_create ();
00370 
00371     cairo_set_target_surface(dstCairo, dstCairoImage->mImageSurface);
00372 
00373     // clip to target
00374     cairo_rectangle(dstCairo, double(aDX), double(aDY), double(aDWidth), double(aDHeight));
00375     cairo_clip(dstCairo);
00376 
00377     // scale up to the size difference
00378     cairo_scale(dstCairo, double(aDWidth)/double(mWidth), double(aDHeight)/double(mHeight));
00379 
00380     // move to where we need to start the image rectangle, so that
00381     // it gets clipped to the right place
00382     cairo_translate(dstCairo, double(aDX), double(aDY));
00383 
00384     // show it
00385     cairo_show_surface (dstCairo, mImageSurface, mWidth, mHeight);
00386 
00387     cairo_destroy (dstCairo);
00388 
00389     return NS_OK;
00390 }
00391 
00392 PRInt8
00393 nsCairoImage::GetAlphaDepth()
00394 {
00395     return mAlphaDepth;
00396 }
00397 
00398 void *
00399 nsCairoImage::GetBitInfo()
00400 {
00401     return NULL;
00402 }
00403 
00404 NS_IMETHODIMP
00405 nsCairoImage::LockImagePixels(PRBool aMaskPixels)
00406 {
00407     if (mImageSurfaceData) {
00408         return NS_OK;
00409     }
00410 
00411     if (mAlphaDepth > 0) {
00412         NS_ASSERTION(mAlphaDepth == 1 || mAlphaDepth == 8,
00413                      "Bad alpha depth");
00414         PRUint32 size = mHeight*(mAlphaDepth == 1 ? ((mWidth+7)/8) : mWidth);
00415         mImageSurfaceAlpha = (PRUint8*)nsMemory::Alloc(size);
00416         if (!mImageSurfaceAlpha)
00417             return NS_ERROR_OUT_OF_MEMORY;
00418     }
00419 
00420     PRUint32 size = mWidth * mHeight * 3;
00421     mImageSurfaceData = (PRUint8*)nsMemory::Alloc(size);
00422     if (!mImageSurfaceData)
00423         return NS_ERROR_OUT_OF_MEMORY;
00424 
00425     PRInt32 count = mWidth*mHeight;
00426     if (mAlphaDepth > 0 && mHadAnyData) {
00427         PRInt32 size = mHeight*(mAlphaDepth == 1 ? ((mWidth+7)/8) : mWidth);
00428         mImageSurfaceAlpha = (PRUint8*)nsMemory::Alloc(size);
00429 
00430         // fill from existing ARGB buffer
00431         if (mAlphaDepth == 8) {
00432             for (PRInt32 i = 0; i < count; ++i) {
00433                 mImageSurfaceAlpha[i] = (PRUint8)(mImageSurfaceBuf[i] >> 24);
00434             }
00435         } else {
00436             PRInt32 i = 0;
00437             for (PRInt32 row = 0; row < mHeight; ++row) {
00438                 PRUint8 alphaBits = 0;
00439                 for (PRInt32 col = 0; col < mWidth; ++col) {
00440                     PRUint8 mask = 1 << (7 - (col&7));
00441                     if (mImageSurfaceBuf[row*mWidth + col] & 0xFF000000) {
00442                         alphaBits |= mask;
00443                     }
00444                     if (mask == 0x01) {
00445                         // This mask byte is complete, write it back
00446                         mImageSurfaceAlpha[i] = alphaBits;
00447                         alphaBits = 0;
00448                         ++i;
00449                     }
00450                 }
00451                 if (mWidth & 7) {
00452                     // write back the incomplete alpha mask
00453                     mImageSurfaceAlpha[i] = alphaBits;
00454                     ++i;
00455                 }
00456             }
00457         }
00458     }
00459     
00460     if (mHadAnyData) {
00461         // fill from existing ARGB buffer
00462         for (PRInt32 i = 0; i < count; ++i) {
00463             ARGBToThreeChannel(&mImageSurfaceBuf[i], &mImageSurfaceData[i*3]);
00464         }
00465     }
00466 
00467     return NS_OK;
00468 }
00469 
00470 NS_IMETHODIMP
00471 nsCairoImage::UnlockImagePixels(PRBool aMaskPixels)
00472 {
00473     return NS_OK;
00474 }