Add support for directly setting image assets and processing Image userdata
This commit is contained in:
parent
ff6e349aef
commit
560ae08424
@ -532,7 +532,10 @@ add_subdirectory(core)
|
||||
|
||||
# Less general purpose code than core that is available to both the game and
|
||||
# application modules.
|
||||
set(STAR_BASE_INCLUDES ${PROJECT_SOURCE_DIR}/base)
|
||||
set(STAR_BASE_INCLUDES
|
||||
${PROJECT_SOURCE_DIR}/base
|
||||
${PROJECT_SOURCE_DIR}/base/scripting
|
||||
)
|
||||
add_subdirectory(base)
|
||||
|
||||
# Platform APIs that are implemented by the application module
|
||||
|
@ -17,9 +17,11 @@ SET (star_base_HEADERS
|
||||
StarMemoryAssetSource.hpp
|
||||
StarMixer.hpp
|
||||
StarPackedAssetSource.hpp
|
||||
StarRootBase.hpp
|
||||
StarVersion.hpp
|
||||
StarVersionOptionParser.hpp
|
||||
StarWorldGeometry.hpp
|
||||
scripting/StarImageLuaBindings.hpp
|
||||
)
|
||||
|
||||
SET (star_base_SOURCES
|
||||
@ -32,8 +34,10 @@ SET (star_base_SOURCES
|
||||
StarMemoryAssetSource.cpp
|
||||
StarMixer.cpp
|
||||
StarPackedAssetSource.cpp
|
||||
StarRootBase.cpp
|
||||
StarVersionOptionParser.cpp
|
||||
StarWorldGeometry.cpp
|
||||
scripting/StarImageLuaBindings.cpp
|
||||
)
|
||||
|
||||
CONFIGURE_FILE (StarVersion.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp)
|
||||
|
@ -145,7 +145,10 @@ Assets::Assets(Settings settings, StringList assetSources) {
|
||||
ByteArray bytes;
|
||||
if (auto str = engine.luaMaybeTo<String>(data))
|
||||
bytes = ByteArray(str->utf8Ptr(), str->utf8Size());
|
||||
else {
|
||||
else if (auto image = engine.luaMaybeTo<Image>(data)) {
|
||||
newFiles->set(path, std::move(*image));
|
||||
return;
|
||||
} else {
|
||||
auto json = engine.luaTo<Json>(data).repr();
|
||||
bytes = ByteArray(json.utf8Ptr(), json.utf8Size());
|
||||
}
|
||||
@ -855,6 +858,19 @@ ByteArray Assets::read(String const& path) const {
|
||||
throw AssetException(strf("No such asset '{}'", path));
|
||||
}
|
||||
|
||||
ImageConstPtr Assets::readImage(String const& path) const {
|
||||
if (auto p = m_files.ptr(path)) {
|
||||
ImageConstPtr image;
|
||||
if (auto memorySource = as<MemoryAssetSource>(p->source))
|
||||
image = memorySource->image(p->sourceName);
|
||||
if (!image)
|
||||
image = make_shared<Image>(Image::readPng(p->source->open(p->sourceName)));
|
||||
return image;
|
||||
}
|
||||
throw AssetException(strf("No such asset '{}'", path));
|
||||
}
|
||||
|
||||
|
||||
Json Assets::checkPatchArray(String const& path, AssetSourcePtr const& source, Json const result, JsonArray const patchData, Maybe<Json> const external) const {
|
||||
auto externalRef = external.value();
|
||||
auto newResult = result;
|
||||
@ -1140,7 +1156,7 @@ shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
|
||||
} else {
|
||||
auto imageData = make_shared<ImageData>();
|
||||
imageData->image = unlockDuring([&]() {
|
||||
return make_shared<Image>(Image::readPng(open(path.basePath)));
|
||||
return readImage(path.basePath);
|
||||
});
|
||||
imageData->frames = bestFramesSpecification(path.basePath);
|
||||
|
||||
|
@ -275,6 +275,7 @@ private:
|
||||
|
||||
IODevicePtr open(String const& basePath) const;
|
||||
ByteArray read(String const& basePath) const;
|
||||
ImageConstPtr readImage(String const& path) const;
|
||||
|
||||
Json readJson(String const& basePath) const;
|
||||
Json checkPatchArray(String const& path, AssetSourcePtr const& source, Json const result, JsonArray const patchData, Maybe<Json> const external) const;
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StarMemoryAssetSource.hpp"
|
||||
#include "StarDataStreamDevices.hpp"
|
||||
#include "StarDataStreamExtra.hpp"
|
||||
#include "StarSha256.hpp"
|
||||
#include "StarImage.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -21,11 +21,17 @@ StringList MemoryAssetSource::assetPaths() const {
|
||||
|
||||
IODevicePtr MemoryAssetSource::open(String const& path) {
|
||||
struct AssetReader : public IODevice {
|
||||
AssetReader(ByteArrayPtr assetData, String name) : assetData(assetData), name(name) { setMode(IOMode::Read); }
|
||||
AssetReader(char* assetData, size_t assetSize, String name) {
|
||||
this->assetData = assetData;
|
||||
this->assetSize = assetSize;
|
||||
this->name = std::move(name);
|
||||
setMode(IOMode::Read);
|
||||
}
|
||||
|
||||
size_t read(char* data, size_t len) override {
|
||||
len = min<StreamOffset>(len, StreamOffset(assetData->size()) - assetPos);
|
||||
assetData->copyTo(data, len);
|
||||
len = min<StreamOffset>(len, StreamOffset(assetSize) - assetPos);
|
||||
memcpy(data, assetData + assetPos, len);
|
||||
assetPos += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -33,25 +39,26 @@ IODevicePtr MemoryAssetSource::open(String const& path) {
|
||||
throw IOException("Assets IODevices are read-only");
|
||||
}
|
||||
|
||||
StreamOffset size() override { return assetData->size(); }
|
||||
StreamOffset size() override { return assetSize; }
|
||||
StreamOffset pos() override { return assetPos; }
|
||||
|
||||
String deviceName() const override { return name; }
|
||||
|
||||
bool atEnd() override {
|
||||
return assetPos >= assetData->size();
|
||||
return assetPos >= assetSize;
|
||||
}
|
||||
|
||||
void seek(StreamOffset p, IOSeek mode) override {
|
||||
if (mode == IOSeek::Absolute)
|
||||
assetPos = p;
|
||||
else if (mode == IOSeek::Relative)
|
||||
assetPos = clamp<StreamOffset>(assetPos + p, 0, assetData->size());
|
||||
assetPos = clamp<StreamOffset>(assetPos + p, 0, assetSize);
|
||||
else
|
||||
assetPos = clamp<StreamOffset>(assetPos - p, 0, assetData->size());
|
||||
assetPos = clamp<StreamOffset>(assetPos - p, 0, assetSize);
|
||||
}
|
||||
|
||||
ByteArrayPtr assetData;
|
||||
char* assetData;
|
||||
size_t assetSize;
|
||||
StreamOffset assetPos = 0;
|
||||
String name;
|
||||
};
|
||||
@ -59,8 +66,12 @@ IODevicePtr MemoryAssetSource::open(String const& path) {
|
||||
auto p = m_files.ptr(path);
|
||||
if (!p)
|
||||
throw AssetSourceException::format("Requested file '{}' does not exist in memory", path);
|
||||
|
||||
return make_shared<AssetReader>(*p, path);
|
||||
else if (auto byteArray = p->ptr<ByteArray>())
|
||||
return make_shared<AssetReader>(byteArray->ptr(), byteArray->size(), path);
|
||||
else {
|
||||
auto image = p->get<ImagePtr>().get();
|
||||
return make_shared<AssetReader>((char*)image->data(), image->width() * image->height() * image->bytesPerPixel(), path);
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryAssetSource::empty() const {
|
||||
@ -76,15 +87,37 @@ bool MemoryAssetSource::erase(String const& path) {
|
||||
}
|
||||
|
||||
void MemoryAssetSource::set(String const& path, ByteArray data) {
|
||||
m_files[path] = make_shared<ByteArray>(std::move(data));
|
||||
m_files[path] = std::move(data);
|
||||
}
|
||||
|
||||
void MemoryAssetSource::set(String const& path, Image const& image) {
|
||||
m_files[path] = make_shared<Image>(image);
|
||||
}
|
||||
|
||||
void MemoryAssetSource::set(String const& path, Image&& image) {
|
||||
m_files[path] = make_shared<Image>(std::move(image));
|
||||
}
|
||||
|
||||
ByteArray MemoryAssetSource::read(String const& path) {
|
||||
auto p = m_files.ptr(path);
|
||||
if (!p)
|
||||
throw AssetSourceException::format("Requested file '{}' does not exist in memory", path);
|
||||
else if (auto bytes = p->ptr<ByteArray>())
|
||||
return *bytes;
|
||||
else {
|
||||
Image const* image = p->get<ImagePtr>().get();
|
||||
return ByteArray((char const*)image->data(), image->width() * image->height() * image->bytesPerPixel());
|
||||
}
|
||||
}
|
||||
|
||||
ImageConstPtr MemoryAssetSource::image(String const& path) {
|
||||
auto p = m_files.ptr(path);
|
||||
if (!p)
|
||||
throw AssetSourceException::format("Requested file '{}' does not exist in memory", path);
|
||||
else if (auto imagePtr = p->ptr<ImagePtr>())
|
||||
return *imagePtr;
|
||||
else
|
||||
return *p->get(); // this is a double indirection, and that freaking sucks!!
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(MemoryAssetSource);
|
||||
STAR_CLASS(Image);
|
||||
|
||||
class MemoryAssetSource : public AssetSource {
|
||||
public:
|
||||
@ -15,17 +16,23 @@ public:
|
||||
JsonObject metadata() const override;
|
||||
StringList assetPaths() const override;
|
||||
|
||||
// do not use the returned IODevice after the file is gone or bad things will happen
|
||||
IODevicePtr open(String const& path) override;
|
||||
|
||||
bool empty() const;
|
||||
bool contains(String const& path) const;
|
||||
bool erase(String const& path);
|
||||
void set(String const& path, ByteArray data);
|
||||
void set(String const& path, Image const& image);
|
||||
void set(String const& path, Image&& image);
|
||||
ByteArray read(String const& path) override;
|
||||
ImageConstPtr image(String const& path);
|
||||
private:
|
||||
typedef Variant<ByteArray, ImagePtr> FileEntry;
|
||||
|
||||
String m_name;
|
||||
JsonObject m_metadata;
|
||||
StringMap<ByteArrayPtr> m_files;
|
||||
StringMap<FileEntry> m_files;
|
||||
};
|
||||
|
||||
}
|
||||
|
23
source/base/StarRootBase.cpp
Normal file
23
source/base/StarRootBase.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "StarRootBase.hpp"
|
||||
|
||||
namespace Star {
|
||||
atomic<RootBase*> RootBase::s_singleton;
|
||||
|
||||
RootBase* RootBase::singletonPtr() {
|
||||
return s_singleton.load();
|
||||
}
|
||||
|
||||
RootBase& RootBase::singleton() {
|
||||
auto ptr = s_singleton.load();
|
||||
if (!ptr)
|
||||
throw RootException("RootBase::singleton() called with no Root instance available");
|
||||
else
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
RootBase::RootBase() {
|
||||
RootBase* oldRoot = nullptr;
|
||||
if (!s_singleton.compare_exchange_strong(oldRoot, this))
|
||||
throw RootException("Singleton Root has been constructed twice");
|
||||
}
|
||||
}
|
24
source/base/StarRootBase.hpp
Normal file
24
source/base/StarRootBase.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "StarAssets.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(Configuration);
|
||||
|
||||
STAR_EXCEPTION(RootException, StarException);
|
||||
|
||||
class RootBase {
|
||||
public:
|
||||
static RootBase* singletonPtr();
|
||||
static RootBase& singleton();
|
||||
|
||||
virtual AssetsConstPtr assets() = 0;
|
||||
virtual ConfigurationPtr configuration() = 0;
|
||||
protected:
|
||||
RootBase();
|
||||
|
||||
static atomic<RootBase*> s_singleton;
|
||||
};
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include "StarImageLuaBindings.hpp"
|
||||
#include "StarLuaConverters.hpp"
|
||||
#include "StarImage.hpp"
|
||||
#include "StarRootBase.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -22,6 +23,15 @@ LuaMethods<Image> LuaUserDataMethods<Image>::make() {
|
||||
return image.subImage(min, size);
|
||||
});
|
||||
|
||||
methods.registerMethod("process", [](Image& image, String const& directives) {
|
||||
return processImageOperations(parseImageOperations(directives), image, [](String const& path) -> Image const* {
|
||||
if (auto root = RootBase::singletonPtr())
|
||||
return root->assets()->image(path).get();
|
||||
else
|
||||
return nullptr;
|
||||
});
|
||||
});
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
@ -127,7 +127,6 @@ SET (star_core_HEADERS
|
||||
StarWorkerPool.hpp
|
||||
StarXXHash.hpp
|
||||
StarZSTDCompression.hpp
|
||||
scripting/StarImageLuaBindings.hpp
|
||||
scripting/StarUtilityLuaBindings.hpp
|
||||
)
|
||||
|
||||
@ -185,7 +184,6 @@ SET (star_core_SOURCES
|
||||
StarUuid.cpp
|
||||
StarWorkerPool.cpp
|
||||
StarZSTDCompression.cpp
|
||||
scripting/StarImageLuaBindings.cpp
|
||||
scripting/StarUtilityLuaBindings.cpp
|
||||
)
|
||||
|
||||
|
@ -60,25 +60,19 @@ namespace {
|
||||
unsigned const RootLoadThreads = 4;
|
||||
}
|
||||
|
||||
atomic<Root*> Root::s_singleton;
|
||||
|
||||
Root* Root::singletonPtr() {
|
||||
return s_singleton.load();
|
||||
return dynamic_cast<Root*>(s_singleton.load());
|
||||
}
|
||||
|
||||
Root& Root::singleton() {
|
||||
auto ptr = s_singleton.load();
|
||||
auto ptr = singletonPtr();
|
||||
if (!ptr)
|
||||
throw RootException("Root::singleton() called with no Root instance available");
|
||||
else
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
Root::Root(Settings settings) {
|
||||
Root* oldRoot = nullptr;
|
||||
if (!s_singleton.compare_exchange_strong(oldRoot, this))
|
||||
throw RootException("Singleton Root has been constructed twice");
|
||||
|
||||
Root::Root(Settings settings) : RootBase() {
|
||||
m_settings = std::move(settings);
|
||||
if (m_settings.runtimeConfigFile)
|
||||
m_runtimeConfigFile = toStoragePath(*m_settings.runtimeConfigFile);
|
||||
|
@ -1,14 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "StarRootBase.hpp"
|
||||
#include "StarJson.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarListener.hpp"
|
||||
#include "StarAssets.hpp"
|
||||
#include "StarConfiguration.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(Configuration);
|
||||
STAR_CLASS(ItemDatabase);
|
||||
STAR_CLASS(MaterialDatabase);
|
||||
STAR_CLASS(ObjectDatabase);
|
||||
@ -50,15 +49,13 @@ STAR_CLASS(CollectionDatabase);
|
||||
|
||||
STAR_CLASS(Root);
|
||||
|
||||
STAR_EXCEPTION(RootException, StarException);
|
||||
|
||||
// Singleton Root object for starbound providing access to the unique
|
||||
// Configuration class, as well as the assets, root factories, and databases.
|
||||
// Root, and all members of Root, should be thread safe. Root initialization
|
||||
// should be completed before any code dependent on Root is started in any
|
||||
// thread, and all Root dependent code in any thread should be finished before
|
||||
// letting Root destruct.
|
||||
class Root {
|
||||
class Root : public RootBase {
|
||||
public:
|
||||
struct Settings {
|
||||
Assets::Settings assetsSettings;
|
||||
@ -135,8 +132,8 @@ public:
|
||||
// All of the Root member accessors are safe to call at any time after Root
|
||||
// initialization, if they are not loaded they will load before returning.
|
||||
|
||||
AssetsConstPtr assets();
|
||||
ConfigurationPtr configuration();
|
||||
AssetsConstPtr assets() override;
|
||||
ConfigurationPtr configuration() override;
|
||||
|
||||
ObjectDatabaseConstPtr objectDatabase();
|
||||
PlantDatabaseConstPtr plantDatabase();
|
||||
@ -164,7 +161,6 @@ public:
|
||||
TreasureDatabaseConstPtr treasureDatabase();
|
||||
DungeonDefinitionsConstPtr dungeonDefinitions();
|
||||
TilesetDatabaseConstPtr tilesetDatabase();
|
||||
StatisticsDatabaseConstPtr statisicsDatabase();
|
||||
StatisticsDatabaseConstPtr statisticsDatabase();
|
||||
EmoteProcessorConstPtr emoteProcessor();
|
||||
SpeciesDatabaseConstPtr speciesDatabase();
|
||||
@ -191,8 +187,6 @@ private:
|
||||
// m_configurationMutex must be held when calling
|
||||
void writeConfig();
|
||||
|
||||
static atomic<Root*> s_singleton;
|
||||
|
||||
Settings m_settings;
|
||||
|
||||
Mutex m_modsMutex;
|
||||
|
Loading…
Reference in New Issue
Block a user