| /* |
| * Copyright 2014 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrPictureUtils.h" |
| |
| #include "SkPaintPriv.h" |
| #include "SkRecord.h" |
| #include "SkRecords.h" |
| |
| SkPicture::AccelData::Key GrAccelData::ComputeAccelDataKey() { |
| static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain(); |
| |
| return gGPUID; |
| } |
| |
| // SkRecord visitor to gather saveLayer/restore information. |
| class CollectLayers { |
| public: |
| CollectLayers(const SkPicture* pict, GrAccelData* accelData) |
| : fPictureID(pict->uniqueID()) |
| , fCTM(&SkMatrix::I()) |
| , fCurrentClipBounds(SkIRect::MakeXYWH(0, 0, pict->width(), pict->height())) |
| , fSaveLayersInStack(0) |
| , fAccelData(accelData) { |
| |
| if (NULL == pict->fRecord.get()) { |
| return; |
| } |
| |
| for (fCurrentOp = 0; fCurrentOp < pict->fRecord->count(); ++fCurrentOp) { |
| pict->fRecord->visit<void>(fCurrentOp, *this); |
| } |
| |
| while (!fSaveStack.isEmpty()) { |
| this->popSaveBlock(); |
| } |
| } |
| |
| template <typename T> void operator()(const T& op) { |
| this->updateCTM(op); |
| this->updateClipBounds(op); |
| this->trackSaveLayers(op); |
| } |
| |
| private: |
| |
| class SaveInfo { |
| public: |
| SaveInfo() { } |
| SaveInfo(int opIndex, bool isSaveLayer, const SkPaint* paint, const SkIRect& bounds) |
| : fStartIndex(opIndex) |
| , fIsSaveLayer(isSaveLayer) |
| , fHasNestedSaveLayer(false) |
| , fPaint(paint) |
| , fBounds(bounds) { |
| |
| } |
| |
| int fStartIndex; |
| bool fIsSaveLayer; |
| bool fHasNestedSaveLayer; |
| const SkPaint* fPaint; |
| SkIRect fBounds; |
| }; |
| |
| uint32_t fPictureID; |
| unsigned int fCurrentOp; |
| const SkMatrix* fCTM; |
| SkIRect fCurrentClipBounds; |
| int fSaveLayersInStack; |
| SkTDArray<SaveInfo> fSaveStack; |
| GrAccelData* fAccelData; |
| |
| template <typename T> void updateCTM(const T&) { /* most ops don't change the CTM */ } |
| void updateCTM(const SkRecords::Restore& op) { fCTM = &op.matrix; } |
| void updateCTM(const SkRecords::SetMatrix& op) { fCTM = &op.matrix; } |
| |
| template <typename T> void updateClipBounds(const T&) { /* most ops don't change the clip */ } |
| // Each of these devBounds fields is the state of the device bounds after the op. |
| // So Restore's devBounds are those bounds saved by its paired Save or SaveLayer. |
| void updateClipBounds(const SkRecords::Restore& op) { fCurrentClipBounds = op.devBounds; } |
| void updateClipBounds(const SkRecords::ClipPath& op) { fCurrentClipBounds = op.devBounds; } |
| void updateClipBounds(const SkRecords::ClipRRect& op) { fCurrentClipBounds = op.devBounds; } |
| void updateClipBounds(const SkRecords::ClipRect& op) { fCurrentClipBounds = op.devBounds; } |
| void updateClipBounds(const SkRecords::ClipRegion& op) { fCurrentClipBounds = op.devBounds; } |
| void updateClipBounds(const SkRecords::SaveLayer& op) { |
| if (NULL != op.bounds) { |
| fCurrentClipBounds.intersect(this->adjustAndMap(*op.bounds, op.paint)); |
| } |
| } |
| |
| template <typename T> void trackSaveLayers(const T& op) { |
| /* most ops aren't involved in saveLayers */ |
| } |
| void trackSaveLayers(const SkRecords::Save& s) { this->pushSaveBlock(); } |
| void trackSaveLayers(const SkRecords::SaveLayer& sl) { this->pushSaveLayerBlock(sl.paint); } |
| void trackSaveLayers(const SkRecords::Restore& r) { this->popSaveBlock(); } |
| void trackSaveLayers(const SkRecords::DrawPicture& dp) { |
| // For sub-pictures, we wrap their layer information within the parent |
| // picture's rendering hierarchy |
| const GrAccelData* childData = GPUOptimize(dp.picture); |
| |
| for (int i = 0; i < childData->numSaveLayers(); ++i) { |
| const GrAccelData::SaveLayerInfo& src = childData->saveLayerInfo(i); |
| |
| this->updateStackForSaveLayer(); |
| |
| GrAccelData::SaveLayerInfo dst; |
| |
| // TODO: need to store an SkRect in GrAccelData::SaveLayerInfo? |
| SkRect srcRect = SkRect::MakeXYWH(SkIntToScalar(src.fOffset.fX), |
| SkIntToScalar(src.fOffset.fY), |
| SkIntToScalar(src.fSize.width()), |
| SkIntToScalar(src.fSize.height())); |
| SkIRect newClip(fCurrentClipBounds); |
| newClip.intersect(this->adjustAndMap(srcRect, dp.paint)); |
| |
| dst.fValid = true; |
| dst.fPictureID = dp.picture->uniqueID(); |
| dst.fSize = SkISize::Make(newClip.width(), newClip.height()); |
| dst.fOffset = SkIPoint::Make(newClip.fLeft, newClip.fTop); |
| dst.fOriginXform = *fCTM; |
| dst.fOriginXform.postConcat(src.fOriginXform); |
| dst.fOriginXform.postTranslate(SkIntToScalar(-newClip.fLeft), |
| SkIntToScalar(-newClip.fTop)); |
| |
| if (NULL == src.fPaint) { |
| dst.fPaint = NULL; |
| } else { |
| dst.fPaint = SkNEW_ARGS(SkPaint, (*src.fPaint)); |
| } |
| |
| dst.fSaveLayerOpID = src.fSaveLayerOpID; |
| dst.fRestoreOpID = src.fRestoreOpID; |
| dst.fHasNestedLayers = src.fHasNestedLayers; |
| dst.fIsNested = fSaveLayersInStack > 0 || src.fIsNested; |
| |
| fAccelData->addSaveLayerInfo(dst); |
| } |
| } |
| |
| void pushSaveBlock() { |
| fSaveStack.push(SaveInfo(fCurrentOp, false, NULL, SkIRect::MakeEmpty())); |
| } |
| |
| // Inform all the saveLayers already on the stack that they now have a |
| // nested saveLayer inside them |
| void updateStackForSaveLayer() { |
| for (int index = fSaveStack.count() - 1; index >= 0; --index) { |
| if (fSaveStack[index].fHasNestedSaveLayer) { |
| break; |
| } |
| fSaveStack[index].fHasNestedSaveLayer = true; |
| if (fSaveStack[index].fIsSaveLayer) { |
| break; |
| } |
| } |
| } |
| |
| void pushSaveLayerBlock(const SkPaint* paint) { |
| this->updateStackForSaveLayer(); |
| |
| fSaveStack.push(SaveInfo(fCurrentOp, true, paint, fCurrentClipBounds)); |
| ++fSaveLayersInStack; |
| } |
| |
| void popSaveBlock() { |
| if (fSaveStack.count() <= 0) { |
| SkASSERT(false); |
| return; |
| } |
| |
| SaveInfo si; |
| fSaveStack.pop(&si); |
| |
| if (!si.fIsSaveLayer) { |
| return; |
| } |
| |
| --fSaveLayersInStack; |
| |
| GrAccelData::SaveLayerInfo slInfo; |
| |
| slInfo.fValid = true; |
| slInfo.fPictureID = fPictureID; |
| slInfo.fSize = SkISize::Make(si.fBounds.width(), si.fBounds.height()); |
| slInfo.fOffset = SkIPoint::Make(si.fBounds.fLeft, si.fBounds.fTop); |
| slInfo.fOriginXform = *fCTM; |
| slInfo.fOriginXform.postTranslate(SkIntToScalar(-si.fBounds.fLeft), |
| SkIntToScalar(-si.fBounds.fTop)); |
| |
| if (NULL == si.fPaint) { |
| slInfo.fPaint = NULL; |
| } else { |
| slInfo.fPaint = SkNEW_ARGS(SkPaint, (*si.fPaint)); |
| } |
| |
| slInfo.fSaveLayerOpID = si.fStartIndex; |
| slInfo.fRestoreOpID = fCurrentOp; |
| slInfo.fHasNestedLayers = si.fHasNestedSaveLayer; |
| slInfo.fIsNested = fSaveLayersInStack > 0; |
| |
| fAccelData->addSaveLayerInfo(slInfo); |
| } |
| |
| // Returns true if rect was meaningfully adjusted for the effects of paint, |
| // false if the paint could affect the rect in unknown ways. |
| static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) { |
| if (paint) { |
| if (paint->canComputeFastBounds()) { |
| *rect = paint->computeFastBounds(*rect, rect); |
| return true; |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| // Adjust rect for all paints that may affect its geometry, then map it to device space. |
| SkIRect adjustAndMap(SkRect rect, const SkPaint* paint) const { |
| // Inverted rectangles really confuse our BBHs. |
| rect.sort(); |
| |
| // Adjust the rect for its own paint. |
| if (!AdjustForPaint(paint, &rect)) { |
| // The paint could do anything to our bounds. The only safe answer is the current clip. |
| return fCurrentClipBounds; |
| } |
| |
| // Adjust rect for all the paints from the SaveLayers we're inside. |
| for (int i = fSaveStack.count() - 1; i >= 0; i--) { |
| if (!AdjustForPaint(fSaveStack[i].fPaint, &rect)) { |
| // Same deal as above. |
| return fCurrentClipBounds; |
| } |
| } |
| |
| // Map the rect back to device space. |
| fCTM->mapRect(&rect); |
| SkIRect devRect; |
| rect.roundOut(&devRect); |
| |
| // Nothing can draw outside the current clip. |
| // (Only bounded ops call into this method, so oddballs like Clear don't matter here.) |
| devRect.intersect(fCurrentClipBounds); |
| return devRect; |
| } |
| }; |
| |
| |
| // GPUOptimize is only intended to be called within the context of SkGpuDevice's |
| // EXPERIMENTAL_optimize method. |
| const GrAccelData* GPUOptimize(const SkPicture* pict) { |
| if (NULL == pict || 0 == pict->width() || 0 == pict->height()) { |
| return NULL; |
| } |
| |
| SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey(); |
| |
| const GrAccelData* existing = |
| static_cast<const GrAccelData*>(pict->EXPERIMENTAL_getAccelData(key)); |
| if (NULL != existing) { |
| return existing; |
| } |
| |
| SkAutoTUnref<GrAccelData> data(SkNEW_ARGS(GrAccelData, (key))); |
| |
| pict->EXPERIMENTAL_addAccelData(data); |
| |
| CollectLayers collector(pict, data); |
| |
| return data; |
| } |