blob: 8eb308694534130921a129a61073d28c1b548851 [file] [log] [blame]
/*
* 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.fOrigin = GrCCPRCoverageProcessor::kAtlasOrigin;
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;
}