blob: fd959cfd61bdb2bec6045a31a98a081fa93a8ffb [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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 <algorithm>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "base/base_paths.h"
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#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/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageGenerator.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkYUVAPixmaps.h"
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/skia_conversions.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, SkSurfaceProps{});
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();
auto pixels_a = base::HeapArray<unsigned char>::Uninit(pixel_size);
auto pixels_b = base::HeapArray<unsigned char>::Uninit(pixel_size);
std::ranges::fill(pixels_a, 0);
std::ranges::fill(pixels_b, 0);
DrawDisplayList(pixels_a.data(), layer_rect, list_a);
DrawDisplayList(pixels_b.data(), layer_rect, list_b);
return pixels_a.as_span() == pixels_b.as_span();
}
Region ImageRectsToRegion(const DiscardableImageMap::Rects& rects) {
Region region;
for (const auto& r : rects) {
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_deferred_paint_record(std::move(input))
.TakePaintImage();
return paint_image;
}
SkYUVAPixmapInfo GetYUVAPixmapInfo(const gfx::Size& image_size,
YUVSubsampling format,
SkYUVAPixmapInfo::DataType yuv_data_type,
bool has_alpha) {
// TODO(skbug.com/10632): Update this when we have planar configs with alpha.
if (has_alpha) {
NOTREACHED();
}
SkYUVAInfo::Subsampling subsampling;
switch (format) {
case YUVSubsampling::k410:
subsampling = SkYUVAInfo::Subsampling::k410;
break;
case YUVSubsampling::k411:
subsampling = SkYUVAInfo::Subsampling::k411;
break;
case YUVSubsampling::k420:
subsampling = SkYUVAInfo::Subsampling::k420;
break;
case YUVSubsampling::k422:
subsampling = SkYUVAInfo::Subsampling::k422;
break;
case YUVSubsampling::k440:
subsampling = SkYUVAInfo::Subsampling::k440;
break;
case YUVSubsampling::k444:
subsampling = SkYUVAInfo::Subsampling::k444;
break;
default:
NOTREACHED();
}
SkYUVAInfo yuva_info({image_size.width(), image_size.height()},
SkYUVAInfo::PlaneConfig::kY_U_V, subsampling,
kJPEG_Full_SkYUVColorSpace);
return SkYUVAPixmapInfo(yuva_info, yuv_data_type, /*row bytes*/ nullptr);
}
PaintImage CreateDiscardablePaintImage(
const gfx::Size& size,
sk_sp<SkColorSpace> color_space,
bool allocate_encoded_data,
PaintImage::Id id,
SkColorType color_type,
std::optional<YUVSubsampling> yuv_format,
SkYUVAPixmapInfo::DataType yuv_data_type) {
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<FakePaintImageGenerator> generator;
if (yuv_format) {
SkYUVAPixmapInfo yuva_pixmap_info =
GetYUVAPixmapInfo(size, *yuv_format, yuv_data_type);
generator = sk_make_sp<FakePaintImageGenerator>(
info, yuva_pixmap_info, 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,
PaintFlags::FilterQuality filter_quality,
const SkM44& matrix) {
SkIRect irect;
rect.roundOut(&irect);
return DrawImage(CreateDiscardablePaintImage(size, color_space), false, 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::kAnimated)
.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(SkImages::RasterFromBitmap(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 CreateSkottieFromString(json);
}
scoped_refptr<SkottieWrapper> CreateSkottieFromString(std::string_view json) {
base::span<const uint8_t> json_span = base::as_byte_span(json);
return SkottieWrapper::UnsafeCreateSerializable(
std::vector<uint8_t>(json_span.begin(), json_span.end()));
}
std::string LoadSkottieFileFromTestData(
base::FilePath::StringViewType animation_file_name) {
base::FilePath animation_path;
CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &animation_path));
animation_path = animation_path.AppendASCII("cc/test/data/lottie")
.Append(base::FilePath(animation_file_name));
std::string animation_json;
CHECK(base::ReadFileToString(animation_path, &animation_json))
<< animation_path;
return animation_json;
}
scoped_refptr<SkottieWrapper> CreateSkottieFromTestDataDir(
base::FilePath::StringViewType animation_file_name) {
return CreateSkottieFromString(
LoadSkottieFileFromTestData(animation_file_name));
}
PaintImage CreateNonDiscardablePaintImage(const gfx::Size& size) {
auto context = GrDirectContext::MakeMock(nullptr);
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_texture_image(SkImages::TextureFromImage(
context.get(), SkImages::RasterFromBitmap(bitmap)),
PaintImage::GetNextContentId())
.TakePaintImage();
}
} // namespace cc