rgss 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.clang-format +6 -0
  3. data/.gitignore +167 -0
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/Rakefile +9 -0
  9. data/ext/rgss/cglm-v0.7.9.tar.gz +0 -0
  10. data/ext/rgss/color.c +599 -0
  11. data/ext/rgss/entity.c +373 -0
  12. data/ext/rgss/extconf.rb +53 -0
  13. data/ext/rgss/font.c +135 -0
  14. data/ext/rgss/game.c +469 -0
  15. data/ext/rgss/game.h +99 -0
  16. data/ext/rgss/gl.c +3217 -0
  17. data/ext/rgss/glad.c +1140 -0
  18. data/ext/rgss/glad.h +2129 -0
  19. data/ext/rgss/glfw.c +1453 -0
  20. data/ext/rgss/graphics.c +324 -0
  21. data/ext/rgss/image.c +274 -0
  22. data/ext/rgss/input.c +745 -0
  23. data/ext/rgss/khrplatform.h +290 -0
  24. data/ext/rgss/mat4.c +279 -0
  25. data/ext/rgss/pax_global_header +1 -0
  26. data/ext/rgss/point.c +253 -0
  27. data/ext/rgss/rect.c +449 -0
  28. data/ext/rgss/rgss.c +56 -0
  29. data/ext/rgss/rgss.h +241 -0
  30. data/ext/rgss/stb_image.h +7762 -0
  31. data/ext/rgss/stb_image_write.h +1690 -0
  32. data/ext/rgss/stb_rect_pack.h +628 -0
  33. data/ext/rgss/stb_truetype.h +5011 -0
  34. data/ext/rgss/utf8.h +1652 -0
  35. data/ext/rgss/uthash.h +1133 -0
  36. data/ext/rgss/vec.c +114 -0
  37. data/ext/rgss/vec.h +192 -0
  38. data/ext/rgss/vec2.c +489 -0
  39. data/ext/rgss/vec3.c +751 -0
  40. data/ext/rgss/vec4.c +681 -0
  41. data/lib/rgss.rb +140 -0
  42. data/lib/rgss/batch.rb +57 -0
  43. data/lib/rgss/blend.rb +47 -0
  44. data/lib/rgss/game_object.rb +28 -0
  45. data/lib/rgss/plane.rb +95 -0
  46. data/lib/rgss/renderable.rb +158 -0
  47. data/lib/rgss/rgss.so +0 -0
  48. data/lib/rgss/shader.rb +94 -0
  49. data/lib/rgss/shaders/sprite-frag.glsl +40 -0
  50. data/lib/rgss/shaders/sprite-vert.glsl +17 -0
  51. data/lib/rgss/sprite.rb +139 -0
  52. data/lib/rgss/stubs/color.rb +318 -0
  53. data/lib/rgss/stubs/gl.rb +1999 -0
  54. data/lib/rgss/stubs/glfw.rb +626 -0
  55. data/lib/rgss/stubs/rect.rb +324 -0
  56. data/lib/rgss/stubs/rpg.rb +267 -0
  57. data/lib/rgss/stubs/tone.rb +65 -0
  58. data/lib/rgss/texture.rb +132 -0
  59. data/lib/rgss/tilemap.rb +116 -0
  60. data/lib/rgss/version.rb +3 -0
  61. data/lib/rgss/viewport.rb +67 -0
  62. data/rgss.gemspec +44 -0
  63. data/test.png +0 -0
  64. metadata +178 -0
@@ -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
+ }
@@ -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
+ }