1/* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkData.h" 9#include "SkDrawable.h" 10#include "SkLayerInfo.h" 11#include "SkPictureRecorder.h" 12#include "SkPictureUtils.h" 13#include "SkRecord.h" 14#include "SkRecordDraw.h" 15#include "SkRecordOpts.h" 16#include "SkRecorder.h" 17#include "SkTypes.h" 18 19SkPictureRecorder::SkPictureRecorder() { 20 fActivelyRecording = false; 21 fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0)))); 22} 23 24SkPictureRecorder::~SkPictureRecorder() {} 25 26SkCanvas* SkPictureRecorder::beginRecording(const SkRect& cullRect, 27 SkBBHFactory* bbhFactory /* = NULL */, 28 uint32_t recordFlags /* = 0 */) { 29 fCullRect = cullRect; 30 fFlags = recordFlags; 31 32 if (bbhFactory) { 33 fBBH.reset((*bbhFactory)(cullRect)); 34 SkASSERT(fBBH.get()); 35 } 36 37 fRecord.reset(SkNEW(SkRecord)); 38 fRecorder->reset(fRecord.get(), cullRect); 39 fActivelyRecording = true; 40 return this->getRecordingCanvas(); 41} 42 43SkCanvas* SkPictureRecorder::getRecordingCanvas() { 44 return fActivelyRecording ? fRecorder.get() : nullptr; 45} 46 47SkPicture* SkPictureRecorder::endRecordingAsPicture() { 48 fActivelyRecording = false; 49 fRecorder->restoreToCount(1); // If we were missing any restores, add them now. 50 // TODO: delay as much of this work until just before first playback? 51 SkRecordOptimize(fRecord); 52 53 SkAutoTUnref<SkLayerInfo> saveLayerData; 54 55 if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) { 56 SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); 57 58 saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key))); 59 } 60 61 SkDrawableList* drawableList = fRecorder->getDrawableList(); 62 SkPicture::SnapshotArray* pictList = drawableList ? drawableList->newDrawableSnapshot() : NULL; 63 64 if (fBBH.get()) { 65 if (saveLayerData) { 66 SkRecordComputeLayers(fCullRect, *fRecord, pictList, fBBH.get(), saveLayerData); 67 } else { 68 SkRecordFillBounds(fCullRect, *fRecord, fBBH.get()); 69 } 70 SkRect bbhBound = fBBH->getRootBound(); 71 SkASSERT((bbhBound.isEmpty() || fCullRect.contains(bbhBound)) 72 || (bbhBound.isEmpty() && fCullRect.isEmpty())); 73 fCullRect = bbhBound; 74 } 75 76 size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures(); 77 for (int i = 0; pictList && i < pictList->count(); i++) { 78 subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); 79 } 80 return SkNEW_ARGS(SkPicture, (fCullRect, 81 fRecord.detach(), 82 pictList, 83 fBBH.detach(), 84 saveLayerData.detach(), 85 subPictureBytes)); 86} 87 88void SkPictureRecorder::partialReplay(SkCanvas* canvas) const { 89 if (NULL == canvas) { 90 return; 91 } 92 93 int drawableCount = 0; 94 SkDrawable* const* drawables = NULL; 95 SkDrawableList* drawableList = fRecorder->getDrawableList(); 96 if (drawableList) { 97 drawableCount = drawableList->count(); 98 drawables = drawableList->begin(); 99 } 100 SkRecordDraw(*fRecord, canvas, NULL, drawables, drawableCount, NULL/*bbh*/, NULL/*callback*/); 101} 102 103/////////////////////////////////////////////////////////////////////////////////////////////////// 104 105class SkRecordedDrawable : public SkDrawable { 106 SkAutoTUnref<SkRecord> fRecord; 107 SkAutoTUnref<SkBBoxHierarchy> fBBH; 108 SkAutoTDelete<SkDrawableList> fDrawableList; 109 const SkRect fBounds; 110 const bool fDoSaveLayerInfo; 111 112public: 113 SkRecordedDrawable(SkRecord* record, SkBBoxHierarchy* bbh, SkDrawableList* drawableList, 114 const SkRect& bounds, bool doSaveLayerInfo) 115 : fRecord(SkRef(record)) 116 , fBBH(SkSafeRef(bbh)) 117 , fDrawableList(drawableList) // we take ownership 118 , fBounds(bounds) 119 , fDoSaveLayerInfo(doSaveLayerInfo) 120 {} 121 122protected: 123 SkRect onGetBounds() override { return fBounds; } 124 125 void onDraw(SkCanvas* canvas) override { 126 SkDrawable* const* drawables = NULL; 127 int drawableCount = 0; 128 if (fDrawableList) { 129 drawables = fDrawableList->begin(); 130 drawableCount = fDrawableList->count(); 131 } 132 SkRecordDraw(*fRecord, canvas, NULL, drawables, drawableCount, fBBH, NULL/*callback*/); 133 } 134 135 SkPicture* onNewPictureSnapshot() override { 136 SkPicture::SnapshotArray* pictList = NULL; 137 if (fDrawableList) { 138 // TODO: should we plumb-down the BBHFactory and recordFlags from our host 139 // PictureRecorder? 140 pictList = fDrawableList->newDrawableSnapshot(); 141 } 142 143 SkAutoTUnref<SkLayerInfo> saveLayerData; 144 145 if (fBBH && fDoSaveLayerInfo) { 146 SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); 147 148 saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key))); 149 150 SkBBoxHierarchy* bbh = NULL; // we've already computed fBBH (received in constructor) 151 // TODO: update saveLayer info computation to reuse the already computed 152 // bounds in 'fBBH' 153 SkRecordComputeLayers(fBounds, *fRecord, pictList, bbh, saveLayerData); 154 } 155 156 size_t subPictureBytes = 0; 157 for (int i = 0; pictList && i < pictList->count(); i++) { 158 subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); 159 } 160 // SkPicture will take ownership of a ref on both fRecord and fBBH. 161 // We're not willing to give up our ownership, so we must ref them for SkPicture. 162 return SkNEW_ARGS(SkPicture, (fBounds, 163 SkRef(fRecord.get()), 164 pictList, 165 SkSafeRef(fBBH.get()), 166 saveLayerData.detach(), 167 subPictureBytes)); 168 } 169}; 170 171SkDrawable* SkPictureRecorder::endRecordingAsDrawable() { 172 fActivelyRecording = false; 173 fRecorder->restoreToCount(1); // If we were missing any restores, add them now. 174 // TODO: delay as much of this work until just before first playback? 175 SkRecordOptimize(fRecord); 176 177 if (fBBH.get()) { 178 SkRecordFillBounds(fCullRect, *fRecord, fBBH.get()); 179 } 180 181 SkDrawable* drawable = SkNEW_ARGS(SkRecordedDrawable, 182 (fRecord, fBBH, fRecorder->detachDrawableList(), 183 fCullRect, 184 SkToBool(fFlags & kComputeSaveLayerInfo_RecordFlag))); 185 186 // release our refs now, so only the drawable will be the owner. 187 fRecord.reset(NULL); 188 fBBH.reset(NULL); 189 190 return drawable; 191} 192