Files
Mozilla/mozilla/gfx/src/windows/nsImageWin.cpp
dougt%meer.net 560070f089 Bug 324971. Moving Minimo + WinCE changes from the trunk to the branch so that Minimo can build.
Changes include:

1. allows for building without MOZ_PSM defined
2. allows for building using vs2005beta2 compiler
3. updates minimo configure.in application settings
4. flips disable-xpcom-obsolete flags around
5. adds a way to build js statically
6. move the build/config logic to build minimo statically
7. change to content/xbl/builtin/Makefile.in so that we export the right
binding file
8. Spatial Navigation improvements
9. widget and gfx WindowsCE-only improvement
10. WinCE UConv simplification
11. Disabling the jpeg encoder on WinCE.  On the trunk there is a configure
option that allow you to do this.
12. xptcall change which is also on the trunk.


a=benjamin@smedbergs.us


git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@188432 18797224-902f-48f8-a5cc-f745e15eee43
2006-01-29 16:51:02 +00:00

2079 lines
68 KiB
C++

/* -*- 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):
* emk <VYV03354@nifty.ne.jp>
*
* 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 "nsImageWin.h"
#include "nsRenderingContextWin.h"
#include "nsDeviceContextWin.h"
#include "imgScaler.h"
#include "nsComponentManagerUtils.h"
static PRInt32 GetPlatform()
{
OSVERSIONINFO versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
::GetVersionEx(&versionInfo);
return versionInfo.dwPlatformId;
}
static PRInt32 GetOsMajorVersion()
{
OSVERSIONINFO versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
::GetVersionEx(&versionInfo);
return versionInfo.dwMajorVersion;
}
PRInt32 nsImageWin::gPlatform = GetPlatform();
PRInt32 nsImageWin::gOsMajorVersion = GetOsMajorVersion();
/** ----------------------------------------------------------------
* Constructor for nsImageWin
* @update dc - 11/20/98
*/
nsImageWin::nsImageWin()
: mImageBits(nsnull)
, mHBitmap(nsnull)
, mAlphaBits(nsnull)
, mColorMap(nsnull)
, mBHead(nsnull)
, mDIBTemp(PR_FALSE)
, mNumBytesPixel(0)
, mNumPaletteColors(0)
, mSizeImage(0)
, mRowBytes(0)
, mIsOptimized(PR_FALSE)
, mDecodedX1(PR_INT32_MAX)
, mDecodedY1(PR_INT32_MAX)
, mDecodedX2(0)
, mDecodedY2(0)
, mIsLocked(PR_FALSE)
, mAlphaDepth(0)
, mARowBytes(0)
, mImageCache(0)
, mInitialized(PR_FALSE)
, mWantsOptimization(PR_FALSE)
, mTimer(nsnull)
, mImagePreMultiplied(PR_FALSE)
{
}
/** ----------------------------------------------------------------
* destructor for nsImageWin
* @update dc - 11/20/98
*/
nsImageWin :: ~nsImageWin()
{
if (mTimer)
mTimer->Cancel();
CleanUpDDB();
CleanUpDIB();
if (mBHead) {
delete[] mBHead;
mBHead = nsnull;
}
if (mAlphaBits) {
delete [] mAlphaBits;
mAlphaBits = nsnull;
}
}
NS_IMPL_ISUPPORTS1(nsImageWin, nsIImage)
/** ---------------------------------------------------
* See documentation in nsIImageWin.h
* @update 3/27/00 dwc
*/
nsresult nsImageWin::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth,
nsMaskRequirements aMaskRequirements)
{
if (mInitialized)
return NS_ERROR_FAILURE;
if (8 == aDepth) {
mNumPaletteColors = 256;
mNumBytesPixel = 1;
} else if (24 == aDepth) {
mNumBytesPixel = 3;
} else {
NS_ASSERTION(PR_FALSE, "unexpected image depth");
return NS_ERROR_UNEXPECTED;
}
// limit images to 64k pixels on a side (~55 feet on a 100dpi monitor)
const PRInt32 k64KLimit = 0x0000FFFF;
if (aWidth > k64KLimit || aHeight > k64KLimit)
return NS_ERROR_FAILURE;
if (0 == mNumPaletteColors) {
// space for the header only (no color table)
mBHead = (LPBITMAPINFOHEADER)new char[sizeof(BITMAPINFO)];
} else {
// Space for the header and the palette. Since we'll be using DIB_PAL_COLORS
// the color table is an array of 16-bit unsigned integers that specify an
// index into the currently realized logical palette
mBHead = (LPBITMAPINFOHEADER)new char[sizeof(BITMAPINFOHEADER) +
(256 * sizeof(WORD))];
}
if (!mBHead)
return NS_ERROR_OUT_OF_MEMORY;
mBHead->biSize = sizeof(BITMAPINFOHEADER);
mBHead->biWidth = aWidth;
mBHead->biHeight = aHeight;
mBHead->biPlanes = 1;
mBHead->biBitCount = (WORD)aDepth;
mBHead->biCompression = BI_RGB;
mBHead->biSizeImage = 0; // not compressed, so we dont need this to be set
mBHead->biXPelsPerMeter = 0;
mBHead->biYPelsPerMeter = 0;
mBHead->biClrUsed = mNumPaletteColors;
mBHead->biClrImportant = mNumPaletteColors;
// Compute the size of the image
mRowBytes = CalcBytesSpan(mBHead->biWidth);
mSizeImage = mRowBytes * mBHead->biHeight; // no compression
// Allocate the image bits
mImageBits = new unsigned char[mSizeImage];
if (!mImageBits) {
delete[] mBHead;
mBHead = nsnull;
return NS_ERROR_OUT_OF_MEMORY;
}
// Need to clear the entire buffer so an incrementally loaded image
// will not have garbage rendered for the unloaded bits.
/* XXX: Since there is a performance hit for doing the clear we need
a different solution. For now, we will let garbage be drawn for
incrementally loaded images. Need a solution where only the portion
of the image that has been loaded is asked to draw.
if (mImageBits != nsnull) {
memset(mImageBits, 128, mSizeImage);
}
*/
if (256 == mNumPaletteColors) {
// Initialize the array of indexes into the logical palette
WORD* palIndx = (WORD*)(((LPBYTE)mBHead) + mBHead->biSize);
for (WORD index = 0; index < 256; index++) {
*palIndx++ = index;
}
}
// Allocate mask image bits if requested
if (aMaskRequirements != nsMaskRequirements_kNoMask) {
if (nsMaskRequirements_kNeeds1Bit == aMaskRequirements) {
mARowBytes = (aWidth + 7) / 8;
mAlphaDepth = 1;
}else{
//NS_ASSERTION(nsMaskRequirements_kNeeds8Bit == aMaskRequirements,
// "unexpected mask depth");
mARowBytes = aWidth;
mAlphaDepth = 8;
}
// 32-bit align each row
mARowBytes = (mARowBytes + 3) & ~0x3;
mAlphaBits = new unsigned char[mARowBytes * aHeight];
if (!mAlphaBits) {
delete[] mBHead;
mBHead = nsnull;
delete[] mImageBits;
mImageBits = nsnull;
return NS_ERROR_OUT_OF_MEMORY;
}
}
// XXX Let's only do this if we actually have a palette...
mColorMap = new nsColorMap;
if (mColorMap != nsnull) {
mColorMap->NumColors = mNumPaletteColors;
mColorMap->Index = nsnull;
if (mColorMap->NumColors > 0) {
mColorMap->Index = new PRUint8[3 * mColorMap->NumColors];
// XXX Note: I added this because purify claims that we make a
// copy of the memory (which we do!). I'm not sure if this
// matters or not, but this shutup purify.
memset(mColorMap->Index, 0, sizeof(PRUint8) * (3 * mColorMap->NumColors));
}
}
mInitialized = PR_TRUE;
return NS_OK;
}
/** ---------------------------------------------------
* See documentation in nsIImageWin.h
* @update 3/27/00 dwc
*/
void
nsImageWin :: ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect)
{
mDecodedX1 = PR_MIN(mDecodedX1, aUpdateRect->x);
mDecodedY1 = PR_MIN(mDecodedY1, aUpdateRect->y);
if (aUpdateRect->YMost() > mDecodedY2)
mDecodedY2 = aUpdateRect->YMost();
if (aUpdateRect->XMost() > mDecodedX2)
mDecodedX2 = aUpdateRect->XMost();
}
/** ---------------------------------------------------
* See documentation in nsIImage.h
*/
PRBool nsImageWin::GetIsImageComplete() {
return mInitialized &&
mDecodedX1 == 0 &&
mDecodedY1 == 0 &&
mDecodedX2 == mBHead->biWidth &&
mDecodedY2 == mBHead->biHeight;
}
//------------------------------------------------------------
struct MONOBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[2];
MONOBITMAPINFO(LONG aWidth, LONG aHeight)
{
memset(&bmiHeader, 0, sizeof(bmiHeader));
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = aWidth;
bmiHeader.biHeight = aHeight;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 1;
// Note that the palette is being set up so the DIB and the DDB have white and
// black reversed. This is because we need the mask to have 0 for the opaque
// pixels of the image, and 1 for the transparent pixels. This way the SRCAND
// operation sets the opaque pixels to 0, and leaves the transparent pixels
// undisturbed
bmiColors[0].rgbBlue = 255;
bmiColors[0].rgbGreen = 255;
bmiColors[0].rgbRed = 255;
bmiColors[0].rgbReserved = 0;
bmiColors[1].rgbBlue = 0;
bmiColors[1].rgbGreen = 0;
bmiColors[1].rgbRed = 0;
bmiColors[1].rgbReserved = 0;
}
};
struct ALPHA8BITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
ALPHA8BITMAPINFO(LONG aWidth, LONG aHeight)
{
memset(&bmiHeader, 0, sizeof(bmiHeader));
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = aWidth;
bmiHeader.biHeight = aHeight;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 8;
/* fill in gray scale palette */
int i;
for(i=0; i < 256; i++){
bmiColors[i].rgbBlue = 255-i;
bmiColors[i].rgbGreen = 255-i;
bmiColors[i].rgbRed = 255-i;
bmiColors[1].rgbReserved = 0;
}
}
};
struct ALPHA24BITMAPINFO {
BITMAPINFOHEADER bmiHeader;
ALPHA24BITMAPINFO(LONG aWidth, LONG aHeight)
{
memset(&bmiHeader, 0, sizeof(bmiHeader));
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = aWidth;
bmiHeader.biHeight = aHeight;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24;
}
};
struct ALPHA32BITMAPINFO {
BITMAPINFOHEADER bmiHeader;
ALPHA32BITMAPINFO(LONG aWidth, LONG aHeight)
{
memset(&bmiHeader, 0, sizeof(bmiHeader));
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = aWidth;
bmiHeader.biHeight = aHeight;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 32;
}
};
static void CompositeBitsInMemory(HDC aTheHDC,
int aDX, int aDY,
int aDWidth, int aDHeight,
int aSX, int aSY,
int aSWidth, int aSHeight,
PRInt32 aSrcy,
PRUint8 *aAlphaBits,
MONOBITMAPINFO *aBMI,
PRUint8* aImageBits,
LPBITMAPINFOHEADER aBHead,
PRInt16 aNumPaletteColors);
// Raster op used with MaskBlt(). Assumes our transparency mask has 0 for the
// opaque pixels and 1 for the transparent pixels. That means we want the
// background raster op (value of 0) to be SRCCOPY, and the foreground raster
// (value of 1) to just use the destination bits
#define MASKBLT_ROP MAKEROP4((DWORD)0x00AA0029, SRCCOPY)
void nsImageWin::CreateImageWithAlphaBits(HDC TheHDC)
{
unsigned char *imageWithAlphaBits;
ALPHA32BITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
mHBitmap = ::CreateDIBSection(TheHDC, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS,
(LPVOID *)&imageWithAlphaBits, NULL, 0);
if (!mHBitmap) {
mIsOptimized = PR_FALSE;
return;
}
if (256 == mNumPaletteColors) {
for (int y = 0; y < mBHead->biHeight; y++) {
unsigned char *imageWithAlphaRow = imageWithAlphaBits + y * mBHead->biWidth * 4;
unsigned char *imageRow = mImageBits + y * mRowBytes;
unsigned char *alphaRow = mAlphaBits + y * mARowBytes;
for (int x = 0; x < mBHead->biWidth;
x++, imageWithAlphaRow += 4, imageRow++, alphaRow++) {
FAST_DIVIDE_BY_255(imageWithAlphaRow[0],
mColorMap->Index[3 * (*imageRow)] * *alphaRow);
FAST_DIVIDE_BY_255(imageWithAlphaRow[1],
mColorMap->Index[3 * (*imageRow) + 1] * *alphaRow);
FAST_DIVIDE_BY_255(imageWithAlphaRow[2],
mColorMap->Index[3 * (*imageRow) + 2] * *alphaRow);
imageWithAlphaRow[3] = *alphaRow;
}
}
} else if (mImagePreMultiplied) {
for (int y = 0; y < mBHead->biHeight; y++) {
unsigned char *imageWithAlphaRow = imageWithAlphaBits + y * mBHead->biWidth * 4;
unsigned char *imageRow = mImageBits + y * mRowBytes;
unsigned char *alphaRow = mAlphaBits + y * mARowBytes;
for (int x = 0; x < mBHead->biWidth;
x++, imageWithAlphaRow += 4, imageRow += 3, alphaRow++) {
memcpy(imageWithAlphaRow, imageRow, 3);
imageWithAlphaRow[3] = *alphaRow;
}
}
} else {
for (int y = 0; y < mBHead->biHeight; y++) {
unsigned char *imageWithAlphaRow = imageWithAlphaBits + y * mBHead->biWidth * 4;
unsigned char *imageRow = mImageBits + y * mRowBytes;
unsigned char *alphaRow = mAlphaBits + y * mARowBytes;
for (int x = 0; x < mBHead->biWidth;
x++, imageWithAlphaRow += 4, imageRow += 3, alphaRow++) {
FAST_DIVIDE_BY_255(imageWithAlphaRow[0], imageRow[0] * *alphaRow);
FAST_DIVIDE_BY_255(imageWithAlphaRow[1], imageRow[1] * *alphaRow);
FAST_DIVIDE_BY_255(imageWithAlphaRow[2], imageRow[2] * *alphaRow);
imageWithAlphaRow[3] = *alphaRow;
}
}
}
mIsOptimized = PR_TRUE;
}
/** ---------------------------------------------------
* See documentation in nsIImageWin.h
* @update 3/27/00 dwc
*/
NS_IMETHODIMP
nsImageWin::Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface,
PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight,
PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight)
{
if (mBHead == nsnull ||
aSWidth < 0 || aDWidth < 0 || aSHeight < 0 || aDHeight < 0)
return NS_ERROR_FAILURE;
if (0 == aSWidth || 0 == aDWidth || 0 == aSHeight || 0 == aDHeight)
return NS_OK;
if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
return NS_OK;
PRInt32 origSHeight = aSHeight, origDHeight = aDHeight;
PRInt32 origSWidth = aSWidth, origDWidth = aDWidth;
// limit the size of the blit to the amount of the image read in
if (aSX + aSWidth > mDecodedX2) {
aDWidth -= ((aSX + aSWidth - mDecodedX2) * origDWidth) / origSWidth;
aSWidth -= (aSX + aSWidth) - mDecodedX2;
}
if (aSX < mDecodedX1) {
aDX += ((mDecodedX1 - aSX) * origDWidth) / origSWidth;
aSX = mDecodedX1;
}
if (aSY + aSHeight > mDecodedY2) {
aDHeight -= ((aSY + aSHeight - mDecodedY2) * origDHeight) / origSHeight;
aSHeight -= (aSY + aSHeight) - mDecodedY2;
}
if (aSY < mDecodedY1) {
aDY += ((mDecodedY1 - aSY) * origDHeight) / origSHeight;
aSY = mDecodedY1;
}
if (aDWidth <= 0 || aDHeight <= 0)
return NS_OK;
// Translate to bottom-up coordinates for the source bitmap
PRInt32 srcy = mBHead->biHeight - (aSY + aSHeight);
HDC TheHDC;
((nsDrawingSurfaceWin *)aSurface)->GetDC(&TheHDC);
if (!TheHDC)
return NS_ERROR_FAILURE;
// find out if the surface is a printer.
PRInt32 canRaster;
((nsDrawingSurfaceWin *)aSurface)->GetTECHNOLOGY(&canRaster);
CreateDDB();
PRBool didComposite = PR_FALSE;
if (!mIsOptimized || !mHBitmap) {
DWORD rop = SRCCOPY;
if (mAlphaBits) {
if (1 == mAlphaDepth) {
MONOBITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
if (canRaster == DT_RASPRINTER) {
CompositeBitsInMemory(TheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, aSY, aSWidth, aSHeight,
srcy, mAlphaBits, &bmi, mImageBits, mBHead,
mNumPaletteColors);
didComposite = PR_TRUE;
} else {
// Put the mask down
::StretchDIBits(TheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, srcy, aSWidth, aSHeight, mAlphaBits,
(LPBITMAPINFO)&bmi, DIB_RGB_COLORS, SRCAND);
rop = SRCPAINT;
}
} else if (8 == mAlphaDepth) {
nsresult rv = DrawComposited(TheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, srcy, aSWidth, aSHeight,
origDWidth, origDHeight);
if (NS_FAILED(rv)) {
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return rv;
}
didComposite = PR_TRUE;
}
} // mAlphaBits
// Put the Image down
if (!didComposite) {
::StretchDIBits(TheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, srcy, aSWidth, aSHeight, mImageBits,
(LPBITMAPINFO)mBHead, 256 == mNumPaletteColors ?
DIB_PAL_COLORS : DIB_RGB_COLORS, rop);
}
} else {
// Optimized. mHBitmap contains the DDB
DWORD rop = SRCCOPY;
if (canRaster == DT_RASPRINTER) {
// To Printer
if (mAlphaBits && mAlphaDepth == 1) {
MONOBITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
if (mImageBits) {
CompositeBitsInMemory(TheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, aSY, aSWidth, aSHeight, srcy,
mAlphaBits, &bmi, mImageBits, mBHead,
mNumPaletteColors);
didComposite = PR_TRUE;
} else {
ConvertDDBtoDIB(); // Create mImageBits
if (mImageBits) {
CompositeBitsInMemory(TheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, aSY, aSWidth, aSHeight, srcy,
mAlphaBits,
&bmi, mImageBits, mBHead,
mNumPaletteColors);
// Clean up the image bits
delete [] mImageBits;
mImageBits = nsnull;
didComposite = PR_TRUE;
} else {
NS_WARNING("Could not composite bits in memory because conversion to DIB failed\n");
}
} // mImageBits
} // mAlphaBits && mAlphaDepth == 1
if (!didComposite &&
(GetDeviceCaps(TheHDC, RASTERCAPS) & (RC_BITBLT | RC_STRETCHBLT)))
PrintDDB(aSurface, aDX, aDY, aDWidth, aDHeight,
aSX, srcy, aSWidth, aSHeight, rop);
} else {
// we are going to the device that created this DDB
// Get srcDC from the drawing surface of aContext's (RenderingContext)
// DeviceContext. Should be the same each time.
nsIDeviceContext *dx;
aContext.GetDeviceContext(dx);
nsIDrawingSurface* ds;
NS_STATIC_CAST(nsDeviceContextWin*, dx)->GetDrawingSurface(aContext, ds);
nsDrawingSurfaceWin *srcDS = (nsDrawingSurfaceWin *)ds;
if (!srcDS) {
NS_RELEASE(dx);
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return NS_ERROR_FAILURE;
}
HDC srcDC;
srcDS->GetDC(&srcDC);
// Draw the Alpha/Mask
if (mAlphaBits && mAlphaDepth == 1) {
MONOBITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
::StretchDIBits(TheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, srcy, aSWidth, aSHeight, mAlphaBits,
(LPBITMAPINFO)&bmi, DIB_RGB_COLORS, SRCAND);
rop = SRCPAINT;
}
// Draw the Image
HBITMAP oldBits = (HBITMAP)::SelectObject(srcDC, mHBitmap);
if (8 == mAlphaDepth) {
BLENDFUNCTION blendFunction;
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.BlendFlags = 0;
blendFunction.SourceConstantAlpha = 255;
blendFunction.AlphaFormat = 1; // AC_SRC_ALPHA
gAlphaBlend(TheHDC, aDX, aDY, aDWidth, aDHeight,
srcDC, aSX, aSY, aSWidth, aSHeight, blendFunction);
} else {
::StretchBlt(TheHDC, aDX, aDY, aDWidth, aDHeight,
srcDC, aSX, aSY, aSWidth, aSHeight, rop);
}
::SelectObject(srcDC, oldBits);
srcDS->ReleaseDC();
NS_RELEASE(dx);
}
}
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return NS_OK;
}
/** ---------------------------------------------------
* This is a helper routine to do the blending for the DrawComposited method
* @update 1/04/02 dwc
*/
void nsImageWin::DrawComposited24(unsigned char *aBits,
PRUint8 *aImageRGB, PRUint32 aStrideRGB,
PRUint8 *aImageAlpha, PRUint32 aStrideAlpha,
int aWidth, int aHeight)
{
PRInt32 targetRowBytes = ((aWidth * 3) + 3) & ~3;
for (int y = 0; y < aHeight; y++) {
unsigned char *targetRow = aBits + y * targetRowBytes;
unsigned char *imageRow = aImageRGB + y * aStrideRGB;
unsigned char *alphaRow = aImageAlpha + y * aStrideAlpha;
for (int x = 0; x < aWidth;
x++, targetRow += 3, imageRow += 3, alphaRow++) {
unsigned alpha = *alphaRow;
MOZ_BLEND(targetRow[0], targetRow[0], imageRow[0], alpha);
MOZ_BLEND(targetRow[1], targetRow[1], imageRow[1], alpha);
MOZ_BLEND(targetRow[2], targetRow[2], imageRow[2], alpha);
}
}
}
/** ---------------------------------------------------
* Blend the image into a 24 bit buffer.. using an 8 bit alpha mask
* @update 1/04/02 dwc
*/
nsresult nsImageWin::DrawComposited(HDC TheHDC, int aDX, int aDY,
int aDWidth, int aDHeight,
int aSX, int aSY, int aSWidth, int aSHeight,
int aOrigDWidth, int aOrigDHeight)
{
HDC memDC = ::CreateCompatibleDC(TheHDC);
if (!memDC)
return NS_ERROR_OUT_OF_MEMORY;
unsigned char *screenBits;
PRBool scaling = PR_FALSE;
/* Both scaled and unscaled images come through this code */
if ((aDWidth != aSWidth) || (aDHeight != aSHeight)) {
scaling = PR_TRUE;
aDWidth = aOrigDWidth;
aDHeight = aOrigDHeight;
aSWidth = mBHead->biWidth;
aSHeight = mBHead->biHeight;
}
ALPHA24BITMAPINFO bmi(aDWidth, aDHeight);
HBITMAP tmpBitmap = ::CreateDIBSection(memDC, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS,
(LPVOID *)&screenBits, NULL, 0);
if (!tmpBitmap) {
::DeleteDC(memDC);
NS_WARNING("nsImageWin::DrawComposited failed to create tmpBitmap\n");
return NS_ERROR_OUT_OF_MEMORY;
}
HBITMAP oldBitmap = (HBITMAP)::SelectObject(memDC, tmpBitmap);
if (!oldBitmap || oldBitmap == (HBITMAP)GDI_ERROR) {
::DeleteObject(tmpBitmap);
::DeleteDC(memDC);
return NS_ERROR_OUT_OF_MEMORY;
}
/* Copy from the HDC */
BOOL retval = ::BitBlt(memDC, 0, 0, aDWidth, aDHeight,
TheHDC, aDX, aDY, SRCCOPY);
if (!retval || !::GdiFlush()) {
/* select the old object again... */
::SelectObject(memDC, oldBitmap);
::DeleteObject(tmpBitmap);
::DeleteDC(memDC);
return NS_ERROR_FAILURE;
}
PRUint8 *imageRGB, *imageAlpha;
PRUint32 strideRGB, strideAlpha;
if (scaling) {
/* Scale our image to match */
imageRGB = (PRUint8 *)nsMemory::Alloc(3*aDWidth*aDHeight);
imageAlpha = (PRUint8 *)nsMemory::Alloc(aDWidth*aDHeight);
if (!imageRGB || !imageAlpha) {
if (imageRGB)
nsMemory::Free(imageRGB);
if (imageAlpha)
nsMemory::Free(imageAlpha);
::SelectObject(memDC, oldBitmap);
::DeleteObject(tmpBitmap);
::DeleteDC(memDC);
return NS_ERROR_FAILURE;
}
strideRGB = 3 * aDWidth;
strideAlpha = aDWidth;
RectStretch(aSWidth, aSHeight, aDWidth, aDHeight,
0, 0, aDWidth-1, aDHeight-1,
mImageBits, mRowBytes, imageRGB, strideRGB, 24);
RectStretch(aSWidth, aSHeight, aDWidth, aDHeight,
0, 0, aDWidth-1, aDHeight-1,
mAlphaBits, mARowBytes, imageAlpha, strideAlpha, 8);
} else {
imageRGB = mImageBits + aSY * mRowBytes + aSX * 3;
imageAlpha = mAlphaBits + aSY * mARowBytes + aSX;
strideRGB = mRowBytes;
strideAlpha = mARowBytes;
}
/* Do composite */
DrawComposited24(screenBits, imageRGB, strideRGB, imageAlpha, strideAlpha,
aDWidth, aDHeight);
if (scaling) {
/* Free scaled images */
nsMemory::Free(imageRGB);
nsMemory::Free(imageAlpha);
}
/* Copy back to the HDC */
/* Use StretchBlt instead of BitBlt here so that we get proper dithering */
if (scaling) {
/* only copy back the valid portion of the image */
retval = ::StretchBlt(TheHDC, aDX, aDY,
aDWidth, (mDecodedY2*aDHeight + aSHeight - 1)/aSHeight,
memDC, 0, 0,
aDWidth, (mDecodedY2*aDHeight + aSHeight - 1)/aSHeight,
SRCCOPY);
} else {
retval = ::StretchBlt(TheHDC, aDX, aDY, aDWidth, aDHeight,
memDC, 0, 0, aDWidth, aDHeight, SRCCOPY);
}
if (!retval) {
::SelectObject(memDC, oldBitmap);
::DeleteObject(tmpBitmap);
::DeleteDC(memDC);
return NS_ERROR_FAILURE;
}
/* we're done, ignore possible further errors */
::SelectObject(memDC, oldBitmap);
::DeleteObject(tmpBitmap);
::DeleteDC(memDC);
return NS_OK;
}
/** ---------------------------------------------------
* See documentation in nsIImageWin.h
* @update 3/27/00 dwc
*/
NS_IMETHODIMP nsImageWin :: Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface,
PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
{
return Draw(aContext, aSurface, 0, 0, mBHead->biWidth, mBHead->biHeight, aX, aY, aWidth, aHeight);
}
/** ---------------------------------------------------
* See documentation in nsIRenderingContext.h
*/
NS_IMETHODIMP nsImageWin::DrawTile(nsIRenderingContext &aContext,
nsIDrawingSurface* aSurface,
PRInt32 aSXOffset, PRInt32 aSYOffset,
PRInt32 aPadX, PRInt32 aPadY,
const nsRect &aDestRect)
{
NS_ASSERTION(!aDestRect.IsEmpty(), "DrawTile doesn't work with empty rects");
if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
return NS_OK;
float scale;
unsigned char *targetRow,*imageRow,*alphaRow;
PRInt32 x0, y0, x1, y1, destScaledWidth, destScaledHeight;
PRInt32 validWidth,validHeight,validX,validY,targetRowBytes;
PRInt32 x,y,width,height,canRaster;
nsCOMPtr<nsIDeviceContext> theDeviceContext;
HDC theHDC;
nscoord ScaledTileWidth,ScaledTileHeight;
PRBool padded = (aPadX || aPadY);
((nsDrawingSurfaceWin *)aSurface)->GetTECHNOLOGY(&canRaster);
aContext.GetDeviceContext(*getter_AddRefs(theDeviceContext));
// We can Progressive Double Blit if we aren't printing to a printer, and
// we aren't a 256 color image, and we don't have an unoptimized 8 bit alpha.
if ((canRaster != DT_RASPRINTER) && (256 != mNumPaletteColors) &&
!(mAlphaDepth == 8 && !mIsOptimized) && !padded)
if (ProgressiveDoubleBlit(theDeviceContext, aSurface,
aSXOffset, aSYOffset, aDestRect))
return NS_OK;
theDeviceContext->GetCanonicalPixelScale(scale);
destScaledWidth = PR_MAX(PRInt32(mBHead->biWidth*scale), 1);
destScaledHeight = PR_MAX(PRInt32(mBHead->biHeight*scale), 1);
validX = 0;
validY = 0;
validWidth = mBHead->biWidth;
validHeight = mBHead->biHeight;
// limit the image rectangle to the size of the image data which
// has been validated.
if (mDecodedY2 < mBHead->biHeight) {
validHeight = mDecodedY2 - mDecodedY1;
destScaledHeight = PR_MAX(PRInt32(validHeight*scale), 1);
}
if (mDecodedX2 < mBHead->biWidth) {
validWidth = mDecodedX2 - mDecodedX1;
destScaledWidth = PR_MAX(PRInt32(validWidth*scale), 1);
}
if (mDecodedY1 > 0) {
validHeight -= mDecodedY1;
destScaledHeight = PR_MAX(PRInt32(validHeight*scale), 1);
validY = mDecodedY1;
}
if (mDecodedX1 > 0) {
validWidth -= mDecodedX1;
destScaledWidth = PR_MAX(PRInt32(validWidth*scale), 1);
validX = mDecodedX1;
}
// put the DestRect into absolute coordintes of the device
y0 = aDestRect.y - aSYOffset;
x0 = aDestRect.x - aSXOffset;
y1 = aDestRect.y + aDestRect.height;
x1 = aDestRect.x + aDestRect.width;
// this is the width and height of the image in pixels
// we need to map this to the pixel height of the device
ScaledTileWidth = PR_MAX(PRInt32(mBHead->biWidth*scale), 1);
ScaledTileHeight = PR_MAX(PRInt32(mBHead->biHeight*scale), 1);
// do alpha depth equal to 8 here.. this needs some special attention
if (mAlphaDepth == 8 && !mIsOptimized && !padded) {
unsigned char *screenBits=nsnull,*adjAlpha,*adjImage,*adjScreen;
HDC memDC=nsnull;
HBITMAP tmpBitmap=nsnull,oldBitmap;
unsigned char alpha;
PRInt32 targetBytesPerPixel,imageBytesPerPixel;
if (!mImageBits) {
ConvertDDBtoDIB();
}
// draw the alpha and the bitmap to an offscreen buffer.. for the blend.. first
((nsDrawingSurfaceWin *)aSurface)->GetDC(&theHDC);
if (theHDC) {
// create a buffer for the blend
memDC = CreateCompatibleDC(theHDC);
width = aDestRect.width;
height = aDestRect.height;
ALPHA24BITMAPINFO bmi(width, height);
tmpBitmap = ::CreateDIBSection(memDC, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, (LPVOID *)&screenBits, NULL, 0);
oldBitmap = (HBITMAP)::SelectObject(memDC, tmpBitmap);
// number of bytes in a row on a 32 bit boundary
targetRowBytes = (bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) >> 5; // number of 32 bit longs
if (((PRUint32)bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) & 0x1F) { // make sure its a multiple of 32
targetRowBytes++; // or else there will not be enough bytes per line
}
targetRowBytes <<= 2; // divide by 4 to get the number of bytes from the number of 32 bit longs
targetBytesPerPixel = bmi.bmiHeader.biBitCount/8;
}
if (!tmpBitmap) {
if (memDC) {
::DeleteDC(memDC);
}
// this failed..and will fall into the slow blitting code
NS_WARNING("The Creation of the tmpBitmap failed \n");
} else {
// Copy from the HDC to the memory DC
// this will be the image on the screen into a buffer for the blend.
::StretchBlt(memDC, 0, 0, width, height,theHDC, aDestRect.x, aDestRect.y, width, height, SRCCOPY);
::GdiFlush();
imageBytesPerPixel = mBHead->biBitCount/8;
// windows bitmaps start at the bottom.. and go up. This messes up the offsets for the
// image and tiles.. so I reverse the the direction.. to go (the normal way) from top to bottom.
adjScreen = screenBits + ((height-1) * targetRowBytes);
adjImage = mImageBits + ((validHeight-1) * mRowBytes);
adjAlpha = mAlphaBits + ((validHeight-1) * mARowBytes);
for (int y = 0,byw=aSYOffset; y < height; y++,byw++) {
if (byw >= ScaledTileHeight) {
byw = 0;
}
targetRow = adjScreen - (y * targetRowBytes);
imageRow = adjImage - (byw * mRowBytes);
alphaRow = adjAlpha - (byw * mARowBytes);
// we only need this adjustment at the beginning of each row
imageRow += (aSXOffset*imageBytesPerPixel);
alphaRow += aSXOffset;
for (int x=0,bxw=aSXOffset;x<width;x++,targetRow+=targetBytesPerPixel,imageRow+=imageBytesPerPixel,bxw++, alphaRow++) {
// if we went past the row width of our buffer.. go back and start again
if (bxw>=ScaledTileWidth) {
bxw = 0;
imageRow = adjImage - (byw * mRowBytes);
alphaRow = adjAlpha - (byw * mARowBytes);
}
alpha = *alphaRow;
MOZ_BLEND(targetRow[0], targetRow[0], imageRow[0], alpha);
MOZ_BLEND(targetRow[1], targetRow[1], imageRow[1], alpha);
MOZ_BLEND(targetRow[2], targetRow[2], imageRow[2], alpha);
}
}
// copy the blended image back to the screen
::StretchBlt(theHDC, aDestRect.x, aDestRect.y, width, height,memDC, 0, 0, width, height, SRCCOPY);
::SelectObject(memDC, oldBitmap);
::DeleteObject(tmpBitmap);
::DeleteDC(memDC);
return(NS_OK);
}
}
// if we got to this point.. everything else failed.. and the slow blit backstop
// will finish this tiling
for (y=y0;y<y1;y+=ScaledTileHeight+aPadY*scale) {
for (x=x0;x<x1;x+=ScaledTileWidth+aPadX*scale) {
Draw(aContext, aSurface,
0, 0, PR_MIN(validWidth, x1-x), PR_MIN(validHeight, y1-y),
x, y, PR_MIN(destScaledWidth, x1-x), PR_MIN(destScaledHeight, y1-y));
}
}
return(NS_OK);
}
/** ---------------------------------------------------
* See documentation in nsImageWin.h
*/
PRBool
nsImageWin::ProgressiveDoubleBlit(nsIDeviceContext *aContext,
nsIDrawingSurface* aSurface,
PRInt32 aSXOffset, PRInt32 aSYOffset,
nsRect aDestRect)
{
/*
(aSXOffset, aSYOffset) is the offset into our image that we want to start
drawing from. We start drawing at (aDestRect.x, aDestRect.y)
- Find first full tile
- progressivly double blit until end of tile area. What's left will be at
maximum a strip on the top and a strip on the left side that has not been
blitted.
Then, in no particular order:
- blit the top strip using a row we already put down
- blit the left strip using a column we already put down
- blit the very topleft, since it will be left out
*/
HDC theHDC;
void *screenBits; // We never use the bits, but we need to pass a variable in
// to create a DIB
((nsDrawingSurfaceWin *)aSurface)->GetDC(&theHDC);
if (!theHDC)
return PR_FALSE;
// create an imageDC
HDC imgDC = ::CreateCompatibleDC(theHDC);
if (!imgDC) {
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return PR_FALSE;
}
CreateDDB();
nsPaletteInfo palInfo;
aContext->GetPaletteInfo(palInfo);
if (palInfo.isPaletteDevice && palInfo.palette) {
#ifndef WINCE
::SetStretchBltMode(imgDC, HALFTONE);
#endif
::SelectPalette(imgDC, (HPALETTE)palInfo.palette, TRUE);
::RealizePalette(imgDC);
}
// Create a maskDC, and fill it with mAlphaBits
HDC maskDC = nsnull;
HBITMAP oldImgMaskBits = nsnull;
HBITMAP maskBits;
HBITMAP mTmpHBitmap = nsnull;
if (mAlphaDepth == 1) {
maskDC = ::CreateCompatibleDC(theHDC);
if (!maskDC) {
::DeleteDC(imgDC);
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return PR_FALSE;
}
MONOBITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
maskBits = ::CreateDIBSection(theHDC, (LPBITMAPINFO)&bmi,
DIB_RGB_COLORS, &screenBits, NULL, 0);
if (!maskBits) {
::DeleteDC(imgDC);
::DeleteDC(maskDC);
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return PR_FALSE;
}
oldImgMaskBits = (HBITMAP)::SelectObject(maskDC, maskBits);
::SetDIBitsToDevice(maskDC, 0, 0, mBHead->biWidth, mBHead->biHeight,
0, 0, 0, mBHead->biHeight, mAlphaBits,
(LPBITMAPINFO)&bmi, DIB_RGB_COLORS);
}
// Fill imageDC with our image
HBITMAP oldImgBits = nsnull;
if (!mIsOptimized || !mHBitmap) {
// Win 95/98/ME can't do > 0xFF0000 DDBs.
if (gPlatform == VER_PLATFORM_WIN32_WINDOWS) {
int bytesPerPix = ::GetDeviceCaps(imgDC, BITSPIXEL) / 8;
if (mBHead->biWidth * mBHead->biHeight * bytesPerPix > 0xFF0000) {
::DeleteDC(imgDC);
if (maskDC) {
if (oldImgMaskBits)
::SelectObject(maskDC, oldImgMaskBits);
::DeleteObject(maskBits);
::DeleteDC(maskDC);
}
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return PR_FALSE;
}
}
mTmpHBitmap = ::CreateCompatibleBitmap(theHDC, mDecodedX2, mDecodedY2);
if (!mTmpHBitmap) {
::DeleteDC(imgDC);
if (maskDC) {
if (oldImgMaskBits)
::SelectObject(maskDC, oldImgMaskBits);
::DeleteObject(maskBits);
::DeleteDC(maskDC);
}
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return PR_FALSE;
}
oldImgBits = (HBITMAP)::SelectObject(imgDC, mTmpHBitmap);
::StretchDIBits(imgDC, 0, 0, mBHead->biWidth, mBHead->biHeight,
0, 0, mBHead->biWidth, mBHead->biHeight,
mImageBits, (LPBITMAPINFO)mBHead,
256 == mNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
SRCCOPY);
} else {
oldImgBits = (HBITMAP)::SelectObject(imgDC, mHBitmap);
}
PRBool result = PR_TRUE;
PRBool useAlphaBlend = mAlphaDepth == 8 && mIsOptimized;
PRInt32 firstWidth = mBHead->biWidth - aSXOffset;
PRInt32 firstHeight = mBHead->biHeight - aSYOffset;
if (aDestRect.width > firstWidth + mBHead->biWidth ||
aDestRect.height > firstHeight + mBHead->biHeight) {
PRInt32 firstPartialWidth = aSXOffset == 0 ? 0 : firstWidth;
PRInt32 firstPartialHeight = aSYOffset == 0 ? 0 : firstHeight;
PRBool hasFullImageWidth = aDestRect.width - firstPartialWidth >= mBHead->biWidth;
PRBool hasFullImageHeight = aDestRect.height - firstPartialHeight >= mBHead->biHeight;
HDC dstDC = theHDC; // Defaulting to drawing to theHDC
HDC dstMaskDC = nsnull; // No Alpha DC by default
HBITMAP dstMaskHBitmap = nsnull; // No Alpha HBITMAP by default either..
HBITMAP oldDstBits, oldDstMaskBits;
HBITMAP dstHBitmap;
do { // Use a do to help with cleanup
nsRect storedDstRect;
// dstDC will only be used if we have an alpha. It is not needed for
// images without an alpha because we can directly doubleblit onto theHDC
if (mAlphaDepth != 0) {
// We have an alpha, which means we can't p. doubleblit directly onto
// theHDC. We must create a temporary DC, p. doubleblit into that, and
// then draw the whole thing to theHDC
storedDstRect = aDestRect;
aDestRect.x = 0;
aDestRect.y = 0;
// The last p. doubleblit is very slow.. it's faster to draw to theHDC
// twice. So only progressive doubleblit up to 1/2 of the tiling area
// that requires full tiles.
if (aDestRect.height - firstPartialHeight >= mBHead->biHeight * 2)
aDestRect.height = PR_ROUNDUP(PRInt32(ceil(aDestRect.height / 2.0)),
mBHead->biHeight)
+ firstPartialHeight;
// Win 95/98/ME can't do > 0xFF0000 DDBs.
// Note: This should be extremely rare. We'd need a tile area (before
// it was split in 1/2 above) of 2048x4080 @ 4 bytesPerPix before
// we hit this.
if (gPlatform == VER_PLATFORM_WIN32_WINDOWS) {
int bytesPerPix = (mAlphaDepth == 8) ? 4 :
::GetDeviceCaps(theHDC, BITSPIXEL) / 8;
if (aDestRect.width * aDestRect.height * bytesPerPix > 0xFF0000) {
result = PR_FALSE;
break;
}
}
// make a temporary offscreen DC
dstDC = ::CreateCompatibleDC(theHDC);
if (!dstDC) {
result = PR_FALSE;
break;
}
// Create HBITMAP for the new DC
if (mAlphaDepth == 8) {
// Create a bitmap of 1 plane, 32 bit color depth
// Can't use ::CreateBitmap, since the resulting HBITMAP will only be
// able to be selected into a compatible HDC (ie. User is @ 24 bit, you
// can't select the 32 HBITMAP into the HDC.)
ALPHA32BITMAPINFO bmi(aDestRect.width, aDestRect.height);
dstHBitmap = ::CreateDIBSection(theHDC, (LPBITMAPINFO)&bmi,
DIB_RGB_COLORS, &screenBits, NULL, 0);
} else {
// Create a normal bitmap for the image, and a monobitmap for alpha
dstHBitmap = ::CreateCompatibleBitmap(theHDC, aDestRect.width,
aDestRect.height);
if (dstHBitmap) {
dstMaskDC = ::CreateCompatibleDC(theHDC);
if (dstMaskDC) {
MONOBITMAPINFO bmi(aDestRect.width, aDestRect.height);
dstMaskHBitmap = ::CreateDIBSection(theHDC, (LPBITMAPINFO)&bmi,
DIB_RGB_COLORS, &screenBits,
NULL, 0);
if (dstMaskHBitmap) {
oldDstMaskBits = (HBITMAP)::SelectObject(dstMaskDC,
dstMaskHBitmap);
} else {
result = PR_FALSE;
break;
}
} // dstMaskDC
} // dstHBitmap
}
if (!dstHBitmap) {
result = PR_FALSE;
break;
}
oldDstBits = (HBITMAP)::SelectObject(dstDC, dstHBitmap);
} // (mAlphaDepth != 0)
PRInt32 imgX = hasFullImageWidth ? 0 : aSXOffset;
PRInt32 imgW = PR_MIN(mBHead->biWidth - imgX, aDestRect.width);
PRInt32 imgY = hasFullImageHeight ? 0 : aSYOffset;
PRInt32 imgH = PR_MIN(mBHead->biHeight - imgY, aDestRect.height);
// surfaceDestPoint is the first position in the destination where we will
// be putting (0,0) of our image down.
nsPoint surfaceDestPoint(aDestRect.x, aDestRect.y);
if (aSXOffset != 0 && hasFullImageWidth)
surfaceDestPoint.x += firstWidth;
if (aSYOffset != 0 && hasFullImageHeight)
surfaceDestPoint.y += firstHeight;
// Plunk one down
BlitImage(dstDC, dstMaskDC,
surfaceDestPoint.x, surfaceDestPoint.y, imgW, imgH,
imgDC, maskDC, imgX, imgY, PR_FALSE);
if (!hasFullImageHeight && imgH != aDestRect.height) {
// Since we aren't drawing a full image in height, there's an area above
// that we can blit to too.
BlitImage(dstDC, dstMaskDC, surfaceDestPoint.x, surfaceDestPoint.y + imgH,
imgW, aDestRect.height - imgH,
imgDC, maskDC, imgX, 0, PR_FALSE);
imgH = aDestRect.height;
}
if (!hasFullImageWidth && imgW != aDestRect.width) {
BlitImage(dstDC, dstMaskDC, surfaceDestPoint.x + imgW, surfaceDestPoint.y,
aDestRect.width - imgW, imgH,
imgDC, maskDC, 0, imgY, PR_FALSE);
imgW = aDestRect.width;
}
nsRect surfaceDestRect;
nsRect surfaceSrcRect(surfaceDestPoint.x, surfaceDestPoint.y, imgW, imgH);
// Progressively DoubleBlit a Row
if (hasFullImageWidth) {
while (surfaceSrcRect.XMost() < aDestRect.XMost()) {
surfaceDestRect.x = surfaceSrcRect.XMost();
surfaceDestRect.width = surfaceSrcRect.width;
if (surfaceDestRect.XMost() >= aDestRect.XMost())
surfaceDestRect.width = aDestRect.XMost() - surfaceDestRect.x;
BlitImage(dstDC, dstMaskDC, surfaceDestRect.x, surfaceSrcRect.y,
surfaceDestRect.width, surfaceSrcRect.height,
dstDC, dstMaskDC, surfaceSrcRect.x, surfaceSrcRect.y,
PR_FALSE);
surfaceSrcRect.width += surfaceDestRect.width;
}
}
// Progressively DoubleBlit a column
if (hasFullImageHeight) {
while (surfaceSrcRect.YMost() < aDestRect.YMost()) {
surfaceDestRect.y = surfaceSrcRect.YMost();
surfaceDestRect.height = surfaceSrcRect.height;
if (surfaceDestRect.YMost() >= aDestRect.YMost())
surfaceDestRect.height = aDestRect.YMost() - surfaceDestRect.y;
BlitImage(dstDC, dstMaskDC, surfaceSrcRect.x, surfaceDestRect.y,
surfaceSrcRect.width, surfaceDestRect.height,
dstDC, dstMaskDC, surfaceSrcRect.x, surfaceSrcRect.y,
PR_FALSE);
surfaceSrcRect.height += surfaceDestRect.height;
}
}
// blit to topleft. This could be done before p doubleblitting
if (surfaceDestPoint.y != aDestRect.y && surfaceDestPoint.x != aDestRect.x)
BlitImage(dstDC, dstMaskDC, aDestRect.x, aDestRect.y,
firstWidth, firstHeight,
dstDC, dstMaskDC,
surfaceDestPoint.x + aSXOffset, surfaceDestPoint.y + aSYOffset,
PR_FALSE);
// blit top row
if (surfaceDestPoint.y != aDestRect.y)
BlitImage(dstDC, dstMaskDC, surfaceDestPoint.x, aDestRect.y,
surfaceSrcRect.width, firstHeight,
dstDC, dstMaskDC, surfaceDestPoint.x, surfaceDestPoint.y + aSYOffset,
PR_FALSE);
// blit left column
if (surfaceDestPoint.x != aDestRect.x)
BlitImage(dstDC, dstMaskDC, aDestRect.x, surfaceDestPoint.y,
firstWidth, surfaceSrcRect.height,
dstDC, dstMaskDC, surfaceDestPoint.x + aSXOffset, surfaceDestPoint.y,
PR_FALSE);
if (mAlphaDepth != 0) {
// blit temporary dstDC (& dstMaskDC, if one exists) to theHDC
BlitImage(theHDC, theHDC, storedDstRect.x, storedDstRect.y,
aDestRect.width, aDestRect.height,
dstDC, dstMaskDC, 0, 0, useAlphaBlend);
// blit again to 2nd part of tile area
if (storedDstRect.height > aDestRect.height)
BlitImage(theHDC, theHDC, storedDstRect.x, storedDstRect.y + aDestRect.height,
aDestRect.width, storedDstRect.height - aDestRect.height,
dstDC, dstMaskDC, 0, firstPartialHeight, useAlphaBlend);
}
} while (PR_FALSE);
if (mAlphaDepth != 0) {
if (dstDC) {
if (dstHBitmap) {
::SelectObject(dstDC, oldDstBits);
::DeleteObject(dstHBitmap);
}
::DeleteDC(dstDC);
}
if (dstMaskDC) {
if (dstMaskHBitmap) {
::SelectObject(dstMaskDC, oldDstMaskBits);
::DeleteObject(dstMaskHBitmap);
}
::DeleteDC(dstMaskDC);
}
}
} else {
// up to 4 image parts.
// top-left
BlitImage(theHDC, theHDC,
aDestRect.x, aDestRect.y, firstWidth, firstHeight,
imgDC, maskDC, aSXOffset, aSYOffset, useAlphaBlend);
// bottom-right
if (aDestRect.width - firstWidth > 0 && aDestRect.height - firstHeight > 0)
BlitImage(theHDC, theHDC,
aDestRect.x + firstWidth, aDestRect.y + firstHeight,
aDestRect.width - firstWidth, aDestRect.height - firstHeight,
imgDC, maskDC, 0, 0, useAlphaBlend);
// bottom-left
if (aDestRect.height - firstHeight > 0)
BlitImage(theHDC, theHDC, aDestRect.x, aDestRect.y + firstHeight,
firstWidth, aDestRect.height - firstHeight,
imgDC, maskDC, aSXOffset, 0, useAlphaBlend);
// top-right
if (aDestRect.width - firstWidth > 0)
BlitImage(theHDC, theHDC, aDestRect.x + firstWidth, aDestRect.y,
aDestRect.width - firstWidth, firstHeight,
imgDC, maskDC, 0, aSYOffset, useAlphaBlend);
}
if (oldImgBits)
::SelectObject(imgDC, oldImgBits);
if (mTmpHBitmap)
::DeleteObject(mTmpHBitmap);
::DeleteDC(imgDC);
if (maskDC) {
if (oldImgMaskBits)
::SelectObject(maskDC, oldImgMaskBits);
::DeleteObject(maskBits);
::DeleteDC(maskDC);
}
((nsDrawingSurfaceWin *)aSurface)->ReleaseDC();
return result;
}
void
nsImageWin::BlitImage(HDC aDstDC, HDC aDstMaskDC, PRInt32 aDstX, PRInt32 aDstY,
PRInt32 aWidth, PRInt32 aHeight,
HDC aSrcDC, HDC aSrcMaskDC, PRInt32 aSrcX, PRInt32 aSrcY,
PRBool aUseAlphaBlend)
{
if (aUseAlphaBlend) {
BLENDFUNCTION blendFunction;
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.BlendFlags = 0;
blendFunction.SourceConstantAlpha = 255;
blendFunction.AlphaFormat = 1;
gAlphaBlend(aDstDC, aDstX, aDstY, aWidth, aHeight,
aSrcDC, aSrcX, aSrcY, aWidth, aHeight, blendFunction);
} else {
if (aSrcMaskDC) {
if (aDstMaskDC == aDstDC) {
::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
aSrcMaskDC, aSrcX, aSrcY, SRCAND);
::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
aSrcDC, aSrcX, aSrcY, SRCPAINT);
} else {
::BitBlt(aDstMaskDC, aDstX, aDstY, aWidth, aHeight,
aSrcMaskDC, aSrcX, aSrcY, SRCCOPY);
::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
aSrcDC, aSrcX, aSrcY, SRCCOPY);
}
} else {
::BitBlt(aDstDC, aDstX, aDstY, aWidth, aHeight,
aSrcDC, aSrcX, aSrcY, SRCCOPY);
}
}
}
ALPHABLENDPROC nsImageWin::gAlphaBlend = NULL;
PRBool nsImageWin::CanAlphaBlend(void)
{
#ifdef WINCE
gAlphaBlend = nsnull;
return PR_FALSE;
#else
static PRBool alreadyChecked = PR_FALSE;
if (!alreadyChecked) {
OSVERSIONINFO os;
os.dwOSVersionInfoSize = sizeof(os);
::GetVersionEx(&os);
// If running on Win98, disable using AlphaBlend()
// to avoid Win98 AlphaBlend() bug
if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId ||
os.dwMajorVersion != 4 || os.dwMinorVersion != 10) {
gAlphaBlend = (ALPHABLENDPROC)::GetProcAddress(::LoadLibrary("msimg32"),
"AlphaBlend");
}
alreadyChecked = PR_TRUE;
}
return gAlphaBlend != NULL;
#endif
}
/** ----------------------------------------------------------------
* Create an optimized bitmap
* @update dc - 11/20/98
* @param aContext - The device context to use for the optimization
*/
nsresult nsImageWin::Optimize(nsIDeviceContext* aContext)
{
// Do the actual optimizing when we first use the image (draw it)
// This saves on GDI resources.
mWantsOptimization = PR_TRUE;
return NS_OK;
}
// CreateDDB only if DDB is wanted
NS_IMETHODIMP nsImageWin::CreateDDB()
{
if (!mWantsOptimization || mIsOptimized) {
// Timer only exists when mIsOptimized. Push timer forwards.
if (mTimer)
mTimer->SetDelay(GFX_MS_REMOVE_DBB);
return NS_OK;
}
// we used to set a flag because a valid HDC may not be ready,
// like at startup, but now we just roll our own HDC for the given screen.
//
// Some images should not be converted to DDB...
//
// Windows 95/98/Me: DDB size cannot exceed ~16MB in size.
// We also can not optimize empty images, or 8-bit alpha depth images on
// Win98 due to a Windows API bug.
//
// On Windows 95/98/Me, GDI resources are very limited. Windows NT has more,
// but is still rather limited. Create DDBs on these platforms only for
// large (> 128k) images to minimize GDI handle usage.
// See bug 205893, bug 204374, and bug 216430 for more info on the situation.
// Bottom-line: we need a better accounting mechanism to avoid exceeding the
// system's GDI object limit. The rather arbitrary size limitation imposed
// here is based on the typical size of the Mozilla memory cache. This is
// most certainly the wrong place to impose this policy, but we do it for
// now as a stop-gap measure.
//
if ((gOsMajorVersion <= VER_OSMAJOR_WIN9598MENT &&
(mSizeImage >= 0xFF0000 || mSizeImage < 0x20000)) ||
(mAlphaDepth == 8 && !CanAlphaBlend())) {
return NS_OK;
}
HDC TheHDC = ::CreateCompatibleDC(NULL);
if (TheHDC != NULL){
// Temporary fix for bug 135226 until we do better decode-time
// dithering and paletized storage of images. Bail on the
// optimization to DDB if we're on a paletted device.
int rasterCaps = ::GetDeviceCaps(TheHDC, RASTERCAPS);
if (RC_PALETTE == (rasterCaps & RC_PALETTE)) {
::DeleteDC(TheHDC);
return NS_OK;
}
// we have to install the correct bitmap to get a good DC going
int planes = ::GetDeviceCaps(TheHDC, PLANES);
int bpp = ::GetDeviceCaps(TheHDC, BITSPIXEL);
HBITMAP tBitmap = ::CreateBitmap(1,1,planes,bpp,NULL);
HBITMAP oldbits = (HBITMAP)::SelectObject(TheHDC,tBitmap);
if (mAlphaDepth == 8) {
CreateImageWithAlphaBits(TheHDC);
} else {
// Apparently, DIBs use less resources than DDBs.
// On Windows 95/98/Me/NT, use DIBs to save resources. On remaining
// platforms, use DDBs. DDBs draw at the same speed or faster when
// drawn to a DDB surface. This is another temporary solution until
// we manage our resources better.
if (gOsMajorVersion <= VER_OSMAJOR_WIN9598MENT) {
LPVOID bits;
mHBitmap = ::CreateDIBSection(TheHDC, (LPBITMAPINFO)mBHead,
256 == mNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
&bits, NULL, 0);
if (mHBitmap) {
memcpy(bits, mImageBits, mSizeImage);
mIsOptimized = PR_TRUE;
} else {
mIsOptimized = PR_FALSE;
}
} else {
mHBitmap = ::CreateDIBitmap(TheHDC, mBHead, CBM_INIT, mImageBits,
(LPBITMAPINFO)mBHead,
256 == mNumPaletteColors ? DIB_PAL_COLORS
: DIB_RGB_COLORS);
mIsOptimized = (mHBitmap != 0);
}
}
if (mIsOptimized) {
::GdiFlush();
CleanUpDIB();
if (mTimer) {
mTimer->SetDelay(GFX_MS_REMOVE_DBB);
} else {
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mTimer)
mTimer->InitWithFuncCallback(nsImageWin::TimerCallBack, this,
GFX_MS_REMOVE_DBB,
nsITimer::TYPE_ONE_SHOT);
}
}
::SelectObject(TheHDC,oldbits);
::DeleteObject(tBitmap);
::DeleteDC(TheHDC);
}
return NS_OK;
}
// Removes the DBB, restoring the imagebits if necessary
NS_IMETHODIMP nsImageWin::RemoveDDB()
{
if (!mIsOptimized && mHBitmap == nsnull)
return NS_OK;
if (!mImageBits) {
nsresult rv = ConvertDDBtoDIB();
if (NS_FAILED(rv))
return rv;
}
CleanUpDDB();
return NS_OK;
}
/** ----------------------------------------------------------------
* Calculate the number of bytes in a span for this image
* @update dc - 11/20/98
* @param aWidth - The width to calulate the number of bytes from
* @return Number of bytes for the span
*/
PRInt32
nsImageWin :: CalcBytesSpan(PRUint32 aWidth)
{
PRInt32 spanBytes;
spanBytes = (aWidth * mBHead->biBitCount) >> 5;
if (((PRUint32)mBHead->biWidth * mBHead->biBitCount) & 0x1F)
spanBytes++;
spanBytes <<= 2;
return(spanBytes);
}
/** ---------------------------------------------------
* See documentation in nsImageWin.h
* @update 4/05/00 dwc
*/
void
nsImageWin::CleanUpDIB()
{
if (mImageBits != nsnull) {
delete [] mImageBits;
mImageBits = nsnull;
}
// Should be an ISupports, so we can release
if (mColorMap != nsnull){
delete [] mColorMap->Index;
delete mColorMap;
mColorMap = nsnull;
}
}
/** ---------------------------------------------------
* See documentation in nsImageWin.h
* @update 4/05/00 dwc
*/
void
nsImageWin :: CleanUpDDB()
{
if (mHBitmap != nsnull) {
if (mTimer) {
mTimer->Cancel();
mTimer = nsnull;
}
::DeleteObject(mHBitmap);
mHBitmap = nsnull;
}
mIsOptimized = PR_FALSE;
}
/** ----------------------------------------------------------------
* See documentation in nsIImage.h
* @update - dwc 5/20/99
* @return the result of the operation, if NS_OK, then the pixelmap is unoptimized
*/
nsresult
nsImageWin::PrintDDB(nsIDrawingSurface* aSurface,
PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight,
PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight,
PRUint32 aROP)
{
HDC theHDC;
UINT palType;
if (mIsOptimized == PR_TRUE){
if (mHBitmap != nsnull){
ConvertDDBtoDIB();
((nsDrawingSurfaceWin *)aSurface)->GetDC(&theHDC);
if (mBHead->biBitCount == 8) {
palType = DIB_PAL_COLORS;
} else {
palType = DIB_RGB_COLORS;
}
::StretchDIBits(theHDC, aDX, aDY, aDWidth, aDHeight,
aSX, aSY, aSWidth, aSHeight, mImageBits,
(LPBITMAPINFO)mBHead, palType, aROP);
// we are finished with this, so delete it
if (mImageBits != nsnull) {
delete [] mImageBits;
mImageBits = nsnull;
}
}
}
return NS_OK;
}
/** ----------------------------------------------------------------
* recreate a device independent image from a device dependent image (DDB)
* Does not remove the DDB
*
* @return the result of the operation, if NS_OK a DIB was created.
*/
nsresult nsImageWin::ConvertDDBtoDIB()
{
HDC memPrDC;
if (mImageBits)
return NS_OK;
if (!mInitialized || mHBitmap == nsnull)
return NS_ERROR_FAILURE;
memPrDC = ::CreateDC("DISPLAY", NULL, NULL, NULL);
if (!memPrDC)
return NS_ERROR_FAILURE;
// Allocate the image bits
mImageBits = new unsigned char[mSizeImage];
if (!mImageBits) {
::DeleteDC(memPrDC);
return NS_ERROR_OUT_OF_MEMORY;
}
PRInt32 retVal =
::GetDIBits(memPrDC, mHBitmap, 0, mBHead->biHeight,
mImageBits, (LPBITMAPINFO)mBHead,
256 == mNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS);
::GdiFlush();
::DeleteDC(memPrDC);
if (retVal == 0) {
delete [] mImageBits;
mImageBits = nsnull;
return NS_ERROR_FAILURE;
}
// If we converted a 8 bit alpha back to bits, those bits are pre-multiplied
if (mAlphaDepth == 8)
mImagePreMultiplied = PR_TRUE;
return NS_OK;
}
/** ---------------------------------------------------
* Lock down the image pixels
*/
NS_IMETHODIMP
nsImageWin::LockImagePixels(PRBool aMaskPixels)
{
/* if (!mHBitmap) return NS_ERROR_NOT_INITIALIZED;
... and do Windows locking of image pixels here, if necessary */
mIsLocked = PR_TRUE;
return NS_OK;
}
/** ---------------------------------------------------
* Unlock the pixels, optimize this nsImageWin
*/
NS_IMETHODIMP
nsImageWin::UnlockImagePixels(PRBool aMaskPixels)
{
mIsLocked = PR_FALSE;
// if memory was allocated temporarily by GetBits, it can now be deleted safely
if (mDIBTemp == PR_TRUE) {
// get rid of this memory
if (mImageBits != nsnull) {
delete [] mImageBits;
mImageBits = nsnull;
}
mDIBTemp = PR_FALSE;
}
/* if (!mHBitmap)
return NS_ERROR_NOT_INITIALIZED;
if (aMaskPixels && !mAlphamHBitmap)
return NS_ERROR_NOT_INITIALIZED;
... and do Windows unlocking of image pixels here, if necessary */
return NS_OK;
}
/** ---------------------------------------------------
* See documentation in nsImageWin.h
* Get a pointer to the bits for the pixelmap. Will convert to DIB if
* only stored in optimized HBITMAP form. Using this routine will
* set the mDIBTemp flag to true so the next unlock will destroy this memory
*
* @return address of the DIB pixel array
*/
PRUint8*
nsImageWin::GetBits()
{
// if mImageBits did not exist.. then
if (!mImageBits) {
ConvertDDBtoDIB();
mDIBTemp = PR_TRUE; // only set to true if the DIB is being created here as temporary
}
return mImageBits;
} // GetBits
/** ---------------------------------------------------
* This blends together GIF's for the animation... called by gfxImageFrame
* currently does not support and 8bit blend. Assumed for animated GIF's only
* @update 07/09/2002 dwc
* @param aDstImage -- destination where to copy to
* @param aDX -- x location of the image
* @param aDY -- y location of the image
* @param aDWidth -- width of the image
* @param aDHeight -- height of the image
*/
NS_IMETHODIMP nsImageWin::DrawToImage(nsIImage* aDstImage, nscoord aDX, nscoord aDY, nscoord aDWidth, nscoord aDHeight)
{
NS_ASSERTION(mAlphaDepth <= 1, "nsImageWin::DrawToImage can only handle 0 & 1 bit Alpha");
if (mAlphaDepth > 1)
return NS_ERROR_UNEXPECTED;
nsImageWin *dest = NS_STATIC_CAST(nsImageWin *, aDstImage);
if (!dest)
return NS_ERROR_FAILURE;
if (aDX >= dest->mBHead->biWidth || aDY >= dest->mBHead->biHeight)
return NS_OK;
if (!dest->mImageBits)
return NS_ERROR_FAILURE;
if (!dest->mIsOptimized) {
// set up some local variable to make things run faster in the loop
PRUint8 *rgbPtr = 0, *alphaPtr = 0;
PRUint32 rgbStride, alphaStride;
PRInt32 srcHeight;
PRUint8 *dstRgbPtr = 0, *dstAlphaPtr = 0;
PRUint32 dstRgbStride, dstAlphaStride;
PRInt32 dstHeight;
rgbPtr = mImageBits;
rgbStride = mRowBytes;
alphaPtr = mAlphaBits;
alphaStride = mARowBytes;
srcHeight = mBHead->biHeight;
dstRgbPtr = dest->mImageBits;
dstRgbStride = dest->mRowBytes;
dstAlphaPtr = dest->mAlphaBits;
dstAlphaStride = dest->mARowBytes;
dstHeight = dest->mBHead->biHeight;
PRInt32 y;
PRInt32 ValidWidth = (aDWidth < (dest->mBHead->biWidth - aDX)) ?
aDWidth : (dest->mBHead->biWidth - aDX);
PRInt32 ValidHeight = (aDHeight < (dstHeight - aDY)) ?
aDHeight : (dstHeight - aDY);
PRUint8 *dst;
PRUint8 *src;
// now composite the two images together
switch (mAlphaDepth) {
case 1:
{
PRUint8 *dstAlpha;
PRUint8 *alpha;
PRUint8 offset = aDX & 0x7; // x starts at 0
for (y=0; y<ValidHeight; y++) {
dst = dstRgbPtr +
(dstHeight - (aDY+y) - 1) * dstRgbStride +
(3 * aDX);
dstAlpha = dstAlphaPtr + (dstHeight - (aDY+y) - 1) * dstAlphaStride;
src = rgbPtr + (srcHeight - y - 1)*rgbStride;
alpha = alphaPtr + (srcHeight - y - 1)*alphaStride;
for (int x = 0;
x < ValidWidth;
x += 8, dst += 3 * 8, src += 3 * 8) {
PRUint8 alphaPixels = *alpha++;
if (alphaPixels == 0) {
// all 8 transparent; jump forward
continue;
}
// 1 or more bits are set, handle dstAlpha now - may not be aligned.
// Are all 8 of these alpha pixels used?
if (x+7 >= 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;
}
}
}
}
}
break;
case 0:
default:
dst = dstRgbPtr + (dstHeight - aDY - 1) * dstRgbStride + 3 * aDX;
src = rgbPtr + (srcHeight - 1) * rgbStride;
for (y = 0; y < ValidHeight; y++) {
memcpy(dst, src, 3 * ValidWidth);
dst -= dstRgbStride;
src -= rgbStride;
}
}
nsRect rect(aDX, aDY, ValidWidth, ValidHeight);
dest->ImageUpdated(nsnull, 0, &rect);
} else {
// dst's Image is optimized (dest->mHBitmap is linked to dest->mImageBits), so just paint on it
if (!dest->mHBitmap)
return NS_ERROR_UNEXPECTED;
HDC dstMemDC = ::CreateCompatibleDC(nsnull);
HBITMAP oldDstBits;
DWORD rop;
oldDstBits = (HBITMAP)::SelectObject(dstMemDC, dest->mHBitmap);
rop = SRCCOPY;
if (mAlphaBits) {
if (1==mAlphaDepth) {
MONOBITMAPINFO bmi(mBHead->biWidth, mBHead->biHeight);
::StretchDIBits(dstMemDC, aDX, aDY, aDWidth, aDHeight,
0, 0,mBHead->biWidth, mBHead->biHeight, mAlphaBits,
(LPBITMAPINFO)&bmi, DIB_RGB_COLORS, SRCAND);
rop = SRCPAINT;
}
}
if (8 == mAlphaDepth) {
nsresult rv = DrawComposited(dstMemDC, aDX, aDY, aDWidth, aDHeight,
0, 0, mBHead->biWidth, mBHead->biHeight,
aDWidth, aDHeight);
if (NS_FAILED(rv)) {
::SelectObject(dstMemDC, oldDstBits);
::DeleteDC(dstMemDC);
return rv;
}
}
// Copy/Paint our rgb to dest
if (mIsOptimized && mHBitmap) {
// We are optimized, use Stretchblt
HDC srcMemDC = ::CreateCompatibleDC(nsnull);
HBITMAP oldSrcBits;
oldSrcBits = (HBITMAP)::SelectObject(srcMemDC, mHBitmap);
::StretchBlt(dstMemDC, aDX, aDY, aDWidth, aDHeight, srcMemDC,
0, 0, mBHead->biWidth, mBHead->biHeight, rop);
::SelectObject(srcMemDC, oldSrcBits);
::DeleteDC(srcMemDC);
} else {
::StretchDIBits(dstMemDC, aDX, aDY, aDWidth, aDHeight,
0, 0, mBHead->biWidth, mBHead->biHeight, mImageBits,
(LPBITMAPINFO)mBHead, DIB_RGB_COLORS, rop);
}
::SelectObject(dstMemDC, oldDstBits);
::DeleteDC(dstMemDC);
}
return NS_OK;
}
/** ---------------------------------------------------
* copy the mask and the image to the passed in DC, do the raster operation in memory before going to the
* printer
*/
void
CompositeBitsInMemory(HDC aTheHDC, int aDX, int aDY, int aDWidth, int aDHeight,
int aSX, int aSY, int aSWidth, int aSHeight,PRInt32 aSrcy,
PRUint8 *aAlphaBits, MONOBITMAPINFO *aBMI,
PRUint8* aImageBits, LPBITMAPINFOHEADER aBHead,
PRInt16 aNumPaletteColors)
{
unsigned char *screenBits;
HDC memDC = ::CreateCompatibleDC(NULL);
if(0!=memDC){
ALPHA24BITMAPINFO offbmi(aSWidth, aSHeight);
HBITMAP tmpBitmap = ::CreateDIBSection(memDC, (LPBITMAPINFO)&offbmi, DIB_RGB_COLORS,
(LPVOID *)&screenBits, NULL, 0);
if(0 != tmpBitmap){
HBITMAP oldBitmap = (HBITMAP)::SelectObject(memDC, tmpBitmap);
if(0!=oldBitmap) {
// pop in the alpha channel
::StretchDIBits(memDC, 0, 0, aSWidth, aSHeight,
aSX, aSrcy, aSWidth, aSHeight,
aAlphaBits, (LPBITMAPINFO)aBMI,
DIB_RGB_COLORS, SRCCOPY);
// paint in the image
::StretchDIBits(memDC, 0, 0, aSWidth, aSHeight,
aSX, aSrcy, aSWidth, aSHeight,
aImageBits, (LPBITMAPINFO)aBHead,
256 == aNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
SRCPAINT);
::GdiFlush();
// output the composed image
::StretchDIBits(aTheHDC, aDX, aDY, aDWidth, aDHeight,
aSX, aSrcy, aSWidth, aSHeight,
screenBits, (LPBITMAPINFO)&offbmi,
256 == aNumPaletteColors ? DIB_PAL_COLORS : DIB_RGB_COLORS,
SRCCOPY);
::SelectObject(memDC, oldBitmap);
}
::DeleteObject(tmpBitmap);
}
::DeleteDC(memDC);
}
}
void nsImageWin::TimerCallBack(nsITimer *aTimer, void *aClosure)
{
nsImageWin *entry = NS_STATIC_CAST(nsImageWin*, aClosure);
if (!entry)
return;
if (NS_FAILED(entry->RemoveDDB())) {
// Try again later. Can't SetDelay while timer is being called, so
// create a new timer instead
entry->mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (entry->mTimer)
entry->mTimer->InitWithFuncCallback(nsImageWin::TimerCallBack, entry,
GFX_MS_REMOVE_DBB,
nsITimer::TYPE_ONE_SHOT);
}
}