Bug 283091 - LockImagePixels should regenerate alpha bits. r=sfraser, sr=tor.

This commit is contained in:
pedemont%us.ibm.com 2005-02-23 20:55:56 +00:00
Родитель f8afe489b4
Коммит 20e16dcc60
2 изменённых файлов: 162 добавлений и 93 удалений

Просмотреть файл

@ -41,10 +41,6 @@
#include "nsRegionPool.h" #include "nsRegionPool.h"
#include "prmem.h" #include "prmem.h"
// NOTE: Define this to premultiply the alpha info into the image. May result
// in a performance boost.
#define PERF_PREMULT_ALPHA
// Number of bits for each component in a pixel. // Number of bits for each component in a pixel.
#define BITS_PER_COMPONENT 8 #define BITS_PER_COMPONENT 8
// Number of components per pixel (i.e. as in ARGB). // Number of components per pixel (i.e. as in ARGB).
@ -159,11 +155,18 @@ nsImageMac::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth,
// 24-bit images are 8 bits per component; the alpha component is ignored // 24-bit images are 8 bits per component; the alpha component is ignored
mRowBytes = CalculateRowBytes(aWidth, aDepth == 24 ? 32 : aDepth); mRowBytes = CalculateRowBytes(aWidth, aDepth == 24 ? 32 : aDepth);
mImageBits = (PRUint8*) PR_Malloc(mHeight * mRowBytes * sizeof(PRUint8)); mImageBits = (PRUint8*) PR_Malloc(mHeight * mRowBytes * sizeof(PRUint8));
if (!mImageBits)
return NS_ERROR_OUT_OF_MEMORY;
if (mAlphaDepth) if (mAlphaDepth)
{ {
mAlphaRowBytes = CalculateRowBytes(aWidth, mAlphaDepth); mAlphaRowBytes = CalculateRowBytes(aWidth, mAlphaDepth);
mAlphaBits = (PRUint8*) PR_Malloc(mHeight * mAlphaRowBytes * sizeof(PRUint8)); mAlphaBits = (PRUint8*) PR_Malloc(mHeight * mAlphaRowBytes * sizeof(PRUint8));
if (!mAlphaBits) {
PR_Free(mImageBits);
mImageBits = nsnull;
return NS_ERROR_OUT_OF_MEMORY;
}
} }
return NS_OK; return NS_OK;
@ -191,45 +194,49 @@ nsImageMac::ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags,
} }
#ifdef PERF_PREMULT_ALPHA
void DataProviderReleaseFunc(void *info, const void *data, size_t size) void DataProviderReleaseFunc(void *info, const void *data, size_t size)
{ {
PR_Free(NS_CONST_CAST(void*, data)); PR_Free(NS_CONST_CAST(void*, data));
} }
#endif
// Create CGImageRef from image bits. Merge alpha bit into mImageBits, which // Create CGImageRef from image bits. Merge alpha bit into mImageBits, which
// contains "place holder" for the alpha information. Then, create the // contains "place holder" for the alpha information. Then, create the
// CGImageRef for use in drawing. // CGImageRef for use in drawing.
void nsImageMac::EnsureCachedImage() nsresult
nsImageMac::EnsureCachedImage()
{ {
// Only create cached image if mPendingUpdate is set. // Only create cached image if mPendingUpdate is set.
if (!mPendingUpdate) if (!mPendingUpdate)
return; return NS_OK;
if (mImage) { if (mImage) {
::CGImageRelease(mImage); ::CGImageRelease(mImage);
mImage = NULL; mImage = NULL;
} }
#ifdef PERF_PREMULT_ALPHA PRUint8* imageData = NULL; // data from which to create CGImage
PRUint8* imgPremultARGBData = NULL; CGImageAlphaInfo alphaInfo; // alpha info for CGImage
if (mAlphaDepth == 8) void (*releaseFunc)(void *info, const void *data, size_t size) = NULL;
imgPremultARGBData = (PRUint8*) PR_Malloc(mWidth * mHeight * COMPS_PER_PIXEL);
PRUint8* tmp = imgPremultARGBData;
#endif
switch (mAlphaDepth) switch (mAlphaDepth)
{ {
case 8: case 8:
{
// For 8-bit alpha, we create our own storage, since we premultiply the
// alpha info into the image bits, but we still want to keep the original
// image bits (mImageBits).
imageData = (PRUint8*) PR_Malloc(mWidth * mHeight * COMPS_PER_PIXEL);
if (!imageData)
return NS_ERROR_OUT_OF_MEMORY;
PRUint8* tmp = imageData;
for (PRInt32 y = 0; y < mHeight; y++) for (PRInt32 y = 0; y < mHeight; y++)
{ {
PRInt32 rowStart = mRowBytes * y; PRInt32 rowStart = mRowBytes * y;
PRInt32 alphaRowStart = mAlphaRowBytes * y; PRInt32 alphaRowStart = mAlphaRowBytes * y;
for (PRInt32 x = 0; x < mWidth; x++) for (PRInt32 x = 0; x < mWidth; x++)
{ {
#ifdef PERF_PREMULT_ALPHA
// Here we combine the alpha information with each pixel component, // Here we combine the alpha information with each pixel component,
// creating an image with 'premultiplied alpha'. // creating an image with 'premultiplied alpha'.
PRUint8 alpha = mAlphaBits[alphaRowStart + x]; PRUint8 alpha = mAlphaBits[alphaRowStart + x];
@ -238,16 +245,20 @@ void nsImageMac::EnsureCachedImage()
FAST_DIVIDE_BY_255(*tmp++, mImageBits[offset + 1] * alpha); FAST_DIVIDE_BY_255(*tmp++, mImageBits[offset + 1] * alpha);
FAST_DIVIDE_BY_255(*tmp++, mImageBits[offset + 2] * alpha); FAST_DIVIDE_BY_255(*tmp++, mImageBits[offset + 2] * alpha);
FAST_DIVIDE_BY_255(*tmp++, mImageBits[offset + 3] * alpha); FAST_DIVIDE_BY_255(*tmp++, mImageBits[offset + 3] * alpha);
#else
// Copy the alpha information into the place holder in the image data.
mImageBits[rowStart + COMPS_PER_PIXEL * x] =
mAlphaBits[alphaRowStart + x];
#endif
} }
} }
// The memory that we pass to the CGDataProvider needs to stick around as
// long as the provider does, and the provider doesn't get destroyed until
// the CGImage is destroyed. So we have this small function which is
// called when the provider is destroyed.
releaseFunc = DataProviderReleaseFunc;
alphaInfo = kCGImageAlphaPremultipliedFirst;
break; break;
}
case 1: case 1:
{
for (PRInt32 y = 0; y < mHeight; y++) for (PRInt32 y = 0; y < mHeight; y++)
{ {
PRInt32 rowStart = mRowBytes * y; PRInt32 rowStart = mRowBytes * y;
@ -256,42 +267,24 @@ void nsImageMac::EnsureCachedImage()
{ {
// Copy the alpha information into the place holder in the image data. // Copy the alpha information into the place holder in the image data.
mImageBits[rowStart + COMPS_PER_PIXEL * x] = mImageBits[rowStart + COMPS_PER_PIXEL * x] =
GetBit(alphaRow, x) ? 255 : 0; GetAlphaBit(alphaRow, x) ? 255 : 0;
} }
} }
alphaInfo = kCGImageAlphaPremultipliedFirst;
imageData = mImageBits;
break; break;
}
case 0: case 0:
default: default:
for (PRInt32 y = 0; y < mHeight; y++) {
{ alphaInfo = kCGImageAlphaNoneSkipFirst;
PRInt32 rowStart = mRowBytes * y; imageData = mImageBits;
for (PRInt32 x = 0; x < mWidth; x++)
{
// No alpha information
mImageBits[rowStart + COMPS_PER_PIXEL * x] = 255;
}
}
break; break;
}
} }
PRUint8* imageData = mImageBits;
CGImageAlphaInfo alphaInfo = kCGImageAlphaFirst;
void (*releaseFunc)(void *info, const void *data, size_t size) = NULL;
#ifdef PERF_PREMULT_ALPHA
if (mAlphaDepth == 8)
{
imageData = imgPremultARGBData;
alphaInfo = kCGImageAlphaPremultipliedFirst;
// The memory that we pass to the CGDataProvider needs to stick around as
// long as the provider does, and the provider doesn't get destroyed until
// the CGImage is destroyed. So we have this small function which is called
// when the provider is destroyed.
releaseFunc = DataProviderReleaseFunc;
}
#endif
CGColorSpaceRef cs = ::CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef cs = ::CGColorSpaceCreateDeviceRGB();
StColorSpaceReleaser csReleaser(cs); StColorSpaceReleaser csReleaser(cs);
CGDataProviderRef prov = ::CGDataProviderCreateWithData(NULL, imageData, CGDataProviderRef prov = ::CGDataProviderCreateWithData(NULL, imageData,
@ -303,6 +296,7 @@ void nsImageMac::EnsureCachedImage()
::CGDataProviderRelease(prov); ::CGDataProviderRelease(prov);
mPendingUpdate = PR_FALSE; mPendingUpdate = PR_FALSE;
return NS_OK;
} }
@ -314,7 +308,8 @@ nsImageMac::Draw(nsIRenderingContext &aContext, nsIDrawingSurface* aSurface,
if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1) if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
return NS_OK; return NS_OK;
EnsureCachedImage(); nsresult rv = EnsureCachedImage();
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 srcWidth = aSWidth; PRInt32 srcWidth = aSWidth;
PRInt32 srcHeight = aSHeight; PRInt32 srcHeight = aSHeight;
@ -391,11 +386,14 @@ NS_IMETHODIMP
nsImageMac::DrawToImage(nsIImage* aDstImage, PRInt32 aDX, PRInt32 aDY, nsImageMac::DrawToImage(nsIImage* aDstImage, PRInt32 aDX, PRInt32 aDY,
PRInt32 aDWidth, PRInt32 aDHeight) PRInt32 aDWidth, PRInt32 aDHeight)
{ {
nsresult rv = NS_ERROR_FAILURE;
nsImageMac* dest = NS_STATIC_CAST(nsImageMac*, aDstImage); nsImageMac* dest = NS_STATIC_CAST(nsImageMac*, aDstImage);
EnsureCachedImage(); nsresult rv = EnsureCachedImage();
dest->EnsureCachedImage(); NS_ENSURE_SUCCESS(rv, rv);
rv = dest->EnsureCachedImage();
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_ERROR_FAILURE;
// Create image storage. The storage is 'owned' by the CGImageRef created // Create image storage. The storage is 'owned' by the CGImageRef created
// below as 'newImageRef'; that is, the storage cannot be deleted until the // below as 'newImageRef'; that is, the storage cannot be deleted until the
@ -405,6 +403,8 @@ nsImageMac::DrawToImage(nsIImage* aDstImage, PRInt32 aDX, PRInt32 aDY,
PRInt32 bytesPerRow = dest->GetLineStride(); PRInt32 bytesPerRow = dest->GetLineStride();
PRInt32 totalBytes = height * bytesPerRow; PRInt32 totalBytes = height * bytesPerRow;
PRUint8* bitmap = (PRUint8*) PR_Malloc(totalBytes); PRUint8* bitmap = (PRUint8*) PR_Malloc(totalBytes);
if (!bitmap)
return NS_ERROR_OUT_OF_MEMORY;
CGColorSpaceRef cs = ::CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef cs = ::CGColorSpaceCreateDeviceRGB();
StColorSpaceReleaser csReleaser(cs); StColorSpaceReleaser csReleaser(cs);
@ -427,11 +427,7 @@ nsImageMac::DrawToImage(nsIImage* aDstImage, PRInt32 aDX, PRInt32 aDY,
::CGContextRelease(bitmapContext); ::CGContextRelease(bitmapContext);
#ifdef PERF_PREMULT_ALPHA
CGImageAlphaInfo alphaInfo = ::CGImageGetAlphaInfo(dest->mImage); CGImageAlphaInfo alphaInfo = ::CGImageGetAlphaInfo(dest->mImage);
#else
CGImageAlphaInfo alphaInfo = kCGImageAlphaFirst;
#endif
// create a new image from the combined bitmap // create a new image from the combined bitmap
CGDataProviderRef prov = ::CGDataProviderCreateWithData(NULL, bitmap, CGDataProviderRef prov = ::CGDataProviderCreateWithData(NULL, bitmap,
@ -480,9 +476,9 @@ nsImageMac::AdoptImage(CGImageRef aNewImage, PRUint8* aNewBitmap)
nsresult nsresult
nsImageMac::Optimize(nsIDeviceContext* aContext) nsImageMac::Optimize(nsIDeviceContext* aContext)
{ {
EnsureCachedImage(); nsresult rv = EnsureCachedImage();
NS_ENSURE_SUCCESS(rv, rv);
#ifdef PERF_PREMULT_ALPHA
// We cannot delete the bitmap data from which mImage was created; the data // We cannot delete the bitmap data from which mImage was created; the data
// needs to stick around at least as long as the mImage is valid. // needs to stick around at least as long as the mImage is valid.
// mImageBits is used by all images, except those that have mAlphaDepth == 8 // mImageBits is used by all images, except those that have mAlphaDepth == 8
@ -491,7 +487,6 @@ nsImageMac::Optimize(nsIDeviceContext* aContext)
PR_Free(mImageBits); PR_Free(mImageBits);
mImageBits = NULL; mImageBits = NULL;
} }
#endif
// mAlphaBits is not really used any more after EnsureCachedImage is called, // mAlphaBits is not really used any more after EnsureCachedImage is called,
// since it's data is merged into mImageBits. // since it's data is merged into mImageBits.
@ -512,31 +507,101 @@ nsImageMac::LockImagePixels(PRBool aMaskPixels)
if (!mOptimized) if (!mOptimized)
return NS_OK; return NS_OK;
// Regenerate mImageBits by drawing mImage into bitmap context. // Need to recreate mAlphaBits and/or mImageBits. We do so by drawing the
// We only need do this if it was destroyed by Optimize(). // CGImage into a bitmap context. Afterwards, 'imageBits' contains the
if (!mImageBits) // image data with premultiplied alpha (if applicable).
{ PRUint8* imageBits = (PRUint8*) PR_Malloc(mHeight * mRowBytes);
mImageBits = (PRUint8*) PR_Malloc(mHeight * mRowBytes); if (!imageBits)
return NS_ERROR_OUT_OF_MEMORY;
CGColorSpaceRef cs = ::CGColorSpaceCreateDeviceRGB();
StColorSpaceReleaser csReleaser(cs);
CGContextRef bitmapContext =
::CGBitmapContextCreate(imageBits, mWidth, mHeight, BITS_PER_COMPONENT,
mRowBytes, cs, kCGImageAlphaPremultipliedFirst);
if (!bitmapContext) {
PR_Free(imageBits);
return NS_ERROR_FAILURE;
}
CGColorSpaceRef cs = ::CGColorSpaceCreateDeviceRGB(); // clear the bitmap context & draw mImage into it
StColorSpaceReleaser csReleaser(cs); CGRect drawRect = ::CGRectMake(0, 0, mWidth, mHeight);
CGContextRef bitmapContext; ::CGContextClearRect(bitmapContext, drawRect);
bitmapContext = ::CGBitmapContextCreate(mImageBits, mWidth, mHeight, ::CGContextDrawImage(bitmapContext, drawRect, mImage);
BITS_PER_COMPONENT, mRowBytes, cs, ::CGContextRelease(bitmapContext);
kCGImageAlphaNoneSkipFirst);
if (bitmapContext != NULL) // 'imageBits' now contains the image and (possibly) alpha bits for image.
{ // Now we need to separate them out.
CGRect drawRect = ::CGRectMake(0, 0, mWidth, mHeight);
// clear the bitmap context if (mAlphaDepth) {
::CGContextClearRect(bitmapContext, drawRect); // Only need to worry about alpha for mAlphaDepth == 1 or 8.
mAlphaBits = (PRUint8*) PR_Malloc(mHeight * mAlphaRowBytes *
::CGContextDrawImage(bitmapContext, drawRect, mImage); sizeof(PRUint8));
::CGContextRelease(bitmapContext); if (!mAlphaBits) {
PR_Free(imageBits);
return NS_ERROR_OUT_OF_MEMORY;
} }
} }
switch (mAlphaDepth)
{
case 8:
{
// Need to recreate mImageBits for mAlphaDepth == 8 only.
// See comments nsImageMac::Optimize().
PRUint8* tmp = imageBits;
mImageBits = (PRUint8*) PR_Malloc(mHeight * mRowBytes * sizeof(PRUint8));
if (!mImageBits) {
PR_Free(mAlphaBits);
mAlphaBits = nsnull;
PR_Free(imageBits);
return NS_ERROR_OUT_OF_MEMORY;
}
// split the alpha bits and image bits into their own structure
for (PRInt32 y = 0; y < mHeight; y++)
{
PRInt32 rowStart = mRowBytes * y;
PRInt32 alphaRowStart = mAlphaRowBytes * y;
for (PRInt32 x = 0; x < mWidth; x++)
{
PRUint8 alpha = *tmp++;
mAlphaBits[alphaRowStart + x] = alpha;
PRUint32 offset = rowStart + COMPS_PER_PIXEL * x;
mImageBits[offset + 1] = ((PRUint32) *tmp++) * 255 / alpha;
mImageBits[offset + 2] = ((PRUint32) *tmp++) * 255 / alpha;
mImageBits[offset + 3] = ((PRUint32) *tmp++) * 255 / alpha;
}
}
break;
}
// mImageBits still exists for mAlphaDepth == 1 or mAlphaDepth == 0,
// since the bits are owned by the CGImageRef. So the only thing to
// do is to recreate the alpha bits for mAlphaDepth == 1.
case 1:
{
// recreate the alpha bits structure
for (PRInt32 y = 0; y < mHeight; y++)
{
PRInt32 rowStart = mRowBytes * y;
PRUint8* alphaRow = mAlphaBits + mAlphaRowBytes * y;
for (PRInt32 x = 0; x < mWidth; x++)
{
if (imageBits[rowStart + COMPS_PER_PIXEL * x])
SetAlphaBit(alphaRow, x);
else
ClearAlphaBit(alphaRow, x);
}
}
}
case 0:
default:
break;
}
PR_Free(imageBits);
return NS_OK; return NS_OK;
} }
@ -782,6 +847,8 @@ nsImageMac::DrawTileQuickly(nsIRenderingContext &aContext,
PRUint32 bitmapRowBytes = tiledCols * mRowBytes; PRUint32 bitmapRowBytes = tiledCols * mRowBytes;
PRUint32 totalBytes = bitmapHeight * bitmapRowBytes; PRUint32 totalBytes = bitmapHeight * bitmapRowBytes;
PRUint8* bitmap = (PRUint8*) PR_Malloc(totalBytes); PRUint8* bitmap = (PRUint8*) PR_Malloc(totalBytes);
if (!bitmap)
return NS_ERROR_OUT_OF_MEMORY;
CGContextRef bitmapContext; CGContextRef bitmapContext;
bitmapContext = ::CGBitmapContextCreate(bitmap, bitmapWidth, bitmapHeight, bitmapContext = ::CGBitmapContextCreate(bitmap, bitmapWidth, bitmapHeight,
@ -904,12 +971,12 @@ NS_IMETHODIMP nsImageMac::DrawTile(nsIRenderingContext &aContext,
PRInt32 aPadX, PRInt32 aPadY, PRInt32 aPadX, PRInt32 aPadY,
const nsRect &aTileRect) const nsRect &aTileRect)
{ {
EnsureCachedImage(); nsresult rv = EnsureCachedImage();
NS_ENSURE_SUCCESS(rv, rv);
if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1) if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1)
return NS_OK; return NS_OK;
nsresult rv = NS_ERROR_FAILURE;
#ifdef USE_CGPATTERN_TILING #ifdef USE_CGPATTERN_TILING
if (nsRenderingContextMac::OnJaguar()) if (nsRenderingContextMac::OnJaguar())
{ {
@ -947,20 +1014,16 @@ NS_IMETHODIMP nsImageMac::DrawTile(nsIRenderingContext &aContext,
::CGContextFillRect(context, tileRect); ::CGContextFillRect(context, tileRect);
surface->EndQuartzDrawing(context); surface->EndQuartzDrawing(context);
rv = NS_OK; return NS_OK;
} }
else
#endif /* USE_CGPATTERN_TILING */ #endif /* USE_CGPATTERN_TILING */
{
// use the manual methods of tiling
if (!aPadX && !aPadY) // use the manual methods of tiling
rv = DrawTileQuickly(aContext, aSurface, aSXOffset, aSYOffset, aTileRect);
if (NS_FAILED(rv)) if (!aPadX && !aPadY) {
rv = SlowTile(aContext, aSurface, aSXOffset, aSYOffset, aPadX, aPadY, return DrawTileQuickly(aContext, aSurface, aSXOffset, aSYOffset, aTileRect);
aTileRect);
} }
return rv; return SlowTile(aContext, aSurface, aSXOffset, aSYOffset, aPadX, aPadY,
aTileRect);
} }

Просмотреть файл

@ -134,12 +134,18 @@ protected:
static PRBool RenderingToPrinter(nsIRenderingContext &aContext); static PRBool RenderingToPrinter(nsIRenderingContext &aContext);
// Recreate internal image structure from updated image bits. // Recreate internal image structure from updated image bits.
void EnsureCachedImage(); nsresult EnsureCachedImage();
// Return alpha bit at position 'x' in the row pointed to by 'rowptr'. // Get/Set/Clear alpha bit at position 'x' in the row pointed to by 'rowptr'.
inline PRUint8 GetBit(PRUint8* rowptr, PRUint32 x) { inline PRUint8 GetAlphaBit(PRUint8* rowptr, PRUint32 x) {
return (rowptr[x >> 3] & (1 << (7 - x & 0x7))); return (rowptr[x >> 3] & (1 << (7 - x & 0x7)));
} }
inline void SetAlphaBit(PRUint8* rowptr, PRUint32 x) {
rowptr[x >> 3] |= (1 << (7 - x & 0x7));
}
inline void ClearAlphaBit(PRUint8* rowptr, PRUint32 x) {
rowptr[x >> 3] &= ~(1 << (7 - x & 0x7));
}
// Takes ownership of the given image and bitmap. The CGImageRef is retained. // Takes ownership of the given image and bitmap. The CGImageRef is retained.
void AdoptImage(CGImageRef aNewImage, PRUint8* aNewBitamp); void AdoptImage(CGImageRef aNewImage, PRUint8* aNewBitamp);