void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer) { unsigned char* pixels = 0; const SkBitmap& canvasBitmap = imageBuffer->bitmap(); const SkBitmap* readbackBitmap = 0; ASSERT(canvasBitmap.config() == SkBitmap::kARGB_8888_Config); if (canvasBitmap.width() == width && canvasBitmap.height() == height) { // This is the fastest and most common case. We read back // directly into the canvas's backing store. readbackBitmap = &canvasBitmap; m_resizingBitmap.reset(); } else { // We need to allocate a temporary bitmap for reading back the // pixel data. We will then use Skia to rescale this bitmap to // the size of the canvas's backing store. if (m_resizingBitmap.width() != width || m_resizingBitmap.height() != height) { m_resizingBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); if (!m_resizingBitmap.allocPixels()) return; } readbackBitmap = &m_resizingBitmap; } // Read back the frame buffer. SkAutoLockPixels bitmapLock(*readbackBitmap); pixels = static_cast<unsigned char*>(readbackBitmap->getPixels()); m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer); readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing); flipVertically(pixels, width, height); readbackBitmap->notifyPixelsChanged(); if (m_resizingBitmap.readyToDraw()) { // We need to draw the resizing bitmap into the canvas's backing store. SkCanvas canvas(canvasBitmap); SkRect dst; dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height())); canvas.drawBitmapRect(m_resizingBitmap, 0, dst); } }
PassRefPtr<Uint8ClampedArray> DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height) { if (m_attributes.premultipliedAlpha) return nullptr; width = size().width(); height = size().height(); Checked<int, RecordOverflow> dataSize = 4; dataSize *= width; dataSize *= height; if (dataSize.hasOverflowed()) return nullptr; RefPtr<Uint8ClampedArray> pixels = Uint8ClampedArray::createUninitialized(width * height * 4); m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer()); readBackFramebuffer(pixels->data(), width, height, ReadbackRGBA, WebGLImageConversion::AlphaDoNothing); flipVertically(pixels->data(), width, height); return pixels.release(); }
bool DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height, SourceDrawingBuffer sourceBuffer, WTF::ArrayBufferContents& contents) { ASSERT(!m_premultipliedAlpha); width = size().width(); height = size().height(); CheckedNumeric<int> dataSize = 4; dataSize *= width; dataSize *= height; if (!dataSize.IsValid()) return false; WTF::ArrayBufferContents pixels(width * height, 4, WTF::ArrayBufferContents::NotShared, WTF::ArrayBufferContents::DontInitialize); GLuint fbo = 0; if (sourceBuffer == FrontBuffer && m_frontColorBuffer.texInfo.textureId) { m_gl->GenFramebuffers(1, &fbo); m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_frontColorBuffer.texInfo.parameters.target, m_frontColorBuffer.texInfo.textureId, 0); } else { m_gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer()); } readBackFramebuffer(static_cast<unsigned char*>(pixels.data()), width, height, ReadbackRGBA, WebGLImageConversion::AlphaDoNothing); flipVertically(static_cast<uint8_t*>(pixels.data()), width, height); if (fbo) { m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_frontColorBuffer.texInfo.parameters.target, 0, 0); m_gl->DeleteFramebuffers(1, &fbo); } restoreFramebufferBindings(); pixels.transfer(contents); return true; }
bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap) { if (!m_context || !m_contentsChanged) return false; m_context->makeContextCurrent(); // Resolve the multisampled buffer into m_colorBuffer texture. if (m_multisampleMode != None) commit(); if (bitmap) { bitmap->setSize(size()); unsigned char* pixels = bitmap->pixels(); bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha; WebGLImageConversion::AlphaOp op = needPremultiply ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing; if (pixels) readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op); } // We must restore the texture binding since creating new textures, // consuming and producing mailboxes changes it. ScopedTextureUnit0BindingRestorer restorer(m_context, m_activeTextureUnit, m_texture2DBinding); // First try to recycle an old buffer. RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox(); // No buffer available to recycle, create a new one. if (!frontColorBufferMailbox) { unsigned newColorBuffer = createColorTexture(m_size); // Bad things happened, abandon ship. if (!newColorBuffer) return false; frontColorBufferMailbox = createNewMailbox(newColorBuffer); } if (m_preserveDrawingBuffer == Discard) { swap(frontColorBufferMailbox->textureId, m_colorBuffer); // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding. // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore // it after attaching the new back buffer here. m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo); if (m_multisampleMode == ImplicitResolve) m_context->framebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0, m_sampleCount); else m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0); } else { m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, frontColorBufferMailbox->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE); } if (m_multisampleMode != None && !m_framebufferBinding) bind(); else restoreFramebufferBinding(); m_contentsChanged = false; m_context->bindTexture(GL_TEXTURE_2D, frontColorBufferMailbox->textureId); m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, frontColorBufferMailbox->mailbox.name); m_context->flush(); frontColorBufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint(); markLayerComposited(); *outMailbox = frontColorBufferMailbox->mailbox; m_frontColorBuffer = frontColorBufferMailbox->textureId; return true; }
bool DrawingBuffer::prepareMailbox(WebExternalTextureMailbox* outMailbox, WebExternalBitmap* bitmap) { if (m_destructionInProgress) { // It can be hit in the following sequence. // 1. WebGL draws something. // 2. The compositor begins the frame. // 3. Javascript makes a context lost using WEBGL_lose_context extension. // 4. Here. return false; } ASSERT(!m_isHidden); if (!m_contentsChanged) return false; if (m_newMailboxCallback) (*m_newMailboxCallback)(); // Resolve the multisampled buffer into m_colorBuffer texture. if (m_antiAliasingMode != None) commit(); if (bitmap) { bitmap->setSize(size()); unsigned char* pixels = bitmap->pixels(); bool needPremultiply = m_wantAlphaChannel && !m_premultipliedAlpha; WebGLImageConversion::AlphaOp op = needPremultiply ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing; if (pixels) readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op); } // We must restore the texture binding since creating new textures, // consuming and producing mailboxes changes it. ScopedTextureUnit0BindingRestorer restorer(m_gl, m_activeTextureUnit, m_texture2DBinding); // First try to recycle an old buffer. RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox(); // No buffer available to recycle, create a new one. if (!frontColorBufferMailbox) frontColorBufferMailbox = createNewMailbox(createTextureAndAllocateMemory(m_size)); if (m_preserveDrawingBuffer == Discard) { std::swap(frontColorBufferMailbox->textureInfo, m_colorBuffer); attachColorBufferToReadFramebuffer(); if (m_discardFramebufferSupported) { // Explicitly discard framebuffer to save GPU memory bandwidth for tile-based GPU arch. const GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT }; m_gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments); } } else { m_gl->CopyTextureCHROMIUM(m_colorBuffer.textureId, frontColorBufferMailbox->textureInfo.textureId, frontColorBufferMailbox->textureInfo.parameters.internalColorFormat, GL_UNSIGNED_BYTE, GL_FALSE, GL_FALSE, GL_FALSE); } restoreFramebufferBindings(); m_contentsChanged = false; m_gl->ProduceTextureDirectCHROMIUM(frontColorBufferMailbox->textureInfo.textureId, frontColorBufferMailbox->textureInfo.parameters.target, frontColorBufferMailbox->mailbox.name); const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM(); m_gl->Flush(); m_gl->GenSyncTokenCHROMIUM(fenceSync, frontColorBufferMailbox->mailbox.syncToken); frontColorBufferMailbox->mailbox.validSyncToken = true; frontColorBufferMailbox->mailbox.allowOverlay = frontColorBufferMailbox->textureInfo.imageId != 0; frontColorBufferMailbox->mailbox.textureTarget = frontColorBufferMailbox->textureInfo.parameters.target; frontColorBufferMailbox->mailbox.textureSize = WebSize(m_size.width(), m_size.height()); setBufferClearNeeded(true); // set m_parentDrawingBuffer to make sure 'this' stays alive as long as it has live mailboxes ASSERT(!frontColorBufferMailbox->m_parentDrawingBuffer); frontColorBufferMailbox->m_parentDrawingBuffer = this; *outMailbox = frontColorBufferMailbox->mailbox; m_frontColorBuffer = { frontColorBufferMailbox->textureInfo, frontColorBufferMailbox->mailbox }; return true; }