#include "printing/metafile_skia.h"
#include <utility>
#include "base/containers/span_reader.h"
#include "build/build_config.h"
#include "cc/paint/paint_op.h"
#include "cc/paint/paint_record.h"
#include "printing/common/metafile_utils.h"
#include "printing/mojom/print.mojom.h"
#include "skia/ext/font_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/codec/SkCodec.h"
#include "third_party/skia/include/codec/SkJpegDecoder.h"
#include "third_party/skia/include/codec/SkPngRustDecoder.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkFont.h"
#include "third_party/skia/include/core/SkFontStyle.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSerialProcs.h"
#include "third_party/skia/include/core/SkSize.h"
#include "third_party/skia/include/core/SkStream.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "third_party/skia/include/encode/SkJpegEncoder.h"
#include "ui/gfx/skia_span_util.h"
namespace printing {
TEST(MetafileSkiaTest, FrameContent) {
constexpr int kPictureSideLen = 100;
constexpr int kPageSideLen = 150;
sk_sp<SkPicture> pic_holder = SkPicture::MakePlaceholder(
SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen));
cc::PaintOpBuffer buffer;
cc::PaintFlags flags;
flags.setColor(SK_ColorWHITE);
const SkRect page_rect = SkRect::MakeXYWH(0, 0, kPageSideLen, kPageSideLen);
buffer.push<cc::DrawRectOp>(page_rect, flags);
const uint32_t content_id = pic_holder->uniqueID();
buffer.push<cc::CustomDataOp>(content_id);
SkSize page_size = SkSize::Make(kPageSideLen, kPageSideLen);
MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP, 1);
metafile.AppendPage(page_size, buffer.ReleaseAsRecord());
metafile.AppendSubframeInfo(content_id, base::UnguessableToken::Create(),
std::move(pic_holder));
metafile.FinishFrameContent();
SkStreamAsset* metafile_stream = metafile.GetPdfData();
ASSERT_TRUE(metafile_stream);
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(kPictureSideLen, kPictureSideLen);
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(SK_ColorRED);
paint.setAlpha(SK_AlphaOPAQUE);
canvas->drawRect(SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen),
paint);
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
EXPECT_TRUE(picture);
PictureDeserializationContext subframes;
subframes[content_id] = picture;
SkDeserialProcs procs = DeserializationProcs(&subframes, nullptr, nullptr);
sk_sp<SkPicture> pic = SkPicture::MakeFromStream(metafile_stream, &procs);
ASSERT_TRUE(pic);
EXPECT_TRUE(pic->cullRect() == page_rect);
SkBitmap bitmap;
bitmap.allocN32Pixels(kPageSideLen, kPageSideLen);
SkCanvas bitmap_canvas(bitmap, SkSurfaceProps{});
pic->playback(&bitmap_canvas);
EXPECT_EQ(bitmap.getColor(0, 0), SK_ColorRED);
EXPECT_EQ(bitmap.getColor(kPictureSideLen - 1, kPictureSideLen - 1),
SK_ColorRED);
EXPECT_EQ(bitmap.getColor(kPictureSideLen / 2, kPictureSideLen / 2),
SK_ColorRED);
EXPECT_EQ(bitmap.getColor(kPictureSideLen, kPictureSideLen), SK_ColorWHITE);
}
TEST(MetafileSkiaTest, GetPageBounds) {
constexpr int kPictureSideLen = 100;
constexpr int kPageSideWidth = 150;
constexpr int kPageSideHeight = 120;
sk_sp<SkPicture> pic_holder = SkPicture::MakePlaceholder(
SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen));
cc::PaintOpBuffer buffer;
cc::PaintFlags flags;
flags.setColor(SK_ColorWHITE);
const SkRect page_rect =
SkRect::MakeXYWH(0, 0, kPageSideWidth, kPageSideHeight);
buffer.push<cc::DrawRectOp>(page_rect, flags);
const uint32_t content_id = pic_holder->uniqueID();
buffer.push<cc::CustomDataOp>(content_id);
SkSize page_size = SkSize::Make(kPageSideWidth, kPageSideHeight);
MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP, 1);
metafile.AppendPage(page_size, buffer.ReleaseAsRecord());
metafile.AppendSubframeInfo(content_id, base::UnguessableToken::Create(),
std::move(pic_holder));
metafile.FinishFrameContent();
EXPECT_EQ(1u, metafile.GetPageCount());
EXPECT_EQ(gfx::Rect(kPageSideWidth, kPageSideHeight),
metafile.GetPageBounds(1));
EXPECT_EQ(gfx::Rect(), metafile.GetPageBounds(0));
EXPECT_EQ(gfx::Rect(), metafile.GetPageBounds(2));
}
TEST(MetafileSkiaTest, MultiPictureDocumentTypefaces) {
constexpr int kPictureSideLen = 100;
constexpr int kPageSideLen = 150;
constexpr int kDocumentCookie = 1;
constexpr int kNumDocumentPages = 2;
ContentProxySet serialize_typeface_ctx;
PictureDeserializationContext subframes;
TypefaceDeserializationContext typefaces;
SkDeserialProcs procs = DeserializationProcs(&subframes, &typefaces, nullptr);
constexpr char kTypefaceName1[] = "sans-serif";
#if BUILDFLAG(IS_WIN)
constexpr char kTypefaceName2[] = "Courier New";
#else
constexpr char kTypefaceName2[] = "monospace";
#endif
constexpr size_t kNumTypefaces = 2;
sk_sp<SkTypeface> typeface1 =
skia::MakeTypefaceFromName(kTypefaceName1, SkFontStyle());
sk_sp<SkTypeface> typeface2 =
skia::MakeTypefaceFromName(kTypefaceName2, SkFontStyle());
const SkFont font1 = SkFont(typeface1, 10);
const SkFont font2 = SkFont(typeface2, 12);
cc::NodeId node_id = 7;
cc::PaintFlags flags_text;
flags_text.setColor(SK_ColorBLACK);
cc::PaintFlags flags;
flags.setColor(SK_ColorWHITE);
const SkRect page_rect = SkRect::MakeXYWH(0, 0, kPageSideLen, kPageSideLen);
SkSize page_size = SkSize::Make(kPageSideLen, kPageSideLen);
for (int i = 0; i < kNumDocumentPages; i++) {
MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP, kDocumentCookie);
metafile.UtilizeTypefaceContext(&serialize_typeface_ctx);
sk_sp<SkPicture> pic_holder = SkPicture::MakePlaceholder(
SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen));
cc::PaintOpBuffer buffer;
buffer.push<cc::DrawRectOp>(page_rect, flags);
const uint32_t content_id = pic_holder->uniqueID();
buffer.push<cc::CustomDataOp>(content_id);
sk_sp<SkTextBlob> text_blob1 = SkTextBlob::MakeFromString("foo", font1);
buffer.push<cc::DrawTextBlobOp>(text_blob1, 0.0f, 0.0f, ++node_id,
flags_text);
sk_sp<SkTextBlob> text_blob2 = SkTextBlob::MakeFromString("bar", font2);
buffer.push<cc::DrawTextBlobOp>(text_blob2, 0.0f, 0.0f, ++node_id,
flags_text);
sk_sp<SkTextBlob> text_blob3 = SkTextBlob::MakeFromString("bar", font2);
buffer.push<cc::DrawTextBlobOp>(text_blob3, 0.0f, 0.0f, ++node_id,
flags_text);
metafile.AppendPage(page_size, buffer.ReleaseAsRecord());
metafile.AppendSubframeInfo(content_id, base::UnguessableToken::Create(),
std::move(pic_holder));
metafile.FinishFrameContent();
SkStreamAsset* metafile_stream = metafile.GetPdfData();
ASSERT_TRUE(metafile_stream);
EXPECT_EQ(typefaces.size(), i ? kNumTypefaces : 0);
ASSERT_TRUE(SkPicture::MakeFromStream(metafile_stream, &procs));
EXPECT_EQ(typefaces.size(), kNumTypefaces);
}
}
TEST(MetafileSkiaTest, SerializeUnencodedRasterImageAsPNG) {
sk_sp<SkSurface> surface =
SkSurfaces::Raster(SkImageInfo::MakeN32(100, 50, kOpaque_SkAlphaType));
SkCanvas* canvas = surface->getCanvas();
SkPaint paint;
paint.setColor(SK_ColorGREEN);
canvas->clear(SK_ColorYELLOW);
canvas->drawRect(SkRect::MakeSize(SkSize::Make(75, 25)), paint);
sk_sp<SkImage> image = surface->makeImageSnapshot();
ASSERT_FALSE(image->refEncodedData());
PictureSerializationContext subframes;
ImageSerializationContext images;
SkSerialProcs procs = SerializationProcs(&subframes, nullptr, &images);
sk_sp<SkData> encoded_data =
(*procs.fImageProc)(image.get(), procs.fImageCtx);
ASSERT_TRUE(encoded_data);
EXPECT_GT(encoded_data->size(), sizeof(uint32_t));
base::SpanReader reader{gfx::SkDataToSpan(encoded_data)};
uint32_t img_id;
ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
EXPECT_EQ(img_id, image->uniqueID());
ASSERT_TRUE(images.contains(img_id));
auto encoded_image = reader.remaining_span();
ASSERT_TRUE(
SkPngRustDecoder::IsPng(encoded_image.data(), encoded_image.size()));
}
TEST(MetafileSkiaTest, SkipEncodingAsPngWhenImageIsAlreadyEncoded) {
sk_sp<SkSurface> surface =
SkSurfaces::Raster(SkImageInfo::MakeN32(100, 50, kOpaque_SkAlphaType));
SkCanvas* canvas = surface->getCanvas();
SkPaint paint;
paint.setColor(SK_ColorGREEN);
canvas->clear(SK_ColorYELLOW);
canvas->drawRect(SkRect::MakeSize(SkSize::Make(75, 25)), paint);
sk_sp<SkImage> unencoded_img = surface->makeImageSnapshot();
ASSERT_FALSE(unencoded_img->refEncodedData());
SkCodecs::Register(SkJpegDecoder::Decoder());
sk_sp<SkData> jpeg_data =
SkJpegEncoder::Encode(nullptr, unencoded_img.get(), SkJpegEncoder::Options{});
sk_sp<SkImage> jpeg_img = SkImages::DeferredFromEncodedData(jpeg_data);
ASSERT_TRUE(jpeg_img->refEncodedData());
PictureSerializationContext subframes;
ImageSerializationContext images;
SkSerialProcs procs = SerializationProcs(&subframes, nullptr, &images);
sk_sp<SkData> encoded_data =
(*procs.fImageProc)(jpeg_img.get(), procs.fImageCtx);
ASSERT_TRUE(encoded_data);
EXPECT_GT(encoded_data->size(), sizeof(uint32_t));
base::SpanReader reader{gfx::SkDataToSpan(encoded_data)};
uint32_t img_id;
ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
EXPECT_EQ(img_id, jpeg_img->uniqueID());
ASSERT_TRUE(images.contains(img_id));
auto encoded_image = reader.remaining_span();
ASSERT_TRUE(
SkJpegDecoder::IsJpeg(encoded_image.data(), encoded_image.size()));
}
TEST(MetafileSkiaTest, SerializeUniqueImages) {
sk_sp<SkSurface> surface =
SkSurfaces::Raster(SkImageInfo::MakeN32(100, 50, kOpaque_SkAlphaType));
SkCanvas* canvas = surface->getCanvas();
SkPaint paint;
paint.setColor(SK_ColorGREEN);
canvas->clear(SK_ColorYELLOW);
canvas->drawRect(SkRect::MakeSize(SkSize::Make(75, 25)), paint);
sk_sp<SkImage> unencoded_img = surface->makeImageSnapshot();
ASSERT_FALSE(unencoded_img->refEncodedData());
PictureSerializationContext subframes;
ImageSerializationContext images;
SkSerialProcs procs = SerializationProcs(&subframes, nullptr, &images);
sk_sp<SkData> encoded_data1 =
(*procs.fImageProc)(unencoded_img.get(), procs.fImageCtx);
ASSERT_TRUE(encoded_data1);
EXPECT_GT(encoded_data1->size(), sizeof(uint32_t));
{
base::SpanReader reader{gfx::SkDataToSpan(encoded_data1)};
uint32_t img_id;
ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
EXPECT_EQ(img_id, unencoded_img->uniqueID());
ASSERT_TRUE(images.contains(img_id));
auto encoded_image = reader.remaining_span();
ASSERT_FALSE(encoded_image.empty());
ASSERT_TRUE(
SkPngRustDecoder::IsPng(encoded_image.data(), encoded_image.size()));
}
sk_sp<SkData> encoded_data2 =
(*procs.fImageProc)(unencoded_img.get(), procs.fImageCtx);
ASSERT_TRUE(encoded_data2);
EXPECT_EQ(encoded_data2->size(), sizeof(uint32_t));
{
base::SpanReader reader{gfx::SkDataToSpan(encoded_data2)};
uint32_t img_id;
ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
EXPECT_EQ(img_id, unencoded_img->uniqueID());
EXPECT_FALSE(reader.remaining());
}
SkCodecs::Register(SkPngRustDecoder::Decoder());
PictureDeserializationContext d_subframes;
ImageDeserializationContext d_images;
SkDeserialProcs d_procs =
DeserializationProcs(&d_subframes, nullptr, &d_images);
sk_sp<SkImage> decoded_image1 = (*d_procs.fImageProc)(
encoded_data1->data(), encoded_data1->size(), d_procs.fImageCtx);
ASSERT_TRUE(decoded_image1);
sk_sp<SkImage> decoded_image2 = (*d_procs.fImageProc)(
encoded_data2->data(), encoded_data2->size(), d_procs.fImageCtx);
ASSERT_TRUE(decoded_image2);
EXPECT_EQ(decoded_image1->uniqueID(), decoded_image2->uniqueID());
}
}