rgss 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.clang-format +6 -0
- data/.gitignore +167 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +9 -0
- data/ext/rgss/cglm-v0.7.9.tar.gz +0 -0
- data/ext/rgss/color.c +599 -0
- data/ext/rgss/entity.c +373 -0
- data/ext/rgss/extconf.rb +53 -0
- data/ext/rgss/font.c +135 -0
- data/ext/rgss/game.c +469 -0
- data/ext/rgss/game.h +99 -0
- data/ext/rgss/gl.c +3217 -0
- data/ext/rgss/glad.c +1140 -0
- data/ext/rgss/glad.h +2129 -0
- data/ext/rgss/glfw.c +1453 -0
- data/ext/rgss/graphics.c +324 -0
- data/ext/rgss/image.c +274 -0
- data/ext/rgss/input.c +745 -0
- data/ext/rgss/khrplatform.h +290 -0
- data/ext/rgss/mat4.c +279 -0
- data/ext/rgss/pax_global_header +1 -0
- data/ext/rgss/point.c +253 -0
- data/ext/rgss/rect.c +449 -0
- data/ext/rgss/rgss.c +56 -0
- data/ext/rgss/rgss.h +241 -0
- data/ext/rgss/stb_image.h +7762 -0
- data/ext/rgss/stb_image_write.h +1690 -0
- data/ext/rgss/stb_rect_pack.h +628 -0
- data/ext/rgss/stb_truetype.h +5011 -0
- data/ext/rgss/utf8.h +1652 -0
- data/ext/rgss/uthash.h +1133 -0
- data/ext/rgss/vec.c +114 -0
- data/ext/rgss/vec.h +192 -0
- data/ext/rgss/vec2.c +489 -0
- data/ext/rgss/vec3.c +751 -0
- data/ext/rgss/vec4.c +681 -0
- data/lib/rgss.rb +140 -0
- data/lib/rgss/batch.rb +57 -0
- data/lib/rgss/blend.rb +47 -0
- data/lib/rgss/game_object.rb +28 -0
- data/lib/rgss/plane.rb +95 -0
- data/lib/rgss/renderable.rb +158 -0
- data/lib/rgss/rgss.so +0 -0
- data/lib/rgss/shader.rb +94 -0
- data/lib/rgss/shaders/sprite-frag.glsl +40 -0
- data/lib/rgss/shaders/sprite-vert.glsl +17 -0
- data/lib/rgss/sprite.rb +139 -0
- data/lib/rgss/stubs/color.rb +318 -0
- data/lib/rgss/stubs/gl.rb +1999 -0
- data/lib/rgss/stubs/glfw.rb +626 -0
- data/lib/rgss/stubs/rect.rb +324 -0
- data/lib/rgss/stubs/rpg.rb +267 -0
- data/lib/rgss/stubs/tone.rb +65 -0
- data/lib/rgss/texture.rb +132 -0
- data/lib/rgss/tilemap.rb +116 -0
- data/lib/rgss/version.rb +3 -0
- data/lib/rgss/viewport.rb +67 -0
- data/rgss.gemspec +44 -0
- data/test.png +0 -0
- metadata +178 -0
data/ext/rgss/graphics.c
ADDED
@@ -0,0 +1,324 @@
|
|
1
|
+
#include "game.h"
|
2
|
+
#include "glad.h"
|
3
|
+
|
4
|
+
VALUE rb_mGraphics;
|
5
|
+
ID render_id;
|
6
|
+
|
7
|
+
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
|
8
|
+
|
9
|
+
#define GL_DEBUG_SOURCE_API 0x8246
|
10
|
+
#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247
|
11
|
+
#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248
|
12
|
+
#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249
|
13
|
+
#define GL_DEBUG_SOURCE_APPLICATION 0x824A
|
14
|
+
#define GL_DEBUG_SOURCE_OTHER 0x824B
|
15
|
+
|
16
|
+
#define GL_DEBUG_TYPE_ERROR 0x824C
|
17
|
+
#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
|
18
|
+
#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
|
19
|
+
#define GL_DEBUG_TYPE_PORTABILITY 0x824F
|
20
|
+
#define GL_DEBUG_TYPE_PERFORMANCE 0x8250
|
21
|
+
#define GL_DEBUG_TYPE_MARKER 0x8268
|
22
|
+
#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269
|
23
|
+
#define GL_DEBUG_TYPE_POP_GROUP 0x826A
|
24
|
+
#define GL_DEBUG_TYPE_OTHER 0x8251
|
25
|
+
|
26
|
+
#define GL_DEBUG_SEVERITY_LOW 0x9148
|
27
|
+
#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
|
28
|
+
#define GL_DEBUG_SEVERITY_HIGH 0x9146
|
29
|
+
#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
|
30
|
+
|
31
|
+
typedef void (*GLDEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *msg,
|
32
|
+
const void *data);
|
33
|
+
typedef void (APIENTRYP PFGLDEBUGMESSAGECALLBACK)(GLDEBUGPROC callback, const void *userParam);
|
34
|
+
|
35
|
+
|
36
|
+
void RGSS_Graphics_GLCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *msg,
|
37
|
+
const void *data)
|
38
|
+
{
|
39
|
+
if(id == 131169 || id == 131185 || id == 131218 || id == 131204)
|
40
|
+
return;
|
41
|
+
|
42
|
+
printf("%s\n", msg);
|
43
|
+
}
|
44
|
+
|
45
|
+
#define RGSS_VIEWPORT(rect) \
|
46
|
+
glViewport(rect.x, rect.y, rect.width, rect.height);\
|
47
|
+
glScissor(rect.x, rect.y, rect.width, rect.height)
|
48
|
+
|
49
|
+
static void RGSS_Graphics_Reshape(int width, int height)
|
50
|
+
{
|
51
|
+
// Calculate ratios between window and internal resolution
|
52
|
+
RGSS_GAME.graphics.ratio[0] = (float)width / RGSS_GAME.graphics.resolution[0];
|
53
|
+
RGSS_GAME.graphics.ratio[1] = (float)height / RGSS_GAME.graphics.resolution[1];
|
54
|
+
float ratio = RGSS_MIN(RGSS_GAME.graphics.ratio[0], RGSS_GAME.graphics.ratio[1]);
|
55
|
+
|
56
|
+
// Calculate letterbox/pillar rendering coordinates as required
|
57
|
+
int x, y, w, h;
|
58
|
+
w = (int)roundf(RGSS_GAME.graphics.resolution[0] * ratio);
|
59
|
+
h = (int)roundf(RGSS_GAME.graphics.resolution[1] * ratio);
|
60
|
+
x = (int)roundf(((float)width - RGSS_GAME.graphics.resolution[0] * ratio) * 0.5f);
|
61
|
+
y = (int)roundf(((float)height - RGSS_GAME.graphics.resolution[1] * ratio) * 0.5f);
|
62
|
+
|
63
|
+
RGSS_GAME.graphics.viewport = (RGSS_Rect){x, y, w, h};
|
64
|
+
glViewport(x, y, w, h);
|
65
|
+
|
66
|
+
// Ensure the clipping area is also cleared
|
67
|
+
glDisable(GL_SCISSOR_TEST);
|
68
|
+
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
69
|
+
glClear(GL_COLOR_BUFFER_BIT);
|
70
|
+
glEnable(GL_SCISSOR_TEST);
|
71
|
+
glScissor(x, y, w, h);
|
72
|
+
glClearColor(RGSS_GAME.graphics.color[0], RGSS_GAME.graphics.color[1], RGSS_GAME.graphics.color[2],
|
73
|
+
RGSS_GAME.graphics.color[3]);
|
74
|
+
}
|
75
|
+
|
76
|
+
static VALUE RGSS_Graphics_GetFPS(VALUE graphics)
|
77
|
+
{
|
78
|
+
return DBL2NUM(RGSS_GAME.window ? RGSS_GAME.time.fps : 0.0);
|
79
|
+
}
|
80
|
+
|
81
|
+
static VALUE RGSS_Graphics_GetBackColor(VALUE graphics)
|
82
|
+
{
|
83
|
+
RGSS_ASSERT_GAME;
|
84
|
+
float *color = RGSS_VEC4_NEW;
|
85
|
+
memcpy(color, RGSS_GAME.graphics.color, RGSS_VEC4_SIZE);
|
86
|
+
return Data_Wrap_Struct(rb_cColor, NULL, free, color);
|
87
|
+
}
|
88
|
+
|
89
|
+
static VALUE RGSS_Graphics_SetBackColor(VALUE graphics, VALUE color)
|
90
|
+
{
|
91
|
+
RGSS_ASSERT_GAME;
|
92
|
+
if (RTEST(color))
|
93
|
+
{
|
94
|
+
float *vec = DATA_PTR(color);
|
95
|
+
memcpy(RGSS_GAME.graphics.color, vec, RGSS_VEC4_SIZE);
|
96
|
+
}
|
97
|
+
else
|
98
|
+
{
|
99
|
+
memset(RGSS_GAME.graphics.color, 0, RGSS_VEC4_SIZE);
|
100
|
+
}
|
101
|
+
glClearColor(RGSS_GAME.graphics.color[0], RGSS_GAME.graphics.color[1], RGSS_GAME.graphics.color[2],
|
102
|
+
RGSS_GAME.graphics.color[3]);
|
103
|
+
return color;
|
104
|
+
}
|
105
|
+
|
106
|
+
static VALUE RGSS_Graphics_Clear(VALUE graphics)
|
107
|
+
{
|
108
|
+
RGSS_ASSERT_GAME;
|
109
|
+
glClear(GL_COLOR_BUFFER_BIT);
|
110
|
+
return Qnil;
|
111
|
+
}
|
112
|
+
|
113
|
+
static VALUE RGSS_Graphics_Capture(VALUE graphics)
|
114
|
+
{
|
115
|
+
RGSS_ASSERT_GAME;
|
116
|
+
|
117
|
+
// glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
|
118
|
+
// TODO: Set projection/viewport to graphics size, render, read framebuffer, return image
|
119
|
+
|
120
|
+
GLFWimage *image = xmalloc(sizeof(GLFWimage));
|
121
|
+
image->width = (int)RGSS_GAME.graphics.resolution[0];
|
122
|
+
image->height = (int)RGSS_GAME.graphics.resolution[1];
|
123
|
+
image->pixels = xmalloc(sizeof(int) * image->width * image->height);
|
124
|
+
|
125
|
+
glViewport(0, 0, image->width, image->height);
|
126
|
+
rb_funcall(graphics, render_id, 1, DBL2NUM(0.0));
|
127
|
+
|
128
|
+
glReadPixels(0, 0, image->width, image->height, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
|
129
|
+
RGSS_VIEWPORT(RGSS_GAME.graphics.viewport);
|
130
|
+
|
131
|
+
return Data_Wrap_Struct(rb_cImage, NULL, RGSS_Image_Free, image);
|
132
|
+
}
|
133
|
+
|
134
|
+
static VALUE RGSS_Graphics_GetResolution(VALUE graphics)
|
135
|
+
{
|
136
|
+
if (RGSS_GAME.window == NULL)
|
137
|
+
return RGSS_Size_New(0, 0);
|
138
|
+
|
139
|
+
return RGSS_Size_New((int)RGSS_GAME.graphics.resolution[0], (int)RGSS_GAME.graphics.resolution[1]);
|
140
|
+
}
|
141
|
+
|
142
|
+
static VALUE RGSS_Graphics_SetResolution(VALUE graphics, VALUE size)
|
143
|
+
{
|
144
|
+
RGSS_ASSERT_GAME;
|
145
|
+
RGSS_Size *ivec = DATA_PTR(size);
|
146
|
+
|
147
|
+
if (ivec->width < 0)
|
148
|
+
rb_raise(rb_eArgError, "width must be greater than 0 (given %d)", ivec->width);
|
149
|
+
if (ivec->height < 0)
|
150
|
+
rb_raise(rb_eArgError, "height must be greater than 0 (given %d)", ivec->height);
|
151
|
+
|
152
|
+
RGSS_GAME.graphics.resolution[0] = (float)ivec->width;
|
153
|
+
RGSS_GAME.graphics.resolution[1] = (float)ivec->height;
|
154
|
+
glm_ortho(0.0f, (float)ivec->width, (float)ivec->height, 0.0f, -1.0f, 1.0f, RGSS_GAME.graphics.projection);
|
155
|
+
|
156
|
+
GLvoid *p = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY);
|
157
|
+
memcpy(p, RGSS_GAME.graphics.projection, RGSS_MAT4_SIZE);
|
158
|
+
glUnmapBuffer(GL_UNIFORM_BUFFER);
|
159
|
+
|
160
|
+
int w, h;
|
161
|
+
glfwGetFramebufferSize(RGSS_GAME.window, &w, &h);
|
162
|
+
RGSS_Graphics_Reshape(w, h);
|
163
|
+
}
|
164
|
+
|
165
|
+
static void RGSS_Graphics_ResizeCallback(GLFWwindow *window, int width, int height)
|
166
|
+
{
|
167
|
+
RGSS_Graphics_Reshape(width, height);
|
168
|
+
// TODO: Call block if set
|
169
|
+
}
|
170
|
+
|
171
|
+
static VALUE RGSS_Graphics_Restore(VALUE graphics)
|
172
|
+
{
|
173
|
+
if (RGSS_GAME.window == NULL)
|
174
|
+
return Qnil;
|
175
|
+
|
176
|
+
RGSS_VIEWPORT(RGSS_GAME.graphics.viewport);
|
177
|
+
glClearColor(RGSS_GAME.graphics.color[0], RGSS_GAME.graphics.color[1], RGSS_GAME.graphics.color[2],
|
178
|
+
RGSS_GAME.graphics.color[3]);
|
179
|
+
glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
|
180
|
+
return Qnil;
|
181
|
+
}
|
182
|
+
|
183
|
+
void RGSS_Graphics_Init(GLFWwindow *window, int width, int height, int vsync)
|
184
|
+
{
|
185
|
+
glfwSwapInterval(vsync);
|
186
|
+
RGSS_GAME.graphics.projection = RGSS_MAT4_NEW;
|
187
|
+
RGSS_GAME.graphics.resolution[0] = (float)width;
|
188
|
+
RGSS_GAME.graphics.resolution[1] = (float)height;
|
189
|
+
|
190
|
+
glm_vec2_one(RGSS_GAME.graphics.ratio);
|
191
|
+
RGSS_GAME.graphics.viewport = (RGSS_Rect){0, 0, width, height};
|
192
|
+
glm_ortho(0.0f, RGSS_GAME.graphics.resolution[0], RGSS_GAME.graphics.resolution[1], 0.0f, -1.0f, 1.0f,
|
193
|
+
RGSS_GAME.graphics.projection);
|
194
|
+
|
195
|
+
glGenBuffers(1, &RGSS_GAME.graphics.ubo);
|
196
|
+
glBindBuffer(GL_UNIFORM_BUFFER, RGSS_GAME.graphics.ubo);
|
197
|
+
glBufferData(GL_UNIFORM_BUFFER, RGSS_MAT4_SIZE, RGSS_GAME.graphics.projection, GL_DYNAMIC_DRAW);
|
198
|
+
glBindBufferBase(GL_UNIFORM_BUFFER, 0, RGSS_GAME.graphics.ubo);
|
199
|
+
glBindBuffer(GL_UNIFORM_BUFFER, GL_NONE);
|
200
|
+
|
201
|
+
glfwSetFramebufferSizeCallback(window, RGSS_Graphics_ResizeCallback);
|
202
|
+
|
203
|
+
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
204
|
+
glEnable(GL_SCISSOR_TEST);
|
205
|
+
glEnable(GL_BLEND);
|
206
|
+
|
207
|
+
if (RGSS_GAME.debug)
|
208
|
+
{
|
209
|
+
void *address;
|
210
|
+
address = glfwGetProcAddress("glDebugMessageCallback");
|
211
|
+
if (address)
|
212
|
+
{
|
213
|
+
PFGLDEBUGMESSAGECALLBACK proc = (PFGLDEBUGMESSAGECALLBACK) address;
|
214
|
+
proc(RGSS_Graphics_GLCallback, NULL);
|
215
|
+
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
216
|
+
}
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
void RGSS_Graphics_Deinit(GLFWwindow *window)
|
221
|
+
{
|
222
|
+
glfwSetFramebufferSizeCallback(window, NULL);
|
223
|
+
|
224
|
+
if (RGSS_GAME.graphics.projection)
|
225
|
+
free(RGSS_GAME.graphics.projection);
|
226
|
+
glDeleteBuffers(1, &RGSS_GAME.graphics.ubo);
|
227
|
+
}
|
228
|
+
|
229
|
+
void RGSS_Graphics_Render(double alpha)
|
230
|
+
{
|
231
|
+
glClear(GL_COLOR_BUFFER_BIT);
|
232
|
+
rb_funcall(rb_mGraphics, render_id, 1, DBL2NUM(alpha));
|
233
|
+
|
234
|
+
RGSS_GAME.time.fps_count++;
|
235
|
+
RGSS_GAME.time.total_frames++;
|
236
|
+
glfwSwapBuffers(RGSS_GAME.window);
|
237
|
+
}
|
238
|
+
|
239
|
+
static VALUE RGSS_Graphics_GetFrameCount(VALUE graphics)
|
240
|
+
{
|
241
|
+
return ULL2NUM(RGSS_GAME.window ? RGSS_GAME.time.total_frames : 0);
|
242
|
+
}
|
243
|
+
|
244
|
+
static VALUE RGSS_Graphics_GetProjection(VALUE graphics)
|
245
|
+
{
|
246
|
+
RGSS_ASSERT_GAME;
|
247
|
+
return Data_Wrap_Struct(rb_cMat4, NULL, RUBY_NEVER_FREE, RGSS_GAME.graphics.projection);
|
248
|
+
}
|
249
|
+
|
250
|
+
// static VALUE RGSS_Graphics_SetProjection(VALUE graphics, VALUE projection)
|
251
|
+
// {
|
252
|
+
// RGSS_ASSERT_GAME;
|
253
|
+
// if (RTEST(projection))
|
254
|
+
// {
|
255
|
+
// vec4 *mat4 = DATA_PTR(projection);
|
256
|
+
// glm_mat4_copy(mat4, RGSS_GAME.graphics.projection);
|
257
|
+
// }
|
258
|
+
// else
|
259
|
+
// {
|
260
|
+
// glm_mat4_identity(RGSS_GAME.graphics.projection);
|
261
|
+
// }
|
262
|
+
// return projection;
|
263
|
+
// }
|
264
|
+
|
265
|
+
static VALUE RGSS_Graphics_Project(int argc, VALUE *argv, VALUE graphics)
|
266
|
+
{
|
267
|
+
rb_need_block();
|
268
|
+
|
269
|
+
VALUE x, y, w, h;
|
270
|
+
rb_scan_args(argc, argv, "13", &x, &y, &w, &h);
|
271
|
+
glBindBuffer(GL_UNIFORM_BUFFER, RGSS_GAME.graphics.ubo);
|
272
|
+
|
273
|
+
if (argc == 1)
|
274
|
+
{
|
275
|
+
glBufferSubData(GL_UNIFORM_BUFFER, 0, RGSS_MAT4_SIZE, DATA_PTR(x));
|
276
|
+
}
|
277
|
+
else if (argc == 4)
|
278
|
+
{
|
279
|
+
mat4 mat;
|
280
|
+
glm_ortho(NUM2FLT(x), NUM2FLT(x + w), NUM2FLT(y), NUM2FLT(y + h), -1.0f, 1.0f, mat);
|
281
|
+
glBufferSubData(GL_UNIFORM_BUFFER, 0, RGSS_MAT4_SIZE, mat);
|
282
|
+
}
|
283
|
+
else
|
284
|
+
{
|
285
|
+
rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1 or 4)", argc);
|
286
|
+
}
|
287
|
+
glBindBuffer(GL_UNIFORM_BUFFER, GL_NONE);
|
288
|
+
|
289
|
+
rb_yield(Qundef);
|
290
|
+
|
291
|
+
glBindBuffer(GL_UNIFORM_BUFFER, RGSS_GAME.graphics.ubo);
|
292
|
+
glBufferSubData(GL_UNIFORM_BUFFER, 0, RGSS_MAT4_SIZE, RGSS_GAME.graphics.projection);
|
293
|
+
glBindBuffer(GL_UNIFORM_BUFFER, GL_NONE);
|
294
|
+
return Qnil;
|
295
|
+
}
|
296
|
+
|
297
|
+
static VALUE RGSS_Graphics_GetUniformBlock(VALUE graphics)
|
298
|
+
{
|
299
|
+
RGSS_ASSERT_GAME;
|
300
|
+
return UINT2NUM(RGSS_GAME.graphics.ubo);
|
301
|
+
}
|
302
|
+
|
303
|
+
void RGSS_Init_Graphics(VALUE parent)
|
304
|
+
{
|
305
|
+
rb_mGraphics = rb_define_module_under(parent, "Graphics");
|
306
|
+
rb_define_singleton_method0(rb_mGraphics, "frame_rate", RGSS_Graphics_GetFPS, 0);
|
307
|
+
rb_define_singleton_method0(rb_mGraphics, "frame_count", RGSS_Graphics_GetFrameCount, 0);
|
308
|
+
rb_define_singleton_method0(rb_mGraphics, "back_color", RGSS_Graphics_GetBackColor, 0);
|
309
|
+
rb_define_singleton_method1(rb_mGraphics, "back_color=", RGSS_Graphics_SetBackColor, 1);
|
310
|
+
rb_define_singleton_method0(rb_mGraphics, "clear", RGSS_Graphics_Clear, 0);
|
311
|
+
rb_define_singleton_method0(rb_mGraphics, "restore", RGSS_Graphics_Restore, 0);
|
312
|
+
rb_define_singleton_method0(rb_mGraphics, "resolution", RGSS_Graphics_GetResolution, 0);
|
313
|
+
rb_define_singleton_method1(rb_mGraphics, "resolution=", RGSS_Graphics_SetResolution, 1);
|
314
|
+
|
315
|
+
rb_define_singleton_method0(rb_mGraphics, "projection", RGSS_Graphics_GetProjection, 0);
|
316
|
+
rb_define_singleton_method0(rb_mGraphics, "ubo", RGSS_Graphics_GetUniformBlock, 0);
|
317
|
+
rb_define_singleton_methodm1(rb_mGraphics, "project", RGSS_Graphics_Project, -1);
|
318
|
+
// rb_define_singleton_method1(rb_mGraphics, "projection=", RGSS_Graphics_SetProjection, 1);
|
319
|
+
|
320
|
+
VALUE singleton = rb_singleton_class(rb_mGraphics);
|
321
|
+
rb_define_alias(singleton, "fps", "frame_rate");
|
322
|
+
|
323
|
+
render_id = rb_intern("render");
|
324
|
+
}
|
data/ext/rgss/image.c
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
#include "rgss.h"
|
2
|
+
#include "GLFW/glfw3.h"
|
3
|
+
|
4
|
+
VALUE rb_cImage;
|
5
|
+
|
6
|
+
#define STBI_NO_PSD 1
|
7
|
+
#define STBI_NO_HDR 1
|
8
|
+
#define STBI_NO_PIC 1
|
9
|
+
#define STBI_NO_PNM 1
|
10
|
+
|
11
|
+
// stb_image.h macros
|
12
|
+
#define STBI_ASSERT RUBY_ASSERT
|
13
|
+
#define STBI_FREE xfree
|
14
|
+
#define STBI_MALLOC xmalloc
|
15
|
+
#define STBI_REALLOC xrealloc
|
16
|
+
#define STB_IMAGE_IMPLEMENTATION
|
17
|
+
#include "stb_image.h"
|
18
|
+
|
19
|
+
// stb_image_write.h macros
|
20
|
+
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
21
|
+
#define STBIW_ASSERT RUBY_ASSERT
|
22
|
+
#define STBIW_MALLOC xmalloc
|
23
|
+
#define STBIW_REALLOC xrealloc
|
24
|
+
#define STBIW_FREE xfree
|
25
|
+
#include "stb_image_write.h"
|
26
|
+
|
27
|
+
#define JPEG_QUALITY 95
|
28
|
+
#define BYTES_PER_PIXEL 4
|
29
|
+
#define COMPONENT_COUNT 4
|
30
|
+
|
31
|
+
void RGSS_Image_Free(void *img)
|
32
|
+
{
|
33
|
+
if (img)
|
34
|
+
{
|
35
|
+
GLFWimage *image = img;
|
36
|
+
if (image->pixels)
|
37
|
+
stbi_image_free(image->pixels);
|
38
|
+
xfree(image);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
static VALUE RGSS_Image_Alloc(VALUE klass)
|
43
|
+
{
|
44
|
+
GLFWimage *image = ALLOC(GLFWimage);
|
45
|
+
memset(image, 0, sizeof(GLFWimage));
|
46
|
+
return Data_Wrap_Struct(klass, NULL, RGSS_Image_Free, image);
|
47
|
+
}
|
48
|
+
|
49
|
+
static VALUE RGSS_Image_GetWidth(VALUE self)
|
50
|
+
{
|
51
|
+
GLFWimage *image = DATA_PTR(self);
|
52
|
+
return INT2NUM(image->width);
|
53
|
+
}
|
54
|
+
|
55
|
+
static VALUE RGSS_Image_GetHeight(VALUE self)
|
56
|
+
{
|
57
|
+
GLFWimage *image = DATA_PTR(self);
|
58
|
+
return INT2NUM(image->height);
|
59
|
+
}
|
60
|
+
|
61
|
+
static VALUE RGSS_Image_GetPixels(VALUE self)
|
62
|
+
{
|
63
|
+
GLFWimage *image = DATA_PTR(self);
|
64
|
+
if (image->pixels)
|
65
|
+
{
|
66
|
+
size_t size = image->width * image->height * BYTES_PER_PIXEL;
|
67
|
+
return rb_str_new((char*) image->pixels, size);
|
68
|
+
}
|
69
|
+
return Qnil;
|
70
|
+
}
|
71
|
+
|
72
|
+
void RGSS_Image_Load(const char *path, int *width, int *height, unsigned char **pixels)
|
73
|
+
{
|
74
|
+
*pixels = stbi_load(path, width, height, NULL, COMPONENT_COUNT);
|
75
|
+
if (pixels == NULL)
|
76
|
+
rb_raise(rb_eRuntimeError, "failed to load image");
|
77
|
+
}
|
78
|
+
|
79
|
+
static VALUE RGSS_Image_Dispose(VALUE self)
|
80
|
+
{
|
81
|
+
GLFWimage *image = DATA_PTR(self);
|
82
|
+
if (image->pixels)
|
83
|
+
{
|
84
|
+
xfree(image->pixels);
|
85
|
+
image->pixels = NULL;
|
86
|
+
}
|
87
|
+
return Qnil;
|
88
|
+
}
|
89
|
+
|
90
|
+
static VALUE RGSS_Image_IsDisposed(VALUE self)
|
91
|
+
{
|
92
|
+
GLFWimage *image = DATA_PTR(self);
|
93
|
+
return RB_BOOL(image->pixels == NULL);
|
94
|
+
}
|
95
|
+
|
96
|
+
static VALUE RGSS_Image_Initialize(int argc, VALUE *argv, VALUE self)
|
97
|
+
{
|
98
|
+
VALUE x, y, blob;
|
99
|
+
rb_scan_args(argc, argv, "12", &x, &y, &blob);
|
100
|
+
|
101
|
+
GLFWimage *image = DATA_PTR(self);
|
102
|
+
|
103
|
+
switch (argc)
|
104
|
+
{
|
105
|
+
case 1:
|
106
|
+
{
|
107
|
+
RGSS_Image_Load(StringValueCStr(x), &image->width, &image->height, &image->pixels);
|
108
|
+
break;
|
109
|
+
}
|
110
|
+
case 2:
|
111
|
+
case 3:
|
112
|
+
{
|
113
|
+
image->width = NUM2INT(x);
|
114
|
+
if (image->width < 1)
|
115
|
+
rb_raise(rb_eArgError, "width must be greater than 0");
|
116
|
+
|
117
|
+
image->height = NUM2INT(y);
|
118
|
+
if (image->height < 1)
|
119
|
+
rb_raise(rb_eArgError, "height must be greater than 0");
|
120
|
+
|
121
|
+
size_t size = image->width * image->height * BYTES_PER_PIXEL;
|
122
|
+
image->pixels = xmalloc(size);
|
123
|
+
if (image->pixels == NULL)
|
124
|
+
rb_raise(rb_eNoMemError, "out of memory");
|
125
|
+
|
126
|
+
if (RTEST(blob))
|
127
|
+
{
|
128
|
+
long len = RSTRING_LEN(blob);
|
129
|
+
if (len != size)
|
130
|
+
rb_raise(rb_eArgError, "invalid blob length (given %d bytes, expected %u bytes)", len, size);
|
131
|
+
memcpy(image->pixels, StringValuePtr(blob), size);
|
132
|
+
}
|
133
|
+
else
|
134
|
+
{
|
135
|
+
memset(image->pixels, 0, size);
|
136
|
+
}
|
137
|
+
|
138
|
+
break;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
|
143
|
+
return self;
|
144
|
+
}
|
145
|
+
|
146
|
+
static VALUE RGSS_Image_GetAddress(VALUE self)
|
147
|
+
{
|
148
|
+
GLFWimage *image = DATA_PTR(self);
|
149
|
+
return PTR2NUM(image->pixels);
|
150
|
+
}
|
151
|
+
|
152
|
+
static inline size_t RGSS_Image_GetOffset(GLFWimage *image, int x, int y)
|
153
|
+
{
|
154
|
+
if (image->pixels == NULL)
|
155
|
+
rb_raise(rb_eArgError, "disposed image");
|
156
|
+
|
157
|
+
if (x < 0 || x >= image->width)
|
158
|
+
rb_raise(rb_eIndexError, "x value out of range (given %d, expected 0..%d)", x, image->width - 1);
|
159
|
+
if (y < 0 || y >= image->height)
|
160
|
+
rb_raise(rb_eIndexError, "y value out of range (given %d, expected 0..%d)", y, image->height - 1);
|
161
|
+
|
162
|
+
return (x * BYTES_PER_PIXEL) + (y * image->width * BYTES_PER_PIXEL);
|
163
|
+
}
|
164
|
+
|
165
|
+
static VALUE RGSS_Image_GetPixel(VALUE self, VALUE x, VALUE y)
|
166
|
+
{
|
167
|
+
GLFWimage *image = DATA_PTR(self);
|
168
|
+
size_t i = RGSS_Image_GetOffset(image, NUM2INT(x), NUM2INT(y));
|
169
|
+
|
170
|
+
float *color = xmalloc(sizeof(float) * 4);
|
171
|
+
color[0] = image->pixels[i + 0] / 255.0f;
|
172
|
+
color[1] = image->pixels[i + 1] / 255.0f;
|
173
|
+
color[2] = image->pixels[i + 2] / 255.0f;
|
174
|
+
color[3] = image->pixels[i + 3] / 255.0f;
|
175
|
+
return Data_Wrap_Struct(rb_cColor, NULL, RUBY_DEFAULT_FREE, color);
|
176
|
+
}
|
177
|
+
|
178
|
+
static VALUE RGSS_Image_SetPixel(VALUE self, VALUE x, VALUE y, VALUE color)
|
179
|
+
{
|
180
|
+
GLFWimage *image = DATA_PTR(self);
|
181
|
+
size_t i = RGSS_Image_GetOffset(image, NUM2INT(x), NUM2INT(y));
|
182
|
+
|
183
|
+
if (RTEST(color))
|
184
|
+
{
|
185
|
+
float *vec = DATA_PTR(color);
|
186
|
+
image->pixels[i + 0] = (unsigned char) roundf(vec[0] * 255.0f);
|
187
|
+
image->pixels[i + 1] = (unsigned char) roundf(vec[1] * 255.0f);
|
188
|
+
image->pixels[i + 2] = (unsigned char) roundf(vec[2] * 255.0f);
|
189
|
+
image->pixels[i + 3] = (unsigned char) roundf(vec[3] * 255.0f);
|
190
|
+
}
|
191
|
+
else
|
192
|
+
memset(&image->pixels[i], 0, BYTES_PER_PIXEL);
|
193
|
+
|
194
|
+
return self;
|
195
|
+
}
|
196
|
+
|
197
|
+
static VALUE RGSS_Image_Save(int argc, VALUE *argv, VALUE self)
|
198
|
+
{
|
199
|
+
VALUE path, format, opts;
|
200
|
+
rb_scan_args(argc, argv, "11:", &path, &format, &opts);
|
201
|
+
|
202
|
+
GLFWimage *image = DATA_PTR(self);
|
203
|
+
if (image->pixels == NULL)
|
204
|
+
rb_raise(rb_eRuntimeError, "disposed image");
|
205
|
+
|
206
|
+
VALUE type = RTEST(format) ? format : STR2SYM("png");
|
207
|
+
|
208
|
+
int result;
|
209
|
+
int flip = RTEST(opts) && RTEST(rb_hash_aref(opts, STR2SYM("flip")));
|
210
|
+
stbi_flip_vertically_on_write(flip);
|
211
|
+
const char *file = StringValueCStr(path);
|
212
|
+
|
213
|
+
if (type == STR2SYM("png"))
|
214
|
+
{
|
215
|
+
stbi_write_png_compression_level = 8;
|
216
|
+
if (RTEST(opts))
|
217
|
+
{
|
218
|
+
VALUE cmp = rb_hash_aref(opts, STR2SYM("compression"));
|
219
|
+
if (RTEST(cmp))
|
220
|
+
stbi_write_png_compression_level = RGSS_MAX(0, RGSS_MIN(9, NUM2INT(cmp)));
|
221
|
+
}
|
222
|
+
|
223
|
+
int stride = image->width * BYTES_PER_PIXEL;
|
224
|
+
result = stbi_write_png(file, image->width, image->height, COMPONENT_COUNT, image->pixels, stride);
|
225
|
+
}
|
226
|
+
else if (type == STR2SYM("jpg") || type == STR2SYM("jpeg"))
|
227
|
+
{
|
228
|
+
int quality = JPEG_QUALITY;
|
229
|
+
if (RTEST(opts))
|
230
|
+
{
|
231
|
+
VALUE q = rb_hash_aref(opts, STR2SYM("quality"));
|
232
|
+
if (RTEST(q))
|
233
|
+
quality = RGSS_MAX(0, RGSS_MIN(100, NUM2INT(q)));
|
234
|
+
}
|
235
|
+
|
236
|
+
result = stbi_write_jpg(file, image->width, image->height, COMPONENT_COUNT, image->pixels, quality);
|
237
|
+
}
|
238
|
+
else if (type == STR2SYM("bmp"))
|
239
|
+
{
|
240
|
+
result = stbi_write_bmp(file, image->width, image->height, COMPONENT_COUNT, image->pixels);
|
241
|
+
}
|
242
|
+
else
|
243
|
+
{
|
244
|
+
rb_raise(rb_eArgError, "invalid image format specified");
|
245
|
+
}
|
246
|
+
|
247
|
+
if (!result)
|
248
|
+
rb_raise(rb_eRuntimeError, "failed to save image");
|
249
|
+
|
250
|
+
return self;
|
251
|
+
}
|
252
|
+
|
253
|
+
void RGSS_Init_Image(VALUE parent)
|
254
|
+
{
|
255
|
+
rb_cImage = rb_define_class_under(parent, "Image", rb_cObject);
|
256
|
+
rb_define_alloc_func(rb_cImage, RGSS_Image_Alloc);
|
257
|
+
|
258
|
+
rb_define_methodm1(rb_cImage, "initialize", RGSS_Image_Initialize, -1);
|
259
|
+
rb_define_method0(rb_cImage, "dispose", RGSS_Image_Dispose, 0);
|
260
|
+
rb_define_method0(rb_cImage, "disposed?", RGSS_Image_IsDisposed, 0);
|
261
|
+
|
262
|
+
rb_define_method0(rb_cImage, "width", RGSS_Image_GetWidth, 0);
|
263
|
+
rb_define_method0(rb_cImage, "height", RGSS_Image_GetHeight, 0);
|
264
|
+
rb_define_method0(rb_cImage, "pixels", RGSS_Image_GetPixels, 0);
|
265
|
+
rb_define_method0(rb_cImage, "address", RGSS_Image_GetAddress, 0);
|
266
|
+
rb_define_method2(rb_cImage, "get_pixel", RGSS_Image_GetPixel, 2);
|
267
|
+
rb_define_method3(rb_cImage, "set_pixel", RGSS_Image_SetPixel, 3);
|
268
|
+
|
269
|
+
rb_define_methodm1(rb_cImage, "save", RGSS_Image_Save, -1);
|
270
|
+
|
271
|
+
rb_define_alias(rb_cImage, "columns", "width");
|
272
|
+
rb_define_alias(rb_cImage, "rows", "height");
|
273
|
+
rb_define_alias(rb_cImage, "blob", "pixels");
|
274
|
+
}
|