bool DrawingDisplayItem::equals(const DisplayItem& other) const
{
    if (!DisplayItem::equals(other))
        return false;

    RefPtr<const SkPicture> picture = this->picture();
    RefPtr<const SkPicture> otherPicture = static_cast<const DrawingDisplayItem&>(other).picture();

    if (!picture && !otherPicture)
        return true;
    if (!picture || !otherPicture)
        return false;

    switch (getUnderInvalidationCheckingMode()) {
    case DrawingDisplayItem::CheckPicture: {
        if (picture->approximateOpCount() != otherPicture->approximateOpCount())
            return false;

        SkDynamicMemoryWStream pictureSerialized;
        picture->serialize(&pictureSerialized);
        SkDynamicMemoryWStream otherPictureSerialized;
        otherPicture->serialize(&otherPictureSerialized);
        if (pictureSerialized.bytesWritten() != otherPictureSerialized.bytesWritten())
            return false;

        RefPtr<SkData> oldData = adoptRef(otherPictureSerialized.copyToData());
        RefPtr<SkData> newData = adoptRef(pictureSerialized.copyToData());
        return oldData->equals(newData.get());
    }
    case DrawingDisplayItem::CheckBitmap: {
        SkRect rect = picture->cullRect();
        if (rect != otherPicture->cullRect())
            return false;

        SkBitmap bitmap;
        bitmap.allocPixels(SkImageInfo::MakeN32Premul(rect.width(), rect.height()));
        SkCanvas canvas(bitmap);
        canvas.translate(-rect.x(), -rect.y());
        canvas.drawPicture(otherPicture.get());
        SkPaint diffPaint;
        diffPaint.setXfermodeMode(SkXfermode::kDifference_Mode);
        canvas.drawPicture(picture.get(), nullptr, &diffPaint);
        return bitmapIsAllZero(bitmap);
    }
    default:
        ASSERT_NOT_REACHED();
    }
    return false;
}
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();
}