already_AddRefed<DrawTarget> nsShmImage::CreateDrawTarget(const mozilla::LayoutDeviceIntRegion& aRegion) { // Wait for any in-flight requests to complete. // Typically X clients would wait for a XShmCompletionEvent to be received, // but this works as it's sent immediately after the request is processed. xcb_generic_error_t* error; if (mLastRequest.sequence != XCB_NONE && (error = xcb_request_check(mConnection, mLastRequest))) { gShmAvailable = false; free(error); return nullptr; } // Due to bug 1205045, we must avoid making GTK calls off the main thread to query window size. // Instead we just track the largest offset within the image we are drawing to and grow the image // to accomodate it. Since usually the entire window is invalidated on the first paint to it, // this should grow the image to the necessary size quickly without many intermediate reallocations. IntRect bounds = aRegion.GetBounds().ToUnknownRect(); IntSize size(bounds.XMost(), bounds.YMost()); if (size.width > mSize.width || size.height > mSize.height) { DestroyImage(); if (!CreateImage(size)) { return nullptr; } } return gfxPlatform::GetPlatform()->CreateDrawTargetForData( reinterpret_cast<unsigned char*>(mShmAddr) + BytesPerPixel(mFormat) * (bounds.y * mSize.width + bounds.x), bounds.Size(), BytesPerPixel(mFormat) * mSize.width, mFormat); }
void DrawEventRecorderMemory::FlushItem(IntRect aRect) { MOZ_RELEASE_ASSERT(!aRect.IsEmpty()); // Detaching our existing resources will add some // destruction events to our stream so we need to do that // first. DetachResources(); // See moz2d_renderer.rs for a description of the stream format WriteElement(mIndex, mOutputStream.mLength); // write out the fonts into the extra data section mSerializeCallback(mOutputStream, mScaledFonts); WriteElement(mIndex, mOutputStream.mLength); WriteElement(mIndex, aRect.x); WriteElement(mIndex, aRect.y); WriteElement(mIndex, aRect.XMost()); WriteElement(mIndex, aRect.YMost()); ClearResources(); // write out a new header for the next recording in the stream WriteHeader(mOutputStream); }
already_AddRefed<CompositingRenderTarget> BasicCompositor::CreateRenderTargetForWindow(const IntRect& aRect, SurfaceInitMode aInit, BufferMode aBufferMode) { if (aBufferMode != BufferMode::BUFFER_NONE) { return CreateRenderTarget(aRect, aInit); } MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); if (aRect.width * aRect.height == 0) { return nullptr; } MOZ_ASSERT(mDrawTarget); // Adjust bounds rect to account for new origin at (0, 0). IntRect rect(0, 0, aRect.XMost(), aRect.YMost()); RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(mDrawTarget, rect); if (aInit == INIT_MODE_CLEAR) { mDrawTarget->ClearRect(gfx::Rect(aRect)); } return rt.forget(); }
void CheckGeneratedImage(Decoder* aDecoder, const IntRect& aRect, uint8_t aFuzz /* = 0 */) { RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); const IntSize surfaceSize = surface->GetSize(); // This diagram shows how the surface is divided into regions that the code // below tests for the correct content. The output rect is the bounds of the // region labeled 'C'. // // +---------------------------+ // | A | // +---------+--------+--------+ // | B | C | D | // +---------+--------+--------+ // | E | // +---------------------------+ // Check that the output rect itself is green. (Region 'C'.) EXPECT_TRUE(RectIsSolidColor(surface, aRect, BGRAColor::Green(), aFuzz)); // Check that the area above the output rect is transparent. (Region 'A'.) EXPECT_TRUE(RectIsSolidColor(surface, IntRect(0, 0, surfaceSize.width, aRect.y), BGRAColor::Transparent(), aFuzz)); // Check that the area to the left of the output rect is transparent. (Region 'B'.) EXPECT_TRUE(RectIsSolidColor(surface, IntRect(0, aRect.y, aRect.x, aRect.YMost()), BGRAColor::Transparent(), aFuzz)); // Check that the area to the right of the output rect is transparent. (Region 'D'.) const int32_t widthOnRight = surfaceSize.width - aRect.XMost(); EXPECT_TRUE(RectIsSolidColor(surface, IntRect(aRect.XMost(), aRect.y, widthOnRight, aRect.YMost()), BGRAColor::Transparent(), aFuzz)); // Check that the area below the output rect is transparent. (Region 'E'.) const int32_t heightBelow = surfaceSize.height - aRect.YMost(); EXPECT_TRUE(RectIsSolidColor(surface, IntRect(0, aRect.YMost(), surfaceSize.width, heightBelow), BGRAColor::Transparent(), aFuzz)); }
void CheckGeneratedPalettedImage(Decoder* aDecoder, const IntRect& aRect) { RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); IntSize imageSize = currentFrame->GetImageSize(); // This diagram shows how the surface is divided into regions that the code // below tests for the correct content. The output rect is the bounds of the // region labeled 'C'. // // +---------------------------+ // | A | // +---------+--------+--------+ // | B | C | D | // +---------+--------+--------+ // | E | // +---------------------------+ // Check that the output rect itself is all 255's. (Region 'C'.) EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, aRect, 255)); // Check that the area above the output rect is all 0's. (Region 'A'.) EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, IntRect(0, 0, imageSize.width, aRect.y), 0)); // Check that the area to the left of the output rect is all 0's. (Region 'B'.) EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, IntRect(0, aRect.y, aRect.x, aRect.YMost()), 0)); // Check that the area to the right of the output rect is all 0's. (Region 'D'.) const int32_t widthOnRight = imageSize.width - aRect.XMost(); EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, IntRect(aRect.XMost(), aRect.y, widthOnRight, aRect.YMost()), 0)); // Check that the area below the output rect is transparent. (Region 'E'.) const int32_t heightBelow = imageSize.height - aRect.YMost(); EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, IntRect(0, aRect.YMost(), imageSize.width, heightBelow), 0)); }
void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntRect &aValue) { if (mType == FilterType::TURBULENCE) { MOZ_ASSERT(aIndex == ATT_TURBULENCE_RECT); mEffect->SetValue(D2D1_TURBULENCE_PROP_OFFSET, D2D1::Vector2F(Float(aValue.X()), Float(aValue.Y()))); mEffect->SetValue(D2D1_TURBULENCE_PROP_SIZE, D2D1::Vector2F(Float(aValue.Width()), Float(aValue.Height()))); return; } UINT32 input = GetD2D1PropForAttribute(mType, aIndex); MOZ_ASSERT(input < mEffect->GetPropertyCount()); mEffect->SetValue(input, D2D1::RectF(Float(aValue.X()), Float(aValue.Y()), Float(aValue.XMost()), Float(aValue.YMost()))); }
bool RectIsSolidColor(SourceSurface* aSurface, const IntRect& aRect, BGRAColor aColor, uint8_t aFuzz /* = 0 */) { IntSize surfaceSize = aSurface->GetSize(); IntRect rect = aRect.Intersect(IntRect(0, 0, surfaceSize.width, surfaceSize.height)); RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface(); ASSERT_TRUE_OR_RETURN(dataSurface != nullptr, false); ASSERT_EQ_OR_RETURN(dataSurface->Stride(), surfaceSize.width * 4, false); DataSourceSurface::ScopedMap mapping(dataSurface, DataSourceSurface::MapType::READ); ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false); uint8_t* data = dataSurface->GetData(); ASSERT_TRUE_OR_RETURN(data != nullptr, false); int32_t rowLength = dataSurface->Stride(); for (int32_t row = rect.y; row < rect.YMost(); ++row) { for (int32_t col = rect.x; col < rect.XMost(); ++col) { int32_t i = row * rowLength + col * 4; if (aFuzz != 0) { ASSERT_LE_OR_RETURN(abs(aColor.mBlue - data[i + 0]), aFuzz, false); ASSERT_LE_OR_RETURN(abs(aColor.mGreen - data[i + 1]), aFuzz, false); ASSERT_LE_OR_RETURN(abs(aColor.mRed - data[i + 2]), aFuzz, false); ASSERT_LE_OR_RETURN(abs(aColor.mAlpha - data[i + 3]), aFuzz, false); } else { ASSERT_EQ_OR_RETURN(aColor.mBlue, data[i + 0], false); ASSERT_EQ_OR_RETURN(aColor.mGreen, data[i + 1], false); ASSERT_EQ_OR_RETURN(aColor.mRed, data[i + 2], false); ASSERT_EQ_OR_RETURN(aColor.mAlpha, data[i + 3], false); } } } return true; }
void DrawTargetD2D1::CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, const IntPoint &aDestination) { MarkChanged(); mDC->SetTransform(D2D1::IdentityMatrix()); mTransformDirty = true; Matrix mat; RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); if (!mat.IsIdentity()) { gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface."; return; } mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)), D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y), Float(aSourceRect.XMost()), Float(aSourceRect.YMost())), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); }
bool PalettedRectIsSolidColor(Decoder* aDecoder, const IntRect& aRect, uint8_t aColor) { RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); uint8_t* imageData; uint32_t imageLength; currentFrame->GetImageData(&imageData, &imageLength); ASSERT_TRUE_OR_RETURN(imageData, false); // Clamp to the frame rect. If any pixels outside the frame rect are included, // we immediately fail, because such pixels don't have any "color" in the // sense this function measures - they're transparent, and that doesn't // necessarily correspond to any color palette index at all. IntRect frameRect = currentFrame->GetRect(); ASSERT_EQ_OR_RETURN(imageLength, uint32_t(frameRect.Area()), false); IntRect rect = aRect.Intersect(frameRect); ASSERT_EQ_OR_RETURN(rect.Area(), aRect.Area(), false); // Translate |rect| by |frameRect.TopLeft()| to reflect the fact that the // frame rect's offset doesn't actually mean anything in terms of the // in-memory representation of the surface. The image data starts at the upper // left corner of the frame rect, in other words. rect -= frameRect.TopLeft(); // Walk through the image data and make sure that the entire rect has the // palette index |aColor|. int32_t rowLength = frameRect.width; for (int32_t row = rect.y; row < rect.YMost(); ++row) { for (int32_t col = rect.x; col < rect.XMost(); ++col) { int32_t i = row * rowLength + col; ASSERT_EQ_OR_RETURN(aColor, imageData[i], false); } } return true; }
void DeprecatedTextureHostSystemMemD3D9::UpdateImpl(const SurfaceDescriptor& aImage, nsIntRegion *aRegion, nsIntPoint *aOffset) { MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TSurfaceDescriptorD3D9); MOZ_ASSERT(mCompositor, "Must have compositor to update."); if (!mCompositor->device()) { return; } IDirect3DTexture9* texture = reinterpret_cast<IDirect3DTexture9*>(aImage.get_SurfaceDescriptorD3D9().texture()); if (!texture) { Reset(); return; } D3DSURFACE_DESC desc; texture->GetLevelDesc(0, &desc); HRESULT hr = texture->GetLevelDesc(0, &desc); if (FAILED(hr)) { Reset(); return; } mSize.width = desc.Width; mSize.height = desc.Height; _D3DFORMAT format = desc.Format; uint32_t bpp = 0; switch (format) { case D3DFMT_X8R8G8B8: mFormat = SurfaceFormat::B8G8R8X8; bpp = 4; break; case D3DFMT_A8R8G8B8: mFormat = SurfaceFormat::B8G8R8A8; bpp = 4; break; case D3DFMT_A8: mFormat = SurfaceFormat::A8; bpp = 1; break; default: NS_ERROR("Bad image format"); } int32_t maxSize = mCompositor->GetMaxTextureSize(); if (mSize.width <= maxSize && mSize.height <= maxSize) { mIsTiled = false; mTexture = TextureToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), texture, mSize, format); if (!mTexture) { NS_WARNING("Could not upload texture"); Reset(); return; } } else { mIsTiled = true; uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) * GetRequiredTilesD3D9(mSize.height, maxSize); mTileTextures.resize(tileCount); for (uint32_t i = 0; i < tileCount; i++) { IntRect tileRect = GetTileRect(i); RECT d3dTileRect; d3dTileRect.left = tileRect.x; d3dTileRect.top = tileRect.y; d3dTileRect.right = tileRect.XMost(); d3dTileRect.bottom = tileRect.YMost(); D3DLOCKED_RECT lockedRect; texture->LockRect(0, &lockedRect, &d3dTileRect, 0); mTileTextures[i] = DataToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), reinterpret_cast<unsigned char*>(lockedRect.pBits), lockedRect.Pitch, tileRect.Size(), format, bpp); texture->UnlockRect(0); if (!mTileTextures[i]) { NS_WARNING("Could not upload texture"); Reset(); return; } } } }
static void BoxBlur(uint8_t* aData, const int32_t aLobes[3][2], int32_t aWidth, int32_t aRows, int32_t aStride, IntRect aSkipRect) { if (aTranspose) { swap(aWidth, aRows); swap(aSkipRect.x, aSkipRect.y); swap(aSkipRect.width, aSkipRect.height); } MOZ_ASSERT(aWidth > 0); // All three passes of the box blur that approximate the Gaussian are done // on each row in turn, so we only need two temporary row buffers to process // each row, instead of a full-sized buffer. Data moves from the source to the // first temporary, from the first temporary to the second, then from the second // back to the destination. This way is more cache-friendly than processing whe // whole buffer in each pass and thus yields a nice speedup. uint8_t* tmpRow = new (std::nothrow) uint8_t[2 * aWidth]; if (!tmpRow) { return; } uint8_t* tmpRow2 = tmpRow + aWidth; const int32_t stride = aTranspose ? 1 : aStride; bool skipRectCoversWholeRow = 0 >= aSkipRect.x && aWidth <= aSkipRect.XMost(); for (int32_t 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). bool inSkipRectY = y >= aSkipRect.y && y < aSkipRect.YMost(); if (inSkipRectY && skipRectCoversWholeRow) { aData += stride * (aSkipRect.YMost() - y); y = aSkipRect.YMost() - 1; continue; } // Read in data from the source transposed if necessary. BoxBlurRow<aTranspose, false>(aData, tmpRow, aLobes[0][0], aLobes[0][1], aWidth, aStride, 0, aWidth); // For the middle pass, the data is already pre-transposed and does not need to be post-transposed yet. BoxBlurRow<false, false>(tmpRow, tmpRow2, aLobes[1][0], aLobes[1][1], aWidth, aStride, 0, aWidth); // Write back data to the destination transposed if necessary too. // Make sure not to overwrite the skip rect by only outputting to the // destination before and after the skip rect, if requested. int32_t skipStart = inSkipRectY ? min(max(aSkipRect.x, 0), aWidth) : aWidth; int32_t skipEnd = max(skipStart, aSkipRect.XMost()); if (skipStart > 0) { BoxBlurRow<false, aTranspose>(tmpRow2, aData, aLobes[2][0], aLobes[2][1], aWidth, aStride, 0, skipStart); } if (skipEnd < aWidth) { BoxBlurRow<false, aTranspose>(tmpRow2, aData, aLobes[2][0], aLobes[2][1], aWidth, aStride, skipEnd, aWidth); } aData += stride; } delete[] tmpRow; }
// |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 CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg, const Rect& aTexCoordRect, TextureSource *aTexture) { NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized"); GLuint vertAttribIndex = aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib); GLuint texCoordAttribIndex = aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib); NS_ASSERTION(texCoordAttribIndex != GLuint(-1), "no texture coords?"); // 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; GLenum wrapMode = aTexture->AsSourceOGL()->GetWrapMode(); IntSize realTexSize = aTexture->GetSize(); if (!mGLContext->CanUploadNonPowerOfTwo()) { realTexSize = IntSize(NextPowerOfTwo(realTexSize.width), NextPowerOfTwo(realTexSize.height)); } // We need to convert back to actual texels here to get proper behaviour with // our GL helper functions. Should fix this sometime. // I want to vomit. IntRect texCoordRect = IntRect(NS_roundf(aTexCoordRect.x * aTexture->GetSize().width), NS_roundf(aTexCoordRect.y * aTexture->GetSize().height), NS_roundf(aTexCoordRect.width * aTexture->GetSize().width), NS_roundf(aTexCoordRect.height * aTexture->GetSize().height)); // This is fairly disgusting - if the texture should be flipped it will have a // negative height, in which case we un-invert the texture coords and pass the // flipped 'flag' to the functions below. We can't just use the inverted coords // because our GL funtions use an explicit flag. bool flipped = false; if (texCoordRect.height < 0) { flipped = true; texCoordRect.y = texCoordRect.YMost(); texCoordRect.height = -texCoordRect.height; } if (wrapMode == LOCAL_GL_REPEAT) { rects.addRect(/* dest rectangle */ 0.0f, 0.0f, 1.0f, 1.0f, /* tex coords */ texCoordRect.x / GLfloat(realTexSize.width), texCoordRect.y / GLfloat(realTexSize.height), texCoordRect.XMost() / GLfloat(realTexSize.width), texCoordRect.YMost() / GLfloat(realTexSize.height), flipped); } else { nsIntRect tcRect(texCoordRect.x, texCoordRect.y, texCoordRect.width, texCoordRect.height); GLContext::DecomposeIntoNoRepeatTriangles(tcRect, nsIntSize(realTexSize.width, realTexSize.height), rects, flipped); } DrawWithVertexBuffer2(mGLContext, mVBOs, LOCAL_GL_TRIANGLES, rects.elements(), vertAttribIndex, rects.vertexPointer(), texCoordAttribIndex, rects.texCoordPointer()); }