IntPoint ScrollableArea::constrainScrollPositionForOverhang(const IntRect& visibleContentRect, const IntSize& totalContentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, int headerHeight, int footerHeight) { // The viewport rect that we're scrolling shouldn't be larger than our document. IntSize idealScrollRectSize(std::min(visibleContentRect.width(), totalContentsSize.width()), std::min(visibleContentRect.height(), totalContentsSize.height())); IntRect scrollRect(scrollPosition + scrollOrigin - IntSize(0, headerHeight), idealScrollRectSize); IntRect documentRect(IntPoint(), IntSize(totalContentsSize.width(), totalContentsSize.height() - headerHeight - footerHeight)); // Use intersection to constrain our ideal scroll rect by the document rect. scrollRect.intersect(documentRect); if (scrollRect.size() != idealScrollRectSize) { // If the rect was clipped, restore its size, effectively pushing it "down" from the top left. scrollRect.setSize(idealScrollRectSize); // If we still clip, push our rect "up" from the bottom right. scrollRect.intersect(documentRect); if (scrollRect.width() < idealScrollRectSize.width()) scrollRect.move(-(idealScrollRectSize.width() - scrollRect.width()), 0); if (scrollRect.height() < idealScrollRectSize.height()) scrollRect.move(0, -(idealScrollRectSize.height() - scrollRect.height())); } return scrollRect.location() - toIntSize(scrollOrigin); }
// Returns true if |a| and |b| share an entire edge (i.e., same width or same // height), and the rectangles do not overlap. static bool sharesEdge(const IntRect& a, const IntRect& b) { return (a.y() == b.y() && a.height() == b.height() && (a.x() == b.maxX() || a.maxX() == b.x())) || (a.x() == b.x() && a.width() == b.width() && (a.y() == b.maxY() || a.maxY() == b.y())); }
void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || isInTransparencyLayer()); if (!hdc || !createdBitmap) { m_data->restore(); return; } if (dstRect.isEmpty()) return; OwnPtr<HBITMAP> bitmap = adoptPtr(static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP))); DIBPixelData pixelData(bitmap.get()); ASSERT(pixelData.bitsPerPixel() == 32); // If this context does not support alpha blending, then it may have // been drawn with GDI functions which always set the alpha channel // to zero. We need to manually set the bitmap to be fully opaque. unsigned char* bytes = reinterpret_cast<unsigned char*>(pixelData.buffer()); if (!supportAlphaBlend) setRGBABitmapAlpha(bytes, pixelData.size().height() * pixelData.bytesPerRow(), 255); drawBitmapToContext(m_data, platformContext()->cr(), pixelData, IntSize(dstRect.x(), dstRect.height() + dstRect.y())); ::DeleteDC(hdc); }
int ScrollbarTheme::thumbThickness(const ScrollbarThemeClient& scrollbar) { IntRect track = trackRect(scrollbar); return scrollbar.orientation() == HorizontalScrollbar ? track.height() : track.width(); }
void PrintContext::computePageRectsWithPageSizeInternal(const FloatSize& pageSizeInPixels, bool allowInlineDirectionTiling) { if (!m_frame->document() || !m_frame->view() || !m_frame->document()->renderView()) return; RenderView* view = m_frame->document()->renderView(); IntRect docRect = view->documentRect(); int pageWidth = pageSizeInPixels.width(); int pageHeight = pageSizeInPixels.height(); bool isHorizontal = view->style().isHorizontalWritingMode(); int docLogicalHeight = isHorizontal ? docRect.height() : docRect.width(); int pageLogicalHeight = isHorizontal ? pageHeight : pageWidth; int pageLogicalWidth = isHorizontal ? pageWidth : pageHeight; int inlineDirectionStart; int inlineDirectionEnd; int blockDirectionStart; int blockDirectionEnd; if (isHorizontal) { if (view->style().isFlippedBlocksWritingMode()) { blockDirectionStart = docRect.maxY(); blockDirectionEnd = docRect.y(); } else { blockDirectionStart = docRect.y(); blockDirectionEnd = docRect.maxY(); } inlineDirectionStart = view->style().isLeftToRightDirection() ? docRect.x() : docRect.maxX(); inlineDirectionEnd = view->style().isLeftToRightDirection() ? docRect.maxX() : docRect.x(); } else { if (view->style().isFlippedBlocksWritingMode()) { blockDirectionStart = docRect.maxX(); blockDirectionEnd = docRect.x(); } else { blockDirectionStart = docRect.x(); blockDirectionEnd = docRect.maxX(); } inlineDirectionStart = view->style().isLeftToRightDirection() ? docRect.y() : docRect.maxY(); inlineDirectionEnd = view->style().isLeftToRightDirection() ? docRect.maxY() : docRect.y(); } unsigned pageCount = ceilf((float)docLogicalHeight / pageLogicalHeight); for (unsigned i = 0; i < pageCount; ++i) { int pageLogicalTop = blockDirectionEnd > blockDirectionStart ? blockDirectionStart + i * pageLogicalHeight : blockDirectionStart - (i + 1) * pageLogicalHeight; if (allowInlineDirectionTiling) { for (int currentInlinePosition = inlineDirectionStart; inlineDirectionEnd > inlineDirectionStart ? currentInlinePosition < inlineDirectionEnd : currentInlinePosition > inlineDirectionEnd; currentInlinePosition += (inlineDirectionEnd > inlineDirectionStart ? pageLogicalWidth : -pageLogicalWidth)) { int pageLogicalLeft = inlineDirectionEnd > inlineDirectionStart ? currentInlinePosition : currentInlinePosition - pageLogicalWidth; IntRect pageRect(pageLogicalLeft, pageLogicalTop, pageLogicalWidth, pageLogicalHeight); if (!isHorizontal) pageRect = pageRect.transposedRect(); m_pageRects.append(pageRect); } } else { int pageLogicalLeft = inlineDirectionEnd > inlineDirectionStart ? inlineDirectionStart : inlineDirectionStart - pageLogicalWidth; IntRect pageRect(pageLogicalLeft, pageLogicalTop, pageLogicalWidth, pageLogicalHeight); if (!isHorizontal) pageRect = pageRect.transposedRect(); m_pageRects.append(pageRect); } } }
bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect, IntRect& webViewRect, int titleBarHeight, IntRect& clip, float scale, bool* treesSwappedPtr, bool* newTreeHasAnimPtr) { if(m_start) { m_start_time = currentTime(); m_current_time = currentTime(); m_total_time = 0; m_iterations = 0; m_avg_fps = 0; m_counter_test = 0; m_start = false; } else{ m_current_time = currentTime(); m_total_time = m_current_time - m_start_time; ++m_iterations; if(m_total_time > 15) { ++m_counter_test; m_avg_fps = m_iterations/m_total_time; } } m_scale = scale; TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom, scale); TilesManager::instance()->incDrawGLCount(); #ifdef DEBUG TilesManager::instance()->getTilesTracker()->clear(); #endif float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO; float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO; bool useMinimalMemory = TilesManager::instance()->useMinimalMemory(); bool useHorzPrefetch = useMinimalMemory ? 0 : viewWidth < baseContentWidth(); bool useVertPrefetch = useMinimalMemory ? 0 : viewHeight < baseContentHeight(); m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0; m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0; XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)", rect.x(), rect.y(), rect.width(), rect.height(), viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom); resetLayersDirtyArea(); // when adding or removing layers, use the the paintingBaseLayer's tree so // that content that moves to the base layer from a layer is synchronized if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) XLOGC("WARNING, scale seems corrupted before update: %e", scale); // Here before we draw, update the BaseTile which has updated content. // Inside this function, just do GPU blits from the transfer queue into // the BaseTiles' texture. TilesManager::instance()->transferQueue()->updateDirtyBaseTiles(); // Upload any pending ImageTexture // Return true if we still have some images to upload. // TODO: upload as many textures as possible within a certain time limit bool ret = ImagesManager::instance()->prepareTextures(this); if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) XLOGC("WARNING, scale seems corrupted after update: %e", scale); // gather the textures we can use TilesManager::instance()->gatherLayerTextures(); double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale); TexturesResult nbTexturesNeeded; bool fastSwap = isScrolling() || m_layersRenderingMode == kSingleSurfaceRendering; ret |= m_treeManager.drawGL(currentTime, rect, viewport, scale, fastSwap, treesSwappedPtr, newTreeHasAnimPtr, &nbTexturesNeeded); if (!ret) resetFrameworkInval(); int nbTexturesForImages = ImagesManager::instance()->nbTextures(); XLOG("*** We have %d textures for images, %d full, %d clipped, total %d / %d", nbTexturesForImages, nbTexturesNeeded.full, nbTexturesNeeded.clipped, nbTexturesNeeded.full + nbTexturesForImages, nbTexturesNeeded.clipped + nbTexturesForImages); nbTexturesNeeded.full += nbTexturesForImages; nbTexturesNeeded.clipped += nbTexturesForImages; ret |= setLayersRenderingMode(nbTexturesNeeded); FloatRect extrasclip(0, 0, rect.width(), rect.height()); TilesManager::instance()->shader()->clip(extrasclip); m_glExtras.drawGL(webViewRect, viewport, titleBarHeight); glBindBuffer(GL_ARRAY_BUFFER, 0); // Clean up GL textures for video layer. TilesManager::instance()->videoLayerManager()->deleteUnusedTextures(); ret |= TilesManager::instance()->invertedScreenSwitch(); if (ret) { // ret==true && empty inval region means we've inval'd everything, // but don't have new content. Keep redrawing full view (0,0,0,0) // until tile generation catches up and we swap pages. bool fullScreenInval = m_frameworkInval.isEmpty(); if (TilesManager::instance()->invertedScreenSwitch()) { fullScreenInval = true; TilesManager::instance()->setInvertedScreenSwitch(false); } if (!fullScreenInval) { FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord( m_frameworkInval); // Inflate the invalidate rect to avoid precision lost. frameworkInval.inflate(1); IntRect inval(frameworkInval.x(), frameworkInval.y(), frameworkInval.width(), frameworkInval.height()); inval.unite(m_frameworkLayersInval); invalRect->setX(inval.x()); invalRect->setY(inval.y()); invalRect->setWidth(inval.width()); invalRect->setHeight(inval.height()); XLOG("invalRect(%d, %d, %d, %d)", inval.x(), inval.y(), inval.width(), inval.height()); if (!invalRect->intersects(rect)) { // invalidate is occurring offscreen, do full inval to guarantee redraw fullScreenInval = true; } } if (fullScreenInval) { invalRect->setX(0); invalRect->setY(0); invalRect->setWidth(0); invalRect->setHeight(0); } } else { resetFrameworkInval(); } #ifdef MEASURES_PERF if (m_measurePerfs) { m_delayTimes[m_timeCounter++] = delta; if (m_timeCounter >= MAX_MEASURES_PERF) dumpMeasures(); } #endif #ifdef DEBUG TilesManager::instance()->getTilesTracker()->showTrackTextures(); ImagesManager::instance()->showImages(); #endif CanvasLayerAndroid::cleanupAssets(); return ret; }
HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { if (dstRect.isEmpty()) return 0; if(!m_data->hdc_refcount++) { m_data->hdc_surface = cairo_win32_surface_create_with_dib(CAIRO_FORMAT_RGB24, dstRect.width(), dstRect.height()); // see http://cairographics.org/FAQ/#paint_from_a_surface cairo_surface_t* source = cairo_get_target( m_data->cr ); cairo_t *cr = cairo_create ( m_data->hdc_surface ); cairo_set_source_surface (cr, source, -dstRect.x(), -dstRect.y() ); cairo_paint(cr); cairo_destroy(cr); m_data->m_hdc = cairo_win32_surface_get_dc(m_data->hdc_surface); SaveDC(m_data->m_hdc); } HDC hdc = m_data->m_hdc; return hdc; }
// Updates the contents of the root layer texture that fall inside the updateRect // and re-composits all sublayers. void LayerRendererChromium::drawLayers(const IntRect& updateRect, const IntRect& visibleRect, const IntRect& contentRect, const IntPoint& scrollPosition) { ASSERT(m_hardwareCompositing); if (!m_rootLayer) return; makeContextCurrent(); GLC(glBindTexture(GL_TEXTURE_2D, m_rootLayerTextureId)); // If the size of the visible area has changed then allocate a new texture // to store the contents of the root layer and adjust the projection matrix // and viewport. int visibleRectWidth = visibleRect.width(); int visibleRectHeight = visibleRect.height(); if (visibleRectWidth != m_rootLayerTextureWidth || visibleRectHeight != m_rootLayerTextureHeight) { m_rootLayerTextureWidth = visibleRect.width(); m_rootLayerTextureHeight = visibleRect.height(); m_projectionMatrix = orthoMatrix(0, visibleRectWidth, visibleRectHeight, 0, -1000, 1000); GLC(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_rootLayerTextureWidth, m_rootLayerTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); } // The GL viewport covers the entire visible area, including the scrollbars. GLC(glViewport(0, 0, visibleRectWidth, visibleRectHeight)); // Bind the common vertex attributes used for drawing all the layers. LayerChromium::prepareForDraw(layerSharedValues()); // FIXME: These calls can be made once, when the compositor context is initialized. GLC(glDisable(GL_DEPTH_TEST)); GLC(glDisable(GL_CULL_FACE)); GLC(glDepthFunc(GL_LEQUAL)); GLC(glClearStencil(0)); if (m_scrollPosition == IntPoint(-1, -1)) m_scrollPosition = scrollPosition; IntPoint scrollDelta = toPoint(scrollPosition - m_scrollPosition); // Scroll only when the updateRect contains pixels for the newly uncovered region to avoid flashing. if ((scrollDelta.x() && updateRect.width() >= abs(scrollDelta.x()) && updateRect.height() >= contentRect.height()) || (scrollDelta.y() && updateRect.height() >= abs(scrollDelta.y()) && updateRect.width() >= contentRect.width())) { // Scrolling works as follows: We render a quad with the current root layer contents // translated by the amount the page has scrolled since the last update and then read the // pixels of the content area (visible area excluding the scroll bars) back into the // root layer texture. The newly exposed area is subesquently filled as usual with // the contents of the updateRect. TransformationMatrix scrolledLayerMatrix; #if PLATFORM(SKIA) float scaleFactor = 1.0f; #elif PLATFORM(CG) // Because the contents of the OpenGL texture are inverted // vertically compared to the Skia backend, we need to move // the backing store in the opposite direction. float scaleFactor = -1.0f; #else #error "Need to implement for your platform." #endif scrolledLayerMatrix.translate3d(0.5 * visibleRect.width() - scrollDelta.x(), 0.5 * visibleRect.height() + scaleFactor * scrollDelta.y(), 0); scrolledLayerMatrix.scale3d(1, -1, 1); useShader(m_scrollShaderProgram); GLC(glUniform1i(m_scrollShaderSamplerLocation, 0)); LayerChromium::drawTexturedQuad(m_projectionMatrix, scrolledLayerMatrix, visibleRect.width(), visibleRect.height(), 1, m_scrollShaderMatrixLocation, -1); GLC(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, contentRect.width(), contentRect.height())); m_scrollPosition = scrollPosition; } else if (abs(scrollDelta.y()) > contentRect.height() || abs(scrollDelta.x()) > contentRect.width()) { // Scrolling larger than the contentRect size does not preserve any of the pixels, so there is // no need to copy framebuffer pixels back into the texture. m_scrollPosition = scrollPosition; } // FIXME: The following check should go away when the compositor renders independently from its own thread. // Ignore a 1x1 update rect at (0, 0) as that's used a way to kick off a redraw for the compositor. if (!(!updateRect.x() && !updateRect.y() && updateRect.width() == 1 && updateRect.height() == 1)) { // Update the root layer texture. ASSERT((updateRect.x() + updateRect.width() <= m_rootLayerTextureWidth) && (updateRect.y() + updateRect.height() <= m_rootLayerTextureHeight)); #if PLATFORM(SKIA) // Get the contents of the updated rect. const SkBitmap bitmap = m_rootLayerCanvas->getDevice()->accessBitmap(false); int rootLayerWidth = bitmap.width(); int rootLayerHeight = bitmap.height(); ASSERT(rootLayerWidth == updateRect.width() && rootLayerHeight == updateRect.height()); void* pixels = bitmap.getPixels(); // Copy the contents of the updated rect to the root layer texture. GLC(glTexSubImage2D(GL_TEXTURE_2D, 0, updateRect.x(), updateRect.y(), updateRect.width(), updateRect.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels)); #elif PLATFORM(CG) // Get the contents of the updated rect. ASSERT(static_cast<int>(CGBitmapContextGetWidth(m_rootLayerCGContext.get())) == updateRect.width() && static_cast<int>(CGBitmapContextGetHeight(m_rootLayerCGContext.get())) == updateRect.height()); void* pixels = m_rootLayerBackingStore.data(); // Copy the contents of the updated rect to the root layer texture. // The origin is at the lower left in Core Graphics' coordinate system. We need to correct for this here. GLC(glTexSubImage2D(GL_TEXTURE_2D, 0, updateRect.x(), m_rootLayerTextureHeight - updateRect.y() - updateRect.height(), updateRect.width(), updateRect.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels)); #else #error "Need to implement for your platform." #endif } glClearColor(0, 0, 1, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Render the root layer using a quad that takes up the entire visible area of the window. // We reuse the shader program used by ContentLayerChromium. const ContentLayerChromium::SharedValues* contentLayerValues = contentLayerSharedValues(); useShader(contentLayerValues->contentShaderProgram()); GLC(glUniform1i(contentLayerValues->shaderSamplerLocation(), 0)); TransformationMatrix layerMatrix; layerMatrix.translate3d(visibleRect.width() * 0.5f, visibleRect.height() * 0.5f, 0); LayerChromium::drawTexturedQuad(m_projectionMatrix, layerMatrix, visibleRect.width(), visibleRect.height(), 1, contentLayerValues->shaderMatrixLocation(), contentLayerValues->shaderAlphaLocation()); // If culling is enabled then we will cull the backface. GLC(glCullFace(GL_BACK)); // The orthographic projection is setup such that Y starts at zero and // increases going down the page so we need to adjust the winding order of // front facing triangles. GLC(glFrontFace(GL_CW)); // The shader used to render layers returns pre-multiplied alpha colors // so we need to send the blending mode appropriately. GLC(glEnable(GL_BLEND)); GLC(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); // Translate all the composited layers by the scroll position. TransformationMatrix matrix; matrix.translate3d(-m_scrollPosition.x(), -m_scrollPosition.y(), 0); // Traverse the layer tree and update the layer transforms. float opacity = 1; const Vector<RefPtr<LayerChromium> >& sublayers = m_rootLayer->getSublayers(); size_t i; for (i = 0; i < sublayers.size(); i++) updateLayersRecursive(sublayers[i].get(), matrix, opacity); m_rootVisibleRect = visibleRect; // Enable scissoring to avoid rendering composited layers over the scrollbars. GLC(glEnable(GL_SCISSOR_TEST)); FloatRect scissorRect(contentRect); // The scissorRect should not include the scroll offset. scissorRect.move(-m_scrollPosition.x(), -m_scrollPosition.y()); scissorToRect(scissorRect); // Clear the stencil buffer to 0. GLC(glClear(GL_STENCIL_BUFFER_BIT)); // Disable writes to the stencil buffer. GLC(glStencilMask(0)); // Traverse the layer tree one more time to draw the layers. for (i = 0; i < sublayers.size(); i++) drawLayersRecursive(sublayers[i].get(), scissorRect); GLC(glDisable(GL_SCISSOR_TEST)); m_gles2Context->swapBuffers(); m_needsDisplay = false; }
bool Surface::blitFromContents(Tile* tile) { if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content()) return false; if (tile->frontTexture() != tile->lastDrawnTexture()) { // CAPPFIX_FLICKERING: fix flickering issue at floating browser mode return false; // CAPPFIX_FLICKERING_END // the below works around an issue where glTexSubImage2d can't update a // texture that hasn't drawn yet by drawing it off screen. // glFlush() and glFinish() work also, but are likely more wasteful. SkRect rect = SkRect::MakeXYWH(-100, -100, 0, 0); FloatRect fillPortion(0, 0, 0, 0); tile->frontTexture()->drawGL(false, rect, 1.0f, 0, false, true, fillPortion); } LayerContent* content = getFirstLayer()->content(); // Extract the dirty rect from the region. Note that this is *NOT* constrained // to this tile IntRect dirtyRect = tile->dirtyArea().getBounds(); IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(), tile->y() * TilesManager::tileHeight(), TilesManager::tileWidth(), TilesManager::tileHeight()); FloatRect tileRectInDoc = tileRect; tileRectInDoc.scale(1 / tile->scale()); dirtyRect.intersect(enclosingIntRect(tileRectInDoc)); PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect); if (!prerenderedInval || prerenderedInval->bitmap.isNull()) return false; SkBitmap sourceBitmap = prerenderedInval->bitmap; // Calculate the screen rect that is dirty, then intersect it with the // tile's screen rect so that we end up with the pixels we need to blit FloatRect screenDirty = dirtyRect; screenDirty.scale(tile->scale()); IntRect enclosingScreenDirty = enclosingIntRect(screenDirty); enclosingScreenDirty.intersect(tileRect); if (enclosingScreenDirty.isEmpty()) return false; // Make sure the screen area we want to blit is contained by the // prerendered screen area if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) { ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain " "enclosingScreenDirty " INT_RECT_FORMAT, INT_RECT_ARGS(prerenderedInval->screenArea), INT_RECT_ARGS(enclosingScreenDirty)); return false; } IntPoint origin = prerenderedInval->screenArea.location(); SkBitmap subset; subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(), enclosingScreenDirty.height()); subset.allocPixels(); int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y(); int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x(); if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset)) return false; // Now upload SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(), enclosingScreenDirty.y() - tileRect.y(), enclosingScreenDirty.width(), enclosingScreenDirty.height()); GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId, subset, textureInval); tile->onBlitUpdate(); return true; }
PassRefPtr<ByteArray> getImageData(const IntRect& rect, SkDevice& srcDevice, const IntSize& size) { RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); SkBitmap::Config srcConfig = srcDevice.accessBitmap(false).config(); if (srcConfig == SkBitmap::kNo_Config) { // This is an empty SkBitmap that could not be configured. ASSERT(!size.width() || !size.height()); return result.release(); } unsigned char* data = result->data(); if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height()) memset(data, 0, result->length()); int originX = rect.x(); int destX = 0; if (originX < 0) { destX = -originX; originX = 0; } int endX = rect.maxX(); if (endX > size.width()) endX = size.width(); int numColumns = endX - originX; if (numColumns <= 0) return result.release(); int originY = rect.y(); int destY = 0; if (originY < 0) { destY = -originY; originY = 0; } int endY = rect.maxY(); if (endY > size.height()) endY = size.height(); int numRows = endY - originY; if (numRows <= 0) return result.release(); ASSERT(srcConfig == SkBitmap::kARGB_8888_Config); unsigned destBytesPerRow = 4 * rect.width(); SkBitmap srcBitmap; srcDevice.readPixels(SkIRect::MakeXYWH(originX, originY, numColumns, numRows), &srcBitmap); unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; // Do conversion of byte order and alpha divide (if necessary) for (int y = 0; y < numRows; ++y) { SkPMColor* srcBitmapRow = srcBitmap.getAddr32(0, y); for (int x = 0; x < numColumns; ++x) { SkPMColor srcPMColor = srcBitmapRow[x]; unsigned char* destPixel = &destRow[x * 4]; if (multiplied == Unmultiplied) { unsigned char a = SkGetPackedA32(srcPMColor); destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0; destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0; destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0; destPixel[3] = a; } else { // Input and output are both pre-multiplied, we just need to re-arrange the // bytes from the bitmap format to RGBA. destPixel[0] = SkGetPackedR32(srcPMColor); destPixel[1] = SkGetPackedG32(srcPMColor); destPixel[2] = SkGetPackedB32(srcPMColor); destPixel[3] = SkGetPackedA32(srcPMColor); } } destRow += destBytesPerRow; } return result.release(); }
void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, SkDevice* dstDevice, const IntSize& size) { ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); int originX = sourceRect.x(); int destX = destPoint.x() + sourceRect.x(); ASSERT(destX >= 0); ASSERT(destX < size.width()); ASSERT(originX >= 0); ASSERT(originX < sourceRect.maxX()); int endX = destPoint.x() + sourceRect.maxX(); ASSERT(endX <= size.width()); int numColumns = endX - destX; int originY = sourceRect.y(); int destY = destPoint.y() + sourceRect.y(); ASSERT(destY >= 0); ASSERT(destY < size.height()); ASSERT(originY >= 0); ASSERT(originY < sourceRect.maxY()); int endY = destPoint.y() + sourceRect.maxY(); ASSERT(endY <= size.height()); int numRows = endY - destY; unsigned srcBytesPerRow = 4 * sourceSize.width(); SkBitmap deviceBitmap = dstDevice->accessBitmap(true); SkAutoLockPixels deviceAutoLock(deviceBitmap); // If the device's bitmap doesn't have pixels we will make a temp and call writePixels on the device. bool temporaryBitmap = !deviceBitmap.getPixels(); SkBitmap destBitmap; if (temporaryBitmap) { destBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow); if (!destBitmap.allocPixels()) CRASH(); } else deviceBitmap.extractSubset(&destBitmap, SkIRect::MakeXYWH(destX, destY, numColumns, numRows)); // Whether we made a temporary or not destBitmap is always configured to be written at 0,0 SkAutoLockPixels destAutoLock(destBitmap); const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { SkPMColor* destRow = destBitmap.getAddr32(0, y); for (int x = 0; x < numColumns; ++x) { const unsigned char* srcPixel = &srcRow[x * 4]; if (multiplied == Unmultiplied) { unsigned char alpha = srcPixel[3]; unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha); unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha); unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha); destRow[x] = SkPackARGB32(alpha, r, g, b); } else destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); } srcRow += srcBytesPerRow; } // If we used a temporary then write it to the device if (temporaryBitmap) dstDevice->writePixels(destBitmap, destX, destY); }
void PluginView::invalidateRect(const IntRect& rect) { if (m_isWindowed) { gtk_widget_queue_draw_area(GTK_WIDGET(platformPluginWidget()), rect.x(), rect.y(), rect.width(), rect.height()); return; } invalidateWindowlessPluginRect(rect); }
void GraphicsContext::drawFocusRing(const Color& color) { if (paintingDisabled()) return; int radius = (focusRingWidth() - 1) / 2; int offset = radius + focusRingOffset(); const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); IntRect finalFocusRect; for (unsigned i = 0; i < rectCount; i++) { IntRect focusRect = rects[i]; focusRect.inflate(offset); finalFocusRect.unite(focusRect); } cairo_t* cr = m_data->cr; cairo_save(cr); // FIXME: These rects should be rounded cairo_rectangle(cr, finalFocusRect.x(), finalFocusRect.y(), finalFocusRect.width(), finalFocusRect.height()); // Force the alpha to 50%. This matches what the Mac does with outline rings. Color ringColor(color.red(), color.green(), color.blue(), 127); setColor(cr, ringColor); cairo_stroke(cr); cairo_restore(cr); }
void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) { if (paintingDisabled() || strokeStyle() == NoStroke) return; int x = rect.x(); int y = rect.y(); float w = rect.width(); float h = rect.height(); float scaleFactor = h / w; float reverseScaleFactor = w / h; float hRadius = w / 2; float vRadius = h / 2; float fa = startAngle; float falen = fa + angleSpan; cairo_t* cr = m_data->cr; cairo_save(cr); if (w != h) cairo_scale(cr, 1., scaleFactor); cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, -fa * M_PI/180, -falen * M_PI/180); if (w != h) cairo_scale(cr, 1., reverseScaleFactor); float width = strokeThickness(); int patWidth = 0; switch (strokeStyle()) { case DottedStroke: patWidth = static_cast<int>(width / 2); break; case DashedStroke: patWidth = 3 * static_cast<int>(width / 2); break; default: break; } setColor(cr, strokeColor()); if (patWidth) { // Example: 80 pixels with a width of 30 pixels. // Remainder is 20. The maximum pixels of line we could paint // will be 50 pixels. int distance; if (hRadius == vRadius) distance = static_cast<int>((M_PI * hRadius) / 2.0); else // We are elliptical and will have to estimate the distance distance = static_cast<int>((M_PI * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0)) / 2.0); int remainder = distance % patWidth; int coverage = distance - remainder; int numSegments = coverage / patWidth; float patternOffset = 0.0; // Special case 1px dotted borders for speed. if (patWidth == 1) patternOffset = 1.0; else { bool evenNumberOfSegments = numSegments % 2 == 0; if (remainder) evenNumberOfSegments = !evenNumberOfSegments; if (evenNumberOfSegments) { if (remainder) { patternOffset += patWidth - remainder; patternOffset += remainder / 2.0; } else patternOffset = patWidth / 2.0; } else { if (remainder) patternOffset = (patWidth - remainder) / 2.0; } } double dash = patWidth; cairo_set_dash(cr, &dash, 1, patternOffset); } cairo_stroke(cr); cairo_restore(cr); }
IntPoint AccessibilityObject::clickPoint() const { IntRect rect = elementRect(); return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); }
PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect) { ASSERT(isFilterSizeValid(rect)); RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); copyPremultipliedImage(imageData.get(), rect); return imageData.release(); }
IntRect AffineTransform::mapRect(const IntRect& rect) const { IntRect result; if (m_m12 == 0.0F && m_m21 == 0.0F) { int x = lround(m_m11 * rect.x() + m_dx); int y = lround(m_m22 * rect.y() + m_dy); int w = lround(m_m11 * rect.width()); int h = lround(m_m22 * rect.height()); if (w < 0) { w = -w; x -= w; } if (h < 0) { h = -h; y -= h; } result = IntRect(x, y, w, h); } else { // see mapToPolygon for explanations of the algorithm. double x0, y0; double x, y; map(rect.location().x(), rect.location().y(), &x0, &y0); double xmin = x0; double ymin = y0; double xmax = x0; double ymax = y0; map(rect.right() + 1, rect.location().y(), &x, &y); if (x < xmin) xmin = x; if (y < ymin) ymin = y; if (x > xmax) xmax = x; if (y > ymax) ymax = y; map(rect.right() + 1, rect.bottom() + 1, &x, &y); if (x < xmin) xmin = x; if (y < ymin) ymin = y; if (x > xmax) xmax = x; if (y > ymax) ymax = y; map(rect.location().x(), rect.bottom() + 1, &x, &y); if (x < xmin) xmin = x; if (y < ymin) ymin = y; if (x > xmax) xmax = x; if (y > ymax) ymax = y; double w = xmax - xmin; double h = ymax - ymin; xmin -= (xmin - x0) / w; ymin -= (ymin - y0) / h; xmax -= (xmax - x0) / w; ymax -= (ymax - y0) / h; result = IntRect(lround(xmin), lround(ymin), lround(xmax)-lround(xmin)+1, lround(ymax)-lround(ymin)+1); } return result; /*IntRect r ( (int)( m_m11*rect.x() + m_m21*rect.y() + m_dx ), (int)(m_m22*rect.y() + m_m12*rect.x() + m_dy), rect.width(), rect.height() ); return r;*/ }
static bool paintMozillaGtkWidget(const RenderThemeGtk* theme, GtkThemeWidgetType type, RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) { GRefPtr<GdkDrawable> pixmap; // Painting is disabled so just claim to have succeeded if (i.context->paintingDisabled()) return false; // No GdkWindow to render to, so return true to fall back if (!i.context->gdkDrawable()) // This is slow, used only during printing process pixmap = adoptGRef(gdk_pixmap_new(0, rect.width(), rect.height(), gdk_visual_get_system()->depth)); GtkWidgetState mozState; setMozillaState(theme, &mozState, o); int flags; // We might want to make setting flags the caller's job at some point rather than doing it here. switch (type) { case MOZ_GTK_BUTTON: flags = GTK_RELIEF_NORMAL; break; case MOZ_GTK_CHECKBUTTON: case MOZ_GTK_RADIOBUTTON: flags = theme->isChecked(o); break; default: flags = 0; break; } GtkTextDirection direction = gtkTextDirection(o->style()->direction()); if (pixmap) { GdkRectangle gdkRect = IntRect(0, 0, rect.width(), rect.height()); moz_gtk_use_theme_parts(theme->partsForDrawable(pixmap.get())); bool result = moz_gtk_widget_paint(type, pixmap.get(), &gdkRect, &gdkRect, &mozState, flags, direction) != MOZ_GTK_SUCCESS; if (!result) { cairo_t* cr = i.context->platformContext(); gdk_cairo_set_source_pixmap(cr, pixmap.get(), rect.x(), rect.y()); cairo_paint(cr); } return result; } AffineTransform ctm = i.context->getCTM(); IntPoint pos = ctm.mapPoint(rect.location()); GdkRectangle gdkRect = IntRect(pos.x(), pos.y(), rect.width(), rect.height()); // Find the clip rectangle cairo_t* cr = i.context->platformContext(); double clipX1, clipX2, clipY1, clipY2; cairo_clip_extents(cr, &clipX1, &clipY1, &clipX2, &clipY2); GdkRectangle gdkClipRect; gdkClipRect.width = clipX2 - clipX1; gdkClipRect.height = clipY2 - clipY1; IntPoint clipPos = ctm.mapPoint(IntPoint(clipX1, clipY1)); gdkClipRect.x = clipPos.x(); gdkClipRect.y = clipPos.y(); gdk_rectangle_intersect(&gdkRect, &gdkClipRect, &gdkClipRect); // Since the theme renderer is going to be drawing onto this GdkDrawable, // select the appropriate widgets for the drawable depth. moz_gtk_use_theme_parts(theme->partsForDrawable(i.context->gdkDrawable())); return moz_gtk_widget_paint(type, i.context->gdkDrawable(), &gdkRect, &gdkClipRect, &mozState, flags, direction) != MOZ_GTK_SUCCESS; }
void ScrollableAreaPainter::drawPlatformResizerImage(GraphicsContext* context, IntRect resizerCornerRect) { float deviceScaleFactor = blink::deviceScaleFactor(scrollableArea().box().frame()); RefPtr<Image> resizeCornerImage; IntSize cornerResizerSize; if (deviceScaleFactor >= 2) { DEFINE_STATIC_REF(Image, resizeCornerImageHiRes, (Image::loadPlatformResource("textAreaResizeCorner@2x"))); resizeCornerImage = resizeCornerImageHiRes; cornerResizerSize = resizeCornerImage->size(); cornerResizerSize.scale(0.5f); } else { DEFINE_STATIC_REF(Image, resizeCornerImageLoRes, (Image::loadPlatformResource("textAreaResizeCorner"))); resizeCornerImage = resizeCornerImageLoRes; cornerResizerSize = resizeCornerImage->size(); } if (scrollableArea().box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { context->save(); context->translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height()); context->scale(-1.0, 1.0); context->drawImage(resizeCornerImage.get(), IntRect(IntPoint(), cornerResizerSize)); context->restore(); return; } IntRect imageRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize); context->drawImage(resizeCornerImage.get(), imageRect); }
// intersectingRegions and remainingFingerRegion are all in main frame contents coordinates, // even on recursive calls of ::findIntersectingRegions. bool FatFingers::findIntersectingRegions(Document* document, Vector<IntersectingRegion>& intersectingRegions, Platform::IntRectRegion& remainingFingerRegion) { if (!document || !document->frame()->view()) return false; // The layout needs to be up-to-date to determine if a node is focusable. document->updateLayoutIgnorePendingStylesheets(); // Create fingerRect. IntPoint frameContentPos(document->frame()->view()->windowToContents(m_webPage->m_mainFrame->view()->contentsToWindow(m_contentPos))); #if DEBUG_FAT_FINGERS IntRect fingerRect(fingerRectForPoint(frameContentPos)); IntRect screenFingerRect = m_webPage->mapToTransformed(fingerRect); log(LogLevelInfo, "fat finger rect now %d, %d, %d, %d", screenFingerRect.x(), screenFingerRect.y(), screenFingerRect.width(), screenFingerRect.height()); // only record the first finger rect if (document == m_webPage->m_mainFrame->document()) m_debugFatFingerRect = m_webPage->mapToTransformed(m_webPage->mapFromContentsToViewport(fingerRect)); #endif bool foundOne = false; RenderLayer* lowestPositionedEnclosingLayerSoFar = 0; // Iterate over the list of nodes (and subrects of nodes where possible), for each saving the // intersection of the bounding box with the finger rect. ListHashSet<RefPtr<Node> > intersectedNodes; getNodesFromRect(document, frameContentPos, intersectedNodes); ListHashSet<RefPtr<Node> >::const_iterator it = intersectedNodes.begin(); ListHashSet<RefPtr<Node> >::const_iterator end = intersectedNodes.end(); for ( ; it != end; ++it) { Node* curNode = (*it).get(); if (!curNode || !curNode->renderer()) continue; if (remainingFingerRegion.isEmpty()) break; bool isElement = curNode->isElementNode(); if (isElement && isValidFrameOwner(toElement(curNode))) { HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(curNode); Document* childDocument = owner && owner->contentFrame() ? owner->contentFrame()->document() : 0; if (!childDocument) continue; ASSERT(childDocument->frame()->view()); foundOne |= findIntersectingRegions(childDocument, intersectingRegions, remainingFingerRegion); } else if (isElement && m_targetType == ClickableElement) { foundOne |= checkForClickableElement(toElement(curNode), intersectingRegions, remainingFingerRegion, lowestPositionedEnclosingLayerSoFar); } else if (m_targetType == Text) foundOne |= checkForText(curNode, intersectingRegions, remainingFingerRegion); } return foundOne; }
static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect) { IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon)); if (iconRect.size() != iconSize) { // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad. GRefPtr<GdkPixbuf> scaledIcon = gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(), GDK_INTERP_BILINEAR); icon = scaledIcon.get(); } cairo_t* cr = context->platformContext()->cr(); cairo_save(cr); gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y()); cairo_paint(cr); cairo_restore(cr); }
void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect) { // Avoid NaN if (!isfinite(phase.x()) || !isfinite(phase.y())) return; cairo_save(cr); RefPtr<cairo_surface_t> clippedImageSurface = 0; if (tileRect.size() != imageSize) { IntRect imageRect = enclosingIntRect(tileRect); clippedImageSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, imageRect.width(), imageRect.height())); RefPtr<cairo_t> clippedImageContext = adoptRef(cairo_create(clippedImageSurface.get())); cairo_set_source_surface(clippedImageContext.get(), image, -tileRect.x(), -tileRect.y()); cairo_paint(clippedImageContext.get()); image = clippedImageSurface.get(); } cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); cairo_matrix_t patternMatrix = cairo_matrix_t(patternTransform); cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()}; cairo_matrix_t combined; cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix); cairo_matrix_invert(&combined); cairo_pattern_set_matrix(pattern, &combined); cairo_set_operator(cr, op); cairo_set_source(cr, pattern); cairo_pattern_destroy(pattern); cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height()); cairo_fill(cr); cairo_restore(cr); }
void InlinePainter::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset, const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline, const Color outlineColor) { RenderStyle* styleToUse = m_renderInline.style(); int outlineWidth = styleToUse->outlineWidth(); EBorderStyle outlineStyle = styleToUse->outlineStyle(); bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext); int offset = m_renderInline.style()->outlineOffset(); LayoutRect box(LayoutPoint(paintOffset.x() + thisline.x() - offset, paintOffset.y() + thisline.y() - offset), LayoutSize(thisline.width() + offset, thisline.height() + offset)); IntRect pixelSnappedBox = pixelSnappedIntRect(box); if (pixelSnappedBox.width() < 0 || pixelSnappedBox.height() < 0) return; IntRect pixelSnappedLastLine = pixelSnappedIntRect(paintOffset.x() + lastline.x(), 0, lastline.width(), 0); IntRect pixelSnappedNextLine = pixelSnappedIntRect(paintOffset.x() + nextline.x(), 0, nextline.width(), 0); // left edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), pixelSnappedBox.x(), pixelSnappedBox.maxY() + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), BSLeft, outlineColor, outlineStyle, (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), antialias); // right edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.maxX(), pixelSnappedBox.y() - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0), BSRight, outlineColor, outlineStyle, (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth), antialias); // upper edge if (thisline.x() < lastline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, std::min(pixelSnappedBox.maxX() + outlineWidth, (lastline.isEmpty() ? 1000000 : pixelSnappedLastLine.x())), pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, (!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (lastline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(lastline.isEmpty() ? -1000000 : pixelSnappedLastLine.maxX(), pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, (!lastline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } // lower edge if (thisline.x() < nextline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), std::min(pixelSnappedBox.maxX() + outlineWidth, !nextline.isEmpty() ? pixelSnappedNextLine.x() + 1 : 1000000), pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, (!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (nextline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(!nextline.isEmpty() ? pixelSnappedNextLine.maxX() : -1000000, pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, (!nextline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } }
void InspectorOverlay::drawNodeHighlight() { if (!m_highlightNode) return; Highlight highlight; buildNodeHighlight(m_highlightNode.get(), m_nodeHighlightConfig, &highlight); RefPtr<InspectorObject> highlightObject = buildObjectForHighlight(m_page->mainFrame()->view(), highlight); Node* node = m_highlightNode.get(); if (node->isElementNode() && m_nodeHighlightConfig.showInfo && node->renderer() && node->document()->frame()) { RefPtr<InspectorObject> elementInfo = InspectorObject::create(); Element* element = toElement(node); bool isXHTML = element->document()->isXHTMLDocument(); elementInfo->setString("tagName", isXHTML ? element->nodeName() : element->nodeName().lower()); elementInfo->setString("idValue", element->getIdAttribute()); HashSet<AtomicString> usedClassNames; if (element->hasClass() && element->isStyledElement()) { StringBuilder classNames; const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames(); size_t classNameCount = classNamesString.size(); for (size_t i = 0; i < classNameCount; ++i) { const AtomicString& className = classNamesString[i]; if (usedClassNames.contains(className)) continue; usedClassNames.add(className); classNames.append('.'); classNames.append(className); } elementInfo->setString("className", classNames.toString()); } RenderObject* renderer = node->renderer(); Frame* containingFrame = node->document()->frame(); FrameView* containingView = containingFrame->view(); IntRect boundingBox = pixelSnappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect())); RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0; elementInfo->setString("nodeWidth", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width())); elementInfo->setString("nodeHeight", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height())); highlightObject->setObject("elementInfo", elementInfo.release()); } evaluateInOverlay("drawNodeHighlight", highlightObject); }
void RenderTheme::paintSliderTicks(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect) { Node* node = o->node(); if (!isHTMLInputElement(node)) return; HTMLInputElement* input = toHTMLInputElement(node); if (!input->isRangeControl()) return; HTMLDataListElement* dataList = input->dataList(); if (!dataList) return; double min = input->minimum(); double max = input->maximum(); ControlPart part = o->style()->appearance(); // We don't support ticks on alternate sliders like MediaVolumeSliders. if (part != SliderHorizontalPart && part != SliderVerticalPart) return; bool isHorizontal = part == SliderHorizontalPart; IntSize thumbSize; RenderObject* thumbRenderer = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderThumb())->renderer(); if (thumbRenderer) { RenderStyle* thumbStyle = thumbRenderer->style(); int thumbWidth = thumbStyle->width().intValue(); int thumbHeight = thumbStyle->height().intValue(); thumbSize.setWidth(isHorizontal ? thumbWidth : thumbHeight); thumbSize.setHeight(isHorizontal ? thumbHeight : thumbWidth); } IntSize tickSize = sliderTickSize(); float zoomFactor = o->style()->effectiveZoom(); FloatRect tickRect; int tickRegionSideMargin = 0; int tickRegionWidth = 0; IntRect trackBounds; RenderObject* trackRenderer = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderTrack())->renderer(); // We can ignoring transforms because transform is handled by the graphics context. if (trackRenderer) trackBounds = trackRenderer->absoluteBoundingBoxRectIgnoringTransforms(); IntRect sliderBounds = o->absoluteBoundingBoxRectIgnoringTransforms(); // Make position relative to the transformed ancestor element. trackBounds.setX(trackBounds.x() - sliderBounds.x() + rect.x()); trackBounds.setY(trackBounds.y() - sliderBounds.y() + rect.y()); if (isHorizontal) { tickRect.setWidth(floor(tickSize.width() * zoomFactor)); tickRect.setHeight(floor(tickSize.height() * zoomFactor)); tickRect.setY(floor(rect.y() + rect.height() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor)); tickRegionSideMargin = trackBounds.x() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0; tickRegionWidth = trackBounds.width() - thumbSize.width(); } else { tickRect.setWidth(floor(tickSize.height() * zoomFactor)); tickRect.setHeight(floor(tickSize.width() * zoomFactor)); tickRect.setX(floor(rect.x() + rect.width() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor)); tickRegionSideMargin = trackBounds.y() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0; tickRegionWidth = trackBounds.height() - thumbSize.width(); } RefPtrWillBeRawPtr<HTMLCollection> options = dataList->options(); GraphicsContextStateSaver stateSaver(*paintInfo.context); paintInfo.context->setFillColor(o->resolveColor(CSSPropertyColor)); for (unsigned i = 0; Element* element = options->item(i); i++) { ASSERT(isHTMLOptionElement(*element)); HTMLOptionElement& optionElement = toHTMLOptionElement(*element); String value = optionElement.value(); if (!input->isValidValue(value)) continue; double parsedValue = parseToDoubleForNumberType(input->sanitizeValue(value)); double tickFraction = (parsedValue - min) / (max - min); double tickRatio = isHorizontal && o->style()->isLeftToRightDirection() ? tickFraction : 1.0 - tickFraction; double tickPosition = round(tickRegionSideMargin + tickRegionWidth * tickRatio); if (isHorizontal) tickRect.setX(tickPosition); else tickRect.setY(tickPosition); paintInfo.context->fillRect(tickRect); } }
void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) { if (context->paintingDisabled()) return; if (!m_visible) return; if (!m_buffer) return; int width = 0, height = 0; int pixelAspectRatioNumerator = 0; int pixelAspectRatioDenominator = 0; double doublePixelAspectRatioNumerator = 0; double doublePixelAspectRatioDenominator = 0; double displayWidth; double displayHeight; double scale, gapHeight, gapWidth; GstCaps *caps = gst_buffer_get_caps(m_buffer); if (!gst_video_format_parse_caps(caps, NULL, &width, &height) || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, &pixelAspectRatioDenominator)) { gst_caps_unref(caps); return; } displayWidth = width; displayHeight = height; doublePixelAspectRatioNumerator = pixelAspectRatioNumerator; doublePixelAspectRatioDenominator = pixelAspectRatioDenominator; cairo_t* cr = context->platformContext(); cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(m_buffer), CAIRO_FORMAT_RGB24, width, height, 4 * width); cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); displayWidth *= doublePixelAspectRatioNumerator / doublePixelAspectRatioDenominator; displayHeight *= doublePixelAspectRatioDenominator / doublePixelAspectRatioNumerator; scale = MIN (rect.width () / displayWidth, rect.height () / displayHeight); displayWidth *= scale; displayHeight *= scale; // Calculate gap between border an picture gapWidth = (rect.width() - displayWidth) / 2.0; gapHeight = (rect.height() - displayHeight) / 2.0; // paint the rectangle on the context and draw the surface inside. cairo_translate(cr, rect.x() + gapWidth, rect.y() + gapHeight); cairo_rectangle(cr, 0, 0, rect.width(), rect.height()); cairo_scale(cr, doublePixelAspectRatioNumerator / doublePixelAspectRatioDenominator, doublePixelAspectRatioDenominator / doublePixelAspectRatioNumerator); cairo_scale(cr, scale, scale); cairo_set_source_surface(cr, src, 0, 0); cairo_fill(cr); cairo_restore(cr); cairo_surface_destroy(src); gst_caps_unref(caps); }
// static bool InspectorHighlight::getBoxModel(Node* node, RefPtr<TypeBuilder::DOM::BoxModel>& model) { LayoutObject* layoutObject = node->layoutObject(); FrameView* view = node->document().view(); if (!layoutObject || !view) return false; FloatQuad content, padding, border, margin; if (!buildNodeQuads(node->layoutObject(), &content, &padding, &border, &margin)) return false; IntRect boundingBox = view->contentsToRootFrame(layoutObject->absoluteBoundingBoxRect()); LayoutBoxModelObject* modelObject = layoutObject->isBoxModelObject() ? toLayoutBoxModelObject(layoutObject) : nullptr; model = TypeBuilder::DOM::BoxModel::create() .setContent(buildArrayForQuad(content)) .setPadding(buildArrayForQuad(padding)) .setBorder(buildArrayForQuad(border)) .setMargin(buildArrayForQuad(margin)) .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width()) .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height()); Shape::DisplayPaths paths; FloatQuad boundsQuad; if (const ShapeOutsideInfo* shapeOutsideInfo = shapeOutsideInfoForNode(node, &paths, &boundsQuad)) { RefPtr<TypeBuilder::DOM::ShapeOutsideInfo> shapeTypeBuilder = TypeBuilder::DOM::ShapeOutsideInfo::create() .setBounds(buildArrayForQuad(boundsQuad)) .setShape(ShapePathBuilder::buildPath(*view, *layoutObject, *shapeOutsideInfo, paths.shape)) .setMarginShape(ShapePathBuilder::buildPath(*view, *layoutObject, *shapeOutsideInfo, paths.marginShape)); model->setShapeOutside(shapeTypeBuilder); } return true; }
void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior) { ts << o.renderName(); if (behavior & RenderAsTextShowAddresses) ts << " " << static_cast<const void*>(&o); if (o.style() && o.style()->zIndex()) ts << " zI: " << o.style()->zIndex(); if (o.node()) { String tagName = getTagName(o.node()); if (!tagName.isEmpty()) { ts << " {" << tagName << "}"; // flag empty or unstyled AppleStyleSpan because we never // want to leave them in the DOM if (isEmptyOrUnstyledAppleStyleSpan(o.node())) ts << " *empty or unstyled AppleStyleSpan*"; } } bool adjustForTableCells = o.containingBlock()->isTableCell(); IntRect r; if (o.isText()) { // FIXME: Would be better to dump the bounding box x and y rather than the first run's x and y, but that would involve updating // many test results. const RenderText& text = *toRenderText(&o); IntRect linesBox = text.linesBoundingBox(); r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height()); if (adjustForTableCells && !text.firstTextBox()) adjustForTableCells = false; } else if (o.isRenderInline()) { // FIXME: Would be better not to just dump 0, 0 as the x and y here. const RenderInline& inlineFlow = *toRenderInline(&o); r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height()); adjustForTableCells = false; } else if (o.isTableCell()) { // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are // captured by the results. const RenderTableCell& cell = *toRenderTableCell(&o); r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter()); } else if (o.isBox()) r = toRenderBox(&o)->frameRect(); // FIXME: Temporary in order to ensure compatibility with existing layout test results. if (adjustForTableCells) r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore()); ts << " " << r; if (!(o.isText() && !o.isBR())) { if (o.isFileUploadControl()) ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue()); if (o.parent() && (o.parent()->style()->color() != o.style()->color())) ts << " [color=" << o.style()->color().nameForRenderTreeAsText() << "]"; if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) && o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb()) // Do not dump invalid or transparent backgrounds, since that is the default. ts << " [bgcolor=" << o.style()->backgroundColor().nameForRenderTreeAsText() << "]"; if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) && o.style()->textFillColor().isValid() && o.style()->textFillColor() != o.style()->color() && o.style()->textFillColor().rgb()) ts << " [textFillColor=" << o.style()->textFillColor().nameForRenderTreeAsText() << "]"; if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) && o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor() != o.style()->color() && o.style()->textStrokeColor().rgb()) ts << " [textStrokeColor=" << o.style()->textStrokeColor().nameForRenderTreeAsText() << "]"; if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) && o.style()->textStrokeWidth() > 0) ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]"; if (!o.isBoxModelObject()) return; const RenderBoxModelObject& box = *toRenderBoxModelObject(&o); if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) { ts << " [border:"; BorderValue prevBorder; if (o.style()->borderTop() != prevBorder) { prevBorder = o.style()->borderTop(); if (!box.borderTop()) ts << " none"; else { ts << " (" << box.borderTop() << "px "; printBorderStyle(ts, o.style()->borderTopStyle()); Color col = o.style()->borderTopColor(); if (!col.isValid()) col = o.style()->color(); ts << col.nameForRenderTreeAsText() << ")"; } } if (o.style()->borderRight() != prevBorder) { prevBorder = o.style()->borderRight(); if (!box.borderRight()) ts << " none"; else { ts << " (" << box.borderRight() << "px "; printBorderStyle(ts, o.style()->borderRightStyle()); Color col = o.style()->borderRightColor(); if (!col.isValid()) col = o.style()->color(); ts << col.nameForRenderTreeAsText() << ")"; } } if (o.style()->borderBottom() != prevBorder) { prevBorder = box.style()->borderBottom(); if (!box.borderBottom()) ts << " none"; else { ts << " (" << box.borderBottom() << "px "; printBorderStyle(ts, o.style()->borderBottomStyle()); Color col = o.style()->borderBottomColor(); if (!col.isValid()) col = o.style()->color(); ts << col.nameForRenderTreeAsText() << ")"; } } if (o.style()->borderLeft() != prevBorder) { prevBorder = o.style()->borderLeft(); if (!box.borderLeft()) ts << " none"; else { ts << " (" << box.borderLeft() << "px "; printBorderStyle(ts, o.style()->borderLeftStyle()); Color col = o.style()->borderLeftColor(); if (!col.isValid()) col = o.style()->color(); ts << col.nameForRenderTreeAsText() << ")"; } } ts << "]"; } } if (o.isTableCell()) { const RenderTableCell& c = *toRenderTableCell(&o); ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]"; } #if ENABLE(DETAILS) if (o.isDetailsMarker()) { ts << ": "; switch (toRenderDetailsMarker(&o)->orientation()) { case RenderDetailsMarker::Left: ts << "left"; break; case RenderDetailsMarker::Right: ts << "right"; break; case RenderDetailsMarker::Up: ts << "up"; break; case RenderDetailsMarker::Down: ts << "down"; break; } } #endif if (o.isListMarker()) { String text = toRenderListMarker(&o)->text(); if (!text.isEmpty()) { if (text.length() != 1) text = quoteAndEscapeNonPrintables(text); else { switch (text[0]) { case bullet: text = "bullet"; break; case blackSquare: text = "black square"; break; case whiteBullet: text = "white bullet"; break; default: text = quoteAndEscapeNonPrintables(text); } } ts << ": " << text; } } if (behavior & RenderAsTextShowIDAndClass) { if (Node* node = o.node()) { if (node->hasID()) ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\""; if (node->hasClass()) { StyledElement* styledElement = static_cast<StyledElement*>(node); String classes; for (size_t i = 0; i < styledElement->classNames().size(); ++i) { if (i > 0) classes += " "; classes += styledElement->classNames()[i]; } ts << " class=\"" + classes + "\""; } } } if (behavior & RenderAsTextShowLayoutState) { bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout(); if (needsLayout) ts << " (needs layout:"; bool havePrevious = false; if (o.selfNeedsLayout()) { ts << " self"; havePrevious = true; } if (o.needsPositionedMovementLayout()) { if (havePrevious) ts << ","; havePrevious = true; ts << " positioned movement"; } if (o.normalChildNeedsLayout()) { if (havePrevious) ts << ","; havePrevious = true; ts << " child"; } if (o.posChildNeedsLayout()) { if (havePrevious) ts << ","; ts << " positioned child"; } if (needsLayout) ts << ")"; } #if PLATFORM(QT) // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget // is invisible the QWidget should be invisible too. if (o.isRenderPart()) { const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o)); if (part->widget() && part->widget()->platformWidget()) { QWidget* wid = part->widget()->platformWidget(); ts << " [QT: "; ts << "geometry: {" << wid->geometry() << "} "; ts << "isHidden: " << wid->isHidden() << " "; ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " "; ts << "isParentVisible: " << part->widget()->isParentVisible() << " "; ts << "mask: {" << wid->mask().boundingRect() << "} ] "; } } #endif }
static bool isDeletableElement(const Node* node) { if (!node || !node->isHTMLElement() || !node->inDocument() || !node->isContentEditable()) return false; // In general we want to only draw the UI around object of a certain area, but we still keep the min width/height to // make sure we don't end up with very thin or very short elements getting the UI. const int minimumArea = 2500; const int minimumWidth = 48; const int minimumHeight = 16; const unsigned minimumVisibleBorders = 1; RenderObject* renderer = node->renderer(); if (!renderer || !renderer->isBox()) return false; // Disallow the body element since it isn't practical to delete, and the deletion UI would be clipped. if (node->hasTagName(bodyTag)) return false; // Disallow elements with any overflow clip, since the deletion UI would be clipped as well. <rdar://problem/6840161> if (renderer->hasOverflowClip()) return false; // Disallow Mail blockquotes since the deletion UI would get in the way of editing for these. if (isMailBlockquote(node)) return false; RenderBox* box = toRenderBox(renderer); IntRect borderBoundingBox = box->borderBoundingBox(); if (borderBoundingBox.width() < minimumWidth || borderBoundingBox.height() < minimumHeight) return false; if ((borderBoundingBox.width() * borderBoundingBox.height()) < minimumArea) return false; if (renderer->isTable()) return true; if (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(iframeTag)) return true; if (renderer->isPositioned()) return true; if (renderer->isRenderBlock() && !renderer->isTableCell()) { RenderStyle* style = renderer->style(); if (!style) return false; // Allow blocks that have background images if (style->hasBackgroundImage() && style->backgroundImage()->canRender(1.0f)) return true; // Allow blocks with a minimum number of non-transparent borders unsigned visibleBorders = style->borderTop().isVisible() + style->borderBottom().isVisible() + style->borderLeft().isVisible() + style->borderRight().isVisible(); if (visibleBorders >= minimumVisibleBorders) return true; // Allow blocks that have a different background from it's parent ContainerNode* parentNode = node->parentNode(); if (!parentNode) return false; RenderObject* parentRenderer = parentNode->renderer(); if (!parentRenderer) return false; RenderStyle* parentStyle = parentRenderer->style(); if (!parentStyle) return false; if (renderer->hasBackground() && (!parentRenderer->hasBackground() || style->visitedDependentColor(CSSPropertyBackgroundColor) != parentStyle->visitedDependentColor(CSSPropertyBackgroundColor))) return true; } return false; }
void TileGrid::revalidateTiles(TileValidationPolicy validationPolicy) { FloatRect coverageRect = m_controller.coverageRect(); IntRect bounds = m_controller.bounds(); if (coverageRect.isEmpty() || bounds.isEmpty()) return; FloatRect scaledRect(coverageRect); scaledRect.scale(m_scale); IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect)); TileCohort currCohort = nextTileCohort(); unsigned tilesInCohort = 0; double minimumRevalidationTimerDuration = std::numeric_limits<double>::max(); bool needsTileRevalidation = false; // Move tiles newly outside the coverage rect into the cohort map. for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { TileInfo& tileInfo = it->value; TileIndex tileIndex = it->key; PlatformCALayer* tileLayer = tileInfo.layer.get(); IntRect tileRect = rectForTileIndex(tileIndex); if (tileRect.intersects(coverageRectInTileCoords)) { tileInfo.cohort = VisibleTileCohort; if (tileInfo.hasStaleContent) { // FIXME: store a dirty region per layer? tileLayer->setNeedsDisplay(); tileInfo.hasStaleContent = false; } } else { // Add to the currentCohort if not already in one. if (tileInfo.cohort == VisibleTileCohort) { tileInfo.cohort = currCohort; ++tilesInCohort; if (m_controller.unparentsOffscreenTiles()) tileLayer->removeFromSuperlayer(); } else if (m_controller.unparentsOffscreenTiles() && m_controller.shouldAggressivelyRetainTiles() && tileLayer->superlayer()) { // Aggressive tile retention means we'll never remove cohorts, but we need to make sure they're unparented. // We can't immediately unparent cohorts comprised of secondary tiles that never touch the primary coverage rect, // because that would defeat the usefulness of prepopulateRect(); instead, age prepopulated tiles out as if they were being removed. for (auto& cohort : m_cohortList) { if (cohort.cohort != tileInfo.cohort) continue; double timeUntilCohortExpires = cohort.timeUntilExpiration(); if (timeUntilCohortExpires > 0) { minimumRevalidationTimerDuration = std::min(minimumRevalidationTimerDuration, timeUntilCohortExpires); needsTileRevalidation = true; } else tileLayer->removeFromSuperlayer(); break; } } } } if (needsTileRevalidation) m_controller.scheduleTileRevalidation(minimumRevalidationTimerDuration); if (tilesInCohort) startedNewCohort(currCohort); if (!m_controller.shouldAggressivelyRetainTiles()) { if (m_controller.shouldTemporarilyRetainTileCohorts()) scheduleCohortRemoval(); else if (tilesInCohort) removeTilesInCohort(currCohort); } // Ensure primary tile coverage tiles. m_primaryTileCoverageRect = ensureTilesForRect(coverageRect, CoverageType::PrimaryTiles); if (validationPolicy & PruneSecondaryTiles) { removeAllSecondaryTiles(); m_cohortList.clear(); } else { for (auto& secondaryCoverageRect : m_secondaryTileCoverageRects) { FloatRect secondaryRectInLayerCoordinates(secondaryCoverageRect); secondaryRectInLayerCoordinates.scale(1 / m_scale); ensureTilesForRect(secondaryRectInLayerCoordinates, CoverageType::SecondaryTiles); } m_secondaryTileCoverageRects.clear(); } if (m_controller.unparentsOffscreenTiles() && (validationPolicy & UnparentAllTiles)) { for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) it->value.layer->removeFromSuperlayer(); } auto boundsAtLastRevalidate = m_controller.boundsAtLastRevalidate(); if (boundsAtLastRevalidate != bounds) { // If there are margin tiles and the bounds have grown taller or wider, then the tiles that used to // be bottom or right margin tiles need to be invalidated. if (m_controller.hasMargins()) { if (bounds.width() > boundsAtLastRevalidate.width() || bounds.height() > boundsAtLastRevalidate.height()) { IntRect boundsWithoutMargin = m_controller.boundsWithoutMargin(); IntRect oldBoundsWithoutMargin = m_controller.boundsAtLastRevalidateWithoutMargin(); if (bounds.height() > boundsAtLastRevalidate.height()) { IntRect formerBottomMarginRect = IntRect(oldBoundsWithoutMargin.x(), oldBoundsWithoutMargin.height(), oldBoundsWithoutMargin.width(), boundsWithoutMargin.height() - oldBoundsWithoutMargin.height()); setNeedsDisplayInRect(formerBottomMarginRect); } if (bounds.width() > boundsAtLastRevalidate.width()) { IntRect formerRightMarginRect = IntRect(oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.y(), boundsWithoutMargin.width() - oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.height()); setNeedsDisplayInRect(formerRightMarginRect); } } } FloatRect scaledBounds(bounds); scaledBounds.scale(m_scale); IntRect boundsInTileCoords(enclosingIntRect(scaledBounds)); TileIndex topLeftForBounds; TileIndex bottomRightForBounds; getTileIndexRangeForRect(boundsInTileCoords, topLeftForBounds, bottomRightForBounds); Vector<TileIndex> tilesToRemove; for (auto& index : m_tiles.keys()) { if (index.y() < topLeftForBounds.y() || index.y() > bottomRightForBounds.y() || index.x() < topLeftForBounds.x() || index.x() > bottomRightForBounds.x()) tilesToRemove.append(index); } removeTiles(tilesToRemove); } m_controller.didRevalidateTiles(); }