blob: bd47db649e6ced525b0ac44eceed61043e9dcee8 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/graphics/paint/DrawingDisplayItem.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/paint/PaintCanvas.h"
#include "public/platform/WebDisplayItemList.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkData.h"
namespace blink {
void DrawingDisplayItem::Replay(GraphicsContext& context) const {
if (record_)
context.DrawRecord(record_);
}
void DrawingDisplayItem::AppendToWebDisplayItemList(
const LayoutSize& visual_rect_offset,
WebDisplayItemList* list) const {
if (record_) {
// Convert visual rect into the GraphicsLayer's coordinate space.
LayoutRect visual_rect = VisualRect();
visual_rect.Move(-visual_rect_offset);
list->AppendDrawingItem(EnclosingIntRect(visual_rect), record_);
}
}
bool DrawingDisplayItem::DrawsContent() const {
return record_.get();
}
#if DCHECK_IS_ON()
void DrawingDisplayItem::PropertiesAsJSON(JSONObject& json) const {
DisplayItem::PropertiesAsJSON(json);
json.SetString("rect", VisualRect().ToString());
json.SetBoolean("opaque", known_to_be_opaque_);
}
#endif
static bool RecordsEqual(sk_sp<const PaintRecord> record1,
sk_sp<const PaintRecord> record2,
const FloatRect& bounds) {
if (record1->size() != record2->size())
return false;
// TODO(enne): PaintRecord should have an operator==
sk_sp<SkData> data1 = ToSkPicture(record1, bounds)->serialize();
sk_sp<SkData> data2 = ToSkPicture(record2, bounds)->serialize();
return data1->equals(data2.get());
}
static SkBitmap RecordToBitmap(sk_sp<const PaintRecord> record,
const IntRect& bounds) {
SkBitmap bitmap;
bitmap.allocPixels(
SkImageInfo::MakeN32Premul(bounds.Width(), bounds.Height()));
SkiaPaintCanvas canvas(bitmap);
canvas.clear(SK_ColorTRANSPARENT);
canvas.translate(-bounds.X(), -bounds.Y());
canvas.drawPicture(std::move(record));
return bitmap;
}
static bool BitmapsEqual(sk_sp<const PaintRecord> record1,
sk_sp<const PaintRecord> record2,
const IntRect& bounds) {
SkBitmap bitmap1 = RecordToBitmap(record1, bounds);
SkBitmap bitmap2 = RecordToBitmap(record2, bounds);
int mismatch_count = 0;
constexpr int kMaxMismatches = 10;
for (int y = 0; y < bounds.Height(); ++y) {
for (int x = 0; x < bounds.Width(); ++x) {
SkColor pixel1 = bitmap1.getColor(x, y);
SkColor pixel2 = bitmap2.getColor(x, y);
if (pixel1 != pixel2) {
if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
return false;
LOG(ERROR) << "x=" << x << " y=" << y << " " << std::hex << pixel1
<< " vs " << std::hex << pixel2;
if (++mismatch_count >= kMaxMismatches)
return false;
}
}
}
return !mismatch_count;
}
bool DrawingDisplayItem::Equals(const DisplayItem& other) const {
if (!DisplayItem::Equals(other))
return false;
const auto& record = GetPaintRecord();
const auto& other_record =
static_cast<const DrawingDisplayItem&>(other).GetPaintRecord();
if (!record && !other_record)
return true;
if (!record || !other_record)
return false;
const auto& bounds = this->VisualRect();
const auto& other_bounds = other.VisualRect();
if (bounds != other_bounds)
return false;
if (RecordsEqual(record, other_record, FloatRect(bounds)))
return true;
// Sometimes the client may produce different records for the same visual
// result, which should be treated as equal.
IntRect int_bounds = EnclosingIntRect(bounds);
// Limit the bounds to prevent OOM.
int_bounds.Intersect(IntRect(int_bounds.X(), int_bounds.Y(), 6000, 6000));
return BitmapsEqual(std::move(record), std::move(other_record), int_bounds);
}
} // namespace blink