/* static */ bool HwcUtils::PrepareLayerRects(nsIntRect aVisible, const gfx::Matrix& aLayerTransform, const gfx::Matrix& aLayerBufferTransform, nsIntRect aClip, nsIntRect aBufferRect, bool aYFlipped, hwc_rect_t* aSourceCrop, hwc_rect_t* aVisibleRegionScreen) { gfxMatrix aTransform = gfx::ThebesMatrix(aLayerTransform); gfxRect visibleRect(aVisible); gfxRect clip(aClip); gfxRect visibleRectScreen = aTransform.TransformBounds(visibleRect); // |clip| is guaranteed to be integer visibleRectScreen.IntersectRect(visibleRectScreen, clip); if (visibleRectScreen.IsEmpty()) { return false; } gfxMatrix inverse = gfx::ThebesMatrix(aLayerBufferTransform); inverse.Invert(); gfxRect crop = inverse.TransformBounds(visibleRectScreen); //clip to buffer size crop.IntersectRect(crop, aBufferRect); crop.Round(); if (crop.IsEmpty()) { return false; } //propagate buffer clipping back to visible rect gfxMatrix layerBufferTransform = gfx::ThebesMatrix(aLayerBufferTransform); visibleRectScreen = layerBufferTransform.TransformBounds(crop); visibleRectScreen.Round(); // Map from layer space to buffer space crop -= aBufferRect.TopLeft(); if (aYFlipped) { crop.y = aBufferRect.height - (crop.y + crop.height); } aSourceCrop->left = crop.x; aSourceCrop->top = crop.y; aSourceCrop->right = crop.x + crop.width; aSourceCrop->bottom = crop.y + crop.height; aVisibleRegionScreen->left = visibleRectScreen.x; aVisibleRegionScreen->top = visibleRectScreen.y; aVisibleRegionScreen->right = visibleRectScreen.x + visibleRectScreen.width; aVisibleRegionScreen->bottom = visibleRectScreen.y + visibleRectScreen.height; return true; }
void gfxASurface::FastMovePixels(const nsIntRect& aSourceRect, const nsIntPoint& aDestTopLeft) { // Used when the backend can internally handle self copies. nsIntRect dest(aDestTopLeft, aSourceRect.Size()); nsRefPtr<gfxContext> ctx = new gfxContext(this); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); nsIntPoint srcOrigin = dest.TopLeft() - aSourceRect.TopLeft(); ctx->SetSource(this, gfxPoint(srcOrigin.x, srcOrigin.y)); ctx->Rectangle(gfxRect(dest.x, dest.y, dest.width, dest.height)); ctx->Fill(); }
/** * Sets hwc layer rectangles required for hwc composition * * @param aVisible Input. Layer's unclipped visible rectangle * The origin is the top-left corner of the layer * @param aTransform Input. Layer's transformation matrix * It transforms from layer space to screen space * @param aClip Input. A clipping rectangle. * The origin is the top-left corner of the screen * @param aBufferRect Input. The layer's buffer bounds * The origin is the top-left corner of the layer * @param aSurceCrop Output. Area of the source to consider, * the origin is the top-left corner of the buffer * @param aVisibleRegionScreen Output. Visible region in screen space. * The origin is the top-left corner of the screen * @return true if the layer should be rendered. * false if the layer can be skipped */ static bool PrepareLayerRects(nsIntRect aVisible, const gfxMatrix& aTransform, nsIntRect aClip, nsIntRect aBufferRect, hwc_rect_t* aSourceCrop, hwc_rect_t* aVisibleRegionScreen) { gfxRect visibleRect(aVisible); gfxRect clip(aClip); gfxRect visibleRectScreen = aTransform.TransformBounds(visibleRect); // |clip| is guaranteed to be integer visibleRectScreen.IntersectRect(visibleRectScreen, clip); if (visibleRectScreen.IsEmpty()) { LOGD("Skip layer"); return false; } gfxMatrix inverse(aTransform); inverse.Invert(); gfxRect crop = inverse.TransformBounds(visibleRectScreen); //clip to buffer size crop.IntersectRect(crop, aBufferRect); crop.RoundOut(); if (crop.IsEmpty()) { LOGD("Skip layer"); return false; } //propagate buffer clipping back to visible rect visibleRectScreen = aTransform.TransformBounds(crop); visibleRectScreen.RoundOut(); // Map from layer space to buffer space crop -= aBufferRect.TopLeft(); aSourceCrop->left = crop.x; aSourceCrop->top = crop.y; aSourceCrop->right = crop.x + crop.width; aSourceCrop->bottom = crop.y + crop.height; aVisibleRegionScreen->left = visibleRectScreen.x; aVisibleRegionScreen->top = visibleRectScreen.y; aVisibleRegionScreen->right = visibleRectScreen.x + visibleRectScreen.width; aVisibleRegionScreen->bottom = visibleRectScreen.y + visibleRectScreen.height; return true; }
void GrallocTextureHostBasic::SetCropRect(nsIntRect aCropRect) { MOZ_ASSERT(aCropRect.TopLeft() == gfx::IntPoint(0, 0)); MOZ_ASSERT(!aCropRect.IsEmpty()); MOZ_ASSERT(aCropRect.width <= mSize.width); MOZ_ASSERT(aCropRect.height <= mSize.height); gfx::IntSize cropSize(aCropRect.width, aCropRect.height); if (mCropSize == cropSize) { return; } mCropSize = cropSize; ClearTextureSource(); }
void GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect, TextureImage *aDst, const nsIntRect& aDstRect) { GLContext *gl = mCompositor->gl(); NS_ASSERTION(!aSrc->InUpdate(), "Source texture is in update!"); NS_ASSERTION(!aDst->InUpdate(), "Destination texture is in update!"); if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty()) return; int savedFb = 0; gl->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb); ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false); ScopedGLState scopedBlendState(gl, LOCAL_GL_BLEND, false); // 2.0 means scale up by two float blitScaleX = float(aDstRect.width) / float(aSrcRect.width); float blitScaleY = float(aDstRect.height) / float(aSrcRect.height); // We start iterating over all destination tiles aDst->BeginBigImageIteration(); do { // calculate portion of the tile that is going to be painted to nsIntRect dstSubRect; nsIntRect dstTextureRect = ThebesIntRect(aDst->GetTileRect()); dstSubRect.IntersectRect(aDstRect, dstTextureRect); // this tile is not part of the destination rectangle aDstRect if (dstSubRect.IsEmpty()) continue; // (*) transform the rect of this tile into the rectangle defined by aSrcRect... nsIntRect dstInSrcRect(dstSubRect); dstInSrcRect.MoveBy(-aDstRect.TopLeft()); // ...which might be of different size, hence scale accordingly dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY); dstInSrcRect.MoveBy(aSrcRect.TopLeft()); SetBlitFramebufferForDestTexture(aDst->GetTextureID()); UseBlitProgram(); aSrc->BeginBigImageIteration(); // now iterate over all tiles in the source Image... do { // calculate portion of the source tile that is in the source rect nsIntRect srcSubRect; nsIntRect srcTextureRect = ThebesIntRect(aSrc->GetTileRect()); srcSubRect.IntersectRect(aSrcRect, srcTextureRect); // this tile is not part of the source rect if (srcSubRect.IsEmpty()) { continue; } // calculate intersection of source rect with destination rect srcSubRect.IntersectRect(srcSubRect, dstInSrcRect); // this tile does not overlap the current destination tile if (srcSubRect.IsEmpty()) { continue; } // We now have the intersection of // the current source tile // and the desired source rectangle // and the destination tile // and the desired destination rectange // in destination space. // We need to transform this back into destination space, inverting the transform from (*) nsIntRect srcSubInDstRect(srcSubRect); srcSubInDstRect.MoveBy(-aSrcRect.TopLeft()); srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY); srcSubInDstRect.MoveBy(aDstRect.TopLeft()); // we transform these rectangles to be relative to the current src and dst tiles, respectively nsIntSize srcSize = srcTextureRect.Size(); nsIntSize dstSize = dstTextureRect.Size(); srcSubRect.MoveBy(-srcTextureRect.x, -srcTextureRect.y); srcSubInDstRect.MoveBy(-dstTextureRect.x, -dstTextureRect.y); float dx0 = 2.0f * float(srcSubInDstRect.x) / float(dstSize.width) - 1.0f; float dy0 = 2.0f * float(srcSubInDstRect.y) / float(dstSize.height) - 1.0f; float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f; float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f; ScopedViewportRect autoViewportRect(gl, 0, 0, dstSize.width, dstSize.height); RectTriangles rects; nsIntSize realTexSize = srcSize; if (!CanUploadNonPowerOfTwo(gl)) { realTexSize = nsIntSize(gfx::NextPowerOfTwo(srcSize.width), gfx::NextPowerOfTwo(srcSize.height)); } if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) { rects.addRect(/* dest rectangle */ dx0, dy0, dx1, dy1, /* tex coords */ srcSubRect.x / float(realTexSize.width), srcSubRect.y / float(realTexSize.height), srcSubRect.XMost() / float(realTexSize.width), srcSubRect.YMost() / float(realTexSize.height)); } else { DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects); // now put the coords into the d[xy]0 .. d[xy]1 coordinate space // from the 0..1 that it comes out of decompose InfallibleTArray<RectTriangles::coord>& coords = rects.vertCoords(); for (unsigned int i = 0; i < coords.Length(); ++i) { coords[i].x = (coords[i].x * (dx1 - dx0)) + dx0; coords[i].y = (coords[i].y * (dy1 - dy0)) + dy0; } } ScopedBindTextureUnit autoTexUnit(gl, LOCAL_GL_TEXTURE0); ScopedBindTexture autoTex(gl, aSrc->GetTextureID()); ScopedVertexAttribPointer autoAttrib0(gl, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.vertCoords().Elements()); ScopedVertexAttribPointer autoAttrib1(gl, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.texCoords().Elements()); gl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements()); } while (aSrc->NextTile()); } while (aDst->NextTile()); // unbind the previous texture from the framebuffer SetBlitFramebufferForDestTexture(0); gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb); }
void gfxImageSurface::MovePixels(const nsIntRect& aSourceRect, const nsIntPoint& aDestTopLeft) { const nsIntRect bounds(0, 0, mSize.width, mSize.height); nsIntPoint offset = aDestTopLeft - aSourceRect.TopLeft(); nsIntRect clippedSource = aSourceRect; clippedSource.IntersectRect(clippedSource, bounds); nsIntRect clippedDest = clippedSource + offset; clippedDest.IntersectRect(clippedDest, bounds); const nsIntRect dest = clippedDest; const nsIntRect source = dest - offset; // NB: this relies on IntersectRect() and operator+/- preserving // x/y for empty rectangles NS_ABORT_IF_FALSE(bounds.Contains(dest) && bounds.Contains(source) && aSourceRect.Contains(source) && nsIntRect(aDestTopLeft, aSourceRect.Size()).Contains(dest) && source.Size() == dest.Size() && offset == (dest.TopLeft() - source.TopLeft()), "Messed up clipping, crash or corruption will follow"); if (source.IsEmpty() || source.IsEqualInterior(dest)) { return; } long naturalStride = ComputeStride(mSize, mFormat); if (mStride == naturalStride && dest.width == bounds.width) { // Fast path: this is a vertical shift of some rows in a // "normal" image surface. We can directly memmove and // hopefully stay in SIMD land. unsigned char* dst = mData + dest.y * mStride; const unsigned char* src = mData + source.y * mStride; size_t nBytes = dest.height * mStride; memmove(dst, src, nBytes); return; } // Slow(er) path: have to move row-by-row. const int32_t bpp = BytePerPixelFromFormat(mFormat); const size_t nRowBytes = dest.width * bpp; // dstRow points at the first pixel within the current destination // row, and similarly for srcRow. endSrcRow is one row beyond the // last row we need to copy. stride is either +mStride or // -mStride, depending on which direction we're copying. unsigned char* dstRow; unsigned char* srcRow; unsigned char* endSrcRow; // NB: this may point outside the image long stride; if (dest.y > source.y) { // We're copying down from source to dest, so walk backwards // starting from the last rows to avoid stomping pixels we // need. stride = -mStride; dstRow = mData + dest.x * bpp + (dest.YMost() - 1) * mStride; srcRow = mData + source.x * bpp + (source.YMost() - 1) * mStride; endSrcRow = mData + source.x * bpp + (source.y - 1) * mStride; } else { stride = mStride; dstRow = mData + dest.x * bpp + dest.y * mStride; srcRow = mData + source.x * bpp + source.y * mStride; endSrcRow = mData + source.x * bpp + source.YMost() * mStride; } for (; srcRow != endSrcRow; dstRow += stride, srcRow += stride) { memmove(dstRow, srcRow, nRowBytes); } }
void gfxXlibNativeRenderer::DrawFallback(DrawTarget* drawTarget, gfxContext* ctx, gfxASurface* target, nsIntSize& size, nsIntRect& drawingRect, bool canDrawOverBackground, uint32_t flags, Screen* screen, Visual* visual, DrawOutput* result) { gfxPoint offset(drawingRect.x, drawingRect.y); DrawingMethod method; nsRefPtr<gfxXlibSurface> tempXlibSurface = CreateTempXlibSurface(target, drawingRect.Size(), canDrawOverBackground, flags, screen, visual, &method); if (!tempXlibSurface) return; if (drawingRect.Size() != size || method == eCopyBackground) { // Only drawing a portion, or copying background, // so won't return a result. result = nullptr; } nsRefPtr<gfxContext> tmpCtx; bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0; if (!drawIsOpaque) { tmpCtx = new gfxContext(tempXlibSurface); if (method == eCopyBackground) { tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE); tmpCtx->SetSource(target, -(offset + ctx->CurrentMatrix().GetTranslation())); // The copy from the tempXlibSurface to the target context should // use operator SOURCE, but that would need a mask to bound the // operation. Here we only copy opaque backgrounds so operator // OVER will behave like SOURCE masked by the surface. NS_ASSERTION(tempXlibSurface->GetContentType() == GFX_CONTENT_COLOR, "Don't copy background with a transparent surface"); } else { tmpCtx->SetOperator(gfxContext::OPERATOR_CLEAR); } tmpCtx->Paint(); } if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) { return; } if (method != eAlphaExtraction) { if (drawTarget) { RefPtr<SourceSurface> sourceSurface = gfxPlatform::GetPlatform()-> GetSourceSurfaceForSurface(drawTarget, tempXlibSurface); drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } else { ctx->SetSource(tempXlibSurface, offset); ctx->Paint(); } if (result) { result->mSurface = tempXlibSurface; /* fill in the result with what we know, which is really just what our assumption was */ result->mUniformAlpha = true; result->mColor.a = 1.0; } return; } nsRefPtr<gfxImageSurface> blackImage = CopyXlibSurfaceToImage(tempXlibSurface, gfxImageFormatARGB32); tmpCtx->SetDeviceColor(gfxRGBA(1.0, 1.0, 1.0)); tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE); tmpCtx->Paint(); DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft()); nsRefPtr<gfxImageSurface> whiteImage = CopyXlibSurfaceToImage(tempXlibSurface, gfxImageFormatRGB24); if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS && whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) { gfxAlphaRecovery::Analysis analysis; if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage, result ? &analysis : nullptr)) return; gfxASurface* paintSurface = blackImage; /* if the caller wants to retrieve the rendered image, put it into a 'similar' surface, and use that as the source for the drawing right now. This means we always return a surface similar to the surface used for 'cr', which is ideal if it's going to be cached and reused. We do not return an image if the result has uniform color (including alpha). */ if (result) { if (analysis.uniformAlpha) { result->mUniformAlpha = true; result->mColor.a = analysis.alpha; } if (analysis.uniformColor) { result->mUniformColor = true; result->mColor.r = analysis.r; result->mColor.g = analysis.g; result->mColor.b = analysis.b; } else { result->mSurface = target-> CreateSimilarSurface(GFX_CONTENT_COLOR_ALPHA, gfxIntSize(size.width, size.height)); gfxContext copyCtx(result->mSurface); copyCtx.SetSource(blackImage); copyCtx.SetOperator(gfxContext::OPERATOR_SOURCE); copyCtx.Paint(); paintSurface = result->mSurface; } } if (drawTarget) { RefPtr<SourceSurface> sourceSurface = gfxPlatform::GetPlatform()-> GetSourceSurfaceForSurface(drawTarget, paintSurface); drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } else { ctx->SetSource(paintSurface, offset); ctx->Paint(); } } }