Back to index

lightning-sunbird  0.9+nobinonly
nsImageQt.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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  * 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  *   Lars Knoll <knoll@kde.org>
00024  *   Zack Rusin <zack@kde.org>
00025  *   John C. Griggs <johng@corel.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 "nsImageQt.h"
00042 #include "nsRenderingContextQt.h"
00043 
00044 #include <qimage.h>
00045 
00046 #include "nspr.h"
00047 #include "qtlog.h"
00048 
00049 #define IsFlagSet(a,b) ((a) & (b))
00050 
00051 #ifdef CHECK_PIXMAPS
00052 #include <qfileinfo.h>
00053 static void savePixmap(const QPixmap &px)
00054 {
00055     static int i = 0;
00056 
00057     QString baseFile( "saved_pixmap" );
00058     const char *format = "PNG";
00059 
00060 
00061     QString fileName = QString("%1%2.%2").arg(baseFile)
00062                        .arg(i).arg(format);
00063 
00064     while (QFileInfo(fileName).exists()) {
00065         ++i;
00066         fileName = QString("%1%2.%2").arg(baseFile)
00067                    .arg(i).arg(format);
00068     }
00069     px.save(fileName, format);
00070 }
00071 #endif
00072 
00073 
00074 
00075 NS_IMPL_ISUPPORTS1(nsImageQt, nsIImage)
00076 
00077 //------------------------------------------------------------
00078 nsImageQt::nsImageQt()
00079     : mWidth(0)
00080     , mHeight(0)
00081     , mDepth(0)
00082     , mRowBytes(0)
00083     , mImageBits(nsnull)
00084     , mAlphaDepth(0)
00085     , mAlphaRowBytes(0)
00086     , mAlphaBits(nsnull)
00087     , mNumBytesPixel(0)
00088     , pixmapDirty(PR_FALSE)
00089 {
00090 }
00091 
00092 //------------------------------------------------------------
00093 nsImageQt::~nsImageQt()
00094 {
00095     if (nsnull != mImageBits) {
00096         delete[] mImageBits;
00097         mImageBits = nsnull;
00098     }
00099     if (nsnull != mAlphaBits) {
00100         delete[] mAlphaBits;
00101         mAlphaBits = nsnull;
00102     }
00103 }
00104 
00105 //------------------------------------------------------------
00106 nsresult nsImageQt::Init(PRInt32 aWidth,PRInt32 aHeight,
00107                          PRInt32 aDepth,
00108                          nsMaskRequirements aMaskRequirements)
00109 {
00110     qDebug("creating a pixmap with [%d,%d,%d]", aWidth, aHeight, aDepth);
00111     // gfxImageFrame forces only one nsImageQt::Init
00112     if (aWidth == 0 || aHeight == 0) {
00113         return NS_ERROR_FAILURE;
00114     }
00115 
00116 //     if (32 == aDepth) {
00117 //         mNumBytesPixel = 4;
00118 //         mDepth = aDepth;
00119 //     }
00120 //     else
00121     if (24 == aDepth) {
00122         mNumBytesPixel = 3;
00123         mDepth = aDepth;
00124     }
00125     else {
00126         NS_NOTREACHED("unexpected image depth");
00127         return NS_ERROR_UNEXPECTED;
00128     }
00129     mWidth = aWidth;
00130     mHeight = aHeight;
00131     mRowBytes = (mWidth*mDepth/8 + 3) & ~0x3;
00132 
00133     switch (aMaskRequirements) {
00134     case nsMaskRequirements_kNeeds1Bit:
00135         mAlphaRowBytes = (aWidth + 7) / 8;
00136         mAlphaDepth = 1;
00137         // 32-bit align each row
00138         mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3;
00139         break;
00140 
00141     case nsMaskRequirements_kNeeds8Bit:
00142         mAlphaRowBytes = aWidth;
00143         mAlphaDepth = 8;
00144         // 32-bit align each row
00145         mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3;
00146         break;
00147     default:
00148         mAlphaDepth = 0;
00149         mAlphaRowBytes = 0;
00150         break;
00151     }
00152 
00153     mImageBits = (PRUint8*)new PRUint8[mRowBytes * mHeight];
00154     mAlphaBits = new PRUint8[mAlphaRowBytes * mHeight];
00155 
00156     pixmapDirty = PR_TRUE;
00157 
00158     PR_LOG(gQtLogModule, QT_BASIC, ("nsImageQt::Init succeeded"));
00159     return NS_OK;
00160 }
00161 
00162 //------------------------------------------------------------
00163 
00164 PRInt32 nsImageQt::GetHeight()
00165 {
00166     return mHeight;
00167 }
00168 
00169 PRInt32 nsImageQt::GetWidth()
00170 {
00171     return mWidth;
00172 }
00173 
00174 PRUint8 *nsImageQt::GetBits()
00175 {
00176     return mImageBits;
00177 }
00178 
00179 void *nsImageQt::GetBitInfo()
00180 {
00181     return nsnull;
00182 }
00183 
00184 PRInt32 nsImageQt::GetLineStride()
00185 {
00186     return mRowBytes;
00187 }
00188 
00189 nsColorMap *nsImageQt::GetColorMap()
00190 {
00191     return nsnull;
00192 }
00193 
00194 PRUint8 *nsImageQt::GetAlphaBits()
00195 {
00196     return mAlphaBits;
00197 }
00198 
00199 PRInt32 nsImageQt::GetAlphaLineStride()
00200 {
00201     return mAlphaRowBytes;
00202 }
00203 
00204 //------------------------------------------------------------
00205 
00206 // set up the palette to the passed in color array, RGB only in this array
00207 void nsImageQt::ImageUpdated(nsIDeviceContext *aContext,
00208                              PRUint8 aFlags,nsRect *aUpdateRect)
00209 {
00210     //qDebug("image updated");
00211     if (IsFlagSet(nsImageUpdateFlags_kBitsChanged,aFlags))
00212         pixmapDirty = PR_TRUE;
00213 
00214     mDecodedRect.UnionRect(mDecodedRect, *aUpdateRect);
00215 }
00216 
00220 PRBool nsImageQt::GetIsImageComplete() {
00221   return mDecodedRect.x == 0 &&
00222          mDecodedRect.y == 0 &&
00223          mDecodedRect.width == mWidth &&
00224          mDecodedRect.height == mHeight;
00225 }
00226 
00227 // Draw the bitmap, this method has a source and destination coordinates
00228 NS_IMETHODIMP nsImageQt::Draw(nsIRenderingContext &aContext,
00229                               nsIDrawingSurface *aSurface,
00230                               PRInt32 aSX, PRInt32 aSY,
00231                               PRInt32 aSWidth, PRInt32 aSHeight,
00232                               PRInt32 aDX, PRInt32 aDY,
00233                               PRInt32 aDWidth, PRInt32 aDHeight)
00234 {
00235     // qDebug("draw x = %d, y = %d, w = %d, h = %d, dx = %d, dy = %d, dw = %d, dy = %d",
00236     //         aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight);
00237     if (aSWidth <= 0 || aDWidth <= 0 || aSHeight <= 0 || aDHeight <= 0)
00238       return NS_OK;
00239     if (nsnull == aSurface) {
00240         return NS_ERROR_FAILURE;
00241     }
00242     if (pixmapDirty)
00243         updatePixmap();
00244 
00245     nsDrawingSurfaceQt *drawing = (nsDrawingSurfaceQt*)aSurface;
00246 
00247     // The code below seems to be wrong and not what gecko expects.
00248 //     if ((aSWidth != aDWidth || aSHeight != aDHeight)
00249 //         && (aSWidth != mWidth || aSHeight != mHeight)) {
00250 //         QPixmap tmp(aSWidth, aSHeight);
00251 //         copyBlt(&tmp, 0, 0, &pixmap, aSX, aSY, aSWidth, aSHeight);
00252 //         drawing->GetGC()->drawPixmap(aDX, aDY, pixmap, aSX, aSY, aSWidth, aSHeight);
00253 //     }
00254 
00255     nsRect sourceRect(aSX, aSY, aSWidth, aSHeight);
00256     if (!sourceRect.IntersectRect(sourceRect, mDecodedRect))
00257         return NS_OK;
00258 
00259     // Now get the part of the image that should be drawn
00260     // Copy into a new image so we can scale afterwards
00261     QImage image(pixmap.convertToImage().copy(sourceRect.x, sourceRect.y,
00262                                               sourceRect.width, sourceRect.height));
00263 
00264     if (image.isNull())
00265         return NS_ERROR_FAILURE;
00266 
00267     // Find the scale factor
00268     float w_factor = (float)aDWidth / aSWidth;
00269     float h_factor = (float)aDHeight / aSHeight;
00270 
00271     // If we had to draw only part of the requested image, must adjust
00272     // destination coordinates
00273     aDX += PRInt32((sourceRect.x - aSX) * w_factor);
00274     aDY += PRInt32((sourceRect.y - aSY) * h_factor);
00275     image = image.scale(int(sourceRect.width * w_factor), int(sourceRect.height * h_factor));
00276     drawing->GetGC()->drawImage(QPoint(aDX, aDY), image);
00277 
00278     //drawing->GetGC()->drawPixmap(aDX, aDY, pixmap, aSX, aSY, aSWidth, aSHeight);
00279 
00280 
00281     return NS_OK;
00282 }
00283 
00284 //------------------------------------------------------------
00285 
00286 // Draw the bitmap, this draw just has destination coordinates
00287 NS_IMETHODIMP nsImageQt::Draw(nsIRenderingContext &aContext,
00288                               nsIDrawingSurface *aSurface,
00289                               PRInt32 aX, PRInt32 aY,
00290                               PRInt32 aWidth, PRInt32 aHeight)
00291 {
00292     return Draw(aContext, aSurface, 0, 0, mWidth, mHeight, aX, aY, aWidth, aHeight);
00293 }
00294 
00295 void nsImageQt::updatePixmap()
00296 {
00297     //qDebug("updatePixmap");
00298     QImage qimage(mWidth, mHeight, 32);
00299     const PRInt32 bytesPerPixel = mDepth / 8;
00300     PRUint8 *image = mImageBits;
00301     PRUint8 *alpha = mAlphaBits;
00302 
00303     PRInt32 i,j;
00304     QRgb *line;
00305 
00306     qimage.setAlphaBuffer(mAlphaDepth != 0);
00307     switch(mAlphaDepth) {
00308     case 0:
00309         for (i = 0; i < mHeight; i++) {
00310             line = (QRgb*)qimage.scanLine(i);
00311 
00312             PRUint8 *imagePtr = image;
00313             for (j = 0; j < mWidth; j++) {
00314                 line[j] = qRgb(*imagePtr, *(imagePtr+1), *(imagePtr+2));
00315                 imagePtr += bytesPerPixel;
00316             }
00317             image += mRowBytes;
00318         }
00319         break;
00320     case 1:
00321         for (i = 0; i < mHeight; i++) {
00322             line = (QRgb*)qimage.scanLine(i);
00323 
00324             PRUint8 *imagePtr = image;
00325             for (j = 0; j < mWidth; j++) {
00326                 uchar a = (alpha[j / 8] & (1 << (7 - (j % 8)))) ? 0xff : 0;
00327                 line[j] = qRgba(*imagePtr, *(imagePtr+1), *(imagePtr+2), a);
00328                 imagePtr += bytesPerPixel;
00329             }
00330             image += mRowBytes;
00331             alpha += mAlphaRowBytes;
00332         }
00333         break;
00334     case 8:
00335         for (i = 0; i < mHeight; i++) {
00336             line = (QRgb*)qimage.scanLine(i);
00337 
00338             PRUint8 *imagePtr = image;
00339             PRUint8 *alphaPtr = alpha;
00340             for (j = 0; j < mWidth; j++) {
00341                 line[j] = qRgba(*imagePtr, *(imagePtr+1), *(imagePtr+2), *alphaPtr);
00342                 imagePtr += bytesPerPixel;
00343                 alphaPtr++;
00344             }
00345             image += mRowBytes;
00346             alpha += mAlphaRowBytes;
00347         }
00348         break;
00349     }
00350 
00351     pixmap = QPixmap(qimage);
00352 
00353 #ifdef CHECK_PIXMAPS
00354     savePixmap(pixmap);
00355 #endif
00356     pixmapDirty = PR_FALSE;
00357 }
00358 
00359 NS_IMETHODIMP nsImageQt::DrawTile(nsIRenderingContext &aContext,
00360                                   nsIDrawingSurface *aSurface,
00361                                   PRInt32 aSXOffset, PRInt32 aSYOffset,
00362                                   PRInt32 aPadX, PRInt32 aPadY,
00363                                   const nsRect &aTileRect)
00364 {
00365     //qDebug("draw tile");
00366 #if 1
00367     nsRect aSrcRect(aSXOffset, aSYOffset, mWidth, mHeight);
00368 
00369     nsDrawingSurfaceQt *drawing = (nsDrawingSurfaceQt*)aSurface;
00370 
00371     if (aTileRect.width <= 0 || aTileRect.height <= 0) {
00372         NS_ASSERTION(aTileRect.width > 0 && aTileRect.height > 0,
00373                      "Error: image has 0 width or height!");
00374         return NS_OK;
00375     }
00376     if (drawing->GetDepth() == 8 || mAlphaDepth == 8) {
00377         PRInt32 aY0 = aTileRect.y, aX0 = aTileRect.x;
00378         PRInt32 aY1 = aTileRect.y + aTileRect.height;
00379         PRInt32 aX1 = aTileRect.x + aTileRect.width;
00380 
00381         for (PRInt32 y = aY0; y < aY1; y += aSrcRect.height + aPadY)
00382             for (PRInt32 x = aX0; x < aX1; x += aSrcRect.width + aPadX)
00383                 Draw(aContext, aSurface, x, y, PR_MIN(aSrcRect.width, aX1 - x),
00384                      PR_MIN(aSrcRect.height, aY1 - y));
00385 
00386         return NS_OK;
00387     }
00388 #if 0
00389     // Render unique image bits onto an off screen pixmap only once
00390     // The image bits can change as a result of ImageUpdated() - for
00391     // example: animated GIFs.
00392     if (nsnull == mImagePixmap) {
00393         CreateImagePixmap();
00394     }
00395     if (nsnull == mImagePixmap)
00396         return NS_ERROR_FAILURE;
00397 
00398     QPixmap qPmap;
00399 
00400     qPmap.convertFromImage(*mImagePixmap);
00401 #endif
00402     /*qDebug("draw tilePixmap x = %d, y = %d, wid = %d, hei = %d, sx = %d, sy =  %d",
00403            aTileRect.x, aTileRect.y, aTileRect.width, aTileRect.height,
00404            aSrcRect.x, aSrcRect.y);*/
00405     drawing->GetGC()->drawTiledPixmap(aTileRect.x, aTileRect.y,
00406                                       aTileRect.width, aTileRect.height,
00407                                       pixmap, aSrcRect.x, aSrcRect.y);
00408     //qPmap, aSrcRect.x, aSrcRect.y);
00409 #endif
00410     return NS_OK;
00411 }
00412 
00413 //------------------------------------------------------------
00414 
00415 nsresult nsImageQt::Optimize(nsIDeviceContext* aContext)
00416 {
00417     return NS_OK;
00418 }
00419 
00420 PRInt32 nsImageQt::GetBytesPix()
00421 {
00422     return mNumBytesPixel;
00423 }
00424 
00425 //------------------------------------------------------------
00426 // lock the image pixels. Implement this if you need it.
00427 NS_IMETHODIMP
00428 nsImageQt::LockImagePixels(PRBool aMaskPixels)
00429 {
00430     return NS_OK;
00431 }
00432 
00433 //------------------------------------------------------------
00434 // unlock the image pixels.  Implement this if you need it.
00435 NS_IMETHODIMP
00436 nsImageQt::UnlockImagePixels(PRBool aMaskPixels)
00437 {
00438     return NS_OK;
00439 }
00440 
00441 NS_IMETHODIMP nsImageQt::DrawToImage(nsIImage* aDstImage,
00442                                       nscoord aDX, nscoord aDY,
00443                                       nscoord aDWidth, nscoord aDHeight)
00444 {
00445     //qDebug("DrawToIMAGE");
00446   nsImageQt *dest = NS_STATIC_CAST(nsImageQt *, aDstImage);
00447 
00448   if (!dest)
00449     return NS_ERROR_FAILURE;
00450 
00451   if (aDX >= dest->mWidth || aDY >= dest->mHeight)
00452     return NS_OK;
00453 
00454   PRUint8 *rgbPtr=0, *alphaPtr=0;
00455   PRUint32 rgbStride, alphaStride;
00456 
00457   rgbPtr = mImageBits;
00458   rgbStride = mRowBytes;
00459   alphaPtr = mAlphaBits;
00460   alphaStride = mAlphaRowBytes;
00461 
00462   PRInt32 y;
00463   PRInt32 ValidWidth = ( aDWidth < ( dest->mWidth - aDX ) ) ? aDWidth : ( dest->mWidth - aDX );
00464   PRInt32 ValidHeight = ( aDHeight < ( dest->mHeight - aDY ) ) ? aDHeight : ( dest->mHeight - aDY );
00465 
00466   // now composite the two images together
00467   switch (mAlphaDepth) {
00468   case 1:
00469     {
00470       PRUint8 *dst = dest->mImageBits + aDY*dest->mRowBytes + 3*aDX;
00471       PRUint8 *dstAlpha = dest->mAlphaBits + aDY*dest->mAlphaRowBytes;
00472       PRUint8 *src = rgbPtr;
00473       PRUint8 *alpha = alphaPtr;
00474       PRUint8 offset = aDX & 0x7; // x starts at 0
00475       int iterations = (ValidWidth+7)/8; // round up
00476 
00477       for (y=0; y<ValidHeight; y++) {
00478         for (int x=0; x<ValidWidth; x += 8, dst += 3*8, src += 3*8) {
00479           PRUint8 alphaPixels = *alpha++;
00480           if (alphaPixels == 0) {
00481             // all 8 transparent; jump forward
00482             continue;
00483           }
00484 
00485           // 1 or more bits are set, handle dstAlpha now - may not be aligned.
00486           // Are all 8 of these alpha pixels used?
00487           if (x+7 >= ValidWidth) {
00488             alphaPixels &= 0xff << (8 - (ValidWidth-x)); // no, mask off unused
00489             if (alphaPixels == 0)
00490               continue;  // no 1 alpha pixels left
00491           }
00492           if (offset == 0) {
00493             dstAlpha[(aDX+x)>>3] |= alphaPixels; // the cheap aligned case
00494           }
00495           else {
00496             dstAlpha[(aDX+x)>>3]       |= alphaPixels >> offset;
00497             // avoid write if no 1's to write - also avoids going past end of array
00498             PRUint8 alphaTemp = alphaPixels << (8U - offset);
00499             if (alphaTemp & 0xff)
00500               dstAlpha[((aDX+x)>>3) + 1] |= alphaTemp;
00501           }
00502 
00503           if (alphaPixels == 0xff) {
00504             // fix - could speed up by gathering a run of 0xff's and doing 1 memcpy
00505             // all 8 pixels set; copy and jump forward
00506             memcpy(dst,src,8*3);
00507             continue;
00508           }
00509           else {
00510             // else mix of 1's and 0's in alphaPixels, do 1 bit at a time
00511             // Don't go past end of line!
00512             PRUint8 *d = dst, *s = src;
00513             for (PRUint8 aMask = 1<<7, j = 0; aMask && j < ValidWidth-x; aMask >>= 1, j++) {
00514               // if this pixel is opaque then copy into the destination image
00515               if (alphaPixels & aMask) {
00516                 // might be faster with *d++ = *s++ 3 times?
00517                 d[0] = s[0];
00518                 d[1] = s[1];
00519                 d[2] = s[2];
00520                 // dstAlpha bit already set
00521               }
00522               d += 3;
00523               s += 3;
00524             }
00525           }
00526         }
00527         // at end of each line, bump pointers.  Use wordy code because of
00528         // bug 127455 to avoid possibility of unsigned underflow
00529         dst = (dst - 3*8*iterations) + dest->mRowBytes;
00530         src = (src - 3*8*iterations) + rgbStride;
00531         alpha = (alpha - iterations) + alphaStride;
00532         dstAlpha += dest->mAlphaRowBytes;
00533       }
00534     }
00535     break;
00536   case 0:
00537   default:
00538     for (y=0; y<ValidHeight; y++)
00539       memcpy(dest->mImageBits + (y+aDY)*dest->mRowBytes + 3*aDX,
00540              rgbPtr + y*rgbStride,
00541              3*ValidWidth);
00542   }
00543 
00544   nsRect rect(aDX, aDY, ValidWidth, ValidHeight);
00545   dest->ImageUpdated(nsnull, 0, &rect);
00546 
00547   return NS_OK;
00548 }