1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "tools/debugger/DebugCanvas.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/core/SkPaint.h" 11cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 12cb93a386Sopenharmony_ci#include "include/core/SkPicture.h" 13cb93a386Sopenharmony_ci#include "include/core/SkPoint.h" 14cb93a386Sopenharmony_ci#include "include/core/SkTextBlob.h" 15cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h" 16cb93a386Sopenharmony_ci#include "include/utils/SkPaintFilterCanvas.h" 17cb93a386Sopenharmony_ci#include "src/core/SkCanvasPriv.h" 18cb93a386Sopenharmony_ci#include "src/core/SkRectPriv.h" 19cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h" 20cb93a386Sopenharmony_ci#include "src/utils/SkJSONWriter.h" 21cb93a386Sopenharmony_ci#include "tools/debugger/DebugLayerManager.h" 22cb93a386Sopenharmony_ci#include "tools/debugger/DrawCommand.h" 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci#include <string> 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci#if SK_GPU_V1 27cb93a386Sopenharmony_ci#include "src/gpu/GrAuditTrail.h" 28cb93a386Sopenharmony_ci#endif 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_ci#define SKDEBUGCANVAS_VERSION 1 31cb93a386Sopenharmony_ci#define SKDEBUGCANVAS_ATTRIBUTE_VERSION "version" 32cb93a386Sopenharmony_ci#define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS "commands" 33cb93a386Sopenharmony_ci#define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL "auditTrail" 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_cinamespace { 36cb93a386Sopenharmony_ci // Constants used in Annotations by Android for keeping track of layers 37cb93a386Sopenharmony_ci static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw"; 38cb93a386Sopenharmony_ci static constexpr char kSurfaceID[] = "SurfaceID"; 39cb93a386Sopenharmony_ci static constexpr char kAndroidClip[] = "AndroidDeviceClipRestriction"; 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci static SkPath arrowHead = SkPath::Polygon({ 42cb93a386Sopenharmony_ci { 0, 0}, 43cb93a386Sopenharmony_ci { 6, -15}, 44cb93a386Sopenharmony_ci { 0, -12}, 45cb93a386Sopenharmony_ci {-6, -15}, 46cb93a386Sopenharmony_ci }, true); 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci void drawArrow(SkCanvas* canvas, const SkPoint& a, const SkPoint& b, const SkPaint& paint) { 49cb93a386Sopenharmony_ci canvas->translate(0.5, 0.5); 50cb93a386Sopenharmony_ci canvas->drawLine(a, b, paint); 51cb93a386Sopenharmony_ci canvas->save(); 52cb93a386Sopenharmony_ci canvas->translate(b.fX, b.fY); 53cb93a386Sopenharmony_ci SkScalar angle = SkScalarATan2((b.fY - a.fY), b.fX - a.fX); 54cb93a386Sopenharmony_ci canvas->rotate(angle * 180 / SK_ScalarPI - 90); 55cb93a386Sopenharmony_ci // arrow head 56cb93a386Sopenharmony_ci canvas->drawPath(arrowHead, paint); 57cb93a386Sopenharmony_ci canvas->restore(); 58cb93a386Sopenharmony_ci canvas->restore(); 59cb93a386Sopenharmony_ci } 60cb93a386Sopenharmony_ci} // namespace 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ciclass DebugPaintFilterCanvas : public SkPaintFilterCanvas { 63cb93a386Sopenharmony_cipublic: 64cb93a386Sopenharmony_ci DebugPaintFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) {} 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ciprotected: 67cb93a386Sopenharmony_ci bool onFilter(SkPaint& paint) const override { 68cb93a386Sopenharmony_ci paint.setColor(SK_ColorRED); 69cb93a386Sopenharmony_ci paint.setAlpha(0x08); 70cb93a386Sopenharmony_ci paint.setBlendMode(SkBlendMode::kSrcOver); 71cb93a386Sopenharmony_ci return true; 72cb93a386Sopenharmony_ci } 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_ci void onDrawPicture(const SkPicture* picture, 75cb93a386Sopenharmony_ci const SkMatrix* matrix, 76cb93a386Sopenharmony_ci const SkPaint* paint) override { 77cb93a386Sopenharmony_ci // We need to replay the picture onto this canvas in order to filter its internal paints. 78cb93a386Sopenharmony_ci this->SkCanvas::onDrawPicture(picture, matrix, paint); 79cb93a386Sopenharmony_ci } 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ciprivate: 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci using INHERITED = SkPaintFilterCanvas; 84cb93a386Sopenharmony_ci}; 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ciDebugCanvas::DebugCanvas(int width, int height) 87cb93a386Sopenharmony_ci : INHERITED(width, height) 88cb93a386Sopenharmony_ci , fOverdrawViz(false) 89cb93a386Sopenharmony_ci , fClipVizColor(SK_ColorTRANSPARENT) 90cb93a386Sopenharmony_ci , fDrawGpuOpBounds(false) 91cb93a386Sopenharmony_ci , fShowAndroidClip(false) 92cb93a386Sopenharmony_ci , fShowOrigin(false) 93cb93a386Sopenharmony_ci , fnextDrawPictureLayerId(-1) 94cb93a386Sopenharmony_ci , fnextDrawImageRectLayerId(-1) 95cb93a386Sopenharmony_ci , fAndroidClip(SkRect::MakeEmpty()) { 96cb93a386Sopenharmony_ci // SkPicturePlayback uses the base-class' quickReject calls to cull clipped 97cb93a386Sopenharmony_ci // operations. This can lead to problems in the debugger which expects all 98cb93a386Sopenharmony_ci // the operations in the captured skp to appear in the debug canvas. To 99cb93a386Sopenharmony_ci // circumvent this we create a wide open clip here (an empty clip rect 100cb93a386Sopenharmony_ci // is not sufficient). 101cb93a386Sopenharmony_ci // Internally, the SkRect passed to clipRect is converted to an SkIRect and 102cb93a386Sopenharmony_ci // rounded out. The following code creates a nearly maximal rect that will 103cb93a386Sopenharmony_ci // not get collapsed by the coming conversions (Due to precision loss the 104cb93a386Sopenharmony_ci // inset has to be surprisingly large). 105cb93a386Sopenharmony_ci SkIRect largeIRect = SkRectPriv::MakeILarge(); 106cb93a386Sopenharmony_ci largeIRect.inset(1024, 1024); 107cb93a386Sopenharmony_ci SkRect large = SkRect::Make(largeIRect); 108cb93a386Sopenharmony_ci#ifdef SK_DEBUG 109cb93a386Sopenharmony_ci SkASSERT(!large.roundOut().isEmpty()); 110cb93a386Sopenharmony_ci#endif 111cb93a386Sopenharmony_ci // call the base class' version to avoid adding a draw command 112cb93a386Sopenharmony_ci this->INHERITED::onClipRect(large, SkClipOp::kIntersect, kHard_ClipEdgeStyle); 113cb93a386Sopenharmony_ci} 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ciDebugCanvas::DebugCanvas(SkIRect bounds) 116cb93a386Sopenharmony_ci : DebugCanvas(bounds.width(), bounds.height()) {} 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_ciDebugCanvas::~DebugCanvas() { fCommandVector.deleteAll(); } 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_civoid DebugCanvas::addDrawCommand(DrawCommand* command) { fCommandVector.push_back(command); } 121cb93a386Sopenharmony_ci 122cb93a386Sopenharmony_civoid DebugCanvas::draw(SkCanvas* canvas) { 123cb93a386Sopenharmony_ci if (!fCommandVector.isEmpty()) { 124cb93a386Sopenharmony_ci this->drawTo(canvas, fCommandVector.count() - 1); 125cb93a386Sopenharmony_ci } 126cb93a386Sopenharmony_ci} 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_civoid DebugCanvas::drawTo(SkCanvas* originalCanvas, int index, int m) { 129cb93a386Sopenharmony_ci SkASSERT(!fCommandVector.isEmpty()); 130cb93a386Sopenharmony_ci SkASSERT(index < fCommandVector.count()); 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci int saveCount = originalCanvas->save(); 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci originalCanvas->resetMatrix(); 135cb93a386Sopenharmony_ci SkCanvasPriv::ResetClip(originalCanvas); 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci DebugPaintFilterCanvas filterCanvas(originalCanvas); 138cb93a386Sopenharmony_ci SkCanvas* finalCanvas = fOverdrawViz ? &filterCanvas : originalCanvas; 139cb93a386Sopenharmony_ci 140cb93a386Sopenharmony_ci#if SK_GPU_V1 141cb93a386Sopenharmony_ci auto dContext = GrAsDirectContext(finalCanvas->recordingContext()); 142cb93a386Sopenharmony_ci 143cb93a386Sopenharmony_ci // If we have a GPU backend we can also visualize the op information 144cb93a386Sopenharmony_ci GrAuditTrail* at = nullptr; 145cb93a386Sopenharmony_ci if (fDrawGpuOpBounds || m != -1) { 146cb93a386Sopenharmony_ci // The audit trail must be obtained from the original canvas. 147cb93a386Sopenharmony_ci at = this->getAuditTrail(originalCanvas); 148cb93a386Sopenharmony_ci } 149cb93a386Sopenharmony_ci#endif 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci for (int i = 0; i <= index; i++) { 152cb93a386Sopenharmony_ci#if SK_GPU_V1 153cb93a386Sopenharmony_ci GrAuditTrail::AutoCollectOps* acb = nullptr; 154cb93a386Sopenharmony_ci if (at) { 155cb93a386Sopenharmony_ci // We need to flush any pending operations, or they might combine with commands below. 156cb93a386Sopenharmony_ci // Previous operations were not registered with the audit trail when they were 157cb93a386Sopenharmony_ci // created, so if we allow them to combine, the audit trail will fail to find them. 158cb93a386Sopenharmony_ci if (dContext) { 159cb93a386Sopenharmony_ci dContext->flush(); 160cb93a386Sopenharmony_ci } 161cb93a386Sopenharmony_ci acb = new GrAuditTrail::AutoCollectOps(at, i); 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci#endif 164cb93a386Sopenharmony_ci if (fCommandVector[i]->isVisible()) { 165cb93a386Sopenharmony_ci fCommandVector[i]->execute(finalCanvas); 166cb93a386Sopenharmony_ci } 167cb93a386Sopenharmony_ci#if SK_GPU_V1 168cb93a386Sopenharmony_ci if (at && acb) { 169cb93a386Sopenharmony_ci delete acb; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci#endif 172cb93a386Sopenharmony_ci } 173cb93a386Sopenharmony_ci 174cb93a386Sopenharmony_ci if (SkColorGetA(fClipVizColor) != 0) { 175cb93a386Sopenharmony_ci finalCanvas->save(); 176cb93a386Sopenharmony_ci SkPaint clipPaint; 177cb93a386Sopenharmony_ci clipPaint.setColor(fClipVizColor); 178cb93a386Sopenharmony_ci finalCanvas->drawPaint(clipPaint); 179cb93a386Sopenharmony_ci finalCanvas->restore(); 180cb93a386Sopenharmony_ci } 181cb93a386Sopenharmony_ci 182cb93a386Sopenharmony_ci fMatrix = finalCanvas->getLocalToDevice(); 183cb93a386Sopenharmony_ci fClip = finalCanvas->getDeviceClipBounds(); 184cb93a386Sopenharmony_ci if (fShowOrigin) { 185cb93a386Sopenharmony_ci const SkPaint originXPaint = SkPaint({1.0, 0, 0, 1.0}); 186cb93a386Sopenharmony_ci const SkPaint originYPaint = SkPaint({0, 1.0, 0, 1.0}); 187cb93a386Sopenharmony_ci // Draw an origin cross at the origin before restoring to assist in visualizing the 188cb93a386Sopenharmony_ci // current matrix. 189cb93a386Sopenharmony_ci drawArrow(finalCanvas, {-50, 0}, {50, 0}, originXPaint); 190cb93a386Sopenharmony_ci drawArrow(finalCanvas, {0, -50}, {0, 50}, originYPaint); 191cb93a386Sopenharmony_ci } 192cb93a386Sopenharmony_ci finalCanvas->restoreToCount(saveCount); 193cb93a386Sopenharmony_ci 194cb93a386Sopenharmony_ci if (fShowAndroidClip) { 195cb93a386Sopenharmony_ci // Draw visualization of android device clip restriction 196cb93a386Sopenharmony_ci SkPaint androidClipPaint; 197cb93a386Sopenharmony_ci androidClipPaint.setARGB(80, 255, 100, 0); 198cb93a386Sopenharmony_ci finalCanvas->drawRect(fAndroidClip, androidClipPaint); 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci#if SK_GPU_V1 202cb93a386Sopenharmony_ci // draw any ops if required and issue a full reset onto GrAuditTrail 203cb93a386Sopenharmony_ci if (at) { 204cb93a386Sopenharmony_ci // just in case there is global reordering, we flush the canvas before querying 205cb93a386Sopenharmony_ci // GrAuditTrail 206cb93a386Sopenharmony_ci GrAuditTrail::AutoEnable ae(at); 207cb93a386Sopenharmony_ci if (dContext) { 208cb93a386Sopenharmony_ci dContext->flush(); 209cb93a386Sopenharmony_ci } 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci // we pick three colorblind-safe colors, 75% alpha 212cb93a386Sopenharmony_ci static const SkColor kTotalBounds = SkColorSetARGB(0xC0, 0x6A, 0x3D, 0x9A); 213cb93a386Sopenharmony_ci static const SkColor kCommandOpBounds = SkColorSetARGB(0xC0, 0xE3, 0x1A, 0x1C); 214cb93a386Sopenharmony_ci static const SkColor kOtherOpBounds = SkColorSetARGB(0xC0, 0xFF, 0x7F, 0x00); 215cb93a386Sopenharmony_ci 216cb93a386Sopenharmony_ci // get the render target of the top device (from the original canvas) so we can ignore ops 217cb93a386Sopenharmony_ci // drawn offscreen 218cb93a386Sopenharmony_ci GrRenderTargetProxy* rtp = SkCanvasPriv::TopDeviceTargetProxy(originalCanvas); 219cb93a386Sopenharmony_ci GrSurfaceProxy::UniqueID proxyID = rtp->uniqueID(); 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_ci // get the bounding boxes to draw 222cb93a386Sopenharmony_ci SkTArray<GrAuditTrail::OpInfo> childrenBounds; 223cb93a386Sopenharmony_ci if (m == -1) { 224cb93a386Sopenharmony_ci at->getBoundsByClientID(&childrenBounds, index); 225cb93a386Sopenharmony_ci } else { 226cb93a386Sopenharmony_ci // the client wants us to draw the mth op 227cb93a386Sopenharmony_ci at->getBoundsByOpsTaskID(&childrenBounds.push_back(), m); 228cb93a386Sopenharmony_ci } 229cb93a386Sopenharmony_ci // Shift the rects half a pixel, so they appear as exactly 1px thick lines. 230cb93a386Sopenharmony_ci finalCanvas->save(); 231cb93a386Sopenharmony_ci finalCanvas->translate(0.5, -0.5); 232cb93a386Sopenharmony_ci SkPaint paint; 233cb93a386Sopenharmony_ci paint.setStyle(SkPaint::kStroke_Style); 234cb93a386Sopenharmony_ci paint.setStrokeWidth(1); 235cb93a386Sopenharmony_ci for (int i = 0; i < childrenBounds.count(); i++) { 236cb93a386Sopenharmony_ci if (childrenBounds[i].fProxyUniqueID != proxyID) { 237cb93a386Sopenharmony_ci // offscreen draw, ignore for now 238cb93a386Sopenharmony_ci continue; 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci paint.setColor(kTotalBounds); 241cb93a386Sopenharmony_ci finalCanvas->drawRect(childrenBounds[i].fBounds, paint); 242cb93a386Sopenharmony_ci for (int j = 0; j < childrenBounds[i].fOps.count(); j++) { 243cb93a386Sopenharmony_ci const GrAuditTrail::OpInfo::Op& op = childrenBounds[i].fOps[j]; 244cb93a386Sopenharmony_ci if (op.fClientID != index) { 245cb93a386Sopenharmony_ci paint.setColor(kOtherOpBounds); 246cb93a386Sopenharmony_ci } else { 247cb93a386Sopenharmony_ci paint.setColor(kCommandOpBounds); 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci finalCanvas->drawRect(op.fBounds, paint); 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci finalCanvas->restore(); 253cb93a386Sopenharmony_ci this->cleanupAuditTrail(at); 254cb93a386Sopenharmony_ci } 255cb93a386Sopenharmony_ci#endif 256cb93a386Sopenharmony_ci} 257cb93a386Sopenharmony_ci 258cb93a386Sopenharmony_civoid DebugCanvas::deleteDrawCommandAt(int index) { 259cb93a386Sopenharmony_ci SkASSERT(index < fCommandVector.count()); 260cb93a386Sopenharmony_ci delete fCommandVector[index]; 261cb93a386Sopenharmony_ci fCommandVector.remove(index); 262cb93a386Sopenharmony_ci} 263cb93a386Sopenharmony_ci 264cb93a386Sopenharmony_ciDrawCommand* DebugCanvas::getDrawCommandAt(int index) const { 265cb93a386Sopenharmony_ci SkASSERT(index < fCommandVector.count()); 266cb93a386Sopenharmony_ci return fCommandVector[index]; 267cb93a386Sopenharmony_ci} 268cb93a386Sopenharmony_ci 269cb93a386Sopenharmony_ci#if SK_GPU_V1 270cb93a386Sopenharmony_ciGrAuditTrail* DebugCanvas::getAuditTrail(SkCanvas* canvas) { 271cb93a386Sopenharmony_ci GrAuditTrail* at = nullptr; 272cb93a386Sopenharmony_ci auto ctx = canvas->recordingContext(); 273cb93a386Sopenharmony_ci if (ctx) { 274cb93a386Sopenharmony_ci at = ctx->priv().auditTrail(); 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci return at; 277cb93a386Sopenharmony_ci} 278cb93a386Sopenharmony_ci 279cb93a386Sopenharmony_civoid DebugCanvas::drawAndCollectOps(SkCanvas* canvas) { 280cb93a386Sopenharmony_ci GrAuditTrail* at = this->getAuditTrail(canvas); 281cb93a386Sopenharmony_ci if (at) { 282cb93a386Sopenharmony_ci // loop over all of the commands and draw them, this is to collect reordering 283cb93a386Sopenharmony_ci // information 284cb93a386Sopenharmony_ci for (int i = 0; i < this->getSize(); i++) { 285cb93a386Sopenharmony_ci GrAuditTrail::AutoCollectOps enable(at, i); 286cb93a386Sopenharmony_ci fCommandVector[i]->execute(canvas); 287cb93a386Sopenharmony_ci } 288cb93a386Sopenharmony_ci 289cb93a386Sopenharmony_ci // in case there is some kind of global reordering 290cb93a386Sopenharmony_ci { 291cb93a386Sopenharmony_ci GrAuditTrail::AutoEnable ae(at); 292cb93a386Sopenharmony_ci 293cb93a386Sopenharmony_ci auto dContext = GrAsDirectContext(canvas->recordingContext()); 294cb93a386Sopenharmony_ci if (dContext) { 295cb93a386Sopenharmony_ci dContext->flush(); 296cb93a386Sopenharmony_ci } 297cb93a386Sopenharmony_ci } 298cb93a386Sopenharmony_ci } 299cb93a386Sopenharmony_ci} 300cb93a386Sopenharmony_ci 301cb93a386Sopenharmony_civoid DebugCanvas::cleanupAuditTrail(GrAuditTrail* at) { 302cb93a386Sopenharmony_ci if (at) { 303cb93a386Sopenharmony_ci GrAuditTrail::AutoEnable ae(at); 304cb93a386Sopenharmony_ci at->fullReset(); 305cb93a386Sopenharmony_ci } 306cb93a386Sopenharmony_ci} 307cb93a386Sopenharmony_ci#endif // SK_GPU_V1 308cb93a386Sopenharmony_ci 309cb93a386Sopenharmony_civoid DebugCanvas::toJSON(SkJSONWriter& writer, 310cb93a386Sopenharmony_ci UrlDataManager& urlDataManager, 311cb93a386Sopenharmony_ci SkCanvas* canvas) { 312cb93a386Sopenharmony_ci#if SK_GPU_V1 313cb93a386Sopenharmony_ci this->drawAndCollectOps(canvas); 314cb93a386Sopenharmony_ci 315cb93a386Sopenharmony_ci // now collect json 316cb93a386Sopenharmony_ci GrAuditTrail* at = this->getAuditTrail(canvas); 317cb93a386Sopenharmony_ci#endif 318cb93a386Sopenharmony_ci writer.appendS32(SKDEBUGCANVAS_ATTRIBUTE_VERSION, SKDEBUGCANVAS_VERSION); 319cb93a386Sopenharmony_ci writer.beginArray(SKDEBUGCANVAS_ATTRIBUTE_COMMANDS); 320cb93a386Sopenharmony_ci 321cb93a386Sopenharmony_ci for (int i = 0; i < this->getSize(); i++) { 322cb93a386Sopenharmony_ci writer.beginObject(); // command 323cb93a386Sopenharmony_ci this->getDrawCommandAt(i)->toJSON(writer, urlDataManager); 324cb93a386Sopenharmony_ci 325cb93a386Sopenharmony_ci#if SK_GPU_V1 326cb93a386Sopenharmony_ci if (at) { 327cb93a386Sopenharmony_ci writer.appendName(SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL); 328cb93a386Sopenharmony_ci at->toJson(writer, i); 329cb93a386Sopenharmony_ci } 330cb93a386Sopenharmony_ci#endif 331cb93a386Sopenharmony_ci writer.endObject(); // command 332cb93a386Sopenharmony_ci } 333cb93a386Sopenharmony_ci 334cb93a386Sopenharmony_ci writer.endArray(); // commands 335cb93a386Sopenharmony_ci#if SK_GPU_V1 336cb93a386Sopenharmony_ci this->cleanupAuditTrail(at); 337cb93a386Sopenharmony_ci#endif 338cb93a386Sopenharmony_ci} 339cb93a386Sopenharmony_ci 340cb93a386Sopenharmony_civoid DebugCanvas::toJSONOpsTask(SkJSONWriter& writer, SkCanvas* canvas) { 341cb93a386Sopenharmony_ci#if SK_GPU_V1 342cb93a386Sopenharmony_ci this->drawAndCollectOps(canvas); 343cb93a386Sopenharmony_ci 344cb93a386Sopenharmony_ci GrAuditTrail* at = this->getAuditTrail(canvas); 345cb93a386Sopenharmony_ci if (at) { 346cb93a386Sopenharmony_ci GrAuditTrail::AutoManageOpsTask enable(at); 347cb93a386Sopenharmony_ci at->toJson(writer); 348cb93a386Sopenharmony_ci this->cleanupAuditTrail(at); 349cb93a386Sopenharmony_ci return; 350cb93a386Sopenharmony_ci } 351cb93a386Sopenharmony_ci#endif 352cb93a386Sopenharmony_ci 353cb93a386Sopenharmony_ci writer.beginObject(); 354cb93a386Sopenharmony_ci writer.endObject(); 355cb93a386Sopenharmony_ci} 356cb93a386Sopenharmony_ci 357cb93a386Sopenharmony_civoid DebugCanvas::setOverdrawViz(bool overdrawViz) { fOverdrawViz = overdrawViz; } 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_civoid DebugCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) { 360cb93a386Sopenharmony_ci this->addDrawCommand(new ClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle)); 361cb93a386Sopenharmony_ci} 362cb93a386Sopenharmony_ci 363cb93a386Sopenharmony_civoid DebugCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) { 364cb93a386Sopenharmony_ci this->addDrawCommand(new ClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle)); 365cb93a386Sopenharmony_ci} 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_civoid DebugCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) { 368cb93a386Sopenharmony_ci this->addDrawCommand(new ClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle)); 369cb93a386Sopenharmony_ci} 370cb93a386Sopenharmony_ci 371cb93a386Sopenharmony_civoid DebugCanvas::onClipRegion(const SkRegion& region, SkClipOp op) { 372cb93a386Sopenharmony_ci this->addDrawCommand(new ClipRegionCommand(region, op)); 373cb93a386Sopenharmony_ci} 374cb93a386Sopenharmony_ci 375cb93a386Sopenharmony_civoid DebugCanvas::onClipShader(sk_sp<SkShader> cs, SkClipOp op) { 376cb93a386Sopenharmony_ci this->addDrawCommand(new ClipShaderCommand(std::move(cs), op)); 377cb93a386Sopenharmony_ci} 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_civoid DebugCanvas::onResetClip() { 380cb93a386Sopenharmony_ci this->addDrawCommand(new ResetClipCommand()); 381cb93a386Sopenharmony_ci} 382cb93a386Sopenharmony_ci 383cb93a386Sopenharmony_civoid DebugCanvas::didConcat44(const SkM44& m) { 384cb93a386Sopenharmony_ci this->addDrawCommand(new Concat44Command(m)); 385cb93a386Sopenharmony_ci this->INHERITED::didConcat44(m); 386cb93a386Sopenharmony_ci} 387cb93a386Sopenharmony_ci 388cb93a386Sopenharmony_civoid DebugCanvas::didScale(SkScalar x, SkScalar y) { 389cb93a386Sopenharmony_ci this->didConcat44(SkM44::Scale(x, y)); 390cb93a386Sopenharmony_ci} 391cb93a386Sopenharmony_ci 392cb93a386Sopenharmony_civoid DebugCanvas::didTranslate(SkScalar x, SkScalar y) { 393cb93a386Sopenharmony_ci this->didConcat44(SkM44::Translate(x, y)); 394cb93a386Sopenharmony_ci} 395cb93a386Sopenharmony_ci 396cb93a386Sopenharmony_civoid DebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { 397cb93a386Sopenharmony_ci // Parse layer-releated annotations added in SkiaPipeline.cpp and RenderNodeDrawable.cpp 398cb93a386Sopenharmony_ci // the format of the annotations is <Indicator|RenderNodeId> 399cb93a386Sopenharmony_ci SkTArray<SkString> tokens; 400cb93a386Sopenharmony_ci SkStrSplit(key, "|", kStrict_SkStrSplitMode, &tokens); 401cb93a386Sopenharmony_ci if (tokens.size() == 2) { 402cb93a386Sopenharmony_ci if (tokens[0].equals(kOffscreenLayerDraw)) { 403cb93a386Sopenharmony_ci // Indicates that the next drawPicture command contains the SkPicture to render the 404cb93a386Sopenharmony_ci // node at this id in an offscreen buffer. 405cb93a386Sopenharmony_ci fnextDrawPictureLayerId = std::stoi(tokens[1].c_str()); 406cb93a386Sopenharmony_ci fnextDrawPictureDirtyRect = rect.roundOut(); 407cb93a386Sopenharmony_ci return; // don't record it 408cb93a386Sopenharmony_ci } else if (tokens[0].equals(kSurfaceID)) { 409cb93a386Sopenharmony_ci // Indicates that the following drawImageRect should draw the offscreen buffer. 410cb93a386Sopenharmony_ci fnextDrawImageRectLayerId = std::stoi(tokens[1].c_str()); 411cb93a386Sopenharmony_ci return; // don't record it 412cb93a386Sopenharmony_ci } 413cb93a386Sopenharmony_ci } 414cb93a386Sopenharmony_ci if (strcmp(kAndroidClip, key) == 0) { 415cb93a386Sopenharmony_ci // Store this frame's android device clip restriction for visualization later. 416cb93a386Sopenharmony_ci // This annotation stands in place of the androidFramework_setDeviceClipRestriction 417cb93a386Sopenharmony_ci // which is unrecordable. 418cb93a386Sopenharmony_ci fAndroidClip = rect; 419cb93a386Sopenharmony_ci } 420cb93a386Sopenharmony_ci this->addDrawCommand(new DrawAnnotationCommand(rect, key, sk_ref_sp(value))); 421cb93a386Sopenharmony_ci} 422cb93a386Sopenharmony_ci 423cb93a386Sopenharmony_civoid DebugCanvas::onDrawImage2(const SkImage* image, 424cb93a386Sopenharmony_ci SkScalar left, 425cb93a386Sopenharmony_ci SkScalar top, 426cb93a386Sopenharmony_ci const SkSamplingOptions& sampling, 427cb93a386Sopenharmony_ci const SkPaint* paint) { 428cb93a386Sopenharmony_ci this->addDrawCommand(new DrawImageCommand(image, left, top, sampling, paint)); 429cb93a386Sopenharmony_ci} 430cb93a386Sopenharmony_ci 431cb93a386Sopenharmony_civoid DebugCanvas::onDrawImageLattice2(const SkImage* image, 432cb93a386Sopenharmony_ci const Lattice& lattice, 433cb93a386Sopenharmony_ci const SkRect& dst, 434cb93a386Sopenharmony_ci SkFilterMode filter, // todo 435cb93a386Sopenharmony_ci const SkPaint* paint) { 436cb93a386Sopenharmony_ci this->addDrawCommand(new DrawImageLatticeCommand(image, lattice, dst, filter, paint)); 437cb93a386Sopenharmony_ci} 438cb93a386Sopenharmony_ci 439cb93a386Sopenharmony_civoid DebugCanvas::onDrawImageRect2(const SkImage* image, 440cb93a386Sopenharmony_ci const SkRect& src, 441cb93a386Sopenharmony_ci const SkRect& dst, 442cb93a386Sopenharmony_ci const SkSamplingOptions& sampling, 443cb93a386Sopenharmony_ci const SkPaint* paint, 444cb93a386Sopenharmony_ci SrcRectConstraint constraint) { 445cb93a386Sopenharmony_ci if (fnextDrawImageRectLayerId != -1 && fLayerManager) { 446cb93a386Sopenharmony_ci // This drawImageRect command would have drawn the offscreen buffer for a layer. 447cb93a386Sopenharmony_ci // On Android, we recorded an SkPicture of the commands that drew to the layer. 448cb93a386Sopenharmony_ci // To render the layer as it would have looked on the frame this DebugCanvas draws, we need 449cb93a386Sopenharmony_ci // to call fLayerManager->getLayerAsImage(id). This must be done just before 450cb93a386Sopenharmony_ci // drawTo(command), since it depends on the index into the layer's commands 451cb93a386Sopenharmony_ci // (managed by fLayerManager) 452cb93a386Sopenharmony_ci // Instead of adding a DrawImageRectCommand, we need a deferred command, that when 453cb93a386Sopenharmony_ci // executed, will call drawImageRect(fLayerManager->getLayerAsImage()) 454cb93a386Sopenharmony_ci this->addDrawCommand(new DrawImageRectLayerCommand( 455cb93a386Sopenharmony_ci fLayerManager, fnextDrawImageRectLayerId, fFrame, src, dst, sampling, 456cb93a386Sopenharmony_ci paint, constraint)); 457cb93a386Sopenharmony_ci } else { 458cb93a386Sopenharmony_ci this->addDrawCommand(new DrawImageRectCommand(image, src, dst, sampling, paint, constraint)); 459cb93a386Sopenharmony_ci } 460cb93a386Sopenharmony_ci // Reset expectation so next drawImageRect is not special. 461cb93a386Sopenharmony_ci fnextDrawImageRectLayerId = -1; 462cb93a386Sopenharmony_ci} 463cb93a386Sopenharmony_ci 464cb93a386Sopenharmony_civoid DebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { 465cb93a386Sopenharmony_ci this->addDrawCommand(new DrawOvalCommand(oval, paint)); 466cb93a386Sopenharmony_ci} 467cb93a386Sopenharmony_ci 468cb93a386Sopenharmony_civoid DebugCanvas::onDrawArc(const SkRect& oval, 469cb93a386Sopenharmony_ci SkScalar startAngle, 470cb93a386Sopenharmony_ci SkScalar sweepAngle, 471cb93a386Sopenharmony_ci bool useCenter, 472cb93a386Sopenharmony_ci const SkPaint& paint) { 473cb93a386Sopenharmony_ci this->addDrawCommand(new DrawArcCommand(oval, startAngle, sweepAngle, useCenter, paint)); 474cb93a386Sopenharmony_ci} 475cb93a386Sopenharmony_ci 476cb93a386Sopenharmony_civoid DebugCanvas::onDrawPaint(const SkPaint& paint) { 477cb93a386Sopenharmony_ci this->addDrawCommand(new DrawPaintCommand(paint)); 478cb93a386Sopenharmony_ci} 479cb93a386Sopenharmony_ci 480cb93a386Sopenharmony_civoid DebugCanvas::onDrawBehind(const SkPaint& paint) { 481cb93a386Sopenharmony_ci this->addDrawCommand(new DrawBehindCommand(paint)); 482cb93a386Sopenharmony_ci} 483cb93a386Sopenharmony_ci 484cb93a386Sopenharmony_civoid DebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { 485cb93a386Sopenharmony_ci this->addDrawCommand(new DrawPathCommand(path, paint)); 486cb93a386Sopenharmony_ci} 487cb93a386Sopenharmony_ci 488cb93a386Sopenharmony_civoid DebugCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { 489cb93a386Sopenharmony_ci this->addDrawCommand(new DrawRegionCommand(region, paint)); 490cb93a386Sopenharmony_ci} 491cb93a386Sopenharmony_ci 492cb93a386Sopenharmony_civoid DebugCanvas::onDrawPicture(const SkPicture* picture, 493cb93a386Sopenharmony_ci const SkMatrix* matrix, 494cb93a386Sopenharmony_ci const SkPaint* paint) { 495cb93a386Sopenharmony_ci if (fnextDrawPictureLayerId != -1 && fLayerManager) { 496cb93a386Sopenharmony_ci fLayerManager->storeSkPicture(fnextDrawPictureLayerId, fFrame, sk_ref_sp(picture), 497cb93a386Sopenharmony_ci fnextDrawPictureDirtyRect); 498cb93a386Sopenharmony_ci } else { 499cb93a386Sopenharmony_ci this->addDrawCommand(new BeginDrawPictureCommand(picture, matrix, paint)); 500cb93a386Sopenharmony_ci SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); 501cb93a386Sopenharmony_ci picture->playback(this); 502cb93a386Sopenharmony_ci this->addDrawCommand(new EndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint))); 503cb93a386Sopenharmony_ci } 504cb93a386Sopenharmony_ci fnextDrawPictureLayerId = -1; 505cb93a386Sopenharmony_ci} 506cb93a386Sopenharmony_ci 507cb93a386Sopenharmony_civoid DebugCanvas::onDrawPoints(PointMode mode, 508cb93a386Sopenharmony_ci size_t count, 509cb93a386Sopenharmony_ci const SkPoint pts[], 510cb93a386Sopenharmony_ci const SkPaint& paint) { 511cb93a386Sopenharmony_ci this->addDrawCommand(new DrawPointsCommand(mode, count, pts, paint)); 512cb93a386Sopenharmony_ci} 513cb93a386Sopenharmony_ci 514cb93a386Sopenharmony_civoid DebugCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { 515cb93a386Sopenharmony_ci // NOTE(chudy): Messing up when renamed to DrawRect... Why? 516cb93a386Sopenharmony_ci addDrawCommand(new DrawRectCommand(rect, paint)); 517cb93a386Sopenharmony_ci} 518cb93a386Sopenharmony_ci 519cb93a386Sopenharmony_civoid DebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { 520cb93a386Sopenharmony_ci this->addDrawCommand(new DrawRRectCommand(rrect, paint)); 521cb93a386Sopenharmony_ci} 522cb93a386Sopenharmony_ci 523cb93a386Sopenharmony_civoid DebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { 524cb93a386Sopenharmony_ci this->addDrawCommand(new DrawDRRectCommand(outer, inner, paint)); 525cb93a386Sopenharmony_ci} 526cb93a386Sopenharmony_ci 527cb93a386Sopenharmony_civoid DebugCanvas::onDrawTextBlob(const SkTextBlob* blob, 528cb93a386Sopenharmony_ci SkScalar x, 529cb93a386Sopenharmony_ci SkScalar y, 530cb93a386Sopenharmony_ci const SkPaint& paint) { 531cb93a386Sopenharmony_ci this->addDrawCommand( 532cb93a386Sopenharmony_ci new DrawTextBlobCommand(sk_ref_sp(const_cast<SkTextBlob*>(blob)), x, y, paint)); 533cb93a386Sopenharmony_ci} 534cb93a386Sopenharmony_ci 535cb93a386Sopenharmony_civoid DebugCanvas::onDrawPatch(const SkPoint cubics[12], 536cb93a386Sopenharmony_ci const SkColor colors[4], 537cb93a386Sopenharmony_ci const SkPoint texCoords[4], 538cb93a386Sopenharmony_ci SkBlendMode bmode, 539cb93a386Sopenharmony_ci const SkPaint& paint) { 540cb93a386Sopenharmony_ci this->addDrawCommand(new DrawPatchCommand(cubics, colors, texCoords, bmode, paint)); 541cb93a386Sopenharmony_ci} 542cb93a386Sopenharmony_ci 543cb93a386Sopenharmony_civoid DebugCanvas::onDrawVerticesObject(const SkVertices* vertices, 544cb93a386Sopenharmony_ci SkBlendMode bmode, 545cb93a386Sopenharmony_ci const SkPaint& paint) { 546cb93a386Sopenharmony_ci this->addDrawCommand( 547cb93a386Sopenharmony_ci new DrawVerticesCommand(sk_ref_sp(const_cast<SkVertices*>(vertices)), bmode, paint)); 548cb93a386Sopenharmony_ci} 549cb93a386Sopenharmony_ci 550cb93a386Sopenharmony_civoid DebugCanvas::onDrawAtlas2(const SkImage* image, 551cb93a386Sopenharmony_ci const SkRSXform xform[], 552cb93a386Sopenharmony_ci const SkRect tex[], 553cb93a386Sopenharmony_ci const SkColor colors[], 554cb93a386Sopenharmony_ci int count, 555cb93a386Sopenharmony_ci SkBlendMode bmode, 556cb93a386Sopenharmony_ci const SkSamplingOptions& sampling, 557cb93a386Sopenharmony_ci const SkRect* cull, 558cb93a386Sopenharmony_ci const SkPaint* paint) { 559cb93a386Sopenharmony_ci this->addDrawCommand( 560cb93a386Sopenharmony_ci new DrawAtlasCommand(image, xform, tex, colors, count, bmode, sampling, cull, paint)); 561cb93a386Sopenharmony_ci} 562cb93a386Sopenharmony_ci 563cb93a386Sopenharmony_civoid DebugCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { 564cb93a386Sopenharmony_ci this->addDrawCommand(new DrawShadowCommand(path, rec)); 565cb93a386Sopenharmony_ci} 566cb93a386Sopenharmony_ci 567cb93a386Sopenharmony_civoid DebugCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) { 568cb93a386Sopenharmony_ci this->addDrawCommand(new DrawDrawableCommand(drawable, matrix)); 569cb93a386Sopenharmony_ci} 570cb93a386Sopenharmony_ci 571cb93a386Sopenharmony_civoid DebugCanvas::onDrawEdgeAAQuad(const SkRect& rect, 572cb93a386Sopenharmony_ci const SkPoint clip[4], 573cb93a386Sopenharmony_ci QuadAAFlags aa, 574cb93a386Sopenharmony_ci const SkColor4f& color, 575cb93a386Sopenharmony_ci SkBlendMode mode) { 576cb93a386Sopenharmony_ci this->addDrawCommand(new DrawEdgeAAQuadCommand(rect, clip, aa, color, mode)); 577cb93a386Sopenharmony_ci} 578cb93a386Sopenharmony_ci 579cb93a386Sopenharmony_civoid DebugCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry set[], 580cb93a386Sopenharmony_ci int count, 581cb93a386Sopenharmony_ci const SkPoint dstClips[], 582cb93a386Sopenharmony_ci const SkMatrix preViewMatrices[], 583cb93a386Sopenharmony_ci const SkSamplingOptions& sampling, 584cb93a386Sopenharmony_ci const SkPaint* paint, 585cb93a386Sopenharmony_ci SrcRectConstraint constraint) { 586cb93a386Sopenharmony_ci this->addDrawCommand(new DrawEdgeAAImageSetCommand( 587cb93a386Sopenharmony_ci set, count, dstClips, preViewMatrices, sampling, paint, constraint)); 588cb93a386Sopenharmony_ci} 589cb93a386Sopenharmony_ci 590cb93a386Sopenharmony_civoid DebugCanvas::willRestore() { 591cb93a386Sopenharmony_ci this->addDrawCommand(new RestoreCommand()); 592cb93a386Sopenharmony_ci this->INHERITED::willRestore(); 593cb93a386Sopenharmony_ci} 594cb93a386Sopenharmony_ci 595cb93a386Sopenharmony_civoid DebugCanvas::willSave() { 596cb93a386Sopenharmony_ci this->addDrawCommand(new SaveCommand()); 597cb93a386Sopenharmony_ci this->INHERITED::willSave(); 598cb93a386Sopenharmony_ci} 599cb93a386Sopenharmony_ci 600cb93a386Sopenharmony_ciSkCanvas::SaveLayerStrategy DebugCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { 601cb93a386Sopenharmony_ci this->addDrawCommand(new SaveLayerCommand(rec)); 602cb93a386Sopenharmony_ci (void)this->INHERITED::getSaveLayerStrategy(rec); 603cb93a386Sopenharmony_ci // No need for a full layer. 604cb93a386Sopenharmony_ci return kNoLayer_SaveLayerStrategy; 605cb93a386Sopenharmony_ci} 606cb93a386Sopenharmony_ci 607cb93a386Sopenharmony_cibool DebugCanvas::onDoSaveBehind(const SkRect* subset) { 608cb93a386Sopenharmony_ci // TODO 609cb93a386Sopenharmony_ci return false; 610cb93a386Sopenharmony_ci} 611cb93a386Sopenharmony_ci 612cb93a386Sopenharmony_civoid DebugCanvas::didSetM44(const SkM44& matrix) { 613cb93a386Sopenharmony_ci this->addDrawCommand(new SetM44Command(matrix)); 614cb93a386Sopenharmony_ci this->INHERITED::didSetM44(matrix); 615cb93a386Sopenharmony_ci} 616cb93a386Sopenharmony_ci 617cb93a386Sopenharmony_civoid DebugCanvas::toggleCommand(int index, bool toggle) { 618cb93a386Sopenharmony_ci SkASSERT(index < fCommandVector.count()); 619cb93a386Sopenharmony_ci fCommandVector[index]->setVisible(toggle); 620cb93a386Sopenharmony_ci} 621cb93a386Sopenharmony_ci 622cb93a386Sopenharmony_cistd::map<int, std::vector<int>> DebugCanvas::getImageIdToCommandMap(UrlDataManager& udm) const { 623cb93a386Sopenharmony_ci // map from image ids to list of commands that reference them. 624cb93a386Sopenharmony_ci std::map<int, std::vector<int>> m; 625cb93a386Sopenharmony_ci 626cb93a386Sopenharmony_ci for (int i = 0; i < this->getSize(); i++) { 627cb93a386Sopenharmony_ci const DrawCommand* command = this->getDrawCommandAt(i); 628cb93a386Sopenharmony_ci int imageIndex = -1; 629cb93a386Sopenharmony_ci // this is not an exaustive list of where images can be used, they show up in paints too. 630cb93a386Sopenharmony_ci switch (command->getOpType()) { 631cb93a386Sopenharmony_ci case DrawCommand::OpType::kDrawImage_OpType: { 632cb93a386Sopenharmony_ci imageIndex = static_cast<const DrawImageCommand*>(command)->imageId(udm); 633cb93a386Sopenharmony_ci break; 634cb93a386Sopenharmony_ci } 635cb93a386Sopenharmony_ci case DrawCommand::OpType::kDrawImageRect_OpType: { 636cb93a386Sopenharmony_ci imageIndex = static_cast<const DrawImageRectCommand*>(command)->imageId(udm); 637cb93a386Sopenharmony_ci break; 638cb93a386Sopenharmony_ci } 639cb93a386Sopenharmony_ci case DrawCommand::OpType::kDrawImageLattice_OpType: { 640cb93a386Sopenharmony_ci imageIndex = static_cast<const DrawImageLatticeCommand*>(command)->imageId(udm); 641cb93a386Sopenharmony_ci break; 642cb93a386Sopenharmony_ci } 643cb93a386Sopenharmony_ci default: break; 644cb93a386Sopenharmony_ci } 645cb93a386Sopenharmony_ci if (imageIndex >= 0) { 646cb93a386Sopenharmony_ci m[imageIndex].push_back(i); 647cb93a386Sopenharmony_ci } 648cb93a386Sopenharmony_ci } 649cb93a386Sopenharmony_ci return m; 650cb93a386Sopenharmony_ci} 651