Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /printing /metafile_skia_unittest.cc
blob: 0c2e113a05287810da632dfa2bfb854a330a72c9 [file] [log] [blame] [edit]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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/SkPngDecoder.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){
constexprint kPictureSideLen=100;
constexprint kPageSideLen=150;
// Create a placeholder picture.
sk_sp<SkPicture> pic_holder=SkPicture::MakePlaceholder(
SkRect::MakeXYWH(0,0, kPictureSideLen, kPictureSideLen));
// Create the page with nested content which is the placeholder and will be
// replaced later.
cc::PaintOpBuffer buffer;
cc::PaintFlags flags;
flags.setColor(SK_ColorWHITE);
constSkRect page_rect=SkRect::MakeXYWH(0,0, kPageSideLen, kPageSideLen);
buffer.push<cc::DrawRectOp>(page_rect, flags);
constuint32_t content_id= pic_holder->uniqueID();
buffer.push<cc::CustomDataOp>(content_id);
SkSize page_size=SkSize::Make(kPageSideLen, kPageSideLen);
// Finish creating the entire metafile.
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);
// Draw a 100 by 100 red square which will be the actual content of
// the placeholder.
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);
// Get the complete picture by replacing the placeholder.
PictureDeserializationContext subframes;
subframes[content_id]= picture;
SkDeserialProcs procs=DeserializationProcs(&subframes,nullptr,nullptr);
sk_sp<SkPicture> pic=SkPicture::MakeFromStream(metafile_stream,&procs);
ASSERT_TRUE(pic);
// Verify the resultant picture is as expected by comparing the sizes and
// detecting the color inside and outside of the square area.
EXPECT_TRUE(pic->cullRect()== page_rect);
SkBitmap bitmap;
bitmap.allocN32Pixels(kPageSideLen, kPageSideLen);
SkCanvas bitmap_canvas(bitmap,SkSurfaceProps{});
pic->playback(&bitmap_canvas);
// Check top left pixel color of the red square.
EXPECT_EQ(bitmap.getColor(0,0), SK_ColorRED);
// Check bottom right pixel of the red square.
EXPECT_EQ(bitmap.getColor(kPictureSideLen-1, kPictureSideLen-1),
SK_ColorRED);
// Check inside of the red square.
EXPECT_EQ(bitmap.getColor(kPictureSideLen/2, kPictureSideLen/2),
SK_ColorRED);
// Check outside of the red square.
EXPECT_EQ(bitmap.getColor(kPictureSideLen, kPictureSideLen), SK_ColorWHITE);
}
TEST(MetafileSkiaTest,GetPageBounds){
constexprint kPictureSideLen=100;
constexprint kPageSideWidth=150;
constexprint kPageSideHeight=120;
// Create a placeholder picture.
sk_sp<SkPicture> pic_holder=SkPicture::MakePlaceholder(
SkRect::MakeXYWH(0,0, kPictureSideLen, kPictureSideLen));
// Create the page with nested content which is the placeholder and will be
// replaced later.
cc::PaintOpBuffer buffer;
cc::PaintFlags flags;
flags.setColor(SK_ColorWHITE);
constSkRect page_rect=
SkRect::MakeXYWH(0,0, kPageSideWidth, kPageSideHeight);
buffer.push<cc::DrawRectOp>(page_rect, flags);
constuint32_t content_id= pic_holder->uniqueID();
buffer.push<cc::CustomDataOp>(content_id);
SkSize page_size=SkSize::Make(kPageSideWidth, kPageSideHeight);
// Finish creating the entire metafile.
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();
// Confirm there is 1 page in the doc.
EXPECT_EQ(1u, metafile.GetPageCount());
// Test in bound case.
EXPECT_EQ(gfx::Rect(kPageSideWidth, kPageSideHeight),
metafile.GetPageBounds(/*page_number=*/1));
// Test out of bounds cases.
EXPECT_EQ(gfx::Rect(), metafile.GetPageBounds(/*page_number=*/0));
EXPECT_EQ(gfx::Rect(), metafile.GetPageBounds(/*page_number=*/2));
}
TEST(MetafileSkiaTest,MultiPictureDocumentTypefaces){
constexprint kPictureSideLen=100;
constexprint kPageSideLen=150;
constexprint kDocumentCookie=1;
constexprint kNumDocumentPages=2;
// The content tracking for serialization/deserialization.
ContentProxySet serialize_typeface_ctx;
PictureDeserializationContext subframes;
TypefaceDeserializationContext typefaces;
SkDeserialProcs procs=DeserializationProcs(&subframes,&typefaces,nullptr);
// The typefaces which will be reused across the multiple (duplicate) pages.
constexprchar kTypefaceName1[]="sans-serif";
#if BUILDFLAG(IS_WIN)
constexprchar kTypefaceName2[]="Courier New";
#else
constexprchar kTypefaceName2[]="monospace";
#endif
constexprsize_t kNumTypefaces=2;
sk_sp<SkTypeface> typeface1=
skia::MakeTypefaceFromName(kTypefaceName1,SkFontStyle());
sk_sp<SkTypeface> typeface2=
skia::MakeTypefaceFromName(kTypefaceName2,SkFontStyle());
constSkFont font1=SkFont(typeface1,10);
constSkFont font2=SkFont(typeface2,12);
// Node IDs for the text, which will increase for each text blob added.
cc::NodeId node_id=7;
// All text can just be black.
cc::PaintFlags flags_text;
flags_text.setColor(SK_ColorBLACK);
// Mark the text on white pages, each of the same size.
cc::PaintFlags flags;
flags.setColor(SK_ColorWHITE);
constSkRect 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);
// When the stream is serialized inside FinishFrameContent(), any typeface
// which is used on any page will be serialized only once by the first
// page's metafile which needed it. Any subsequent page that reuses the
// same typeface will rely upon `serialize_typeface_ctx` which is used by
// printing::SerializeOopTypeface() to optimize away the need to resend.
metafile.UtilizeTypefaceContext(&serialize_typeface_ctx);
sk_sp<SkPicture> pic_holder=SkPicture::MakePlaceholder(
SkRect::MakeXYWH(0,0, kPictureSideLen, kPictureSideLen));
// Create the page for the text content.
cc::PaintOpBuffer buffer;
buffer.push<cc::DrawRectOp>(page_rect, flags);
constuint32_t content_id= pic_holder->uniqueID();
buffer.push<cc::CustomDataOp>(content_id);
// Mark the page with some text using multiple fonts.
// Use the first font.
sk_sp<SkTextBlob> text_blob1=SkTextBlob::MakeFromString("foo", font1);
buffer.push<cc::DrawTextBlobOp>(text_blob1,0.0f,0.0f,++node_id,
flags_text);
// Use the second font.
sk_sp<SkTextBlob> text_blob2=SkTextBlob::MakeFromString("bar", font2);
buffer.push<cc::DrawTextBlobOp>(text_blob2,0.0f,0.0f,++node_id,
flags_text);
// Reuse the first font again on same page.
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);
// Deserialize the stream. Any given typeface is expected to appear only
// once in the stream, so the deserialization context of `typefaces` bundled
// with `procs` should be empty the first time through, and afterwards
// there should never be more than the number of unique typefaces we used,
// regardless of number of pages.
EXPECT_EQ(typefaces.size(), i? kNumTypefaces:0);
ASSERT_TRUE(SkPicture::MakeFromStream(metafile_stream,&procs));
EXPECT_EQ(typefaces.size(), kNumTypefaces);
}
}
TEST(MetafileSkiaTest,SerializeUnencodedRasterImageAsPNG){
// Make raster surface
sk_sp<SkSurface> surface=
SkSurfaces::Raster(SkImageInfo::MakeN32(100,50, kOpaque_SkAlphaType));
SkCanvas* canvas= surface->getCanvas();
// Draw to it
SkPaint paint;
paint.setColor(SK_ColorGREEN);
canvas->clear(SK_ColorYELLOW);
canvas->drawRect(SkRect::MakeSize(SkSize::Make(75,25)), paint);
// Make sure that the image is not encoded
sk_sp<SkImage> image= surface->makeImageSnapshot();
ASSERT_FALSE(image->refEncodedData());
// Use the image serialization proc and assert that we get encoded data back
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));
// We expect unencoded images to be encoded as PNG.
auto encoded_image= reader.remaining_span();
ASSERT_TRUE(
SkPngDecoder::IsPng(encoded_image.data(), encoded_image.size()));
}
TEST(MetafileSkiaTest,SkipEncodingAsPngWhenImageIsAlreadyEncoded){
// Make raster surface
sk_sp<SkSurface> surface=
SkSurfaces::Raster(SkImageInfo::MakeN32(100,50, kOpaque_SkAlphaType));
SkCanvas* canvas= surface->getCanvas();
// Draw to it
SkPaint paint;
paint.setColor(SK_ColorGREEN);
canvas->clear(SK_ColorYELLOW);
canvas->drawRect(SkRect::MakeSize(SkSize::Make(75,25)), paint);
// Get an image that is not encoded
sk_sp<SkImage> unencoded_img= surface->makeImageSnapshot();
ASSERT_FALSE(unencoded_img->refEncodedData());
// Encode the image data as JPEG
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());
// Call serialization proc on the JPEG image
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));
// Make sure the data is still encoded as JPEG
auto encoded_image= reader.remaining_span();
ASSERT_TRUE(
SkJpegDecoder::IsJpeg(encoded_image.data(), encoded_image.size()));
}
TEST(MetafileSkiaTest,SerializeUniqueImages){
// Make raster surface
sk_sp<SkSurface> surface=
SkSurfaces::Raster(SkImageInfo::MakeN32(100,50, kOpaque_SkAlphaType));
SkCanvas* canvas= surface->getCanvas();
// Draw to it
SkPaint paint;
paint.setColor(SK_ColorGREEN);
canvas->clear(SK_ColorYELLOW);
canvas->drawRect(SkRect::MakeSize(SkSize::Make(75,25)), paint);
// Get an image that is not encoded
sk_sp<SkImage> unencoded_img= surface->makeImageSnapshot();
ASSERT_FALSE(unencoded_img->refEncodedData());
// Call serialization proc on the image for the first time.
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));
// The image is expected to be encoded as PNG.
auto encoded_image= reader.remaining_span();
ASSERT_FALSE(encoded_image.empty());
ASSERT_TRUE(
SkPngDecoder::IsPng(encoded_image.data(), encoded_image.size()));
}
// Call the serialization proc on the image for the second time.
sk_sp<SkData> encoded_data2=
(*procs.fImageProc)(unencoded_img.get(), procs.fImageCtx);
ASSERT_TRUE(encoded_data2);
EXPECT_EQ(encoded_data2->size(),sizeof(uint32_t));
{
// The second serialization should only return image id.
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());
}
// Deserialization.
SkCodecs::Register(SkPngDecoder::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());
}
}// namespace printing

[8]ページ先頭

©2009-2025 Movatter.jp