| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrDDLTextureStripAtlas.h" |
| |
| #include "GrContextPriv.h" |
| #include "GrTexture.h" |
| #include "SkGr.h" |
| #include "SkTSearch.h" |
| |
| GrDDLTextureStripAtlas::GrDDLTextureStripAtlas(const Desc& desc) |
| : INHERITED(desc) |
| , fAtlasBitmap(nullptr) |
| , fMaxNumRows(desc.fHeight / desc.fRowHeight) |
| , fCurRow(0) |
| , fRows(new AtlasRow[fMaxNumRows]) { |
| SkASSERT(fMaxNumRows * fDesc.fRowHeight == fDesc.fHeight); |
| SkDEBUGCODE(this->validate();) |
| } |
| |
| GrDDLTextureStripAtlas::~GrDDLTextureStripAtlas() { delete[] fRows; } |
| |
| // Flush the current state of the atlas. |
| void GrDDLTextureStripAtlas::finish(GrProxyProvider* proxyProvider) { |
| SkDEBUGCODE(this->validate();) |
| |
| if (!fCurRow) { |
| SkASSERT(!fCurProxy && !fAtlasBitmap); |
| return; |
| } |
| |
| int height = fCurRow * fDesc.fRowHeight; |
| SkASSERT(height <= fDesc.fHeight); |
| |
| SkImageInfo ii = SkImageInfo::Make(fDesc.fWidth, height, |
| fDesc.fColorType, kPremul_SkAlphaType); |
| fAtlasBitmap->allocPixels(ii); |
| |
| for (int i = 0; i < fCurRow; ++i) { |
| SkASSERT(fRows[i].fBitmap.height() == fDesc.fRowHeight); |
| |
| int yPos = i * fDesc.fRowHeight; |
| fAtlasBitmap->writePixels(fRows[i].fBitmap.pixmap(), 0, yPos); |
| } |
| |
| GrUniqueKey key; |
| { |
| static const GrUniqueKey::Domain kTextureStripAtlasDomain = GrUniqueKey::GenerateDomain(); |
| GrUniqueKey::Builder builder(&key, kTextureStripAtlasDomain, fCurRow, |
| "DDL Texture Strip Atlas"); |
| for (int i = 0; i < fCurRow; ++i) { |
| builder[i] = fRows[i].fBitmap.getGenerationID(); |
| } |
| builder.finish(); |
| } |
| |
| sk_sp<GrTextureProxy> interloper = proxyProvider->findProxyByUniqueKey(key, |
| fCurProxy->origin()); |
| if (!interloper) { |
| // In the unlikely event that there is already a proxy with this key (i.e., it has exactly |
| // the same strips in exactly the same order) we'll just let it keep the key. |
| proxyProvider->assignUniqueKeyToProxy(key, fCurProxy.get()); |
| } |
| |
| // reset the state for the next aggregate texture |
| for (int i = 0; i < fCurRow; ++i) { |
| fRows[i].fBitmap.reset(); |
| } |
| fCurRow = 0; |
| fCurProxy = nullptr; |
| fAtlasBitmap = nullptr; |
| fKeyTable.rewind(); |
| SkDEBUGCODE(this->validate();) |
| } |
| |
| int GrDDLTextureStripAtlas::addStrip(GrContext* context, const SkBitmap& bitmap) { |
| SkDEBUGCODE(this->validate();) |
| |
| const int key = bitmap.getGenerationID(); |
| int index = this->searchByKey(key); |
| |
| if (fCurRow >= fMaxNumRows && index < 0) { |
| // The current atlas is full and adding another strip would make it overflow. Calve it off |
| // and allow the next block to start a new one. |
| this->finish(context->contextPriv().proxyProvider()); |
| index = this->searchByKey(key); // 'finish' cleared the table |
| } |
| |
| if (!fCurProxy) { |
| SkASSERT(!fAtlasBitmap); |
| |
| const GrCaps* caps = context->contextPriv().caps(); |
| GrPixelConfig pixelConfig = SkColorType2GrPixelConfig(fDesc.fColorType); |
| SkASSERT(kUnknown_GrPixelConfig != pixelConfig); |
| |
| SkBitmap* atlasBitmap = new SkBitmap(); |
| |
| fCurProxy = GrProxyProvider::MakeFullyLazyProxy( |
| [atlasBitmap, pixelConfig](GrResourceProvider* provider) -> sk_sp<GrSurface> { |
| if (!provider) { |
| delete atlasBitmap; |
| return sk_sp<GrSurface>(); |
| } |
| // When this is called 'atlasBitmap' should've been filled in and be |
| // non-empty |
| SkASSERT(atlasBitmap->width() && atlasBitmap->height()); |
| GrSurfaceDesc desc; |
| desc.fFlags = kNone_GrSurfaceFlags; |
| desc.fWidth = atlasBitmap->width(); |
| desc.fHeight = atlasBitmap->height(); |
| desc.fConfig = pixelConfig; |
| |
| GrMipLevel mipLevel = { atlasBitmap->getPixels(), atlasBitmap->rowBytes() }; |
| |
| return provider->createTexture(desc, SkBudgeted::kYes, |
| SkBackingFit::kExact, mipLevel); |
| }, |
| GrProxyProvider::Renderable::kNo, kTopLeft_GrSurfaceOrigin, pixelConfig, *caps); |
| |
| fAtlasBitmap = atlasBitmap; |
| } |
| |
| SkASSERT(bitmap.width() == fDesc.fWidth); |
| SkASSERT(bitmap.height() == fDesc.fRowHeight); |
| SkASSERT(!context->contextPriv().resourceProvider()); // This atlas class is DDL specific |
| SkASSERT(fCurRow < fMaxNumRows); |
| |
| int rowNumber = -1; |
| |
| if (index >= 0) { |
| // We already have the data in a row, so we can just return that row |
| AtlasRow* row = fKeyTable[index]; |
| |
| // Since all the rows are always stored in a contiguous array, we can save the memory |
| // required for storing row numbers and just compute it with some pointer arithmetic |
| rowNumber = static_cast<int>(row - fRows); |
| } else { |
| // ~index is the index where we will insert the new key to keep things sorted |
| index = ~index; |
| |
| rowNumber = fCurRow; |
| fRows[fCurRow].fBitmap = bitmap; |
| |
| AtlasRow* row = &fRows[rowNumber]; |
| fKeyTable.insert(index, 1, &row); |
| |
| ++fCurRow; |
| SkASSERT(fCurRow <= fMaxNumRows); |
| } |
| |
| SkASSERT(rowNumber >= 0); |
| SkDEBUGCODE(this->validate();) |
| return rowNumber; |
| } |
| |
| int GrDDLTextureStripAtlas::searchByKey(uint32_t generationID) { |
| static struct AtlasRowLessFunctor { |
| bool operator()(const AtlasRow* row, const uint32_t& id) const { |
| return row->fBitmap.getGenerationID() < id; |
| } |
| bool operator()(const uint32_t& id, const AtlasRow* row) const { |
| return id < row->fBitmap.getGenerationID(); |
| } |
| } functor; |
| |
| return SkTSearch(fKeyTable.begin(), fKeyTable.count(), generationID, sizeof(AtlasRow*), |
| functor); |
| } |
| |
| #ifdef SK_DEBUG |
| void GrDDLTextureStripAtlas::validate() { |
| static const int kBitmapInvalidGenID = 0; |
| |
| // Our key table should be sorted |
| uint32_t prev = fKeyTable.count() >= 1 ? fKeyTable[0]->fBitmap.getGenerationID() : 0; |
| for (int i = 1; i < fKeyTable.count(); ++i) { |
| AtlasRow* row = fKeyTable[i]; |
| SkASSERT(prev < row->fBitmap.getGenerationID()); |
| SkASSERT(row->fBitmap.getGenerationID() != kBitmapInvalidGenID); |
| prev = row->fBitmap.getGenerationID(); |
| } |
| |
| for (int i = 0; i < fCurRow; ++i) { |
| // These should all have a valid bitmap and be in the search table |
| SkASSERT(fRows[i].fBitmap.getGenerationID() != kBitmapInvalidGenID); |
| SkASSERT(this->searchByKey(fRows[i].fBitmap.getGenerationID()) >= 0); |
| } |
| for (int i = fCurRow; i < fMaxNumRows; ++i) { |
| // These should all be empty |
| SkASSERT(fRows[i].fBitmap.getGenerationID() == kBitmapInvalidGenID); |
| } |
| } |
| #endif |