| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrCCPRAtlas.h" |
| |
| #include "GrOnFlushResourceProvider.h" |
| #include "GrClip.h" |
| #include "GrRectanizer_skyline.h" |
| #include "GrTextureProxy.h" |
| #include "GrRenderTargetContext.h" |
| #include "SkMakeUnique.h" |
| #include "SkMathPriv.h" |
| #include "ccpr/GrCCPRCoverageProcessor.h" |
| #include "ops/GrDrawOp.h" |
| |
| class GrCCPRAtlas::Node { |
| public: |
| Node(std::unique_ptr<Node> previous, int l, int t, int r, int b) |
| : fPrevious(std::move(previous)) |
| , fX(l), fY(t) |
| , fRectanizer(r - l, b - t) {} |
| |
| Node* previous() const { return fPrevious.get(); } |
| |
| bool addRect(int w, int h, SkIPoint16* loc) { |
| static constexpr int kPad = 1; |
| |
| if (!fRectanizer.addRect(w + kPad, h + kPad, loc)) { |
| return false; |
| } |
| loc->fX += fX; |
| loc->fY += fY; |
| return true; |
| } |
| |
| private: |
| const std::unique_ptr<Node> fPrevious; |
| const int fX, fY; |
| GrRectanizerSkyline fRectanizer; |
| }; |
| |
| GrCCPRAtlas::GrCCPRAtlas(const GrCaps& caps, int minWidth, int minHeight) |
| : fMaxAtlasSize(caps.maxRenderTargetSize()) |
| , fDrawBounds{0, 0} { |
| SkASSERT(fMaxAtlasSize <= caps.maxTextureSize()); |
| SkASSERT(SkTMax(minWidth, minHeight) <= fMaxAtlasSize); |
| int initialSize = GrNextPow2(SkTMax(minWidth, minHeight)); |
| initialSize = SkTMax(int(kMinSize), initialSize); |
| initialSize = SkTMin(initialSize, fMaxAtlasSize); |
| fHeight = fWidth = initialSize; |
| fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, initialSize, initialSize); |
| } |
| |
| GrCCPRAtlas::~GrCCPRAtlas() { |
| } |
| |
| bool GrCCPRAtlas::addRect(int w, int h, SkIPoint16* loc) { |
| // This can't be called anymore once finalize() has been called. |
| SkASSERT(!fTextureProxy); |
| |
| if (!this->internalPlaceRect(w, h, loc)) { |
| return false; |
| } |
| |
| fDrawBounds.fWidth = SkTMax(fDrawBounds.width(), loc->x() + w); |
| fDrawBounds.fHeight = SkTMax(fDrawBounds.height(), loc->y() + h); |
| return true; |
| } |
| |
| bool GrCCPRAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) { |
| SkASSERT(SkTMax(w, h) < fMaxAtlasSize); |
| |
| for (Node* node = fTopNode.get(); node; node = node->previous()) { |
| if (node->addRect(w, h, loc)) { |
| return true; |
| } |
| } |
| |
| // The rect didn't fit. Grow the atlas and try again. |
| do { |
| SkASSERT(SkTMax(fWidth, fHeight) <= fMaxAtlasSize); |
| if (fWidth == fMaxAtlasSize && fHeight == fMaxAtlasSize) { |
| return false; |
| } |
| if (fHeight <= fWidth) { |
| int top = fHeight; |
| fHeight = SkTMin(fHeight * 2, fMaxAtlasSize); |
| fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight); |
| } else { |
| int left = fWidth; |
| fWidth = SkTMin(fWidth * 2, fMaxAtlasSize); |
| fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight); |
| } |
| } while (!fTopNode->addRect(w, h, loc)); |
| |
| return true; |
| } |
| |
| sk_sp<GrRenderTargetContext> GrCCPRAtlas::finalize(GrOnFlushResourceProvider* onFlushRP, |
| std::unique_ptr<GrDrawOp> atlasOp) { |
| SkASSERT(!fTextureProxy); |
| |
| GrSurfaceDesc desc; |
| desc.fWidth = fWidth; |
| desc.fHeight = fHeight; |
| desc.fConfig = kAlpha_half_GrPixelConfig; |
| sk_sp<GrRenderTargetContext> rtc = onFlushRP->makeRenderTargetContext(desc, nullptr, nullptr); |
| if (!rtc) { |
| SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n", |
| fWidth, fHeight); |
| return nullptr; |
| } |
| |
| SkIRect clearRect = SkIRect::MakeSize(fDrawBounds); |
| rtc->clear(&clearRect, 0, true); |
| rtc->addDrawOp(GrNoClip(), std::move(atlasOp)); |
| |
| fTextureProxy = sk_ref_sp(rtc->asTextureProxy()); |
| return rtc; |
| } |