Files
leocad/common/lc_texturemanager.cpp
T
Leonardo Zide 61be531444 Manage piece data ownership with scoped asset references
Replace manual piece-data retain/release bookkeeping with shared copyable
asset-reference handles. Pieces, models, selection, minifigs, thumbnails,
and stored insertion previews retain their dependencies explicitly, while
transient queries continue to borrow pointers.

Separate registered lookup and acquisition from asynchronous load requests
and explicit readiness waits. Keep loader-held references through work and
notification delivery, resetting shared request handles on the UI thread
and guarding callback-triggered library teardown.

Validated with the Qt 6.10 debug build and 26 offscreen lifecycle checks,
including cancellation, stale requests, copy/move operations, model/preview
teardown, undo, project merge/export, and loader shutdown.
2026-10-19 10:02:26 -07:00

125 lines
2.8 KiB
C++

#include "lc_global.h"
#include "lc_texturemanager.h"
#include "lc_file.h"
#include "lc_string.h"
lcTextureManager::~lcTextureManager()
{
Clear();
}
void lcTextureManager::Clear()
{
QMutexLocker Lock(&mMutex);
for (lcTexture* Texture : mTextures)
delete Texture;
mTextures.clear();
}
void lcTextureManager::AddTextureReference(lcTexture* Texture)
{
QMutexLocker Lock(&mMutex);
Texture->AddRef();
}
lcTexture* lcTextureManager::AcquireTexture(const char* TextureName, const lcFileSource& Source, const QString& ProjectPath)
{
if (!TextureName[0] || strlen(TextureName) >= LC_TEXTURE_NAME_LEN)
return nullptr;
if (!Source.EmbeddedData && Source.FilePath.isEmpty() && Source.ArchiveIndex == -1)
return nullptr;
QMutexLocker Lock(&mMutex);
for (lcTexture* Texture : mTextures)
{
if (Texture->mSource != Source)
continue;
Texture->AddRef();
return Texture;
}
lcTexture* Texture = new lcTexture(LC_TEXTURE_MIPMAPS);
lcstrcpy(Texture->mName, TextureName);
Texture->mSource = Source;
Texture->SetTemporary(bool(Source.EmbeddedData) || !ProjectPath.isEmpty());
Texture->AddRef();
mTextures.push_back(Texture);
return Texture;
}
lcTextureBuildResult lcTextureManager::BuildTextureData(lcFile& File)
{
lcTextureBuildResult Result = DecodeTextureData(File);
if (!Result.DecodedImage.isNull())
{
Result.DecodedImage = lcTexture::PrepareImage(std::move(Result.DecodedImage));
if (Result.DecodedImage.isNull())
Result.Error = lcTextureLoadError::DecodeFailed;
}
return Result;
}
lcTextureBuildResult lcTextureManager::DecodeTextureData(lcFile& File)
{
lcTextureBuildResult Result;
bool Loaded = false;
if (lcMemFile* MemoryFile = dynamic_cast<lcMemFile*>(&File))
{
if (MemoryFile->mBuffer && MemoryFile->mPosition < MemoryFile->mFileSize)
{
const size_t Length = MemoryFile->mFileSize - MemoryFile->mPosition;
if (Length <= static_cast<size_t>(std::numeric_limits<int>::max()))
Loaded = Result.DecodedImage.loadFromData(MemoryFile->mBuffer + MemoryFile->mPosition, static_cast<int>(Length));
}
}
else if (lcDiskFile* DiskFile = dynamic_cast<lcDiskFile*>(&File))
{
QFile& Device = DiskFile->GetFile();
const qint64 Position = Device.pos();
if (Position >= 0)
{
Loaded = Result.DecodedImage.load(&Device, nullptr);
const bool Restored = Device.seek(Position);
Loaded = Loaded && Restored;
}
}
if (!Loaded || Result.DecodedImage.isNull())
{
Result.DecodedImage = QImage();
Result.Error = lcTextureLoadError::DecodeFailed;
}
return Result;
}
void lcTextureManager::ReleaseTexture(lcTexture* Texture)
{
QMutexLocker Lock(&mMutex);
if (Texture->Release() == 0 && Texture->IsTemporary())
{
std::vector<lcTexture*>::iterator TextureIt = std::find(mTextures.begin(), mTextures.end(), Texture);
if (TextureIt != mTextures.end())
mTextures.erase(TextureIt);
delete Texture;
}
}