1
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10
11#include "GrAtlas.h"
12#include "GrContext.h"
13#include "GrGpu.h"
14#include "GrRectanizer.h"
15#include "GrPlotMgr.h"
16
17#if 0
18#define GR_PLOT_WIDTH   8
19#define GR_PLOT_HEIGHT  4
20#define GR_ATLAS_WIDTH  256
21#define GR_ATLAS_HEIGHT 256
22
23#define GR_ATLAS_TEXTURE_WIDTH  (GR_PLOT_WIDTH * GR_ATLAS_WIDTH)
24#define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT)
25
26#else
27
28#define GR_ATLAS_TEXTURE_WIDTH  1024
29#define GR_ATLAS_TEXTURE_HEIGHT 2048
30
31#define GR_ATLAS_WIDTH  341
32#define GR_ATLAS_HEIGHT 341
33
34#define GR_PLOT_WIDTH   (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH)
35#define GR_PLOT_HEIGHT  (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT)
36
37#endif
38
39///////////////////////////////////////////////////////////////////////////////
40
41#define BORDER      1
42
43#if GR_DEBUG
44    static int gCounter;
45#endif
46
47// for testing
48#define FONT_CACHE_STATS 0
49#if FONT_CACHE_STATS
50static int g_UploadCount = 0;
51#endif
52
53GrAtlas::GrAtlas(GrAtlasMgr* mgr, int plotX, int plotY, GrMaskFormat format) {
54    fAtlasMgr = mgr;    // just a pointer, not an owner
55    fNext = NULL;
56    fUsed = false;
57
58    fTexture = mgr->getTexture(format); // we're not an owner, just a pointer
59    fPlot.set(plotX, plotY);
60
61    fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH - BORDER,
62                                   GR_ATLAS_HEIGHT - BORDER);
63
64    fMaskFormat = format;
65
66#if GR_DEBUG
67//    GrPrintf(" GrAtlas %p [%d %d] %d\n", this, plotX, plotY, gCounter);
68    gCounter += 1;
69#endif
70}
71
72GrAtlas::~GrAtlas() {
73    fAtlasMgr->freePlot(fMaskFormat, fPlot.fX, fPlot.fY);
74
75    delete fRects;
76
77#if GR_DEBUG
78    --gCounter;
79//    GrPrintf("~GrAtlas %p [%d %d] %d\n", this, fPlot.fX, fPlot.fY, gCounter);
80#endif
81}
82
83bool GrAtlas::RemoveUnusedAtlases(GrAtlasMgr* atlasMgr, GrAtlas** startAtlas) {
84    // GrAtlas** is used so that a pointer to the head element can be passed in and
85    // modified when the first element is deleted
86    GrAtlas** atlasRef = startAtlas;
87    GrAtlas* atlas = *startAtlas;
88    bool removed = false;
89    while (NULL != atlas) {
90        if (!atlas->used()) {
91            *atlasRef = atlas->fNext;
92            atlasMgr->deleteAtlas(atlas);
93            atlas = *atlasRef;
94            removed = true;
95        } else {
96            atlasRef = &atlas->fNext;
97            atlas = atlas->fNext;
98        }
99    }
100
101    return removed;
102}
103
104static void adjustForPlot(GrIPoint16* loc, const GrIPoint16& plot) {
105    loc->fX += plot.fX * GR_ATLAS_WIDTH;
106    loc->fY += plot.fY * GR_ATLAS_HEIGHT;
107}
108
109static uint8_t* zerofill(uint8_t* ptr, int count) {
110    while (--count >= 0) {
111        *ptr++ = 0;
112    }
113    return ptr;
114}
115
116bool GrAtlas::addSubImage(int width, int height, const void* image,
117                          GrIPoint16* loc) {
118    if (!fRects->addRect(width + BORDER, height + BORDER, loc)) {
119        return false;
120    }
121
122    SkAutoSMalloc<1024> storage;
123    int dstW = width + 2*BORDER;
124    int dstH = height + 2*BORDER;
125    if (BORDER) {
126        const int bpp = GrMaskFormatBytesPerPixel(fMaskFormat);
127        const size_t dstRB = dstW * bpp;
128        uint8_t* dst = (uint8_t*)storage.reset(dstH * dstRB);
129        Gr_bzero(dst, dstRB);                // zero top row
130        dst += dstRB;
131        for (int y = 0; y < height; y++) {
132            dst = zerofill(dst, bpp);   // zero left edge
133            memcpy(dst, image, width * bpp);
134            dst += width * bpp;
135            dst = zerofill(dst, bpp);   // zero right edge
136            image = (const void*)((const char*)image + width * bpp);
137        }
138        Gr_bzero(dst, dstRB);                // zero bottom row
139        image = storage.get();
140    }
141    adjustForPlot(loc, fPlot);
142    GrContext* context = fTexture->getContext();
143    // We pass the flag that does not force a flush. We assume our caller is
144    // smart and hasn't referenced the part of the texture we're about to update
145    // since the last flush.
146    context->writeTexturePixels(fTexture,
147                                loc->fX, loc->fY, dstW, dstH,
148                                fTexture->config(), image, 0,
149                                GrContext::kDontFlush_PixelOpsFlag);
150
151    // now tell the caller to skip the top/left BORDER
152    loc->fX += BORDER;
153    loc->fY += BORDER;
154
155#if FONT_CACHE_STATS
156    ++g_UploadCount;
157#endif
158
159    return true;
160}
161
162///////////////////////////////////////////////////////////////////////////////
163
164GrAtlasMgr::GrAtlasMgr(GrGpu* gpu) {
165    fGpu = gpu;
166    gpu->ref();
167    Gr_bzero(fTexture, sizeof(fTexture));
168    fPlotMgr = SkNEW_ARGS(GrPlotMgr, (GR_PLOT_WIDTH, GR_PLOT_HEIGHT));
169}
170
171GrAtlasMgr::~GrAtlasMgr() {
172    for (size_t i = 0; i < GR_ARRAY_COUNT(fTexture); i++) {
173        GrSafeUnref(fTexture[i]);
174    }
175    delete fPlotMgr;
176
177    fGpu->unref();
178#if FONT_CACHE_STATS
179      GrPrintf("Num uploads: %d\n", g_UploadCount);
180#endif
181}
182
183static GrPixelConfig maskformat2pixelconfig(GrMaskFormat format) {
184    switch (format) {
185        case kA8_GrMaskFormat:
186            return kAlpha_8_GrPixelConfig;
187        case kA565_GrMaskFormat:
188            return kRGB_565_GrPixelConfig;
189        case kA888_GrMaskFormat:
190            return kSkia8888_GrPixelConfig;
191        default:
192            GrAssert(!"unknown maskformat");
193    }
194    return kUnknown_GrPixelConfig;
195}
196
197GrAtlas* GrAtlasMgr::addToAtlas(GrAtlas** atlas,
198                                int width, int height, const void* image,
199                                GrMaskFormat format,
200                                GrIPoint16* loc) {
201    GrAssert(NULL == *atlas || (*atlas)->getMaskFormat() == format);
202
203    // iterate through entire atlas list, see if we can find a hole
204    GrAtlas* atlasIter = *atlas;
205    while (atlasIter) {
206        if (atlasIter->addSubImage(width, height, image, loc)) {
207            return atlasIter;
208        }
209        atlasIter = atlasIter->fNext;
210    }
211
212    // If the above fails, then either we have no starting atlas, or the current
213    // atlas list is full. Either way we need to allocate a new atlas
214
215    GrIPoint16 plot;
216    if (!fPlotMgr->newPlot(&plot)) {
217        return NULL;
218    }
219
220    GrAssert(0 == kA8_GrMaskFormat);
221    GrAssert(1 == kA565_GrMaskFormat);
222    if (NULL == fTexture[format]) {
223        // TODO: Update this to use the cache rather than directly creating a texture.
224        GrTextureDesc desc;
225        desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
226        desc.fWidth = GR_ATLAS_TEXTURE_WIDTH;
227        desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT;
228        desc.fConfig = maskformat2pixelconfig(format);
229
230        fTexture[format] = fGpu->createTexture(desc, NULL, 0);
231        if (NULL == fTexture[format]) {
232            return NULL;
233        }
234    }
235
236    GrAtlas* newAtlas = SkNEW_ARGS(GrAtlas, (this, plot.fX, plot.fY, format));
237    if (!newAtlas->addSubImage(width, height, image, loc)) {
238        delete newAtlas;
239        return NULL;
240    }
241
242    // new atlas, put at head
243    newAtlas->fNext = *atlas;
244    *atlas = newAtlas;
245
246    return newAtlas;
247}
248
249void GrAtlasMgr::freePlot(GrMaskFormat format, int x, int y) {
250    GrAssert(fPlotMgr->isBusy(x, y));
251    fPlotMgr->freePlot(x, y);
252}
253