4747#include " game_interpreter_control_variables.h"
4848#include " game_windows.h"
4949#include " json_helper.h"
50+ #include " lcf/rpg/savepicture.h"
5051#include " maniac_patch.h"
5152#include " memory_management.h"
5253#include " pixel_format.h"
@@ -4722,13 +4723,11 @@ bool Game_Interpreter::CommandManiacGetPictureInfo(lcf::rpg::EventCommand const&
47224723 // Type 3: Pixel Data Extraction
47234724 if (info_type == 3 ) {
47244725 auto * sprite = pic.sprite .get ();
4725- if (!sprite) return true ;
4726-
47274726 auto bitmap = sprite->GetBitmap ();
47284727
47294728 // If this is a Window (String Picture), the visual content is generated by the Window class.
47304729 // We force a refresh/draw cycle here to ensure we read the actual text/window graphics.
4731- if (data. easyrpg_type == lcf::rpg::SavePicture::EasyRpgType_window ) {
4730+ if (pic. IsWindowAttached () ) {
47324731 const auto & window = Main_Data::game_windows->GetWindow (pic_id);
47334732 if (window.window ) {
47344733 bitmap->Clear ();
@@ -4746,7 +4745,6 @@ bool Game_Interpreter::CommandManiacGetPictureInfo(lcf::rpg::EventCommand const&
47464745 // Bit 0: Ignore Alpha (return 0x00RRGGBB instead of 0xFFRRGGBB)
47474746 bool ignore_alpha = (com.parameters [2 ] & 2 ) != 0 ;
47484747
4749- // Creates a snapshot of the current frame
47504748 Rect frame_rect{pic_x, pic_y, pic_w, pic_h};
47514749
47524750 if (!ManiacPatch::WritePixelsToVariable (*bitmap, frame_rect, dst_var_id, ignore_alpha, *Main_Data::game_variables)) {
@@ -5372,121 +5370,59 @@ bool Game_Interpreter::CommandManiacEditPicture(lcf::rpg::EventCommand const& co
53725370 return true ;
53735371 }
53745372
5375- auto & pic = Main_Data::game_pictures->GetPicture (pic_id);
5373+ auto & picture = Main_Data::game_pictures->GetPicture (pic_id);
53765374
5377- if (pic .IsRequestPending ()) {
5378- pic .MakeRequestImportant ();
5375+ if (picture .IsRequestPending ()) {
5376+ picture .MakeRequestImportant ();
53795377 _async_op = AsyncOp::MakeYieldRepeat ();
53805378 return true ;
53815379 }
53825380
5383- auto * sprite = pic.sprite .get ();
5384- if (!sprite) return true ;
5385-
5381+ auto * sprite = picture.sprite .get ();
53865382 auto bitmap = sprite->GetBitmap ();
5387- if (!bitmap) return true ;
53885383
5389- // 1. Calculate Spritesheet Offset
5384+ // Calculate Spritesheet Offset
53905385 // Maniacs operations are relative to the currently active cell.
5391- int offset_x = 0 ;
5392- int offset_y = 0 ;
5393-
5394- const auto & data = pic.data ;
5395- if (data.spritesheet_cols > 1 || data.spritesheet_rows > 1 ) {
5396- int frame_width = bitmap->GetWidth () / data.spritesheet_cols ;
5397- int frame_height = bitmap->GetHeight () / data.spritesheet_rows ;
5398-
5399- // Map current frame index to X/Y coords
5400- offset_x = (data.spritesheet_frame % data.spritesheet_cols ) * frame_width;
5401- offset_y = (data.spritesheet_frame / data.spritesheet_cols ) * frame_height;
5402- }
5403-
5404- // 2. COW & Window Detach Logic
5405- BitmapRef writeable_bitmap = bitmap;
54065386
5407- bool is_cached = !bitmap->GetId ().empty () && !StartsWith (bitmap->GetId (), " Canvas:" );
5408- bool wrong_format = bitmap->bpp () != 4 ;
5409- bool is_window = data.easyrpg_type == lcf::rpg::SavePicture::EasyRpgType_window;
5387+ // Determine Spritesheet frame
5388+ Rect src_rect = sprite->GetSrcRect ();
54105389
5411- if (is_cached || wrong_format || is_window) {
5412- writeable_bitmap = Bitmap::Create (bitmap->GetWidth (), bitmap->GetHeight ());
5413- writeable_bitmap->BlitFast (0 , 0 , *bitmap, bitmap->GetRect (), 255 );
5390+ BitmapRef writable_bitmap = bitmap;
54145391
5415- writeable_bitmap->SetId (" Canvas:" + std::to_string (pic_id));
5416-
5417- sprite->SetBitmap (writeable_bitmap);
5418-
5419- if (is_window) {
5420- pic.data .easyrpg_type = lcf::rpg::SavePicture::EasyRpgType_default;
5392+ if (picture.IsWindowAttached ()) {
5393+ // If this is a Window (String Picture), the visual content is generated by the Window class.
5394+ // We force a refresh/draw cycle here to ensure we read the actual text/window graphics.
5395+ const auto & window = Main_Data::game_windows->GetWindow (pic_id);
5396+ if (window.window ) {
5397+ bitmap->Clear ();
5398+ window.window ->Draw (*bitmap);
54215399 }
5422-
5423- // Force sprite to recalculate its clipping rectangle immediately.
5424- // Otherwise, SetBitmap resets src_rect to full size, showing the whole sheet.
5425- sprite->OnPictureShow ();
5400+ } else if (picture.IsCanvas ()) {
5401+ // no-op
5402+ } else {
5403+ // Must be copied to avoid modifiying the original picture
5404+ writable_bitmap = Bitmap::Create (*bitmap, src_rect, true );
54265405 }
54275406
5428- // 3. Parameters
5429- int x = ValueOrVariableBitfield (com.parameters [0 ], 1 , com.parameters [2 ]);
5430- int y = ValueOrVariableBitfield (com.parameters [0 ], 2 , com.parameters [3 ]);
5431- int w = ValueOrVariableBitfield (com.parameters [0 ], 3 , com.parameters [4 ]);
5432- int h = ValueOrVariableBitfield (com.parameters [0 ], 4 , com.parameters [5 ]);
5407+ picture.data .easyrpg_type = lcf::rpg::SavePicture::EasyRpgType_canvas;
54335408
5434- int src_var_start = ValueOrVariableBitfield (com.parameters [0 ], 5 , com.parameters [6 ]);
5409+ // Packing: x pos, y pos, width, height, var_id
5410+ int pic_x = ValueOrVariableBitfield (com.parameters [0 ], 1 , com.parameters [2 ]);
5411+ int pic_y = ValueOrVariableBitfield (com.parameters [0 ], 2 , com.parameters [3 ]);
5412+ int pic_w = ValueOrVariableBitfield (com.parameters [0 ], 3 , com.parameters [4 ]);
5413+ int pic_h = ValueOrVariableBitfield (com.parameters [0 ], 4 , com.parameters [5 ]);
5414+ int start_var_id = ValueOrVariableBitfield (com.parameters [0 ], 5 , com.parameters [6 ]);
54355415
54365416 int flags = com.parameters [7 ];
5437- bool flag_opaq = (flags & 1 ) != 0 ;
5438- bool flag_skip_trans = (flags & 2 ) != 0 ;
5439-
5440- // 4. Drawing Loop
5441- int bmp_w = writeable_bitmap->GetWidth ();
5442- int bmp_h = writeable_bitmap->GetHeight ();
5443-
5444- if (w <= 0 || h <= 0 ) return true ;
5417+ // When no flag is set the area is cleared and a OP_OVER blit occurs
5418+ bool flag_opaq = (flags & 1 ) != 0 ; // Blit with OP_SRC
5419+ bool flag_skip_trans = (flags & 2 ) != 0 ; // Blit with OP_OVER
54455420
5446- uint32_t * pixels = static_cast < uint32_t *>(writeable_bitmap-> pixels ()) ;
5447- int pitch = writeable_bitmap-> pitch () / 4 ;
5421+ bool clear_dst = !flag_opaq && !flag_skip_trans ;
5422+ bool ignore_alpha = flag_opaq ;
54485423
5449- int current_var = src_var_start;
5450- auto & vars = *Main_Data::game_variables;
5451-
5452- for (int iy = 0 ; iy < h; ++iy) {
5453- // Apply Y Offset here
5454- int py = y + iy + offset_y;
5455-
5456- if (py < 0 || py >= bmp_h) {
5457- current_var += w;
5458- continue ;
5459- }
5460-
5461- for (int ix = 0 ; ix < w; ++ix) {
5462- int32_t color_val = vars.Get (current_var++);
5463-
5464- // Apply X Offset here
5465- int px = x + ix + offset_x;
5466-
5467- if (px < 0 || px >= bmp_w) continue ;
5468-
5469- uint8_t a = (color_val >> 24 ) & 0xFF ;
5470- uint8_t r = (color_val >> 16 ) & 0xFF ;
5471- uint8_t g = (color_val >> 8 ) & 0xFF ;
5472- uint8_t b = (color_val) & 0xFF ;
5473-
5474- if (flag_skip_trans && a == 0 ) continue ;
5475-
5476- if (flag_opaq) {
5477- a = 0xFF ;
5478- }
5479-
5480- if (a < 255 ) {
5481- r = (r * a) / 255 ;
5482- g = (g * a) / 255 ;
5483- b = (b * a) / 255 ;
5484- }
5485-
5486- uint32_t final_pixel = Bitmap::pixel_format.rgba_to_uint32_t (r, g, b, a);
5487- pixels[py * pitch + px] = final_pixel;
5488- }
5489- }
5424+ Rect frame_rect{pic_x, pic_y, pic_w, pic_h};
5425+ ManiacPatch::ReadPixelsFromVariable (*writable_bitmap, frame_rect, start_var_id, clear_dst, ignore_alpha, *Main_Data::game_variables);
54905426
54915427 return true ;
54925428}
@@ -5553,9 +5489,17 @@ bool Game_Interpreter::CommandManiacWritePicture(lcf::rpg::EventCommand const& c
55535489 const auto sprite = picture.sprite .get ();
55545490
55555491 // Retrieve bitmap
5556- if (picture.IsWindowAttached () || picture.IsCanvas ()) {
5557- // Cannot change transparency of images that are not reloadable from a file
5558- // Appears to match Maniacs behaviour
5492+ // Cannot change transparency of images that are not reloadable from a file (window and canvas)
5493+ // Appears to match Maniacs behaviour
5494+ if (picture.IsWindowAttached ()) {
5495+ // If this is a Window (String Picture), the visual content is generated by the Window class.
5496+ // We force a refresh/draw cycle here to ensure we read the actual text/window graphics.
5497+ const auto & window = Main_Data::game_windows->GetWindow (pic_id);
5498+ if (window.window ) {
5499+ bitmap->Clear ();
5500+ window.window ->Draw (*bitmap);
5501+ }
5502+ } else if (picture.IsCanvas ()) {
55595503 bitmap = picture.sprite ->GetBitmap ();
55605504 } else if (picture.data .name .empty ()) {
55615505 // Not much we can do here (also shouldn't happen normally)
0 commit comments