mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-18 20:43:04 +01:00
Start working on custom image APIs
This commit is contained in:
102
distribution/openrct2.d.ts
vendored
102
distribution/openrct2.d.ts
vendored
@@ -2794,6 +2794,108 @@ declare global {
|
||||
* Useful for displaying how fragmented the allocated image list is.
|
||||
*/
|
||||
getAvailableAllocationRanges(): ImageIndexRange[];
|
||||
|
||||
/**
|
||||
* Allocates one or more contigous image IDs.
|
||||
* @param count The number of image IDs to allocate.
|
||||
* @returns the range of allocated image IDs or null if the range could not be allocated.
|
||||
*/
|
||||
allocate(count: number): ImageIndexRange | null;
|
||||
|
||||
/**
|
||||
* Frees one or more contigous image IDs.
|
||||
* An error will occur if attempting the given range contains an ID not owned by the plugin.
|
||||
* @param range The range of images to free.
|
||||
*/
|
||||
free(range: ImageIndexRange): void;
|
||||
|
||||
/**
|
||||
* Sets the pixel data for a given image ID.
|
||||
*
|
||||
* Will error if given an ID of an image not owned by this plugin.
|
||||
* @param id The id of the image to set the pixels of.
|
||||
* @param data The pixel data.
|
||||
*/
|
||||
setPixelData(id: number, data: PixelData): void;
|
||||
|
||||
/**
|
||||
* Gets a {@link GraphicsContext} for the given image so that you can draw directly to it.
|
||||
* Allocates or reallocates the image if not previously allocated or if the size is changed.
|
||||
* The pixels of the image will persist between calls, so you can draw over the top of what
|
||||
* is currently there. The default pixel colour will be 0 (transparent).
|
||||
*
|
||||
* Drawing a large number of pixels each frame can be expensive, so caching as many as you
|
||||
* can in images is a good way to improve performance.
|
||||
*
|
||||
* Will error if given an ID of an image not owned by this plugin.
|
||||
* @param id The id of the image to get a {@link GraphicsContext} for.
|
||||
* @param size The size the image that should be allocated.
|
||||
*/
|
||||
getGraphicsContext(id: number, size: ScreenSize): GraphicsContext;
|
||||
}
|
||||
|
||||
type PixelData = RawPixelData | RlePixelData | PngPixelData;
|
||||
|
||||
/**
|
||||
* Raw pixel data that is not encoded. A contiguous sequence of bytes
|
||||
* representing the 8bpp pixel values with a optional padding between
|
||||
* each horizontal row.
|
||||
*/
|
||||
interface RawPixelData {
|
||||
type: 'raw';
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
/**
|
||||
* The length of each horizontal row in bytes.
|
||||
*/
|
||||
stride?: number;
|
||||
|
||||
/**
|
||||
* Data can either by a:
|
||||
* - A base64 string.
|
||||
* - An array of bytes
|
||||
* - A {@link Uint8Array} of bytes
|
||||
*/
|
||||
data: string | number | Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pixel data that is encoded as RCT run-length encoded data.
|
||||
*/
|
||||
interface RlePixelData {
|
||||
type: 'rle';
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
/**
|
||||
* Data can either by a:
|
||||
* - A base64 string.
|
||||
* - An array of bytes
|
||||
* - A {@link Uint8Array} of bytes
|
||||
*/
|
||||
data: string | number | Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pixel data that is encoded as a .png file.
|
||||
*/
|
||||
interface PngPixelData {
|
||||
type: 'png';
|
||||
|
||||
/**
|
||||
* If keep is specified for palette, the raw 8bpp .png bytes will be loaded. The palette
|
||||
* in the .png will not be read. This will improve load performance.
|
||||
*/
|
||||
palette?: 'keep';
|
||||
|
||||
/**
|
||||
* Data can either by a:
|
||||
* - A base64 string.
|
||||
* - An array of bytes
|
||||
* - A {@link Uint8Array} of bytes
|
||||
*/
|
||||
data: string | number | Uint8Array;
|
||||
}
|
||||
|
||||
interface ImageIndexRange {
|
||||
|
||||
@@ -32,6 +32,9 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
dukglue_register_method(ctx, &ScImageManager::getPredefinedRange, "getPredefinedRange");
|
||||
dukglue_register_method(ctx, &ScImageManager::getAvailableAllocationRanges, "getAvailableAllocationRanges");
|
||||
dukglue_register_method(ctx, &ScImageManager::allocate, "allocate");
|
||||
dukglue_register_method(ctx, &ScImageManager::free, "free");
|
||||
dukglue_register_method(ctx, &ScImageManager::setPixelData, "setPixelData");
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -74,6 +77,150 @@ namespace OpenRCT2::Scripting
|
||||
return DukValue::take_from_stack(_ctx);
|
||||
}
|
||||
|
||||
DukValue allocate(int32_t count)
|
||||
{
|
||||
std::vector<rct_g1_element> images;
|
||||
images.resize(count);
|
||||
|
||||
auto base = gfx_object_allocate_images(images.data(), count);
|
||||
if (base == ImageIndexUndefined)
|
||||
{
|
||||
return ToDuk(_ctx, nullptr);
|
||||
}
|
||||
return CreateImageIndexRange(base, count);
|
||||
}
|
||||
|
||||
void free(const DukValue& range)
|
||||
{
|
||||
auto start = range["start"].as_int();
|
||||
auto count = range["count"].as_int();
|
||||
gfx_object_free_images(start, count);
|
||||
}
|
||||
|
||||
static void setPixelDataFromBuffer(
|
||||
uint8_t* dst, const uint8_t* src, size_t srcLen, int32_t width, int32_t height, int32_t stride)
|
||||
{
|
||||
auto srcEnd = src + srcLen;
|
||||
auto dstLen = static_cast<size_t>(width) * height;
|
||||
if (stride == width)
|
||||
{
|
||||
std::memcpy(dst, src, std::min(srcLen, dstLen));
|
||||
if (dstLen > srcLen)
|
||||
{
|
||||
std::memset(dst + srcLen, 0, dstLen - srcLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memset(dst, 0, dstLen);
|
||||
auto srcLine = src;
|
||||
for (int32_t y = 0; y < height; y++)
|
||||
{
|
||||
auto dstI = y * width;
|
||||
auto lineWidth = std::min<size_t>(srcEnd - srcLine, width);
|
||||
std::memcpy(dst + dstI, srcLine, lineWidth);
|
||||
if (lineWidth < width)
|
||||
{
|
||||
break;
|
||||
}
|
||||
srcLine += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelData(int32_t id, const DukValue& pixelData)
|
||||
{
|
||||
auto dukType = pixelData["type"];
|
||||
auto& type = dukType.as_string();
|
||||
if (type == "raw")
|
||||
{
|
||||
auto width = pixelData["width"].as_int();
|
||||
auto height = pixelData["height"].as_int();
|
||||
auto stride = AsOrDefault(pixelData["stride"], width);
|
||||
auto data = pixelData["data"];
|
||||
auto padding = stride - width;
|
||||
|
||||
auto imageData = new (std::nothrow) uint8_t[width * height];
|
||||
if (imageData == nullptr)
|
||||
{
|
||||
duk_pop(_ctx);
|
||||
duk_error(_ctx, DUK_ERR_ERROR, "Unable to allocate memory for pixel data.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Setup the g1 element
|
||||
rct_g1_element el{};
|
||||
auto* lastel = gfx_get_g1_element(id);
|
||||
if (lastel != nullptr)
|
||||
{
|
||||
el = *lastel;
|
||||
delete el.offset;
|
||||
}
|
||||
|
||||
el.offset = imageData;
|
||||
el.width = width;
|
||||
el.height = height;
|
||||
gfx_set_g1_element(id, &el);
|
||||
|
||||
// Set the pixel data
|
||||
if (data.is_array())
|
||||
{
|
||||
// From array of numbers
|
||||
data.push();
|
||||
duk_uarridx_t i = 0;
|
||||
for (int32_t y = 0; y < height; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < width; x++)
|
||||
{
|
||||
auto dstI = y * width + x;
|
||||
if (duk_get_prop_index(_ctx, -1, i))
|
||||
{
|
||||
imageData[dstI] = duk_get_int(_ctx, -1) & 0xFF;
|
||||
duk_pop(_ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageData[dstI] = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i += padding;
|
||||
}
|
||||
duk_pop(_ctx);
|
||||
}
|
||||
else if (data.type() == DukValue::Type::STRING)
|
||||
{
|
||||
// From base64 string
|
||||
data.push();
|
||||
duk_base64_decode(_ctx, -1);
|
||||
duk_size_t bufferLen{};
|
||||
const auto* buffer = reinterpret_cast<uint8_t*>(duk_get_buffer_data(_ctx, -1, &bufferLen));
|
||||
if (buffer != nullptr)
|
||||
{
|
||||
setPixelDataFromBuffer(imageData, buffer, bufferLen, width, height, stride);
|
||||
}
|
||||
duk_pop(_ctx);
|
||||
}
|
||||
else if (data.type() == DukValue::Type::OBJECT)
|
||||
{
|
||||
// From Uint8Array
|
||||
data.push();
|
||||
duk_size_t bufferLen{};
|
||||
const auto* buffer = reinterpret_cast<uint8_t*>(duk_get_buffer_data(_ctx, -1, &bufferLen));
|
||||
if (buffer != nullptr)
|
||||
{
|
||||
setPixelDataFromBuffer(imageData, buffer, bufferLen, width, height, stride);
|
||||
}
|
||||
duk_pop(_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(_ctx, DUK_ERR_ERROR, "Unsupported pixel data type.");
|
||||
}
|
||||
}
|
||||
|
||||
DukValue CreateImageIndexRange(size_t start, size_t count) const
|
||||
{
|
||||
DukObject obj(_ctx);
|
||||
|
||||
@@ -590,6 +590,11 @@ void ScriptEngine::StopUnloadRegisterAllPlugins()
|
||||
|
||||
void ScriptEngine::LoadTransientPlugins()
|
||||
{
|
||||
if (!_initialised)
|
||||
{
|
||||
Initialise();
|
||||
RefreshPlugins();
|
||||
}
|
||||
_transientPluginsEnabled = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user