Skip to content

Commit 1cd6b0f

Browse files
committed
Render
1 parent f9b8319 commit 1cd6b0f

File tree

8 files changed

+225
-1
lines changed

8 files changed

+225
-1
lines changed

mod.json

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

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())
48+
return;
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::sharedDirector()->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: 17 additions & 0 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)();
@@ -64,6 +66,21 @@ void DevTools::drawBasicAttributes(CCNode* node) {
6466
if (ImGui::Button(U8STR(FEATHER_COPY " Copy Class Name"))) {
6567
clipboard::write(getNodeName(node));
6668
}
69+
ImGui::SameLine();
70+
if (ImGui::Button("Render")) {
71+
72+
file::pick(file::PickMode::SaveFile, file::FilePickOptions {
73+
.filters = {{ .description = "PNG Image", .files = {"*.png"} }}
74+
}).listen([node](auto choice) {
75+
if (auto file = choice->ok()) {
76+
int width, height;
77+
auto bytes = renderToBytes(node, width, height);
78+
79+
saveRenderToFile(bytes, width, height, file->c_str());
80+
}
81+
});
82+
}
83+
6784
ImGui::Text("Address: %s", fmt::to_string(fmt::ptr(node)).c_str());
6885
ImGui::SameLine();
6986
if (ImGui::Button(U8STR(FEATHER_COPY " Copy"))) {

src/platform/Android.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,11 @@ std::string formatAddressIntoOffsetImpl(uintptr_t addr, bool module) {
1313
return fmt::format("{:#x}", addr);
1414
}
1515

16+
void saveRenderToFile(std::vector<uint8_t> const& data, float width, float height, char const* filename) {
17+
auto img = new CCImage;
18+
img->initWithImageData((void*)data.data(), data.size(), kCCImageFormatRGBA8888, width, height, 8);
19+
img->saveToFile(filename);
20+
}
21+
22+
1623
#endif

src/platform/Mac.mm

Lines changed: 80 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,9 @@
1820
#include <mach-o/dyld.h>
1921
#import <Foundation/Foundation.h>
2022

23+
#import <CoreGraphics/CoreGraphics.h>
24+
#include <ImageIO/CGImageDestination.h>
25+
2126
static std::vector<struct dyld_image_info const*> getAllImages() {
2227
std::vector<struct dyld_image_info const*> images;
2328
struct task_dyld_info dyldInfo;
@@ -86,4 +91,79 @@
8691
else return fmt::format("{:#x}", addr - base);
8792
}
8893

94+
void saveRenderToFile(std::vector<uint8_t> const& data, int width, int height, char const* filename) {
95+
assert(width * height * 4 == data.size());
96+
97+
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, &data[0], data.size(), NULL);
98+
if (!provider) {
99+
geode::log::error("Failed to create CGDataProvider");
100+
return;
101+
}
102+
103+
// create cgImg
104+
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
105+
if (!colorSpace) {
106+
geode::log::error("Failed to create CGColorSpace");
107+
CGDataProviderRelease(provider);
108+
return;
109+
}
110+
CGImageRef cgImg = CGImageCreate(
111+
width,
112+
height,
113+
8,
114+
32,
115+
width * 4,
116+
colorSpace,
117+
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast,
118+
provider,
119+
NULL,
120+
false,
121+
kCGRenderingIntentDefault
122+
);
123+
124+
if (!cgImg) {
125+
geode::log::error("Failed to create CGImage");
126+
CGDataProviderRelease(provider);
127+
CGColorSpaceRelease(colorSpace);
128+
return;
129+
}
130+
131+
CFMutableDataRef pngData = CFDataCreateMutable(NULL, 0);
132+
CGImageDestinationRef destination = CGImageDestinationCreateWithData(pngData, kUTTypePNG, 1, NULL);
133+
if (!destination) {
134+
geode::log::error("Failed to create CGImageDestination");
135+
CFRelease(pngData);
136+
CGImageRelease(cgImg);
137+
CGDataProviderRelease(provider);
138+
CGColorSpaceRelease(colorSpace);
139+
return;
140+
}
141+
142+
CGImageDestinationAddImage(destination, cgImg, nil);
143+
if (!CGImageDestinationFinalize(destination)) {
144+
geode::log::error("Failed to write image to data");
145+
CFRelease(destination);
146+
CFRelease(pngData);
147+
CGImageRelease(cgImg);
148+
CGDataProviderRelease(provider);
149+
CGColorSpaceRelease(colorSpace);
150+
return;
151+
}
152+
153+
std::vector<uint8_t> vec;
154+
vec.resize(CFDataGetLength(pngData));
155+
CFDataGetBytes(pngData, CFRangeMake(0, vec.size()), vec.data());
156+
157+
if (auto err = geode::utils::file::writeBinary(filename, vec).err()) {
158+
geode::log::error("Failed to write image to {}: {}", filename, err);
159+
}
160+
161+
CFRelease(destination);
162+
CFRelease(pngData);
163+
CGImageRelease(cgImg);
164+
CGColorSpaceRelease(colorSpace);
165+
CGDataProviderRelease(provider);
166+
}
167+
168+
89169
#endif

src/platform/Win32.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class $modify(CCEGLView) {
5959

6060
#include "utils.hpp"
6161

62+
void saveRenderToFile(std::vector<uint8_t> const& data, float width, float height, char const* filename) {
63+
auto img = new CCImage;
64+
img->initWithImageData((void*)data.data(), data.size(), kCCImageFormatRGBA8888, width, height, 8);
65+
img->saveToFile(filename);
66+
}
67+
6268
std::string formatAddressIntoOffsetImpl(uintptr_t addr, bool module) {
6369
HMODULE mod;
6470

src/platform/utils.cpp

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

3+
#include <OpenGL/gl.h>
34
#include <unordered_map>
5+
#include <cocos2d.h>
46

57
std::string formatAddressIntoOffset(uintptr_t addr, bool module) {
68
static std::unordered_map<uintptr_t, std::pair<std::string, std::string>> formatted;
@@ -16,4 +18,85 @@ std::string formatAddressIntoOffset(uintptr_t addr, bool module) {
1618
if(module) return pair.first;
1719
else return pair.second;
1820
}
21+
}
22+
23+
std::vector<uint8_t> renderToBytes(cocos2d::CCNode* node, int& width, int& height) {
24+
// Get scale from cocos2d units to opengl units
25+
GLint viewport[4];
26+
glGetIntegerv(GL_VIEWPORT, viewport);
27+
auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize();
28+
29+
width = node->getContentSize().width * (viewport[2] / winSize.width);
30+
height = node->getContentSize().height * (viewport[3] / winSize.height);
31+
32+
// Create Texture
33+
GLuint texture;
34+
glGenTextures(1, &texture);
35+
glBindTexture(GL_TEXTURE_2D, texture);
36+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
37+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
38+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
39+
40+
// Create Framebuffer Obejct
41+
GLuint fbo;
42+
glGenFramebuffers(1, &fbo);
43+
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
44+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
45+
46+
glBindTexture(GL_TEXTURE_2D, 0);
47+
48+
// Clear any data
49+
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
50+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
51+
52+
// Flip Y axis when projecting
53+
kmGLMatrixMode(KM_GL_PROJECTION);
54+
kmGLPushMatrix();
55+
kmGLLoadIdentity();
56+
57+
kmMat4 ortho;
58+
kmMat4OrthographicProjection(&ortho,
59+
0.0f, winSize.width, // left, right
60+
winSize.height, 0.0f, // !!! Swap top and bottom
61+
-1.0f, 1.0f // near, far
62+
);
63+
kmGLMultMatrix(&ortho);
64+
65+
// Transform matrix so the node is drawn at 0,0
66+
kmGLMatrixMode(KM_GL_MODELVIEW);
67+
kmGLPushMatrix();
68+
kmGLLoadIdentity();
69+
70+
auto anchor = node->isIgnoreAnchorPointForPosition() ? ccp(0, 0) : node->getAnchorPointInPoints();
71+
kmGLTranslatef(
72+
anchor.x - node->getPositionX(),
73+
anchor.y - node->getPositionY() + (winSize.height - node->getContentSize().height),
74+
0
75+
);
76+
77+
// Visit
78+
node->visit();
79+
80+
// Undo matrix transformations
81+
kmGLPopMatrix();
82+
kmGLMatrixMode(KM_GL_PROJECTION);
83+
kmGLPopMatrix();
84+
kmGLMatrixMode(KM_GL_MODELVIEW);
85+
86+
// Read from Framebuffer
87+
std::vector<unsigned char> pixels(width * height * 4); // RGBA8
88+
glReadPixels(
89+
0, 0, width, height,
90+
GL_RGBA, GL_UNSIGNED_BYTE,
91+
pixels.data()
92+
);
93+
94+
// Unbind Framebuffer
95+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
96+
97+
// Delete
98+
glDeleteFramebuffers(1, &fbo);
99+
glDeleteTextures(1, &texture);
100+
101+
return pixels;
19102
}

src/platform/utils.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ static inline std::string getNodeName(cocos2d::CCObject* node) {
2727
std::string formatAddressIntoOffset(uintptr_t addr, bool module);
2828

2929
std::string formatAddressIntoOffsetImpl(uintptr_t addr, bool module);
30+
31+
32+
std::vector<uint8_t> renderToBytes(cocos2d::CCNode* node, int& width, int& height);
33+
void saveRenderToFile(std::vector<uint8_t> const& data, float width, float height, char const* filename);

0 commit comments

Comments
 (0)