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.
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
+ }