123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254 |
- // Copyright 2017 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/paint/paint_op_buffer.h"
- #include <algorithm>
- #include "base/bind.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/scoped_refptr.h"
- #include "base/strings/stringprintf.h"
- #include "base/test/bind.h"
- #include "cc/paint/decoded_draw_image.h"
- #include "cc/paint/display_item_list.h"
- #include "cc/paint/image_provider.h"
- #include "cc/paint/image_transfer_cache_entry.h"
- #include "cc/paint/paint_flags.h"
- #include "cc/paint/paint_image_builder.h"
- #include "cc/paint/paint_op_buffer_serializer.h"
- #include "cc/paint/paint_op_reader.h"
- #include "cc/paint/paint_op_writer.h"
- #include "cc/paint/shader_transfer_cache_entry.h"
- #include "cc/paint/skottie_resource_metadata.h"
- #include "cc/paint/skottie_text_property_value.h"
- #include "cc/paint/skottie_wrapper.h"
- #include "cc/paint/transfer_cache_entry.h"
- #include "cc/test/lottie_test_data.h"
- #include "cc/test/paint_op_helper.h"
- #include "cc/test/skia_common.h"
- #include "cc/test/test_options_provider.h"
- #include "cc/test/test_paint_worklet_input.h"
- #include "cc/test/test_skcanvas.h"
- #include "cc/test/transfer_cache_test_helper.h"
- #include "skia/buildflags.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/skia/include/core/SkColor.h"
- #include "third_party/skia/include/core/SkMaskFilter.h"
- #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
- #include "third_party/skia/include/effects/SkDashPathEffect.h"
- #include "third_party/skia/include/effects/SkLayerDrawLooper.h"
- #include "third_party/skia/include/private/chromium/SkChromeRemoteGlyphCache.h"
- #include "ui/gfx/geometry/test/geometry_util.h"
- using testing::_;
- using testing::AtLeast;
- using testing::Contains;
- using testing::Key;
- using testing::Mock;
- using testing::NiceMock;
- using testing::NotNull;
- namespace cc {
- namespace {
- // An arbitrary size guaranteed to fit the size of any serialized op in this
- // unit test. This can also be used for deserialized op size safely in this
- // unit test suite as generally deserialized ops are smaller.
- static constexpr size_t kBufferBytesPerOp = 1000 + sizeof(LargestPaintOp);
- template <typename T>
- void ValidateOps(PaintOpBuffer* buffer) {
- // Make sure all test data is valid before serializing it.
- for (auto* op : PaintOpBuffer::Iterator(buffer))
- EXPECT_TRUE(static_cast<T*>(op)->IsValid());
- }
- } // namespace
- class PaintOpSerializationTestUtils {
- public:
- static void FillArbitraryShaderValues(PaintShader* shader, bool use_matrix) {
- shader->shader_type_ = PaintShader::Type::kTwoPointConicalGradient;
- shader->flags_ = 12345;
- shader->end_radius_ = 12.3f;
- shader->start_radius_ = 13.4f;
- shader->tx_ = SkTileMode::kRepeat;
- shader->ty_ = SkTileMode::kMirror;
- shader->fallback_color_ = {0.99f, 0.98f, 0.97f, 0.99f};
- shader->scaling_behavior_ = PaintShader::ScalingBehavior::kRasterAtScale;
- if (use_matrix) {
- shader->local_matrix_.emplace(SkMatrix::I());
- shader->local_matrix_->setSkewX(10);
- shader->local_matrix_->setSkewY(20);
- }
- shader->center_ = SkPoint::Make(50, 40);
- shader->tile_ = SkRect::MakeXYWH(7, 77, 777, 7777);
- shader->start_point_ = SkPoint::Make(-1, -5);
- shader->end_point_ = SkPoint::Make(13, -13);
- shader->start_degrees_ = 123;
- shader->end_degrees_ = 456;
- // TODO(vmpstr): Add PaintImage/PaintRecord.
- shader->colors_ = {{0.1f, 0.2f, 0.3f, 0.4f},
- {0.05f, 0.15f, 0.25f, 0.35f},
- {0.0f, 0.5f, 0.9f, 0.1f}};
- shader->positions_ = {0.f, 0.4f, 1.f};
- }
- };
- TEST(PaintOpBufferTest, Empty) {
- PaintOpBuffer buffer;
- EXPECT_EQ(buffer.size(), 0u);
- EXPECT_EQ(buffer.bytes_used(), sizeof(PaintOpBuffer));
- EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false);
- buffer.Reset();
- EXPECT_EQ(buffer.size(), 0u);
- EXPECT_EQ(buffer.bytes_used(), sizeof(PaintOpBuffer));
- EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false);
- PaintOpBuffer buffer2(std::move(buffer));
- EXPECT_EQ(buffer.size(), 0u);
- EXPECT_EQ(buffer.bytes_used(), sizeof(PaintOpBuffer));
- EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false);
- EXPECT_EQ(buffer2.size(), 0u);
- EXPECT_EQ(buffer2.bytes_used(), sizeof(PaintOpBuffer));
- EXPECT_EQ(PaintOpBuffer::Iterator(&buffer2), false);
- }
- class PaintOpAppendTest : public ::testing::Test {
- public:
- PaintOpAppendTest() {
- rect_ = SkRect::MakeXYWH(2, 3, 4, 5);
- flags_.setColor(SK_ColorMAGENTA);
- flags_.setAlpha(100);
- }
- void PushOps(PaintOpBuffer* buffer) {
- buffer->push<SaveLayerOp>(&rect_, &flags_);
- buffer->push<SaveOp>();
- buffer->push<DrawColorOp>(draw_color_, blend_);
- buffer->push<RestoreOp>();
- EXPECT_EQ(buffer->size(), 4u);
- }
- void VerifyOps(PaintOpBuffer* buffer) {
- EXPECT_EQ(buffer->size(), 4u);
- PaintOpBuffer::Iterator iter(buffer);
- ASSERT_EQ(iter->GetType(), PaintOpType::SaveLayer);
- SaveLayerOp* save_op = static_cast<SaveLayerOp*>(*iter);
- EXPECT_EQ(save_op->bounds, rect_);
- EXPECT_EQ(save_op->flags, flags_);
- ++iter;
- ASSERT_EQ(iter->GetType(), PaintOpType::Save);
- ++iter;
- ASSERT_EQ(iter->GetType(), PaintOpType::DrawColor);
- DrawColorOp* op = static_cast<DrawColorOp*>(*iter);
- EXPECT_EQ(op->color, draw_color_);
- EXPECT_EQ(op->mode, blend_);
- ++iter;
- ASSERT_EQ(iter->GetType(), PaintOpType::Restore);
- ++iter;
- EXPECT_FALSE(iter);
- }
- private:
- SkRect rect_;
- PaintFlags flags_;
- SkColor4f draw_color_ = SkColors::kRed;
- SkBlendMode blend_ = SkBlendMode::kSrc;
- };
- TEST_F(PaintOpAppendTest, SimpleAppend) {
- PaintOpBuffer buffer;
- PushOps(&buffer);
- VerifyOps(&buffer);
- buffer.Reset();
- PushOps(&buffer);
- VerifyOps(&buffer);
- }
- TEST_F(PaintOpAppendTest, MoveThenDestruct) {
- PaintOpBuffer original;
- PushOps(&original);
- VerifyOps(&original);
- PaintOpBuffer destination(std::move(original));
- VerifyOps(&destination);
- // Original should be empty, and safe to destruct.
- EXPECT_EQ(original.size(), 0u);
- EXPECT_EQ(original.bytes_used(), sizeof(PaintOpBuffer));
- }
- TEST_F(PaintOpAppendTest, MoveThenDestructOperatorEq) {
- PaintOpBuffer original;
- PushOps(&original);
- VerifyOps(&original);
- PaintOpBuffer destination;
- destination = std::move(original);
- VerifyOps(&destination);
- // Original should be empty, and safe to destruct.
- EXPECT_EQ(original.size(), 0u);
- EXPECT_EQ(original.bytes_used(), sizeof(PaintOpBuffer));
- EXPECT_EQ(PaintOpBuffer::Iterator(&original), false);
- }
- TEST_F(PaintOpAppendTest, MoveThenReappend) {
- PaintOpBuffer original;
- PushOps(&original);
- PaintOpBuffer destination(std::move(original));
- // Should be possible to reappend to the original and get the same result.
- PushOps(&original);
- VerifyOps(&original);
- EXPECT_EQ(original, destination);
- }
- TEST_F(PaintOpAppendTest, MoveThenReappendOperatorEq) {
- PaintOpBuffer original;
- PushOps(&original);
- PaintOpBuffer destination;
- destination = std::move(original);
- // Should be possible to reappend to the original and get the same result.
- PushOps(&original);
- VerifyOps(&original);
- EXPECT_EQ(original, destination);
- }
- // Verify that a SaveLayerAlpha / Draw / Restore can be optimized to just
- // a draw with opacity.
- TEST(PaintOpBufferTest, SaveDrawRestore) {
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- int paint_flags_alpha = 50;
- PaintFlags draw_flags;
- draw_flags.setColor(SkColors::kMagenta);
- draw_flags.setAlpha(paint_flags_alpha);
- EXPECT_TRUE(draw_flags.SupportsFoldingAlpha());
- SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
- buffer.push<DrawRectOp>(rect, draw_flags);
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(0, canvas.save_count_);
- EXPECT_EQ(0, canvas.restore_count_);
- EXPECT_EQ(rect, canvas.draw_rect_);
- // Expect the alpha from the draw and the save layer to be folded together.
- // Since alpha is stored in a uint8_t and gets rounded, so use tolerance.
- float expected_alpha = alpha * paint_flags_alpha / 255.0f;
- EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlphaf()), 0.01f);
- }
- // Verify that we don't optimize SaveLayerAlpha / DrawTextBlob / Restore.
- TEST(PaintOpBufferTest, SaveDrawTextBlobRestore) {
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- PaintFlags paint_flags;
- EXPECT_TRUE(paint_flags.SupportsFoldingAlpha());
- buffer.push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), 0.0f,
- 0.0f, paint_flags);
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(1, canvas.save_count_);
- EXPECT_EQ(1, canvas.restore_count_);
- }
- // The same as SaveDrawRestore, but test that the optimization doesn't apply
- // when the drawing op's flags are not compatible with being folded into the
- // save layer with opacity.
- TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) {
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- PaintFlags draw_flags;
- draw_flags.setColor(SkColors::kMagenta);
- draw_flags.setAlpha(50);
- draw_flags.setBlendMode(SkBlendMode::kSrc);
- EXPECT_FALSE(draw_flags.SupportsFoldingAlpha());
- SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
- buffer.push<DrawRectOp>(rect, draw_flags);
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(1, canvas.save_count_);
- EXPECT_EQ(1, canvas.restore_count_);
- EXPECT_EQ(rect, canvas.draw_rect_);
- EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha());
- }
- // Same as above, but the save layer itself appears to be a noop.
- // See: http://crbug.com/748485. If the inner draw op itself
- // doesn't support folding, then the external save can't be skipped.
- TEST(PaintOpBufferTest, SaveDrawRestore_BadFlags255Alpha) {
- PaintOpBuffer buffer;
- float alpha = 1.0f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- PaintFlags draw_flags;
- draw_flags.setColor(SkColors::kMagenta);
- draw_flags.setAlpha(50);
- draw_flags.setBlendMode(SkBlendMode::kColorBurn);
- EXPECT_FALSE(draw_flags.SupportsFoldingAlpha());
- SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
- buffer.push<DrawRectOp>(rect, draw_flags);
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(1, canvas.save_count_);
- EXPECT_EQ(1, canvas.restore_count_);
- EXPECT_EQ(rect, canvas.draw_rect_);
- }
- // The same as SaveDrawRestore, but test that the optimization doesn't apply
- // when there are more than one ops between the save and restore.
- TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) {
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- PaintFlags draw_flags;
- draw_flags.setColor(SkColors::kMagenta);
- draw_flags.setAlpha(50);
- draw_flags.setBlendMode(SkBlendMode::kSrcOver);
- EXPECT_TRUE(draw_flags.SupportsFoldingAlpha());
- SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
- buffer.push<DrawRectOp>(rect, draw_flags);
- buffer.push<NoopOp>();
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(1, canvas.save_count_);
- EXPECT_EQ(1, canvas.restore_count_);
- EXPECT_EQ(rect, canvas.draw_rect_);
- EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha());
- }
- // Verify that the save draw restore code works with a single op
- // that's not a draw op, and the optimization does not kick in.
- TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpNotADrawOp) {
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- buffer.push<NoopOp>();
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(1, canvas.save_count_);
- EXPECT_EQ(1, canvas.restore_count_);
- }
- // Test that the save/draw/restore optimization applies if the single op
- // is a DrawRecord that itself has a single draw op.
- TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleOp) {
- sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>();
- int paint_flags_alpha = 50;
- PaintFlags draw_flags;
- draw_flags.setColor(SkColors::kMagenta);
- draw_flags.setAlpha(paint_flags_alpha);
- EXPECT_TRUE(draw_flags.SupportsFoldingAlpha());
- SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
- record->push<DrawRectOp>(rect, draw_flags);
- EXPECT_EQ(record->size(), 1u);
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- buffer.push<DrawRecordOp>(std::move(record));
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(0, canvas.save_count_);
- EXPECT_EQ(0, canvas.restore_count_);
- EXPECT_EQ(rect, canvas.draw_rect_);
- float expected_alpha = alpha * paint_flags_alpha / 255.f;
- EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlphaf()), 0.01f);
- }
- // The same as the above SingleOpRecord test, but the single op is not
- // a draw op. So, there's no way to fold in the save layer optimization.
- // Verify that the optimization doesn't apply and that this doesn't crash.
- // See: http://crbug.com/712093.
- TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleNonDrawOp) {
- sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>();
- record->push<NoopOp>();
- EXPECT_EQ(record->size(), 1u);
- EXPECT_FALSE(record->GetFirstOp()->IsDrawOp());
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- buffer.push<DrawRecordOp>(std::move(record));
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(1, canvas.save_count_);
- EXPECT_EQ(1, canvas.restore_count_);
- }
- TEST(PaintOpBufferTest, SaveLayerRestore_DrawColor) {
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- SkColor original = SkColorSetA(50, SK_ColorRED);
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- buffer.push<DrawColorOp>(SkColor4f::FromColor(original),
- SkBlendMode::kSrcOver);
- buffer.push<RestoreOp>();
- SaveCountingCanvas canvas;
- buffer.Playback(&canvas);
- EXPECT_EQ(canvas.save_count_, 0);
- EXPECT_EQ(canvas.restore_count_, 0);
- uint8_t expected_alpha = SkMulDiv255Round(alpha, SkColorGetA(original));
- EXPECT_EQ(canvas.paint_.getColor(), SkColorSetA(original, expected_alpha));
- }
- TEST(PaintOpBufferTest, DiscardableImagesTracking_EmptyBuffer) {
- PaintOpBuffer buffer;
- EXPECT_FALSE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, DiscardableImagesTracking_NoImageOp) {
- PaintOpBuffer buffer;
- PaintFlags flags;
- buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags);
- EXPECT_FALSE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImage) {
- PaintOpBuffer buffer;
- PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0));
- EXPECT_TRUE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImage) {
- scoped_refptr<TestPaintWorkletInput> input =
- base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f));
- PaintOpBuffer buffer;
- PaintImage image = CreatePaintWorkletPaintImage(input);
- buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0));
- EXPECT_TRUE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImageRect) {
- scoped_refptr<TestPaintWorkletInput> input =
- base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f));
- PaintOpBuffer buffer;
- PaintImage image = CreatePaintWorkletPaintImage(input);
- SkRect src = SkRect::MakeEmpty();
- SkRect dst = SkRect::MakeEmpty();
- buffer.push<DrawImageRectOp>(image, src, dst,
- SkCanvas::kStrict_SrcRectConstraint);
- EXPECT_TRUE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) {
- PaintOpBuffer buffer;
- PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100),
- SkRect::MakeWH(100, 100),
- SkCanvas::kFast_SrcRectConstraint);
- EXPECT_TRUE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, DiscardableImagesTracking_OpWithFlags) {
- PaintOpBuffer buffer;
- PaintFlags flags;
- auto image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- flags.setShader(PaintShader::MakeImage(std::move(image), SkTileMode::kClamp,
- SkTileMode::kClamp, nullptr));
- buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags);
- EXPECT_TRUE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, SlowPaths) {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 0);
- // Op without slow paths
- PaintFlags noop_flags;
- SkRect rect = SkRect::MakeXYWH(2, 3, 4, 5);
- buffer->push<SaveLayerOp>(&rect, &noop_flags);
- // Line op with a slow path
- PaintFlags line_effect_slow;
- line_effect_slow.setStrokeWidth(1.f);
- line_effect_slow.setStyle(PaintFlags::kStroke_Style);
- line_effect_slow.setStrokeCap(PaintFlags::kRound_Cap);
- SkScalar intervals[] = {1.f, 1.f};
- line_effect_slow.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
- buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect_slow);
- EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 1);
- // Line effect special case that Skia handles specially.
- PaintFlags line_effect = line_effect_slow;
- line_effect.setStrokeCap(PaintFlags::kButt_Cap);
- buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect);
- EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 1);
- // Antialiased convex path is not slow.
- SkPath path;
- path.addCircle(2, 2, 5);
- EXPECT_TRUE(path.isConvex());
- buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, /*antialias=*/true,
- UsePaintCache::kDisabled);
- EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 1);
- // Concave paths are slow only when antialiased.
- SkPath concave = path;
- concave.addCircle(3, 4, 2);
- EXPECT_FALSE(concave.isConvex());
- buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, /*antialias=*/true,
- UsePaintCache::kDisabled);
- EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 2);
- buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, /*antialias=*/false,
- UsePaintCache::kDisabled);
- EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 2);
- // Drawing a record with slow paths into another adds the same
- // number of slow paths as the record.
- auto buffer2 = sk_make_sp<PaintOpBuffer>();
- EXPECT_EQ(0, buffer2->num_slow_paths_up_to_min_for_MSAA());
- buffer2->push<DrawRecordOp>(buffer);
- EXPECT_EQ(2, buffer2->num_slow_paths_up_to_min_for_MSAA());
- buffer2->push<DrawRecordOp>(buffer);
- EXPECT_EQ(4, buffer2->num_slow_paths_up_to_min_for_MSAA());
- }
- TEST(PaintOpBufferTest, NonAAPaint) {
- // PaintOpWithFlags
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer->HasNonAAPaint());
- // Add a PaintOpWithFlags (in this case a line) with AA.
- PaintFlags line_effect;
- line_effect.setAntiAlias(true);
- buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect);
- EXPECT_FALSE(buffer->HasNonAAPaint());
- // Add a PaintOpWithFlags (in this case a line) without AA.
- PaintFlags line_effect_no_aa;
- line_effect_no_aa.setAntiAlias(false);
- buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect_no_aa);
- EXPECT_TRUE(buffer->HasNonAAPaint());
- }
- // ClipPathOp
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer->HasNonAAPaint());
- SkPath path;
- path.addCircle(2, 2, 5);
- // ClipPathOp with AA
- buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, /*antialias=*/true,
- UsePaintCache::kDisabled);
- EXPECT_FALSE(buffer->HasNonAAPaint());
- // ClipPathOp without AA
- buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, /*antialias=*/false,
- UsePaintCache::kDisabled);
- EXPECT_TRUE(buffer->HasNonAAPaint());
- }
- // ClipRRectOp
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer->HasNonAAPaint());
- // ClipRRectOp with AA
- buffer->push<ClipRRectOp>(SkRRect::MakeEmpty(), SkClipOp::kIntersect,
- true /* antialias */);
- EXPECT_FALSE(buffer->HasNonAAPaint());
- // ClipRRectOp without AA
- buffer->push<ClipRRectOp>(SkRRect::MakeEmpty(), SkClipOp::kIntersect,
- false /* antialias */);
- EXPECT_TRUE(buffer->HasNonAAPaint());
- }
- // Drawing a record with non-aa paths into another propogates the value.
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer->HasNonAAPaint());
- auto sub_buffer = sk_make_sp<PaintOpBuffer>();
- SkPath path;
- path.addCircle(2, 2, 5);
- sub_buffer->push<ClipPathOp>(path, SkClipOp::kIntersect,
- /*antialias=*/false, UsePaintCache::kDisabled);
- EXPECT_TRUE(sub_buffer->HasNonAAPaint());
- buffer->push<DrawRecordOp>(sub_buffer);
- EXPECT_TRUE(buffer->HasNonAAPaint());
- }
- // The following PaintOpWithFlags types are overridden to *not* ever have
- // non-AA paint. AA is hard to notice, and these kick us out of MSAA in too
- // many cases.
- // DrawImageOp
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer->HasNonAAPaint());
- PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- PaintFlags non_aa_flags;
- non_aa_flags.setAntiAlias(true);
- buffer->push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0),
- SkSamplingOptions(), &non_aa_flags);
- EXPECT_FALSE(buffer->HasNonAAPaint());
- }
- // DrawIRectOp
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer->HasNonAAPaint());
- PaintFlags non_aa_flags;
- non_aa_flags.setAntiAlias(true);
- buffer->push<DrawIRectOp>(SkIRect::MakeWH(1, 1), non_aa_flags);
- EXPECT_FALSE(buffer->HasNonAAPaint());
- }
- // SaveLayerOp
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer->HasNonAAPaint());
- PaintFlags non_aa_flags;
- non_aa_flags.setAntiAlias(true);
- auto bounds = SkRect::MakeWH(1, 1);
- buffer->push<SaveLayerOp>(&bounds, &non_aa_flags);
- EXPECT_FALSE(buffer->HasNonAAPaint());
- }
- }
- class PaintOpBufferOffsetsTest : public ::testing::Test {
- public:
- void SetUp() override {}
- void TearDown() override {
- offsets_.clear();
- buffer_.Reset();
- }
- template <typename T, typename... Args>
- void push_op(Args&&... args) {
- offsets_.push_back(buffer_.next_op_offset());
- buffer_.push<T>(std::forward<Args>(args)...);
- }
- // Returns a subset of offsets_ by selecting only the specified indices.
- std::vector<size_t> Select(const std::vector<size_t>& indices) {
- std::vector<size_t> result;
- for (size_t i : indices)
- result.push_back(offsets_[i]);
- return result;
- }
- void Playback(SkCanvas* canvas, const std::vector<size_t>& offsets) {
- buffer_.Playback(canvas, PlaybackParams(nullptr), &offsets);
- }
- protected:
- std::vector<size_t> offsets_;
- PaintOpBuffer buffer_;
- };
- TEST_F(PaintOpBufferOffsetsTest, EmptyClipRectShouldRejectAnOp) {
- SkCanvas device(0, 0);
- SkCanvas* canvas = &device;
- canvas->translate(-254, 0);
- SkIRect bounds = canvas->getDeviceClipBounds();
- EXPECT_TRUE(bounds.isEmpty());
- SkMatrix ctm = canvas->getTotalMatrix();
- EXPECT_EQ(ctm[2], -254);
- scoped_refptr<TestPaintWorkletInput> input =
- base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f));
- PaintImage image = CreatePaintWorkletPaintImage(input);
- SkRect src = SkRect::MakeLTRB(0, 0, 100, 100);
- SkRect dst = SkRect::MakeLTRB(168, -23, 268, 77);
- push_op<DrawImageRectOp>(image, src, dst,
- SkCanvas::kStrict_SrcRectConstraint);
- std::vector<size_t> offsets = Select({0});
- for (PaintOpBuffer::PlaybackFoldingIterator iter(&buffer_, &offsets); iter;
- ++iter) {
- const PaintOp* op = *iter;
- EXPECT_EQ(op->GetType(), PaintOpType::DrawImageRect);
- EXPECT_TRUE(PaintOp::QuickRejectDraw(op, canvas));
- }
- }
- TEST_F(PaintOpBufferOffsetsTest, ContiguousIndices) {
- testing::StrictMock<MockCanvas> canvas;
- push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(4u), SkBlendMode::kClear);
- // Plays all items.
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 3, 4}));
- }
- TEST_F(PaintOpBufferOffsetsTest, NonContiguousIndices) {
- testing::StrictMock<MockCanvas> canvas;
- push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(4u), SkBlendMode::kClear);
- // Plays 0, 1, 3, 4 indices.
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 3, 4}));
- }
- TEST_F(PaintOpBufferOffsetsTest, FirstTwoIndices) {
- testing::StrictMock<MockCanvas> canvas;
- push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(4u), SkBlendMode::kClear);
- // Plays first two indices.
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
- Playback(&canvas, Select({0, 1}));
- }
- TEST_F(PaintOpBufferOffsetsTest, MiddleIndex) {
- testing::StrictMock<MockCanvas> canvas;
- push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(4u), SkBlendMode::kClear);
- // Plays index 2.
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s);
- Playback(&canvas, Select({2}));
- }
- TEST_F(PaintOpBufferOffsetsTest, LastTwoElements) {
- testing::StrictMock<MockCanvas> canvas;
- push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(4u), SkBlendMode::kClear);
- // Plays last two elements.
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({3, 4}));
- }
- TEST_F(PaintOpBufferOffsetsTest, ContiguousIndicesWithSaveLayerAlphaRestore) {
- testing::StrictMock<MockCanvas> canvas;
- push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear);
- float alpha = 0.4f;
- push_op<SaveLayerAlphaOp>(nullptr, alpha);
- push_op<RestoreOp>();
- push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(4u), SkBlendMode::kClear);
- // Items are {0, 1, save, restore, 2, 3, 4}.
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
- // The empty SaveLayerAlpha/Restore is dropped.
- EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
- Mock::VerifyAndClearExpectations(&canvas);
- }
- TEST_F(PaintOpBufferOffsetsTest,
- NonContiguousIndicesWithSaveLayerAlphaRestore) {
- testing::StrictMock<MockCanvas> canvas;
- push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear);
- float alpha = 0.4f;
- push_op<SaveLayerAlphaOp>(nullptr, alpha);
- push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear);
- push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear);
- push_op<RestoreOp>();
- push_op<DrawColorOp>(SkColor4f::FromColor(4u), SkBlendMode::kClear);
- // Items are {0, 1, save, 2, 3, restore, 4}.
- // Plays back all indices.
- {
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
- // The SaveLayerAlpha/Restore is not dropped if we draw the middle
- // range, as we need them to represent the two draws inside the layer
- // correctly.
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
- }
- Mock::VerifyAndClearExpectations(&canvas);
- // Skips the middle indices.
- {
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
- // The now-empty SaveLayerAlpha/Restore is dropped
- EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 5, 6}));
- }
- Mock::VerifyAndClearExpectations(&canvas);
- }
- TEST_F(PaintOpBufferOffsetsTest,
- ContiguousIndicesWithSaveLayerAlphaDrawRestore) {
- testing::StrictMock<MockCanvas> canvas;
- auto add_draw_rect = [this](SkColor c) {
- PaintFlags flags;
- flags.setColor(c);
- push_op<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
- };
- add_draw_rect(0u);
- add_draw_rect(1u);
- float alpha = 0.4f;
- push_op<SaveLayerAlphaOp>(nullptr, alpha);
- add_draw_rect(2u);
- push_op<RestoreOp>();
- add_draw_rect(3u);
- add_draw_rect(4u);
- // Items are {0, 1, save, 2, restore, 3, 4}.
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
- // The empty SaveLayerAlpha/Restore is dropped, the containing
- // operation can be drawn with alpha.
- EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
- Mock::VerifyAndClearExpectations(&canvas);
- }
- TEST_F(PaintOpBufferOffsetsTest,
- NonContiguousIndicesWithSaveLayerAlphaDrawRestore) {
- testing::StrictMock<MockCanvas> canvas;
- auto add_draw_rect = [this](SkColor c) {
- PaintFlags flags;
- flags.setColor(c);
- push_op<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
- };
- add_draw_rect(0u);
- add_draw_rect(1u);
- float alpha = 0.4f;
- push_op<SaveLayerAlphaOp>(nullptr, alpha);
- add_draw_rect(2u);
- add_draw_rect(3u);
- add_draw_rect(4u);
- push_op<RestoreOp>();
- // Items are are {0, 1, save, 2, 3, 4, restore}.
- // If the middle range is played, then the SaveLayerAlpha/Restore
- // can't be dropped.
- {
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
- }
- Mock::VerifyAndClearExpectations(&canvas);
- // If the middle range is not played, then the SaveLayerAlpha/Restore
- // can be dropped.
- {
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 5, 6}));
- }
- Mock::VerifyAndClearExpectations(&canvas);
- // If the middle range is not played, then the SaveLayerAlpha/Restore
- // can be dropped.
- {
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s);
- Playback(&canvas, Select({0, 1, 2, 3, 6}));
- }
- }
- TEST(PaintOpBufferTest, SaveLayerAlphaDrawRestoreWithBadBlendMode) {
- PaintOpBuffer buffer;
- testing::StrictMock<MockCanvas> canvas;
- auto add_draw_rect = [](PaintOpBuffer* buffer, SkColor c) {
- PaintFlags flags;
- flags.setColor(c);
- // This blend mode prevents the optimization.
- flags.setBlendMode(SkBlendMode::kSrc);
- buffer->push<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
- };
- add_draw_rect(&buffer, 0u);
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- add_draw_rect(&buffer, 1u);
- buffer.push<RestoreOp>();
- add_draw_rect(&buffer, 2u);
- {
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s);
- buffer.Playback(&canvas);
- }
- }
- TEST(PaintOpBufferTest, UnmatchedSaveRestoreNoSideEffects) {
- PaintOpBuffer buffer;
- testing::StrictMock<MockCanvas> canvas;
- auto add_draw_rect = [](PaintOpBuffer* buffer, SkColor c) {
- PaintFlags flags;
- flags.setColor(c);
- buffer->push<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
- };
- // Push 2 saves.
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- add_draw_rect(&buffer, 0u);
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- add_draw_rect(&buffer, 1u);
- add_draw_rect(&buffer, 2u);
- // But only 1 restore.
- buffer.push<RestoreOp>();
- testing::Sequence s;
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- // We will restore back to the original save count regardless with 2 restores.
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- buffer.Playback(&canvas);
- }
- std::vector<float> test_floats = {0.f,
- 1.f,
- -1.f,
- 2384.981971f,
- 0.0001f,
- std::numeric_limits<float>::min(),
- std::numeric_limits<float>::max(),
- std::numeric_limits<float>::infinity()};
- std::vector<uint8_t> test_uint8s = {
- 0, 255, 128, 10, 45,
- };
- static SkRect make_largest_skrect() {
- const float limit = std::numeric_limits<float>::max();
- return {-limit, -limit, limit, limit};
- }
- static SkIRect make_largest_skirect() {
- // we use half the limit, so that the resulting width/height will not
- // overflow.
- const int32_t limit = std::numeric_limits<int32_t>::max() >> 1;
- return {-limit, -limit, limit, limit};
- }
- std::vector<SkRect> test_rects = {
- SkRect::MakeXYWH(1, 2.5, 3, 4), SkRect::MakeXYWH(0, 0, 0, 0),
- make_largest_skrect(), SkRect::MakeXYWH(0.5f, 0.5f, 8.2f, 8.2f),
- SkRect::MakeXYWH(-1, -1, 0, 0), SkRect::MakeXYWH(-100, -101, -102, -103),
- SkRect::MakeXYWH(0, 0, 0, 0), SkRect::MakeXYWH(0, 0, 0, 0),
- SkRect::MakeXYWH(0, 0, 0, 0), SkRect::MakeXYWH(0, 0, 0, 0),
- SkRect::MakeXYWH(0, 0, 0, 0), SkRect::MakeXYWH(0, 0, 0, 0),
- };
- std::vector<SkRRect> test_rrects = {
- SkRRect::MakeEmpty(), SkRRect::MakeOval(SkRect::MakeXYWH(1, 2, 3, 4)),
- SkRRect::MakeRect(SkRect::MakeXYWH(-10, 100, 5, 4)),
- [] {
- SkRRect rrect = SkRRect::MakeEmpty();
- rrect.setNinePatch(SkRect::MakeXYWH(10, 20, 30, 40), 1, 2, 3, 4);
- return rrect;
- }(),
- };
- std::vector<SkIRect> test_irects = {
- SkIRect::MakeXYWH(1, 2, 3, 4), SkIRect::MakeXYWH(0, 0, 0, 0),
- make_largest_skirect(), SkIRect::MakeXYWH(0, 0, 10, 10),
- SkIRect::MakeXYWH(-1, -1, 0, 0), SkIRect::MakeXYWH(-100, -101, -102, -103)};
- std::vector<uint32_t> test_ids = {0, 1, 56, 0xFFFFFFFF, 0xFFFFFFFE, 0x10001};
- std::vector<SkM44> test_matrices = {
- SkM44(),
- SkM44::Scale(3.91f, 4.31f, 1.0f),
- SkM44::Translate(-5.2f, 8.7f, 0.0f),
- SkM44(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- SkM44(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16),
- };
- std::vector<SkPath> test_paths = {
- [] {
- SkPath path;
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
- return path;
- }(),
- [] {
- SkPath path;
- path.addCircle(2, 2, 5);
- path.addCircle(3, 4, 2);
- path.addArc(SkRect::MakeXYWH(1, 2, 3, 4), 5, 6);
- return path;
- }(),
- SkPath(),
- };
- std::vector<PaintFlags> test_flags = {
- PaintFlags(),
- [] {
- PaintFlags flags;
- flags.setColor(SK_ColorMAGENTA);
- flags.setStrokeWidth(4.2f);
- flags.setStrokeMiter(5.91f);
- flags.setBlendMode(SkBlendMode::kDst);
- flags.setStrokeCap(PaintFlags::kSquare_Cap);
- flags.setStrokeJoin(PaintFlags::kBevel_Join);
- flags.setStyle(PaintFlags::kStroke_Style);
- flags.setFilterQuality(PaintFlags::FilterQuality::kMedium);
- flags.setShader(PaintShader::MakeColor({0.1f, 0.2f, 0.3f, 0.4f}));
- return flags;
- }(),
- [] {
- PaintFlags flags;
- flags.setColor(SK_ColorCYAN);
- flags.setAlpha(103);
- flags.setStrokeWidth(0.32f);
- flags.setStrokeMiter(7.98f);
- flags.setBlendMode(SkBlendMode::kSrcOut);
- flags.setStrokeCap(PaintFlags::kRound_Cap);
- flags.setStrokeJoin(PaintFlags::kRound_Join);
- flags.setStyle(PaintFlags::kFill_Style);
- flags.setFilterQuality(PaintFlags::FilterQuality::kHigh);
- SkScalar intervals[] = {1.f, 1.f};
- flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
- flags.setMaskFilter(SkMaskFilter::MakeBlur(
- SkBlurStyle::kOuter_SkBlurStyle, 4.3f));
- flags.setColorFilter(SkColorMatrixFilter::MakeLightingFilter(
- SK_ColorYELLOW, SK_ColorGREEN));
- SkLayerDrawLooper::Builder looper_builder;
- looper_builder.addLayer();
- looper_builder.addLayer(2.3f, 4.5f);
- SkLayerDrawLooper::LayerInfo layer_info;
- layer_info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit;
- layer_info.fColorMode = SkBlendMode::kDst;
- layer_info.fOffset.set(-1.f, 5.2f);
- looper_builder.addLayer(layer_info);
- flags.setLooper(looper_builder.detach());
- sk_sp<PaintShader> shader =
- PaintShader::MakeColor(SkColors::kTransparent);
- PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(),
- true);
- flags.setShader(std::move(shader));
- return flags;
- }(),
- [] {
- PaintFlags flags;
- flags.setShader(PaintShader::MakeColor({0.1f, 0.2f, 0.3f, 0.4f}));
- return flags;
- }(),
- [] {
- PaintFlags flags;
- sk_sp<PaintShader> shader =
- PaintShader::MakeColor(SkColors::kTransparent);
- PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(),
- false);
- flags.setShader(std::move(shader));
- return flags;
- }(),
- [] {
- PaintFlags flags;
- SkPoint points[2] = {SkPoint::Make(1, 2), SkPoint::Make(3, 4)};
- SkColor4f colors[3] = {{0.1f, 0.2f, 0.3f, 0.4f},
- {0.4f, 0.3f, 0.2f, 0.1f},
- {0.2f, 0.4f, 0.6f, 0.0f}};
- SkScalar positions[3] = {0.f, 0.3f, 1.f};
- flags.setShader(PaintShader::MakeLinearGradient(points, colors, positions,
- 3, SkTileMode::kMirror));
- return flags;
- }(),
- [] {
- PaintFlags flags;
- SkColor4f colors[3] = {{0.1f, 0.2f, 0.3f, 0.4f},
- {0.4f, 0.3f, 0.2f, 0.1f},
- {0.2f, 0.4f, 0.6f, 0.0f}};
- flags.setShader(PaintShader::MakeSweepGradient(
- 0.2f, -0.8f, colors, nullptr, 3, SkTileMode::kMirror, 10, 20));
- return flags;
- }(),
- PaintFlags(),
- PaintFlags(),
- };
- std::vector<SkColor4f> test_colors = {
- {0, 0, 0, 0}, {1, 1, 1, 1}, {1, 0.04, 1, 0},
- {0, 0.08, 1, 1}, {1, 0, 1, 0.12}, {0.16, 0, 0, 1},
- };
- std::vector<std::string> test_strings = {
- "", "foobar",
- "blarbideeblarasdfaiousydfp234poiausdofiuapsodfjknla;sdfkjasd;f",
- };
- std::vector<std::vector<SkPoint>> test_point_arrays = {
- std::vector<SkPoint>(),
- {SkPoint::Make(1, 2)},
- {SkPoint::Make(1, 2), SkPoint::Make(-5.4f, -3.8f)},
- {SkPoint::Make(0, 0), SkPoint::Make(5, 6), SkPoint::Make(-1, -1),
- SkPoint::Make(9, 9), SkPoint::Make(50, 50), SkPoint::Make(100, 100)},
- };
- // TODO(enne): In practice, probably all paint images need to be uploaded
- // ahead of time and not be bitmaps. These paint images should be fake
- // gpu resource paint images.
- std::vector<PaintImage> test_images = {
- CreateDiscardablePaintImage(gfx::Size(5, 10)),
- CreateDiscardablePaintImage(gfx::Size(1, 1)),
- CreateDiscardablePaintImage(gfx::Size(50, 50)),
- };
- #if BUILDFLAG(SKIA_SUPPORT_SKOTTIE)
- bool kIsSkottieSupported = true;
- #else
- bool kIsSkottieSupported = false;
- #endif
- // Writes as many ops in |buffer| as can fit in |output_size| to |output|.
- // Records the numbers of bytes written for each op.
- class SimpleSerializer {
- public:
- SimpleSerializer(void* output, size_t output_size)
- : current_(static_cast<char*>(output)),
- output_size_(output_size),
- remaining_(output_size) {}
- void Serialize(const PaintOpBuffer& buffer) {
- bytes_written_.resize(buffer.size());
- for (size_t i = 0; i < buffer.size(); ++i)
- bytes_written_[i] = 0;
- size_t op_idx = 0;
- for (const auto* op : PaintOpBuffer::Iterator(&buffer)) {
- size_t bytes_written = op->Serialize(
- current_, remaining_, options_provider_.serialize_options(), nullptr,
- SkM44(), SkM44());
- if (!bytes_written)
- return;
- PaintOp* written = reinterpret_cast<PaintOp*>(current_.get());
- EXPECT_EQ(op->GetType(), written->GetType());
- EXPECT_EQ(bytes_written, written->skip);
- bytes_written_[op_idx] = bytes_written;
- op_idx++;
- current_ += bytes_written;
- remaining_ -= bytes_written;
- // Number of bytes bytes_written must be a multiple of PaintOpAlign
- // unless the buffer is filled entirely.
- if (remaining_ != 0u)
- DCHECK_EQ(0u, bytes_written % PaintOpBuffer::PaintOpAlign);
- }
- }
- const std::vector<size_t>& bytes_written() const { return bytes_written_; }
- size_t TotalBytesWritten() const { return output_size_ - remaining_; }
- TestOptionsProvider* options_provider() { return &options_provider_; }
- private:
- raw_ptr<char> current_ = nullptr;
- size_t output_size_ = 0u;
- size_t remaining_ = 0u;
- std::vector<size_t> bytes_written_;
- TestOptionsProvider options_provider_;
- };
- class DeserializerIterator {
- public:
- DeserializerIterator(const void* input,
- size_t input_size,
- const PaintOp::DeserializeOptions& options)
- : DeserializerIterator(input,
- static_cast<const char*>(input),
- input_size,
- input_size,
- options) {}
- DeserializerIterator(DeserializerIterator&&) = default;
- DeserializerIterator& operator=(DeserializerIterator&&) = default;
- ~DeserializerIterator() { DestroyDeserializedOp(); }
- DeserializerIterator begin() {
- return DeserializerIterator(input_, static_cast<const char*>(input_),
- input_size_, input_size_, options_);
- }
- DeserializerIterator end() {
- return DeserializerIterator(input_,
- static_cast<const char*>(input_) + input_size_,
- input_size_, 0, options_);
- }
- bool operator!=(const DeserializerIterator& other) {
- return input_ != other.input_ || current_ != other.current_ ||
- input_size_ != other.input_size_ || remaining_ != other.remaining_;
- }
- DeserializerIterator& operator++() {
- CHECK_GE(remaining_, last_bytes_read_);
- current_ += last_bytes_read_;
- remaining_ -= last_bytes_read_;
- if (remaining_ > 0)
- CHECK_GE(remaining_, 4u);
- DeserializeCurrentOp();
- return *this;
- }
- operator bool() const { return remaining_ == 0u; }
- const PaintOp* operator->() const { return deserialized_op_; }
- const PaintOp* operator*() const { return deserialized_op_; }
- private:
- DeserializerIterator(const void* input,
- const char* current,
- size_t input_size,
- size_t remaining,
- const PaintOp::DeserializeOptions& options)
- : input_(input),
- current_(current),
- input_size_(input_size),
- remaining_(remaining),
- options_(options) {
- data_.reset(static_cast<char*>(base::AlignedAlloc(
- sizeof(LargestPaintOp), PaintOpBuffer::PaintOpAlign)));
- DeserializeCurrentOp();
- }
- void DestroyDeserializedOp() {
- if (!deserialized_op_)
- return;
- deserialized_op_->DestroyThis();
- deserialized_op_ = nullptr;
- }
- void DeserializeCurrentOp() {
- DestroyDeserializedOp();
- if (!remaining_)
- return;
- deserialized_op_ = PaintOp::Deserialize(current_, remaining_, data_.get(),
- sizeof(LargestPaintOp),
- &last_bytes_read_, options_);
- }
- raw_ptr<const void> input_ = nullptr;
- const char* current_ = nullptr;
- size_t input_size_ = 0u;
- size_t remaining_ = 0u;
- size_t last_bytes_read_ = 0u;
- PaintOp::DeserializeOptions options_;
- std::unique_ptr<char, base::AlignedFreeDeleter> data_;
- raw_ptr<PaintOp> deserialized_op_ = nullptr;
- };
- void PushAnnotateOps(PaintOpBuffer* buffer) {
- buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::URL, test_rects[0],
- SkData::MakeWithCString("thingerdoowhatchamagig"));
- // Deliberately test both null and empty SkData.
- buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::LINK_TO_DESTINATION,
- test_rects[1], nullptr);
- buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::NAMED_DESTINATION,
- test_rects[2], SkData::MakeEmpty());
- ValidateOps<AnnotateOp>(buffer);
- }
- void PushClipPathOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_paths.size(); ++i) {
- SkClipOp op = i % 3 ? SkClipOp::kDifference : SkClipOp::kIntersect;
- buffer->push<ClipPathOp>(test_paths[i], op, /*antialias=*/!!(i % 2),
- UsePaintCache::kDisabled);
- }
- ValidateOps<ClipPathOp>(buffer);
- }
- void PushClipRectOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_rects.size(); ++i) {
- SkClipOp op = i % 2 ? SkClipOp::kIntersect : SkClipOp::kDifference;
- bool antialias = !!(i % 3);
- buffer->push<ClipRectOp>(test_rects[i], op, antialias);
- }
- ValidateOps<ClipRectOp>(buffer);
- }
- void PushClipRRectOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_rrects.size(); ++i) {
- SkClipOp op = i % 2 ? SkClipOp::kIntersect : SkClipOp::kDifference;
- bool antialias = !!(i % 3);
- buffer->push<ClipRRectOp>(test_rrects[i], op, antialias);
- }
- ValidateOps<ClipRRectOp>(buffer);
- }
- void PushConcatOps(PaintOpBuffer* buffer) {
- for (auto& test_matrix : test_matrices)
- buffer->push<ConcatOp>(test_matrix);
- ValidateOps<ConcatOp>(buffer);
- }
- void PushCustomDataOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_ids.size(); ++i)
- buffer->push<CustomDataOp>(test_ids[i]);
- ValidateOps<CustomDataOp>(buffer);
- }
- void PushDrawColorOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_colors.size(); ++i) {
- buffer->push<DrawColorOp>(test_colors[i], static_cast<SkBlendMode>(i));
- }
- ValidateOps<DrawColorOp>(buffer);
- }
- void PushDrawDRRectOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_rrects.size() - 1, test_flags.size());
- for (size_t i = 0; i < len; ++i) {
- buffer->push<DrawDRRectOp>(test_rrects[i], test_rrects[i + 1],
- test_flags[i]);
- }
- ValidateOps<DrawDRRectOp>(buffer);
- }
- void PushDrawImageOps(PaintOpBuffer* buffer) {
- size_t len =
- std::min({test_images.size(), test_flags.size(), test_floats.size() - 1});
- for (size_t i = 0; i < len; ++i) {
- buffer->push<DrawImageOp>(test_images[i], test_floats[i],
- test_floats[i + 1],
- PaintFlags::FilterQualityToSkSamplingOptions(
- test_flags[i].getFilterQuality()),
- &test_flags[i]);
- }
- // Test optional flags
- // TODO(enne): maybe all these optional ops should not be optional.
- buffer->push<DrawImageOp>(test_images[0], test_floats[0], test_floats[1]);
- ValidateOps<DrawImageOp>(buffer);
- }
- void PushDrawImageRectOps(PaintOpBuffer* buffer) {
- size_t len =
- std::min({test_images.size(), test_flags.size(), test_rects.size() - 1});
- for (size_t i = 0; i < len; ++i) {
- SkCanvas::SrcRectConstraint constraint =
- i % 2 ? SkCanvas::kStrict_SrcRectConstraint
- : SkCanvas::kFast_SrcRectConstraint;
- buffer->push<DrawImageRectOp>(test_images[i], test_rects[i],
- test_rects[i + 1],
- PaintFlags::FilterQualityToSkSamplingOptions(
- test_flags[i].getFilterQuality()),
- &test_flags[i], constraint);
- }
- // Test optional flags.
- buffer->push<DrawImageRectOp>(test_images[0], test_rects[0], test_rects[1],
- SkCanvas::kStrict_SrcRectConstraint);
- ValidateOps<DrawImageRectOp>(buffer);
- }
- void PushDrawIRectOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_irects.size(), test_flags.size());
- for (size_t i = 0; i < len; ++i)
- buffer->push<DrawIRectOp>(test_irects[i], test_flags[i]);
- ValidateOps<DrawIRectOp>(buffer);
- }
- void PushDrawLineOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_floats.size() - 3, test_flags.size());
- for (size_t i = 0; i < len; ++i) {
- buffer->push<DrawLineOp>(test_floats[i], test_floats[i + 1],
- test_floats[i + 2], test_floats[i + 3],
- test_flags[i]);
- }
- ValidateOps<DrawLineOp>(buffer);
- }
- void PushDrawOvalOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_paths.size(), test_flags.size());
- for (size_t i = 0; i < len; ++i)
- buffer->push<DrawOvalOp>(test_rects[i], test_flags[i]);
- ValidateOps<DrawOvalOp>(buffer);
- }
- void PushDrawPathOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_paths.size(), test_flags.size());
- for (size_t i = 0; i < len; ++i)
- buffer->push<DrawPathOp>(test_paths[i], test_flags[i],
- UsePaintCache::kDisabled);
- ValidateOps<DrawPathOp>(buffer);
- }
- void PushDrawRectOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_rects.size(), test_flags.size());
- for (size_t i = 0; i < len; ++i)
- buffer->push<DrawRectOp>(test_rects[i], test_flags[i]);
- ValidateOps<DrawRectOp>(buffer);
- }
- void PushDrawRRectOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_rrects.size(), test_flags.size());
- for (size_t i = 0; i < len; ++i)
- buffer->push<DrawRRectOp>(test_rrects[i], test_flags[i]);
- ValidateOps<DrawRRectOp>(buffer);
- }
- SkottieFrameDataMap GetTestImagesForSkottie(SkottieWrapper& skottie,
- const SkRect& skottie_rect,
- PaintFlags::FilterQuality quality,
- float t) {
- SkottieFrameDataMap images;
- skottie.Seek(
- t,
- base::BindLambdaForTesting([&](SkottieResourceIdHash asset_id,
- float t_frame, sk_sp<SkImage>& image_out,
- SkSamplingOptions& sampling_out) {
- SkottieFrameData frame_data;
- frame_data.image = CreateBitmapImage(
- gfx::Size(skottie_rect.width() / 2, skottie_rect.height() / 2));
- frame_data.quality = quality;
- images[asset_id] = std::move(frame_data);
- return SkottieWrapper::FrameDataFetchResult::NO_UPDATE;
- }));
- return images;
- }
- SkottieFrameDataMap GetNullImagesForSkottie(SkottieWrapper& skottie, float t) {
- SkottieFrameDataMap images;
- skottie.Seek(
- t, base::BindLambdaForTesting(
- [&](SkottieResourceIdHash asset_id, float t_frame,
- sk_sp<SkImage>& image_out, SkSamplingOptions& sampling_out) {
- images[asset_id] = SkottieFrameData();
- return SkottieWrapper::FrameDataFetchResult::NO_UPDATE;
- }));
- return images;
- }
- void PushDrawSkottieOps(PaintOpBuffer* buffer) {
- std::vector<scoped_refptr<SkottieWrapper>> test_skotties;
- std::vector<float> test_skottie_floats;
- std::vector<SkRect> test_skottie_rects;
- std::vector<SkottieFrameDataMap> test_skottie_images;
- std::vector<SkottieColorMap> test_skottie_color_maps;
- std::vector<SkottieTextPropertyValueMap> test_skottie_text_maps;
- if (kIsSkottieSupported) {
- test_skotties = {
- CreateSkottie(gfx::Size(10, 20), 4),
- CreateSkottie(gfx::Size(100, 40), 5),
- CreateSkottie(gfx::Size(80, 70), 6),
- CreateSkottieFromString(kLottieDataWith2Assets),
- CreateSkottieFromString(kLottieDataWith2Assets),
- CreateSkottieFromTestDataDir(kLottieDataWith2TextFileName)};
- test_skottie_floats = {0, 0.1f, 1.f, 0.2f, 0.2f, 0.3f};
- test_skottie_rects = {
- SkRect::MakeXYWH(10, 20, 30, 40), SkRect::MakeXYWH(0, 5, 10, 20),
- SkRect::MakeXYWH(6, 0, 3, 50), SkRect::MakeXYWH(10, 10, 100, 100),
- SkRect::MakeXYWH(10, 10, 100, 100), SkRect::MakeXYWH(5, 5, 50, 50)};
- test_skottie_images = {
- SkottieFrameDataMap(),
- SkottieFrameDataMap(),
- SkottieFrameDataMap(),
- GetTestImagesForSkottie(*test_skotties[3], test_skottie_rects[3],
- PaintFlags::FilterQuality::kHigh,
- test_skottie_floats[3]),
- GetNullImagesForSkottie(*test_skotties[4], test_skottie_floats[4]),
- SkottieFrameDataMap()};
- test_skottie_color_maps = {
- {SkottieMapColor("green", SK_ColorGREEN),
- SkottieMapColor("yellow", SK_ColorYELLOW),
- SkottieMapColor("red", SK_ColorRED),
- SkottieMapColor("blue", SK_ColorBLUE)},
- {},
- {SkottieMapColor("green", SK_ColorGREEN)},
- {SkottieMapColor("transparent", SK_ColorTRANSPARENT)},
- {},
- {}};
- test_skottie_text_maps = {
- {},
- {},
- {},
- {},
- {},
- {{HashSkottieResourceId(kLottieDataWith2TextNode1),
- SkottieTextPropertyValue(
- std::string(kLottieDataWith2TextNode1Text.data()),
- kLottieDataWith2TextNode1Box)},
- {HashSkottieResourceId(kLottieDataWith2TextNode2),
- SkottieTextPropertyValue(
- std::string(kLottieDataWith2TextNode2Text.data()),
- kLottieDataWith2TextNode2Box)}}};
- }
- size_t len = std::min(test_skotties.size(), test_flags.size());
- for (size_t i = 0; i < len; i++) {
- buffer->push<DrawSkottieOp>(test_skotties[i], test_skottie_rects[i],
- test_skottie_floats[i], test_skottie_images[i],
- test_skottie_color_maps[i],
- test_skottie_text_maps[i]);
- }
- ValidateOps<DrawSkottieOp>(buffer);
- }
- void PushDrawTextBlobOps(PaintOpBuffer* buffer) {
- static std::vector<std::vector<sk_sp<SkTypeface>>> test_typefaces = {
- [] {
- return std::vector<sk_sp<SkTypeface>>{SkTypeface::MakeDefault()};
- }(),
- [] {
- return std::vector<sk_sp<SkTypeface>>{SkTypeface::MakeDefault(),
- SkTypeface::MakeDefault()};
- }(),
- };
- static std::vector<sk_sp<SkTextBlob>> test_paint_blobs = {
- [] {
- SkFont font;
- font.setTypeface(test_typefaces[0][0]);
- SkTextBlobBuilder builder;
- int glyph_count = 5;
- const auto& run = builder.allocRun(font, glyph_count, 1.2f, 2.3f);
- // allocRun() allocates only the glyph buffer.
- std::fill(run.glyphs, run.glyphs + glyph_count, 0);
- return builder.make();
- }(),
- [] {
- SkFont font;
- font.setTypeface(test_typefaces[1][0]);
- SkTextBlobBuilder builder;
- int glyph_count = 5;
- const auto& run1 = builder.allocRun(font, glyph_count, 1.2f, 2.3f);
- // allocRun() allocates only the glyph buffer.
- std::fill(run1.glyphs, run1.glyphs + glyph_count, 0);
- glyph_count = 16;
- const auto& run2 = builder.allocRunPos(font, glyph_count);
- // allocRun() allocates the glyph buffer, and 2 scalars per glyph for
- // the pos buffer.
- std::fill(run2.glyphs, run2.glyphs + glyph_count, 0);
- std::fill(run2.pos, run2.pos + glyph_count * 2, 0);
- font.setTypeface(test_typefaces[1][1]);
- glyph_count = 8;
- const auto& run3 = builder.allocRunPosH(font, glyph_count, 0);
- // allocRun() allocates the glyph buffer, and 1 scalar per glyph for the
- // pos buffer.
- std::fill(run3.glyphs, run3.glyphs + glyph_count, 0);
- std::fill(run3.pos, run3.pos + glyph_count, 0);
- return builder.make();
- }(),
- };
- size_t len = std::min(
- {test_paint_blobs.size(), test_flags.size(), test_floats.size() - 1});
- for (size_t i = 0; i < len; ++i) {
- buffer->push<DrawTextBlobOp>(test_paint_blobs[i], test_floats[i],
- test_floats[i + 1], test_flags[i]);
- }
- ValidateOps<DrawTextBlobOp>(buffer);
- }
- void PushNoopOps(PaintOpBuffer* buffer) {
- buffer->push<NoopOp>();
- buffer->push<NoopOp>();
- buffer->push<NoopOp>();
- buffer->push<NoopOp>();
- ValidateOps<NoopOp>(buffer);
- }
- void PushRestoreOps(PaintOpBuffer* buffer) {
- buffer->push<RestoreOp>();
- buffer->push<RestoreOp>();
- buffer->push<RestoreOp>();
- buffer->push<RestoreOp>();
- ValidateOps<RestoreOp>(buffer);
- }
- void PushRotateOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_floats.size(); ++i)
- buffer->push<RotateOp>(test_floats[i]);
- ValidateOps<RotateOp>(buffer);
- }
- void PushSaveOps(PaintOpBuffer* buffer) {
- buffer->push<SaveOp>();
- buffer->push<SaveOp>();
- buffer->push<SaveOp>();
- buffer->push<SaveOp>();
- ValidateOps<SaveOp>(buffer);
- }
- void PushSaveLayerOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_flags.size(), test_rects.size());
- for (size_t i = 0; i < len; ++i)
- buffer->push<SaveLayerOp>(&test_rects[i], &test_flags[i]);
- // Test combinations of optional args.
- buffer->push<SaveLayerOp>(nullptr, &test_flags[0]);
- buffer->push<SaveLayerOp>(&test_rects[0], nullptr);
- buffer->push<SaveLayerOp>(nullptr, nullptr);
- ValidateOps<SaveLayerOp>(buffer);
- }
- void PushSaveLayerAlphaOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_floats.size(), test_rects.size());
- for (size_t i = 0; i < len; ++i)
- buffer->push<SaveLayerAlphaOp>(&test_rects[i], test_floats[i]);
- // Test optional args.
- buffer->push<SaveLayerAlphaOp>(nullptr, test_floats[0]);
- ValidateOps<SaveLayerAlphaOp>(buffer);
- }
- void PushScaleOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_floats.size() - 1; i += 2)
- buffer->push<ScaleOp>(test_floats[i], test_floats[i + 1]);
- ValidateOps<ScaleOp>(buffer);
- }
- void PushSetMatrixOps(PaintOpBuffer* buffer) {
- for (auto& test_matrix : test_matrices)
- buffer->push<SetMatrixOp>(test_matrix);
- ValidateOps<SetMatrixOp>(buffer);
- }
- void PushTranslateOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_floats.size() - 1; i += 2)
- buffer->push<TranslateOp>(test_floats[i], test_floats[i + 1]);
- ValidateOps<TranslateOp>(buffer);
- }
- void PushSetNodeIdOps(PaintOpBuffer* buffer) {
- for (size_t i = 0; i < test_ids.size(); i++)
- buffer->push<SetNodeIdOp>(static_cast<int>(test_ids[i]));
- ValidateOps<SetNodeIdOp>(buffer);
- }
- class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
- public:
- PaintOpType GetParamType() const {
- return static_cast<PaintOpType>(GetParam());
- }
- void PushTestOps(PaintOpType type) {
- switch (type) {
- case PaintOpType::Annotate:
- PushAnnotateOps(&buffer_);
- break;
- case PaintOpType::ClipPath:
- PushClipPathOps(&buffer_);
- break;
- case PaintOpType::ClipRect:
- PushClipRectOps(&buffer_);
- break;
- case PaintOpType::ClipRRect:
- PushClipRRectOps(&buffer_);
- break;
- case PaintOpType::Concat:
- PushConcatOps(&buffer_);
- break;
- case PaintOpType::CustomData:
- PushCustomDataOps(&buffer_);
- break;
- case PaintOpType::DrawColor:
- PushDrawColorOps(&buffer_);
- break;
- case PaintOpType::DrawDRRect:
- PushDrawDRRectOps(&buffer_);
- break;
- case PaintOpType::DrawImage:
- PushDrawImageOps(&buffer_);
- break;
- case PaintOpType::DrawImageRect:
- PushDrawImageRectOps(&buffer_);
- break;
- case PaintOpType::DrawIRect:
- PushDrawIRectOps(&buffer_);
- break;
- case PaintOpType::DrawLine:
- PushDrawLineOps(&buffer_);
- break;
- case PaintOpType::DrawOval:
- PushDrawOvalOps(&buffer_);
- break;
- case PaintOpType::DrawPath:
- PushDrawPathOps(&buffer_);
- break;
- case PaintOpType::DrawRecord:
- // Not supported.
- break;
- case PaintOpType::DrawRect:
- PushDrawRectOps(&buffer_);
- break;
- case PaintOpType::DrawRRect:
- PushDrawRRectOps(&buffer_);
- break;
- case PaintOpType::DrawSkottie:
- PushDrawSkottieOps(&buffer_);
- break;
- case PaintOpType::DrawTextBlob:
- // TODO(crbug.com/1321150): fix the test for DrawTextBlobs
- // PushDrawTextBlobOps(&buffer_);
- break;
- case PaintOpType::Noop:
- PushNoopOps(&buffer_);
- break;
- case PaintOpType::Restore:
- PushRestoreOps(&buffer_);
- break;
- case PaintOpType::Rotate:
- PushRotateOps(&buffer_);
- break;
- case PaintOpType::Save:
- PushSaveOps(&buffer_);
- break;
- case PaintOpType::SaveLayer:
- PushSaveLayerOps(&buffer_);
- break;
- case PaintOpType::SaveLayerAlpha:
- PushSaveLayerAlphaOps(&buffer_);
- break;
- case PaintOpType::Scale:
- PushScaleOps(&buffer_);
- break;
- case PaintOpType::SetMatrix:
- PushSetMatrixOps(&buffer_);
- break;
- case PaintOpType::Translate:
- PushTranslateOps(&buffer_);
- break;
- case PaintOpType::SetNodeId:
- PushSetNodeIdOps(&buffer_);
- break;
- }
- }
- void ResizeOutputBuffer() {
- // An arbitrary deserialization buffer size that should fit all the ops
- // in the buffer_.
- output_size_ = kBufferBytesPerOp * buffer_.size();
- output_.reset(static_cast<char*>(
- base::AlignedAlloc(output_size_, PaintOpBuffer::PaintOpAlign)));
- }
- bool IsTypeSupported() {
- // TODO(crbug.com/1321150): fix the test for DrawTextBlobs
- if (GetParamType() == PaintOpType::DrawTextBlob)
- return false;
- // DrawRecordOps must be flattened and are not currently serialized. All
- // other types must push non-zero amounts of ops in PushTestOps.
- return GetParamType() != PaintOpType::DrawRecord &&
- (GetParamType() != PaintOpType::DrawSkottie || kIsSkottieSupported);
- }
- protected:
- std::unique_ptr<char, base::AlignedFreeDeleter> output_;
- size_t output_size_ = 0u;
- PaintOpBuffer buffer_;
- };
- INSTANTIATE_TEST_SUITE_P(
- P,
- PaintOpSerializationTest,
- ::testing::Range(static_cast<uint8_t>(0),
- static_cast<uint8_t>(PaintOpType::LastPaintOpType)));
- // Test serializing and then deserializing all test ops. They should all
- // write successfully and be identical to the original ops in the buffer.
- TEST_P(PaintOpSerializationTest, SmokeTest) {
- if (!IsTypeSupported())
- return;
- PushTestOps(GetParamType());
- ResizeOutputBuffer();
- SimpleSerializer serializer(output_.get(), output_size_);
- auto canvas =
- serializer.options_provider()->strike_server()->makeAnalysisCanvas(
- 1024, 768, {}, nullptr, true);
- PlaybackParams params(nullptr, canvas->getLocalToDevice());
- params.is_analyzing = true;
- buffer_.Playback(canvas.get(), params);
- std::vector<uint8_t> strike_data;
- serializer.options_provider()->strike_server()->writeStrikeData(&strike_data);
- if (!strike_data.empty()) {
- serializer.options_provider()->strike_client()->readStrikeData(
- strike_data.data(), strike_data.size());
- }
- serializer.Serialize(buffer_);
- // Expect all ops to write more than 0 bytes.
- for (size_t i = 0; i < buffer_.size(); ++i) {
- SCOPED_TRACE(base::StringPrintf(
- "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), i));
- EXPECT_GT(serializer.bytes_written()[i], 0u);
- }
- PaintOpBuffer::Iterator iter(&buffer_);
- size_t i = 0;
- for (auto* base_written : DeserializerIterator(
- output_.get(), serializer.TotalBytesWritten(),
- serializer.options_provider()->deserialize_options())) {
- SCOPED_TRACE(base::StringPrintf(
- "%s #%zu", PaintOpTypeToString(GetParamType()).c_str(), i));
- ASSERT_EQ(!*iter, !base_written);
- EXPECT_EQ(**iter, *base_written);
- ++iter;
- ++i;
- }
- EXPECT_EQ(buffer_.size(), i);
- }
- // Verify for all test ops that serializing into a smaller size aborts
- // correctly and doesn't write anything.
- TEST_P(PaintOpSerializationTest, SerializationFailures) {
- if (!IsTypeSupported())
- return;
- PushTestOps(GetParamType());
- ResizeOutputBuffer();
- SimpleSerializer serializer(output_.get(), output_size_);
- serializer.Serialize(buffer_);
- std::vector<size_t> bytes_written = serializer.bytes_written();
- TestOptionsProvider options_provider;
- size_t op_idx = 0;
- for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter, ++op_idx) {
- SCOPED_TRACE(base::StringPrintf(
- "%s #%zu", PaintOpTypeToString(GetParamType()).c_str(), op_idx));
- size_t expected_bytes = bytes_written[op_idx];
- EXPECT_GT(expected_bytes, 0u);
- EXPECT_EQ(expected_bytes,
- base::bits::AlignUp(expected_bytes, PaintOpWriter::Alignment()));
- // Attempt to write op into a buffer of size |i|, and only expect
- // it to succeed if the buffer is large enough.
- for (size_t i = 0; i < bytes_written[op_idx] + 2; ++i) {
- options_provider.ClearPaintCache();
- options_provider.ForcePurgeSkottieSerializationHistory();
- size_t written_bytes = iter->Serialize(
- output_.get(), i, options_provider.serialize_options(), nullptr,
- SkM44(), SkM44());
- if (i >= expected_bytes) {
- EXPECT_EQ(expected_bytes, written_bytes) << "i: " << i;
- } else {
- EXPECT_EQ(0u, written_bytes) << "i: " << i;
- }
- }
- }
- }
- // Verify that deserializing test ops from too small buffers aborts
- // correctly, in case the deserialized data is lying about how big it is.
- TEST_P(PaintOpSerializationTest, DeserializationFailures) {
- if (!IsTypeSupported())
- return;
- PushTestOps(GetParamType());
- ResizeOutputBuffer();
- SimpleSerializer serializer(output_.get(), output_size_);
- serializer.Serialize(buffer_);
- TestOptionsProvider* options_provider = serializer.options_provider();
- char* first = static_cast<char*>(output_.get());
- char* current = first;
- static constexpr size_t kAlign = PaintOpBuffer::PaintOpAlign;
- static constexpr size_t kOutputOpSize = kBufferBytesPerOp;
- std::unique_ptr<char, base::AlignedFreeDeleter> deserialize_buffer_(
- static_cast<char*>(base::AlignedAlloc(kOutputOpSize, kAlign)));
- size_t op_idx = 0;
- size_t total_read = 0;
- for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter, ++op_idx) {
- PaintOp* serialized = reinterpret_cast<PaintOp*>(current);
- uint32_t skip = serialized->skip;
- // Read from buffers of various sizes to make sure that having a serialized
- // op size that is larger than the input buffer provided causes a
- // deserialization failure to return nullptr. Also test a few valid sizes
- // larger than read size.
- for (size_t read_size = 0; read_size < skip + kAlign * 2 + 2; ++read_size) {
- SCOPED_TRACE(
- base::StringPrintf("%s #%zd, read_size: %zu, align: %zu, skip: %u",
- PaintOpTypeToString(GetParamType()).c_str(),
- op_idx, read_size, kAlign, skip));
- // Because PaintOp::Deserialize early outs when the input size is < skip
- // deliberately lie about the skip. This op tooooootally fits.
- // This will verify that individual op deserializing code behaves
- // properly when presented with invalid offsets.
- serialized->skip = read_size;
- size_t bytes_read = 0;
- PaintOp* written = PaintOp::Deserialize(
- current, read_size, deserialize_buffer_.get(), kOutputOpSize,
- &bytes_read, options_provider->deserialize_options());
- // Deserialize buffers with valid ops until the last op. This verifies
- // that the complete buffer is invalidated on encountering the first
- // corrupted op.
- auto deserialized_buffer = PaintOpBuffer::MakeFromMemory(
- first, total_read + read_size,
- options_provider->deserialize_options());
- // Skips are only valid if they are aligned.
- if (read_size >= skip && read_size % kAlign == 0) {
- ASSERT_NE(nullptr, written);
- ASSERT_LE(written->skip, kOutputOpSize);
- EXPECT_EQ(GetParamType(), written->GetType());
- EXPECT_EQ(serialized->skip, bytes_read);
- ASSERT_NE(nullptr, deserialized_buffer);
- EXPECT_EQ(deserialized_buffer->size(), op_idx + 1);
- } else if (read_size == 0 && op_idx != 0) {
- // If no data was read for a subsequent op while some ops were
- // deserialized, we still have a valid buffer with the deserialized ops.
- ASSERT_NE(nullptr, deserialized_buffer);
- EXPECT_EQ(deserialized_buffer->size(), op_idx);
- } else {
- // If a subsequent op was corrupted or no ops could be serialized, we
- // have an invalid buffer.
- EXPECT_EQ(nullptr, written);
- // If the buffer is exactly 0 bytes, then MakeFromMemory treats it as a
- // valid empty buffer.
- if (deserialized_buffer) {
- EXPECT_EQ(0u, read_size);
- EXPECT_EQ(0u, deserialized_buffer->size());
- // Verify that we can create an iterator from this buffer, but it's
- // empty.
- PaintOpBuffer::Iterator it(deserialized_buffer.get());
- EXPECT_FALSE(it);
- } else {
- EXPECT_NE(0u, read_size);
- EXPECT_EQ(nullptr, deserialized_buffer.get());
- }
- }
- if (written)
- written->DestroyThis();
- }
- serialized->skip = skip;
- current += skip;
- total_read += skip;
- }
- }
- TEST_P(PaintOpSerializationTest, UsesOverridenFlags) {
- if (!PaintOp::TypeHasFlags(GetParamType()))
- return;
- // TODO(crbug.com/1321150): fix the test for DrawTextBlobs
- if (GetParamType() == PaintOpType::DrawTextBlob)
- return;
- PushTestOps(GetParamType());
- ResizeOutputBuffer();
- TestOptionsProvider options_provider;
- size_t deserialized_size = sizeof(LargestPaintOp) + PaintOp::kMaxSkip;
- std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
- static_cast<char*>(
- base::AlignedAlloc(deserialized_size, PaintOpBuffer::PaintOpAlign)));
- for (const auto* op : PaintOpBuffer::Iterator(&buffer_)) {
- size_t bytes_written = op->Serialize(output_.get(), output_size_,
- options_provider.serialize_options(),
- nullptr, SkM44(), SkM44());
- size_t bytes_read = 0u;
- PaintOp* written = PaintOp::Deserialize(
- output_.get(), bytes_written, deserialized.get(), deserialized_size,
- &bytes_read, options_provider.deserialize_options());
- ASSERT_TRUE(written) << PaintOpTypeToString(GetParamType());
- EXPECT_EQ(*op, *written);
- written->DestroyThis();
- written = nullptr;
- PaintFlags override_flags = static_cast<const PaintOpWithFlags*>(op)->flags;
- override_flags.setAlpha(override_flags.getAlpha() * 0.5);
- bytes_written = op->Serialize(output_.get(), output_size_,
- options_provider.serialize_options(),
- &override_flags, SkM44(), SkM44());
- written = PaintOp::Deserialize(
- output_.get(), bytes_written, deserialized.get(), deserialized_size,
- &bytes_read, options_provider.deserialize_options());
- ASSERT_TRUE(written);
- ASSERT_TRUE(written->IsPaintOpWithFlags());
- EXPECT_EQ(static_cast<const PaintOpWithFlags*>(written)->flags.getAlpha(),
- override_flags.getAlpha());
- written->DestroyThis();
- written = nullptr;
- }
- }
- TEST(PaintOpSerializationTest, CompleteBufferSerialization) {
- PaintOpBuffer buffer;
- PushDrawIRectOps(&buffer);
- PaintOpBufferSerializer::Preamble preamble;
- preamble.content_size = gfx::Size(1000, 1000);
- preamble.playback_rect = gfx::Rect(preamble.content_size);
- preamble.full_raster_rect = preamble.playback_rect;
- preamble.requires_clear = true;
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(&buffer, nullptr, preamble);
- ASSERT_NE(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- // The deserialized buffer has an extra pair of save/restores and a clear, for
- // the preamble and root buffer.
- ASSERT_EQ(deserialized_buffer->size(), buffer.size() + 4u);
- size_t i = 0;
- auto serialized_iter = PaintOpBuffer::Iterator(&buffer);
- for (const auto* op : PaintOpBuffer::Iterator(deserialized_buffer.get())) {
- SCOPED_TRACE(i);
- i++;
- if (i == 1) {
- // Save.
- ASSERT_EQ(op->GetType(), PaintOpType::Save)
- << PaintOpTypeToString(op->GetType());
- continue;
- }
- if (i == 2) {
- // Preamble partial raster clear.
- ASSERT_EQ(op->GetType(), PaintOpType::DrawColor)
- << PaintOpTypeToString(op->GetType());
- continue;
- }
- if (i == 3) {
- // Preamble playback rect clip.
- ASSERT_EQ(op->GetType(), PaintOpType::ClipRect)
- << PaintOpTypeToString(op->GetType());
- EXPECT_EQ(static_cast<const ClipRectOp*>(op)->rect,
- gfx::RectToSkRect(preamble.playback_rect));
- continue;
- }
- if (serialized_iter) {
- // Root buffer.
- ASSERT_EQ(op->GetType(), (*serialized_iter)->GetType())
- << PaintOpTypeToString(op->GetType());
- EXPECT_EQ(*op, **serialized_iter);
- ++serialized_iter;
- continue;
- }
- // End restore.
- ASSERT_EQ(op->GetType(), PaintOpType::Restore)
- << PaintOpTypeToString(op->GetType());
- }
- }
- TEST(PaintOpSerializationTest, DoNotPreservePaintOps) {
- PaintOpBuffer buffer;
- PushDrawIRectOps(&buffer);
- PaintOpBufferSerializer::Preamble preamble;
- preamble.content_size = gfx::Size(1000, 1000);
- preamble.playback_rect = gfx::Rect(preamble.content_size);
- preamble.full_raster_rect = preamble.playback_rect;
- preamble.requires_clear = true;
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.SerializeAndDestroy(&buffer, nullptr, preamble);
- ASSERT_NE(serializer.written(), 0u);
- EXPECT_TRUE(buffer.are_ops_destroyed());
- }
- TEST(PaintOpSerializationTest, Preamble) {
- PaintOpBufferSerializer::Preamble preamble;
- preamble.content_size = gfx::Size(30, 40);
- preamble.full_raster_rect = gfx::Rect(10, 20, 8, 7);
- preamble.playback_rect = gfx::Rect(12, 25, 1, 2);
- preamble.post_translation = gfx::Vector2dF(4.3f, 7.f);
- preamble.post_scale = gfx::Vector2dF(0.5f, 0.5f);
- preamble.requires_clear = true;
- PaintOpBuffer buffer;
- buffer.push<DrawColorOp>(SkColors::kBlue, SkBlendMode::kSrc);
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.SerializeAndDestroy(&buffer, nullptr, preamble);
- ASSERT_NE(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- // 5 ops for the preamble and 2 for save/restore.
- ASSERT_EQ(deserialized_buffer->size(), buffer.size() + 7u);
- size_t i = 0;
- for (const auto* op : PaintOpBuffer::Iterator(deserialized_buffer.get())) {
- i++;
- if (i == 1) {
- // Save.
- ASSERT_EQ(op->GetType(), PaintOpType::Save)
- << PaintOpTypeToString(op->GetType());
- continue;
- }
- if (i == 2) {
- // Translate.
- ASSERT_EQ(op->GetType(), PaintOpType::Translate)
- << PaintOpTypeToString(op->GetType());
- const auto* translate_op = static_cast<const TranslateOp*>(op);
- EXPECT_EQ(translate_op->dx, -preamble.full_raster_rect.x());
- EXPECT_EQ(translate_op->dy, -preamble.full_raster_rect.y());
- continue;
- }
- if (i == 3) {
- // Clip.
- ASSERT_EQ(op->GetType(), PaintOpType::ClipRect)
- << PaintOpTypeToString(op->GetType());
- const auto* clip_op = static_cast<const ClipRectOp*>(op);
- EXPECT_RECTF_EQ(gfx::SkRectToRectF(clip_op->rect),
- gfx::RectF(preamble.playback_rect));
- continue;
- }
- if (i == 4) {
- // Post translate.
- ASSERT_EQ(op->GetType(), PaintOpType::Translate)
- << PaintOpTypeToString(op->GetType());
- const auto* translate_op = static_cast<const TranslateOp*>(op);
- EXPECT_EQ(translate_op->dx, preamble.post_translation.x());
- EXPECT_EQ(translate_op->dy, preamble.post_translation.y());
- continue;
- }
- if (i == 5) {
- // Scale.
- ASSERT_EQ(op->GetType(), PaintOpType::Scale)
- << PaintOpTypeToString(op->GetType());
- const auto* scale_op = static_cast<const ScaleOp*>(op);
- EXPECT_EQ(scale_op->sx, preamble.post_scale.x());
- EXPECT_EQ(scale_op->sy, preamble.post_scale.y());
- continue;
- }
- if (i == 6) {
- // Partial raster clear goes last.
- ASSERT_EQ(op->GetType(), PaintOpType::DrawColor)
- << PaintOpTypeToString(op->GetType());
- const auto* draw_color_op = static_cast<const DrawColorOp*>(op);
- EXPECT_EQ(draw_color_op->color, SkColors::kTransparent);
- EXPECT_EQ(draw_color_op->mode, SkBlendMode::kSrc);
- continue;
- }
- if (i == 7) {
- // Buffer.
- EXPECT_EQ(*op, *buffer.GetFirstOp());
- continue;
- }
- // End restore.
- ASSERT_EQ(op->GetType(), PaintOpType::Restore)
- << PaintOpTypeToString(op->GetType());
- }
- }
- TEST(PaintOpSerializationTest, SerializesNestedRecords) {
- auto record = sk_make_sp<PaintOpBuffer>();
- record->push<ScaleOp>(0.5f, 0.75f);
- record->push<DrawRectOp>(SkRect::MakeWH(10.f, 20.f), PaintFlags());
- PaintOpBuffer buffer;
- buffer.push<DrawRecordOp>(record);
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- PaintOpBufferSerializer::Preamble preamble;
- serializer.Serialize(&buffer, nullptr, preamble);
- ASSERT_NE(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- ASSERT_EQ(deserialized_buffer->size(), record->size() + 5u);
- size_t i = 0;
- auto serialized_iter = PaintOpBuffer::Iterator(record.get());
- for (const auto* op : PaintOpBuffer::Iterator(deserialized_buffer.get())) {
- i++;
- if (i == 1 || i == 3) {
- // First 2 saves.
- ASSERT_EQ(op->GetType(), PaintOpType::Save)
- << PaintOpTypeToString(op->GetType());
- continue;
- }
- // Clear.
- if (i == 2) {
- ASSERT_EQ(op->GetType(), PaintOpType::DrawColor)
- << PaintOpTypeToString(op->GetType());
- continue;
- }
- if (serialized_iter) {
- // Nested buffer.
- ASSERT_EQ(op->GetType(), (*serialized_iter)->GetType())
- << PaintOpTypeToString(op->GetType());
- EXPECT_EQ(*op, **serialized_iter);
- ++serialized_iter;
- continue;
- }
- // End restores.
- ASSERT_EQ(op->GetType(), PaintOpType::Restore)
- << PaintOpTypeToString(op->GetType());
- }
- }
- TEST(PaintOpBufferTest, ClipsImagesDuringSerialization) {
- struct {
- gfx::Rect clip_rect;
- gfx::Rect image_rect;
- bool should_draw;
- } test_cases[] = {
- {gfx::Rect(0, 0, 100, 100), gfx::Rect(50, 50, 100, 100), true},
- {gfx::Rect(0, 0, 100, 100), gfx::Rect(105, 105, 100, 100), false},
- {gfx::Rect(0, 0, 500, 500), gfx::Rect(450, 450, 100, 100), true},
- {gfx::Rect(0, 0, 500, 500), gfx::Rect(750, 750, 100, 100), false},
- {gfx::Rect(250, 250, 250, 250), gfx::Rect(450, 450, 100, 100), true},
- {gfx::Rect(250, 250, 250, 250), gfx::Rect(50, 50, 100, 100), false},
- {gfx::Rect(0, 0, 100, 500), gfx::Rect(250, 250, 100, 100), false},
- {gfx::Rect(0, 0, 200, 500), gfx::Rect(100, 250, 100, 100), true}};
- for (const auto& test_case : test_cases) {
- PaintOpBuffer buffer;
- buffer.push<DrawImageOp>(
- CreateDiscardablePaintImage(test_case.image_rect.size()),
- static_cast<SkScalar>(test_case.image_rect.x()),
- static_cast<SkScalar>(test_case.image_rect.y()));
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- PaintOpBufferSerializer::Preamble preamble;
- preamble.playback_rect = test_case.clip_rect;
- preamble.full_raster_rect = gfx::Rect(0, 0, test_case.clip_rect.right(),
- test_case.clip_rect.bottom());
- // Avoid clearing.
- preamble.content_size = gfx::Size(1000, 1000);
- preamble.requires_clear = false;
- serializer.SerializeAndDestroy(&buffer, nullptr, preamble);
- ASSERT_NE(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- auto deserialized_iter = PaintOpBuffer::Iterator(deserialized_buffer.get());
- ASSERT_EQ((*deserialized_iter)->GetType(), PaintOpType::Save)
- << PaintOpTypeToString((*deserialized_iter)->GetType());
- ++deserialized_iter;
- ASSERT_EQ((*deserialized_iter)->GetType(), PaintOpType::ClipRect)
- << PaintOpTypeToString((*deserialized_iter)->GetType());
- ++deserialized_iter;
- if (test_case.should_draw) {
- ASSERT_EQ((*deserialized_iter)->GetType(), PaintOpType::DrawImage)
- << PaintOpTypeToString((*deserialized_iter)->GetType());
- ++deserialized_iter;
- }
- ASSERT_EQ((*deserialized_iter)->GetType(), PaintOpType::Restore)
- << PaintOpTypeToString((*deserialized_iter)->GetType());
- ++deserialized_iter;
- ASSERT_EQ(deserialized_iter.end(), deserialized_iter);
- }
- }
- TEST(PaintOpBufferSerializationTest, AlphaFoldingDuringSerialization) {
- PaintOpBuffer buffer;
- float alpha = 0.4f;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
- PaintFlags draw_flags;
- draw_flags.setColor(SkColors::kMagenta);
- draw_flags.setAlpha(50);
- SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
- buffer.push<DrawRectOp>(rect, draw_flags);
- buffer.push<RestoreOp>();
- PaintOpBufferSerializer::Preamble preamble;
- preamble.content_size = gfx::Size(1000, 1000);
- preamble.playback_rect = gfx::Rect(gfx::Size(100, 100));
- preamble.full_raster_rect = preamble.playback_rect;
- preamble.requires_clear = false;
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.SerializeAndDestroy(&buffer, nullptr, preamble);
- ASSERT_NE(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- // 4 additional ops for save, clip, clear, and restore.
- ASSERT_EQ(deserialized_buffer->size(), 4u);
- size_t i = 0;
- for (const auto* op : PaintOpBuffer::Iterator(deserialized_buffer.get())) {
- ++i;
- if (i == 1) {
- EXPECT_EQ(op->GetType(), PaintOpType::Save);
- continue;
- }
- if (i == 2) {
- EXPECT_EQ(op->GetType(), PaintOpType::ClipRect);
- continue;
- }
- if (i == 4) {
- EXPECT_EQ(op->GetType(), PaintOpType::Restore);
- continue;
- }
- ASSERT_EQ(op->GetType(), PaintOpType::DrawRect);
- // Expect the alpha from the draw and the save layer to be folded together.
- // Since alpha is stored in a uint8_t and gets rounded, so use tolerance.
- float expected_alpha = alpha * 50;
- EXPECT_LE(std::abs(expected_alpha -
- static_cast<const DrawRectOp*>(op)->flags.getAlpha()),
- 1.f);
- }
- }
- // Test generic PaintOp deserializing failure cases.
- TEST(PaintOpBufferTest, PaintOpDeserialize) {
- static constexpr size_t kSize = sizeof(LargestPaintOp) + 100;
- static constexpr size_t kAlign = PaintOpBuffer::PaintOpAlign;
- std::unique_ptr<char, base::AlignedFreeDeleter> input_(
- static_cast<char*>(base::AlignedAlloc(kSize, kAlign)));
- std::unique_ptr<char, base::AlignedFreeDeleter> output_(
- static_cast<char*>(base::AlignedAlloc(kSize, kAlign)));
- PaintOpBuffer buffer;
- buffer.push<DrawColorOp>(SkColors::kMagenta, SkBlendMode::kSrc);
- PaintOpBuffer::Iterator iter(&buffer);
- PaintOp* op = *iter;
- ASSERT_TRUE(op);
- TestOptionsProvider options_provider;
- size_t bytes_written =
- op->Serialize(input_.get(), kSize, options_provider.serialize_options(),
- nullptr, SkM44(), SkM44());
- ASSERT_GT(bytes_written, 0u);
- // can deserialize from exactly the right size
- size_t bytes_read = 0;
- PaintOp* success =
- PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize,
- &bytes_read, options_provider.deserialize_options());
- ASSERT_TRUE(success);
- EXPECT_EQ(bytes_written, bytes_read);
- success->DestroyThis();
- // fail to deserialize if skip goes past input size
- // (the DeserializationFailures test above tests if the skip is lying)
- for (size_t i = 0; i < bytes_written - 1; ++i)
- EXPECT_FALSE(PaintOp::Deserialize(input_.get(), i, output_.get(), kSize,
- &bytes_read,
- options_provider.deserialize_options()));
- // unaligned skips fail to deserialize
- PaintOp* serialized = reinterpret_cast<PaintOp*>(input_.get());
- EXPECT_EQ(0u, serialized->skip % kAlign);
- serialized->skip -= 1;
- EXPECT_FALSE(PaintOp::Deserialize(input_.get(), bytes_written, output_.get(),
- kSize, &bytes_read,
- options_provider.deserialize_options()));
- serialized->skip += 1;
- // bogus types fail to deserialize
- serialized->type = static_cast<uint8_t>(PaintOpType::LastPaintOpType) + 1;
- EXPECT_FALSE(PaintOp::Deserialize(input_.get(), bytes_written, output_.get(),
- kSize, &bytes_read,
- options_provider.deserialize_options()));
- }
- // Test that deserializing invalid SkClipOp enums fails silently.
- // Skia release asserts on this in several places so these are not safe
- // to pass through to the SkCanvas API.
- TEST(PaintOpBufferTest, ValidateSkClip) {
- size_t buffer_size = kBufferBytesPerOp;
- std::unique_ptr<char, base::AlignedFreeDeleter> serialized(static_cast<char*>(
- base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
- std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
- static_cast<char*>(
- base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
- PaintOpBuffer buffer;
- // Successful first op.
- SkPath path;
- buffer.push<ClipPathOp>(path, SkClipOp::kMax_EnumValue, /*antialias=*/true,
- UsePaintCache::kDisabled);
- // Bad other ops.
- SkClipOp bad_clip = static_cast<SkClipOp>(
- static_cast<uint32_t>(SkClipOp::kMax_EnumValue) + 1);
- buffer.push<ClipPathOp>(path, bad_clip, /*antialias=*/true,
- UsePaintCache::kDisabled);
- buffer.push<ClipRectOp>(test_rects[0], bad_clip, true);
- buffer.push<ClipRRectOp>(test_rrects[0], bad_clip, false);
- // SkClipOp is serialized to uint8_t (see WriteEnum). Values outside uint8_t
- // would crash the serialization, so this is the max value that passes checked
- // cast but will still fail validation during deserialization.
- SkClipOp bad_clip_max = static_cast<SkClipOp>(static_cast<uint8_t>(~0));
- buffer.push<ClipRectOp>(test_rects[1], bad_clip_max, false);
- TestOptionsProvider options_provider;
- int op_idx = 0;
- for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
- const PaintOp* op = *iter;
- size_t bytes_written = op->Serialize(serialized.get(), buffer_size,
- options_provider.serialize_options(),
- nullptr, SkM44(), SkM44());
- ASSERT_GT(bytes_written, 0u);
- size_t bytes_read = 0;
- PaintOp* written = PaintOp::Deserialize(
- serialized.get(), bytes_written, deserialized.get(), buffer_size,
- &bytes_read, options_provider.deserialize_options());
- // First op should succeed. Other ops with bad enums should
- // serialize correctly but fail to deserialize due to the bad
- // SkClipOp enum.
- if (!op_idx) {
- EXPECT_TRUE(written) << "op: " << op_idx;
- EXPECT_EQ(bytes_written, bytes_read);
- written->DestroyThis();
- } else {
- EXPECT_FALSE(written) << "op: " << op_idx;
- }
- ++op_idx;
- }
- }
- TEST(PaintOpBufferTest, ValidateSkBlendMode) {
- size_t buffer_size = kBufferBytesPerOp;
- std::unique_ptr<char, base::AlignedFreeDeleter> serialized(static_cast<char*>(
- base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
- std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
- static_cast<char*>(
- base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
- PaintOpBuffer buffer;
- // Successful first two ops.
- buffer.push<DrawColorOp>(SkColors::kMagenta, SkBlendMode::kDstIn);
- PaintFlags good_flags = test_flags[0];
- good_flags.setBlendMode(SkBlendMode::kColorBurn);
- buffer.push<DrawRectOp>(test_rects[0], good_flags);
- // Modes that are not supported by drawColor or SkPaint.
- SkBlendMode bad_modes_for_draw_color[] = {
- SkBlendMode::kOverlay,
- SkBlendMode::kDarken,
- SkBlendMode::kLighten,
- SkBlendMode::kColorDodge,
- SkBlendMode::kColorBurn,
- SkBlendMode::kHardLight,
- SkBlendMode::kSoftLight,
- SkBlendMode::kDifference,
- SkBlendMode::kExclusion,
- SkBlendMode::kMultiply,
- SkBlendMode::kHue,
- SkBlendMode::kSaturation,
- SkBlendMode::kColor,
- SkBlendMode::kLuminosity,
- static_cast<SkBlendMode>(static_cast<uint8_t>(SkBlendMode::kLastMode) +
- 1),
- static_cast<SkBlendMode>(static_cast<uint8_t>(~0)),
- };
- SkBlendMode bad_modes_for_flags[] = {
- static_cast<SkBlendMode>(static_cast<uint8_t>(SkBlendMode::kLastMode) +
- 1),
- static_cast<SkBlendMode>(static_cast<uint8_t>(~0)),
- };
- for (size_t i = 0; i < std::size(bad_modes_for_draw_color); ++i) {
- buffer.push<DrawColorOp>(SkColors::kMagenta, bad_modes_for_draw_color[i]);
- }
- for (size_t i = 0; i < std::size(bad_modes_for_flags); ++i) {
- PaintFlags flags = test_flags[i % test_flags.size()];
- flags.setBlendMode(bad_modes_for_flags[i]);
- buffer.push<DrawRectOp>(test_rects[i % test_rects.size()], flags);
- }
- TestOptionsProvider options_provider;
- int op_idx = 0;
- for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
- const PaintOp* op = *iter;
- size_t bytes_written = op->Serialize(serialized.get(), buffer_size,
- options_provider.serialize_options(),
- nullptr, SkM44(), SkM44());
- ASSERT_GT(bytes_written, 0u);
- size_t bytes_read = 0;
- PaintOp* written = PaintOp::Deserialize(
- serialized.get(), bytes_written, deserialized.get(), buffer_size,
- &bytes_read, options_provider.deserialize_options());
- // First two ops should succeed. Other ops with bad enums should
- // serialize correctly but fail to deserialize due to the bad
- // SkBlendMode enum.
- if (op_idx < 2) {
- EXPECT_TRUE(written) << "op: " << op_idx;
- EXPECT_EQ(bytes_written, bytes_read);
- written->DestroyThis();
- } else {
- EXPECT_FALSE(written) << "op: " << op_idx;
- }
- ++op_idx;
- }
- }
- TEST(PaintOpBufferTest, ValidateRects) {
- size_t buffer_size = kBufferBytesPerOp;
- std::unique_ptr<char, base::AlignedFreeDeleter> serialized(static_cast<char*>(
- base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
- std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
- static_cast<char*>(
- base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
- // Used for QuickRejectDraw
- SkCanvas device(256, 256);
- SkCanvas* canvas = &device;
- SkRect bad_rect = SkRect::MakeEmpty();
- bad_rect.fBottom = std::numeric_limits<float>::quiet_NaN();
- EXPECT_FALSE(bad_rect.isFinite());
- // Push all op variations that take rects.
- PaintOpBuffer buffer;
- buffer.push<AnnotateOp>(PaintCanvas::AnnotationType::URL, bad_rect,
- SkData::MakeWithCString("test1"));
- buffer.push<ClipRectOp>(bad_rect, SkClipOp::kDifference, true);
- buffer.push<DrawImageRectOp>(test_images[0], bad_rect, test_rects[1],
- SkCanvas::kStrict_SrcRectConstraint);
- buffer.push<DrawImageRectOp>(test_images[0], test_rects[0], bad_rect,
- SkCanvas::kStrict_SrcRectConstraint);
- buffer.push<DrawOvalOp>(bad_rect, test_flags[0]);
- buffer.push<DrawRectOp>(bad_rect, test_flags[0]);
- buffer.push<SaveLayerOp>(&bad_rect, nullptr);
- buffer.push<SaveLayerOp>(&bad_rect, &test_flags[0]);
- buffer.push<SaveLayerAlphaOp>(&bad_rect, test_floats[0]);
- TestOptionsProvider options_provider;
- // Every op should serialize but fail to deserialize due to the bad rect.
- int op_idx = 0;
- for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
- const PaintOp* op = *iter;
- size_t bytes_written = op->Serialize(serialized.get(), buffer_size,
- options_provider.serialize_options(),
- nullptr, SkM44(), SkM44());
- ASSERT_GT(bytes_written, 0u);
- size_t bytes_read = 0;
- PaintOp* written = PaintOp::Deserialize(
- serialized.get(), bytes_written, deserialized.get(), buffer_size,
- &bytes_read, options_provider.deserialize_options());
- EXPECT_FALSE(written) << "op: " << op_idx;
- // Additionally, every draw op should be rejected by QuickRejectDraw if
- // the paint op buffer were played back directly without going through
- // deserialization (e.g. canvas2D, crbug.com/1186392)
- if (op->IsDrawOp()) {
- EXPECT_TRUE(PaintOp::QuickRejectDraw(op, canvas));
- }
- ++op_idx;
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawImageOp) {
- PaintOpBuffer buffer;
- PushDrawImageOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawImageOp*>(base_op);
- SkRect image_rect = SkRect::MakeXYWH(op->left, op->top, op->image.width(),
- op->image.height());
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, image_rect.makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawImageRectOp) {
- PaintOpBuffer buffer;
- PushDrawImageRectOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawImageRectOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->dst.makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawIRectOp) {
- PaintOpBuffer buffer;
- PushDrawIRectOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawIRectOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, SkRect::Make(op->rect).makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawOvalOp) {
- PaintOpBuffer buffer;
- PushDrawOvalOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawOvalOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->oval.makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawPathOp) {
- PaintOpBuffer buffer;
- PushDrawPathOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawPathOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->path.getBounds().makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawRectOp) {
- PaintOpBuffer buffer;
- PushDrawRectOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawRectOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->rect.makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawRRectOp) {
- PaintOpBuffer buffer;
- PushDrawRRectOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawRRectOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->rrect.rect().makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawLineOp) {
- PaintOpBuffer buffer;
- PushDrawLineOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawLineOp*>(base_op);
- SkRect line_rect;
- line_rect.fLeft = op->x0;
- line_rect.fTop = op->y0;
- line_rect.fRight = op->x1;
- line_rect.fBottom = op->y1;
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, line_rect.makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawDRRectOp) {
- PaintOpBuffer buffer;
- PushDrawDRRectOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawDRRectOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->outer.getBounds().makeSorted());
- }
- }
- TEST(PaintOpBufferTest, BoundingRect_DrawTextBlobOp) {
- PaintOpBuffer buffer;
- PushDrawTextBlobOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawTextBlobOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->blob->bounds().makeOffset(op->x, op->y).makeSorted());
- }
- }
- class MockImageProvider : public ImageProvider {
- public:
- MockImageProvider() = default;
- explicit MockImageProvider(bool fail_all_decodes)
- : fail_all_decodes_(fail_all_decodes) {}
- MockImageProvider(std::vector<SkSize> src_rect_offset,
- std::vector<SkSize> scale,
- std::vector<PaintFlags::FilterQuality> quality)
- : src_rect_offset_(src_rect_offset), scale_(scale), quality_(quality) {}
- ~MockImageProvider() override = default;
- ImageProvider::ScopedResult GetRasterContent(
- const DrawImage& draw_image) override {
- decoded_images_.push_back(draw_image);
- if (draw_image.paint_image().IsPaintWorklet())
- return ScopedResult(record_);
- if (fail_all_decodes_)
- return ImageProvider::ScopedResult();
- SkBitmap bitmap;
- bitmap.allocPixelsFlags(SkImageInfo::MakeN32Premul(10, 10),
- SkBitmap::kZeroPixels_AllocFlag);
- sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
- size_t i = index_++;
- return ScopedResult(DecodedDrawImage(image, nullptr, src_rect_offset_[i],
- scale_[i], quality_[i], true));
- }
- void SetRecord(sk_sp<PaintRecord> record) { record_ = std::move(record); }
- const std::vector<DrawImage>& decoded_images() const {
- return decoded_images_;
- }
- private:
- std::vector<SkSize> src_rect_offset_;
- std::vector<SkSize> scale_;
- std::vector<PaintFlags::FilterQuality> quality_;
- size_t index_ = 0;
- bool fail_all_decodes_ = false;
- sk_sp<PaintRecord> record_;
- std::vector<DrawImage> decoded_images_;
- };
- TEST(PaintOpBufferTest, SkipsOpsOutsideClip) {
- // All ops with images draw outside the clip and should be skipped. If any
- // call is made to the ImageProvider, it should crash.
- MockImageProvider image_provider;
- PaintOpBuffer buffer;
- // Apply a clip outside the region for images.
- buffer.push<ClipRectOp>(SkRect::MakeXYWH(0, 0, 100, 100),
- SkClipOp::kIntersect, false);
- PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f);
- PaintFlags image_flags;
- image_flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat,
- SkTileMode::kRepeat, nullptr));
- buffer.push<DrawRectOp>(SkRect::MakeXYWH(110, 110, 100, 100), image_flags);
- SkRect rect = SkRect::MakeXYWH(0, 0, 100, 100);
- buffer.push<DrawRectOp>(rect, PaintFlags());
- // The single save/restore call is from the PaintOpBuffer's use of
- // SkAutoRestoreCanvas.
- testing::StrictMock<MockCanvas> canvas;
- testing::Sequence s;
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(_)).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- buffer.Playback(&canvas, PlaybackParams(&image_provider));
- }
- TEST(PaintOpBufferTest, SkipsOpsWithFailedDecodes) {
- MockImageProvider image_provider(true);
- PaintOpBuffer buffer;
- PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f);
- PaintFlags image_flags;
- image_flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat,
- SkTileMode::kRepeat, nullptr));
- buffer.push<DrawRectOp>(SkRect::MakeXYWH(110, 110, 100, 100), image_flags);
- buffer.push<DrawColorOp>(SkColors::kRed, SkBlendMode::kSrcOver);
- testing::StrictMock<MockCanvas> canvas;
- testing::Sequence s;
- EXPECT_CALL(canvas, OnDrawPaintWithColor(_)).InSequence(s);
- buffer.Playback(&canvas, PlaybackParams(&image_provider));
- }
- MATCHER(NonLazyImage, "") {
- return !arg->isLazyGenerated();
- }
- MATCHER_P(MatchesPaintImage, paint_image, "") {
- return arg.paint_image() == paint_image;
- }
- MATCHER_P2(MatchesRect, rect, scale, "") {
- EXPECT_EQ(arg.x(), rect.x() * scale.width());
- EXPECT_EQ(arg.y(), rect.y() * scale.height());
- EXPECT_EQ(arg.width(), rect.width() * scale.width());
- EXPECT_EQ(arg.height(), rect.height() * scale.height());
- return true;
- }
- MATCHER_P(MatchesQuality, quality, "") {
- return quality == arg->getFilterQuality();
- }
- MATCHER_P2(MatchesShader, flags, scale, "") {
- SkMatrix matrix;
- SkTileMode xy[2];
- SkImage* image = arg.getShader()->isAImage(&matrix, xy);
- EXPECT_FALSE(image->isLazyGenerated());
- SkSize local_scale;
- matrix.decomposeScale(&local_scale, nullptr);
- EXPECT_EQ(local_scale.width(), 1.0f / scale.width());
- EXPECT_EQ(local_scale.height(), 1.0f / scale.height());
- EXPECT_EQ(flags.getShader()->tx(), xy[0]);
- EXPECT_EQ(flags.getShader()->ty(), xy[1]);
- return true;
- }
- TEST(PaintOpBufferTest, RasterPaintWorkletImageRectBasicCase) {
- sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
- PaintFlags noop_flags;
- SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 100, 100);
- paint_worklet_buffer->push<TranslateOp>(8.0f, 8.0f);
- paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
- PaintFlags draw_flags;
- draw_flags.setColor(0u);
- SkRect rect = SkRect::MakeXYWH(0, 0, 100, 100);
- paint_worklet_buffer->push<DrawRectOp>(rect, draw_flags);
- MockImageProvider provider;
- provider.SetRecord(paint_worklet_buffer);
- PaintOpBuffer blink_buffer;
- scoped_refptr<TestPaintWorkletInput> input =
- base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
- PaintImage image = CreatePaintWorkletPaintImage(input);
- SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
- SkRect dst = SkRect::MakeXYWH(0, 0, 100, 100);
- blink_buffer.push<DrawImageRectOp>(image, src, dst,
- SkCanvas::kStrict_SrcRectConstraint);
- testing::StrictMock<MockCanvas> canvas;
- testing::Sequence s;
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, didTranslate(8.0f, 8.0f));
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnDrawRectWithColor(0u));
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- blink_buffer.Playback(&canvas, PlaybackParams(&provider));
- }
- TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) {
- sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
- PaintFlags noop_flags;
- SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 10, 10);
- paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
- PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- paint_worklet_buffer->push<DrawImageOp>(
- paint_image, 0.0f, 0.0f, SkSamplingOptions(SkFilterMode::kLinear),
- nullptr);
- std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
- std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)};
- std::vector<PaintFlags::FilterQuality> quality = {
- PaintFlags::FilterQuality::kHigh};
- MockImageProvider provider(src_rect_offset, scale_adjustment, quality);
- provider.SetRecord(paint_worklet_buffer);
- PaintOpBuffer blink_buffer;
- scoped_refptr<TestPaintWorkletInput> input =
- base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
- PaintImage image = CreatePaintWorkletPaintImage(input);
- SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
- SkRect dst = SkRect::MakeXYWH(5, 7, 100, 100);
- blink_buffer.push<DrawImageRectOp>(image, src, dst,
- SkCanvas::kStrict_SrcRectConstraint);
- testing::StrictMock<MockCanvas> canvas;
- testing::Sequence s;
- SkSamplingOptions sampling({0, 1.0f / 2});
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, didConcat44(SkM44::Translate(5.0f, 7.0f)));
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(),
- 1.0f / scale_adjustment[0].height()));
- EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling, _));
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- blink_buffer.Playback(&canvas, PlaybackParams(&provider));
- }
- TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) {
- sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
- PaintFlags noop_flags;
- SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 10, 10);
- paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
- PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- paint_worklet_buffer->push<DrawImageOp>(
- paint_image, 0.0f, 0.0f, SkSamplingOptions(SkFilterMode::kLinear),
- nullptr);
- std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
- std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)};
- std::vector<PaintFlags::FilterQuality> quality = {
- PaintFlags::FilterQuality::kHigh};
- MockImageProvider provider(src_rect_offset, scale_adjustment, quality);
- provider.SetRecord(paint_worklet_buffer);
- PaintOpBuffer blink_buffer;
- scoped_refptr<TestPaintWorkletInput> input =
- base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
- PaintImage image = CreatePaintWorkletPaintImage(input);
- SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
- SkRect dst = SkRect::MakeXYWH(0, 0, 200, 150);
- blink_buffer.push<DrawImageRectOp>(image, src, dst,
- SkCanvas::kStrict_SrcRectConstraint);
- testing::StrictMock<MockCanvas> canvas;
- testing::Sequence s;
- SkSamplingOptions sampling({0, 1.0f / 2});
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, didConcat44(SkM44::Scale(2.f, 1.5f)));
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(),
- 1.0f / scale_adjustment[0].height()));
- EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling, _));
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- blink_buffer.Playback(&canvas, PlaybackParams(&provider));
- }
- TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) {
- sk_sp<PaintOpBuffer> paint_worklet_buffer = sk_make_sp<PaintOpBuffer>();
- PaintFlags noop_flags;
- SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 60, 60);
- paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags);
- SkSamplingOptions linear(SkFilterMode::kLinear);
- PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- // One rect inside the src-rect, one outside.
- paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, linear,
- nullptr);
- paint_worklet_buffer->push<DrawImageOp>(paint_image, 50.0f, 50.0f, linear,
- nullptr);
- std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
- std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)};
- std::vector<PaintFlags::FilterQuality> quality = {
- PaintFlags::FilterQuality::kHigh};
- MockImageProvider provider(src_rect_offset, scale_adjustment, quality);
- provider.SetRecord(paint_worklet_buffer);
- PaintOpBuffer blink_buffer;
- scoped_refptr<TestPaintWorkletInput> input =
- base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
- PaintImage image = CreatePaintWorkletPaintImage(input);
- SkRect src = SkRect::MakeXYWH(0, 0, 20, 20);
- SkRect dst = SkRect::MakeXYWH(0, 0, 20, 20);
- blink_buffer.push<DrawImageRectOp>(image, src, dst,
- SkCanvas::kStrict_SrcRectConstraint);
- testing::StrictMock<MockCanvas> canvas;
- testing::Sequence s;
- SkSamplingOptions sampling({0, 1.0f / 2});
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(),
- 1.0f / scale_adjustment[0].height()));
- EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling, _));
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- blink_buffer.Playback(&canvas, PlaybackParams(&provider));
- }
- TEST(PaintOpBufferTest, ReplacesImagesFromProvider) {
- std::vector<SkSize> src_rect_offset = {
- SkSize::MakeEmpty(), SkSize::Make(2.0f, 2.0f), SkSize::Make(3.0f, 3.0f)};
- std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f),
- SkSize::Make(0.3f, 0.3f),
- SkSize::Make(0.4f, 0.4f)};
- std::vector<PaintFlags::FilterQuality> quality = {
- PaintFlags::FilterQuality::kHigh, PaintFlags::FilterQuality::kMedium,
- PaintFlags::FilterQuality::kHigh};
- MockImageProvider image_provider(src_rect_offset, scale_adjustment, quality);
- PaintOpBuffer buffer;
- SkRect rect = SkRect::MakeWH(10, 10);
- SkSamplingOptions sampling(SkFilterMode::kLinear);
- PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, sampling, nullptr);
- buffer.push<DrawImageRectOp>(paint_image, rect, rect, sampling, nullptr,
- SkCanvas::kFast_SrcRectConstraint);
- PaintFlags flags;
- flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat,
- SkTileMode::kRepeat, nullptr));
- buffer.push<DrawOvalOp>(SkRect::MakeWH(10, 10), flags);
- testing::StrictMock<MockCanvas> canvas;
- testing::Sequence s;
- SkSamplingOptions sampling0({0, 1.0f / 2});
- SkSamplingOptions sampling1(SkFilterMode::kLinear, SkMipmapMode::kNearest);
- // Save/scale/image/restore from DrawImageop.
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(),
- 1.0f / scale_adjustment[0].height()));
- EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling0, _));
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- // DrawImageRectop.
- SkRect src_rect =
- rect.makeOffset(src_rect_offset[1].width(), src_rect_offset[1].height());
- EXPECT_CALL(canvas,
- onDrawImageRect2(NonLazyImage(),
- MatchesRect(src_rect, scale_adjustment[1]),
- SkRect::MakeWH(10, 10), sampling1, _,
- SkCanvas::kFast_SrcRectConstraint));
- // DrawOvalop.
- EXPECT_CALL(canvas, onDrawOval(SkRect::MakeWH(10, 10),
- MatchesShader(flags, scale_adjustment[2])));
- buffer.Playback(&canvas, PlaybackParams(&image_provider));
- }
- TEST(PaintOpBufferTest, DrawImageRectOpWithLooperNoImageProvider) {
- PaintOpBuffer buffer;
- PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- SkLayerDrawLooper::Builder sk_draw_looper_builder;
- sk_draw_looper_builder.addLayer(20.0, 20.0);
- SkLayerDrawLooper::LayerInfo info_unmodified;
- sk_draw_looper_builder.addLayerOnTop(info_unmodified);
- PaintFlags paint_flags;
- paint_flags.setLooper(sk_draw_looper_builder.detach());
- buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100),
- SkRect::MakeWH(100, 100), SkSamplingOptions(),
- &paint_flags, SkCanvas::kFast_SrcRectConstraint);
- testing::StrictMock<MockCanvas> canvas;
- EXPECT_CALL(canvas, willSave);
- EXPECT_CALL(canvas, didTranslate);
- EXPECT_CALL(canvas, willRestore);
- EXPECT_CALL(canvas, onDrawImageRect2).Times(2);
- buffer.Playback(&canvas, PlaybackParams(nullptr));
- }
- TEST(PaintOpBufferTest, DrawImageRectOpWithLooperWithImageProvider) {
- PaintOpBuffer buffer;
- PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- SkLayerDrawLooper::Builder sk_draw_looper_builder;
- sk_draw_looper_builder.addLayer(20.0, 20.0);
- SkLayerDrawLooper::LayerInfo info_unmodified;
- sk_draw_looper_builder.addLayerOnTop(info_unmodified);
- PaintFlags paint_flags;
- paint_flags.setLooper(sk_draw_looper_builder.detach());
- buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100),
- SkRect::MakeWH(100, 100), SkSamplingOptions(),
- &paint_flags, SkCanvas::kFast_SrcRectConstraint);
- testing::StrictMock<MockCanvas> canvas;
- EXPECT_CALL(canvas, willSave);
- EXPECT_CALL(canvas, didTranslate);
- EXPECT_CALL(canvas, willRestore);
- EXPECT_CALL(canvas, onDrawImageRect2).Times(2);
- std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
- std::vector<SkSize> scale_adjustment = {SkSize::Make(1.0f, 1.0f)};
- std::vector<PaintFlags::FilterQuality> quality = {
- PaintFlags::FilterQuality::kHigh};
- MockImageProvider image_provider(src_rect_offset, scale_adjustment, quality);
- buffer.Playback(&canvas, PlaybackParams(&image_provider));
- }
- TEST(PaintOpBufferTest, ReplacesImagesFromProviderOOP) {
- PaintOpBuffer buffer;
- SkSize expected_scale = SkSize::Make(0.2f, 0.5f);
- SkRect rect = SkRect::MakeWH(10, 10);
- PaintFlags flags;
- SkSamplingOptions sampling(SkFilterMode::kLinear);
- PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- buffer.push<ScaleOp>(expected_scale.width(), expected_scale.height());
- buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, sampling, nullptr);
- buffer.push<DrawImageRectOp>(paint_image, rect, rect, sampling, nullptr,
- SkCanvas::kFast_SrcRectConstraint);
- flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat,
- SkTileMode::kRepeat, nullptr));
- buffer.push<DrawOvalOp>(SkRect::MakeWH(10, 10), flags);
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(&buffer);
- ASSERT_NE(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- for (auto* op : PaintOpBuffer::Iterator(deserialized_buffer.get())) {
- testing::NiceMock<MockCanvas> canvas;
- PlaybackParams params(nullptr);
- testing::Sequence s;
- if (op->GetType() == PaintOpType::DrawImage) {
- // Save/scale/image/restore from DrawImageop.
- EXPECT_CALL(canvas, willSave()).InSequence(s);
- EXPECT_CALL(canvas, didScale(1.0f / expected_scale.width(),
- 1.0f / expected_scale.height()));
- EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, _, _));
- EXPECT_CALL(canvas, willRestore()).InSequence(s);
- op->Raster(&canvas, params);
- } else if (op->GetType() == PaintOpType::DrawImageRect) {
- EXPECT_CALL(canvas, onDrawImageRect2(NonLazyImage(),
- MatchesRect(rect, expected_scale),
- SkRect::MakeWH(10, 10), _, _,
- SkCanvas::kFast_SrcRectConstraint));
- op->Raster(&canvas, params);
- } else if (op->GetType() == PaintOpType::DrawOval) {
- EXPECT_CALL(canvas, onDrawOval(SkRect::MakeWH(10, 10),
- MatchesShader(flags, expected_scale)));
- op->Raster(&canvas, params);
- }
- }
- }
- class PaintFilterSerializationTest : public ::testing::TestWithParam<bool> {};
- INSTANTIATE_TEST_SUITE_P(PaintFilterSerializationTests,
- PaintFilterSerializationTest,
- ::testing::Values(true, false));
- TEST_P(PaintFilterSerializationTest, Basic) {
- SkScalar scalars[9] = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f};
- std::vector<sk_sp<PaintFilter>> filters = {
- sk_sp<PaintFilter>{new ColorFilterPaintFilter(
- SkColorFilters::LinearToSRGBGamma(), nullptr)},
- sk_sp<PaintFilter>{
- new BlurPaintFilter(0.5f, 0.3f, SkTileMode::kRepeat, nullptr)},
- sk_sp<PaintFilter>{new DropShadowPaintFilter(
- 5.f, 10.f, 0.1f, 0.3f, SkColors::kBlue,
- DropShadowPaintFilter::ShadowMode::kDrawShadowOnly, nullptr)},
- sk_sp<PaintFilter>{new MagnifierPaintFilter(SkRect::MakeXYWH(5, 6, 7, 8),
- 10.5f, nullptr)},
- sk_sp<PaintFilter>{new AlphaThresholdPaintFilter(
- SkRegion(SkIRect::MakeXYWH(0, 0, 100, 200)), 10.f, 20.f, nullptr)},
- sk_sp<PaintFilter>{new MatrixConvolutionPaintFilter(
- SkISize::Make(3, 3), scalars, 30.f, 123.f, SkIPoint::Make(0, 0),
- SkTileMode::kDecal, true, nullptr)},
- sk_sp<PaintFilter>{new MorphologyPaintFilter(
- MorphologyPaintFilter::MorphType::kErode, 15.5f, 30.2f, nullptr)},
- sk_sp<PaintFilter>{new OffsetPaintFilter(-1.f, -2.f, nullptr)},
- sk_sp<PaintFilter>{new TilePaintFilter(
- SkRect::MakeXYWH(1, 2, 3, 4), SkRect::MakeXYWH(4, 3, 2, 1), nullptr)},
- sk_sp<PaintFilter>{new TurbulencePaintFilter(
- TurbulencePaintFilter::TurbulenceType::kFractalNoise, 3.3f, 4.4f, 2,
- 123, nullptr)},
- sk_sp<PaintFilter>{new MatrixPaintFilter(
- SkMatrix::I(), PaintFlags::FilterQuality::kHigh, nullptr)},
- sk_sp<PaintFilter>{new LightingDistantPaintFilter(
- PaintFilter::LightingType::kSpecular, SkPoint3::Make(1, 2, 3),
- SkColors::kCyan, 1.1f, 2.2f, 3.3f, nullptr)},
- sk_sp<PaintFilter>{new LightingPointPaintFilter(
- PaintFilter::LightingType::kDiffuse, SkPoint3::Make(2, 3, 4),
- SkColors::kRed, 1.2f, 3.4f, 5.6f, nullptr)},
- sk_sp<PaintFilter>{new LightingSpotPaintFilter(
- PaintFilter::LightingType::kSpecular, SkPoint3::Make(100, 200, 300),
- SkPoint3::Make(400, 500, 600), 1, 2, SkColors::kMagenta, 3, 4, 5,
- nullptr)},
- sk_sp<PaintFilter>{
- new ImagePaintFilter(CreateDiscardablePaintImage(gfx::Size(100, 100)),
- SkRect::MakeWH(50, 50), SkRect::MakeWH(70, 70),
- PaintFlags::FilterQuality::kMedium)}};
- filters.emplace_back(new ComposePaintFilter(filters[0], filters[1]));
- filters.emplace_back(
- new XfermodePaintFilter(SkBlendMode::kDst, filters[2], filters[3]));
- filters.emplace_back(new ArithmeticPaintFilter(1.1f, 2.2f, 3.3f, 4.4f, false,
- filters[4], filters[5]));
- filters.emplace_back(new DisplacementMapEffectPaintFilter(
- SkColorChannel::kR, SkColorChannel::kG, 10, filters[6], filters[7]));
- filters.emplace_back(new MergePaintFilter(filters.data(), filters.size()));
- filters.emplace_back(new RecordPaintFilter(
- sk_sp<PaintRecord>{new PaintRecord}, SkRect::MakeXYWH(10, 15, 20, 25)));
- // Use a non-identity ctm to confirm that RecordPaintFilters are converted
- // from raster-at-scale to fixed scale properly.
- float scale_x = 2.f;
- float scale_y = 3.f;
- SkM44 ctm = SkM44::Scale(scale_x, scale_y);
- TestOptionsProvider options_provider;
- for (size_t i = 0; i < filters.size(); ++i) {
- SCOPED_TRACE(i);
- auto& filter = filters[i];
- std::vector<uint8_t> memory;
- size_t buffer_size = filter->type() == PaintFilter::Type::kPaintRecord
- ? PaintOpBuffer::kInitialBufferSize
- : PaintFilter::GetFilterSize(filter.get());
- buffer_size += PaintOpWriter::HeaderBytes();
- memory.resize(buffer_size);
- PaintOpWriter writer(memory.data(), memory.size(),
- options_provider.serialize_options(), GetParam());
- writer.Write(filter.get(), ctm);
- ASSERT_GT(writer.size(), 0u) << PaintFilter::TypeToString(filter->type());
- sk_sp<PaintFilter> deserialized_filter;
- PaintOpReader reader(memory.data(), writer.size(),
- options_provider.deserialize_options(), GetParam());
- reader.Read(&deserialized_filter);
- ASSERT_TRUE(deserialized_filter);
- if (filter->type() == PaintFilter::Type::kPaintRecord) {
- // The filter's scaling behavior should be converted to kFixedScale so
- // they are no longer equal.
- ASSERT_EQ(deserialized_filter->type(), PaintFilter::Type::kPaintRecord);
- const RecordPaintFilter& expected =
- static_cast<const RecordPaintFilter&>(*filter);
- const RecordPaintFilter& actual =
- static_cast<const RecordPaintFilter&>(*deserialized_filter);
- EXPECT_EQ(actual.scaling_behavior(),
- RecordPaintFilter::ScalingBehavior::kFixedScale);
- SkRect expected_bounds =
- SkRect::MakeXYWH(scale_x * expected.record_bounds().x(),
- scale_y * expected.record_bounds().y(),
- scale_x * expected.record_bounds().width(),
- scale_y * expected.record_bounds().height());
- EXPECT_EQ(actual.record_bounds(), expected_bounds);
- EXPECT_EQ(actual.raster_scale().width(), scale_x);
- EXPECT_EQ(actual.raster_scale().height(), scale_y);
- // And the first op in the deserialized filter's record should be a
- // ScaleOp containing the extracted scale factors (if there's no
- // security constraints that disable record serialization)
- if (!GetParam()) {
- const ScaleOp* scale = actual.record()->GetOpAtForTesting<ScaleOp>(0);
- ASSERT_TRUE(scale);
- EXPECT_EQ(scale->sx, scale_x);
- EXPECT_EQ(scale->sy, scale_y);
- }
- } else {
- EXPECT_TRUE(*filter == *deserialized_filter);
- }
- }
- }
- TEST(PaintOpBufferTest, PaintRecordShaderSerialization) {
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- sk_sp<PaintOpBuffer> record_buffer(new PaintOpBuffer);
- record_buffer->push<DrawRectOp>(SkRect::MakeXYWH(0, 0, 1, 1), PaintFlags());
- TestOptionsProvider options_provider;
- PaintFlags flags;
- flags.setShader(PaintShader::MakePaintRecord(
- record_buffer, SkRect::MakeWH(10, 10), SkTileMode::kClamp,
- SkTileMode::kRepeat, nullptr));
- PaintOpBuffer buffer;
- buffer.push<DrawRectOp>(SkRect::MakeXYWH(1, 2, 3, 4), flags);
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(&buffer);
- ASSERT_TRUE(serializer.valid());
- ASSERT_GT(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- PaintOpBuffer::Iterator it(deserialized_buffer.get());
- ASSERT_TRUE(it);
- auto* op = *it;
- ASSERT_TRUE(op->GetType() == PaintOpType::DrawRect);
- auto* rect_op = static_cast<DrawRectOp*>(op);
- EXPECT_SKRECT_EQ(rect_op->rect, SkRect::MakeXYWH(1, 2, 3, 4));
- EXPECT_TRUE(rect_op->flags == flags);
- EXPECT_TRUE(*rect_op->flags.getShader() == *flags.getShader());
- }
- #if BUILDFLAG(SKIA_SUPPORT_SKOTTIE)
- TEST(PaintOpBufferTest, BoundingRect_DrawSkottieOp) {
- PaintOpBuffer buffer;
- PushDrawSkottieOps(&buffer);
- SkRect rect;
- for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
- auto* op = static_cast<DrawSkottieOp*>(base_op);
- ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
- EXPECT_EQ(rect, op->dst.makeSorted());
- }
- }
- // Skottie-specific deserialization failure case.
- TEST(PaintOpBufferTest,
- DrawSkottieOpSerializationFailureFromUnPrivilegedProcess) {
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- scoped_refptr<SkottieWrapper> skottie =
- CreateSkottie(gfx::Size(100, 100), /*duration_secs=*/1);
- ASSERT_TRUE(skottie->is_valid());
- const SkRect input_rect = SkRect::MakeIWH(400, 300);
- const float input_t = 0.4f;
- PaintOpBuffer buffer;
- buffer.push<DrawSkottieOp>(
- skottie, input_rect, input_t,
- GetTestImagesForSkottie(*skottie, input_rect,
- PaintFlags::FilterQuality::kHigh, input_t),
- SkottieColorMap(), SkottieTextPropertyValueMap());
- // Serialize
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(&buffer);
- ASSERT_TRUE(serializer.valid());
- ASSERT_GT(serializer.written(), 0u);
- // De-Serialize
- PaintOp::DeserializeOptions d_options(options_provider.deserialize_options());
- // Deserialization should fail on a non privileged process.
- d_options.is_privileged = false;
- auto deserialized_buffer = PaintOpBuffer::MakeFromMemory(
- memory.get(), serializer.written(), d_options);
- ASSERT_FALSE(deserialized_buffer);
- }
- TEST(PaintOpBufferTest, DrawSkottieOpRasterWithoutImageAssets) {
- scoped_refptr<SkottieWrapper> skottie =
- CreateSkottie(gfx::Size(100, 100), /*duration_secs=*/5);
- SkRect skottie_rect = SkRect::MakeWH(100, 100);
- DrawSkottieOp skottie_op(skottie, skottie_rect, /*t=*/0.1,
- /*images=*/SkottieFrameDataMap(), SkottieColorMap(),
- SkottieTextPropertyValueMap());
- PlaybackParams playback_params(/*image_provider=*/nullptr);
- {
- NiceMock<MockCanvas> canvas;
- EXPECT_CALL(canvas, onDrawImage2(_, _, _, _, _)).Times(0);
- DrawSkottieOp::Raster(&skottie_op, &canvas, playback_params);
- }
- }
- TEST(PaintOpBufferTest, DrawSkottieOpRasterWithNullImages) {
- scoped_refptr<SkottieWrapper> skottie =
- CreateSkottieFromString(kLottieDataWith2Assets);
- SkRect skottie_rect = SkRect::MakeWH(100, 100);
- SkottieFrameDataMap images_in = GetNullImagesForSkottie(*skottie, /*t=*/0.1f);
- ASSERT_FALSE(images_in.empty());
- DrawSkottieOp skottie_op(skottie, skottie_rect, /*t=*/0.1, images_in,
- SkottieColorMap(), SkottieTextPropertyValueMap());
- PlaybackParams playback_params(/*image_provider=*/nullptr);
- {
- NiceMock<MockCanvas> canvas;
- EXPECT_CALL(canvas, onDrawImage2(_, _, _, _, _)).Times(0);
- DrawSkottieOp::Raster(&skottie_op, &canvas, playback_params);
- }
- }
- TEST(PaintOpBufferTest, DrawSkottieOpRasterWithoutImageProvider) {
- scoped_refptr<SkottieWrapper> skottie =
- CreateSkottieFromString(kLottieDataWith2Assets);
- SkRect skottie_rect = SkRect::MakeWH(100, 100);
- SkottieFrameDataMap images_in = GetTestImagesForSkottie(
- *skottie, skottie_rect, PaintFlags::FilterQuality::kHigh, /*t=*/0.1f);
- ASSERT_FALSE(images_in.empty());
- DrawSkottieOp skottie_op(skottie, skottie_rect, /*t=*/0.1, images_in,
- SkottieColorMap(), SkottieTextPropertyValueMap());
- PlaybackParams playback_params(/*image_provider=*/nullptr);
- {
- NiceMock<MockCanvas> canvas;
- // Do not over-assert. Ultimately it is up to Skottie's implementation how
- // many "draw image" calls are made, and what the arguments are. But it's
- // fair to say that it has to make at least one "draw image" call for a
- // frame in the animation that renders one of the assets.
- EXPECT_CALL(canvas, onDrawImage2(NotNull(), _, _, _, _)).Times(AtLeast(1));
- DrawSkottieOp::Raster(&skottie_op, &canvas, playback_params);
- }
- }
- TEST(PaintOpBufferTest, DrawSkottieOpRasterWithImageProvider) {
- scoped_refptr<SkottieWrapper> skottie =
- CreateSkottieFromString(kLottieDataWith2Assets);
- SkRect skottie_rect = SkRect::MakeWH(100, 100);
- std::vector<SkSize> src_rect_offset = {SkSize::Make(2.0f, 2.0f),
- SkSize::Make(3.0f, 3.0f)};
- std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f),
- SkSize::Make(0.3f, 0.3f)};
- std::vector<PaintFlags::FilterQuality> quality = {
- PaintFlags::FilterQuality::kHigh, PaintFlags::FilterQuality::kMedium};
- MockImageProvider image_provider(src_rect_offset, scale_adjustment, quality);
- PlaybackParams playback_params(&image_provider);
- ASSERT_TRUE(image_provider.decoded_images().empty());
- {
- SkottieFrameDataMap images_in = GetTestImagesForSkottie(
- *skottie, skottie_rect, PaintFlags::FilterQuality::kHigh, /*t=*/0.25f);
- ASSERT_THAT(images_in, Contains(Key(HashSkottieResourceId("image_0"))));
- DrawSkottieOp skottie_op(skottie, skottie_rect, /*t=*/0.25, images_in,
- SkottieColorMap(), SkottieTextPropertyValueMap());
- NiceMock<MockCanvas> canvas;
- EXPECT_CALL(canvas, onDrawImage2(NotNull(), _, _, _, _)).Times(AtLeast(1));
- DrawSkottieOp::Raster(&skottie_op, &canvas, playback_params);
- ASSERT_EQ(image_provider.decoded_images().size(), 1u);
- EXPECT_THAT(image_provider.decoded_images(),
- Contains(MatchesPaintImage(
- images_in.at(HashSkottieResourceId("image_0")).image)));
- }
- {
- SkottieFrameDataMap images_in = GetTestImagesForSkottie(
- *skottie, skottie_rect, PaintFlags::FilterQuality::kHigh, /*t=*/0.75f);
- ASSERT_THAT(images_in, Contains(Key(HashSkottieResourceId("image_1"))));
- DrawSkottieOp skottie_op(skottie, skottie_rect, /*t=*/0.75, images_in,
- SkottieColorMap(), SkottieTextPropertyValueMap());
- NiceMock<MockCanvas> canvas;
- EXPECT_CALL(canvas, onDrawImage2(NotNull(), _, _, _, _)).Times(AtLeast(1));
- DrawSkottieOp::Raster(&skottie_op, &canvas, playback_params);
- ASSERT_EQ(image_provider.decoded_images().size(), 2u);
- EXPECT_THAT(image_provider.decoded_images(),
- Contains(MatchesPaintImage(
- images_in.at(HashSkottieResourceId("image_1")).image)));
- }
- }
- TEST(PaintOpBufferTest, DiscardableImagesTrackingSkottieOpNoImages) {
- PaintOpBuffer buffer;
- buffer.push<DrawSkottieOp>(
- CreateSkottie(gfx::Size(100, 100), /*duration_secs=*/1),
- /*dst=*/SkRect::MakeWH(100, 100), /*t=*/0.1f, SkottieFrameDataMap(),
- SkottieColorMap(), SkottieTextPropertyValueMap());
- EXPECT_FALSE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, DiscardableImagesTrackingSkottieOpWithImages) {
- PaintOpBuffer buffer;
- scoped_refptr<SkottieWrapper> skottie =
- CreateSkottieFromString(kLottieDataWith2Assets);
- SkRect skottie_rect = SkRect::MakeWH(100, 100);
- SkottieFrameDataMap images_in = GetTestImagesForSkottie(
- *skottie, skottie_rect, PaintFlags::FilterQuality::kHigh, /*t=*/0.1f);
- ASSERT_FALSE(images_in.empty());
- buffer.push<DrawSkottieOp>(skottie, skottie_rect, /*t=*/0.1f, images_in,
- SkottieColorMap(), SkottieTextPropertyValueMap());
- EXPECT_TRUE(buffer.HasDiscardableImages());
- }
- TEST(PaintOpBufferTest, OpHasDiscardableImagesSkottieOpNoImages) {
- DrawSkottieOp op(CreateSkottie(gfx::Size(100, 100), /*duration_secs=*/1),
- /*dst=*/SkRect::MakeWH(100, 100), /*t=*/0.1f,
- SkottieFrameDataMap(), SkottieColorMap(),
- SkottieTextPropertyValueMap());
- EXPECT_FALSE(PaintOp::OpHasDiscardableImages(&op));
- }
- TEST(PaintOpBufferTest, OpHasDiscardableImagesSkottieOpWithImages) {
- scoped_refptr<SkottieWrapper> skottie =
- CreateSkottieFromString(kLottieDataWith2Assets);
- SkRect skottie_rect = SkRect::MakeWH(100, 100);
- SkottieFrameDataMap images_in = GetTestImagesForSkottie(
- *skottie, skottie_rect, PaintFlags::FilterQuality::kHigh, /*t=*/0.1f);
- ASSERT_FALSE(images_in.empty());
- DrawSkottieOp op(skottie, skottie_rect, /*t=*/0.1f, images_in,
- SkottieColorMap(), SkottieTextPropertyValueMap());
- EXPECT_TRUE(PaintOp::OpHasDiscardableImages(&op));
- }
- #endif // BUILDFLAG(SKIA_SUPPORT_SKOTTIE)
- TEST(PaintOpBufferTest, CustomData) {
- // Basic tests: size, move, comparison.
- {
- PaintOpBuffer buffer;
- EXPECT_EQ(buffer.size(), 0u);
- EXPECT_EQ(buffer.bytes_used(), sizeof(PaintOpBuffer));
- buffer.push<CustomDataOp>(1234u);
- EXPECT_EQ(buffer.size(), 1u);
- EXPECT_GT(buffer.bytes_used(),
- sizeof(PaintOpBuffer) + sizeof(CustomDataOp));
- PaintOpBuffer new_buffer = std::move(buffer);
- EXPECT_EQ(buffer.size(), 0u);
- EXPECT_EQ(new_buffer.size(), 1u);
- EXPECT_EQ(new_buffer.GetFirstOp()->GetType(), PaintOpType::CustomData);
- PaintOpBuffer buffer2;
- buffer2.push<CustomDataOp>(1234u);
- EXPECT_TRUE(*new_buffer.GetFirstOp() == *buffer2.GetFirstOp());
- }
- // Push and verify.
- {
- PaintOpBuffer buffer;
- buffer.push<SaveOp>();
- buffer.push<CustomDataOp>(0xFFFFFFFF);
- buffer.push<RestoreOp>();
- EXPECT_EQ(buffer.size(), 3u);
- PaintOpBuffer::Iterator iter(&buffer);
- ASSERT_EQ(iter->GetType(), PaintOpType::Save);
- ++iter;
- ASSERT_EQ(iter->GetType(), PaintOpType::CustomData);
- ++iter;
- ASSERT_EQ(iter->GetType(), PaintOpType::Restore);
- ++iter;
- }
- // Playback.
- {
- PaintOpBuffer buffer;
- buffer.push<CustomDataOp>(9999u);
- testing::StrictMock<MockCanvas> canvas;
- EXPECT_CALL(canvas, onCustomCallback(&canvas, 9999)).Times(1);
- buffer.Playback(&canvas, PlaybackParams(nullptr, SkM44(),
- base::BindRepeating(
- &MockCanvas::onCustomCallback,
- base::Unretained(&canvas))));
- }
- }
- TEST(PaintOpBufferTest, SecurityConstrainedImageSerialization) {
- auto image = CreateDiscardablePaintImage(gfx::Size(10, 10));
- sk_sp<PaintFilter> filter = sk_make_sp<ImagePaintFilter>(
- image, SkRect::MakeWH(10, 10), SkRect::MakeWH(10, 10),
- PaintFlags::FilterQuality::kLow);
- const bool enable_security_constraints = true;
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- PaintOpWriter writer(memory.get(), PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options(),
- enable_security_constraints);
- writer.Write(filter.get(), SkM44());
- sk_sp<PaintFilter> out_filter;
- PaintOpReader reader(memory.get(), writer.size(),
- options_provider.deserialize_options(),
- enable_security_constraints);
- reader.Read(&out_filter);
- EXPECT_TRUE(*filter == *out_filter);
- }
- TEST(PaintOpBufferTest, DrawImageRectSerializeScaledImages) {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- // scales: x dimension = x0.25, y dimension = x5
- // translations here are arbitrary
- SkRect src = SkRect::MakeXYWH(3, 4, 20, 6);
- SkRect dst = SkRect::MakeXYWH(20, 38, 5, 30);
- // Adjust transform matrix so that order of operations for src->dst is
- // confirmed to be applied before the canvas's transform.
- buffer->push<TranslateOp>(.5f * dst.centerX(), 2.f * dst.centerY());
- buffer->push<RotateOp>(90.f);
- buffer->push<TranslateOp>(-.5f * dst.centerX(), -2.f * dst.centerY());
- buffer->push<ScaleOp>(0.5f, 2.0f);
- buffer->push<DrawImageRectOp>(CreateDiscardablePaintImage(gfx::Size(32, 16)),
- src, dst, SkCanvas::kStrict_SrcRectConstraint);
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(buffer.get());
- ASSERT_EQ(options_provider.decoded_images().size(), 1u);
- auto scale = options_provider.decoded_images().at(0).scale();
- EXPECT_EQ(scale.width(), 0.5f * 0.25f);
- EXPECT_EQ(scale.height(), 2.0f * 5.0f);
- }
- TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) {
- auto record_buffer = sk_make_sp<PaintOpBuffer>();
- record_buffer->push<DrawImageOp>(
- CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f);
- auto shader = PaintShader::MakePaintRecord(
- record_buffer, SkRect::MakeWH(10.f, 10.f), SkTileMode::kRepeat,
- SkTileMode::kRepeat, nullptr);
- shader->set_has_animated_images(true);
- auto buffer = sk_make_sp<PaintOpBuffer>();
- buffer->push<ScaleOp>(0.5f, 0.8f);
- PaintFlags flags;
- flags.setShader(shader);
- buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(buffer.get());
- ASSERT_EQ(options_provider.decoded_images().size(), 1u);
- auto scale = options_provider.decoded_images().at(0).scale();
- EXPECT_EQ(scale.width(), 0.5f);
- EXPECT_EQ(scale.height(), 0.8f);
- }
- TEST(PaintOpBufferTest, RecordShadersCached) {
- auto record_buffer = sk_make_sp<PaintOpBuffer>();
- record_buffer->push<DrawImageOp>(
- CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f);
- auto shader = PaintShader::MakePaintRecord(
- record_buffer, SkRect::MakeWH(10.f, 10.f), SkTileMode::kRepeat,
- SkTileMode::kRepeat, nullptr);
- shader->set_has_animated_images(false);
- auto shader_id = shader->paint_record_shader_id();
- TestOptionsProvider options_provider;
- auto* transfer_cache = options_provider.transfer_cache_helper();
- // Generate serialized |memory|.
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- size_t memory_written = 0;
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- PaintFlags flags;
- flags.setShader(shader);
- buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(buffer.get());
- memory_written = serializer.written();
- }
- // Generate serialized |memory_scaled|, which is the same pob, but with
- // a scale factor.
- std::unique_ptr<char, base::AlignedFreeDeleter> memory_scaled(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- size_t memory_scaled_written = 0;
- {
- auto buffer = sk_make_sp<PaintOpBuffer>();
- PaintFlags flags;
- flags.setShader(shader);
- // This buffer has an additional scale op.
- buffer->push<ScaleOp>(2.0f, 3.7f);
- buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
- SimpleBufferSerializer serializer(memory_scaled.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(buffer.get());
- memory_scaled_written = serializer.written();
- }
- // Hold onto records so PaintShader pointer comparisons are valid.
- sk_sp<PaintRecord> records[5];
- SkPicture* last_shader = nullptr;
- std::vector<uint8_t> scratch_buffer;
- PaintOp::DeserializeOptions deserialize_options(
- transfer_cache, options_provider.service_paint_cache(),
- options_provider.strike_client(), &scratch_buffer, true, nullptr);
- // Several deserialization test cases:
- // (0) deserialize once, verify cached is the same as deserialized version
- // (1) deserialize again, verify shader gets reused
- // (2) change scale, verify shader is new
- // (3) sanity check, same new scale + same new colorspace, shader is reused.
- for (size_t i = 0; i < 4; ++i) {
- if (i < 2) {
- records[i] = PaintOpBuffer::MakeFromMemory(memory.get(), memory_written,
- deserialize_options);
- } else {
- records[i] = PaintOpBuffer::MakeFromMemory(
- memory_scaled.get(), memory_scaled_written, deserialize_options);
- }
- auto* entry =
- transfer_cache->GetEntryAs<ServiceShaderTransferCacheEntry>(shader_id);
- ASSERT_TRUE(entry);
- if (i < 2)
- EXPECT_EQ(records[i]->size(), 1u);
- else
- EXPECT_EQ(records[i]->size(), 2u);
- for (auto* base_op : PaintOpBuffer::Iterator(records[i].get())) {
- if (base_op->GetType() != PaintOpType::DrawRect)
- continue;
- auto* op = static_cast<const DrawRectOp*>(base_op);
- // In every case, the shader in the op should get cached for future
- // use.
- auto* op_skshader = op->flags.getShader()->sk_cached_picture_.get();
- EXPECT_EQ(op_skshader, entry->shader()->sk_cached_picture_.get());
- switch (i) {
- case 0:
- // Nothing to check.
- break;
- case 1:
- EXPECT_EQ(op_skshader, last_shader);
- break;
- case 2:
- EXPECT_NE(op_skshader, last_shader);
- break;
- case 3:
- EXPECT_EQ(op_skshader, last_shader);
- break;
- }
- last_shader = op_skshader;
- }
- }
- }
- TEST(PaintOpBufferTest, RecordShadersCachedSize) {
- auto record_buffer = sk_make_sp<PaintOpBuffer>();
- size_t estimated_image_size = 30 * 30 * 4;
- auto image = CreateBitmapImage(gfx::Size(30, 30));
- record_buffer->push<DrawImageOp>(image, 0.f, 0.f);
- auto shader = PaintShader::MakePaintRecord(
- record_buffer, SkRect::MakeWH(10.f, 10.f), SkTileMode::kRepeat,
- SkTileMode::kRepeat, nullptr);
- shader->set_has_animated_images(false);
- auto shader_id = shader->paint_record_shader_id();
- TestOptionsProvider options_provider;
- auto* transfer_cache = options_provider.transfer_cache_helper();
- // Generate serialized |memory|.
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- auto buffer = sk_make_sp<PaintOpBuffer>();
- PaintFlags flags;
- flags.setShader(shader);
- buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- options_provider.context_supports_distance_field_text();
- serializer.Serialize(buffer.get());
- std::vector<uint8_t> scratch_buffer;
- PaintOp::DeserializeOptions deserialize_options(
- transfer_cache, options_provider.service_paint_cache(),
- options_provider.strike_client(), &scratch_buffer, true, nullptr);
- auto record = PaintOpBuffer::MakeFromMemory(
- memory.get(), serializer.written(), deserialize_options);
- auto* shader_entry =
- transfer_cache->GetEntryAs<ServiceShaderTransferCacheEntry>(shader_id);
- ASSERT_TRUE(shader_entry);
- // The size of the shader in the cache should be bigger than both the record
- // and the image. Exact numbers not used here to not overfit this test.
- size_t shader_size = shader_entry->CachedSize();
- EXPECT_GT(estimated_image_size, serializer.written());
- EXPECT_GT(shader_size, estimated_image_size);
- }
- TEST(PaintOpBufferTest, RecordFilterSerializeScaledImages) {
- auto record_buffer = sk_make_sp<PaintOpBuffer>();
- record_buffer->push<DrawImageOp>(
- CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f);
- auto filter =
- sk_make_sp<RecordPaintFilter>(record_buffer, SkRect::MakeWH(10.f, 10.f));
- auto buffer = sk_make_sp<PaintOpBuffer>();
- buffer->push<ScaleOp>(0.5f, 0.8f);
- PaintFlags flags;
- flags.setImageFilter(filter);
- buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(buffer.get());
- ASSERT_EQ(options_provider.decoded_images().size(), 1u);
- auto scale = options_provider.decoded_images().at(0).scale();
- EXPECT_EQ(scale.width(), 0.5f);
- EXPECT_EQ(scale.height(), 0.8f);
- }
- TEST(PaintOpBufferTest, TotalOpCount) {
- auto record_buffer = sk_make_sp<PaintOpBuffer>();
- auto sub_record_buffer = sk_make_sp<PaintOpBuffer>();
- auto sub_sub_record_buffer = sk_make_sp<PaintOpBuffer>();
- PushDrawRectOps(sub_sub_record_buffer.get());
- PushDrawRectOps(sub_record_buffer.get());
- PushDrawRectOps(record_buffer.get());
- sub_record_buffer->push<DrawRecordOp>(sub_sub_record_buffer);
- record_buffer->push<DrawRecordOp>(sub_record_buffer);
- size_t len = std::min(test_rects.size(), test_flags.size());
- EXPECT_EQ(len, sub_sub_record_buffer->total_op_count());
- EXPECT_EQ(2 * len + 1, sub_record_buffer->total_op_count());
- EXPECT_EQ(3 * len + 2, record_buffer->total_op_count());
- }
- TEST(PaintOpBufferTest, NullImages) {
- PaintOpBuffer buffer;
- buffer.push<DrawImageOp>(PaintImage(), 0.f, 0.f);
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- TestOptionsProvider options_provider;
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(&buffer);
- ASSERT_TRUE(serializer.valid());
- ASSERT_GT(serializer.written(), 0u);
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- ASSERT_EQ(deserialized_buffer->size(), 1u);
- ASSERT_EQ(deserialized_buffer->GetFirstOp()->GetType(),
- PaintOpType::DrawImage);
- }
- TEST(PaintOpBufferTest, HasDrawOpsAndHasDrawTextOps) {
- auto buffer1 = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer1->has_draw_ops());
- EXPECT_FALSE(buffer1->has_draw_text_ops());
- buffer1->push<DrawRectOp>(SkRect::MakeWH(3, 4), PaintFlags());
- PushDrawRectOps(buffer1.get());
- EXPECT_TRUE(buffer1->has_draw_ops());
- EXPECT_FALSE(buffer1->has_draw_text_ops());
- auto buffer2 = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer2->has_draw_ops());
- EXPECT_FALSE(buffer2->has_draw_text_ops());
- buffer2->push<DrawRecordOp>(std::move(buffer1));
- EXPECT_TRUE(buffer2->has_draw_ops());
- EXPECT_FALSE(buffer2->has_draw_text_ops());
- buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()),
- 0.0f, 0.0f, PaintFlags());
- EXPECT_TRUE(buffer2->has_draw_ops());
- EXPECT_TRUE(buffer2->has_draw_text_ops());
- buffer2->push<DrawRectOp>(SkRect::MakeWH(4, 5), PaintFlags());
- EXPECT_TRUE(buffer2->has_draw_ops());
- EXPECT_TRUE(buffer2->has_draw_text_ops());
- auto buffer3 = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer3->has_draw_text_ops());
- EXPECT_FALSE(buffer3->has_draw_ops());
- buffer3->push<DrawRecordOp>(std::move(buffer2));
- EXPECT_TRUE(buffer3->has_draw_ops());
- EXPECT_TRUE(buffer3->has_draw_text_ops());
- }
- TEST(PaintOpBufferTest, HasEffectsPreventingLCDTextForSaveLayerAlpha) {
- auto buffer1 = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
- buffer1->push<DrawRectOp>(SkRect::MakeWH(3, 4), PaintFlags());
- EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
- auto buffer2 = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
- buffer2->push<DrawRecordOp>(std::move(buffer1));
- EXPECT_FALSE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
- buffer2->push<SaveLayerOp>(nullptr, nullptr);
- EXPECT_TRUE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
- buffer2->push<DrawRectOp>(SkRect::MakeWH(4, 5), PaintFlags());
- EXPECT_TRUE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
- auto buffer3 = sk_make_sp<PaintOpBuffer>();
- EXPECT_FALSE(buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
- buffer3->push<DrawRecordOp>(std::move(buffer2));
- EXPECT_TRUE(buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
- }
- TEST(PaintOpBufferTest, NeedsAdditionalInvalidationForLCDText) {
- auto buffer1 = sk_make_sp<PaintOpBuffer>();
- buffer1->push<SaveLayerAlphaOp>(nullptr, 0.4f);
- EXPECT_FALSE(buffer1->has_draw_text_ops());
- EXPECT_TRUE(buffer1->has_save_layer_alpha_ops());
- EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
- auto buffer2 = sk_make_sp<PaintOpBuffer>();
- buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()),
- 0.0f, 0.0f, PaintFlags());
- buffer2->push<SaveLayerOp>(nullptr, nullptr);
- EXPECT_TRUE(buffer2->has_draw_ops());
- EXPECT_FALSE(buffer2->has_save_layer_alpha_ops());
- EXPECT_TRUE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
- // Neither buffer has effects preventing lcd text for SaveLayerAlpha.
- EXPECT_FALSE(buffer1->NeedsAdditionalInvalidationForLCDText(*buffer2));
- EXPECT_FALSE(buffer2->NeedsAdditionalInvalidationForLCDText(*buffer1));
- {
- auto buffer3 = sk_make_sp<PaintOpBuffer>();
- buffer3->push<DrawRecordOp>(buffer2);
- EXPECT_TRUE(
- buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
- // Neither buffer has both DrawText and SaveLayerAlpha.
- EXPECT_FALSE(buffer1->NeedsAdditionalInvalidationForLCDText(*buffer3));
- EXPECT_FALSE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer1));
- EXPECT_FALSE(buffer2->NeedsAdditionalInvalidationForLCDText(*buffer3));
- EXPECT_FALSE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer2));
- }
- {
- buffer1->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()),
- 0.0f, 0.0f, PaintFlags());
- EXPECT_TRUE(buffer1->has_draw_text_ops());
- EXPECT_TRUE(buffer1->has_save_layer_alpha_ops());
- EXPECT_FALSE(
- buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
- auto buffer3 = sk_make_sp<PaintOpBuffer>();
- buffer3->push<DrawRecordOp>(buffer1);
- buffer3->push<DrawRecordOp>(buffer2);
- EXPECT_TRUE(buffer3->has_draw_text_ops());
- EXPECT_TRUE(buffer3->has_save_layer_alpha_ops());
- EXPECT_TRUE(
- buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
- // Both have DrawText and SaveLayerAlpha, and have different
- // has_effects_preventing_lcd_text_for_save_layer_alpha().
- EXPECT_TRUE(buffer1->NeedsAdditionalInvalidationForLCDText(*buffer3));
- EXPECT_TRUE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer1));
- EXPECT_FALSE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer3));
- }
- }
- // A regression test for crbug.com/1195276. Ensure that PlaybackParams works
- // with SetMatrix operations.
- TEST(PaintOpBufferTest, SetMatrixOpWithNonIdentityPlaybackParams) {
- for (const auto& original_ctm : test_matrices) {
- for (const auto& matrix : test_matrices) {
- SkCanvas device(0, 0);
- SkCanvas* canvas = &device;
- PlaybackParams params(nullptr, original_ctm);
- SetMatrixOp op(matrix);
- SetMatrixOp::Raster(&op, canvas, params);
- EXPECT_TRUE(AnnotateOp::AreSkM44sEqual(canvas->getLocalToDevice(),
- SkM44(original_ctm, matrix)));
- }
- }
- }
- TEST(PaintOpBufferTest, PathCaching) {
- SkPath path;
- PaintFlags flags;
- // Grow path large enough to trigger caching
- path.moveTo(0, 0);
- for (int x = 1; x < 100; ++x)
- path.lineTo(x, x % 1);
- TestOptionsProvider options_provider;
- std::unique_ptr<char, base::AlignedFreeDeleter> memory(
- static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
- PaintOpBuffer::PaintOpAlign)));
- auto buffer = sk_make_sp<PaintOpBuffer>();
- buffer->push<DrawPathOp>(path, flags, UsePaintCache::kEnabled);
- SimpleBufferSerializer serializer(memory.get(),
- PaintOpBuffer::kInitialBufferSize,
- options_provider.serialize_options());
- serializer.Serialize(buffer.get());
- EXPECT_TRUE(options_provider.client_paint_cache()->Get(
- PaintCacheDataType::kPath, path.getGenerationID()));
- auto deserialized_buffer =
- PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
- options_provider.deserialize_options());
- ASSERT_TRUE(deserialized_buffer);
- ASSERT_EQ(deserialized_buffer->size(), 1u);
- ASSERT_EQ(deserialized_buffer->GetFirstOp()->GetType(),
- PaintOpType::DrawPath);
- SkPath cached_path;
- EXPECT_TRUE(options_provider.service_paint_cache()->GetPath(
- path.getGenerationID(), &cached_path));
- }
- } // namespace cc
|