/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Lars Knoll * Zack Rusin * John C. Griggs * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsImageQt.h" #include "nsRenderingContextQt.h" #include #include "nspr.h" #include "qtlog.h" #define IsFlagSet(a,b) ((a) & (b)) #ifdef CHECK_PIXMAPS #include static void savePixmap(const QPixmap &px) { static int i = 0; QString baseFile( "saved_pixmap" ); const char *format = "PNG"; QString fileName = QString("%1%2.%2").arg(baseFile) .arg(i).arg(format); while (QFileInfo(fileName).exists()) { ++i; fileName = QString("%1%2.%2").arg(baseFile) .arg(i).arg(format); } px.save(fileName, format); } #endif NS_IMPL_ISUPPORTS1(nsImageQt, nsIImage) //------------------------------------------------------------ nsImageQt::nsImageQt() : mWidth(0) , mHeight(0) , mDepth(0) , mRowBytes(0) , mImageBits(nsnull) , mAlphaDepth(0) , mAlphaRowBytes(0) , mAlphaBits(nsnull) , mNumBytesPixel(0) , pixmapDirty(PR_FALSE) { } //------------------------------------------------------------ nsImageQt::~nsImageQt() { if (nsnull != mImageBits) { delete[] mImageBits; mImageBits = nsnull; } if (nsnull != mAlphaBits) { delete[] mAlphaBits; mAlphaBits = nsnull; } } //------------------------------------------------------------ nsresult nsImageQt::Init(PRInt32 aWidth,PRInt32 aHeight, PRInt32 aDepth, nsMaskRequirements aMaskRequirements) { qDebug("creating a pixmap with [%d,%d,%d]", aWidth, aHeight, aDepth); // gfxImageFrame forces only one nsImageQt::Init if (aWidth == 0 || aHeight == 0) { return NS_ERROR_FAILURE; } // if (32 == aDepth) { // mNumBytesPixel = 4; // mDepth = aDepth; // } // else if (24 == aDepth) { mNumBytesPixel = 3; mDepth = aDepth; } else { NS_NOTREACHED("unexpected image depth"); return NS_ERROR_UNEXPECTED; } mWidth = aWidth; mHeight = aHeight; mRowBytes = (mWidth*mDepth/8 + 3) & ~0x3; switch (aMaskRequirements) { case nsMaskRequirements_kNeeds1Bit: mAlphaRowBytes = (aWidth + 7) / 8; mAlphaDepth = 1; // 32-bit align each row mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3; break; case nsMaskRequirements_kNeeds8Bit: mAlphaRowBytes = aWidth; mAlphaDepth = 8; // 32-bit align each row mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3; break; default: mAlphaDepth = 0; mAlphaRowBytes = 0; break; } mImageBits = (PRUint8*)new PRUint8[mRowBytes * mHeight]; mAlphaBits = new PRUint8[mAlphaRowBytes * mHeight]; pixmapDirty = PR_TRUE; PR_LOG(gQtLogModule, QT_BASIC, ("nsImageQt::Init succeeded")); return NS_OK; } //------------------------------------------------------------ PRInt32 nsImageQt::GetHeight() { return mHeight; } PRInt32 nsImageQt::GetWidth() { return mWidth; } PRUint8 *nsImageQt::GetBits() { return mImageBits; } void *nsImageQt::GetBitInfo() { return nsnull; } PRInt32 nsImageQt::GetLineStride() { return mRowBytes; } nsColorMap *nsImageQt::GetColorMap() { return nsnull; } PRUint8 *nsImageQt::GetAlphaBits() { return mAlphaBits; } PRInt32 nsImageQt::GetAlphaLineStride() { return mAlphaRowBytes; } //------------------------------------------------------------ // set up the palette to the passed in color array, RGB only in this array void nsImageQt::ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags,nsRect *aUpdateRect) { //qDebug("image updated"); if (IsFlagSet(nsImageUpdateFlags_kBitsChanged,aFlags)) pixmapDirty = PR_TRUE; mDecodedRect.UnionRect(mDecodedRect, *aUpdateRect); } /** --------------------------------------------------- * See documentation in nsIImage.h */ PRBool nsImageQt::GetIsImageComplete() { return mDecodedRect.x == 0 && mDecodedRect.y == 0 && mDecodedRect.width == mWidth && mDecodedRect.height == mHeight; } // Draw the bitmap, this method has a source and destination coordinates NS_IMETHODIMP nsImageQt::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface, PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) { // qDebug("draw x = %d, y = %d, w = %d, h = %d, dx = %d, dy = %d, dw = %d, dy = %d", // aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight); if (aSWidth <= 0 || aDWidth <= 0 || aSHeight <= 0 || aDHeight <= 0) return NS_OK; if (nsnull == aSurface) { return NS_ERROR_FAILURE; } if (pixmapDirty) updatePixmap(); nsDrawingSurfaceQt *drawing = (nsDrawingSurfaceQt*)aSurface; // The code below seems to be wrong and not what gecko expects. // if ((aSWidth != aDWidth || aSHeight != aDHeight) // && (aSWidth != mWidth || aSHeight != mHeight)) { // QPixmap tmp(aSWidth, aSHeight); // copyBlt(&tmp, 0, 0, &pixmap, aSX, aSY, aSWidth, aSHeight); // drawing->GetGC()->drawPixmap(aDX, aDY, pixmap, aSX, aSY, aSWidth, aSHeight); // } nsRect sourceRect(aSX, aSY, aSWidth, aSHeight); if (!sourceRect.IntersectRect(sourceRect, mDecodedRect)) return NS_OK; // Now get the part of the image that should be drawn // Copy into a new image so we can scale afterwards QImage image(pixmap.convertToImage().copy(sourceRect.x, sourceRect.y, sourceRect.width, sourceRect.height)); if (image.isNull()) return NS_ERROR_FAILURE; // Find the scale factor float w_factor = (float)aDWidth / aSWidth; float h_factor = (float)aDHeight / aSHeight; // If we had to draw only part of the requested image, must adjust // destination coordinates aDX += PRInt32((sourceRect.x - aSX) * w_factor); aDY += PRInt32((sourceRect.y - aSY) * h_factor); image = image.scale(int(sourceRect.width * w_factor), int(sourceRect.height * h_factor)); drawing->GetGC()->drawImage(QPoint(aDX, aDY), image); //drawing->GetGC()->drawPixmap(aDX, aDY, pixmap, aSX, aSY, aSWidth, aSHeight); return NS_OK; } //------------------------------------------------------------ // Draw the bitmap, this draw just has destination coordinates NS_IMETHODIMP nsImageQt::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface, PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) { return Draw(aContext, aSurface, 0, 0, mWidth, mHeight, aX, aY, aWidth, aHeight); } void nsImageQt::updatePixmap() { //qDebug("updatePixmap"); QImage qimage(mWidth, mHeight, 32); const PRInt32 bytesPerPixel = mDepth / 8; PRUint8 *image = mImageBits; PRUint8 *alpha = mAlphaBits; PRInt32 i,j; QRgb *line; qimage.setAlphaBuffer(mAlphaDepth != 0); switch(mAlphaDepth) { case 0: for (i = 0; i < mHeight; i++) { line = (QRgb*)qimage.scanLine(i); PRUint8 *imagePtr = image; for (j = 0; j < mWidth; j++) { line[j] = qRgb(*imagePtr, *(imagePtr+1), *(imagePtr+2)); imagePtr += bytesPerPixel; } image += mRowBytes; } break; case 1: for (i = 0; i < mHeight; i++) { line = (QRgb*)qimage.scanLine(i); PRUint8 *imagePtr = image; for (j = 0; j < mWidth; j++) { uchar a = (alpha[j / 8] & (1 << (7 - (j % 8)))) ? 0xff : 0; line[j] = qRgba(*imagePtr, *(imagePtr+1), *(imagePtr+2), a); imagePtr += bytesPerPixel; } image += mRowBytes; alpha += mAlphaRowBytes; } break; case 8: for (i = 0; i < mHeight; i++) { line = (QRgb*)qimage.scanLine(i); PRUint8 *imagePtr = image; PRUint8 *alphaPtr = alpha; for (j = 0; j < mWidth; j++) { line[j] = qRgba(*imagePtr, *(imagePtr+1), *(imagePtr+2), *alphaPtr); imagePtr += bytesPerPixel; alphaPtr++; } image += mRowBytes; alpha += mAlphaRowBytes; } break; } pixmap = QPixmap(qimage); #ifdef CHECK_PIXMAPS savePixmap(pixmap); #endif pixmapDirty = PR_FALSE; } NS_IMETHODIMP nsImageQt::DrawTile(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface, PRInt32 aSXOffset, PRInt32 aSYOffset, PRInt32 aPadX, PRInt32 aPadY, const nsRect &aTileRect) { //qDebug("draw tile"); #if 1 nsRect aSrcRect(aSXOffset, aSYOffset, mWidth, mHeight); nsDrawingSurfaceQt *drawing = (nsDrawingSurfaceQt*)aSurface; if (aTileRect.width <= 0 || aTileRect.height <= 0) { NS_ASSERTION(aTileRect.width > 0 && aTileRect.height > 0, "Error: image has 0 width or height!"); return NS_OK; } if (drawing->GetDepth() == 8 || mAlphaDepth == 8) { PRInt32 aY0 = aTileRect.y, aX0 = aTileRect.x; PRInt32 aY1 = aTileRect.y + aTileRect.height; PRInt32 aX1 = aTileRect.x + aTileRect.width; for (PRInt32 y = aY0; y < aY1; y += aSrcRect.height + aPadY) for (PRInt32 x = aX0; x < aX1; x += aSrcRect.width + aPadX) Draw(aContext, aSurface, x, y, PR_MIN(aSrcRect.width, aX1 - x), PR_MIN(aSrcRect.height, aY1 - y)); return NS_OK; } #if 0 // Render unique image bits onto an off screen pixmap only once // The image bits can change as a result of ImageUpdated() - for // example: animated GIFs. if (nsnull == mImagePixmap) { CreateImagePixmap(); } if (nsnull == mImagePixmap) return NS_ERROR_FAILURE; QPixmap qPmap; qPmap.convertFromImage(*mImagePixmap); #endif /*qDebug("draw tilePixmap x = %d, y = %d, wid = %d, hei = %d, sx = %d, sy = %d", aTileRect.x, aTileRect.y, aTileRect.width, aTileRect.height, aSrcRect.x, aSrcRect.y);*/ drawing->GetGC()->drawTiledPixmap(aTileRect.x, aTileRect.y, aTileRect.width, aTileRect.height, pixmap, aSrcRect.x, aSrcRect.y); //qPmap, aSrcRect.x, aSrcRect.y); #endif return NS_OK; } //------------------------------------------------------------ nsresult nsImageQt::Optimize(nsIDeviceContext* aContext) { return NS_OK; } PRInt32 nsImageQt::GetBytesPix() { return mNumBytesPixel; } //------------------------------------------------------------ // lock the image pixels. Implement this if you need it. NS_IMETHODIMP nsImageQt::LockImagePixels(PRBool aMaskPixels) { return NS_OK; } //------------------------------------------------------------ // unlock the image pixels. Implement this if you need it. NS_IMETHODIMP nsImageQt::UnlockImagePixels(PRBool aMaskPixels) { return NS_OK; } NS_IMETHODIMP nsImageQt::DrawToImage(nsIImage* aDstImage, nscoord aDX, nscoord aDY, nscoord aDWidth, nscoord aDHeight) { //qDebug("DrawToIMAGE"); nsImageQt *dest = NS_STATIC_CAST(nsImageQt *, aDstImage); if (!dest) return NS_ERROR_FAILURE; if (aDX >= dest->mWidth || aDY >= dest->mHeight) return NS_OK; PRUint8 *rgbPtr=0, *alphaPtr=0; PRUint32 rgbStride, alphaStride; rgbPtr = mImageBits; rgbStride = mRowBytes; alphaPtr = mAlphaBits; alphaStride = mAlphaRowBytes; PRInt32 y; PRInt32 ValidWidth = ( aDWidth < ( dest->mWidth - aDX ) ) ? aDWidth : ( dest->mWidth - aDX ); PRInt32 ValidHeight = ( aDHeight < ( dest->mHeight - aDY ) ) ? aDHeight : ( dest->mHeight - aDY ); // now composite the two images together switch (mAlphaDepth) { case 1: { PRUint8 *dst = dest->mImageBits + aDY*dest->mRowBytes + 3*aDX; PRUint8 *dstAlpha = dest->mAlphaBits + aDY*dest->mAlphaRowBytes; PRUint8 *src = rgbPtr; PRUint8 *alpha = alphaPtr; PRUint8 offset = aDX & 0x7; // x starts at 0 int iterations = (ValidWidth+7)/8; // round up for (y=0; y= ValidWidth) { alphaPixels &= 0xff << (8 - (ValidWidth-x)); // no, mask off unused if (alphaPixels == 0) continue; // no 1 alpha pixels left } if (offset == 0) { dstAlpha[(aDX+x)>>3] |= alphaPixels; // the cheap aligned case } else { dstAlpha[(aDX+x)>>3] |= alphaPixels >> offset; // avoid write if no 1's to write - also avoids going past end of array PRUint8 alphaTemp = alphaPixels << (8U - offset); if (alphaTemp & 0xff) dstAlpha[((aDX+x)>>3) + 1] |= alphaTemp; } if (alphaPixels == 0xff) { // fix - could speed up by gathering a run of 0xff's and doing 1 memcpy // all 8 pixels set; copy and jump forward memcpy(dst,src,8*3); continue; } else { // else mix of 1's and 0's in alphaPixels, do 1 bit at a time // Don't go past end of line! PRUint8 *d = dst, *s = src; for (PRUint8 aMask = 1<<7, j = 0; aMask && j < ValidWidth-x; aMask >>= 1, j++) { // if this pixel is opaque then copy into the destination image if (alphaPixels & aMask) { // might be faster with *d++ = *s++ 3 times? d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; // dstAlpha bit already set } d += 3; s += 3; } } } // at end of each line, bump pointers. Use wordy code because of // bug 127455 to avoid possibility of unsigned underflow dst = (dst - 3*8*iterations) + dest->mRowBytes; src = (src - 3*8*iterations) + rgbStride; alpha = (alpha - iterations) + alphaStride; dstAlpha += dest->mAlphaRowBytes; } } break; case 0: default: for (y=0; ymImageBits + (y+aDY)*dest->mRowBytes + 3*aDX, rgbPtr + y*rgbStride, 3*ValidWidth); } nsRect rect(aDX, aDY, ValidWidth, ValidHeight); dest->ImageUpdated(nsnull, 0, &rect); return NS_OK; }