Copy image to clipboard for GraphicsEditor
This commit is contained in:
@@ -86,6 +86,7 @@ if(APPLE)
|
||||
app/core/platform/file_dialog.mm
|
||||
app/core/platform/app_delegate.mm
|
||||
app/core/platform/font_loader.mm
|
||||
app/core/platform/clipboard.mm
|
||||
)
|
||||
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
|
||||
10
src/app/core/platform/clipboard.h
Normal file
10
src/app/core/platform/clipboard.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <vector>
|
||||
|
||||
void CopyImageToClipboard(const std::vector<uint8_t>& data);
|
||||
void GetImageFromClipboard(std::vector<uint8_t>& data, int& width, int& height);
|
||||
|
||||
#endif
|
||||
42
src/app/core/platform/clipboard.mm
Normal file
42
src/app/core/platform/clipboard.mm
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "clipboard.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
void CopyImageToClipboard(const std::vector<uint8_t>& pngData) {
|
||||
NSData* data = [NSData dataWithBytes:pngData.data() length:pngData.size()];
|
||||
NSImage* image = [[NSImage alloc] initWithData:data];
|
||||
|
||||
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||
[pasteboard clearContents];
|
||||
[pasteboard writeObjects:@[ image ]];
|
||||
}
|
||||
|
||||
void GetImageFromClipboard(std::vector<uint8_t>& pixel_data, int& width, int& height) {
|
||||
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||
NSArray* classArray = [NSArray arrayWithObject:[NSImage class]];
|
||||
NSDictionary* options = [NSDictionary dictionary];
|
||||
|
||||
NSImage* image = [pasteboard readObjectsForClasses:classArray options:options].firstObject;
|
||||
if (!image) {
|
||||
width = height = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Assuming the image is in an RGBA format
|
||||
CGImageRef cgImage = [image CGImageForProposedRect:nil context:nil hints:nil];
|
||||
width = (int)CGImageGetWidth(cgImage);
|
||||
height = (int)CGImageGetHeight(cgImage);
|
||||
|
||||
size_t bytesPerRow = 4 * width;
|
||||
size_t totalBytes = bytesPerRow * height;
|
||||
pixel_data.resize(totalBytes);
|
||||
|
||||
CGContextRef context = CGBitmapContextCreate(
|
||||
pixel_data.data(), width, height, 8, bytesPerRow, CGColorSpaceCreateDeviceRGB(),
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
|
||||
CGContextRelease(context);
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/core/platform/clipboard.h"
|
||||
#include "app/editor/modules/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/compression.h"
|
||||
@@ -28,6 +29,16 @@ using ImGui::InputInt;
|
||||
using ImGui::InputText;
|
||||
using ImGui::SameLine;
|
||||
|
||||
constexpr ImGuiTableFlags kGfxEditTableFlags =
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_SizingFixedFit;
|
||||
|
||||
constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
|
||||
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
|
||||
ImGuiTabBarFlags_FittingPolicyResizeDown |
|
||||
ImGuiTabBarFlags_TabListPopupButton;
|
||||
|
||||
absl::Status GraphicsEditor::Update() {
|
||||
TAB_BAR("##TabBar")
|
||||
status_ = UpdateGfxEdit();
|
||||
@@ -41,11 +52,7 @@ absl::Status GraphicsEditor::Update() {
|
||||
absl::Status GraphicsEditor::UpdateGfxEdit() {
|
||||
TAB_ITEM("Graphics Editor")
|
||||
|
||||
if (ImGui::BeginTable("##GfxEditTable", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_SizingFixedFit,
|
||||
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name : kGfxEditColumnNames)
|
||||
ImGui::TableSetupColumn(name.data());
|
||||
@@ -73,33 +80,62 @@ absl::Status GraphicsEditor::UpdateGfxEdit() {
|
||||
}
|
||||
|
||||
void GraphicsEditor::DrawGfxEditToolset() {
|
||||
if (ImGui::BeginTable("##GfxEditToolset", 7, ImGuiTableFlags_SizingFixedFit,
|
||||
if (ImGui::BeginTable("##GfxEditToolset", 9, ImGuiTableFlags_SizingFixedFit,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name : {"Select", "Pencil", "Fill", "Zoom Out", "Zoom In",
|
||||
"Current Color", "Tile Size"})
|
||||
for (const auto& name :
|
||||
{"Select", "Pencil", "Fill", "Copy Sheet", "Paste Sheet", "Zoom Out",
|
||||
"Zoom In", "Current Color", "Tile Size"})
|
||||
ImGui::TableSetupColumn(name);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_SELECT_ALL)) {
|
||||
if (Button(ICON_MD_SELECT_ALL)) {
|
||||
gfx_edit_mode_ = GfxEditMode::kSelect;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_DRAW)) {
|
||||
if (Button(ICON_MD_DRAW)) {
|
||||
gfx_edit_mode_ = GfxEditMode::kPencil;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_FORMAT_COLOR_FILL)) {
|
||||
if (Button(ICON_MD_FORMAT_COLOR_FILL)) {
|
||||
gfx_edit_mode_ = GfxEditMode::kFill;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_ZOOM_OUT)) {
|
||||
if (Button(ICON_MD_CONTENT_COPY)) {
|
||||
std::vector<uint8_t> png_data =
|
||||
rom()->bitmap_manager().GetBitmap(current_sheet_)->GetPngData();
|
||||
CopyImageToClipboard(png_data);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (Button(ICON_MD_CONTENT_PASTE)) {
|
||||
std::vector<uint8_t> png_data;
|
||||
int width, height;
|
||||
GetImageFromClipboard(png_data, width, height);
|
||||
if (png_data.size() > 0) {
|
||||
rom()
|
||||
->bitmap_manager()
|
||||
.GetBitmap(current_sheet_)
|
||||
->LoadFromPngData(png_data, width, height);
|
||||
rom()->UpdateBitmap(rom()
|
||||
->mutable_bitmap_manager()
|
||||
->mutable_bitmap(current_sheet_)
|
||||
.get());
|
||||
}
|
||||
}
|
||||
HOVER_HINT("Paste from Clipboard");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (Button(ICON_MD_ZOOM_OUT)) {
|
||||
if (current_scale_ >= 0.0f) {
|
||||
current_scale_ -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_ZOOM_IN)) {
|
||||
if (Button(ICON_MD_ZOOM_IN)) {
|
||||
if (current_scale_ <= 16.0f) {
|
||||
current_scale_ += 1.0f;
|
||||
}
|
||||
@@ -129,38 +165,42 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::PopStyleVar();
|
||||
gui::Canvas graphics_bin_canvas_;
|
||||
graphics_bin_canvas_.UpdateEvent(
|
||||
[&]() {
|
||||
if (value.get()->IsActive()) {
|
||||
auto texture = value.get()->texture();
|
||||
graphics_bin_canvas_.GetDrawList()->AddImage(
|
||||
(void*)texture,
|
||||
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2,
|
||||
graphics_bin_canvas_.GetZeroPoint().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.GetZeroPoint().x +
|
||||
value.get()->width() * sheet_scale_,
|
||||
graphics_bin_canvas_.GetZeroPoint().y +
|
||||
value.get()->height() * sheet_scale_));
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
current_sheet_ = key;
|
||||
open_sheets_.insert(key);
|
||||
}
|
||||
auto select_tile_event = [&]() {
|
||||
if (value.get()->IsActive()) {
|
||||
auto texture = value.get()->texture();
|
||||
graphics_bin_canvas_.GetDrawList()->AddImage(
|
||||
(void*)texture,
|
||||
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2,
|
||||
graphics_bin_canvas_.GetZeroPoint().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.GetZeroPoint().x +
|
||||
value.get()->width() * sheet_scale_,
|
||||
graphics_bin_canvas_.GetZeroPoint().y +
|
||||
value.get()->height() * sheet_scale_));
|
||||
|
||||
// Add a slightly transparent rectangle behind the text
|
||||
ImVec2 textPos(graphics_bin_canvas_.GetZeroPoint().x + 2,
|
||||
graphics_bin_canvas_.GetZeroPoint().y + 2);
|
||||
ImVec2 textSize =
|
||||
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
|
||||
ImVec2 rectMin(textPos.x, textPos.y);
|
||||
ImVec2 rectMax(textPos.x + textSize.x, textPos.y + textSize.y);
|
||||
graphics_bin_canvas_.GetDrawList()->AddRectFilled(
|
||||
rectMin, rectMax, IM_COL32(0, 125, 0, 128));
|
||||
graphics_bin_canvas_.GetDrawList()->AddText(
|
||||
textPos, IM_COL32(125, 255, 125, 255),
|
||||
absl::StrFormat("%02X", key).c_str());
|
||||
}
|
||||
},
|
||||
ImVec2(0x100 + 1, 0x40 + 1), 0x20, sheet_scale_,
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
current_sheet_ = key;
|
||||
open_sheets_.insert(key);
|
||||
}
|
||||
|
||||
// Add a slightly transparent rectangle behind the text
|
||||
ImVec2 text_pos(graphics_bin_canvas_.GetZeroPoint().x + 2,
|
||||
graphics_bin_canvas_.GetZeroPoint().y + 2);
|
||||
ImVec2 text_size =
|
||||
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
|
||||
ImVec2 rent_min(text_pos.x, text_pos.y);
|
||||
ImVec2 rent_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
|
||||
|
||||
graphics_bin_canvas_.GetDrawList()->AddRectFilled(
|
||||
rent_min, rent_max, IM_COL32(0, 125, 0, 128));
|
||||
|
||||
graphics_bin_canvas_.GetDrawList()->AddText(
|
||||
text_pos, IM_COL32(125, 255, 125, 255),
|
||||
absl::StrFormat("%02X", key).c_str());
|
||||
}
|
||||
};
|
||||
|
||||
graphics_bin_canvas_.UpdateEvent(
|
||||
select_tile_event, ImVec2(0x100 + 1, 0x40 + 1), 0x20, sheet_scale_,
|
||||
/*grid_size=*/16.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::EndChild();
|
||||
@@ -173,11 +213,7 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
static int next_tab_id = 0;
|
||||
|
||||
if (ImGui::BeginTabBar("##GfxEditTabBar",
|
||||
ImGuiTabBarFlags_AutoSelectNewTabs |
|
||||
ImGuiTabBarFlags_Reorderable |
|
||||
ImGuiTabBarFlags_FittingPolicyResizeDown |
|
||||
ImGuiTabBarFlags_TabListPopupButton)) {
|
||||
if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
|
||||
if (ImGui::TabItemButton(
|
||||
"+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) {
|
||||
open_sheets_.insert(next_tab_id++);
|
||||
@@ -185,6 +221,8 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
|
||||
// Submit our regular tabs
|
||||
for (auto& each : open_sheets_) {
|
||||
current_sheet_ = each;
|
||||
|
||||
bool open = true;
|
||||
if (ImGui::BeginTabItem(absl::StrFormat("%d", each).c_str(), &open,
|
||||
ImGuiTabItemFlags_None)) {
|
||||
@@ -197,59 +235,61 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
child_window_sheets_.insert(each);
|
||||
}
|
||||
}
|
||||
ImGui::BeginChild(
|
||||
absl::StrFormat("##GfxEditPaletteChild%d", each).c_str(),
|
||||
ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
||||
current_sheet_ = each;
|
||||
|
||||
const auto child_id =
|
||||
absl::StrFormat("##GfxEditPaletteChildWindow%d", each);
|
||||
ImGui::BeginChild(child_id.c_str(), ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
||||
|
||||
auto draw_tile_event = [&]() {
|
||||
// Convert the ImVec4 into a 16-bit value
|
||||
Uint8 r = static_cast<Uint8>(current_color_.x * 31);
|
||||
Uint8 g = static_cast<Uint8>(current_color_.y * 31);
|
||||
Uint8 b = static_cast<Uint8>(current_color_.z * 31);
|
||||
Uint16 snes_color =
|
||||
((r & 0x1F) << 10) | ((g & 0x1F) << 5) | (b & 0x1F);
|
||||
|
||||
auto click_position =
|
||||
current_sheet_canvas_.GetCurrentDrawnTilePosition();
|
||||
|
||||
// Calculate the tile index for x and y based on the
|
||||
// click_position
|
||||
int tile_index_x =
|
||||
(static_cast<int>(click_position.x) % 8) / tile_size_;
|
||||
int tile_index_y =
|
||||
(static_cast<int>(click_position.y) % 8) / tile_size_;
|
||||
|
||||
// Calculate the pixel start position based on tile index and tile
|
||||
// size
|
||||
ImVec2 start_position;
|
||||
start_position.x = tile_index_x * tile_size_;
|
||||
start_position.y = tile_index_y * tile_size_;
|
||||
|
||||
// Get the current map's bitmap from the BitmapTable
|
||||
gfx::Bitmap& current_bitmap = *rom()->bitmap_manager()[each];
|
||||
|
||||
// Update the bitmap's pixel data based on the start_position and
|
||||
// tile_data
|
||||
for (int y = 0; y < tile_size_; ++y) {
|
||||
for (int x = 0; x < tile_size_; ++x) {
|
||||
int pixel_index =
|
||||
(start_position.y + y) * current_bitmap.width() +
|
||||
(start_position.x + x);
|
||||
current_bitmap.WriteToPixel(pixel_index, snes_color);
|
||||
}
|
||||
}
|
||||
|
||||
// rom()->bitmap_manager()[each]->WriteToPixel(position,
|
||||
// snesColor);
|
||||
|
||||
rom()->UpdateBitmap(
|
||||
rom()->mutable_bitmap_manager()->mutable_bitmap(each).get());
|
||||
};
|
||||
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
*rom()->bitmap_manager()[each], current_color_,
|
||||
[&]() {
|
||||
// Convert the ImVec4 into a 16-bit value
|
||||
Uint8 r = static_cast<Uint8>(current_color_.x * 31);
|
||||
Uint8 g = static_cast<Uint8>(current_color_.y * 31);
|
||||
Uint8 b = static_cast<Uint8>(current_color_.z * 31);
|
||||
Uint16 snesColor =
|
||||
((r & 0x1F) << 10) | ((g & 0x1F) << 5) | (b & 0x1F);
|
||||
|
||||
auto click_position =
|
||||
current_sheet_canvas_.GetCurrentDrawnTilePosition();
|
||||
|
||||
// Calculate the tile index for x and y based on the
|
||||
// click_position
|
||||
int tile_index_x =
|
||||
(static_cast<int>(click_position.x) % 8) / tile_size_;
|
||||
int tile_index_y =
|
||||
(static_cast<int>(click_position.y) % 8) / tile_size_;
|
||||
|
||||
// Calculate the pixel start position based on tile index and tile
|
||||
// size
|
||||
ImVec2 start_position;
|
||||
start_position.x = tile_index_x * tile_size_;
|
||||
start_position.y = tile_index_y * tile_size_;
|
||||
|
||||
// Get the current map's bitmap from the BitmapTable
|
||||
gfx::Bitmap& current_bitmap = *rom()->bitmap_manager()[each];
|
||||
|
||||
// Update the bitmap's pixel data based on the start_position and
|
||||
// tile_data
|
||||
for (int y = 0; y < tile_size_; ++y) {
|
||||
for (int x = 0; x < tile_size_; ++x) {
|
||||
int pixel_index =
|
||||
(start_position.y + y) * current_bitmap.width() +
|
||||
(start_position.x + x);
|
||||
current_bitmap.WriteToPixel(pixel_index, snesColor);
|
||||
}
|
||||
}
|
||||
|
||||
// rom()->bitmap_manager()[each]->WriteToPixel(position,
|
||||
// snesColor);
|
||||
|
||||
rom()->UpdateBitmap(
|
||||
rom()->mutable_bitmap_manager()->mutable_bitmap(each).get());
|
||||
},
|
||||
*rom()->bitmap_manager()[each], current_color_, draw_tile_event,
|
||||
ImVec2(0x100, 0x40), tile_size_, current_scale_, 8.0f);
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
@@ -297,7 +337,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
auto palette_group = rom()->GetPaletteGroup(
|
||||
auto palette_group = rom()->palette_group(
|
||||
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
|
||||
auto palette = palette_group[edit_palette_index_];
|
||||
@@ -678,7 +718,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
||||
converted_sheet);
|
||||
|
||||
if (rom()->isLoaded()) {
|
||||
auto palette_group = rom()->GetPaletteGroup("ow_main");
|
||||
auto palette_group = rom()->palette_group("ow_main");
|
||||
z3_rom_palette_ = palette_group[current_palette_];
|
||||
if (col_file_) {
|
||||
bin_bitmap_.ApplyPalette(col_file_palette_);
|
||||
@@ -711,7 +751,7 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
} else {
|
||||
// ROM palette
|
||||
auto palette_group =
|
||||
rom()->GetPaletteGroup(kPaletteGroupAddressesKeys[current_palette_]);
|
||||
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
|
||||
z3_rom_palette_ = palette_group[current_palette_index_];
|
||||
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
}
|
||||
@@ -736,7 +776,7 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
} else {
|
||||
// ROM palette
|
||||
auto palette_group =
|
||||
rom()->GetPaletteGroup(kPaletteGroupAddressesKeys[current_palette_]);
|
||||
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
|
||||
z3_rom_palette_ = palette_group[current_palette_index_];
|
||||
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
@@ -67,12 +67,18 @@ class GraphicsEditor : public SharedROM {
|
||||
absl::Status Update();
|
||||
|
||||
private:
|
||||
enum class GfxEditMode {
|
||||
kSelect,
|
||||
kPencil,
|
||||
kFill,
|
||||
};
|
||||
|
||||
// Graphics Editor Tab
|
||||
absl::Status UpdateGfxEdit();
|
||||
void DrawGfxEditToolset();
|
||||
absl::Status UpdateGfxSheetList();
|
||||
absl::Status UpdateGfxTabView();
|
||||
absl::Status UpdatePaletteColumn();
|
||||
void DrawGfxEditToolset();
|
||||
|
||||
// Link Graphics Edit Tab
|
||||
absl::Status UpdateLinkGfxView();
|
||||
@@ -80,23 +86,24 @@ class GraphicsEditor : public SharedROM {
|
||||
// Prototype Graphics Viewer
|
||||
absl::Status UpdateScadView();
|
||||
|
||||
absl::Status DrawToolset();
|
||||
|
||||
// Import Functions
|
||||
absl::Status DrawCgxImport();
|
||||
absl::Status DrawScrImport();
|
||||
absl::Status DrawFileImport();
|
||||
absl::Status DrawObjImport();
|
||||
absl::Status DrawTilemapImport();
|
||||
|
||||
// Other Functions
|
||||
absl::Status DrawToolset();
|
||||
absl::Status DrawPaletteControls();
|
||||
absl::Status DrawClipboardImport();
|
||||
absl::Status DrawExperimentalFeatures();
|
||||
absl::Status DrawMemoryEditor();
|
||||
|
||||
absl::Status DecompressImportData(int size);
|
||||
|
||||
absl::Status DecompressSuperDonkey();
|
||||
|
||||
// Member Variables
|
||||
ImVec4 current_color_;
|
||||
uint16_t current_sheet_ = 0;
|
||||
uint8_t tile_size_ = 0x08;
|
||||
@@ -115,92 +122,67 @@ class GraphicsEditor : public SharedROM {
|
||||
uint64_t current_offset_ = 0;
|
||||
uint64_t current_size_ = 0;
|
||||
uint64_t current_palette_index_ = 0;
|
||||
|
||||
int current_bpp_ = 0;
|
||||
|
||||
int scr_mod_value_ = 0;
|
||||
|
||||
uint64_t num_sheets_to_load_ = 1;
|
||||
|
||||
uint64_t bin_size_ = 0;
|
||||
|
||||
uint64_t clipboard_offset_ = 0;
|
||||
uint64_t clipboard_size_ = 0;
|
||||
|
||||
bool refresh_graphics_ = false;
|
||||
bool open_memory_editor_ = false;
|
||||
|
||||
bool gfx_loaded_ = false;
|
||||
bool is_open_ = false;
|
||||
bool super_donkey_ = false;
|
||||
|
||||
bool col_file_ = false;
|
||||
bool cgx_loaded_ = false;
|
||||
bool scr_loaded_ = false;
|
||||
bool obj_loaded_ = false;
|
||||
|
||||
bool tilemap_loaded_ = false;
|
||||
|
||||
char file_path_[256] = "";
|
||||
char col_file_path_[256] = "";
|
||||
char col_file_name_[256] = "";
|
||||
|
||||
char cgx_file_path_[256] = "";
|
||||
char cgx_file_name_[256] = "";
|
||||
|
||||
char scr_file_path_[256] = "";
|
||||
char scr_file_name_[256] = "";
|
||||
|
||||
char obj_file_path_[256] = "";
|
||||
|
||||
char tilemap_file_path_[256] = "";
|
||||
char tilemap_file_name_[256] = "";
|
||||
|
||||
GfxEditMode gfx_edit_mode_ = GfxEditMode::kSelect;
|
||||
|
||||
ROM temp_rom_;
|
||||
ROM tilemap_rom_;
|
||||
|
||||
zelda3::Overworld overworld_;
|
||||
|
||||
MemoryEditor cgx_memory_editor_;
|
||||
MemoryEditor col_memory_editor_;
|
||||
|
||||
PaletteEditor palette_editor_;
|
||||
|
||||
Bytes import_data_;
|
||||
Bytes graphics_buffer_;
|
||||
|
||||
std::vector<uint8_t> decoded_cgx_;
|
||||
std::vector<uint8_t> cgx_data_;
|
||||
std::vector<uint8_t> extra_cgx_data_;
|
||||
std::vector<SDL_Color> decoded_col_;
|
||||
|
||||
std::vector<uint8_t> scr_data_;
|
||||
std::vector<uint8_t> decoded_scr_data_;
|
||||
|
||||
gfx::Bitmap cgx_bitmap_;
|
||||
gfx::Bitmap scr_bitmap_;
|
||||
gfx::Bitmap bin_bitmap_;
|
||||
|
||||
gfx::Bitmap link_full_sheet_;
|
||||
|
||||
gfx::BitmapTable graphics_bin_;
|
||||
gfx::BitmapTable clipboard_graphics_bin_;
|
||||
|
||||
gfx::BitmapTable link_graphics_;
|
||||
|
||||
gfx::PaletteGroup col_file_palette_group_;
|
||||
|
||||
gfx::SNESPalette z3_rom_palette_;
|
||||
gfx::SNESPalette col_file_palette_;
|
||||
|
||||
gfx::SNESPalette link_palette_;
|
||||
|
||||
gui::Canvas import_canvas_;
|
||||
gui::Canvas scr_canvas_;
|
||||
gui::Canvas super_donkey_canvas_;
|
||||
gui::Canvas current_sheet_canvas_;
|
||||
// gui::Canvas graphics_bin_canvas_;
|
||||
|
||||
absl::Status status_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "bitmap.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <png.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -23,6 +24,174 @@ void GrayscalePalette(SDL_Palette *palette) {
|
||||
palette->colors[i].b = i * 31;
|
||||
}
|
||||
}
|
||||
|
||||
void PngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
std::vector<uint8_t> *p = (std::vector<uint8_t> *)png_get_io_ptr(png_ptr);
|
||||
p->insert(p->end(), data, data + length);
|
||||
}
|
||||
|
||||
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer) {
|
||||
png_structp png_ptr = png_create_write_struct("1.6.40", NULL, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
SDL_Log("Failed to create PNG write struct");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
||||
SDL_Log("Failed to create PNG info struct");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
SDL_Log("Error during PNG write");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn(png_ptr, &buffer, PngWriteCallback, NULL);
|
||||
|
||||
png_colorp pal_ptr;
|
||||
|
||||
/* Prepare chunks */
|
||||
int colortype = PNG_COLOR_MASK_COLOR;
|
||||
int i = 0;
|
||||
SDL_Palette *pal;
|
||||
if (surface->format->BytesPerPixel > 0 &&
|
||||
surface->format->BytesPerPixel <= 8 && (pal = surface->format->palette)) {
|
||||
SDL_Log("Writing PNG image with palette");
|
||||
colortype |= PNG_COLOR_MASK_PALETTE;
|
||||
pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color));
|
||||
for (i = 0; i < pal->ncolors; i++) {
|
||||
pal_ptr[i].red = pal->colors[i].r;
|
||||
pal_ptr[i].green = pal->colors[i].g;
|
||||
pal_ptr[i].blue = pal->colors[i].b;
|
||||
}
|
||||
png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
|
||||
free(pal_ptr);
|
||||
}
|
||||
|
||||
auto depth = surface->format->BitsPerPixel;
|
||||
|
||||
// Set image attributes.
|
||||
png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, depth, colortype,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_set_bgr(png_ptr);
|
||||
|
||||
// Write the image data.
|
||||
std::vector<png_bytep> row_pointers(surface->h);
|
||||
for (int y = 0; y < surface->h; ++y) {
|
||||
row_pointers[y] = (png_bytep)(surface->pixels) + y * surface->pitch;
|
||||
}
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, row_pointers.data());
|
||||
|
||||
SDL_Log("Writing PNG image...");
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
SDL_Log("PNG image write complete");
|
||||
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PngReadCallback(png_structp png_ptr, png_bytep outBytes,
|
||||
png_size_t byteCountToRead) {
|
||||
png_voidp io_ptr = png_get_io_ptr(png_ptr);
|
||||
if (!io_ptr) return;
|
||||
|
||||
std::vector<uint8_t> *png_data =
|
||||
reinterpret_cast<std::vector<uint8_t> *>(io_ptr);
|
||||
size_t pos = png_data->size() - byteCountToRead;
|
||||
memcpy(outBytes, png_data->data() + pos, byteCountToRead);
|
||||
png_data->resize(pos); // Reduce the buffer size
|
||||
}
|
||||
|
||||
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
||||
SDL_Surface **outSurface) {
|
||||
std::vector<uint8_t> data(png_data);
|
||||
png_structp png_ptr = png_create_read_struct("1.6.40", NULL, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
throw std::runtime_error("Failed to create PNG read struct");
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
throw std::runtime_error("Failed to create PNG info struct");
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
throw std::runtime_error("Error during PNG read");
|
||||
}
|
||||
|
||||
// Set our custom read function
|
||||
png_set_read_fn(png_ptr, &data, PngReadCallback);
|
||||
|
||||
// Read the PNG info
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
uint32_t width = png_get_image_width(png_ptr, info_ptr);
|
||||
uint32_t height = png_get_image_height(png_ptr, info_ptr);
|
||||
png_byte color_type = png_get_color_type(png_ptr, info_ptr);
|
||||
png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||
|
||||
// Set up transformations, e.g., strip 16-bit PNGs down to 8-bit, expand
|
||||
// palettes, etc.
|
||||
if (bit_depth == 16) {
|
||||
png_set_strip_16(png_ptr);
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
// PNG files pack pixels, expand them
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
|
||||
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||
}
|
||||
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
// Update info structure with transformations
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
|
||||
// Read the file
|
||||
std::vector<png_bytep> row_pointers(height);
|
||||
std::vector<uint8_t> raw_data(width * height *
|
||||
4); // Assuming 4 bytes per pixel (RGBA)
|
||||
for (size_t y = 0; y < height; y++) {
|
||||
row_pointers[y] = &raw_data[y * width * 4];
|
||||
}
|
||||
|
||||
png_read_image(png_ptr, row_pointers.data());
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
// Create SDL_Surface from raw pixel data
|
||||
*outSurface = SDL_CreateRGBSurfaceWithFormatFrom(
|
||||
raw_data.data(), width, height, 32, width * 4, SDL_PIXELFORMAT_RGBA32);
|
||||
if (*outSurface == nullptr) {
|
||||
SDL_Log("SDL_CreateRGBSurfaceWithFormatFrom failed: %s\n", SDL_GetError());
|
||||
} else {
|
||||
SDL_Log("Successfully created SDL_Surface from PNG data");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Bitmap::Bitmap(int width, int height, int depth, int data_size) {
|
||||
@@ -143,6 +312,20 @@ void Bitmap::SetSurface(SDL_Surface *surface) {
|
||||
surface, SDL_Surface_Deleter());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Bitmap::GetPngData() {
|
||||
ConvertSurfaceToPNG(surface_.get(), png_data_);
|
||||
return png_data_;
|
||||
}
|
||||
|
||||
void Bitmap::LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||
int height) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
SDL_Surface *surface = surface_.get();
|
||||
ConvertPngToSurface(png_data, &surface);
|
||||
surface_.reset(surface);
|
||||
}
|
||||
|
||||
// Convert SNESPalette to SDL_Palette for surface.
|
||||
void Bitmap::ApplyPalette(const SNESPalette &palette) {
|
||||
palette_ = palette;
|
||||
@@ -215,15 +398,6 @@ void Bitmap::InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
|
||||
GrayscalePalette(surface_->format->palette);
|
||||
}
|
||||
|
||||
void Bitmap::ReserveData(uint32_t width, uint32_t height, uint32_t depth,
|
||||
uint32_t size) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
depth_ = depth;
|
||||
data_.reserve(size);
|
||||
pixel_data_ = data_.data();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -27,24 +27,11 @@ class Bitmap {
|
||||
InitializeFromData(width, height, depth, data);
|
||||
}
|
||||
|
||||
// Function to create texture from pixel data
|
||||
void CreateTextureFromData() {
|
||||
active_ = true;
|
||||
surface_ = std::shared_ptr<SDL_Surface>(
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
|
||||
SDL_PIXELFORMAT_INDEX8),
|
||||
SDL_Surface_Deleter());
|
||||
surface_->pixels = data_.data();
|
||||
data_size_ = data_.size();
|
||||
}
|
||||
|
||||
void Create(int width, int height, int depth, int data_size);
|
||||
void Create(int width, int height, int depth, const Bytes &data);
|
||||
|
||||
void InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
|
||||
const Bytes &data);
|
||||
void ReserveData(uint32_t width, uint32_t height, uint32_t depth,
|
||||
uint32_t size);
|
||||
|
||||
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||
@@ -53,6 +40,9 @@ class Bitmap {
|
||||
|
||||
void SaveSurfaceToFile(std::string_view filename);
|
||||
void SetSurface(SDL_Surface *surface);
|
||||
std::vector<uint8_t> GetPngData();
|
||||
void LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||
int height);
|
||||
|
||||
void ApplyPalette(const SNESPalette &palette);
|
||||
void ApplyPaletteWithTransparent(const SNESPalette &palette, int index);
|
||||
@@ -75,6 +65,22 @@ class Bitmap {
|
||||
modified_ = true;
|
||||
}
|
||||
|
||||
void Get8x8Tile(int tile_index, int x, int y, std::vector<uint8_t> &tile_data,
|
||||
int &tile_data_offset) {
|
||||
int tile_offset = tile_index * 64;
|
||||
int tile_x = x * 8;
|
||||
int tile_y = y * 8;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int row_offset = tile_offset + (i * 8);
|
||||
for (int j = 0; j < 8; j++) {
|
||||
int pixel_offset = row_offset + j;
|
||||
int pixel_value = data_[pixel_offset];
|
||||
tile_data[tile_data_offset] = pixel_value;
|
||||
tile_data_offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
// Reset texture_
|
||||
if (texture_) {
|
||||
@@ -105,10 +111,11 @@ class Bitmap {
|
||||
auto depth() const { return depth_; }
|
||||
auto size() const { return data_size_; }
|
||||
auto data() const { return data_.data(); }
|
||||
auto mutable_data() { return data_; }
|
||||
auto &mutable_data() { return data_; }
|
||||
auto mutable_pixel_data() { return pixel_data_; }
|
||||
auto surface() const { return surface_.get(); }
|
||||
auto mutable_surface() { return surface_.get(); }
|
||||
void set_data(const Bytes &data) { data_ = data; }
|
||||
|
||||
auto vector() const { return data_; }
|
||||
auto at(int i) const { return data_[i]; }
|
||||
@@ -142,12 +149,17 @@ class Bitmap {
|
||||
int height_ = 0;
|
||||
int depth_ = 0;
|
||||
int data_size_ = 0;
|
||||
|
||||
bool freed_ = false;
|
||||
bool active_ = false;
|
||||
bool modified_ = false;
|
||||
void *texture_pixels = nullptr;
|
||||
|
||||
uchar *pixel_data_;
|
||||
Bytes data_;
|
||||
|
||||
std::vector<uint8_t> png_data_;
|
||||
|
||||
gfx::SNESPalette palette_;
|
||||
std::shared_ptr<SDL_Texture> texture_ = nullptr;
|
||||
std::shared_ptr<SDL_Surface> surface_ = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user