Skip to content

Commit 77c4efa

Browse files
matcoolcamila314hiimjasmine00
authored
node screenshot feature (#65)
* Render * change arg type * update comment * uhhh fixes i guess * more fixes * These are ints * fix typo * use geode 4.9.0 features * rename button to screenshot --------- Co-authored-by: camila314 <[email protected]> Co-authored-by: Jasmine <[email protected]>
1 parent 745bc8a commit 77c4efa

File tree

11 files changed

+172
-25
lines changed

11 files changed

+172
-25
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ add_library(${PROJECT_NAME} SHARED ${SOURCES} src/platform/Mac.mm)
1717

1818
set_source_files_properties(src/platform/Mac.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
1919

20+
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS" OR IOS)
21+
target_link_libraries(${PROJECT_NAME} "-framework CoreGraphics")
22+
endif()
23+
2024
if (NOT DEFINED ENV{GEODE_SDK})
2125
message(FATAL_ERROR "Unable to find Geode SDK! Please define GEODE_SDK environment variable to point to Geode")
2226
else()

mod.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"geode": "4.8.0",
2+
"geode": "4.9.0",
33
"version": "v1.10.0",
44
"gd": {
55
"win": "2.2074",

src/DevTools.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ void DevTools::toggle() {
275275
this->show(!m_visible);
276276
}
277277

278+
bool DevTools::isVisible() {
279+
return m_visible;
280+
}
281+
278282
void DevTools::sceneChanged() {
279283
m_selectedNode = nullptr;
280284
}

src/DevTools.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,6 @@ class DevTools {
116116

117117
void show(bool visible);
118118
void toggle();
119+
120+
bool isVisible();
119121
};

src/main.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <Geode/modify/CCDirector.hpp>
66
#include <Geode/modify/CCEGLView.hpp>
77
#include <Geode/modify/CCNode.hpp>
8+
#include <Geode/modify/GameToolbox.hpp>
89
#include "DevTools.hpp"
910
#include <imgui.h>
1011
#include "ImGui.hpp"
@@ -41,6 +42,32 @@ class $modify(MenuLayer) {
4142

4243
#endif
4344

45+
class $modify(GameToolbox) {
46+
static void preVisitWithClippingRect(CCNode* node, CCRect clipRect) {
47+
if (!node->isVisible() || !DevTools::get()->isVisible())
48+
return GameToolbox::preVisitWithClippingRect(node, clipRect);
49+
50+
glEnable(GL_SCISSOR_TEST);
51+
52+
clipRect.origin = node->convertToWorldSpace(clipRect.origin);
53+
54+
kmMat4 mat;
55+
kmGLGetMatrix(KM_GL_PROJECTION, &mat);
56+
if (mat.mat[5] < 0) {
57+
auto ws = CCDirector::get()->getWinSize();
58+
clipRect.origin.y = ws.height - (clipRect.origin.y + node->getContentSize().height);
59+
}
60+
61+
CCEGLView::get()->setScissorInPoints(
62+
clipRect.origin.x,
63+
clipRect.origin.y,
64+
clipRect.size.width,
65+
clipRect.size.height
66+
);
67+
}
68+
69+
};
70+
4471
class $modify(CCDirector) {
4572
void willSwitchToScene(CCScene* scene) {
4673
CCDirector::willSwitchToScene(scene);

src/pages/Attributes.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
#include <ccTypes.h>
88
#include <Geode/ui/SimpleAxisLayout.hpp>
99
#include <Geode/ui/Layout.hpp>
10+
#include <Geode/utils/file.hpp>
1011

1112
using namespace geode::prelude;
1213

1314
#define AXIS_GET(Name_) \
1415
&AxisLayoutOptions::get##Name_, \
1516
&AxisLayoutOptions::set##Name_
1617

18+
1719
template <class T, class R>
1820
bool checkbox(const char* text, T* ptr, bool(T::* get)(), R(T::* set)(bool)) {
1921
bool value = (ptr->*get)();
@@ -72,13 +74,31 @@ void DevTools::drawBasicAttributes(CCNode* node) {
7274
}
7375
ImGui::SameLine();
7476
if (ImGui::Button(U8STR(FEATHER_COPY " Copy Class Name"))) {
75-
clipboard::write(getNodeName(node));
77+
clipboard::write(std::string(geode::cocos::getObjectName(node)));
78+
}
79+
ImGui::SameLine();
80+
if (ImGui::Button(U8STR(FEATHER_SAVE " Screenshot"))) {
81+
file::pick(file::PickMode::SaveFile, file::FilePickOptions {
82+
.filters = {{ .description = "PNG Image", .files = {"*.png"} }}
83+
}).listen([node](auto choice) {
84+
if (auto file = choice->ok()) {
85+
int width, height;
86+
auto bytes = renderToBytes(node, width, height);
87+
88+
auto path = string::pathToString(*file);
89+
if (!path.ends_with(".png")) {
90+
path += ".png";
91+
}
92+
saveRenderToFile(bytes, width, height, path.c_str());
93+
}
94+
});
7695
}
96+
7797
ImGui::Text("Address: %s", fmt::to_string(fmt::ptr(node)).c_str());
7898
ImGui::SameLine();
7999
if (ImGui::Button(U8STR(FEATHER_COPY " Copy"))) {
80100
clipboard::write(
81-
utils::intToHex(reinterpret_cast<uintptr_t>(node))
101+
fmt::format("{:#x}", reinterpret_cast<uintptr_t>(node))
82102
);
83103
}
84104
if (node->getUserData()) {

src/pages/Tree.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void DevTools::drawTreeBranch(CCNode* node, size_t index) {
2424
flags |= ImGuiTreeNodeFlags_OpenOnArrow;
2525
}
2626
std::stringstream name;
27-
name << "[" << index << "] " << getNodeName(node) << " ";
27+
name << "[" << index << "] " << geode::cocos::getObjectName(node) << " ";
2828
if (node->getTag() != -1) {
2929
name << "(" << node->getTag() << ") ";
3030
}

src/platform/Android.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ using namespace geode::prelude;
77
#include "utils.hpp"
88

99
std::string formatAddressIntoOffsetImpl(uintptr_t addr, bool module) {
10-
if (addr > base::get() && addr - 0x1000000 < base::get())
10+
if (addr > base::get() && addr - 0x1000000 < base::get()) {
1111
if(module) return fmt::format("libcocos2d.so + {:#x}", addr - base::get());
1212
else return fmt::format("{:#x}", addr - base::get());
13+
}
1314
return fmt::format("{:#x}", addr);
1415
}
1516

src/platform/Mac.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "utils.hpp"
66

77
#include <Geode/utils/string.hpp>
8+
#include <Geode/utils/file.hpp>
9+
#include <Geode/loader/Log.hpp>
810
#include <array>
911
#include <thread>
1012
#include <execinfo.h>
@@ -18,6 +20,13 @@
1820
#include <mach-o/dyld.h>
1921
#import <Foundation/Foundation.h>
2022

23+
#import <CoreGraphics/CoreGraphics.h>
24+
#ifdef GEODE_IS_MACOS
25+
#include <ImageIO/CGImageDestination.h>
26+
#else
27+
#import <UIKit/UIKit.h>
28+
#endif
29+
2130
static std::vector<struct dyld_image_info const*> getAllImages() {
2231
std::vector<struct dyld_image_info const*> images;
2332
struct task_dyld_info dyldInfo;
@@ -86,4 +95,5 @@
8695
else return fmt::format("{:#x}", addr - base);
8796
}
8897

98+
8999
#endif

src/platform/utils.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
#include "utils.hpp"
22

3+
#if defined(GEODE_IS_MACOS)
4+
#include <OpenGL/gl.h>
5+
#elif defined(GEODE_IS_IOS)
6+
#include <OpenGLES/ES2/gl.h>
7+
#endif
38
#include <unordered_map>
9+
#include <cocos2d.h>
10+
11+
using namespace cocos2d;
412

513
std::string formatAddressIntoOffset(uintptr_t addr, bool module) {
614
static std::unordered_map<uintptr_t, std::pair<std::string, std::string>> formatted;
@@ -16,4 +24,92 @@ std::string formatAddressIntoOffset(uintptr_t addr, bool module) {
1624
if(module) return pair.first;
1725
else return pair.second;
1826
}
27+
}
28+
29+
std::vector<uint8_t> renderToBytes(CCNode* node, int& width, int& height) {
30+
// Get scale from cocos2d units to opengl units
31+
GLint viewport[4];
32+
glGetIntegerv(GL_VIEWPORT, viewport);
33+
auto winSize = CCDirector::get()->getWinSize();
34+
35+
width = node->getContentSize().width * (viewport[2] / winSize.width);
36+
height = node->getContentSize().height * (viewport[3] / winSize.height);
37+
38+
// Create Texture
39+
GLuint texture;
40+
glGenTextures(1, &texture);
41+
glBindTexture(GL_TEXTURE_2D, texture);
42+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
43+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
44+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
45+
46+
// Create Framebuffer Object
47+
GLuint fbo;
48+
glGenFramebuffers(1, &fbo);
49+
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
50+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
51+
52+
// Unbind texture
53+
glBindTexture(GL_TEXTURE_2D, 0);
54+
55+
// Clear any data
56+
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
57+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
58+
59+
// Flip Y when projecting
60+
kmGLMatrixMode(KM_GL_PROJECTION);
61+
kmGLPushMatrix();
62+
kmGLLoadIdentity();
63+
64+
kmMat4 ortho;
65+
kmMat4OrthographicProjection(&ortho,
66+
0.0f, winSize.width,
67+
winSize.height, 0.0f,
68+
-1.0f, 1.0f
69+
);
70+
kmGLMultMatrix(&ortho);
71+
72+
// Transform matrix so the node is drawn at 0,0
73+
kmGLMatrixMode(KM_GL_MODELVIEW);
74+
kmGLPushMatrix();
75+
kmGLLoadIdentity();
76+
77+
auto anchor = node->isIgnoreAnchorPointForPosition() ? ccp(0, 0) : node->getAnchorPointInPoints();
78+
kmGLTranslatef(
79+
anchor.x - node->getPositionX(),
80+
anchor.y - node->getPositionY() + (winSize.height - node->getContentSize().height),
81+
0
82+
);
83+
84+
// Visit
85+
node->visit();
86+
87+
// Undo matrix transformations
88+
kmGLPopMatrix();
89+
kmGLMatrixMode(KM_GL_PROJECTION);
90+
kmGLPopMatrix();
91+
kmGLMatrixMode(KM_GL_MODELVIEW);
92+
93+
// Read from Framebuffer
94+
std::vector<unsigned char> pixels(width * height * 4); // RGBA8
95+
glReadPixels(
96+
0, 0, width, height,
97+
GL_RGBA, GL_UNSIGNED_BYTE,
98+
pixels.data()
99+
);
100+
101+
// Unbind Framebuffer
102+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
103+
104+
// Delete
105+
glDeleteFramebuffers(1, &fbo);
106+
glDeleteTextures(1, &texture);
107+
108+
return pixels;
109+
}
110+
111+
void saveRenderToFile(std::vector<uint8_t> const& data, int width, int height, char const* filename) {
112+
auto img = new CCImage();
113+
img->initWithImageData((void*)data.data(), data.size(), CCImage::kFmtRawData, width, height, 8);
114+
img->saveToFile(filename, false);
19115
}

0 commit comments

Comments
 (0)