blob: 3da647b72ea9acb6a00f7eae19a3f8f80d4409b5 [file] [log] [blame]
// Copyright 2012 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 "cc/test/skia_common.h"
#include <stddef.h>
#include <string>
#include "base/strings/string_number_conversions.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/draw_image.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/skottie_wrapper.h"
#include "cc/test/fake_paint_image_generator.h"
#include "third_party/skia/include/core/SkImageGenerator.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/skia_util.h"
namespace cc {
namespace {
constexpr char kSkottieJSON[] =
"{"
" \"v\" : \"4.12.0\","
" \"fr\": 30,"
" \"w\" : $WIDTH,"
" \"h\" : $HEIGHT,"
" \"ip\": 0,"
" \"op\": $DURATION,"
" \"assets\": [],"
" \"layers\": ["
" {"
" \"ty\": 1,"
" \"sw\": $WIDTH,"
" \"sh\": $HEIGHT,"
" \"sc\": \"#00ff00\","
" \"ip\": 0,"
" \"op\": $DURATION"
" }"
" ]"
"}";
constexpr char kSkottieWidthToken[] = "$WIDTH";
constexpr char kSkottieHeightToken[] = "$HEIGHT";
constexpr char kSkottieDurationToken[] = "$DURATION";
constexpr int kFps = 30;
} // namespace
void DrawDisplayList(unsigned char* buffer,
const gfx::Rect& layer_rect,
scoped_refptr<const DisplayItemList> list) {
SkImageInfo info =
SkImageInfo::MakeN32Premul(layer_rect.width(), layer_rect.height());
SkBitmap bitmap;
bitmap.installPixels(info, buffer, info.minRowBytes());
SkCanvas canvas(bitmap);
canvas.clipRect(gfx::RectToSkRect(layer_rect));
list->Raster(&canvas);
}
bool AreDisplayListDrawingResultsSame(const gfx::Rect& layer_rect,
const DisplayItemList* list_a,
const DisplayItemList* list_b) {
const size_t pixel_size = 4 * layer_rect.size().GetArea();
std::unique_ptr<unsigned char[]> pixels_a(new unsigned char[pixel_size]);
std::unique_ptr<unsigned char[]> pixels_b(new unsigned char[pixel_size]);
memset(pixels_a.get(), 0, pixel_size);
memset(pixels_b.get(), 0, pixel_size);
DrawDisplayList(pixels_a.get(), layer_rect, list_a);
DrawDisplayList(pixels_b.get(), layer_rect, list_b);
return !memcmp(pixels_a.get(), pixels_b.get(), pixel_size);
}
Region ImageRectsToRegion(const DiscardableImageMap::Rects& rects) {
Region region;
for (const auto& r : rects.container())
region.Union(r);
return region;
}
sk_sp<PaintImageGenerator> CreatePaintImageGenerator(const gfx::Size& size) {
return sk_make_sp<FakePaintImageGenerator>(
SkImageInfo::MakeN32Premul(size.width(), size.height()));
}
PaintImage CreatePaintWorkletPaintImage(
scoped_refptr<PaintWorkletInput> input) {
auto paint_image = PaintImageBuilder::WithDefault()
.set_id(1)
.set_paint_worklet_input(std::move(input))
.TakePaintImage();
return paint_image;
}
SkYUVASizeInfo GetYUV420SizeInfo(const gfx::Size& image_size, bool has_alpha) {
const SkISize uv_size = SkISize::Make((image_size.width() + 1) / 2,
(image_size.height() + 1) / 2);
const size_t uv_width = base::checked_cast<size_t>(uv_size.width());
SkYUVASizeInfo yuva_size_info;
yuva_size_info.fSizes[SkYUVAIndex::kY_Index].set(image_size.width(),
image_size.height());
yuva_size_info.fWidthBytes[SkYUVAIndex::kY_Index] =
base::checked_cast<size_t>(image_size.width());
yuva_size_info.fSizes[SkYUVAIndex::kU_Index] = uv_size;
yuva_size_info.fWidthBytes[SkYUVAIndex::kU_Index] = uv_width;
yuva_size_info.fSizes[SkYUVAIndex::kV_Index] = uv_size;
yuva_size_info.fWidthBytes[SkYUVAIndex::kV_Index] = uv_width;
if (has_alpha) {
yuva_size_info.fSizes[SkYUVAIndex::kA_Index].set(image_size.width(),
image_size.height());
yuva_size_info.fWidthBytes[SkYUVAIndex::kA_Index] =
base::checked_cast<size_t>(image_size.width());
} else {
yuva_size_info.fSizes[SkYUVAIndex::kA_Index] = SkISize::MakeEmpty();
yuva_size_info.fWidthBytes[SkYUVAIndex::kA_Index] = 0u;
}
return yuva_size_info;
}
PaintImage CreateDiscardablePaintImage(const gfx::Size& size,
sk_sp<SkColorSpace> color_space,
bool allocate_encoded_data,
PaintImage::Id id,
SkColorType color_type,
bool is_yuv) {
if (!color_space)
color_space = SkColorSpace::MakeSRGB();
if (id == PaintImage::kInvalidId)
id = PaintImage::GetNextId();
SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), color_type,
kPremul_SkAlphaType, color_space);
sk_sp<PaintImageGenerator> generator;
if (is_yuv) {
// TODO(crbug.com/915972): Remove assumption of YUV420 in tests once we
// support other subsamplings.
generator = sk_make_sp<FakePaintImageGenerator>(
info, GetYUV420SizeInfo(size),
std::vector<FrameMetadata>{FrameMetadata()}, allocate_encoded_data);
} else {
generator = sk_make_sp<FakePaintImageGenerator>(
info, std::vector<FrameMetadata>{FrameMetadata()},
allocate_encoded_data);
}
auto paint_image =
PaintImageBuilder::WithDefault()
.set_id(id)
.set_paint_image_generator(generator)
// For simplicity, assume that any paint image created for testing is
// unspecified decode mode as would be the case with most img tags on
// the web.
.set_decoding_mode(PaintImage::DecodingMode::kUnspecified)
.TakePaintImage();
return paint_image;
}
DrawImage CreateDiscardableDrawImage(const gfx::Size& size,
sk_sp<SkColorSpace> color_space,
SkRect rect,
SkFilterQuality filter_quality,
const SkMatrix& matrix) {
SkIRect irect;
rect.roundOut(&irect);
return DrawImage(CreateDiscardablePaintImage(size, color_space), irect,
filter_quality, matrix);
}
PaintImage CreateAnimatedImage(const gfx::Size& size,
std::vector<FrameMetadata> frames,
int repetition_count,
PaintImage::Id id) {
return PaintImageBuilder::WithDefault()
.set_id(id)
.set_paint_image_generator(sk_make_sp<FakePaintImageGenerator>(
SkImageInfo::MakeN32Premul(size.width(), size.height()),
std::move(frames)))
.set_animation_type(PaintImage::AnimationType::ANIMATED)
.set_repetition_count(repetition_count)
.TakePaintImage();
}
PaintImage CreateBitmapImage(const gfx::Size& size, SkColorType color_type) {
SkBitmap bitmap;
auto info = SkImageInfo::Make(size.width(), size.height(), color_type,
kPremul_SkAlphaType, nullptr /* color_space */);
bitmap.allocPixels(info);
bitmap.eraseColor(SK_AlphaTRANSPARENT);
return PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
.set_image(SkImage::MakeFromBitmap(bitmap),
PaintImage::GetNextContentId())
.TakePaintImage();
}
scoped_refptr<SkottieWrapper> CreateSkottie(const gfx::Size& size,
int duration_secs) {
std::string json(kSkottieJSON);
for (size_t pos = json.find(kSkottieWidthToken); pos != std::string::npos;
pos = json.find(kSkottieWidthToken)) {
json.replace(pos, strlen(kSkottieWidthToken),
base::NumberToString(size.width()));
}
for (size_t pos = json.find(kSkottieHeightToken); pos != std::string::npos;
pos = json.find(kSkottieHeightToken)) {
json.replace(pos, strlen(kSkottieHeightToken),
base::NumberToString(size.height()));
}
for (size_t pos = json.find(kSkottieDurationToken); pos != std::string::npos;
pos = json.find(kSkottieDurationToken)) {
json.replace(pos, strlen(kSkottieDurationToken),
base::NumberToString(duration_secs * kFps));
}
return base::MakeRefCounted<SkottieWrapper>(
std::make_unique<SkMemoryStream>(json.c_str(), json.size()));
}
PaintImage CreateNonDiscardablePaintImage(const gfx::Size& size) {
sk_sp<const GrGLInterface> gl_interface(GrGLCreateNullInterface());
auto context = GrContext::MakeGL(std::move(gl_interface));
SkBitmap bitmap;
auto info = SkImageInfo::Make(size.width(), size.height(), kN32_SkColorType,
kPremul_SkAlphaType, nullptr /* color_space */);
bitmap.allocPixels(info);
bitmap.eraseColor(SK_AlphaTRANSPARENT);
return PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
.set_image(SkImage::MakeFromBitmap(bitmap)->makeTextureImage(
context.get(), nullptr),
PaintImage::GetNextContentId())
.TakePaintImage();
}
} // namespace cc