// Computes the area in which aRect1 and aRect2 overlap and fills 'this' with // the result. Returns FALSE if the rectangles don't intersect. PRBool nsIntRect::IntersectRect(const nsIntRect &aRect1, const nsIntRect &aRect2) { PRInt32 xmost1 = aRect1.XMost(); PRInt32 ymost1 = aRect1.YMost(); PRInt32 xmost2 = aRect2.XMost(); PRInt32 ymost2 = aRect2.YMost(); PRInt32 temp; x = PR_MAX(aRect1.x, aRect2.x); y = PR_MAX(aRect1.y, aRect2.y); // Compute the destination width temp = PR_MIN(xmost1, xmost2); if (temp <= x) { Empty(); return PR_FALSE; } width = temp - x; // Compute the destination height temp = PR_MIN(ymost1, ymost2); if (temp <= y) { Empty(); return PR_FALSE; } height = temp - y; return PR_TRUE; }
/** * Identical to BoxBlurHorizontal, except it blurs top and bottom instead of * left and right. * XXX shouldn't we pass stride in separately here? */ static void BoxBlurVertical(unsigned char* aInput, unsigned char* aOutput, PRInt32 aTopLobe, PRInt32 aBottomLobe, PRInt32 aWidth, PRInt32 aRows, const nsIntRect& aSkipRect) { NS_ASSERTION(aRows > 0, "Can't handle zero rows here"); PRInt32 boxSize = aTopLobe + aBottomLobe + 1; PRBool skipRectCoversWholeColumn = 0 >= aSkipRect.y && aRows <= aSkipRect.YMost(); for (PRInt32 x = 0; x < aWidth; x++) { PRBool inSkipRectX = x >= aSkipRect.x && x < aSkipRect.XMost(); if (inSkipRectX && skipRectCoversWholeColumn) { x = aSkipRect.XMost() - 1; continue; } PRInt32 alphaSum = 0; for (PRInt32 i = 0; i < boxSize; i++) { PRInt32 pos = i - aTopLobe; // See assertion above; if aRows is zero, then we would have no // valid position to clamp to. pos = NS_MAX(pos, 0); pos = NS_MIN(pos, aRows - 1); alphaSum += aInput[aWidth * pos + x]; } for (PRInt32 y = 0; y < aRows; y++) { if (inSkipRectX && y >= aSkipRect.y && y < aSkipRect.YMost()) { y = aSkipRect.YMost(); if (y >= aRows) break; alphaSum = 0; for (PRInt32 i = 0; i < boxSize; i++) { PRInt32 pos = y + i - aTopLobe; // See assertion above; if aRows is zero, then we would have no // valid position to clamp to. pos = NS_MAX(pos, 0); pos = NS_MIN(pos, aRows - 1); alphaSum += aInput[aWidth * pos + x]; } } PRInt32 tmp = y - aTopLobe; PRInt32 last = NS_MAX(tmp, 0); PRInt32 next = NS_MIN(tmp + boxSize, aRows - 1); aOutput[aWidth * y + x] = alphaSum/boxSize; alphaSum += aInput[aWidth * next + x] - aInput[aWidth * last + x]; } } }
// Computes the smallest rectangle that contains both aRect1 and aRect2 and // fills 'this' with the result. Returns FALSE if both aRect1 and aRect2 are // empty and TRUE otherwise PRBool nsIntRect::UnionRect(const nsIntRect &aRect1, const nsIntRect &aRect2) { PRBool result = PR_TRUE; // Is aRect1 empty? if (aRect1.IsEmpty()) { if (aRect2.IsEmpty()) { // Both rectangles are empty which is an error Empty(); result = PR_FALSE; } else { // aRect1 is empty so set the result to aRect2 *this = aRect2; } } else if (aRect2.IsEmpty()) { // aRect2 is empty so set the result to aRect1 *this = aRect1; } else { PRInt32 xmost1 = aRect1.XMost(); PRInt32 xmost2 = aRect2.XMost(); PRInt32 ymost1 = aRect1.YMost(); PRInt32 ymost2 = aRect2.YMost(); // Compute the origin x = PR_MIN(aRect1.x, aRect2.x); y = PR_MIN(aRect1.y, aRect2.y); // Compute the size width = PR_MAX(xmost1, xmost2) - x; height = PR_MAX(ymost1, ymost2) - y; } return result; }
/** * Identical to BoxBlurHorizontal, except it blurs top and bottom instead of * left and right. * XXX shouldn't we pass stride in separately here? */ static void BoxBlurVertical(unsigned char* aInput, unsigned char* aOutput, PRInt32 aTopLobe, PRInt32 aBottomLobe, PRInt32 aWidth, PRInt32 aRows, const nsIntRect& aSkipRect) { PRInt32 boxSize = aTopLobe + aBottomLobe + 1; PRBool skipRectCoversWholeColumn = 0 >= aSkipRect.y && aRows <= aSkipRect.YMost(); for (PRInt32 x = 0; x < aWidth; x++) { PRBool inSkipRectX = x >= aSkipRect.x && x < aSkipRect.XMost(); if (inSkipRectX && skipRectCoversWholeColumn) { x = aSkipRect.XMost() - 1; continue; } PRInt32 alphaSum = 0; for (PRInt32 i = 0; i < boxSize; i++) { PRInt32 pos = i - aTopLobe; pos = NS_MAX(pos, 0); pos = NS_MIN(pos, aRows - 1); alphaSum += aInput[aWidth * pos + x]; } for (PRInt32 y = 0; y < aRows; y++) { if (inSkipRectX && y >= aSkipRect.y && y < aSkipRect.YMost()) { y = aSkipRect.YMost(); if (y >= aRows) break; alphaSum = 0; for (PRInt32 i = 0; i < boxSize; i++) { PRInt32 pos = y + i - aTopLobe; pos = NS_MAX(pos, 0); pos = NS_MIN(pos, aRows - 1); alphaSum += aInput[aWidth * pos + x]; } } PRInt32 tmp = y - aTopLobe; PRInt32 last = NS_MAX(tmp, 0); PRInt32 next = NS_MIN(tmp + boxSize, aRows - 1); aOutput[aWidth * y + x] = alphaSum/boxSize; alphaSum += aInput[aWidth * next + x] - aInput[aWidth * last + x]; } } }
static void ComputesRGBLuminanceMask(uint8_t *aData, int32_t aStride, const nsIntRect &aRect, float aOpacity) { for (int32_t y = aRect.y; y < aRect.YMost(); y++) { for (int32_t x = aRect.x; x < aRect.XMost(); x++) { uint8_t *pixel = aData + aStride * y + 4 * x; uint8_t a = pixel[GFX_ARGB32_OFFSET_A]; uint8_t luminance; if (a) { /* sRGB -> intensity (unpremultiply cancels out the * (a/255.0) multiplication with aOpacity */ luminance = static_cast<uint8_t> ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 + pixel[GFX_ARGB32_OFFSET_G] * 0.7154 + pixel[GFX_ARGB32_OFFSET_B] * 0.0721) * aOpacity); } else { luminance = 0; } memset(pixel, luminance, 4); } } }
void nsSVGUtils::UnPremultiplyImageDataAlpha(PRUint8 *data, PRInt32 stride, const nsIntRect &rect) { for (PRInt32 y = rect.y; y < rect.YMost(); y++) { for (PRInt32 x = rect.x; x < rect.XMost(); x++) { PRUint8 *pixel = data + stride * y + 4 * x; PRUint8 a = pixel[GFX_ARGB32_OFFSET_A]; if (a == 255) continue; if (a) { pixel[GFX_ARGB32_OFFSET_B] = (255 * pixel[GFX_ARGB32_OFFSET_B]) / a; pixel[GFX_ARGB32_OFFSET_G] = (255 * pixel[GFX_ARGB32_OFFSET_G]) / a; pixel[GFX_ARGB32_OFFSET_R] = (255 * pixel[GFX_ARGB32_OFFSET_R]) / a; } else { pixel[GFX_ARGB32_OFFSET_B] = 0; pixel[GFX_ARGB32_OFFSET_G] = 0; pixel[GFX_ARGB32_OFFSET_R] = 0; } } } }
void SVGFEGaussianBlurElement::GaussianBlur(const Image* aSource, const Image* aTarget, const nsIntRect& aDataRect, uint32_t aDX, uint32_t aDY) { NS_ASSERTION(nsIntRect(0, 0, aTarget->mImage->Width(), aTarget->mImage->Height()).Contains(aDataRect), "aDataRect out of bounds"); nsAutoArrayPtr<uint8_t> tmp(new uint8_t[aTarget->mImage->GetDataSize()]); if (!tmp) return; memset(tmp, 0, aTarget->mImage->GetDataSize()); bool alphaOnly = AreAllColorChannelsZero(aTarget); const uint8_t* sourceData = aSource->mImage->Data(); uint8_t* targetData = aTarget->mImage->Data(); uint32_t stride = aTarget->mImage->Stride(); if (aDX == 0) { CopyDataRect(tmp, sourceData, stride, aDataRect); } else { int32_t longLobe = aDX/2; int32_t shortLobe = (aDX & 1) ? longLobe : longLobe - 1; for (int32_t major = aDataRect.y; major < aDataRect.YMost(); ++major) { int32_t ms = major*stride; BoxBlur(sourceData + ms, tmp + ms, 4, aDataRect.x, aDataRect.XMost(), longLobe, shortLobe, alphaOnly); BoxBlur(tmp + ms, targetData + ms, 4, aDataRect.x, aDataRect.XMost(), shortLobe, longLobe, alphaOnly); BoxBlur(targetData + ms, tmp + ms, 4, aDataRect.x, aDataRect.XMost(), longLobe, longLobe, alphaOnly); } } if (aDY == 0) { CopyDataRect(targetData, tmp, stride, aDataRect); } else { int32_t longLobe = aDY/2; int32_t shortLobe = (aDY & 1) ? longLobe : longLobe - 1; for (int32_t major = aDataRect.x; major < aDataRect.XMost(); ++major) { int32_t ms = major*4; BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, shortLobe, alphaOnly); BoxBlur(targetData + ms, tmp + ms, stride, aDataRect.y, aDataRect.YMost(), shortLobe, longLobe, alphaOnly); BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, longLobe, alphaOnly); } } }
// Clip aTarget's image to its filter primitive subregion. // aModifiedRect contains all the pixels which might not be RGBA(0,0,0,0), // it's relative to the surface data. static void ClipTarget(nsSVGFilterInstance* aInstance, const nsSVGFE::Image* aTarget, const nsIntRect& aModifiedRect) { nsIntPoint surfaceTopLeft = aInstance->GetSurfaceRect().TopLeft(); NS_ASSERTION(aInstance->GetSurfaceRect().Contains(aModifiedRect + surfaceTopLeft), "Modified data area overflows the surface?"); nsIntRect clip = aModifiedRect; nsSVGUtils::ClipToGfxRect(&clip, aTarget->mFilterPrimitiveSubregion - gfxPoint(surfaceTopLeft.x, surfaceTopLeft.y)); ClearRect(aTarget->mImage, aModifiedRect.x, aModifiedRect.y, aModifiedRect.XMost(), clip.y); ClearRect(aTarget->mImage, aModifiedRect.x, clip.y, clip.x, clip.YMost()); ClearRect(aTarget->mImage, clip.XMost(), clip.y, aModifiedRect.XMost(), clip.YMost()); ClearRect(aTarget->mImage, aModifiedRect.x, clip.YMost(), aModifiedRect.XMost(), aModifiedRect.YMost()); }
// |aTexCoordRect| is the rectangle from the texture that we want to // draw using the given program. The program already has a necessary // offset and scale, so the geometry that needs to be drawn is a unit // square from 0,0 to 1,1. // // |aTexSize| is the actual size of the texture, as it can be larger // than the rectangle given by |aTexCoordRect|. void LayerManagerOGL::BindAndDrawQuadWithTextureRect(LayerProgram *aProg, const nsIntRect& aTexCoordRect, const nsIntSize& aTexSize, GLenum aWrapMode) { GLuint vertAttribIndex = aProg->AttribLocation(LayerProgram::VertexAttrib); GLuint texCoordAttribIndex = aProg->AttribLocation(LayerProgram::TexCoordAttrib); NS_ASSERTION(texCoordAttribIndex != GLuint(-1), "no texture coords?"); // clear any bound VBO so that glVertexAttribPointer() goes back to // "pointer mode" mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); // Given what we know about these textures and coordinates, we can // compute fmod(t, 1.0f) to get the same texture coordinate out. If // the texCoordRect dimension is < 0 or > width/height, then we have // wraparound that we need to deal with by drawing multiple quads, // because we can't rely on full non-power-of-two texture support // (which is required for the REPEAT wrap mode). GLContext::RectTriangles rects; if (aWrapMode == LOCAL_GL_REPEAT) { rects.addRect(/* dest rectangle */ 0.0f, 0.0f, 1.0f, 1.0f, /* tex coords */ aTexCoordRect.x / GLfloat(aTexSize.width), aTexCoordRect.y / GLfloat(aTexSize.height), aTexCoordRect.XMost() / GLfloat(aTexSize.width), aTexCoordRect.YMost() / GLfloat(aTexSize.height)); } else { GLContext::DecomposeIntoNoRepeatTriangles(aTexCoordRect, aTexSize, rects); } mGLContext->fVertexAttribPointer(vertAttribIndex, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.vertexPointer()); mGLContext->fVertexAttribPointer(texCoordAttribIndex, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.texCoordPointer()); { mGLContext->fEnableVertexAttribArray(texCoordAttribIndex); { mGLContext->fEnableVertexAttribArray(vertAttribIndex); mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements()); mGLContext->fDisableVertexAttribArray(vertAttribIndex); } mGLContext->fDisableVertexAttribArray(texCoordAttribIndex); } }
void SwapChainD3D9::Present(const nsIntRect &aRect) { RECT r; r.left = aRect.x; r.top = aRect.y; r.right = aRect.XMost(); r.bottom = aRect.YMost(); mSwapChain->Present(&r, &r, 0, 0, 0); }
void AndroidGeckoLayerClient::SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect) { NS_ASSERTION(!isNull(), "SetFirstPaintViewport called on null layer client!"); JNIEnv *env = GetJNIForThread(); // this is called on the compositor thread if (!env) return; AutoLocalJNIFrame jniFrame(env, 0); return env->CallVoidMethod(wrapped_obj, jSetFirstPaintViewport, (float)aOffset.x, (float)aOffset.y, aZoom, (float)aPageRect.x, (float)aPageRect.y, (float)aPageRect.XMost(), (float)aPageRect.YMost(), aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost()); }
nsIntRect RotateRect(nsIntRect aRect, const nsIntRect& aBounds, ScreenRotation aRotation) { switch (aRotation) { case ROTATION_0: return aRect; case ROTATION_90: return nsIntRect(aRect.Y(), aBounds.Width() - aRect.XMost(), aRect.Height(), aRect.Width()); case ROTATION_180: return nsIntRect(aBounds.Width() - aRect.XMost(), aBounds.Height() - aRect.YMost(), aRect.Width(), aRect.Height()); case ROTATION_270: return nsIntRect(aBounds.Height() - aRect.YMost(), aRect.X(), aRect.Height(), aRect.Width()); default: MOZ_CRASH("Unknown rotation"); } }
static void ComputeAlphaMask(uint8_t *aData, int32_t aStride, const nsIntRect &aRect, float aOpacity) { for (int32_t y = aRect.y; y < aRect.YMost(); y++) { for (int32_t x = aRect.x; x < aRect.XMost(); x++) { uint8_t *pixel = aData + aStride * y + 4 * x; uint8_t luminance = pixel[GFX_ARGB32_OFFSET_A] * aOpacity; memset(pixel, luminance, 4); } } }
already_AddRefed<gfxContext> ThebesLayerBuffer::GetContextForQuadrantUpdate(const nsIntRect& aBounds) { nsRefPtr<gfxContext> ctx = new gfxContext(mBuffer); // Figure out which quadrant to draw in PRInt32 xBoundary = mBufferRect.XMost() - mBufferRotation.x; PRInt32 yBoundary = mBufferRect.YMost() - mBufferRotation.y; XSide sideX = aBounds.XMost() <= xBoundary ? RIGHT : LEFT; YSide sideY = aBounds.YMost() <= yBoundary ? BOTTOM : TOP; nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY); NS_ASSERTION(quadrantRect.Contains(aBounds), "Messed up quadrants"); ctx->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y)); return ctx.forget(); }
void nsSVGUtils::ConvertImageDataFromLinearRGB(PRUint8 *data, PRInt32 stride, const nsIntRect &rect) { for (PRInt32 y = rect.y; y < rect.YMost(); y++) { for (PRInt32 x = rect.x; x < rect.XMost(); x++) { PRUint8 *pixel = data + stride * y + 4 * x; pixel[GFX_ARGB32_OFFSET_B] = glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_B]]; pixel[GFX_ARGB32_OFFSET_G] = glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_G]]; pixel[GFX_ARGB32_OFFSET_R] = glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_R]]; } } }
already_AddRefed<gfxContext> RotatedContentBuffer::GetContextForQuadrantUpdate(const nsIntRect& aBounds, ContextSource aSource, nsIntPoint *aTopLeft) { if (!EnsureBuffer()) { return nullptr; } nsRefPtr<gfxContext> ctx; if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) { if (!EnsureBufferOnWhite()) { return nullptr; } MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite); RefPtr<DrawTarget> dualDT = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite); ctx = new gfxContext(dualDT); } else if (aSource == BUFFER_WHITE) { if (!EnsureBufferOnWhite()) { return nullptr; } ctx = new gfxContext(mDTBufferOnWhite); } else { // BUFFER_BLACK, or BUFFER_BOTH with a single buffer. ctx = new gfxContext(mDTBuffer); } // Figure out which quadrant to draw in int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x; int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y; XSide sideX = aBounds.XMost() <= xBoundary ? RIGHT : LEFT; YSide sideY = aBounds.YMost() <= yBoundary ? BOTTOM : TOP; nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY); NS_ASSERTION(quadrantRect.Contains(aBounds), "Messed up quadrants"); ctx->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y)); if (aTopLeft) { *aTopLeft = nsIntPoint(quadrantRect.x, quadrantRect.y); } return ctx.forget(); }
void nsSVGUtils::PremultiplyImageDataAlpha(PRUint8 *data, PRInt32 stride, const nsIntRect &rect) { for (PRInt32 y = rect.y; y < rect.YMost(); y++) { for (PRInt32 x = rect.x; x < rect.XMost(); x++) { PRUint8 *pixel = data + stride * y + 4 * x; PRUint8 a = pixel[GFX_ARGB32_OFFSET_A]; if (a == 255) continue; FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_B], pixel[GFX_ARGB32_OFFSET_B] * a); FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_G], pixel[GFX_ARGB32_OFFSET_G] * a); FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_R], pixel[GFX_ARGB32_OFFSET_R] * a); } } }
static void ComputeLinearRGBLuminanceMask(uint8_t *aData, int32_t aStride, const nsIntRect &aRect, float aOpacity) { for (int32_t y = aRect.y; y < aRect.YMost(); y++) { for (int32_t x = aRect.x; x < aRect.XMost(); x++) { uint8_t *pixel = aData + aStride * y + 4 * x; uint8_t a = pixel[GFX_ARGB32_OFFSET_A]; uint8_t luminance; // unpremultiply if (a) { if (a != 255) { pixel[GFX_ARGB32_OFFSET_B] = (255 * pixel[GFX_ARGB32_OFFSET_B]) / a; pixel[GFX_ARGB32_OFFSET_G] = (255 * pixel[GFX_ARGB32_OFFSET_G]) / a; pixel[GFX_ARGB32_OFFSET_R] = (255 * pixel[GFX_ARGB32_OFFSET_R]) / a; } /* sRGB -> linearRGB -> intensity */ luminance = static_cast<uint8_t> ((gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_R]] * 0.2125 + gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_G]] * 0.7154 + gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_B]] * 0.0721) * (a / 255.0) * aOpacity); } else { luminance = 0; } memset(pixel, luminance, 4); } } }
/* static */ void WinUtils::InvalidatePluginAsWorkaround(nsIWidget *aWidget, const nsIntRect &aRect) { aWidget->Invalidate(aRect); // XXX - Even more evil workaround!! See bug 762948, flash's bottom // level sandboxed window doesn't seem to get our invalidate. We send // an invalidate to it manually. This is totally specialized for this // bug, for other child window structures this will just be a more or // less bogus invalidate but since that should not have any bad // side-effects this will have to do for now. HWND current = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW); RECT windowRect; RECT parentRect; ::GetWindowRect(current, &parentRect); HWND next = current; do { current = next; ::EnumChildWindows(current, &EnumFirstChild, (LPARAM)&next); ::GetWindowRect(next, &windowRect); // This is relative to the screen, adjust it to be relative to the // window we're reconfiguring. windowRect.left -= parentRect.left; windowRect.top -= parentRect.top; } while (next != current && windowRect.top == 0 && windowRect.left == 0); if (windowRect.top == 0 && windowRect.left == 0) { RECT rect; rect.left = aRect.x; rect.top = aRect.y; rect.right = aRect.XMost(); rect.bottom = aRect.YMost(); ::InvalidateRect(next, &rect, FALSE); } }
nsresult SVGFETileElement::Filter(nsSVGFilterInstance *instance, const nsTArray<const Image*>& aSources, const Image* aTarget, const nsIntRect& rect) { // XXX This code depends on the surface rect containing the filter // primitive subregion. ComputeTargetBBox, ComputeNeededSourceBBoxes // and ComputeChangeBBox are all pessimal, so that will normally be OK, // but nothing clips mFilterPrimitiveSubregion so this should be changed. nsIntRect tile; bool res = gfxUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion, &tile); NS_ENSURE_TRUE(res, NS_ERROR_FAILURE); // asserts on failure (not if (tile.IsEmpty()) return NS_OK; const nsIntRect &surfaceRect = instance->GetSurfaceRect(); if (!tile.Intersects(surfaceRect)) { // nothing to draw return NS_OK; } // clip tile tile = tile.Intersect(surfaceRect); // Get it into surface space tile -= surfaceRect.TopLeft(); uint8_t* sourceData = aSources[0]->mImage->Data(); uint8_t* targetData = aTarget->mImage->Data(); uint32_t stride = aTarget->mImage->Stride(); /* * priority: left before right before centre * and * top before bottom before centre * * eg: If we have a target area which is 1.5 times the width of a tile, * then, based on alignment, we get: * 'left and right' * or * 'left and centre' * */ int32_t leftPartialTileWidth; int32_t rightPartialTileWidth; int32_t centreWidth; ComputePartialTileExtents(&leftPartialTileWidth, &rightPartialTileWidth, ¢reWidth, rect.x, rect.width, tile.x, tile.width); int32_t topPartialTileHeight; int32_t bottomPartialTileHeight; int32_t centreHeight; ComputePartialTileExtents(&topPartialTileHeight, &bottomPartialTileHeight, ¢reHeight, rect.y, rect.height, tile.y, tile.height); /* We have nine regions of the target area which have to be tiled differetly: * * Top Left, Top Middle, Top Right, * Left Middle, Centre, Right Middle, * Bottom Left, Bottom Middle, Bottom Right * * + Centre is tiled by repeating the tiled image in full. * + Top Left, Top Middle and Top Right: * Some of the rows from the top of the tile will be clipped here. * + Bottom Left, Bottom Middle and Bottom Right: * Some of the rows from the bottom of the tile will be clipped here. * + Top Left, Left Middle and Bottom left: * Some of the columns from the Left of the tile will be clipped here. * + Top Right, Right Middle and Bottom Right: * Some of the columns from the right of the tile will be clipped here. * * If the sizes and positions of the target and tile are such that the tile * aligns exactly on any (or all) of the edges, then some (or all) of the * regions above (except Centre) will be zero sized. */ nsIntRect targetRects[] = { // Top Left nsIntRect(rect.x, rect.y, leftPartialTileWidth, topPartialTileHeight), // Top Middle nsIntRect(rect.x + leftPartialTileWidth, rect.y, centreWidth, topPartialTileHeight), // Top Right nsIntRect(rect.XMost() - rightPartialTileWidth, rect.y, rightPartialTileWidth, topPartialTileHeight), // Left Middle nsIntRect(rect.x, rect.y + topPartialTileHeight, leftPartialTileWidth, centreHeight), // Centre nsIntRect(rect.x + leftPartialTileWidth, rect.y + topPartialTileHeight, centreWidth, centreHeight), // Right Middle nsIntRect(rect.XMost() - rightPartialTileWidth, rect.y + topPartialTileHeight, rightPartialTileWidth, centreHeight), // Bottom Left nsIntRect(rect.x, rect.YMost() - bottomPartialTileHeight, leftPartialTileWidth, bottomPartialTileHeight), // Bottom Middle nsIntRect(rect.x + leftPartialTileWidth, rect.YMost() - bottomPartialTileHeight, centreWidth, bottomPartialTileHeight), // Bottom Right nsIntRect(rect.XMost() - rightPartialTileWidth, rect.YMost() - bottomPartialTileHeight, rightPartialTileWidth, bottomPartialTileHeight) }; nsIntRect tileRects[] = { // Top Left nsIntRect(tile.XMost() - leftPartialTileWidth, tile.YMost() - topPartialTileHeight, leftPartialTileWidth, topPartialTileHeight), // Top Middle nsIntRect(tile.x, tile.YMost() - topPartialTileHeight, tile.width, topPartialTileHeight), // Top Right nsIntRect(tile.x, tile.YMost() - topPartialTileHeight, rightPartialTileWidth, topPartialTileHeight), // Left Middle nsIntRect(tile.XMost() - leftPartialTileWidth, tile.y, leftPartialTileWidth, tile.height), // Centre nsIntRect(tile.x, tile.y, tile.width, tile.height), // Right Middle nsIntRect(tile.x, tile.y, rightPartialTileWidth, tile.height), // Bottom Left nsIntRect(tile.XMost() - leftPartialTileWidth, tile.y, leftPartialTileWidth, bottomPartialTileHeight), // Bottom Middle nsIntRect(tile.x, tile.y, tile.width, bottomPartialTileHeight), // Bottom Right nsIntRect(tile.x, tile.y, rightPartialTileWidth, bottomPartialTileHeight) }; for (uint32_t i = 0; i < ArrayLength(targetRects); ++i) { TilePixels(targetData, sourceData, targetRects[i], tileRects[i], stride); } return NS_OK; }
/** * Box blur involves looking at one pixel, and setting its value to the average * of its neighbouring pixels. * @param aInput The input buffer. * @param aOutput The output buffer. * @param aLeftLobe The number of pixels to blend on the left. * @param aRightLobe The number of pixels to blend on the right. * @param aWidth The number of columns in the buffers. * @param aRows The number of rows in the buffers. * @param aSkipRect An area to skip blurring in. * XXX shouldn't we pass stride in separately here? */ static void BoxBlurHorizontal(unsigned char* aInput, unsigned char* aOutput, PRInt32 aLeftLobe, PRInt32 aRightLobe, PRInt32 aWidth, PRInt32 aRows, const nsIntRect& aSkipRect) { NS_ASSERTION(aWidth > 0, "Can't handle zero width here"); PRInt32 boxSize = aLeftLobe + aRightLobe + 1; PRBool skipRectCoversWholeRow = 0 >= aSkipRect.x && aWidth <= aSkipRect.XMost(); for (PRInt32 y = 0; y < aRows; y++) { // Check whether the skip rect intersects this row. If the skip // rect covers the whole surface in this row, we can avoid // this row entirely (and any others along the skip rect). PRBool inSkipRectY = y >= aSkipRect.y && y < aSkipRect.YMost(); if (inSkipRectY && skipRectCoversWholeRow) { y = aSkipRect.YMost() - 1; continue; } PRInt32 alphaSum = 0; for (PRInt32 i = 0; i < boxSize; i++) { PRInt32 pos = i - aLeftLobe; // See assertion above; if aWidth is zero, then we would have no // valid position to clamp to. pos = NS_MAX(pos, 0); pos = NS_MIN(pos, aWidth - 1); alphaSum += aInput[aWidth * y + pos]; } for (PRInt32 x = 0; x < aWidth; x++) { // Check whether we are within the skip rect. If so, go // to the next point outside the skip rect. if (inSkipRectY && x >= aSkipRect.x && x < aSkipRect.XMost()) { x = aSkipRect.XMost(); if (x >= aWidth) break; // Recalculate the neighbouring alpha values for // our new point on the surface. alphaSum = 0; for (PRInt32 i = 0; i < boxSize; i++) { PRInt32 pos = x + i - aLeftLobe; // See assertion above; if aWidth is zero, then we would have no // valid position to clamp to. pos = NS_MAX(pos, 0); pos = NS_MIN(pos, aWidth - 1); alphaSum += aInput[aWidth * y + pos]; } } PRInt32 tmp = x - aLeftLobe; PRInt32 last = NS_MAX(tmp, 0); PRInt32 next = NS_MIN(tmp + boxSize, aWidth - 1); aOutput[aWidth * y + x] = alphaSum/boxSize; alphaSum += aInput[aWidth * y + next] - aInput[aWidth * y + last]; } } }
nsresult nsThebesImage::ThebesDrawTile(gfxContext *thebesContext, nsIDeviceContext* dx, const gfxPoint& offset, const gfxRect& targetRect, const nsIntRect& aSubimageRect, const PRInt32 xPadding, const PRInt32 yPadding) { NS_ASSERTION(xPadding >= 0 && yPadding >= 0, "negative padding"); if (targetRect.size.width <= 0.0 || targetRect.size.height <= 0.0) return NS_OK; // don't do anything if we have a transparent pixel source if (mSinglePixel && mSinglePixelColor.a == 0.0) return NS_OK; PRBool doSnap = !(thebesContext->CurrentMatrix().HasNonTranslation()); PRBool hasPadding = ((xPadding != 0) || (yPadding != 0)); gfxImageSurface::gfxImageFormat format = mFormat; gfxPoint tmpOffset = offset; if (mSinglePixel && !hasPadding) { thebesContext->SetDeviceColor(mSinglePixelColor); } else { nsRefPtr<gfxASurface> surface; PRInt32 width, height; if (hasPadding) { /* Ugh we have padding; create a temporary surface that's the size of the surface + pad area, * and render the image into it first. Then we'll tile that surface. */ width = mWidth + xPadding; height = mHeight + yPadding; // Reject over-wide or over-tall images. if (!AllowedImageSize(width, height)) return NS_ERROR_FAILURE; format = gfxASurface::ImageFormatARGB32; surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface( gfxIntSize(width, height), format); if (!surface || surface->CairoStatus()) { return NS_ERROR_OUT_OF_MEMORY; } gfxContext tmpContext(surface); if (mSinglePixel) { tmpContext.SetDeviceColor(mSinglePixelColor); } else { tmpContext.SetSource(ThebesSurface()); } tmpContext.SetOperator(gfxContext::OPERATOR_SOURCE); tmpContext.Rectangle(gfxRect(0, 0, mWidth, mHeight)); tmpContext.Fill(); } else { width = mWidth; height = mHeight; surface = ThebesSurface(); } // Scale factor to account for CSS pixels; note that the offset (and // therefore p0) is in device pixels, while the width and height are in // CSS pixels. gfxFloat scale = gfxFloat(dx->AppUnitsPerDevPixel()) / gfxFloat(nsIDeviceContext::AppUnitsPerCSSPixel()); if ((aSubimageRect.width < width || aSubimageRect.height < height) && (thebesContext->CurrentMatrix().HasNonTranslation() || scale != 1.0)) { // Some of the source image should not be drawn, and we're going // to be doing more than just translation, so we might accidentally // sample the non-drawn pixels. Avoid that by creating a // temporary image representing the portion that will be drawn, // with built-in padding since we can't use EXTEND_PAD and // EXTEND_REPEAT at the same time for different axes. PRInt32 padX = aSubimageRect.width < width ? 1 : 0; PRInt32 padY = aSubimageRect.height < height ? 1 : 0; PRInt32 tileWidth = PR_MIN(aSubimageRect.width, width); PRInt32 tileHeight = PR_MIN(aSubimageRect.height, height); // This tmpSurface will contain a snapshot of the repeated // tile image at (aSubimageRect.x, aSubimageRect.y, // tileWidth, tileHeight), with padX padding added to the left // and right sides and padY padding added to the top and bottom // sides. nsRefPtr<gfxASurface> tmpSurface; tmpSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface( gfxIntSize(tileWidth + 2*padX, tileHeight + 2*padY), format); if (!tmpSurface || tmpSurface->CairoStatus()) { return NS_ERROR_OUT_OF_MEMORY; } gfxContext tmpContext(tmpSurface); tmpContext.SetOperator(gfxContext::OPERATOR_SOURCE); gfxPattern pat(surface); pat.SetExtend(gfxPattern::EXTEND_REPEAT); // Copy the needed portion of the source image to the temporary // surface. We also copy over horizontal and/or vertical padding // strips one pixel wide, plus the corner pixels if necessary. // So in the most general case the temporary surface ends up // looking like // P P P ... P P P // P X X ... X X P // P X X ... X X P // ............... // P X X ... X X P // P X X ... X X P // P P P ... P P P // Where each P pixel has the color of its nearest source X // pixel. We implement this as a loop over all nine possible // areas, [padding, body, padding] x [padding, body, padding]. // Note that we will not need padding on both axes unless // we are painting just a single tile, in which case this // will hardly ever get called since nsCSSRendering converts // the single-tile case to nsLayoutUtils::DrawImage. But this // could be called on other paths (XUL trees?) and it's simpler // and clearer to do it the general way. PRInt32 destY = 0; for (PRInt32 y = -1; y <= 1; ++y) { PRInt32 stripHeight = y == 0 ? tileHeight : padY; if (stripHeight == 0) continue; PRInt32 srcY = y == 1 ? aSubimageRect.YMost() - padY : aSubimageRect.y; PRInt32 destX = 0; for (PRInt32 x = -1; x <= 1; ++x) { PRInt32 stripWidth = x == 0 ? tileWidth : padX; if (stripWidth == 0) continue; PRInt32 srcX = x == 1 ? aSubimageRect.XMost() - padX : aSubimageRect.x; gfxMatrix patMat; patMat.Translate(gfxPoint(srcX - destX, srcY - destY)); pat.SetMatrix(patMat); tmpContext.SetPattern(&pat); tmpContext.Rectangle(gfxRect(destX, destY, stripWidth, stripHeight)); tmpContext.Fill(); tmpContext.NewPath(); destX += stripWidth; } destY += stripHeight; } // tmpOffset was the top-left of the old tile image. Make it // the top-left of the new tile image. Note that tmpOffset is // in destination coordinate space so we have to scale our // CSS pixels. tmpOffset += gfxPoint(aSubimageRect.x - padX, aSubimageRect.y - padY)/scale; surface = tmpSurface; } gfxMatrix patMat; gfxPoint p0; p0.x = - floor(tmpOffset.x + 0.5); p0.y = - floor(tmpOffset.y + 0.5); patMat.Scale(scale, scale); patMat.Translate(p0); gfxPattern pat(surface); pat.SetExtend(gfxPattern::EXTEND_REPEAT); pat.SetMatrix(patMat); #ifndef XP_MACOSX if (scale < 1.0) { // See bug 324698. This is a workaround. See comments // by the earlier SetFilter call. pat.SetFilter(0); } #endif thebesContext->SetPattern(&pat); } gfxContext::GraphicsOperator op = thebesContext->CurrentOperator(); if (op == gfxContext::OPERATOR_OVER && format == gfxASurface::ImageFormatRGB24) thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE); thebesContext->NewPath(); thebesContext->Rectangle(targetRect, doSnap); thebesContext->Fill(); thebesContext->SetOperator(op); thebesContext->SetDeviceColor(gfxRGBA(0,0,0,0)); return NS_OK; }
nsresult SVGFEMorphologyElement::Filter(nsSVGFilterInstance* instance, const nsTArray<const Image*>& aSources, const Image* aTarget, const nsIntRect& rect) { int32_t rx, ry; GetRXY(&rx, &ry, *instance); if (rx < 0 || ry < 0) { // XXX SVGContentUtils::ReportToConsole() return NS_OK; } if (rx == 0 && ry == 0) { return NS_OK; } // Clamp radii to prevent completely insane values: rx = std::min(rx, 100000); ry = std::min(ry, 100000); uint8_t* sourceData = aSources[0]->mImage->Data(); uint8_t* targetData = aTarget->mImage->Data(); int32_t stride = aTarget->mImage->Stride(); uint8_t extrema[4]; // RGBA magnitude of extrema uint16_t op = mEnumAttributes[OPERATOR].GetAnimValue(); // Scan the kernel for each pixel to determine max/min RGBA values. for (int32_t y = rect.y; y < rect.YMost(); y++) { int32_t startY = std::max(0, y - ry); // We need to read pixels not just in 'rect', which is limited to // the dirty part of our filter primitive subregion, but all pixels in // the given radii from the source surface, so use the surface size here. int32_t endY = std::min(y + ry, instance->GetSurfaceHeight() - 1); for (int32_t x = rect.x; x < rect.XMost(); x++) { int32_t startX = std::max(0, x - rx); int32_t endX = std::min(x + rx, instance->GetSurfaceWidth() - 1); int32_t targIndex = y * stride + 4 * x; for (int32_t i = 0; i < 4; i++) { extrema[i] = sourceData[targIndex + i]; } for (int32_t y1 = startY; y1 <= endY; y1++) { for (int32_t x1 = startX; x1 <= endX; x1++) { for (int32_t i = 0; i < 4; i++) { uint8_t pixel = sourceData[y1 * stride + 4 * x1 + i]; if ((extrema[i] > pixel && op == SVG_OPERATOR_ERODE) || (extrema[i] < pixel && op == SVG_OPERATOR_DILATE)) { extrema[i] = pixel; } } } } targetData[targIndex ] = extrema[0]; targetData[targIndex+1] = extrema[1]; targetData[targIndex+2] = extrema[2]; targetData[targIndex+3] = extrema[3]; } } return NS_OK; }