/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape 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/NPL/ * * 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): * Peter Hartshorn * Stuart Parmenter * Tim Rowley -- 8bit alpha compositing * * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, 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 NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsImageXlib.h" #include "nsDrawingSurfaceXlib.h" #include "nsRenderingContextXlib.h" #include "xlibrgb.h" #include "prlog.h" #include "nsRect.h" #include "drawers.h" #include "imgScaler.h" #define IsFlagSet(a,b) ((a) & (b)) #ifdef PR_LOGGING static PRLogModuleInfo *ImageXlibLM = PR_NewLogModule("ImageXlib"); #endif /* PR_LOGGING */ /* XXX we are simply creating a GC and setting its function to Copy. we shouldn't be doing this every time this method is called. this creates way more trips to the server than we should be doing so we are creating a static one. */ static GC s1bitGC = 0; static GC sXbitGC = 0; XlibRgbHandle *nsImageXlib::mXlibRgbHandle = nsnull; Display *nsImageXlib::mDisplay = nsnull; nsImageXlib::nsImageXlib() : mImageBits(nsnull) , mAlphaBits(nsnull) , mWidth(0) , mHeight(0) , mDepth(0) , mRowBytes(0) , mSizeImage(0) , mNumBytesPixel(0) , mImagePixmap(nsnull) , mAlphaPixmap(nsnull) , mAlphaDepth(0) , mAlphaRowBytes(0) , mAlphaValid(PR_FALSE) , mIsSpacer(PR_TRUE) , mGC(nsnull) , mPendingUpdate(PR_FALSE) , mDecodedX1(PR_INT32_MAX) , mDecodedY1(PR_INT32_MAX) , mDecodedX2(0) , mDecodedY2(0) { PR_LOG(ImageXlibLM, PR_LOG_DEBUG, ("nsImageXlib::nsImageXlib()\n")); if (!mXlibRgbHandle) { mXlibRgbHandle = xxlib_find_handle(XXLIBRGB_DEFAULT_HANDLE); mDisplay = xxlib_rgb_get_display(mXlibRgbHandle); } if (!mXlibRgbHandle || !mDisplay) abort(); } nsImageXlib::~nsImageXlib() { PR_LOG(ImageXlibLM, PR_LOG_DEBUG,("nsImageXlib::nsImageXlib()\n")); if (nsnull != mImageBits) { delete[] mImageBits; mImageBits = nsnull; } if (nsnull != mAlphaBits) { delete[] mAlphaBits; mAlphaBits = nsnull; if (mAlphaPixmap != nsnull) { // The display cant be null. It gets fetched from the drawing // surface used to create the pixmap. It gets assigned once // in Draw() NS_ASSERTION(nsnull != mDisplay,"display is null."); #ifdef XLIB_PIXMAP_DEBUG printf("XFreePixmap(display = %p)\n",mDisplay); #endif XFreePixmap(mDisplay, mAlphaPixmap); } } if (mImagePixmap != 0) { NS_ASSERTION(nsnull != mDisplay,"display is null."); #ifdef XLIB_PIXMAP_DEBUG printf("XFreePixmap(display = %p)\n",mDisplay); #endif XFreePixmap(mDisplay, mImagePixmap); } if(mGC) { XFreeGC(mDisplay, mGC); mGC=nsnull; } if(sXbitGC && mDisplay) // Sometimes mDisplay is null, let orhers free { XFreeGC(mDisplay, sXbitGC); sXbitGC=nsnull; } if(s1bitGC && mDisplay) // Sometimes mDisplay is null, let orhers free { XFreeGC(mDisplay, s1bitGC); s1bitGC=nsnull; } } NS_IMPL_ISUPPORTS1(nsImageXlib, nsIImage) nsresult nsImageXlib::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequirements aMaskRequirements) { // gfxImageFrame makes sure nsImageXlib::Init gets called only once if ((aWidth == 0) || (aHeight == 0)) return NS_ERROR_FAILURE; if (24 == aDepth) { mNumBytesPixel = 3; } else { NS_ASSERTION(PR_FALSE, "unexpected image depth"); return NS_ERROR_UNEXPECTED; } mWidth = aWidth; mHeight = aHeight; mDepth = aDepth; // Create the memory for the image ComputeMetrics(); mImageBits = (PRUint8*)new PRUint8[mSizeImage]; switch(aMaskRequirements) { case nsMaskRequirements_kNeeds1Bit: mAlphaRowBytes = (aWidth + 7) / 8; mAlphaDepth = 1; // 32-bit align each row mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3; mAlphaBits = new unsigned char[mAlphaRowBytes * aHeight]; break; case nsMaskRequirements_kNeeds8Bit: mAlphaRowBytes = aWidth; mAlphaDepth = 8; // 32-bit align each row mAlphaRowBytes = (mAlphaRowBytes + 3) & ~0x3; mAlphaBits = new unsigned char[mAlphaRowBytes * aHeight]; break; default: break; // avoid compiler warning } return NS_OK; } //--------------------------------------------------------------------- PRInt32 nsImageXlib::GetHeight() { return mHeight; } PRInt32 nsImageXlib::GetWidth() { return mWidth; } PRUint8 *nsImageXlib::GetBits() { return mImageBits; } void *nsImageXlib::GetBitInfo() { return nsnull; } PRInt32 nsImageXlib::GetLineStride() { return mRowBytes; } nsColorMap *nsImageXlib::GetColorMap() { return nsnull; } PRUint8 *nsImageXlib::GetAlphaBits() { return mAlphaBits; } PRInt32 nsImageXlib::GetAlphaLineStride() { return mAlphaRowBytes; } //----------------------------------------------------------------------- // Set up the palette to the passed in color array, RGB only in this array void nsImageXlib::ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect) { mPendingUpdate = PR_TRUE; mUpdateRegion.Or(mUpdateRegion, *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(); } void nsImageXlib::UpdateCachedImage() { nsRegionRectIterator ri(mUpdateRegion); const nsRect *rect; while (rect = ri.Next()) { // fprintf(stderr, "ImageUpdated %p x,y=(%d %d) width,height=(%d %d)\n", // this, rect->x, rect->y, rect->width, rect->height); unsigned bottom, left, right; bottom = rect->y + rect->height; left = rect->x; right = left + rect->width; // check if the image has an all-opaque 8-bit alpha mask if ((mAlphaDepth==8) && !mAlphaValid) { for (unsigned y=rect->y; (y> (left & 0x7); PRUint8 rightmask = 0xff << (7 - ((right-1) & 0x7)); // byte where the first/last bits of the update region are located PRUint32 leftindex = left >> 3; PRUint32 rightindex = (right-1) >> 3; // first/last bits in the same byte - combine mask into leftmask // and fill rightmask so we don't try using it if (leftindex == rightindex) { leftmask &= rightmask; rightmask = 0xff; } // check the leading bits if (leftmask != 0xff) { PRUint8 *ptr = mAlphaBits + mAlphaRowBytes * rect->y + leftindex; for (unsigned y=rect->y; yy + rightindex; for (unsigned y=rect->y; yy; (yx, rect->y, rect->width, rect->height, XLIB_RGB_DITHER_MAX, mImageBits + mRowBytes * rect->y + 3 * rect->x, mRowBytes, rect->x, rect->y); } } mUpdateRegion.SetEmpty(); mPendingUpdate = PR_FALSE; mFlags = nsImageUpdateFlags_kBitsChanged; // this should be 0'd out by Draw() } NS_IMETHODIMP nsImageXlib::DrawScaled(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) { PRInt32 origSHeight = aSHeight, origDHeight = aDHeight; PRInt32 origSWidth = aSWidth, origDWidth = aDWidth; if (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; // 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) || (aSWidth <= 0 || aSHeight <= 0)) return NS_OK; nsIDrawingSurfaceXlib *drawing = NS_STATIC_CAST(nsIDrawingSurfaceXlib *, aSurface); if (mAlphaDepth == 1) CreateAlphaBitmap(mWidth, mHeight); if ((mAlphaDepth == 8) && mAlphaValid) { DrawComposited(aContext, aSurface, aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight); return NS_OK; } #ifdef HAVE_XIE /* XIE seriosly loses scaling images with alpha */ if (!mAlphaDepth) { /* Draw with XIE */ PRBool succeeded = PR_FALSE; xGC *xiegc = ((nsRenderingContextXlib&)aContext).GetGC(); Drawable drawable; drawing->GetDrawable(drawable); succeeded = DrawScaledImageXIE(mDisplay, drawable, *xiegc, mImagePixmap, mWidth, mHeight, aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight); xiegc->Release(); if (succeeded) return NS_OK; } #endif /* the good scaling way, right from GTK */ GC gc = 0; Pixmap pixmap = 0; if (mAlphaDepth==1) { PRUint32 scaledRowBytes = (aDWidth+7)>>3; // round to next byte PRUint8 *scaledAlpha = (PRUint8 *)nsMemory::Alloc(aDHeight*scaledRowBytes); // code below attempts to draw the image without the mask if mask // creation fails for some reason. thus no easy-out "return" if (scaledAlpha) { memset(scaledAlpha, 0, aDHeight*scaledRowBytes); RectStretch(mWidth, mHeight, origDWidth, origDHeight, aDX, aDY, aDX + aDWidth - 1, aDY + aDHeight - 1, mAlphaBits, mAlphaRowBytes, scaledAlpha, scaledRowBytes, 1); pixmap = XCreatePixmap(mDisplay, DefaultRootWindow(mDisplay), aDWidth, aDHeight, 1); XImage *ximage = 0; if (pixmap) { ximage = XCreateImage(mDisplay, xxlib_rgb_get_visual(mXlibRgbHandle), 1, XYPixmap, 0, (char *)scaledAlpha, aDWidth, aDHeight, 8, scaledRowBytes); } if (ximage) { ximage->bits_per_pixel=1; ximage->bitmap_bit_order=MSBFirst; ximage->byte_order = MSBFirst; GC tmpGC; XGCValues gcv; memset(&gcv, 0, sizeof(XGCValues)); gcv.function = GXcopy; tmpGC = XCreateGC(mDisplay, pixmap, GCFunction, &gcv); if (tmpGC) { XPutImage(mDisplay, pixmap, tmpGC, ximage, 0, 0, 0, 0, aDWidth, aDHeight); XFreeGC(mDisplay, tmpGC); } else { // can't write into the clip mask - destroy so we don't use it if (pixmap) XFreePixmap(mDisplay, pixmap); pixmap = 0; } ximage->data = 0; XDestroyImage(ximage); } nsMemory::Free(scaledAlpha); } } xGC *imageGC = nsnull; if (pixmap) { XGCValues values; memset(&values, 0, sizeof(XGCValues)); values.clip_x_origin = aDX; values.clip_y_origin = aDY; values.clip_mask = pixmap; Drawable drawable; drawing->GetDrawable(drawable); gc = XCreateGC(mDisplay, drawable, GCClipXOrigin | GCClipYOrigin | GCClipMask, &values); } else { imageGC = ((nsRenderingContextXlib&)aContext).GetGC(); gc = *imageGC; } PRUint8 *scaledRGB = (PRUint8 *)nsMemory::Alloc(3*aDWidth*aDHeight); if (scaledRGB && gc) { RectStretch(mWidth, mHeight, origDWidth, origDHeight, aDX, aDY, aDX + aDWidth - 1, aDY + aDHeight - 1, mImageBits, mRowBytes, scaledRGB, 3*aDWidth, 24); Drawable drawable; drawing->GetDrawable(drawable); xxlib_draw_rgb_image(mXlibRgbHandle, drawable, gc, aDX, aDY, aDWidth, aDHeight, XLIB_RGB_DITHER_MAX, scaledRGB, 3*aDWidth); nsMemory::Free(scaledRGB); } if (imageGC) imageGC->Release(); else if (gc) XFreeGC(mDisplay, gc); if (pixmap) XFreePixmap(mDisplay, pixmap); mFlags = 0; return NS_OK; } // Draw the bitmap, this method has a source and destination coordinates NS_IMETHODIMP nsImageXlib::Draw(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) { if (aSurface == nsnull) return NS_ERROR_FAILURE; if (mPendingUpdate) UpdateCachedImage(); if ((mAlphaDepth == 1) && mIsSpacer) return NS_OK; if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1) return NS_OK; if (aSWidth != aDWidth || aSHeight != aDHeight) { return DrawScaled(aContext, aSurface, aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight); } if (aSWidth <= 0 || aDWidth <= 0 || aSHeight <= 0 || aDHeight <= 0) { NS_ASSERTION(aSWidth > 0 && aDWidth > 0 && aSHeight > 0 && aDHeight > 0, "You can't draw an image with a 0 width or height!"); return NS_OK; } // limit the size of the blit to the amount of the image read in PRInt32 j = aSX + aSWidth; PRInt32 z; if (j > mDecodedX2) { z = j - mDecodedX2; aDWidth -= z; aSWidth -= z; } if (aSX < mDecodedX1) { aDX += mDecodedX1 - aSX; aSX = mDecodedX1; } j = aSY + aSHeight; if (j > mDecodedY2) { z = j - mDecodedY2; aDHeight -= z; aSHeight -= z; } if (aSY < mDecodedY1) { aDY += mDecodedY1 - aSY; aSY = mDecodedY1; } if (aDWidth <= 0 || aDHeight <= 0 || aSWidth <= 0 || aSHeight <= 0) return NS_OK; if ((mAlphaDepth == 8) && mAlphaValid) { DrawComposited(aContext, aSurface, aSX, aSY, aSWidth, aSHeight, aDX, aDY, aSWidth, aSHeight); return NS_OK; } nsIDrawingSurfaceXlib *drawing = NS_STATIC_CAST(nsIDrawingSurfaceXlib *, aSurface); if (mAlphaDepth == 1) CreateAlphaBitmap(mWidth, mHeight); GC copyGC; xGC *gc = ((nsRenderingContextXlib&)aContext).GetGC(); if (mAlphaPixmap) { if (mGC) { /* reuse GC */ copyGC = mGC; SetupGCForAlpha(copyGC, aDX - aSX, aDY - aSY); } else { /* make a new one */ /* this repeats things done in SetupGCForAlpha */ XGCValues xvalues; memset(&xvalues, 0, sizeof(XGCValues)); unsigned long xvalues_mask = 0; xvalues.clip_x_origin = aDX - aSX; xvalues.clip_y_origin = aDY - aSY; if (IsFlagSet(nsImageUpdateFlags_kBitsChanged, mFlags)) { xvalues_mask = GCClipXOrigin | GCClipYOrigin | GCClipMask; xvalues.clip_mask = mAlphaPixmap; } Drawable drawable; drawing->GetDrawable(drawable); mGC = XCreateGC(mDisplay, drawable, xvalues_mask , &xvalues); copyGC = mGC; } } else { /* !mAlphaPixmap */ copyGC = *gc; } Drawable drawable; drawing->GetDrawable(drawable); XCopyArea(mDisplay, mImagePixmap, drawable, copyGC, aSX, aSY, aSWidth, aSHeight, aDX, aDY); gc->Release(); mFlags = 0; return NS_OK; } // ----------------------------------------------------------------- // 8-bit alpha composite drawing static unsigned findIndex32(unsigned mask) { switch (mask) { case 0xff: return 3; case 0xff00: return 2; case 0xff0000: return 1; default: return 0; } } static unsigned findIndex24(unsigned mask) { switch(mask) { case 0xff: return 2; case 0xff00: return 1; default: return 0; } } // 32-bit (888) truecolor convert/composite function void nsImageXlib::DrawComposited32(PRBool isLSB, PRBool flipBytes, PRUint8 *imageOrigin, PRUint32 imageStride, PRUint8 *alphaOrigin, PRUint32 alphaStride, unsigned width, unsigned height, XImage *ximage, unsigned char *readData) { Visual *visual = xxlib_rgb_get_visual(mXlibRgbHandle); unsigned redIndex = findIndex32(visual->red_mask); unsigned greenIndex = findIndex32(visual->green_mask); unsigned blueIndex = findIndex32(visual->blue_mask); if (flipBytes^isLSB) { redIndex = 3-redIndex; greenIndex = 3-greenIndex; blueIndex = 3-blueIndex; } for (unsigned y=0; ydata +y*ximage->bytes_per_line; unsigned char *targetRow = readData +3*(y*ximage->width); unsigned char *imageRow = imageOrigin +y*imageStride; unsigned char *alphaRow = alphaOrigin +y*alphaStride; for (unsigned i=0; ired_mask); unsigned greenIndex = findIndex24(visual->green_mask); unsigned blueIndex = findIndex24(visual->blue_mask); if (flipBytes^isLSB) { redIndex = 2-redIndex; greenIndex = 2-greenIndex; blueIndex = 2-blueIndex; } for (unsigned y=0; ydata +y*ximage->bytes_per_line; unsigned char *targetRow = readData +3*(y*ximage->width); unsigned char *imageRow = imageOrigin +y*imageStride; unsigned char *alphaRow = alphaOrigin +y*alphaStride; for (unsigned i=0; ired_mask) == 5) ? scaled5 : scaled6; unsigned *greenScale = (xxlib_get_prec_from_mask(visual->green_mask) == 5) ? scaled5 : scaled6; unsigned *blueScale = (xxlib_get_prec_from_mask(visual->blue_mask) == 5) ? scaled5 : scaled6; unsigned long redShift = xxlib_get_shift_from_mask(visual->red_mask); unsigned long greenShift = xxlib_get_shift_from_mask(visual->green_mask); unsigned long blueShift = xxlib_get_shift_from_mask(visual->blue_mask); for (unsigned y=0; ydata +y*ximage->bytes_per_line; unsigned char *targetRow = readData +3*(y*ximage->width); unsigned char *imageRow = imageOrigin +y*imageStride; unsigned char *alphaRow = alphaOrigin +y*alphaStride; for (unsigned i=0; ired_mask) >> redShift], imageRow[0], alpha); MOZ_BLEND(targetRow[1], greenScale[(pix&visual->green_mask) >> greenShift], imageRow[1], alpha); MOZ_BLEND(targetRow[2], blueScale[(pix&visual->blue_mask) >> blueShift], imageRow[2], alpha); } } } // Generic convert/composite function void nsImageXlib::DrawCompositedGeneral(PRBool isLSB, PRBool flipBytes, PRUint8 *imageOrigin, PRUint32 imageStride, PRUint8 *alphaOrigin, PRUint32 alphaStride, unsigned width, unsigned height, XImage *ximage, unsigned char *readData) { Visual *visual = xxlib_rgb_get_visual(mXlibRgbHandle); unsigned char *target = readData; // flip bytes if (flipBytes && (ximage->bits_per_pixel>=16)) { for (int row=0; rowheight; row++) { unsigned char *ptr = (unsigned char*)ximage->data + row*ximage->bytes_per_line; if (ximage->bits_per_pixel==24) { // Aurgh.... for (int col=0; colbytes_per_line; col+=(ximage->bits_per_pixel/8)) { unsigned char tmp; tmp = *ptr; *ptr = *(ptr+2); *(ptr+2) = tmp; ptr+=3; } continue; } for (int col=0; colbytes_per_line; col+=(ximage->bits_per_pixel/8)) { unsigned char tmp; switch (ximage->bits_per_pixel) { case 16: tmp = *ptr; *ptr = *(ptr+1); *(ptr+1) = tmp; ptr+=2; break; case 32: tmp = *ptr; *ptr = *(ptr+3); *(ptr+3) = tmp; tmp = *(ptr+1); *(ptr+1) = *(ptr+2); *(ptr+2) = tmp; ptr+=4; break; } } } } unsigned redScale = 8 - xxlib_get_prec_from_mask(visual->red_mask); unsigned greenScale = 8 - xxlib_get_prec_from_mask(visual->green_mask); unsigned blueScale = 8 - xxlib_get_prec_from_mask(visual->blue_mask); unsigned redFill = 0xff >> xxlib_get_prec_from_mask(visual->red_mask); unsigned greenFill = 0xff >> xxlib_get_prec_from_mask(visual->green_mask); unsigned blueFill = 0xff >> xxlib_get_prec_from_mask(visual->blue_mask); unsigned long redShift = xxlib_get_shift_from_mask(visual->red_mask); unsigned long greenShift = xxlib_get_shift_from_mask(visual->green_mask); unsigned long blueShift = xxlib_get_shift_from_mask(visual->blue_mask); for (int row=0; rowheight; row++) { unsigned char *ptr = (unsigned char *)ximage->data + row*ximage->bytes_per_line; for (int col=0; colwidth; col++) { unsigned pix = 0; switch (ximage->bits_per_pixel) { case 1: pix = (*ptr>>(col%8))&1; if ((col%8)==7) ptr++; break; case 4: pix = (col&1)?(*ptr>>4):(*ptr&0xf); if (col&1) ptr++; break; case 8: pix = *ptr++; break; case 16: pix = *((short *)ptr); ptr+=2; break; case 24: if (isLSB) pix = (*(ptr+2)<<16) | (*(ptr+1)<<8) | *ptr; else pix = (*ptr<<16) | (*(ptr+1)<<8) | *(ptr+2); ptr+=3; break; case 32: pix = *((unsigned *)ptr); ptr+=4; break; } *target++ = redFill|((pix&visual->red_mask) >> redShift)<green_mask) >> greenShift)<blue_mask) >> blueShift)<GetDrawable(drawable); Visual *visual = xxlib_rgb_get_visual(mXlibRgbHandle); // I hate clipping... too! PRUint32 surfaceWidth, surfaceHeight; drawing->GetDimensions(&surfaceWidth, &surfaceHeight); int readX, readY; unsigned readWidth, readHeight, destX, destY; if ((aDY >= (int)surfaceHeight) || (aDX >= (int)surfaceWidth) || (aDY + aDHeight <= 0) || (aDX + aDWidth <= 0)) { // This should never happen if the layout engine is sane, // as it means we're trying to draw an image which is outside // the drawing surface. Bulletproof gfx for now... return; } if (aDX < 0) { readX = 0; readWidth = aDWidth + aDX; destX = aSX - aDX; } else { readX = aDX; readWidth = aDWidth; destX = aSX; } if (aDY < 0) { readY = 0; readHeight = aDHeight + aDY; destY = aSY - aDY; } else { readY = aDY; readHeight = aDHeight; destY = aSY; } if (readX+readWidth > surfaceWidth) readWidth = surfaceWidth-readX; if (readY+readHeight > surfaceHeight) readHeight = surfaceHeight-readY; if ((readHeight <= 0) || (readWidth <= 0)) return; // fprintf(stderr, "aX=%d aY=%d, aWidth=%u aHeight=%u\n", aX, aY, aWidth, aHeight); // fprintf(stderr, "surfaceWidth=%u surfaceHeight=%u\n", surfaceWidth, surfaceHeight); // fprintf(stderr, "readX=%u readY=%u readWidth=%u readHeight=%u destX=%u destY=%u\n\n", // readX, readY, readWidth, readHeight, destX, destY); XImage *ximage = XGetImage(mDisplay, drawable, readX, readY, readWidth, readHeight, AllPlanes, ZPixmap); NS_ASSERTION((ximage != NULL), "XGetImage() failed"); if (!ximage) return; unsigned char *readData = (unsigned char *)nsMemory::Alloc(3*readWidth*readHeight); PRUint8 *scaledImage = 0; PRUint8 *scaledAlpha = 0; PRUint8 *imageOrigin, *alphaOrigin; PRUint32 imageStride, alphaStride; /* image needs to be scaled */ if ((aSWidth!=aDWidth) || (aSHeight!=aDHeight)) { PRUint32 x1, y1, x2, y2; x1 = (destX*aSWidth)/aDWidth; y1 = (destY*aSHeight)/aDHeight; x2 = ((destX+readWidth)*aSWidth)/aDWidth; y2 = ((destY+readHeight)*aSHeight)/aDHeight; scaledImage = (PRUint8 *)nsMemory::Alloc(3*aDWidth*aDHeight); scaledAlpha = (PRUint8 *)nsMemory::Alloc(aDWidth*aDHeight); if (!scaledImage || !scaledAlpha) { XDestroyImage(ximage); nsMemory::Free(readData); if (scaledImage) nsMemory::Free(scaledImage); if (scaledAlpha) nsMemory::Free(scaledAlpha); return; } RectStretch(aSWidth, aSHeight, aDWidth, aDHeight, 0, 0, aDWidth-1, aDHeight-1, mImageBits, mRowBytes, scaledImage, 3*readWidth, 24); RectStretch(x1, y1, x2-1, y2-1, 0, 0, aDWidth-1, aDHeight-1, mAlphaBits, mAlphaRowBytes, scaledAlpha, readWidth, 8); imageOrigin = scaledImage; imageStride = 3*readWidth; alphaOrigin = scaledAlpha; alphaStride = readWidth; } else { imageOrigin = mImageBits + destY*mRowBytes + 3*destX; imageStride = mRowBytes; alphaOrigin = mAlphaBits + destY*mAlphaRowBytes + destX; alphaStride = mAlphaRowBytes; } PRBool isLSB; unsigned int test = 1; isLSB = (((char *)&test)[0]) ? 1 : 0; int red_prec = xxlib_get_prec_from_mask(visual->red_mask); int green_prec = xxlib_get_prec_from_mask(visual->green_mask); int blue_prec = xxlib_get_prec_from_mask(visual->blue_mask); PRBool flipBytes = ( isLSB && ximage->byte_order != LSBFirst) || (!isLSB && ximage->byte_order == LSBFirst); if ((ximage->bits_per_pixel==32) && (red_prec == 8) && (green_prec == 8) && (blue_prec == 8)) DrawComposited32(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData); else if ((ximage->bits_per_pixel==24) && (red_prec == 8) && (green_prec == 8) && (blue_prec == 8)) DrawComposited24(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData); else if ((ximage->bits_per_pixel==16) && ((red_prec == 5) || (red_prec == 6)) && ((green_prec == 5) || (green_prec == 6)) && ((blue_prec == 5) || (blue_prec == 6))) DrawComposited16(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData); else DrawCompositedGeneral(isLSB, flipBytes, imageOrigin, imageStride, alphaOrigin, alphaStride, readWidth, readHeight, ximage, readData); xGC *imageGC = ((nsRenderingContextXlib&)aContext).GetGC(); xxlib_draw_rgb_image(mXlibRgbHandle, drawable, *imageGC, readX, readY, readWidth, readHeight, XLIB_RGB_DITHER_MAX, readData, 3*readWidth); XDestroyImage(ximage); imageGC->Release(); nsMemory::Free(readData); if (scaledImage) nsMemory::Free(scaledImage); if (scaledAlpha) nsMemory::Free(scaledAlpha); mFlags = 0; } void nsImageXlib::CreateAlphaBitmap(PRInt32 aWidth, PRInt32 aHeight) { XImage *x_image = nsnull; XGCValues gcv; /* Create gc clip-mask on demand */ if (mAlphaBits && IsFlagSet(nsImageUpdateFlags_kBitsChanged, mFlags)) { if (!mAlphaPixmap) mAlphaPixmap = XCreatePixmap(mDisplay, DefaultRootWindow(mDisplay), aWidth, aHeight, 1); // Make an image out of the alpha-bits created by the image library x_image = XCreateImage(mDisplay, xxlib_rgb_get_visual(mXlibRgbHandle), 1, /* visual depth...1 for bitmaps */ XYPixmap, 0, /* x offset, XXX fix this */ (char *)mAlphaBits, /* cast away our sign. */ aWidth, aHeight, 32, /* bitmap pad */ mAlphaRowBytes); /* bytes per line */ x_image->bits_per_pixel=1; /* Image library always places pixels left-to-right MSB to LSB */ x_image->bitmap_bit_order = MSBFirst; /* This definition doesn't depend on client byte ordering because the image library ensures that the bytes in bitmask data are arranged left to right on the screen, low to high address in memory. */ x_image->byte_order = MSBFirst; #if defined(IS_LITTLE_ENDIAN) // no, it's still MSB XXX check on this!! // x_image->byte_order = LSBFirst; #elif defined (IS_BIG_ENDIAN) x_image->byte_order = MSBFirst; #else #error ERROR! Endianness is unknown; #endif /* Copy the XImage to mAlphaPixmap */ if (!s1bitGC) { memset(&gcv, 0, sizeof(XGCValues)); gcv.function = GXcopy; s1bitGC = XCreateGC(mDisplay, mAlphaPixmap, GCFunction, &gcv); } XPutImage(mDisplay, mAlphaPixmap, s1bitGC, x_image, 0, 0, 0, 0, aWidth, aHeight); /* Now we are done with the temporary image */ x_image->data = 0; /* Don't free the IL_Pixmap's bits. */ XDestroyImage(x_image); } } void nsImageXlib::CreateOffscreenPixmap(PRInt32 aWidth, PRInt32 aHeight) { if (mImagePixmap == nsnull) { mImagePixmap = XCreatePixmap(mDisplay, XDefaultRootWindow(mDisplay), aWidth, aHeight, xxlib_rgb_get_depth(mXlibRgbHandle)); } } void nsImageXlib::SetupGCForAlpha(GC aGC, PRInt32 aX, PRInt32 aY) { if (mAlphaPixmap) { XGCValues xvalues; memset(&xvalues, 0, sizeof(XGCValues)); unsigned long xvalues_mask = 0; xvalues.clip_x_origin = aX; xvalues.clip_y_origin = aY; xvalues_mask = GCClipXOrigin | GCClipYOrigin | GCClipMask; xvalues.function = GXcopy; xvalues.clip_mask = mAlphaPixmap; XChangeGC(mDisplay, aGC, xvalues_mask, &xvalues); } } // Draw the bitmap. This draw just has destination coordinates NS_IMETHODIMP nsImageXlib::Draw(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) { if (mPendingUpdate) UpdateCachedImage(); if ((mAlphaDepth == 1) && mIsSpacer) return NS_OK; if (aSurface == nsnull) return NS_ERROR_FAILURE; if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1) return NS_OK; if ((mAlphaDepth == 8) && mAlphaValid) { DrawComposited(aContext, aSurface, 0, 0, aWidth, aHeight, aX, aY, aWidth, aHeight); return NS_OK; } // XXX it is said that this is temporary code if ((aWidth != mWidth) || (aHeight != mHeight)) { aWidth = mWidth; aHeight = mHeight; } nsIDrawingSurfaceXlib *drawing = NS_STATIC_CAST(nsIDrawingSurfaceXlib *, aSurface); PRInt32 validX = 0, validY = 0, validWidth = aWidth, validHeight = aHeight; if ((mDecodedY2 < aHeight)) { validHeight = mDecodedY2 - mDecodedY1; } if ((mDecodedX2 < aWidth)) { validWidth = mDecodedX2 - mDecodedX1; } if ((mDecodedY1 > 0)) { validHeight -= mDecodedY1; validY = mDecodedY1; } if ((mDecodedX1 > 0)) { validHeight -= mDecodedX1; validX = mDecodedX1; } CreateAlphaBitmap(aWidth, aHeight); GC copyGC; xGC *gc = ((nsRenderingContextXlib&)aContext).GetGC(); if (mAlphaPixmap) { if (mGC) { /* reuse GC */ copyGC = mGC; SetupGCForAlpha(copyGC, aX, aY); } else { /* make a new one */ /* this repeats things done in SetupGCForAlpha */ XGCValues xvalues; memset(&xvalues, 0, sizeof(XGCValues)); unsigned long xvalues_mask = 0; xvalues.clip_x_origin = aX; xvalues.clip_y_origin = aY; if (IsFlagSet(nsImageUpdateFlags_kBitsChanged, mFlags)) { xvalues_mask = GCClipXOrigin | GCClipYOrigin | GCClipMask; xvalues.clip_mask = mAlphaPixmap; } Drawable drawable; drawing->GetDrawable(drawable); mGC = XCreateGC(mDisplay, drawable, xvalues_mask , &xvalues); copyGC = mGC; } } else { /* !mAlphaPixmap */ copyGC = *gc; } Drawable drawable; drawing->GetDrawable(drawable); XCopyArea(mDisplay, mImagePixmap, drawable, copyGC, validX, validY, validWidth, validHeight, validX + aX, validY + aY); gc->Release(); mFlags = 0; return NS_OK; } void nsImageXlib::TilePixmap(Pixmap src, Pixmap dest, PRInt32 aSXOffset, PRInt32 aSYOffset, const nsRect &destRect, const nsRect &clipRect, PRBool useClip) { GC gc; XGCValues values; unsigned long valuesMask; memset(&values, 0, sizeof(XGCValues)); values.fill_style = FillTiled; values.tile = src; values.ts_x_origin = destRect.x - aSXOffset; values.ts_y_origin = destRect.y - aSYOffset; valuesMask = GCTile | GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle; gc = XCreateGC(mDisplay, src, valuesMask, &values); if (useClip) { XRectangle xrectangle; xrectangle.x = clipRect.x; xrectangle.y = clipRect.y; xrectangle.width = clipRect.width; xrectangle.height = clipRect.height; XSetClipRectangles(mDisplay, gc, 0, 0, &xrectangle, 1, Unsorted); } XFillRectangle(mDisplay, dest, gc, destRect.x, destRect.y, destRect.width, destRect.height); XFreeGC(mDisplay, gc); } NS_IMETHODIMP nsImageXlib::DrawTile(nsIRenderingContext &aContext, nsDrawingSurface aSurface, PRInt32 aSXOffset, PRInt32 aSYOffset, PRInt32 aPadX, PRInt32 aPadY, const nsRect &aTileRect) { if (mPendingUpdate) UpdateCachedImage(); if ((mAlphaDepth == 1) && mIsSpacer) return NS_OK; if (aTileRect.width <= 0 || aTileRect.height <= 0) { return NS_OK; } if (mDecodedX2 < mDecodedX1 || mDecodedY2 < mDecodedY1) return NS_OK; nsIDrawingSurfaceXlib *drawing = NS_STATIC_CAST(nsIDrawingSurfaceXlib *, aSurface); PRBool partial = PR_FALSE; PRInt32 validX = 0, validY = 0, validWidth = mWidth, validHeight = mHeight; // limit the image rectangle to the size of the image data which // has been validated. if (mDecodedY2 < mHeight) { validHeight = mDecodedY2 - mDecodedY1; partial = PR_TRUE; } if (mDecodedX2 < mWidth) { validWidth = mDecodedX2 - mDecodedX1; partial = PR_TRUE; } if (mDecodedY1 > 0) { validHeight -= mDecodedY1; validY = mDecodedY1; partial = PR_TRUE; } if (mDecodedX1 > 0) { validWidth -= mDecodedX1; validX = mDecodedX1; partial = PR_TRUE; } if (validWidth == 0 || validHeight == 0) { return NS_OK; } if (partial || ((mAlphaDepth == 8) && mAlphaValid) || (aPadX || aPadY)) { PRInt32 aY0 = aTileRect.y - aSYOffset, aX0 = aTileRect.x - aSXOffset, aY1 = aTileRect.y + aTileRect.height, aX1 = aTileRect.x + aTileRect.width; // Set up clipping and call Draw(). PRBool clipState; aContext.PushState(); ((nsRenderingContextXlib&)aContext).SetClipRectInPixels( aTileRect, nsClipCombine_kIntersect, clipState); ((nsRenderingContextXlib&)aContext).UpdateGC(); for (PRInt32 y = aY0; y < aY1; y += mHeight + aPadY) for (PRInt32 x = aX0; x < aX1; x += mWidth + aPadX) Draw(aContext,aSurface, x, y, PR_MIN(validWidth, aX1 - x), PR_MIN(validHeight, aY1 - y)); aContext.PopState(clipState); return NS_OK; } CreateOffscreenPixmap(mWidth, mHeight); if (mAlphaDepth == 1) { Pixmap tileImg; Pixmap tileMask; CreateAlphaBitmap(validWidth, validHeight); nsRect tmpRect(0,0,aTileRect.width, aTileRect.height); XlibRgbHandle *drawingXHandle; drawing->GetXlibRgbHandle(drawingXHandle); tileImg = XCreatePixmap(mDisplay, mImagePixmap, aTileRect.width, aTileRect.height, xxlib_rgb_get_depth(drawingXHandle)); TilePixmap(mImagePixmap, tileImg, aSXOffset, aSYOffset, tmpRect, tmpRect, PR_FALSE); // tile alpha mask tileMask = XCreatePixmap(mDisplay, mAlphaPixmap, aTileRect.width, aTileRect.height, mAlphaDepth); TilePixmap(mAlphaPixmap, tileMask, aSXOffset, aSYOffset, tmpRect, tmpRect, PR_FALSE); GC fgc; XGCValues values; unsigned long valuesMask; Drawable drawable; drawing->GetDrawable(drawable); memset(&values, 0, sizeof(XGCValues)); values.clip_mask = tileMask; values.clip_x_origin = aTileRect.x; values.clip_y_origin = aTileRect.y; valuesMask = GCClipXOrigin | GCClipYOrigin | GCClipMask; fgc = XCreateGC(mDisplay, drawable, valuesMask, &values); XCopyArea(mDisplay, tileImg, drawable, fgc, 0,0, aTileRect.width, aTileRect.height, aTileRect.x, aTileRect.y); XFreePixmap(mDisplay, tileImg); XFreePixmap(mDisplay, tileMask); XFreeGC(mDisplay, fgc); } else { // In the non-alpha case, xlib can tile for us nsRect clipRect; PRBool isValid; aContext.GetClipRect(clipRect, isValid); Drawable drawable; drawing->GetDrawable(drawable); TilePixmap(mImagePixmap, drawable, aSXOffset, aSYOffset, aTileRect, clipRect, PR_FALSE); } mFlags = 0; return NS_OK; } //---------------------------------------------------------------------- nsresult nsImageXlib::Optimize(nsIDeviceContext *aContext) { return NS_OK; } //---------------------------------------------------------------------- // Lock the image pixels. NS_IMETHODIMP nsImageXlib::LockImagePixels(PRBool aMaskPixels) { return NS_OK; } //--------------------------------------------------------------------- // unlock the image pixels. Implement this if you need it. NS_IMETHODIMP nsImageXlib::UnlockImagePixels(PRBool aMaskPixels) { return NS_OK; } NS_IMETHODIMP nsImageXlib::DrawToImage(nsIImage* aDstImage, nscoord aDX, nscoord aDY, nscoord aDWidth, nscoord aDHeight) { nsImageXlib *dest = NS_STATIC_CAST(nsImageXlib *, aDstImage); if (!dest) return NS_ERROR_FAILURE; if (aDX >= dest->mWidth || aDY >= dest->mHeight) return NS_OK; if (mPendingUpdate) UpdateCachedImage(); if (!dest->mImagePixmap) dest->CreateOffscreenPixmap(dest->mWidth, dest->mHeight); if (!dest->mImagePixmap || !mImagePixmap) return NS_ERROR_FAILURE; GC gc = XCreateGC(mDisplay, dest->mImagePixmap, 0, NULL); if (mAlphaDepth == 1) CreateAlphaBitmap(mWidth, mHeight); if (mAlphaPixmap) SetupGCForAlpha(gc, aDX, aDY); XCopyArea(dest->mDisplay, mImagePixmap, dest->mImagePixmap, gc, 0, 0, mWidth, mHeight, aDX, aDY); XFreeGC(mDisplay, gc); if (!mIsSpacer || !mAlphaDepth) dest->mIsSpacer = PR_FALSE; // need to copy the mImageBits in case we're rendered scaled PRUint8 *scaledImage = 0, *scaledAlpha = 0; PRUint8 *rgbPtr=0, *alphaPtr=0; PRUint32 rgbStride, alphaStride = 0; if ((aDWidth != mWidth) || (aDHeight != mHeight)) { // scale factor in DrawTo... start scaling scaledImage = (PRUint8 *)nsMemory::Alloc(3*aDWidth*aDHeight); if (!scaledImage) return NS_ERROR_OUT_OF_MEMORY; RectStretch(mWidth, mHeight, aDWidth, aDHeight, 0, 0, aDWidth-1, aDHeight-1, mImageBits, mRowBytes, scaledImage, 3*aDWidth, 24); if (mAlphaDepth) { if (mAlphaDepth==1) alphaStride = (aDWidth+7)>>3; // round to next byte else alphaStride = aDWidth; scaledAlpha = (PRUint8 *)nsMemory::Alloc(alphaStride*aDHeight); if (!scaledAlpha) { nsMemory::Free(scaledImage); return NS_ERROR_OUT_OF_MEMORY; } RectStretch(mWidth, mHeight, aDWidth, aDHeight, 0, 0, aDWidth-1, aDHeight-1, mAlphaBits, mAlphaRowBytes, scaledAlpha, alphaStride, mAlphaDepth); } rgbPtr = scaledImage; rgbStride = 3*aDWidth; alphaPtr = scaledAlpha; } else { rgbPtr = mImageBits; rgbStride = mRowBytes; alphaPtr = mAlphaBits; alphaStride = mAlphaRowBytes; } PRInt32 y; PRInt32 ValidWidth = ( aDWidth < ( dest->mWidth - aDX ) ) ? aDWidth : ( dest->mWidth - aDX ); PRInt32 ValidHeight = ( aDHeight < ( dest->mHeight - aDY ) ) ? aDHeight : ( dest->mHeight - aDY ); // now composite the two images together switch (mAlphaDepth) { case 1: { PRUint8 *dst = dest->mImageBits + aDY*dest->mRowBytes + 3*aDX; PRUint8 *dstAlpha = dest->mAlphaBits + aDY*dest->mAlphaRowBytes; PRUint8 *src = rgbPtr; PRUint8 *alpha = alphaPtr; PRUint8 offset = aDX & 0x7; // x starts at 0 int iterations = (ValidWidth+7)/8; // round up for (y=0; y= ValidWidth) { alphaPixels &= 0xff << (8 - (ValidWidth-x)); // no, mask off unused if (alphaPixels == 0) continue; // no 1 alpha pixels left } if (offset == 0) { dstAlpha[(aDX+x)>>3] |= alphaPixels; // the cheap aligned case } else { dstAlpha[(aDX+x)>>3] |= alphaPixels >> offset; // avoid write if no 1's to write - also avoids going past end of array PRUint8 alphaTemp = alphaPixels << (8U - offset); if (alphaTemp & 0xff) dstAlpha[((aDX+x)>>3) + 1] |= alphaTemp; } if (alphaPixels == 0xff) { // fix - could speed up by gathering a run of 0xff's and doing 1 memcpy // all 8 pixels set; copy and jump forward memcpy(dst,src,8*3); continue; } else { // else mix of 1's and 0's in alphaPixels, do 1 bit at a time // Don't go past end of line! PRUint8 *d = dst, *s = src; for (PRUint8 aMask = 1<<7, j = 0; aMask && j < ValidWidth-x; aMask >>= 1, j++) { // if this pixel is opaque then copy into the destination image if (alphaPixels & aMask) { // might be faster with *d++ = *s++ 3 times? d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; // dstAlpha bit already set } d += 3; s += 3; } } } // at end of each line, bump pointers. Use wordy code because of // bug 127455 to avoid possibility of unsigned underflow dst = (dst - 3*8*iterations) + dest->mRowBytes; src = (src - 3*8*iterations) + rgbStride; alpha = (alpha - iterations) + alphaStride; dstAlpha += dest->mAlphaRowBytes; } } break; case 8: // fix? Does this matter? GTK doesn't support this. Others? for (y=0; ymImageBits + (y+aDY)*dest->mRowBytes + 3*aDX; PRUint8 *dstAlpha = dest->mAlphaBits + (y+aDY)*dest->mAlphaRowBytes + aDX; PRUint8 *src = rgbPtr + y*rgbStride; PRUint8 *alpha = alphaPtr + y*alphaStride; for (int x=0; xmImageBits + (y+aDY)*dest->mRowBytes + 3*aDX, rgbPtr + y*rgbStride, 3*ValidWidth); } if (scaledAlpha) nsMemory::Free(scaledAlpha); if (scaledImage) nsMemory::Free(scaledImage); return NS_OK; }