ruby2d 0.12.0 → 1.0.0
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 +4 -4
- data/LICENSE.md +7 -0
- data/README.md +141 -0
- data/USAGE.md +2435 -0
- data/assets/Rakefile +637 -0
- data/assets/build_support/CMakeLists.txt +94 -0
- data/assets/build_support/mruby/build_config.rb +34 -0
- data/assets/build_support/mruby/build_config_wasm.rb +30 -0
- data/assets/build_support/sdl_test.c +174 -0
- data/assets/deps.yaml +16 -0
- data/assets/platform/include/SDL3/SDL.h +91 -0
- data/assets/platform/include/SDL3/SDL_assert.h +705 -0
- data/assets/platform/include/SDL3/SDL_asyncio.h +550 -0
- data/assets/platform/include/SDL3/SDL_atomic.h +692 -0
- data/assets/platform/include/SDL3/SDL_audio.h +2370 -0
- data/assets/platform/include/SDL3/SDL_begin_code.h +553 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_bits.h +47 -27
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_blendmode.h +59 -55
- data/assets/platform/include/SDL3/SDL_camera.h +535 -0
- data/assets/platform/include/SDL3/SDL_clipboard.h +331 -0
- data/assets/{include/SDL2/close_code.h → platform/include/SDL3/SDL_close_code.h} +9 -8
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_copying.h +3 -1
- data/assets/platform/include/SDL3/SDL_cpuinfo.h +374 -0
- data/assets/platform/include/SDL3/SDL_dialog.h +343 -0
- data/assets/platform/include/SDL3/SDL_dlopennote.h +234 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_egl.h +11 -8
- data/assets/platform/include/SDL3/SDL_endian.h +645 -0
- data/assets/platform/include/SDL3/SDL_error.h +226 -0
- data/assets/platform/include/SDL3/SDL_events.h +1645 -0
- data/assets/platform/include/SDL3/SDL_filesystem.h +537 -0
- data/assets/platform/include/SDL3/SDL_gamepad.h +1656 -0
- data/assets/platform/include/SDL3/SDL_gpu.h +4608 -0
- data/assets/platform/include/SDL3/SDL_guid.h +106 -0
- data/assets/platform/include/SDL3/SDL_haptic.h +1461 -0
- data/assets/platform/include/SDL3/SDL_hidapi.h +571 -0
- data/assets/platform/include/SDL3/SDL_hints.h +5001 -0
- data/assets/platform/include/SDL3/SDL_init.h +507 -0
- data/assets/platform/include/SDL3/SDL_intrin.h +410 -0
- data/assets/platform/include/SDL3/SDL_iostream.h +1379 -0
- data/assets/platform/include/SDL3/SDL_joystick.h +1385 -0
- data/assets/platform/include/SDL3/SDL_keyboard.h +608 -0
- data/assets/platform/include/SDL3/SDL_keycode.h +347 -0
- data/assets/platform/include/SDL3/SDL_loadso.h +145 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_locale.h +40 -24
- data/assets/platform/include/SDL3/SDL_log.h +541 -0
- data/assets/platform/include/SDL3/SDL_main.h +704 -0
- data/assets/platform/include/SDL3/SDL_main_impl.h +151 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_messagebox.h +85 -48
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_metal.h +29 -29
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_misc.h +13 -12
- data/assets/platform/include/SDL3/SDL_mouse.h +813 -0
- data/assets/platform/include/SDL3/SDL_mutex.h +1117 -0
- data/assets/platform/include/SDL3/SDL_oldnames.h +1339 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengl.h +1247 -278
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengl_glext.h +5 -1
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles.h +6 -7
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2.h +10 -11
- data/assets/platform/include/SDL3/SDL_pen.h +198 -0
- data/assets/platform/include/SDL3/SDL_pixels.h +1441 -0
- data/assets/{include/SDL2/SDL_test_log.h → platform/include/SDL3/SDL_platform.h} +25 -26
- data/assets/platform/include/SDL3/SDL_platform_defines.h +497 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_power.h +40 -20
- data/assets/platform/include/SDL3/SDL_process.h +441 -0
- data/assets/platform/include/SDL3/SDL_properties.h +572 -0
- data/assets/platform/include/SDL3/SDL_rect.h +528 -0
- data/assets/platform/include/SDL3/SDL_render.h +3028 -0
- data/assets/platform/include/SDL3/SDL_revision.h +59 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_scancode.h +59 -68
- data/assets/platform/include/SDL3/SDL_sensor.h +321 -0
- data/assets/platform/include/SDL3/SDL_stdinc.h +6186 -0
- data/assets/platform/include/SDL3/SDL_storage.h +686 -0
- data/assets/platform/include/SDL3/SDL_surface.h +1769 -0
- data/assets/platform/include/SDL3/SDL_system.h +840 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test.h +16 -22
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_assert.h +27 -34
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_common.h +109 -52
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_compare.h +22 -14
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_crc32.h +27 -30
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_font.h +50 -49
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_fuzzer.h +64 -79
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_harness.h +47 -30
- data/assets/platform/include/SDL3/SDL_test_log.h +83 -0
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_md5.h +21 -28
- data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_memory.h +19 -16
- data/assets/platform/include/SDL3/SDL_thread.h +602 -0
- data/assets/platform/include/SDL3/SDL_time.h +249 -0
- data/assets/platform/include/SDL3/SDL_timer.h +454 -0
- data/assets/platform/include/SDL3/SDL_touch.h +184 -0
- data/assets/platform/include/SDL3/SDL_tray.h +544 -0
- data/assets/platform/include/SDL3/SDL_version.h +198 -0
- data/assets/platform/include/SDL3/SDL_video.h +3497 -0
- data/assets/platform/include/SDL3/SDL_vulkan.h +287 -0
- data/assets/platform/include/SDL3_image/SDL_image.h +3328 -0
- data/assets/platform/include/SDL3_mixer/SDL_mixer.h +3369 -0
- data/assets/platform/include/SDL3_ttf/SDL_textengine.h +181 -0
- data/assets/platform/include/SDL3_ttf/SDL_ttf.h +2833 -0
- data/assets/{include → platform/include}/mrbconf.h +42 -31
- data/assets/{include → platform/include}/mruby/array.h +3 -4
- data/assets/{include → platform/include}/mruby/boxing_nan.h +10 -0
- data/assets/platform/include/mruby/boxing_no.h +59 -0
- data/assets/{include → platform/include}/mruby/boxing_word.h +72 -26
- data/assets/platform/include/mruby/class.h +157 -0
- data/assets/{include → platform/include}/mruby/common.h +30 -1
- data/assets/{include → platform/include}/mruby/compile.h +52 -74
- data/assets/{include → platform/include}/mruby/data.h +2 -2
- data/assets/{include → platform/include}/mruby/debug.h +12 -5
- data/assets/{include → platform/include}/mruby/dump.h +7 -9
- data/assets/{include → platform/include}/mruby/endian.h +1 -1
- data/assets/platform/include/mruby/error.h +173 -0
- data/assets/platform/include/mruby/gc.h +79 -0
- data/assets/{include → platform/include}/mruby/hash.h +1 -7
- data/assets/platform/include/mruby/internal.h +282 -0
- data/assets/platform/include/mruby/irep.h +143 -0
- data/assets/{include → platform/include}/mruby/istruct.h +1 -1
- data/assets/platform/include/mruby/khash.h +455 -0
- data/assets/platform/include/mruby/mempool.h +19 -0
- data/assets/{include → platform/include}/mruby/numeric.h +7 -10
- data/assets/platform/include/mruby/object.h +51 -0
- data/assets/{include → platform/include}/mruby/opcode.h +5 -5
- data/assets/{include → platform/include}/mruby/ops.h +19 -6
- data/assets/platform/include/mruby/presym/id.h +1447 -0
- data/assets/{include → platform/include}/mruby/presym/scanning.h +10 -7
- data/assets/platform/include/mruby/presym/table.h +2893 -0
- data/assets/platform/include/mruby/presym.h +65 -0
- data/assets/platform/include/mruby/proc.h +184 -0
- data/assets/{include → platform/include}/mruby/range.h +0 -2
- data/assets/{include → platform/include}/mruby/string.h +33 -53
- data/assets/platform/include/mruby/throw.h +81 -0
- data/assets/{include → platform/include}/mruby/value.h +64 -29
- data/assets/{include → platform/include}/mruby/variable.h +6 -23
- data/assets/{include → platform/include}/mruby/version.h +7 -7
- data/assets/{include → platform/include}/mruby.h +238 -116
- data/assets/platform/macos-arm64/bin/mrbc +0 -0
- data/assets/platform/macos-arm64/lib/libSDL3.a +0 -0
- data/assets/platform/macos-arm64/lib/libSDL3_image.a +0 -0
- data/assets/platform/macos-arm64/lib/libSDL3_mixer.a +0 -0
- data/assets/platform/macos-arm64/lib/libSDL3_ttf.a +0 -0
- data/assets/platform/macos-arm64/lib/libmruby.a +0 -0
- data/assets/platform/wasm/lib/libFLAC.a +0 -0
- data/assets/platform/wasm/lib/libSDL3.a +0 -0
- data/assets/platform/wasm/lib/libSDL3_image.a +0 -0
- data/assets/platform/wasm/lib/libSDL3_mixer.a +0 -0
- data/assets/platform/wasm/lib/libSDL3_ttf.a +0 -0
- data/assets/platform/wasm/lib/libfreetype.a +0 -0
- data/assets/platform/wasm/lib/libharfbuzz.a +0 -0
- data/assets/platform/wasm/lib/libmruby.a +0 -0
- data/assets/platform/wasm/lib/libogg.a +0 -0
- data/assets/platform/wasm/lib/libplutosvg.a +0 -0
- data/assets/platform/wasm/lib/libplutovg.a +0 -0
- data/assets/platform/wasm/lib/libpng.a +0 -0
- data/assets/platform/wasm/lib/libpng16.a +0 -0
- data/assets/platform/wasm/lib/libvorbis.a +0 -0
- data/assets/platform/wasm/lib/libvorbisfile.a +0 -0
- data/assets/platform/wasm/lib/libzlibstatic.a +0 -0
- data/assets/platform/windows-arm64-mingw-ucrt/bin/mrbc.exe +0 -0
- data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3.a +0 -0
- data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3_image.a +0 -0
- data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3_mixer.a +0 -0
- data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3_ttf.a +0 -0
- data/assets/platform/windows-arm64-mingw-ucrt/lib/libmruby.a +0 -0
- data/assets/platform/windows-x86_64-mingw-ucrt/bin/mrbc.exe +0 -0
- data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3.a +0 -0
- data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3_image.a +0 -0
- data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3_mixer.a +0 -0
- data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3_ttf.a +0 -0
- data/assets/platform/windows-x86_64-mingw-ucrt/lib/libmruby.a +0 -0
- data/assets/resources/fonts/outfit/OFL.txt +93 -0
- data/assets/resources/fonts/outfit/outfit.ttf +0 -0
- data/assets/resources/fonts/roboto_mono/OFL.txt +91 -0
- data/assets/resources/fonts/roboto_mono/roboto_mono.ttf +0 -0
- data/assets/resources/icons/icon.icns +0 -0
- data/assets/resources/icons/icon.png +0 -0
- data/assets/resources/spritesheets/License.txt +28 -0
- data/assets/resources/spritesheets/spritesheet-backgrounds.png +0 -0
- data/assets/resources/spritesheets/spritesheet-backgrounds.xml +16 -0
- data/assets/resources/spritesheets/spritesheet-characters.png +0 -0
- data/assets/resources/spritesheets/spritesheet-characters.xml +47 -0
- data/assets/resources/spritesheets/spritesheet-enemies.png +0 -0
- data/assets/resources/spritesheets/spritesheet-enemies.xml +62 -0
- data/assets/resources/spritesheets/spritesheet-tiles.png +0 -0
- data/assets/resources/spritesheets/spritesheet-tiles.xml +316 -0
- data/assets/{wasm → resources/web}/template.html +1 -1
- data/assets/target.rb +131 -0
- data/bin/ruby2d +281 -82
- data/examples/astar.rb +269 -0
- data/examples/asteroids.rb +403 -0
- data/examples/bezier_editor.rb +96 -0
- data/examples/boids.rb +191 -0
- data/examples/bouncing_balls.rb +119 -0
- data/examples/breakout.rb +186 -0
- data/examples/constellations.rb +100 -0
- data/examples/dashboard.rb +133 -0
- data/examples/double_pendulum.rb +254 -0
- data/examples/falling_sand.rb +291 -0
- data/examples/fireworks.rb +110 -0
- data/examples/fluid_dynamics.rb +340 -0
- data/examples/fourier_epicycles.rb +142 -0
- data/examples/fractal_tree.rb +326 -0
- data/examples/game_of_life.rb +148 -0
- data/examples/gamepads.rb +333 -0
- data/examples/hill_driver.rb +613 -0
- data/examples/logo_animation.rb +251 -0
- data/examples/lorenz_attractor.rb +115 -0
- data/examples/mandelbrot.rb +207 -0
- data/examples/marching_squares.rb +200 -0
- data/examples/maze.rb +323 -0
- data/examples/nbody.rb +338 -0
- data/examples/outrun.rb +391 -0
- data/examples/pixel_paint.rb +313 -0
- data/examples/platformer.rb +335 -0
- data/examples/ray_casting_maze.rb +175 -0
- data/examples/shadow_casting.rb +151 -0
- data/examples/shape_recognizer.rb +908 -0
- data/examples/snake.rb +144 -0
- data/examples/sprite_sheets.rb +246 -0
- data/examples/swarm.rb +206 -0
- data/examples/tsp_annealing.rb +104 -0
- data/examples/ui_controls.rb +163 -0
- data/examples/wireframe_3d.rb +139 -0
- data/ext/ruby2d/audio.c +324 -0
- data/ext/ruby2d/canvas.c +1715 -494
- data/ext/ruby2d/ext.c +541 -0
- data/ext/ruby2d/extconf.rb +122 -145
- data/ext/ruby2d/font.c +566 -20
- data/ext/ruby2d/fps.c +92 -0
- data/ext/ruby2d/image.c +466 -41
- data/ext/ruby2d/ruby2d.c +374 -1890
- data/ext/ruby2d/ruby2d.h +840 -460
- data/ext/ruby2d/shapes.c +931 -101
- data/ext/ruby2d/text.c +586 -21
- data/ext/ruby2d/window.c +1693 -283
- data/lib/ruby2d/atlas_parser.rb +194 -0
- data/lib/ruby2d/audio.rb +73 -0
- data/lib/ruby2d/benchmark.rb +259 -0
- data/lib/ruby2d/bitmap_text.rb +155 -0
- data/lib/ruby2d/button.rb +402 -0
- data/lib/ruby2d/canvas.rb +703 -279
- data/lib/ruby2d/circle.rb +206 -29
- data/lib/ruby2d/cli/browser.rb +314 -0
- data/lib/ruby2d/cli/build.rb +471 -257
- data/lib/ruby2d/cli/colorize.rb +7 -5
- data/lib/ruby2d/cli/examples.rb +127 -0
- data/lib/ruby2d/cli/launch.rb +24 -39
- data/lib/ruby2d/cli/lib_files.rb +48 -0
- data/lib/ruby2d/cli/messages.rb +40 -0
- data/lib/ruby2d/cli/setup.rb +273 -0
- data/lib/ruby2d/cli/static_server.rb +160 -0
- data/lib/ruby2d/cli/usage.rb +255 -0
- data/lib/ruby2d/color.rb +191 -69
- data/lib/ruby2d/core.rb +37 -19
- data/lib/ruby2d/deps_help.rb +89 -0
- data/lib/ruby2d/dsl.rb +51 -12
- data/lib/ruby2d/ellipse.rb +228 -0
- data/lib/ruby2d/exceptions.rb +0 -2
- data/lib/ruby2d/font.rb +34 -74
- data/lib/ruby2d/gamepad.rb +323 -0
- data/lib/ruby2d/gem_paths.rb +19 -0
- data/lib/ruby2d/image.rb +155 -52
- data/lib/ruby2d/interactive.rb +103 -0
- data/lib/ruby2d/json_parser.rb +268 -0
- data/lib/ruby2d/line.rb +194 -85
- data/lib/ruby2d/mruby_compat.rb +12 -0
- data/lib/ruby2d/polygon.rb +297 -0
- data/lib/ruby2d/polyline.rb +368 -0
- data/lib/ruby2d/quad.rb +358 -94
- data/lib/ruby2d/rectangle.rb +77 -19
- data/lib/ruby2d/renderable.rb +411 -11
- data/lib/ruby2d/sprite.rb +471 -180
- data/lib/ruby2d/sprite_sheet.rb +64 -0
- data/lib/ruby2d/square.rb +29 -17
- data/lib/ruby2d/text.rb +196 -65
- data/lib/ruby2d/tileset.rb +139 -81
- data/lib/ruby2d/triangle.rb +324 -89
- data/lib/ruby2d/version.rb +1 -3
- data/lib/ruby2d/vertices.rb +62 -85
- data/lib/ruby2d/warnings.rb +29 -0
- data/lib/ruby2d/window/class_methods.rb +218 -0
- data/lib/ruby2d/window/gamepad_events.rb +141 -0
- data/lib/ruby2d/window/key_events.rb +74 -0
- data/lib/ruby2d/window/mouse_events.rb +171 -0
- data/lib/ruby2d/window/object_events.rb +168 -0
- data/lib/ruby2d/window.rb +632 -585
- data/lib/ruby2d.rb +14 -19
- metadata +275 -428
- data/assets/app.icns +0 -0
- data/assets/include/GLES2/gl2.h +0 -656
- data/assets/include/GLES2/gl2ext.h +0 -3949
- data/assets/include/GLES2/gl2ext_angle.h +0 -701
- data/assets/include/GLES2/gl2platform.h +0 -27
- data/assets/include/GLES3/gl3.h +0 -1192
- data/assets/include/GLES3/gl31.h +0 -1507
- data/assets/include/GLES3/gl32.h +0 -1808
- data/assets/include/GLES3/gl3platform.h +0 -27
- data/assets/include/KHR/khrplatform.h +0 -290
- data/assets/include/SDL2/SDL.h +0 -233
- data/assets/include/SDL2/SDL_assert.h +0 -326
- data/assets/include/SDL2/SDL_atomic.h +0 -415
- data/assets/include/SDL2/SDL_audio.h +0 -1500
- data/assets/include/SDL2/SDL_clipboard.h +0 -141
- data/assets/include/SDL2/SDL_config.h +0 -61
- data/assets/include/SDL2/SDL_config_android.h +0 -194
- data/assets/include/SDL2/SDL_config_emscripten.h +0 -218
- data/assets/include/SDL2/SDL_config_iphoneos.h +0 -217
- data/assets/include/SDL2/SDL_config_macosx.h +0 -277
- data/assets/include/SDL2/SDL_config_minimal.h +0 -95
- data/assets/include/SDL2/SDL_config_ngage.h +0 -89
- data/assets/include/SDL2/SDL_config_os2.h +0 -207
- data/assets/include/SDL2/SDL_config_pandora.h +0 -141
- data/assets/include/SDL2/SDL_config_windows.h +0 -331
- data/assets/include/SDL2/SDL_config_wingdk.h +0 -253
- data/assets/include/SDL2/SDL_config_winrt.h +0 -220
- data/assets/include/SDL2/SDL_config_xbox.h +0 -235
- data/assets/include/SDL2/SDL_cpuinfo.h +0 -594
- data/assets/include/SDL2/SDL_endian.h +0 -348
- data/assets/include/SDL2/SDL_error.h +0 -163
- data/assets/include/SDL2/SDL_events.h +0 -1166
- data/assets/include/SDL2/SDL_filesystem.h +0 -149
- data/assets/include/SDL2/SDL_gamecontroller.h +0 -1074
- data/assets/include/SDL2/SDL_gesture.h +0 -117
- data/assets/include/SDL2/SDL_guid.h +0 -100
- data/assets/include/SDL2/SDL_haptic.h +0 -1341
- data/assets/include/SDL2/SDL_hidapi.h +0 -451
- data/assets/include/SDL2/SDL_hints.h +0 -2569
- data/assets/include/SDL2/SDL_image.h +0 -2173
- data/assets/include/SDL2/SDL_joystick.h +0 -1066
- data/assets/include/SDL2/SDL_keyboard.h +0 -353
- data/assets/include/SDL2/SDL_keycode.h +0 -358
- data/assets/include/SDL2/SDL_loadso.h +0 -115
- data/assets/include/SDL2/SDL_log.h +0 -404
- data/assets/include/SDL2/SDL_main.h +0 -275
- data/assets/include/SDL2/SDL_mixer.h +0 -2784
- data/assets/include/SDL2/SDL_mouse.h +0 -465
- data/assets/include/SDL2/SDL_mutex.h +0 -471
- data/assets/include/SDL2/SDL_name.h +0 -33
- data/assets/include/SDL2/SDL_pixels.h +0 -644
- data/assets/include/SDL2/SDL_platform.h +0 -261
- data/assets/include/SDL2/SDL_quit.h +0 -58
- data/assets/include/SDL2/SDL_rect.h +0 -376
- data/assets/include/SDL2/SDL_render.h +0 -1919
- data/assets/include/SDL2/SDL_revision.h +0 -6
- data/assets/include/SDL2/SDL_rwops.h +0 -841
- data/assets/include/SDL2/SDL_sensor.h +0 -322
- data/assets/include/SDL2/SDL_shape.h +0 -155
- data/assets/include/SDL2/SDL_stdinc.h +0 -830
- data/assets/include/SDL2/SDL_surface.h +0 -997
- data/assets/include/SDL2/SDL_system.h +0 -623
- data/assets/include/SDL2/SDL_syswm.h +0 -386
- data/assets/include/SDL2/SDL_test_images.h +0 -78
- data/assets/include/SDL2/SDL_test_random.h +0 -115
- data/assets/include/SDL2/SDL_thread.h +0 -464
- data/assets/include/SDL2/SDL_timer.h +0 -222
- data/assets/include/SDL2/SDL_touch.h +0 -150
- data/assets/include/SDL2/SDL_ttf.h +0 -2316
- data/assets/include/SDL2/SDL_types.h +0 -29
- data/assets/include/SDL2/SDL_version.h +0 -204
- data/assets/include/SDL2/SDL_video.h +0 -2150
- data/assets/include/SDL2/SDL_vulkan.h +0 -215
- data/assets/include/SDL2/begin_code.h +0 -187
- data/assets/include/mruby/boxing_no.h +0 -58
- data/assets/include/mruby/class.h +0 -108
- data/assets/include/mruby/error.h +0 -147
- data/assets/include/mruby/gc.h +0 -94
- data/assets/include/mruby/irep.h +0 -147
- data/assets/include/mruby/khash.h +0 -284
- data/assets/include/mruby/object.h +0 -43
- data/assets/include/mruby/presym/disable.h +0 -70
- data/assets/include/mruby/presym/enable.h +0 -37
- data/assets/include/mruby/presym.h +0 -40
- data/assets/include/mruby/proc.h +0 -211
- data/assets/include/mruby/throw.h +0 -66
- data/assets/macos/universal/bin/mrbc +0 -0
- data/assets/macos/universal/lib/libFLAC.a +0 -0
- data/assets/macos/universal/lib/libSDL2.a +0 -0
- data/assets/macos/universal/lib/libSDL2_image.a +0 -0
- data/assets/macos/universal/lib/libSDL2_mixer.a +0 -0
- data/assets/macos/universal/lib/libSDL2_ttf.a +0 -0
- data/assets/macos/universal/lib/libavif.a +0 -0
- data/assets/macos/universal/lib/libbrotlicommon-static.a +0 -0
- data/assets/macos/universal/lib/libbrotlidec-static.a +0 -0
- data/assets/macos/universal/lib/libfreetype.a +0 -0
- data/assets/macos/universal/lib/libgraphite2.a +0 -0
- data/assets/macos/universal/lib/libharfbuzz.a +0 -0
- data/assets/macos/universal/lib/libhwy.a +0 -0
- data/assets/macos/universal/lib/libjpeg.a +0 -0
- data/assets/macos/universal/lib/libjxl.a +0 -0
- data/assets/macos/universal/lib/libmodplug.a +0 -0
- data/assets/macos/universal/lib/libmpg123.a +0 -0
- data/assets/macos/universal/lib/libmruby.a +0 -0
- data/assets/macos/universal/lib/libogg.a +0 -0
- data/assets/macos/universal/lib/libpng.a +0 -0
- data/assets/macos/universal/lib/libtiff.a +0 -0
- data/assets/macos/universal/lib/libvorbis.a +0 -0
- data/assets/macos/universal/lib/libvorbisfile.a +0 -0
- data/assets/macos/universal/lib/libwebp.a +0 -0
- data/assets/macos/universal/lib/libzstd.a +0 -0
- data/assets/test_media/README.md +0 -3
- data/assets/test_media/bitstream_vera/COPYRIGHT.txt +0 -124
- data/assets/test_media/bitstream_vera/vera.ttf +0 -0
- data/assets/test_media/boom.png +0 -0
- data/assets/test_media/coin.png +0 -0
- data/assets/test_media/colors.png +0 -0
- data/assets/test_media/controller.png +0 -0
- data/assets/test_media/dial.wav +0 -0
- data/assets/test_media/hero.png +0 -0
- data/assets/test_media/image.bmp +0 -0
- data/assets/test_media/image.jpg +0 -0
- data/assets/test_media/image.png +0 -0
- data/assets/test_media/music.flac +0 -0
- data/assets/test_media/music.mp3 +0 -0
- data/assets/test_media/music.ogg +0 -0
- data/assets/test_media/music.wav +0 -0
- data/assets/test_media/originals/boom.pxm +0 -0
- data/assets/test_media/originals/coin.pxm +0 -0
- data/assets/test_media/originals/controller.sketch +0 -0
- data/assets/test_media/originals/hero.pxm +0 -0
- data/assets/test_media/originals/image.pxm +0 -0
- data/assets/test_media/originals/music.caf +0 -0
- data/assets/test_media/originals/texture_atlas.pxm +0 -0
- data/assets/test_media/rondo_alla_turka.ogg +0 -0
- data/assets/test_media/sound.flac +0 -0
- data/assets/test_media/sound.mp3 +0 -0
- data/assets/test_media/sound.ogg +0 -0
- data/assets/test_media/sound.wav +0 -0
- data/assets/test_media/sprite_sheet.png +0 -0
- data/assets/test_media/texture_atlas.png +0 -0
- data/assets/wasm/libmruby.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/bin/mrbc.exe +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libFLAC.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libLerc.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2_image.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2_mixer.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2_ttf.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libbrotlicommon.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libbrotlidec.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libbz2.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libdeflate.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libfreetype.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libglew32.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libgraphite2.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libharfbuzz.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libhwy.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libjbig.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libjpeg.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libjxl.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/liblzma.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libmodplug.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libmpg123.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libmruby.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libogg.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libopus.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libopusfile.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libpng.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libsndfile.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libssp.a +0 -1
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libstdc++.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libtiff.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libvorbis.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libvorbisfile.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libwebp.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libz.a +0 -0
- data/assets/windows/mingw-w64-ucrt-x86_64/lib/libzstd.a +0 -0
- data/assets/windows/mingw-w64-x86_64/bin/mrbc.exe +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libFLAC.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libLerc.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libSDL2.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libSDL2_image.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libSDL2_mixer.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libSDL2_ttf.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libbrotlicommon.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libbrotlidec.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libbz2.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libdeflate.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libfreetype.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libglew32.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libgraphite2.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libharfbuzz.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libhwy.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libjbig.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libjpeg.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libjxl.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/liblzma.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libmodplug.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libmpg123.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libmruby.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libogg.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libopus.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libopusfile.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libpng.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libsndfile.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libssp.a +0 -1
- data/assets/windows/mingw-w64-x86_64/lib/libstdc++.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libtiff.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libvorbis.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libvorbisfile.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libwebp.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libz.a +0 -0
- data/assets/windows/mingw-w64-x86_64/lib/libzstd.a +0 -0
- data/assets/xcode/ios/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -98
- data/assets/xcode/ios/Assets.xcassets/Contents.json +0 -6
- data/assets/xcode/ios/Base.lproj/LaunchScreen.storyboard +0 -27
- data/assets/xcode/ios/Info.plist +0 -43
- data/assets/xcode/ios/MRuby.framework/Headers/mrbconf.h +0 -143
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/array.h +0 -280
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/boxing_nan.h +0 -102
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/boxing_no.h +0 -56
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/boxing_word.h +0 -144
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/class.h +0 -97
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/common.h +0 -77
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/compile.h +0 -195
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/data.h +0 -75
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/debug.h +0 -67
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/dump.h +0 -196
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/error.h +0 -75
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/gc.h +0 -91
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/hash.h +0 -215
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/irep.h +0 -75
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/istruct.h +0 -47
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/khash.h +0 -274
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/numeric.h +0 -161
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/object.h +0 -44
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/opcode.h +0 -69
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/ops.h +0 -117
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/proc.h +0 -131
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/range.h +0 -49
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/re.h +0 -16
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/string.h +0 -444
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/throw.h +0 -55
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/value.h +0 -309
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/variable.h +0 -136
- data/assets/xcode/ios/MRuby.framework/Headers/mruby/version.h +0 -110
- data/assets/xcode/ios/MRuby.framework/Headers/mruby.h +0 -1282
- data/assets/xcode/ios/MRuby.framework/Info.plist +0 -20
- data/assets/xcode/ios/MRuby.framework/MRuby +0 -0
- data/assets/xcode/ios/MyApp.xcodeproj/project.pbxproj +0 -368
- data/assets/xcode/ios/MyApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- data/assets/xcode/ios/MyApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- data/assets/xcode/ios/MyApp.xcodeproj/project.xcworkspace/xcshareddata/MyApp.xcscmblueprint +0 -30
- data/assets/xcode/ios/main.c +0 -1
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json +0 -6
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json +0 -17
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json +0 -6
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json +0 -6
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json +0 -6
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json +0 -17
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json +0 -6
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json +0 -6
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json +0 -32
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json +0 -16
- data/assets/xcode/tvos/Assets.xcassets/Contents.json +0 -6
- data/assets/xcode/tvos/Assets.xcassets/LaunchImage.launchimage/Contents.json +0 -22
- data/assets/xcode/tvos/Info.plist +0 -30
- data/assets/xcode/tvos/MRuby.framework/Headers/mrbconf.h +0 -143
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/array.h +0 -280
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/boxing_nan.h +0 -102
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/boxing_no.h +0 -56
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/boxing_word.h +0 -144
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/class.h +0 -97
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/common.h +0 -77
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/compile.h +0 -195
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/data.h +0 -75
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/debug.h +0 -67
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/dump.h +0 -196
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/error.h +0 -75
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/gc.h +0 -91
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/hash.h +0 -215
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/irep.h +0 -75
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/istruct.h +0 -47
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/khash.h +0 -274
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/numeric.h +0 -161
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/object.h +0 -44
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/opcode.h +0 -69
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/ops.h +0 -117
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/proc.h +0 -131
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/range.h +0 -49
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/re.h +0 -16
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/string.h +0 -444
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/throw.h +0 -55
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/value.h +0 -309
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/variable.h +0 -136
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby/version.h +0 -110
- data/assets/xcode/tvos/MRuby.framework/Headers/mruby.h +0 -1282
- data/assets/xcode/tvos/MRuby.framework/Info.plist +0 -20
- data/assets/xcode/tvos/MRuby.framework/MRuby +0 -0
- data/assets/xcode/tvos/MyApp.xcodeproj/project.pbxproj +0 -350
- data/assets/xcode/tvos/MyApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- data/assets/xcode/tvos/MyApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- data/assets/xcode/tvos/MyApp.xcodeproj/project.xcworkspace/xcshareddata/MyApp.xcscmblueprint +0 -30
- data/assets/xcode/tvos/main.c +0 -1
- data/ext/ruby2d/common.c +0 -185
- data/ext/ruby2d/controllers.c +0 -110
- data/ext/ruby2d/gl.c +0 -398
- data/ext/ruby2d/gl2.c +0 -86
- data/ext/ruby2d/gl3.c +0 -304
- data/ext/ruby2d/gles.c +0 -302
- data/ext/ruby2d/input.c +0 -48
- data/ext/ruby2d/music.c +0 -127
- data/ext/ruby2d/sound.c +0 -132
- data/lib/ruby2d/cli/console.rb +0 -65
- data/lib/ruby2d/cli/enable_console.rb +0 -7
- data/lib/ruby2d/cli/platform.rb +0 -17
- data/lib/ruby2d/music.rb +0 -77
- data/lib/ruby2d/pixel.rb +0 -18
- data/lib/ruby2d/pixmap.rb +0 -39
- data/lib/ruby2d/pixmap_atlas.rb +0 -56
- data/lib/ruby2d/sound.rb +0 -61
- data/lib/ruby2d/texture.rb +0 -38
- /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_gl2.h +0 -0
- /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_gl2ext.h +0 -0
- /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_gl2platform.h +0 -0
- /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_khrplatform.h +0 -0
- /data/assets/{include → platform/include}/mruby/re.h +0 -0
data/ext/ruby2d/canvas.c
CHANGED
|
@@ -2,539 +2,1760 @@
|
|
|
2
2
|
|
|
3
3
|
#include "ruby2d.h"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
static
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
5
|
+
R2D_DEFINE_DATA_TYPE(R2D_Canvas);
|
|
6
|
+
|
|
7
|
+
// Canvas-specific ivar IDs. Shared ivar IDs live in ext.c. Note that canvas
|
|
8
|
+
// uses @tint instead of @color, but the underlying r/g/b/a components are
|
|
9
|
+
// the shared ones.
|
|
10
|
+
static R_ID id_ext_canvas;
|
|
11
|
+
static R_ID id_tint;
|
|
12
|
+
static R_ID id_fill_r, id_fill_g, id_fill_b, id_fill_a;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* Initialize
|
|
17
|
+
*/
|
|
18
|
+
void R2D_Canvas_Init() {
|
|
19
|
+
id_ext_canvas = r_id("@ext_canvas");
|
|
20
|
+
id_tint = r_id("@tint");
|
|
21
|
+
id_fill_r = r_id("@fill_r");
|
|
22
|
+
id_fill_g = r_id("@fill_g");
|
|
23
|
+
id_fill_b = r_id("@fill_b");
|
|
24
|
+
id_fill_a = r_id("@fill_a");
|
|
25
|
+
|
|
26
|
+
r_define_class_method(ruby2d_ext_module, "canvas_create", ruby2d_ext_canvas_create, r_args_variadic);
|
|
27
|
+
r_define_class_method(ruby2d_ext_module, "canvas_draw", ruby2d_ext_canvas_draw, r_args_variadic);
|
|
28
|
+
r_define_class_method(ruby2d_ext_module, "canvas_clear", ruby2d_ext_canvas_clear, r_args_variadic);
|
|
29
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_triangle", ruby2d_ext_canvas_fill_triangle, r_args_variadic);
|
|
30
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_triangle_lerp", ruby2d_ext_canvas_fill_triangle_lerp, r_args_variadic);
|
|
31
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_rectangle", ruby2d_ext_canvas_fill_rectangle, r_args_variadic);
|
|
32
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_rectangles", ruby2d_ext_canvas_fill_rectangles, r_args_variadic);
|
|
33
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_pixel_grid", ruby2d_ext_canvas_fill_pixel_grid, r_args_variadic);
|
|
34
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_ellipse", ruby2d_ext_canvas_fill_ellipse, r_args_variadic);
|
|
35
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_polygon", ruby2d_ext_canvas_fill_polygon, r_args_variadic);
|
|
36
|
+
r_define_class_method(ruby2d_ext_module, "canvas_fill_polygon_lerp", ruby2d_ext_canvas_fill_polygon_lerp, r_args_variadic);
|
|
37
|
+
r_define_class_method(ruby2d_ext_module, "canvas_draw_line", ruby2d_ext_canvas_draw_line, r_args_variadic);
|
|
38
|
+
r_define_class_method(ruby2d_ext_module, "canvas_draw_line_lerp", ruby2d_ext_canvas_draw_line_lerp, r_args_variadic);
|
|
39
|
+
r_define_class_method(ruby2d_ext_module, "canvas_draw_lines", ruby2d_ext_canvas_draw_lines, r_args_variadic);
|
|
40
|
+
r_define_class_method(ruby2d_ext_module, "canvas_stroke_polygon", ruby2d_ext_canvas_stroke_polygon, r_args_variadic);
|
|
41
|
+
r_define_class_method(ruby2d_ext_module, "canvas_stroke_polyline", ruby2d_ext_canvas_stroke_polyline, r_args_variadic);
|
|
42
|
+
r_define_class_method(ruby2d_ext_module, "canvas_draw_image", ruby2d_ext_canvas_draw_image, r_args_variadic);
|
|
43
|
+
r_define_class_method(ruby2d_ext_module, "canvas_draw_text", ruby2d_ext_canvas_draw_text, r_args_variadic);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* Ruby2D::Canvas#ext_create
|
|
49
|
+
*/
|
|
50
|
+
R_VAL ruby2d_ext_canvas_create(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
51
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
52
|
+
if (argc != 1) r_raise("Ruby2D::Ext.canvas_create expects 1 arg (canvas), got %d", (int)argc);
|
|
53
|
+
R_VAL obj = argv[0];
|
|
54
|
+
|
|
55
|
+
// Ensure SDL subsystems are initialized (display_scale is set in R2D_Init)
|
|
56
|
+
if (!R2D_Init()) r_raise("Ruby2D: failed to initialize: %s", SDL_GetError());
|
|
57
|
+
|
|
58
|
+
int width = obj_int(obj, id_width);
|
|
59
|
+
int height = obj_int(obj, id_height);
|
|
60
|
+
|
|
61
|
+
/* Create the surface at physical pixel dimensions so canvas content is
|
|
62
|
+
crisp on HiDPI displays. The Ruby-side width/height remain in logical
|
|
63
|
+
coordinates; drawing methods scale inputs by the same factor.
|
|
64
|
+
|
|
65
|
+
A Canvas captures its scale once, here, and never re-evaluates (unlike
|
|
66
|
+
text, which re-rasterizes from its string when the scale changes — a
|
|
67
|
+
Canvas holds the user's accumulated drawing and can't be recreated).
|
|
68
|
+
If no window exists yet (a Canvas built as the very first Ruby 2D
|
|
69
|
+
object), R2D_GetAssetScale() would report 1.0 and freeze the surface at
|
|
70
|
+
logical resolution. Fall back to the init display scale — known since
|
|
71
|
+
R2D_Init, before any window — so the common HiDPI case stays crisp. */
|
|
72
|
+
float scale = R2D_GetWindow() ? R2D_GetAssetScale() : R2D_GetInitDisplayScale();
|
|
73
|
+
int pixel_w = (int)(width * scale);
|
|
74
|
+
int pixel_h = (int)(height * scale);
|
|
75
|
+
|
|
76
|
+
SDL_Surface *surface = SDL_CreateSurface(pixel_w, pixel_h, SDL_PIXELFORMAT_RGBA32);
|
|
77
|
+
if (!surface) {
|
|
78
|
+
r_raise("SDL_CreateSurface failed: %s", SDL_GetError());
|
|
79
|
+
return R_NIL;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fill with the fill color
|
|
83
|
+
float fr = obj_float(obj, id_fill_r);
|
|
84
|
+
float fg = obj_float(obj, id_fill_g);
|
|
85
|
+
float fb = obj_float(obj, id_fill_b);
|
|
86
|
+
float fa = obj_float(obj, id_fill_a);
|
|
87
|
+
|
|
88
|
+
Uint8 r = (Uint8)(fr * 255);
|
|
89
|
+
Uint8 g = (Uint8)(fg * 255);
|
|
90
|
+
Uint8 b = (Uint8)(fb * 255);
|
|
91
|
+
Uint8 a = (Uint8)(fa * 255);
|
|
92
|
+
|
|
93
|
+
Uint32 color = SDL_MapSurfaceRGBA(surface, r, g, b, a);
|
|
94
|
+
SDL_FillSurfaceRect(surface, NULL, color);
|
|
95
|
+
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
|
|
96
|
+
|
|
97
|
+
R2D_Canvas *can = ALLOC(R2D_Canvas);
|
|
98
|
+
can->surface = surface;
|
|
99
|
+
can->texture = NULL;
|
|
100
|
+
can->scale = scale;
|
|
101
|
+
can->dirty = false;
|
|
102
|
+
can->dirty_rect = (SDL_Rect){ 0, 0, 0, 0 };
|
|
103
|
+
|
|
104
|
+
obj_set_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
105
|
+
|
|
106
|
+
return R_TRUE;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
static void canvas_mark_dirty(R2D_Canvas *can, float min_x, float min_y,
|
|
111
|
+
float max_x, float max_y);
|
|
112
|
+
static void canvas_mark_dirty_all(R2D_Canvas *can);
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
/*
|
|
116
|
+
* Ruby2D::Canvas#ext_draw
|
|
117
|
+
*/
|
|
118
|
+
R_VAL ruby2d_ext_canvas_draw(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
119
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
120
|
+
if (argc != 3) r_raise("Ruby2D::Ext.canvas_draw expects 3 args (canvas, rx, ry), got %d", (int)argc);
|
|
121
|
+
R_VAL obj = argv[0];
|
|
122
|
+
float crx = NUM2DBL(argv[1]);
|
|
123
|
+
float cry = NUM2DBL(argv[2]);
|
|
124
|
+
|
|
125
|
+
R2D_Canvas *can;
|
|
126
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
127
|
+
|
|
128
|
+
/* Create the persistent GPU texture on first draw. Created explicitly in
|
|
129
|
+
the surface's pixel format (rather than SDL_CreateTextureFromSurface) so
|
|
130
|
+
the incremental SDL_UpdateTexture uploads below can pass surface pixel
|
|
131
|
+
rows through unconverted. The texture then lives for the canvas's
|
|
132
|
+
lifetime — mutations upload into it instead of re-creating it. */
|
|
133
|
+
if (can->texture == NULL) {
|
|
134
|
+
can->texture = SDL_CreateTexture(R2D_GetRenderer(), SDL_PIXELFORMAT_RGBA32,
|
|
135
|
+
SDL_TEXTUREACCESS_STATIC,
|
|
136
|
+
can->surface->w, can->surface->h);
|
|
137
|
+
if (!can->texture) {
|
|
138
|
+
r_raise("SDL_CreateTexture failed: %s", SDL_GetError());
|
|
139
|
+
return R_NIL;
|
|
110
140
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{
|
|
184
|
-
|
|
141
|
+
SDL_SetTextureBlendMode(can->texture, SDL_BLENDMODE_BLEND);
|
|
142
|
+
canvas_mark_dirty_all(can); // populate with a full first upload
|
|
143
|
+
// Don't destroy the surface — Canvas needs it for future draw operations
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Upload pending surface mutations — just the dirty union, not the whole
|
|
147
|
+
// surface. A settled canvas skips this entirely.
|
|
148
|
+
if (can->dirty) {
|
|
149
|
+
const SDL_Rect *dr = &can->dirty_rect;
|
|
150
|
+
const Uint8 *pixels = (const Uint8 *)can->surface->pixels
|
|
151
|
+
+ (size_t)dr->y * can->surface->pitch
|
|
152
|
+
+ (size_t)dr->x * 4;
|
|
153
|
+
R2D_CheckSDL(SDL_UpdateTexture(can->texture, dr, pixels, can->surface->pitch),
|
|
154
|
+
"SDL_UpdateTexture");
|
|
155
|
+
can->dirty = false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
SDL_FRect dst_rect = {
|
|
159
|
+
obj_float(obj, id_x),
|
|
160
|
+
obj_float(obj, id_y),
|
|
161
|
+
obj_float(obj, id_width),
|
|
162
|
+
obj_float(obj, id_height)
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
R_VAL color_obj = r_ivar_get(obj, id_tint);
|
|
166
|
+
SDL_SetTextureColorModFloat(can->texture,
|
|
167
|
+
NUM2DBL(r_ivar_get(color_obj, id_r)),
|
|
168
|
+
NUM2DBL(r_ivar_get(color_obj, id_g)),
|
|
169
|
+
NUM2DBL(r_ivar_get(color_obj, id_b))
|
|
170
|
+
);
|
|
171
|
+
SDL_SetTextureAlphaModFloat(can->texture, NUM2DBL(r_ivar_get(color_obj, id_a)));
|
|
172
|
+
|
|
173
|
+
SDL_FPoint center = {
|
|
174
|
+
crx - obj_float(obj, id_x),
|
|
175
|
+
cry - obj_float(obj, id_y)
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
R2D_CheckSDL(SDL_RenderTextureRotated(
|
|
179
|
+
R2D_GetRenderer(),
|
|
180
|
+
can->texture,
|
|
181
|
+
NULL,
|
|
182
|
+
&dst_rect,
|
|
183
|
+
obj_float(obj, id_rotate),
|
|
184
|
+
¢er,
|
|
185
|
+
SDL_FLIP_NONE
|
|
186
|
+
), "SDL_RenderTextureRotated");
|
|
187
|
+
|
|
188
|
+
return R_TRUE;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
/*
|
|
193
|
+
* Swap two float values
|
|
194
|
+
*/
|
|
195
|
+
static void swap_float(float *a, float *b) {
|
|
196
|
+
float t = *a;
|
|
197
|
+
*a = *b;
|
|
198
|
+
*b = t;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
* Composite a straight-alpha RGBA source over one RGBA8888 destination pixel,
|
|
204
|
+
* in place, using Porter-Duff "over". Callers handle the opaque (a==255) fast
|
|
205
|
+
* path; this is the partly-transparent case.
|
|
206
|
+
*
|
|
207
|
+
* The canvas stores straight (non-premultiplied) RGBA, so the blended color is
|
|
208
|
+
* divided by the combined output coverage. When the destination is already
|
|
209
|
+
* opaque (da==255) this reduces exactly — bit for bit — to the legacy
|
|
210
|
+
* src*a + dst*(255-a) blend; only partly-transparent destinations (overlay
|
|
211
|
+
* canvases) change, where the old math composited them too dark.
|
|
212
|
+
*/
|
|
213
|
+
static inline void canvas_blend_over(Uint8 *pixel, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
|
214
|
+
Uint8 dr = pixel[0], dg = pixel[1], db = pixel[2], da = pixel[3];
|
|
215
|
+
int inv = 255 - a;
|
|
216
|
+
|
|
217
|
+
// Fast path: an opaque destination (the common case — painting onto a freshly
|
|
218
|
+
// cleared opaque canvas) stays opaque, and "over" reduces bit for bit to the
|
|
219
|
+
// legacy src*a + dst*(255-a) blend with a constant /255 divisor. The compiler
|
|
220
|
+
// lowers that to a multiply+shift, dropping the three runtime divides below.
|
|
221
|
+
if (da == 255) {
|
|
222
|
+
pixel[0] = (Uint8)((r * a + dr * inv) / 255);
|
|
223
|
+
pixel[1] = (Uint8)((g * a + dg * inv) / 255);
|
|
224
|
+
pixel[2] = (Uint8)((b * a + db * inv) / 255);
|
|
225
|
+
pixel[3] = 255;
|
|
185
226
|
return;
|
|
227
|
+
}
|
|
186
228
|
|
|
187
|
-
//
|
|
188
|
-
int
|
|
229
|
+
int dst_w = da * inv / 255; // destination coverage behind src: da*(1-a)
|
|
230
|
+
int out_a = a + dst_w; // combined src-over-dst coverage; divisor below
|
|
231
|
+
if (out_a == 0) { // src and dst both fully transparent
|
|
232
|
+
pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
pixel[0] = (Uint8)((r * a + dr * dst_w) / out_a);
|
|
236
|
+
pixel[1] = (Uint8)((g * a + dg * dst_w) / out_a);
|
|
237
|
+
pixel[2] = (Uint8)((b * a + db * dst_w) / out_a);
|
|
238
|
+
pixel[3] = (Uint8)out_a;
|
|
239
|
+
}
|
|
189
240
|
|
|
190
|
-
int nconcave =
|
|
191
|
-
num_points == 3
|
|
192
|
-
? 0 // triangles are always convex
|
|
193
|
-
: count_concave_corners(points, num_points, concave_point_indices,
|
|
194
|
-
MAX_CONCAVE_POINT_INDICES);
|
|
195
241
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
242
|
+
/*
|
|
243
|
+
* Fill a triangle on an SDL_Surface using scanline rasterization.
|
|
244
|
+
* Coordinates are in surface pixel space. Color components are 0-255.
|
|
245
|
+
*/
|
|
246
|
+
static void canvas_fill_triangle_on_surface(
|
|
247
|
+
SDL_Surface *surface,
|
|
248
|
+
float fx1, float fy1, float fx2, float fy2, float fx3, float fy3,
|
|
249
|
+
Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
|
250
|
+
|
|
251
|
+
if (a == 0) return; // fully transparent: nothing to draw
|
|
252
|
+
|
|
253
|
+
// A non-finite vertex (NaN/Inf, e.g. from a degenerate user coordinate) would
|
|
254
|
+
// make the (int)ceilf casts below undefined behavior and could index out of
|
|
255
|
+
// the surface. Skip the draw, matching the no-op philosophy for degenerate
|
|
256
|
+
// geometry elsewhere in this file.
|
|
257
|
+
if (!isfinite(fx1) || !isfinite(fy1) || !isfinite(fx2) ||
|
|
258
|
+
!isfinite(fy2) || !isfinite(fx3) || !isfinite(fy3)) return;
|
|
259
|
+
|
|
260
|
+
// Sort vertices by y coordinate
|
|
261
|
+
if (fy1 > fy2) { swap_float(&fx1, &fx2); swap_float(&fy1, &fy2); }
|
|
262
|
+
if (fy1 > fy3) { swap_float(&fx1, &fx3); swap_float(&fy1, &fy3); }
|
|
263
|
+
if (fy2 > fy3) { swap_float(&fx2, &fx3); swap_float(&fy2, &fy3); }
|
|
264
|
+
|
|
265
|
+
// Half-open y coverage: row i is filled iff ceil(ymin) <= i < ceil(ymax),
|
|
266
|
+
// matching the x-extent rule below and canvas_fill_rectangle_on_surface. Using
|
|
267
|
+
// floorf here would close the interval and over-cover integer-aligned bottom
|
|
268
|
+
// edges by one row (visible as 1px-too-thick horizontal lines at 2x scale).
|
|
269
|
+
int top = (int)ceilf(fy1);
|
|
270
|
+
int middle = (int)ceilf(fy2);
|
|
271
|
+
int bottom = (int)ceilf(fy3) - 1;
|
|
272
|
+
|
|
273
|
+
if (top < 0) top = 0;
|
|
274
|
+
if (bottom >= surface->h) bottom = surface->h - 1;
|
|
275
|
+
|
|
276
|
+
float dy13 = fy3 - fy1;
|
|
277
|
+
float dy12 = fy2 - fy1;
|
|
278
|
+
float dy23 = fy3 - fy2;
|
|
279
|
+
|
|
280
|
+
// The fill color is constant across the triangle, so pack it once (endian-safe
|
|
281
|
+
// via SDL_MapSurfaceRGBA) and write opaque pixels with a single 32-bit store
|
|
282
|
+
// instead of four byte stores.
|
|
283
|
+
Uint32 packed = SDL_MapSurfaceRGBA(surface, r, g, b, 255);
|
|
284
|
+
|
|
285
|
+
for (int y = top; y <= bottom; y++) {
|
|
286
|
+
float xa, xb;
|
|
287
|
+
|
|
288
|
+
// Long edge (v1 to v3)
|
|
289
|
+
if (dy13 > 0.0f) {
|
|
290
|
+
xa = fx1 + (fx3 - fx1) * ((float)y - fy1) / dy13;
|
|
291
|
+
} else {
|
|
292
|
+
xa = fx1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Short edge depends on top or bottom half
|
|
296
|
+
if (y < middle) {
|
|
297
|
+
if (dy12 > 0.0f) {
|
|
298
|
+
xb = fx1 + (fx2 - fx1) * ((float)y - fy1) / dy12;
|
|
299
|
+
} else {
|
|
300
|
+
xb = fx1;
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
if (dy23 > 0.0f) {
|
|
304
|
+
xb = fx2 + (fx3 - fx2) * ((float)y - fy2) / dy23;
|
|
305
|
+
} else {
|
|
306
|
+
xb = fx2;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
int x_start = (int)ceilf(fminf(xa, xb));
|
|
311
|
+
int x_end = (int)ceilf(fmaxf(xa, xb)) - 1;
|
|
312
|
+
|
|
313
|
+
if (x_start < 0) x_start = 0;
|
|
314
|
+
if (x_end >= surface->w) x_end = surface->w - 1;
|
|
315
|
+
|
|
316
|
+
// RGBA32 guarantees R,G,B,A byte order in memory
|
|
317
|
+
Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
|
|
318
|
+
for (int x = x_start; x <= x_end; x++) {
|
|
319
|
+
Uint8 *pixel = row + x * 4;
|
|
320
|
+
if (a == 255) {
|
|
321
|
+
SDL_memcpy(pixel, &packed, 4);
|
|
322
|
+
} else {
|
|
323
|
+
canvas_blend_over(pixel, r, g, b, a);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
199
326
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
/*
|
|
331
|
+
* Fill a triangle on an SDL_Surface with per-vertex color interpolation.
|
|
332
|
+
* Uses barycentric coordinates to interpolate RGBA across the triangle.
|
|
333
|
+
* Coordinates are in surface pixel space. Color components are 0-255.
|
|
334
|
+
*/
|
|
335
|
+
static void canvas_fill_triangle_lerp_on_surface(
|
|
336
|
+
SDL_Surface *surface,
|
|
337
|
+
float fx1, float fy1, Uint8 r1, Uint8 g1, Uint8 b1, Uint8 a1,
|
|
338
|
+
float fx2, float fy2, Uint8 r2, Uint8 g2, Uint8 b2, Uint8 a2,
|
|
339
|
+
float fx3, float fy3, Uint8 r3, Uint8 g3, Uint8 b3, Uint8 a3) {
|
|
340
|
+
|
|
341
|
+
// Fully transparent: alpha interpolates linearly, so if all three vertices
|
|
342
|
+
// are 0 every interior pixel is too. Skip before walking any scanline,
|
|
343
|
+
// mirroring the solid rasterizer's `a == 0` early-out.
|
|
344
|
+
if (a1 == 0 && a2 == 0 && a3 == 0) return;
|
|
345
|
+
|
|
346
|
+
// A non-finite vertex (NaN/Inf) would make the (int)ceilf casts below
|
|
347
|
+
// undefined behavior; skip the draw rather than risk an out-of-bounds write.
|
|
348
|
+
if (!isfinite(fx1) || !isfinite(fy1) || !isfinite(fx2) ||
|
|
349
|
+
!isfinite(fy2) || !isfinite(fx3) || !isfinite(fy3)) return;
|
|
350
|
+
|
|
351
|
+
// Barycentric denominator (constant for the whole triangle)
|
|
352
|
+
float denom = (fy2 - fy3) * (fx1 - fx3) + (fx3 - fx2) * (fy1 - fy3);
|
|
353
|
+
if (fabsf(denom) < 0.0001f) return;
|
|
354
|
+
float inv_denom = 1.0f / denom;
|
|
355
|
+
|
|
356
|
+
// Sort vertices by y for scanline traversal (keep originals for barycentric)
|
|
357
|
+
float sx1 = fx1, sy1 = fy1;
|
|
358
|
+
float sx2 = fx2, sy2 = fy2;
|
|
359
|
+
float sx3 = fx3, sy3 = fy3;
|
|
360
|
+
|
|
361
|
+
if (sy1 > sy2) { swap_float(&sx1, &sx2); swap_float(&sy1, &sy2); }
|
|
362
|
+
if (sy1 > sy3) { swap_float(&sx1, &sx3); swap_float(&sy1, &sy3); }
|
|
363
|
+
if (sy2 > sy3) { swap_float(&sx2, &sx3); swap_float(&sy2, &sy3); }
|
|
364
|
+
|
|
365
|
+
// Half-open y coverage (see canvas_fill_triangle_on_surface) — keeps the lerp
|
|
366
|
+
// rasterizer consistent with the solid one and avoids over-covering integer-
|
|
367
|
+
// aligned bottom edges by a row.
|
|
368
|
+
int top = (int)ceilf(sy1);
|
|
369
|
+
int middle = (int)ceilf(sy2);
|
|
370
|
+
int bottom = (int)ceilf(sy3) - 1;
|
|
371
|
+
|
|
372
|
+
if (top < 0) top = 0;
|
|
373
|
+
if (bottom >= surface->h) bottom = surface->h - 1;
|
|
374
|
+
|
|
375
|
+
float dy13 = sy3 - sy1;
|
|
376
|
+
float dy12 = sy2 - sy1;
|
|
377
|
+
float dy23 = sy3 - sy2;
|
|
378
|
+
|
|
379
|
+
for (int y = top; y <= bottom; y++) {
|
|
380
|
+
float xa, xb;
|
|
381
|
+
|
|
382
|
+
if (dy13 > 0.0f)
|
|
383
|
+
xa = sx1 + (sx3 - sx1) * ((float)y - sy1) / dy13;
|
|
384
|
+
else
|
|
385
|
+
xa = sx1;
|
|
386
|
+
|
|
387
|
+
if (y < middle) {
|
|
388
|
+
if (dy12 > 0.0f)
|
|
389
|
+
xb = sx1 + (sx2 - sx1) * ((float)y - sy1) / dy12;
|
|
390
|
+
else
|
|
391
|
+
xb = sx1;
|
|
392
|
+
} else {
|
|
393
|
+
if (dy23 > 0.0f)
|
|
394
|
+
xb = sx2 + (sx3 - sx2) * ((float)y - sy2) / dy23;
|
|
395
|
+
else
|
|
396
|
+
xb = sx2;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
int x_start = (int)ceilf(fminf(xa, xb));
|
|
400
|
+
int x_end = (int)ceilf(fmaxf(xa, xb)) - 1;
|
|
401
|
+
|
|
402
|
+
if (x_start < 0) x_start = 0;
|
|
403
|
+
if (x_end >= surface->w) x_end = surface->w - 1;
|
|
404
|
+
|
|
405
|
+
Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
|
|
406
|
+
for (int x = x_start; x <= x_end; x++) {
|
|
407
|
+
// Barycentric weights from original (unsorted) vertices
|
|
408
|
+
float w1 = ((fy2 - fy3) * ((float)x - fx3) + (fx3 - fx2) * ((float)y - fy3)) * inv_denom;
|
|
409
|
+
float w2 = ((fy3 - fy1) * ((float)x - fx3) + (fx1 - fx3) * ((float)y - fy3)) * inv_denom;
|
|
410
|
+
float w3 = 1.0f - w1 - w2;
|
|
411
|
+
|
|
412
|
+
w1 = fminf(fmaxf(w1, 0.0f), 1.0f);
|
|
413
|
+
w2 = fminf(fmaxf(w2, 0.0f), 1.0f);
|
|
414
|
+
w3 = fminf(fmaxf(w3, 0.0f), 1.0f);
|
|
415
|
+
|
|
416
|
+
Uint8 r = (Uint8)(w1 * r1 + w2 * r2 + w3 * r3);
|
|
417
|
+
Uint8 g = (Uint8)(w1 * g1 + w2 * g2 + w3 * g3);
|
|
418
|
+
Uint8 b = (Uint8)(w1 * b1 + w2 * b2 + w3 * b3);
|
|
419
|
+
Uint8 a = (Uint8)(w1 * a1 + w2 * a2 + w3 * a3);
|
|
420
|
+
|
|
421
|
+
Uint8 *pixel = row + x * 4;
|
|
422
|
+
if (a == 255) {
|
|
423
|
+
pixel[0] = r;
|
|
424
|
+
pixel[1] = g;
|
|
425
|
+
pixel[2] = b;
|
|
426
|
+
pixel[3] = 255;
|
|
427
|
+
} else if (a > 0) {
|
|
428
|
+
canvas_blend_over(pixel, r, g, b, a);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
204
431
|
}
|
|
205
|
-
// else
|
|
206
|
-
// printf("TODO: Non-convex polygon with %d concave corners\n", nconcave);
|
|
207
432
|
}
|
|
208
433
|
|
|
434
|
+
|
|
435
|
+
/*
|
|
436
|
+
* Draw a thick line on an SDL_Surface as a quad (two triangles).
|
|
437
|
+
* Coordinates are in surface pixel space. Color components are 0-255.
|
|
438
|
+
*/
|
|
439
|
+
static void canvas_draw_line_on_surface(
|
|
440
|
+
SDL_Surface *surface,
|
|
441
|
+
float x1, float y1, float x2, float y2,
|
|
442
|
+
float width,
|
|
443
|
+
Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
|
444
|
+
|
|
445
|
+
float dx = x2 - x1;
|
|
446
|
+
float dy = y2 - y1;
|
|
447
|
+
float len = sqrtf(dx * dx + dy * dy);
|
|
448
|
+
|
|
449
|
+
if (len < 0.0001f) return;
|
|
450
|
+
|
|
451
|
+
// Perpendicular unit vector scaled by half width
|
|
452
|
+
float hw = width / 2.0f;
|
|
453
|
+
float ox = (-dy / len) * hw;
|
|
454
|
+
float oy = ( dx / len) * hw;
|
|
455
|
+
|
|
456
|
+
// Four corners of the line quad
|
|
457
|
+
float qx1 = x1 + ox, qy1 = y1 + oy;
|
|
458
|
+
float qx2 = x1 - ox, qy2 = y1 - oy;
|
|
459
|
+
float qx3 = x2 - ox, qy3 = y2 - oy;
|
|
460
|
+
float qx4 = x2 + ox, qy4 = y2 + oy;
|
|
461
|
+
|
|
462
|
+
canvas_fill_triangle_on_surface(surface, qx1, qy1, qx2, qy2, qx3, qy3, r, g, b, a);
|
|
463
|
+
canvas_fill_triangle_on_surface(surface, qx1, qy1, qx3, qy3, qx4, qy4, r, g, b, a);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
|
|
209
467
|
/*
|
|
210
|
-
* Draw a thick line on
|
|
211
|
-
*
|
|
468
|
+
* Draw a thick line on an SDL_Surface as a quad with per-vertex colors.
|
|
469
|
+
* The 4 colors map to corners: c1=start+perp, c2=start-perp, c3=end-perp,
|
|
470
|
+
* c4=end+perp, matching R2D_DrawLine's vertex layout.
|
|
212
471
|
*/
|
|
213
|
-
void
|
|
214
|
-
|
|
215
|
-
|
|
472
|
+
static void canvas_draw_line_lerp_on_surface(
|
|
473
|
+
SDL_Surface *surface,
|
|
474
|
+
float x1, float y1, float x2, float y2,
|
|
475
|
+
float width,
|
|
476
|
+
Uint8 r1, Uint8 g1, Uint8 b1, Uint8 a1,
|
|
477
|
+
Uint8 r2, Uint8 g2, Uint8 b2, Uint8 a2,
|
|
478
|
+
Uint8 r3, Uint8 g3, Uint8 b3, Uint8 a3,
|
|
479
|
+
Uint8 r4, Uint8 g4, Uint8 b4, Uint8 a4) {
|
|
480
|
+
|
|
481
|
+
float dx = x2 - x1;
|
|
482
|
+
float dy = y2 - y1;
|
|
483
|
+
float len = sqrtf(dx * dx + dy * dy);
|
|
484
|
+
|
|
485
|
+
if (len < 0.0001f) return;
|
|
486
|
+
|
|
487
|
+
float hw = width / 2.0f;
|
|
488
|
+
float ox = (-dy / len) * hw;
|
|
489
|
+
float oy = ( dx / len) * hw;
|
|
490
|
+
|
|
491
|
+
float qx1 = x1 + ox, qy1 = y1 + oy;
|
|
492
|
+
float qx2 = x1 - ox, qy2 = y1 - oy;
|
|
493
|
+
float qx3 = x2 - ox, qy3 = y2 - oy;
|
|
494
|
+
float qx4 = x2 + ox, qy4 = y2 + oy;
|
|
495
|
+
|
|
496
|
+
canvas_fill_triangle_lerp_on_surface(surface,
|
|
497
|
+
qx1, qy1, r1, g1, b1, a1,
|
|
498
|
+
qx2, qy2, r2, g2, b2, a2,
|
|
499
|
+
qx3, qy3, r3, g3, b3, a3);
|
|
500
|
+
canvas_fill_triangle_lerp_on_surface(surface,
|
|
501
|
+
qx1, qy1, r1, g1, b1, a1,
|
|
502
|
+
qx3, qy3, r3, g3, b3, a3,
|
|
503
|
+
qx4, qy4, r4, g4, b4, a4);
|
|
504
|
+
}
|
|
216
505
|
|
|
217
|
-
if (thickness <= 1)
|
|
218
|
-
return;
|
|
219
506
|
|
|
220
|
-
|
|
507
|
+
/*
|
|
508
|
+
* Lerp two Uint8 channel values at parameter t in [0, 1].
|
|
509
|
+
*/
|
|
510
|
+
static inline Uint8 lerp_u8(Uint8 a, Uint8 b, float t) {
|
|
511
|
+
return (Uint8)((float)a + ((float)b - (float)a) * t + 0.5f);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
/*
|
|
516
|
+
* Stroke a polyline on an SDL_Surface with miter joins.
|
|
517
|
+
* When closed is true, the last vertex connects back to the first.
|
|
518
|
+
* When closed is false, endpoints get butt caps.
|
|
519
|
+
* verts is [x0, y0, x1, y1, ...], n is the vertex count.
|
|
520
|
+
* colors is n*4 Uint8s (rgba per vertex); outer/inner at each vertex share its
|
|
521
|
+
* color, so per-edge quads interpolate between adjacent vertex colors.
|
|
522
|
+
* Coordinates are in surface pixel space. Color components are 0-255.
|
|
523
|
+
*
|
|
524
|
+
* Outline geometry is computed by the shared R2D_ComputeStrokeOutline so
|
|
525
|
+
* Canvas strokes match the GPU-side R2D_StrokePath at corners.
|
|
526
|
+
*/
|
|
527
|
+
static void canvas_stroke_polyline_on_surface(
|
|
528
|
+
SDL_Surface *surface,
|
|
529
|
+
float *verts, int n, int closed, float width,
|
|
530
|
+
const Uint8 *colors) {
|
|
531
|
+
|
|
532
|
+
if (n < 2) return;
|
|
533
|
+
|
|
534
|
+
// Most strokes are small (triangle, quad, default circle), so serve the
|
|
535
|
+
// outline buffers from the stack and fall back to the heap only past the
|
|
536
|
+
// threshold — no malloc/free per call on the per-frame canvas paint path.
|
|
537
|
+
float outer_stack[R2D_STROKE_STACK_N * 2];
|
|
538
|
+
float inner_stack[R2D_STROKE_STACK_N * 2];
|
|
539
|
+
float *outer, *inner;
|
|
540
|
+
bool heap = n > R2D_STROKE_STACK_N;
|
|
541
|
+
|
|
542
|
+
if (heap) {
|
|
543
|
+
outer = (float *)malloc(n * 2 * sizeof(float));
|
|
544
|
+
inner = (float *)malloc(n * 2 * sizeof(float));
|
|
545
|
+
if (!outer || !inner) { free(outer); free(inner); return; }
|
|
546
|
+
} else {
|
|
547
|
+
outer = outer_stack;
|
|
548
|
+
inner = inner_stack;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
R2D_ComputeStrokeOutline(verts, n, closed, width, R2D_MITER_LIMIT, outer, inner);
|
|
221
552
|
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
553
|
+
// Draw a quad for each edge segment
|
|
554
|
+
int edges = closed ? n : n - 1;
|
|
555
|
+
for (int i = 0; i < edges; i++) {
|
|
556
|
+
int j = (i + 1) % n;
|
|
225
557
|
|
|
226
|
-
|
|
227
|
-
|
|
558
|
+
Uint8 ri = colors[i * 4], gi = colors[i * 4 + 1];
|
|
559
|
+
Uint8 bi = colors[i * 4 + 2], ai = colors[i * 4 + 3];
|
|
560
|
+
Uint8 rj = colors[j * 4], gj = colors[j * 4 + 1];
|
|
561
|
+
Uint8 bj = colors[j * 4 + 2], aj = colors[j * 4 + 3];
|
|
228
562
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
563
|
+
canvas_fill_triangle_lerp_on_surface(surface,
|
|
564
|
+
outer[i*2], outer[i*2+1], ri, gi, bi, ai,
|
|
565
|
+
inner[i*2], inner[i*2+1], ri, gi, bi, ai,
|
|
566
|
+
inner[j*2], inner[j*2+1], rj, gj, bj, aj);
|
|
233
567
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
568
|
+
canvas_fill_triangle_lerp_on_surface(surface,
|
|
569
|
+
outer[i*2], outer[i*2+1], ri, gi, bi, ai,
|
|
570
|
+
inner[j*2], inner[j*2+1], rj, gj, bj, aj,
|
|
571
|
+
outer[j*2], outer[j*2+1], rj, gj, bj, aj);
|
|
237
572
|
}
|
|
238
573
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
574
|
+
if (heap) {
|
|
575
|
+
free(outer);
|
|
576
|
+
free(inner);
|
|
577
|
+
}
|
|
242
578
|
}
|
|
243
579
|
|
|
580
|
+
|
|
244
581
|
/*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
582
|
+
* Union a just-mutated surface region into the canvas's pending dirty rect so
|
|
583
|
+
* the next ext_draw uploads it to the persistent GPU texture (the texture is
|
|
584
|
+
* never destroyed on mutation — a full re-create + full-surface upload per
|
|
585
|
+
* animated frame is exactly what made canvases expensive, especially on
|
|
586
|
+
* WebGL). Also mark the next tick as needing a rendered frame so the mutation
|
|
587
|
+
* is visible in :on_demand render mode without an explicit request_render
|
|
588
|
+
* call. Tolerates being called before the window is shown (Canvases can be
|
|
589
|
+
* drawn into pre-show); the dirty mark is a no-op in :continuous mode but the
|
|
590
|
+
* atomic write is cheap.
|
|
591
|
+
*
|
|
592
|
+
* Bounds are floats in surface pixel space, any corner order. They are
|
|
593
|
+
* expanded outward by one pixel so the differing edge conventions of the
|
|
594
|
+
* rasterizers (ceil starts, inclusive vs half-open ends) are always covered —
|
|
595
|
+
* a pixel of extra upload is harmless, a missed pixel is a stale texel.
|
|
247
596
|
*/
|
|
248
|
-
void
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
597
|
+
static void canvas_mark_dirty(R2D_Canvas *can, float min_x, float min_y,
|
|
598
|
+
float max_x, float max_y) {
|
|
599
|
+
int x0, y0, x1, y1;
|
|
600
|
+
|
|
601
|
+
// Non-finite bounds (NaN/Inf) make the int casts below undefined behavior;
|
|
602
|
+
// fall back to the whole surface rather than guess.
|
|
603
|
+
if (!isfinite(min_x) || !isfinite(min_y) || !isfinite(max_x) || !isfinite(max_y)) {
|
|
604
|
+
x0 = 0; y0 = 0; x1 = can->surface->w; y1 = can->surface->h;
|
|
605
|
+
} else {
|
|
606
|
+
if (min_x > max_x) swap_float(&min_x, &max_x);
|
|
607
|
+
if (min_y > max_y) swap_float(&min_y, &max_y);
|
|
608
|
+
x0 = (int)floorf(min_x) - 1;
|
|
609
|
+
y0 = (int)floorf(min_y) - 1;
|
|
610
|
+
x1 = (int)ceilf(max_x) + 1;
|
|
611
|
+
y1 = (int)ceilf(max_y) + 1;
|
|
612
|
+
if (x0 < 0) x0 = 0;
|
|
613
|
+
if (y0 < 0) y0 = 0;
|
|
614
|
+
if (x1 > can->surface->w) x1 = can->surface->w;
|
|
615
|
+
if (y1 > can->surface->h) y1 = can->surface->h;
|
|
616
|
+
if (x0 >= x1 || y0 >= y1) return; // fully off-surface: nothing uploaded
|
|
254
617
|
}
|
|
255
618
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
// inner coords
|
|
275
|
-
verts[4].position = (SDL_FPoint){.x = x + half_thick, .y = y + half_thick};
|
|
276
|
-
verts[5].position =
|
|
277
|
-
(SDL_FPoint){.x = x + width - half_thick, .y = y + half_thick};
|
|
278
|
-
verts[6].position =
|
|
279
|
-
(SDL_FPoint){.x = x + width - half_thick, .y = y + height - half_thick};
|
|
280
|
-
verts[7].position =
|
|
281
|
-
(SDL_FPoint){.x = x + half_thick, .y = y + height - half_thick};
|
|
282
|
-
|
|
283
|
-
int indices[] = {
|
|
284
|
-
0, 4, 1, // top outer triangle
|
|
285
|
-
4, 1, 5, // inner
|
|
286
|
-
1, 5, 2, // right outer
|
|
287
|
-
5, 2, 6, // inner
|
|
288
|
-
2, 6, 3, // bottom outer
|
|
289
|
-
6, 3, 7, // inner
|
|
290
|
-
3, 7, 0, // left outer
|
|
291
|
-
7, 0, 4 // inner
|
|
292
|
-
};
|
|
293
|
-
SDL_RenderGeometry(render, NULL, verts, 8, indices, 24);
|
|
619
|
+
if (can->dirty) {
|
|
620
|
+
int ex1 = can->dirty_rect.x + can->dirty_rect.w;
|
|
621
|
+
int ey1 = can->dirty_rect.y + can->dirty_rect.h;
|
|
622
|
+
if (x0 > can->dirty_rect.x) x0 = can->dirty_rect.x;
|
|
623
|
+
if (y0 > can->dirty_rect.y) y0 = can->dirty_rect.y;
|
|
624
|
+
if (x1 < ex1) x1 = ex1;
|
|
625
|
+
if (y1 < ey1) y1 = ey1;
|
|
626
|
+
} else {
|
|
627
|
+
can->dirty = true;
|
|
628
|
+
}
|
|
629
|
+
can->dirty_rect.x = x0;
|
|
630
|
+
can->dirty_rect.y = y0;
|
|
631
|
+
can->dirty_rect.w = x1 - x0;
|
|
632
|
+
can->dirty_rect.h = y1 - y0;
|
|
633
|
+
|
|
634
|
+
if (R2D_GetWindow()) {
|
|
635
|
+
SDL_SetAtomicInt(&R2D_GetWindow()->render_pending, 1);
|
|
636
|
+
}
|
|
294
637
|
}
|
|
295
638
|
|
|
639
|
+
|
|
296
640
|
/*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
641
|
+
* Mark the entire surface dirty — the first draw populates the fresh texture
|
|
642
|
+
* with a full upload of everything drawn pre-show.
|
|
299
643
|
*/
|
|
300
|
-
void
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
644
|
+
static void canvas_mark_dirty_all(R2D_Canvas *can) {
|
|
645
|
+
canvas_mark_dirty(can, 0.0f, 0.0f,
|
|
646
|
+
(float)can->surface->w, (float)can->surface->h);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
/*
|
|
651
|
+
* Mark the bounding box of a vertex list ([x0, y0, x1, y1, ...], surface
|
|
652
|
+
* pixel space) dirty, expanded by `pad` on every side — for strokes, whose
|
|
653
|
+
* geometry extends beyond the vertices (up to half-width * miter limit at a
|
|
654
|
+
* miter join).
|
|
655
|
+
*/
|
|
656
|
+
static void canvas_mark_dirty_verts(R2D_Canvas *can, const float *verts,
|
|
657
|
+
int n, float pad) {
|
|
658
|
+
float min_x = verts[0], min_y = verts[1];
|
|
659
|
+
float max_x = verts[0], max_y = verts[1];
|
|
660
|
+
for (int i = 1; i < n; i++) {
|
|
661
|
+
float x = verts[i * 2];
|
|
662
|
+
float y = verts[i * 2 + 1];
|
|
663
|
+
if (x < min_x) min_x = x;
|
|
664
|
+
if (x > max_x) max_x = x;
|
|
665
|
+
if (y < min_y) min_y = y;
|
|
666
|
+
if (y > max_y) max_y = y;
|
|
306
667
|
}
|
|
668
|
+
canvas_mark_dirty(can, min_x - pad, min_y - pad, max_x + pad, max_y + pad);
|
|
669
|
+
}
|
|
307
670
|
|
|
308
|
-
// renders a thick circle by treating each segment as a monotonic quad
|
|
309
|
-
// and rendering as two triangles per segment
|
|
310
|
-
SDL_Vertex verts[4];
|
|
311
|
-
|
|
312
|
-
// colour all vertices
|
|
313
|
-
verts[3].color = verts[2].color = verts[1].color = verts[0].color =
|
|
314
|
-
(SDL_Color){.r = r, .g = g, .b = b, .a = a};
|
|
315
|
-
float half_thick = thickness / 2.0f;
|
|
316
|
-
SDL_FPoint outer_radius = {.x = xradius + half_thick,
|
|
317
|
-
.y = yradius + half_thick};
|
|
318
|
-
SDL_FPoint inner_radius = {.x = xradius - half_thick,
|
|
319
|
-
.y = yradius - half_thick};
|
|
320
|
-
float cache_cosf = cosf(0), cache_sinf = sinf(0);
|
|
321
|
-
float unit_angle = 2 * M_PI / sectors;
|
|
322
|
-
|
|
323
|
-
int indices[] = {0, 1, 3, 3, 1, 2};
|
|
324
|
-
|
|
325
|
-
// point at 0 radians
|
|
326
|
-
verts[0].position = (SDL_FPoint){.x = x + outer_radius.x * cache_cosf,
|
|
327
|
-
.y = y + outer_radius.y * cache_sinf};
|
|
328
|
-
verts[3].position = (SDL_FPoint){.x = x + inner_radius.x * cache_cosf,
|
|
329
|
-
.y = y + inner_radius.y * cache_sinf};
|
|
330
|
-
for (float i = 1; i <= sectors; i++) {
|
|
331
|
-
float angle = i * unit_angle;
|
|
332
|
-
// re-use index 0 and 3 from previous calculation
|
|
333
|
-
verts[1].position = verts[0].position;
|
|
334
|
-
verts[2].position = verts[3].position;
|
|
335
|
-
// calculate new index 0 and 3 values
|
|
336
|
-
cache_cosf = cosf(angle);
|
|
337
|
-
cache_sinf = sinf(angle);
|
|
338
|
-
verts[0].position = (SDL_FPoint){.x = x + outer_radius.x * cache_cosf,
|
|
339
|
-
.y = y + outer_radius.y * cache_sinf};
|
|
340
|
-
verts[3].position = (SDL_FPoint){.x = x + inner_radius.x * cache_cosf,
|
|
341
|
-
.y = y + inner_radius.y * cache_sinf};
|
|
342
|
-
SDL_RenderGeometry(render, NULL, verts, 4, indices, 6);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/*
|
|
347
|
-
* Draw a thin (single pixel) ellipse on a canvas using a pre-converted RGBA
|
|
348
|
-
* colour value.
|
|
349
|
-
*/
|
|
350
|
-
void R2D_Canvas_DrawThinEllipse(SDL_Renderer *render, int x, int y,
|
|
351
|
-
float xradius, float yradius, float sectors,
|
|
352
|
-
int r, int g, int b, int a)
|
|
353
|
-
{
|
|
354
|
-
float unit_angle = 2 * M_PI / sectors;
|
|
355
|
-
// point at 0 radians
|
|
356
|
-
float x1 = x + xradius * cosf(0);
|
|
357
|
-
float y1 = y + yradius * sinf(0);
|
|
358
|
-
SDL_SetRenderDrawColor(render, r, g, b, a);
|
|
359
|
-
for (float i = 1; i <= sectors; i++) {
|
|
360
|
-
float angle = i * unit_angle;
|
|
361
|
-
// re-use from previous calculation
|
|
362
|
-
float x2 = x1;
|
|
363
|
-
float y2 = y1;
|
|
364
|
-
// calculate new point
|
|
365
|
-
x1 = x + xradius * cosf(angle);
|
|
366
|
-
y1 = y + yradius * sinf(angle);
|
|
367
|
-
SDL_RenderDrawLine(render, x1, y1, x2, y2);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/*
|
|
372
|
-
* Compute the intersection between two lines represented by the input line
|
|
373
|
-
* segments. Unlike a typical line segment collision detector, this function
|
|
374
|
-
* will find a possible intersection point even if that point is not on either
|
|
375
|
-
* of the input line "segments". This helps find where two line segments "would"
|
|
376
|
-
* intersect if they were long enough, in addition to the case when the segments
|
|
377
|
-
* intersect.
|
|
378
|
-
* @param line1_p1
|
|
379
|
-
* @param line1_p2
|
|
380
|
-
* @param line2_p1
|
|
381
|
-
* @param line2_p2
|
|
382
|
-
* @param intersection Pointer into which to write the point of intersection.
|
|
383
|
-
* @return true if an intersection is found, false if the lines are parallel
|
|
384
|
-
* (collinear).
|
|
385
|
-
*/
|
|
386
|
-
static bool intersect_two_lines(const vector_t *line1_p1,
|
|
387
|
-
const vector_t *line1_p2,
|
|
388
|
-
const vector_t *line2_p1,
|
|
389
|
-
const vector_t *line2_p2,
|
|
390
|
-
vector_t *intersection)
|
|
391
|
-
{
|
|
392
|
-
vector_t alpha = {.x = line1_p2->x - line1_p1->x,
|
|
393
|
-
.y = line1_p2->y - line1_p1->y};
|
|
394
|
-
vector_t beta = {.x = line2_p1->x - line2_p2->x,
|
|
395
|
-
.y = line2_p1->y - line2_p2->y};
|
|
396
|
-
float denom = (alpha.y * beta.x) - (alpha.x * beta.y);
|
|
397
|
-
|
|
398
|
-
if (denom == 0)
|
|
399
|
-
return false; // collinear
|
|
400
|
-
|
|
401
|
-
vector_t theta = {.x = line1_p1->x - line2_p1->x,
|
|
402
|
-
.y = line1_p1->y - line2_p1->y};
|
|
403
|
-
float alpha_numerator = beta.y * theta.x - (beta.x * theta.y);
|
|
404
|
-
|
|
405
|
-
intersection->x = alpha.x;
|
|
406
|
-
intersection->y = alpha.y;
|
|
407
|
-
vector_times_scalar(intersection, alpha_numerator / denom);
|
|
408
|
-
vector_plus_vector(intersection, line1_p1);
|
|
409
|
-
return true;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
#define POLYLINE_RENDER_NVERTS 6
|
|
413
|
-
#define POLYLINE_RENDER_NINDICES 6
|
|
414
|
-
|
|
415
|
-
/*
|
|
416
|
-
* Draw a thick N-point polyline, with a mitre join where two
|
|
417
|
-
* segments are joined.
|
|
418
|
-
* @param [int] thickness must be > 1, else does nothing
|
|
419
|
-
*/
|
|
420
|
-
void R2D_Canvas_DrawThickPolyline(SDL_Renderer *render, SDL_FPoint *points,
|
|
421
|
-
int num_points, int thickness, int r, int g,
|
|
422
|
-
int b, int a, bool skip_first_last)
|
|
423
|
-
{
|
|
424
|
-
if (thickness <= 1)
|
|
425
|
-
return;
|
|
426
|
-
if (num_points < 3)
|
|
427
|
-
return;
|
|
428
671
|
|
|
429
|
-
|
|
672
|
+
/*
|
|
673
|
+
* Ruby2D::Canvas#ext_fill_triangle
|
|
674
|
+
* Arguments: [x1, y1, x2, y2, x3, y3, r, g, b, a]
|
|
675
|
+
*/
|
|
676
|
+
R_VAL ruby2d_ext_canvas_fill_triangle(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
677
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
678
|
+
if (argc != 11) r_raise("Ruby2D::Ext.canvas_fill_triangle expects 11 args, got %d", (int)argc);
|
|
679
|
+
R_VAL obj = argv[0];
|
|
680
|
+
|
|
681
|
+
R2D_Canvas *can;
|
|
682
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
683
|
+
|
|
684
|
+
float scale = can->scale;
|
|
685
|
+
float x1 = (float)NUM2DBL(argv[1]) * scale;
|
|
686
|
+
float y1 = (float)NUM2DBL(argv[2]) * scale;
|
|
687
|
+
float x2 = (float)NUM2DBL(argv[3]) * scale;
|
|
688
|
+
float y2 = (float)NUM2DBL(argv[4]) * scale;
|
|
689
|
+
float x3 = (float)NUM2DBL(argv[5]) * scale;
|
|
690
|
+
float y3 = (float)NUM2DBL(argv[6]) * scale;
|
|
691
|
+
float r = (float)NUM2DBL(argv[7]);
|
|
692
|
+
float g = (float)NUM2DBL(argv[8]);
|
|
693
|
+
float b = (float)NUM2DBL(argv[9]);
|
|
694
|
+
float af = (float)NUM2DBL(argv[10]);
|
|
695
|
+
|
|
696
|
+
Uint8 cr = (Uint8)(r * 255);
|
|
697
|
+
Uint8 cg = (Uint8)(g * 255);
|
|
698
|
+
Uint8 cb = (Uint8)(b * 255);
|
|
699
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
700
|
+
|
|
701
|
+
if (ca == 0) return R_TRUE; // fully transparent: skip lock and texture rebuild
|
|
702
|
+
|
|
703
|
+
SDL_LockSurface(can->surface);
|
|
704
|
+
canvas_fill_triangle_on_surface(can->surface, x1, y1, x2, y2, x3, y3, cr, cg, cb, ca);
|
|
705
|
+
SDL_UnlockSurface(can->surface);
|
|
706
|
+
|
|
707
|
+
canvas_mark_dirty(can,
|
|
708
|
+
fminf(x1, fminf(x2, x3)), fminf(y1, fminf(y2, y3)),
|
|
709
|
+
fmaxf(x1, fmaxf(x2, x3)), fmaxf(y1, fmaxf(y2, y3)));
|
|
710
|
+
|
|
711
|
+
return R_TRUE;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
/*
|
|
716
|
+
* Ruby2D::Canvas#ext_fill_triangle_lerp
|
|
717
|
+
* Arguments: [x1, y1, r1, g1, b1, a1, x2, y2, r2, g2, b2, a2, x3, y3, r3, g3, b3, a3]
|
|
718
|
+
* Each vertex has its own color; colors are interpolated across the triangle.
|
|
719
|
+
*/
|
|
720
|
+
R_VAL ruby2d_ext_canvas_fill_triangle_lerp(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
721
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
722
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_fill_triangle_lerp expects 2 args, got %d", (int)argc);
|
|
723
|
+
R_VAL obj = argv[0];
|
|
724
|
+
R_VAL a = argv[1];
|
|
725
|
+
|
|
726
|
+
R2D_Canvas *can;
|
|
727
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
728
|
+
|
|
729
|
+
float scale = can->scale;
|
|
730
|
+
|
|
731
|
+
float x1 = (float)NUM2DBL(r_ary_entry(a, 0)) * scale;
|
|
732
|
+
float y1 = (float)NUM2DBL(r_ary_entry(a, 1)) * scale;
|
|
733
|
+
float r1 = (float)NUM2DBL(r_ary_entry(a, 2));
|
|
734
|
+
float g1 = (float)NUM2DBL(r_ary_entry(a, 3));
|
|
735
|
+
float b1 = (float)NUM2DBL(r_ary_entry(a, 4));
|
|
736
|
+
float a1 = (float)NUM2DBL(r_ary_entry(a, 5));
|
|
737
|
+
|
|
738
|
+
float x2 = (float)NUM2DBL(r_ary_entry(a, 6)) * scale;
|
|
739
|
+
float y2 = (float)NUM2DBL(r_ary_entry(a, 7)) * scale;
|
|
740
|
+
float r2 = (float)NUM2DBL(r_ary_entry(a, 8));
|
|
741
|
+
float g2 = (float)NUM2DBL(r_ary_entry(a, 9));
|
|
742
|
+
float b2 = (float)NUM2DBL(r_ary_entry(a, 10));
|
|
743
|
+
float a2 = (float)NUM2DBL(r_ary_entry(a, 11));
|
|
744
|
+
|
|
745
|
+
float x3 = (float)NUM2DBL(r_ary_entry(a, 12)) * scale;
|
|
746
|
+
float y3 = (float)NUM2DBL(r_ary_entry(a, 13)) * scale;
|
|
747
|
+
float r3 = (float)NUM2DBL(r_ary_entry(a, 14));
|
|
748
|
+
float g3 = (float)NUM2DBL(r_ary_entry(a, 15));
|
|
749
|
+
float b3 = (float)NUM2DBL(r_ary_entry(a, 16));
|
|
750
|
+
float a3 = (float)NUM2DBL(r_ary_entry(a, 17));
|
|
751
|
+
|
|
752
|
+
SDL_LockSurface(can->surface);
|
|
753
|
+
canvas_fill_triangle_lerp_on_surface(can->surface,
|
|
754
|
+
x1, y1, (Uint8)(r1 * 255), (Uint8)(g1 * 255), (Uint8)(b1 * 255), (Uint8)(a1 * 255),
|
|
755
|
+
x2, y2, (Uint8)(r2 * 255), (Uint8)(g2 * 255), (Uint8)(b2 * 255), (Uint8)(a2 * 255),
|
|
756
|
+
x3, y3, (Uint8)(r3 * 255), (Uint8)(g3 * 255), (Uint8)(b3 * 255), (Uint8)(a3 * 255));
|
|
757
|
+
SDL_UnlockSurface(can->surface);
|
|
758
|
+
|
|
759
|
+
canvas_mark_dirty(can,
|
|
760
|
+
fminf(x1, fminf(x2, x3)), fminf(y1, fminf(y2, y3)),
|
|
761
|
+
fmaxf(x1, fmaxf(x2, x3)), fmaxf(y1, fmaxf(y2, y3)));
|
|
762
|
+
|
|
763
|
+
return R_TRUE;
|
|
764
|
+
}
|
|
430
765
|
|
|
431
|
-
// Prepare to store the different points used map
|
|
432
|
-
// the triangles that render the thick polyline
|
|
433
|
-
SDL_Vertex verts[POLYLINE_RENDER_NVERTS];
|
|
434
766
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
767
|
+
/*
|
|
768
|
+
* Fill a rectangle on an SDL_Surface.
|
|
769
|
+
* Coordinates are in surface pixel space (pre-scaled). Color components are 0-255.
|
|
770
|
+
* Opaque fills use SDL_FillSurfaceRect; semi-transparent fills alpha-blend
|
|
771
|
+
* per pixel (surface is locked internally for that path).
|
|
772
|
+
*/
|
|
773
|
+
static void canvas_fill_rectangle_on_surface(
|
|
774
|
+
SDL_Surface *surface,
|
|
775
|
+
float fx, float fy, float fw, float fh,
|
|
776
|
+
Uint8 cr, Uint8 cg, Uint8 cb, Uint8 ca) {
|
|
777
|
+
|
|
778
|
+
// A non-finite coordinate or size (NaN/Inf) makes the (int)ceilf casts below
|
|
779
|
+
// undefined behavior and could produce an out-of-bounds rect. Skip the draw.
|
|
780
|
+
if (!isfinite(fx) || !isfinite(fy) || !isfinite(fw) || !isfinite(fh)) return;
|
|
781
|
+
|
|
782
|
+
// Half-open pixel coverage: a rect (x, y, w, h) covers the continuous
|
|
783
|
+
// region [x, x+w) x [y, y+h), so pixel i is filled iff ceil(x) <= i < ceil(x+w).
|
|
784
|
+
// Matches the ceilf convention used by the triangle rasterizer, so
|
|
785
|
+
// fill_rectangle and draw_line share pixel edges for non-integer inputs.
|
|
786
|
+
int rx = (int)ceilf(fx);
|
|
787
|
+
int ry = (int)ceilf(fy);
|
|
788
|
+
int rw = (int)ceilf(fx + fw) - rx;
|
|
789
|
+
int rh = (int)ceilf(fy + fh) - ry;
|
|
790
|
+
SDL_Rect rect = { rx, ry, rw, rh };
|
|
791
|
+
|
|
792
|
+
if (ca == 255) {
|
|
793
|
+
// Opaque — use fast SDL fill
|
|
794
|
+
Uint32 color = SDL_MapSurfaceRGBA(surface, cr, cg, cb, 255);
|
|
795
|
+
SDL_FillSurfaceRect(surface, &rect, color);
|
|
796
|
+
} else if (ca > 0) {
|
|
797
|
+
// Semi-transparent — alpha blend per pixel
|
|
798
|
+
// Clamp rect to surface bounds
|
|
799
|
+
int x_start = rx < 0 ? 0 : rx;
|
|
800
|
+
int y_start = ry < 0 ? 0 : ry;
|
|
801
|
+
int x_end = rx + rw;
|
|
802
|
+
int y_end = ry + rh;
|
|
803
|
+
if (x_end > surface->w) x_end = surface->w;
|
|
804
|
+
if (y_end > surface->h) y_end = surface->h;
|
|
805
|
+
|
|
806
|
+
SDL_LockSurface(surface);
|
|
807
|
+
for (int y = y_start; y < y_end; y++) {
|
|
808
|
+
Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
|
|
809
|
+
for (int x = x_start; x < x_end; x++) {
|
|
810
|
+
Uint8 *pixel = row + x * 4;
|
|
811
|
+
canvas_blend_over(pixel, cr, cg, cb, ca);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
SDL_UnlockSurface(surface);
|
|
438
815
|
}
|
|
816
|
+
}
|
|
439
817
|
|
|
440
|
-
// the indices into the vertex, always the same two triangles
|
|
441
|
-
const int indices[] = {
|
|
442
|
-
0, 1, 2, // left outer triangle
|
|
443
|
-
0, 2, 3, // left inner triangle
|
|
444
|
-
};
|
|
445
818
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
819
|
+
/*
|
|
820
|
+
* Ruby2D::Canvas#ext_fill_rectangle
|
|
821
|
+
* Arguments: [x, y, width, height, r, g, b, a]
|
|
822
|
+
*/
|
|
823
|
+
R_VAL ruby2d_ext_canvas_fill_rectangle(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
824
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
825
|
+
if (argc != 9) r_raise("Ruby2D::Ext.canvas_fill_rectangle expects 9 args, got %d", (int)argc);
|
|
826
|
+
R_VAL obj = argv[0];
|
|
827
|
+
|
|
828
|
+
R2D_Canvas *can;
|
|
829
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
830
|
+
|
|
831
|
+
float scale = can->scale;
|
|
832
|
+
float fx = (float)NUM2DBL(argv[1]) * scale;
|
|
833
|
+
float fy = (float)NUM2DBL(argv[2]) * scale;
|
|
834
|
+
float fw = (float)NUM2DBL(argv[3]) * scale;
|
|
835
|
+
float fh = (float)NUM2DBL(argv[4]) * scale;
|
|
836
|
+
float rf = (float)NUM2DBL(argv[5]);
|
|
837
|
+
float gf = (float)NUM2DBL(argv[6]);
|
|
838
|
+
float bf = (float)NUM2DBL(argv[7]);
|
|
839
|
+
float af = (float)NUM2DBL(argv[8]);
|
|
840
|
+
|
|
841
|
+
Uint8 cr = (Uint8)(rf * 255);
|
|
842
|
+
Uint8 cg = (Uint8)(gf * 255);
|
|
843
|
+
Uint8 cb = (Uint8)(bf * 255);
|
|
844
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
845
|
+
|
|
846
|
+
if (ca == 0) return R_TRUE; // fully transparent: skip fill and texture rebuild
|
|
847
|
+
|
|
848
|
+
canvas_fill_rectangle_on_surface(can->surface, fx, fy, fw, fh, cr, cg, cb, ca);
|
|
849
|
+
canvas_mark_dirty(can, fx, fy, fx + fw, fy + fh);
|
|
850
|
+
|
|
851
|
+
return R_TRUE;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
/*
|
|
856
|
+
* Ruby2D::Canvas#ext_fill_rectangles
|
|
857
|
+
* Arguments: [n, x1, y1, w1, h1, x2, y2, w2, h2, ..., r, g, b, a]
|
|
858
|
+
* Fills n rectangles with a single shared color in one FFI crossing.
|
|
859
|
+
*/
|
|
860
|
+
R_VAL ruby2d_ext_canvas_fill_rectangles(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
861
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
862
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_fill_rectangles expects 2 args, got %d", (int)argc);
|
|
863
|
+
R_VAL obj = argv[0];
|
|
864
|
+
R_VAL a = argv[1];
|
|
865
|
+
|
|
866
|
+
R2D_Canvas *can;
|
|
867
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
868
|
+
|
|
869
|
+
if ((int)r_ary_len(a) < 1) return R_TRUE;
|
|
870
|
+
int n = (int)NUM2INT(r_ary_entry(a, 0));
|
|
871
|
+
if (n < 1) return R_TRUE;
|
|
872
|
+
|
|
873
|
+
// Guard the array length: [count] + n*[x,y,w,h] + [r,g,b,a]. A short array
|
|
874
|
+
// would make r_ary_entry return nil and NUM2DBL(nil) error (mruby aborts).
|
|
875
|
+
long need = 5L + (long)n * 4;
|
|
876
|
+
if ((long)r_ary_len(a) < need)
|
|
877
|
+
r_raise("Ruby2D::Ext.canvas_fill_rectangles: array has %d values for %d rects (need %ld)",
|
|
878
|
+
(int)r_ary_len(a), n, need);
|
|
879
|
+
|
|
880
|
+
int base = 1 + n * 4;
|
|
881
|
+
float rf = (float)NUM2DBL(r_ary_entry(a, base));
|
|
882
|
+
float gf = (float)NUM2DBL(r_ary_entry(a, base + 1));
|
|
883
|
+
float bf = (float)NUM2DBL(r_ary_entry(a, base + 2));
|
|
884
|
+
float af = (float)NUM2DBL(r_ary_entry(a, base + 3));
|
|
885
|
+
|
|
886
|
+
Uint8 cr = (Uint8)(rf * 255);
|
|
887
|
+
Uint8 cg = (Uint8)(gf * 255);
|
|
888
|
+
Uint8 cb = (Uint8)(bf * 255);
|
|
889
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
890
|
+
|
|
891
|
+
if (ca == 0) return R_TRUE;
|
|
892
|
+
|
|
893
|
+
float scale = can->scale;
|
|
894
|
+
for (int i = 0; i < n; i++) {
|
|
895
|
+
int o = 1 + i * 4;
|
|
896
|
+
float fx = (float)NUM2DBL(r_ary_entry(a, o)) * scale;
|
|
897
|
+
float fy = (float)NUM2DBL(r_ary_entry(a, o + 1)) * scale;
|
|
898
|
+
float fw = (float)NUM2DBL(r_ary_entry(a, o + 2)) * scale;
|
|
899
|
+
float fh = (float)NUM2DBL(r_ary_entry(a, o + 3)) * scale;
|
|
900
|
+
canvas_fill_rectangle_on_surface(can->surface, fx, fy, fw, fh, cr, cg, cb, ca);
|
|
901
|
+
canvas_mark_dirty(can, fx, fy, fx + fw, fy + fh);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
return R_TRUE;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
/*
|
|
909
|
+
* Ruby2D::Canvas#ext_fill_pixel_grid
|
|
910
|
+
* Arguments:
|
|
911
|
+
* header = [cols, rows, cell_w, cell_h, ox, oy]
|
|
912
|
+
* colors = flat array of cols*rows*4 floats in 0..1
|
|
913
|
+
* (r0, g0, b0, a0, r1, g1, b1, a1, ...)
|
|
914
|
+
* Fills a regular cols x rows grid of equal-size rectangles, each with its
|
|
915
|
+
* own RGBA color. Cell (col, row) is the rect at
|
|
916
|
+
* (ox + col*cell_w, oy + row*cell_h, cell_w, cell_h), colored by
|
|
917
|
+
* colors[(row*cols + col)*4 .. +3]. Single FFI crossing for the whole grid:
|
|
918
|
+
* one surface lock, one texture invalidate, cells with a==0 skipped.
|
|
919
|
+
*/
|
|
920
|
+
R_VAL ruby2d_ext_canvas_fill_pixel_grid(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
921
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
922
|
+
if (argc != 3) r_raise("Ruby2D::Ext.canvas_fill_pixel_grid expects 3 args, got %d", (int)argc);
|
|
923
|
+
R_VAL obj = argv[0];
|
|
924
|
+
R_VAL header = argv[1];
|
|
925
|
+
R_VAL colors = argv[2];
|
|
926
|
+
|
|
927
|
+
R2D_Canvas *can;
|
|
928
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
929
|
+
|
|
930
|
+
// Guard array lengths before the read loops: a short array would make
|
|
931
|
+
// r_ary_entry return nil and NUM2DBL/NUM2INT(nil) error (hard-abort on mruby).
|
|
932
|
+
if ((int)r_ary_len(header) < 6)
|
|
933
|
+
r_raise("Ruby2D::Ext.canvas_fill_pixel_grid: header needs 6 values, got %d",
|
|
934
|
+
(int)r_ary_len(header));
|
|
935
|
+
|
|
936
|
+
int cols = (int)NUM2INT(r_ary_entry(header, 0));
|
|
937
|
+
int rows = (int)NUM2INT(r_ary_entry(header, 1));
|
|
938
|
+
if (cols < 1 || rows < 1) return R_TRUE;
|
|
939
|
+
|
|
940
|
+
long need = (long)cols * rows * 4;
|
|
941
|
+
if ((long)r_ary_len(colors) < need)
|
|
942
|
+
r_raise("Ruby2D::Ext.canvas_fill_pixel_grid: %d color values for a %dx%d grid (need %ld)",
|
|
943
|
+
(int)r_ary_len(colors), cols, rows, need);
|
|
944
|
+
|
|
945
|
+
float scale = can->scale;
|
|
946
|
+
float cell_w = (float)NUM2DBL(r_ary_entry(header, 2)) * scale;
|
|
947
|
+
float cell_h = (float)NUM2DBL(r_ary_entry(header, 3)) * scale;
|
|
948
|
+
float ox = (float)NUM2DBL(r_ary_entry(header, 4)) * scale;
|
|
949
|
+
float oy = (float)NUM2DBL(r_ary_entry(header, 5)) * scale;
|
|
950
|
+
|
|
951
|
+
// A non-finite cell size or origin (NaN/Inf) would make the per-cell
|
|
952
|
+
// (int)ceilf casts below undefined behavior; every cell derives from these
|
|
953
|
+
// four values, so skip the whole grid.
|
|
954
|
+
if (!isfinite(cell_w) || !isfinite(cell_h) || !isfinite(ox) || !isfinite(oy))
|
|
955
|
+
return R_TRUE;
|
|
956
|
+
|
|
957
|
+
SDL_Surface *surface = can->surface;
|
|
958
|
+
|
|
959
|
+
SDL_LockSurface(surface);
|
|
960
|
+
for (int gr = 0; gr < rows; gr++) {
|
|
961
|
+
for (int gc = 0; gc < cols; gc++) {
|
|
962
|
+
int o = (gr * cols + gc) * 4;
|
|
963
|
+
// Clamp channels to [0,1] before the 8-bit cast. This is the one
|
|
964
|
+
// user-facing color path that builds no Color object, so it has no
|
|
965
|
+
// Ruby-side clamp; an out-of-range value would wrap (1.5 -> 126) rather
|
|
966
|
+
// than saturate.
|
|
967
|
+
float af = (float)NUM2DBL(r_ary_entry(colors, o + 3));
|
|
968
|
+
if (af < 0.0f) af = 0.0f; else if (af > 1.0f) af = 1.0f;
|
|
969
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
970
|
+
if (ca == 0) continue;
|
|
971
|
+
|
|
972
|
+
float rf = (float)NUM2DBL(r_ary_entry(colors, o));
|
|
973
|
+
float gf = (float)NUM2DBL(r_ary_entry(colors, o + 1));
|
|
974
|
+
float bf = (float)NUM2DBL(r_ary_entry(colors, o + 2));
|
|
975
|
+
if (rf < 0.0f) rf = 0.0f; else if (rf > 1.0f) rf = 1.0f;
|
|
976
|
+
if (gf < 0.0f) gf = 0.0f; else if (gf > 1.0f) gf = 1.0f;
|
|
977
|
+
if (bf < 0.0f) bf = 0.0f; else if (bf > 1.0f) bf = 1.0f;
|
|
978
|
+
Uint8 cr = (Uint8)(rf * 255);
|
|
979
|
+
Uint8 cg = (Uint8)(gf * 255);
|
|
980
|
+
Uint8 cb = (Uint8)(bf * 255);
|
|
981
|
+
|
|
982
|
+
// Half-open pixel coverage, matching canvas_fill_rectangle_on_surface
|
|
983
|
+
float fx = ox + gc * cell_w;
|
|
984
|
+
float fy = oy + gr * cell_h;
|
|
985
|
+
int rx = (int)ceilf(fx);
|
|
986
|
+
int ry = (int)ceilf(fy);
|
|
987
|
+
int rw = (int)ceilf(fx + cell_w) - rx;
|
|
988
|
+
int rh = (int)ceilf(fy + cell_h) - ry;
|
|
989
|
+
|
|
990
|
+
int x_start = rx < 0 ? 0 : rx;
|
|
991
|
+
int y_start = ry < 0 ? 0 : ry;
|
|
992
|
+
int x_end = rx + rw;
|
|
993
|
+
int y_end = ry + rh;
|
|
994
|
+
if (x_end > surface->w) x_end = surface->w;
|
|
995
|
+
if (y_end > surface->h) y_end = surface->h;
|
|
996
|
+
|
|
997
|
+
if (ca == 255) {
|
|
998
|
+
// Color is constant within the cell: pack once, store 32 bits per pixel.
|
|
999
|
+
Uint32 packed = SDL_MapSurfaceRGBA(surface, cr, cg, cb, 255);
|
|
1000
|
+
for (int y = y_start; y < y_end; y++) {
|
|
1001
|
+
Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
|
|
1002
|
+
for (int x = x_start; x < x_end; x++) {
|
|
1003
|
+
Uint8 *pixel = row + x * 4;
|
|
1004
|
+
SDL_memcpy(pixel, &packed, 4);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
} else {
|
|
1008
|
+
for (int y = y_start; y < y_end; y++) {
|
|
1009
|
+
Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
|
|
1010
|
+
for (int x = x_start; x < x_end; x++) {
|
|
1011
|
+
Uint8 *pixel = row + x * 4;
|
|
1012
|
+
canvas_blend_over(pixel, cr, cg, cb, ca);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
509
1016
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
1017
|
+
}
|
|
1018
|
+
SDL_UnlockSurface(surface);
|
|
1019
|
+
|
|
1020
|
+
canvas_mark_dirty(can, ox, oy, ox + cols * cell_w, oy + rows * cell_h);
|
|
1021
|
+
|
|
1022
|
+
return R_TRUE;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
/*
|
|
1027
|
+
* Ruby2D::Canvas#ext_fill_ellipse
|
|
1028
|
+
* Arguments: [x, y, xradius, yradius, r, g, b, a]
|
|
1029
|
+
*/
|
|
1030
|
+
R_VAL ruby2d_ext_canvas_fill_ellipse(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1031
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1032
|
+
if (argc != 9) r_raise("Ruby2D::Ext.canvas_fill_ellipse expects 9 args, got %d", (int)argc);
|
|
1033
|
+
R_VAL obj = argv[0];
|
|
1034
|
+
|
|
1035
|
+
R2D_Canvas *can;
|
|
1036
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1037
|
+
|
|
1038
|
+
float scale = can->scale;
|
|
1039
|
+
float cx = (float)NUM2DBL(argv[1]) * scale;
|
|
1040
|
+
float cy = (float)NUM2DBL(argv[2]) * scale;
|
|
1041
|
+
float xradius = (float)NUM2DBL(argv[3]) * scale;
|
|
1042
|
+
float yradius = (float)NUM2DBL(argv[4]) * scale;
|
|
1043
|
+
float rf = (float)NUM2DBL(argv[5]);
|
|
1044
|
+
float gf = (float)NUM2DBL(argv[6]);
|
|
1045
|
+
float bf = (float)NUM2DBL(argv[7]);
|
|
1046
|
+
float af = (float)NUM2DBL(argv[8]);
|
|
1047
|
+
|
|
1048
|
+
Uint8 cr = (Uint8)(rf * 255);
|
|
1049
|
+
Uint8 cg = (Uint8)(gf * 255);
|
|
1050
|
+
Uint8 cb = (Uint8)(bf * 255);
|
|
1051
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
1052
|
+
|
|
1053
|
+
if (ca == 0) return R_TRUE;
|
|
1054
|
+
|
|
1055
|
+
// A non-finite center or radius (NaN/Inf) would make the (int)ceilf/floorf
|
|
1056
|
+
// casts below undefined behavior (and `radius <= 0` does not catch NaN, since
|
|
1057
|
+
// every NaN comparison is false). Skip the draw before the scanline math.
|
|
1058
|
+
if (!isfinite(cx) || !isfinite(cy) || !isfinite(xradius) || !isfinite(yradius))
|
|
1059
|
+
return R_TRUE;
|
|
1060
|
+
|
|
1061
|
+
// A zero (or negative) radius has no area to fill. Guard before the scanline
|
|
1062
|
+
// math below: yradius == 0 makes yr2 == 0, so (dy * dy) / yr2 is 0.0 / 0.0 ==
|
|
1063
|
+
// NaN, and converting the resulting NaN pixel bounds to int is undefined
|
|
1064
|
+
// behavior that produces an out-of-bounds heap write.
|
|
1065
|
+
if (xradius <= 0.0f || yradius <= 0.0f) return R_TRUE;
|
|
1066
|
+
|
|
1067
|
+
SDL_Surface *surface = can->surface;
|
|
1068
|
+
float yr2 = yradius * yradius;
|
|
1069
|
+
|
|
1070
|
+
int y_start = (int)ceilf(cy - yradius);
|
|
1071
|
+
int y_end = (int)floorf(cy + yradius);
|
|
1072
|
+
if (y_start < 0) y_start = 0;
|
|
1073
|
+
if (y_end >= surface->h) y_end = surface->h - 1;
|
|
1074
|
+
|
|
1075
|
+
// The fill color is constant across the ellipse, so pack it once and write
|
|
1076
|
+
// opaque pixels with a single 32-bit store instead of four byte stores.
|
|
1077
|
+
Uint32 packed = SDL_MapSurfaceRGBA(surface, cr, cg, cb, 255);
|
|
1078
|
+
|
|
1079
|
+
SDL_LockSurface(surface);
|
|
1080
|
+
for (int y = y_start; y <= y_end; y++) {
|
|
1081
|
+
float dy = (float)y - cy;
|
|
1082
|
+
// Boundary-row float rounding can push (dy*dy)/yr2 just above 1.0, making
|
|
1083
|
+
// the radicand negative; sqrtf would yield NaN and (int)ceilf(NaN) is UB.
|
|
1084
|
+
float t = 1.0f - (dy * dy) / yr2;
|
|
1085
|
+
if (t < 0.0f) t = 0.0f;
|
|
1086
|
+
float dx = xradius * sqrtf(t);
|
|
1087
|
+
int x_start = (int)ceilf(cx - dx);
|
|
1088
|
+
int x_end = (int)floorf(cx + dx);
|
|
1089
|
+
if (x_start < 0) x_start = 0;
|
|
1090
|
+
if (x_end >= surface->w) x_end = surface->w - 1;
|
|
1091
|
+
|
|
1092
|
+
Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
|
|
1093
|
+
for (int x = x_start; x <= x_end; x++) {
|
|
1094
|
+
Uint8 *pixel = row + x * 4;
|
|
1095
|
+
if (ca == 255) {
|
|
1096
|
+
SDL_memcpy(pixel, &packed, 4);
|
|
1097
|
+
} else {
|
|
1098
|
+
canvas_blend_over(pixel, cr, cg, cb, ca);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
SDL_UnlockSurface(surface);
|
|
1103
|
+
|
|
1104
|
+
canvas_mark_dirty(can, cx - xradius, cy - yradius, cx + xradius, cy + yradius);
|
|
1105
|
+
|
|
1106
|
+
return R_TRUE;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
/*
|
|
1111
|
+
* Ruby2D::Canvas#ext_fill_polygon
|
|
1112
|
+
* Arguments: [n, x1, y1, x2, y2, ..., r, g, b, a]
|
|
1113
|
+
* Uses ear-clipping triangulation (R2D_TriangulatePolygon) so concave
|
|
1114
|
+
* polygons fill correctly.
|
|
1115
|
+
*/
|
|
1116
|
+
R_VAL ruby2d_ext_canvas_fill_polygon(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1117
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1118
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_fill_polygon expects 2 args, got %d", (int)argc);
|
|
1119
|
+
R_VAL obj = argv[0];
|
|
1120
|
+
R_VAL a = argv[1];
|
|
1121
|
+
|
|
1122
|
+
R2D_Canvas *can;
|
|
1123
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1124
|
+
|
|
1125
|
+
if ((int)r_ary_len(a) < 1) return R_TRUE;
|
|
1126
|
+
int n = (int)NUM2INT(r_ary_entry(a, 0));
|
|
1127
|
+
if (n < 3) return R_TRUE;
|
|
1128
|
+
|
|
1129
|
+
// [n, x1, y1, ... xn, yn, r, g, b, a] — guard before indexing (a short array
|
|
1130
|
+
// would hard-abort under mruby), matching the sibling fill_* methods.
|
|
1131
|
+
long need = 1 + (long)n * 2 + 4;
|
|
1132
|
+
if ((long)r_ary_len(a) < need)
|
|
1133
|
+
r_raise("Ruby2D::Ext.canvas_fill_polygon: array has %d values for %d vertices (need %ld)",
|
|
1134
|
+
(int)r_ary_len(a), n, need);
|
|
1135
|
+
|
|
1136
|
+
float scale = can->scale;
|
|
1137
|
+
float *verts = (float *)malloc(n * 2 * sizeof(float));
|
|
1138
|
+
if (!verts) return R_TRUE;
|
|
1139
|
+
|
|
1140
|
+
for (int i = 0; i < n; i++) {
|
|
1141
|
+
verts[i*2] = (float)NUM2DBL(r_ary_entry(a, 1 + i*2)) * scale;
|
|
1142
|
+
verts[i*2+1] = (float)NUM2DBL(r_ary_entry(a, 2 + i*2)) * scale;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
int base = 1 + n * 2;
|
|
1146
|
+
float rf = (float)NUM2DBL(r_ary_entry(a, base));
|
|
1147
|
+
float gf = (float)NUM2DBL(r_ary_entry(a, base + 1));
|
|
1148
|
+
float bf = (float)NUM2DBL(r_ary_entry(a, base + 2));
|
|
1149
|
+
float af = (float)NUM2DBL(r_ary_entry(a, base + 3));
|
|
1150
|
+
|
|
1151
|
+
Uint8 cr = (Uint8)(rf * 255);
|
|
1152
|
+
Uint8 cg = (Uint8)(gf * 255);
|
|
1153
|
+
Uint8 cb = (Uint8)(bf * 255);
|
|
1154
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
1155
|
+
|
|
1156
|
+
if (ca > 0) {
|
|
1157
|
+
int tri_count = 0;
|
|
1158
|
+
int *indices = R2D_TriangulatePolygon(verts, n, &tri_count);
|
|
1159
|
+
if (indices && tri_count > 0) {
|
|
1160
|
+
SDL_LockSurface(can->surface);
|
|
1161
|
+
for (int t = 0; t < tri_count; t++) {
|
|
1162
|
+
int i1 = indices[t * 3];
|
|
1163
|
+
int i2 = indices[t * 3 + 1];
|
|
1164
|
+
int i3 = indices[t * 3 + 2];
|
|
1165
|
+
canvas_fill_triangle_on_surface(can->surface,
|
|
1166
|
+
verts[i1*2], verts[i1*2+1],
|
|
1167
|
+
verts[i2*2], verts[i2*2+1],
|
|
1168
|
+
verts[i3*2], verts[i3*2+1],
|
|
1169
|
+
cr, cg, cb, ca);
|
|
1170
|
+
}
|
|
1171
|
+
SDL_UnlockSurface(can->surface);
|
|
1172
|
+
canvas_mark_dirty_verts(can, verts, n, 0.0f);
|
|
1173
|
+
}
|
|
1174
|
+
free(indices);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
free(verts);
|
|
1178
|
+
return R_TRUE;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
/*
|
|
1183
|
+
* Ruby2D::Canvas#ext_fill_polygon_lerp
|
|
1184
|
+
* Arguments: [n, x1, y1, r1, g1, b1, a1, x2, y2, r2, g2, b2, a2, ...]
|
|
1185
|
+
* Per-vertex colors. Triangulated via ear clipping; each emitted triangle
|
|
1186
|
+
* is rasterized with barycentric color interpolation between its three
|
|
1187
|
+
* source vertices' colors.
|
|
1188
|
+
*/
|
|
1189
|
+
R_VAL ruby2d_ext_canvas_fill_polygon_lerp(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1190
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1191
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_fill_polygon_lerp expects 2 args, got %d", (int)argc);
|
|
1192
|
+
R_VAL obj = argv[0];
|
|
1193
|
+
R_VAL a = argv[1];
|
|
1194
|
+
|
|
1195
|
+
R2D_Canvas *can;
|
|
1196
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1197
|
+
|
|
1198
|
+
if ((int)r_ary_len(a) < 1) return R_TRUE;
|
|
1199
|
+
int n = (int)NUM2INT(r_ary_entry(a, 0));
|
|
1200
|
+
if (n < 3) return R_TRUE;
|
|
1201
|
+
|
|
1202
|
+
// [n, x1, y1, r1, g1, b1, a1, ...] — 6 entries per vertex. Guard before
|
|
1203
|
+
// indexing (a short array would hard-abort under mruby).
|
|
1204
|
+
long need = 1 + (long)n * 6;
|
|
1205
|
+
if ((long)r_ary_len(a) < need)
|
|
1206
|
+
r_raise("Ruby2D::Ext.canvas_fill_polygon_lerp: array has %d values for %d vertices (need %ld)",
|
|
1207
|
+
(int)r_ary_len(a), n, need);
|
|
1208
|
+
|
|
1209
|
+
float scale = can->scale;
|
|
1210
|
+
float *verts = (float *)malloc(n * 2 * sizeof(float));
|
|
1211
|
+
Uint8 *colors = (Uint8 *)malloc(n * 4 * sizeof(Uint8));
|
|
1212
|
+
if (!verts || !colors) { free(verts); free(colors); return R_TRUE; }
|
|
1213
|
+
|
|
1214
|
+
// Each vertex packs as (x, y, r, g, b, a) — 6 entries.
|
|
1215
|
+
for (int i = 0; i < n; i++) {
|
|
1216
|
+
int o = 1 + i * 6;
|
|
1217
|
+
verts[i*2] = (float)NUM2DBL(r_ary_entry(a, o)) * scale;
|
|
1218
|
+
verts[i*2+1] = (float)NUM2DBL(r_ary_entry(a, o + 1)) * scale;
|
|
1219
|
+
colors[i*4] = (Uint8)((float)NUM2DBL(r_ary_entry(a, o + 2)) * 255);
|
|
1220
|
+
colors[i*4+1] = (Uint8)((float)NUM2DBL(r_ary_entry(a, o + 3)) * 255);
|
|
1221
|
+
colors[i*4+2] = (Uint8)((float)NUM2DBL(r_ary_entry(a, o + 4)) * 255);
|
|
1222
|
+
colors[i*4+3] = (Uint8)((float)NUM2DBL(r_ary_entry(a, o + 5)) * 255);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
int tri_count = 0;
|
|
1226
|
+
int *indices = R2D_TriangulatePolygon(verts, n, &tri_count);
|
|
1227
|
+
if (indices && tri_count > 0) {
|
|
1228
|
+
SDL_LockSurface(can->surface);
|
|
1229
|
+
for (int t = 0; t < tri_count; t++) {
|
|
1230
|
+
int i1 = indices[t * 3];
|
|
1231
|
+
int i2 = indices[t * 3 + 1];
|
|
1232
|
+
int i3 = indices[t * 3 + 2];
|
|
1233
|
+
canvas_fill_triangle_lerp_on_surface(can->surface,
|
|
1234
|
+
verts[i1*2], verts[i1*2+1],
|
|
1235
|
+
colors[i1*4], colors[i1*4+1], colors[i1*4+2], colors[i1*4+3],
|
|
1236
|
+
verts[i2*2], verts[i2*2+1],
|
|
1237
|
+
colors[i2*4], colors[i2*4+1], colors[i2*4+2], colors[i2*4+3],
|
|
1238
|
+
verts[i3*2], verts[i3*2+1],
|
|
1239
|
+
colors[i3*4], colors[i3*4+1], colors[i3*4+2], colors[i3*4+3]);
|
|
518
1240
|
}
|
|
1241
|
+
SDL_UnlockSurface(can->surface);
|
|
1242
|
+
canvas_mark_dirty_verts(can, verts, n, 0.0f);
|
|
1243
|
+
}
|
|
1244
|
+
free(indices);
|
|
1245
|
+
|
|
1246
|
+
free(verts);
|
|
1247
|
+
free(colors);
|
|
1248
|
+
return R_TRUE;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
|
|
1252
|
+
/*
|
|
1253
|
+
* Ruby2D::Canvas#ext_draw_line
|
|
1254
|
+
* Arguments: [x1, y1, x2, y2, stroke_width, r, g, b, a, dash, gap]
|
|
1255
|
+
* When dash > 0, draws a dashed line; otherwise a solid line.
|
|
1256
|
+
*/
|
|
1257
|
+
R_VAL ruby2d_ext_canvas_draw_line(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1258
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1259
|
+
if (argc != 12) r_raise("Ruby2D::Ext.canvas_draw_line expects 12 args, got %d", (int)argc);
|
|
1260
|
+
R_VAL obj = argv[0];
|
|
1261
|
+
|
|
1262
|
+
R2D_Canvas *can;
|
|
1263
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1264
|
+
|
|
1265
|
+
float scale = can->scale;
|
|
1266
|
+
float x1 = (float)NUM2DBL(argv[1]) * scale;
|
|
1267
|
+
float y1 = (float)NUM2DBL(argv[2]) * scale;
|
|
1268
|
+
float x2 = (float)NUM2DBL(argv[3]) * scale;
|
|
1269
|
+
float y2 = (float)NUM2DBL(argv[4]) * scale;
|
|
1270
|
+
float sw = (float)NUM2DBL(argv[5]) * scale;
|
|
1271
|
+
float rf = (float)NUM2DBL(argv[6]);
|
|
1272
|
+
float gf = (float)NUM2DBL(argv[7]);
|
|
1273
|
+
float bf = (float)NUM2DBL(argv[8]);
|
|
1274
|
+
float af = (float)NUM2DBL(argv[9]);
|
|
1275
|
+
float dash = (float)NUM2DBL(argv[10]) * scale;
|
|
1276
|
+
float gap = (float)NUM2DBL(argv[11]) * scale;
|
|
1277
|
+
|
|
1278
|
+
Uint8 cr = (Uint8)(rf * 255);
|
|
1279
|
+
Uint8 cg = (Uint8)(gf * 255);
|
|
1280
|
+
Uint8 cb = (Uint8)(bf * 255);
|
|
1281
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
1282
|
+
|
|
1283
|
+
if (ca == 0) return R_TRUE;
|
|
1284
|
+
|
|
1285
|
+
SDL_LockSurface(can->surface);
|
|
1286
|
+
|
|
1287
|
+
if (dash > 0.0f) {
|
|
1288
|
+
if (dash < 0.5f) dash = 0.5f;
|
|
1289
|
+
if (gap < 0.5f) gap = 0.5f;
|
|
1290
|
+
|
|
1291
|
+
float dx = x2 - x1;
|
|
1292
|
+
float dy = y2 - y1;
|
|
1293
|
+
float len = sqrtf(dx * dx + dy * dy);
|
|
1294
|
+
if (len >= 0.0001f) {
|
|
1295
|
+
float ux = dx / len;
|
|
1296
|
+
float uy = dy / len;
|
|
1297
|
+
|
|
1298
|
+
float t = 0.0f;
|
|
1299
|
+
int drawing = 1;
|
|
1300
|
+
while (t < len) {
|
|
1301
|
+
float seg = drawing ? dash : gap;
|
|
1302
|
+
if (t + seg > len) seg = len - t;
|
|
1303
|
+
|
|
1304
|
+
if (drawing) {
|
|
1305
|
+
float sx1 = x1 + ux * t;
|
|
1306
|
+
float sy1 = y1 + uy * t;
|
|
1307
|
+
float sx2 = x1 + ux * (t + seg);
|
|
1308
|
+
float sy2 = y1 + uy * (t + seg);
|
|
1309
|
+
canvas_draw_line_on_surface(can->surface, sx1, sy1, sx2, sy2, sw, cr, cg, cb, ca);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
t += seg;
|
|
1313
|
+
drawing = !drawing;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
} else {
|
|
1317
|
+
canvas_draw_line_on_surface(can->surface, x1, y1, x2, y2, sw, cr, cg, cb, ca);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
SDL_UnlockSurface(can->surface);
|
|
1321
|
+
{
|
|
1322
|
+
float hw = sw * 0.5f;
|
|
1323
|
+
canvas_mark_dirty(can, fminf(x1, x2) - hw, fminf(y1, y2) - hw,
|
|
1324
|
+
fmaxf(x1, x2) + hw, fmaxf(y1, y2) + hw);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
return R_TRUE;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
/*
|
|
1332
|
+
* Ruby2D::Canvas#ext_draw_line_lerp
|
|
1333
|
+
* Arguments: [x1, y1, x2, y2, stroke_width,
|
|
1334
|
+
* r1, g1, b1, a1, r2, g2, b2, a2,
|
|
1335
|
+
* r3, g3, b3, a3, r4, g4, b4, a4,
|
|
1336
|
+
* dash, gap]
|
|
1337
|
+
* 4 per-corner colors mirror R2D_DrawLine's vertex layout
|
|
1338
|
+
* (c1=start+perp, c2=start-perp, c3=end-perp, c4=end+perp). When dash > 0,
|
|
1339
|
+
* draws a dashed line and interpolates endpoint colors along the length.
|
|
1340
|
+
*/
|
|
1341
|
+
R_VAL ruby2d_ext_canvas_draw_line_lerp(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1342
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1343
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_draw_line_lerp expects 2 args, got %d", (int)argc);
|
|
1344
|
+
R_VAL obj = argv[0];
|
|
1345
|
+
R_VAL a = argv[1];
|
|
1346
|
+
|
|
1347
|
+
R2D_Canvas *can;
|
|
1348
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1349
|
+
|
|
1350
|
+
float scale = can->scale;
|
|
1351
|
+
float x1 = (float)NUM2DBL(r_ary_entry(a, 0)) * scale;
|
|
1352
|
+
float y1 = (float)NUM2DBL(r_ary_entry(a, 1)) * scale;
|
|
1353
|
+
float x2 = (float)NUM2DBL(r_ary_entry(a, 2)) * scale;
|
|
1354
|
+
float y2 = (float)NUM2DBL(r_ary_entry(a, 3)) * scale;
|
|
1355
|
+
float sw = (float)NUM2DBL(r_ary_entry(a, 4)) * scale;
|
|
1356
|
+
|
|
1357
|
+
Uint8 r1 = (Uint8)(NUM2DBL(r_ary_entry(a, 5)) * 255);
|
|
1358
|
+
Uint8 g1 = (Uint8)(NUM2DBL(r_ary_entry(a, 6)) * 255);
|
|
1359
|
+
Uint8 b1 = (Uint8)(NUM2DBL(r_ary_entry(a, 7)) * 255);
|
|
1360
|
+
Uint8 a1 = (Uint8)(NUM2DBL(r_ary_entry(a, 8)) * 255);
|
|
1361
|
+
Uint8 r2 = (Uint8)(NUM2DBL(r_ary_entry(a, 9)) * 255);
|
|
1362
|
+
Uint8 g2 = (Uint8)(NUM2DBL(r_ary_entry(a, 10)) * 255);
|
|
1363
|
+
Uint8 b2 = (Uint8)(NUM2DBL(r_ary_entry(a, 11)) * 255);
|
|
1364
|
+
Uint8 a2 = (Uint8)(NUM2DBL(r_ary_entry(a, 12)) * 255);
|
|
1365
|
+
Uint8 r3 = (Uint8)(NUM2DBL(r_ary_entry(a, 13)) * 255);
|
|
1366
|
+
Uint8 g3 = (Uint8)(NUM2DBL(r_ary_entry(a, 14)) * 255);
|
|
1367
|
+
Uint8 b3 = (Uint8)(NUM2DBL(r_ary_entry(a, 15)) * 255);
|
|
1368
|
+
Uint8 a3 = (Uint8)(NUM2DBL(r_ary_entry(a, 16)) * 255);
|
|
1369
|
+
Uint8 r4 = (Uint8)(NUM2DBL(r_ary_entry(a, 17)) * 255);
|
|
1370
|
+
Uint8 g4 = (Uint8)(NUM2DBL(r_ary_entry(a, 18)) * 255);
|
|
1371
|
+
Uint8 b4 = (Uint8)(NUM2DBL(r_ary_entry(a, 19)) * 255);
|
|
1372
|
+
Uint8 a4 = (Uint8)(NUM2DBL(r_ary_entry(a, 20)) * 255);
|
|
1373
|
+
|
|
1374
|
+
float dash = (float)NUM2DBL(r_ary_entry(a, 21)) * scale;
|
|
1375
|
+
float gap = (float)NUM2DBL(r_ary_entry(a, 22)) * scale;
|
|
1376
|
+
|
|
1377
|
+
SDL_LockSurface(can->surface);
|
|
1378
|
+
|
|
1379
|
+
if (dash > 0.0f) {
|
|
1380
|
+
if (dash < 0.5f) dash = 0.5f;
|
|
1381
|
+
if (gap < 0.5f) gap = 0.5f;
|
|
1382
|
+
|
|
1383
|
+
float dx = x2 - x1;
|
|
1384
|
+
float dy = y2 - y1;
|
|
1385
|
+
float len = sqrtf(dx * dx + dy * dy);
|
|
1386
|
+
if (len >= 0.0001f) {
|
|
1387
|
+
float ux = dx / len;
|
|
1388
|
+
float uy = dy / len;
|
|
1389
|
+
|
|
1390
|
+
float t = 0.0f;
|
|
1391
|
+
int drawing = 1;
|
|
1392
|
+
while (t < len) {
|
|
1393
|
+
float seg = drawing ? dash : gap;
|
|
1394
|
+
if (t + seg > len) seg = len - t;
|
|
1395
|
+
|
|
1396
|
+
if (drawing) {
|
|
1397
|
+
float t1 = t / len;
|
|
1398
|
+
float t2 = (t + seg) / len;
|
|
1399
|
+
|
|
1400
|
+
// Top edge (c1->c4) and bottom edge (c2->c3) interpolated at t1,t2
|
|
1401
|
+
Uint8 sr1 = lerp_u8(r1, r4, t1), sg1 = lerp_u8(g1, g4, t1);
|
|
1402
|
+
Uint8 sb1 = lerp_u8(b1, b4, t1), sa1 = lerp_u8(a1, a4, t1);
|
|
1403
|
+
Uint8 er1 = lerp_u8(r1, r4, t2), eg1 = lerp_u8(g1, g4, t2);
|
|
1404
|
+
Uint8 eb1 = lerp_u8(b1, b4, t2), ea1 = lerp_u8(a1, a4, t2);
|
|
1405
|
+
|
|
1406
|
+
Uint8 sr2 = lerp_u8(r2, r3, t1), sg2 = lerp_u8(g2, g3, t1);
|
|
1407
|
+
Uint8 sb2 = lerp_u8(b2, b3, t1), sa2 = lerp_u8(a2, a3, t1);
|
|
1408
|
+
Uint8 er2 = lerp_u8(r2, r3, t2), eg2 = lerp_u8(g2, g3, t2);
|
|
1409
|
+
Uint8 eb2 = lerp_u8(b2, b3, t2), ea2 = lerp_u8(a2, a3, t2);
|
|
1410
|
+
|
|
1411
|
+
float sx1 = x1 + ux * t, sy1 = y1 + uy * t;
|
|
1412
|
+
float sx2 = x1 + ux * (t + seg), sy2 = y1 + uy * (t + seg);
|
|
1413
|
+
|
|
1414
|
+
canvas_draw_line_lerp_on_surface(can->surface, sx1, sy1, sx2, sy2, sw,
|
|
1415
|
+
sr1, sg1, sb1, sa1,
|
|
1416
|
+
sr2, sg2, sb2, sa2,
|
|
1417
|
+
er2, eg2, eb2, ea2,
|
|
1418
|
+
er1, eg1, eb1, ea1);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
t += seg;
|
|
1422
|
+
drawing = !drawing;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
} else {
|
|
1426
|
+
canvas_draw_line_lerp_on_surface(can->surface, x1, y1, x2, y2, sw,
|
|
1427
|
+
r1, g1, b1, a1,
|
|
1428
|
+
r2, g2, b2, a2,
|
|
1429
|
+
r3, g3, b3, a3,
|
|
1430
|
+
r4, g4, b4, a4);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
SDL_UnlockSurface(can->surface);
|
|
1434
|
+
{
|
|
1435
|
+
float hw = sw * 0.5f;
|
|
1436
|
+
canvas_mark_dirty(can, fminf(x1, x2) - hw, fminf(y1, y2) - hw,
|
|
1437
|
+
fmaxf(x1, x2) + hw, fmaxf(y1, y2) + hw);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
return R_TRUE;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
/*
|
|
1445
|
+
* Ruby2D::Canvas#ext_draw_lines
|
|
1446
|
+
* Arguments: [n, x1a, y1a, x2a, y2a, x1b, y1b, x2b, y2b, ..., stroke_width, r, g, b, a]
|
|
1447
|
+
* Draws n line segments with a single shared color and stroke width in one
|
|
1448
|
+
* FFI crossing.
|
|
1449
|
+
*/
|
|
1450
|
+
R_VAL ruby2d_ext_canvas_draw_lines(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1451
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1452
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_draw_lines expects 2 args, got %d", (int)argc);
|
|
1453
|
+
R_VAL obj = argv[0];
|
|
1454
|
+
R_VAL a = argv[1];
|
|
1455
|
+
|
|
1456
|
+
R2D_Canvas *can;
|
|
1457
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1458
|
+
|
|
1459
|
+
int n = (int)NUM2INT(r_ary_entry(a, 0));
|
|
1460
|
+
if (n < 1) return R_TRUE;
|
|
1461
|
+
|
|
1462
|
+
float scale = can->scale;
|
|
1463
|
+
int base = 1 + n * 4;
|
|
1464
|
+
float sw = (float)NUM2DBL(r_ary_entry(a, base)) * scale;
|
|
1465
|
+
float rf = (float)NUM2DBL(r_ary_entry(a, base + 1));
|
|
1466
|
+
float gf = (float)NUM2DBL(r_ary_entry(a, base + 2));
|
|
1467
|
+
float bf = (float)NUM2DBL(r_ary_entry(a, base + 3));
|
|
1468
|
+
float af = (float)NUM2DBL(r_ary_entry(a, base + 4));
|
|
1469
|
+
|
|
1470
|
+
Uint8 cr = (Uint8)(rf * 255);
|
|
1471
|
+
Uint8 cg = (Uint8)(gf * 255);
|
|
1472
|
+
Uint8 cb = (Uint8)(bf * 255);
|
|
1473
|
+
Uint8 ca = (Uint8)(af * 255);
|
|
1474
|
+
|
|
1475
|
+
if (ca == 0) return R_TRUE;
|
|
1476
|
+
|
|
1477
|
+
float hw = sw * 0.5f;
|
|
1478
|
+
SDL_LockSurface(can->surface);
|
|
1479
|
+
for (int i = 0; i < n; i++) {
|
|
1480
|
+
int o = 1 + i * 4;
|
|
1481
|
+
float x1 = (float)NUM2DBL(r_ary_entry(a, o)) * scale;
|
|
1482
|
+
float y1 = (float)NUM2DBL(r_ary_entry(a, o + 1)) * scale;
|
|
1483
|
+
float x2 = (float)NUM2DBL(r_ary_entry(a, o + 2)) * scale;
|
|
1484
|
+
float y2 = (float)NUM2DBL(r_ary_entry(a, o + 3)) * scale;
|
|
1485
|
+
canvas_draw_line_on_surface(can->surface, x1, y1, x2, y2, sw, cr, cg, cb, ca);
|
|
1486
|
+
canvas_mark_dirty(can, fminf(x1, x2) - hw, fminf(y1, y2) - hw,
|
|
1487
|
+
fmaxf(x1, x2) + hw, fmaxf(y1, y2) + hw);
|
|
1488
|
+
}
|
|
1489
|
+
SDL_UnlockSurface(can->surface);
|
|
1490
|
+
|
|
1491
|
+
return R_TRUE;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
|
|
1495
|
+
/*
|
|
1496
|
+
* Shared body for stroke_polygon/stroke_polyline. `vbase` is the array index
|
|
1497
|
+
* of the first vertex coordinate (the leading args differ between the two);
|
|
1498
|
+
* `closed` joins the last vertex back to the first.
|
|
1499
|
+
*/
|
|
1500
|
+
static R_VAL canvas_stroke_impl(R2D_Canvas *can, R_VAL a, int n, int vbase, int closed) {
|
|
1501
|
+
if (n < 2) return R_TRUE;
|
|
1502
|
+
|
|
1503
|
+
float scale = can->scale;
|
|
1504
|
+
// Small strokes use stack scratch; heap only past the threshold.
|
|
1505
|
+
float verts_stack[R2D_STROKE_STACK_N * 2];
|
|
1506
|
+
Uint8 colors_stack[R2D_STROKE_STACK_N * 4];
|
|
1507
|
+
float *verts;
|
|
1508
|
+
Uint8 *colors;
|
|
1509
|
+
bool heap = n > R2D_STROKE_STACK_N;
|
|
1510
|
+
|
|
1511
|
+
if (heap) {
|
|
1512
|
+
verts = (float *)malloc(n * 2 * sizeof(float));
|
|
1513
|
+
colors = (Uint8 *)malloc(n * 4 * sizeof(Uint8));
|
|
1514
|
+
if (!verts || !colors) { free(verts); free(colors); return R_TRUE; }
|
|
1515
|
+
} else {
|
|
1516
|
+
verts = verts_stack;
|
|
1517
|
+
colors = colors_stack;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
for (int i = 0; i < n; i++) {
|
|
1521
|
+
verts[i*2] = (float)NUM2DBL(r_ary_entry(a, vbase + i*2)) * scale;
|
|
1522
|
+
verts[i*2+1] = (float)NUM2DBL(r_ary_entry(a, vbase + 1 + i*2)) * scale;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
int base = vbase + n * 2;
|
|
1526
|
+
float sw = (float)NUM2DBL(r_ary_entry(a, base)) * scale;
|
|
519
1527
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
verts
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
1528
|
+
int has_any_alpha = 0;
|
|
1529
|
+
for (int i = 0; i < n; i++) {
|
|
1530
|
+
colors[i*4] = (Uint8)(NUM2DBL(r_ary_entry(a, base + 1 + i*4)) * 255);
|
|
1531
|
+
colors[i*4 + 1] = (Uint8)(NUM2DBL(r_ary_entry(a, base + 2 + i*4)) * 255);
|
|
1532
|
+
colors[i*4 + 2] = (Uint8)(NUM2DBL(r_ary_entry(a, base + 3 + i*4)) * 255);
|
|
1533
|
+
colors[i*4 + 3] = (Uint8)(NUM2DBL(r_ary_entry(a, base + 4 + i*4)) * 255);
|
|
1534
|
+
if (colors[i*4 + 3] > 0) has_any_alpha = 1;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
if (has_any_alpha) {
|
|
1538
|
+
SDL_LockSurface(can->surface);
|
|
1539
|
+
canvas_stroke_polyline_on_surface(can->surface, verts, n, closed, sw, colors);
|
|
1540
|
+
SDL_UnlockSurface(can->surface);
|
|
1541
|
+
// Miter joins extend past the vertices by up to hw * R2D_MITER_LIMIT
|
|
1542
|
+
canvas_mark_dirty_verts(can, verts, n, 0.5f * sw * R2D_MITER_LIMIT);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
if (heap) {
|
|
1546
|
+
free(verts);
|
|
1547
|
+
free(colors);
|
|
1548
|
+
}
|
|
1549
|
+
return R_TRUE;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
/*
|
|
1554
|
+
* Ruby2D::Canvas#ext_stroke_polygon
|
|
1555
|
+
* Arguments: [n, x1, y1, x2, y2, ..., stroke_width, r1,g1,b1,a1, ...rn,gn,bn,an]
|
|
1556
|
+
*/
|
|
1557
|
+
R_VAL ruby2d_ext_canvas_stroke_polygon(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1558
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1559
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_stroke_polygon expects 2 args, got %d", (int)argc);
|
|
1560
|
+
R_VAL obj = argv[0];
|
|
1561
|
+
R_VAL a = argv[1];
|
|
1562
|
+
|
|
1563
|
+
R2D_Canvas *can;
|
|
1564
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1565
|
+
|
|
1566
|
+
int n = (int)NUM2INT(r_ary_entry(a, 0));
|
|
1567
|
+
return canvas_stroke_impl(can, a, n, 1, 1);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
|
|
1571
|
+
/*
|
|
1572
|
+
* Ruby2D::Canvas#ext_stroke_polyline
|
|
1573
|
+
* Arguments: [closed, n, x1, y1, x2, y2, ..., stroke_width, r1,g1,b1,a1, ...rn,gn,bn,an]
|
|
1574
|
+
*/
|
|
1575
|
+
R_VAL ruby2d_ext_canvas_stroke_polyline(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1576
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1577
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_stroke_polyline expects 2 args, got %d", (int)argc);
|
|
1578
|
+
R_VAL obj = argv[0];
|
|
1579
|
+
R_VAL a = argv[1];
|
|
1580
|
+
|
|
1581
|
+
R2D_Canvas *can;
|
|
1582
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1583
|
+
|
|
1584
|
+
int closed = (int)NUM2INT(r_ary_entry(a, 0));
|
|
1585
|
+
int n = (int)NUM2INT(r_ary_entry(a, 1));
|
|
1586
|
+
return canvas_stroke_impl(can, a, n, 2, closed);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
/*
|
|
1591
|
+
* Ruby2D::Canvas#ext_draw_image
|
|
1592
|
+
* Blit an Image's SDL_Surface onto this Canvas's SDL_Surface.
|
|
1593
|
+
* Arguments: img_obj (Ruby2D::Image), a = [x, y, width, height]
|
|
1594
|
+
*/
|
|
1595
|
+
R_VAL ruby2d_ext_canvas_draw_image(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1596
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1597
|
+
if (argc != 3) r_raise("Ruby2D::Ext.canvas_draw_image expects 3 args (canvas, image, params), got %d", (int)argc);
|
|
1598
|
+
R_VAL obj = argv[0];
|
|
1599
|
+
R_VAL img_obj = argv[1];
|
|
1600
|
+
R_VAL a = argv[2];
|
|
1601
|
+
|
|
1602
|
+
R2D_Canvas *can;
|
|
1603
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1604
|
+
|
|
1605
|
+
// Get the R2D_Image struct from the Image object's @ext_image ivar. Guard
|
|
1606
|
+
// against a nil/absent handle (wrong object type, or a failed Image) before
|
|
1607
|
+
// dereferencing — r_data_ptr on a non-data value would otherwise crash.
|
|
1608
|
+
R_VAL ext_image_val = r_ivar_get(img_obj, id_ext_image);
|
|
1609
|
+
if (!r_test(ext_image_val)) {
|
|
1610
|
+
r_error("Canvas#draw_image requires a valid Ruby2D::Image");
|
|
1611
|
+
return R_NIL;
|
|
1612
|
+
}
|
|
1613
|
+
R2D_Image *img = (R2D_Image *)r_data_ptr(ext_image_val);
|
|
1614
|
+
|
|
1615
|
+
if (!img->surface) {
|
|
1616
|
+
r_error("Image surface is not available for canvas blitting");
|
|
1617
|
+
return R_NIL;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
float scale = can->scale;
|
|
1621
|
+
int dx = (int)(NUM2DBL(r_ary_entry(a, 0)) * scale);
|
|
1622
|
+
int dy = (int)(NUM2DBL(r_ary_entry(a, 1)) * scale);
|
|
1623
|
+
int dw = (int)(NUM2DBL(r_ary_entry(a, 2)) * scale);
|
|
1624
|
+
int dh = (int)(NUM2DBL(r_ary_entry(a, 3)) * scale);
|
|
1625
|
+
|
|
1626
|
+
// Nothing to draw for a degenerate (zero/negative) destination rect.
|
|
1627
|
+
if (dw <= 0 || dh <= 0) return R_TRUE;
|
|
1628
|
+
|
|
1629
|
+
SDL_Rect dst_rect = { dx, dy, dw, dh };
|
|
1630
|
+
|
|
1631
|
+
SDL_SetSurfaceBlendMode(img->surface, SDL_BLENDMODE_BLEND);
|
|
1632
|
+
SDL_BlitSurfaceScaled(img->surface, NULL, can->surface, &dst_rect, SDL_SCALEMODE_LINEAR);
|
|
1633
|
+
|
|
1634
|
+
canvas_mark_dirty(can, (float)dx, (float)dy, (float)(dx + dw), (float)(dy + dh));
|
|
1635
|
+
|
|
1636
|
+
return R_TRUE;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
|
|
1640
|
+
/*
|
|
1641
|
+
* Ruby2D::Canvas#ext_draw_text
|
|
1642
|
+
* Blit a Text's SDL_Surface onto this Canvas's SDL_Surface with color modulation.
|
|
1643
|
+
* Arguments: txt_obj (Ruby2D::Text), a = [x, y, width, height, r, g, b, a]
|
|
1644
|
+
*/
|
|
1645
|
+
R_VAL ruby2d_ext_canvas_draw_text(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1646
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1647
|
+
if (argc != 3) r_raise("Ruby2D::Ext.canvas_draw_text expects 3 args (canvas, text, params), got %d", (int)argc);
|
|
1648
|
+
R_VAL obj = argv[0];
|
|
1649
|
+
R_VAL txt_obj = argv[1];
|
|
1650
|
+
R_VAL a = argv[2];
|
|
1651
|
+
|
|
1652
|
+
R2D_Canvas *can;
|
|
1653
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1654
|
+
|
|
1655
|
+
// Get the R2D_Text struct from the Text object's @ext_text ivar. Guard
|
|
1656
|
+
// against a nil/absent handle (wrong object type, or a failed Text) before
|
|
1657
|
+
// dereferencing — r_data_ptr on a non-data value would otherwise crash.
|
|
1658
|
+
R_VAL ext_text_val = r_ivar_get(txt_obj, id_ext_text);
|
|
1659
|
+
if (!r_test(ext_text_val)) {
|
|
1660
|
+
r_error("Canvas#draw_text requires a valid Ruby2D::Text");
|
|
1661
|
+
return R_NIL;
|
|
1662
|
+
}
|
|
1663
|
+
R2D_Text *txt = (R2D_Text *)r_data_ptr(ext_text_val);
|
|
1664
|
+
|
|
1665
|
+
if (txt->empty) return R_TRUE; // empty content: nothing to blit
|
|
1666
|
+
|
|
1667
|
+
if (!txt->surface) {
|
|
1668
|
+
r_error("Text surface is not available for canvas blitting");
|
|
1669
|
+
return R_NIL;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
float scale = can->scale;
|
|
1673
|
+
int dx = (int)(NUM2DBL(r_ary_entry(a, 0)) * scale);
|
|
1674
|
+
int dy = (int)(NUM2DBL(r_ary_entry(a, 1)) * scale);
|
|
1675
|
+
int dw = (int)(NUM2DBL(r_ary_entry(a, 2)) * scale);
|
|
1676
|
+
int dh = (int)(NUM2DBL(r_ary_entry(a, 3)) * scale);
|
|
1677
|
+
|
|
1678
|
+
// Nothing to draw for a degenerate (zero/negative) destination rect.
|
|
1679
|
+
if (dw <= 0 || dh <= 0) return R_TRUE;
|
|
1680
|
+
|
|
1681
|
+
float rf = (float)NUM2DBL(r_ary_entry(a, 4));
|
|
1682
|
+
float gf = (float)NUM2DBL(r_ary_entry(a, 5));
|
|
1683
|
+
float bf = (float)NUM2DBL(r_ary_entry(a, 6));
|
|
1684
|
+
float af = (float)NUM2DBL(r_ary_entry(a, 7));
|
|
1685
|
+
|
|
1686
|
+
// Apply color/alpha modulation to tint the white text surface
|
|
1687
|
+
SDL_SetSurfaceColorMod(txt->surface, (Uint8)(rf * 255), (Uint8)(gf * 255), (Uint8)(bf * 255));
|
|
1688
|
+
SDL_SetSurfaceAlphaMod(txt->surface, (Uint8)(af * 255));
|
|
1689
|
+
SDL_SetSurfaceBlendMode(txt->surface, SDL_BLENDMODE_BLEND);
|
|
1690
|
+
|
|
1691
|
+
SDL_Rect dst_rect = { dx, dy, dw, dh };
|
|
1692
|
+
SDL_BlitSurfaceScaled(txt->surface, NULL, can->surface, &dst_rect, SDL_SCALEMODE_LINEAR);
|
|
1693
|
+
|
|
1694
|
+
// Reset color/alpha mod to defaults so GPU rendering is unaffected
|
|
1695
|
+
SDL_SetSurfaceColorMod(txt->surface, 255, 255, 255);
|
|
1696
|
+
SDL_SetSurfaceAlphaMod(txt->surface, 255);
|
|
1697
|
+
|
|
1698
|
+
canvas_mark_dirty(can, (float)dx, (float)dy, (float)(dx + dw), (float)(dy + dh));
|
|
1699
|
+
|
|
1700
|
+
return R_TRUE;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
|
|
1704
|
+
/*
|
|
1705
|
+
* Ruby2D::Canvas#ext_clear
|
|
1706
|
+
* Arguments: [r, g, b, a, x, y, width, height]
|
|
1707
|
+
* Resets pixels on the surface to the given color (no blending).
|
|
1708
|
+
* When x/y are 0 and width/height match the surface, clears the entire canvas.
|
|
1709
|
+
*/
|
|
1710
|
+
R_VAL ruby2d_ext_canvas_clear(RUBY2D_METHOD_ARGS_VARIADIC) {
|
|
1711
|
+
RUBY2D_EXTRACT_VARIADIC;
|
|
1712
|
+
if (argc != 2) r_raise("Ruby2D::Ext.canvas_clear expects 2 args, got %d", (int)argc);
|
|
1713
|
+
R_VAL obj = argv[0];
|
|
1714
|
+
R_VAL a = argv[1];
|
|
1715
|
+
|
|
1716
|
+
R2D_Canvas *can;
|
|
1717
|
+
obj_struct(obj, id_ext_canvas, R2D_Canvas, can);
|
|
1718
|
+
|
|
1719
|
+
Uint8 cr = (Uint8)((float)NUM2DBL(r_ary_entry(a, 0)) * 255);
|
|
1720
|
+
Uint8 cg = (Uint8)((float)NUM2DBL(r_ary_entry(a, 1)) * 255);
|
|
1721
|
+
Uint8 cb = (Uint8)((float)NUM2DBL(r_ary_entry(a, 2)) * 255);
|
|
1722
|
+
Uint8 ca = (Uint8)((float)NUM2DBL(r_ary_entry(a, 3)) * 255);
|
|
1723
|
+
|
|
1724
|
+
float scale = can->scale;
|
|
1725
|
+
int rx = (int)(NUM2DBL(r_ary_entry(a, 4)) * scale);
|
|
1726
|
+
int ry = (int)(NUM2DBL(r_ary_entry(a, 5)) * scale);
|
|
1727
|
+
int rw = (int)(NUM2DBL(r_ary_entry(a, 6)) * scale);
|
|
1728
|
+
int rh = (int)(NUM2DBL(r_ary_entry(a, 7)) * scale);
|
|
1729
|
+
|
|
1730
|
+
Uint32 color = SDL_MapSurfaceRGBA(can->surface, cr, cg, cb, ca);
|
|
1731
|
+
|
|
1732
|
+
// If the rect covers the entire surface, pass NULL for a full clear
|
|
1733
|
+
if (rx == 0 && ry == 0 && rw == can->surface->w && rh == can->surface->h) {
|
|
1734
|
+
SDL_FillSurfaceRect(can->surface, NULL, color);
|
|
1735
|
+
} else {
|
|
1736
|
+
SDL_Rect rect = { rx, ry, rw, rh };
|
|
1737
|
+
SDL_FillSurfaceRect(can->surface, &rect, color);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
canvas_mark_dirty(can, (float)rx, (float)ry, (float)(rx + rw), (float)(ry + rh));
|
|
1741
|
+
|
|
1742
|
+
return R_TRUE;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
|
|
1746
|
+
/*
|
|
1747
|
+
* Free canvas
|
|
1748
|
+
*/
|
|
1749
|
+
static void R2D_Canvas_free(void *p) {
|
|
1750
|
+
if (!p) return;
|
|
1751
|
+
R2D_Canvas *can = (R2D_Canvas *)p;
|
|
1752
|
+
if (can->surface) {
|
|
1753
|
+
SDL_DestroySurface(can->surface);
|
|
1754
|
+
can->surface = NULL;
|
|
1755
|
+
}
|
|
1756
|
+
if (can->texture) {
|
|
1757
|
+
if (R2D_RendererAlive()) SDL_DestroyTexture(can->texture);
|
|
1758
|
+
can->texture = NULL;
|
|
539
1759
|
}
|
|
1760
|
+
xfree(can);
|
|
540
1761
|
}
|