void StackLayout::doRelayout() { const List<DisplayItem *> &items = getItems(); DisplayItem *inputField = items.getAt(0); int bottom; if (isItemVisible(inputField)) { setItemSize(inputField, getWidth(), font->getLineHeight()); bottom = getHeight() - inputField->getHeight(); setItemPosition(inputField, 0, bottom); } else { bottom = getHeight(); } int i = 0; upperItemNr = 0; while (bottom > 0 && i + firstLevelVisible < stack->getLength()) { upperItemNr++; DisplayItem *item; if (items.getLength() <= upperItemNr) { item = stack->getAt(i + firstLevelVisible)->getDisplayItem(*font); addItem(item); } else { item = items.getAt(upperItemNr); if (item == NULL) { item = stack->getAt(i + firstLevelVisible)->getDisplayItem(*font); setItemAt(upperItemNr, item); } } if (item) { int nw = getNumberWidth(upperItemNr + firstLevelVisible); setItemSize(item, getWidth() - nw, -1); int h = item->getHeight(); if (h < font->getLineHeight()) h = font->getLineHeight(); bottom -= h; int w = item->getWidth(); if (w < getWidth() - nw) setItemPosition(item, getWidth() - w, bottom); else setItemPosition(item, nw, bottom); } else { bottom -= font->getLineHeight(); } i++; } while (items.getLength() > upperItemNr + 1) { removeItemAt(upperItemNr + 1); } while (bottom > 0) { upperItemNr++; bottom -= font->getLineHeight(); } upperItemScanStart = -bottom; }
void DisplayItemList::addItemToIndexIfNeeded(const DisplayItem& displayItem, size_t index, DisplayItemIndicesByClientMap& displayItemIndicesByClient) { if (!displayItem.isCacheable()) return; DisplayItemIndicesByClientMap::iterator it = displayItemIndicesByClient.find(displayItem.client()); Vector<size_t>& indices = it == displayItemIndicesByClient.end() ? displayItemIndicesByClient.add(displayItem.client(), Vector<size_t>()).storedValue->value : it->value; indices.append(index); }
int StackLayout::getItemHeight(int i) { if (i >= getItems().getLength()) return font->getLineHeight(); int h = 0; DisplayItem *item = getItems().getAt(i); if (item) h = item->getHeight(); if (h < font->getLineHeight()) h = font->getLineHeight(); return h; }
static void showUnderInvalidationError(const char* reason, const DisplayItem& displayItem) { #ifndef NDEBUG WTFLogAlways("%s: %s\nSee http://crbug.com/450725.", reason, displayItem.asDebugString().utf8().data()); #else WTFLogAlways("%s. Run debug build to get more details\nSee http://crbug.com/450725.", reason); #endif // NDEBUG }
bool PaintChunker::incrementDisplayItemIndex(const DisplayItem& item) { DCHECK(RuntimeEnabledFeatures::slimmingPaintV2Enabled()); ItemBehavior behavior; Optional<PaintChunk::Id> newChunkId; if (DisplayItem::isForeignLayerType(item.getType())) { behavior = RequiresSeparateChunk; // Use null chunkId if we are skipping cache, so that the chunk will not // match any old chunk and will be treated as brand new. if (!item.skippedCache()) newChunkId.emplace(item.getId()); // Clear m_currentChunkId so that any display items after the foreign layer // without a new chunk id will be treated as having no id to avoid the chunk // from using the same id as the chunk before the foreign layer chunk. m_currentChunkId = WTF::nullopt; } else { behavior = DefaultBehavior; if (!item.skippedCache() && m_currentChunkId) newChunkId.emplace(*m_currentChunkId); } if (m_chunks.isEmpty()) { PaintChunk newChunk(0, 1, newChunkId ? &*newChunkId : nullptr, m_currentProperties); m_chunks.append(newChunk); m_chunkBehavior.append(behavior); return true; } auto& lastChunk = m_chunks.last(); bool canContinueChunk = m_currentProperties == lastChunk.properties && behavior != RequiresSeparateChunk && m_chunkBehavior.last() != RequiresSeparateChunk; if (canContinueChunk) { lastChunk.endIndex++; return false; } PaintChunk newChunk(lastChunk.endIndex, lastChunk.endIndex + 1, newChunkId ? &*newChunkId : nullptr, m_currentProperties); m_chunks.append(newChunk); m_chunkBehavior.append(behavior); return true; }
void PaintController::checkCachedDisplayItemIsUnchanged(const char* messagePrefix, const DisplayItem& newItem, const DisplayItem& oldItem) { ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()); ASSERT(!newItem.isCached()); ASSERT(!oldItem.isCached()); if (newItem.skippedCache()) { showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: skipped-cache in cached subsequence", &newItem, &oldItem); ASSERT_NOT_REACHED(); } if (newItem.isCacheable() && !m_validlyCachedClients.contains(&newItem.client())) { showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: invalidated in cached subsequence", &newItem, &oldItem); ASSERT_NOT_REACHED(); } if (newItem.equals(oldItem)) return; showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: display item changed", &newItem, &oldItem); #ifndef NDEBUG if (newItem.isDrawing()) { RefPtr<const SkPicture> newPicture = static_cast<const DrawingDisplayItem&>(newItem).picture(); RefPtr<const SkPicture> oldPicture = static_cast<const DrawingDisplayItem&>(oldItem).picture(); String oldPictureDebugString = oldPicture ? pictureAsDebugString(oldPicture.get()) : "None"; String newPictureDebugString = newPicture ? pictureAsDebugString(newPicture.get()) : "None"; WTFLogAlways("old picture:\n%s\n", oldPictureDebugString.utf8().data()); WTFLogAlways("new picture:\n%s\n", newPictureDebugString.utf8().data()); } #endif // NDEBUG ASSERT_NOT_REACHED(); }
void StackLayout::initLineScanning(int scanLine, int drawOffsetX, int drawWidth) { itemNr = upperItemNr; itemScanLine = upperItemScanStart + scanLine; while (itemScanLine > getItemHeight(itemNr)) { itemScanLine -= getItemHeight(itemNr); itemNr--; } this->drawOffsetX = drawOffsetX; this->drawWidth = drawWidth; currentNumberWidth = getNumberWidth(itemNr + firstLevelVisible); DisplayItem *item = NULL; if (itemNr < getItems().getLength()) item = getItems().getAt(itemNr); if (item) { if (drawOffsetX <= item->getX()) item->initLineScanning(scanLine - item->getY(), 0, drawWidth - item->getX()); else item->initLineScanning(scanLine - item->getY(), drawOffsetX - item->getX(), drawWidth); } }
void PaintController::processNewItem(DisplayItem& displayItem) { ASSERT(!m_constructionDisabled); ASSERT(!skippingCache() || !displayItem.isCached()); if (displayItem.isCached()) ++m_numCachedNewItems; #if ENABLE(ASSERT) // Verify noop begin/end pairs have been removed. if (m_newDisplayItemList.size() >= 2 && displayItem.isEnd()) { const auto& beginDisplayItem = m_newDisplayItemList[m_newDisplayItemList.size() - 2]; if (beginDisplayItem.isBegin() && beginDisplayItem.type() != DisplayItem::Subsequence && !beginDisplayItem.drawsContent()) ASSERT(!displayItem.isEndAndPairedWith(beginDisplayItem.type())); } #endif if (!m_scopeStack.isEmpty()) displayItem.setScope(m_scopeStack.last()); #if ENABLE(ASSERT) size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), m_newDisplayItemIndicesByClient, m_newDisplayItemList); if (index != kNotFound) { #ifndef NDEBUG showDebugData(); WTFLogAlways("DisplayItem %s has duplicated id with previous %s (index=%d)\n", displayItem.asDebugString().utf8().data(), m_newDisplayItemList[index].asDebugString().utf8().data(), static_cast<int>(index)); #endif ASSERT_NOT_REACHED(); } addItemToIndexIfNeeded(displayItem, m_newDisplayItemList.size() - 1, m_newDisplayItemIndicesByClient); #endif // ENABLE(ASSERT) if (skippingCache()) displayItem.setSkippedCache(); if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) m_newPaintChunks.incrementDisplayItemIndex(); }
void DisplayItemList::checkCachedDisplayItemIsUnchanged(const DisplayItem& displayItem, DisplayItemIndicesByClientMap& displayItemIndicesByClient) { ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()); if (!displayItem.isDrawing() || displayItem.skippedCache() || !clientCacheIsValid(displayItem.client())) return; // If checking under-invalidation, we always generate new display item even if the client is not invalidated. // Checks if the new picture is the same as the cached old picture. If the new picture is different but // the client is not invalidated, issue error about under-invalidation. size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), displayItemIndicesByClient, m_currentDisplayItems); if (index == kNotFound) { showUnderInvalidationError("ERROR: under-invalidation: no cached display item", displayItem); ASSERT_NOT_REACHED(); return; } DisplayItems::iterator foundItem = m_currentDisplayItems.begin() + index; RefPtr<const SkPicture> newPicture = static_cast<const DrawingDisplayItem&>(displayItem).picture(); RefPtr<const SkPicture> oldPicture = static_cast<const DrawingDisplayItem&>(*foundItem).picture(); // Invalidate the display item so that we can check if there are any remaining cached display items after merging. foundItem->clearClientForUnderInvalidationChecking(); if (!newPicture && !oldPicture) return; if (newPicture && oldPicture) { switch (static_cast<const DrawingDisplayItem&>(displayItem).underInvalidationCheckingMode()) { case DrawingDisplayItem::CheckPicture: if (newPicture->approximateOpCount() == oldPicture->approximateOpCount()) { SkDynamicMemoryWStream newPictureSerialized; newPicture->serialize(&newPictureSerialized); SkDynamicMemoryWStream oldPictureSerialized; oldPicture->serialize(&oldPictureSerialized); if (newPictureSerialized.bytesWritten() == oldPictureSerialized.bytesWritten()) { RefPtr<SkData> oldData = adoptRef(oldPictureSerialized.copyToData()); RefPtr<SkData> newData = adoptRef(newPictureSerialized.copyToData()); if (oldData->equals(newData.get())) return; } } break; case DrawingDisplayItem::CheckBitmap: if (newPicture->cullRect() == oldPicture->cullRect()) { SkBitmap bitmap; SkRect rect = newPicture->cullRect(); bitmap.allocPixels(SkImageInfo::MakeN32Premul(rect.width(), rect.height())); SkCanvas canvas(bitmap); canvas.translate(-rect.x(), -rect.y()); canvas.drawPicture(oldPicture.get()); SkPaint diffPaint; diffPaint.setXfermodeMode(SkXfermode::kDifference_Mode); canvas.drawPicture(newPicture.get(), nullptr, &diffPaint); if (bitmapIsAllZero(bitmap)) // Contents are the same. return; } default: ASSERT_NOT_REACHED(); } } showUnderInvalidationError("ERROR: under-invalidation: display item changed", displayItem); #ifndef NDEBUG String oldPictureDebugString = oldPicture ? pictureAsDebugString(oldPicture.get()) : "None"; String newPictureDebugString = newPicture ? pictureAsDebugString(newPicture.get()) : "None"; WTFLogAlways("old picture:\n%s\n", oldPictureDebugString.utf8().data()); WTFLogAlways("new picture:\n%s\n", newPictureDebugString.utf8().data()); #endif // NDEBUG ASSERT_NOT_REACHED(); }
void StackLayout::processLine(uint8_t *pixelData, int dataOffset) { if (itemNr < 0) return; if (itemScanLine >= getItemHeight(itemNr)) { itemScanLine = 0; if (--itemNr < 0) return; currentNumberWidth = getNumberWidth(itemNr + firstLevelVisible); } DisplayItem *item = NULL; if (itemNr < getItems().getLength()) item = getItems().getAt(itemNr); if (itemScanLine == 0 && item) { if (drawOffsetX <= item->getX()) item->initLineScanning(0, 0, drawWidth - item->getX()); else item->initLineScanning(0, drawOffsetX - item->getX(), drawWidth); } if (itemNr > 0) drawNumber(pixelData, dataOffset, itemNr + firstLevelVisible); if (item != NULL && isItemVisible(item)) { if (itemScanLine < item->getHeight()) { if (drawOffsetX <= item->getX()) item->processLine(pixelData, dataOffset + item->getX() - drawOffsetX); else item->processLine(pixelData, dataOffset); } } itemScanLine++; }