Fixing Cairo image bugs. Not part of the build

git-svn-id: svn://10.0.0.236/trunk@172730 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
roc+%cs.cmu.edu
2005-04-26 05:42:22 +00:00
parent 2157d41187
commit c265dafad5
3 changed files with 139 additions and 130 deletions

View File

@@ -62,8 +62,7 @@ static void ARGBToThreeChannel(PRUint32* aARGB, PRUint8* aData) {
#endif
}
static void ThreeChannelToARGB(PRUint8* aData, PRUint32* aARGB) {
PRUint32 v = *aARGB & 0xFF000000;
static PRUint32 ThreeChannelToARGB(PRUint8* aData, PRUint8 aAlpha) {
PRUint8 r, g, b;
#if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
// BGR format; assume little-endian system
@@ -76,8 +75,7 @@ static void ThreeChannelToARGB(PRUint8* aData, PRUint32* aARGB) {
// RGB, red byte first
r = aData[0]; g = aData[1]; b = aData[2];
#endif
v |= (r << 16) | (g << 8) | b;
*aARGB = v;
return (aAlpha << 24) | (r << 16) | (g << 8) | b;
}
nsCairoImage::nsCairoImage()
@@ -89,8 +87,7 @@ nsCairoImage::nsCairoImage()
mImageSurfaceData(nsnull),
mImageSurfaceAlpha(nsnull),
mAlphaDepth(0),
mHadAnyAlphaValues(PR_FALSE),
mHadAnyPixelValues(PR_FALSE)
mHadAnyData(PR_FALSE)
{
}
@@ -234,6 +231,54 @@ nsCairoImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface,
return Draw(aContext, aSurface, 0, 0, mWidth, mHeight, aX, aY, aWidth, aHeight);
}
void
nsCairoImage::UpdateFromImageData()
{
if (!mImageSurfaceData)
return;
NS_ASSERTION(mAlphaDepth == 0 || mAlphaDepth == 1 || mAlphaDepth == 8,
"Bad alpha depth");
PRInt32 alphaIndex = 0;
PRInt32 bufIndex = 0;
for (PRInt32 row = 0; row < mHeight; ++row) {
for (PRInt32 col = 0; col < mWidth; ++col) {
PRUint8 alpha = 0xFF;
if (mAlphaDepth == 1) {
PRUint8 mask = 1 << (7 - (col&7));
if (!(mImageSurfaceAlpha[alphaIndex] & mask)) {
alpha = 0;
}
if (mask == 0x01) {
++alphaIndex;
}
} else if (mAlphaDepth == 8) {
alpha = mImageSurfaceAlpha[bufIndex];
}
mImageSurfaceBuf[bufIndex] = ThreeChannelToARGB(&mImageSurfaceData[bufIndex*3], alpha);
++bufIndex;
}
if (mAlphaDepth == 1) {
if (mWidth & 7) {
++alphaIndex;
}
}
}
if (PR_FALSE) { // Enabling this saves memory but can lead to pathological
// behaviour
nsMemory::Free(mImageSurfaceData);
mImageSurfaceData = nsnull;
if (mImageSurfaceAlpha) {
nsMemory::Free(mImageSurfaceAlpha);
mImageSurfaceAlpha = nsnull;
}
mHadAnyData = PR_TRUE;
}
}
NS_IMETHODIMP
nsCairoImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface,
PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight,
@@ -244,12 +289,15 @@ nsCairoImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface,
aSX, aSY, aSWidth, aSHeight,
aDX, aDY, aDWidth, aDHeight);
#endif
UpdateFromImageData();
nsCairoDrawingSurface *dstSurf = NS_STATIC_CAST(nsCairoDrawingSurface*, aSurface);
nsCairoRenderingContext *cairoContext = NS_STATIC_CAST(nsCairoRenderingContext*, &aContext);
cairo_t *dstCairo = cairoContext->GetCairo();
cairo_translate(dstCairo, aDX, aDY);
cairo_pattern_t *pat = cairo_pattern_create_for_surface (mImageSurface);
cairo_matrix_t* matrix = cairo_matrix_create();
if (matrix) {
@@ -262,8 +310,9 @@ nsCairoImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface,
cairo_pattern_destroy (pat);
cairo_new_path(dstCairo);
cairo_rectangle (dstCairo, aDX, aDY, aDWidth, aDHeight);
cairo_rectangle (dstCairo, 0, 0, aDWidth, aDHeight);
cairo_fill (dstCairo);
cairo_translate(dstCairo, -aDX, -aDY);
return NS_OK;
}
@@ -275,6 +324,8 @@ nsCairoImage::DrawTile(nsIRenderingContext &aContext,
PRInt32 aPadX, PRInt32 aPadY,
const nsRect &aTileRect)
{
UpdateFromImageData();
if (aPadX || aPadY)
fprintf (stderr, "Warning: nsCairoImage::DrawTile given padX(%d)/padY(%d), ignoring\n", aPadX, aPadY);
@@ -283,32 +334,26 @@ nsCairoImage::DrawTile(nsIRenderingContext &aContext,
cairo_t *dstCairo = cairoContext->GetCairo();
cairo_save(dstCairo);
// just in case this needs setting
cairo_set_target_surface(dstCairo, dstSurf->GetCairoSurface());
// coords are absolute again
cairo_identity_matrix(dstCairo);
cairo_translate(dstCairo, aTileRect.x, aTileRect.y);
cairo_pattern_t *pat = cairo_pattern_create_for_surface (mImageSurface);
cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
if (aSXOffset != 0 || aSYOffset != 0) {
cairo_matrix_t* matrix = cairo_matrix_create();
if (matrix) {
cairo_matrix_set_affine(matrix, 1, 0, 0, 1, aSXOffset, aSYOffset);
cairo_pattern_set_matrix(pat, matrix);
cairo_matrix_destroy(matrix);
}
cairo_matrix_t* matrix = cairo_matrix_create();
if (matrix) {
nsCOMPtr<nsIDeviceContext> dc;
aContext.GetDeviceContext(*getter_AddRefs(dc));
float app2dev = dc->AppUnitsToDevUnits();
cairo_matrix_set_affine(matrix, app2dev, 0, 0, app2dev, aSXOffset, aSYOffset);
cairo_pattern_set_matrix(pat, matrix);
cairo_matrix_destroy(matrix);
}
cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT);
cairo_set_pattern (dstCairo, pat);
cairo_pattern_destroy (pat);
cairo_new_path(dstCairo);
cairo_rectangle (dstCairo, aTileRect.x, aTileRect.y, aTileRect.width, aTileRect.height);
cairo_rectangle (dstCairo, 0, 0, aTileRect.width, aTileRect.height);
cairo_fill (dstCairo);
cairo_restore(dstCairo);
cairo_translate(dstCairo, -aTileRect.x, -aTileRect.y);
return NS_OK;
}
@@ -316,6 +361,8 @@ nsCairoImage::DrawTile(nsIRenderingContext &aContext,
NS_IMETHODIMP
nsCairoImage::DrawToImage(nsIImage* aDstImage, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight)
{
UpdateFromImageData();
nsCairoImage *dstCairoImage = NS_STATIC_CAST(nsCairoImage*, aDstImage);
cairo_t *dstCairo = cairo_create ();
@@ -356,106 +403,71 @@ nsCairoImage::GetBitInfo()
NS_IMETHODIMP
nsCairoImage::LockImagePixels(PRBool aMaskPixels)
{
PRInt32 count = mWidth*mHeight;
if (aMaskPixels) {
NS_ASSERTION(!mImageSurfaceAlpha, "already locked alphas");
if (mImageSurfaceData) {
return NS_OK;
}
if (mAlphaDepth > 0) {
NS_ASSERTION(mAlphaDepth == 1 || mAlphaDepth == 8,
"Bad alpha depth");
PRInt32 size = mHeight*(mAlphaDepth == 1 ? ((mWidth+7)/8) : mWidth);
PRUint32 size = mHeight*(mAlphaDepth == 1 ? ((mWidth+7)/8) : mWidth);
mImageSurfaceAlpha = (PRUint8*)nsMemory::Alloc(size);
if (!mImageSurfaceAlpha)
return NS_ERROR_OUT_OF_MEMORY;
if (mHadAnyAlphaValues) {
// fill from existing ARGB buffer
if (mAlphaDepth == 8) {
for (PRInt32 i = 0; i < count; ++i) {
mImageSurfaceAlpha[i] = (PRUint8)(mImageSurfaceBuf[i] >> 24);
}
} else {
PRInt32 i = 0;
for (PRInt32 row = 0; row < mHeight; ++row) {
PRUint8 alphaBits = 0;
for (PRInt32 col = 0; col < mWidth; ++col) {
PRUint8 mask = 1 << (7 - (col&7));
if (mImageSurfaceBuf[row*mWidth + col] & 0xFF000000) {
alphaBits |= mask;
}
if (mask == 0x01) {
// This mask byte is complete, write it back
mImageSurfaceAlpha[i] = alphaBits;
alphaBits = 0;
++i;
}
}
PRUint32 size = mWidth * mHeight * 3;
mImageSurfaceData = (PRUint8*)nsMemory::Alloc(size);
if (!mImageSurfaceData)
return NS_ERROR_OUT_OF_MEMORY;
PRInt32 count = mWidth*mHeight;
if (mAlphaDepth > 0 && mHadAnyData) {
PRInt32 size = mHeight*(mAlphaDepth == 1 ? ((mWidth+7)/8) : mWidth);
mImageSurfaceAlpha = (PRUint8*)nsMemory::Alloc(size);
// fill from existing ARGB buffer
if (mAlphaDepth == 8) {
for (PRInt32 i = 0; i < count; ++i) {
mImageSurfaceAlpha[i] = (PRUint8)(mImageSurfaceBuf[i] >> 24);
}
} else {
PRInt32 i = 0;
for (PRInt32 row = 0; row < mHeight; ++row) {
PRUint8 alphaBits = 0;
for (PRInt32 col = 0; col < mWidth; ++col) {
PRUint8 mask = 1 << (7 - (col&7));
if (mImageSurfaceBuf[row*mWidth + col] & 0xFF000000) {
alphaBits |= mask;
}
if (mWidth & 7) {
// write back the incomplete alpha mask
if (mask == 0x01) {
// This mask byte is complete, write it back
mImageSurfaceAlpha[i] = alphaBits;
alphaBits = 0;
++i;
}
}
}
}
} else {
NS_ASSERTION(!mImageSurfaceData, "already locked pixels");
mImageSurfaceData = (PRUint8*)nsMemory::Alloc(mWidth * mHeight * 3);
if (!mImageSurfaceData)
return NS_ERROR_OUT_OF_MEMORY;
if (mHadAnyPixelValues) {
// fill from existing ARGB buffer
for (PRInt32 i = 0; i < count; ++i) {
ARGBToThreeChannel(&mImageSurfaceBuf[i], &mImageSurfaceData[i*3]);
if (mWidth & 7) {
// write back the incomplete alpha mask
mImageSurfaceAlpha[i] = alphaBits;
++i;
}
}
}
}
if (mHadAnyData) {
// fill from existing ARGB buffer
for (PRInt32 i = 0; i < count; ++i) {
ARGBToThreeChannel(&mImageSurfaceBuf[i], &mImageSurfaceData[i*3]);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCairoImage::UnlockImagePixels(PRBool aMaskPixels)
{
PRInt32 count = mWidth*mHeight;
if (aMaskPixels) {
NS_ASSERTION(mImageSurfaceAlpha, "alpha values not locked");
NS_ASSERTION(mAlphaDepth == 1 || mAlphaDepth == 8,
"Bad alpha depth");
if (mAlphaDepth == 8) {
for (PRInt32 i = 0; i < count; ++i) {
PRInt32 v = mImageSurfaceBuf[i] & 0xFFFFFF;
v |= mImageSurfaceAlpha[i] << 24;
mImageSurfaceBuf[i] = v;
}
} else {
PRInt32 i = 0;
for (PRInt32 row = 0; row < mHeight; ++row) {
for (PRInt32 col = 0; col < mWidth; ++col) {
PRInt32 index = row*mWidth + col;
PRInt32 v = mImageSurfaceBuf[index] & 0xFFFFFF;
PRUint8 mask = 1 << (7 - (col&7));
if (mImageSurfaceAlpha[i] & mask) {
v |= 0xFF000000;
}
mImageSurfaceBuf[index] = v;
if (mask == 0x01) {
++i;
}
}
if (mWidth & 7) {
++i;
}
}
}
nsMemory::Free(mImageSurfaceAlpha);
mImageSurfaceAlpha = nsnull;
mHadAnyAlphaValues = PR_TRUE;
} else {
NS_ASSERTION(mImageSurfaceData, "had not locked pixels");
for (PRInt32 i = 0; i < count; ++i) {
ThreeChannelToARGB(&mImageSurfaceData[i*3], &mImageSurfaceBuf[i]);
}
nsMemory::Free(mImageSurfaceData);
mImageSurfaceData = nsnull;
mHadAnyPixelValues = PR_TRUE;
}
return NS_OK;
}

View File

@@ -88,6 +88,8 @@ public:
NS_IMETHOD LockImagePixels(PRBool aMaskPixels);
NS_IMETHOD UnlockImagePixels(PRBool aMaskPixels);
void UpdateFromImageData();
protected:
PRInt32 mWidth;
PRInt32 mHeight;
@@ -97,13 +99,13 @@ protected:
cairo_format_t mCairoFormat;
// Where the mImageSurface data lives (in the mCairoFormat)
PRUint32* mImageSurfaceBuf;
// Where gfxIImageFrame data lives temporarily during LockImagePixels
// Where gfxIImageFrame data lives while the image data is dirty
// (from LockImagePixels(Alpha) to the next paint)
PRUint8* mImageSurfaceData;
PRUint8* mImageSurfaceAlpha;
PRUint8 mAlphaDepth;
PRPackedBool mHadAnyAlphaValues;
PRPackedBool mHadAnyPixelValues;
PRPackedBool mHadAnyData;
};
#endif // NSCAIROIMAGE__H__

View File

@@ -205,6 +205,7 @@ nsCairoRenderingContext::PushTranslation(PushedTranslation* aState)
{
// XXX this is slow!
PushState();
return NS_OK;
}
NS_IMETHODIMP
@@ -212,6 +213,7 @@ nsCairoRenderingContext::PopTranslation(PushedTranslation* aState)
{
// XXX this is slow!
PopState();
return NS_OK;
}
NS_IMETHODIMP
@@ -896,18 +898,6 @@ nsCairoRenderingContext::DrawTile(imgIContainer *aImage,
nscoord aXOffset, nscoord aYOffset,
const nsRect * aTargetRect)
{
// from nsRenderingContextImpl.cpp
nsRect dr(*aTargetRect);
double x = dr.x, y = dr.y, w = dr.width, h = dr.height;
cairo_transform_point(mCairo, &x, &y);
cairo_transform_distance(mCairo, &w, &h);
dr.x = (int) x; dr.y = (int) y; dr.width = (int) w; dr.height = (int) h;
x = aXOffset; y = aYOffset;
cairo_transform_point(mCairo, &x, &y);
aXOffset = (int) x; aYOffset = (int) y;
nscoord width, height;
aImage->GetWidth(&width);
aImage->GetHeight(&height);
@@ -915,9 +905,6 @@ nsCairoRenderingContext::DrawTile(imgIContainer *aImage,
if (width == 0 || height == 0)
return PR_FALSE;
nscoord xOffset = (dr.x - aXOffset) % width;
nscoord yOffset = (dr.y - aYOffset) % height;
nsCOMPtr<gfxIImageFrame> iframe;
aImage->GetCurrentFrame(getter_AddRefs(iframe));
if (!iframe) return NS_ERROR_FAILURE;
@@ -925,16 +912,24 @@ nsCairoRenderingContext::DrawTile(imgIContainer *aImage,
nsCOMPtr<nsIImage> img(do_GetInterface(iframe));
if (!img) return NS_ERROR_FAILURE;
/* bug 113561 - frame can be smaller than container */
// For Bug 87819
// iframe may want image to start at different position, so adjust
nsRect iframeRect;
iframe->GetRect(iframeRect);
// offsets are always in appunits, they have
// nothing to do with the current transform
float app2dev;
app2dev = mDeviceContext->AppUnitsToDevUnits();
nsPoint s;
s.x = NSToIntRound(app2dev*aXOffset);
s.y = NSToIntRound(app2dev*aYOffset);
PRInt32 padx = width - iframeRect.width;
PRInt32 pady = height - iframeRect.height;
return img->DrawTile(*this, mDrawingSurface,
xOffset - iframeRect.x, yOffset - iframeRect.y,
padx, pady,
dr);
return img->DrawTile(*this, mDrawingSurface, s.x - iframeRect.x, s.y - iframeRect.y,
padx, pady, *aTargetRect);
}
//