/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** 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): * * Alternatively, the contents of this file may be used under the terms of * either of 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 "nsImagePh.h" #include "nsRenderingContextPh.h" #include "nsPhGfxLog.h" #include "nsDeviceContextPh.h" #include "nspr.h" #include #include #include #include "photon/PhRender.h" NS_IMPL_ISUPPORTS1(nsImagePh, nsIImage) /* for mImageFlags */ #define IMAGE_SHMEM 0x1 #define ALPHA_SHMEM 0x2 #define ZOOM_SHMEM 0x4 #define IMAGE_SHMEM_THRESHOLD 4096 // ---------------------------------------------------------------- nsImagePh :: nsImagePh() { mImageBits = nsnull; mWidth = 0; mHeight = 0; mDepth = 0; mAlphaBits = nsnull; mAlphaDepth = 0; mRowBytes = 0; mAlphaHeight = 0; mAlphaWidth = 0; mImageFlags = 0; mAlphaRowBytes = 0; mNaturalWidth = 0; mNaturalHeight = 0; memset(&mPhImage, 0, sizeof(PhImage_t)); mPhImageZoom = NULL; mDecodedY2_when_scaled = 0; mDirtyFlags = 0; } // ---------------------------------------------------------------- nsImagePh :: ~nsImagePh() { if (mImageBits != nsnull) { if( mImageFlags & IMAGE_SHMEM ) PgShmemDestroy( mImageBits ); else delete [] mImageBits; mImageBits = nsnull; } if (mAlphaBits != nsnull) { if( mImageFlags & ALPHA_SHMEM ) PgShmemDestroy( mAlphaBits ); else delete [] mAlphaBits; mAlphaBits = nsnull; } if( mPhImageZoom ) { if( mImageFlags & ZOOM_SHMEM ) PgShmemDestroy( mPhImageZoom->image ); else free( mPhImageZoom->image ); if( mPhImageZoom->mask_bm ) free( mPhImageZoom->mask_bm ); free( mPhImageZoom ); mPhImageZoom = NULL; } memset(&mPhImage, 0, sizeof(PhImage_t)); } /** ---------------------------------------------------------------- * Initialize the nsImagePh object * @param aWidth - Width of the image * @param aHeight - Height of the image * @param aDepth - Depth of the image * @param aMaskRequirements - A mask used to specify if alpha is needed. * @result NS_OK if the image was initied ok */ nsresult nsImagePh :: Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth,nsMaskRequirements aMaskRequirements) { int type = -1; mImageFlags = 0; if (mImageBits != nsnull) { if( mImageFlags & IMAGE_SHMEM ) PgShmemDestroy( mImageBits ); else delete [] mImageBits; mImageBits = nsnull; } if (mAlphaBits != nsnull) { if( mImageFlags & ALPHA_SHMEM ) PgShmemDestroy( mAlphaBits ); else delete [] mAlphaBits; mAlphaBits = nsnull; } if( mPhImageZoom ) { if( mImageFlags & ZOOM_SHMEM ) PgShmemDestroy( mPhImageZoom->image ); else free( mPhImageZoom->image ); if( mPhImageZoom->mask_bm ) free( mPhImageZoom->mask_bm ); free( mPhImageZoom ); mPhImageZoom = NULL; } SetDecodedRect(0,0,0,0); //init switch (aDepth) { case 24: type = Pg_IMAGE_DIRECT_888; mNumBytesPixel = 3; break; // case 16: // type = Pg_IMAGE_DIRECT_555; // mNumBytesPixel = 2; // break; case 8: // type = Pg_IMAGE_PALETTE_BYTE; // mNumBytesPixel = 1; // break; default: NS_ASSERTION(PR_FALSE, "unexpected image depth"); return NS_ERROR_UNEXPECTED; break; } mWidth = aWidth; mHeight = aHeight; mDepth = aDepth; /* Allocate the Image Data */ PRInt32 image_size = mNumBytesPixel * mWidth * mHeight; /* TODO: don't allow shared memory contexts if the graphics driver isn't a local device */ if (image_size >= IMAGE_SHMEM_THRESHOLD) { mImageBits = (PRUint8 *) PgShmemCreate( image_size, NULL ); mImageFlags |= IMAGE_SHMEM; } else { mImageBits = new PRUint8[ image_size ]; memset( mImageBits, 0, image_size ); } switch(aMaskRequirements) { default: case nsMaskRequirements_kNoMask: mAlphaBits = nsnull; mAlphaWidth = 0; mAlphaHeight = 0; mAlphaRowBytes = 0; break; case nsMaskRequirements_kNeeds1Bit: { mAlphaRowBytes = (aWidth + 7) / 8; mAlphaDepth = 1; int alphasize = mAlphaRowBytes * aHeight; mAlphaBits = new PRUint8[ alphasize ]; memset( mAlphaBits, 0, alphasize ); mAlphaWidth = aWidth; mAlphaHeight = aHeight; } break; case nsMaskRequirements_kNeeds8Bit: { mAlphaRowBytes = aWidth; mAlphaDepth = 8; int alphasize = mAlphaRowBytes * aHeight; if( alphasize > IMAGE_SHMEM_THRESHOLD ) { mAlphaBits = ( PRUint8 * ) PgShmemCreate( alphasize, NULL ); mImageFlags |= ALPHA_SHMEM; } else mAlphaBits = new PRUint8[ alphasize ]; memset( mAlphaBits, 0, alphasize ); mAlphaWidth = aWidth; mAlphaHeight = aHeight; } break; } // mPhImage.image_tag = PtCRC( (char *)mImageBits, image_size ); mPhImage.image = (char *)mImageBits; mPhImage.size.w = mWidth; mPhImage.size.h = 0; mRowBytes = mPhImage.bpl = mNumBytesPixel * mWidth; mPhImage.type = type; if (aMaskRequirements == nsMaskRequirements_kNeeds1Bit) { mPhImage.mask_bm = (char *)mAlphaBits; mPhImage.mask_bpl = mAlphaRowBytes; } return NS_OK; } /** --------------------------------------------------- * See documentation in nsIImage.h */ PRBool nsImagePh::GetIsImageComplete() { return mDecodedX1 == 0 && mDecodedY1 == 0 && mDecodedX2 == mWidth && mDecodedY2 == mHeight; } /** ---------------------------------------------------------------- * Draw the bitmap, this method has a source and destination coordinates * @update dc - 11/20/98 * @param aContext - the rendering context to draw with * @param aSurface - The HDC in a nsDrawingsurfacePh to copy the bits to. * @param aSX - source horizontal location * @param aSY - source vertical location * @param aSWidth - source width * @param aSHeight - source height * @param aDX - destination location * @param aDY - destination location * @param aDWidth - destination width * @param aDHeight - destination height * @result NS_OK if the draw worked */ NS_IMETHODIMP nsImagePh :: Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface, PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) { PhRect_t clip = { {aDX, aDY}, {aDX + aDWidth-1, aDY + aDHeight-1} }; PhPoint_t pos = { aDX - aSX, aDY - aSY}; int use_zoom = 0; if( !aSWidth || !aSHeight || !aDWidth || !aDHeight ) return NS_OK; ///* ATENTIE */ printf( "this=%p size=%d,%d src=(%d %d %d %d) dest=(%d %d %d %d)\n", //this, mPhImage.size.w, mPhImage.size.h, aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight ); PhDrawContext_t *dc = ((nsDrawingSurfacePh*)aSurface)->GetDC(); PhGC_t *gc = PgGetGCCx( dc ); if( (aSWidth != aDWidth || aSHeight != aDHeight) ) { /* the case below happens frequently - a 1x1 image needs to be stretched, or a line or a column */ if( aSWidth == 1 && aSHeight == 1 || aSWidth == 1 && aSHeight == aDHeight || aDHeight == 1 && aSWidth == aDWidth ) { /* you can strech the image by drawing it repeateadly */ PhPoint_t space = { 0, 0 }; PhPoint_t rep = { aDWidth, aDHeight }; /* is this a 1x1 transparent image used for spacing? */ if( mWidth == 1 && mHeight == 1 && mAlphaDepth == 1 && mAlphaBits[0] == 0x0 ) return NS_OK; PgSetMultiClipCx( gc, 1, &clip ); if ((mAlphaDepth == 1) || (mAlphaDepth == 0)) { if( mImageFlags & IMAGE_SHMEM ) PgDrawRepPhImageCxv( dc, &mPhImage, 0, &pos, &rep, &space ); else PgDrawRepPhImageCx( dc, &mPhImage, 0, &pos, &rep, &space ); } else { PgMap_t map; map.dim.w = mAlphaWidth; map.dim.h = mAlphaHeight; map.bpl = mAlphaRowBytes; map.bpp = mAlphaDepth; map.map = (unsigned char *)mAlphaBits; PgSetAlphaBlendCx( gc, &map, 0 ); PgAlphaOnCx( gc ); if( mImageFlags & IMAGE_SHMEM ) PgDrawRepPhImageCxv( dc, &mPhImage, 0, &pos, &rep, &space ); else PgDrawRepPhImageCx( dc, &mPhImage, 0, &pos, &rep, &space ); PgAlphaOffCx( gc ); PgSetAlphaBlendCx( gc, NULL, 0 ); /* this shouldn't be necessary, but the ph lib's gc is holding onto our mAlphaBits */ } PgSetMultiClipCx( gc, 0, NULL ); return NS_OK; } else if( mPhImage.size.h > 0 ) { /* keeping the proportions, what is the size of mPhImageZoom that can give use the aDWidth and aDHeight? */ PRInt32 scaled_w = aDWidth * mPhImage.size.w / aSWidth; PRInt32 scaled_h = aDHeight * mPhImage.size.h / aSHeight; use_zoom = 1; if( mPhImageZoom == NULL || mPhImageZoom->size.w != scaled_w || mPhImageZoom->size.h != scaled_h || mDecodedY2_when_scaled != mDecodedY2 || mDirtyFlags != 0 ) { /* we already had a scaled image, but the scaling factor was different from what we need now */ mDirtyFlags = 0; if ( mPhImageZoom ) { if( mImageFlags & ZOOM_SHMEM ) { PgFlushCx( dc ); PgWaitHWIdle(); PgShmemDestroy( mPhImageZoom->image ); } else free( mPhImageZoom->image ); if( mPhImageZoom->mask_bm ) free( mPhImageZoom->mask_bm ); free( mPhImageZoom ); mPhImageZoom = NULL; } /* record the mDecodedY1 at the time of scaling */ mDecodedY2_when_scaled = mDecodedY2; /* this is trying to estimate the image data size of the zoom image */ if (( mPhImage.bpl * scaled_w * scaled_h / mPhImage.size.w ) < IMAGE_SHMEM_THRESHOLD) { mPhImageZoom = PiResizeImage( &mPhImage, NULL, scaled_w, scaled_h, Pi_USE_COLORS); mImageFlags &= ~ZOOM_SHMEM; } else { mPhImageZoom = PiResizeImage( &mPhImage, NULL, scaled_w, scaled_h, Pi_USE_COLORS|Pi_SHMEM); mImageFlags |= ZOOM_SHMEM; } ///* ATENTIE */ printf( "\t\t\tzoom from=%d,%d to=%d,%d\n", mPhImage.size.w, mPhImage.size.h, scaled_w, scaled_h ); } } } PgSetMultiClipCx( gc, 1, &clip ); if ((mAlphaDepth == 1) || (mAlphaDepth == 0)) { if( use_zoom ) { if( mImageFlags & ZOOM_SHMEM ) PgDrawPhImageCxv( dc, &pos, mPhImageZoom, 0 ); else PgDrawPhImageCx( dc, &pos, mPhImageZoom, 0 ); } else { if( mImageFlags & IMAGE_SHMEM ) PgDrawPhImageCxv( dc, &pos, &mPhImage, 0 ); else PgDrawPhImageCx( dc, &pos, &mPhImage, 0 ); } } else { PgMap_t map; map.dim.w = mAlphaWidth; map.dim.h = mAlphaHeight; map.bpl = mAlphaRowBytes; map.bpp = mAlphaDepth; map.map = (unsigned char *)mAlphaBits; PgSetAlphaBlendCx( gc, &map, 0 ); PgAlphaOnCx( gc ); if( use_zoom ) { if( mImageFlags & ZOOM_SHMEM ) PgDrawPhImageCxv( dc, &pos, mPhImageZoom, 0 ); else PgDrawPhImageCx( dc, &pos, mPhImageZoom, 0 ); } else { if( mImageFlags & IMAGE_SHMEM ) PgDrawPhImageCxv( dc, &pos, &mPhImage, 0 ); else PgDrawPhImageCx( dc, &pos, &mPhImage, 0 ); } PgAlphaOffCx( gc ); PgSetAlphaBlendCx( gc, NULL, 0 ); /* this shouldn't be necessary, but the ph lib's gc is holding onto our mAlphaBits */ } PgSetMultiClipCx( gc, 0, NULL ); return NS_OK; } NS_IMETHODIMP nsImagePh :: Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface, PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) { PhPoint_t pos = { aX, aY }; if (!aSurface || !mImageBits) return (NS_ERROR_FAILURE); PhDrawContext_t *dc = ((nsDrawingSurfacePh*)aSurface)->GetDC(); if ((mAlphaDepth == 1) || (mAlphaDepth == 0)) { if( mImageFlags & IMAGE_SHMEM ) PgDrawPhImageCxv( dc, &pos, &mPhImage, 0 ); else PgDrawPhImageCx( dc, &pos, &mPhImage, 0 ); } else if (mAlphaDepth == 8) { PgMap_t map; PhGC_t *gc = PgGetGCCx( dc ); map.dim.w = mAlphaWidth; map.dim.h = mAlphaHeight; map.bpl = mAlphaRowBytes; map.bpp = mAlphaDepth; map.map = (unsigned char *)mAlphaBits; PgSetAlphaBlendCx( gc, &map, 0 ); PgAlphaOnCx( gc ); if( mImageFlags & IMAGE_SHMEM ) PgDrawPhImageCxv( dc, &pos, &mPhImage, 0 ); else PgDrawPhImageCx( dc, &pos, &mPhImage, 0 ); PgAlphaOffCx( gc ); PgSetAlphaBlendCx( gc, NULL, 0 ); /* this shouldn't be necessary, but the ph lib's gc is holding onto our mAlphaBits */ } return NS_OK; } /* New Tile code *********************************************************************/ NS_IMETHODIMP nsImagePh::DrawTile( nsIRenderingContext &aContext, nsIDrawingSurface* aSurface, PRInt32 aSXOffset, PRInt32 aSYOffset, PRInt32 aPadX, PRInt32 aPadY, const nsRect &aTileRect ) { PhPoint_t pos, space, rep; PhDrawContext_t *dc ; PhGC_t *gc; /* is this a 1x1 transparent image used for spacing? */ if( mWidth == 1 && mHeight == 1 && mAlphaDepth == 1 && mAlphaBits[0] == 0x0 ) return NS_OK; dc = ((nsDrawingSurfacePh*)aSurface)->GetDC(); gc = PgGetGCCx( dc ); // since there is an offset into the image and I only want to draw full // images, shift the position back and set clipping so that it looks right pos.x = aTileRect.x - aSXOffset; pos.y = aTileRect.y - aSYOffset; space.x = mPhImage.size.w + aPadX; space.y = mPhImage.size.h + aPadY; rep.x = ( aTileRect.width + aSXOffset + space.x - 1 ) / space.x; rep.y = ( aTileRect.height + aSYOffset + space.y - 1 ) / space.y; /* not sure if cliping is necessary */ PhRect_t clip = { {aTileRect.x, aTileRect.y}, {aTileRect.x + aTileRect.width-1, aTileRect.y + aTileRect.height-1} }; PgSetMultiClipCx( gc, 1, &clip ); if ((mAlphaDepth == 1) || (mAlphaDepth == 0)) { if( mImageFlags & IMAGE_SHMEM ) PgDrawRepPhImageCxv( dc, &mPhImage, 0, &pos, &rep, &space ); else PgDrawRepPhImageCx( dc, &mPhImage, 0, &pos, &rep, &space ); } else { PgMap_t map; map.dim.w = mAlphaWidth; map.dim.h = mAlphaHeight; map.bpl = mAlphaRowBytes; map.bpp = mAlphaDepth; map.map = (unsigned char *)mAlphaBits; PgSetAlphaBlendCx( gc, &map, 0 ); PgAlphaOnCx( gc ); if( mImageFlags & IMAGE_SHMEM ) PgDrawRepPhImageCxv( dc, &mPhImage, 0, &pos, &rep, &space ); else PgDrawRepPhImageCx( dc, &mPhImage, 0, &pos, &rep, &space ); PgAlphaOffCx( gc ); PgSetAlphaBlendCx( gc, NULL, 0 ); /* this shouldn't be necessary, but the ph lib's gc is holding onto our mAlphaBits */ } PgSetMultiClipCx( gc, 0, NULL ); return NS_OK; } /* End New Tile code *****************************************************************/ //------------------------------------------------------------ nsresult nsImagePh :: Optimize(nsIDeviceContext* aContext) { /* TODO: need to invalidate the caches if the graphics system has changed somehow (ie: mode switch, dragged to a remote display, etc... */ /* TODO: remote graphics drivers should always cache the images offscreen regardless of whether there is a mask or not, the Draw code will need to be updated for this */ return NS_OK; } /** * BitBlit the entire (no cropping) nsIImage to another nsImage, the source and dest can be scaled * @update - saari 03/08/01 * @param aDstImage the nsImage to blit to * @param aDX The destination horizontal location * @param aDY The destination vertical location * @param aDWidth The destination width of the pixelmap * @param aDHeight The destination height of the pixelmap * @return if TRUE, no errors */ NS_IMETHODIMP nsImagePh::DrawToImage(nsIImage* aDstImage, nscoord aDX, nscoord aDY, nscoord aDWidth, nscoord aDHeight) { nsImagePh *dest = static_cast(aDstImage); if (!dest) return NS_ERROR_FAILURE; if (!dest->mPhImage.image) return NS_ERROR_FAILURE; PhArea_t sarea, darea; PhImage_t *pimage = NULL; int start, x, y; char mbit, mbyte; int release = 0; sarea.pos.x = sarea.pos.y = 0; darea.pos.x = aDX; darea.pos.y = aDY; darea.size.w = sarea.size.w = aDWidth; darea.size.h = sarea.size.h = aDHeight; if( mPhImage.size.h > 0 && ( aDWidth != mPhImage.size.w || aDHeight != mPhImage.size.h ) ) { release = 1; if ((aDHeight * mPhImage.bpl) < IMAGE_SHMEM_THRESHOLD) pimage = PiResizeImage(&mPhImage, NULL, aDWidth, aDHeight, Pi_USE_COLORS); else pimage = PiResizeImage(&mPhImage, NULL, aDWidth, aDHeight, Pi_USE_COLORS|Pi_SHMEM); } else pimage = &mPhImage; if( pimage == NULL ) return NS_OK; start = (aDY * dest->mPhImage.bpl) + (aDX * mNumBytesPixel); for (y = 0; y < pimage->size.h; y++) { for (x = 0; x < pimage->size.w; x++) { if (pimage->mask_bm) { mbyte = *(pimage->mask_bm + (pimage->mask_bpl * y) + (x >> 3)); mbit = x & 7; if (!(mbyte & (0x80 >> mbit))) continue; } memcpy(dest->mPhImage.image + start + (dest->mPhImage.bpl * y) + (x*mNumBytesPixel), pimage->image + (pimage->bpl * y) + (x*mNumBytesPixel), mNumBytesPixel); } } if( release ) { pimage->flags = Ph_RELEASE_IMAGE_ALL; PhReleaseImage(pimage); free( pimage ); } return NS_OK; }