diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 312beba13b..cb3b6329d1 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -2829,7 +2829,9 @@ declare global { setPixelData(id: number, data: PixelData): void; /** - * Gets a {@link GraphicsContext} for the given image so that you can draw directly to it. + * Calls the given function with a {@link GraphicsContext} for the given image, allowing the + * ability to 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). @@ -2838,10 +2840,11 @@ declare global { * 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 id The id of the image to draw to. * @param size The size the image that should be allocated. + * @param callback The function that will draw to the image. */ - getGraphicsContext(id: number, size: ScreenSize): GraphicsContext; + draw(id: number, size: ScreenSize, callback: (g: GraphicsContext) => void): void; } type PixelData = RawPixelData | RlePixelData | PngPixelData; diff --git a/src/openrct2-ui/scripting/CustomImages.cpp b/src/openrct2-ui/scripting/CustomImages.cpp index 5204148806..826f512fc4 100644 --- a/src/openrct2-ui/scripting/CustomImages.cpp +++ b/src/openrct2-ui/scripting/CustomImages.cpp @@ -11,9 +11,12 @@ # include "CustomImages.h" +# include "ScGraphicsContext.hpp" + # include # include # include +# include # include using namespace OpenRCT2::Drawing; @@ -393,6 +396,7 @@ namespace OpenRCT2::Scripting el.flags |= G1_FLAG_RLE_COMPRESSION; } gfx_set_g1_element(id, &el); + drawing_engine_invalidate_image(id); } void DukSetPixelData(duk_context* ctx, ImageIndex id, const DukValue& dukPixelData) @@ -409,6 +413,59 @@ namespace OpenRCT2::Scripting } } + void DukDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const DukValue& callback) + { + auto* ctx = scriptEngine.GetContext(); + auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); + + auto drawingEngine = std::make_unique(GetContext()->GetUiContext()); + rct_drawpixelinfo dpi; + dpi.DrawingEngine = drawingEngine.get(); + dpi.width = size.width; + dpi.height = size.height; + + auto createNewImage = false; + auto g1 = gfx_get_g1_element(id); + if (g1 == nullptr || g1->width != size.width || g1->height != size.height || (g1->flags & G1_FLAG_RLE_COMPRESSION)) + { + createNewImage = true; + } + + if (createNewImage) + { + auto bufferSize = size.width* size.height; + dpi.bits = new uint8_t[bufferSize]; + std::memset(dpi.bits, 0, bufferSize); + + // Draw the original image if we are creating a new one + gfx_draw_sprite(&dpi, ImageId(id), { 0, 0 }); + } + else + { + dpi.bits = g1->offset; + } + + auto dukG = GetObjectAsDukValue(ctx, std::make_shared(ctx, dpi)); + scriptEngine.ExecutePluginCall(plugin, callback, { dukG }, false); + + if (createNewImage) + { + rct_g1_element newg1{}; + if (g1 != nullptr) + { + delete[] g1->offset; + newg1 = *g1; + } + newg1.offset = dpi.bits; + newg1.width = size.width; + newg1.height = size.height; + newg1.flags = 0; + gfx_set_g1_element(id, &newg1); + } + + drawing_engine_invalidate_image(id); + } + } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2-ui/scripting/CustomImages.h b/src/openrct2-ui/scripting/CustomImages.h index 98e954833f..930586b8cc 100644 --- a/src/openrct2-ui/scripting/CustomImages.h +++ b/src/openrct2-ui/scripting/CustomImages.h @@ -25,6 +25,7 @@ namespace OpenRCT2::Scripting DukValue DukGetImageInfo(duk_context* ctx, ImageIndex id); DukValue DukGetImagePixelData(duk_context* ctx, ImageIndex id); void DukSetPixelData(duk_context* ctx, ImageIndex id, const DukValue& dukPixelData); + void DukDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const DukValue& callback); } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/ScImageManager.hpp b/src/openrct2-ui/scripting/ScImageManager.hpp index ced6dd20d1..9f1362e10c 100644 --- a/src/openrct2-ui/scripting/ScImageManager.hpp +++ b/src/openrct2-ui/scripting/ScImageManager.hpp @@ -40,6 +40,7 @@ namespace OpenRCT2::Scripting dukglue_register_method(ctx, &ScImageManager::getImageInfo, "getImageInfo"); dukglue_register_method(ctx, &ScImageManager::getPixelData, "getPixelData"); dukglue_register_method(ctx, &ScImageManager::setPixelData, "setPixelData"); + dukglue_register_method(ctx, &ScImageManager::draw, "draw"); } private: @@ -127,6 +128,21 @@ namespace OpenRCT2::Scripting DukSetPixelData(_ctx, id, pixelData); } + void draw(int32_t id, const DukValue& dukSize, const DukValue& callback) + { + auto width = dukSize["width"].as_int(); + auto height = dukSize["height"].as_int(); + + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); + if (!DoesPluginOwnImage(plugin, id)) + { + duk_error(_ctx, DUK_ERR_ERROR, "This plugin did not allocate the specified image."); + } + + DukDrawCustomImage(scriptEngine, id, { width, height }, callback); + } + DukValue CreateImageIndexRange(size_t start, size_t count) const { DukObject obj(_ctx); diff --git a/src/openrct2/scripting/Plugin.cpp b/src/openrct2/scripting/Plugin.cpp index 89afb8f446..17a9e47094 100644 --- a/src/openrct2/scripting/Plugin.cpp +++ b/src/openrct2/scripting/Plugin.cpp @@ -89,17 +89,18 @@ void Plugin::Start() throw std::runtime_error("No main function specified."); } + _hasStarted = true; + mainFunc.push(); auto result = duk_pcall(_context, 0); if (result != DUK_ERR_NONE) { auto val = std::string(duk_safe_to_string(_context, -1)); duk_pop(_context); + _hasStarted = false; throw std::runtime_error("[" + _metadata.Name + "] " + val); } duk_pop(_context); - - _hasStarted = true; } void Plugin::StopBegin()