Files
GDevelop/GDevelop.js/Bindings/ObjectJsImplementation.cpp
T
Aurélien Vivet 9d09ad4dd9 Add Bitmap Text objects, an efficient way to display texts using custom "bitmap fonts" (#2432)
* This allows to display texts on screen that use a "bitmap font", generated with softwares like [BMFont](https://www.angelcode.com/products/bmfont/) or [bmGlyph](http://www.bmglyph.com/).
* Bitmap fonts allow advanced effects and custom design of each character, with complete control over the appearance of the text. This is useful for making a custom score counter, titles, button labels...
* They also render very well in a pixel-perfect, pixel-art or retro-like game.
* Finally, these Bitmap Texts are fast and efficient to render on screen: useful for scores or texts that are updated frequently.
2021-05-07 14:57:00 +01:00

199 lines
7.2 KiB
C++

#include "ObjectJsImplementation.h"
#include <GDCore/IDE/Project/ArbitraryResourceWorker.h>
#include <GDCore/Project/Object.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Project/PropertyDescriptor.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <emscripten.h>
#include <map>
using namespace gd;
std::unique_ptr<gd::Object> ObjectJsImplementation::Clone() const {
ObjectJsImplementation* clone = new ObjectJsImplementation(*this);
// Copy the references to the JS implementations of the functions (because we
// want an object cloned from C++ to retain the functions implemented in JS).
EM_ASM_INT(
{
var clone = Module['wrapPointer']($0, Module['ObjectJsImplementation']);
var self = Module['wrapPointer']($1, Module['ObjectJsImplementation']);
clone['getProperties'] = self['getProperties'];
clone['updateProperty'] = self['updateProperty'];
clone['getInitialInstanceProperties'] =
self['getInitialInstanceProperties'];
clone['updateInitialInstanceProperty'] =
self['updateInitialInstanceProperty'];
},
(int)clone,
(int)this);
return std::unique_ptr<gd::Object>(clone);
}
std::map<gd::String, gd::PropertyDescriptor>
ObjectJsImplementation::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor>* jsCreatedProperties = nullptr;
std::map<gd::String, gd::PropertyDescriptor> copiedProperties;
jsCreatedProperties = (std::map<gd::String, gd::PropertyDescriptor>*)EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('getProperties'))
throw 'getProperties is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(UTF8ToString($1));
var newProperties = self['getProperties'](objectContent);
if (!newProperties)
throw 'getProperties returned nothing in a gd::ObjectJsImplementation.';
return getPointer(newProperties);
},
(int)this,
jsonContent.c_str());
copiedProperties = *jsCreatedProperties;
delete jsCreatedProperties;
return copiedProperties;
}
bool ObjectJsImplementation::UpdateProperty(const gd::String& arg0,
const gd::String& arg1) {
jsonContent = (const char*)EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('updateProperty'))
throw 'updateProperty is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(UTF8ToString($1));
self['updateProperty'](
objectContent, UTF8ToString($2), UTF8ToString($3));
return ensureString(JSON.stringify(objectContent));
},
(int)this,
jsonContent.c_str(),
arg0.c_str(),
arg1.c_str());
return true;
}
std::map<gd::String, gd::PropertyDescriptor>
ObjectJsImplementation::GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) {
std::map<gd::String, gd::PropertyDescriptor>* jsCreatedProperties = nullptr;
std::map<gd::String, gd::PropertyDescriptor> copiedProperties;
jsCreatedProperties = (std::map<gd::String, gd::PropertyDescriptor>*)EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('getInitialInstanceProperties'))
throw 'getInitialInstanceProperties is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(UTF8ToString($1));
var newProperties = self['getInitialInstanceProperties'](
objectContent,
wrapPointer($2, Module['InitialInstance']),
wrapPointer($3, Module['Project']),
wrapPointer($4, Module['Layout']));
if (!newProperties)
throw 'getInitialInstanceProperties returned nothing in a gd::ObjectJsImplementation.';
return getPointer(newProperties);
},
(int)this,
jsonContent.c_str(),
(int)&instance,
(int)&project,
(int)&scene);
copiedProperties = *jsCreatedProperties;
delete jsCreatedProperties;
return copiedProperties;
}
bool ObjectJsImplementation::UpdateInitialInstanceProperty(
gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) {
return EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('updateInitialInstanceProperty'))
throw 'updateInitialInstanceProperty is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(UTF8ToString($1));
return self['updateInitialInstanceProperty'](
objectContent,
wrapPointer($2, Module['InitialInstance']),
UTF8ToString($3),
UTF8ToString($4),
wrapPointer($5, Module['Project']),
wrapPointer($6, Module['Layout']));
},
(int)this,
jsonContent.c_str(),
(int)&instance,
name.c_str(),
value.c_str(),
(int)&project,
(int)&scene);
}
void ObjectJsImplementation::DoSerializeTo(SerializerElement& arg0) const {
arg0.AddChild("content") = gd::Serializer::FromJSON(jsonContent);
}
void ObjectJsImplementation::DoUnserializeFrom(Project& arg0,
const SerializerElement& arg1) {
jsonContent = gd::Serializer::ToJSON(arg1.GetChild("content"));
}
void ObjectJsImplementation::__destroy__() { // Useless?
EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('__destroy__'))
throw 'a JSImplementation must implement all functions, you forgot ObjectJsImplementation::__destroy__.';
self['__destroy__']();
},
(int)this);
}
void ObjectJsImplementation::ExposeResources(
gd::ArbitraryResourceWorker& worker) {
std::map<gd::String, gd::PropertyDescriptor> properties = GetProperties();
for (auto& property : properties) {
const String& propertyName = property.first;
const gd::PropertyDescriptor& propertyDescriptor = property.second;
if (propertyDescriptor.GetType() == "resource") {
auto& extraInfo = propertyDescriptor.GetExtraInfo();
const gd::String& resourceType = extraInfo.empty() ? "" : extraInfo[0];
const gd::String& oldPropertyValue = propertyDescriptor.GetValue();
gd::String newPropertyValue = oldPropertyValue;
if (resourceType == "image") {
worker.ExposeImage(newPropertyValue);
} else if (resourceType == "audio") {
worker.ExposeAudio(newPropertyValue);
} else if (resourceType == "font") {
worker.ExposeFont(newPropertyValue);
} else if (resourceType == "video") {
// Not supported in gd::ArbitraryResourceWorker
} else if (resourceType == "json") {
// Not supported in gd::ArbitraryResourceWorker
} else if (resourceType == "bitmapFont") {
worker.ExposeBitmapFont(newPropertyValue);
}
if (newPropertyValue != oldPropertyValue) {
UpdateProperty(propertyName, newPropertyValue);
}
}
}
}