/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrLayerAtlas_DEFINED
#define GrLayerAtlas_DEFINED
#include "GrTexture.h"
#include "SkPoint.h"
#include "SkTDArray.h"
#include "SkTInternalLList.h"
class GrLayerAtlas;
class GrTextureProvider;
class GrRectanizer;
// The backing GrTexture for a GrLayerAtlas is broken into a spatial grid of Plots. When
// the atlas needs space on the texture (i.e., in response to an addToAtlas call), it
// iterates through the plots in use by the requesting client looking for space and,
// if no space is found, opens up a new Plot for that client. The Plots keep track of
// subimage placement via their GrRectanizer.
//
// If all Plots are full, the replacement strategy is up to the client. The Plot::reset
// call will remove a Plot's knowledge of any allocated rects - freeing its space for reuse.
class GrLayerAtlas {
public:
class Plot {
SK_DECLARE_INTERNAL_LLIST_INTERFACE(Plot); // In an MRU llist
public:
// This returns a plot ID unique to each plot in the atlas. They are
// consecutive and start at 0.
int id() const { return fID; }
void reset();
private:
friend class GrLayerAtlas;
Plot();
~Plot(); // does not try to delete the fNext field
void init(int id, int offX, int offY, int width, int height);
bool allocateRect(int width, int height, SkIPoint16*);
int fID;
GrRectanizer* fRects;
SkIPoint16 fOffset; // the offset of the plot in the backing texture
};
// This class allows each client to independently track the Plots in
// which its data is stored.
// For example, multiple pictures may simultaneously store their layers in the
// layer atlas. When a picture goes away it can use the ClientPlotUsage to remove itself
// from those plots.
class ClientPlotUsage {
public:
ClientPlotUsage(int maxPlots)
SkDEBUGCODE(: fMaxPlots(maxPlots)) {
fPlots.setReserve(maxPlots);
}
bool isEmpty() const { return 0 == fPlots.count(); }
int numPlots() const { return fPlots.count(); }
Plot* plot(int index) { return fPlots[index]; }
void appendPlot(Plot* plot) {
SkASSERT(fPlots.count() <= fMaxPlots);
SkASSERT(!fPlots.contains(plot));
*fPlots.append() = plot;
}
// remove reference to 'plot'
void removePlot(const Plot* plot) {
int index = fPlots.find(const_cast<Plot*>(plot));
if (index >= 0) {
fPlots.remove(index);
}
}
#ifdef SK_DEBUG
bool contains(const Plot* plot) const {
return fPlots.contains(const_cast<Plot*>(plot));
}
#endif
private:
SkTDArray<Plot*> fPlots;
SkDEBUGCODE(int fMaxPlots;)
};
GrLayerAtlas(GrTextureProvider*, GrPixelConfig, GrSurfaceFlags flags,
const SkISize& backingTextureSize,
int numPlotsX, int numPlotsY);
~GrLayerAtlas();
// Requests a width x height block in the atlas. Upon success it returns
// the containing Plot and absolute location in the backing texture.
// nullptr is returned if there is no more space in the atlas.
Plot* addToAtlas(ClientPlotUsage*, int width, int height, SkIPoint16* loc);
GrTexture* getTextureOrNull() const {
return fTexture;
}
GrTexture* getTexture() const {
SkASSERT(fTexture);
return fTexture;
}
bool reattachBackingTexture();
void detachBackingTexture() {
fTexture.reset(nullptr);
}
void resetPlots();
enum IterOrder {
kLRUFirst_IterOrder,
kMRUFirst_IterOrder
};
typedef SkTInternalLList<Plot> PlotList;
typedef PlotList::Iter PlotIter;
Plot* iterInit(PlotIter* iter, IterOrder order) {
return iter->init(fPlotList, kLRUFirst_IterOrder == order
? PlotList::Iter::kTail_IterStart
: PlotList::Iter::kHead_IterStart);
}
private:
void createBackingTexture();
void makeMRU(Plot* plot);
GrTextureProvider* fTexProvider;
GrPixelConfig fPixelConfig;
GrSurfaceFlags fFlags;
SkAutoTUnref<GrTexture> fTexture;
SkISize fBackingTextureSize;
// allocated array of Plots
Plot* fPlotArray;
// LRU list of Plots (MRU at head - LRU at tail)
PlotList fPlotList;
};
#endif