Repository: revery-ui/revery
Branch: master
Commit: 141f70f69d6a
Files: 471
Total size: 1.4 MB
Directory structure:
gitextract_o1re1h8t/
├── .ci/
│ ├── .gitattributes
│ ├── create-docs.yml
│ ├── esy-bench.yml
│ ├── esy-build-steps.yml
│ ├── esy-check-hygiene.yml
│ ├── format.sh
│ ├── publish-build-cache.yml
│ ├── publish-release.yml
│ ├── restore-build-cache.yml
│ └── use-node.yml
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── Revery.opam
├── ReveryBench.opam
├── ReveryExampleJs.opam
├── ReveryExamples.opam
├── ReveryTest.opam
├── ThirdPartyLicenses.txt
├── azure-pipelines.yml
├── bench/
│ ├── exe/
│ │ ├── Bench.re
│ │ └── dune
│ └── lib/
│ ├── BenchFramework.re
│ ├── DrawBench.re
│ ├── LayoutBench.re
│ ├── NodeUtility.re
│ ├── PaintBench.re
│ ├── RecalculateBench.re
│ ├── SurfaceUtility.re
│ ├── ViewNodeBench.re
│ └── dune
├── bench.json
├── doc.json
├── dune
├── dune-project
├── dune-workspace
├── examples/
│ ├── AnalogClock.re
│ ├── Border.re
│ ├── Boxshadow.re
│ ├── Calculator.re
│ ├── CanQuitExample.re
│ ├── CanvasExample.re
│ ├── CheckboxExample.re
│ ├── DefaultButton.re
│ ├── DropdownExample.re
│ ├── Examples.re
│ ├── FileDragAndDrop.re
│ ├── Flexbox.re
│ ├── FocusExample.re
│ ├── FontAwesome5FreeSolid.otf
│ ├── FontsExample.re
│ ├── GameOfLife.re
│ ├── Hello.re
│ ├── HitTests.re
│ ├── HoverExample.re
│ ├── ImageQualityExample.re
│ ├── Info.plist
│ ├── InputExample.re
│ ├── LayerExample.re
│ ├── MarkdownExample.re
│ ├── NativeFileExample.re
│ ├── NativeIconExample.re
│ ├── NativeInputExample.re
│ ├── NativeMenuExample.re
│ ├── NativeNotificationExample.re
│ ├── NestedClickable.re
│ ├── RadioButtonExample.re
│ ├── RichTextExample.re
│ ├── SVGExample.re
│ ├── ScreenCapture.re
│ ├── ScrollView.re
│ ├── Slider.re
│ ├── SpringExample.re
│ ├── Stopwatch.re
│ ├── TextExample.re
│ ├── TodoExample.re
│ ├── TreeView.re
│ ├── URLFileOpen.re
│ ├── WavFilePlaybackExample.re
│ ├── WindowControl.re
│ ├── ZoomExample.re
│ ├── dune
│ ├── gl-matrix-min.js
│ ├── index.html
│ └── stubs/
│ ├── ExampleStubs.re
│ ├── dune
│ ├── example_stubs.c
│ └── example_stubs.js
├── examples.json
├── include/
│ ├── COPYING.txt
│ ├── KHR/
│ │ └── khrplatform.h
│ ├── glad/
│ │ └── glad.h
│ └── stb_image.h
├── js.json
├── lsan.supp
├── package.json
├── packages/
│ ├── reason-harfbuzz/
│ │ ├── examples/
│ │ │ └── harfbuzz-cli/
│ │ │ ├── HarfbuzzCli.re
│ │ │ ├── dune
│ │ │ └── run-harfbuzz.sh
│ │ ├── src/
│ │ │ ├── Harfbuzz.re
│ │ │ ├── Harfbuzz.rei
│ │ │ ├── config/
│ │ │ │ ├── discover.re
│ │ │ │ └── dune
│ │ │ ├── dune
│ │ │ └── harfbuzz.cpp
│ │ └── test/
│ │ ├── FeaturesTest.re
│ │ ├── ShapingTest.re
│ │ ├── TestFramework.re
│ │ └── dune
│ ├── reason-sdl2/
│ │ └── src/
│ │ ├── Float32Array.re
│ │ ├── Uint16Array.re
│ │ ├── config/
│ │ │ ├── discover.re
│ │ │ └── dune
│ │ ├── dune
│ │ ├── sdl2.re
│ │ ├── sdl2_stubs.js
│ │ ├── sdl2_wrapper.cpp
│ │ └── stb_image.cpp
│ ├── reason-skia/
│ │ ├── bench/
│ │ │ ├── BenchFramework.re
│ │ │ ├── CanvasBench.re
│ │ │ ├── ColorBench.re
│ │ │ ├── MatrixBench.re
│ │ │ ├── RectBench.re
│ │ │ ├── SkiaPaintBench.re
│ │ │ └── dune
│ │ ├── examples/
│ │ │ ├── skia-cli/
│ │ │ │ ├── LICENSE_FiraCode.txt
│ │ │ │ ├── LICENSE_ORBITRON.md
│ │ │ │ ├── SkiaCli.re
│ │ │ │ └── dune
│ │ │ ├── skia-font-manager-cli/
│ │ │ │ ├── SkiaFontManagerCli.re
│ │ │ │ ├── dune
│ │ │ │ └── run-skia-fontmanager.sh
│ │ │ └── skia-sdl2/
│ │ │ ├── SkiaSdl.re
│ │ │ └── dune
│ │ ├── src/
│ │ │ ├── Skia.re
│ │ │ ├── Skia.rei
│ │ │ ├── config/
│ │ │ │ ├── discover.re
│ │ │ │ └── dune
│ │ │ ├── dune
│ │ │ └── wrapped/
│ │ │ ├── bindings/
│ │ │ │ ├── SkiaWrappedBindings.re
│ │ │ │ └── dune
│ │ │ ├── c/
│ │ │ │ ├── c_stubs.c
│ │ │ │ ├── c_stubs.h
│ │ │ │ └── dune
│ │ │ ├── lib/
│ │ │ │ ├── SkiaWrapped.re
│ │ │ │ ├── dune
│ │ │ │ └── raw_bindings.c
│ │ │ ├── stubgen/
│ │ │ │ ├── dune
│ │ │ │ ├── stubgen.ml
│ │ │ │ └── types_stubgen.ml
│ │ │ └── types/
│ │ │ ├── SkiaWrappedTypes.re
│ │ │ └── dune
│ │ └── test/
│ │ ├── ColorTest.re
│ │ ├── MatrixTest.re
│ │ ├── PaintTest.re
│ │ ├── RectTest.re
│ │ ├── TestFramework.re
│ │ └── dune
│ ├── revery-text-wrap/
│ │ ├── examples/
│ │ │ └── text-wrap-cli/
│ │ │ ├── ReveryTextWrapCli.re
│ │ │ └── dune
│ │ └── src/
│ │ ├── Revery_TextWrap.re
│ │ ├── Revery_TextWrap.rei
│ │ ├── Tokenize.re
│ │ └── dune
│ └── zed/
│ └── src/
│ ├── dune
│ ├── zed_utf8.ml
│ └── zed_utf8.mli
├── reason-harfbuzz.opam
├── reason-sdl2.opam
├── reason-skia.opam
├── scripts/
│ ├── docker/
│ │ ├── archlinux/
│ │ │ └── Dockerfile
│ │ └── centos/
│ │ └── Dockerfile
│ ├── docker-build.sh
│ ├── make-binary-file.js
│ └── release.sh
├── src/
│ ├── Core/
│ │ ├── App.re
│ │ ├── App.rei
│ │ ├── Color.re
│ │ ├── Color.rei
│ │ ├── Colors.re
│ │ ├── Environment.re
│ │ ├── Environment.rei
│ │ ├── Event.re
│ │ ├── Events.re
│ │ ├── GarbageCollector.re
│ │ ├── Key.re
│ │ ├── Key.rei
│ │ ├── Log.re
│ │ ├── Log.rei
│ │ ├── MouseButton.re
│ │ ├── MouseButton.rei
│ │ ├── MouseCursors.re
│ │ ├── MouseCursors.rei
│ │ ├── Performance.re
│ │ ├── Revery_Core.re
│ │ ├── TextOverflow.re
│ │ ├── TextWrapping.re
│ │ ├── Tick.re
│ │ ├── Time.re
│ │ ├── Time.rei
│ │ ├── UniqueId.re
│ │ ├── Vsync.re
│ │ ├── Window.re
│ │ ├── Window.rei
│ │ ├── WindowCreateOptions.re
│ │ ├── WindowStyles.re
│ │ └── dune
│ ├── Draw/
│ │ ├── CanvasContext.re
│ │ ├── DebugDraw.re
│ │ ├── ImageResizeMode.re
│ │ ├── Revery_Draw.re
│ │ ├── Text.re
│ │ ├── Text.rei
│ │ └── dune
│ ├── Font/
│ │ ├── Discovery.re
│ │ ├── Feature.re
│ │ ├── Feature.rei
│ │ ├── Features.re
│ │ ├── Features.rei
│ │ ├── FontCache.re
│ │ ├── FontCache.rei
│ │ ├── FontFamily.re
│ │ ├── FontFamily.rei
│ │ ├── FontManager.re
│ │ ├── FontMetrics.re
│ │ ├── FontRenderer.re
│ │ ├── FontWeight.re
│ │ ├── FontWidth.re
│ │ ├── Revery_Font.re
│ │ ├── ShapeResult.re
│ │ ├── Smoothing.re
│ │ ├── dune
│ │ └── files/
│ │ └── Inconsolata.otf
│ ├── IO/
│ │ ├── File.re
│ │ ├── File.rei
│ │ ├── Image.re
│ │ ├── Image.rei
│ │ ├── LwtLetOperators.re
│ │ ├── Revery_IO.re
│ │ └── dune
│ ├── Lwt/
│ │ ├── Revery_Lwt.re
│ │ └── dune
│ ├── Math/
│ │ ├── Angle.re
│ │ ├── BoundingBox2d.re
│ │ ├── BoundingBox2d.rei
│ │ ├── Revery_Math.re
│ │ └── dune
│ ├── Native/
│ │ ├── Dialog.re
│ │ ├── Environment.re
│ │ ├── Gtk.re
│ │ ├── Icon.re
│ │ ├── Initialization.re
│ │ ├── Input.re
│ │ ├── Input.rei
│ │ ├── Locale.re
│ │ ├── Menu.re
│ │ ├── Menu.rei
│ │ ├── NSObject.re
│ │ ├── NSView.re
│ │ ├── Notification.re
│ │ ├── ReveryCocoa.h
│ │ ├── ReveryGtk.h
│ │ ├── ReveryLinux.h
│ │ ├── ReveryMac.h
│ │ ├── ReveryNSObject.c
│ │ ├── ReveryWin32.h
│ │ ├── ReveryWindows.h
│ │ ├── Revery_Native.c
│ │ ├── Revery_Native.re
│ │ ├── Shell.re
│ │ ├── Window.re
│ │ ├── caml_values.h
│ │ ├── cocoa/
│ │ │ ├── ReveryAppDelegate.c
│ │ │ ├── ReveryAppDelegate.h
│ │ │ ├── ReveryAppDelegate_func.c
│ │ │ ├── ReveryAppDelegate_func.h
│ │ │ ├── ReveryButtonTarget.c
│ │ │ ├── ReveryButtonTarget.h
│ │ │ ├── ReveryMenuItemTarget.c
│ │ │ ├── ReveryMenuItemTarget.h
│ │ │ ├── ReveryNSView.c
│ │ │ ├── ReveryNSViewCoords.c
│ │ │ ├── ReveryNSViewCoords.h
│ │ │ ├── ReveryProgressBar.c
│ │ │ ├── ReveryProgressBar.h
│ │ │ └── SDLAppDelegate.h
│ │ ├── config/
│ │ │ ├── discover.re
│ │ │ └── dune
│ │ ├── dialog.c
│ │ ├── dialog.js
│ │ ├── dialog_cocoa.c
│ │ ├── dialog_gtk.c
│ │ ├── dialog_win32.c
│ │ ├── dune
│ │ ├── environment.c
│ │ ├── environment_linux.c
│ │ ├── environment_mac.c
│ │ ├── environment_windows.c
│ │ ├── gtk/
│ │ │ ├── ReveryGtk.c
│ │ │ └── ReveryGtk_Widget.c
│ │ ├── icon.c
│ │ ├── icon_cocoa.c
│ │ ├── icon_win32.c
│ │ ├── input.c
│ │ ├── input_cocoa.c
│ │ ├── locale.c
│ │ ├── locale_cocoa.c
│ │ ├── locale_win32.c
│ │ ├── menu.c
│ │ ├── menu.h
│ │ ├── menu_cocoa.c
│ │ ├── notification.c
│ │ ├── notification_cocoa.c
│ │ ├── shell.c
│ │ ├── shell_cocoa.c
│ │ ├── shell_gtk.c
│ │ ├── shell_win32.c
│ │ ├── utilities.c
│ │ ├── utilities.h
│ │ ├── win32_target.h
│ │ ├── window.c
│ │ └── window_cocoa.c
│ ├── Platform.re
│ ├── Revery.re
│ ├── UI/
│ │ ├── Animation.re
│ │ ├── Animation.rei
│ │ ├── CanvasNode.re
│ │ ├── Container.re
│ │ ├── Dimensions.re
│ │ ├── Easing.re
│ │ ├── FileDrop.re
│ │ ├── Focus.re
│ │ ├── Focus.rei
│ │ ├── HitTest.re
│ │ ├── ImageNode.re
│ │ ├── ImageResizeMode.re
│ │ ├── Keyboard.re
│ │ ├── LayerNode.re
│ │ ├── Layout.re
│ │ ├── Mouse.re
│ │ ├── Mouse.rei
│ │ ├── NativeButtonNode.re
│ │ ├── Node.re
│ │ ├── NodeDrawContext.re
│ │ ├── NodeEvents.re
│ │ ├── Offset.re
│ │ ├── Overflow.re
│ │ ├── React.re
│ │ ├── Reconciler.re
│ │ ├── Render.re
│ │ ├── RenderCondition.re
│ │ ├── RenderContainer.re
│ │ ├── Revery_UI.re
│ │ ├── RichText.re
│ │ ├── RichText.rei
│ │ ├── Selector.re
│ │ ├── Spring.re
│ │ ├── Spring.rei
│ │ ├── Style.re
│ │ ├── TextNode.re
│ │ ├── Transform.re
│ │ ├── Transform.rei
│ │ ├── Ui.re
│ │ ├── Ui.rei
│ │ ├── UiEvents.re
│ │ ├── ViewNode.re
│ │ └── dune
│ ├── UI_Components/
│ │ ├── Button.re
│ │ ├── Button.rei
│ │ ├── Center.re
│ │ ├── Checkbox.re
│ │ ├── Checkbox.rei
│ │ ├── Clickable.re
│ │ ├── Clickable.rei
│ │ ├── ClickableText.re
│ │ ├── ClipContainer.re
│ │ ├── ClipContainer.rei
│ │ ├── Column.re
│ │ ├── Container.re
│ │ ├── Container.rei
│ │ ├── Dropdown.re
│ │ ├── ExpandContainer.re
│ │ ├── Input.re
│ │ ├── Link.re
│ │ ├── Markdown.re
│ │ ├── Markdown.rei
│ │ ├── Positioned.re
│ │ ├── Positioned.rei
│ │ ├── RadioButtons.re
│ │ ├── Revery_UI_Components.re
│ │ ├── RichTextView.re
│ │ ├── Row.re
│ │ ├── SVG.re
│ │ ├── ScrollView.re
│ │ ├── Slider.re
│ │ ├── Stack.re
│ │ ├── Ticker.re
│ │ ├── Ticker.rei
│ │ ├── Tree.re
│ │ └── dune
│ ├── UI_Hooks/
│ │ ├── Effect.re
│ │ ├── Effect.rei
│ │ ├── Reducer.re
│ │ ├── Reducer.rei
│ │ ├── Ref.re
│ │ ├── Ref.rei
│ │ ├── Revery_UI_Hooks.re
│ │ ├── Spring.re
│ │ ├── State.re
│ │ ├── State.rei
│ │ ├── Tick.re
│ │ ├── Tick.rei
│ │ ├── Timer.re
│ │ └── dune
│ ├── UI_Primitives/
│ │ ├── AllowPointer.re
│ │ ├── BoxShadow.re
│ │ ├── Canvas.re
│ │ ├── IgnorePointer.re
│ │ ├── Image.re
│ │ ├── Image.rei
│ │ ├── Layer.re
│ │ ├── NativeButton.re
│ │ ├── Opacity.re
│ │ ├── Padding.re
│ │ ├── PrimitiveNodeFactory.re
│ │ ├── Revery_UI_Primitives.re
│ │ ├── Text.re
│ │ ├── View.re
│ │ └── dune
│ ├── Utility/
│ │ ├── HeadlessWindow.re
│ │ ├── HeadlessWindow.rei
│ │ ├── Revery_Utility.re
│ │ └── dune
│ ├── dune
│ └── index.mld
├── test/
│ ├── Core/
│ │ ├── ColorTests.re
│ │ ├── EnvironmentTests.re
│ │ ├── TestFramework.re
│ │ ├── TextTests.re
│ │ ├── TickTest.re
│ │ └── dune
│ ├── Font/
│ │ ├── FamilyTest.re
│ │ ├── FontCacheTest.re
│ │ ├── TestFramework.re
│ │ └── dune
│ ├── Math/
│ │ ├── BoundingBox2dTests.re
│ │ ├── ClampTest.re
│ │ ├── InterpolateTest.re
│ │ ├── TestFramework.re
│ │ └── dune
│ ├── TestRunner.re
│ ├── UI/
│ │ ├── AnimationTest.re
│ │ ├── HooksTest.re
│ │ ├── MouseTest.re
│ │ ├── NodeTests.re
│ │ ├── ReconcilerTests.re
│ │ ├── StyleTest.re
│ │ ├── TestFramework.re
│ │ └── dune
│ ├── collateral/
│ │ ├── FiraCode-LICENSE.txt
│ │ ├── JetBrainsMono-LICENSE.txt
│ │ ├── dune
│ │ └── test-asset.txt
│ └── dune
├── test.json
└── update-lockfiles.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .ci/.gitattributes
================================================
# Declare shell files to have LF endings on checkout
# On Windows, the default git setting for `core.autocrlf`
# means that when checking out code, LF endings get converted
# to CRLF. This causes problems for shell scripts, as bash
# gets choked up on the extra `\r` character.
*.sh text eol=lf
================================================
FILE: .ci/create-docs.yml
================================================
# These steps are only run on Linux
steps:
- script: "esy '@doc' install"
displayName: "Install doc dependency"
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- script: "esy '@doc' build"
displayName: "Build docs"
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- script: echo '##vso[task.setvariable variable=docsPath]'$(esy '@doc' echo '#{self.target_dir}/default/_doc/_html')
displayName: "Save docsPath in variable"
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- task: PublishBuildArtifacts@1
displayName: "Publish Artifact: Docs"
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
inputs:
PathtoPublish: $(docsPath)
ArtifactName: Docs
================================================
FILE: .ci/esy-bench.yml
================================================
# Run benchmarks
steps:
- script: esy @bench install
displayName: 'esy @bench install'
- script: esy @bench build
displayName: 'esy @bench build'
- script: esy @bench x ReveryBench
displayName: 'esy @bench x ReveryBench'
================================================
FILE: .ci/esy-build-steps.yml
================================================
# Cross-platform set of build steps for building esy projects
steps:
- script: npm install -g esy@0.6.7
displayName: 'npm install -g esy@0.6.7'
- script: esy install
displayName: 'esy install'
- script: esy build
displayName: 'esy build'
- script: esy @test install
displayName: 'esy @test install'
- script: esy @test run
displayName: 'esy @test run'
- script: esy @examples install
displayName: 'esy @examples install'
- script: esy @examples run-harfbuzz
displayName: 'esy @examples run-harfbuzz'
- script: esy @examples x SkiaCli
displayName: '@esy @examples x SkiaCli'
- script: esy @examples x SkiaCli.bc
displayName: '@esy @examples x SkiaCli.bc'
- script: esy @examples run-skia-fontmanager
displayName: 'esy @examples run-skia-fontmanager'
- script: esy @examples x ReveryTextWrapCli
displayName: 'esy @examples x ReveryTextWrapCli'
- script: esy @examples x ReveryTextWrapCli.bc
displayName: 'esy @examples x ReveryTextWrapCli.bc'
================================================
FILE: .ci/esy-check-hygiene.yml
================================================
# Cross-platform set of build steps for building esy projects
steps:
- script: npm install -g esy@0.6.7
displayName: 'npm install -g esy@0.6.7'
- script: esy install
displayName: 'esy install'
- script: git diff --exit-code
displayName: 'check that `esy.lock` is up-to-date. If this fails, commit `esy.lock` changes and re-submit PR.'
- script: esy @bench install
displayName: 'esy @bench install'
- script: git diff --exit-code
displayName: 'check that `bench.esy.lock` is up-to-date. If this fails, commit `bench.esy.lock` changes and re-submit PR.'
- script: esy @js install
displayName: 'esy @js install'
- script: git diff --exit-code
displayName: 'check that `js.esy.lock` is up-to-date. If this fails, commit `js.esy.lock` changes and re-submit PR.'
- script: esy @test install
displayName: 'esy @test install'
- script: git diff --exit-code
displayName: 'check that `test.esy.lock` is up-to-date. If this fails, commit `test.esy.lock` changes and re-submit PR.'
- script: esy @doc install
displayName: 'esy @doc install'
- script: git diff --exit-code
displayName: 'check that `doc.esy.lock` is up-to-date. If this fails, commit `doc.esy.lock` changes and re-submit PR.'
- script: esy build
displayName: 'esy build'
- script: esy format
displayName: esy format
- script: git diff --exit-code
displayName: 'check that formatting is correct. If this fails, run `esy format` and re-submit PR.'
- script: esy b dune build @check
displayName: 'esy b dune build @check'
================================================
FILE: .ci/format.sh
================================================
#!/usr/bin/env bash
OS=$1
dune build @fmt --auto-promote
caml_output=$?
if [[ $OS == "windows" ]]
then
files=$(/usr/bin/find $(pwd) -type f \( -iname \*.c -o -iname \*.h -o -iname \*.cpp \) -not -path "*_esy/*")
native_output=$(astyle -n -Q --style=java --s4 $(cygpath -w $files))
elif [[ $OS == "darwin" ]]
then
native_output=$(find -E . -regex '.*\.(c|h|cpp)' -type f -not -path "*_esy/*" -exec astyle -n -Q --style=java --s4 {} \;)
elif [[ $OS == "linux" ]]
then
native_output=$(find ./ -type f \( -iname \*.c -o -iname \*.h -o -iname \*.cpp \) -not -path "*_esy/*" -exec astyle -n -Q --style=java --s4 {} \;)
fi
if [[ $native_output ]]
then
printf "\nFormatted the following native stubs:\n%s\n" "$native_output"
fi
if [[ $caml_output != 0 ]] || [[ $native_output != "" ]]
then
exit 1
fi
================================================
FILE: .ci/publish-build-cache.yml
================================================
# Steps for publishing project cache
steps:
- bash: 'mkdir -p $(STAGING_DIRECTORY)'
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: '[Cache][Publish] Create cache directory'
- bash: |
cd $(ESY__CACHE_INSTALL_PATH)
pwd
STDIR=$STAGING_DIRECTORY
if [ "$AGENT_OS" == "Windows_NT" ]; then
STDIR=$( cygpath --unix --absolute "$STAGING_DIRECTORY")
fi
echo "STDIR: $STDIR"
tar -czf "$STDIR/esy-cache.tar" .
workingDirectory: ''
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: '[Cache][Publish] Tar esy cache directory'
# - bash: 'cd $(ESY__NPM_ROOT) && tar -czf $(STAGING_DIRECTORY)/npm-cache.tar .'
# condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
# displayName: '[Cache][Publish] Tar npm cache directory'
- task: PublishBuildArtifacts@1
displayName: '[Cache][Publish] Upload tarball'
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
pathToPublish: '$(STAGING_DIRECTORY)'
artifactName: 'cache-$(Agent.OS)-install'
parallel: true
parallelCount: 8
================================================
FILE: .ci/publish-release.yml
================================================
# Steps for publishing project cache
steps:
- task: PublishBuildArtifacts@1
displayName: '[Release]'
inputs:
pathToPublish: '_release'
artifactName: 'release-$(Agent.OS)'
parallel: true
parallelCount: 8
================================================
FILE: .ci/restore-build-cache.yml
================================================
# Steps for restoring project cache
steps:
- task: DownloadBuildArtifacts@0
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: '[Cache][Restore] Restore install'
inputs:
buildType: 'specific'
project: '$(System.TeamProject)'
pipeline: '$(Build.DefinitionName)'
branchName: 'refs/heads/master'
buildVersionToDownload: 'latestFromBranch'
downloadType: 'single'
artifactName: 'cache-$(Agent.OS)-install'
downloadPath: '$(STAGING_DIRECTORY)'
continueOnError: true
- bash: 'mkdir -p $(ESY__CACHE_INSTALL_PATH)'
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: '[Cache][Restore] Create cache directory'
# - bash: 'cd $(ESY__NPM_ROOT) && tar -xf $(STAGING_DIRECTORY)/cache-$(Agent.OS)-install/npm-cache.tar -C .'
# continueOnError: true
# condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))
# displayName: '[Cache][Restore] Untar npm cache directory'
- bash: |
cd $(ESY__CACHE_INSTALL_PATH)
pwd
STDIR=$STAGING_DIRECTORY
if [ "$AGENT_OS" == "Windows_NT" ]; then
STDIR=$( cygpath --unix --absolute "$STAGING_DIRECTORY")
fi
echo "STDIR: $STDIR"
tar -xf "$STDIR/cache-$(Agent.OS)-install/esy-cache.tar" -C .
continueOnError: true
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: '[Cache][Restore] Untar esy cache directory'
- bash: 'rm -rf *'
continueOnError: true
workingDirectory: '$(STAGING_DIRECTORY)'
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))
displayName: '[Cache][Restore] Clean up'
================================================
FILE: .ci/use-node.yml
================================================
steps:
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: 12.x
================================================
FILE: .gitattributes
================================================
# Declare shell files to have LF endings on checkout
# On Windows, the default git setting for `core.autocrlf`
# means that when checking out code, LF endings get converted
# to CRLF. This causes problems for shell scripts, as bash
# gets choked up on the extra `\r` character.
*.sh text eol=lf
# Hide lockfile updates
*esy.lock/* linguist-generated
# Recognize all re files as Reason
*.re linguist-language=Reason
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
open_collective: revery
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Create a report to help us improve
---
- Operating System:
- Revery Version:
- OCaml version:
- Native, Bytecode, or JS build:
- Link to github repo:
- Steps to reproduce:
- Actual Result:
- Expected Result:
- Additional Information:
- Search terms used:
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature Request
about: Suggest an idea for this project
---
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
# missing trailing slash since it's a symlink with esy
node_modules
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
# dune build folder
_esy/
# install files
*.install
**/.merlin
#macOS
.DS_Store
skia-c-example.png
skia-svg-example.png
skia-font-manager-output.png
Arial.ttf
.vscode
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at oss@outrunlabs.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Bryan Phelps
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Build native , high-performance , cross-platform desktop apps with reason!
---
:construction: __NOTE:__ Revery is a work-in-progress and in active development! :construction:
To get a taste of Revery, check out our JavaScript + WebGL build on the [playground](https://outrunlabs.com/revery/playground). For the best experience, though, you'll want to try a [native build](https://github.com/revery-ui/revery/wiki/Building-&-Installing).
## Motivation
Today, [Electron](https://electronjs.org/) is one of the most popular tools for building desktop apps - using an HTML, JS, CSS stack. However, it has a heavy footprint in terms of both RAM and CPU - __essentially packing an entire browser into the app.__ Even with that tradeoff, it has a lot of great aspects - it's the quickest way to build a cross-platform app & it provides a great development experience - as can be testified by its usage in popular apps like VSCode, Discord, and Slack.
Revery is kind of like super-fast, _native code_ Electron - with bundled React-like/Redux-like libraries and a fast build system - all ready to go!
Revery is built with [reasonml](https://reasonml.github.io), which is a javascript-like syntax on top of [OCaml](https://ocaml.org) This means that the language is accessible to JS developers.
Your apps are compiled to native code with the Reason / OCaml toolchain - with __instant startup__ and __performance comparable to native C code.__ Revery features platform-accelerated, GPU-accelerated rendering. The compiler itself is fast, too!
Revery is an experiment - can we provide a great developer experience and help teams be productive, without making sacrifices on performance?
### Design Decisions
- __Consistent cross-platform behavior__
A major value prop of Electron is that you can build for all platforms at once. You have great confidence as a developer that your app will look and work the same across different platforms. Revery is the same - aside from platform-specific behavior, if your app looks or behaves differently on another platform, that's a bug! As a consequence, Revery is like [flutter](https://flutter.io) in that it __does not use native widgets__. This means more work for us, but also that we have more predictable functionality cross-platform!
> __NOTE:__ If you're looking for something that does leverage native widgets, check out [briskml](https://github.com/briskml/brisk). Another alternative is the [cuite](https://github.com/let-def/cuite) OCaml binding for [Qt](https://github.com/let-def/cuite).
- __High performance__
Performance should be at the forefront, and not a compromise - we need to develop and build benchmarks that help ensure top-notch performance and start-up time.
- __Type-safe, functional code__
We might have some dirty mutable objects for performance - but our high-level API should be purely functional. You should be able to follow the React model of modelling your UI as a _pure function_ of application state -> UI.
## Getting Started
- Check out [revery-quick-start](https://github.com/revery-ui/revery-quick-start) to get up and running with your own Revery app!
- Try out our [interactive playground](https://www.outrunlabs.com/revery/playground/)
- Read through our [docs](https://www.outrunlabs.com/revery/api/revery/)
## Contributing
We'd love your help, and welcome PRs and contributions.
Some ideas for getting started:
- [Build and run](https://github.com/revery-ui/revery/wiki/Building-&-Installing) Revery
- View our [Roadmap](https://github.com/revery-ui/revery/wiki/Roadmap)
- Help us improve our [documentation](https://github.com/revery-ui/revery/blob/master/src/index.mld)
- Help us build [examples](https://github.com/revery-ui/revery/tree/master/examples)
- Help us [fix bugs](https://github.com/revery-ui/revery/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+label%3A%22bug%22) and [build features](https://github.com/revery-ui/revery/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22++-label%3Abug)
- Help us [log bugs and open issues](https://github.com/bryphe/revery/issues/new)
- Support the project on [OpenCollective](https://opencollective.com/revery)
- Follow us on [Twitter](https://twitter.com/reveryui) or chat with us on [Discord](https://discord.gg/UvQ2cFn)!
## License
Revery is provided under the [MIT License](LICENSE).
Revery bundles several dependencies under their own license terms - please refer to [ThirdPartyLicenses.txt](./ThirdPartyLicenses.txt).
## Contributors
Thanks to everyone who has [contributed](https://github.com/revery-ui/revery/graphs/contributors) to Revery!
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/revery#backer)]
## Built with Revery
## Special Thanks
`revery` would not be possible without a bunch of cool tech:
- [ocaml](https://ocaml.org) made these tools possible - thanks [Inria](http://gallium.inria.fr/) & [OCaml Labs](http://ocamllabs.io/)!
- [reasonml](https://reasonml.github.io) made revery possible - thanks @jordwalke!
- [flex](https://github.com/jordwalke/flex) by @jordwalke
- [briskml](https://github.com/briskml)
- [brisk-reconciler](https://github.com/briskml/brisk-reconciler) - the "native React" implementation.
- [reason-sdl2](https://github.com/revery-ui/reason-sdl2)
- [SDL2](https://www.libsdl.org)
- [stb-image](https://github.com/nothings/stb)
- [reason-fontkit](https://github.com/bryphe/reason-fontkit)
- [freetype2](https://www.freetype.org)
- [harfbuzz](https://www.freedesktop.org/wiki/Software/HarfBuzz)
- [reason-gl-matrix](https://github.com/bryphe/reason-gl-matrix)
- [gl-matrix](http://glmatrix.net)
- [glm](https://glm.g-truc.net/0.9.9/index.html)
- [@reason-native/console](https://github.com/facebookexperimental/reason-native/tree/master/src/console)
`revery` was inspired by some __awesome projects:__
- [react-native](https://facebook.github.io/react-native/)
- [ReactMini](https://github.com/reasonml/reason-react/tree/master/ReactMini)
- [cuite](https://github.com/let-def/cuite)
- [wall](https://github.com/let-def/wall)
- [elm](https://elm-lang.org/)
- [reprocessing](https://github.com/Schmavery/reprocessing)
# Hot reload
We don't have a Hot Reload yet but it is on our roadmap. In the meantime, you can check branch [feat/hot-reload](https://github.com/revery-ui/revery/tree/feat/hot-reload) to see the progression.
In the meantime @mbernat has done a [script](https://gist.github.com/mbernat/abf651653c123374037c27377f41d0a0) that allow to relaunch the APP when the binary changed.
================================================
FILE: Revery.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outlook.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: ReveryBench.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outlook.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: ReveryExampleJs.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outlook.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: ReveryExamples.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outlook.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: ReveryTest.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outlook.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: ThirdPartyLicenses.txt
================================================
Revery
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Outrun Labs, LLC received such components are set forth below. Outrun Labs, LLC reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise.
1. MinCW Runtime License (https://sourceforge.net/p/mingw/mingw-org-wsl/ci/21762bb4a1bd0c88c38eead03f59e8d994349e83/tree/LICENSE)
2. Roboto Font by Google (https://github.com/google/fonts/tree/master/apache/roboto)
3. FontAwesome/Font-Awesome (https://github.com/FontAwesome/Font-Awesome)
4. SDL2 (https://www.libsdl.org)
5. Zed (https://github.com/ocaml-community/zed)
%% MingW Runtime License NOTICES AND INFORMATION BEGIN HERE
==============================================
Copyright (c) 2012 MinGW.org project
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice, this permission notice and the below disclaimer
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
==============================================
END of MingW Runtime License NOTICES AND INFORMATION
%% Roboto Font NOTICES AND INFORMATION BEGIN HERE
==============================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================
END of Roboto Font NOTICES AND INFORMATION
%% FontAwesome NOTICES AND INFORMATION BEGIN HERE
==============================================
Font Awesome Free License
-------------------------
Font Awesome Free is free, open source, and GPL friendly. You can use it for
commercial projects, open source projects, or really almost whatever you want.
Full Font Awesome Free license: https://fontawesome.com/license/free.
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
packaged as SVG and JS file types.
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
In the Font Awesome Free download, the SIL OLF license applies to all icons
packaged as web and desktop font files.
# Code: MIT License (https://opensource.org/licenses/MIT)
In the Font Awesome Free download, the MIT license applies to all non-font and
non-icon files.
# Attribution
Attribution is required by MIT, SIL OLF, and CC BY licenses. Downloaded Font
Awesome Free files already contain embedded comments with sufficient
attribution, so you shouldn't need to do anything additional when using these
files normally.
We've kept attribution comments terse, so we ask that you do not actively work
to remove them from files, especially code. They're a great way for folks to
learn about Font Awesome.
# Brand Icons
All brand icons are trademarks of their respective owners. The use of these
trademarks does not indicate endorsement of the trademark holder by Font
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**
==============================================
END of FontAwesome NOTICES AND INFORMATION
%% SDL 2 NOTICES AND INFORMATION BEGIN HERE
==============================================
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
==============================================
END of SDL 2 NOTICES AND INFORMATION
%% Zed NOTICES AND INFORMATION BEGIN HERE
==============================================
Copyright (c) 2011, Jeremie Dimino
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Jeremie Dimino nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
==============================================
END of Zed NOTICES AND INFORMATION
%% NOTICES AND INFORMATION BEGIN HERE
==============================================
==============================================
END of NOTICES AND INFORMATION
================================================
FILE: azure-pipelines.yml
================================================
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
name: $(Build.SourceVersion)
# Master build triggers
trigger:
- master
# PR Triggers
pr:
autoCancel: true
branches:
include:
- master
paths:
exclude:
- README.md
jobs:
- job: Linux
timeoutInMinutes: 0
pool:
vmImage: 'ubuntu-20.04'
variables:
STAGING_DIRECTORY: $(Build.StagingDirectory)
ESY__CACHE_INSTALL_PATH: /home/vsts/.esy/3_____________________________________________________________________/i
ESY__CACHE_SOURCE_TARBALL_PATH: /home/vsts/.esy/source/i
# ESY__NPM_ROOT: /opt/hostedtoolcache/node/8.14.0/x64/lib/node_modules/esy
steps:
- script: sudo apt-get update
- script: sudo apt-get install -y libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev mesa-utils mesa-utils-extra ragel libgtk-3-dev nasm libxxf86vm-dev
- template: .ci/use-node.yml
- template: .ci/restore-build-cache.yml
- template: .ci/esy-build-steps.yml
- template: .ci/esy-bench.yml
- template: .ci/create-docs.yml
- template: .ci/publish-build-cache.yml
- job: CentOS
displayName: 'Linux - CentOS - Docker Image'
timeoutInMinutes: 0
pool:
vmImage: 'ubuntu-20.04'
variables:
STAGING_DIRECTORY: $(Build.StagingDirectory)
ESY__CACHE_INSTALL_PATH: /home/vsts/.esy/3_____________________________________________________________________/i
ESY__CACHE_SOURCE_TARBALL_PATH: /home/vsts/.esy/source/i
# ESY__NPM_ROOT: /opt/hostedtoolcache/node/8.14.0/x64/lib/node_modules/esy
steps:
- script: docker build -t centos scripts/docker/centos
displayName: 'docker build'
- script: docker run --rm --mount src=`pwd`,target=/revery,type=bind centos /bin/bash -c 'cd revery && ls -a'
- script: docker run --cap-add SYS_ADMIN --device /dev/fuse --security-opt apparmor:unconfined --rm --mount src=`pwd`,target=/revery,type=bind centos /bin/bash -c 'cd revery && ./scripts/docker-build.sh'
- job: Hygiene_Checks
displayName: 'Hygiene Checks'
timeoutInMinutes: 0
pool:
vmImage: 'macOS 10.14'
variables:
STAGING_DIRECTORY: $(Build.StagingDirectory)
ESY__CACHE_INSTALL_PATH: /Users/runner/.esy/3__________________________________________________________________/i
ESY__CACHE_SOURCE_TARBALL_PATH: /Users/runner/.esy/source/i
# ESY__NPM_ROOT: /usr/local/lib/node_modules/esy
steps:
- script: brew update-reset
- script: brew install libpng ragel
- script: node --version
- template: .ci/restore-build-cache.yml
- template: .ci/esy-check-hygiene.yml
- template: .ci/publish-build-cache.yml
- job: MacOS
timeoutInMinutes: 0
pool:
vmImage: 'macOS 10.14'
variables:
STAGING_DIRECTORY: $(Build.StagingDirectory)
ESY__CACHE_INSTALL_PATH: /Users/runner/.esy/3__________________________________________________________________/i
ESY__CACHE_SOURCE_TARBALL_PATH: /Users/runner/.esy/source/i
# ESY__NPM_ROOT: /usr/local/lib/node_modules/esy
steps:
- script: brew update-reset
- script: brew install libpng ragel
- script: node --version
- template: .ci/restore-build-cache.yml
- template: .ci/esy-build-steps.yml
- template: .ci/esy-bench.yml
- template: .ci/publish-build-cache.yml
- job: Windows
timeoutInMinutes: 0
pool:
vmImage: 'vs2017-win2016'
variables:
STAGING_DIRECTORY: $(Build.StagingDirectory)
ESY__CACHE_INSTALL_PATH: /C/Users/VssAdministrator/.esy/3______________________________________________________/i
ESY__CACHE_SOURCE_TARBALL_PATH: /C/Users/VssAdministrator/.esy/source/i
# ESY__NPM_ROOT: /C/npm/prefix/node_modules/esy
steps:
- template: .ci/use-node.yml
- template: .ci/restore-build-cache.yml
- template: .ci/esy-build-steps.yml
- template: .ci/esy-bench.yml
- template: .ci/publish-build-cache.yml
================================================
FILE: bench/exe/Bench.re
================================================
ReveryBench.BenchFramework.cli();
Skia_Bench.BenchFramework.cli();
================================================
FILE: bench/exe/dune
================================================
(executable
(name bench)
(public_name ReveryBench)
(libraries ReveryBench.lib Skia_Bench)
(package ReveryBench))
(install
(section bin)
(package ReveryBench)
(files TestFont.ttf))
================================================
FILE: bench/lib/BenchFramework.re
================================================
include Reperf.Make({
let config = Reperf.Config.create(~snapshotDir="bench/__snapshots__", ());
});
================================================
FILE: bench/lib/DrawBench.re
================================================
open BenchFramework;
open Revery;
open Revery.Draw;
let options = Reperf.Options.create(~iterations=10000, ());
let setup = () => {
let surface = SurfaceUtility.makeSurface(800l, 600l) |> Option.get;
CanvasContext.createFromSurface(surface);
};
module Data = {
let testString = String.make(50, 'a') ++ String.make(50, 'X');
let testFallbackString = "ABC鬼";
let paint = {
let textPaint = Skia.Paint.make();
Skia.Paint.setTextEncoding(textPaint, GlyphId);
Skia.Paint.setLcdRenderText(textPaint, true);
Skia.Paint.setAntiAlias(textPaint, true);
Skia.Paint.setTextSize(textPaint, 20.);
textPaint;
};
let rectPaint = Skia.Paint.make();
};
let drawText = canvasContext => {
let maybeSkia =
Revery.Font.Family.fromFile("TestFont.ttf")
|> Revery.Font.Family.toSkia(Normal);
switch (Revery.Font.load(maybeSkia)) {
| Error(_) => failwith("Unable to load font!")
| Ok(font) =>
Skia.Paint.setColor(Data.paint, Revery_Core.Color.toSkia(Colors.white));
let shapedText =
Data.testString
|> Revery.Font.shape(font)
|> Revery.Font.ShapeResult.getGlyphStrings;
shapedText
|> List.iter(((typeface, string)) => {
Skia.Paint.setTypeface(Data.paint, typeface);
CanvasContext.drawText(
~x=1.,
~y=1.,
~paint=Data.paint,
~text=string,
canvasContext,
);
});
};
};
let drawFallbackText = canvasContext => {
let maybeSkia =
Revery.Font.Family.system("Arial") |> Revery.Font.Family.toSkia(Normal);
switch (Revery.Font.load(maybeSkia)) {
| Error(_) => failwith("Unable to load font!")
| Ok(font) =>
Skia.Paint.setColor(Data.paint, Revery_Core.Color.toSkia(Colors.white));
let shapedText =
Data.testFallbackString
|> Revery.Font.shape(font)
|> Revery.Font.ShapeResult.getGlyphStrings;
shapedText
|> List.iter(((typeface, string)) => {
Skia.Paint.setTypeface(Data.paint, typeface);
CanvasContext.drawText(
~x=1.,
~y=1.,
~paint=Data.paint,
~text=string,
canvasContext,
);
});
};
};
let drawRect = canvasContext => {
Skia.Paint.setColor(Data.rectPaint, Revery.Color.toSkia(Colors.green));
CanvasContext.drawRectLtwh(
~paint=Data.rectPaint,
~left=1.,
~top=1.,
~width=10.,
~height=20.,
canvasContext,
);
};
bench(~name="Draw: drawText", ~options, ~setup, ~f=drawText, ());
bench(
~name="Draw: drawText (second iteration, cached)",
~options,
~setup,
~f=drawText,
(),
);
bench(
~name="Draw: drawFallbackText",
~options,
~setup,
~f=drawFallbackText,
(),
);
bench(
~name="Draw: drawFallbackText (second iteration, cached)",
~options,
~setup,
~f=drawFallbackText,
(),
);
bench(~name="Draw: drawRectLtwh", ~options, ~setup, ~f=drawRect, ());
================================================
FILE: bench/lib/LayoutBench.re
================================================
open BenchFramework;
open Revery.UI;
let options = Reperf.Options.create(~iterations=10000, ());
let createNode = () => {
let _ = (new node)();
();
};
let layoutNode = (force: bool, n: node) => {
Layout.layout(~force, n);
};
bench(
~name="Node: create single node",
~options,
~setup=() => (),
~f=createNode,
(),
);
bench(
~name="Layout: layout single node (force re-layout)",
~options,
~setup=
NodeUtility.setupNode(~style=Style.make(~width=100, ~height=100, ())),
~f=layoutNode(true),
(),
);
bench(
~name="Layout: layout node tree (4 deep, 4 wide) (force re-layout))",
~options,
~setup=NodeUtility.setupNodeTree(~depth=4, ~breadth=4),
~f=layoutNode(true),
(),
);
bench(
~name="Layout: layout single node",
~options,
~setup=
NodeUtility.setupNode(~style=Style.make(~width=100, ~height=100, ())),
~f=layoutNode(false),
(),
);
bench(
~name="Layout: layout node tree (4 deep, 4 wide)",
~options,
~setup=NodeUtility.setupNodeTree(~depth=4, ~breadth=4),
~f=layoutNode(false),
(),
);
================================================
FILE: bench/lib/NodeUtility.re
================================================
open Revery.UI;
let setupNode = (~style, ()) => {
let n = (new node)();
n#setStyle(style);
n;
};
let rec setupNodeTree =
(
~depth: int,
~breadth: int,
~style=Style.make(~width=400, ~height=400, ()),
(),
) => {
let i = ref(breadth);
let n = setupNode(~style, ());
while (i^ > 0 && depth > 0) {
let newNode = setupNodeTree(~depth=depth - 1, ~breadth, ());
n#addChild(newNode, 0);
decr(i);
};
n;
};
================================================
FILE: bench/lib/PaintBench.re
================================================
open BenchFramework;
open Revery;
open Revery.Draw;
let options = Reperf.Options.create(~iterations=10000, ());
module Data = {
let color = Colors.red;
let paint = Skia.Paint.make();
};
let setPaintColor = () => {
let () = Skia.Paint.setColor(Data.paint, Revery.Color.toSkia(Data.color));
();
};
bench(
~name="Paint: Set color",
~options,
~setup=() => (),
~f=setPaintColor,
(),
);
================================================
FILE: bench/lib/RecalculateBench.re
================================================
open BenchFramework;
open Revery.UI;
let options = Reperf.Options.create(~iterations=1000, ());
let recalculate = (n: node) => {
n#recalculate();
};
bench(
~name="Recalculate: recalculate single node",
~options,
~setup=
NodeUtility.setupNode(~style=Style.make(~width=100, ~height=100, ())),
~f=recalculate,
(),
);
bench(
~name="Recalculate: recalculate node tree (4 deep, 4 wide)",
~options,
~setup=NodeUtility.setupNodeTree(~depth=4, ~breadth=4),
~f=recalculate,
(),
);
================================================
FILE: bench/lib/SurfaceUtility.re
================================================
open Skia;
let makeSurface = (width, height) => {
let imageInfo = Skia.ImageInfo.make(width, height, Rgba8888, Premul, None);
Surface.makeRaster(imageInfo, 0, None);
};
================================================
FILE: bench/lib/ViewNodeBench.re
================================================
open BenchFramework;
open Revery.Draw;
open Revery.UI;
let options = Reperf.Options.create(~iterations=10000, ());
let createViewNode = () => {
let viewNode = (new viewNode)();
viewNode#recalculate();
viewNode;
};
let setup = () => {
let surface = SurfaceUtility.makeSurface(800l, 600l) |> Option.get;
let canvas = CanvasContext.createFromSurface(surface);
NodeDrawContext.create(
~dpi=1.0,
~canvasScalingFactor=1.0,
~debug=false,
~canvas,
~zIndex=0,
~opacity=1.0,
(),
);
};
module Data = {
let viewNode = createViewNode();
};
let draw = context => {
Data.viewNode#draw(context);
};
bench(~name="ViewNode: draw single node", ~options, ~setup, ~f=draw, ());
================================================
FILE: bench/lib/dune
================================================
(library
(name ReveryBench)
(public_name ReveryBench.lib)
(ocamlopt_flags -linkall)
(libraries Revery reperf.lib))
================================================
FILE: bench.json
================================================
{
"source": "./package.json",
"scripts": {
"run": "esy '@bench' x ReveryBench"
},
"override": {
"build": ["dune build -p reason-harfbuzz,reason-skia,reason-sdl2,Revery,ReveryBench -j4"],
"install": [
"esy-installer ReveryBench.install"
]
}
}
================================================
FILE: doc.json
================================================
{
"source": "./package.json",
"scripts": {
"run": "esy '@doc' x http-server #{self.target_dir}/default/_doc/_html",
"print": "esy '@doc' echo #{self.target_dir}/default/_doc/_html"
},
"override": {
"build": ["dune build @doc -p reason-harfbuzz,reason-skia,reason-sdl2,Revery -j4"],
"dependencies": {
"@opam/odoc": "*",
"http-server": "*"
}
}
}
================================================
FILE: dune
================================================
(dirs src packages test examples bench)
================================================
FILE: dune-project
================================================
(lang dune 2.5)
(formatting (enabled_for reason))
================================================
FILE: dune-workspace
================================================
(lang dune 2.0)
(context (default
(disable_dynamically_linked_foreign_archives true)))
================================================
FILE: examples/AnalogClock.re
================================================
open Revery;
open Revery.Math;
open Revery.UI;
module AnalogClock = {
module DegreeUtils = {
type t = {
hourDegrees: float,
minuteDegrees: float,
secondDegrees: float,
};
let numberToPercentage = (~number, ~max) => number /. max *. 100.;
let percentageToDegrees = percentage => percentage *. 360. /. 100.;
let getDegreesFromTime = ({tm_hour, tm_min, tm_sec, _}: Unix.tm) => {
let hourDegrees =
numberToPercentage(~number=float_of_int(tm_hour), ~max=12.)
|> percentageToDegrees;
let minuteDegrees =
numberToPercentage(~number=float_of_int(tm_min), ~max=60.)
|> percentageToDegrees;
let secondDegrees =
numberToPercentage(~number=float_of_int(tm_sec), ~max=60.)
|> percentageToDegrees;
{hourDegrees, minuteDegrees, secondDegrees};
};
};
type action =
| UpdateTime;
type state = {currentTime: Unix.tm};
let reducer = (action, _state) => {
switch (action) {
| UpdateTime => {currentTime: Unix.time() |> Unix.localtime}
};
};
let%component make = () => {
let%hook (state, dispatch) =
Hooks.reducer(
~initialState={currentTime: Unix.time() |> Unix.localtime},
reducer,
);
let {hourDegrees, minuteDegrees, secondDegrees}: DegreeUtils.t =
state.currentTime |> DegreeUtils.getDegreesFromTime;
let%hook () =
Hooks.effect(
OnMount,
() => {
let clear =
Tick.interval(
~name="Clock Timer",
_ => dispatch(UpdateTime),
Time.seconds(1),
);
Some(clear);
},
);
let containerStyle =
Style.[
alignItems(`Center),
bottom(0),
justifyContent(`Center),
left(0),
position(`Absolute),
right(0),
top(0),
];
let clockContainer =
Style.[
border(~width=4, ~color=Colors.white),
height(300),
padding(15),
position(`Relative),
width(300),
];
let clockPointer = Style.[position(`Absolute), left(150)];
let hourStyle =
Style.(
merge(
~source=clockPointer,
~target=[
backgroundColor(Colors.white),
height(90),
top(105),
transform([
Transform.Rotate(Angle.from_degrees(hourDegrees)),
Transform.TranslateY(-45.),
]),
width(4),
],
)
);
let minutesStyle =
Style.(
merge(
~source=clockPointer,
~target=[
backgroundColor(Colors.white),
height(120),
top(90),
transform([
Transform.Rotate(Angle.from_degrees(minuteDegrees)),
Transform.TranslateY(-60.),
]),
width(4),
],
)
);
let secondsStyle =
Style.(
merge(
~source=clockPointer,
~target=[
backgroundColor(Colors.red),
height(150),
top(75),
transform([
Transform.Rotate(Angle.from_degrees(secondDegrees)),
Transform.TranslateY(-75.),
]),
width(2),
],
)
);
;
};
};
let render = () => ;
================================================
FILE: examples/Border.re
================================================
open Revery;
open Revery.UI;
let defaultStyle =
Style.[
backgroundColor(Color.multiplyAlpha(0.2, Colors.white)),
position(`Relative),
left(100),
top(100),
width(200),
height(200),
border(~width=15, ~color=Colors.red),
borderHorizontal(~width=8, ~color=Colors.blue),
borderTop(~width=15, ~color=Colors.red),
borderLeft(~width=30, ~color=Colors.green),
borderRadius(16.0),
];
let innerStyle =
Style.[
backgroundColor(Colors.yellow),
position(`Relative),
left(0),
top(0),
width(30),
height(30),
borderHorizontal(~color=Colors.black, ~width=3),
];
let textStyle = Style.[color(Colors.black)];
let render = () =>
;
================================================
FILE: examples/Boxshadow.re
================================================
open Revery;
open Revery.UI;
let parentStyles =
Style.[
position(`Relative),
flexGrow(1),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];
let blackShadow =
Style.[
backgroundColor(Colors.blue),
position(`Relative),
width(100),
height(100),
boxShadow(
~yOffset=-10.,
~xOffset=0.,
~blurRadius=15.,
~color=Colors.black,
~spreadRadius=10.,
),
marginVertical(30),
];
let subtleBlackShadow =
Style.[
backgroundColor(Colors.teal),
position(`Relative),
width(100),
height(100),
boxShadow(
~yOffset=1.,
~xOffset=1.,
~blurRadius=5.,
~color=Color.rgba(0., 0., 0., 0.2),
~spreadRadius=0.,
),
marginVertical(30),
];
let greenShadow =
Style.[
backgroundColor(Colors.red),
position(`Relative),
width(100),
height(100),
boxShadow(
~yOffset=10.,
~xOffset=-30.,
~blurRadius=20.,
~color=Colors.green,
~spreadRadius=0.,
),
marginVertical(30),
];
let render = () =>
;
================================================
FILE: examples/Calculator.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module Row = {
let make = (~children, ()) => {
let style =
Style.[
flexDirection(`Row),
alignItems(`Stretch),
justifyContent(`Center),
flexGrow(1),
];
...children ;
};
};
module Column = {
let make = (~children, ()) => {
let style =
Style.[
flexDirection(`Column),
alignItems(`Stretch),
justifyContent(`Center),
backgroundColor(Colors.darkGrey),
flexGrow(1),
];
...children ;
};
};
module Button = {
let make =
(~fontFamily=Font.Family.default, ~contents: string, ~onClick, ()) => {
let clickableStyle =
Style.[
position(`Relative),
backgroundColor(Colors.lightGrey),
justifyContent(`Center),
alignItems(`Center),
flexGrow(1),
/* Min width */
width(125),
margin(10),
];
let viewStyle =
Style.[
position(`Relative),
justifyContent(`Center),
alignItems(`Center),
];
let textStyle =
Style.[color(Colors.black), textWrap(TextWrapping.NoWrap)];
;
};
};
module Display = {
let make = (~display: string, ~curNum: string, ()) => {
let viewStyle =
Style.[
backgroundColor(Colors.white),
height(120),
flexDirection(`Column),
alignItems(`Stretch),
justifyContent(`FlexStart),
flexGrow(2),
];
let displayStyle = Style.[color(Colors.black), margin(15)];
let numStyle = Style.[color(Colors.black), margin(15)];
;
};
};
type operator = [ | `Nop | `Add | `Sub | `Mul | `Div];
let showFloat = float => {
let string = string_of_float(float);
if (String.length(string) > 1 && string.[String.length(string) - 1] == '.') {
String.sub(string, 0, String.length(string) - 1);
} else {
string;
};
};
type state = {
operator, /* Current operator being applied */
result: float, /* The actual numerical result */
display: string, /* The equation displayed */
number: string /* Current number being typed */
};
type action =
| BackspaceKeyPressed
| ClearKeyPressed(bool) /* true = AC pressed */
| DotKeyPressed
| NumberKeyPressed(string)
| OperationKeyPressed(operator)
| PlusMinusKeyPressed
| ResultKeyPressed;
let eval = (state, newOp) => {
/* Figure out what the string for the next operation will be */
let newOpString =
switch (newOp) {
| `Nop => ""
| `Add => "+"
| `Sub => "-"
| `Mul => "×"
| `Div => "÷"
};
/* Split the current display on ! and get everything after (to clear errors) */
let partitionedDisplay = String.split_on_char('!', state.display);
let display =
List.nth(partitionedDisplay, List.length(partitionedDisplay) - 1);
let (newDisplay, newResult) =
switch (state.operator) {
| #operator when state.number == "" => (
"Error: Can't evaluate binary operator without input!",
state.result,
)
| `Nop => (state.number ++ newOpString, float_of_string(state.number))
| `Add => (
display ++ state.number ++ newOpString,
state.result +. float_of_string(state.number),
)
| `Sub => (
display ++ state.number ++ newOpString,
state.result -. float_of_string(state.number),
)
| `Mul => (
display ++ state.number ++ newOpString,
state.result *. float_of_string(state.number),
)
| `Div =>
if (float_of_string(state.number) != 0.) {
(
display ++ state.number ++ newOpString,
state.result /. float_of_string(state.number),
);
} else {
("Error: Divide by zero!", state.result);
}
};
(newResult, newDisplay);
};
let reducer = (action, state) =>
switch (action) {
| BackspaceKeyPressed =>
state.number == ""
? state
: {
...state,
number: String.sub(state.number, 0, String.length(state.number) - 1),
}
| ClearKeyPressed(ac) =>
ac
? {operator: `Nop, result: 0., display: "", number: ""}
: {...state, number: ""}
| DotKeyPressed =>
String.contains(state.number, '.')
? state : {...state, number: state.number ++ "."}
| NumberKeyPressed(n) => {...state, number: state.number ++ n}
| OperationKeyPressed(o) =>
let (result, display) = eval(state, o);
{operator: o, result, display, number: ""};
| PlusMinusKeyPressed =>
if (state.number != "" && state.number.[0] == '-') {
{
...state,
number: String.sub(state.number, 1, String.length(state.number) - 1),
};
} else {
{...state, number: "-" ++ state.number};
}
| ResultKeyPressed =>
let (result, display) = eval(state, `Nop);
{operator: `Nop, result, display, number: showFloat(result)};
};
module KeyboardInput = {
type state = {
ref: option(node),
hasFocus: bool,
};
type action =
| Focused(bool)
| SetRef(node);
let reducer = (action, state) =>
switch (action) {
| Focused(v) => {...state, hasFocus: v}
| SetRef(v) => {...state, ref: Some(v)}
};
let%component make = (~dispatch as parentDispatch, ()) => {
let%hook (v, dispatch) =
Hooks.reducer(~initialState={ref: None, hasFocus: false}, reducer);
let%hook () =
Hooks.effect(
Always,
() => {
if (!v.hasFocus) {
switch (v.ref) {
| Some(v) => Focus.focus(v)
| None => ()
};
};
None;
},
);
let onBlur = () => {
dispatch(Focused(false));
};
let onFocus = () => {
dispatch(Focused(true));
};
let respondToKeys = (event: NodeEvents.keyEventParams) => {
Key.Keycode.(
switch (event.keycode) {
| v when v == backspace => parentDispatch(BackspaceKeyPressed)
| v when v == c && event.ctrlKey =>
parentDispatch(ClearKeyPressed(true))
| v when v == c => parentDispatch(ClearKeyPressed(false))
/* + key */
| v when v == Key.Keycode.equals && event.shiftKey =>
parentDispatch(OperationKeyPressed(`Add))
| v when v == Key.Keycode.equals && event.ctrlKey =>
parentDispatch(PlusMinusKeyPressed)
| v when v == Key.Keycode.minus =>
parentDispatch(OperationKeyPressed(`Sub))
/* * key */
| v when v == digit8 && event.shiftKey =>
parentDispatch(OperationKeyPressed(`Mul))
| v when v == slash => parentDispatch(OperationKeyPressed(`Div))
| v when v == period => parentDispatch(DotKeyPressed)
| v when v == equals => parentDispatch(ResultKeyPressed)
| v when v == digit0 => parentDispatch(NumberKeyPressed("0"))
| v when v == digit1 => parentDispatch(NumberKeyPressed("1"))
| v when v == digit2 => parentDispatch(NumberKeyPressed("2"))
| v when v == digit3 => parentDispatch(NumberKeyPressed("3"))
| v when v == digit4 => parentDispatch(NumberKeyPressed("4"))
| v when v == digit5 => parentDispatch(NumberKeyPressed("5"))
| v when v == digit6 => parentDispatch(NumberKeyPressed("6"))
| v when v == digit7 => parentDispatch(NumberKeyPressed("7"))
| v when v == digit8 => parentDispatch(NumberKeyPressed("8"))
| v when v == digit9 => parentDispatch(NumberKeyPressed("9"))
| _ => ()
}
);
};
dispatch(SetRef(r))}
onBlur
onFocus
style=Style.[position(`Absolute), width(1), height(1)]
onKeyDown=respondToKeys
/>;
};
};
module Calculator = {
let%component make = () => {
let%hook ({display, number, _}, dispatch) =
Hooks.reducer(
~initialState={operator: `Nop, result: 0., display: "", number: ""},
reducer,
);
dispatch(ClearKeyPressed(true))}
/>
dispatch(ClearKeyPressed(false))}
/>
dispatch(PlusMinusKeyPressed)} />
/* TODO: Switch to a font with a backspace character */
dispatch(BackspaceKeyPressed)}
/>
dispatch(NumberKeyPressed("7"))}
/>
dispatch(NumberKeyPressed("8"))}
/>
dispatch(NumberKeyPressed("9"))}
/>
dispatch(OperationKeyPressed(`Div))}
/>
dispatch(NumberKeyPressed("4"))}
/>
dispatch(NumberKeyPressed("5"))}
/>
dispatch(NumberKeyPressed("6"))}
/>
dispatch(OperationKeyPressed(`Mul))}
/>
dispatch(NumberKeyPressed("1"))}
/>
dispatch(NumberKeyPressed("2"))}
/>
dispatch(NumberKeyPressed("3"))}
/>
dispatch(OperationKeyPressed(`Sub))}
/>
dispatch(DotKeyPressed)} />
dispatch(NumberKeyPressed("0"))}
/>
dispatch(ResultKeyPressed)} />
dispatch(OperationKeyPressed(`Add))}
/>
;
};
};
let render = _ => ;
================================================
FILE: examples/CanQuitExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module CanQuit = {
type checkboxState = {canQuit: bool};
let%component make = () => {
let initialCheckboxState = {canQuit: true};
let%hook ({canQuit}, setCheckboxState) =
Hooks.state(initialCheckboxState);
{
let win = getActiveWindow();
switch (win) {
| Some(win) => Window.setCanQuitCallback(win, () => !canQuit)
| None => ()
};
setCheckboxState(({canQuit}) => {canQuit: !canQuit});
}}
style=Style.[border(~width=2, ~color=Colors.green)]
checked=canQuit
/>
;
};
};
let render = () => {
;
};
================================================
FILE: examples/CanvasExample.re
================================================
open Revery;
open Revery.Font;
open Revery.Draw;
open Revery.UI;
let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];
let outerBox =
Style.[width(450), height(450), backgroundColor(Colors.black)];
module Sample = {
let make = () => {
{
let transform = Skia.Matrix.make();
Skia.Matrix.setIdentity(transform);
CanvasContext.concat(transform, canvasContext);
let paint = Skia.Paint.make();
Skia.Paint.setColor(
paint,
Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l),
);
let rect = Skia.Rect.makeLtrb(1.0, 1.0, 101., 201.);
CanvasContext.drawRect(~rect, ~paint, canvasContext);
let stroke = Skia.Paint.make();
Skia.Paint.setColor(
stroke,
Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0x00l),
);
Skia.Paint.setAntiAlias(stroke, true);
Skia.Paint.setStyle(stroke, Stroke);
Skia.Paint.setStrokeWidth(stroke, 2.);
let innerPath = Skia.Path.make();
Skia.Path.lineTo(innerPath, 5., 5.);
Skia.Path.lineTo(innerPath, 15., -5.);
Skia.Path.lineTo(innerPath, 20., 0.);
let translate = Skia.Matrix.makeScale(20., 20., 20., 20.);
let pathEffect =
Skia.PathEffect.create2dPath(~matrix=translate, innerPath);
Skia.Paint.setPathEffect(stroke, pathEffect);
let path = Skia.Path.make();
Skia.Path.addCircle(path, 125., 125., ~radius=100., ());
CanvasContext.drawPath(~path, ~paint=stroke, canvasContext);
let maybeSkia =
Revery_Font.Family.fromFile("Roboto-Regular.ttf")
|> Revery_Font.Family.toSkia(Revery_Font.Weight.Normal);
switch (Revery_Font.load(maybeSkia)) {
| Error(_) => ()
| Ok(font) =>
let textPaint = Skia.Paint.make();
Skia.Paint.setColor(textPaint, Color.toSkia(Colors.white));
Skia.Paint.setLcdRenderText(textPaint, true);
Skia.Paint.setAntiAlias(textPaint, true);
Skia.Paint.setTextSize(textPaint, 20.);
let shapedText =
"Hello, World"
|> FontCache.shape(font)
|> ShapeResult.getGlyphStrings;
Skia.Paint.setTextEncoding(textPaint, GlyphId);
shapedText
|> List.iter(((typeface, string)) => {
Skia.Paint.setTypeface(textPaint, typeface);
CanvasContext.drawText(
~paint=textPaint,
~x=10.0,
~y=20.0,
~text=string,
canvasContext,
);
});
};
let fill = Skia.Paint.make();
Skia.Paint.setColor(
fill,
Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l),
);
let rect = Skia.Rect.makeLtrb(200., 150., 250., 300.);
CanvasContext.drawOval(~rect, ~paint=fill, canvasContext);
let red = Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l);
let blue = Skia.Color.makeArgb(0xFFl, 0x00l, 0x00l, 0xFFl);
// Test out some layers...
let layer =
CanvasContext.createLayer(
~width=128l,
~height=128l,
canvasContext,
)
|> Option.get;
// Draw a circle onto our new layer...
let paint = Skia.Paint.make();
let linearGradient =
Skia.Shader.makeLinearGradient2(
~startPoint=Skia.Point.make(16., 16.0),
~stopPoint=Skia.Point.make(48.0, 48.0),
~startColor=red,
~stopColor=blue,
~tileMode=`clamp,
);
Skia.Paint.setShader(paint, linearGradient);
Skia.Paint.setColor(
paint,
Skia.Color.makeArgb(0xFFl, 0x00l, 0xFFl, 0x00l),
);
CanvasContext.drawCircle(
~x=32.,
~y=32.,
~radius=16.,
~paint,
layer,
);
// And then draw the layer a bunch of places!
let paint = Skia.Paint.make();
CanvasContext.drawLayer(
~paint,
~layer,
~x=300.,
~y=300.,
canvasContext,
);
CanvasContext.drawLayer(
~paint,
~layer,
~x=264.,
~y=264.,
canvasContext,
);
CanvasContext.drawLayer(
~paint,
~layer,
~x=264.,
~y=300.,
canvasContext,
);
CanvasContext.drawLayer(
~paint,
~layer,
~x=300.,
~y=264.,
canvasContext,
);
}}
/>
;
};
};
let render = () => ;
================================================
FILE: examples/CheckboxExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module Check = {
type checkboxState = {
first: bool,
second: bool,
};
let getCheckboxText = checked => checked ? "Checked!" : "Not Checked!";
let%component make = () => {
let initialCheckboxState = {first: false, second: true};
let%hook ({first, second}, setCheckboxState) =
Hooks.state(initialCheckboxState);
setCheckboxState(_ => {first: !first, second})}
style=Style.[marginBottom(10)]
/>
setCheckboxState(_ => {second: !second, first})}
style=Style.[border(~width=2, ~color=Colors.green)]
checked=second
/>
;
};
};
let render = () => {
;
};
================================================
FILE: examples/DefaultButton.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module DefaultButtonWithCounter = {
let%component make = () => {
let%hook (count, setCount) = Hooks.state(0);
let increment = () => setCount(_ => count + 1);
let containerStyle =
Style.[justifyContent(`Center), alignItems(`Center)];
let countContainer =
Style.[
width(300),
height(300),
alignItems(`Center),
justifyContent(`Center),
];
let countStyle = Style.[margin(24), color(Colors.black)];
let countStr = string_of_int(count);
;
};
};
let render = () => ;
================================================
FILE: examples/DropdownExample.re
================================================
open Revery.UI;
open Revery.UI.Components;
let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
];
let textStyle = Style.[marginBottom(20)];
module DropdownExample = {
let items: DropdownInt.items = [
{value: 1, label: "First option"},
{value: 2, label: "Second option"},
{value: 3, label: "Third option"},
{value: 4, label: "Fourth option"},
{value: 5, label: "A really, really, really long option"},
];
let%component make = () => {
let%hook (selectedItem, setSelectedItem) =
Hooks.state(List.nth(items, 0));
setSelectedItem(_ => item)} />
;
};
};
let render = () => ;
================================================
FILE: examples/Examples.re
================================================
open Revery;
open ExampleStubs;
module SliderExample = Slider;
module ScrollViewExample = ScrollView;
open Revery.UI;
open Revery.UI.Components;
let bgColor = Color.hex("#212733");
let activeBackgroundColor = Color.hex("#2E3440");
let inactiveBackgroundColor = Color.hex("#272d39");
let selectionHighlight = Color.hex("#90f7ff");
type example = {
name: string,
render: Window.t => element,
source: string,
};
let examples = [
{name: "Animation", render: _w => Hello.render(), source: "Hello.re"},
{
name: "Spring",
render: _w => SpringExample.render(),
source: "SpringExample.re",
},
{
name: "CanQuit",
render: _ => CanQuitExample.render(),
source: "CanQuit.re",
},
{
name: "Button",
render: _ => DefaultButton.render(),
source: "DefaultButton.re",
},
{
name: "Checkbox",
render: _ => CheckboxExample.render(),
source: "CheckboxExample.re",
},
{name: "Slider", render: _ => SliderExample.render(), source: "Slider.re"},
{name: "Border", render: _ => Border.render(), source: "Border.re"},
{
name: "ScrollView",
render: _ => ScrollViewExample.render(),
source: "ScrollView.re",
},
{
name: "Calculator",
render: w => Calculator.render(w),
source: "Calculator.re",
},
{name: "Flexbox", render: _ => Flexbox.render(), source: "Flexbox.re"},
{
name: "Box Shadow",
render: _ => Boxshadow.render(),
source: "Boxshadow.re",
},
{name: "Focus", render: _ => FocusExample.render(), source: "Focus.re"},
{name: "Fonts", render: _ => FontsExample.render(), source: "Fonts.re"},
{
name: "Stopwatch",
render: _ => Stopwatch.render(),
source: "Stopwatch.re",
},
{
name: "Native: File(s)/Folders(s)",
render: _ => NativeFileExample.render(),
source: "NativeFileExample.re",
},
{
name: "Native: Notifications",
render: _ => NativeNotificationExample.render(),
source: "NativeNotificationExample.re",
},
{
name: "Native: Icon Features",
render: w => NativeIconExample.render(w),
source: "NativeIconExample.re",
},
{
name: "Native: OSX Menu",
render: w => NativeMenuExample.render(w),
source: "NativeMenuExample.re",
},
{
name: "Native: Inputs",
render: _ => NativeInputExample.render(),
source: "NativeInputExample.re",
},
{
name: "Input",
render: _ => InputExample.render(),
source: "InputExample.re",
},
{
name: "Radio Button",
render: _ => RadioButtonExample.render(),
source: "RadioButtonExample.re",
},
{
name: "Game Of Life",
render: _ => GameOfLife.render(),
source: "GameOfLife.re",
},
{
name: "Screen Capture",
render: w => ScreenCapture.render(w),
source: "ScreenCapture.re",
},
{
name: "Tree View",
render: w => TreeView.render(w),
source: "TreeView.re",
},
{
name: "Analog Clock",
render: _ => AnalogClock.render(),
source: "AnalogClock.re",
},
{
name: "TodoMVC",
render: _ => TodoExample.render(),
source: "TodoExample.re",
},
{
name: "Dropdown",
render: _ => DropdownExample.render(),
source: "DropdownExample.re",
},
{
name: "Text",
render: _w => TextExample.render(),
source: "TextExample.re",
},
{
name: "Hover Example",
render: _ => HoverExample.render(),
source: "HoverExample.re",
},
{
name: "Canvas Example",
render: _ => CanvasExample.render(),
source: "CanvasExample.re",
},
{
name: "SVG Example",
render: _ => SVGExample.render(),
source: "SVGExample.re",
},
{
name: "Zoom Example",
render: _ => ZoomExample.render(),
source: "ZoomExample.re",
},
{
name: "Nested Clickables",
render: _ => NestedClickable.render(),
source: "NestedClickable.re",
},
{
name: "File Drag & Drop",
render: _ => FileDragAndDrop.render(),
source: "FileDragAndDrop.re",
},
{
name: "Rich Text Example",
render: _ => RichTextExample.render(),
source: "RichTextExample.re",
},
{
name: "Shell: Open URL",
render: _ => URLFileOpen.render(),
source: "URLFileOpen.re",
},
{
name: "Window: Hit Tests/Zones",
render: w => HitTests.render(w),
source: "HitTests.re",
},
{
name: "Window: Control",
render: w => WindowControl.render(w),
source: "WindowControl.re",
},
{
name: "Markdown",
render: _ => MarkdownExample.render(),
source: "MarkdownExample.re",
},
{
name: "ImageQuality",
render: _ => ImageQualityExample.render(),
source: "ImageQualityExample.re",
},
{
name: "Layer",
render: _ => LayerExample.render(),
source: "LayerExample.re",
},
{
name: "WavFilePlayback",
render: _ => WavFilePlaybackExample.render(),
source: "WavFilePlaybackExample.re",
},
];
let getExampleByName = name =>
List.find(example => example.name == name, examples);
let noop = () => ();
module ExampleButton = {
let make = (~isActive, ~name, ~onClick, ()) => {
let highlightColor =
isActive ? selectionHighlight : Colors.transparentWhite;
let buttonOpacity = 1.0;
let bgColor = isActive ? activeBackgroundColor : inactiveBackgroundColor;
let wrapperStyle =
Style.[
borderLeft(~width=4, ~color=highlightColor),
backgroundColor(bgColor),
];
let textColor = isActive ? Colors.white : Colors.grey;
let textHeaderStyle = Style.[color(textColor), margin(16)];
;
};
};
module ExampleHost = {
let%component make = (~window, ~initialExample, ()) => {
let%hook (selectedExample, setSelectedExample) =
Hooks.state(getExampleByName(initialExample));
let renderButton = example => {
let isActive = example === selectedExample;
let onClick = _ => {
Window.setTitle(window, "Revery Example - " ++ example.name);
prerr_endline("SOURCE FILE: " ++ example.source);
notifyExampleSwitched(example.source);
setSelectedExample(_ => example);
};
;
};
let buttons = List.map(renderButton, examples);
let exampleView = selectedExample.render(window);
()}
style=Style.[
position(`Absolute),
justifyContent(`Center),
alignItems(`Center),
backgroundColor(bgColor),
bottom(0),
top(0),
left(0),
right(0),
flexDirection(`Row),
]>
{buttons |> React.listToElement}
exampleView
;
};
};
let init = app => {
Revery.App.initConsole();
Timber.App.enable(Timber.Reporter.console());
Timber.App.setLevel(Timber.Level.perf);
App.onIdle(app, () => prerr_endline("Idle!"))
|> (ignore: Revery.App.unsubscribe => unit);
App.onBeforeQuit(
app,
(_code: int) => {
prerr_endline("Quitting!");
App.AllowQuit;
},
)
|> (ignore: Revery.App.unsubscribe => unit);
let initialExample = ref("Canvas Example");
let decorated = ref(true);
let forceScaleFactor = ref(None);
let showFPSCounter = ref(false);
Arg.parse(
[
("--trace", Unit(() => Timber.App.setLevel(Timber.Level.trace)), ""),
("--no-decoration", Unit(() => decorated := false), ""),
("--example", String(name => initialExample := name), ""),
(
"--force-device-scale-factor",
Float(scaleFactor => forceScaleFactor := Some(scaleFactor)),
"",
),
("--show-fps", Unit(() => showFPSCounter := true), ""),
],
_ => (),
"There is only --trace, --example, --no-decoration, --show-fps, and --force-device-scale-factor",
);
let initialExample = initialExample^;
let maximized = Environment.webGL;
let windowWidth = 800;
let windowHeight = 480;
print_endline("Hello from example app");
let window =
App.createWindow(
~createOptions=
WindowCreateOptions.create(
~width=windowWidth,
~height=windowHeight,
~maximized,
~titlebarStyle=Transparent,
~icon=Some("revery-icon.png"),
~decorated=decorated^,
~forceScaleFactor=forceScaleFactor^,
~acceleration=`Auto,
(),
),
app,
"Welcome to Revery!",
);
if (showFPSCounter^) {
Window.showFPSCounter(window);
};
if (Environment.webGL) {
Window.maximize(window);
};
/* NOTE: If you want to use network-calls or other IO, uncomment this line */
// let _startEventLoop = Revery_Lwt.startEventLoop();
let _unsubscribe =
Window.onFocusGained(window, () => print_endline("Focus gained"));
let _unsubscribe =
Window.onFocusLost(window, () => print_endline("Focus lost"));
let _unsubscribe =
Window.onMaximized(window, () => print_endline("Maximized!"));
let _unsubscribe =
Window.onFullscreen(window, () => print_endline("Fullscreen!"));
let _unsubscribe =
Window.onMinimized(window, () => print_endline("Minimized!"));
let _unsubscribe =
Window.onRestored(window, () => print_endline("Restored!"));
let _unsubscribe =
Window.onSizeChanged(window, ({width, height}) =>
print_endline(Printf.sprintf("Size changed: %d x %d", width, height))
);
let _unsubscribe =
Window.onMoved(window, ((x, y)) =>
print_endline(Printf.sprintf("Moved: %d x %d", x, y))
);
print_endline(
Printf.sprintf("Operating system: %s", Environment.osString),
);
let _renderFunction =
UI.start(window, );
();
};
Printexc.record_backtrace(true);
App.start(init);
================================================
FILE: examples/FileDragAndDrop.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module Styles = {
open Style;
let droppable = [
width(100),
height(100),
backgroundColor(Colors.blue),
justifyContent(`Center),
alignItems(`Center),
];
let outer = [
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
justifyContent(`Center),
alignItems(`Center),
];
let text = Style.[color(Colors.white)];
let activeStyle = Style.[color(Colors.blue)];
let inactiveStyle = Style.[color(Colors.lightBlue)];
};
let filesToText = files =>
List.map(
file =>
Native.Shell.openFile(file) |> ignore}
text=file
activeStyle=Styles.activeStyle
inactiveStyle=Styles.inactiveStyle
/>,
files,
);
let%component dnd = () => {
let%hook (files: list(string), setFiles) = Hooks.state([]);
{React.listToElement(filesToText(files))}
setFiles(_ => e.paths)}>
;
};
let render = () => ;
================================================
FILE: examples/Flexbox.re
================================================
open Revery;
open Revery.UI;
let parentWidth = 400;
let childWidth = 300;
let parentStyles =
(~alignment as a=`Auto, ~direction as d=`Row, ~justify as j=`Center, ()) =>
Style.[
backgroundColor(Colors.green),
position(`Relative),
width(parentWidth),
height(100),
alignItems(a),
justifyContent(j),
flexDirection(d),
];
let childStyles =
Style.[
backgroundColor(Colors.blue),
position(`Relative),
width(childWidth),
height(40),
];
let defaultTextStyles =
Style.[color(Colors.white), backgroundColor(Colors.blue)];
let parentColumnStyle = parentStyles(~direction=`Column);
let headerStyle =
Style.[marginTop(25), marginBottom(25), color(Colors.white)];
let horizontalStyles =
;
let verticalStyles =
;
let render = () => horizontalStyles verticalStyles ;
================================================
FILE: examples/FocusExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module SimpleButton = {
let%component make = () => {
let%hook (count, setCount) = Hooks.state(0);
let%hook (focused, setFocus) = Hooks.state(false);
let increment = () => setCount(_ => count + 1);
let txt = focused ? "Focused" : "Unfocused";
let textContent = txt ++ " me: " ++ string_of_int(count);
setFocus(_ => true)}
onBlur={() => setFocus(_ => false)}>
;
};
};
let render = () =>
;
================================================
FILE: examples/FontsExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module FontComponent = {
type state = {
family: string,
bold: bool,
italic: bool,
};
let initialState: state = {family: "Arial", bold: false, italic: false};
type actions =
| SetBold(bool)
| SetItalic(bool)
| SetFamily(string);
let reducer = (action: actions, state: state) => {
let state =
switch (action) {
| SetBold(v) => {...state, bold: v}
| SetItalic(v) => {...state, italic: v}
| SetFamily(v) => {...state, family: v}
};
state;
};
let%component make = () => {
let%hook (state, dispatch) = Hooks.reducer(~initialState, reducer);
let fontExample =
;
dispatch(SetFamily(value))}
value={state.family}
/>
dispatch(SetBold(!state.bold))}
/>
dispatch(SetItalic(!state.italic))}
/>
fontExample
;
};
};
let render = () => {
;
};
================================================
FILE: examples/GameOfLife.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module ViewPort = {
type t = {
xMin: int,
yMin: int,
xMax: int,
yMax: int,
};
type direction =
| North
| East
| South
| West;
let create = size => {xMin: 0, xMax: size, yMin: 0, yMax: size};
let changeDirection = (viewPort, direction) =>
switch (direction) {
| North => {...viewPort, yMin: viewPort.yMin - 1, yMax: viewPort.yMax - 1}
| East => {...viewPort, xMin: viewPort.xMin + 1, xMax: viewPort.xMax + 1}
| South => {...viewPort, yMin: viewPort.yMin + 1, yMax: viewPort.yMax + 1}
| West => {...viewPort, xMin: viewPort.xMin - 1, xMax: viewPort.xMax - 1}
};
let zoomOut = viewPort => {
xMin: viewPort.xMin - 1,
xMax: viewPort.xMax + 1,
yMin: viewPort.yMin - 1,
yMax: viewPort.yMax + 1,
};
let zoomIn = viewPort =>
if (viewPort.xMax > viewPort.xMin + 2) {
{
xMin: viewPort.xMin + 1,
xMax: viewPort.xMax - 1,
yMin: viewPort.yMin + 1,
yMax: viewPort.yMax - 1,
};
} else {
viewPort;
};
};
module Position = {
type t = (int, int);
};
type cell =
| Alive
| Dead;
module Universe = {
module T =
Map.Make({
type t = Position.t;
let compare = compare;
});
include T;
let universeFromList = positions =>
List.fold_left(
(acc, pos) => T.add(pos, Alive, acc),
T.empty,
positions,
);
};
module Examples = {
let blinker = Universe.universeFromList([(5, 4), (5, 5), (5, 6)]);
let pulsar =
Universe.universeFromList([
(4, 2),
(5, 2),
(6, 2),
(10, 2),
(11, 2),
(12, 2),
(2, 4),
(7, 4),
(9, 4),
(14, 4),
(2, 5),
(7, 5),
(9, 5),
(14, 5),
(2, 6),
(7, 6),
(9, 6),
(14, 6),
(4, 7),
(5, 7),
(6, 7),
(10, 7),
(11, 7),
(12, 7),
(4, 9),
(5, 9),
(6, 9),
(10, 9),
(11, 9),
(12, 9),
(2, 10),
(7, 10),
(9, 10),
(14, 10),
(2, 11),
(7, 11),
(9, 11),
(14, 11),
(2, 12),
(7, 12),
(9, 12),
(14, 12),
(4, 14),
(5, 14),
(6, 14),
(10, 14),
(11, 14),
(12, 14),
]);
};
module GameOfLife = {
/* helper function for options */
let withDefault = (opt, ~default) =>
switch (opt) {
| Some(x) => x
| None => default
};
let findCell = (universe, position) =>
Universe.find_opt(position, universe)
|> withDefault(~default=Dead)
|> (cell => (position, cell));
let numberOfLive = neighbours =>
neighbours
|> List.filter(x =>
switch (x) {
| Alive => true
| _ => false
}
)
|> List.length;
type lifeCycle =
| Dies
| Revives
| Same;
let underPopulationRule = (cell, neighbours) =>
switch (cell) {
| Alive =>
if (numberOfLive(neighbours) < 2) {
Dies;
} else {
Same;
}
| Dead => Same
};
let livesOnRule = (cell, neighbours) =>
switch (cell) {
| Alive =>
let numberOfLiveNeighbours = numberOfLive(neighbours);
if (numberOfLiveNeighbours == 2 || numberOfLiveNeighbours == 3) {
Same;
} else {
Dies;
};
| Dead => Same
};
let overPopulationRule = (cell, neighbours) =>
switch (cell) {
| Alive =>
if (numberOfLive(neighbours) > 3) {
Dies;
} else {
Same;
}
| Dead => Same
};
let reproductionRule = (cell, neighbours) =>
switch (cell) {
| Alive => Same
| Dead =>
if (numberOfLive(neighbours) == 3) {
Revives;
} else {
Same;
}
};
let reduceLifeCycle = (cell, neighbours) => {
let actions = [
underPopulationRule(cell, neighbours),
livesOnRule(cell, neighbours),
overPopulationRule(cell, neighbours),
reproductionRule(cell, neighbours),
];
let reducedLifeCycle =
actions |> List.filter((!=)(Same)) |> (l => List.nth_opt(l, 0));
withDefault(reducedLifeCycle, ~default=Same);
};
let applyRules = (cell, neighbours) => {
let action = reduceLifeCycle(cell, neighbours);
switch (action) {
| Dies => Dead
| Revives => Alive
| Same => cell
};
};
let neighbourPositions = ((x, y)) => [
(x - 1, y - 1),
(x, y - 1),
(x + 1, y - 1),
(x - 1, y),
(x + 1, y),
(x - 1, y + 1),
(x, y + 1),
(x + 1, y + 1),
];
let evolveCell = (universe, (position, cell)) => {
let neighbours =
List.map(
position => findCell(universe, position) |> snd,
neighbourPositions(position),
);
let evolvedCell = applyRules(cell, neighbours);
(position, evolvedCell);
};
let evolve = universe => {
let find_relevant_cells = position =>
neighbourPositions(position) |> List.map(findCell(universe));
let keys = Universe.bindings(universe) |> List.map(fst);
keys
|> List.map(find_relevant_cells)
|> List.concat
|> List.sort_uniq(compare)
|> List.map(evolveCell(universe))
|> List.filter(x => snd(x) |> (==)(Alive))
|> List.map(fst)
|> Universe.universeFromList;
};
};
module Row = {
let style =
Style.[
flexDirection(`Row),
alignItems(`Stretch),
justifyContent(`Center),
backgroundColor(Colors.darkGrey),
flexGrow(1),
];
let make = (~children, ()) => ...children ;
};
module Column = {
let style =
Style.[
flexDirection(`Column),
alignItems(`Stretch),
justifyContent(`Center),
flexGrow(1),
];
let make = (~children, ()) => ...children ;
};
module Cell = {
let clickableStyle =
Style.[
position(`Relative),
backgroundColor(Colors.transparentWhite),
justifyContent(`Center),
alignItems(`Stretch),
flexGrow(1),
margin(0),
];
let baseStyle =
Style.[
flexDirection(`Column),
alignItems(`Stretch),
justifyContent(`Center),
flexGrow(1),
];
let aliveStyle =
Style.(merge(~source=baseStyle, ~target=[backgroundColor(Colors.red)]));
let deadStyle =
Style.(
merge(~source=baseStyle, ~target=[backgroundColor(Colors.black)])
);
let make = (~cell, ~onClick, ()) => {
let style =
switch (cell) {
| Alive =>
| Dead =>
};
style ;
};
};
let viewPortRender =
(viewPort: ViewPort.t, universe: Universe.t(cell), onClick) => {
let cell = pos =>
switch (Universe.find_opt(pos, universe)) {
| Some(_) => Alive
| None => Dead
};
let rec range = (min, max, result) =>
if (min == max) {
result;
} else {
range(min, max - 1, [max, ...result]);
};
let cols = range(viewPort.xMin, viewPort.xMax, []);
let rows = range(viewPort.yMin, viewPort.yMax, []);
List.map(
x =>
{List.map(
y =>
| onClick((x, y))} />
|
,
rows,
)
|> React.listToElement}
,
cols,
);
};
type state = {
viewPort: ViewPort.t,
universe: Universe.t(cell),
isRunning: bool,
dispose,
}
and dispose = unit => unit;
type action =
| TimerTick(Time.t)
| StartTimer(dispose)
| StopTimer
| ToggleAlive(Position.t)
| MoveViewPort(ViewPort.direction)
| ZoomViewPort(zoom)
and zoom =
| ZoomIn
| ZoomOut;
let noop = () => ();
let reducer = (action, state) =>
switch (action) {
| TimerTick(_t) => {...state, universe: GameOfLife.evolve(state.universe)}
| StartTimer(dispose) => {...state, dispose, isRunning: true}
| StopTimer =>
state.dispose();
{...state, dispose: noop, isRunning: false};
| ToggleAlive(position) => {
...state,
universe:
Universe.update(
position,
cell =>
switch (cell) {
| Some(Alive) => None
| _ => Some(Alive)
},
state.universe,
),
}
| MoveViewPort(direction) => {
...state,
viewPort: ViewPort.changeDirection(state.viewPort, direction),
}
| ZoomViewPort(zoom) => {
...state,
viewPort:
switch (zoom) {
| ZoomIn => ViewPort.zoomIn(state.viewPort)
| ZoomOut => ViewPort.zoomOut(state.viewPort)
},
}
};
module GameOfLiveComponent = {
let controlsStyle = Style.[height(120), flexDirection(`Row)];
let%component make = (~state, ()) => {
let%hook (state, dispatch) = Hooks.reducer(~initialState=state, reducer);
let%hook () =
Hooks.effect(OnMount, () => Some(() => dispatch(StopTimer)));
let toggleAlive = pos => dispatch(ToggleAlive(pos));
let startStop = () =>
state.isRunning
? dispatch(StopTimer)
: {
let dispose =
Tick.interval(
~name="GameOfLife Timer",
t => dispatch(TimerTick(t)),
Time.zero,
);
dispatch(StartTimer(dispose));
};
let iconFamily = Font.Family.fromFile("FontAwesome5FreeSolid.otf");
{viewPortRender(state.viewPort, state.universe, toggleAlive)
|> React.listToElement}
dispatch(MoveViewPort(North))}
/>
dispatch(MoveViewPort(South))}
/>
dispatch(MoveViewPort(East))}
/>
dispatch(MoveViewPort(West))}
/>
dispatch(ZoomViewPort(ZoomIn))}
/>
dispatch(ZoomViewPort(ZoomOut))}
/>
;
};
};
let render = () => {
let viewPort = ViewPort.create(15);
let state = {
viewPort,
universe: Examples.pulsar,
isRunning: false,
dispose: noop,
};
;
};
================================================
FILE: examples/Hello.re
================================================
open Revery;
open Revery.Math;
open Revery.UI;
open Revery.UI.Components;
module RepoLink = {
let make = () => {
let activeStyle = Style.[color(Colors.blue)];
let inactiveStyle = Style.[color(Colors.lightBlue)];
;
};
};
module Logo = {
let rotationAnimation =
Animation.(
zip((
animate(Time.seconds(9))
|> tween(0., 6.28)
|> repeat
|> delay(Time.seconds(1)),
animate(Time.seconds(4))
|> tween(0., 6.28)
|> repeat
|> delay(Time.ms(500)),
))
);
let%component make = () => {
let%hook (shouldRotate, setShouldRotate) = Hooks.state(true);
let%hook ((rotationX, rotationY), _animationState, resetRotation) =
Hooks.animation(
~name="Rotation Animation",
rotationAnimation,
~active=shouldRotate,
);
let%hook (opacity, setOpacity) = Hooks.state(1.0);
let%hook transitionedOpacity =
Hooks.transition(~name="Opacity Transition Animation", opacity);
setShouldRotate((!))}
title={shouldRotate ? "Pause" : "Resume"}
/>
{
setShouldRotate(_ => true);
resetRotation();
}}
title="Restart"
/>
setOpacity(_ => 1.0)}
title="Show it"
/>
setOpacity(_ => 0.0)}
title="Hide it"
/>
;
};
};
module AnimatedText = {
let%component make = (~text: string, ~delay: Time.t, ()) => {
let%hook (animatedOpacity, _state, _reset) =
Hooks.animation(
~name="Text Opacity Animation",
Animation.(
animate(Time.seconds(1))
|> delay(Time.seconds(1))
|> ease(Easing.easeOut)
|> tween(0., 1.)
),
);
let%hook (translate, _state, _reset) =
Hooks.animation(
~name="Text Translate Animation",
Animation.animate(Time.ms(500))
|> Animation.delay(delay)
|> Animation.ease(Easing.easeOut)
|> Animation.tween(50., 0.),
);
let textHeaderStyle =
Style.[
color(Colors.white),
marginHorizontal(8),
transform([Transform.TranslateY(translate)]),
];
;
};
};
let render = () =>
;
================================================
FILE: examples/HitTests.re
================================================
open Revery;
open Revery.UI;
module Styles = {
open Style;
let outer = [
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
justifyContent(`Center),
alignItems(`Center),
];
let draggable = [
width(100),
height(100),
backgroundColor(Colors.blue),
justifyContent(`Center),
alignItems(`Center),
];
let text = Style.[color(Colors.white)];
};
let hitTests = (~window, ()) =>
{Window.isDecorated(window)
?
:
}
;
let render = window => ;
================================================
FILE: examples/HoverExample.re
================================================
open Revery;
open Revery.UI;
type state = {
parentBackground: Color.t,
childOneBackground: Color.t,
childTwoBackground: Color.t,
childThreeBackground: Color.t,
childFourBackground: Color.t,
};
type action =
| SetParentBackground(Color.t)
| SetChildOneBackground(Color.t)
| SetChildTwoBackground(Color.t)
| SetChildThreeBackground(Color.t)
| SetChildFourBackground(Color.t);
let reducer = (action: action, state) => {
switch (action) {
| SetParentBackground(c) => {...state, parentBackground: c}
| SetChildOneBackground(c) => {...state, childOneBackground: c}
| SetChildTwoBackground(c) => {...state, childTwoBackground: c}
| SetChildThreeBackground(c) => {...state, childThreeBackground: c}
| SetChildFourBackground(c) => {...state, childFourBackground: c}
};
};
module HoverExample = {
let initialState = {
parentBackground: Colors.darkGray,
childOneBackground: Colors.blanchedAlmond,
childTwoBackground: Colors.blueViolet,
childThreeBackground: Colors.burlyWood,
childFourBackground: Colors.cornflowerBlue,
};
let%component make = () => {
let%hook (state, dispatch) = Hooks.reducer(~initialState, reducer);
dispatch(SetParentBackground(Colors.lightGray))}
onMouseOut={_ =>
dispatch(SetParentBackground(initialState.parentBackground))
}>
dispatch(SetChildOneBackground(Colors.aquamarine))
}
onMouseLeave={_ =>
dispatch(SetChildOneBackground(initialState.childOneBackground))
}
/>
dispatch(SetChildTwoBackground(Colors.forestGreen))
}
onMouseOut={_ =>
dispatch(SetChildTwoBackground(initialState.childTwoBackground))
}
/>
dispatch(SetChildThreeBackground(Colors.darkSalmon))
}
onMouseOut={_ =>
dispatch(
SetChildThreeBackground(initialState.childThreeBackground),
)
}
/>
dispatch(SetChildFourBackground(Colors.tomato))}
onMouseLeave={_ =>
dispatch(
SetChildFourBackground(initialState.childFourBackground),
)
}
/>
;
};
};
let render = () => {
;
};
================================================
FILE: examples/ImageQualityExample.re
================================================
open Revery.UI;
module ImageSample = {
let make = (~quality, ()) => {
;
};
};
module Images = {
let make = () => {
;
};
};
let render = () => ;
================================================
FILE: examples/Info.plist
================================================
CFBundleIdentifier
com.revery.examples
================================================
FILE: examples/InputExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];
let controlsStyle =
Style.[
margin(10),
flexDirection(`Row),
justifyContent(`Center),
alignItems(`Center),
];
let textStyle =
Style.[
color(Colors.white),
width(100),
margin(14),
textWrap(TextWrapping.NoWrap),
];
module Example = {
type inputFields = {
first: string,
second: string,
third: string,
isPassword: bool,
};
let%component make = () => {
let%hook ({first, isPassword, _}, setValue) =
Hooks.state({first: "", second: "", third: "", isPassword: false});
setValue(state => {...state, first: value})
}
value=first
/>
setValue(state => {...state, first: ""})}
/>
setValue(state => {...state, first: "New value"})}
/>
Native.Input.openEmojiPanel()}
/>
setValue(state => {...state, first: value})
}
value=first
isPassword
/>
setValue(state => {...state, isPassword: !state.isPassword})
}
style=Style.[border(~width=2, ~color=Colors.green)]
checked=isPassword
/>
print_endline("Input example focused")}
onBlur={() => print_endline("Input example blurred")}
onChange={(value, _) =>
setValue(state => {...state, second: value})
}
onKeyDown={_ => print_endline("key event")}
style=Style.[
backgroundColor(Colors.paleVioletRed),
color(Colors.white),
height(50),
]
/>
;
};
};
let render = () => ;
================================================
FILE: examples/LayerExample.re
================================================
open Revery.UI;
module Layers = {
let make = () => {
;
};
};
let render = () => ;
================================================
FILE: examples/MarkdownExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module Styles = {
open Style;
let outer = [
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
margin(6),
justifyContent(`FlexStart),
alignItems(`FlexStart),
];
let whiteText = [color(Colors.white)];
let linkActive = [color(Colors.blue)];
let linkInactive = [color(Colors.lightBlue)];
};
let markdown =
Markdown.make(
~paragraphStyle=Styles.whiteText,
~h1Style=Styles.whiteText,
~h2Style=Styles.whiteText,
~h3Style=Styles.whiteText,
~h4Style=Styles.whiteText,
~h5Style=Styles.whiteText,
~h6Style=Styles.whiteText,
~activeLinkStyle=Styles.linkActive,
~inactiveLinkStyle=Styles.linkInactive,
);
let example = () =>
;
let render = () => ;
================================================
FILE: examples/NativeFileExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
open Revery.Native;
module NativeFileExamples = {
let%component make = () => {
let%hook (fileListOpt, setFileListOpt) = Hooks.state(None);
let%hook (allowMultiple, setAllowMultiple) = Hooks.state(false);
let%hook (showHidden, setShowHidden) = Hooks.state(false);
let%hook (openFolders, setOpenFolders) = Hooks.state(false);
let openFile = () => {
let o =
Dialog.openFiles(
~allowMultiple,
~canChooseDirectories=openFolders,
~showHidden,
~title="Revery Open File Example",
~buttonText=
allowMultiple ? "Open file(s) in Revery" : "Open file in Revery",
(),
);
setFileListOpt(_ => o);
};
let optionStyle = Style.[color(Colors.white)];
let titleStyle = Style.[color(Colors.white)];
let renderFilePath = (path: string) =>
;
let containerStyle =
Style.[
position(`Absolute),
justifyContent(`Center),
alignItems(`Center),
bottom(0),
top(0),
left(0),
right(0),
];
setAllowMultiple(am => !am)}
/>
setShowHidden(sh => !sh)}
/>
setOpenFolders(ofv => !ofv)}
/>
{switch (fileListOpt) {
| Some(fileList) =>
fileList
|> Array.map(renderFilePath)
|> Array.to_list
|> React.listToElement
| None =>
}}
;
};
};
let render = () => ;
================================================
FILE: examples/NativeIconExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
open Revery.Native;
module Window = Revery.Window;
module NativeFileExamples = {
let%component make = (~window as w, ()) => {
let%hook (maybeIcon, setMaybeIcon) = Hooks.state(None);
let%hook (progress, setIconProgress) =
Hooks.state(Icon.Determinate(0.));
let%hook () =
Hooks.effect(
OnMount,
() => {
let ih = Icon.get();
setMaybeIcon(_ => Some(ih));
Some(() => Icon.hideProgress(w |> Window.getSdlWindow, ih));
},
);
let%hook () =
Hooks.effect(
If((!=), progress),
() => {
maybeIcon
|> Option.iter(icon =>
Icon.setProgress(w |> Window.getSdlWindow, icon, progress)
);
None;
},
);
let optionStyle = Style.[color(Colors.white)];
let titleStyle = Style.[color(Colors.white)];
let containerStyle =
Style.[
position(`Absolute),
justifyContent(`Center),
alignItems(`Center),
bottom(0),
top(0),
left(0),
right(0),
];
setIconProgress(_ => Icon.Determinate(x))}
maximumValue=1.0
/>
"Indeterminate"
| Determinate(n) => string_of_float(n)
}
)
}
/>
setIconProgress(_ => Icon.Indeterminate)}
/>
;
};
};
let render = window => ;
================================================
FILE: examples/NativeInputExample.re
================================================
open Revery;
open Revery.UI;
module View = {
let noop = () => ();
let%component make = () => {
let%hook (isColumn, setIsColumn) = Hooks.state(true);
let containerStyle =
Style.[
position(`Absolute),
justifyContent(`Center),
alignItems(`Center),
flexDirection(isColumn ? `Column : `Row),
bottom(0),
top(0),
left(0),
right(0),
];
setIsColumn(ic => !ic)}
style=Style.[flexGrow(1), color(Colors.teal)]
onMouseEnter={_ => print_endline("Mouse Entered!")}
/>
print_endline("You clicked a button!")}
/>
;
};
};
let render = () => ;
================================================
FILE: examples/NativeMenuExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
open Revery.Native;
module Internal = {
let nonce = ref(0);
};
module View = {
let make = (~window, ()) => {
let onClickCreateMenu = () => {
incr(Internal.nonce);
let currentNonce = Internal.nonce^;
let startTime = Time.now();
let menuBar = Menu.getMenuBarHandle();
Menu.clear(menuBar);
let menu1 = Menu.create("Test 1");
Menu.addSubmenu(~parent=menuBar, ~child=menu1);
let menuCallback = (str, ~fromKeyPress, ()) => {
print_endline(
Printf.sprintf(
"%s clicked: %d (from key press: %b)",
str,
currentNonce,
fromKeyPress,
),
);
};
let item11 =
Menu.Item.create(
~title="Item 1.1",
~onClick=menuCallback("Item 1.1"),
~keyEquivalent=Menu.KeyEquivalent.ofString("a"),
(),
);
Menu.addItem(menu1, item11);
let menu2 = Menu.create("Test 2");
Menu.addSubmenu(~parent=menuBar, ~child=menu2);
let item12 =
Menu.Item.create(
~title="Item 1.2",
~onClick=menuCallback("Item 1.2"),
~keyEquivalent=
Menu.KeyEquivalent.ofString("b")
|> (ke => Menu.KeyEquivalent.enableShift(ke, true)),
(),
);
let item13 =
Menu.Item.create(
~title="Item 1.3",
~onClick=menuCallback("Item 1.3"),
~keyEquivalent=
Menu.KeyEquivalent.ofString("Space")
|> (ke => Menu.KeyEquivalent.enableCtrl(ke, true)),
(),
);
let item14 =
Menu.Item.create(
~title="Item 1.4",
~onClick=menuCallback("Item 1.4"),
~keyEquivalent=Menu.KeyEquivalent.ofString("ESC"),
(),
);
let item15 =
Menu.Item.create(
~title="Item 1.5",
~onClick=menuCallback("Item 1.5"),
~keyEquivalent=Menu.KeyEquivalent.ofString("Tab"),
(),
);
Menu.addItem(menu2, item12);
Menu.addItem(menu2, item13);
Menu.addItem(menu2, item14);
Menu.addItem(menu2, item15);
let menu3 = Menu.create("Test 3");
Menu.addSubmenu(~parent=menuBar, ~child=menu3);
let subMenu31 = Menu.create("Submenu 1");
Menu.addSubmenu(~parent=menu3, ~child=subMenu31);
let item311 =
Menu.Item.create(
~title="Item 3.1.1",
~onClick=menuCallback("Item 3.1.1"),
~keyEquivalent=
Menu.KeyEquivalent.ofString("c")
|> (ke => Menu.KeyEquivalent.enableAlt(ke, true)),
(),
);
let separator = Menu.Item.createSeparator();
let item312 =
Menu.Item.create(
~title="Item 3.1.2",
~onClick=menuCallback("Item 3.1.2"),
(),
);
Menu.Item.setEnabled(item312, false);
let item313 =
Menu.Item.create(
~title="Item 3.1.3",
~onClick=menuCallback("Item 3.1.3"),
(),
);
Menu.Item.setVisible(item313, false); // Shouldn't show up
Menu.addItem(subMenu31, item311);
Menu.addItem(subMenu31, separator);
Menu.addItem(subMenu31, item312);
Menu.addItem(subMenu31, item313);
let endTime = Time.now();
let delta = Time.(endTime - startTime) |> Time.toFloatSeconds;
print_endline("Render time: " ++ string_of_float(delta));
};
let onMouseUp = (evt: NodeEvents.mouseButtonEventParams) =>
if (evt.button == MouseButton.BUTTON_RIGHT) {
let menu = Menu.create("Right click");
let item1 =
Menu.Item.create(
~title="Click me 1",
~onClick=
(~fromKeyPress as _, ()) => print_endline("You clicked me!"),
(),
);
let item2 =
Menu.Item.create(
~title="Click me 2",
~onClick=
(~fromKeyPress as _, ()) => print_endline("You clicked me!"),
(),
);
Menu.addItem(menu, item1);
Menu.addItem(menu, item2);
Menu.displayIn(
~x=int_of_float(evt.mouseX),
~y=int_of_float(evt.mouseY),
menu,
window |> Revery.Window.getSdlWindow,
);
};
let containerStyle =
Style.[
position(`Absolute),
justifyContent(`Center),
alignItems(`Center),
bottom(0),
top(0),
left(0),
right(0),
];
;
};
};
let render = window => ;
================================================
FILE: examples/NativeNotificationExample.re
================================================
open Revery.UI;
open Revery.UI.Components;
open Revery.Native;
module Example = {
let make = () => {
let notification =
Notification.create(
~title="Revery Test",
~body="This is a test!",
~mute=false,
~onClick=() => print_endline("Notification clicked!"),
(),
);
Notification.dispatch(notification)}
/>
Notification.scheduleFromNow(5, notification)}
/>
;
};
};
let render = () => ;
================================================
FILE: examples/NestedClickable.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module Styles = {
open Style;
let outer = [
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
justifyContent(`Center),
alignItems(`Center),
backgroundColor(Colors.yellow),
];
};
let%component clickies = () => {
let%hook (text, setText) = Hooks.state("Click something");
setText(_ => "Clicked outside")}>
setText(_ => "Clicked inside")} />
;
};
let render = () => ;
================================================
FILE: examples/RadioButtonExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module RadioExample = {
let%component make = () => {
let%hook (radioVal, setRadioVal) = Hooks.state("Select a button!");
setRadioVal(_ => "Radio Button Value: " ++ string_of_int(txt))
}
defaultSelected=0
iconSize=20.
buttons=[
{text: "Button 1", value: 1},
{text: "Button 2", value: 2},
{text: "Button 3", value: 3},
{text: "Button 4", value: 4},
]
/>
;
};
};
let render = () => ;
================================================
FILE: examples/RichTextExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
open Revery.Font;
let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];
module SampleRichText = {
let make = () => {
let richtext =
RichText.(
text("Hello ", ~color=Colors.red, ~fontWeight=Weight.Bold)
++ text("world", ~color=Colors.green)
++ text("!", ~color=Colors.yellow)
|> fontSize(20.)
|> italic
);
let dimensions = RichText.measure(richtext);
let widthText = "Width: " ++ string_of_int(dimensions.width);
let heightText = "Height: " ++ string_of_int(dimensions.height);
;
};
};
let render = () => ;
================================================
FILE: examples/SVGExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
let examples = [
(
"",
{|
|},
),
(
"",
{|
|},
),
(
"",
{|
|},
),
(
" - heart",
{|
|},
),
(
" - moveTo",
{|
|},
),
(
" - lineTo",
{|
|},
),
(
" - cubic bezier",
{|
|},
),
(
" - quadratic bezier",
{|
|},
),
(
" - quadratic bezier*",
{|
|},
),
(
" - quadratic bezier 2",
{|
Example quad01 - quadratic Bézier commands in path data
Picture showing a "Q" a "T" command,
along with annotations showing the control points
and end points
|},
),
(
" - elliptic arc",
{|
|},
),
(
" - close path",
{|
|},
),
(
"Material - disc",
{|
|},
),
(
"Material - flash",
{|
|},
),
(
"Onivim logo",
{|
logo
|},
),
];
module SVGExample = {
module Styles = {
open Style;
let container = [
backgroundColor(Color.rgba(1., 1., 1., 0.1)),
flexDirection(`Row),
];
let buttons = [width(200), color(Colors.white), marginLeft(10)];
};
let%component make = () => {
let%hook (currentExample, setExample) = {
let (_, data) = List.hd(examples);
Hooks.state(data);
};
let%hook (width, setWidth) = Hooks.state(300.);
let%hook (height, setHeight) = Hooks.state(300.);
let buttons =
List.map(
((text, value)) => RadioButtonsString.{text, value},
examples,
);
let onChange = data => setExample(_ => data);
setWidth(_ => w)}
maximumValue=300.
minimumValue=50.
initialValue=width
/>
setHeight(_ => h)}
maximumValue=500.
minimumValue=50.
initialValue=height
/>
;
};
};
let render = () => ;
================================================
FILE: examples/ScreenCapture.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
let backgroundColor = Color.hex("#212733");
let activeBackgroundColor = Color.hex("#2E3440");
let inactiveBackgroundColor = Color.hex("#272d39");
let selectionHighlight = Color.hex("#90f7ff");
module ActionButton = {
let make = (~name, ~onClick, ()) => {
let wrapperStyle =
Style.[
backgroundColor(selectionHighlight),
border(~width=4, ~color=activeBackgroundColor),
];
let textHeaderStyle = Style.[color(Colors.black), margin(16)];
;
};
};
module CaptureArea = {
let%component make = (~w, ()) => {
let%hook (count, setCount) = Hooks.state(0);
let%hook (file, setFile) = Hooks.state(None);
let capture = () => {
let exed = Environment.getExecutingDirectory();
let filename = Printf.sprintf("Scrot_%d.tga", count);
let fullname = exed ++ filename;
Window.takeScreenshot(w, fullname);
setCount(_ => count + 1);
setFile(_ => Some(filename));
};
let viewStyle =
Style.[
position(`Absolute),
left(0),
right(0),
top(0),
bottom(0),
flexDirection(`Column),
];
let imageStyle = Style.[width(400), height(300)];
{switch (file) {
| None =>
| Some(src) =>
}}
;
};
};
let render = w => ;
================================================
FILE: examples/ScrollView.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];
let outerBox =
Style.[width(200), height(200), backgroundColor(Colors.black)];
let innerBox =
Style.[
width(450),
height(450),
backgroundColor(Color.rgba(0., 1., 0., 0.5)),
];
module Sample = {
let%component make = () => {
let%hook (bounce, setBounce) = Hooks.state(true);
setBounce(isBounce => !isBounce)}
checked=bounce
style=Style.[marginBottom(10)]
/>
;
};
};
let render = () => ;
================================================
FILE: examples/Slider.re
================================================
open Revery;
open Revery.Math;
open Revery.UI;
open Revery.UI.Components;
module AdjustableLogo = {
let%component make = () => {
let%hook (rotationX, setRotationX) =
Hooks.reducer(~initialState=0., (value, _) => value);
let%hook (rotationY, setRotationY) =
Hooks.reducer(~initialState=0., (value, _) => value);
let%hook (rotationZ, setRotationZ) =
Hooks.reducer(~initialState=0., (value, _) => value);
let containerStyle =
Style.[
flexGrow(1),
justifyContent(`Center),
alignItems(`Center),
flexDirection(`Column),
];
let textStyle = Style.[color(Colors.white), width(100), margin(14)];
let controlsStyle =
Style.[
margin(10),
flexDirection(`Row),
justifyContent(`Center),
alignItems(`Center),
];
let sliderContainerStyle =
Style.[
margin(10),
borderBottom(~width=1, ~color=Colors.darkGray),
flexDirection(`Row),
justifyContent(`Center),
alignItems(`Center),
];
let verticalSliderContainerStyle =
Style.[
margin(10),
borderRight(~width=1, ~color=Colors.darkGray),
flexDirection(`Column),
justifyContent(`Center),
alignItems(`Center),
];
let toDeg = r => 180. *. r /. pi;
let toDegString = r => r |> toDeg |> floor |> string_of_float;
let twoPi = 2. *. pi;
;
};
};
let render = () => ;
================================================
FILE: examples/SpringExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module SpringyLogo = {
let make = (~width, ~onMouseDown, ~onMouseUp, ()) => {
let intWidth = int_of_float(width);
;
};
};
module SliderControl = {
module Styles = {
let sliderContainerStyle =
Style.[
margin(10),
borderBottom(~width=1, ~color=Colors.darkGray),
flexDirection(`Row),
justifyContent(`Center),
alignItems(`Center),
];
let textStyle = Style.[color(Colors.white), width(100), margin(14)];
};
let float_rounded_string = v => v |> int_of_float |> string_of_int;
let make =
(~text, ~minimumValue, ~maximumValue, ~value, ~onValueChanged, ()) => {
onValueChanged(v)}
value
minimumValue
maximumValue
/>
;
};
};
module Example = {
module Styles = {
let containerStyle =
Style.[
flexGrow(1),
justifyContent(`Center),
alignItems(`Center),
flexDirection(`Column),
];
};
let%component make = () => {
let%hook (stiffness, setStiffness) =
Hooks.reducer(~initialState=160., (value, _) => value);
let%hook (damping, setDamping) =
Hooks.reducer(~initialState=10., (value, _) => value);
let%hook (targetPosition, setTargetPosition) =
Hooks.reducer(~initialState=256.0, (value, _) => value);
let%hook (logoWidth, setImmediately) =
Hooks.spring(
~name="Spring",
~target=targetPosition,
~initialState=
Spring.{
value: 256.,
velocity: 100.,
acceleration: 0.,
time: Time.now(),
},
~restThreshold=1.0,
Spring.Options.create(~damping, ~stiffness, ()),
);
let setImmediately = value => {
setTargetPosition(value);
setImmediately(value);
};
let onMouseDown = _ => {
setTargetPosition(512.0);
};
let onMouseUp = _ => setTargetPosition(256.0);
setDamping(v)}
/>
setStiffness(v)}
/>
setImmediately(v)}
/>
;
};
};
let render = () => ;
================================================
FILE: examples/Stopwatch.re
================================================
open Revery;
open Revery.Math;
open Revery.UI;
open Revery.UI.Components;
module Clock = {
type dispose = unit => unit;
let noop = () => ();
type state = {
dispose,
isRunning: bool,
elapsedTime: Time.t,
};
type action =
| Start(dispose)
| Stop
| TimerTick(Time.t);
let reducer = (a, s) =>
switch (a) {
| Start(f) => {dispose: f, isRunning: true, elapsedTime: Time.zero}
| Stop =>
s.dispose();
let ret = {dispose: noop, isRunning: false, elapsedTime: Time.zero};
ret;
| TimerTick(t) => {
...s,
elapsedTime: s.isRunning ? Time.(s.elapsedTime + t) : s.elapsedTime,
}
};
let%component make = () => {
let%hook (state, dispatch) =
Hooks.reducer(
~initialState={
isRunning: false,
dispose: noop,
elapsedTime: Time.zero,
},
reducer,
);
/*
* We'll make sure to dispatch the 'Stop' action when unmounting,
* so we don't have a runaway timer!
*/
let%hook () = Hooks.effect(OnMount, () => Some(() => dispatch(Stop)));
let startStop = () =>
state.isRunning
? dispatch(Stop)
/*
* If we're not already running, we'll start a timer job
* and use the delta time it passes to update our reducer.
*/
: {
let dispose =
Tick.interval(
~name="Stopwatch Interval",
t => dispatch(TimerTick(t)),
Time.zero,
);
/* We'll also keep a handle on the dispose function so we can make sure its called on stop*/
dispatch(Start(dispose));
};
let buttonText = state.isRunning ? "STOP" : "START";
let marcherOpacity = state.isRunning ? 1.0 : 0.0;
let getMarcherPosition = t =>
sin(Time.toFloatSeconds(t) *. 2. *. pi) /. 2. +. 0.5;
;
};
};
let render = () => ;
================================================
FILE: examples/TextExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`SpaceAround),
flexDirection(`Column),
];
let slidersViewStyle = Style.[height(300)];
let textStyle =
Style.[
color(Colors.white),
width(100),
margin(14),
textWrap(TextWrapping.NoWrap),
];
let controlsStyle =
Style.[
margin(10),
flexDirection(`Row),
justifyContent(`Center),
alignItems(`Center),
];
let overflowStyles = (style, textWidth) =>
Style.[
color(Colors.white),
width(textWidth),
textOverflow(style),
lineHeight(1.5),
border(~color=Colors.blueViolet, ~width=1),
backgroundColor(Colors.black),
];
module SampleText = {
let%component make = () => {
let%hook (fontSizeSliderVal, setFontSize) =
Hooks.reducer(~initialState=20., (value, _) => value);
let%hook (widthSliderVal, setWidth) =
Hooks.reducer(~initialState=200., (value, _) => value);
let%hook (hyphenate, setHyphenate) = Hooks.state(false);
let textContent =
"All work and no play makes Jack a dull boy. "
++ "The quick brown fox jumps over the lazy dog.";
let maxFontSize = 40.;
let maxWidth = 400.;
let textFontSize = fontSizeSliderVal;
let textWidth = int_of_float(widthSliderVal);
let wrapping =
if (hyphenate) {TextWrapping.WrapHyphenate} else {TextWrapping.Wrap};
setFontSize(v)}
value=fontSizeSliderVal
maximumValue=maxFontSize
/>
string_of_float)}
/>
setWidth(w)}
value=widthSliderVal
maximumValue=maxWidth
/>
string_of_float)}
/>
setHyphenate(h => !h)}
style=Style.[border(~width=2, ~color=Colors.green)]
checked=hyphenate
/>
;
};
};
let render = () => ;
================================================
FILE: examples/TodoExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module Constants = {
let fontSize = 12.;
};
module Theme = {
let fontSize = 16.;
let rem = factor => fontSize *. factor;
let remi = factor => rem(factor) |> int_of_float;
let appBackground = Color.hex("#f4edfe");
let textColor = Color.hex("#513B70");
let dimmedTextColor = Color.hex("#DAC5F7");
let titleTextColor = Color.hex("#EADDFC");
let panelBackground = Color.hex("#F9F5FF");
let panelBorderColor = Color.hex("#EADDFC");
let panelBorder = Style.border(~width=1, ~color=panelBorderColor);
let buttonColor = Color.hex("#9573C4");
let hoveredButtonColor = Color.hex("#C9AEF0");
let dangerColor = Color.hex("#f7c5c6");
};
module Filter = {
type t =
| All
| Active
| Completed;
let toString = (v: t) =>
switch (v) {
| All => "All"
| Active => "Active"
| Completed => "Completed"
};
};
module Button = {
module Styles = {
let box = (~isSelected, ~isHovered) =>
Style.[
position(`Relative),
justifyContent(`Center),
alignItems(`Center),
paddingVertical(Theme.remi(0.15)),
paddingHorizontal(Theme.remi(0.5)),
marginHorizontal(Theme.remi(0.2)),
border(
~width=1,
~color=
switch (isSelected, isHovered) {
| (true, _) => Theme.buttonColor
| (false, true) => Theme.hoveredButtonColor
| (false, false) => Colors.transparentWhite
},
),
borderRadius(2.),
];
let text =
Style.[color(Theme.buttonColor), textWrap(TextWrapping.NoWrap)];
};
let%component make =
(
~label,
~onClick=?,
~isSelected=false,
~tabindex=?,
~onFocus=?,
~onBlur=?,
(),
) => {
let%hook (isHovered, onMouseOver, onMouseOut) = Hooks.hover();
;
};
};
module Checkbox = {
module Styles = {
let box =
Style.[
width(Theme.remi(1.5)),
height(Theme.remi(1.5)),
justifyContent(`Center),
alignItems(`Center),
Theme.panelBorder,
];
let checkmark =
Style.[
color(Theme.hoveredButtonColor),
textWrap(TextWrapping.NoWrap),
transform(Transform.[TranslateY(2.)]),
];
};
let make = (~isChecked, ~onToggle, ()) => {
;
};
};
module AddTodo = {
module Styles = {
let container =
Style.[
flexDirection(`Row),
backgroundColor(Theme.panelBackground),
Theme.panelBorder,
margin(2),
alignItems(`Center),
overflow(`Hidden),
];
let toggleAll = areAllCompleted =>
Style.[
color(areAllCompleted ? Theme.textColor : Theme.dimmedTextColor),
transform(Transform.[TranslateY(2.)]),
marginLeft(12),
];
let input =
Style.[border(~width=0, ~color=Colors.transparentWhite), width(4000)]; // Not ideal, should be possible to use flexGrow(1) instead
};
let make = (~text, ~areAllCompleted, ~onInput, ~onSubmit, ~onToggleAll, ()) => {
let onKeyDown = (event: NodeEvents.keyEventParams) =>
if (event.keycode == 13) {
onSubmit();
};
onInput(value)}
onKeyDown
/>
;
};
};
module Todo = {
module Styles = {
let box =
Style.[
flexDirection(`Row),
margin(2),
paddingVertical(4),
paddingHorizontal(8),
alignItems(`Center),
backgroundColor(Theme.panelBackground),
Theme.panelBorder,
];
let text = isChecked =>
Style.[
margin(6),
color(isChecked ? Theme.dimmedTextColor : Theme.textColor),
flexGrow(1),
];
let removeButton = isHovered =>
Style.[
color(isHovered ? Theme.dangerColor : Colors.transparentWhite),
transform(Transform.[TranslateY(2.)]),
marginRight(6),
];
};
type t = {
id: int,
task: string,
isDone: bool,
};
let%component make = (~task, ~onToggle, ~onRemove, ()) => {
let%hook (isHovered, setHovered) = Hooks.state(false);
setHovered(_wasHovered => true)}
onMouseOut={_ => setHovered(_wasHovered => false)}>
;
};
};
module Footer = {
module Styles = {
let container =
Style.[flexDirection(`Row), justifyContent(`SpaceBetween)];
let filterButtonsContainer =
Style.[
flexGrow(1),
width(0),
flexDirection(`Row),
alignItems(`Center),
justifyContent(`Center),
alignSelf(`Center),
transform(Transform.[TranslateY(-2.)]),
];
let leftFlexContainer = Style.[flexGrow(1), width(0)];
let rightFlexContainer =
Style.[
flexGrow(1),
width(0),
flexDirection(`Row),
justifyContent(`FlexEnd),
];
let itemsLeft =
Style.[color(Theme.buttonColor), textWrap(TextWrapping.NoWrap)];
let clearCompleted = isHovered =>
Style.[
color(isHovered ? Theme.hoveredButtonColor : Theme.buttonColor),
textWrap(TextWrapping.NoWrap),
];
};
let make =
(
~activeCount,
~completedCount,
~currentFilter,
~onSelectFilter,
~onClearCompleted,
(),
) => {
let itemsLeft = {
let text =
switch (activeCount) {
| 1 => "1 item left"
| n => Printf.sprintf("%i items left", n)
};
;
};
let filterButtonsView = {
let button = filter =>
onSelectFilter(filter)}
/>;
{button(All)}
{button(Active)}
{button(Completed)}
;
};
module ClearCompleted = {
let%component make = (~onClearCompleted, ~completedCount, ()) => {
let%hook (isHovered, setHovered) = Hooks.state(false);
let text =
switch (completedCount) {
| 0 => ""
| n => Printf.sprintf("Clear completed (%i)", n)
};
setHovered(_wasHovered => true)}
onMouseOut={_ => setHovered(_wasHovered => false)}>
;
};
};
itemsLeft
filterButtonsView
;
};
};
module TodoMVC = {
module Styles = {
let appContainer =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Stretch),
justifyContent(`Center),
flexDirection(`Column),
backgroundColor(Theme.appBackground),
paddingVertical(2),
paddingHorizontal(6),
overflow(`Hidden),
];
let title =
Style.[
color(Theme.titleTextColor),
alignSelf(`Center),
marginTop(Theme.remi(2.)),
textWrap(TextWrapping.NoWrap),
];
let todoList = Style.[flexGrow(1)];
};
type state = {
todos: list(Todo.t),
filter: Filter.t,
inputValue: string,
nextId: int,
};
let initialState = {
todos:
Todo.[
{id: 0, task: "Buy Milk", isDone: false},
{id: 1, task: "Wag the Dog", isDone: true},
],
filter: All,
inputValue: "",
nextId: 2,
};
type action =
| Add
| SetFilter(Filter.t)
| UpdateInput(string)
| Toggle(int)
| Remove(int)
| ToggleAll
| ClearCompleted;
let reducer = (action: action, state: state) =>
switch (action) {
| Add => {
...state,
todos: [
{id: state.nextId, task: state.inputValue, isDone: false},
...state.todos,
],
inputValue: "",
nextId: state.nextId + 1,
}
| UpdateInput(text) => {...state, inputValue: text}
| Toggle(id) =>
let todos =
List.map(
(item: Todo.t) =>
item.id == id ? {...item, isDone: !item.isDone} : item,
state.todos,
);
{...state, todos};
| Remove(id) =>
let todos = List.filter((item: Todo.t) => item.id != id, state.todos);
{...state, todos};
| SetFilter(filter) => {...state, filter}
| ToggleAll =>
let areAllCompleted =
List.for_all((item: Todo.t) => item.isDone, state.todos);
let todos =
List.map(
(item: Todo.t) => {...item, isDone: !areAllCompleted},
state.todos,
);
{...state, todos};
| ClearCompleted =>
let todos = List.filter((item: Todo.t) => !item.isDone, state.todos);
{...state, todos};
};
let%component make = () => {
let%hook ({todos, inputValue, filter as currentFilter, _}, dispatch) =
Hooks.reducer(~initialState, reducer);
let header = {
;
};
let addTodoView = {
let onInput = value => dispatch(UpdateInput(value));
let onSubmit = () => dispatch(Add);
let onToggleAll = () => dispatch(ToggleAll);
let areAllCompleted =
List.for_all((item: Todo.t) => item.isDone, todos);
;
};
let todoListView = {
let onToggle = (id, ()) => dispatch(Toggle(id));
let onRemove = (id, ()) => dispatch(Remove(id));
let filteredTodos =
List.filter(
task =>
switch (filter) {
| All => true
| Active => !task.Todo.isDone
| Completed => task.Todo.isDone
},
todos,
);
{List.map(
(task: Todo.t) =>
,
filteredTodos,
)
|> React.listToElement}
;
};
let footer = {
let onSelectFilter = filter => dispatch(SetFilter(filter));
let onClearCompleted = () => dispatch(ClearCompleted);
let activeCount =
todos |> List.filter((item: Todo.t) => !item.isDone) |> List.length;
let completedCount =
todos |> List.filter((item: Todo.t) => item.isDone) |> List.length;
;
};
header
addTodoView
todoListView
footer
;
};
};
let render = () => ;
================================================
FILE: examples/TreeView.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module TreeView = {
let stringTree =
Tree.(
Node(
{data: "root", id: 1, status: Open},
[
Node(
{data: "subfolder 1", id: 2, status: Open},
[
Node(
{data: "subdirectory 1", id: 3, status: Closed},
[Empty, Empty],
),
],
),
Node(
{data: "home", id: 4, status: Open},
[
Node(
{status: Closed, id: 5, data: "downloads"},
[Empty, Empty],
),
Node(
{data: "desktop", id: 6, status: Open},
[
Node(
{status: Open, id: 7, data: "subfolder 2"},
[
Node(
{status: Open, id: 8, data: "pictures"},
[
Node({status: Closed, id: 12, data: "Images"}, []),
Node(
{status: Closed, id: 10, data: "holiday 2018"},
[],
),
Node(
{status: Closed, id: 11, data: "Graduation 2017"},
[],
),
],
),
Empty,
],
),
Node(
{data: "subfolder 3", id: 9, status: Closed},
[Empty, Empty],
),
],
),
],
),
],
)
);
type heirarchy = {
name: string,
level: string,
};
let animalKingdom =
Tree.(
Node(
{
id: 1,
status: Open,
data: {
name: "Animalia",
level: "Kingdom",
},
},
[
Node(
{
id: 2,
data: {
name: "chordate",
level: "phylum",
},
status: Open,
},
[
Node(
{
id: 3,
status: Open,
data: {
name: "mammal",
level: "class",
},
},
[
Node(
{
id: 4,
status: Open,
data: {
name: "carnivora",
level: "order",
},
},
[Empty, Empty],
),
Empty,
],
),
Empty,
],
),
Node(
{
id: 5,
status: Open,
data: {
name: "anthropoda",
level: "phylum",
},
},
[
Node(
{
id: 6,
status: Open,
data: {
name: "insect",
level: "class",
},
},
[
Node(
{
id: 7,
status: Open,
data: {
name: "dipthera",
level: "order",
},
},
[Empty, Empty],
),
Empty,
],
),
Empty,
],
),
],
)
);
let emptyRenderer =
Some(
indent =>
,
);
let make = (~renderer=?, ()) => {
switch (renderer) {
| Some(fn) =>
| None =>
};
};
let customRenderer = (~indent, content) => {
open Tree;
let {data, _} = content;
let textStyles = Style.[color(Colors.black)];
;
};
};
let titleStyles = Style.[color(Colors.white), marginVertical(10)];
let render = _w => {
let exampleContainer =
Style.[
flexDirection(`Column),
justifyContent(`Center),
alignItems(`Center),
width(200),
];
;
};
================================================
FILE: examples/URLFileOpen.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];
module Example = {
let%component make = () => {
let%hook (url, setURL) =
Hooks.state("https://github.com/revery-ui/revery");
setURL(_ => value)}
value=url
/>
Native.Shell.openURL(url) |> ignore}
title="Open!"
/>
;
};
};
let render = () => ;
================================================
FILE: examples/WavFilePlaybackExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
open Revery.Native;
module AudioHelpers = {
let getBitsPerSample =
fun
| Sdl2.Audio.Format.U8
| S8 => 8
| S16LSB
| S16MSB
| U16LSB
| U16MSB => 16
| S32LSB
| S32MSB
| F32LSB
| F32MSB => 32;
let getAudioDuration = (spec: Sdl2.Audio.Spec.t, bytes) => {
bytes / (spec.freq * spec.channels * (getBitsPerSample(spec.format) / 8));
};
};
module Styles = {
let container =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];
let text = Style.[color(Colors.white), margin(14)];
let audioPlayerTrack =
Style.[
width(200),
marginBottom(14),
padding(2),
borderRadius(8.),
backgroundColor(Colors.dimGrey),
alignItems(`Center),
flexDirection(`Row),
justifyContent(`Center),
];
};
module WavFilePlaybackExamples = {
let%component make = () => {
let%hook (filename, setFilename) = Hooks.state("");
let%hook (audioData, setAudioData) = Hooks.state(None);
let%hook (audioDevice, setAudioDevice) = Hooks.state(None);
let%hook (audioDeviceStatus, setAudioDeviceStatus) =
Hooks.state(Sdl2.Audio.Device.Status.Stopped);
let%hook (audioQueuedBytes, setAudioQueuedBytes) = Hooks.state(0);
let%hook () =
Hooks.tick(~name="", ~tickRate=Time.milliseconds(50), _ => {
switch (audioDevice) {
| Some(device) =>
let status = Sdl2.Audio.Device.getStatus(device);
setAudioDeviceStatus(_ => status);
let bytes = Sdl2.Audio.getQueuedSize(device);
setAudioQueuedBytes(_ => bytes);
| _ => ()
}
});
let audioQueuedBytes = float(audioQueuedBytes);
let audioTotalBytes =
switch (audioData) {
| Some((_, _, len)) => float(len)
| _ => 0.
};
let audioPlayedBytes =
audioQueuedBytes > 0. ? audioTotalBytes -. audioQueuedBytes : 0.;
let audioPlaybackProgress = audioPlayedBytes /. audioTotalBytes;
let audioDuration =
switch (audioData) {
| Some((spec: Sdl2.Audio.Spec.t, _, bytes)) =>
AudioHelpers.getAudioDuration(spec, bytes)
| _ => 0
};
let audioElapsed =
int_of_float(float(audioDuration) *. audioPlaybackProgress);
let openAudioDevice = desiredSpec => {
switch (audioDevice) {
| Some(device) => Sdl2.Audio.Device.close(device)
| _ => ()
};
switch (
Sdl2.Audio.Device.open_(
None,
false,
desiredSpec,
Sdl2.Audio.Device.AllowedChanges.none,
)
) {
| Error(err) => prerr_endline(err)
| Ok((device, _obstainedSpec)) => setAudioDevice(_ => Some(device))
};
};
let loadWavFile = filename => {
switch (Sdl2.Audio.Wav.load(filename)) {
| Error(err) => prerr_endline(err)
| Ok(data) =>
setAudioData(_ => Some(data));
let (spec, _, _) = data;
openAudioDevice(spec);
};
};
let openFile = () => {
let files =
Dialog.openFiles(
~fileTypes=[|"wav", "wave"|],
~allowMultiple=false,
~canChooseDirectories=false,
~showHidden=false,
~title="Revery Open File Example",
~buttonText="Open file in Revery",
(),
);
switch (files) {
| Some([|path|]) =>
setFilename(_ => path |> Filename.basename);
loadWavFile(path);
| _ => ()
};
};
let play = () => {
switch (audioData, audioDevice) {
| (Some((_spec, buf, len)), Some(device)) =>
if (audioQueuedBytes > 0.) {
Sdl2.Audio.Device.pause(device, false);
} else {
switch (Sdl2.Audio.queue(device, buf, len)) {
| Error(err) => prerr_endline(err)
| Ok(_) => Sdl2.Audio.Device.pause(device, false)
};
}
| _ => ()
};
};
let pause = () => {
switch (audioDevice) {
| Some(device) => Sdl2.Audio.Device.pause(device, true)
| _ => ()
};
};
if (filename == "") {
;
} else {
toString)
/>
toString)
/>
{switch (audioDeviceStatus) {
| Stopped
| Paused =>
| Playing =>
audioQueuedBytes == 0.
?
:
}}
;
};
};
};
let render = () => ;
================================================
FILE: examples/WindowControl.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module Styles = {
open Style;
let outer = [
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
justifyContent(`Center),
alignItems(`Center),
];
let text = Style.[color(Colors.white)];
};
let winControl = (~window as w, ()) =>
Window.maximize(w)} />
Window.minimize(w)} />
Window.restore(w)} />
;
let render = window => ;
================================================
FILE: examples/ZoomExample.re
================================================
open Revery;
open Revery.UI;
open Revery.UI.Components;
module ZoomButton = {
let make = (~zoom, ~onClick, ()) => {
onClick(zoom)}
/>;
};
};
module Zoom = {
let%component make = () => {
let window = UI.getActiveWindow();
let zoomV =
switch (window) {
| Some(v) => Window.getZoom(v)
| None => (-1.0)
};
let%hook (currentZoom, setCurrentZoom) = Hooks.state(zoomV);
let textStyle = Style.[color(Colors.white), width(100), margin(14)];
let setZoom = zoom => {
switch (window) {
| Some(v) =>
Window.setZoom(v, zoom);
setCurrentZoom(_ => zoom);
| None => ()
};
};
print_endline("Zoomv: " ++ string_of_float(currentZoom));
;
};
};
let render = () => ;
================================================
FILE: examples/dune
================================================
(executables
(names Examples)
(preprocess
(pps brisk-reconciler.ppx lwt_ppx))
(package ReveryExamples)
(public_names Examples)
(libraries ExampleStubs skia str Revery Revery_Lwt flex timber))
(install
(section bin)
(package Revery)
(files Info.plist Roboto-Regular.ttf Roboto-RegularItalic.ttf
Roboto-Bold.ttf FontAwesome5FreeSolid.otf binary.dat outrun-logo.png
reason-logo.png outrun-icon.png revery-icon.png))
================================================
FILE: examples/gl-matrix-min.js
================================================
/*!
@fileoverview gl-matrix - High performance matrix and vector operations
@author Brandon Jones
@author Colin MacKenzie IV
@version 2.7.0
Copyright (c) 2015-2018, Brandon Jones, Colin MacKenzie IV.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}("undefined"!=typeof self?self:this,function(){return function(t){var n={};function r(a){if(n[a])return n[a].exports;var e=n[a]={i:a,l:!1,exports:{}};return t[a].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=t,r.c=n,r.d=function(t,n,a){r.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:a})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,n){if(1&n&&(t=r(t)),8&n)return t;if(4&n&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var e in t)r.d(a,e,function(n){return t[n]}.bind(null,e));return a},r.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(n,"a",n),n},r.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},r.p="",r(r.s=10)}([function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.setMatrixArrayType=function(t){n.ARRAY_TYPE=t},n.toRadian=function(t){return t*e},n.equals=function(t,n){return Math.abs(t-n)<=a*Math.max(1,Math.abs(t),Math.abs(n))};var a=n.EPSILON=1e-6;n.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,n.RANDOM=Math.random;var e=Math.PI/180},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.len=n.sqrDist=n.dist=n.div=n.mul=n.sub=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t[2]=Math.ceil(n[2]),t[3]=Math.ceil(n[3]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t[2]=Math.floor(n[2]),t[3]=Math.floor(n[3]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t[2]=Math.round(n[2]),t[3]=Math.round(n[3]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},n.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},n.random=function(t,n){var r,e,u,o,i,s;n=n||1;do{r=2*a.RANDOM()-1,e=2*a.RANDOM()-1,i=r*r+e*e}while(i>=1);do{u=2*a.RANDOM()-1,o=2*a.RANDOM()-1,s=u*u+o*o}while(s>=1);var c=Math.sqrt((1-i)/s);return t[0]=n*r,t[1]=n*e,t[2]=n*u*c,t[3]=n*o*c,t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},n.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2],c=r[3],f=c*a+i*u-s*e,M=c*e+s*a-o*u,h=c*u+o*e-i*a,l=-o*a-i*e-s*u;return t[0]=f*c+l*-o+M*-s-h*-i,t[1]=M*c+l*-i+h*-o-f*-s,t[2]=h*c+l*-s+f*-i-M*-o,t[3]=n[3],t},n.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[3]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u}function f(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)}function M(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e}n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.len=f,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=4),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i1?0:e<-1?Math.PI:Math.acos(e)},n.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=n[0],i=n[1],s=n[2];return Math.abs(r-o)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(e-i)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(i))&&Math.abs(u-s)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(s))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(3);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function u(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)}function o(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t}function s(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t}function c(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t}function f(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)}function M(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e}function h(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a}function l(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t}function v(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}n.sub=i,n.mul=s,n.div=c,n.dist=f,n.sqrDist=M,n.len=u,n.sqrLen=h,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=3),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;ia.EPSILON?(t[0]=n[0]/e,t[1]=n[1]/e,t[2]=n[2]/e):(t[0]=1,t[1]=0,t[2]=0);return r},n.multiply=f,n.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+o*i,t[1]=e*s+u*i,t[2]=u*s-e*i,t[3]=o*s-a*i,t},n.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s-u*i,t[1]=e*s+o*i,t[2]=u*s+a*i,t[3]=o*s-e*i,t},n.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+e*i,t[1]=e*s-a*i,t[2]=u*s+o*i,t[3]=o*s-u*i,t},n.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},n.slerp=M,n.random=function(t){var n=a.RANDOM(),r=a.RANDOM(),e=a.RANDOM(),u=Math.sqrt(1-n),o=Math.sqrt(n);return t[0]=u*Math.sin(2*Math.PI*r),t[1]=u*Math.cos(2*Math.PI*r),t[2]=o*Math.sin(2*Math.PI*e),t[3]=o*Math.cos(2*Math.PI*e),t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},n.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},n.fromMat3=h,n.fromEuler=function(t,n,r,a){var e=.5*Math.PI/180;n*=e,r*=e,a*=e;var u=Math.sin(n),o=Math.cos(n),i=Math.sin(r),s=Math.cos(r),c=Math.sin(a),f=Math.cos(a);return t[0]=u*s*f-o*i*c,t[1]=o*i*f+u*s*c,t[2]=o*s*c-u*i*f,t[3]=o*s*f+u*i*c,t},n.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"};var a=i(r(0)),e=i(r(5)),u=i(r(2)),o=i(r(1));function i(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function s(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function c(t,n,r){r*=.5;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t}function f(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*f+o*i+e*c-u*s,t[1]=e*f+o*s+u*i-a*c,t[2]=u*f+o*c+a*s-e*i,t[3]=o*f-a*i-e*s-u*c,t}function M(t,n,r,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=r[0],f=r[1],M=r[2],h=r[3],l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;return(v=u*c+o*f+i*M+s*h)<0&&(v=-v,c=-c,f=-f,M=-M,h=-h),1-v>a.EPSILON?(l=Math.acos(v),d=Math.sin(l),b=Math.sin((1-e)*l)/d,m=Math.sin(e*l)/d):(b=1-e,m=e),t[0]=b*u+m*c,t[1]=b*o+m*f,t[2]=b*i+m*M,t[3]=b*s+m*h,t}function h(t,n){var r=n[0]+n[4]+n[8],a=void 0;if(r>0)a=Math.sqrt(r+1),t[3]=.5*a,a=.5/a,t[0]=(n[5]-n[7])*a,t[1]=(n[6]-n[2])*a,t[2]=(n[1]-n[3])*a;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;a=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*a,a=.5/a,t[3]=(n[3*u+o]-n[3*o+u])*a,t[u]=(n[3*u+e]+n[3*e+u])*a,t[o]=(n[3*o+e]+n[3*e+o])*a}return t}n.clone=o.clone,n.fromValues=o.fromValues,n.copy=o.copy,n.set=o.set,n.add=o.add,n.mul=f,n.scale=o.scale,n.dot=o.dot,n.lerp=o.lerp;var l=n.length=o.length,v=(n.len=l,n.squaredLength=o.squaredLength),d=(n.sqrLen=v,n.normalize=o.normalize);n.exactEquals=o.exactEquals,n.equals=o.equals,n.rotationTo=function(){var t=u.create(),n=u.fromValues(1,0,0),r=u.fromValues(0,1,0);return function(a,e,o){var i=u.dot(e,o);return i<-.999999?(u.cross(t,n,e),u.len(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),c(a,t,Math.PI),a):i>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+i,d(a,a))}}(),n.sqlerp=function(){var t=s(),n=s();return function(r,a,e,u,o,i){return M(t,a,o,i),M(n,e,u,i),M(r,t,n,2*i*(1-i)),r}}(),n.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],d(n,h(n,t))}}()},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(16);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0);return t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.fromValues=function(t,n,r,e,u,o,i,s,c,f,M,h,l,v,d,b){var m=new a.ARRAY_TYPE(16);return m[0]=t,m[1]=n,m[2]=r,m[3]=e,m[4]=u,m[5]=o,m[6]=i,m[7]=s,m[8]=c,m[9]=f,m[10]=M,m[11]=h,m[12]=l,m[13]=v,m[14]=d,m[15]=b,m},n.set=function(t,n,r,a,e,u,o,i,s,c,f,M,h,l,v,d,b){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t[9]=f,t[10]=M,t[11]=h,t[12]=l,t[13]=v,t[14]=d,t[15]=b,t},n.identity=e,n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(e*Y-a*L-u*_)*S,t[2]=(d*R-b*O+m*E)*S,t[3]=(h*O-M*R-l*E)*S,t[4]=(s*x-o*L-c*q)*S,t[5]=(r*L-e*x+u*q)*S,t[6]=(b*A-v*R-m*P)*S,t[7]=(f*R-h*A+l*P)*S,t[8]=(o*Y-i*x+c*y)*S,t[9]=(a*x-r*Y-u*y)*S,t[10]=(v*O-d*A+m*p)*S,t[11]=(M*A-f*O-l*p)*S,t[12]=(i*q-o*_-s*y)*S,t[13]=(r*_-a*q+e*y)*S,t[14]=(d*P-v*E-b*p)*S,t[15]=(f*E-M*P+h*p)*S,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15];return t[0]=i*(h*m-l*b)-M*(s*m-c*b)+d*(s*l-c*h),t[1]=-(a*(h*m-l*b)-M*(e*m-u*b)+d*(e*l-u*h)),t[2]=a*(s*m-c*b)-i*(e*m-u*b)+d*(e*c-u*s),t[3]=-(a*(s*l-c*h)-i*(e*l-u*h)+M*(e*c-u*s)),t[4]=-(o*(h*m-l*b)-f*(s*m-c*b)+v*(s*l-c*h)),t[5]=r*(h*m-l*b)-f*(e*m-u*b)+v*(e*l-u*h),t[6]=-(r*(s*m-c*b)-o*(e*m-u*b)+v*(e*c-u*s)),t[7]=r*(s*l-c*h)-o*(e*l-u*h)+f*(e*c-u*s),t[8]=o*(M*m-l*d)-f*(i*m-c*d)+v*(i*l-c*M),t[9]=-(r*(M*m-l*d)-f*(a*m-u*d)+v*(a*l-u*M)),t[10]=r*(i*m-c*d)-o*(a*m-u*d)+v*(a*c-u*i),t[11]=-(r*(i*l-c*M)-o*(a*l-u*M)+f*(a*c-u*i)),t[12]=-(o*(M*b-h*d)-f*(i*b-s*d)+v*(i*h-s*M)),t[13]=r*(M*b-h*d)-f*(a*b-e*d)+v*(a*h-e*M),t[14]=-(r*(i*b-s*d)-o*(a*b-e*d)+v*(a*s-e*i)),t[15]=r*(i*h-s*M)-o*(a*h-e*M)+f*(a*s-e*i),t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8],f=t[9],M=t[10],h=t[11],l=t[12],v=t[13],d=t[14],b=t[15];return(n*o-r*u)*(M*b-h*d)-(n*i-a*u)*(f*b-h*v)+(n*s-e*u)*(f*d-M*v)+(r*i-a*o)*(c*b-h*l)-(r*s-e*o)*(c*d-M*l)+(a*s-e*i)*(c*v-f*l)},n.multiply=u,n.translate=function(t,n,r){var a=r[0],e=r[1],u=r[2],o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;n===t?(t[12]=n[0]*a+n[4]*e+n[8]*u+n[12],t[13]=n[1]*a+n[5]*e+n[9]*u+n[13],t[14]=n[2]*a+n[6]*e+n[10]*u+n[14],t[15]=n[3]*a+n[7]*e+n[11]*u+n[15]):(o=n[0],i=n[1],s=n[2],c=n[3],f=n[4],M=n[5],h=n[6],l=n[7],v=n[8],d=n[9],b=n[10],m=n[11],t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=f,t[5]=M,t[6]=h,t[7]=l,t[8]=v,t[9]=d,t[10]=b,t[11]=m,t[12]=o*a+f*e+v*u+n[12],t[13]=i*a+M*e+d*u+n[13],t[14]=s*a+h*e+b*u+n[14],t[15]=c*a+l*e+m*u+n[15]);return t},n.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.rotate=function(t,n,r,e){var u=e[0],o=e[1],i=e[2],s=Math.sqrt(u*u+o*o+i*i),c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0,p=void 0,P=void 0,A=void 0,E=void 0,O=void 0,R=void 0,y=void 0,q=void 0,x=void 0,_=void 0,Y=void 0,L=void 0,S=void 0,w=void 0,I=void 0;if(s0?(r[0]=2*(c*s+h*e+f*i-M*u)/l,r[1]=2*(f*s+h*u+M*e-c*i)/l,r[2]=2*(M*s+h*i+c*u-f*e)/l):(r[0]=2*(c*s+h*e+f*i-M*u),r[1]=2*(f*s+h*u+M*e-c*i),r[2]=2*(M*s+h*i+c*u-f*e));return o(t,n,r),t},n.getTranslation=function(t,n){return t[0]=n[12],t[1]=n[13],t[2]=n[14],t},n.getScaling=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[4],o=n[5],i=n[6],s=n[8],c=n[9],f=n[10];return t[0]=Math.sqrt(r*r+a*a+e*e),t[1]=Math.sqrt(u*u+o*o+i*i),t[2]=Math.sqrt(s*s+c*c+f*f),t},n.getRotation=function(t,n){var r=n[0]+n[5]+n[10],a=0;r>0?(a=2*Math.sqrt(r+1),t[3]=.25*a,t[0]=(n[6]-n[9])/a,t[1]=(n[8]-n[2])/a,t[2]=(n[1]-n[4])/a):n[0]>n[5]&&n[0]>n[10]?(a=2*Math.sqrt(1+n[0]-n[5]-n[10]),t[3]=(n[6]-n[9])/a,t[0]=.25*a,t[1]=(n[1]+n[4])/a,t[2]=(n[8]+n[2])/a):n[5]>n[10]?(a=2*Math.sqrt(1+n[5]-n[0]-n[10]),t[3]=(n[8]-n[2])/a,t[0]=(n[1]+n[4])/a,t[1]=.25*a,t[2]=(n[6]+n[9])/a):(a=2*Math.sqrt(1+n[10]-n[0]-n[5]),t[3]=(n[1]-n[4])/a,t[0]=(n[8]+n[2])/a,t[1]=(n[6]+n[9])/a,t[2]=.25*a);return t},n.fromRotationTranslationScale=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],s=e+e,c=u+u,f=o+o,M=e*s,h=e*c,l=e*f,v=u*c,d=u*f,b=o*f,m=i*s,p=i*c,P=i*f,A=a[0],E=a[1],O=a[2];return t[0]=(1-(v+b))*A,t[1]=(h+P)*A,t[2]=(l-p)*A,t[3]=0,t[4]=(h-P)*E,t[5]=(1-(M+b))*E,t[6]=(d+m)*E,t[7]=0,t[8]=(l+p)*O,t[9]=(d-m)*O,t[10]=(1-(M+v))*O,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},n.fromRotationTranslationScaleOrigin=function(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=u+u,f=o+o,M=i+i,h=u*c,l=u*f,v=u*M,d=o*f,b=o*M,m=i*M,p=s*c,P=s*f,A=s*M,E=a[0],O=a[1],R=a[2],y=e[0],q=e[1],x=e[2],_=(1-(d+m))*E,Y=(l+A)*E,L=(v-P)*E,S=(l-A)*O,w=(1-(h+m))*O,I=(b+p)*O,N=(v+P)*R,g=(b-p)*R,T=(1-(h+d))*R;return t[0]=_,t[1]=Y,t[2]=L,t[3]=0,t[4]=S,t[5]=w,t[6]=I,t[7]=0,t[8]=N,t[9]=g,t[10]=T,t[11]=0,t[12]=r[0]+y-(_*y+S*q+N*x),t[13]=r[1]+q-(Y*y+w*q+g*x),t[14]=r[2]+x-(L*y+I*q+T*x),t[15]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[1]=f+m,t[2]=h-b,t[3]=0,t[4]=f-m,t[5]=1-c-v,t[6]=l+d,t[7]=0,t[8]=h+b,t[9]=l-d,t[10]=1-c-M,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.frustum=function(t,n,r,a,e,u,o){var i=1/(r-n),s=1/(e-a),c=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*s,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*s,t[10]=(o+u)*c,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*c,t[15]=0,t},n.perspective=function(t,n,r,a,e){var u=1/Math.tan(n/2),o=void 0;t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=e&&e!==1/0?(o=1/(a-e),t[10]=(e+a)*o,t[14]=2*e*a*o):(t[10]=-1,t[14]=-2*a);return t},n.perspectiveFromFieldOfView=function(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),s=2/(o+i),c=2/(e+u);return t[0]=s,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=c,t[6]=0,t[7]=0,t[8]=-(o-i)*s*.5,t[9]=(e-u)*c*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t},n.ortho=function(t,n,r,a,e,u,o){var i=1/(n-r),s=1/(a-e),c=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*s,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*c,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*s,t[14]=(o+u)*c,t[15]=1,t},n.lookAt=function(t,n,r,u){var o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=n[0],m=n[1],p=n[2],P=u[0],A=u[1],E=u[2],O=r[0],R=r[1],y=r[2];if(Math.abs(b-O)0&&(l=1/Math.sqrt(l),f*=l,M*=l,h*=l);var v=s*h-c*M,d=c*f-i*h,b=i*M-s*f;(l=v*v+d*d+b*b)>0&&(l=1/Math.sqrt(l),v*=l,d*=l,b*=l);return t[0]=v,t[1]=d,t[2]=b,t[3]=0,t[4]=M*b-h*d,t[5]=h*v-f*b,t[6]=f*d-M*v,t[7]=0,t[8]=f,t[9]=M,t[10]=h,t[11]=0,t[12]=e,t[13]=u,t[14]=o,t[15]=1,t},n.str=function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t[9]=n[9]+r[9],t[10]=n[10]+r[10],t[11]=n[11]+r[11],t[12]=n[12]+r[12],t[13]=n[13]+r[13],t[14]=n[14]+r[14],t[15]=n[15]+r[15],t},n.subtract=i,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t[9]=n[9]*r,t[10]=n[10]*r,t[11]=n[11]*r,t[12]=n[12]*r,t[13]=n[13]*r,t[14]=n[14]*r,t[15]=n[15]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t[9]=n[9]+r[9]*a,t[10]=n[10]+r[10]*a,t[11]=n[11]+r[11]*a,t[12]=n[12]+r[12]*a,t[13]=n[13]+r[13]*a,t[14]=n[14]+r[14]*a,t[15]=n[15]+r[15]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[10]===n[10]&&t[11]===n[11]&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[15]===n[15]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=t[9],l=t[10],v=t[11],d=t[12],b=t[13],m=t[14],p=t[15],P=n[0],A=n[1],E=n[2],O=n[3],R=n[4],y=n[5],q=n[6],x=n[7],_=n[8],Y=n[9],L=n[10],S=n[11],w=n[12],I=n[13],N=n[14],g=n[15];return Math.abs(r-P)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(P))&&Math.abs(e-A)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(A))&&Math.abs(u-E)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(E))&&Math.abs(o-O)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(O))&&Math.abs(i-R)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(R))&&Math.abs(s-y)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(y))&&Math.abs(c-q)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(q))&&Math.abs(f-x)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(x))&&Math.abs(M-_)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(_))&&Math.abs(h-Y)<=a.EPSILON*Math.max(1,Math.abs(h),Math.abs(Y))&&Math.abs(l-L)<=a.EPSILON*Math.max(1,Math.abs(l),Math.abs(L))&&Math.abs(v-S)<=a.EPSILON*Math.max(1,Math.abs(v),Math.abs(S))&&Math.abs(d-w)<=a.EPSILON*Math.max(1,Math.abs(d),Math.abs(w))&&Math.abs(b-I)<=a.EPSILON*Math.max(1,Math.abs(b),Math.abs(I))&&Math.abs(m-N)<=a.EPSILON*Math.max(1,Math.abs(m),Math.abs(N))&&Math.abs(p-g)<=a.EPSILON*Math.max(1,Math.abs(p),Math.abs(g))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function u(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=n[9],l=n[10],v=n[11],d=n[12],b=n[13],m=n[14],p=n[15],P=r[0],A=r[1],E=r[2],O=r[3];return t[0]=P*a+A*i+E*M+O*d,t[1]=P*e+A*s+E*h+O*b,t[2]=P*u+A*c+E*l+O*m,t[3]=P*o+A*f+E*v+O*p,P=r[4],A=r[5],E=r[6],O=r[7],t[4]=P*a+A*i+E*M+O*d,t[5]=P*e+A*s+E*h+O*b,t[6]=P*u+A*c+E*l+O*m,t[7]=P*o+A*f+E*v+O*p,P=r[8],A=r[9],E=r[10],O=r[11],t[8]=P*a+A*i+E*M+O*d,t[9]=P*e+A*s+E*h+O*b,t[10]=P*u+A*c+E*l+O*m,t[11]=P*o+A*f+E*v+O*p,P=r[12],A=r[13],E=r[14],O=r[15],t[12]=P*a+A*i+E*M+O*d,t[13]=P*e+A*s+E*h+O*b,t[14]=P*u+A*c+E*l+O*m,t[15]=P*o+A*f+E*v+O*p,t}function o(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=a+a,s=e+e,c=u+u,f=a*i,M=a*s,h=a*c,l=e*s,v=e*c,d=u*c,b=o*i,m=o*s,p=o*c;return t[0]=1-(l+d),t[1]=M+p,t[2]=h-m,t[3]=0,t[4]=M-p,t[5]=1-(f+d),t[6]=v+b,t[7]=0,t[8]=h+m,t[9]=v-b,t[10]=1-(f+l),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t[9]=n[9]-r[9],t[10]=n[10]-r[10],t[11]=n[11]-r[11],t[12]=n[12]-r[12],t[13]=n[13]-r[13],t[14]=n[14]-r[14],t[15]=n[15]-r[15],t}n.mul=u,n.sub=i},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(9);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0);return t[0]=1,t[4]=1,t[8]=1,t},n.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},n.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromValues=function(t,n,r,e,u,o,i,s,c){var f=new a.ARRAY_TYPE(9);return f[0]=t,f[1]=n,f[2]=r,f[3]=e,f[4]=u,f[5]=o,f[6]=i,f[7]=s,f[8]=c,f},n.set=function(t,n,r,a,e,u,o,i,s,c){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=f*o-i*c,h=-f*u+i*s,l=c*u-o*s,v=r*M+a*h+e*l;if(!v)return null;return v=1/v,t[0]=M*v,t[1]=(-f*a+e*c)*v,t[2]=(i*a-e*o)*v,t[3]=h*v,t[4]=(f*r-e*s)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-c*r+a*s)*v,t[8]=(o*r-a*u)*v,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8];return t[0]=o*f-i*c,t[1]=e*c-a*f,t[2]=a*i-e*o,t[3]=i*s-u*f,t[4]=r*f-e*s,t[5]=e*u-r*i,t[6]=u*c-o*s,t[7]=a*s-r*c,t[8]=r*o-a*u,t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8];return n*(c*u-o*s)+r*(-c*e+o*i)+a*(s*e-u*i)},n.multiply=e,n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=s,t[6]=h*a+l*o+c,t[7]=h*e+l*i+f,t[8]=h*u+l*s+M,t},n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=Math.sin(r),l=Math.cos(r);return t[0]=l*a+h*o,t[1]=l*e+h*i,t[2]=l*u+h*s,t[3]=l*o-h*a,t[4]=l*i-h*e,t[5]=l*s-h*u,t[6]=c,t[7]=f,t[8]=M,t},n.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[3]=f-m,t[6]=h+b,t[1]=f+m,t[4]=1-c-v,t[7]=l-d,t[2]=h-b,t[5]=l+d,t[8]=1-c-M,t},n.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(s*x-o*L-c*q)*S,t[2]=(o*Y-i*x+c*y)*S,t[3]=(e*Y-a*L-u*_)*S,t[4]=(r*L-e*x+u*q)*S,t[5]=(a*x-r*Y-u*y)*S,t[6]=(d*R-b*O+m*E)*S,t[7]=(b*A-v*R-m*P)*S,t[8]=(v*O-d*A+m*p)*S,t},n.projection=function(t,n,r){return t[0]=2/n,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/r,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t},n.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=n[0],l=n[1],v=n[2],d=n[3],b=n[4],m=n[5],p=n[6],P=n[7],A=n[8];return Math.abs(r-h)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(h))&&Math.abs(e-l)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(l))&&Math.abs(u-v)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(v))&&Math.abs(o-d)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(d))&&Math.abs(i-b)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(b))&&Math.abs(s-m)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(m))&&Math.abs(c-p)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(p))&&Math.abs(f-P)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(P))&&Math.abs(M-A)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(A))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1],v=r[2],d=r[3],b=r[4],m=r[5],p=r[6],P=r[7],A=r[8];return t[0]=h*a+l*o+v*c,t[1]=h*e+l*i+v*f,t[2]=h*u+l*s+v*M,t[3]=d*a+b*o+m*c,t[4]=d*e+b*i+m*f,t[5]=d*u+b*s+m*M,t[6]=p*a+P*o+A*c,t[7]=p*e+P*i+A*f,t[8]=p*u+P*s+A*M,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.sqrDist=n.dist=n.div=n.mul=n.sub=n.len=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},n.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},n.set=function(t,n,r){return t[0]=n,t[1]=r,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},n.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},n.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},n.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},n.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},n.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},n.transformMat3=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},n.rotate=function(t,n,r,a){var e=n[0]-r[0],u=n[1]-r[1],o=Math.sin(a),i=Math.cos(a);return t[0]=e*i-u*o+r[0],t[1]=e*o+u*i+r[1],t},n.angle=function(t,n){var r=t[0],a=t[1],e=n[0],u=n[1],o=r*r+a*a;o>0&&(o=1/Math.sqrt(o));var i=e*e+u*u;i>0&&(i=1/Math.sqrt(i));var s=(r*e+a*u)*o*i;return s>1?0:s<-1?Math.PI:Math.acos(s)},n.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]},n.equals=function(t,n){var r=t[0],e=t[1],u=n[0],o=n[1];return Math.abs(r-u)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(u))&&Math.abs(e-o)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(o))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(2);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a}function f(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)}function M(t){var n=t[0],r=t[1];return n*n+r*r}n.len=f,n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=2),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i0){r=Math.sqrt(r);var a=n[0]/r,e=n[1]/r,u=n[2]/r,o=n[3]/r,i=n[4],s=n[5],c=n[6],f=n[7],M=a*i+e*s+u*c+o*f;t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=(i-a*M)/r,t[5]=(s-e*M)/r,t[6]=(c-u*M)/r,t[7]=(f-o*M)/r}return t},n.str=function(t){return"quat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=n[0],h=n[1],l=n[2],v=n[3],d=n[4],b=n[5],m=n[6],p=n[7];return Math.abs(r-M)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(M))&&Math.abs(e-h)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(u-l)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(l))&&Math.abs(o-v)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(v))&&Math.abs(i-d)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(d))&&Math.abs(s-b)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(b))&&Math.abs(c-m)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(m))&&Math.abs(f-p)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(p))};var a=o(r(0)),e=o(r(3)),u=o(r(4));function o(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function i(t,n,r){var a=.5*r[0],e=.5*r[1],u=.5*r[2],o=n[0],i=n[1],s=n[2],c=n[3];return t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=a*c+e*s-u*i,t[5]=e*c+u*o-a*s,t[6]=u*c+a*i-e*o,t[7]=-a*o-e*i-u*s,t}function s(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t}n.getReal=e.copy;n.setReal=e.copy;function c(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[4],s=r[5],c=r[6],f=r[7],M=n[4],h=n[5],l=n[6],v=n[7],d=r[0],b=r[1],m=r[2],p=r[3];return t[0]=a*p+o*d+e*m-u*b,t[1]=e*p+o*b+u*d-a*m,t[2]=u*p+o*m+a*b-e*d,t[3]=o*p-a*d-e*b-u*m,t[4]=a*f+o*i+e*c-u*s+M*p+v*d+h*m-l*b,t[5]=e*f+o*s+u*i-a*c+h*p+v*b+l*d-M*m,t[6]=u*f+o*c+a*s-e*i+l*p+v*m+M*b-h*d,t[7]=o*f-a*i-e*s-u*c+v*p-M*d-h*b-l*m,t}n.mul=c;var f=n.dot=e.dot;var M=n.length=e.length,h=(n.len=M,n.squaredLength=e.squaredLength);n.sqrLen=h},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(6);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[4]=0,t[5]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},n.fromValues=function(t,n,r,e,u,o){var i=new a.ARRAY_TYPE(6);return i[0]=t,i[1]=n,i[2]=r,i[3]=e,i[4]=u,i[5]=o,i},n.set=function(t,n,r,a,e,u,o){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=r*u-a*e;if(!s)return null;return s=1/s,t[0]=u*s,t[1]=-a*s,t[2]=-e*s,t[3]=r*s,t[4]=(e*i-u*o)*s,t[5]=(a*o-r*i)*s,t},n.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=Math.sin(r),f=Math.cos(r);return t[0]=a*f+u*c,t[1]=e*f+o*c,t[2]=a*-c+u*f,t[3]=e*-c+o*f,t[4]=i,t[5]=s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a*c,t[1]=e*c,t[2]=u*f,t[3]=o*f,t[4]=i,t[5]=s,t},n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*c+u*f+i,t[5]=e*c+o*f+s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},n.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=n[0],f=n[1],M=n[2],h=n[3],l=n[4],v=n[5];return Math.abs(r-c)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(c))&&Math.abs(e-f)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(f))&&Math.abs(u-M)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(M))&&Math.abs(o-h)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(h))&&Math.abs(i-l)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(l))&&Math.abs(s-v)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(v))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1],M=r[2],h=r[3],l=r[4],v=r[5];return t[0]=a*c+u*f,t[1]=e*c+o*f,t[2]=a*M+u*h,t[3]=e*M+o*h,t[4]=a*l+u*v+i,t[5]=e*l+o*v+s,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(4);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;if(!o)return null;return o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t},n.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},n.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*i,t[1]=e*s+o*i,t[2]=a*-i+u*s,t[3]=e*-i+o*s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*s,t[3]=o*s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},n.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},n.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))},n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*i+u*s,t[1]=e*i+o*s,t[2]=a*c+u*f,t[3]=e*c+o*f,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.vec4=n.vec3=n.vec2=n.quat2=n.quat=n.mat4=n.mat3=n.mat2d=n.mat2=n.glMatrix=void 0;var a=l(r(0)),e=l(r(9)),u=l(r(8)),o=l(r(5)),i=l(r(4)),s=l(r(3)),c=l(r(7)),f=l(r(6)),M=l(r(2)),h=l(r(1));function l(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}n.glMatrix=a,n.mat2=e,n.mat2d=u,n.mat3=o,n.mat4=i,n.quat=s,n.quat2=c,n.vec2=f,n.vec3=M,n.vec4=h}])});
================================================
FILE: examples/index.html
================================================
This is the title of the webpage!
================================================
FILE: examples/stubs/ExampleStubs.re
================================================
/* Notify external environments of switching tabs */
external notifyExampleSwitched: string => unit =
"revery_example_notify_changed";
================================================
FILE: examples/stubs/dune
================================================
(library
(name ExampleStubs)
(foreign_stubs
(language c)
(names example_stubs)
(flags :standard -Wall -Wextra -Werror))
(js_of_ocaml
(javascript_files example_stubs.js)))
================================================
FILE: examples/stubs/example_stubs.c
================================================
#include
#include
#include
#include
#include
CAMLprim value revery_example_notify_changed(value vExample) {
CAMLparam1(vExample);
const char *szExampleSource = String_val(vExample);
printf("Switched to example: %s\n", szExampleSource);
CAMLreturn(Val_unit);
}
================================================
FILE: examples/stubs/example_stubs.js
================================================
// Provides: revery_example_notify_changed
// Requires: caml_to_js_string
function revery_example_notify_changed(src) {
var window = joo_global_object.window;
if (window && window["__revery_playground_example_notify_changed"]) {
window["__revery_playground_example_notify_changed"](caml_to_js_string(src));
}
}
================================================
FILE: examples.json
================================================
{
"source": "./package.json",
"scripts": {
"run": "esy @examples x Examples",
"run-harfbuzz": "esy @examples x bash -c run-harfbuzz.sh #{os}",
"run-skia-fontmanager": "esy @examples x bash -c run-skia-fontmanager.sh #{os}"
},
"override": {
"build": ["dune build -p reason-harfbuzz,reason-skia,reason-sdl2,Revery,ReveryExamples -j4"],
"install": [
"esy-installer reason-harfbuzz.install",
"esy-installer reason-skia.install",
"esy-installer Revery.install",
"esy-installer ReveryExamples.install"
]
}
}
================================================
FILE: include/COPYING.txt
================================================
Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2016 Camilla Berglund
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would
be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
================================================
FILE: include/KHR/khrplatform.h
================================================
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using
*/
#include
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using
*/
#include
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */
================================================
FILE: include/glad/glad.h
================================================
/*
OpenGL ES loader generated by glad 0.1.27 on Tue Sep 11 02:18:13 2018.
Language/Generator: C/C++
Specification: gl
APIs: gles2=2.0
Profile: core
Extensions:
Loader: True
Local files: False
Omit khrplatform: False
Reproducible: False
Commandline:
--profile="core" --api="gles2=2.0" --generator="c" --spec="gl" --extensions=""
Online:
http://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gles2%3D2.0
*/
#ifndef __glad_h_
#define __glad_h_
#ifdef __gl2_h_
#error OpenGL ES 2 header already included, remove this include, glad already provides it
#endif
#define __gl2_h_
#ifdef __gl3_h_
#error OpenGL ES 3 header already included, remove this include, glad already provides it
#endif
#define __gl3_h_
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include
#endif
#ifndef APIENTRY
#define APIENTRY
#endif
#ifndef APIENTRYP
#define APIENTRYP APIENTRY *
#endif
#ifndef GLAPIENTRY
#define GLAPIENTRY APIENTRY
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct gladGLversionStruct {
int major;
int minor;
};
typedef void* (* GLADloadproc)(const char *name);
#ifndef GLAPI
# if defined(GLAD_GLAPI_EXPORT)
# if defined(_WIN32) || defined(__CYGWIN__)
# if defined(GLAD_GLAPI_EXPORT_BUILD)
# if defined(__GNUC__)
# define GLAPI __attribute__ ((dllexport)) extern
# else
# define GLAPI __declspec(dllexport) extern
# endif
# else
# if defined(__GNUC__)
# define GLAPI __attribute__ ((dllimport)) extern
# else
# define GLAPI __declspec(dllimport) extern
# endif
# endif
# elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)
# define GLAPI __attribute__ ((visibility ("default"))) extern
# else
# define GLAPI extern
# endif
# else
# define GLAPI extern
# endif
#endif
GLAPI struct gladGLversionStruct GLVersion;
GLAPI int gladLoadGLES2Loader(GLADloadproc);
#include
#include
#ifndef GLEXT_64_TYPES_DEFINED
/* This code block is duplicated in glxext.h, so must be protected */
#define GLEXT_64_TYPES_DEFINED
/* Define int32_t, int64_t, and uint64_t types for UST/MSC */
/* (as used in the GL_EXT_timer_query extension). */
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#include
#elif defined(__sun__) || defined(__digital__)
#include
#if defined(__STDC__)
#if defined(__arch64__) || defined(_LP64)
typedef long int int64_t;
typedef unsigned long int uint64_t;
#else
typedef long long int int64_t;
typedef unsigned long long int uint64_t;
#endif /* __arch64__ */
#endif /* __STDC__ */
#elif defined( __VMS ) || defined(__sgi)
#include
#elif defined(__SCO__) || defined(__USLC__)
#include
#elif defined(__UNIXOS2__) || defined(__SOL64__)
typedef long int int32_t;
typedef long long int int64_t;
typedef unsigned long long int uint64_t;
#elif defined(_WIN32) && defined(__GNUC__)
#include
#elif defined(_WIN32)
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
/* Fallback if nothing above works */
#include
#endif
#endif
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef void GLvoid;
typedef signed char GLbyte;
typedef short GLshort;
typedef int GLint;
typedef int GLclampx;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned int GLuint;
typedef int GLsizei;
typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
typedef void *GLeglClientBufferEXT;
typedef void *GLeglImageOES;
typedef char GLchar;
typedef char GLcharARB;
#ifdef __APPLE__
typedef void *GLhandleARB;
#else
typedef unsigned int GLhandleARB;
#endif
typedef unsigned short GLhalfARB;
typedef unsigned short GLhalf;
typedef GLint GLfixed;
typedef khronos_intptr_t GLintptr;
typedef khronos_ssize_t GLsizeiptr;
typedef int64_t GLint64;
typedef uint64_t GLuint64;
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)
typedef long GLintptrARB;
#else
typedef ptrdiff_t GLintptrARB;
#endif
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)
typedef long GLsizeiptrARB;
#else
typedef ptrdiff_t GLsizeiptrARB;
#endif
typedef int64_t GLint64EXT;
typedef uint64_t GLuint64EXT;
typedef struct __GLsync *GLsync;
struct _cl_context;
struct _cl_event;
typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);
typedef unsigned short GLhalfNV;
typedef GLintptr GLvdpauSurfaceNV;
typedef void (APIENTRY *GLVULKANPROCNV)(void);
#define GL_DEPTH_BUFFER_BIT 0x00000100
#define GL_STENCIL_BUFFER_BIT 0x00000400
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_FALSE 0
#define GL_TRUE 1
#define GL_POINTS 0x0000
#define GL_LINES 0x0001
#define GL_LINE_LOOP 0x0002
#define GL_LINE_STRIP 0x0003
#define GL_TRIANGLES 0x0004
#define GL_TRIANGLE_STRIP 0x0005
#define GL_TRIANGLE_FAN 0x0006
#define GL_ZERO 0
#define GL_ONE 1
#define GL_SRC_COLOR 0x0300
#define GL_ONE_MINUS_SRC_COLOR 0x0301
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_DST_ALPHA 0x0304
#define GL_ONE_MINUS_DST_ALPHA 0x0305
#define GL_DST_COLOR 0x0306
#define GL_ONE_MINUS_DST_COLOR 0x0307
#define GL_SRC_ALPHA_SATURATE 0x0308
#define GL_FUNC_ADD 0x8006
#define GL_BLEND_EQUATION 0x8009
#define GL_BLEND_EQUATION_RGB 0x8009
#define GL_BLEND_EQUATION_ALPHA 0x883D
#define GL_FUNC_SUBTRACT 0x800A
#define GL_FUNC_REVERSE_SUBTRACT 0x800B
#define GL_BLEND_DST_RGB 0x80C8
#define GL_BLEND_SRC_RGB 0x80C9
#define GL_BLEND_DST_ALPHA 0x80CA
#define GL_BLEND_SRC_ALPHA 0x80CB
#define GL_CONSTANT_COLOR 0x8001
#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
#define GL_CONSTANT_ALPHA 0x8003
#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
#define GL_BLEND_COLOR 0x8005
#define GL_ARRAY_BUFFER 0x8892
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_ARRAY_BUFFER_BINDING 0x8894
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
#define GL_STREAM_DRAW 0x88E0
#define GL_STATIC_DRAW 0x88E4
#define GL_DYNAMIC_DRAW 0x88E8
#define GL_BUFFER_SIZE 0x8764
#define GL_BUFFER_USAGE 0x8765
#define GL_CURRENT_VERTEX_ATTRIB 0x8626
#define GL_FRONT 0x0404
#define GL_BACK 0x0405
#define GL_FRONT_AND_BACK 0x0408
#define GL_TEXTURE_2D 0x0DE1
#define GL_CULL_FACE 0x0B44
#define GL_BLEND 0x0BE2
#define GL_DITHER 0x0BD0
#define GL_STENCIL_TEST 0x0B90
#define GL_DEPTH_TEST 0x0B71
#define GL_SCISSOR_TEST 0x0C11
#define GL_POLYGON_OFFSET_FILL 0x8037
#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
#define GL_SAMPLE_COVERAGE 0x80A0
#define GL_NO_ERROR 0
#define GL_INVALID_ENUM 0x0500
#define GL_INVALID_VALUE 0x0501
#define GL_INVALID_OPERATION 0x0502
#define GL_OUT_OF_MEMORY 0x0505
#define GL_CW 0x0900
#define GL_CCW 0x0901
#define GL_LINE_WIDTH 0x0B21
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
#define GL_CULL_FACE_MODE 0x0B45
#define GL_FRONT_FACE 0x0B46
#define GL_DEPTH_RANGE 0x0B70
#define GL_DEPTH_WRITEMASK 0x0B72
#define GL_DEPTH_CLEAR_VALUE 0x0B73
#define GL_DEPTH_FUNC 0x0B74
#define GL_STENCIL_CLEAR_VALUE 0x0B91
#define GL_STENCIL_FUNC 0x0B92
#define GL_STENCIL_FAIL 0x0B94
#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
#define GL_STENCIL_REF 0x0B97
#define GL_STENCIL_VALUE_MASK 0x0B93
#define GL_STENCIL_WRITEMASK 0x0B98
#define GL_STENCIL_BACK_FUNC 0x8800
#define GL_STENCIL_BACK_FAIL 0x8801
#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
#define GL_STENCIL_BACK_REF 0x8CA3
#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
#define GL_VIEWPORT 0x0BA2
#define GL_SCISSOR_BOX 0x0C10
#define GL_COLOR_CLEAR_VALUE 0x0C22
#define GL_COLOR_WRITEMASK 0x0C23
#define GL_UNPACK_ALIGNMENT 0x0CF5
#define GL_PACK_ALIGNMENT 0x0D05
#define GL_MAX_TEXTURE_SIZE 0x0D33
#define GL_MAX_VIEWPORT_DIMS 0x0D3A
#define GL_SUBPIXEL_BITS 0x0D50
#define GL_RED_BITS 0x0D52
#define GL_GREEN_BITS 0x0D53
#define GL_BLUE_BITS 0x0D54
#define GL_ALPHA_BITS 0x0D55
#define GL_DEPTH_BITS 0x0D56
#define GL_STENCIL_BITS 0x0D57
#define GL_POLYGON_OFFSET_UNITS 0x2A00
#define GL_POLYGON_OFFSET_FACTOR 0x8038
#define GL_TEXTURE_BINDING_2D 0x8069
#define GL_SAMPLE_BUFFERS 0x80A8
#define GL_SAMPLES 0x80A9
#define GL_SAMPLE_COVERAGE_VALUE 0x80AA
#define GL_SAMPLE_COVERAGE_INVERT 0x80AB
#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
#define GL_DONT_CARE 0x1100
#define GL_FASTEST 0x1101
#define GL_NICEST 0x1102
#define GL_GENERATE_MIPMAP_HINT 0x8192
#define GL_BYTE 0x1400
#define GL_UNSIGNED_BYTE 0x1401
#define GL_SHORT 0x1402
#define GL_UNSIGNED_SHORT 0x1403
#define GL_INT 0x1404
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_FIXED 0x140C
#define GL_DEPTH_COMPONENT 0x1902
#define GL_ALPHA 0x1906
#define GL_RGB 0x1907
#define GL_RGBA 0x1908
#define GL_LUMINANCE 0x1909
#define GL_LUMINANCE_ALPHA 0x190A
#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_MAX_VERTEX_ATTRIBS 0x8869
#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
#define GL_MAX_VARYING_VECTORS 0x8DFC
#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
#define GL_SHADER_TYPE 0x8B4F
#define GL_DELETE_STATUS 0x8B80
#define GL_LINK_STATUS 0x8B82
#define GL_VALIDATE_STATUS 0x8B83
#define GL_ATTACHED_SHADERS 0x8B85
#define GL_ACTIVE_UNIFORMS 0x8B86
#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
#define GL_ACTIVE_ATTRIBUTES 0x8B89
#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
#define GL_CURRENT_PROGRAM 0x8B8D
#define GL_NEVER 0x0200
#define GL_LESS 0x0201
#define GL_EQUAL 0x0202
#define GL_LEQUAL 0x0203
#define GL_GREATER 0x0204
#define GL_NOTEQUAL 0x0205
#define GL_GEQUAL 0x0206
#define GL_ALWAYS 0x0207
#define GL_KEEP 0x1E00
#define GL_REPLACE 0x1E01
#define GL_INCR 0x1E02
#define GL_DECR 0x1E03
#define GL_INVERT 0x150A
#define GL_INCR_WRAP 0x8507
#define GL_DECR_WRAP 0x8508
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_EXTENSIONS 0x1F03
#define GL_NEAREST 0x2600
#define GL_LINEAR 0x2601
#define GL_NEAREST_MIPMAP_NEAREST 0x2700
#define GL_LINEAR_MIPMAP_NEAREST 0x2701
#define GL_NEAREST_MIPMAP_LINEAR 0x2702
#define GL_LINEAR_MIPMAP_LINEAR 0x2703
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_WRAP_S 0x2802
#define GL_TEXTURE_WRAP_T 0x2803
#define GL_TEXTURE 0x1702
#define GL_TEXTURE_CUBE_MAP 0x8513
#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE1 0x84C1
#define GL_TEXTURE2 0x84C2
#define GL_TEXTURE3 0x84C3
#define GL_TEXTURE4 0x84C4
#define GL_TEXTURE5 0x84C5
#define GL_TEXTURE6 0x84C6
#define GL_TEXTURE7 0x84C7
#define GL_TEXTURE8 0x84C8
#define GL_TEXTURE9 0x84C9
#define GL_TEXTURE10 0x84CA
#define GL_TEXTURE11 0x84CB
#define GL_TEXTURE12 0x84CC
#define GL_TEXTURE13 0x84CD
#define GL_TEXTURE14 0x84CE
#define GL_TEXTURE15 0x84CF
#define GL_TEXTURE16 0x84D0
#define GL_TEXTURE17 0x84D1
#define GL_TEXTURE18 0x84D2
#define GL_TEXTURE19 0x84D3
#define GL_TEXTURE20 0x84D4
#define GL_TEXTURE21 0x84D5
#define GL_TEXTURE22 0x84D6
#define GL_TEXTURE23 0x84D7
#define GL_TEXTURE24 0x84D8
#define GL_TEXTURE25 0x84D9
#define GL_TEXTURE26 0x84DA
#define GL_TEXTURE27 0x84DB
#define GL_TEXTURE28 0x84DC
#define GL_TEXTURE29 0x84DD
#define GL_TEXTURE30 0x84DE
#define GL_TEXTURE31 0x84DF
#define GL_ACTIVE_TEXTURE 0x84E0
#define GL_REPEAT 0x2901
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_MIRRORED_REPEAT 0x8370
#define GL_FLOAT_VEC2 0x8B50
#define GL_FLOAT_VEC3 0x8B51
#define GL_FLOAT_VEC4 0x8B52
#define GL_INT_VEC2 0x8B53
#define GL_INT_VEC3 0x8B54
#define GL_INT_VEC4 0x8B55
#define GL_BOOL 0x8B56
#define GL_BOOL_VEC2 0x8B57
#define GL_BOOL_VEC3 0x8B58
#define GL_BOOL_VEC4 0x8B59
#define GL_FLOAT_MAT2 0x8B5A
#define GL_FLOAT_MAT3 0x8B5B
#define GL_FLOAT_MAT4 0x8B5C
#define GL_SAMPLER_2D 0x8B5E
#define GL_SAMPLER_CUBE 0x8B60
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
#define GL_COMPILE_STATUS 0x8B81
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_SHADER_SOURCE_LENGTH 0x8B88
#define GL_SHADER_COMPILER 0x8DFA
#define GL_SHADER_BINARY_FORMATS 0x8DF8
#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
#define GL_LOW_FLOAT 0x8DF0
#define GL_MEDIUM_FLOAT 0x8DF1
#define GL_HIGH_FLOAT 0x8DF2
#define GL_LOW_INT 0x8DF3
#define GL_MEDIUM_INT 0x8DF4
#define GL_HIGH_INT 0x8DF5
#define GL_FRAMEBUFFER 0x8D40
#define GL_RENDERBUFFER 0x8D41
#define GL_RGBA4 0x8056
#define GL_RGB5_A1 0x8057
#define GL_RGB565 0x8D62
#define GL_DEPTH_COMPONENT16 0x81A5
#define GL_STENCIL_INDEX8 0x8D48
#define GL_RENDERBUFFER_WIDTH 0x8D42
#define GL_RENDERBUFFER_HEIGHT 0x8D43
#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
#define GL_RENDERBUFFER_RED_SIZE 0x8D50
#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51
#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52
#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53
#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54
#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0
#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_DEPTH_ATTACHMENT 0x8D00
#define GL_STENCIL_ATTACHMENT 0x8D20
#define GL_NONE 0
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
#define GL_FRAMEBUFFER_BINDING 0x8CA6
#define GL_RENDERBUFFER_BINDING 0x8CA7
#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
#ifndef GL_ES_VERSION_2_0
#define GL_ES_VERSION_2_0 1
GLAPI int GLAD_GL_ES_VERSION_2_0;
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture);
GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture;
#define glActiveTexture glad_glActiveTexture
typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);
GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader;
#define glAttachShader glad_glAttachShader
typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name);
GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;
#define glBindAttribLocation glad_glBindAttribLocation
typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer;
#define glBindBuffer glad_glBindBuffer
typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);
GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;
#define glBindFramebuffer glad_glBindFramebuffer
typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer);
GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;
#define glBindRenderbuffer glad_glBindRenderbuffer
typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture);
GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture;
#define glBindTexture glad_glBindTexture
typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
GLAPI PFNGLBLENDCOLORPROC glad_glBlendColor;
#define glBlendColor glad_glBlendColor
typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode);
GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation;
#define glBlendEquation glad_glBlendEquation
typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha);
GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;
#define glBlendEquationSeparate glad_glBlendEquationSeparate
typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor);
GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc;
#define glBlendFunc glad_glBlendFunc
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;
#define glBlendFuncSeparate glad_glBlendFuncSeparate
typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
GLAPI PFNGLBUFFERDATAPROC glad_glBufferData;
#define glBufferData glad_glBufferData
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;
#define glBufferSubData glad_glBufferSubData
typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);
GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;
#define glCheckFramebufferStatus glad_glCheckFramebufferStatus
typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask);
GLAPI PFNGLCLEARPROC glad_glClear;
#define glClear glad_glClear
typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
GLAPI PFNGLCLEARCOLORPROC glad_glClearColor;
#define glClearColor glad_glClearColor
typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat d);
GLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf;
#define glClearDepthf glad_glClearDepthf
typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s);
GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil;
#define glClearStencil glad_glClearStencil
typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
GLAPI PFNGLCOLORMASKPROC glad_glColorMask;
#define glColorMask glad_glColorMask
typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);
GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;
#define glCompileShader glad_glCompileShader
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);
GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;
#define glCompressedTexImage2D glad_glCompressedTexImage2D
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);
GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;
#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D
typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
GLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;
#define glCopyTexImage2D glad_glCopyTexImage2D
typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;
#define glCopyTexSubImage2D glad_glCopyTexSubImage2D
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void);
GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
#define glCreateProgram glad_glCreateProgram
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type);
GLAPI PFNGLCREATESHADERPROC glad_glCreateShader;
#define glCreateShader glad_glCreateShader
typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode);
GLAPI PFNGLCULLFACEPROC glad_glCullFace;
#define glCullFace glad_glCullFace
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);
GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;
#define glDeleteBuffers glad_glDeleteBuffers
typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers);
GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;
#define glDeleteFramebuffers glad_glDeleteFramebuffers
typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program);
GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;
#define glDeleteProgram glad_glDeleteProgram
typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers);
GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;
#define glDeleteRenderbuffers glad_glDeleteRenderbuffers
typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader);
GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader;
#define glDeleteShader glad_glDeleteShader
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures);
GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures;
#define glDeleteTextures glad_glDeleteTextures
typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func);
GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc;
#define glDepthFunc glad_glDepthFunc
typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag);
GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask;
#define glDepthMask glad_glDepthMask
typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f);
GLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef;
#define glDepthRangef glad_glDepthRangef
typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader);
GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader;
#define glDetachShader glad_glDetachShader
typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap);
GLAPI PFNGLDISABLEPROC glad_glDisable;
#define glDisable glad_glDisable
typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);
GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;
#define glDisableVertexAttribArray glad_glDisableVertexAttribArray
typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);
GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays;
#define glDrawArrays glad_glDrawArrays
typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices);
GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements;
#define glDrawElements glad_glDrawElements
typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap);
GLAPI PFNGLENABLEPROC glad_glEnable;
#define glEnable glad_glEnable
typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;
#define glEnableVertexAttribArray glad_glEnableVertexAttribArray
typedef void (APIENTRYP PFNGLFINISHPROC)(void);
GLAPI PFNGLFINISHPROC glad_glFinish;
#define glFinish glad_glFinish
typedef void (APIENTRYP PFNGLFLUSHPROC)(void);
GLAPI PFNGLFLUSHPROC glad_glFlush;
#define glFlush glad_glFlush
typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;
#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer
typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
GLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;
#define glFramebufferTexture2D glad_glFramebufferTexture2D
typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode);
GLAPI PFNGLFRONTFACEPROC glad_glFrontFace;
#define glFrontFace glad_glFrontFace
typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers);
GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers;
#define glGenBuffers glad_glGenBuffers
typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target);
GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;
#define glGenerateMipmap glad_glGenerateMipmap
typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers);
GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;
#define glGenFramebuffers glad_glGenFramebuffers
typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers);
GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;
#define glGenRenderbuffers glad_glGenRenderbuffers
typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures);
GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures;
#define glGenTextures glad_glGenTextures
typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;
#define glGetActiveAttrib glad_glGetActiveAttrib
typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;
#define glGetActiveUniform glad_glGetActiveUniform
typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);
GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;
#define glGetAttachedShaders glad_glGetAttachedShaders
typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name);
GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;
#define glGetAttribLocation glad_glGetAttribLocation
typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data);
GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv;
#define glGetBooleanv glad_glGetBooleanv
typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;
#define glGetBufferParameteriv glad_glGetBufferParameteriv
typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void);
GLAPI PFNGLGETERRORPROC glad_glGetError;
#define glGetError glad_glGetError
typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data);
GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv;
#define glGetFloatv glad_glGetFloatv
typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params);
GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;
#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv
typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data);
GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv;
#define glGetIntegerv glad_glGetIntegerv
typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params);
GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;
#define glGetProgramiv glad_glGetProgramiv
typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;
#define glGetProgramInfoLog glad_glGetProgramInfoLog
typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;
#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv
typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params);
GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv;
#define glGetShaderiv glad_glGetShaderiv
typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;
#define glGetShaderInfoLog glad_glGetShaderInfoLog
typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision);
GLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;
#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat
typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);
GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;
#define glGetShaderSource glad_glGetShaderSource
typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name);
GLAPI PFNGLGETSTRINGPROC glad_glGetString;
#define glGetString glad_glGetString
typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params);
GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;
#define glGetTexParameterfv glad_glGetTexParameterfv
typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;
#define glGetTexParameteriv glad_glGetTexParameteriv
typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params);
GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;
#define glGetUniformfv glad_glGetUniformfv
typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params);
GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;
#define glGetUniformiv glad_glGetUniformiv
typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name);
GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;
#define glGetUniformLocation glad_glGetUniformLocation
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params);
GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;
#define glGetVertexAttribfv glad_glGetVertexAttribfv
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params);
GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;
#define glGetVertexAttribiv glad_glGetVertexAttribiv
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer);
GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;
#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv
typedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode);
GLAPI PFNGLHINTPROC glad_glHint;
#define glHint glad_glHint
typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer);
GLAPI PFNGLISBUFFERPROC glad_glIsBuffer;
#define glIsBuffer glad_glIsBuffer
typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap);
GLAPI PFNGLISENABLEDPROC glad_glIsEnabled;
#define glIsEnabled glad_glIsEnabled
typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer);
GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;
#define glIsFramebuffer glad_glIsFramebuffer
typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program);
GLAPI PFNGLISPROGRAMPROC glad_glIsProgram;
#define glIsProgram glad_glIsProgram
typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer);
GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;
#define glIsRenderbuffer glad_glIsRenderbuffer
typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader);
GLAPI PFNGLISSHADERPROC glad_glIsShader;
#define glIsShader glad_glIsShader
typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture);
GLAPI PFNGLISTEXTUREPROC glad_glIsTexture;
#define glIsTexture glad_glIsTexture
typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width);
GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth;
#define glLineWidth glad_glLineWidth
typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program);
GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram;
#define glLinkProgram glad_glLinkProgram
typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param);
GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei;
#define glPixelStorei glad_glPixelStorei
typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units);
GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;
#define glPolygonOffset glad_glPolygonOffset
typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
GLAPI PFNGLREADPIXELSPROC glad_glReadPixels;
#define glReadPixels glad_glReadPixels
typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)(void);
GLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;
#define glReleaseShaderCompiler glad_glReleaseShaderCompiler
typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;
#define glRenderbufferStorage glad_glRenderbufferStorage
typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert);
GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;
#define glSampleCoverage glad_glSampleCoverage
typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
GLAPI PFNGLSCISSORPROC glad_glScissor;
#define glScissor glad_glScissor
typedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length);
GLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary;
#define glShaderBinary glad_glShaderBinary
typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource;
#define glShaderSource glad_glShaderSource
typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask);
GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc;
#define glStencilFunc glad_glStencilFunc
typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask);
GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;
#define glStencilFuncSeparate glad_glStencilFuncSeparate
typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask);
GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask;
#define glStencilMask glad_glStencilMask
typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask);
GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;
#define glStencilMaskSeparate glad_glStencilMaskSeparate
typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass);
GLAPI PFNGLSTENCILOPPROC glad_glStencilOp;
#define glStencilOp glad_glStencilOp
typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;
#define glStencilOpSeparate glad_glStencilOpSeparate
typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D;
#define glTexImage2D glad_glTexImage2D
typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param);
GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf;
#define glTexParameterf glad_glTexParameterf
typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params);
GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;
#define glTexParameterfv glad_glTexParameterfv
typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);
GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri;
#define glTexParameteri glad_glTexParameteri
typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params);
GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;
#define glTexParameteriv glad_glTexParameteriv
typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;
#define glTexSubImage2D glad_glTexSubImage2D
typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0);
GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f;
#define glUniform1f glad_glUniform1f
typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value);
GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv;
#define glUniform1fv glad_glUniform1fv
typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0);
GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i;
#define glUniform1i glad_glUniform1i
typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value);
GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv;
#define glUniform1iv glad_glUniform1iv
typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1);
GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f;
#define glUniform2f glad_glUniform2f
typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value);
GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv;
#define glUniform2fv glad_glUniform2fv
typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1);
GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i;
#define glUniform2i glad_glUniform2i
typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value);
GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv;
#define glUniform2iv glad_glUniform2iv
typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f;
#define glUniform3f glad_glUniform3f
typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value);
GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv;
#define glUniform3fv glad_glUniform3fv
typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2);
GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i;
#define glUniform3i glad_glUniform3i
typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value);
GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv;
#define glUniform3iv glad_glUniform3iv
typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
GLAPI PFNGLUNIFORM4FPROC glad_glUniform4f;
#define glUniform4f glad_glUniform4f
typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value);
GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv;
#define glUniform4fv glad_glUniform4fv
typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i;
#define glUniform4i glad_glUniform4i
typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value);
GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv;
#define glUniform4iv glad_glUniform4iv
typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;
#define glUniformMatrix2fv glad_glUniformMatrix2fv
typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;
#define glUniformMatrix3fv glad_glUniformMatrix3fv
typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;
#define glUniformMatrix4fv glad_glUniformMatrix4fv
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program);
GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram;
#define glUseProgram glad_glUseProgram
typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program);
GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;
#define glValidateProgram glad_glValidateProgram
typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x);
GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;
#define glVertexAttrib1f glad_glVertexAttrib1f
typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v);
GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;
#define glVertexAttrib1fv glad_glVertexAttrib1fv
typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y);
GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;
#define glVertexAttrib2f glad_glVertexAttrib2f
typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v);
GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;
#define glVertexAttrib2fv glad_glVertexAttrib2fv
typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z);
GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;
#define glVertexAttrib3f glad_glVertexAttrib3f
typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v);
GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;
#define glVertexAttrib3fv glad_glVertexAttrib3fv
typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;
#define glVertexAttrib4f glad_glVertexAttrib4f
typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v);
GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;
#define glVertexAttrib4fv glad_glVertexAttrib4fv
typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;
#define glVertexAttribPointer glad_glVertexAttribPointer
typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
GLAPI PFNGLVIEWPORTPROC glad_glViewport;
#define glViewport glad_glViewport
#endif
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: include/stb_image.h
================================================
/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
Do this:
#define STB_IMAGE_IMPLEMENTATION
before you include this file in *one* C or C++ file to create the implementation.
// i.e. it should look like this:
#include ...
#include ...
#include ...
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free
QUICK NOTES:
Primarily of interest to game developers and other people who can
avoid problematic images and only need the trivial interface
JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
PNG 1/2/4/8/16-bit-per-channel
TGA (not sure what subset, if a subset)
BMP non-1bpp, non-RLE
PSD (composited view only, no extra channels, 8/16 bit-per-channel)
GIF (*comp always reports as 4-channel)
HDR (radiance rgbE format)
PIC (Softimage PIC)
PNM (PPM and PGM binary only)
Animated GIF still needs a proper API, but here's one way to do it:
http://gist.github.com/urraka/685d9a6340b26b830d49
- decode from memory or through FILE (define STBI_NO_STDIO to remove code)
- decode from arbitrary I/O callbacks
- SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
Full documentation under "DOCUMENTATION" below.
LICENSE
See end of file for license information.
RECENT REVISION HISTORY:
2.19 (2018-02-11) fix warning
2.18 (2018-01-30) fix warnings
2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings
2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes
2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC
2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes
2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64
RGB-format JPEG; remove white matting in PSD;
allocate large structures on the stack;
correct channel count for PNG & BMP
2.10 (2016-01-22) avoid warning introduced in 2.09
2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
See end of file for full revision history.
============================ Contributors =========================
Image formats Extensions, features
Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
github:urraka (animated gif) Junggon Kim (PNM comments)
Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
socks-the-fox (16-bit PNG)
Jeremy Sawicki (handle all ImageNet JPGs)
Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
Arseny Kapoulkine
John-Mark Allen
Bug & warning fixes
Marc LeBlanc David Woo Guillaume George Martins Mozeiko
Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan
Dave Moore Roy Eltham Hayaki Saito Nathan Reed
Won Chun Luke Graham Johan Duparc Nick Verigakis
the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh
Janez Zemva John Bartholomew Michal Cichon github:romigrou
Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar
Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex
Ryamond Barbiero Paul Du Bois Engin Manap github:grim210
Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw
Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus
Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo
Christian Floisand Kevin Schmidt github:darealshinji
Blazej Dariusz Roszkowski github:Michaelangel007
*/
#ifndef STBI_INCLUDE_STB_IMAGE_H
#define STBI_INCLUDE_STB_IMAGE_H
// DOCUMENTATION
//
// Limitations:
// - no 12-bit-per-channel JPEG
// - no JPEGs with arithmetic coding
// - GIF always returns *comp=4
//
// Basic usage (see HDR discussion below for HDR usage):
// int x,y,n;
// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
// // ... process data if not NULL ...
// // ... x = width, y = height, n = # 8-bit components per pixel ...
// // ... replace '0' with '1'..'4' to force that many components per pixel
// // ... but 'n' will always be the number that it would have been if you said 0
// stbi_image_free(data)
//
// Standard parameters:
// int *x -- outputs image width in pixels
// int *y -- outputs image height in pixels
// int *channels_in_file -- outputs # of image components in image file
// int desired_channels -- if non-zero, # of image components requested in result
//
// The return value from an image loader is an 'unsigned char *' which points
// to the pixel data, or NULL on an allocation failure or if the image is
// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
// with each pixel consisting of N interleaved 8-bit components; the first
// pixel pointed to is top-left-most in the image. There is no padding between
// image scanlines or between pixels, regardless of format. The number of
// components N is 'desired_channels' if desired_channels is non-zero, or
// *channels_in_file otherwise. If desired_channels is non-zero,
// *channels_in_file has the number of components that _would_ have been
// output otherwise. E.g. if you set desired_channels to 4, you will always
// get RGBA output, but you can check *channels_in_file to see if it's trivially
// opaque because e.g. there were only 3 channels in the source image.
//
// An output image with N components has the following components interleaved
// in this order in each pixel:
//
// N=#comp components
// 1 grey
// 2 grey, alpha
// 3 red, green, blue
// 4 red, green, blue, alpha
//
// If image loading fails for any reason, the return value will be NULL,
// and *x, *y, *channels_in_file will be unchanged. The function
// stbi_failure_reason() can be queried for an extremely brief, end-user
// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS
// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
// more user-friendly ones.
//
// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
//
// ===========================================================================
//
// Philosophy
//
// stb libraries are designed with the following priorities:
//
// 1. easy to use
// 2. easy to maintain
// 3. good performance
//
// Sometimes I let "good performance" creep up in priority over "easy to maintain",
// and for best performance I may provide less-easy-to-use APIs that give higher
// performance, in addition to the easy to use ones. Nevertheless, it's important
// to keep in mind that from the standpoint of you, a client of this library,
// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.
//
// Some secondary priorities arise directly from the first two, some of which
// make more explicit reasons why performance can't be emphasized.
//
// - Portable ("ease of use")
// - Small source code footprint ("easy to maintain")
// - No dependencies ("ease of use")
//
// ===========================================================================
//
// I/O callbacks
//
// I/O callbacks allow you to read from arbitrary sources, like packaged
// files or some other source. Data read from callbacks are processed
// through a small internal buffer (currently 128 bytes) to try to reduce
// overhead.
//
// The three functions you must define are "read" (reads some bytes of data),
// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
//
// ===========================================================================
//
// SIMD support
//
// The JPEG decoder will try to automatically use SIMD kernels on x86 when
// supported by the compiler. For ARM Neon support, you must explicitly
// request it.
//
// (The old do-it-yourself SIMD API is no longer supported in the current
// code.)
//
// On x86, SSE2 will automatically be used when available based on a run-time
// test; if not, the generic C versions are used as a fall-back. On ARM targets,
// the typical path is to have separate builds for NEON and non-NEON devices
// (at least this is true for iOS and Android). Therefore, the NEON support is
// toggled by a build flag: define STBI_NEON to get NEON loops.
//
// If for some reason you do not want to use any of SIMD code, or if
// you have issues compiling it, you can disable it entirely by
// defining STBI_NO_SIMD.
//
// ===========================================================================
//
// HDR image support (disable by defining STBI_NO_HDR)
//
// stb_image now supports loading HDR images in general, and currently
// the Radiance .HDR file format, although the support is provided
// generically. You can still load any file through the existing interface;
// if you attempt to load an HDR file, it will be automatically remapped to
// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
// both of these constants can be reconfigured through this interface:
//
// stbi_hdr_to_ldr_gamma(2.2f);
// stbi_hdr_to_ldr_scale(1.0f);
//
// (note, do not use _inverse_ constants; stbi_image will invert them
// appropriately).
//
// Additionally, there is a new, parallel interface for loading files as
// (linear) floats to preserve the full dynamic range:
//
// float *data = stbi_loadf(filename, &x, &y, &n, 0);
//
// If you load LDR images through this interface, those images will
// be promoted to floating point values, run through the inverse of
// constants corresponding to the above:
//
// stbi_ldr_to_hdr_scale(1.0f);
// stbi_ldr_to_hdr_gamma(2.2f);
//
// Finally, given a filename (or an open file or memory block--see header
// file for details) containing image data, you can query for the "most
// appropriate" interface to use (that is, whether the image is HDR or
// not), using:
//
// stbi_is_hdr(char *filename);
//
// ===========================================================================
//
// iPhone PNG support:
//
// By default we convert iphone-formatted PNGs back to RGB, even though
// they are internally encoded differently. You can disable this conversion
// by by calling stbi_convert_iphone_png_to_rgb(0), in which case
// you will always just get the native iphone "format" through (which
// is BGR stored in RGB).
//
// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
// pixel to remove any premultiplied alpha *only* if the image file explicitly
// says there's premultiplied data (currently only happens in iPhone images,
// and only if iPhone convert-to-rgb processing is on).
//
// ===========================================================================
//
// ADDITIONAL CONFIGURATION
//
// - You can suppress implementation of any of the decoders to reduce
// your code footprint by #defining one or more of the following
// symbols before creating the implementation.
//
// STBI_NO_JPEG
// STBI_NO_PNG
// STBI_NO_BMP
// STBI_NO_PSD
// STBI_NO_TGA
// STBI_NO_GIF
// STBI_NO_HDR
// STBI_NO_PIC
// STBI_NO_PNM (.ppm and .pgm)
//
// - You can request *only* certain decoders and suppress all other ones
// (this will be more forward-compatible, as addition of new decoders
// doesn't require you to disable them explicitly):
//
// STBI_ONLY_JPEG
// STBI_ONLY_PNG
// STBI_ONLY_BMP
// STBI_ONLY_PSD
// STBI_ONLY_TGA
// STBI_ONLY_GIF
// STBI_ONLY_HDR
// STBI_ONLY_PIC
// STBI_ONLY_PNM (.ppm and .pgm)
//
// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
//
#ifndef STBI_NO_STDIO
#include
#endif // STBI_NO_STDIO
#define STBI_VERSION 1
enum {
STBI_default = 0, // only used for desired_channels
STBI_grey = 1,
STBI_grey_alpha = 2,
STBI_rgb = 3,
STBI_rgb_alpha = 4
};
typedef unsigned char stbi_uc;
typedef unsigned short stbi_us;
#ifdef __cplusplus
extern "C" {
#endif
#ifdef STB_IMAGE_STATIC
#define STBIDEF static
#else
#define STBIDEF extern
#endif
//////////////////////////////////////////////////////////////////////////////
//
// PRIMARY API - works on images of any type
//
//
// load image by filename, open file, or memory buffer
//
typedef struct {
int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
int (*eof) (void *user); // returns nonzero if we are at end of file/data
} stbi_io_callbacks;
////////////////////////////////////
//
// 8-bits-per-channel interface
//
STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
#ifndef STBI_NO_GIF
STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
#endif
#ifndef STBI_NO_STDIO
STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
// for stbi_load_from_file, file pointer is left pointing immediately after image
#endif
////////////////////////////////////
//
// 16-bits-per-channel interface
//
STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
#ifndef STBI_NO_STDIO
STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
#endif
////////////////////////////////////
//
// float-per-channel interface
//
#ifndef STBI_NO_LINEAR
STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
#ifndef STBI_NO_STDIO
STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
#endif
#endif
#ifndef STBI_NO_HDR
STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
STBIDEF void stbi_hdr_to_ldr_scale(float scale);
#endif // STBI_NO_HDR
#ifndef STBI_NO_LINEAR
STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
STBIDEF void stbi_ldr_to_hdr_scale(float scale);
#endif // STBI_NO_LINEAR
// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
#ifndef STBI_NO_STDIO
STBIDEF int stbi_is_hdr (char const *filename);
STBIDEF int stbi_is_hdr_from_file(FILE *f);
#endif // STBI_NO_STDIO
// get a VERY brief reason for failure
// NOT THREADSAFE
STBIDEF const char *stbi_failure_reason (void);
// free the loaded image -- this is just free()
STBIDEF void stbi_image_free (void *retval_from_stbi_load);
// get image dimensions & components without fully decoding
STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);
STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);
#ifndef STBI_NO_STDIO
STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
STBIDEF int stbi_is_16_bit (char const *filename);
STBIDEF int stbi_is_16_bit_from_file(FILE *f);
#endif
// for image formats that explicitly notate that they have premultiplied alpha,
// we just return the colors as stored in the file. set this flag to force
// unpremultiplication. results are undefined if the unpremultiply overflow.
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
// indicate whether we should process iphone images back to canonical format,
// or just pass them through "as-is"
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
// flip the image vertically, so the first pixel in the output array is the bottom left
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
// ZLIB client - used by PNG, available for other purposes
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
#ifdef __cplusplus
}
#endif
//
//
//// end header file /////////////////////////////////////////////////////
#endif // STBI_INCLUDE_STB_IMAGE_H
#ifdef STB_IMAGE_IMPLEMENTATION
#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
|| defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
|| defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
|| defined(STBI_ONLY_ZLIB)
#ifndef STBI_ONLY_JPEG
#define STBI_NO_JPEG
#endif
#ifndef STBI_ONLY_PNG
#define STBI_NO_PNG
#endif
#ifndef STBI_ONLY_BMP
#define STBI_NO_BMP
#endif
#ifndef STBI_ONLY_PSD
#define STBI_NO_PSD
#endif
#ifndef STBI_ONLY_TGA
#define STBI_NO_TGA
#endif
#ifndef STBI_ONLY_GIF
#define STBI_NO_GIF
#endif
#ifndef STBI_ONLY_HDR
#define STBI_NO_HDR
#endif
#ifndef STBI_ONLY_PIC
#define STBI_NO_PIC
#endif
#ifndef STBI_ONLY_PNM
#define STBI_NO_PNM
#endif
#endif
#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
#define STBI_NO_ZLIB
#endif
#include
#include // ptrdiff_t on osx
#include
#include
#include
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#include // ldexp, pow
#endif
#ifndef STBI_NO_STDIO
#include
#endif
#ifndef STBI_ASSERT
#include
#define STBI_ASSERT(x) assert(x)
#endif
#ifndef _MSC_VER
#ifdef __cplusplus
#define stbi_inline inline
#else
#define stbi_inline
#endif
#else
#define stbi_inline __forceinline
#endif
#ifdef _MSC_VER
typedef unsigned short stbi__uint16;
typedef signed short stbi__int16;
typedef unsigned int stbi__uint32;
typedef signed int stbi__int32;
#else
#include
typedef uint16_t stbi__uint16;
typedef int16_t stbi__int16;
typedef uint32_t stbi__uint32;
typedef int32_t stbi__int32;
#endif
// should produce compiler error if size is wrong
typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#ifdef _MSC_VER
#define STBI_NOTUSED(v) (void)(v)
#else
#define STBI_NOTUSED(v) (void)sizeof(v)
#endif
#ifdef _MSC_VER
#define STBI_HAS_LROTL
#endif
#ifdef STBI_HAS_LROTL
#define stbi_lrot(x,y) _lrotl(x,y)
#else
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
#endif
#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
// ok
#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
// ok
#else
#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
#endif
#ifndef STBI_MALLOC
#define STBI_MALLOC(sz) malloc(sz)
#define STBI_REALLOC(p,newsz) realloc(p,newsz)
#define STBI_FREE(p) free(p)
#endif
#ifndef STBI_REALLOC_SIZED
#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
#endif
// x86/x64 detection
#if defined(__x86_64__) || defined(_M_X64)
#define STBI__X64_TARGET
#elif defined(__i386) || defined(_M_IX86)
#define STBI__X86_TARGET
#endif
#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
// which in turn means it gets to use SSE2 everywhere. This is unfortunate,
// but previous attempts to provide the SSE2 functions with runtime
// detection caused numerous issues. The way architecture extensions are
// exposed in GCC/Clang is, sadly, not really suited for one-file libs.
// New behavior: if compiled with -msse2, we use SSE2 without any
// detection; if not, we don't use it at all.
#define STBI_NO_SIMD
#endif
#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
//
// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
// simultaneously enabling "-mstackrealign".
//
// See https://github.com/nothings/stb/issues/81 for more information.
//
// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
#define STBI_NO_SIMD
#endif
#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
#define STBI_SSE2
#include
#ifdef _MSC_VER
#if _MSC_VER >= 1400 // not VC6
#include // __cpuid
static int stbi__cpuid3(void) {
int info[4];
__cpuid(info,1);
return info[3];
}
#else
static int stbi__cpuid3(void) {
int res;
__asm {
mov eax,1
cpuid
mov res,edx
}
return res;
}
#endif
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
static int stbi__sse2_available(void) {
int info3 = stbi__cpuid3();
return ((info3 >> 26) & 1) != 0;
}
#else // assume GCC-style if not VC++
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
static int stbi__sse2_available(void) {
// If we're even attempting to compile this on GCC/Clang, that means
// -msse2 is on, which means the compiler is allowed to use SSE2
// instructions at will, and so are we.
return 1;
}
#endif
#endif
// ARM NEON
#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
#undef STBI_NEON
#endif
#ifdef STBI_NEON
#include
// assume GCC or Clang on ARM targets
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#ifndef STBI_SIMD_ALIGN
#define STBI_SIMD_ALIGN(type, name) type name
#endif
///////////////////////////////////////////////
//
// stbi__context struct and start_xxx functions
// stbi__context structure is our basic context used by all images, so it
// contains all the IO context, plus some basic image information
typedef struct {
stbi__uint32 img_x, img_y;
int img_n, img_out_n;
stbi_io_callbacks io;
void *io_user_data;
int read_from_callbacks;
int buflen;
stbi_uc buffer_start[128];
stbi_uc *img_buffer, *img_buffer_end;
stbi_uc *img_buffer_original, *img_buffer_original_end;
} stbi__context;
static void stbi__refill_buffer(stbi__context *s);
// initialize a memory-decode context
static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) {
s->io.read = NULL;
s->read_from_callbacks = 0;
s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
}
// initialize a callback-based context
static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) {
s->io = *c;
s->io_user_data = user;
s->buflen = sizeof(s->buffer_start);
s->read_from_callbacks = 1;
s->img_buffer_original = s->buffer_start;
stbi__refill_buffer(s);
s->img_buffer_original_end = s->img_buffer_end;
}
#ifndef STBI_NO_STDIO
static int stbi__stdio_read(void *user, char *data, int size) {
return (int) fread(data,1,size,(FILE*) user);
}
static void stbi__stdio_skip(void *user, int n) {
fseek((FILE*) user, n, SEEK_CUR);
}
static int stbi__stdio_eof(void *user) {
return feof((FILE*) user);
}
static stbi_io_callbacks stbi__stdio_callbacks = {
stbi__stdio_read,
stbi__stdio_skip,
stbi__stdio_eof,
};
static void stbi__start_file(stbi__context *s, FILE *f) {
stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);
}
//static void stop_file(stbi__context *s) { }
#endif // !STBI_NO_STDIO
static void stbi__rewind(stbi__context *s) {
// conceptually rewind SHOULD rewind to the beginning of the stream,
// but we just rewind to the beginning of the initial buffer, because
// we only use it after doing 'test', which only ever looks at at most 92 bytes
s->img_buffer = s->img_buffer_original;
s->img_buffer_end = s->img_buffer_original_end;
}
enum {
STBI_ORDER_RGB,
STBI_ORDER_BGR
};
typedef struct {
int bits_per_channel;
int num_channels;
int channel_order;
} stbi__result_info;
#ifndef STBI_NO_JPEG
static int stbi__jpeg_test(stbi__context *s);
static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);
#endif
#ifndef STBI_NO_PNG
static int stbi__png_test(stbi__context *s);
static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__png_is16(stbi__context *s);
#endif
#ifndef STBI_NO_BMP
static int stbi__bmp_test(stbi__context *s);
static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp);
#endif
#ifndef STBI_NO_TGA
static int stbi__tga_test(stbi__context *s);
static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp);
#endif
#ifndef STBI_NO_PSD
static int stbi__psd_test(stbi__context *s);
static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc);
static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__psd_is16(stbi__context *s);
#endif
#ifndef STBI_NO_HDR
static int stbi__hdr_test(stbi__context *s);
static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp);
#endif
#ifndef STBI_NO_PIC
static int stbi__pic_test(stbi__context *s);
static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);
#endif
#ifndef STBI_NO_GIF
static int stbi__gif_test(stbi__context *s);
static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
#endif
#ifndef STBI_NO_PNM
static int stbi__pnm_test(stbi__context *s);
static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
#endif
// this is not threadsafe
static const char *stbi__g_failure_reason;
STBIDEF const char *stbi_failure_reason(void) {
return stbi__g_failure_reason;
}
static int stbi__err(const char *str) {
stbi__g_failure_reason = str;
return 0;
}
static void *stbi__malloc(size_t size) {
return STBI_MALLOC(size);
}
// stb_image uses ints pervasively, including for offset calculations.
// therefore the largest decoded image size we can support with the
// current code, even on 64-bit targets, is INT_MAX. this is not a
// significant limitation for the intended use case.
//
// we do, however, need to make sure our size calculations don't
// overflow. hence a few helper functions for size calculations that
// multiply integers together, making sure that they're non-negative
// and no overflow occurs.
// return 1 if the sum is valid, 0 on overflow.
// negative terms are considered invalid.
static int stbi__addsizes_valid(int a, int b) {
if (b < 0) return 0;
// now 0 <= b <= INT_MAX, hence also
// 0 <= INT_MAX - b <= INTMAX.
// And "a + b <= INT_MAX" (which might overflow) is the
// same as a <= INT_MAX - b (no overflow)
return a <= INT_MAX - b;
}
// returns 1 if the product is valid, 0 on overflow.
// negative factors are considered invalid.
static int stbi__mul2sizes_valid(int a, int b) {
if (a < 0 || b < 0) return 0;
if (b == 0) return 1; // mul-by-0 is always safe
// portable way to check for no overflows in a*b
return a <= INT_MAX/b;
}
// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow
static int stbi__mad2sizes_valid(int a, int b, int add) {
return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);
}
// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow
static int stbi__mad3sizes_valid(int a, int b, int c, int add) {
return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
stbi__addsizes_valid(a*b*c, add);
}
// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) {
return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add);
}
#endif
// mallocs with size overflow checking
static void *stbi__malloc_mad2(int a, int b, int add) {
if (!stbi__mad2sizes_valid(a, b, add)) return NULL;
return stbi__malloc(a*b + add);
}
static void *stbi__malloc_mad3(int a, int b, int c, int add) {
if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;
return stbi__malloc(a*b*c + add);
}
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) {
if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
return stbi__malloc(a*b*c*d + add);
}
#endif
// stbi__err - error
// stbi__errpf - error returning pointer to float
// stbi__errpuc - error returning pointer to unsigned char
#ifdef STBI_NO_FAILURE_STRINGS
#define stbi__err(x,y) 0
#elif defined(STBI_FAILURE_USERMSG)
#define stbi__err(x,y) stbi__err(y)
#else
#define stbi__err(x,y) stbi__err(x)
#endif
#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
STBIDEF void stbi_image_free(void *retval_from_stbi_load) {
STBI_FREE(retval_from_stbi_load);
}
#ifndef STBI_NO_LINEAR
static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
#endif
#ifndef STBI_NO_HDR
static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp);
#endif
static int stbi__vertically_flip_on_load = 0;
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) {
stbi__vertically_flip_on_load = flag_true_if_should_flip;
}
static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) {
memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields
ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed
ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
ri->num_channels = 0;
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_PNG
if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_BMP
if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_GIF
if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_PSD
if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc);
#endif
#ifndef STBI_NO_PIC
if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_HDR
if (stbi__hdr_test(s)) {
float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri);
return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
}
#endif
#ifndef STBI_NO_TGA
// test tga last because it's a crappy test!
if (stbi__tga_test(s))
return stbi__tga_load(s,x,y,comp,req_comp, ri);
#endif
return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
}
static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) {
int i;
int img_len = w * h * channels;
stbi_uc *reduced;
reduced = (stbi_uc *) stbi__malloc(img_len);
if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory");
for (i = 0; i < img_len; ++i)
reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling
STBI_FREE(orig);
return reduced;
}
static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) {
int i;
int img_len = w * h * channels;
stbi__uint16 *enlarged;
enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);
if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
for (i = 0; i < img_len; ++i)
enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff
STBI_FREE(orig);
return enlarged;
}
static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) {
int row;
size_t bytes_per_row = (size_t)w * bytes_per_pixel;
stbi_uc temp[2048];
stbi_uc *bytes = (stbi_uc *)image;
for (row = 0; row < (h>>1); row++) {
stbi_uc *row0 = bytes + row*bytes_per_row;
stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;
// swap row0 with row1
size_t bytes_left = bytes_per_row;
while (bytes_left) {
size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
memcpy(temp, row0, bytes_copy);
memcpy(row0, row1, bytes_copy);
memcpy(row1, temp, bytes_copy);
row0 += bytes_copy;
row1 += bytes_copy;
bytes_left -= bytes_copy;
}
}
}
static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) {
int slice;
int slice_size = w * h * bytes_per_pixel;
stbi_uc *bytes = (stbi_uc *)image;
for (slice = 0; slice < z; ++slice) {
stbi__vertical_flip(bytes, w, h, bytes_per_pixel);
bytes += slice_size;
}
}
static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) {
stbi__result_info ri;
void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
if (result == NULL)
return NULL;
if (ri.bits_per_channel != 8) {
STBI_ASSERT(ri.bits_per_channel == 16);
result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
ri.bits_per_channel = 8;
}
// @TODO: move stbi__convert_format to here
if (stbi__vertically_flip_on_load) {
int channels = req_comp ? req_comp : *comp;
stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
}
return (unsigned char *) result;
}
static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) {
stbi__result_info ri;
void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
if (result == NULL)
return NULL;
if (ri.bits_per_channel != 16) {
STBI_ASSERT(ri.bits_per_channel == 8);
result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
ri.bits_per_channel = 16;
}
// @TODO: move stbi__convert_format16 to here
// @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision
if (stbi__vertically_flip_on_load) {
int channels = req_comp ? req_comp : *comp;
stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));
}
return (stbi__uint16 *) result;
}
#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR)
static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) {
if (stbi__vertically_flip_on_load && result != NULL) {
int channels = req_comp ? req_comp : *comp;
stbi__vertical_flip(result, *x, *y, channels * sizeof(float));
}
}
#endif
#ifndef STBI_NO_STDIO
static FILE *stbi__fopen(char const *filename, char const *mode) {
FILE *f;
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != fopen_s(&f, filename, mode))
f=0;
#else
f = fopen(filename, mode);
#endif
return f;
}
STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) {
FILE *f = stbi__fopen(filename, "rb");
unsigned char *result;
if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
result = stbi_load_from_file(f,x,y,comp,req_comp);
fclose(f);
return result;
}
STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) {
unsigned char *result;
stbi__context s;
stbi__start_file(&s,f);
result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
}
return result;
}
STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) {
stbi__uint16 *result;
stbi__context s;
stbi__start_file(&s,f);
result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
}
return result;
}
STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) {
FILE *f = stbi__fopen(filename, "rb");
stbi__uint16 *result;
if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file");
result = stbi_load_from_file_16(f,x,y,comp,req_comp);
fclose(f);
return result;
}
#endif //!STBI_NO_STDIO
STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) {
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
}
STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) {
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);
return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
}
STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) {
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
}
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) {
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
}
#ifndef STBI_NO_GIF
STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) {
unsigned char *result;
stbi__context s;
stbi__start_mem(&s,buffer,len);
result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);
if (stbi__vertically_flip_on_load) {
stbi__vertical_flip_slices( result, *x, *y, *z, *comp );
}
return result;
}
#endif
#ifndef STBI_NO_LINEAR
static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) {
unsigned char *data;
#ifndef STBI_NO_HDR
if (stbi__hdr_test(s)) {
stbi__result_info ri;
float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri);
if (hdr_data)
stbi__float_postprocess(hdr_data,x,y,comp,req_comp);
return hdr_data;
}
#endif
data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);
if (data)
return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
}
STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) {
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) {
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
#ifndef STBI_NO_STDIO
STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) {
float *result;
FILE *f = stbi__fopen(filename, "rb");
if (!f) return stbi__errpf("can't fopen", "Unable to open file");
result = stbi_loadf_from_file(f,x,y,comp,req_comp);
fclose(f);
return result;
}
STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) {
stbi__context s;
stbi__start_file(&s,f);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
#endif // !STBI_NO_STDIO
#endif // !STBI_NO_LINEAR
// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
// reports false!
STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) {
#ifndef STBI_NO_HDR
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__hdr_test(&s);
#else
STBI_NOTUSED(buffer);
STBI_NOTUSED(len);
return 0;
#endif
}
#ifndef STBI_NO_STDIO
STBIDEF int stbi_is_hdr (char const *filename) {
FILE *f = stbi__fopen(filename, "rb");
int result=0;
if (f) {
result = stbi_is_hdr_from_file(f);
fclose(f);
}
return result;
}
STBIDEF int stbi_is_hdr_from_file(FILE *f) {
#ifndef STBI_NO_HDR
long pos = ftell(f);
int res;
stbi__context s;
stbi__start_file(&s,f);
res = stbi__hdr_test(&s);
fseek(f, pos, SEEK_SET);
return res;
#else
STBI_NOTUSED(f);
return 0;
#endif
}
#endif // !STBI_NO_STDIO
STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) {
#ifndef STBI_NO_HDR
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi__hdr_test(&s);
#else
STBI_NOTUSED(clbk);
STBI_NOTUSED(user);
return 0;
#endif
}
#ifndef STBI_NO_LINEAR
static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;
STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) {
stbi__l2h_gamma = gamma;
}
STBIDEF void stbi_ldr_to_hdr_scale(float scale) {
stbi__l2h_scale = scale;
}
#endif
static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) {
stbi__h2l_gamma_i = 1/gamma;
}
STBIDEF void stbi_hdr_to_ldr_scale(float scale) {
stbi__h2l_scale_i = 1/scale;
}
//////////////////////////////////////////////////////////////////////////////
//
// Common code used by all image loaders
//
enum {
STBI__SCAN_load=0,
STBI__SCAN_type,
STBI__SCAN_header
};
static void stbi__refill_buffer(stbi__context *s) {
int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
if (n == 0) {
// at end of file, treat same as if from memory, but need to handle case
// where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
s->read_from_callbacks = 0;
s->img_buffer = s->buffer_start;
s->img_buffer_end = s->buffer_start+1;
*s->img_buffer = 0;
} else {
s->img_buffer = s->buffer_start;
s->img_buffer_end = s->buffer_start + n;
}
}
stbi_inline static stbi_uc stbi__get8(stbi__context *s) {
if (s->img_buffer < s->img_buffer_end)
return *s->img_buffer++;
if (s->read_from_callbacks) {
stbi__refill_buffer(s);
return *s->img_buffer++;
}
return 0;
}
stbi_inline static int stbi__at_eof(stbi__context *s) {
if (s->io.read) {
if (!(s->io.eof)(s->io_user_data)) return 0;
// if feof() is true, check if buffer = end
// special case: we've only got the special 0 character at the end
if (s->read_from_callbacks == 0) return 1;
}
return s->img_buffer >= s->img_buffer_end;
}
static void stbi__skip(stbi__context *s, int n) {
if (n < 0) {
s->img_buffer = s->img_buffer_end;
return;
}
if (s->io.read) {
int blen = (int) (s->img_buffer_end - s->img_buffer);
if (blen < n) {
s->img_buffer = s->img_buffer_end;
(s->io.skip)(s->io_user_data, n - blen);
return;
}
}
s->img_buffer += n;
}
static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) {
if (s->io.read) {
int blen = (int) (s->img_buffer_end - s->img_buffer);
if (blen < n) {
int res, count;
memcpy(buffer, s->img_buffer, blen);
count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
res = (count == (n-blen));
s->img_buffer = s->img_buffer_end;
return res;
}
}
if (s->img_buffer+n <= s->img_buffer_end) {
memcpy(buffer, s->img_buffer, n);
s->img_buffer += n;
return 1;
} else
return 0;
}
static int stbi__get16be(stbi__context *s) {
int z = stbi__get8(s);
return (z << 8) + stbi__get8(s);
}
static stbi__uint32 stbi__get32be(stbi__context *s) {
stbi__uint32 z = stbi__get16be(s);
return (z << 16) + stbi__get16be(s);
}
#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
// nothing
#else
static int stbi__get16le(stbi__context *s) {
int z = stbi__get8(s);
return z + (stbi__get8(s) << 8);
}
#endif
#ifndef STBI_NO_BMP
static stbi__uint32 stbi__get32le(stbi__context *s) {
stbi__uint32 z = stbi__get16le(s);
return z + (stbi__get16le(s) << 16);
}
#endif
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
//////////////////////////////////////////////////////////////////////////////
//
// generic converter from built-in img_n to req_comp
// individual types do this automatically as much as possible (e.g. jpeg
// does all cases internally since it needs to colorspace convert anyway,
// and it never has alpha, so very few cases ). png can automatically
// interleave an alpha=255 channel, but falls back to this for other cases
//
// assume data buffer is malloced, so malloc a new one and free that one
// only failure mode is malloc failing
static stbi_uc stbi__compute_y(int r, int g, int b) {
return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8);
}
static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) {
int i,j;
unsigned char *good;
if (req_comp == img_n) return data;
STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);
if (good == NULL) {
STBI_FREE(data);
return stbi__errpuc("outofmem", "Out of memory");
}
for (j=0; j < (int) y; ++j) {
unsigned char *src = data + j * x * img_n ;
unsigned char *dest = good + j * x * req_comp;
#define STBI__COMBO(a,b) ((a)*8+(b))
#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
// convert source image with img_n components to one with req_comp components;
// avoid switch per pixel, so use switch per scanline and massive macros
switch (STBI__COMBO(img_n, req_comp)) {
STBI__CASE(1,2) {
dest[0]=src[0], dest[1]=255;
}
break;
STBI__CASE(1,3) {
dest[0]=dest[1]=dest[2]=src[0];
}
break;
STBI__CASE(1,4) {
dest[0]=dest[1]=dest[2]=src[0], dest[3]=255;
}
break;
STBI__CASE(2,1) {
dest[0]=src[0];
}
break;
STBI__CASE(2,3) {
dest[0]=dest[1]=dest[2]=src[0];
}
break;
STBI__CASE(2,4) {
dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];
}
break;
STBI__CASE(3,4) {
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255;
}
break;
STBI__CASE(3,1) {
dest[0]=stbi__compute_y(src[0],src[1],src[2]);
}
break;
STBI__CASE(3,2) {
dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255;
}
break;
STBI__CASE(4,1) {
dest[0]=stbi__compute_y(src[0],src[1],src[2]);
}
break;
STBI__CASE(4,2) {
dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3];
}
break;
STBI__CASE(4,3) {
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];
}
break;
default:
STBI_ASSERT(0);
}
#undef STBI__CASE
}
STBI_FREE(data);
return good;
}
static stbi__uint16 stbi__compute_y_16(int r, int g, int b) {
return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8);
}
static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) {
int i,j;
stbi__uint16 *good;
if (req_comp == img_n) return data;
STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2);
if (good == NULL) {
STBI_FREE(data);
return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
}
for (j=0; j < (int) y; ++j) {
stbi__uint16 *src = data + j * x * img_n ;
stbi__uint16 *dest = good + j * x * req_comp;
#define STBI__COMBO(a,b) ((a)*8+(b))
#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
// convert source image with img_n components to one with req_comp components;
// avoid switch per pixel, so use switch per scanline and massive macros
switch (STBI__COMBO(img_n, req_comp)) {
STBI__CASE(1,2) {
dest[0]=src[0], dest[1]=0xffff;
}
break;
STBI__CASE(1,3) {
dest[0]=dest[1]=dest[2]=src[0];
}
break;
STBI__CASE(1,4) {
dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff;
}
break;
STBI__CASE(2,1) {
dest[0]=src[0];
}
break;
STBI__CASE(2,3) {
dest[0]=dest[1]=dest[2]=src[0];
}
break;
STBI__CASE(2,4) {
dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];
}
break;
STBI__CASE(3,4) {
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff;
}
break;
STBI__CASE(3,1) {
dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);
}
break;
STBI__CASE(3,2) {
dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff;
}
break;
STBI__CASE(4,1) {
dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);
}
break;
STBI__CASE(4,2) {
dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3];
}
break;
STBI__CASE(4,3) {
dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];
}
break;
default:
STBI_ASSERT(0);
}
#undef STBI__CASE
}
STBI_FREE(data);
return good;
}
#ifndef STBI_NO_LINEAR
static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) {
int i,k,n;
float *output;
if (!data) return NULL;
output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0);
if (output == NULL) {
STBI_FREE(data);
return stbi__errpf("outofmem", "Out of memory");
}
// compute number of non-alpha components
if (comp & 1) n = comp;
else n = comp-1;
for (i=0; i < x*y; ++i) {
for (k=0; k < n; ++k) {
output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
}
if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;
}
STBI_FREE(data);
return output;
}
#endif
#ifndef STBI_NO_HDR
#define stbi__float2int(x) ((int) (x))
static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) {
int i,k,n;
stbi_uc *output;
if (!data) return NULL;
output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0);
if (output == NULL) {
STBI_FREE(data);
return stbi__errpuc("outofmem", "Out of memory");
}
// compute number of non-alpha components
if (comp & 1) n = comp;
else n = comp-1;
for (i=0; i < x*y; ++i) {
for (k=0; k < n; ++k) {
float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;
if (z < 0) z = 0;
if (z > 255) z = 255;
output[i*comp + k] = (stbi_uc) stbi__float2int(z);
}
if (k < comp) {
float z = data[i*comp+k] * 255 + 0.5f;
if (z < 0) z = 0;
if (z > 255) z = 255;
output[i*comp + k] = (stbi_uc) stbi__float2int(z);
}
}
STBI_FREE(data);
return output;
}
#endif
//////////////////////////////////////////////////////////////////////////////
//
// "baseline" JPEG/JFIF decoder
//
// simple implementation
// - doesn't support delayed output of y-dimension
// - simple interface (only one output format: 8-bit interleaved RGB)
// - doesn't try to recover corrupt jpegs
// - doesn't allow partial loading, loading multiple at once
// - still fast on x86 (copying globals into locals doesn't help x86)
// - allocates lots of intermediate memory (full size of all components)
// - non-interleaved case requires this anyway
// - allows good upsampling (see next)
// high-quality
// - upsampled channels are bilinearly interpolated, even across blocks
// - quality integer IDCT derived from IJG's 'slow'
// performance
// - fast huffman; reasonable integer IDCT
// - some SIMD kernels for common paths on targets with SSE2/NEON
// - uses a lot of intermediate memory, could cache poorly
#ifndef STBI_NO_JPEG
// huffman decoding acceleration
#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
typedef struct {
stbi_uc fast[1 << FAST_BITS];
// weirdly, repacking this into AoS is a 10% speed loss, instead of a win
stbi__uint16 code[256];
stbi_uc values[256];
stbi_uc size[257];
unsigned int maxcode[18];
int delta[17]; // old 'firstsymbol' - old 'firstcode'
} stbi__huffman;
typedef struct {
stbi__context *s;
stbi__huffman huff_dc[4];
stbi__huffman huff_ac[4];
stbi__uint16 dequant[4][64];
stbi__int16 fast_ac[4][1 << FAST_BITS];
// sizes for components, interleaved MCUs
int img_h_max, img_v_max;
int img_mcu_x, img_mcu_y;
int img_mcu_w, img_mcu_h;
// definition of jpeg image component
struct {
int id;
int h,v;
int tq;
int hd,ha;
int dc_pred;
int x,y,w2,h2;
stbi_uc *data;
void *raw_data, *raw_coeff;
stbi_uc *linebuf;
short *coeff; // progressive only
int coeff_w, coeff_h; // number of 8x8 coefficient blocks
} img_comp[4];
stbi__uint32 code_buffer; // jpeg entropy-coded buffer
int code_bits; // number of valid bits
unsigned char marker; // marker seen while filling entropy buffer
int nomore; // flag if we saw a marker so must stop
int progressive;
int spec_start;
int spec_end;
int succ_high;
int succ_low;
int eob_run;
int jfif;
int app14_color_transform; // Adobe APP14 tag
int rgb;
int scan_n, order[4];
int restart_interval, todo;
// kernels
void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);
void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);
stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);
} stbi__jpeg;
static int stbi__build_huffman(stbi__huffman *h, int *count) {
int i,j,k=0;
unsigned int code;
// build size list for each symbol (from JPEG spec)
for (i=0; i < 16; ++i)
for (j=0; j < count[i]; ++j)
h->size[k++] = (stbi_uc) (i+1);
h->size[k] = 0;
// compute actual symbols (from jpeg spec)
code = 0;
k = 0;
for(j=1; j <= 16; ++j) {
// compute delta to add to code to compute symbol id
h->delta[j] = k - code;
if (h->size[k] == j) {
while (h->size[k] == j)
h->code[k++] = (stbi__uint16) (code++);
if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG");
}
// compute largest code + 1 for this size, preshifted as needed later
h->maxcode[j] = code << (16-j);
code <<= 1;
}
h->maxcode[j] = 0xffffffff;
// build non-spec acceleration table; 255 is flag for not-accelerated
memset(h->fast, 255, 1 << FAST_BITS);
for (i=0; i < k; ++i) {
int s = h->size[i];
if (s <= FAST_BITS) {
int c = h->code[i] << (FAST_BITS-s);
int m = 1 << (FAST_BITS-s);
for (j=0; j < m; ++j) {
h->fast[c+j] = (stbi_uc) i;
}
}
}
return 1;
}
// build a table that decodes both magnitude and value of small ACs in
// one go.
static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) {
int i;
for (i=0; i < (1 << FAST_BITS); ++i) {
stbi_uc fast = h->fast[i];
fast_ac[i] = 0;
if (fast < 255) {
int rs = h->values[fast];
int run = (rs >> 4) & 15;
int magbits = rs & 15;
int len = h->size[fast];
if (magbits && len + magbits <= FAST_BITS) {
// magnitude code followed by receive_extend code
int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);
int m = 1 << (magbits - 1);
if (k < m) k += (~0U << magbits) + 1;
// if the result is small enough, we can fit it in fast_ac table
if (k >= -128 && k <= 127)
fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits));
}
}
}
}
static void stbi__grow_buffer_unsafe(stbi__jpeg *j) {
do {
unsigned int b = j->nomore ? 0 : stbi__get8(j->s);
if (b == 0xff) {
int c = stbi__get8(j->s);
while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes
if (c != 0) {
j->marker = (unsigned char) c;
j->nomore = 1;
return;
}
}
j->code_buffer |= b << (24 - j->code_bits);
j->code_bits += 8;
} while (j->code_bits <= 24);
}
// (1 << n) - 1
static const stbi__uint32 stbi__bmask[17]= {0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
// decode a jpeg huffman value from the bitstream
stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) {
unsigned int temp;
int c,k;
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
// look at the top FAST_BITS and determine what symbol ID it is,
// if the code is <= FAST_BITS
c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
k = h->fast[c];
if (k < 255) {
int s = h->size[k];
if (s > j->code_bits)
return -1;
j->code_buffer <<= s;
j->code_bits -= s;
return h->values[k];
}
// naive test is to shift the code_buffer down so k bits are
// valid, then test against maxcode. To speed this up, we've
// preshifted maxcode left so that it has (16-k) 0s at the
// end; in other words, regardless of the number of bits, it
// wants to be compared against something shifted to have 16;
// that way we don't need to shift inside the loop.
temp = j->code_buffer >> 16;
for (k=FAST_BITS+1 ; ; ++k)
if (temp < h->maxcode[k])
break;
if (k == 17) {
// error! code not found
j->code_bits -= 16;
return -1;
}
if (k > j->code_bits)
return -1;
// convert the huffman code to the symbol id
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
// convert the id to a symbol
j->code_bits -= k;
j->code_buffer <<= k;
return h->values[c];
}
// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j);
sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
k = stbi_lrot(j->code_buffer, n);
STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask)));
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
j->code_bits -= n;
return k + (stbi__jbias[n] & ~sgn);
}
// get some unsigned bits
stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) {
unsigned int k;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
k = stbi_lrot(j->code_buffer, n);
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
j->code_bits -= n;
return k;
}
stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) {
unsigned int k;
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
k = j->code_buffer;
j->code_buffer <<= 1;
--j->code_bits;
return k & 0x80000000;
}
// given a value that's at position X in the zigzag stream,
// where does it appear in the 8x8 matrix coded as row-major?
static const stbi_uc stbi__jpeg_dezigzag[64+15] = {
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
// let corrupt input sample past end
63, 63, 63, 63, 63, 63, 63, 63,
63, 63, 63, 63, 63, 63, 63
};
// decode one 64-entry block--
static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) {
int diff,dc,k;
int t;
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
t = stbi__jpeg_huff_decode(j, hdc);
if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
// 0 all the ac values now so we can do it 32-bits at a time
memset(data,0,64*sizeof(data[0]));
diff = t ? stbi__extend_receive(j, t) : 0;
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
data[0] = (short) (dc * dequant[0]);
// decode AC components, see JPEG spec
k = 1;
do {
unsigned int zig;
int c,r,s;
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
r = fac[c];
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
j->code_buffer <<= s;
j->code_bits -= s;
// decode into unzigzag'd location
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) ((r >> 8) * dequant[zig]);
} else {
int rs = stbi__jpeg_huff_decode(j, hac);
if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
s = rs & 15;
r = rs >> 4;
if (s == 0) {
if (rs != 0xf0) break; // end block
k += 16;
} else {
k += r;
// decode into unzigzag'd location
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);
}
}
} while (k < 64);
return 1;
}
static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) {
int diff,dc;
int t;
if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
if (j->succ_high == 0) {
// first scan for DC coefficient, must be first
memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
t = stbi__jpeg_huff_decode(j, hdc);
diff = t ? stbi__extend_receive(j, t) : 0;
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
data[0] = (short) (dc << j->succ_low);
} else {
// refinement scan for DC coefficient
if (stbi__jpeg_get_bit(j))
data[0] += (short) (1 << j->succ_low);
}
return 1;
}
// @OPTIMIZE: store non-zigzagged during the decode passes,
// and only de-zigzag when dequantizing
static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) {
int k;
if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
if (j->succ_high == 0) {
int shift = j->succ_low;
if (j->eob_run) {
--j->eob_run;
return 1;
}
k = j->spec_start;
do {
unsigned int zig;
int c,r,s;
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
r = fac[c];
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
j->code_buffer <<= s;
j->code_bits -= s;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) ((r >> 8) << shift);
} else {
int rs = stbi__jpeg_huff_decode(j, hac);
if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
s = rs & 15;
r = rs >> 4;
if (s == 0) {
if (r < 15) {
j->eob_run = (1 << r);
if (r)
j->eob_run += stbi__jpeg_get_bits(j, r);
--j->eob_run;
break;
}
k += 16;
} else {
k += r;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) (stbi__extend_receive(j,s) << shift);
}
}
} while (k <= j->spec_end);
} else {
// refinement scan for these AC coefficients
short bit = (short) (1 << j->succ_low);
if (j->eob_run) {
--j->eob_run;
for (k = j->spec_start; k <= j->spec_end; ++k) {
short *p = &data[stbi__jpeg_dezigzag[k]];
if (*p != 0)
if (stbi__jpeg_get_bit(j))
if ((*p & bit)==0) {
if (*p > 0)
*p += bit;
else
*p -= bit;
}
}
} else {
k = j->spec_start;
do {
int r,s;
int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh
if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
s = rs & 15;
r = rs >> 4;
if (s == 0) {
if (r < 15) {
j->eob_run = (1 << r) - 1;
if (r)
j->eob_run += stbi__jpeg_get_bits(j, r);
r = 64; // force end of block
} else {
// r=15 s=0 should write 16 0s, so we just do
// a run of 15 0s and then write s (which is 0),
// so we don't have to do anything special here
}
} else {
if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG");
// sign bit
if (stbi__jpeg_get_bit(j))
s = bit;
else
s = -bit;
}
// advance by r
while (k <= j->spec_end) {
short *p = &data[stbi__jpeg_dezigzag[k++]];
if (*p != 0) {
if (stbi__jpeg_get_bit(j))
if ((*p & bit)==0) {
if (*p > 0)
*p += bit;
else
*p -= bit;
}
} else {
if (r == 0) {
*p = (short) s;
break;
}
--r;
}
}
} while (k <= j->spec_end);
}
}
return 1;
}
// take a -128..127 value and stbi__clamp it and convert to 0..255
stbi_inline static stbi_uc stbi__clamp(int x) {
// trick to use a single test to catch both cases
if ((unsigned int) x > 255) {
if (x < 0) return 0;
if (x > 255) return 255;
}
return (stbi_uc) x;
}
#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5)))
#define stbi__fsh(x) ((x) * 4096)
// derived from jidctint -- DCT_ISLOW
#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
p2 = s2; \
p3 = s6; \
p1 = (p2+p3) * stbi__f2f(0.5411961f); \
t2 = p1 + p3*stbi__f2f(-1.847759065f); \
t3 = p1 + p2*stbi__f2f( 0.765366865f); \
p2 = s0; \
p3 = s4; \
t0 = stbi__fsh(p2+p3); \
t1 = stbi__fsh(p2-p3); \
x0 = t0+t3; \
x3 = t0-t3; \
x1 = t1+t2; \
x2 = t1-t2; \
t0 = s7; \
t1 = s5; \
t2 = s3; \
t3 = s1; \
p3 = t0+t2; \
p4 = t1+t3; \
p1 = t0+t3; \
p2 = t1+t2; \
p5 = (p3+p4)*stbi__f2f( 1.175875602f); \
t0 = t0*stbi__f2f( 0.298631336f); \
t1 = t1*stbi__f2f( 2.053119869f); \
t2 = t2*stbi__f2f( 3.072711026f); \
t3 = t3*stbi__f2f( 1.501321110f); \
p1 = p5 + p1*stbi__f2f(-0.899976223f); \
p2 = p5 + p2*stbi__f2f(-2.562915447f); \
p3 = p3*stbi__f2f(-1.961570560f); \
p4 = p4*stbi__f2f(-0.390180644f); \
t3 += p1+p4; \
t2 += p2+p3; \
t1 += p2+p4; \
t0 += p1+p3;
static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) {
int i,val[64],*v=val;
stbi_uc *o;
short *d = data;
// columns
for (i=0; i < 8; ++i,++d, ++v) {
// if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
&& d[40]==0 && d[48]==0 && d[56]==0) {
// no shortcut 0 seconds
// (1|2|3|4|5|6|7)==0 0 seconds
// all separate -0.047 seconds
// 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
int dcterm = d[0]*4;
v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
} else {
STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])
// constants scaled things up by 1<<12; let's bring them back
// down, but keep 2 extra bits of precision
x0 += 512;
x1 += 512;
x2 += 512;
x3 += 512;
v[ 0] = (x0+t3) >> 10;
v[56] = (x0-t3) >> 10;
v[ 8] = (x1+t2) >> 10;
v[48] = (x1-t2) >> 10;
v[16] = (x2+t1) >> 10;
v[40] = (x2-t1) >> 10;
v[24] = (x3+t0) >> 10;
v[32] = (x3-t0) >> 10;
}
}
for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
// no fast case since the first 1D IDCT spread components out
STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
// constants scaled things up by 1<<12, plus we had 1<<2 from first
// loop, plus horizontal and vertical each scale by sqrt(8) so together
// we've got an extra 1<<3, so 1<<17 total we need to remove.
// so we want to round that, which means adding 0.5 * 1<<17,
// aka 65536. Also, we'll end up with -128 to 127 that we want
// to encode as 0..255 by adding 128, so we'll add that before the shift
x0 += 65536 + (128<<17);
x1 += 65536 + (128<<17);
x2 += 65536 + (128<<17);
x3 += 65536 + (128<<17);
// tried computing the shifts into temps, or'ing the temps to see
// if any were out of range, but that was slower
o[0] = stbi__clamp((x0+t3) >> 17);
o[7] = stbi__clamp((x0-t3) >> 17);
o[1] = stbi__clamp((x1+t2) >> 17);
o[6] = stbi__clamp((x1-t2) >> 17);
o[2] = stbi__clamp((x2+t1) >> 17);
o[5] = stbi__clamp((x2-t1) >> 17);
o[3] = stbi__clamp((x3+t0) >> 17);
o[4] = stbi__clamp((x3-t0) >> 17);
}
}
#ifdef STBI_SSE2
// sse2 integer IDCT. not the fastest possible implementation but it
// produces bit-identical results to the generic C version so it's
// fully "transparent".
static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) {
// This is constructed to match our regular (generic) integer IDCT exactly.
__m128i row0, row1, row2, row3, row4, row5, row6, row7;
__m128i tmp;
// dot product constant: even elems=x, odd elems=y
#define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))
// out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit)
// out(1) = c1[even]*x + c1[odd]*y
#define dct_rot(out0,out1, x,y,c0,c1) \
__m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \
__m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \
__m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \
__m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \
__m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \
__m128i out1##_h = _mm_madd_epi16(c0##hi, c1)
// out = in << 12 (in 16-bit, out 32-bit)
#define dct_widen(out, in) \
__m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \
__m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)
// wide add
#define dct_wadd(out, a, b) \
__m128i out##_l = _mm_add_epi32(a##_l, b##_l); \
__m128i out##_h = _mm_add_epi32(a##_h, b##_h)
// wide sub
#define dct_wsub(out, a, b) \
__m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \
__m128i out##_h = _mm_sub_epi32(a##_h, b##_h)
// butterfly a/b, add bias, then shift by "s" and pack
#define dct_bfly32o(out0, out1, a,b,bias,s) \
{ \
__m128i abiased_l = _mm_add_epi32(a##_l, bias); \
__m128i abiased_h = _mm_add_epi32(a##_h, bias); \
dct_wadd(sum, abiased, b); \
dct_wsub(dif, abiased, b); \
out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \
out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \
}
// 8-bit interleave step (for transposes)
#define dct_interleave8(a, b) \
tmp = a; \
a = _mm_unpacklo_epi8(a, b); \
b = _mm_unpackhi_epi8(tmp, b)
// 16-bit interleave step (for transposes)
#define dct_interleave16(a, b) \
tmp = a; \
a = _mm_unpacklo_epi16(a, b); \
b = _mm_unpackhi_epi16(tmp, b)
#define dct_pass(bias,shift) \
{ \
/* even part */ \
dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \
__m128i sum04 = _mm_add_epi16(row0, row4); \
__m128i dif04 = _mm_sub_epi16(row0, row4); \
dct_widen(t0e, sum04); \
dct_widen(t1e, dif04); \
dct_wadd(x0, t0e, t3e); \
dct_wsub(x3, t0e, t3e); \
dct_wadd(x1, t1e, t2e); \
dct_wsub(x2, t1e, t2e); \
/* odd part */ \
dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \
dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \
__m128i sum17 = _mm_add_epi16(row1, row7); \
__m128i sum35 = _mm_add_epi16(row3, row5); \
dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \
dct_wadd(x4, y0o, y4o); \
dct_wadd(x5, y1o, y5o); \
dct_wadd(x6, y2o, y5o); \
dct_wadd(x7, y3o, y4o); \
dct_bfly32o(row0,row7, x0,x7,bias,shift); \
dct_bfly32o(row1,row6, x1,x6,bias,shift); \
dct_bfly32o(row2,row5, x2,x5,bias,shift); \
dct_bfly32o(row3,row4, x3,x4,bias,shift); \
}
__m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));
__m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f));
__m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));
__m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));
__m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f));
__m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f));
__m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f));
__m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f));
// rounding biases in column/row passes, see stbi__idct_block for explanation.
__m128i bias_0 = _mm_set1_epi32(512);
__m128i bias_1 = _mm_set1_epi32(65536 + (128<<17));
// load
row0 = _mm_load_si128((const __m128i *) (data + 0*8));
row1 = _mm_load_si128((const __m128i *) (data + 1*8));
row2 = _mm_load_si128((const __m128i *) (data + 2*8));
row3 = _mm_load_si128((const __m128i *) (data + 3*8));
row4 = _mm_load_si128((const __m128i *) (data + 4*8));
row5 = _mm_load_si128((const __m128i *) (data + 5*8));
row6 = _mm_load_si128((const __m128i *) (data + 6*8));
row7 = _mm_load_si128((const __m128i *) (data + 7*8));
// column pass
dct_pass(bias_0, 10);
{
// 16bit 8x8 transpose pass 1
dct_interleave16(row0, row4);
dct_interleave16(row1, row5);
dct_interleave16(row2, row6);
dct_interleave16(row3, row7);
// transpose pass 2
dct_interleave16(row0, row2);
dct_interleave16(row1, row3);
dct_interleave16(row4, row6);
dct_interleave16(row5, row7);
// transpose pass 3
dct_interleave16(row0, row1);
dct_interleave16(row2, row3);
dct_interleave16(row4, row5);
dct_interleave16(row6, row7);
}
// row pass
dct_pass(bias_1, 17);
{
// pack
__m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7
__m128i p1 = _mm_packus_epi16(row2, row3);
__m128i p2 = _mm_packus_epi16(row4, row5);
__m128i p3 = _mm_packus_epi16(row6, row7);
// 8bit 8x8 transpose pass 1
dct_interleave8(p0, p2); // a0e0a1e1...
dct_interleave8(p1, p3); // c0g0c1g1...
// transpose pass 2
dct_interleave8(p0, p1); // a0c0e0g0...
dct_interleave8(p2, p3); // b0d0f0h0...
// transpose pass 3
dct_interleave8(p0, p2); // a0b0c0d0...
dct_interleave8(p1, p3); // a4b4c4d4...
// store
_mm_storel_epi64((__m128i *) out, p0);
out += out_stride;
_mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e));
out += out_stride;
_mm_storel_epi64((__m128i *) out, p2);
out += out_stride;
_mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e));
out += out_stride;
_mm_storel_epi64((__m128i *) out, p1);
out += out_stride;
_mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e));
out += out_stride;
_mm_storel_epi64((__m128i *) out, p3);
out += out_stride;
_mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e));
}
#undef dct_const
#undef dct_rot
#undef dct_widen
#undef dct_wadd
#undef dct_wsub
#undef dct_bfly32o
#undef dct_interleave8
#undef dct_interleave16
#undef dct_pass
}
#endif // STBI_SSE2
#ifdef STBI_NEON
// NEON integer IDCT. should produce bit-identical
// results to the generic C version.
static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) {
int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;
int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));
int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));
int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f));
int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f));
int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));
int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));
int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));
int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));
int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f));
int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f));
int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f));
int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f));
#define dct_long_mul(out, inq, coeff) \
int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \
int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)
#define dct_long_mac(out, acc, inq, coeff) \
int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \
int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)
#define dct_widen(out, inq) \
int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \
int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)
// wide add
#define dct_wadd(out, a, b) \
int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \
int32x4_t out##_h = vaddq_s32(a##_h, b##_h)
// wide sub
#define dct_wsub(out, a, b) \
int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \
int32x4_t out##_h = vsubq_s32(a##_h, b##_h)
// butterfly a/b, then shift using "shiftop" by "s" and pack
#define dct_bfly32o(out0,out1, a,b,shiftop,s) \
{ \
dct_wadd(sum, a, b); \
dct_wsub(dif, a, b); \
out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \
out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \
}
#define dct_pass(shiftop, shift) \
{ \
/* even part */ \
int16x8_t sum26 = vaddq_s16(row2, row6); \
dct_long_mul(p1e, sum26, rot0_0); \
dct_long_mac(t2e, p1e, row6, rot0_1); \
dct_long_mac(t3e, p1e, row2, rot0_2); \
int16x8_t sum04 = vaddq_s16(row0, row4); \
int16x8_t dif04 = vsubq_s16(row0, row4); \
dct_widen(t0e, sum04); \
dct_widen(t1e, dif04); \
dct_wadd(x0, t0e, t3e); \
dct_wsub(x3, t0e, t3e); \
dct_wadd(x1, t1e, t2e); \
dct_wsub(x2, t1e, t2e); \
/* odd part */ \
int16x8_t sum15 = vaddq_s16(row1, row5); \
int16x8_t sum17 = vaddq_s16(row1, row7); \
int16x8_t sum35 = vaddq_s16(row3, row5); \
int16x8_t sum37 = vaddq_s16(row3, row7); \
int16x8_t sumodd = vaddq_s16(sum17, sum35); \
dct_long_mul(p5o, sumodd, rot1_0); \
dct_long_mac(p1o, p5o, sum17, rot1_1); \
dct_long_mac(p2o, p5o, sum35, rot1_2); \
dct_long_mul(p3o, sum37, rot2_0); \
dct_long_mul(p4o, sum15, rot2_1); \
dct_wadd(sump13o, p1o, p3o); \
dct_wadd(sump24o, p2o, p4o); \
dct_wadd(sump23o, p2o, p3o); \
dct_wadd(sump14o, p1o, p4o); \
dct_long_mac(x4, sump13o, row7, rot3_0); \
dct_long_mac(x5, sump24o, row5, rot3_1); \
dct_long_mac(x6, sump23o, row3, rot3_2); \
dct_long_mac(x7, sump14o, row1, rot3_3); \
dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \
dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \
dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \
dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \
}
// load
row0 = vld1q_s16(data + 0*8);
row1 = vld1q_s16(data + 1*8);
row2 = vld1q_s16(data + 2*8);
row3 = vld1q_s16(data + 3*8);
row4 = vld1q_s16(data + 4*8);
row5 = vld1q_s16(data + 5*8);
row6 = vld1q_s16(data + 6*8);
row7 = vld1q_s16(data + 7*8);
// add DC bias
row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));
// column pass
dct_pass(vrshrn_n_s32, 10);
// 16bit 8x8 transpose
{
// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.
// whether compilers actually get this is another story, sadly.
#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }
#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }
#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }
// pass 1
dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6
dct_trn16(row2, row3);
dct_trn16(row4, row5);
dct_trn16(row6, row7);
// pass 2
dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4
dct_trn32(row1, row3);
dct_trn32(row4, row6);
dct_trn32(row5, row7);
// pass 3
dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0
dct_trn64(row1, row5);
dct_trn64(row2, row6);
dct_trn64(row3, row7);
#undef dct_trn16
#undef dct_trn32
#undef dct_trn64
}
// row pass
// vrshrn_n_s32 only supports shifts up to 16, we need
// 17. so do a non-rounding shift of 16 first then follow
// up with a rounding shift by 1.
dct_pass(vshrn_n_s32, 16);
{
// pack and round
uint8x8_t p0 = vqrshrun_n_s16(row0, 1);
uint8x8_t p1 = vqrshrun_n_s16(row1, 1);
uint8x8_t p2 = vqrshrun_n_s16(row2, 1);
uint8x8_t p3 = vqrshrun_n_s16(row3, 1);
uint8x8_t p4 = vqrshrun_n_s16(row4, 1);
uint8x8_t p5 = vqrshrun_n_s16(row5, 1);
uint8x8_t p6 = vqrshrun_n_s16(row6, 1);
uint8x8_t p7 = vqrshrun_n_s16(row7, 1);
// again, these can translate into one instruction, but often don't.
#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }
#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }
#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }
// sadly can't use interleaved stores here since we only write
// 8 bytes to each scan line!
// 8x8 8-bit transpose pass 1
dct_trn8_8(p0, p1);
dct_trn8_8(p2, p3);
dct_trn8_8(p4, p5);
dct_trn8_8(p6, p7);
// pass 2
dct_trn8_16(p0, p2);
dct_trn8_16(p1, p3);
dct_trn8_16(p4, p6);
dct_trn8_16(p5, p7);
// pass 3
dct_trn8_32(p0, p4);
dct_trn8_32(p1, p5);
dct_trn8_32(p2, p6);
dct_trn8_32(p3, p7);
// store
vst1_u8(out, p0);
out += out_stride;
vst1_u8(out, p1);
out += out_stride;
vst1_u8(out, p2);
out += out_stride;
vst1_u8(out, p3);
out += out_stride;
vst1_u8(out, p4);
out += out_stride;
vst1_u8(out, p5);
out += out_stride;
vst1_u8(out, p6);
out += out_stride;
vst1_u8(out, p7);
#undef dct_trn8_8
#undef dct_trn8_16
#undef dct_trn8_32
}
#undef dct_long_mul
#undef dct_long_mac
#undef dct_widen
#undef dct_wadd
#undef dct_wsub
#undef dct_bfly32o
#undef dct_pass
}
#endif // STBI_NEON
#define STBI__MARKER_none 0xff
// if there's a pending marker from the entropy stream, return that
// otherwise, fetch from the stream and get a marker. if there's no
// marker, return 0xff, which is never a valid marker value
static stbi_uc stbi__get_marker(stbi__jpeg *j) {
stbi_uc x;
if (j->marker != STBI__MARKER_none) {
x = j->marker;
j->marker = STBI__MARKER_none;
return x;
}
x = stbi__get8(j->s);
if (x != 0xff) return STBI__MARKER_none;
while (x == 0xff)
x = stbi__get8(j->s); // consume repeated 0xff fill bytes
return x;
}
// in each scan, we'll have scan_n components, and the order
// of the components is specified by order[]
#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
// after a restart interval, stbi__jpeg_reset the entropy decoder and
// the dc prediction
static void stbi__jpeg_reset(stbi__jpeg *j) {
j->code_bits = 0;
j->code_buffer = 0;
j->nomore = 0;
j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;
j->marker = STBI__MARKER_none;
j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
j->eob_run = 0;
// no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
// since we don't even allow 1<<30 pixels
}
static int stbi__parse_entropy_coded_data(stbi__jpeg *z) {
stbi__jpeg_reset(z);
if (!z->progressive) {
if (z->scan_n == 1) {
int i,j;
STBI_SIMD_ALIGN(short, data[64]);
int n = z->order[0];
// non-interleaved data, we just need to process one block at a time,
// in trivial scanline order
// number of blocks to do just depends on how many actual "pixels" this
// component has, independent of interleaved MCU blocking and such
int w = (z->img_comp[n].x+7) >> 3;
int h = (z->img_comp[n].y+7) >> 3;
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
int ha = z->img_comp[n].ha;
if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
// every data block is an MCU, so countdown the restart interval
if (--z->todo <= 0) {
if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
// if it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
if (!STBI__RESTART(z->marker)) return 1;
stbi__jpeg_reset(z);
}
}
}
return 1;
} else { // interleaved
int i,j,k,x,y;
STBI_SIMD_ALIGN(short, data[64]);
for (j=0; j < z->img_mcu_y; ++j) {
for (i=0; i < z->img_mcu_x; ++i) {
// scan an interleaved mcu... process scan_n components in order
for (k=0; k < z->scan_n; ++k) {
int n = z->order[k];
// scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (y=0; y < z->img_comp[n].v; ++y) {
for (x=0; x < z->img_comp[n].h; ++x) {
int x2 = (i*z->img_comp[n].h + x)*8;
int y2 = (j*z->img_comp[n].v + y)*8;
int ha = z->img_comp[n].ha;
if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);
}
}
}
// after all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
if (--z->todo <= 0) {
if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
if (!STBI__RESTART(z->marker)) return 1;
stbi__jpeg_reset(z);
}
}
}
return 1;
}
} else {
if (z->scan_n == 1) {
int i,j;
int n = z->order[0];
// non-interleaved data, we just need to process one block at a time,
// in trivial scanline order
// number of blocks to do just depends on how many actual "pixels" this
// component has, independent of interleaved MCU blocking and such
int w = (z->img_comp[n].x+7) >> 3;
int h = (z->img_comp[n].y+7) >> 3;
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
if (z->spec_start == 0) {
if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
return 0;
} else {
int ha = z->img_comp[n].ha;
if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))
return 0;
}
// every data block is an MCU, so countdown the restart interval
if (--z->todo <= 0) {
if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
if (!STBI__RESTART(z->marker)) return 1;
stbi__jpeg_reset(z);
}
}
}
return 1;
} else { // interleaved
int i,j,k,x,y;
for (j=0; j < z->img_mcu_y; ++j) {
for (i=0; i < z->img_mcu_x; ++i) {
// scan an interleaved mcu... process scan_n components in order
for (k=0; k < z->scan_n; ++k) {
int n = z->order[k];
// scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (y=0; y < z->img_comp[n].v; ++y) {
for (x=0; x < z->img_comp[n].h; ++x) {
int x2 = (i*z->img_comp[n].h + x);
int y2 = (j*z->img_comp[n].v + y);
short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);
if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
return 0;
}
}
}
// after all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
if (--z->todo <= 0) {
if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
if (!STBI__RESTART(z->marker)) return 1;
stbi__jpeg_reset(z);
}
}
}
return 1;
}
}
}
static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) {
int i;
for (i=0; i < 64; ++i)
data[i] *= dequant[i];
}
static void stbi__jpeg_finish(stbi__jpeg *z) {
if (z->progressive) {
// dequantize and idct the data
int i,j,n;
for (n=0; n < z->s->img_n; ++n) {
int w = (z->img_comp[n].x+7) >> 3;
int h = (z->img_comp[n].y+7) >> 3;
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);
z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
}
}
}
}
}
static int stbi__process_marker(stbi__jpeg *z, int m) {
int L;
switch (m) {
case STBI__MARKER_none: // no marker found
return stbi__err("expected marker","Corrupt JPEG");
case 0xDD: // DRI - specify restart interval
if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG");
z->restart_interval = stbi__get16be(z->s);
return 1;
case 0xDB: // DQT - define quantization table
L = stbi__get16be(z->s)-2;
while (L > 0) {
int q = stbi__get8(z->s);
int p = q >> 4, sixteen = (p != 0);
int t = q & 15,i;
if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG");
if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG");
for (i=0; i < 64; ++i)
z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));
L -= (sixteen ? 129 : 65);
}
return L==0;
case 0xC4: // DHT - define huffman table
L = stbi__get16be(z->s)-2;
while (L > 0) {
stbi_uc *v;
int sizes[16],i,n=0;
int q = stbi__get8(z->s);
int tc = q >> 4;
int th = q & 15;
if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG");
for (i=0; i < 16; ++i) {
sizes[i] = stbi__get8(z->s);
n += sizes[i];
}
L -= 17;
if (tc == 0) {
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
v = z->huff_dc[th].values;
} else {
if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;
v = z->huff_ac[th].values;
}
for (i=0; i < n; ++i)
v[i] = stbi__get8(z->s);
if (tc != 0)
stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);
L -= n;
}
return L==0;
}
// check for comment block or APP blocks
if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
L = stbi__get16be(z->s);
if (L < 2) {
if (m == 0xFE)
return stbi__err("bad COM len","Corrupt JPEG");
else
return stbi__err("bad APP len","Corrupt JPEG");
}
L -= 2;
if (m == 0xE0 && L >= 5) { // JFIF APP0 segment
static const unsigned char tag[5] = {'J','F','I','F','\0'};
int ok = 1;
int i;
for (i=0; i < 5; ++i)
if (stbi__get8(z->s) != tag[i])
ok = 0;
L -= 5;
if (ok)
z->jfif = 1;
} else if (m == 0xEE && L >= 12) { // Adobe APP14 segment
static const unsigned char tag[6] = {'A','d','o','b','e','\0'};
int ok = 1;
int i;
for (i=0; i < 6; ++i)
if (stbi__get8(z->s) != tag[i])
ok = 0;
L -= 6;
if (ok) {
stbi__get8(z->s); // version
stbi__get16be(z->s); // flags0
stbi__get16be(z->s); // flags1
z->app14_color_transform = stbi__get8(z->s); // color transform
L -= 6;
}
}
stbi__skip(z->s, L);
return 1;
}
return stbi__err("unknown marker","Corrupt JPEG");
}
// after we see SOS
static int stbi__process_scan_header(stbi__jpeg *z) {
int i;
int Ls = stbi__get16be(z->s);
z->scan_n = stbi__get8(z->s);
if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG");
if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG");
for (i=0; i < z->scan_n; ++i) {
int id = stbi__get8(z->s), which;
int q = stbi__get8(z->s);
for (which = 0; which < z->s->img_n; ++which)
if (z->img_comp[which].id == id)
break;
if (which == z->s->img_n) return 0; // no match
z->img_comp[which].hd = q >> 4;
if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG");
z->img_comp[which].ha = q & 15;
if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG");
z->order[i] = which;
}
{
int aa;
z->spec_start = stbi__get8(z->s);
z->spec_end = stbi__get8(z->s); // should be 63, but might be 0
aa = stbi__get8(z->s);
z->succ_high = (aa >> 4);
z->succ_low = (aa & 15);
if (z->progressive) {
if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)
return stbi__err("bad SOS", "Corrupt JPEG");
} else {
if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG");
if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG");
z->spec_end = 63;
}
}
return 1;
}
static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) {
int i;
for (i=0; i < ncomp; ++i) {
if (z->img_comp[i].raw_data) {
STBI_FREE(z->img_comp[i].raw_data);
z->img_comp[i].raw_data = NULL;
z->img_comp[i].data = NULL;
}
if (z->img_comp[i].raw_coeff) {
STBI_FREE(z->img_comp[i].raw_coeff);
z->img_comp[i].raw_coeff = 0;
z->img_comp[i].coeff = 0;
}
if (z->img_comp[i].linebuf) {
STBI_FREE(z->img_comp[i].linebuf);
z->img_comp[i].linebuf = NULL;
}
}
return why;
}
static int stbi__process_frame_header(stbi__jpeg *z, int scan) {
stbi__context *s = z->s;
int Lf,p,i,q, h_max=1,v_max=1,c;
Lf = stbi__get16be(s);
if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG
p = stbi__get8(s);
if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
s->img_y = stbi__get16be(s);
if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
s->img_x = stbi__get16be(s);
if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires
c = stbi__get8(s);
if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG");
s->img_n = c;
for (i=0; i < c; ++i) {
z->img_comp[i].data = NULL;
z->img_comp[i].linebuf = NULL;
}
if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG");
z->rgb = 0;
for (i=0; i < s->img_n; ++i) {
static const unsigned char rgb[3] = { 'R', 'G', 'B' };
z->img_comp[i].id = stbi__get8(s);
if (s->img_n == 3 && z->img_comp[i].id == rgb[i])
++z->rgb;
q = stbi__get8(s);
z->img_comp[i].h = (q >> 4);
if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG");
z->img_comp[i].v = q & 15;
if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG");
z->img_comp[i].tq = stbi__get8(s);
if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG");
}
if (scan != STBI__SCAN_load) return 1;
if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode");
for (i=0; i < s->img_n; ++i) {
if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;
if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
}
// compute interleaved mcu info
z->img_h_max = h_max;
z->img_v_max = v_max;
z->img_mcu_w = h_max * 8;
z->img_mcu_h = v_max * 8;
// these sizes can't be more than 17 bits
z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;
z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;
for (i=0; i < s->img_n; ++i) {
// number of effective pixels (e.g. for non-interleaved MCU)
z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;
z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;
// to simplify generation, we'll allocate enough memory to decode
// the bogus oversized data from using interleaved MCUs and their
// big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
// discard the extra data until colorspace conversion
//
// img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)
// so these muls can't overflow with 32-bit ints (which we require)
z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
z->img_comp[i].coeff = 0;
z->img_comp[i].raw_coeff = 0;
z->img_comp[i].linebuf = NULL;
z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);
if (z->img_comp[i].raw_data == NULL)
return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
// align blocks for idct using mmx/sse
z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);
if (z->progressive) {
// w2, h2 are multiples of 8 (see above)
z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;
z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;
z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);
if (z->img_comp[i].raw_coeff == NULL)
return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);
}
}
return 1;
}
// use comparisons since in some cases we handle more than one case (e.g. SOF)
#define stbi__DNL(x) ((x) == 0xdc)
#define stbi__SOI(x) ((x) == 0xd8)
#define stbi__EOI(x) ((x) == 0xd9)
#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
#define stbi__SOS(x) ((x) == 0xda)
#define stbi__SOF_progressive(x) ((x) == 0xc2)
static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) {
int m;
z->jfif = 0;
z->app14_color_transform = -1; // valid values are 0,1,2
z->marker = STBI__MARKER_none; // initialize cached marker to empty
m = stbi__get_marker(z);
if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG");
if (scan == STBI__SCAN_type) return 1;
m = stbi__get_marker(z);
while (!stbi__SOF(m)) {
if (!stbi__process_marker(z,m)) return 0;
m = stbi__get_marker(z);
while (m == STBI__MARKER_none) {
// some files have extra padding after their blocks, so ok, we'll scan
if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG");
m = stbi__get_marker(z);
}
}
z->progressive = stbi__SOF_progressive(m);
if (!stbi__process_frame_header(z, scan)) return 0;
return 1;
}
// decode image to YCbCr format
static int stbi__decode_jpeg_image(stbi__jpeg *j) {
int m;
for (m = 0; m < 4; m++) {
j->img_comp[m].raw_data = NULL;
j->img_comp[m].raw_coeff = NULL;
}
j->restart_interval = 0;
if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;
m = stbi__get_marker(j);
while (!stbi__EOI(m)) {
if (stbi__SOS(m)) {
if (!stbi__process_scan_header(j)) return 0;
if (!stbi__parse_entropy_coded_data(j)) return 0;
if (j->marker == STBI__MARKER_none ) {
// handle 0s at the end of image data from IP Kamera 9060
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
if (x == 255) {
j->marker = stbi__get8(j->s);
break;
}
}
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
}
} else if (stbi__DNL(m)) {
int Ld = stbi__get16be(j->s);
stbi__uint32 NL = stbi__get16be(j->s);
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
} else {
if (!stbi__process_marker(j, m)) return 0;
}
m = stbi__get_marker(j);
}
if (j->progressive)
stbi__jpeg_finish(j);
return 1;
}
// static jfif-centered resampling (across block boundaries)
typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,
int w, int hs);
#define stbi__div4(x) ((stbi_uc) ((x) >> 2))
static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {
STBI_NOTUSED(out);
STBI_NOTUSED(in_far);
STBI_NOTUSED(w);
STBI_NOTUSED(hs);
return in_near;
}
static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {
// need to generate two samples vertically for every one in input
int i;
STBI_NOTUSED(hs);
for (i=0; i < w; ++i)
out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);
return out;
}
static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {
// need to generate two samples horizontally for every one in input
int i;
stbi_uc *input = in_near;
if (w == 1) {
// if only one sample, can't do any interpolation
out[0] = out[1] = input[0];
return out;
}
out[0] = input[0];
out[1] = stbi__div4(input[0]*3 + input[1] + 2);
for (i=1; i < w-1; ++i) {
int n = 3*input[i]+2;
out[i*2+0] = stbi__div4(n+input[i-1]);
out[i*2+1] = stbi__div4(n+input[i+1]);
}
out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);
out[i*2+1] = input[w-1];
STBI_NOTUSED(in_far);
STBI_NOTUSED(hs);
return out;
}
#define stbi__div16(x) ((stbi_uc) ((x) >> 4))
static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {
// need to generate 2x2 samples for every one in input
int i,t0,t1;
if (w == 1) {
out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
return out;
}
t1 = 3*in_near[0] + in_far[0];
out[0] = stbi__div4(t1+2);
for (i=1; i < w; ++i) {
t0 = t1;
t1 = 3*in_near[i]+in_far[i];
out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
}
out[w*2-1] = stbi__div4(t1+2);
STBI_NOTUSED(hs);
return out;
}
#if defined(STBI_SSE2) || defined(STBI_NEON)
static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {
// need to generate 2x2 samples for every one in input
int i=0,t0,t1;
if (w == 1) {
out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
return out;
}
t1 = 3*in_near[0] + in_far[0];
// process groups of 8 pixels for as long as we can.
// note we can't handle the last pixel in a row in this loop
// because we need to handle the filter boundary conditions.
for (; i < ((w-1) & ~7); i += 8) {
#if defined(STBI_SSE2)
// load and perform the vertical filtering pass
// this uses 3*x + y = 4*x + (y - x)
__m128i zero = _mm_setzero_si128();
__m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i));
__m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));
__m128i farw = _mm_unpacklo_epi8(farb, zero);
__m128i nearw = _mm_unpacklo_epi8(nearb, zero);
__m128i diff = _mm_sub_epi16(farw, nearw);
__m128i nears = _mm_slli_epi16(nearw, 2);
__m128i curr = _mm_add_epi16(nears, diff); // current row
// horizontal filter works the same based on shifted vers of current
// row. "prev" is current row shifted right by 1 pixel; we need to
// insert the previous pixel value (from t1).
// "next" is current row shifted left by 1 pixel, with first pixel
// of next block of 8 pixels added in.
__m128i prv0 = _mm_slli_si128(curr, 2);
__m128i nxt0 = _mm_srli_si128(curr, 2);
__m128i prev = _mm_insert_epi16(prv0, t1, 0);
__m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);
// horizontal filter, polyphase implementation since it's convenient:
// even pixels = 3*cur + prev = cur*4 + (prev - cur)
// odd pixels = 3*cur + next = cur*4 + (next - cur)
// note the shared term.
__m128i bias = _mm_set1_epi16(8);
__m128i curs = _mm_slli_epi16(curr, 2);
__m128i prvd = _mm_sub_epi16(prev, curr);
__m128i nxtd = _mm_sub_epi16(next, curr);
__m128i curb = _mm_add_epi16(curs, bias);
__m128i even = _mm_add_epi16(prvd, curb);
__m128i odd = _mm_add_epi16(nxtd, curb);
// interleave even and odd pixels, then undo scaling.
__m128i int0 = _mm_unpacklo_epi16(even, odd);
__m128i int1 = _mm_unpackhi_epi16(even, odd);
__m128i de0 = _mm_srli_epi16(int0, 4);
__m128i de1 = _mm_srli_epi16(int1, 4);
// pack and write output
__m128i outv = _mm_packus_epi16(de0, de1);
_mm_storeu_si128((__m128i *) (out + i*2), outv);
#elif defined(STBI_NEON)
// load and perform the vertical filtering pass
// this uses 3*x + y = 4*x + (y - x)
uint8x8_t farb = vld1_u8(in_far + i);
uint8x8_t nearb = vld1_u8(in_near + i);
int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));
int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));
int16x8_t curr = vaddq_s16(nears, diff); // current row
// horizontal filter works the same based on shifted vers of current
// row. "prev" is current row shifted right by 1 pixel; we need to
// insert the previous pixel value (from t1).
// "next" is current row shifted left by 1 pixel, with first pixel
// of next block of 8 pixels added in.
int16x8_t prv0 = vextq_s16(curr, curr, 7);
int16x8_t nxt0 = vextq_s16(curr, curr, 1);
int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);
int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);
// horizontal filter, polyphase implementation since it's convenient:
// even pixels = 3*cur + prev = cur*4 + (prev - cur)
// odd pixels = 3*cur + next = cur*4 + (next - cur)
// note the shared term.
int16x8_t curs = vshlq_n_s16(curr, 2);
int16x8_t prvd = vsubq_s16(prev, curr);
int16x8_t nxtd = vsubq_s16(next, curr);
int16x8_t even = vaddq_s16(curs, prvd);
int16x8_t odd = vaddq_s16(curs, nxtd);
// undo scaling and round, then store with even/odd phases interleaved
uint8x8x2_t o;
o.val[0] = vqrshrun_n_s16(even, 4);
o.val[1] = vqrshrun_n_s16(odd, 4);
vst2_u8(out + i*2, o);
#endif
// "previous" value for next iter
t1 = 3*in_near[i+7] + in_far[i+7];
}
t0 = t1;
t1 = 3*in_near[i] + in_far[i];
out[i*2] = stbi__div16(3*t1 + t0 + 8);
for (++i; i < w; ++i) {
t0 = t1;
t1 = 3*in_near[i]+in_far[i];
out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
}
out[w*2-1] = stbi__div4(t1+2);
STBI_NOTUSED(hs);
return out;
}
#endif
static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {
// resample with nearest-neighbor
int i,j;
STBI_NOTUSED(in_far);
for (i=0; i < w; ++i)
for (j=0; j < hs; ++j)
out[i*hs+j] = in_near[i];
return out;
}
// this is a reduced-precision calculation of YCbCr-to-RGB introduced
// to make sure the code produces the same results in both SIMD and scalar
#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8)
static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) {
int i;
for (i=0; i < count; ++i) {
int y_fixed = (y[i] << 20) + (1<<19); // rounding
int r,g,b;
int cr = pcr[i] - 128;
int cb = pcb[i] - 128;
r = y_fixed + cr* stbi__float2fixed(1.40200f);
g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
b = y_fixed + cb* stbi__float2fixed(1.77200f);
r >>= 20;
g >>= 20;
b >>= 20;
if ((unsigned) r > 255) {
if (r < 0) r = 0;
else r = 255;
}
if ((unsigned) g > 255) {
if (g < 0) g = 0;
else g = 255;
}
if ((unsigned) b > 255) {
if (b < 0) b = 0;
else b = 255;
}
out[0] = (stbi_uc)r;
out[1] = (stbi_uc)g;
out[2] = (stbi_uc)b;
out[3] = 255;
out += step;
}
}
#if defined(STBI_SSE2) || defined(STBI_NEON)
static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) {
int i = 0;
#ifdef STBI_SSE2
// step == 3 is pretty ugly on the final interleave, and i'm not convinced
// it's useful in practice (you wouldn't use it for textures, for example).
// so just accelerate step == 4 case.
if (step == 4) {
// this is a fairly straightforward implementation and not super-optimized.
__m128i signflip = _mm_set1_epi8(-0x80);
__m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f));
__m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));
__m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));
__m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f));
__m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);
__m128i xw = _mm_set1_epi16(255); // alpha channel
for (; i+7 < count; i += 8) {
// load
__m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));
__m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));
__m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));
__m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
__m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128
// unpack to short (and left-shift cr, cb by 8)
__m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
__m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
__m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);
// color transform
__m128i yws = _mm_srli_epi16(yw, 4);
__m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
__m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
__m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
__m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
__m128i rws = _mm_add_epi16(cr0, yws);
__m128i gwt = _mm_add_epi16(cb0, yws);
__m128i bws = _mm_add_epi16(yws, cb1);
__m128i gws = _mm_add_epi16(gwt, cr1);
// descale
__m128i rw = _mm_srai_epi16(rws, 4);
__m128i bw = _mm_srai_epi16(bws, 4);
__m128i gw = _mm_srai_epi16(gws, 4);
// back to byte, set up for transpose
__m128i brb = _mm_packus_epi16(rw, bw);
__m128i gxb = _mm_packus_epi16(gw, xw);
// transpose to interleave channels
__m128i t0 = _mm_unpacklo_epi8(brb, gxb);
__m128i t1 = _mm_unpackhi_epi8(brb, gxb);
__m128i o0 = _mm_unpacklo_epi16(t0, t1);
__m128i o1 = _mm_unpackhi_epi16(t0, t1);
// store
_mm_storeu_si128((__m128i *) (out + 0), o0);
_mm_storeu_si128((__m128i *) (out + 16), o1);
out += 32;
}
}
#endif
#ifdef STBI_NEON
// in this version, step=3 support would be easy to add. but is there demand?
if (step == 4) {
// this is a fairly straightforward implementation and not super-optimized.
uint8x8_t signflip = vdup_n_u8(0x80);
int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f));
int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f));
int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f));
int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f));
for (; i+7 < count; i += 8) {
// load
uint8x8_t y_bytes = vld1_u8(y + i);
uint8x8_t cr_bytes = vld1_u8(pcr + i);
uint8x8_t cb_bytes = vld1_u8(pcb + i);
int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));
int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));
// expand to s16
int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));
int16x8_t crw = vshll_n_s8(cr_biased, 7);
int16x8_t cbw = vshll_n_s8(cb_biased, 7);
// color transform
int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);
int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);
int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);
int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);
int16x8_t rws = vaddq_s16(yws, cr0);
int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);
int16x8_t bws = vaddq_s16(yws, cb1);
// undo scaling, round, convert to byte
uint8x8x4_t o;
o.val[0] = vqrshrun_n_s16(rws, 4);
o.val[1] = vqrshrun_n_s16(gws, 4);
o.val[2] = vqrshrun_n_s16(bws, 4);
o.val[3] = vdup_n_u8(255);
// store, interleaving r/g/b/a
vst4_u8(out, o);
out += 8*4;
}
}
#endif
for (; i < count; ++i) {
int y_fixed = (y[i] << 20) + (1<<19); // rounding
int r,g,b;
int cr = pcr[i] - 128;
int cb = pcb[i] - 128;
r = y_fixed + cr* stbi__float2fixed(1.40200f);
g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
b = y_fixed + cb* stbi__float2fixed(1.77200f);
r >>= 20;
g >>= 20;
b >>= 20;
if ((unsigned) r > 255) {
if (r < 0) r = 0;
else r = 255;
}
if ((unsigned) g > 255) {
if (g < 0) g = 0;
else g = 255;
}
if ((unsigned) b > 255) {
if (b < 0) b = 0;
else b = 255;
}
out[0] = (stbi_uc)r;
out[1] = (stbi_uc)g;
out[2] = (stbi_uc)b;
out[3] = 255;
out += step;
}
}
#endif
// set up the kernels
static void stbi__setup_jpeg(stbi__jpeg *j) {
j->idct_block_kernel = stbi__idct_block;
j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;
j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;
#ifdef STBI_SSE2
if (stbi__sse2_available()) {
j->idct_block_kernel = stbi__idct_simd;
j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
}
#endif
#ifdef STBI_NEON
j->idct_block_kernel = stbi__idct_simd;
j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
#endif
}
// clean up the temporary component buffers
static void stbi__cleanup_jpeg(stbi__jpeg *j) {
stbi__free_jpeg_components(j, j->s->img_n, 0);
}
typedef struct {
resample_row_func resample;
stbi_uc *line0,*line1;
int hs,vs; // expansion factor in each axis
int w_lores; // horizontal pixels pre-expansion
int ystep; // how far through vertical expansion we are
int ypos; // which pre-expansion row we're on
} stbi__resample;
// fast 0..255 * 0..255 => 0..255 rounded multiplication
static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) {
unsigned int t = x*y + 128;
return (stbi_uc) ((t + (t >>8)) >> 8);
}
static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) {
int n, decode_n, is_rgb;
z->s->img_n = 0; // make stbi__cleanup_jpeg safe
// validate req_comp
if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
// load a jpeg image from whichever source, but leave in YCbCr format
if (!stbi__decode_jpeg_image(z)) {
stbi__cleanup_jpeg(z);
return NULL;
}
// determine actual number of components to generate
n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1;
is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));
if (z->s->img_n == 3 && n < 3 && !is_rgb)
decode_n = 1;
else
decode_n = z->s->img_n;
// resample and color-convert
{
int k;
unsigned int i,j;
stbi_uc *output;
stbi_uc *coutput[4];
stbi__resample res_comp[4];
for (k=0; k < decode_n; ++k) {
stbi__resample *r = &res_comp[k];
// allocate line buffer big enough for upsampling off the edges
// with upsample factor of 4
z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);
if (!z->img_comp[k].linebuf) {
stbi__cleanup_jpeg(z);
return stbi__errpuc("outofmem", "Out of memory");
}
r->hs = z->img_h_max / z->img_comp[k].h;
r->vs = z->img_v_max / z->img_comp[k].v;
r->ystep = r->vs >> 1;
r->w_lores = (z->s->img_x + r->hs-1) / r->hs;
r->ypos = 0;
r->line0 = r->line1 = z->img_comp[k].data;
if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;
else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;
else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;
else r->resample = stbi__resample_row_generic;
}
// can't error after this so, this is safe
output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);
if (!output) {
stbi__cleanup_jpeg(z);
return stbi__errpuc("outofmem", "Out of memory");
}
// now go ahead and resample
for (j=0; j < z->s->img_y; ++j) {
stbi_uc *out = output + n * z->s->img_x * j;
for (k=0; k < decode_n; ++k) {
stbi__resample *r = &res_comp[k];
int y_bot = r->ystep >= (r->vs >> 1);
coutput[k] = r->resample(z->img_comp[k].linebuf,
y_bot ? r->line1 : r->line0,
y_bot ? r->line0 : r->line1,
r->w_lores, r->hs);
if (++r->ystep >= r->vs) {
r->ystep = 0;
r->line0 = r->line1;
if (++r->ypos < z->img_comp[k].y)
r->line1 += z->img_comp[k].w2;
}
}
if (n >= 3) {
stbi_uc *y = coutput[0];
if (z->s->img_n == 3) {
if (is_rgb) {
for (i=0; i < z->s->img_x; ++i) {
out[0] = y[i];
out[1] = coutput[1][i];
out[2] = coutput[2][i];
out[3] = 255;
out += n;
}
} else {
z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
}
} else if (z->s->img_n == 4) {
if (z->app14_color_transform == 0) { // CMYK
for (i=0; i < z->s->img_x; ++i) {
stbi_uc m = coutput[3][i];
out[0] = stbi__blinn_8x8(coutput[0][i], m);
out[1] = stbi__blinn_8x8(coutput[1][i], m);
out[2] = stbi__blinn_8x8(coutput[2][i], m);
out[3] = 255;
out += n;
}
} else if (z->app14_color_transform == 2) { // YCCK
z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
for (i=0; i < z->s->img_x; ++i) {
stbi_uc m = coutput[3][i];
out[0] = stbi__blinn_8x8(255 - out[0], m);
out[1] = stbi__blinn_8x8(255 - out[1], m);
out[2] = stbi__blinn_8x8(255 - out[2], m);
out += n;
}
} else { // YCbCr + alpha? Ignore the fourth channel for now
z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
}
} else
for (i=0; i < z->s->img_x; ++i) {
out[0] = out[1] = out[2] = y[i];
out[3] = 255; // not used if n==3
out += n;
}
} else {
if (is_rgb) {
if (n == 1)
for (i=0; i < z->s->img_x; ++i)
*out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
else {
for (i=0; i < z->s->img_x; ++i, out += 2) {
out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
out[1] = 255;
}
}
} else if (z->s->img_n == 4 && z->app14_color_transform == 0) {
for (i=0; i < z->s->img_x; ++i) {
stbi_uc m = coutput[3][i];
stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);
stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);
stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);
out[0] = stbi__compute_y(r, g, b);
out[1] = 255;
out += n;
}
} else if (z->s->img_n == 4 && z->app14_color_transform == 2) {
for (i=0; i < z->s->img_x; ++i) {
out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);
out[1] = 255;
out += n;
}
} else {
stbi_uc *y = coutput[0];
if (n == 1)
for (i=0; i < z->s->img_x; ++i) out[i] = y[i];
else
for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255;
}
}
}
stbi__cleanup_jpeg(z);
*out_x = z->s->img_x;
*out_y = z->s->img_y;
if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output
return output;
}
}
static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {
unsigned char* result;
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
STBI_NOTUSED(ri);
j->s = s;
stbi__setup_jpeg(j);
result = load_jpeg_image(j, x,y,comp,req_comp);
STBI_FREE(j);
return result;
}
static int stbi__jpeg_test(stbi__context *s) {
int r;
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
j->s = s;
stbi__setup_jpeg(j);
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
stbi__rewind(s);
STBI_FREE(j);
return r;
}
static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) {
if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
stbi__rewind( j->s );
return 0;
}
if (x) *x = j->s->img_x;
if (y) *y = j->s->img_y;
if (comp) *comp = j->s->img_n >= 3 ? 3 : 1;
return 1;
}
static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) {
int result;
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
j->s = s;
result = stbi__jpeg_info_raw(j, x, y, comp);
STBI_FREE(j);
return result;
}
#endif
// public domain zlib decode v0.2 Sean Barrett 2006-11-18
// simple implementation
// - all input must be provided in an upfront buffer
// - all output is written to a single output buffer (can malloc/realloc)
// performance
// - fast huffman
#ifndef STBI_NO_ZLIB
// fast-way is faster to check than jpeg huffman, but slow way is slower
#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
// zlib-style huffman encoding
// (jpegs packs from left, zlib from right, so can't share code)
typedef struct {
stbi__uint16 fast[1 << STBI__ZFAST_BITS];
stbi__uint16 firstcode[16];
int maxcode[17];
stbi__uint16 firstsymbol[16];
stbi_uc size[288];
stbi__uint16 value[288];
} stbi__zhuffman;
stbi_inline static int stbi__bitreverse16(int n) {
n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
return n;
}
stbi_inline static int stbi__bit_reverse(int v, int bits) {
STBI_ASSERT(bits <= 16);
// to bit reverse n bits, reverse 16 and shift
// e.g. 11 bits, bit reverse and shift away 5
return stbi__bitreverse16(v) >> (16-bits);
}
static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) {
int i,k=0;
int code, next_code[16], sizes[17];
// DEFLATE spec for generating codes
memset(sizes, 0, sizeof(sizes));
memset(z->fast, 0, sizeof(z->fast));
for (i=0; i < num; ++i)
++sizes[sizelist[i]];
sizes[0] = 0;
for (i=1; i < 16; ++i)
if (sizes[i] > (1 << i))
return stbi__err("bad sizes", "Corrupt PNG");
code = 0;
for (i=1; i < 16; ++i) {
next_code[i] = code;
z->firstcode[i] = (stbi__uint16) code;
z->firstsymbol[i] = (stbi__uint16) k;
code = (code + sizes[i]);
if (sizes[i])
if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG");
z->maxcode[i] = code << (16-i); // preshift for inner loop
code <<= 1;
k += sizes[i];
}
z->maxcode[16] = 0x10000; // sentinel
for (i=0; i < num; ++i) {
int s = sizelist[i];
if (s) {
int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
z->size [c] = (stbi_uc ) s;
z->value[c] = (stbi__uint16) i;
if (s <= STBI__ZFAST_BITS) {
int j = stbi__bit_reverse(next_code[s],s);
while (j < (1 << STBI__ZFAST_BITS)) {
z->fast[j] = fastv;
j += (1 << s);
}
}
++next_code[s];
}
}
return 1;
}
// zlib-from-memory implementation for PNG reading
// because PNG allows splitting the zlib stream arbitrarily,
// and it's annoying structurally to have PNG call ZLIB call PNG,
// we require PNG read all the IDATs and combine them into a single
// memory buffer
typedef struct {
stbi_uc *zbuffer, *zbuffer_end;
int num_bits;
stbi__uint32 code_buffer;
char *zout;
char *zout_start;
char *zout_end;
int z_expandable;
stbi__zhuffman z_length, z_distance;
} stbi__zbuf;
stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) {
if (z->zbuffer >= z->zbuffer_end) return 0;
return *z->zbuffer++;
}
static void stbi__fill_bits(stbi__zbuf *z) {
do {
STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
z->num_bits += 8;
} while (z->num_bits <= 24);
}
stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) {
unsigned int k;
if (z->num_bits < n) stbi__fill_bits(z);
k = z->code_buffer & ((1 << n) - 1);
z->code_buffer >>= n;
z->num_bits -= n;
return k;
}
static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) {
int b,s,k;
// not resolved by fast table, so compute it the slow way
// use jpeg approach, which requires MSbits at top
k = stbi__bit_reverse(a->code_buffer, 16);
for (s=STBI__ZFAST_BITS+1; ; ++s)
if (k < z->maxcode[s])
break;
if (s == 16) return -1; // invalid code!
// code size is s, so:
b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
STBI_ASSERT(z->size[b] == s);
a->code_buffer >>= s;
a->num_bits -= s;
return z->value[b];
}
stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) {
int b,s;
if (a->num_bits < 16) stbi__fill_bits(a);
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
if (b) {
s = b >> 9;
a->code_buffer >>= s;
a->num_bits -= s;
return b & 511;
}
return stbi__zhuffman_decode_slowpath(a, z);
}
static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) { // need to make room for n bytes
char *q;
int cur, limit, old_limit;
z->zout = zout;
if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
cur = (int) (z->zout - z->zout_start);
limit = old_limit = (int) (z->zout_end - z->zout_start);
while (cur + n > limit)
limit *= 2;
q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
STBI_NOTUSED(old_limit);
if (q == NULL) return stbi__err("outofmem", "Out of memory");
z->zout_start = q;
z->zout = q + cur;
z->zout_end = q + limit;
return 1;
}
static const int stbi__zlength_base[31] = {
3,4,5,6,7,8,9,10,11,13,
15,17,19,23,27,31,35,43,51,59,
67,83,99,115,131,163,195,227,258,0,0
};
static const int stbi__zlength_extra[31]=
{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0
};
static const int stbi__zdist_extra[32] =
{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static int stbi__parse_huffman_block(stbi__zbuf *a) {
char *zout = a->zout;
for(;;) {
int z = stbi__zhuffman_decode(a, &a->z_length);
if (z < 256) {
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes
if (zout >= a->zout_end) {
if (!stbi__zexpand(a, zout, 1)) return 0;
zout = a->zout;
}
*zout++ = (char) z;
} else {
stbi_uc *p;
int len,dist;
if (z == 256) {
a->zout = zout;
return 1;
}
z -= 257;
len = stbi__zlength_base[z];
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
z = stbi__zhuffman_decode(a, &a->z_distance);
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
dist = stbi__zdist_base[z];
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
if (zout + len > a->zout_end) {
if (!stbi__zexpand(a, zout, len)) return 0;
zout = a->zout;
}
p = (stbi_uc *) (zout - dist);
if (dist == 1) { // run of one byte; common in images.
stbi_uc v = *p;
if (len) {
do *zout++ = v;
while (--len);
}
} else {
if (len) {
do *zout++ = *p++;
while (--len);
}
}
}
}
}
static int stbi__compute_huffman_codes(stbi__zbuf *a) {
static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
stbi__zhuffman z_codelength;
stbi_uc lencodes[286+32+137];//padding for maximum single op
stbi_uc codelength_sizes[19];
int i,n;
int hlit = stbi__zreceive(a,5) + 257;
int hdist = stbi__zreceive(a,5) + 1;
int hclen = stbi__zreceive(a,4) + 4;
int ntot = hlit + hdist;
memset(codelength_sizes, 0, sizeof(codelength_sizes));
for (i=0; i < hclen; ++i) {
int s = stbi__zreceive(a,3);
codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;
}
if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
n = 0;
while (n < ntot) {
int c = stbi__zhuffman_decode(a, &z_codelength);
if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
if (c < 16)
lencodes[n++] = (stbi_uc) c;
else {
stbi_uc fill = 0;
if (c == 16) {
c = stbi__zreceive(a,2)+3;
if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG");
fill = lencodes[n-1];
} else if (c == 17)
c = stbi__zreceive(a,3)+3;
else {
STBI_ASSERT(c == 18);
c = stbi__zreceive(a,7)+11;
}
if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG");
memset(lencodes+n, fill, c);
n += c;
}
}
if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG");
if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
return 1;
}
static int stbi__parse_uncompressed_block(stbi__zbuf *a) {
stbi_uc header[4];
int len,nlen,k;
if (a->num_bits & 7)
stbi__zreceive(a, a->num_bits & 7); // discard
// drain the bit-packed data into header
k = 0;
while (a->num_bits > 0) {
header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check
a->code_buffer >>= 8;
a->num_bits -= 8;
}
STBI_ASSERT(a->num_bits == 0);
// now fill header the normal way
while (k < 4)
header[k++] = stbi__zget8(a);
len = header[1] * 256 + header[0];
nlen = header[3] * 256 + header[2];
if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
if (a->zout + len > a->zout_end)
if (!stbi__zexpand(a, a->zout, len)) return 0;
memcpy(a->zout, a->zbuffer, len);
a->zbuffer += len;
a->zout += len;
return 1;
}
static int stbi__parse_zlib_header(stbi__zbuf *a) {
int cmf = stbi__zget8(a);
int cm = cmf & 15;
/* int cinfo = cmf >> 4; */
int flg = stbi__zget8(a);
if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png
// window = 1 << (8 + cinfo)... but who cares, we fully buffer output
return 1;
}
static const stbi_uc stbi__zdefault_length[288] = {
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8
};
static const stbi_uc stbi__zdefault_distance[32] = {
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
};
/*
Init algorithm:
{
int i; // use <= to match clearly with spec
for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8;
for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9;
for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7;
for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8;
for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5;
}
*/
static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) {
int final, type;
if (parse_header)
if (!stbi__parse_zlib_header(a)) return 0;
a->num_bits = 0;
a->code_buffer = 0;
do {
final = stbi__zreceive(a,1);
type = stbi__zreceive(a,2);
if (type == 0) {
if (!stbi__parse_uncompressed_block(a)) return 0;
} else if (type == 3) {
return 0;
} else {
if (type == 1) {
// use fixed code lengths
if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, 288)) return 0;
if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
} else {
if (!stbi__compute_huffman_codes(a)) return 0;
}
if (!stbi__parse_huffman_block(a)) return 0;
}
} while (!final);
return 1;
}
static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) {
a->zout_start = obuf;
a->zout = obuf;
a->zout_end = obuf + olen;
a->z_expandable = exp;
return stbi__parse_zlib(a, parse_header);
}
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) {
stbi__zbuf a;
char *p = (char *) stbi__malloc(initial_size);
if (p == NULL) return NULL;
a.zbuffer = (stbi_uc *) buffer;
a.zbuffer_end = (stbi_uc *) buffer + len;
if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
STBI_FREE(a.zout_start);
return NULL;
}
}
STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) {
return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
}
STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) {
stbi__zbuf a;
char *p = (char *) stbi__malloc(initial_size);
if (p == NULL) return NULL;
a.zbuffer = (stbi_uc *) buffer;
a.zbuffer_end = (stbi_uc *) buffer + len;
if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
STBI_FREE(a.zout_start);
return NULL;
}
}
STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) {
stbi__zbuf a;
a.zbuffer = (stbi_uc *) ibuffer;
a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
return (int) (a.zout - a.zout_start);
else
return -1;
}
STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) {
stbi__zbuf a;
char *p = (char *) stbi__malloc(16384);
if (p == NULL) return NULL;
a.zbuffer = (stbi_uc *) buffer;
a.zbuffer_end = (stbi_uc *) buffer+len;
if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
STBI_FREE(a.zout_start);
return NULL;
}
}
STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) {
stbi__zbuf a;
a.zbuffer = (stbi_uc *) ibuffer;
a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
return (int) (a.zout - a.zout_start);
else
return -1;
}
#endif
// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
// simple implementation
// - only 8-bit samples
// - no CRC checking
// - allocates lots of intermediate memory
// - avoids problem of streaming data between subsystems
// - avoids explicit window management
// performance
// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
#ifndef STBI_NO_PNG
typedef struct {
stbi__uint32 length;
stbi__uint32 type;
} stbi__pngchunk;
static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) {
stbi__pngchunk c;
c.length = stbi__get32be(s);
c.type = stbi__get32be(s);
return c;
}
static int stbi__check_png_header(stbi__context *s) {
static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
int i;
for (i=0; i < 8; ++i)
if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG");
return 1;
}
typedef struct {
stbi__context *s;
stbi_uc *idata, *expanded, *out;
int depth;
} stbi__png;
enum {
STBI__F_none=0,
STBI__F_sub=1,
STBI__F_up=2,
STBI__F_avg=3,
STBI__F_paeth=4,
// synthetic filters used for first scanline to avoid needing a dummy row of 0s
STBI__F_avg_first,
STBI__F_paeth_first
};
static stbi_uc first_row_filter[5] = {
STBI__F_none,
STBI__F_sub,
STBI__F_none,
STBI__F_avg_first,
STBI__F_paeth_first
};
static int stbi__paeth(int a, int b, int c) {
int p = a + b - c;
int pa = abs(p-a);
int pb = abs(p-b);
int pc = abs(p-c);
if (pa <= pb && pa <= pc) return a;
if (pb <= pc) return b;
return c;
}
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
// create the png data from post-deflated data
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) {
int bytes = (depth == 16? 2 : 1);
stbi__context *s = a->s;
stbi__uint32 i,j,stride = x*out_n*bytes;
stbi__uint32 img_len, img_width_bytes;
int k;
int img_n = s->img_n; // copy it into a local for later
int output_bytes = out_n*bytes;
int filter_bytes = img_n*bytes;
int width = x;
STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
if (!a->out) return stbi__err("outofmem", "Out of memory");
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
img_len = (img_width_bytes + 1) * y;
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
// but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),
// so just check for raw_len < img_len always.
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
for (j=0; j < y; ++j) {
stbi_uc *cur = a->out + stride*j;
stbi_uc *prior;
int filter = *raw++;
if (filter > 4)
return stbi__err("invalid filter","Corrupt PNG");
if (depth < 8) {
STBI_ASSERT(img_width_bytes <= x);
cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
filter_bytes = 1;
width = img_width_bytes;
}
prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
// if first row, use special filter that doesn't sample previous row
if (j == 0) filter = first_row_filter[filter];
// handle first byte explicitly
for (k=0; k < filter_bytes; ++k) {
switch (filter) {
case STBI__F_none :
cur[k] = raw[k];
break;
case STBI__F_sub :
cur[k] = raw[k];
break;
case STBI__F_up :
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
break;
case STBI__F_avg :
cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
break;
case STBI__F_paeth :
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0));
break;
case STBI__F_avg_first :
cur[k] = raw[k];
break;
case STBI__F_paeth_first:
cur[k] = raw[k];
break;
}
}
if (depth == 8) {
if (img_n != out_n)
cur[img_n] = 255; // first pixel
raw += img_n;
cur += out_n;
prior += out_n;
} else if (depth == 16) {
if (img_n != out_n) {
cur[filter_bytes] = 255; // first pixel top byte
cur[filter_bytes+1] = 255; // first pixel bottom byte
}
raw += filter_bytes;
cur += output_bytes;
prior += output_bytes;
} else {
raw += 1;
cur += 1;
prior += 1;
}
// this is a little gross, so that we don't switch per-pixel or per-component
if (depth < 8 || img_n == out_n) {
int nk = (width - 1)*filter_bytes;
#define STBI__CASE(f) \
case f: \
for (k=0; k < nk; ++k)
switch (filter) {
// "none" filter turns into a memcpy here; make that explicit.
case STBI__F_none:
memcpy(cur, raw, nk);
break;
STBI__CASE(STBI__F_sub) {
cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
}
break;
STBI__CASE(STBI__F_up) {
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
}
break;
STBI__CASE(STBI__F_avg) {
cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
}
break;
STBI__CASE(STBI__F_paeth) {
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes]));
}
break;
STBI__CASE(STBI__F_avg_first) {
cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
}
break;
STBI__CASE(STBI__F_paeth_first) {
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0));
}
break;
}
#undef STBI__CASE
raw += nk;
} else {
STBI_ASSERT(img_n+1 == out_n);
#define STBI__CASE(f) \
case f: \
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
for (k=0; k < filter_bytes; ++k)
switch (filter) {
STBI__CASE(STBI__F_none) {
cur[k] = raw[k];
}
break;
STBI__CASE(STBI__F_sub) {
cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]);
}
break;
STBI__CASE(STBI__F_up) {
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
}
break;
STBI__CASE(STBI__F_avg) {
cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1));
}
break;
STBI__CASE(STBI__F_paeth) {
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes]));
}
break;
STBI__CASE(STBI__F_avg_first) {
cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1));
}
break;
STBI__CASE(STBI__F_paeth_first) {
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0));
}
break;
}
#undef STBI__CASE
// the loop above sets the high byte of the pixels' alpha, but for
// 16 bit png files we also need the low byte set. we'll do that here.
if (depth == 16) {
cur = a->out + stride*j; // start at the beginning of the row again
for (i=0; i < x; ++i,cur+=output_bytes) {
cur[filter_bytes+1] = 255;
}
}
}
}
// we make a separate pass to expand bits to pixels; for performance,
// this could run two scanlines behind the above code, so it won't
// intefere with filtering but will still be in the cache.
if (depth < 8) {
for (j=0; j < y; ++j) {
stbi_uc *cur = a->out + stride*j;
stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
// unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
// png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
// note that the final byte might overshoot and write more data than desired.
// we can allocate enough data that this never writes out of memory, but it
// could also overwrite the next scanline. can it overwrite non-empty data
// on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
// so we need to explicitly clamp the final ones
if (depth == 4) {
for (k=x*img_n; k >= 2; k-=2, ++in) {
*cur++ = scale * ((*in >> 4) );
*cur++ = scale * ((*in ) & 0x0f);
}
if (k > 0) *cur++ = scale * ((*in >> 4) );
} else if (depth == 2) {
for (k=x*img_n; k >= 4; k-=4, ++in) {
*cur++ = scale * ((*in >> 6) );
*cur++ = scale * ((*in >> 4) & 0x03);
*cur++ = scale * ((*in >> 2) & 0x03);
*cur++ = scale * ((*in ) & 0x03);
}
if (k > 0) *cur++ = scale * ((*in >> 6) );
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
} else if (depth == 1) {
for (k=x*img_n; k >= 8; k-=8, ++in) {
*cur++ = scale * ((*in >> 7) );
*cur++ = scale * ((*in >> 6) & 0x01);
*cur++ = scale * ((*in >> 5) & 0x01);
*cur++ = scale * ((*in >> 4) & 0x01);
*cur++ = scale * ((*in >> 3) & 0x01);
*cur++ = scale * ((*in >> 2) & 0x01);
*cur++ = scale * ((*in >> 1) & 0x01);
*cur++ = scale * ((*in ) & 0x01);
}
if (k > 0) *cur++ = scale * ((*in >> 7) );
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
}
if (img_n != out_n) {
int q;
// insert alpha = 255
cur = a->out + stride*j;
if (img_n == 1) {
for (q=x-1; q >= 0; --q) {
cur[q*2+1] = 255;
cur[q*2+0] = cur[q];
}
} else {
STBI_ASSERT(img_n == 3);
for (q=x-1; q >= 0; --q) {
cur[q*4+3] = 255;
cur[q*4+2] = cur[q*3+2];
cur[q*4+1] = cur[q*3+1];
cur[q*4+0] = cur[q*3+0];
}
}
}
}
} else if (depth == 16) {
// force the image data from big-endian to platform-native.
// this is done in a separate pass due to the decoding relying
// on the data being untouched, but could probably be done
// per-line during decode if care is taken.
stbi_uc *cur = a->out;
stbi__uint16 *cur16 = (stbi__uint16*)cur;
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
*cur16 = (cur[0] << 8) | cur[1];
}
}
return 1;
}
static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) {
int bytes = (depth == 16 ? 2 : 1);
int out_bytes = out_n * bytes;
stbi_uc *final;
int p;
if (!interlaced)
return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
// de-interlacing
final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
for (p=0; p < 7; ++p) {
int xorig[] = { 0,4,0,2,0,1,0 };
int yorig[] = { 0,0,4,0,2,0,1 };
int xspc[] = { 8,8,4,4,2,2,1 };
int yspc[] = { 8,8,8,4,4,2,2 };
int i,j,x,y;
// pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
if (x && y) {
stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
STBI_FREE(final);
return 0;
}
for (j=0; j < y; ++j) {
for (i=0; i < x; ++i) {
int out_y = j*yspc[p]+yorig[p];
int out_x = i*xspc[p]+xorig[p];
memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,
a->out + (j*x+i)*out_bytes, out_bytes);
}
}
STBI_FREE(a->out);
image_data += img_len;
image_data_len -= img_len;
}
}
a->out = final;
return 1;
}
static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) {
stbi__context *s = z->s;
stbi__uint32 i, pixel_count = s->img_x * s->img_y;
stbi_uc *p = z->out;
// compute color-based transparency, assuming we've
// already got 255 as the alpha value in the output
STBI_ASSERT(out_n == 2 || out_n == 4);
if (out_n == 2) {
for (i=0; i < pixel_count; ++i) {
p[1] = (p[0] == tc[0] ? 0 : 255);
p += 2;
}
} else {
for (i=0; i < pixel_count; ++i) {
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
p[3] = 0;
p += 4;
}
}
return 1;
}
static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) {
stbi__context *s = z->s;
stbi__uint32 i, pixel_count = s->img_x * s->img_y;
stbi__uint16 *p = (stbi__uint16*) z->out;
// compute color-based transparency, assuming we've
// already got 65535 as the alpha value in the output
STBI_ASSERT(out_n == 2 || out_n == 4);
if (out_n == 2) {
for (i = 0; i < pixel_count; ++i) {
p[1] = (p[0] == tc[0] ? 0 : 65535);
p += 2;
}
} else {
for (i = 0; i < pixel_count; ++i) {
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
p[3] = 0;
p += 4;
}
}
return 1;
}
static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) {
stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
stbi_uc *p, *temp_out, *orig = a->out;
p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);
if (p == NULL) return stbi__err("outofmem", "Out of memory");
// between here and free(out) below, exitting would leak
temp_out = p;
if (pal_img_n == 3) {
for (i=0; i < pixel_count; ++i) {
int n = orig[i]*4;
p[0] = palette[n ];
p[1] = palette[n+1];
p[2] = palette[n+2];
p += 3;
}
} else {
for (i=0; i < pixel_count; ++i) {
int n = orig[i]*4;
p[0] = palette[n ];
p[1] = palette[n+1];
p[2] = palette[n+2];
p[3] = palette[n+3];
p += 4;
}
}
STBI_FREE(a->out);
a->out = temp_out;
STBI_NOTUSED(len);
return 1;
}
static int stbi__unpremultiply_on_load = 0;
static int stbi__de_iphone_flag = 0;
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) {
stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
}
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) {
stbi__de_iphone_flag = flag_true_if_should_convert;
}
static void stbi__de_iphone(stbi__png *z) {
stbi__context *s = z->s;
stbi__uint32 i, pixel_count = s->img_x * s->img_y;
stbi_uc *p = z->out;
if (s->img_out_n == 3) { // convert bgr to rgb
for (i=0; i < pixel_count; ++i) {
stbi_uc t = p[0];
p[0] = p[2];
p[2] = t;
p += 3;
}
} else {
STBI_ASSERT(s->img_out_n == 4);
if (stbi__unpremultiply_on_load) {
// convert bgr to rgb and unpremultiply
for (i=0; i < pixel_count; ++i) {
stbi_uc a = p[3];
stbi_uc t = p[0];
if (a) {
stbi_uc half = a / 2;
p[0] = (p[2] * 255 + half) / a;
p[1] = (p[1] * 255 + half) / a;
p[2] = ( t * 255 + half) / a;
} else {
p[0] = p[2];
p[2] = t;
}
p += 4;
}
} else {
// convert bgr to rgb
for (i=0; i < pixel_count; ++i) {
stbi_uc t = p[0];
p[0] = p[2];
p[2] = t;
p += 4;
}
}
}
}
#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))
static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) {
stbi_uc palette[1024], pal_img_n=0;
stbi_uc has_trans=0, tc[3];
stbi__uint16 tc16[3];
stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
int first=1,k,interlace=0, color=0, is_iphone=0;
stbi__context *s = z->s;
z->expanded = NULL;
z->idata = NULL;
z->out = NULL;
if (!stbi__check_png_header(s)) return 0;
if (scan == STBI__SCAN_type) return 1;
for (;;) {
stbi__pngchunk c = stbi__get_chunk_header(s);
switch (c.type) {
case STBI__PNG_TYPE('C','g','B','I'):
is_iphone = 1;
stbi__skip(s, c.length);
break;
case STBI__PNG_TYPE('I','H','D','R'): {
int comp,filter;
if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
first = 0;
if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
s->img_x = stbi__get32be(s);
if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
s->img_y = stbi__get32be(s);
if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
z->depth = stbi__get8(s);
if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
color = stbi__get8(s);
if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG");
if (color == 3) pal_img_n = 3;
else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
comp = stbi__get8(s);
if (comp) return stbi__err("bad comp method","Corrupt PNG");
filter= stbi__get8(s);
if (filter) return stbi__err("bad filter method","Corrupt PNG");
interlace = stbi__get8(s);
if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG");
if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG");
if (!pal_img_n) {
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
if (scan == STBI__SCAN_header) return 1;
} else {
// if paletted, then pal_n is our final components, and
// img_n is # components to decompress/filter.
s->img_n = 1;
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
// if SCAN_header, have to scan to see if we have a tRNS
}
break;
}
case STBI__PNG_TYPE('P','L','T','E'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
pal_len = c.length / 3;
if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG");
for (i=0; i < pal_len; ++i) {
palette[i*4+0] = stbi__get8(s);
palette[i*4+1] = stbi__get8(s);
palette[i*4+2] = stbi__get8(s);
palette[i*4+3] = 255;
}
break;
}
case STBI__PNG_TYPE('t','R','N','S'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
if (pal_img_n) {
if (scan == STBI__SCAN_header) {
s->img_n = 4;
return 1;
}
if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG");
if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG");
pal_img_n = 4;
for (i=0; i < c.length; ++i)
palette[i*4+3] = stbi__get8(s);
} else {
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
has_trans = 1;
if (z->depth == 16) {
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
} else {
for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
}
}
break;
}
case STBI__PNG_TYPE('I','D','A','T'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
if (scan == STBI__SCAN_header) {
s->img_n = pal_img_n;
return 1;
}
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
stbi__uint32 idata_limit_old = idata_limit;
stbi_uc *p;
if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
while (ioff + c.length > idata_limit)
idata_limit *= 2;
STBI_NOTUSED(idata_limit_old);
p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit);
if (p == NULL) return stbi__err("outofmem", "Out of memory");
z->idata = p;
}
if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
ioff += c.length;
break;
}
case STBI__PNG_TYPE('I','E','N','D'): {
stbi__uint32 raw_len, bpl;
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (scan != STBI__SCAN_load) return 1;
if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
// initial guess for decoded data size to avoid unnecessary reallocs
bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component
raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
if (z->expanded == NULL) return 0; // zlib should set error
STBI_FREE(z->idata);
z->idata = NULL;
if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
s->img_out_n = s->img_n+1;
else
s->img_out_n = s->img_n;
if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
if (has_trans) {
if (z->depth == 16) {
if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
} else {
if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
}
}
if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
stbi__de_iphone(z);
if (pal_img_n) {
// pal_img_n == 3 or 4
s->img_n = pal_img_n; // record the actual colors we had
s->img_out_n = pal_img_n;
if (req_comp >= 3) s->img_out_n = req_comp;
if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
return 0;
} else if (has_trans) {
// non-paletted image with tRNS -> source image has (constant) alpha
++s->img_n;
}
STBI_FREE(z->expanded);
z->expanded = NULL;
return 1;
}
default:
// if critical, fail
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if ((c.type & (1 << 29)) == 0) {
#ifndef STBI_NO_FAILURE_STRINGS
// not threadsafe
static char invalid_chunk[] = "XXXX PNG chunk not known";
invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
#endif
return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
}
stbi__skip(s, c.length);
break;
}
// end of PNG chunk, read and skip CRC
stbi__get32be(s);
}
}
static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) {
void *result=NULL;
if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
if (p->depth < 8)
ri->bits_per_channel = 8;
else
ri->bits_per_channel = p->depth;
result = p->out;
p->out = NULL;
if (req_comp && req_comp != p->s->img_out_n) {
if (ri->bits_per_channel == 8)
result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
else
result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
p->s->img_out_n = req_comp;
if (result == NULL) return result;
}
*x = p->s->img_x;
*y = p->s->img_y;
if (n) *n = p->s->img_n;
}
STBI_FREE(p->out);
p->out = NULL;
STBI_FREE(p->expanded);
p->expanded = NULL;
STBI_FREE(p->idata);
p->idata = NULL;
return result;
}
static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {
stbi__png p;
p.s = s;
return stbi__do_png(&p, x,y,comp,req_comp, ri);
}
static int stbi__png_test(stbi__context *s) {
int r;
r = stbi__check_png_header(s);
stbi__rewind(s);
return r;
}
static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) {
if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
stbi__rewind( p->s );
return 0;
}
if (x) *x = p->s->img_x;
if (y) *y = p->s->img_y;
if (comp) *comp = p->s->img_n;
return 1;
}
static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) {
stbi__png p;
p.s = s;
return stbi__png_info_raw(&p, x, y, comp);
}
static int stbi__png_is16(stbi__context *s) {
stbi__png p;
p.s = s;
if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
return 0;
if (p.depth != 16) {
stbi__rewind(p.s);
return 0;
}
return 1;
}
#endif
// Microsoft/Windows BMP image
#ifndef STBI_NO_BMP
static int stbi__bmp_test_raw(stbi__context *s) {
int r;
int sz;
if (stbi__get8(s) != 'B') return 0;
if (stbi__get8(s) != 'M') return 0;
stbi__get32le(s); // discard filesize
stbi__get16le(s); // discard reserved
stbi__get16le(s); // discard reserved
stbi__get32le(s); // discard data offset
sz = stbi__get32le(s);
r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);
return r;
}
static int stbi__bmp_test(stbi__context *s) {
int r = stbi__bmp_test_raw(s);
stbi__rewind(s);
return r;
}
// returns 0..31 for the highest set bit
static int stbi__high_bit(unsigned int z) {
int n=0;
if (z == 0) return -1;
if (z >= 0x10000) n += 16, z >>= 16;
if (z >= 0x00100) n += 8, z >>= 8;
if (z >= 0x00010) n += 4, z >>= 4;
if (z >= 0x00004) n += 2, z >>= 2;
if (z >= 0x00002) n += 1, z >>= 1;
return n;
}
static int stbi__bitcount(unsigned int a) {
a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
a = (a + (a >> 8)); // max 16 per 8 bits
a = (a + (a >> 16)); // max 32 per 8 bits
return a & 0xff;
}
// extract an arbitrarily-aligned N-bit value (N=bits)
// from v, and then make it 8-bits long and fractionally
// extend it to full full range.
static int stbi__shiftsigned(int v, int shift, int bits) {
static unsigned int mul_table[9] = {
0,
0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/,
0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/,
};
static unsigned int shift_table[9] = {
0, 0,0,1,0,2,4,6,0,
};
if (shift < 0)
v <<= -shift;
else
v >>= shift;
STBI_ASSERT(v >= 0 && v < 256);
v >>= (8-bits);
STBI_ASSERT(bits >= 0 && bits <= 8);
return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits];
}
typedef struct {
int bpp, offset, hsz;
unsigned int mr,mg,mb,ma, all_a;
} stbi__bmp_data;
static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) {
int hsz;
if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP");
stbi__get32le(s); // discard filesize
stbi__get16le(s); // discard reserved
stbi__get16le(s); // discard reserved
info->offset = stbi__get32le(s);
info->hsz = hsz = stbi__get32le(s);
info->mr = info->mg = info->mb = info->ma = 0;
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
if (hsz == 12) {
s->img_x = stbi__get16le(s);
s->img_y = stbi__get16le(s);
} else {
s->img_x = stbi__get32le(s);
s->img_y = stbi__get32le(s);
}
if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP");
info->bpp = stbi__get16le(s);
if (hsz != 12) {
int compress = stbi__get32le(s);
if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
stbi__get32le(s); // discard sizeof
stbi__get32le(s); // discard hres
stbi__get32le(s); // discard vres
stbi__get32le(s); // discard colorsused
stbi__get32le(s); // discard max important
if (hsz == 40 || hsz == 56) {
if (hsz == 56) {
stbi__get32le(s);
stbi__get32le(s);
stbi__get32le(s);
stbi__get32le(s);
}
if (info->bpp == 16 || info->bpp == 32) {
if (compress == 0) {
if (info->bpp == 32) {
info->mr = 0xffu << 16;
info->mg = 0xffu << 8;
info->mb = 0xffu << 0;
info->ma = 0xffu << 24;
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
info->mr = 31u << 10;
info->mg = 31u << 5;
info->mb = 31u << 0;
}
} else if (compress == 3) {
info->mr = stbi__get32le(s);
info->mg = stbi__get32le(s);
info->mb = stbi__get32le(s);
// not documented, but generated by photoshop and handled by mspaint
if (info->mr == info->mg && info->mg == info->mb) {
// ?!?!?
return stbi__errpuc("bad BMP", "bad BMP");
}
} else
return stbi__errpuc("bad BMP", "bad BMP");
}
} else {
int i;
if (hsz != 108 && hsz != 124)
return stbi__errpuc("bad BMP", "bad BMP");
info->mr = stbi__get32le(s);
info->mg = stbi__get32le(s);
info->mb = stbi__get32le(s);
info->ma = stbi__get32le(s);
stbi__get32le(s); // discard color space
for (i=0; i < 12; ++i)
stbi__get32le(s); // discard color space parameters
if (hsz == 124) {
stbi__get32le(s); // discard rendering intent
stbi__get32le(s); // discard offset of profile data
stbi__get32le(s); // discard size of profile data
stbi__get32le(s); // discard reserved
}
}
}
return (void *) 1;
}
static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {
stbi_uc *out;
unsigned int mr=0,mg=0,mb=0,ma=0, all_a;
stbi_uc pal[256][4];
int psize=0,i,j,width;
int flip_vertically, pad, target;
stbi__bmp_data info;
STBI_NOTUSED(ri);
info.all_a = 255;
if (stbi__bmp_parse_header(s, &info) == NULL)
return NULL; // error code already set
flip_vertically = ((int) s->img_y) > 0;
s->img_y = abs((int) s->img_y);
mr = info.mr;
mg = info.mg;
mb = info.mb;
ma = info.ma;
all_a = info.all_a;
if (info.hsz == 12) {
if (info.bpp < 24)
psize = (info.offset - 14 - 24) / 3;
} else {
if (info.bpp < 16)
psize = (info.offset - 14 - info.hsz) >> 2;
}
s->img_n = ma ? 4 : 3;
if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
target = req_comp;
else
target = s->img_n; // if they want monochrome, we'll post-convert
// sanity-check size
if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0))
return stbi__errpuc("too large", "Corrupt BMP");
out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
if (info.bpp < 16) {
int z=0;
if (psize == 0 || psize > 256) {
STBI_FREE(out);
return stbi__errpuc("invalid", "Corrupt BMP");
}
for (i=0; i < psize; ++i) {
pal[i][2] = stbi__get8(s);
pal[i][1] = stbi__get8(s);
pal[i][0] = stbi__get8(s);
if (info.hsz != 12) stbi__get8(s);
pal[i][3] = 255;
}
stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
if (info.bpp == 1) width = (s->img_x + 7) >> 3;
else if (info.bpp == 4) width = (s->img_x + 1) >> 1;
else if (info.bpp == 8) width = s->img_x;
else {
STBI_FREE(out);
return stbi__errpuc("bad bpp", "Corrupt BMP");
}
pad = (-width)&3;
if (info.bpp == 1) {
for (j=0; j < (int) s->img_y; ++j) {
int bit_offset = 7, v = stbi__get8(s);
for (i=0; i < (int) s->img_x; ++i) {
int color = (v>>bit_offset)&0x1;
out[z++] = pal[color][0];
out[z++] = pal[color][1];
out[z++] = pal[color][2];
if((--bit_offset) < 0) {
bit_offset = 7;
v = stbi__get8(s);
}
}
stbi__skip(s, pad);
}
} else {
for (j=0; j < (int) s->img_y; ++j) {
for (i=0; i < (int) s->img_x; i += 2) {
int v=stbi__get8(s),v2=0;
if (info.bpp == 4) {
v2 = v & 15;
v >>= 4;
}
out[z++] = pal[v][0];
out[z++] = pal[v][1];
out[z++] = pal[v][2];
if (target == 4) out[z++] = 255;
if (i+1 == (int) s->img_x) break;
v = (info.bpp == 8) ? stbi__get8(s) : v2;
out[z++] = pal[v][0];
out[z++] = pal[v][1];
out[z++] = pal[v][2];
if (target == 4) out[z++] = 255;
}
stbi__skip(s, pad);
}
}
} else {
int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
int z = 0;
int easy=0;
stbi__skip(s, info.offset - 14 - info.hsz);
if (info.bpp == 24) width = 3 * s->img_x;
else if (info.bpp == 16) width = 2*s->img_x;
else /* bpp = 32 and pad = 0 */ width=0;
pad = (-width) & 3;
if (info.bpp == 24) {
easy = 1;
} else if (info.bpp == 32) {
if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
easy = 2;
}
if (!easy) {
if (!mr || !mg || !mb) {
STBI_FREE(out);
return stbi__errpuc("bad masks", "Corrupt BMP");
}
// right shift amt to put high bit in position #7
rshift = stbi__high_bit(mr)-7;
rcount = stbi__bitcount(mr);
gshift = stbi__high_bit(mg)-7;
gcount = stbi__bitcount(mg);
bshift = stbi__high_bit(mb)-7;
bcount = stbi__bitcount(mb);
ashift = stbi__high_bit(ma)-7;
acount = stbi__bitcount(ma);
}
for (j=0; j < (int) s->img_y; ++j) {
if (easy) {
for (i=0; i < (int) s->img_x; ++i) {
unsigned char a;
out[z+2] = stbi__get8(s);
out[z+1] = stbi__get8(s);
out[z+0] = stbi__get8(s);
z += 3;
a = (easy == 2 ? stbi__get8(s) : 255);
all_a |= a;
if (target == 4) out[z++] = a;
}
} else {
int bpp = info.bpp;
for (i=0; i < (int) s->img_x; ++i) {
stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));
unsigned int a;
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
all_a |= a;
if (target == 4) out[z++] = STBI__BYTECAST(a);
}
}
stbi__skip(s, pad);
}
}
// if alpha channel is all 0s, replace with all 255s
if (target == 4 && all_a == 0)
for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)
out[i] = 255;
if (flip_vertically) {
stbi_uc t;
for (j=0; j < (int) s->img_y>>1; ++j) {
stbi_uc *p1 = out + j *s->img_x*target;
stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;
for (i=0; i < (int) s->img_x*target; ++i) {
t = p1[i], p1[i] = p2[i], p2[i] = t;
}
}
}
if (req_comp && req_comp != target) {
out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);
if (out == NULL) return out; // stbi__convert_format frees input on failure
}
*x = s->img_x;
*y = s->img_y;
if (comp) *comp = s->img_n;
return out;
}
#endif
// Targa Truevision - TGA
// by Jonathan Dummer
#ifndef STBI_NO_TGA
// returns STBI_rgb or whatever, 0 on error
static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) {
// only RGB or RGBA (incl. 16bit) or grey allowed
if (is_rgb16) *is_rgb16 = 0;
switch(bits_per_pixel) {
case 8:
return STBI_grey;
case 16:
if(is_grey) return STBI_grey_alpha;
// fallthrough
case 15:
if(is_rgb16) *is_rgb16 = 1;
return STBI_rgb;
case 24: // fallthrough
case 32:
return bits_per_pixel/8;
default:
return 0;
}
}
static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) {
int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;
int sz, tga_colormap_type;
stbi__get8(s); // discard Offset
tga_colormap_type = stbi__get8(s); // colormap type
if( tga_colormap_type > 1 ) {
stbi__rewind(s);
return 0; // only RGB or indexed allowed
}
tga_image_type = stbi__get8(s); // image type
if ( tga_colormap_type == 1 ) { // colormapped (paletted) image
if (tga_image_type != 1 && tga_image_type != 9) {
stbi__rewind(s);
return 0;
}
stbi__skip(s,4); // skip index of first colormap entry and number of entries
sz = stbi__get8(s); // check bits per palette color entry
if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) {
stbi__rewind(s);
return 0;
}
stbi__skip(s,4); // skip image x and y origin
tga_colormap_bpp = sz;
} else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE
if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) {
stbi__rewind(s);
return 0; // only RGB or grey allowed, +/- RLE
}
stbi__skip(s,9); // skip colormap specification and image x/y origin
tga_colormap_bpp = 0;
}
tga_w = stbi__get16le(s);
if( tga_w < 1 ) {
stbi__rewind(s);
return 0; // test width
}
tga_h = stbi__get16le(s);
if( tga_h < 1 ) {
stbi__rewind(s);
return 0; // test height
}
tga_bits_per_pixel = stbi__get8(s); // bits per pixel
stbi__get8(s); // ignore alpha bits
if (tga_colormap_bpp != 0) {
if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {
// when using a colormap, tga_bits_per_pixel is the size of the indexes
// I don't think anything but 8 or 16bit indexes makes sense
stbi__rewind(s);
return 0;
}
tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);
} else {
tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);
}
if(!tga_comp) {
stbi__rewind(s);
return 0;
}
if (x) *x = tga_w;
if (y) *y = tga_h;
if (comp) *comp = tga_comp;
return 1; // seems to have passed everything
}
static int stbi__tga_test(stbi__context *s) {
int res = 0;
int sz, tga_color_type;
stbi__get8(s); // discard Offset
tga_color_type = stbi__get8(s); // color type
if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed
sz = stbi__get8(s); // image type
if ( tga_color_type == 1 ) { // colormapped (paletted) image
if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9
stbi__skip(s,4); // skip index of first colormap entry and number of entries
sz = stbi__get8(s); // check bits per palette color entry
if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
stbi__skip(s,4); // skip image x and y origin
} else { // "normal" image w/o colormap
if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE
stbi__skip(s,9); // skip colormap specification and image x/y origin
}
if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width
if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height
sz = stbi__get8(s); // bits per pixel
if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index
if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
res = 1; // if we got this far, everything's good and we can return 1 instead of 0
errorEnd:
stbi__rewind(s);
return res;
}
// read 16bit value and convert to 24bit RGB
static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) {
stbi__uint16 px = (stbi__uint16)stbi__get16le(s);
stbi__uint16 fiveBitMask = 31;
// we have 3 channels with 5bits each
int r = (px >> 10) & fiveBitMask;
int g = (px >> 5) & fiveBitMask;
int b = px & fiveBitMask;
// Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later
out[0] = (stbi_uc)((r * 255)/31);
out[1] = (stbi_uc)((g * 255)/31);
out[2] = (stbi_uc)((b * 255)/31);
// some people claim that the most significant bit might be used for alpha
// (possibly if an alpha-bit is set in the "image descriptor byte")
// but that only made 16bit test images completely translucent..
// so let's treat all 15 and 16bit TGAs as RGB with no alpha.
}
static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {
// read in the TGA header stuff
int tga_offset = stbi__get8(s);
int tga_indexed = stbi__get8(s);
int tga_image_type = stbi__get8(s);
int tga_is_RLE = 0;
int tga_palette_start = stbi__get16le(s);
int tga_palette_len = stbi__get16le(s);
int tga_palette_bits = stbi__get8(s);
int tga_x_origin = stbi__get16le(s);
int tga_y_origin = stbi__get16le(s);
int tga_width = stbi__get16le(s);
int tga_height = stbi__get16le(s);
int tga_bits_per_pixel = stbi__get8(s);
int tga_comp, tga_rgb16=0;
int tga_inverted = stbi__get8(s);
// int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)
// image data
unsigned char *tga_data;
unsigned char *tga_palette = NULL;
int i, j;
unsigned char raw_data[4] = {0};
int RLE_count = 0;
int RLE_repeating = 0;
int read_next_pixel = 1;
STBI_NOTUSED(ri);
// do a tiny bit of precessing
if ( tga_image_type >= 8 ) {
tga_image_type -= 8;
tga_is_RLE = 1;
}
tga_inverted = 1 - ((tga_inverted >> 5) & 1);
// If I'm paletted, then I'll use the number of bits from the palette
if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);
else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);
if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency
return stbi__errpuc("bad format", "Can't find out TGA pixelformat");
// tga info
*x = tga_width;
*y = tga_height;
if (comp) *comp = tga_comp;
if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0))
return stbi__errpuc("too large", "Corrupt TGA");
tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);
if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");
// skip to the data's starting position (offset usually = 0)
stbi__skip(s, tga_offset );
if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {
for (i=0; i < tga_height; ++i) {
int row = tga_inverted ? tga_height -i - 1 : i;
stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
stbi__getn(s, tga_row, tga_width * tga_comp);
}
} else {
// do I need to load a palette?
if ( tga_indexed) {
// any data to skip? (offset usually = 0)
stbi__skip(s, tga_palette_start );
// load the palette
tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0);
if (!tga_palette) {
STBI_FREE(tga_data);
return stbi__errpuc("outofmem", "Out of memory");
}
if (tga_rgb16) {
stbi_uc *pal_entry = tga_palette;
STBI_ASSERT(tga_comp == STBI_rgb);
for (i=0; i < tga_palette_len; ++i) {
stbi__tga_read_rgb16(s, pal_entry);
pal_entry += tga_comp;
}
} else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {
STBI_FREE(tga_data);
STBI_FREE(tga_palette);
return stbi__errpuc("bad palette", "Corrupt TGA");
}
}
// load the data
for (i=0; i < tga_width * tga_height; ++i) {
// if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
if ( tga_is_RLE ) {
if ( RLE_count == 0 ) {
// yep, get the next byte as a RLE command
int RLE_cmd = stbi__get8(s);
RLE_count = 1 + (RLE_cmd & 127);
RLE_repeating = RLE_cmd >> 7;
read_next_pixel = 1;
} else if ( !RLE_repeating ) {
read_next_pixel = 1;
}
} else {
read_next_pixel = 1;
}
// OK, if I need to read a pixel, do it now
if ( read_next_pixel ) {
// load however much data we did have
if ( tga_indexed ) {
// read in index, then perform the lookup
int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);
if ( pal_idx >= tga_palette_len ) {
// invalid index
pal_idx = 0;
}
pal_idx *= tga_comp;
for (j = 0; j < tga_comp; ++j) {
raw_data[j] = tga_palette[pal_idx+j];
}
} else if(tga_rgb16) {
STBI_ASSERT(tga_comp == STBI_rgb);
stbi__tga_read_rgb16(s, raw_data);
} else {
// read in the data raw
for (j = 0; j < tga_comp; ++j) {
raw_data[j] = stbi__get8(s);
}
}
// clear the reading flag for the next pixel
read_next_pixel = 0;
} // end of reading a pixel
// copy data
for (j = 0; j < tga_comp; ++j)
tga_data[i*tga_comp+j] = raw_data[j];
// in case we're in RLE mode, keep counting down
--RLE_count;
}
// do I need to invert the image?
if ( tga_inverted ) {
for (j = 0; j*2 < tga_height; ++j) {
int index1 = j * tga_width * tga_comp;
int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
for (i = tga_width * tga_comp; i > 0; --i) {
unsigned char temp = tga_data[index1];
tga_data[index1] = tga_data[index2];
tga_data[index2] = temp;
++index1;
++index2;
}
}
}
// clear my palette, if I had one
if ( tga_palette != NULL ) {
STBI_FREE( tga_palette );
}
}
// swap RGB - if the source data was RGB16, it already is in the right order
if (tga_comp >= 3 && !tga_rgb16) {
unsigned char* tga_pixel = tga_data;
for (i=0; i < tga_width * tga_height; ++i) {
unsigned char temp = tga_pixel[0];
tga_pixel[0] = tga_pixel[2];
tga_pixel[2] = temp;
tga_pixel += tga_comp;
}
}
// convert to target component count
if (req_comp && req_comp != tga_comp)
tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
// the things I do to get rid of an error message, and yet keep
// Microsoft's C compilers happy... [8^(
tga_palette_start = tga_palette_len = tga_palette_bits =
tga_x_origin = tga_y_origin = 0;
// OK, done
return tga_data;
}
#endif
// *************************************************************************************************
// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB
#ifndef STBI_NO_PSD
static int stbi__psd_test(stbi__context *s) {
int r = (stbi__get32be(s) == 0x38425053);
stbi__rewind(s);
return r;
}
static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) {
int count, nleft, len;
count = 0;
while ((nleft = pixelCount - count) > 0) {
len = stbi__get8(s);
if (len == 128) {
// No-op.
} else if (len < 128) {
// Copy next len+1 bytes literally.
len++;
if (len > nleft) return 0; // corrupt data
count += len;
while (len) {
*p = stbi__get8(s);
p += 4;
len--;
}
} else if (len > 128) {
stbi_uc val;
// Next -len+1 bytes in the dest are replicated from next source byte.
// (Interpret len as a negative 8-bit int.)
len = 257 - len;
if (len > nleft) return 0; // corrupt data
val = stbi__get8(s);
count += len;
while (len) {
*p = val;
p += 4;
len--;
}
}
}
return 1;
}
static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) {
int pixelCount;
int channelCount, compression;
int channel, i;
int bitdepth;
int w,h;
stbi_uc *out;
STBI_NOTUSED(ri);
// Check identifier
if (stbi__get32be(s) != 0x38425053) // "8BPS"
return stbi__errpuc("not PSD", "Corrupt PSD image");
// Check file type version.
if (stbi__get16be(s) != 1)
return stbi__errpuc("wrong version", "Unsupported version of PSD image");
// Skip 6 reserved bytes.
stbi__skip(s, 6 );
// Read the number of channels (R, G, B, A, etc).
channelCount = stbi__get16be(s);
if (channelCount < 0 || channelCount > 16)
return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image");
// Read the rows and columns of the image.
h = stbi__get32be(s);
w = stbi__get32be(s);
// Make sure the depth is 8 bits.
bitdepth = stbi__get16be(s);
if (bitdepth != 8 && bitdepth != 16)
return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit");
// Make sure the color mode is RGB.
// Valid options are:
// 0: Bitmap
// 1: Grayscale
// 2: Indexed color
// 3: RGB color
// 4: CMYK color
// 7: Multichannel
// 8: Duotone
// 9: Lab color
if (stbi__get16be(s) != 3)
return stbi__errpuc("wrong color format", "PSD is not in RGB color format");
// Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
stbi__skip(s,stbi__get32be(s) );
// Skip the image resources. (resolution, pen tool paths, etc)
stbi__skip(s, stbi__get32be(s) );
// Skip the reserved data.
stbi__skip(s, stbi__get32be(s) );
// Find out if the data is compressed.
// Known values:
// 0: no compression
// 1: RLE compressed
compression = stbi__get16be(s);
if (compression > 1)
return stbi__errpuc("bad compression", "PSD has an unknown compression format");
// Check size
if (!stbi__mad3sizes_valid(4, w, h, 0))
return stbi__errpuc("too large", "Corrupt PSD");
// Create the destination image.
if (!compression && bitdepth == 16 && bpc == 16) {
out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0);
ri->bits_per_channel = 16;
} else
out = (stbi_uc *) stbi__malloc(4 * w*h);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
pixelCount = w*h;
// Initialize the data to zero.
//memset( out, 0, pixelCount * 4 );
// Finally, the image data.
if (compression) {
// RLE as used by .PSD and .TIFF
// Loop until you get the number of unpacked bytes you are expecting:
// Read the next source byte into n.
// If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
// Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
// Else if n is 128, noop.
// Endloop
// The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
// which we're going to just skip.
stbi__skip(s, h * channelCount * 2 );
// Read the RLE data by channel.
for (channel = 0; channel < 4; channel++) {
stbi_uc *p;
p = out+channel;
if (channel >= channelCount) {
// Fill this channel with default data.
for (i = 0; i < pixelCount; i++, p += 4)
*p = (channel == 3 ? 255 : 0);
} else {
// Read the RLE data.
if (!stbi__psd_decode_rle(s, p, pixelCount)) {
STBI_FREE(out);
return stbi__errpuc("corrupt", "bad RLE data");
}
}
}
} else {
// We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
// where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image.
// Read the data by channel.
for (channel = 0; channel < 4; channel++) {
if (channel >= channelCount) {
// Fill this channel with default data.
if (bitdepth == 16 && bpc == 16) {
stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
stbi__uint16 val = channel == 3 ? 65535 : 0;
for (i = 0; i < pixelCount; i++, q += 4)
*q = val;
} else {
stbi_uc *p = out+channel;
stbi_uc val = channel == 3 ? 255 : 0;
for (i = 0; i < pixelCount; i++, p += 4)
*p = val;
}
} else {
if (ri->bits_per_channel == 16) { // output bpc
stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
for (i = 0; i < pixelCount; i++, q += 4)
*q = (stbi__uint16) stbi__get16be(s);
} else {
stbi_uc *p = out+channel;
if (bitdepth == 16) { // input bpc
for (i = 0; i < pixelCount; i++, p += 4)
*p = (stbi_uc) (stbi__get16be(s) >> 8);
} else {
for (i = 0; i < pixelCount; i++, p += 4)
*p = stbi__get8(s);
}
}
}
}
}
// remove weird white matte from PSD
if (channelCount >= 4) {
if (ri->bits_per_channel == 16) {
for (i=0; i < w*h; ++i) {
stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i;
if (pixel[3] != 0 && pixel[3] != 65535) {
float a = pixel[3] / 65535.0f;
float ra = 1.0f / a;
float inv_a = 65535.0f * (1 - ra);
pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a);
pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a);
pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a);
}
}
} else {
for (i=0; i < w*h; ++i) {
unsigned char *pixel = out + 4*i;
if (pixel[3] != 0 && pixel[3] != 255) {
float a = pixel[3] / 255.0f;
float ra = 1.0f / a;
float inv_a = 255.0f * (1 - ra);
pixel[0] = (unsigned char) (pixel[0]*ra + inv_a);
pixel[1] = (unsigned char) (pixel[1]*ra + inv_a);
pixel[2] = (unsigned char) (pixel[2]*ra + inv_a);
}
}
}
}
// convert to desired output format
if (req_comp && req_comp != 4) {
if (ri->bits_per_channel == 16)
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h);
else
out = stbi__convert_format(out, 4, req_comp, w, h);
if (out == NULL) return out; // stbi__convert_format frees input on failure
}
if (comp) *comp = 4;
*y = h;
*x = w;
return out;
}
#endif
// *************************************************************************************************
// Softimage PIC loader
// by Tom Seddon
//
// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/
#ifndef STBI_NO_PIC
static int stbi__pic_is4(stbi__context *s,const char *str) {
int i;
for (i=0; i<4; ++i)
if (stbi__get8(s) != (stbi_uc)str[i])
return 0;
return 1;
}
static int stbi__pic_test_core(stbi__context *s) {
int i;
if (!stbi__pic_is4(s,"\x53\x80\xF6\x34"))
return 0;
for(i=0; i<84; ++i)
stbi__get8(s);
if (!stbi__pic_is4(s,"PICT"))
return 0;
return 1;
}
typedef struct {
stbi_uc size,type,channel;
} stbi__pic_packet;
static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) {
int mask=0x80, i;
for (i=0; i<4; ++i, mask>>=1) {
if (channel & mask) {
if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short");
dest[i]=stbi__get8(s);
}
}
return dest;
}
static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) {
int mask=0x80,i;
for (i=0; i<4; ++i, mask>>=1)
if (channel&mask)
dest[i]=src[i];
}
static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) {
int act_comp=0,num_packets=0,y,chained;
stbi__pic_packet packets[10];
// this will (should...) cater for even some bizarre stuff like having data
// for the same channel in multiple packets.
do {
stbi__pic_packet *packet;
if (num_packets==sizeof(packets)/sizeof(packets[0]))
return stbi__errpuc("bad format","too many packets");
packet = &packets[num_packets++];
chained = stbi__get8(s);
packet->size = stbi__get8(s);
packet->type = stbi__get8(s);
packet->channel = stbi__get8(s);
act_comp |= packet->channel;
if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)");
if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp");
} while (chained);
*comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?
for(y=0; ytype) {
default:
return stbi__errpuc("bad format","packet has bad compression type");
case 0: {//uncompressed
int x;
for(x=0; xchannel,dest))
return 0;
break;
}
case 1: { //Pure RLE
int left=width, i;
while (left>0) {
stbi_uc count,value[4];
count=stbi__get8(s);
if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)");
if (count > left)
count = (stbi_uc) left;
if (!stbi__readval(s,packet->channel,value)) return 0;
for(i=0; ichannel,dest,value);
left -= count;
}
}
break;
case 2: {//Mixed RLE
int left=width;
while (left>0) {
int count = stbi__get8(s), i;
if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)");
if (count >= 128) { // Repeated
stbi_uc value[4];
if (count==128)
count = stbi__get16be(s);
else
count -= 127;
if (count > left)
return stbi__errpuc("bad file","scanline overrun");
if (!stbi__readval(s,packet->channel,value))
return 0;
for(i=0; ichannel,dest,value);
} else { // Raw
++count;
if (count>left) return stbi__errpuc("bad file","scanline overrun");
for(i=0; ichannel,dest))
return 0;
}
left-=count;
}
break;
}
}
}
}
return result;
}
static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) {
stbi_uc *result;
int i, x,y, internal_comp;
STBI_NOTUSED(ri);
if (!comp) comp = &internal_comp;
for (i=0; i<92; ++i)
stbi__get8(s);
x = stbi__get16be(s);
y = stbi__get16be(s);
if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)");
if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode");
stbi__get32be(s); //skip `ratio'
stbi__get16be(s); //skip `fields'
stbi__get16be(s); //skip `pad'
// intermediate buffer is RGBA
result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
memset(result, 0xff, x*y*4);
if (!stbi__pic_load_core(s,x,y,comp, result)) {
STBI_FREE(result);
result=0;
}
*px = x;
*py = y;
if (req_comp == 0) req_comp = *comp;
result=stbi__convert_format(result,4,req_comp,x,y);
return result;
}
static int stbi__pic_test(stbi__context *s) {
int r = stbi__pic_test_core(s);
stbi__rewind(s);
return r;
}
#endif
// *************************************************************************************************
// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
#ifndef STBI_NO_GIF
typedef struct {
stbi__int16 prefix;
stbi_uc first;
stbi_uc suffix;
} stbi__gif_lzw;
typedef struct {
int w,h;
stbi_uc *out; // output buffer (always 4 components)
stbi_uc *background; // The current "background" as far as a gif is concerned
stbi_uc *history;
int flags, bgindex, ratio, transparent, eflags;
stbi_uc pal[256][4];
stbi_uc lpal[256][4];
stbi__gif_lzw codes[8192];
stbi_uc *color_table;
int parse, step;
int lflags;
int start_x, start_y;
int max_x, max_y;
int cur_x, cur_y;
int line_size;
int delay;
} stbi__gif;
static int stbi__gif_test_raw(stbi__context *s) {
int sz;
if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;
sz = stbi__get8(s);
if (sz != '9' && sz != '7') return 0;
if (stbi__get8(s) != 'a') return 0;
return 1;
}
static int stbi__gif_test(stbi__context *s) {
int r = stbi__gif_test_raw(s);
stbi__rewind(s);
return r;
}
static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) {
int i;
for (i=0; i < num_entries; ++i) {
pal[i][2] = stbi__get8(s);
pal[i][1] = stbi__get8(s);
pal[i][0] = stbi__get8(s);
pal[i][3] = transp == i ? 0 : 255;
}
}
static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) {
stbi_uc version;
if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
return stbi__err("not GIF", "Corrupt GIF");
version = stbi__get8(s);
if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF");
if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF");
stbi__g_failure_reason = "";
g->w = stbi__get16le(s);
g->h = stbi__get16le(s);
g->flags = stbi__get8(s);
g->bgindex = stbi__get8(s);
g->ratio = stbi__get8(s);
g->transparent = -1;
if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments
if (is_info) return 1;
if (g->flags & 0x80)
stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);
return 1;
}
static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) {
stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
if (!stbi__gif_header(s, g, comp, 1)) {
STBI_FREE(g);
stbi__rewind( s );
return 0;
}
if (x) *x = g->w;
if (y) *y = g->h;
STBI_FREE(g);
return 1;
}
static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) {
stbi_uc *p, *c;
int idx;
// recurse to decode the prefixes, since the linked-list is backwards,
// and working backwards through an interleaved image would be nasty
if (g->codes[code].prefix >= 0)
stbi__out_gif_code(g, g->codes[code].prefix);
if (g->cur_y >= g->max_y) return;
idx = g->cur_x + g->cur_y;
p = &g->out[idx];
g->history[idx / 4] = 1;
c = &g->color_table[g->codes[code].suffix * 4];
if (c[3] > 128) { // don't render transparent pixels;
p[0] = c[2];
p[1] = c[1];
p[2] = c[0];
p[3] = c[3];
}
g->cur_x += 4;
if (g->cur_x >= g->max_x) {
g->cur_x = g->start_x;
g->cur_y += g->step;
while (g->cur_y >= g->max_y && g->parse > 0) {
g->step = (1 << g->parse) * g->line_size;
g->cur_y = g->start_y + (g->step >> 1);
--g->parse;
}
}
}
static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) {
stbi_uc lzw_cs;
stbi__int32 len, init_code;
stbi__uint32 first;
stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
stbi__gif_lzw *p;
lzw_cs = stbi__get8(s);
if (lzw_cs > 12) return NULL;
clear = 1 << lzw_cs;
first = 1;
codesize = lzw_cs + 1;
codemask = (1 << codesize) - 1;
bits = 0;
valid_bits = 0;
for (init_code = 0; init_code < clear; init_code++) {
g->codes[init_code].prefix = -1;
g->codes[init_code].first = (stbi_uc) init_code;
g->codes[init_code].suffix = (stbi_uc) init_code;
}
// support no starting clear code
avail = clear+2;
oldcode = -1;
len = 0;
for(;;) {
if (valid_bits < codesize) {
if (len == 0) {
len = stbi__get8(s); // start new block
if (len == 0)
return g->out;
}
--len;
bits |= (stbi__int32) stbi__get8(s) << valid_bits;
valid_bits += 8;
} else {
stbi__int32 code = bits & codemask;
bits >>= codesize;
valid_bits -= codesize;
// @OPTIMIZE: is there some way we can accelerate the non-clear path?
if (code == clear) { // clear code
codesize = lzw_cs + 1;
codemask = (1 << codesize) - 1;
avail = clear + 2;
oldcode = -1;
first = 0;
} else if (code == clear + 1) { // end of stream code
stbi__skip(s, len);
while ((len = stbi__get8(s)) > 0)
stbi__skip(s,len);
return g->out;
} else if (code <= avail) {
if (first) {
return stbi__errpuc("no clear code", "Corrupt GIF");
}
if (oldcode >= 0) {
p = &g->codes[avail++];
if (avail > 8192) {
return stbi__errpuc("too many codes", "Corrupt GIF");
}
p->prefix = (stbi__int16) oldcode;
p->first = g->codes[oldcode].first;
p->suffix = (code == avail) ? p->first : g->codes[code].first;
} else if (code == avail)
return stbi__errpuc("illegal code in raster", "Corrupt GIF");
stbi__out_gif_code(g, (stbi__uint16) code);
if ((avail & codemask) == 0 && avail <= 0x0FFF) {
codesize++;
codemask = (1 << codesize) - 1;
}
oldcode = code;
} else {
return stbi__errpuc("illegal code in raster", "Corrupt GIF");
}
}
}
}
// this function is designed to support animated gifs, although stb_image doesn't support it
// two back is the image from two frames ago, used for a very specific disposal format
static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) {
int dispose;
int first_frame;
int pi;
int pcount;
// on first frame, any non-written pixels get the background colour (non-transparent)
first_frame = 0;
if (g->out == 0) {
if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
g->history = (stbi_uc *) stbi__malloc(g->w * g->h);
if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
// image is treated as "tranparent" at the start - ie, nothing overwrites the current background;
// background colour is only used for pixels that are not rendered first frame, after that "background"
// color refers to teh color that was there the previous frame.
memset( g->out, 0x00, 4 * g->w * g->h );
memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent)
memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame
first_frame = 1;
} else {
// second frame - how do we dispoase of the previous one?
dispose = (g->eflags & 0x1C) >> 2;
pcount = g->w * g->h;
if ((dispose == 3) && (two_back == 0)) {
dispose = 2; // if I don't have an image to revert back to, default to the old background
}
if (dispose == 3) { // use previous graphic
for (pi = 0; pi < pcount; ++pi) {
if (g->history[pi]) {
memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );
}
}
} else if (dispose == 2) {
// restore what was changed last frame to background before that frame;
for (pi = 0; pi < pcount; ++pi) {
if (g->history[pi]) {
memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );
}
}
} else {
// This is a non-disposal case eithe way, so just
// leave the pixels as is, and they will become the new background
// 1: do not dispose
// 0: not specified.
}
// background is what out is after the undoing of the previou frame;
memcpy( g->background, g->out, 4 * g->w * g->h );
}
// clear my history;
memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame
for (;;) {
int tag = stbi__get8(s);
switch (tag) {
case 0x2C: { /* Image Descriptor */
stbi__int32 x, y, w, h;
stbi_uc *o;
x = stbi__get16le(s);
y = stbi__get16le(s);
w = stbi__get16le(s);
h = stbi__get16le(s);
if (((x + w) > (g->w)) || ((y + h) > (g->h)))
return stbi__errpuc("bad Image Descriptor", "Corrupt GIF");
g->line_size = g->w * 4;
g->start_x = x * 4;
g->start_y = y * g->line_size;
g->max_x = g->start_x + w * 4;
g->max_y = g->start_y + h * g->line_size;
g->cur_x = g->start_x;
g->cur_y = g->start_y;
g->lflags = stbi__get8(s);
if (g->lflags & 0x40) {
g->step = 8 * g->line_size; // first interlaced spacing
g->parse = 3;
} else {
g->step = g->line_size;
g->parse = 0;
}
if (g->lflags & 0x80) {
stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
g->color_table = (stbi_uc *) g->lpal;
} else if (g->flags & 0x80) {
g->color_table = (stbi_uc *) g->pal;
} else
return stbi__errpuc("missing color table", "Corrupt GIF");
o = stbi__process_gif_raster(s, g);
if (o == NULL) return NULL;
// if this was the first frame,
pcount = g->w * g->h;
if (first_frame && (g->bgindex > 0)) {
// if first frame, any pixel not drawn to gets the background color
for (pi = 0; pi < pcount; ++pi) {
if (g->history[pi] == 0) {
g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;
memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );
}
}
}
return o;
}
case 0x21: { // Comment Extension.
int len;
int ext = stbi__get8(s);
if (ext == 0xF9) { // Graphic Control Extension.
len = stbi__get8(s);
if (len == 4) {
g->eflags = stbi__get8(s);
g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths.
// unset old transparent
if (g->transparent >= 0) {
g->pal[g->transparent][3] = 255;
}
if (g->eflags & 0x01) {
g->transparent = stbi__get8(s);
if (g->transparent >= 0) {
g->pal[g->transparent][3] = 0;
}
} else {
// don't need transparent
stbi__skip(s, 1);
g->transparent = -1;
}
} else {
stbi__skip(s, len);
break;
}
}
while ((len = stbi__get8(s)) != 0) {
stbi__skip(s, len);
}
break;
}
case 0x3B: // gif stream termination code
return (stbi_uc *) s; // using '1' causes warning on some compilers
default:
return stbi__errpuc("unknown code", "Corrupt GIF");
}
}
}
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) {
if (stbi__gif_test(s)) {
int layers = 0;
stbi_uc *u = 0;
stbi_uc *out = 0;
stbi_uc *two_back = 0;
stbi__gif g;
int stride;
memset(&g, 0, sizeof(g));
if (delays) {
*delays = 0;
}
do {
u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
if (u) {
*x = g.w;
*y = g.h;
++layers;
stride = g.w * g.h * 4;
if (out) {
out = (stbi_uc*) STBI_REALLOC( out, layers * stride );
if (delays) {
*delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers );
}
} else {
out = (stbi_uc*)stbi__malloc( layers * stride );
if (delays) {
*delays = (int*) stbi__malloc( layers * sizeof(int) );
}
}
memcpy( out + ((layers - 1) * stride), u, stride );
if (layers >= 2) {
two_back = out - 2 * stride;
}
if (delays) {
(*delays)[layers - 1U] = g.delay;
}
}
} while (u != 0);
// free temp buffer;
STBI_FREE(g.out);
STBI_FREE(g.history);
STBI_FREE(g.background);
// do the final conversion after loading everything;
if (req_comp && req_comp != 4)
out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);
*z = layers;
return out;
} else {
return stbi__errpuc("not GIF", "Image was not as a gif type.");
}
}
static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {
stbi_uc *u = 0;
stbi__gif g;
memset(&g, 0, sizeof(g));
u = stbi__gif_load_next(s, &g, comp, req_comp, 0);
if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
if (u) {
*x = g.w;
*y = g.h;
// moved conversion to after successful load so that the same
// can be done for multiple frames.
if (req_comp && req_comp != 4)
u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
}
// free buffers needed for multiple frame loading;
STBI_FREE(g.history);
STBI_FREE(g.background);
return u;
}
static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) {
return stbi__gif_info_raw(s,x,y,comp);
}
#endif
// *************************************************************************************************
// Radiance RGBE HDR loader
// originally by Nicolas Schulz
#ifndef STBI_NO_HDR
static int stbi__hdr_test_core(stbi__context *s, const char *signature) {
int i;
for (i=0; signature[i]; ++i)
if (stbi__get8(s) != signature[i])
return 0;
stbi__rewind(s);
return 1;
}
static int stbi__hdr_test(stbi__context* s) {
int r = stbi__hdr_test_core(s, "#?RADIANCE\n");
stbi__rewind(s);
if(!r) {
r = stbi__hdr_test_core(s, "#?RGBE\n");
stbi__rewind(s);
}
return r;
}
#define STBI__HDR_BUFLEN 1024
static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) {
int len=0;
char c = '\0';
c = (char) stbi__get8(z);
while (!stbi__at_eof(z) && c != '\n') {
buffer[len++] = c;
if (len == STBI__HDR_BUFLEN-1) {
// flush to end of line
while (!stbi__at_eof(z) && stbi__get8(z) != '\n')
;
break;
}
c = (char) stbi__get8(z);
}
buffer[len] = 0;
return buffer;
}
static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) {
if ( input[3] != 0 ) {
float f1;
// Exponent
f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
if (req_comp <= 2)
output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
else {
output[0] = input[0] * f1;
output[1] = input[1] * f1;
output[2] = input[2] * f1;
}
if (req_comp == 2) output[1] = 1;
if (req_comp == 4) output[3] = 1;
} else {
switch (req_comp) {
case 4:
output[3] = 1; /* fallthrough */
case 3:
output[0] = output[1] = output[2] = 0;
break;
case 2:
output[1] = 1; /* fallthrough */
case 1:
output[0] = 0;
break;
}
}
}
static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {
char buffer[STBI__HDR_BUFLEN];
char *token;
int valid = 0;
int width, height;
stbi_uc *scanline;
float *hdr_data;
int len;
unsigned char count, value;
int i, j, k, c1,c2, z;
const char *headerToken;
STBI_NOTUSED(ri);
// Check identifier
headerToken = stbi__hdr_gettoken(s,buffer);
if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0)
return stbi__errpf("not HDR", "Corrupt HDR image");
// Parse header
for(;;) {
token = stbi__hdr_gettoken(s,buffer);
if (token[0] == 0) break;
if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
}
if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format");
// Parse width and height
// can't use sscanf() if we're not using stdio!
token = stbi__hdr_gettoken(s,buffer);
if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
token += 3;
height = (int) strtol(token, &token, 10);
while (*token == ' ') ++token;
if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
token += 3;
width = (int) strtol(token, NULL, 10);
*x = width;
*y = height;
if (comp) *comp = 3;
if (req_comp == 0) req_comp = 3;
if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0))
return stbi__errpf("too large", "HDR image is too large");
// Read data
hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);
if (!hdr_data)
return stbi__errpf("outofmem", "Out of memory");
// Load image data
// image data is stored as some number of sca
if ( width < 8 || width >= 32768) {
// Read flat data
for (j=0; j < height; ++j) {
for (i=0; i < width; ++i) {
stbi_uc rgbe[4];
main_decode_loop:
stbi__getn(s, rgbe, 4);
stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
}
}
} else {
// Read RLE-encoded data
scanline = NULL;
for (j = 0; j < height; ++j) {
c1 = stbi__get8(s);
c2 = stbi__get8(s);
len = stbi__get8(s);
if (c1 != 2 || c2 != 2 || (len & 0x80)) {
// not run-length encoded, so we have to actually use THIS data as a decoded
// pixel (note this can't be a valid pixel--one of RGB must be >= 128)
stbi_uc rgbe[4];
rgbe[0] = (stbi_uc) c1;
rgbe[1] = (stbi_uc) c2;
rgbe[2] = (stbi_uc) len;
rgbe[3] = (stbi_uc) stbi__get8(s);
stbi__hdr_convert(hdr_data, rgbe, req_comp);
i = 1;
j = 0;
STBI_FREE(scanline);
goto main_decode_loop; // yes, this makes no sense
}
len <<= 8;
len |= stbi__get8(s);
if (len != width) {
STBI_FREE(hdr_data);
STBI_FREE(scanline);
return stbi__errpf("invalid decoded scanline length", "corrupt HDR");
}
if (scanline == NULL) {
scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0);
if (!scanline) {
STBI_FREE(hdr_data);
return stbi__errpf("outofmem", "Out of memory");
}
}
for (k = 0; k < 4; ++k) {
int nleft;
i = 0;
while ((nleft = width - i) > 0) {
count = stbi__get8(s);
if (count > 128) {
// Run
value = stbi__get8(s);
count -= 128;
if (count > nleft) {
STBI_FREE(hdr_data);
STBI_FREE(scanline);
return stbi__errpf("corrupt", "bad RLE data in HDR");
}
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = value;
} else {
// Dump
if (count > nleft) {
STBI_FREE(hdr_data);
STBI_FREE(scanline);
return stbi__errpf("corrupt", "bad RLE data in HDR");
}
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = stbi__get8(s);
}
}
}
for (i=0; i < width; ++i)
stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
}
if (scanline)
STBI_FREE(scanline);
}
return hdr_data;
}
static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) {
char buffer[STBI__HDR_BUFLEN];
char *token;
int valid = 0;
int dummy;
if (!x) x = &dummy;
if (!y) y = &dummy;
if (!comp) comp = &dummy;
if (stbi__hdr_test(s) == 0) {
stbi__rewind( s );
return 0;
}
for(;;) {
token = stbi__hdr_gettoken(s,buffer);
if (token[0] == 0) break;
if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
}
if (!valid) {
stbi__rewind( s );
return 0;
}
token = stbi__hdr_gettoken(s,buffer);
if (strncmp(token, "-Y ", 3)) {
stbi__rewind( s );
return 0;
}
token += 3;
*y = (int) strtol(token, &token, 10);
while (*token == ' ') ++token;
if (strncmp(token, "+X ", 3)) {
stbi__rewind( s );
return 0;
}
token += 3;
*x = (int) strtol(token, NULL, 10);
*comp = 3;
return 1;
}
#endif // STBI_NO_HDR
#ifndef STBI_NO_BMP
static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) {
void *p;
stbi__bmp_data info;
info.all_a = 255;
p = stbi__bmp_parse_header(s, &info);
stbi__rewind( s );
if (p == NULL)
return 0;
if (x) *x = s->img_x;
if (y) *y = s->img_y;
if (comp) *comp = info.ma ? 4 : 3;
return 1;
}
#endif
#ifndef STBI_NO_PSD
static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) {
int channelCount, dummy, depth;
if (!x) x = &dummy;
if (!y) y = &dummy;
if (!comp) comp = &dummy;
if (stbi__get32be(s) != 0x38425053) {
stbi__rewind( s );
return 0;
}
if (stbi__get16be(s) != 1) {
stbi__rewind( s );
return 0;
}
stbi__skip(s, 6);
channelCount = stbi__get16be(s);
if (channelCount < 0 || channelCount > 16) {
stbi__rewind( s );
return 0;
}
*y = stbi__get32be(s);
*x = stbi__get32be(s);
depth = stbi__get16be(s);
if (depth != 8 && depth != 16) {
stbi__rewind( s );
return 0;
}
if (stbi__get16be(s) != 3) {
stbi__rewind( s );
return 0;
}
*comp = 4;
return 1;
}
static int stbi__psd_is16(stbi__context *s) {
int channelCount, depth;
if (stbi__get32be(s) != 0x38425053) {
stbi__rewind( s );
return 0;
}
if (stbi__get16be(s) != 1) {
stbi__rewind( s );
return 0;
}
stbi__skip(s, 6);
channelCount = stbi__get16be(s);
if (channelCount < 0 || channelCount > 16) {
stbi__rewind( s );
return 0;
}
(void) stbi__get32be(s);
(void) stbi__get32be(s);
depth = stbi__get16be(s);
if (depth != 16) {
stbi__rewind( s );
return 0;
}
return 1;
}
#endif
#ifndef STBI_NO_PIC
static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) {
int act_comp=0,num_packets=0,chained,dummy;
stbi__pic_packet packets[10];
if (!x) x = &dummy;
if (!y) y = &dummy;
if (!comp) comp = &dummy;
if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) {
stbi__rewind(s);
return 0;
}
stbi__skip(s, 88);
*x = stbi__get16be(s);
*y = stbi__get16be(s);
if (stbi__at_eof(s)) {
stbi__rewind( s);
return 0;
}
if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
stbi__rewind( s );
return 0;
}
stbi__skip(s, 8);
do {
stbi__pic_packet *packet;
if (num_packets==sizeof(packets)/sizeof(packets[0]))
return 0;
packet = &packets[num_packets++];
chained = stbi__get8(s);
packet->size = stbi__get8(s);
packet->type = stbi__get8(s);
packet->channel = stbi__get8(s);
act_comp |= packet->channel;
if (stbi__at_eof(s)) {
stbi__rewind( s );
return 0;
}
if (packet->size != 8) {
stbi__rewind( s );
return 0;
}
} while (chained);
*comp = (act_comp & 0x10 ? 4 : 3);
return 1;
}
#endif
// *************************************************************************************************
// Portable Gray Map and Portable Pixel Map loader
// by Ken Miller
//
// PGM: http://netpbm.sourceforge.net/doc/pgm.html
// PPM: http://netpbm.sourceforge.net/doc/ppm.html
//
// Known limitations:
// Does not support comments in the header section
// Does not support ASCII image data (formats P2 and P3)
// Does not support 16-bit-per-channel
#ifndef STBI_NO_PNM
static int stbi__pnm_test(stbi__context *s) {
char p, t;
p = (char) stbi__get8(s);
t = (char) stbi__get8(s);
if (p != 'P' || (t != '5' && t != '6')) {
stbi__rewind( s );
return 0;
}
return 1;
}
static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {
stbi_uc *out;
STBI_NOTUSED(ri);
if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
return 0;
*x = s->img_x;
*y = s->img_y;
if (comp) *comp = s->img_n;
if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))
return stbi__errpuc("too large", "PNM too large");
out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
if (req_comp && req_comp != s->img_n) {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
if (out == NULL) return out; // stbi__convert_format frees input on failure
}
return out;
}
static int stbi__pnm_isspace(char c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}
static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) {
for (;;) {
while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
*c = (char) stbi__get8(s);
if (stbi__at_eof(s) || *c != '#')
break;
while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' )
*c = (char) stbi__get8(s);
}
}
static int stbi__pnm_isdigit(char c) {
return c >= '0' && c <= '9';
}
static int stbi__pnm_getinteger(stbi__context *s, char *c) {
int value = 0;
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
value = value*10 + (*c - '0');
*c = (char) stbi__get8(s);
}
return value;
}
static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) {
int maxv, dummy;
char c, p, t;
if (!x) x = &dummy;
if (!y) y = &dummy;
if (!comp) comp = &dummy;
stbi__rewind(s);
// Get identifier
p = (char) stbi__get8(s);
t = (char) stbi__get8(s);
if (p != 'P' || (t != '5' && t != '6')) {
stbi__rewind(s);
return 0;
}
*comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm
c = (char) stbi__get8(s);
stbi__pnm_skip_whitespace(s, &c);
*x = stbi__pnm_getinteger(s, &c); // read width
stbi__pnm_skip_whitespace(s, &c);
*y = stbi__pnm_getinteger(s, &c); // read height
stbi__pnm_skip_whitespace(s, &c);
maxv = stbi__pnm_getinteger(s, &c); // read max value
if (maxv > 255)
return stbi__err("max value > 255", "PPM image not 8-bit");
else
return 1;
}
#endif
static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) {
#ifndef STBI_NO_JPEG
if (stbi__jpeg_info(s, x, y, comp)) return 1;
#endif
#ifndef STBI_NO_PNG
if (stbi__png_info(s, x, y, comp)) return 1;
#endif
#ifndef STBI_NO_GIF
if (stbi__gif_info(s, x, y, comp)) return 1;
#endif
#ifndef STBI_NO_BMP
if (stbi__bmp_info(s, x, y, comp)) return 1;
#endif
#ifndef STBI_NO_PSD
if (stbi__psd_info(s, x, y, comp)) return 1;
#endif
#ifndef STBI_NO_PIC
if (stbi__pic_info(s, x, y, comp)) return 1;
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_info(s, x, y, comp)) return 1;
#endif
#ifndef STBI_NO_HDR
if (stbi__hdr_info(s, x, y, comp)) return 1;
#endif
// test tga last because it's a crappy test!
#ifndef STBI_NO_TGA
if (stbi__tga_info(s, x, y, comp))
return 1;
#endif
return stbi__err("unknown image type", "Image not of any known type, or corrupt");
}
static int stbi__is_16_main(stbi__context *s) {
#ifndef STBI_NO_PNG
if (stbi__png_is16(s)) return 1;
#endif
#ifndef STBI_NO_PSD
if (stbi__psd_is16(s)) return 1;
#endif
return 0;
}
#ifndef STBI_NO_STDIO
STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) {
FILE *f = stbi__fopen(filename, "rb");
int result;
if (!f) return stbi__err("can't fopen", "Unable to open file");
result = stbi_info_from_file(f, x, y, comp);
fclose(f);
return result;
}
STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) {
int r;
stbi__context s;
long pos = ftell(f);
stbi__start_file(&s, f);
r = stbi__info_main(&s,x,y,comp);
fseek(f,pos,SEEK_SET);
return r;
}
STBIDEF int stbi_is_16_bit(char const *filename) {
FILE *f = stbi__fopen(filename, "rb");
int result;
if (!f) return stbi__err("can't fopen", "Unable to open file");
result = stbi_is_16_bit_from_file(f);
fclose(f);
return result;
}
STBIDEF int stbi_is_16_bit_from_file(FILE *f) {
int r;
stbi__context s;
long pos = ftell(f);
stbi__start_file(&s, f);
r = stbi__is_16_main(&s);
fseek(f,pos,SEEK_SET);
return r;
}
#endif // !STBI_NO_STDIO
STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) {
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__info_main(&s,x,y,comp);
}
STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) {
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
return stbi__info_main(&s,x,y,comp);
}
STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) {
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__is_16_main(&s);
}
STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) {
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
return stbi__is_16_main(&s);
}
#endif // STB_IMAGE_IMPLEMENTATION
/*
revision history:
2.19 (2018-02-11) fix warning
2.18 (2018-01-30) fix warnings
2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug
1-bit BMP
*_is_16_bit api
avoid warnings
2.16 (2017-07-23) all functions have 16-bit variants;
STBI_NO_STDIO works again;
compilation fixes;
fix rounding in unpremultiply;
optimize vertical flip;
disable raw_len validation;
documentation fixes
2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;
warning fixes; disable run-time SSE detection on gcc;
uniform handling of optional "return" values;
thread-safe initialization of zlib tables
2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
2.13 (2016-11-29) add 16-bit API, only supported for PNG right now
2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
2.11 (2016-04-02) allocate large structures on the stack
remove white matting for transparent PSD
fix reported channel count for PNG & BMP
re-enable SSE2 in non-gcc 64-bit
support RGB-formatted JPEG
read 16-bit PNGs (only as 8-bit)
2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED
2.09 (2016-01-16) allow comments in PNM files
16-bit-per-pixel TGA (not bit-per-component)
info() for TGA could break due to .hdr handling
info() for BMP to shares code instead of sloppy parse
can use STBI_REALLOC_SIZED if allocator doesn't support realloc
code cleanup
2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
2.07 (2015-09-13) fix compiler warnings
partial animated GIF support
limited 16-bpc PSD support
#ifdef unused functions
bug with < 92 byte PIC,PNM,HDR,TGA
2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
2.03 (2015-04-12) extra corruption checking (mmozeiko)
stbi_set_flip_vertically_on_load (nguillemot)
fix NEON support; fix mingw support
2.02 (2015-01-19) fix incorrect assert, fix warning
2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
progressive JPEG (stb)
PGM/PPM support (Ken Miller)
STBI_MALLOC,STBI_REALLOC,STBI_FREE
GIF bugfix -- seemingly never worked
STBI_NO_*, STBI_ONLY_*
1.48 (2014-12-14) fix incorrectly-named assert()
1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
optimize PNG (ryg)
fix bug in interlaced PNG with user-specified channel count (stb)
1.46 (2014-08-26)
fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
1.45 (2014-08-16)
fix MSVC-ARM internal compiler error by wrapping malloc
1.44 (2014-08-07)
various warning fixes from Ronny Chevalier
1.43 (2014-07-15)
fix MSVC-only compiler problem in code changed in 1.42
1.42 (2014-07-09)
don't define _CRT_SECURE_NO_WARNINGS (affects user code)
fixes to stbi__cleanup_jpeg path
added STBI_ASSERT to avoid requiring assert.h
1.41 (2014-06-25)
fix search&replace from 1.36 that messed up comments/error messages
1.40 (2014-06-22)
fix gcc struct-initialization warning
1.39 (2014-06-15)
fix to TGA optimization when req_comp != number of components in TGA;
fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)
add support for BMP version 5 (more ignored fields)
1.38 (2014-06-06)
suppress MSVC warnings on integer casts truncating values
fix accidental rename of 'skip' field of I/O
1.37 (2014-06-04)
remove duplicate typedef
1.36 (2014-06-03)
convert to header file single-file library
if de-iphone isn't set, load iphone images color-swapped instead of returning NULL
1.35 (2014-05-27)
various warnings
fix broken STBI_SIMD path
fix bug where stbi_load_from_file no longer left file pointer in correct place
fix broken non-easy path for 32-bit BMP (possibly never used)
TGA optimization by Arseny Kapoulkine
1.34 (unknown)
use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case
1.33 (2011-07-14)
make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
1.32 (2011-07-13)
support for "info" function for all supported filetypes (SpartanJ)
1.31 (2011-06-20)
a few more leak fixes, bug in PNG handling (SpartanJ)
1.30 (2011-06-11)
added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
removed deprecated format-specific test/load functions
removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
fix inefficiency in decoding 32-bit BMP (David Woo)
1.29 (2010-08-16)
various warning fixes from Aurelien Pocheville
1.28 (2010-08-01)
fix bug in GIF palette transparency (SpartanJ)
1.27 (2010-08-01)
cast-to-stbi_uc to fix warnings
1.26 (2010-07-24)
fix bug in file buffering for PNG reported by SpartanJ
1.25 (2010-07-17)
refix trans_data warning (Won Chun)
1.24 (2010-07-12)
perf improvements reading from files on platforms with lock-heavy fgetc()
minor perf improvements for jpeg
deprecated type-specific functions so we'll get feedback if they're needed
attempt to fix trans_data warning (Won Chun)
1.23 fixed bug in iPhone support
1.22 (2010-07-10)
removed image *writing* support
stbi_info support from Jetro Lauha
GIF support from Jean-Marc Lienher
iPhone PNG-extensions from James Brown
warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)
1.21 fix use of 'stbi_uc' in header (reported by jon blow)
1.20 added support for Softimage PIC, by Tom Seddon
1.19 bug in interlaced PNG corruption check (found by ryg)
1.18 (2008-08-02)
fix a threading bug (local mutable static)
1.17 support interlaced PNG
1.16 major bugfix - stbi__convert_format converted one too many pixels
1.15 initialize some fields for thread safety
1.14 fix threadsafe conversion bug
header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
1.13 threadsafe
1.12 const qualifiers in the API
1.11 Support installable IDCT, colorspace conversion routines
1.10 Fixes for 64-bit (don't use "unsigned long")
optimized upsampling by Fabian "ryg" Giesen
1.09 Fix format-conversion for PSD code (bad global variables!)
1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
1.07 attempt to fix C++ warning/errors again
1.06 attempt to fix C++ warning/errors again
1.05 fix TGA loading to return correct *comp and use good luminance calc
1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
1.02 support for (subset of) HDR files, float interface for preferred access to them
1.01 fix bug: possible bug in handling right-side up bmps... not sure
fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all
1.00 interface to zlib that skips zlib header
0.99 correct handling of alpha in palette
0.98 TGA loader by lonesock; dynamically add loaders (untested)
0.97 jpeg errors on too large a file; also catch another malloc failure
0.96 fix detection of invalid v value - particleman@mollyrocket forum
0.95 during header scan, seek to markers in case of padding
0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
0.93 handle jpegtran output; verbose errors
0.92 read 4,8,16,24,32-bit BMP files of several formats
0.91 output 24-bit Windows 3.0 BMP files
0.90 fix a few more warnings; bump version number to approach 1.0
0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
0.60 fix compiling as c++
0.59 fix warnings: merge Dave Moore's -Wall fixes
0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
0.56 fix bug: zlib uncompressed mode len vs. nlen
0.55 fix bug: restart_interval not initialized to 0
0.54 allow NULL for 'int *comp'
0.53 fix bug in png 3->4; speedup png decoding
0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
0.51 obey req_comp requests, 1-component jpegs return as 1-component,
on 'test' only check type, not whether we support this variant
0.50 (2006-11-19)
first released version
*/
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/
================================================
FILE: js.json
================================================
{
"source": "./package.json",
"scripts": {
"run": "esy '@js' x bash -c 'http-server #{self.bin}'"
},
"override": {
"build": ["dune build --root . -j4"],
"install": [
"esy-installer Revery.install",
"esy-installer ReveryExampleJs.install"
],
"dependencies": {
"@opam/js_of_ocaml": "*",
"@opam/js_of_ocaml-compiler": "*",
"@opam/js_of_ocaml-lwt": "*",
"http-server": "*"
}
}
}
================================================
FILE: lsan.supp
================================================
# Add suppression for libfontconfig, which has known leaks
leak:*libfontconfig*
================================================
FILE: package.json
================================================
{
"name": "revery",
"version": "0.32.0",
"description": "App toolkit built with Reason",
"license": "MIT",
"bugs": {
"url": "https://github.com/bryphe/revery/issues"
},
"scripts": {
"test": "esy b dune runtest",
"format": "esy #{os == 'windows' ? 'b' : ''} bash .ci/format.sh #{os}",
"run": "esy @examples x Examples"
},
"homepage": "https://github.com/bryphe/revery#readme",
"esy": {
"buildEnv": {
"PKG_CONFIG_PATH": "/usr/lib64/pkgconfig:$PKG_CONFIG_PATH"
},
"exportedEnv": {
"PKG_CONFIG_PATH": {
"val": "/usr/lib64/pkgconfig:$PKG_CONFIG_PATH",
"scope": "global"
}
},
"build": "dune build -p reason-harfbuzz,reason-skia,reason-sdl2,Revery",
"install": [
"esy-installer reason-harfbuzz.install",
"esy-installer reason-skia.install",
"esy-installer reason-sdl2.install",
"esy-installer Revery.install",
"bash -c \"#{os == 'windows' ? 'cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/*.dll \\'$cur__bin\\'': ':'}\"",
"bash -c \"cp #{esy-skia.bin}/skia.dll \\'$cur__bin\\' #{os == 'windows' ? '' : '2>/dev/null || true'}\"",
"bash -c \"cp #{esy-sdl2.bin}/*.dll \\'$cur__bin\\' #{os == 'windows' ? '' : '2>/dev/null || true'}\"",
"bash -c \"cp #{esy-angle-prebuilt.bin}/*.dll \\'$cur__bin\\' #{os == 'windows' ? '' : '2>/dev/null || true'}\""
]
},
"dependencies": {
"@opam/bos": "0.2.0",
"@opam/ctypes": "0.15.1",
"@opam/dune": "^2.5.0",
"@opam/dune-configurator": "*",
"@opam/reason": "^3.6.2",
"@opam/lru": "bryphe/lru:lru.opam#2708c70",
"@opam/lwt": "^5.0.0",
"@opam/lwt_ppx": "^1.1.0",
"@opam/markup": "0.8.2",
"@opam/ppx_deriving": "*",
"@opam/ppx_optcomp": "v0.14.0",
"@opam/omd": "ocaml/omd:omd.opam#1535e3c",
"@opam/uucp": "*",
"@opam/uutf": "*",
"@opam/charInfo_width": "*",
"@brisk/brisk-reconciler": "briskml/brisk-reconciler#c9d5c4c",
"esy-angle-prebuilt": "1.0.0",
"esy-freetype2": "^2.9.1008",
"@onivim/reason-native-crash-utils": "^1.0.2",
"@revery/esy-harfbuzz": "^2.6.8002",
"esy-skia": "revery-ui/esy-skia#1c81aac",
"esy-sdl2": "^2.0.14000",
"flex": "jordwalke/flex#6ff12fe",
"reperf": "^1.5.1",
"rench": "^1.10.0",
"rebez": "jchavarri/rebez#03fa3b7",
"@revery/timber": "^2.0.0",
"@reason-native/rely": "^3.2.1"
},
"resolutions": {
"@esy-ocaml/reason": "EduardoRFS/reason:reason.json#35aa4df3de0daa60bdc1133dcf97855decac48f7"
},
"devDependencies": {
"ocaml": "4.12.x",
"esy-astyle": "zbaylin/esy-astyle#59bc21a",
"@opam/ocaml-lsp-server": "*"
}
}
================================================
FILE: packages/reason-harfbuzz/examples/harfbuzz-cli/HarfbuzzCli.re
================================================
open Harfbuzz;
Printexc.record_backtrace(true);
let isNative =
switch (Sys.backend_type) {
| Native => true
| Bytecode => true
| _ => false
};
let getExecutingDirectory = () =>
isNative ? Filename.dirname(Sys.argv[0]) ++ Filename.dir_sep : "";
let run = () => {
let show = ({glyphId, cluster}: hb_shape) =>
Printf.sprintf("GlyphID: %d Cluster: %d", glyphId, cluster);
let renderString = (~features=[], font, str) => {
let shapes = Harfbuzz.hb_shape(~features, font, str);
print_endline("-- " ++ str ++ " --");
Array.iter(s => {print_endline("- SHAPE: " ++ show(s))}, shapes);
print_endline("----");
};
let compiledVersion = hb_version_string_compiled();
let runtimeVersion = hb_version_string_runtime();
print_endline(
"** Harfbuzz CLI, compiled version: "
++ compiledVersion
++ ", runtime version: "
++ runtimeVersion
++ " **\n",
);
print_endline("__ Font Path: Roboto Regular __");
let result = hb_face_from_path("test/collateral/FiraCode-Regular.ttf");
let features = [
Harfbuzz.{tag: "dlig", value: 1, start: `Start, stop: `End},
];
switch (result) {
| Error(msg) => failwith(msg)
| Ok(font) =>
renderString(font, "abc");
renderString(font, "Harfbuzz");
renderString(font, "ff");
renderString(~features, font, "ff");
renderString(font, "κόσμε");
renderString(font, "\t");
};
};
run();
================================================
FILE: packages/reason-harfbuzz/examples/harfbuzz-cli/dune
================================================
(executable
(name HarfbuzzCli)
(package ReveryExamples)
(public_name HarfbuzzCli)
(libraries bigarray harfbuzz reason-native-crash-utils.asan))
(install
(section bin)
(package ReveryExamples)
(files run-harfbuzz.sh))
================================================
FILE: packages/reason-harfbuzz/examples/harfbuzz-cli/run-harfbuzz.sh
================================================
set -e
CWD=$(dirname $0)
if [[ $1 == 'windows' ]]; then executable="HarfbuzzCli.exe"; else executable="HarfbuzzCli"; fi
LSAN_OPTIONS=suppressions=lsan.supp $CWD/$executable
================================================
FILE: packages/reason-harfbuzz/src/Harfbuzz.re
================================================
type hb_shape = {
glyphId: int,
cluster: int,
};
module Internal = {
type face;
type feature = {
tag: string,
value: int,
start: int,
stop: int,
};
external hb_face_from_path: string => result(face, string) =
"rehb_face_from_path";
external hb_face_from_data: (string, int) => result(face, string) =
"rehb_face_from_bytes";
external hb_destroy_face: face => unit = "rehb_destroy_face";
external hb_shape:
(face, string, array(feature), int, int) => array(hb_shape) =
"rehb_shape";
// hb-version
external hb_version_string_compiled: unit => string =
"rehb_version_string_compiled";
external hb_version_string_runtime: unit => string =
"rehb_version_string_runtime";
};
type position = [ | `Start | `End | `Position(int)];
type feature = {
tag: string,
value: int,
start: position,
stop: position,
};
type hb_face = {face: Internal.face};
let positionToInt = position =>
switch (position) {
| `Position(n) => n
| `Start => 0
| `End => (-1)
};
let hb_face_from_path = str => {
switch (Internal.hb_face_from_path(str)) {
| Error(msg) => Error(msg)
| Ok(face) =>
let ret = {face: face};
let finalise = ({face}) => Internal.hb_destroy_face(face);
Gc.finalise(finalise, ret);
Ok(ret);
};
};
let hb_shape = (~features=[], ~start=`Start, ~stop=`End, {face}, str) => {
let arr =
features
|> List.map(
feat => {
tag: feat.tag,
value: feat.value,
start: positionToInt(feat.start),
stop: positionToInt(feat.stop),
}: feature => Internal.feature,
)
|> Array.of_list;
let startPosition = positionToInt(start);
let length =
switch (stop) {
| `Position(n) => n - startPosition
| `Start => 0
| `End => (-1)
};
Internal.hb_shape(face, str, arr, startPosition, length);
};
let hb_new_face = str => hb_face_from_path(str);
let hb_face_from_data = bytes => {
switch (Internal.hb_face_from_data(bytes, String.length(bytes))) {
| Error(_) as e => e
| Ok(face) =>
let ret = {face: face};
let finalise = ({face}) => Internal.hb_destroy_face(face);
Gc.finalise(finalise, ret);
Ok(ret);
};
};
let hb_version_string_compiled = Internal.hb_version_string_compiled;
let hb_version_string_runtime = Internal.hb_version_string_runtime;
================================================
FILE: packages/reason-harfbuzz/src/Harfbuzz.rei
================================================
type hb_face;
type hb_shape = {
glyphId: int,
cluster: int,
};
type position = [ | `Start | `End | `Position(int)];
type feature = {
tag: string,
value: int,
start: position,
stop: position,
};
let hb_face_from_path: string => result(hb_face, string);
let hb_face_from_data: string => result(hb_face, string);
[@ocaml.deprecated "Deprecated in favor of hb_face_from_path"]
let hb_new_face: string => result(hb_face, string);
let hb_shape:
(
~features: list(feature)=?,
~start: position=?,
~stop: position=?,
hb_face,
string
) =>
array(hb_shape);
let hb_version_string_compiled: unit => string;
let hb_version_string_runtime: unit => string;
================================================
FILE: packages/reason-harfbuzz/src/config/discover.re
================================================
module Configurator = Configurator.V1;
type os =
| Android
| IOS
| Linux
| Mac
| Windows;
let detect_system_header = {|
#if __APPLE__
#include
#if TARGET_OS_IPHONE
#define PLATFORM_NAME "ios"
#else
#define PLATFORM_NAME "mac"
#endif
#elif __linux__
#if __ANDROID__
#define PLATFORM_NAME "android"
#else
#define PLATFORM_NAME "linux"
#endif
#elif WIN32
#define PLATFORM_NAME "windows"
#endif
|};
let get_os = t => {
let header = {
let file = Filename.temp_file("discover", "os.h");
let fd = open_out(file);
output_string(fd, detect_system_header);
close_out(fd);
file;
};
let platform =
Configurator.C_define.import(
t,
~includes=[header],
[("PLATFORM_NAME", String)],
);
switch (platform) {
| [(_, String("android"))] => Android
| [(_, String("ios"))] => IOS
| [(_, String("linux"))] => Linux
| [(_, String("mac"))] => Mac
| [(_, String("windows"))] => Windows
| _ => failwith("Unknown operating system")
};
};
let c_flags = ["-fPIC", "-I", Sys.getenv("HARFBUZZ_INCLUDE_PATH")];
let ccopt = s => ["-ccopt", s];
let cclib = s => ["-cclib", s];
let lib_path_flags = [] @ ccopt("-L" ++ Sys.getenv("HARFBUZZ_LIB_PATH"));
let extraFlags = os =>
switch (os) {
| Windows => cclib("-lpthread")
| Linux => ccopt("-L/usr/lib")
| _ => []
};
let flags = os => [] @ cclib("-lharfbuzz") @ extraFlags(os) @ lib_path_flags;
let cxx_flags = os =>
switch (os) {
| Windows => c_flags @ ["-fno-exceptions", "-fno-rtti", "-lstdc++"]
| _ => c_flags
};
Configurator.main(~name="reason-harfbuzz", conf => {
let os = get_os(conf);
Configurator.Flags.write_sexp("c_flags.sexp", c_flags);
Configurator.Flags.write_sexp("cxx_flags.sexp", cxx_flags(os));
Configurator.Flags.write_sexp("flags.sexp", flags(os));
});
================================================
FILE: packages/reason-harfbuzz/src/config/dune
================================================
(executable
(name discover)
(libraries dune-configurator))
================================================
FILE: packages/reason-harfbuzz/src/dune
================================================
(library
(name harfbuzz)
(public_name reason-harfbuzz)
(foreign_stubs
(language c)
(flags
(:include c_flags.sexp)))
(foreign_stubs
(language cxx)
(names harfbuzz)
(flags
(:include cxx_flags.sexp)))
(library_flags
(:include flags.sexp)))
(rule
(targets c_flags.sexp cxx_flags.sexp flags.sexp)
(deps
(:discover config/discover.exe))
(action
(run %{discover})))
================================================
FILE: packages/reason-harfbuzz/src/harfbuzz.cpp
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* #define TEST_FONT "E:/FiraCode-Regular.ttf" */
/* #define TEST_FONT "E:/Hasklig-Medium.otf" */
/* #define TEST_FONT "E:/Cassandra.ttf" */
/* #define TEST_FONT "E:/Lato-Regular.ttf" */
extern "C" {
// hb_font_t*
CAMLprim value Val_success(value v) {
CAMLparam1(v);
CAMLlocal1(some);
some = caml_alloc(1, 0);
Store_field(some, 0, v);
CAMLreturn(some);
}
CAMLprim value Val_error(const char *szMsg) {
CAMLparam0();
CAMLlocal1(error);
error = caml_alloc(1, 1);
Store_field(error, 0, caml_copy_string(szMsg));
CAMLreturn(error);
}
/* Use native open type implementation to load font
https://github.com/harfbuzz/harfbuzz/issues/255 */
hb_font_t *get_font_ot(char *data, int length, int size) {
hb_blob_t *blob =
hb_blob_create(data, length, HB_MEMORY_MODE_WRITABLE, (void *)data, free);
hb_face_t *face = hb_face_create(blob, 0);
hb_blob_destroy(blob); // face will keep a reference to blob
hb_font_t *font = hb_font_create(face);
hb_face_destroy(face); // font will keep a reference to face
hb_ot_font_set_funcs(font);
hb_font_set_scale(font, size, size);
return font;
}
CAMLprim value rehb_destroy_face(value vFont) {
CAMLparam1(vFont);
hb_font_t *pFont = (hb_font_t*)vFont;
if (pFont) {
hb_font_destroy(pFont);
}
CAMLreturn(Val_unit);
}
CAMLprim value rehb_face_from_path(value vString) {
CAMLparam1(vString);
CAMLlocal1(ret);
const char *szFont = String_val(vString);
FILE *file = fopen(szFont, "rb");
if (!file) {
CAMLreturn(Val_error("File does not exist"));
}
fseek(file, 0, SEEK_END);
unsigned int length = ftell(file);
fseek(file, 0, SEEK_SET);
char *data = (char *)malloc(length);
fread(data, length, 1, file);
fclose(file);
hb_font_t *hb_font;
hb_font = get_font_ot(data, length, 12 /*iSize*/ * 64);
if (!hb_font) {
ret = Val_error("Unable to load font");
} else {
ret = Val_success((value)hb_font);
}
CAMLreturn(ret);
}
CAMLprim value rehb_face_from_bytes(value vPtr, value vLength) {
CAMLparam2(vPtr, vLength);
CAMLlocal1(ret);
char *caml_data = Bp_val(vPtr);
int length = Int_val(vLength);
char *data = (char *)malloc(length * sizeof(char));
std::memcpy(data, caml_data, length);
hb_font_t *hb_font;
hb_font = get_font_ot(data, length, 12 /*iSize*/ * 64);
if (!hb_font) {
ret = Val_error("Unable to load font");
} else {
ret = Val_success((value)hb_font);
}
CAMLreturn(ret);
}
static value createShapeTuple(unsigned int codepoint, unsigned int cluster) {
CAMLparam0();
CAMLlocal1(ret);
ret = caml_alloc(2, 0);
Store_field(ret, 0, Val_int(codepoint));
Store_field(ret, 1, Val_int(cluster));
CAMLreturn(ret);
}
CAMLprim value rehb_shape(value vFace, value vString, value vFeatures, value vStart, value vLen) {
CAMLparam5(vFace, vString, vFeatures, vStart, vLen);
CAMLlocal2(ret, feat);
int start = Int_val(vStart);
int len = Int_val(vLen);
int featuresLen = Wosize_val(vFeatures);
hb_feature_t *features = (hb_feature_t *)malloc(featuresLen * sizeof(hb_feature_t));
for (int i = 0; i < featuresLen; i++) {
feat = Field(vFeatures, i);
const char *tag = String_val(Field(feat, 0));
features[i].tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
features[i].value = Int_val(Field(feat, 1));
features[i].start = Int_val(Field(feat, 2));
features[i].end = Int_val(Field(feat, 3));
}
hb_font_t *hb_font = (hb_font_t *)vFace;
hb_buffer_t *hb_buffer;
hb_buffer = hb_buffer_create();
hb_buffer_add_utf8(hb_buffer, String_val(vString), -1, start, len);
hb_buffer_guess_segment_properties(hb_buffer);
hb_shape(hb_font, hb_buffer, features, featuresLen);
unsigned int glyph_count;
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, &glyph_count);
ret = caml_alloc(glyph_count, 0);
for (int i = 0; i < glyph_count; i++) {
Store_field(ret, i, createShapeTuple(info[i].codepoint, info[i].cluster));
}
free(features);
hb_buffer_destroy(hb_buffer);
CAMLreturn(ret);
}
CAMLprim value rehb_version_string_compiled() {
CAMLparam0();
CAMLlocal1(ret);
ret = caml_copy_string(HB_VERSION_STRING);
CAMLreturn(ret);
}
CAMLprim value rehb_version_string_runtime() {
CAMLparam0();
CAMLlocal1(ret);
ret = caml_copy_string(hb_version_string());
CAMLreturn(ret);
}
}
================================================
FILE: packages/reason-harfbuzz/test/FeaturesTest.re
================================================
open Harfbuzz;
open TestFramework;
describe("Features", ({test, _}) => {
test("default (ff)", ({expect, _}) => {
let expectedResult = [|
{glyphId: 74, cluster: 0},
{glyphId: 74, cluster: 1},
|];
let shapes = hb_shape(font, "ff");
expect.equal(expectedResult, shapes);
});
test("default (fi)", ({expect, _}) => {
let expectedResult = [|{glyphId: 444, cluster: 0}|];
let shapes = hb_shape(font, "fi");
expect.equal(expectedResult, shapes);
});
test("discretionary ligatures enabled (ff)", ({expect, _}) => {
let expectedResult = [|{glyphId: 443, cluster: 0}|];
let features = [{tag: "dlig", value: 1, start: `Start, stop: `End}];
let shapes = hb_shape(~features, font, "ff");
expect.equal(shapes, expectedResult);
});
test("standard ligatures disabled (fi)", ({expect, _}) => {
let expectedResult = [|
{glyphId: 74, cluster: 0},
{glyphId: 77, cluster: 1},
|];
let features = [{tag: "liga", value: 0, start: `Start, stop: `End}];
let shapes = hb_shape(~features, font, "fi");
expect.equal(shapes, expectedResult);
});
});
================================================
FILE: packages/reason-harfbuzz/test/ShapingTest.re
================================================
open Harfbuzz;
open TestFramework;
describe("Shaping", ({test, _}) => {
test("whole string", ({expect, _}) => {
let expectedResult = [|
{glyphId: 69, cluster: 0},
{glyphId: 70, cluster: 1},
{glyphId: 71, cluster: 2},
|];
let shapes = hb_shape(font, "abc");
expect.equal(expectedResult, shapes);
});
test("substring", ({expect, _}) => {
let expectedResult = [|{glyphId: 70, cluster: 1}|];
let shapes =
hb_shape(font, "abc", ~start=`Position(1), ~stop=`Position(2));
expect.equal(expectedResult, shapes);
});
test("substring UTF-8", ({expect, _}) => {
let expectedResult = [|{glyphId: 1007, cluster: 1}|];
let shapes =
hb_shape(font, "aҙc", ~start=`Position(1), ~stop=`Position(3));
expect.equal(expectedResult, shapes);
});
});
================================================
FILE: packages/reason-harfbuzz/test/TestFramework.re
================================================
include Rely.Make({
let config =
Rely.TestFrameworkConfig.initialize({
snapshotDir: "./__snapshots__",
projectDir: "",
});
});
let font =
Harfbuzz.hb_face_from_path("./examples/Roboto-Regular.ttf") |> Result.get_ok;
================================================
FILE: packages/reason-harfbuzz/test/dune
================================================
(library
(name Harfbuzz_Test)
(ocamlopt_flags -linkall -g)
(libraries rely.lib rely.internal reason-harfbuzz))
================================================
FILE: packages/reason-sdl2/src/Float32Array.re
================================================
type t = Bigarray.Array1.t(float, Bigarray.float32_elt, Bigarray.c_layout);
let of_array = array =>
Bigarray.Array1.of_array(Bigarray.Float32, Bigarray.C_layout, array);
================================================
FILE: packages/reason-sdl2/src/Uint16Array.re
================================================
type t =
Bigarray.Array1.t(int, Bigarray.int16_unsigned_elt, Bigarray.c_layout);
let of_array = array =>
Bigarray.Array1.of_array(Bigarray.Int16_unsigned, Bigarray.C_layout, array);
================================================
FILE: packages/reason-sdl2/src/config/discover.re
================================================
module Configurator = Configurator.V1;
type os =
| Android
| IOS
| Linux
| Mac
| Windows;
let detect_system_header = {|
#if __APPLE__
#include
#if TARGET_OS_IPHONE
#define PLATFORM_NAME "ios"
#else
#define PLATFORM_NAME "mac"
#endif
#elif __linux__
#if __ANDROID__
#define PLATFORM_NAME "android"
#else
#define PLATFORM_NAME "linux"
#endif
#elif WIN32
#define PLATFORM_NAME "windows"
#endif
|};
let get_os = t => {
let header = {
let file = Filename.temp_file("discover", "os.h");
let fd = open_out(file);
output_string(fd, detect_system_header);
close_out(fd);
file;
};
let platform =
Configurator.C_define.import(
t,
~includes=[header],
[("PLATFORM_NAME", String)],
);
switch (platform) {
| [(_, String("android"))] => Android
| [(_, String("ios"))] => IOS
| [(_, String("linux"))] => Linux
| [(_, String("mac"))] => Mac
| [(_, String("windows"))] => Windows
| _ => failwith("Unknown operating system")
};
};
let root = Sys.getenv("cur__root");
let c_flags = [
"-I",
Sys.getenv("SDL2_INCLUDE_PATH"),
"-I",
Filename.concat(root, "include"),
"-I",
Filename.concat(root, "src"),
];
let c_flags = os =>
switch (os) {
| Android
| IOS
| Mac => c_flags
| Linux => c_flags @ ["-fPIC"]
| Windows => c_flags @ ["-mwindows"]
};
let libFolderPath = Sys.getenv("SDL2_LIB_PATH");
let libFilePath = libFolderPath ++ "/libSDL2.a";
prerr_endline("SDL2 Library Folder Path: " ++ libFolderPath);
let ccopt = s => ["-ccopt", s];
let cclib = s => ["-cclib", s];
let flags = os =>
switch (os) {
| Android =>
[]
@ ccopt(libFilePath)
@ cclib("-lEGL")
@ cclib("-lGLESv1_CM")
@ cclib("-lGLESv2")
| IOS =>
[]
@ ccopt(libFilePath)
@ ccopt("-framework Foundation")
@ ccopt("-framework OpenGLES")
@ ccopt("-framework UIKit")
@ ccopt("-framework IOKit")
@ ccopt("-framework CoreVideo")
@ ccopt("-framework CoreAudio")
@ ccopt("-framework AudioToolbox")
@ ccopt("-framework Metal")
@ ccopt("-liconv")
| Linux =>
[]
@ cclib("-lGL")
@ cclib("-lGLU")
@ ccopt(libFilePath)
@ cclib("-lX11")
@ cclib("-lXxf86vm")
@ cclib("-lXrandr")
@ cclib("-lXinerama")
@ cclib("-lXcursor")
@ cclib("-lpthread")
@ cclib("-lXi")
| Mac =>
[]
@ ccopt(libFilePath)
@ ccopt("-framework AppKit")
@ ccopt("-framework Foundation")
@ ccopt("-framework OpenGL")
@ ccopt("-framework Cocoa")
@ ccopt("-framework IOKit")
@ ccopt("-framework CoreVideo")
@ ccopt("-framework CoreAudio")
@ ccopt("-framework AudioToolbox")
@ ccopt("-framework ForceFeedback")
@ ccopt("-framework Metal")
@ ccopt("-framework Carbon")
@ ccopt("-liconv")
| Windows =>
let maybeAngleLibPath = Sys.getenv_opt("ANGLE_LIB_PATH");
let angleLibPath =
switch (maybeAngleLibPath) {
| None =>
prerr_endline("Unable to get libraries for esy-angle-prebuilt");
failwith("Unable to get libraries for esy-angle-prebuilt)");
| Some(path) =>
print_endline("ANGLE Library Path: " ++ path);
path;
};
let eglPath = angleLibPath ++ "/libEGL.a";
let glesv2Path = angleLibPath ++ "/libGLESv2.a";
[]
// On Windows, we ship the DLL side-by-side
@ ccopt("-L" ++ libFolderPath)
@ cclib("-lSDL2")
@ ccopt(eglPath)
@ ccopt(glesv2Path)
// We use the ANGLE DLLs (to use Direct3D apis instead of OpenGL)
@ cclib("-lgdi32")
@ cclib("-subsystem windows");
};
let c_library_flags = os =>
switch (os) {
| Android
| IOS
| Mac
| Linux => [libFilePath]
| Windows => ["-L" ++ libFolderPath, "-lSDL2"]
};
let cxx_flags = os =>
switch (os) {
| Android
| Linux => c_flags(os) @ ["-std=c++11"]
| IOS
| Mac => c_flags(os) @ ["-x", "objective-c++"]
| Windows =>
c_flags(os) @ ["-fno-exceptions", "-fno-rtti", "-lstdc++", "-mwindows"]
};
Configurator.main(~name="reason-sdl2", conf => {
let os = get_os(conf);
Configurator.Flags.write_sexp("c_library_flags.sexp", c_library_flags(os));
Configurator.Flags.write_sexp("c_flags.sexp", c_flags(os));
Configurator.Flags.write_sexp("cxx_flags.sexp", cxx_flags(os));
Configurator.Flags.write_sexp("flags.sexp", flags(os));
});
================================================
FILE: packages/reason-sdl2/src/config/dune
================================================
(executable
(name discover)
(libraries dune.configurator))
================================================
FILE: packages/reason-sdl2/src/dune
================================================
(library
(name sdl2)
(public_name reason-sdl2)
(library_flags
(:include flags.sexp))
(c_library_flags
(:include c_library_flags.sexp))
(foreign_stubs
(language c)
(flags
(:include c_flags.sexp)))
(foreign_stubs
(language cxx)
(names sdl2_wrapper stb_image)
(flags
(:include cxx_flags.sexp))))
(rule
(targets c_flags.sexp cxx_flags.sexp flags.sexp c_library_flags.sexp)
(deps
(:discover config/discover.exe))
(action
(run %{discover})))
================================================
FILE: packages/reason-sdl2/src/sdl2.re
================================================
module Float32Array = Float32Array;
module Uint16Array = Uint16Array;
module Size = {
type t = {
width: int,
height: int,
};
};
module Rect = {
type t = {
x: int,
y: int,
width: int,
height: int,
};
let toString = ({x, y, width, height}) =>
Printf.sprintf("x: %d y: %d width: %d height: %d", x, y, width, height);
};
module ScreenSaver = {
external enable: unit => unit = "resdl_SDL_EnableScreenSaver";
external disable: unit => unit = "resdl_SDL_DisableScreenSaver";
external isEnabled: unit => bool = "resdl_SDL_IsScreenSaverEnabled";
};
module Surface = {
type t;
external createFromImagePath: string => result(t, string) =
"resdl_SDL_CreateRGBSurfaceFromImage";
};
module Audio = {
module Format = {
type t =
| S8
| U8
| S16LSB
| S16MSB
| U16LSB
| U16MSB
| S32LSB
| S32MSB
| F32LSB
| F32MSB;
};
module Buffer = {
type t;
};
module Spec = {
type t = {
freq: int,
format: Format.t,
channels: int,
silence: int,
samples: int,
padding: int,
size: int,
};
};
module Device = {
module Status = {
type t =
| Stopped
| Playing
| Paused;
};
module AllowedChanges: {
type t;
let frequency: t;
let format: t;
let channels: t;
let samples: t;
let any: t;
let none: t;
let (|||): (t, t) => t;
} = {
type t = int;
let frequency = 0x00000001;
let format = 0x00000002;
let channels = 0x00000004;
let samples = 0x00000008;
let (|||) = (lor);
let any = frequency lor format lor channels lor samples;
let none = 0;
};
type t;
external open_:
(option(string), bool, Spec.t, AllowedChanges.t) =>
result((t, Spec.t), string) =
"resdl_SDL_OpenAudioDevice";
external close: t => unit = "resdl_SDL_CloseAudioDevice";
external getStatus: t => Status.t = "resdl_SDL_GetAudioDeviceStatus";
external pause: (t, bool) => unit = "resdl_SDL_PauseAudioDevice";
};
module Wav = {
external load: string => result((Spec.t, Buffer.t, int), string) =
"resdl_SDL_LoadWAV";
};
external queue: (Device.t, Buffer.t, int) => result(unit, string) =
"resdl_SDL_QueueAudio";
external clearQueued: Device.t => unit = "resdl_SDL_ClearQueuedAudio";
external getQueuedSize: Device.t => int = "resdl_SDL_GetQueuedAudioSize";
};
module Clipboard = {
external getText: unit => option(string) = "resdl_SDL_GetClipboardText";
external setText: string => unit = "resdl_SDL_SetClipboardText";
external hasText: unit => bool = "resdl_SDL_HasClipboardText";
};
module PixelFormat = {
type t;
external toString: t => string = "resdl_SDL_GetPixelFormatName";
};
module Display = {
type t;
module Dpi = {
type t = {
ddpi: float,
hdpi: float,
vdpi: float,
};
let show = (v: t) => {
Printf.sprintf("ddpi: %f hdpi: %f vdpi: %f", v.ddpi, v.hdpi, v.vdpi);
};
};
module Mode = {
type t = {
pixelFormat: PixelFormat.t,
width: int,
height: int,
refreshRate: int,
};
let show = (v: t) => {
Printf.sprintf(
"pixelFormat: %s width: %d height: %d refreshRate: %d",
v.pixelFormat |> PixelFormat.toString,
v.width,
v.height,
v.refreshRate,
);
};
};
external getNumberOfDisplays: unit => int = "resdl_SDL_GetNumVideoDisplays";
let getDisplays = () =>
List.init(getNumberOfDisplays(), (i) => (Obj.magic(i: int): t));
external getDPI: t => Dpi.t = "resdl_SDL_GetDisplayDPI";
external getCurrentMode: t => Mode.t = "resdl_SDL_GetCurrentDisplayMode";
external getDesktopMode: t => Mode.t = "resdl_SDL_GetDesktopDisplayMode";
external getName: t => string = "resdl_SDL_GetDisplayName";
external getBounds: t => Rect.t = "resdl_SDL_GetDisplayBounds";
external getUsableBounds: t => Rect.t = "resdl_SDL_GetDisplayUsableBounds";
};
module Log = {
type category =
| Application
| Error
| Assert
| System
| Audio
| Video
| Render
| Input
| Test
| Custom
| Unknown;
type priority =
| Verbose
| Debug
| Info
| Warn
| Error
| Critical;
type logOutputFunction = (category, priority, string) => unit;
let _outputFunction: ref(logOutputFunction) = ref((_, _, _) => ());
let setOutputFunction = outputFunction => {
_outputFunction := outputFunction;
};
let _onLog = (category, priority, str) => {
_outputFunction^(category, priority, str);
};
Callback.register("reason_sdl2_onLog", _onLog);
};
module Platform = {
external getName: unit => string = "resdl_SDL_GetPlatform";
external getVersion: unit => string = "resdl_SDL_GetVersion";
// Windows only
external win32AllocConsole: unit => int = "resdl_SDL_WinAllocConsole";
external win32AttachConsole: unit => int = "resdl_SDL_WinAttachConsole";
};
module Window = {
type t;
type hitTestResult =
| Normal
| Draggable
| ResizeTopLeft
| ResizeTop
| ResizeTopRight
| ResizeRight
| ResizeBottomRight
| ResizeBottom
| ResizeBottomLeft
| ResizeLeft;
type hitTestCallback = (t, int, int) => hitTestResult;
external create:
(
string,
[ | `Undefined | `Centered | `Absolute(int)],
[ | `Undefined | `Centered | `Absolute(int)],
int,
int,
[ | `Auto | `ForceHardware | `ForceSoftware]
) =>
t =
"resdl_SDL_CreateWindow_byte" "resdl_SDL_CreateWindow";
external getId: t => int = "resdl_SDL_GetWindowId";
external getSize: t => Size.t = "resdl_SDL_GetWindowSize";
external getPosition: t => (int, int) = "resdl_SDL_GetWindowPosition";
external setBordered: (t, bool) => unit = "resdl_SDL_SetWindowBordered";
external getPixelFormat: t => PixelFormat.t =
"resdl_SDL_GetWindowPixelFormat";
external setIcon: (t, Surface.t) => unit = "resdl_SDL_SetWindowIcon";
external setTransparency: (t, float) => unit =
"resdl_SDL_SetWindowTransparency";
external setPosition: (t, int, int) => unit = "resdl_SDL_SetWindowPosition";
external center: t => unit = "resdl_SDL_WindowCenter";
external setResizable: (t, bool) => unit = "resdl_SDL_SetWindowResizable";
external setSize: (t, int, int) => unit = "resdl_SDL_SetWindowSize";
external setTitle: (t, string) => unit = "resdl_SDL_SetWindowTitle";
external setMinimumSize: (t, int, int) => unit =
"resdl_SDL_SetWindowMinimumSize";
external _enableHitTest: t => unit = "resdl_SDL_EnableHitTest";
external _disableHitTest: t => unit = "resdl_SDL_EnableHitTest";
let _idToHitTest: Hashtbl.t(int, hitTestCallback) = Hashtbl.create(16);
let setHitTest = (win: t, cb: option(hitTestCallback)) => {
switch (cb) {
| None => _disableHitTest(win)
| Some(v) =>
_enableHitTest(win);
Hashtbl.add(_idToHitTest, getId(win), v);
};
};
let _hitTest = (win: t, x: int, y: int) => {
let id = getId(win);
switch (Hashtbl.find_opt(_idToHitTest, id)) {
| Some(v) => v(win, x, y)
| None => Normal
};
};
Callback.register("__sdl2_caml_hittest__", _hitTest);
external hide: t => unit = "resdl_SDL_HideWindow";
external raise: t => unit = "resdl_SDL_RaiseWindow";
external show: t => unit = "resdl_SDL_ShowWindow";
external minimize: t => unit = "resdl_SDL_MinimizeWindow";
external restore: t => unit = "resdl_SDL_RestoreWindow";
external maximize: t => unit = "resdl_SDL_MaximizeWindow";
external isMaximized: t => bool = "resdl_SDL_IsWindowMaximized";
external isFullscreen: t => bool = "resdl_SDL_IsWindowFullscreen";
external getDisplay: t => Display.t = "resdl_SDL_GetWindowDisplayIndex";
// Windows-Only: Set DPI Aware process flag
// Other platforms: no-op
external setWin32ProcessDPIAware: t => unit =
"resdl_SDL_SetWin32ProcessDPIAware";
// WINDOWS-ONLY: Get the monitor scale factor for the window
// Other platforms: Always returns 1.0
external getWin32ScaleFactor: t => float = "resdl_SDL_GetWin32ScaleFactor";
type nativeWindow;
external getNativeWindow: t => nativeWindow = "resdl_SDL_GetNativeWindow";
// MacOS-Only
external setMacTitlebarTransparent: t => unit =
"resdl_SDL_SetMacTitlebarTransparent";
external setMacTitlebarHidden: t => unit = "resdl_SDL_SetMacTitlebarHidden";
external setMacBackgroundColor: (t, float, float, float, float) => unit =
"resdl_SDL_SetMacBackgroundColor";
external getMacTitlebarHeight: t => float = "resdl_SDL_GetMacTitlebarHeight";
};
module Gl = {
type context;
type glString =
| Vendor
| Renderer
| Version
| ShadingLanguageVersion;
type glInt =
| FramebufferBinding;
external setup: Window.t => context = "resdl_SDL_GL_Setup";
external makeCurrent: (Window.t, context) => unit =
"resdl_SDL_GL_MakeCurrent";
external swapWindow: Window.t => unit = "resdl_SDL_GL_SwapWindow";
external getDrawableSize: Window.t => Size.t =
"resdl_SDL_GL_GetDrawableSize";
external setSwapInterval: int => unit = "resdl_SDL_GL_SetSwapInterval";
external getString: glString => string = "resdl_SDL_GL_GetString";
external getFramebufferBinding: unit => int =
"resdl_SDL_GL_GetFramebufferBinding";
};
external delay: int => unit = "resdl_SDL_Delay";
external init: unit => int = "resdl_SDL_Init";
external main: (int, array(string), unit => unit) => unit = "resdl_SDL_main";
let main = cb => main(Array.length(Sys.argv), Sys.argv, cb);
module TextInput = {
[@noalloc] external start: unit => unit = "resdl_SDL_StartTextInput";
[@noalloc] external stop: unit => unit = "resdl_SDL_StopTextInput";
[@noalloc]
external setInputRect: (int, int, int, int) => unit =
"resdl_SDL_SetTextInputRect";
[@noalloc] external isActive: unit => bool = "resdl_SDL_IsTextInputActive";
};
module MouseButton = {
type t =
| Left
| Middle
| Right
| X1
| X2;
let show = v =>
switch (v) {
| Left => "Left"
| Middle => "Middle"
| Right => "Right"
| X1 => "X1"
| X2 => "X2"
};
};
module Mouse = {
external capture: bool => int = "resdl_SDL_CaptureMouse";
external getGlobalPosition: unit => (int, int) =
"resdl_SDL_GetGlobalMouseState";
};
module Scancode = {
type t = int;
external getName: t => string = "resdl_SDL_GetScancodeName";
external ofName: string => t = "resdl_SDL_GetScancodeFromName";
[@noalloc] external ofInt: int => t = "resdl_PassThrough";
[@noalloc] external toInt: t => int = "resdl_PassThrough";
// Incrementally add these as needed from:
// https://wiki.libsdl.org/SDLScancodeLookup
let unknown = 0;
};
module Keycode = {
type t = int;
// as some keycodes are bigger than 2^30 but still less than 2^31
// so by using the negative numbers we can fit them in a normal int
let int = Int32.to_int;
external getName: t => string = "resdl_SDL_GetKeyName";
external ofName: string => t = "resdl_SDL_GetKeyFromName";
[@noalloc]
external ofScancode: Scancode.t => t = "resdl_SDL_GetKeyFromScancode";
[@noalloc]
external toScancode: t => Scancode.t = "resdl_SDL_GetScancodeFromKey";
// Incrementally add these as needed from:
// https://wiki.libsdl.org/SDLKeycodeLookup
let unknown = int(0l);
let backspace = int(8l);
let return = int(13l);
let escape = int(27l);
let space = int(32l);
let left_paren = int(40l);
let right_paren = int(41l);
let asterisk = int(42l);
let plus = int(43l);
let minus = int(45l);
let period = int(46l);
let slash = int(47l);
let caret = int(94l);
let equals = int(61l);
let digit0 = int(48l);
let digit1 = int(49l);
let digit2 = int(50l);
let digit3 = int(51l);
let digit4 = int(52l);
let digit5 = int(53l);
let digit6 = int(54l);
let digit7 = int(55l);
let digit8 = int(56l);
let digit9 = int(57l);
let pad_divide = int(1073741908l);
let pad_multiply = int(1073741909l);
let pad_minus = int(1073741910l);
let pad_plus = int(1073741911l);
let pad_period = int(1073741923l);
let pad_equals = int(1073741927l);
let p_digit1 = int(1073741913l);
let p_digit2 = int(1073741914l);
let p_digit3 = int(1073741915l);
let p_digit4 = int(1073741916l);
let p_digit5 = int(1073741917l);
let p_digit6 = int(1073741918l);
let p_digit7 = int(1073741919l);
let p_digit8 = int(1073741920l);
let p_digit9 = int(1073741921l);
let p_digit0 = int(1073741922l);
let a = int(97l);
let b = int(98l);
let c = int(99l);
let d = int(100l);
let e = int(101l);
let f = int(102l);
let g = int(103l);
let h = int(104l);
let i = int(105l);
let j = int(106l);
let k = int(107l);
let l = int(108l);
let m = int(109l);
let n = int(110l);
let o = int(111l);
let p = int(112l);
let q = int(113l);
let r = int(114l);
let s = int(115l);
let t = int(116l);
let u = int(117l);
let v = int(118l);
let w = int(119l);
let x = int(120l);
let y = int(121l);
let z = int(122l);
let delete = int(127l);
let right = int(1073741903l);
let left = int(1073741904l);
};
module WheelType = {
type t =
| Last
| Undefined
| Touchscreen
| Touchpad
| Wheel
| WheelPrecise
| OtherNonKinetic
| OtherKinetic;
};
module Keymod = {
type t = int;
let none = 0;
[@noalloc] external isLeftShiftDown: t => bool = "resdl_SDL_ModLeftShift";
[@noalloc] external isRightShiftDown: t => bool = "resdl_SDL_ModRightShift";
let isShiftDown = v => isLeftShiftDown(v) || isRightShiftDown(v);
[@noalloc]
external isLeftControlDown: t => bool = "resdl_SDL_ModLeftControl";
[@noalloc]
external isRightControlDown: t => bool = "resdl_SDL_ModRightControl";
let isControlDown = v => isLeftControlDown(v) || isRightControlDown(v);
[@noalloc] external isLeftAltDown: t => bool = "resdl_SDL_ModLeftAlt";
[@noalloc] external isRightAltDown: t => bool = "resdl_SDL_ModRightAlt";
let isAltDown = v => isLeftAltDown(v) || isRightAltDown(v);
[@noalloc] external isLeftGuiDown: t => bool = "resdl_SDL_ModLeftGui";
[@noalloc] external isRightGuiDown: t => bool = "resdl_SDL_ModRightGui";
let isGuiDown = v => isLeftGuiDown(v) || isRightGuiDown(v);
[@noalloc] external isNumLockDown: t => bool = "resdl_SDL_ModNumLockDown";
[@noalloc] external isCapsLockDown: t => bool = "resdl_SDL_ModCapsLockDown";
[@noalloc] external isAltGrKeyDown: t => bool = "resdl_SDL_ModAltGrDown";
let show = (v: t) => {
let int_of_bool = b => b ? 1 : 0;
Printf.sprintf(
"Keymods - LSHIFT: %d RSHIFT: %d LCTRL: %d RCTRL: %d LALT: %d RALT: %d LGUI: %d RGUI: %d NUM: %d CAPS: %d ALTGR: %d",
int_of_bool(isLeftShiftDown(v)),
int_of_bool(isRightShiftDown(v)),
int_of_bool(isLeftControlDown(v)),
int_of_bool(isRightControlDown(v)),
int_of_bool(isLeftAltDown(v)),
int_of_bool(isRightAltDown(v)),
int_of_bool(isLeftGuiDown(v)),
int_of_bool(isRightGuiDown(v)),
int_of_bool(isNumLockDown(v)),
int_of_bool(isCapsLockDown(v)),
int_of_bool(isAltGrKeyDown(v)),
);
};
[@noalloc] external getState: unit => t = "resdl_SDL_GetModState";
[@noalloc] external setState: t => unit = "resdl_SDL_SetModState";
};
module Event = {
type mouseMotion = {
windowID: int,
x: int,
y: int,
};
type mouseWheel = {
windowID: int,
deltaX: int,
deltaY: int,
isFlipped: bool,
};
type mousePan = {
windowID: int,
deltaX: int,
deltaY: int,
containsX: bool,
containsY: bool,
isFling: bool,
isInterrupt: bool,
source: WheelType.t,
timestamp: int,
};
type mouseButtonEvent = {
windowID: int,
button: MouseButton.t,
clicks: int,
x: int,
y: int,
};
type keyboardEvent = {
windowID: int,
repeat: bool,
keymod: Keymod.t,
scancode: Scancode.t,
keycode: Keycode.t,
};
type textInputEvent = {
windowID: int,
text: string,
};
type textEditingEvent = {
windowID: int,
text: string,
start: int,
length: int,
};
type windowEvent = {windowID: int};
type windowMoveEvent = {
windowID: int,
x: int,
y: int,
};
type windowSizeEvent = {
windowID: int,
width: int,
height: int,
};
/* Drop events
dropNotificationEvent occurs when SDL is notifying when a drop
is about to/has taken place through DROPBEGIN & DROPCOMPLETE
dropEvent occurs when a file is actually being dropped
The main difference is that dropEvent includes a file/text path, whereas
dropNotificationEvent does not.
*/
type dropNotificationEvent = {
windowID: int,
timestamp: int,
x: int,
y: int,
};
type dropEvent = {
windowID: int,
file: string,
timestamp: int,
x: int,
y: int,
};
type t =
| Quit
| MouseMotion(mouseMotion) // 0
| MouseWheel(mouseWheel) // 1
| MouseButtonDown(mouseButtonEvent) // 2
| MouseButtonUp(mouseButtonEvent) // 3
| KeyDown(keyboardEvent) // 4
| KeyUp(keyboardEvent) // 5
| TextInput(textInputEvent) // 6
| TextEditing(textEditingEvent) // 7
| WindowShown(windowEvent) // 8
| WindowHidden(windowEvent) // 9
| WindowExposed(windowEvent) // 10
| WindowMoved(windowMoveEvent) // 11
| WindowResized(windowSizeEvent) // 12
| WindowSizeChanged(windowSizeEvent) // 13
| WindowMinimized(windowEvent) // 14
| WindowMaximized(windowEvent) // 15
| WindowRestored(windowEvent) // 16
| WindowEnter(windowEvent) // 17
| WindowLeave(windowEvent) // 18
| WindowFocusGained(windowEvent) // 19
| WindowFocusLost(windowEvent) // 20
| WindowClosed(windowEvent) // 21
| WindowTakeFocus(windowEvent) // 22
| WindowHitTest(windowEvent) // 23
| MousePan(mousePan) // 24
| DropText(dropEvent) // 25
| DropFile(dropEvent) // 26
| DropBegin(dropNotificationEvent) // 27
| DropComplete(dropNotificationEvent) // 28
| WindowFullscreen(windowEvent) // 29
// An event that hasn't been implemented yet
| Unknown
| KeymapChanged;
let show = (v: t) => {
switch (v) {
| Quit => "Quit"
| MouseMotion({windowID, x, y}) =>
Printf.sprintf("MouseMotion windowId: %d x: %d y: %d", windowID, x, y)
| MouseWheel({windowID, deltaX, deltaY, isFlipped}) =>
Printf.sprintf(
"MouseWheel windowId: %d x: %d y: %d isFlipped: %d",
windowID,
deltaX,
deltaY,
isFlipped ? 1 : 0,
)
| MousePan({
windowID,
deltaX,
deltaY,
containsX,
containsY,
isFling,
isInterrupt,
_,
}) =>
Printf.sprintf(
"Pan event: %d %d %d %d %d %d %d",
windowID,
deltaX,
deltaY,
if (containsX) {1} else {0},
if (containsY) {1} else {0},
if (isFling) {1} else {0},
if (isInterrupt) {1} else {0},
)
| MouseButtonUp({windowID, button, _}) =>
Printf.sprintf(
"MouseButtonUp windowId: %d button: %s",
windowID,
MouseButton.show(button),
)
| MouseButtonDown({windowID, button, _}) =>
Printf.sprintf(
"MouseButtonDown windowId: %d button: %s",
windowID,
MouseButton.show(button),
)
| KeyDown({repeat, keymod, scancode, keycode, _}) =>
Printf.sprintf(
"KeyDown repeat %d:\n -- %s\n -- Scancode: %s\n -- Keycode: %s\n",
repeat ? 1 : 0,
Keymod.show(keymod),
Scancode.getName(scancode),
Keycode.getName(keycode),
)
| KeyUp({repeat, keymod, scancode, keycode, _}) =>
Printf.sprintf(
"KeyUp repeat %d:\n -- %s\n -- Scancode: %s\n -- Keycode: %s\n",
repeat ? 1 : 0,
Keymod.show(keymod),
Scancode.getName(scancode),
Keycode.getName(keycode),
)
| TextEditing({text, start, length, _}) =>
Printf.sprintf(
"TextEditing:\n -- start: %d length: %d text: %s\n",
start,
length,
text,
)
| TextInput({text, _}) =>
Printf.sprintf("TextInput:\n -- text: %s\n", text)
| WindowShown({windowID}) =>
Printf.sprintf("WindowShown: %d\n", windowID)
| WindowHidden({windowID}) =>
Printf.sprintf("WindowHidden: %d\n", windowID)
| WindowExposed({windowID}) =>
Printf.sprintf("WindowExposed: %d\n", windowID)
| WindowMoved({windowID, x, y}) =>
Printf.sprintf(
"WindowMoved - windowID: %d x: %d y: %d\n",
windowID,
x,
y,
)
| WindowResized({windowID, width, height}) =>
Printf.sprintf(
"WindowResized - windowID: %d x: %d y: %d\n",
windowID,
width,
height,
)
| WindowSizeChanged({windowID, width, height}) =>
Printf.sprintf(
"WindowSizeChanged - windowID: %d x: %d y: %d\n",
windowID,
width,
height,
)
| WindowMinimized({windowID}) =>
Printf.sprintf("WindowMinimized: %d\n", windowID)
| WindowMaximized({windowID}) =>
Printf.sprintf("WindowMaximized: %d\n", windowID)
| WindowFullscreen({windowID}) =>
Printf.sprintf("WindowFullscreen: %d\n", windowID)
| WindowRestored({windowID}) =>
Printf.sprintf("WindowRestored: %d\n", windowID)
| WindowEnter({windowID}) =>
Printf.sprintf("WindowEnter: %d\n", windowID)
| WindowLeave({windowID}) =>
Printf.sprintf("WindowLeave: %d\n", windowID)
| WindowFocusGained({windowID}) =>
Printf.sprintf("WindowFocusGained: %d\n", windowID)
| WindowFocusLost({windowID}) =>
Printf.sprintf("WindowFocusLost: %d\n", windowID)
| WindowClosed({windowID}) =>
Printf.sprintf("WindowClosed: %d\n", windowID)
| WindowTakeFocus({windowID}) =>
Printf.sprintf("WindowTakeFocus: %d\n", windowID)
| WindowHitTest({windowID}) =>
Printf.sprintf("WindowHitTest: %d\n", windowID)
| DropText({windowID, file, x, y, _}) =>
Printf.sprintf(
"DropText - windowID: %d file: %s x: %d y: %d\n",
windowID,
file,
x,
y,
)
| DropFile({windowID, file, x, y, _}) =>
Printf.sprintf(
"DropFile - windowID: %d file: %s x: %d y: %d\n",
windowID,
file,
x,
y,
)
| DropBegin({windowID, x, y, _}) =>
Printf.sprintf("DropBegin - windowID: %d x: %d y: %d\n", windowID, x, y)
| DropComplete({windowID, x, y, _}) =>
Printf.sprintf(
"DropComplete - windowID: %d x: %d y: %d\n",
windowID,
x,
y,
)
| KeymapChanged => "KeymapChanged"
| Unknown => "Unknown"
};
};
external poll: unit => option(t) = "resdl_SDL_PollEvent";
external push: unit => unit = "resdl_SDL_PushEvent";
external wait: unit => result(t, string) = "resdl_SDL_WaitEvent";
external waitTimeout: int => option(t) = "resdl_SDL_WaitTimeoutEvent";
};
module Cursor = {
type systemCursor =
| Arrow
| IBeam
| Wait
| Crosshair
| WaitArrow
| SizeNWSE
| SizeNESW
| SizeWE
| SizeNS
| SizeAll
| No
| Hand;
type t;
external createSystem: systemCursor => t = "resdl_SDL_CreateSystemCursor";
external set: t => unit = "resdl_SDL_SetCursor";
};
module MessageBox = {
type flags =
| Error
| Warning
| Information;
external showSimple: (flags, string, string, option(Window.t)) => unit =
"resdl_SDL_ShowSimpleMessageBox";
};
module Timekeeping = {
external getTicks: unit => int = "resdl_SDL_GetTicks";
};
module Version = {
type t = {
major: int,
minor: int,
patch: int,
};
external getCompiled: unit => t = "resdl_SDL_GetCompiledVersion";
external getLinked: unit => t = "resdl_SDL_GetLinkedVersion";
let toString: t => string =
({major, minor, patch}) =>
Printf.sprintf("Major: %d Minor: %d Patch: %d", major, minor, patch);
};
type renderFunction = unit => bool;
external _javaScriptRenderLoop: renderFunction => unit =
"resdl__javascript__renderloop";
external _javaScriptRenderLoop: renderFunction => unit =
"resdl__javascript__renderloop";
let _nativeLoop = renderFn => {
while (!renderFn()) {
();
/*Thread.yield();*/
};
();
};
let renderLoop = (renderFunction: renderFunction) => {
Callback.register("__sdl2_caml_resize__", renderFunction);
switch (Sys.backend_type) {
| Native => _nativeLoop(renderFunction)
| Bytecode => _nativeLoop(renderFunction)
| _ => _javaScriptRenderLoop(renderFunction)
};
};
================================================
FILE: packages/reason-sdl2/src/sdl2_stubs.js
================================================
//Provides: resdl_SDL_Init
function resdl_SDL_Init() {
joo_global_object._time = {
start: Date.now(),
offset: 0,
};
joo_global_object._events = [];
joo_global_object._popEvent = function () {
if (joo_global_object._events.length > 0) {
return joo_global_object._events.shift();
} else {
return null;
}
};
joo_global_object._pushEvent = function (evt) {
joo_global_object._events.push(evt);
};
joo_global_object.window.addEventListener("resize", function () {
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
wins[i]._notifyResize();
}
});
joo_global_object._mouseState = {};
joo_global_object.window.addEventListener("mousemove", function (e) {
var activeWindow = joo_global_object._activeWindow;
joo_global_object._mouseState.x = e.pageX - activeWindow.x;
joo_global_object._mouseState.y = e.pageY - activeWindow.y;
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
var win = wins[i];
win._notifyMouseMove(e.pageX - win.x, e.pageY - win.y);
}
});
joo_global_object.window.addEventListener("wheel", function (e) {
// TODO: account for deltaMode
var deltaX = e.deltaX;
var deltaY = e.deltaY;
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
wins[i]._notifyScroll(deltaX, deltaY);
}
});
joo_global_object.window.addEventListener("keypress", function (keyEvent) {
if (keyEvent.key && keyEvent.key.length === 1) {
var codepoint = keyEvent.key.codePointAt(0);
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
wins[i]._notifyChar(codepoint);
}
}
});
joo_global_object.window.addEventListener("mousedown", function (mouseEvent) {
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
wins[i]._notifyMouseButton(mouseEvent, 2);
}
});
joo_global_object.window.addEventListener("mouseup", function (mouseEvent) {
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
wins[i]._notifyMouseButton(mouseEvent, 3);
}
});
joo_global_object.window.addEventListener("keydown", function (keyEvent) {
if (keyEvent.key) {
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
var pressMode = keyEvent.repeat ? 2 : 0;
wins[i]._notifyKey(keyEvent, pressMode);
}
}
});
joo_global_object.window.addEventListener("keyup", function (keyEvent) {
if (keyEvent.key) {
var wins = joo_global_object._activeWindows;
for (var i = 0; i < wins.length; i++) {
wins[i]._notifyKey(keyEvent, 1);
}
}
});
};
// Provides: caml_glfwGetCursorPos
// Requires: caml_js_to_array
function caml_glfwGetCursorPos(w) {
// TODO: Window parameter is currently ignored, but
// we should calculate the mouse position relative to it.
return caml_js_to_array([joo_global_object._mouseState.x, joo_global_object._mouseState.y]);
}
// Provides: resdl_SDL_CreateSystemCursor
function resdl_SDL_CreateSystemCursor(shape) {
switch (shape) {
case 0: return "default";
case 1: return "text";
case 3: return "crosshair";
case 4: return "wait";
case 5: return "nwse-resize";
case 6: return "nesw-resize";
case 7: return "we-resize";
case 8: return "ns-resize";
case 9: return "resize";
case 10: return "not-allowed";
case 11: return "pointer";
default:
joo_global_object.console.warn("Unsupported cursor shape.");
return "default";
}
}
// Provides: resdl_SDL_EnableScreenSaver
function resdl_SDL_EnableScreenSaver() {
// no op
}
// Provides: resdl_SDL_DisableScreenSaver
function resdl_SDL_DisableScreenSaver() {
// no op
}
// Provides: resdl_SDL_IsScreenSaverEnabled
function resdl_SDL_IsScreenSaverEnabled() {
return false;
}
// Provides: resdl_SDL_GL_SetSwapInterval
function resdl_SDL_GL_SetSwapInterval() {
// no op
}
// Provides: resdl_SDL_PushEvent
function resdl_SDL_PushEvent() {
// no op
}
// Provides: resdl_SDL_GL_MakeCurrent
function resdl_SDL_GL_MakeCurrent() {
// no op
}
// Provides: resdl_SDL_SetWin32ProcessDPIAware
function resdl_SDL_SetWin32ProcessDPIAware() {
// no op
}
// Provides: resdl_SDL_GetWin32ScaleFactor
function resdl_SDL_GetWin32ScaleFactor() {
return 1;
}
// Provides: resdl_SDL_GetWindowDisplayIndex
function resdl_SDL_GetWindowDisplayIndex() {
return 0;
}
// Provides: resdl_SDL_GetDisplayDPI
function resdl_SDL_GetDisplayDPI() {
return 96;
}
// Provides: resdl_SDL_GetDesktopDisplayMode
function resdl_SDL_GetDesktopDisplayMode() {
return [0, 100, 200, 0];
}
// Provides: resdl_SDL_WindowCenter
function resdl_SDL_WindowCenter() {
//no-op
}
// Provides: caml_glfwDestroyCursor
function caml_glfwDestroyCursor(cursor) {
// no op
}
// Provides: resdl_SDL_SetCursor
function resdl_SDL_SetCursor(cursor) {
joo_global_object._activeWindow.canvas.style.cursor = cursor;
}
// Provides: resdl_SDL_SetWindowMinimumSize
function resdl_SDL_SetWindowMinimumSize(win, minWidth, minHeight) {
// no-op
}
// Provides: caml_glfwGetTime_byte
function caml_glfwGetTime_byte() {
return (joo_global_object._time.offset + (Date.now() - joo_global_object._time.start)) / 1000;
}
// Provides: caml_glfwSetTime_byte
function caml_glfwSetTime_byte(t) {
joo_global_object._time.offset = t * 1000;
joo_global_object._time.start = Date.now();
}
// Provides: caml_glfwGetPrimaryMonitor
function caml_glfwGetPrimaryMonitor() {
// No-op
}
// Provides: caml_glfwGetVideoMode
function caml_glfwGetVideoMode() {
var win = joo_global_object.window;
return [0, win.innerWidth, win.innerHeight];
};
// Provides: caml_glfwGetMonitorPos
function caml_glfwGetMonitorPos() {
return [0, 0, 0];
};
// Provides: caml_glfwGetMonitorPhysicalSize
function caml_glfwGetMonitorPhysicalSize() {
var win = joo_global_object.window;
var dpi = 96;
var widthMM = (win.innerWidth * 25.4) / dpi;
var heightMM = (win.innerHeight * 25.4) / dpi;
return [0, widthMM, heightMM];
};
// Provides: resdl_SDL_GetWindowSize
function resdl_SDL_GetWindowSize(w) {
var pixelRatio = joo_global_object.window.devicePixelRatio;
var width = w.canvas.width / pixelRatio;
var height = w.canvas.height / pixelRatio;
return [0, width, height];
}
// Provides: resdl_SDL_GetWindowId
function resdl_SDL_GetWindowId(w) {
return 0;
}
// Provides: resdl_SDL_GL_GetDrawableSize
function resdl_SDL_GL_GetDrawableSize(w) {
var pixelRatio = joo_global_object.window.devicePixelRatio;
var width = w.canvas.width;
var height = w.canvas.height;
return [0, width, height];
}
// Provides: caml_glfwDefaultWindowHints
function caml_glfwDefaultWindowHints(w) {
joo_global_object.console.warn("glfwDefaultWindowHints not implemented in WebGL");
}
// Provides: caml_glfwShowWindow
function caml_glfwShowWindow(w) {
joo_global_object.console.warn("glfwShowWindow not implemented in WebGL");
}
// Provides: caml_glfwHideWindow
function caml_glfwHideWindow(w) {
joo_global_object.console.warn("glfwHideWindow not implemented in WebGL");
}
// Provides: caml_test_callback_success
function caml_test_callback_success(s, f) {
s(999);
}
// Provides: resdl__javascript__renderloop
function resdl__javascript__renderloop(loopFunc) {
function renderLoop(t) {
var ret = loopFunc(t);
if (!ret) {
joo_global_object.window.requestAnimationFrame(renderLoop);
}
}
joo_global_object.window.requestAnimationFrame(renderLoop);
}
//Provides: resdl_SDL_ShowWindow
function resdl_SDL_ShowWindow(w) {
// no-op
}
//Provides: resdl_SDL_CreateWindow
function resdl_SDL_CreateWindow(width, height, title) {
var canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.bottom = "0px";
canvas.style.width = width.toString() + "px";
canvas.style.height = height.toString() + "px";
var pixelRatio = joo_global_object.window.devicePixelRatio;
canvas.width = width * pixelRatio;
canvas.height = height * pixelRatio;
joo_global_object._activeWindows = joo_global_object._activeWindows || [];
document.body.appendChild(canvas);
var w = {
id: 0,
canvas: canvas,
title: title,
isMaximized: false,
onSetFramebufferSize: null,
onSetWindowSize: null,
onKey: null,
onChar: null,
x: 0,
y: 0,
};
var notifyChar = function (codepoint) {
if (w.onChar) {
w.onChar(w, codepoint);
}
};
var notifyKey = function (keyEvent, pressMode) {
if (w.onKey) {
if (keyEvent.key && keyEvent.key.length == 1) {
var code = keyEvent.key.toUpperCase().charCodeAt(0);
var modifier = 0;
modifier = keyEvent.shiftKey ? modifier + 1 : modifier;
modifier = keyEvent.ctrlKey ? modifier + 2 : modifier;
modifier = keyEvent.altKey ? modifier + 4 : modifier;
modifier = keyEvent.metaKey ? modifier + 8 : modifier
w.onKey(w, code, keyEvent.location, pressMode, modifier);
};
}
};
var notifyMouseButton = function(mouseEvent, pressMode) {
joo_global_object._pushEvent(
[pressMode,
[0,
w.id,
0,
0,
joo_global_object._mouseState.x,
joo_global_object._mouseState.y]]);
};
var notifyMouseMove = function(x, y) {
joo_global_object._pushEvent(
[0, [0, w.id, x, y]]
);
};
var notifyResize = function () {
if (w.isMaximized) {
var pixelRatio = joo_global_object.window.devicePixelRatio;
canvas.width = canvas.offsetWidth * pixelRatio;
canvas.height = canvas.offsetHeight * pixelRatio;
joo_global_object._pushEvent([12, [0, w.id, canvas.offsetWidth, canvas.offsetHeight]]);
}
};
var notifyScroll = function (x, y) {
joo_global_object._pushEvent([1, [0, w.id, x, y, false]]);
};
w._notifyResize = notifyResize;
w._notifyChar = notifyChar;
w._notifyScroll = notifyScroll;
w._notifyKey = notifyKey;
w._notifyMouseButton = notifyMouseButton;
w._notifyMouseMove = notifyMouseMove;
joo_global_object._activeWindows.push(w);
return w;
};
// Provides: resdl_SDL_SetWindowPosition
function resdl_SDL_SetWindowPosition(w, x, y) {
var canvas = w.canvas;
canvas.style.transform = "translate(" + x.toString() + "px, " + y.toString() + "px)";
w.x = x;
w.y = y;
}
// Provides: resdl_SDL_SetWindowSize
function resdl_SDL_SetWindowSize(w, width, height) {
var canvas = w.canvas;
canvas.style.width = width.toString() + "px";
canvas.style.height = height.toString() + "px";
var pixelRatio = joo_global_object.window.devicePixelRatio;
canvas.width = width * pixelRatio;
canvas.height = height * pixelRatio;
}
// Provides: resdl_SDL_SetWindowTitle
function resdl_SDL_SetWindowTitle(w, title) {
var t = title.toString();
w.title = t;
document.title = t;
}
// Provides: resdl_SDL_CreateRGBSurfaceFromImage
function resdl_SDL_CreateRGBSurfaceFromImage(path) {
return [0, path];
};
// Provides: resdl_SDL_SetWindowIcon
function resdl_SDL_SetWindowIcon(w, surface) {
var p = surface.toString();
var link = document.querySelector("link[rel*='icon']") || document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = p;
document.getElementsByTagName('head')[0].appendChild(link);
}
// Provides: resdl_SDL_SetTextInputRect
function resdl_SDL_SetTextInputRect(x, y, w, h) {
// TODDO: No-op
}
// Provides: resdl_SDL_StartTextInput
function resdl_SDL_StartTextInput() {
// TODDO: No-op
}
// Provides: resdl_SDL_MaximizeWindow
function resdl_SDL_MaximizeWindow(w) {
if (w && !w.isMaximized) {
var pixelRatio = joo_global_object.window.devicePixelRatio;
var canvas = w.canvas;
canvas.style.width = "100%";
canvas.style.height = "100%";
canvas.width = canvas.offsetWidth * pixelRatio;
canvas.height = canvas.offsetHeight * pixelRatio;
w.isMaximized = true;
w._notifyResize();
}
}
// Provides: resdl_SDL_GL_Setup
function resdl_SDL_GL_Setup(win) {
var gl = win.canvas.getContext('webgl');
joo_global_object.window.__glfw__gl__ = gl;
joo_global_object.window._activeWindow = win;
joo_global_object.variantToTextureType = {
'0': gl.TEXTURE_2D,
};
joo_global_object.variantToEnableOption = {
'0': gl.DEPTH_TEST,
'1': gl.BLEND,
'2': gl.SCISSOR_TEST,
};
joo_global_object.variantToBlendFunc = {
'0': gl.ZERO,
'1': gl.ONE,
'2': gl.SRC_ALPHA,
'3': gl.ONE_MINUS_SRC_ALPHA,
};
joo_global_object.variantToTextureParameter = {
'0': gl.TEXTURE_WRAP_S,
'1': gl.TEXTURE_WRAP_T,
'2': gl.TEXTURE_MIN_FILTER,
'3': gl.TEXTURE_MAG_FILTER,
};
joo_global_object.variantToPixelAlignmentParameter = {
'0': gl.PACK_ALIGNMENT,
'1': gl.UNPACK_ALIGNMENT,
};
joo_global_object.variantToTextureParameterValue = {
'0': gl.REPEAT,
'1': gl.LINEAR,
'2': gl.CLAMP_TO_EDGE,
}
joo_global_object.variantToFormat = {
'0': gl.ALPHA,
'1': gl.RGB,
'2': gl.RGBA,
}
joo_global_object.formatToNumChannels = {
'6406': 1, // ALPHA
'6407': 3, // RGB
'6408': 4, // RGBA
}
joo_global_object.variantToType = {
'0': gl.FLOAT,
'1': gl.UNSIGNED_BYTE,
'2': gl.UNSIGNED_SHORT,
}
joo_global_object.variantToBufferType = {
'0': gl.ARRAY_BUFFER,
'1': gl.ELEMENT_ARRAY_BUFFER,
}
joo_global_object.variantToDrawMode = {
'0': gl.TRIANGLES,
'1': gl.TRIANGLE_STRIP
}
joo_global_object.gl = gl;
}
// Provides: resdl_SDL_PollEvent
function resdl_SDL_PollEvent() {
var evt = joo_global_object._popEvent();
if (evt != null) {
return [0, evt];
} else {
return 0;
}
return 0;
}
// Provides: resdl_SDL_ShowSimpleMessageBox
function resdl_SDL_ShowSimpleMessageBox(flags, title, msg, win) {
joo_global_object.alert(msg);
}
// Provides: resdl_SDL_GetNativeWindow
function resdl_SDL_GetNativeWindow(win) {
// TODO: Return canvas?
return null;
}
// Provides: resdl_SDL_WinAttachConsole
function resdl_SDL_WinAttachConsole() {
// no-op
return 1;
}
// Provides: resdl_SDL_WinAllocConsole
function resdl_SDL_WinAllocConsole() {
// no-op
return 1;
}
// Provides: resdl_SDL_GetPlatform
// Requires: caml_js_to_string
function resdl_SDL_GetPlatform() {
return caml_js_to_string("Browser");
}
// Provides: resdl_SDL_GetVersion
// Requires: caml_js_to_string
function resdl_SDL_GetVersion() {
return caml_js_to_string("Not implemented");
}
// Provides: resdl_SDL_SetMacBackgroundColor
function resdl_SDL_SetMacBackgroundColor() {
// No-op
}
// Provides: resdl_SDL_SetMacTitlebarTransparent
function resdl_SDL_SetMacTitlebarTransparent() {
// No-op
}
// Provides: resdl_SDL_EnableHitTest
function resdl_SDL_EnableHitTest() {
// No-op
}
// Provides: resdl_SDL_DisableHitTest
function resdl_SDL_DisableHitTest() {
// No-op
}
// Provides: resdl_SDL_SetWindowBordered
function resdl_SDL_SetWindowBordered() {
// No-op
}
// Provides: resdl_SDL_SetWindowResizable
function resdl_SDL_SetWindowResizable() {
// No-op
}
// Provides: resdl_SDL_GetModState
function resdl_SDL_GetModState() {
return 0;
}
// Provides: resdl_SDL_WaitTimeoutEvent
// Requires: resdl_SDL_PollEvent
function resdl_SDL_WaitTimeoutEvent() {
joo_global_object.console.warn("waitTimeout not supported in JSOO");
return resdl_SDL_PollEvent();
}
// Provides: resdl_SDL_GL_SwapWindow
function resdl_SDL_GL_SwapWindow() {
// no op
}
================================================
FILE: packages/reason-sdl2/src/sdl2_wrapper.cpp
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "stb_image.h"
#include
#include
#include
#include
#include
#ifdef SDL_VIDEO_DRIVER_COCOA
#import
#endif
#ifdef WIN32
#include
#include
#include
#endif
#include
#define Val_none Val_int(0)
static value Val_some(value v) {
CAMLparam1(v);
CAMLlocal1(some);
some = caml_alloc(1, 0);
Store_field(some, 0, v);
CAMLreturn(some);
}
static value Val_ok(value v) {
CAMLparam1(v);
CAMLlocal1(some);
some = caml_alloc(1, 0);
Store_field(some, 0, v);
CAMLreturn(some);
}
static value Val_error(value v) {
CAMLparam1(v);
CAMLlocal1(some);
some = caml_alloc(1, 1);
Store_field(some, 0, v);
CAMLreturn(some);
}
extern "C" {
/* Create an OCaml value encapsulating the pointer p */
value resdl_wrapPointer(void *p) {
value v = caml_alloc(1, Abstract_tag);
*((void **) Data_abstract_val(v)) = p;
return v;
}
/* Extract the pointer encapsulated in the given OCaml value */
void *resdl_unwrapPointer(value v) {
return *((void **) Data_abstract_val(v));
}
CAMLprim value resdl_SDL_EnableScreenSaver() {
CAMLparam0();
SDL_EnableScreenSaver();
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_DisableScreenSaver() {
CAMLparam0();
SDL_DisableScreenSaver();
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_IsScreenSaverEnabled() {
CAMLparam0();
int result = SDL_IsScreenSaverEnabled() == SDL_TRUE;
CAMLreturn(Val_int(result));
}
CAMLprim value resdl_SDL_SetMainReady() {
SDL_SetMainReady();
return Val_unit;
}
CAMLprim value resdl_SDL_DestroyWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_DestroyWindow(win);
CAMLreturn(Val_unit);
}
SDL_HitTestResult resdl_hit_test(SDL_Window *win, const SDL_Point *area,
void *data) {
CAMLparam0();
CAMLlocal2(vWin, vRet);
static const value *hitTestCallback = NULL;
if (hitTestCallback == NULL) {
hitTestCallback = caml_named_value("__sdl2_caml_hittest__");
}
vWin = resdl_wrapPointer(win);
vRet = caml_callback3(*hitTestCallback, vWin, Val_int(area->x),
Val_int(area->y));
SDL_HitTestResult result;
switch (Int_val(vRet)) {
case 0:
result = SDL_HITTEST_NORMAL;
break;
case 1:
result = SDL_HITTEST_DRAGGABLE;
break;
case 2:
result = SDL_HITTEST_RESIZE_TOPLEFT;
break;
case 3:
result = SDL_HITTEST_RESIZE_TOP;
break;
case 4:
result = SDL_HITTEST_RESIZE_TOPRIGHT;
break;
case 5:
result = SDL_HITTEST_RESIZE_RIGHT;
break;
case 6:
result = SDL_HITTEST_RESIZE_BOTTOMRIGHT;
break;
case 7:
result = SDL_HITTEST_RESIZE_BOTTOM;
break;
case 8:
result = SDL_HITTEST_RESIZE_BOTTOMLEFT;
break;
case 9:
result = SDL_HITTEST_RESIZE_LEFT;
break;
default:
result = SDL_HITTEST_NORMAL;
break;
}
CAMLreturnT(SDL_HitTestResult, result);
};
CAMLprim value resdl_SDL_EnableHitTest(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SetWindowHitTest(win, resdl_hit_test, NULL);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_DisableHitTest(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SetWindowHitTest(win, NULL, NULL);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_Delay(value delay) {
CAMLparam1(delay);
int d = Int_val(delay);
SDL_Delay(d);
CAMLreturn(Val_unit);
}
#ifdef WIN32
typedef enum PROCESS_DPI_AWARENESS {
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS;
HWND getHWNDFromSDLWindow(SDL_Window *win) {
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(win, &wmInfo);
return wmInfo.info.win.window;
};
#endif
CAMLprim value resdl_SDL_GetPlatform() {
CAMLparam0();
CAMLlocal1(ret);
const char *str = SDL_GetPlatform();
ret = caml_copy_string(str);
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetVersion() {
CAMLparam0();
CAMLlocal1(ret);
const char *str;
#ifdef __APPLE__
NSProcessInfo *pInfo = [NSProcessInfo processInfo];
NSString *version = [pInfo operatingSystemVersionString];
str = [version UTF8String];
#else
str = "??.??.??";
#endif
ret = caml_copy_string(str);
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetNativeWindow(value vWin) {
CAMLparam1(vWin);
CAMLlocal1(vNativeWindow);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(win, &wmInfo);
void *pNativeWindow = NULL;
switch (wmInfo.subsystem) {
#ifdef SDL_VIDEO_DRIVER_WINDOWS
case SDL_SYSWM_WINDOWS:
pNativeWindow = (void *)wmInfo.info.win.window;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_UIKIT
case SDL_SYSWM_UIKIT:
pNativeWindow = (void *)wmInfo.info.uikit.window;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_COCOA
case SDL_SYSWM_COCOA:
pNativeWindow = (void *)wmInfo.info.cocoa.window;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_ANDROID
case SDL_SYSWM_ANDROID:
pNativeWindow = (void *)wmInfo.info.android.window;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_X11
case SDL_SYSWM_X11:
pNativeWindow = (void *)wmInfo.info.x11.window;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
case SDL_SYSWM_WAYLAND:
pNativeWindow = (void *)wmInfo.info.wl.surface;
break;
#endif
default:
break;
}
vNativeWindow = resdl_wrapPointer(pNativeWindow);
CAMLreturn(vNativeWindow);
};
#ifdef WIN32
// This method is calling after attach / alloc console
// to wire up the new stdin/stdout/stderr.
// See further details (thanks @dra27 for the help!)
// - https://github.com/ocaml/ocaml/issues/9252
void resdl_Win32AttachStdIO() {
int fd_in = _open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE),
_O_RDONLY | _O_BINARY);
int fd_out = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE),
_O_WRONLY | _O_BINARY);
int fd_err = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE),
_O_WRONLY | _O_BINARY);
if (fd_in) {
dup2(fd_in, 0);
close(fd_in);
SetStdHandle(STD_INPUT_HANDLE, (HANDLE)_get_osfhandle(0));
}
if (fd_out) {
dup2(fd_out, 1);
close(fd_out);
SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(1));
}
if (fd_err) {
dup2(fd_err, 2);
close(fd_err);
SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(2));
}
*stdin = *(fdopen(0, "rb"));
*stdout = *(fdopen(1, "wb"));
*stderr = *(fdopen(2, "wb"));
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}
#endif
CAMLprim value resdl_SDL_WinAttachConsole() {
CAMLparam0();
int ret = 0;
#ifdef WIN32
// Only attach if we don't already have a stdout handle
if (GetStdHandle(STD_OUTPUT_HANDLE) == NULL) {
ret = AttachConsole(ATTACH_PARENT_PROCESS);
if (ret) {
resdl_Win32AttachStdIO();
}
} else {
// There's already a stdout handle available,
// so return success
ret = 1;
}
#endif
CAMLreturn(Val_int(ret));
}
CAMLprim value resdl_SDL_WinAllocConsole() {
CAMLparam0();
int ret = 0;
#ifdef WIN32
ret = AllocConsole();
if (ret) {
resdl_Win32AttachStdIO();
}
#endif
CAMLreturn(Val_int(ret));
}
CAMLprim value resdl_SDL_SetMacTitlebarHidden(value vWin) {
CAMLparam1(vWin);
#ifdef SDL_VIDEO_DRIVER_COCOA
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(win, &wmInfo);
NSWindow *nWindow = wmInfo.info.cocoa.window;
[nWindow
setStyleMask:[nWindow styleMask] | NSWindowStyleMaskDocModalWindow];
#endif
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_SetMacTitlebarTransparent(value vWin) {
CAMLparam1(vWin);
#ifdef SDL_VIDEO_DRIVER_COCOA
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(win, &wmInfo);
NSWindow *nWindow = wmInfo.info.cocoa.window;
[nWindow
setStyleMask:[nWindow styleMask] | NSWindowStyleMaskFullSizeContentView];
[nWindow setTitlebarAppearsTransparent:YES];
nWindow.titleVisibility = NSWindowTitleHidden;
#endif
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_GetMacTitlebarHeight(value vWin) {
CAMLparam1(vWin);
double titlebarHeight = 0.0;
#ifdef SDL_VIDEO_DRIVER_COCOA
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(win, &wmInfo);
NSWindow *nWindow = wmInfo.info.cocoa.window;
// Sourced from: https://stackoverflow.com/a/59323932/12701512
CGFloat windowFrameHeight = CGRectGetHeight([nWindow contentView].frame);
CGFloat contentLayoutRectHeight = CGRectGetHeight([nWindow contentLayoutRect]);
titlebarHeight = (double)(windowFrameHeight - contentLayoutRectHeight);
#endif
CAMLreturn(caml_copy_double(titlebarHeight));
}
CAMLprim value resdl_SDL_SetMacBackgroundColor(value vWin, value r, value g,
value b, value a) {
CAMLparam5(vWin, r, g, b, a);
#ifdef SDL_VIDEO_DRIVER_COCOA
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(win, &wmInfo);
double red = Double_val(r);
double green = Double_val(g);
double blue = Double_val(b);
double alpha = Double_val(a);
NSWindow *nWindow = wmInfo.info.cocoa.window;
NSColor *rgb =
[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];
[nWindow setBackgroundColor:rgb];
#endif
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_SetWin32ProcessDPIAware(value vWin) {
CAMLparam1(vWin);
#ifdef WIN32
void *userDLL;
BOOL(WINAPI * SetProcessDPIAware)(void); // Vista and later
void *shcoreDLL;
HRESULT(WINAPI * SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS dpiAwareness);
userDLL = SDL_LoadObject("USER32.DLL");
if (userDLL) {
SetProcessDPIAware =
(BOOL(WINAPI *)(void))SDL_LoadFunction(userDLL, "SetProcessDPIAware");
}
shcoreDLL = SDL_LoadObject("SHCORE.DLL");
if (shcoreDLL) {
SetProcessDpiAwareness =
(HRESULT(WINAPI *)(PROCESS_DPI_AWARENESS))SDL_LoadFunction(
shcoreDLL, "SetProcessDpiAwareness");
}
if (SetProcessDpiAwareness) {
// Try Windows 8.1+ version
HRESULT result = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
} else if (SetProcessDPIAware) {
// Try Vista - Windows 8 version.
// This has a constant scale factor for all monitors.
BOOL success = SetProcessDPIAware();
}
#endif
CAMLreturn(Val_unit);
};
CAMLprim value resdl_SDL_GetWin32ScaleFactor(value vWin) {
CAMLparam1(vWin);
#ifdef WIN32
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
HWND hwnd = getHWNDFromSDLWindow(win);
HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
void *shcoreDLL;
HRESULT(WINAPI * GetScaleFactorForMonitor)(HMONITOR hmon, int *pScale);
shcoreDLL = SDL_LoadObject("SHCORE.DLL");
if (shcoreDLL) {
GetScaleFactorForMonitor =
(HRESULT(WINAPI *)(HMONITOR, int *))SDL_LoadFunction(
shcoreDLL, "GetScaleFactorForMonitor");
}
if (GetScaleFactorForMonitor) {
int pScale;
GetScaleFactorForMonitor(hmon, &pScale);
CAMLreturn(caml_copy_double(pScale / 100.0));
} else {
CAMLreturn(caml_copy_double(1.0));
}
#else
CAMLreturn(caml_copy_double(1.0));
#endif
};
CAMLprim value resdl_SDL_GetDisplayDPI(value vDisplay) {
CAMLparam1(vDisplay);
CAMLlocal1(ret);
int displayIndex = Int_val(vDisplay);
float ddpi, hdpi, vdpi;
SDL_GetDisplayDPI(displayIndex, &ddpi, &hdpi, &vdpi);
ret = caml_alloc(3, 0);
Store_double_field(ret, 0, ddpi);
Store_double_field(ret, 1, hdpi);
Store_double_field(ret, 2, vdpi);
CAMLreturn(ret);
};
CAMLprim value resdl_SDL_GetCurrentDisplayMode(value vDisplay) {
CAMLparam1(vDisplay);
CAMLlocal1(ret);
int displayIndex = Int_val(vDisplay);
SDL_DisplayMode current;
SDL_GetCurrentDisplayMode(displayIndex, ¤t);
ret = caml_alloc(4, 0);
Store_field(ret, 0, Val_int(current.format));
Store_field(ret, 1, Val_int(current.w));
Store_field(ret, 2, Val_int(current.h));
Store_field(ret, 3, Val_int(current.refresh_rate));
CAMLreturn(ret);
};
CAMLprim value resdl_SDL_GetDesktopDisplayMode(value vDisplay) {
CAMLparam1(vDisplay);
CAMLlocal1(ret);
int displayIndex = Int_val(vDisplay);
SDL_DisplayMode current;
SDL_GetDesktopDisplayMode(displayIndex, ¤t);
ret = caml_alloc(4, 0);
Store_field(ret, 0, Val_int(current.format));
Store_field(ret, 1, Val_int(current.w));
Store_field(ret, 2, Val_int(current.h));
Store_field(ret, 3, Val_int(current.refresh_rate));
CAMLreturn(ret);
};
CAMLprim value resdl_SDL_GetDisplayBounds(value vDisplay) {
CAMLparam1(vDisplay);
CAMLlocal1(rect);
int displayIndex = Int_val(vDisplay);
SDL_Rect sdlRect;
SDL_GetDisplayBounds(displayIndex, &sdlRect);
rect = caml_alloc(4, 0);
Store_field(rect, 0, Val_int(sdlRect.x));
Store_field(rect, 1, Val_int(sdlRect.y));
Store_field(rect, 2, Val_int(sdlRect.w));
Store_field(rect, 3, Val_int(sdlRect.h));
CAMLreturn(rect);
};
CAMLprim value resdl_SDL_GetDisplayName(value vDisplay) {
CAMLparam1(vDisplay);
CAMLlocal1(retStr);
int displayIndex = Int_val(vDisplay);
const char *szDisplayName = SDL_GetDisplayName(displayIndex);
if (!szDisplayName) {
szDisplayName = "(Null)";
};
retStr = caml_copy_string(szDisplayName);
CAMLreturn(retStr);
};
CAMLprim value resdl_SDL_GetDisplayUsableBounds(value vDisplay) {
CAMLparam1(vDisplay);
CAMLlocal1(rect);
int displayIndex = Int_val(vDisplay);
SDL_Rect sdlRect;
SDL_GetDisplayUsableBounds(displayIndex, &sdlRect);
rect = caml_alloc(4, 0);
Store_field(rect, 0, Val_int(sdlRect.x));
Store_field(rect, 1, Val_int(sdlRect.y));
Store_field(rect, 2, Val_int(sdlRect.w));
Store_field(rect, 3, Val_int(sdlRect.h));
CAMLreturn(rect);
};
CAMLprim value resdl_SDL_GetNumVideoDisplays(value vUnit) {
CAMLparam0();
int num = SDL_GetNumVideoDisplays();
CAMLreturn(Val_int(num));
};
CAMLprim value resdl_SDL_GetPixelFormatName(value vPixelFormat) {
CAMLparam1(vPixelFormat);
CAMLlocal1(ret);
Uint32 format = Int_val(vPixelFormat);
const char *szPixelFormatName = SDL_GetPixelFormatName(format);
if (!szPixelFormatName) {
ret = caml_copy_string("(null)");
} else {
ret = caml_copy_string(szPixelFormatName);
}
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetWindowDisplayIndex(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
int idx = SDL_GetWindowDisplayIndex(win);
CAMLreturn(Val_int(idx));
};
CAMLprim value resdl_SDL_GetWindowPixelFormat(value vWin) {
CAMLparam1(vWin);
SDL_Window *pWin = (SDL_Window *)resdl_unwrapPointer(vWin);
Uint32 format = SDL_GetWindowPixelFormat(pWin);
CAMLreturn(Val_int(format));
};
CAMLprim value resdl_SDL_GL_SetSwapInterval(value vInterval) {
int interval = Int_val(vInterval);
SDL_GL_SetSwapInterval(interval);
return Val_unit;
};
CAMLprim value resdl_SDL_GL_Setup(value vWin) {
CAMLparam1(vWin);
CAMLlocal1(vCtx);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_GLContext ctx = SDL_GL_CreateContext(win);
if (!ctx) {
SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_GL_Setup failed: %s\n",
SDL_GetError());
}
vCtx = resdl_wrapPointer(ctx);
CAMLreturn(vCtx);
}
typedef const GLubyte *(*glGetStringFunc)(GLenum);
CAMLprim value resdl_SDL_GL_GetString(value vStr) {
CAMLparam1(vStr);
CAMLlocal1(ret);
GLenum name = GL_VENDOR;
switch (Int_val(vStr)) {
case 0:
name = GL_VENDOR;
break;
case 1:
name = GL_RENDERER;
break;
case 2:
name = GL_VERSION;
break;
case 3:
name = GL_SHADING_LANGUAGE_VERSION;
break;
default:
break;
}
glGetStringFunc glGetString =
(glGetStringFunc)(SDL_GL_GetProcAddress("glGetString"));
if (!glGetString) {
ret = caml_copy_string("Unable to get OpenGL proc address for glGetString");
} else {
const char *sz = (const char *)((void *)glGetString(name));
if (!sz) {
ret = caml_copy_string("(null)");
} else {
ret = caml_copy_string(sz);
}
}
CAMLreturn(ret);
}
typedef void (*glGetIntegervFunc)(GLenum, GLint*);
CAMLprim value resdl_SDL_GL_GetFramebufferBinding(value vInt) {
CAMLparam1(vInt);
GLenum name = GL_FRAMEBUFFER_BINDING;
switch (Int_val(vInt)) {
case 0:
name = GL_FRAMEBUFFER_BINDING;
break;
default:
break;
}
GLint ret = -1;
glGetIntegervFunc glGetIntegerv =
(glGetIntegervFunc)(SDL_GL_GetProcAddress("glGetIntegerv"));
if (glGetIntegerv) {
glGetIntegerv(name, &ret);
}
CAMLreturn(Val_int(ret));
}
CAMLprim value resdl_SDL_GL_MakeCurrent(value vWin, value vContext) {
CAMLparam2(vWin, vContext);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_GLContext ctx = (SDL_GLContext)resdl_unwrapPointer(vContext);
SDL_GL_MakeCurrent(win, ctx);
CAMLreturn(Val_unit);
};
CAMLprim value resdl_SDL_GetClipboardText(value vUnit) {
CAMLparam0();
CAMLlocal1(ret);
char *clip = SDL_GetClipboardText();
if (clip == NULL) {
ret = Val_none;
} else {
ret = Val_some(caml_copy_string(clip));
}
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_HasClipboardText(value vUnit) {
CAMLparam0();
SDL_bool res = SDL_HasClipboardText();
CAMLreturn(res == SDL_TRUE ? Val_bool(1) : Val_bool(0));
}
CAMLprim value resdl_SDL_SetClipboardText(value vText) {
CAMLparam1(vText);
SDL_SetClipboardText(String_val(vText));
CAMLreturn(Val_unit);
}
CAMLprim value Val_SDL_WindowEvent(int type, int windowID) {
CAMLparam0();
CAMLlocal2(ret, v);
v = caml_alloc(1, 0);
Store_field(v, 0, Val_int(windowID));
ret = caml_alloc(1, type);
Store_field(ret, 0, v);
CAMLreturn(ret);
}
CAMLprim value Val_SDL_WindowEventWithData(int type, int windowID, int data1,
int data2) {
CAMLparam0();
CAMLlocal2(ret, v);
v = caml_alloc(3, 0);
Store_field(v, 0, Val_int(windowID));
Store_field(v, 1, Val_int(data1));
Store_field(v, 2, Val_int(data2));
ret = caml_alloc(1, type);
Store_field(ret, 0, v);
CAMLreturn(ret);
}
void getNonFocusedMousePosition(SDL_Window *window, int *localMouseX, int *localMouseY) {
int globalMouseX, globalMouseY;
SDL_GetGlobalMouseState(&globalMouseX, &globalMouseY);
int windowX, windowY;
SDL_GetWindowPosition(window, &windowX, &windowY);
*localMouseX = globalMouseX - windowX;
*localMouseY = globalMouseY - windowY;
}
CAMLprim value Val_SDL_Event(SDL_Event *event) {
CAMLparam0();
CAMLlocal2(v, vInner);
int tag, mouseButton, mousePosX, mousePosY;
switch (event->type) {
case SDL_QUIT:
v = Val_int(0);
break;
case SDL_MOUSEMOTION:
vInner = caml_alloc(3, 0);
Store_field(vInner, 0, Val_int(event->motion.windowID));
Store_field(vInner, 1, Val_int(event->motion.x));
Store_field(vInner, 2, Val_int(event->motion.y));
v = caml_alloc(1, 0);
Store_field(v, 0, vInner);
break;
case SDL_MOUSEWHEEL:
v = caml_alloc(1, 1);
vInner = caml_alloc(4, 0);
Store_field(vInner, 0, Val_int(event->wheel.windowID));
Store_field(vInner, 1, Val_int(event->wheel.x));
Store_field(vInner, 2, Val_int(event->wheel.y));
Store_field(vInner, 3,
Val_bool(event->wheel.direction == SDL_MOUSEWHEEL_FLIPPED));
Store_field(v, 0, vInner);
break;
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
if (event->type == SDL_MOUSEBUTTONDOWN)
v = caml_alloc(1, 2);
else
v = caml_alloc(1, 3);
mouseButton = 0;
switch (event->button.button) {
case SDL_BUTTON_LEFT:
mouseButton = 0;
break;
case SDL_BUTTON_MIDDLE:
mouseButton = 1;
break;
case SDL_BUTTON_RIGHT:
mouseButton = 2;
break;
case SDL_BUTTON_X1:
mouseButton = 3;
break;
case SDL_BUTTON_X2:
mouseButton = 4;
break;
default:
mouseButton = 0;
break;
}
vInner = caml_alloc(5, 0);
Store_field(vInner, 0, Val_int(event->button.windowID));
Store_field(vInner, 1, Val_int(mouseButton));
Store_field(vInner, 2, Val_int(event->button.clicks));
Store_field(vInner, 3, Val_int(event->button.x));
Store_field(vInner, 4, Val_int(event->button.y));
Store_field(v, 0, vInner);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
v = caml_alloc(1, event->type == SDL_KEYDOWN ? 4 : 5);
vInner = caml_alloc(5, 0);
Store_field(vInner, 0, Val_int(event->key.windowID));
Store_field(vInner, 1, Val_bool(event->key.repeat));
Store_field(vInner, 2, Val_int(event->key.keysym.mod));
Store_field(vInner, 3, Val_int(event->key.keysym.scancode));
Store_field(vInner, 4, Val_int(event->key.keysym.sym));
Store_field(v, 0, vInner);
break;
case SDL_TEXTINPUT:
v = caml_alloc(1, 6);
vInner = caml_alloc(2, 0);
Store_field(vInner, 0, Val_int(event->text.windowID));
Store_field(vInner, 1, caml_copy_string(event->text.text));
Store_field(v, 0, vInner);
break;
case SDL_TEXTEDITING:
v = caml_alloc(1, 7);
vInner = caml_alloc(4, 0);
Store_field(vInner, 0, Val_int(event->edit.windowID));
Store_field(vInner, 1, caml_copy_string(event->edit.text));
Store_field(vInner, 2, Val_int(event->edit.start));
Store_field(vInner, 3, Val_int(event->edit.length));
Store_field(v, 0, vInner);
break;
case SDL_PANEVENT:
v = caml_alloc(1, 24);
vInner = caml_alloc(9, 0);
Store_field(vInner, 0, Val_int(event->window.windowID));
Store_field(vInner, 1, Val_int(event->pan.x));
Store_field(vInner, 2, Val_int(event->pan.y));
Store_field(vInner, 3, Val_bool(event->pan.contains_x));
Store_field(vInner, 4, Val_bool(event->pan.contains_y));
Store_field(vInner, 5, Val_bool(event->pan.fling));
Store_field(vInner, 6, Val_bool(event->pan.interrupt));
// verify this is the correct way of representing a ref to some WheelType.t
Store_field(vInner, 7, Val_int(event->pan.source_type));
Store_field(vInner, 8, Val_int(event->pan.timestamp));
Store_field(v, 0, vInner);
break;
case SDL_DROPTEXT:
v = caml_alloc(1, 25);
vInner = caml_alloc(5, 0);
getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);
Store_field(vInner, 0, Val_int(event->drop.windowID));
Store_field(vInner, 1, caml_copy_string(event->drop.file));
Store_field(vInner, 2, Val_int(event->drop.timestamp));
Store_field(vInner, 3, Val_int(mousePosX));
Store_field(vInner, 4, Val_int(mousePosY));
Store_field(v, 0, vInner);
SDL_free(event->drop.file);
break;
case SDL_DROPFILE:
v = caml_alloc(1, 26);
vInner = caml_alloc(5, 0);
getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);
Store_field(vInner, 0, Val_int(event->drop.windowID));
Store_field(vInner, 1, caml_copy_string(event->drop.file));
Store_field(vInner, 2, Val_int(event->drop.timestamp));
Store_field(vInner, 3, Val_int(mousePosX));
Store_field(vInner, 4, Val_int(mousePosY));
Store_field(v, 0, vInner);
SDL_free(event->drop.file);
break;
case SDL_DROPBEGIN:
v = caml_alloc(1, 27);
vInner = caml_alloc(4, 0);
getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);
Store_field(vInner, 0, Val_int(event->drop.windowID));
Store_field(vInner, 1, Val_int(event->drop.timestamp));
Store_field(vInner, 2, Val_int(mousePosX));
Store_field(vInner, 3, Val_int(mousePosY));
Store_field(v, 0, vInner);
SDL_free(event->drop.file);
break;
case SDL_DROPCOMPLETE:
v = caml_alloc(1, 28);
vInner = caml_alloc(4, 0);
getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);
Store_field(vInner, 0, Val_int(event->drop.windowID));
Store_field(vInner, 1, Val_int(event->drop.timestamp));
Store_field(vInner, 2, Val_int(mousePosX));
Store_field(vInner, 3, Val_int(mousePosY));
Store_field(v, 0, vInner);
SDL_free(event->drop.file);
break;
case SDL_KEYMAPCHANGED:
v = Val_int(2);
break;
case SDL_WINDOWEVENT:
switch (event->window.event) {
case SDL_WINDOWEVENT_SHOWN:
v = Val_SDL_WindowEvent(8, event->window.windowID);
break;
case SDL_WINDOWEVENT_HIDDEN:
v = Val_SDL_WindowEvent(9, event->window.windowID);
break;
case SDL_WINDOWEVENT_EXPOSED:
v = Val_SDL_WindowEvent(10, event->window.windowID);
break;
case SDL_WINDOWEVENT_MOVED:
v = Val_SDL_WindowEventWithData(11, event->window.windowID,
event->window.data1, event->window.data2);
break;
case SDL_WINDOWEVENT_RESIZED:
v = Val_SDL_WindowEventWithData(12, event->window.windowID,
event->window.data1, event->window.data2);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
v = Val_SDL_WindowEventWithData(13, event->window.windowID,
event->window.data1, event->window.data2);
break;
case SDL_WINDOWEVENT_MINIMIZED:
v = Val_SDL_WindowEvent(14, event->window.windowID);
break;
case SDL_WINDOWEVENT_MAXIMIZED:
v = Val_SDL_WindowEvent(15, event->window.windowID);
break;
case SDL_WINDOWEVENT_FULLSCREEN:
v = Val_SDL_WindowEvent(29, event->window.windowID);
break;
case SDL_WINDOWEVENT_RESTORED:
v = Val_SDL_WindowEvent(16, event->window.windowID);
break;
case SDL_WINDOWEVENT_ENTER:
v = Val_SDL_WindowEvent(17, event->window.windowID);
break;
case SDL_WINDOWEVENT_LEAVE:
v = Val_SDL_WindowEvent(18, event->window.windowID);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
v = Val_SDL_WindowEvent(19, event->window.windowID);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
v = Val_SDL_WindowEvent(20, event->window.windowID);
break;
case SDL_WINDOWEVENT_CLOSE:
v = Val_SDL_WindowEvent(21, event->window.windowID);
break;
case SDL_WINDOWEVENT_TAKE_FOCUS:
v = Val_SDL_WindowEvent(22, event->window.windowID);
break;
case SDL_WINDOWEVENT_HIT_TEST:
v = Val_SDL_WindowEvent(23, event->window.windowID);
break;
default:
v = Val_int(1);
};
break;
default:
v = Val_int(1);
}
CAMLreturn(v);
};
CAMLprim value resdl_SDL_PollEvent() {
CAMLparam0();
CAMLlocal2(ret, evt);
SDL_Event e;
int result = SDL_PollEvent(&e);
if (result == 0) {
ret = Val_none;
} else {
evt = Val_SDL_Event(&e);
ret = Val_some(evt);
}
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_WaitEvent() {
CAMLparam0();
CAMLlocal2(ret, evt);
SDL_Event e;
int result = SDL_WaitEvent(&e);
if (result == 1) {
evt = Val_SDL_Event(&e);
ret = Val_ok(evt);
} else {
ret = Val_error(caml_copy_string(SDL_GetError()));
}
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_PushEvent() {
CAMLparam0();
SDL_Event user_event;
user_event.type = SDL_USEREVENT;
SDL_PushEvent(&user_event);
CAMLreturn(Val_unit);
};
CAMLprim value resdl_SDL_WaitTimeoutEvent(value vTimeout) {
CAMLparam1(vTimeout);
CAMLlocal2(ret, evt);
int timeout = Int_val(vTimeout);
SDL_Event e;
int result = SDL_WaitEventTimeout(&e, timeout);
if (result == 1) {
evt = Val_SDL_Event(&e);
ret = Val_some(evt);
} else {
ret = Val_none;
}
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetTicks() {
CAMLparam0();
int result = SDL_GetTicks();
CAMLreturn(Val_int(result));
}
CAMLprim value resdl_SDL_GetWindowSize(value vWindow) {
CAMLparam1(vWindow);
CAMLlocal1(ret);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);
int width, height = 0;
SDL_GetWindowSize(win, &width, &height);
ret = caml_alloc(2, 0);
Store_field(ret, 0, Val_int(width));
Store_field(ret, 1, Val_int(height));
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetWindowPosition(value vWindow) {
CAMLparam1(vWindow);
CAMLlocal1(position);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);
int x, y = 0;
SDL_GetWindowPosition(win, &x, &y);
position = caml_alloc(2, 0);
Store_field(position, 0, Val_int(x));
Store_field(position, 1, Val_int(y));
CAMLreturn(position);
}
CAMLprim value resdl_SDL_GL_GetDrawableSize(value vWindow) {
CAMLparam1(vWindow);
CAMLlocal1(ret);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);
int width, height = 0;
SDL_GL_GetDrawableSize(win, &width, &height);
ret = caml_alloc(2, 0);
Store_field(ret, 0, Val_int(width));
Store_field(ret, 1, Val_int(height));
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_SetWindowIcon(value vWindow, value vIcon) {
CAMLparam2(vWindow, vIcon);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);
SDL_Surface *surface = (SDL_Surface *)resdl_unwrapPointer(vIcon);
SDL_SetWindowIcon(win, surface);
CAMLreturn(Val_unit);
};
CAMLprim value resdl_SDL_SetWindowTransparency(value vWindow,
value vTransparency) {
CAMLparam2(vWindow, vTransparency);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);
double transparency = Double_val(vTransparency);
int result;
result = SDL_SetWindowOpacity(win, transparency);
if (result == -1) {
printf("WARNING: Setting transparency not supported!");
}
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_CreateSystemCursor(value vCursor) {
CAMLparam1(vCursor);
CAMLlocal1(vCursorRet);
SDL_Cursor *cursor = NULL;
SDL_SystemCursor id = SDL_SYSTEM_CURSOR_ARROW;
switch (Int_val(vCursor)) {
case 1:
id = SDL_SYSTEM_CURSOR_IBEAM;
break;
case 2:
id = SDL_SYSTEM_CURSOR_WAIT;
break;
case 3:
id = SDL_SYSTEM_CURSOR_CROSSHAIR;
break;
case 4:
id = SDL_SYSTEM_CURSOR_WAITARROW;
break;
case 5:
id = SDL_SYSTEM_CURSOR_SIZENWSE;
break;
case 6:
id = SDL_SYSTEM_CURSOR_SIZENESW;
break;
case 7:
id = SDL_SYSTEM_CURSOR_SIZEWE;
break;
case 8:
id = SDL_SYSTEM_CURSOR_SIZENS;
break;
case 9:
id = SDL_SYSTEM_CURSOR_SIZEALL;
break;
case 10:
id = SDL_SYSTEM_CURSOR_NO;
break;
case 11:
id = SDL_SYSTEM_CURSOR_HAND;
break;
default:
id = SDL_SYSTEM_CURSOR_ARROW;
break;
}
cursor = SDL_CreateSystemCursor(id);
vCursorRet = resdl_wrapPointer(cursor);
CAMLreturn(vCursorRet);
}
CAMLprim value resdl_SDL_StartTextInput() {
SDL_StartTextInput();
return Val_unit;
}
CAMLprim value resdl_SDL_StopTextInput() {
SDL_StopTextInput();
return Val_unit;
}
CAMLprim value resdl_SDL_SetTextInputRect(value vX, value vY, value vWidth,
value vHeight) {
int x = Int_val(vX);
int y = Int_val(vY);
int width = Int_val(vWidth);
int height = Int_val(vHeight);
SDL_Rect rect;
rect.x = x;
rect.y = y;
rect.w = width;
rect.h = height;
SDL_SetTextInputRect(&rect);
return Val_unit;
}
CAMLprim value resdl_SDL_IsTextInputActive() {
return Val_bool(SDL_IsTextInputActive());
}
CAMLprim value resdl_SDL_SetCursor(value vCursor) {
CAMLparam1(vCursor);
SDL_Cursor *cursor = (SDL_Cursor *)resdl_unwrapPointer(vCursor);
SDL_SetCursor(cursor);
CAMLreturn(Val_unit);
}
static SDL_AudioFormat SDL_AudioFormat_val(value tag) {
switch (Int_val(tag)) {
case 0:
return AUDIO_S8;
case 1:
return AUDIO_U8;
case 2:
return AUDIO_S16LSB;
case 3:
return AUDIO_S16MSB;
case 4:
return AUDIO_U16LSB;
case 5:
return AUDIO_U16MSB;
case 6:
return AUDIO_S32LSB;
case 7:
return AUDIO_S32MSB;
case 8:
return AUDIO_F32LSB;
case 9:
return AUDIO_F32MSB;
default:
return 0;
}
}
CAMLprim value Val_SDL_AudioFormat(SDL_AudioFormat format) {
CAMLparam0();
int tag;
switch (format) {
case AUDIO_S8:
tag = 0;
break;
case AUDIO_U8:
tag = 1;
break;
case AUDIO_S16LSB:
tag = 2;
break;
case AUDIO_S16MSB:
tag = 3;
break;
case AUDIO_U16LSB:
tag = 4;
break;
case AUDIO_U16MSB:
tag = 5;
break;
case AUDIO_S32LSB:
tag = 6;
break;
case AUDIO_S32MSB:
tag = 7;
break;
case AUDIO_F32LSB:
tag = 8;
break;
case AUDIO_F32MSB:
tag = 9;
break;
}
CAMLreturn(Val_int(tag));
}
CAMLprim value Val_SDL_AudioSpec(SDL_AudioSpec spec) {
CAMLparam0();
CAMLlocal1(ret);
ret = caml_alloc(7, 0);
Store_field(ret, 0, Val_int(spec.freq));
Store_field(ret, 1, Val_SDL_AudioFormat(spec.format));
Store_field(ret, 2, Val_int(spec.channels));
Store_field(ret, 3, Val_int(spec.silence));
Store_field(ret, 4, Val_int(spec.samples));
Store_field(ret, 5, Val_int(spec.padding));
Store_field(ret, 6, Val_int(spec.size));
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_LoadWAV(value vPath) {
CAMLparam1(vPath);
CAMLlocal3(ret, vSpec, vTup3);
SDL_AudioSpec spec;
Uint8 *audioBuf;
Uint32 audioLen;
SDL_LoadWAV(String_val(vPath), &spec, &audioBuf, &audioLen);
if (audioBuf == NULL) {
ret = Val_error(caml_copy_string(SDL_GetError()));
CAMLreturn(ret);
}
vSpec = Val_SDL_AudioSpec(spec);
vTup3 = caml_alloc_tuple(3);
Store_field(vTup3, 0, vSpec);
Store_field(vTup3, 1, resdl_wrapPointer(audioBuf));
Store_field(vTup3, 2, Val_int(audioLen));
ret = Val_ok(vTup3);
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_OpenAudioDevice(
value vDeviceNameOpt,
value vIsCapture,
value vWant,
value vAllowedChanges) {
CAMLparam4(vDeviceNameOpt, vIsCapture, vWant, vAllowedChanges);
CAMLlocal3(ret, vHave, vTup2);
const char *deviceName = Is_block(vDeviceNameOpt)
? String_val(Field(vDeviceNameOpt, 0))
: NULL;
int isCapture = Bool_val(vIsCapture);
SDL_AudioSpec want = {0};
want.freq = Int_val(Field(vWant, 0));
want.format = SDL_AudioFormat_val(Field(vWant, 1));
want.channels = Int_val(Field(vWant, 2));
want.silence = Int_val(Field(vWant, 3));
want.samples = Int_val(Field(vWant, 4));
want.padding = Int_val(Field(vWant, 5));
want.size = Int_val(Field(vWant, 6));
SDL_AudioSpec have = {0};
int allowedChanges = Int_val(vAllowedChanges);
SDL_AudioDeviceID device = SDL_OpenAudioDevice(
deviceName,
isCapture,
&want,
&have,
allowedChanges);
if (device == 0) {
ret = Val_error(caml_copy_string(SDL_GetError()));
CAMLreturn(ret);
}
vHave = Val_SDL_AudioSpec(have);
vTup2 = caml_alloc_tuple(2);
Store_field(vTup2, 0, Val_int(device));
Store_field(vTup2, 1, vHave);
ret = Val_ok(vTup2);
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_CloseAudioDevice(value vDevice) {
CAMLparam1(vDevice);
SDL_AudioDeviceID device = Int_val(vDevice);
SDL_CloseAudioDevice(device);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_GetAudioDeviceStatus(value vDevice) {
CAMLparam1(vDevice);
SDL_AudioDeviceID device = Int_val(vDevice);
SDL_AudioStatus status = SDL_GetAudioDeviceStatus(device);
CAMLreturn(Val_int(status));
}
CAMLprim value resdl_SDL_PauseAudioDevice(value vDevice, value vPauseOn) {
CAMLparam2(vDevice, vPauseOn);
SDL_AudioDeviceID device = Int_val(vDevice);
int pauseOn = Bool_val(vPauseOn);
SDL_PauseAudioDevice(device, pauseOn);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_QueueAudio(value vDevice, value vBuf, value vLen) {
CAMLparam3(vDevice, vBuf, vLen);
CAMLlocal1(ret);
SDL_AudioDeviceID device = Int_val(vDevice);
const void *buf = resdl_unwrapPointer(vBuf);
Uint32 len = Int_val(vLen);
if (SDL_QueueAudio(device, buf, len) < 0) {
ret = Val_error(caml_copy_string(SDL_GetError()));
CAMLreturn(ret);
}
ret = Val_ok(Val_unit);
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_ClearQueuedAudio(value vDevice) {
CAMLparam1(vDevice);
SDL_AudioDeviceID device = Int_val(vDevice);
SDL_ClearQueuedAudio(device);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_GetQueuedAudioSize(value vDevice) {
CAMLparam1(vDevice);
SDL_AudioDeviceID device = Int_val(vDevice);
Uint32 bytes = SDL_GetQueuedAudioSize(device);
CAMLreturn(Val_int(bytes));
}
CAMLprim value resdl_SDL_CreateRGBSurfaceFromImage(value vPath) {
CAMLparam1(vPath);
CAMLlocal1(ret);
// FROM:
// https://wiki.libsdl.org/SDL_CreateRGBSurfaceFrom
int req_format = STBI_rgb_alpha;
int width, height, orig_format;
unsigned char *data =
stbi_load(String_val(vPath), &width, &height, &orig_format, req_format);
if (data == NULL) {
ret = Val_error(caml_copy_string(stbi_failure_reason()));
} else {
// Set up the pixel format color masks for RGB(A) byte arrays.
// Only STBI_rgb (3) and STBI_rgb_alpha (4) are supported here!
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
int shift = (req_format == STBI_rgb) ? 8 : 0;
rmask = 0xff000000 >> shift;
gmask = 0x00ff0000 >> shift;
bmask = 0x0000ff00 >> shift;
amask = 0x000000ff >> shift;
#else // little endian, like x86
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = (req_format == STBI_rgb) ? 0 : 0xff000000;
#endif
int depth, pitch;
if (req_format == STBI_rgb) {
depth = 24;
pitch = 3 * width; // 3 bytes per pixel * pixels per row
} else { // STBI_rgb_alpha (RGBA)
depth = 32;
pitch = 4 * width;
}
SDL_Surface *surf = SDL_CreateRGBSurfaceFrom(
(void *)data, width, height, depth, pitch, rmask, gmask, bmask, amask);
if (surf == NULL) {
ret = Val_error(caml_copy_string(SDL_GetError()));
stbi_image_free(data);
} else {
ret = Val_ok(resdl_wrapPointer(surf));
}
}
CAMLreturn(ret);
};
CAMLprim value resdl_SDL_GL_SwapWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
caml_release_runtime_system();
SDL_GL_SwapWindow(win);
caml_acquire_runtime_system();
CAMLreturn(Val_unit);
}
SDL_HitTestResult hittest(SDL_Window *win, const SDL_Point *area, void *data) {
return SDL_HITTEST_DRAGGABLE;
}
CAMLprim value resdl_SDL_SetWindowSize(value vWin, value vW, value vH) {
CAMLparam3(vWin, vW, vH);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
int w = Int_val(vW);
int h = Int_val(vH);
SDL_SetWindowSize(win, w, h);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_SetWindowMinimumSize(value vWin, value vW, value vH) {
CAMLparam3(vWin, vW, vH);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
int w = Int_val(vW);
int h = Int_val(vH);
SDL_SetWindowMinimumSize(win, w, h);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_SetWindowTitle(value vWin, value vTitle) {
CAMLparam2(vWin, vTitle);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
const char *title = (const char *)String_val(vTitle);
SDL_SetWindowTitle(win, title);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_SetWindowPosition(value vWin, value vX, value vY) {
CAMLparam3(vWin, vX, vY);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
int x = Int_val(vX);
int y = Int_val(vY);
SDL_SetWindowPosition(win, x, y);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_WindowCenter(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_SetWindowPosition(win, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
CAMLreturn(Val_unit);
}
int resdl_eventWatcher(void *data, SDL_Event *event) {
if (event->type == SDL_WINDOWEVENT &&
event->window.event == SDL_WINDOWEVENT_RESIZED) {
value args[] = {Val_unit};
static const value *resizeCallback = NULL;
if (resizeCallback == NULL) {
resizeCallback = caml_named_value("__sdl2_caml_resize__");
}
if (resizeCallback) {
caml_callbackN(*resizeCallback, 1, args);
}
}
return 0;
}
CAMLprim value resdl_SDL_CreateWindow(value vName, value vX, value vY,
value vWidth, value vHeight, value vAcceleration) {
CAMLparam5(vName, vX, vY, vWidth, vHeight);
CAMLxparam1(vAcceleration);
CAMLlocal1(vWindow);
int x;
if (vX == hash_variant("Centered")) {
x = SDL_WINDOWPOS_CENTERED;
} else if (Is_block(vX) && Field(vX, 0) == hash_variant("Absolute")) {
x = Int_val(Field(vX, 1));
} else {
x = SDL_WINDOWPOS_UNDEFINED;
};
int y;
if (vY == hash_variant("Centered")) {
y = SDL_WINDOWPOS_CENTERED;
} else if (Is_block(vY) && Field(vY, 0) == hash_variant("Absolute")) {
y = Int_val(Field(vY, 1));
} else {
y = SDL_WINDOWPOS_UNDEFINED;
};
int width = Int_val(vWidth);
int height = Int_val(vHeight);
// According to the docs - `SDL_GL_SetAttribute` needs
// to be called prior to creating the window.
// Attributes pulled from:
// https://github.com/google/skia/blob/master/example/SkiaSDLExample.cpp
static const int kStencilBits = 8; // Skia needs 8 stencil bits
#ifdef WIN32
SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_EGL, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
#elif SDL_VIDEO_OPENGL
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#ifdef SDL_VIDEO_DRIVER_X11
// Disable compositing suppression - https://github.com/onivim/oni2/issues/2003
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
#endif
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, kStencilBits);
// If vAcceleration is Auto, don't set SDL_GL_ACCELERATED_VISUAL at all.
if (vAcceleration == hash_variant("ForceHardware")) {
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
} else if (vAcceleration == hash_variant("ForceSoftware")) {
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 0);
}
SDL_Window *win = (SDL_CreateWindow(
String_val(vName), x, y, width, height,
SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE));
if (!win) {
SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_CreateWindow failed: %s\n",
SDL_GetError());
}
// Only add the resize-event-watcher for OSX, since it's the platform that requires this to get
// real-time resize notifications + re-renders.
// This change caused a crash in Linux + Wayland: https://github.com/onivim/oni2/issues/3646
// (with the crash callstack pointing to an error acquiring runtime lock after rendering)
#ifdef __APPLE__
SDL_AddEventWatch(resdl_eventWatcher, NULL);
#endif
vWindow = resdl_wrapPointer(win);
CAMLreturn(vWindow);
}
CAMLprim value resdl_SDL_CreateWindow_byte(value *argv, int argn) {
return resdl_SDL_CreateWindow(
argv[0],
argv[1],
argv[2],
argv[3],
argv[4],
argv[5]
);
}
CAMLprim value resdl_SDL_SetWindowBordered(value vWin, value vBordered) {
CAMLparam2(vWin, vBordered);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_bool bordered = Int_val(vBordered) == 1 ? SDL_TRUE : SDL_FALSE;
SDL_SetWindowBordered(win, bordered);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_SetWindowResizable(value vWin, value vResizable) {
CAMLparam2(vWin, vResizable);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_bool resize = Int_val(vResizable) == 1 ? SDL_TRUE : SDL_FALSE;
SDL_SetWindowResizable(win, resize);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_HideWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_HideWindow(win);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_MaximizeWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_MaximizeWindow(win);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_IsWindowMaximized(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
Uint32 flags = SDL_GetWindowFlags(win);
bool hasMaximizedFlag = (flags & SDL_WINDOW_MAXIMIZED) != 0;
CAMLreturn(Val_bool(hasMaximizedFlag));
}
CAMLprim value resdl_SDL_IsWindowFullscreen(value vWin) {
CAMLparam1(vWin);
// SDL's fullscreen window flags don't work on macOS
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
bool isFullscreen;
#ifdef SDL_VIDEO_DRIVER_COCOA
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(win, &wmInfo);
NSWindow *nWindow = wmInfo.info.cocoa.window;
isFullscreen = (([nWindow styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen);
#else
Uint32 flags = SDL_GetWindowFlags(win);
isFullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0;
#endif
CAMLreturn(Val_bool(isFullscreen));
}
CAMLprim value resdl_SDL_MinimizeWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_MinimizeWindow(win);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_RaiseWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_RaiseWindow(win);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_RestoreWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_RestoreWindow(win);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_ShowWindow(value vWin) {
CAMLparam1(vWin);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);
SDL_ShowWindow(win);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_GetWindowId(value vWindow) {
CAMLparam1(vWindow);
SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);
int id = SDL_GetWindowID(win);
CAMLreturn(Val_int(id));
}
void resdl_onLog(void *unused, int category, SDL_LogPriority priority,
const char *message) {
CAMLparam0();
CAMLlocal1(messageString);
static const value *reason_sdl_onLog = NULL;
if (reason_sdl_onLog == NULL) {
reason_sdl_onLog = caml_named_value("reason_sdl2_onLog");
}
int iCategory, iPriority;
switch (category) {
case SDL_LOG_CATEGORY_APPLICATION:
iCategory = 0;
break;
case SDL_LOG_CATEGORY_ERROR:
iCategory = 1;
break;
case SDL_LOG_CATEGORY_ASSERT:
iCategory = 2;
break;
case SDL_LOG_CATEGORY_SYSTEM:
iCategory = 3;
break;
case SDL_LOG_CATEGORY_AUDIO:
iCategory = 4;
break;
case SDL_LOG_CATEGORY_VIDEO:
iCategory = 5;
break;
case SDL_LOG_CATEGORY_RENDER:
iCategory = 6;
break;
case SDL_LOG_CATEGORY_INPUT:
iCategory = 7;
break;
case SDL_LOG_CATEGORY_TEST:
iCategory = 8;
case SDL_LOG_CATEGORY_CUSTOM:
iCategory = 9;
break;
default:
iCategory = 10;
break;
}
switch (priority) {
case SDL_LOG_PRIORITY_VERBOSE:
iPriority = 0;
break;
case SDL_LOG_PRIORITY_DEBUG:
iPriority = 1;
break;
case SDL_LOG_PRIORITY_INFO:
iPriority = 2;
break;
case SDL_LOG_PRIORITY_WARN:
iPriority = 3;
break;
case SDL_LOG_PRIORITY_ERROR:
iPriority = 4;
break;
case SDL_LOG_PRIORITY_CRITICAL:
iPriority = 5;
break;
default:
iPriority = 0;
break;
}
messageString = caml_copy_string(message);
caml_callback3(*reason_sdl_onLog, Val_int(iCategory), Val_int(iPriority),
messageString);
CAMLreturn0;
}
CAMLprim value resdl_SDL_Init() {
CAMLparam0();
SDL_LogSetOutputFunction(&resdl_onLog, NULL);
int ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
if (ret < 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_Init failed: %s\n",
SDL_GetError());
}
CAMLreturn(Val_int(ret));
}
#if SDL_VIDEO_DRIVER_UIKIT
value resdl_uikit_main_callback;
int resdl_uikit_SDL_main (int argc, char *argv[]) {
caml_callback(resdl_uikit_main_callback, Val_unit);
return 0;
}
#endif
CAMLprim value resdl_SDL_main(value ml_argc, value ml_argv, value closure) {
CAMLparam3(ml_argc, ml_argv, closure);
CAMLlocal1(tmp);
#if SDL_VIDEO_DRIVER_UIKIT
int argc = Int_val(ml_argc);
char **argv = (char**)calloc(sizeof(char*), argc + 1);
for (int i = 0; i < argc; i++) {
argv[i] = (char*)String_val(Field(ml_argv, i));
}
resdl_uikit_main_callback = closure;
SDL_UIKitRunApp(argc, argv, resdl_uikit_SDL_main);
#else
caml_callback(closure, Val_unit);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_GetScancodeName(value vScancode) {
CAMLparam1(vScancode);
CAMLlocal1(ret);
SDL_Scancode scancode = (SDL_Scancode)Int_val(vScancode);
ret = caml_copy_string(SDL_GetScancodeName(scancode));
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetKeyFromName(value vKeyName) {
CAMLparam1(vKeyName);
int keycode = (int)SDL_GetKeyFromName(String_val(vKeyName));
CAMLreturn(Val_int(keycode));
}
CAMLprim value resdl_SDL_GetScancodeFromName(value vScancodeName) {
CAMLparam1(vScancodeName);
int scancode = (int)SDL_GetScancodeFromName(String_val(vScancodeName));
CAMLreturn(Val_int(scancode));
}
CAMLprim value resdl_SDL_GetKeyName(value vKey) {
CAMLparam1(vKey);
CAMLlocal1(ret);
SDL_Keycode key = (SDL_Keycode)Int_val(vKey);
ret = caml_copy_string(SDL_GetKeyName(key));
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetKeyFromScancode(value vScancode) {
SDL_Scancode scancode = (SDL_Scancode)Int_val(vScancode);
return Val_int(SDL_GetKeyFromScancode(scancode));
}
CAMLprim value resdl_SDL_GetScancodeFromKey(value vKey) {
SDL_Keycode key = (SDL_Keycode)Int_val(vKey);
return Val_int(SDL_GetScancodeFromKey(key));
}
CAMLprim value resdl_SDL_ModLeftShift(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_LSHIFT) == KMOD_LSHIFT);
};
CAMLprim value resdl_SDL_ModRightShift(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_RSHIFT) == KMOD_RSHIFT);
};
CAMLprim value resdl_SDL_ModLeftControl(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_LCTRL) == KMOD_LCTRL);
};
CAMLprim value resdl_SDL_ModRightControl(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_RCTRL) == KMOD_RCTRL);
};
CAMLprim value resdl_SDL_ModLeftAlt(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_LALT) == KMOD_LALT);
};
CAMLprim value resdl_SDL_ModRightAlt(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_RALT) == KMOD_RALT);
};
CAMLprim value resdl_SDL_ModLeftGui(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_LGUI) == KMOD_LGUI);
};
CAMLprim value resdl_SDL_ModRightGui(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_RGUI) == KMOD_RGUI);
};
CAMLprim value resdl_SDL_ModNumLockDown(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_NUM) == KMOD_NUM);
};
CAMLprim value resdl_SDL_ModCapsLockDown(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_CAPS) == KMOD_CAPS);
};
CAMLprim value resdl_SDL_ModAltGrDown(value vMod) {
return Val_bool((Int_val(vMod) & KMOD_MODE) == KMOD_MODE);
};
CAMLprim value resdl_SDL_GetModState(value vUnit) {
return Val_int(SDL_GetModState());
};
CAMLprim value resdl_SDL_SetModState(value vMod) {
SDL_SetModState((SDL_Keymod)Int_val(vMod));
return Val_unit;
};
CAMLprim value resdl_SDL_GetCompiledVersion(value vUnit) {
CAMLparam0();
CAMLlocal1(ret);
SDL_version compiled;
SDL_VERSION(&compiled);
ret = caml_alloc(3, 0);
Store_field(ret, 0, Val_int(compiled.major));
Store_field(ret, 1, Val_int(compiled.minor));
Store_field(ret, 2, Val_int(compiled.patch));
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_GetLinkedVersion(value vUnit) {
CAMLparam0();
CAMLlocal1(ret);
SDL_version linked;
SDL_GetVersion(&linked);
ret = caml_alloc(3, 0);
Store_field(ret, 0, Val_int(linked.major));
Store_field(ret, 1, Val_int(linked.minor));
Store_field(ret, 2, Val_int(linked.patch));
CAMLreturn(ret);
}
CAMLprim value resdl_SDL_ShowSimpleMessageBox(value vFlags, value vTitle,
value vMessage, value vWindow) {
CAMLparam4(vFlags, vTitle, vMessage, vWindow);
int flags = SDL_MESSAGEBOX_INFORMATION;
switch (Int_val(vFlags)) {
case 0:
flags = SDL_MESSAGEBOX_ERROR;
break;
case 1:
flags = SDL_MESSAGEBOX_WARNING;
break;
default:
flags = SDL_MESSAGEBOX_INFORMATION;
break;
}
const char *title = String_val(vTitle);
const char *msg = String_val(vMessage);
SDL_Window *win = NULL;
if (Is_block(vWindow)) {
win = (SDL_Window *)resdl_unwrapPointer(Field(vWindow, 0));
}
SDL_ShowSimpleMessageBox(flags, title, msg, win);
CAMLreturn(Val_unit);
}
CAMLprim value resdl_SDL_CaptureMouse(value vEnabled) {
CAMLparam0();
SDL_bool enabled = Int_val(vEnabled) == 1 ? SDL_TRUE : SDL_FALSE;
CAMLreturn(Val_int(SDL_CaptureMouse(enabled)));
}
CAMLprim value resdl_SDL_GetGlobalMouseState(value vUnit) {
CAMLparam0();
CAMLlocal1(ret);
int x, y;
SDL_GetGlobalMouseState(&x, &y);
ret = caml_alloc(2, 0);
Store_field(ret, 0, Val_int(x));
Store_field(ret, 1, Val_int(y));
CAMLreturn(ret);
}
CAMLprim value resdl_PassThrough(value v) {
return v;
};
CAMLprim value resdl__javascript__renderloop() {
return Val_unit;
}
};
================================================
FILE: packages/reason-sdl2/src/stb_image.cpp
================================================
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
================================================
FILE: packages/reason-skia/bench/BenchFramework.re
================================================
include Reperf.Make({
let config = Reperf.Config.create(~snapshotDir="bench/__snapshots__", ());
});
================================================
FILE: packages/reason-skia/bench/CanvasBench.re
================================================
open BenchFramework;
open Skia;
let options = Reperf.Options.create(~iterations=100000, ());
module Data = {
let makeSurface = (width, height) => {
let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);
Surface.makeRaster(imageInfo, 0, None);
};
let surface = makeSurface(800l, 600l) |> Option.get;
let canvas = Surface.getCanvas(surface);
let paint = Paint.make();
};
let drawRectLtwh = () => {
Skia.Canvas.drawRectLtwh(Data.canvas, 1.0, 2.0, 100., 200., Data.paint);
};
bench(
~name="Canvas: drawRectLtwh",
~options,
~setup=() => (),
~f=drawRectLtwh,
(),
);
================================================
FILE: packages/reason-skia/bench/ColorBench.re
================================================
open BenchFramework;
let options = Reperf.Options.create(~iterations=100000, ());
module Data = {
let initialColor = Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl);
};
let makeArgb = () => {
let _: Skia.Color.t = Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl);
();
};
let makeArgbFloat = () => {
let _: Skia.Color.t = Skia.Color.Float.makeArgb(0.1, 0.2, 0.3, 0.4);
();
};
let getA = () => {
let _: int32 = Skia.Color.getA(Data.initialColor);
();
};
let getAFloat = () => {
let _: float = Skia.Color.Float.getA(Data.initialColor);
();
};
bench(
~name="Color: makeArgb (float)",
~options,
~setup=() => (),
~f=makeArgbFloat,
(),
);
bench(
~name="Color: getA (float)",
~options,
~setup=() => (),
~f=getAFloat,
(),
);
bench(~name="Color: makeArgb", ~options, ~setup=() => (), ~f=makeArgb, ());
bench(~name="Color: getA", ~options, ~setup=() => (), ~f=makeArgb, ());
================================================
FILE: packages/reason-skia/bench/MatrixBench.re
================================================
open BenchFramework;
let options = Reperf.Options.create(~iterations=100000, ());
module Data = {
let initialMatrix = Skia.Matrix.make();
let scale = {
let mat = Skia.Matrix.make();
Skia.Matrix.setScale(mat, 1.0, 2.0, 3.0, 4.0);
mat;
};
let translate = {
let mat = Skia.Matrix.make();
Skia.Matrix.setTranslate(mat, 1.0, 2.0);
mat;
};
let out = Skia.Matrix.make();
let rect = Skia.Rect.makeLtrb(1., 1., 100., 200.);
let outRect = Skia.Rect.makeLtrb(0., 0., 0., 0.);
};
let setScale = () => {
let () = Skia.Matrix.setScale(Data.initialMatrix, 1.0, 2.0, 1.0, 1.0);
();
};
let setTranslate = () => {
let () = Skia.Matrix.setTranslate(Data.initialMatrix, 1.0, 2.0);
();
};
let concat = () => {
let () = Skia.Matrix.concat(Data.out, Data.scale, Data.translate);
();
};
let preConcat = () => {
let () = Skia.Matrix.preConcat(Data.out, Data.scale);
();
};
let postConcat = () => {
let () = Skia.Matrix.postConcat(Data.out, Data.translate);
();
};
let mapRect = () => {
let () = Skia.Matrix.mapRect(Data.scale, Data.rect, Data.outRect);
();
};
bench(~name="Matrix: mapRect", ~options, ~setup=() => (), ~f=mapRect, ());
bench(~name="Matrix: preConcat", ~options, ~setup=() => (), ~f=preConcat, ());
bench(
~name="Matrix: postConcat",
~options,
~setup=() => (),
~f=postConcat,
(),
);
bench(~name="Matrix: concat", ~options, ~setup=() => (), ~f=concat, ());
bench(
~name="Matrix: setTranslate",
~options,
~setup=() => (),
~f=setTranslate,
(),
);
bench(~name="Matrix: setScale", ~options, ~setup=() => (), ~f=setScale, ());
================================================
FILE: packages/reason-skia/bench/RectBench.re
================================================
open BenchFramework;
let options = Reperf.Options.create(~iterations=100000, ());
module Data = {
let initialRect = Skia.Rect.makeLtrb(0., 0., 0., 0.);
};
let setLtrb = () => {
let () =
Skia.Rect.Mutable.setLtrb(~out=Data.initialRect, 1.0, 2.0, 3.0, 4.0);
();
};
let makeLtrb = () => {
let _: Skia.Rect.t = Skia.Rect.makeLtrb(1.0, 2.0, 3.0, 4.0);
();
};
let getLeft = () => {
let _: float = Skia.Rect.getLeft(Data.initialRect);
();
};
bench(~name="Rect: makeLtrb", ~options, ~setup=() => (), ~f=makeLtrb, ());
bench(~name="Rect: setLtrb", ~options, ~setup=() => (), ~f=setLtrb, ());
bench(~name="Rect: getLeft", ~options, ~setup=() => (), ~f=getLeft, ());
================================================
FILE: packages/reason-skia/bench/SkiaPaintBench.re
================================================
open BenchFramework;
open Skia;
let options = Reperf.Options.create(~iterations=100000, ());
module Data = {
let initialPaint = Paint.make();
let initialColor = Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl);
};
let make = () => {
let _: Paint.t = Paint.make();
();
};
let setColor = () => {
let () = Paint.setColor(Data.initialPaint, Data.initialColor);
();
};
bench(~name="Paint: make", ~options, ~setup=() => (), ~f=make, ());
bench(~name="Paint: setColor", ~options, ~setup=() => (), ~f=setColor, ());
================================================
FILE: packages/reason-skia/bench/dune
================================================
(library
(name Skia_Bench)
(ocamlopt_flags -linkall)
(libraries reason-skia reason-skia.wrapped reason-skia.wrapped.bindings
reperf.lib))
================================================
FILE: packages/reason-skia/examples/skia-cli/LICENSE_FiraCode.txt
================================================
Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
================================================
FILE: packages/reason-skia/examples/skia-cli/LICENSE_ORBITRON.md
================================================
Copyright (c) 2009, Matt McInerney , with Reserved Font Name: "Orbitron".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
Version 1.1 - 26 February 2007
SIL Open Font License
====================================================
Preamble
----------
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
Definitions
-------------
`"Font Software"` refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
`"Reserved Font Name"` refers to any names specified as such after the
copyright statement(s).
`"Original Version"` refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
`"Modified Version"` refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
`"Author"` refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
Permission & Conditions
------------------------
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1. Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2. Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3. No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5. The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
Termination
-----------
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
================================================
FILE: packages/reason-skia/examples/skia-cli/SkiaCli.re
================================================
open Skia;
let makeSurface = (width, height) => {
let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);
Surface.makeRaster(imageInfo, 0, None);
};
let emitPng = (path, surface) => {
let image = Surface.makeImageSnapshot(surface);
let data = Image.encodeToData(image);
let dataString = Data.makeString(data);
let fileOutputChannel = open_out_bin(path);
output_string(fileOutputChannel, dataString);
close_out(fileOutputChannel);
};
let draw = canvas => {
let fill = Paint.make();
Paint.setColor(fill, Color.makeArgb(0xFFl, 0x00l, 0x00l, 0xFFl));
Canvas.drawPaint(canvas, fill);
Paint.setColor(fill, Color.makeArgb(0xFFl, 0x00l, 0xFFl, 0xFFl));
let rect = Rect.makeLtrb(100., 100., 540., 380.);
Canvas.drawRect(canvas, rect, fill);
let stroke = Paint.make();
Paint.setColor(stroke, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l));
Paint.setAntiAlias(stroke, true);
Paint.setStyle(stroke, Stroke);
Paint.setStrokeWidth(stroke, 5.);
let path = Path.make();
Path.moveTo(path, 50., 50.);
Path.lineTo(path, 590., 50.);
Path.cubicTo(path, -490., 50., 1130., 430., 50., 430.);
Path.lineTo(path, 590., 430.);
Path.close(path);
Path.addCircle(path, 100., 200., ~radius=75., ());
let roundRect = Rect.makeLtrb(300., 400., 240., 280.);
Path.addRoundRect(path, roundRect, 25., 25., ());
Path.addCircle(path, 100., 300., ~radius=100., ~direction=`cw, ());
Canvas.drawPath(canvas, path, stroke);
Paint.setColor(fill, Color.makeArgb(0xCCl, 0x00l, 0xFFl, 0x00l));
Paint.setImageFilter(
fill,
Some(
ImageFilter.makeDropShadow(
10.,
10.,
3.,
3.,
Color.makeArgb(0xAAl, 0x00l, 0x00l, 0x00l),
DrawShadowAndForeground,
None,
None,
),
),
);
let rect2 = Rect.makeLtrb(120., 120., 520., 360.);
let ovalPaint = Skia.Paint.make();
Skia.Paint.setColor(
ovalPaint,
Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0x00l),
);
let innerPath = Skia.Path.make();
Skia.Path.lineTo(innerPath, 5., 5.);
Skia.Path.lineTo(innerPath, 15., -5.);
Skia.Path.lineTo(innerPath, 20., 0.);
let translate = Skia.Matrix.makeScale(20., 20., 20., 20.);
Skia.Paint.setAntiAlias(ovalPaint, true);
Skia.Paint.setStyle(ovalPaint, Stroke);
Skia.Paint.setStrokeWidth(ovalPaint, 1.);
let pathEffect = Skia.PathEffect.create2dPath(~matrix=translate, innerPath);
Skia.Paint.setPathEffect(ovalPaint, pathEffect);
Canvas.drawOval(canvas, rect2, ovalPaint);
let fill3 = Paint.make();
Paint.setColor(fill3, Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl));
Paint.setTextSize(fill3, 30.);
let nonExistentTypeface = Typeface.makeFromFile("non-existent-font.ttf", 0);
assert(nonExistentTypeface == None);
// Draw text
let filePath =
Sys.getcwd()
++ "/packages/reason-skia/examples/skia-cli/Orbitron Medium.ttf";
print_endline("Loading font: " ++ filePath);
let maybeTypeface = Typeface.makeFromFile(filePath, 0);
switch (maybeTypeface) {
| None => failwith("Unable to load font: " ++ filePath)
| Some(typeFace) =>
print_endline(__LOC__ ++ ": we will set.");
Paint.setTypeface(fill3, typeFace);
print_endline(__LOC__ ++ ": setTypeface is OK.");
Canvas.drawText(canvas, "Hello, world!", 30., 30., fill3);
let metrics = FontMetrics.make();
let _ret: float = Paint.getFontMetrics(fill3, metrics, 1.0);
print_endline(
"-- Top: " ++ string_of_float(FontMetrics.getTop(metrics)),
);
print_endline(
"-- Bottom: " ++ string_of_float(FontMetrics.getBottom(metrics)),
);
print_endline(
"-- Underline position: "
++ string_of_float(FontMetrics.getUnderlinePosition(metrics)),
);
print_endline(
"-- Underline thickness: "
++ string_of_float(FontMetrics.getUnderlineThickness(metrics)),
);
print_endline("-- Ret: " ++ string_of_float(_ret));
print_endline(__LOC__ ++ ": We return.");
// Measure
let measurement = Paint.measureText(fill3, "Hello, world!", None);
print_endline("Measured text: " ++ string_of_float(measurement));
Paint.setTextSize(fill3, 50.);
let largeMeasurement = Paint.measureText(fill3, "Hello, world!", None);
print_endline(
"Large measured text: " ++ string_of_float(largeMeasurement),
);
};
// Turn off drop shadow
Paint.setImageFilter(fill, None);
// Validate loading a non-existent file returns None, but doesn't crash
let nonExistentData = Data.makeFromFileName("file-that-does-not-exist.png");
switch (nonExistentData) {
| Some(_) => failwith("Somehow we loaded a non-existent file...")
| None => print_endline("Got None for file that doesn't exist.")
};
// Load and draw image
let imgPath = Sys.getcwd() ++ "/assets/uv.png";
print_endline("Loading image: " ++ imgPath);
let maybeImgData = Data.makeFromFileName(imgPath);
let maybeImg =
switch (maybeImgData) {
| Some(imgData) =>
let strLen = String.length(Data.makeString(imgData));
print_endline("Bytes loaded: " ++ string_of_int(strLen));
let ret = Image.makeFromEncoded(imgData, None);
print_endline("Got image!");
ret;
| None => failwith("Unable to load img: " ++ imgPath)
};
switch (maybeImg) {
| None => failwith("Unable to load image: uv.png")
| Some(img) =>
print_endline(
Printf.sprintf(
"%s Image dimensions: %dx%d",
imgPath,
Skia.Image.width(img),
Skia.Image.height(img),
),
);
let imgFill = Paint.make();
Paint.setAlpha(imgFill, 0.0);
Canvas.drawImage(canvas, img, 250., 250., Some(imgFill));
let sourceRect = Rect.makeLtrb(0., 0., 128., 128.);
let destRect = Rect.makeLtrb(200., 200., 264., 264.);
Canvas.drawImageRect(
canvas,
img,
Some(sourceRect),
destRect,
Some(imgFill),
);
};
// Draw text w/ ligature
let filePath =
Sys.getcwd()
++ "/packages/reason-skia/examples/skia-cli/FiraCode-Regular.ttf";
print_endline("Loading font: " ++ filePath);
let maybeTypeface = Typeface.makeFromFile(filePath, 0);
switch (maybeTypeface) {
| None => failwith("Unable to load font: " ++ filePath)
| Some(typeFace) =>
let fill = Paint.make();
Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl));
Paint.setTextSize(fill, 30.);
Paint.setTypeface(fill, typeFace);
Paint.setSubpixelText(fill, true);
Paint.setTextEncoding(fill, GlyphId);
let glyphsToString = glyphs => {
let len = List.length(glyphs);
let bytes = Bytes.create(len * 2);
let rec loop = (glyphs, idx) => {
switch (glyphs) {
| [hd, ...tail] =>
let lowerBit = hd land 255;
let highBit = (hd land 255 lsl 8) lsr 8;
Bytes.set(bytes, idx * 2 + 0, Char.chr(lowerBit));
Bytes.set(bytes, idx * 2 + 1, Char.chr(highBit));
loop(tail, idx + 1);
| [] => ()
};
};
loop(glyphs, 0);
Bytes.to_string(bytes);
};
// For FiraCode, this is a==>b
let str = glyphsToString([136, 1624, 1624, 1495, 148]);
Canvas.drawText(canvas, str, 50., 100., fill);
};
let fill = Paint.make();
Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0xFFl));
Canvas.drawRectLtwh(canvas, 50., 75., 100., 200., fill);
let fill = Paint.make();
Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l));
Canvas.drawCircle(canvas, 320., 240., 30., fill);
// Creating empty shader
let _emptyShader = Skia.Shader.makeEmpty();
let red = Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l);
let green = Color.makeArgb(0xFFl, 0x00l, 0xFFl, 0x00l);
let blue = Color.makeArgb(0xFFl, 0x00l, 0x00l, 0xFFl);
// Creating a 2-stop linear gradient
let linearGradient2 =
Skia.Shader.makeLinearGradient2(
~startPoint=Skia.Point.make(0.0, 0.0),
~stopPoint=Skia.Point.make(100.0, 100.0),
~startColor=red,
~stopColor=blue,
~tileMode=`repeat,
);
let fill = Paint.make();
Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l));
Paint.setShader(fill, linearGradient2);
Canvas.drawRectLtwh(canvas, 0., 0., 100., 100., fill);
// Creating a 3-stop linear gradient
let linearGradient3 =
Skia.Shader.makeLinearGradient(
~startPoint=Skia.Point.make(0.0, 0.0),
~stopPoint=Skia.Point.make(100.0, 0.0),
~colorStops=
Skia.Shader.[
{color: red, position: 0.0},
{color: blue, position: 0.25},
{color: green, position: 1.0},
],
~tileMode=`repeat,
);
let fill = Paint.make();
Paint.setShader(fill, linearGradient3);
Canvas.drawRectLtwh(canvas, 100., 100., 100., 100., fill);
};
let drawSvg = canvas => {
let drawFromFile = dX => {
let stream = Stream.makeFileStream("./assets/revery.svg") |> Option.get;
let svg = SVG.makeFromStream(stream) |> Option.get;
Printf.printf(
"SVG: file container size: w=%f h=%f\n",
SVG.getContainerWidth(svg),
SVG.getContainerHeight(svg),
);
Canvas.translate(canvas, dX, 0.);
SVG.render(svg, canvas);
SVG.getContainerWidth(svg);
};
let drawFromString = dX => {
let svgStr = {|
|};
let stream =
Stream.makeMemoryStreamFromString(svgStr, String.length(svgStr));
let svg = SVG.makeFromStream(stream) |> Option.get;
Printf.printf(
"SVG: string container size: w=%f h=%f\n",
SVG.getContainerWidth(svg),
SVG.getContainerHeight(svg),
);
Canvas.translate(canvas, dX, 0.);
SVG.render(svg, canvas);
SVG.getContainerWidth(svg);
};
drawFromFile(0.) |> drawFromString |> ignore;
};
let surface = makeSurface(640l, 480l) |> Option.get;
let svgSurface = makeSurface(1280l, 1280l) |> Option.get;
let canvas = Surface.getCanvas(surface);
let svgCanvas = Surface.getCanvas(svgSurface);
draw(canvas);
drawSvg(svgCanvas);
emitPng("skia-c-example.png", surface);
emitPng("skia-svg-example.png", svgSurface);
Gc.full_major();
print_endline("Done!");
================================================
FILE: packages/reason-skia/examples/skia-cli/dune
================================================
(executables
(names SkiaCli)
(package ReveryExamples)
(public_names SkiaCli)
(modes native byte)
(libraries reason-skia reason-skia.wrapped.bindings reason-skia.wrapped
reason-native-crash-utils.asan))
(install
(section bin)
(package ReveryExamples)
(files SkiaCli.bc))
================================================
FILE: packages/reason-skia/examples/skia-font-manager-cli/SkiaFontManagerCli.re
================================================
open Skia;
let emitPng = (path, surface) => {
let image = Surface.makeImageSnapshot(surface);
let data = Image.encodeToData(image);
let dataString = Data.makeString(data);
let fileOutputChannel = open_out_bin(path);
output_string(fileOutputChannel, dataString);
close_out(fileOutputChannel);
};
let makeSurface = (width, height) => {
let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);
Surface.makeRaster(imageInfo, 0, None);
};
let draw = canvas => {
let fontManager = FontManager.makeDefault();
let style = FontStyle.make(400, 5, Upright);
let maybeTypeface =
FontManager.matchFamilyStyle(fontManager, "Arial", style);
let fill = Paint.make();
Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl));
Paint.setTextSize(fill, 16.);
Paint.setSubpixelText(fill, true);
switch (maybeTypeface) {
| None =>
print_endline("Normal Arial not found. Ensure you have it available.")
| Some(typeface) =>
Paint.setTypeface(fill, typeface);
Canvas.drawText(canvas, "Arial (System)", 10., 20., fill);
let stream = Typeface.toStream(typeface);
let length = Stream.getLength(stream);
Printf.printf("Stream length: %d\n", length);
let data = Data.makeFromStream(stream, length);
let oc = open_out("Arial.ttf");
print_endline("Writing Arial Normal to Arial.ttf...");
Printf.fprintf(oc, "%s", Data.makeString(data));
print_endline("Written!");
close_out(oc);
};
let maybeTypeface =
FontManager.matchFamilyStyle(fontManager, "Times New Roman", style);
switch (maybeTypeface) {
| None =>
print_endline(
"Normal Times New Roman not found. Ensure you have it available.",
)
| Some(typeface) =>
Paint.setTypeface(fill, typeface);
Canvas.drawText(canvas, "Times New Roman (System)", 10., 40., fill);
};
let maybeTypeface =
FontManager.matchFamilyStyle(fontManager, "Consolas", style);
switch (maybeTypeface) {
| None =>
print_endline(
"Normal Consolas not found. Ensure your system has it available.",
)
| Some(typeface) =>
Paint.setTypeface(fill, typeface);
let metrics = FontMetrics.make();
let _ret: float = Paint.getFontMetrics(fill, metrics, 1.0);
print_endline("__Consolas__");
print_endline(
"-- Average character width: "
++ string_of_float(FontMetrics.getAvgCharacterWidth(metrics)),
);
print_endline(
"-- Maximum character width: "
++ string_of_float(FontMetrics.getMaxCharacterWidth(metrics)),
);
};
let filePath =
Sys.getcwd()
++ "/packages/reason-skia/examples/skia-cli/FiraCode-Regular.ttf";
print_endline("Loading font: " ++ filePath);
let maybeTypeface = Typeface.makeFromFile(filePath, 0);
switch (maybeTypeface) {
| None => failwith("Unable to load font: " ++ filePath)
| Some(typeface) =>
Paint.setTypeface(fill, typeface);
let metrics = FontMetrics.make();
let _ret: float = Paint.getFontMetrics(fill, metrics, 1.0);
print_endline("__Fira Code__");
print_endline(
"-- Average character width: "
++ string_of_float(FontMetrics.getAvgCharacterWidth(metrics)),
);
print_endline(
"-- Maximum character width: "
++ string_of_float(FontMetrics.getMaxCharacterWidth(metrics)),
);
};
let emoji = "😃";
let char = Zed_utf8.get(emoji, 0);
let maybeTypeface =
FontManager.matchFamilyStyleCharacter(
fontManager,
"Arial",
style,
["en_US"],
char,
);
switch (maybeTypeface) {
| Some(typeface) =>
print_endline(
"Found font for emoji: " ++ Typeface.getFamilyName(typeface),
);
Paint.setTypeface(fill, typeface);
Canvas.drawText(canvas, emoji, 10., 60., fill);
| None => print_endline("No emoji font found")
};
let japanese = "鬼";
let char = Zed_utf8.get(japanese, 0);
let maybeTypeface =
FontManager.matchFamilyStyleCharacter(
fontManager,
"Arial",
style,
["en_US"],
char,
);
switch (maybeTypeface) {
| Some(typeface) =>
print_endline(
"Found font for Japanese: " ++ Typeface.getFamilyName(typeface),
);
Paint.setTypeface(fill, typeface);
Canvas.drawText(canvas, japanese, 10., 80., fill);
| None => print_endline("No Japanese font found")
};
};
let surface = makeSurface(640l, 480l) |> Option.get;
let canvas = Surface.getCanvas(surface);
draw(canvas);
emitPng("skia-font-manager-output.png", surface);
print_endline("Done!");
================================================
FILE: packages/reason-skia/examples/skia-font-manager-cli/dune
================================================
(executables
(names SkiaFontManagerCli)
(package ReveryExamples)
(public_names SkiaFontManagerCli)
(modes native byte)
(libraries reason-skia reason-native-crash-utils.asan
reason-skia.wrapped.bindings reason-skia.wrapped Revery.zed))
(install
(section bin)
(package ReveryExamples)
(files SkiaFontManagerCli.bc run-skia-fontmanager.sh))
================================================
FILE: packages/reason-skia/examples/skia-font-manager-cli/run-skia-fontmanager.sh
================================================
set -e
CWD=$(dirname $0)
if [[ $1 == 'windows' ]]; then executable="SkiaFontManagerCli.exe"; else executable="SkiaFontManagerCli"; fi
LSAN_OPTIONS=suppressions=lsan.supp $CWD/$executable
================================================
FILE: packages/reason-skia/examples/skia-sdl2/SkiaSdl.re
================================================
open Skia;
let ctx = ref(None);
let printEnv = env =>
switch (Sys.getenv_opt(env)) {
| None => print_endline(env ++ " : not defined ")
| Some(v) => print_endline(env ++ " : " ++ v)
};
printEnv("WAYLAND_DISPLAY");
printEnv("XDG_SESSION_TYPE");
let createSkiaGraphicsContext = (_window: Sdl2.Window.t) => {
print_endline("Creating graphics context");
let nativeInterface = Skia.Gr.Gl.Interface.makeNative();
switch (nativeInterface) {
| Some(_) => print_endline("Native interface created successfully.")
| None => print_endline("Native interface is null")
};
let interface = Skia.Gr.Gl.Interface.(Sys.win32 ? makeSdl2ES() : makeSdl2());
print_endline("Have interface...");
let context = Skia.Gr.Context.makeGl(interface);
switch (context) {
| None => failwith("Unable to create graphics context")
| Some(glContext) =>
print_endline("glContext created successfully");
ctx := Some(glContext);
glContext;
};
};
let createSkiaSurface =
(window: Sdl2.Window.t, skiaContext: Skia.Gr.Context.t) => {
let framebufferInfo =
Gr.Gl.FramebufferInfo.make(
Unsigned.UInt.of_int(0),
Unsigned.UInt.of_int(0x8058),
);
let framebufferSize = Sdl2.Gl.getDrawableSize(window);
let backendRenderTarget =
Gr.BackendRenderTarget.makeGl(
framebufferSize.width,
framebufferSize.height,
0,
8,
framebufferInfo,
);
let surfaceProps = SurfaceProps.make(Unsigned.UInt32.of_int(0), RgbH);
switch (
Surface.makeFromBackendRenderTarget(
skiaContext,
backendRenderTarget,
BottomLeft,
Rgba8888,
None,
Some(surfaceProps),
)
) {
| None => failwith("Unable to create skia surface.")
| Some(surface) =>
Printf.printf(
"Successfully created canvas: %dx%d\n",
framebufferSize.width,
framebufferSize.height,
);
surface;
};
};
let run = () => {
let _ = Sdl2.init();
let attachResult = Sdl2.Platform.win32AttachConsole();
// If we were unable to attach a console, try allocating a new one
let _code =
if (attachResult == 0) {
Sdl2.Platform.win32AllocConsole();
} else {
attachResult;
};
let primaryWindow =
Sdl2.Window.create("test", `Undefined, `Undefined, 100, 100, `Auto);
let glContext = Sdl2.Gl.setup(primaryWindow);
Sdl2.Gl.setSwapInterval(1);
Sdl2.Window.setTitle(primaryWindow, "reason-skia-sdl2 example");
Sdl2.Window.setWin32ProcessDPIAware(primaryWindow);
let _scale = Sdl2.Window.getWin32ScaleFactor(primaryWindow);
let _display = Sdl2.Window.getDisplay(primaryWindow);
Sdl2.Window.setSize(primaryWindow, 800, 600);
Sdl2.Window.center(primaryWindow);
Sdl2.Window.show(primaryWindow);
Sdl2.Window.setResizable(primaryWindow, true);
Sdl2.Window.setMinimumSize(primaryWindow, 200, 100);
Sdl2.Gl.makeCurrent(primaryWindow, glContext);
let skiaContext = createSkiaGraphicsContext(primaryWindow);
let skiaSurface = createSkiaSurface(primaryWindow, skiaContext);
let canvas = Skia.Surface.getCanvas(skiaSurface);
let render = (window, context, surface) => {
//print_endline("-- Render: start");
ignore(context);
ignore(surface);
let color = Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l);
Skia.Canvas.clear(canvas, color);
let paint = Skia.Paint.make();
Skia.Paint.setColor(
paint,
Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl),
);
Skia.Canvas.drawText(canvas, "Hello, world!", 50., 50., paint);
Skia.Canvas.flush(canvas);
Sdl2.Gl.swapWindow(window);
//print_endline("-- Render: end");
};
//let frame = ref(0);
Sdl2.renderLoop(() => {
switch (Sdl2.Event.poll()) {
| None => ()
| Some(evt) =>
switch (evt) {
| Sdl2.Event.Quit => exit(0)
| _ => ()
}
};
render(primaryWindow, skiaContext, skiaSurface);
Gc.full_major();
false;
});
Lwt.return();
};
run();
================================================
FILE: packages/reason-skia/examples/skia-sdl2/dune
================================================
(executables
(names SkiaSdl)
(package ReveryExamples)
(public_names SkiaSdl)
(modes native byte)
(libraries reason-skia sdl2 lwt integers reason-skia.wrapped.bindings
reason-skia.wrapped))
(install
(section bin)
(package ReveryExamples)
(files SkiaSdl.bc))
================================================
FILE: packages/reason-skia/src/Skia.re
================================================
type colorType = SkiaWrapped.colorType;
type alphaType = SkiaWrapped.alphaType;
module Color = {
type t = int32;
[@noalloc]
external makeArgb:
([@unboxed] int32, [@unboxed] int32, [@unboxed] int32, [@unboxed] int32) =>
[@unboxed] int32 =
"reason_skia_stub_sk_color_set_argb_byte"
"reason_skia_stub_sk_color_set_argb";
[@noalloc]
external getA: ([@unboxed] int32) => [@unboxed] int32 =
"reason_skia_stub_sk_color_get_a_byte" "reason_skia_stub_sk_color_get_a";
[@noalloc]
external getR: ([@unboxed] int32) => [@unboxed] int32 =
"reason_skia_stub_sk_color_get_r_byte" "reason_skia_stub_sk_color_get_r";
[@noalloc]
external getG: ([@unboxed] int32) => [@unboxed] int32 =
"reason_skia_stub_sk_color_get_g_byte" "reason_skia_stub_sk_color_get_g";
[@noalloc]
external getB: ([@unboxed] int32) => [@unboxed] int32 =
"reason_skia_stub_sk_color_get_b_byte" "reason_skia_stub_sk_color_get_b";
module Float = {
external makeArgb:
(
[@unboxed] float,
[@unboxed] float,
[@unboxed] float,
[@unboxed] float
) =>
[@unboxed] int32 =
"reason_skia_color_float_make_argb_byte"
"reason_skia_color_float_make_argb";
[@noalloc]
external getA: ([@unboxed] int32) => [@unboxed] float =
"reason_skia_stub_sk_color_float_get_a_byte"
"reason_skia_stub_sk_color_float_get_a";
external getR: ([@unboxed] int32) => [@unboxed] float =
"reason_skia_stub_sk_color_float_get_r_byte"
"reason_skia_stub_sk_color_float_get_r";
external getG: ([@unboxed] int32) => [@unboxed] float =
"reason_skia_stub_sk_color_float_get_g_byte"
"reason_skia_stub_sk_color_float_get_g";
external getB: ([@unboxed] int32) => [@unboxed] float =
"reason_skia_stub_sk_color_float_get_b_byte"
"reason_skia_stub_sk_color_float_get_b";
};
};
module FontMetrics = {
type t = SkiaWrapped.FontMetrics.t;
let make = SkiaWrapped.FontMetrics.make;
let getAscent = SkiaWrapped.FontMetrics.getAscent;
let getDescent = SkiaWrapped.FontMetrics.getDescent;
let getTop = SkiaWrapped.FontMetrics.getTop;
let getBottom = SkiaWrapped.FontMetrics.getBottom;
let getUnderlineThickness = SkiaWrapped.FontMetrics.getUnderlineThickness;
let getUnderlinePosition = SkiaWrapped.FontMetrics.getUnderlinePosition;
let getAvgCharacterWidth = SkiaWrapped.FontMetrics.getAvgCharacterWidth;
let getMaxCharacterWidth = SkiaWrapped.FontMetrics.getMaxCharacterWidth;
};
module FilterQuality = {
type t = SkiaWrapped.FilterQuality.t;
};
module Hinting = {
type t = SkiaWrapped.Hinting.t;
};
module TextEncoding = {
type t = SkiaWrapped.TextEncoding.t;
};
module ImageFilter = {
type t = SkiaWrapped.ImageFilter.t;
module CropRect = {
type t = SkiaWrapped.ImageFilter.CropRect.t;
};
module DropShadow = {
type shadowMode = SkiaWrapped.ImageFilter.DropShadow.shadowMode;
let make =
(
dx,
dy,
sigmaX,
sigmaY,
color,
shadowMode,
inputOption,
cropRectOption,
) => {
let imageFilter =
SkiaWrapped.ImageFilter.DropShadow.allocate(
dx,
dy,
sigmaX,
sigmaY,
// TODO: Make fast
Unsigned.UInt32.of_int32(color),
shadowMode,
inputOption,
cropRectOption,
);
Gc.finalise(SkiaWrapped.ImageFilter.delete, imageFilter);
imageFilter;
};
};
let makeDropShadow = DropShadow.make;
};
module Paint = {
type t = SkiaWrapped.Paint.t;
type style = SkiaWrapped.Paint.style;
module CI = Cstubs_internals;
let make = () => {
let paint = SkiaWrapped.Paint.allocate();
Gc.finalise(SkiaWrapped.Paint.delete, paint);
paint;
};
let measureText = (paint, text, rectOpt) => {
SkiaWrapped.Paint.measureText(paint, text, String.length(text), rectOpt);
};
[@noalloc]
external _setColor: (CI.fatptr(_), [@unboxed] int32) => unit =
"reason_skia_paint_set_color_byte" "reason_skia_paint_set_color";
[@noalloc]
external _setAlphaf: (CI.fatptr(_), [@unboxed] float) => unit =
"reason_skia_paint_set_alphaf_byte" "reason_skia_paint_set_alphaf";
let setColor = (paint, color) => _setColor(CI.cptr(paint), color);
let setAlpha = (paint, alpha) => _setAlphaf(CI.cptr(paint), alpha);
let getFilterQuality = SkiaWrapped.Paint.getFilterQuality;
let setFilterQuality = SkiaWrapped.Paint.setFilterQuality;
let setHinting = SkiaWrapped.Paint.setHinting;
let getHinting = SkiaWrapped.Paint.getHinting;
let isAutohinted = SkiaWrapped.Paint.isAutohinted;
let setAutohinted = SkiaWrapped.Paint.setAutohinted;
let setAntiAlias = SkiaWrapped.Paint.setAntiAlias;
let setStyle = SkiaWrapped.Paint.setStyle;
let setStrokeWidth = SkiaWrapped.Paint.setStrokeWidth;
let setLcdRenderText = SkiaWrapped.Paint.setLcdRenderText;
let setSubpixelText = SkiaWrapped.Paint.setSubpixelText;
let setTextSize = SkiaWrapped.Paint.setTextSize;
let setTypeface = SkiaWrapped.Paint.setTypeface;
let getFontMetrics = SkiaWrapped.Paint.getFontMetrics;
let setImageFilter = SkiaWrapped.Paint.setImageFilter;
let setPathEffect = SkiaWrapped.Paint.setPathEffect;
let getPathEffect = SkiaWrapped.Paint.getPathEffect;
let setTextEncoding = SkiaWrapped.Paint.setTextEncoding;
let getTextEncoding = SkiaWrapped.Paint.getTextEncoding;
let setShader = SkiaWrapped.Paint.setShader;
};
module Point = {
type t = SkiaWrapped.Point.t;
let make = SkiaWrapped.Point.make;
let getX = SkiaWrapped.Point.getX;
let getY = SkiaWrapped.Point.getY;
};
module Vector = {
type t = SkiaWrapped.Vector.t;
let make = SkiaWrapped.Vector.make;
let getX = SkiaWrapped.Vector.getX;
let getY = SkiaWrapped.Vector.getY;
};
module Shader = {
type t = SkiaWrapped.Shader.t;
type tileMode = SkiaWrapped.Shader.tileMode;
let makeEmpty = () => {
let empty = SkiaWrapped.Shader.empty();
Gc.finalise(SkiaWrapped.Shader.unref, empty);
empty;
};
let makeLinearGradient2 =
(~startPoint, ~stopPoint, ~startColor, ~stopColor, ~tileMode) => {
let gradient =
SkiaWrapped.Shader.makeLinearGradient2(
startPoint,
stopPoint,
Unsigned.UInt32.of_int32(startColor),
Unsigned.UInt32.of_int32(stopColor),
tileMode,
);
Gc.finalise(SkiaWrapped.Shader.unref, gradient);
gradient;
};
type colorStop = {
color: Color.t,
position: float,
};
let makeLinearGradient = (~startPoint, ~stopPoint, ~colorStops, ~tileMode) => {
let (colors, positions) =
List.fold_left(
(acc, curr) => {
let (accColors, accPositions) = acc;
let {color, position}: colorStop = curr;
(
[Unsigned.UInt32.of_int32(color), ...accColors],
[position, ...accPositions],
);
},
([], []),
colorStops,
);
Ctypes.(
{
let colorsArray = CArray.of_list(uint32_t, colors |> List.rev);
let positionsArray = CArray.of_list(float, positions |> List.rev);
let gradient =
SkiaWrapped.Shader.makeLinearGradient(
startPoint,
stopPoint,
CArray.start(colorsArray),
CArray.start(positionsArray),
CArray.length(colorsArray),
tileMode,
);
Gc.finalise(SkiaWrapped.Shader.unref, gradient);
gradient;
}
);
};
};
module Matrix44 = {
type t = SkiaWrapped.Matrix44.t;
let makeEmpty = () => {
let mat = SkiaWrapped.Matrix44.allocate();
Gc.finalise(SkiaWrapped.Matrix44.destroy, mat);
mat;
};
let makeIdentity = () => {
let mat = SkiaWrapped.Matrix44.allocate_identity();
Gc.finalise(SkiaWrapped.Matrix44.destroy, mat);
mat;
};
let get = SkiaWrapped.Matrix44.get;
let set = SkiaWrapped.Matrix44.set;
let setRotateAboutDegrees = SkiaWrapped.Matrix44.setRotateAboutDegrees;
let setRotateAboutRadians = SkiaWrapped.Matrix44.setRotateAboutRadians;
let setTranslate = SkiaWrapped.Matrix44.setTranslate;
let preTranslate = SkiaWrapped.Matrix44.preTranslate;
let postTranslate = SkiaWrapped.Matrix44.postTranslate;
let setScale = SkiaWrapped.Matrix44.setScale;
let preScale = SkiaWrapped.Matrix44.preScale;
let postScale = SkiaWrapped.Matrix44.postScale;
let setConcat = SkiaWrapped.Matrix44.setConcat;
let preConcat = SkiaWrapped.Matrix44.preConcat;
let postConcat = SkiaWrapped.Matrix44.postConcat;
let toMatrix = SkiaWrapped.Matrix44.toMatrix;
};
module IRect = {
type t = SkiaWrapped.IRect.t;
let makeEmpty = SkiaWrapped.IRect.makeEmpty;
let makeLtrb = SkiaWrapped.IRect.makeLtrb;
};
module Rect = {
type t = SkiaWrapped.Rect.t;
module CI = Cstubs_internals;
module Mutable = {
[@noalloc]
external _set:
(
CI.fatptr(_),
[@unboxed] float,
[@unboxed] float,
[@unboxed] float,
[@unboxed] float
) =>
unit =
"reason_skia_rect_set_byte" "reason_skia_rect_set";
let setLtrb = (~out, left, top, right, bottom) =>
_set(CI.cptr(out), left, top, right, bottom);
};
[@noalloc]
external _getLeft: CI.fatptr(_) => [@unboxed] float =
"reason_skia_rect_get_left_byte" "reason_skia_rect_get_left";
[@noalloc]
external _getTop: CI.fatptr(_) => [@unboxed] float =
"reason_skia_rect_get_top_byte" "reason_skia_rect_get_top";
[@noalloc]
external _getRight: CI.fatptr(_) => [@unboxed] float =
"reason_skia_rect_get_right_byte" "reason_skia_rect_get_right";
[@noalloc]
external _getBottom: CI.fatptr(_) => [@unboxed] float =
"reason_skia_rect_get_bottom_byte" "reason_skia_rect_get_bottom";
let getLeft = rect => _getLeft(CI.cptr(rect));
let getTop = rect => _getTop(CI.cptr(rect));
let getBottom = rect => _getBottom(CI.cptr(rect));
let getRight = rect => _getRight(CI.cptr(rect));
let makeEmpty = SkiaWrapped.Rect.makeEmpty;
let makeLtrb = SkiaWrapped.Rect.makeLtrb;
let toString = rect => {
let left = getLeft(rect);
let right = getRight(rect);
let top = getTop(rect);
let bottom = getBottom(rect);
Printf.sprintf(
"Rect - left: %f top: %f right: %f bottom: %f",
left,
top,
right,
bottom,
);
};
};
module FontStyle = {
type t = SkiaWrapped.FontStyle.t;
type slant = SkiaWrapped.FontStyle.slant;
let getSlant = SkiaWrapped.FontStyle.getSlant;
let getWidth = SkiaWrapped.FontStyle.getWidth;
let getWeight = SkiaWrapped.FontStyle.getWeight;
let make = (weight, width, slant) => {
let style = SkiaWrapped.FontStyle.make(weight, width, slant);
Gc.finalise(SkiaWrapped.FontStyle.delete, style);
style;
};
};
module FontManager = {
type t = SkiaWrapped.FontManager.t;
let makeDefault = () => {
let mgr = SkiaWrapped.FontManager.makeDefault();
Gc.finalise(SkiaWrapped.FontManager.delete, mgr);
mgr;
};
let matchFamilyStyle = (mgr, family, style) => {
let typeface =
SkiaWrapped.FontManager.matchFamilyStyle(mgr, family, style);
switch (typeface) {
| Some(tf) =>
Gc.finalise(SkiaWrapped.Typeface.delete, tf);
Some(tf);
| None => None
};
};
let matchFamilyStyleCharacter = (mgr, family, style, locales, character) => {
open Ctypes;
let cLocales = CArray.of_list(string, locales);
let character32 = character |> Uchar.to_int |> Int32.of_int;
let maybeTypeface =
SkiaWrapped.FontManager.matchFamilyStyleCharacter(
mgr,
family,
style,
cLocales |> CArray.start,
CArray.length(cLocales),
character32,
);
switch (maybeTypeface) {
| Some(tf) as ret =>
Gc.finalise(SkiaWrapped.Typeface.delete, tf);
ret;
| None => None
};
};
};
module RRect = {
type t = SkiaWrapped.RRect.t;
type rRectType = SkiaWrapped.RRect.rRectType;
type corner = SkiaWrapped.RRect.corner;
let make = () => {
let rRect = SkiaWrapped.RRect.allocate();
Gc.finalise(SkiaWrapped.RRect.delete, rRect);
rRect;
};
let copy = originalRRect => {
let rRect = SkiaWrapped.RRect.allocateCopy(originalRRect);
Gc.finalise(SkiaWrapped.RRect.delete, rRect);
rRect;
};
let getType = SkiaWrapped.RRect.getType;
let getRect = SkiaWrapped.RRect.getRect;
let getRadii = SkiaWrapped.RRect.getRadii;
let getWidth = SkiaWrapped.RRect.getWidth;
let getHeight = SkiaWrapped.RRect.getHeight;
let setEmpty = SkiaWrapped.RRect.setEmpty;
let setRect = SkiaWrapped.RRect.setRect;
let setOval = SkiaWrapped.RRect.setOval;
let setRectXy = SkiaWrapped.RRect.setRectXy;
let setNinePatch = SkiaWrapped.RRect.setNinePatch;
let setRectRadii =
(
rRect,
rect,
topLeftRadii,
topRightRadii,
bottomRightRadii,
bottomLeftRadii,
) => {
let radiiCArray =
Ctypes.CArray.make(SkiaWrappedBindings.SkiaTypes.Vector.t, 4);
Ctypes.CArray.unsafe_set(radiiCArray, 0, Ctypes.(!@topLeftRadii));
Ctypes.CArray.unsafe_set(radiiCArray, 1, Ctypes.(!@topRightRadii));
Ctypes.CArray.unsafe_set(radiiCArray, 2, Ctypes.(!@bottomRightRadii));
Ctypes.CArray.unsafe_set(radiiCArray, 3, Ctypes.(!@bottomLeftRadii));
let radiiPointer = Ctypes.CArray.start(radiiCArray);
SkiaWrapped.RRect.setRectRadii(rRect, rect, radiiPointer);
};
let inset = SkiaWrapped.RRect.inset;
let outset = SkiaWrapped.RRect.outset;
let offset = SkiaWrapped.RRect.offset;
let contains = SkiaWrapped.RRect.contains;
let isValid = SkiaWrapped.RRect.isValid;
let transform = SkiaWrapped.RRect.transform;
};
module Path = {
type t = SkiaWrapped.Path.t;
type pathDirection = SkiaWrapped.Path.pathDirection;
let make = () => {
let path = SkiaWrapped.Path.allocate();
Gc.finalise(SkiaWrapped.Path.delete, path);
path;
};
let addCircle = (path, x, y, ~radius, ~direction: pathDirection=`cw, ()) =>
SkiaWrapped.Path.addCircle(path, x, y, radius, direction);
let addRoundRect = (path, rect, rx, ry, ~direction: pathDirection=`cw, ()) =>
SkiaWrapped.Path.addRoundRect(path, rect, rx, ry, direction);
let moveTo = SkiaWrapped.Path.moveTo;
let rMoveTo = SkiaWrapped.Path.rMoveTo;
let lineTo = SkiaWrapped.Path.lineTo;
let rLineTo = SkiaWrapped.Path.rLineTo;
let cubicTo = SkiaWrapped.Path.cubicTo;
let rCubicTo = SkiaWrapped.Path.rCubicTo;
let quadTo = SkiaWrapped.Path.quadTo;
let rQuadTo = SkiaWrapped.Path.rQuadTo;
let arcTo = SkiaWrapped.Path.arcTo;
let rArcTo = SkiaWrapped.Path.rArcTo;
let close = SkiaWrapped.Path.close;
let getLastPoint = SkiaWrapped.Path.getLastPoint;
};
module Matrix = {
type t = SkiaWrapped.Matrix.t;
let make = SkiaWrapped.Matrix.make;
let setAll = SkiaWrapped.Matrix.setAll;
let get = SkiaWrapped.Matrix.get;
let set = SkiaWrapped.Matrix.set;
module CI = Cstubs_internals;
[@noalloc]
external _setScale:
(
CI.fatptr(_),
[@unboxed] float,
[@unboxed] float,
[@unboxed] float,
[@unboxed] float
) =>
unit =
"reason_skia_matrix_set_scale_byte" "reason_skia_matrix_set_scale";
[@noalloc]
external _setTranslate:
(CI.fatptr(_), [@unboxed] float, [@unboxed] float) => unit =
"reason_skia_matrix_set_translate_byte" "reason_skia_matrix_set_translate";
let setScale = (mat, scaleX, scaleY, pivotX, pivotY) =>
_setScale(CI.cptr(mat), scaleX, scaleY, pivotX, pivotY);
let setTranslate = (matrix, translateX, translateY) =>
_setTranslate(CI.cptr(matrix), translateX, translateY);
let makeAll =
(
scaleX,
skewX,
translateX,
skewY,
scaleY,
translateY,
perspective0,
perspective1,
perspective2,
) => {
let matrix = make();
setAll(
matrix,
scaleX,
skewX,
translateX,
skewY,
scaleY,
translateY,
perspective0,
perspective1,
perspective2,
);
matrix;
};
let makeScale = (scaleX, scaleY, pivotX, pivotY) => {
let matrix = make();
setScale(matrix, scaleX, scaleY, pivotX, pivotY);
matrix;
};
let makeTranslate = (translateX, translateY) => {
let matrix = make();
setTranslate(matrix, translateX, translateY);
matrix;
};
let getScaleX = matrix => get(matrix, 0);
let getScaleY = matrix => get(matrix, 4);
let getSkewX = matrix => get(matrix, 1);
let getSkewY = matrix => get(matrix, 3);
let getTranslateX = matrix => get(matrix, 2);
let getTranslateY = matrix => get(matrix, 5);
let getPerspX = matrix => get(matrix, 6);
let getPerspY = matrix => get(matrix, 7);
let setScaleX = (matrix, scaleX) => set(matrix, 0, scaleX);
let setScaleY = (matrix, scaleY) => set(matrix, 4, scaleY);
let setSkewX = (matrix, skewX) => set(matrix, 1, skewX);
let setSkewY = (matrix, skewY) => set(matrix, 3, skewY);
let setTranslateX = (matrix, translateX) => set(matrix, 2, translateX);
let setTranslateY = (matrix, translateY) => set(matrix, 5, translateY);
let setPerspX = (matrix, perspectiveX) => set(matrix, 6, perspectiveX);
let setPerspY = (matrix, perspectiveY) => set(matrix, 7, perspectiveY);
let setSkew = (matrix, skewX, skewY, pivotX, pivotY) =>
setAll(
matrix,
1.,
skewX,
-. skewX *. pivotY,
skewY,
1.,
-. skewY *. pivotX,
0.,
0.,
1.,
);
let setIdentity = matrix =>
setAll(matrix, 1., 0., 0., 0., 1., 0., 0., 0., 1.);
let reset = setIdentity;
let invert = SkiaWrapped.Matrix.invert;
let concat = SkiaWrapped.Matrix.concat;
let preConcat = SkiaWrapped.Matrix.preConcat;
let postConcat = SkiaWrapped.Matrix.postConcat;
let mapRect = SkiaWrapped.Matrix.mapRect;
let mapPoints = SkiaWrapped.Matrix.mapPoints;
let mapVectors = SkiaWrapped.Matrix.mapVectors;
let mapXy = SkiaWrapped.Matrix.mapXy;
let mapVector = SkiaWrapped.Matrix.mapVector;
let mapRadius = SkiaWrapped.Matrix.mapRadius;
let identity = makeAll(1., 0., 0., 0., 1., 0., 0., 0., 1.);
};
module PathEffect = {
module Style = {
type t = SkiaWrapped.PathEffect.Style.t;
};
type t = SkiaWrapped.PathEffect.t;
let create1d = (~style, ~advance, ~phase, path) => {
let pathEffect =
SkiaWrapped.PathEffect.allocate1d(path, advance, phase, style);
Gc.finalise(SkiaWrapped.PathEffect.delete, pathEffect);
pathEffect;
};
let create2dLine = (~width, ~matrix: Matrix.t) => {
let pathEffect = SkiaWrapped.PathEffect.allocate2dLine(width, matrix);
Gc.finalise(SkiaWrapped.PathEffect.delete, pathEffect);
pathEffect;
};
let create2dPath = (~matrix: Matrix.t, path) => {
let pathEffect = SkiaWrapped.PathEffect.allocate2dPath(matrix, path);
Gc.finalise(SkiaWrapped.PathEffect.delete, pathEffect);
pathEffect;
};
};
module ColorSpace = {
type t = SkiaWrapped.ColorSpace.t;
};
type data = SkiaWrapped.data;
module Stream = {
type t = SkiaWrapped.Stream.t;
let hasLength = SkiaWrapped.Stream.hasLength;
let getLength = SkiaWrapped.Stream.getLength;
let makeFileStream = path => {
let maybeStream = SkiaWrapped.Stream.makeFileStream(path);
maybeStream
|> Option.iter(stream =>
Gc.finalise(SkiaWrapped.Stream.deleteFileStream, stream)
);
maybeStream;
};
let makeMemoryStreamFromString = (str, length) => {
let stream =
SkiaWrapped.Stream.makeMemoryStreamFromString(str, length, true);
Gc.finalise(SkiaWrapped.Stream.deleteMemoryStream, stream);
stream;
};
let makeMemoryStreamFromData = data => {
let stream = SkiaWrapped.Stream.makeMemoryStreamFromData(data);
Gc.finalise(SkiaWrapped.Stream.deleteMemoryStream, stream);
stream;
};
};
module Data = {
type t = data;
let makeString = data => {
let dataPtr =
Ctypes.from_voidp(Ctypes.char, SkiaWrapped.Data.getData(data));
let dataSize = Unsigned.Size_t.to_int(SkiaWrapped.Data.getSize(data));
Ctypes.string_from_ptr(dataPtr, ~length=dataSize);
};
let makeFromFileName = path => {
let maybeData = SkiaWrapped.Data.makeFromFileName(path);
switch (maybeData) {
| Some(data) => Gc.finalise(SkiaWrapped.Data.delete, data)
| None => ()
};
maybeData;
};
let makeFromStream = (stream, length) => {
let data = SkiaWrapped.Data.makeFromStream(stream, length);
Gc.finalise(SkiaWrapped.Data.delete, data);
data;
};
};
module Typeface = {
type t = SkiaWrapped.Typeface.t;
let getFamilyName = tf => {
let skStr = SkiaWrapped.Typeface.getFamilyName(tf);
Gc.finalise(SkiaWrapped.String.delete, skStr);
skStr |> SkiaWrapped.String.toString;
};
let makeFromName = SkiaWrapped.Typeface.makeFromName;
let makeFromFile = SkiaWrapped.Typeface.makeFromFile;
let toStream = typeface => {
let stream = SkiaWrapped.Typeface.openStream(typeface, None);
Gc.finalise(SkiaWrapped.Stream.delete, stream);
stream;
};
let getFontStyle = typeface => {
let style = SkiaWrapped.Typeface.getFontStyle(typeface);
Gc.finalise(SkiaWrapped.FontStyle.delete, style);
style;
};
let getUniqueID = SkiaWrapped.Typeface.getUniqueID;
let equal = (tfA, tfB) => {
let styleA = getFontStyle(tfA);
let styleB = getFontStyle(tfB);
String.equal(getFamilyName(tfA), getFamilyName(tfB))
&& FontStyle.getWidth(styleA) == FontStyle.getWidth(styleB)
&& FontStyle.getSlant(styleA) == FontStyle.getSlant(styleB)
&& FontStyle.getWeight(styleA) == FontStyle.getWeight(styleB);
};
};
module ImageInfo = {
type t = SkiaWrapped.ImageInfo.t;
let make = SkiaWrapped.ImageInfo.make;
};
module Image = {
type t =
Ctypes_static.ptr(
Ctypes.structure(SkiaWrappedBindings.SkiaTypes.Image.t),
);
let makeFromEncoded = (encodedData, subset) => {
switch (SkiaWrapped.Image.allocateFromEncoded(encodedData, subset)) {
| Some(image) =>
Gc.finalise(SkiaWrapped.Image.delete, image);
Some(image);
| None => None
};
};
let encodeToData = image => {
let data = SkiaWrapped.Image.encode(image);
Gc.finalise(SkiaWrapped.Data.delete, data);
data;
};
let width = SkiaWrapped.Image.width;
let height = SkiaWrapped.Image.height;
};
type pixelGeometry = SkiaWrapped.pixelGeometry;
module Gr = {
type surfaceOrigin = SkiaWrapped.Gr.surfaceOrigin;
module Gl = {
module Interface = {
type t = SkiaWrapped.Gr.Gl.Interface.t;
let makeNative = SkiaWrapped.Gr.Gl.Interface.makeNative;
let makeSdl2 = SkiaWrapped.Gr.Gl.Interface.makeSdl2;
let makeSdl2ES = SkiaWrapped.Gr.Gl.Interface.makeSdl2ES;
};
module FramebufferInfo = {
type t = SkiaWrapped.Gr.Gl.FramebufferInfo.t;
let make = SkiaWrapped.Gr.Gl.FramebufferInfo.make;
};
};
module Context = {
type t = SkiaWrapped.Gr.Context.t;
let makeGl = SkiaWrapped.Gr.Context.makeGl;
};
module BackendRenderTarget = {
type t = SkiaWrapped.Gr.BackendRenderTarget.t;
let makeGl = SkiaWrapped.Gr.BackendRenderTarget.makeGl;
};
};
type clipOp = SkiaWrapped.clipOp;
module Canvas = {
type t =
Ctypes_static.ptr(
Ctypes.structure(SkiaWrappedBindings.SkiaTypes.Canvas.t),
);
// TODO: Make fast
let clear = (canvas, color) =>
SkiaWrapped.Canvas.clear(canvas, Unsigned.UInt32.of_int32(color));
let drawPaint = SkiaWrapped.Canvas.drawPaint;
let drawRect = SkiaWrapped.Canvas.drawRect;
let drawRectLtwh = SkiaWrapped.Canvas.drawRectLtwh;
let drawRoundRect = SkiaWrapped.Canvas.drawRoundRect;
let drawRRect = SkiaWrapped.Canvas.drawRRect;
let drawOval = SkiaWrapped.Canvas.drawOval;
let drawPath = SkiaWrapped.Canvas.drawPath;
let drawCircle = SkiaWrapped.Canvas.drawCircle;
let drawText = (canvas, text, x, y, paint) => {
SkiaWrapped.Canvas.drawText(
canvas,
text,
String.length(text),
x,
y,
paint,
);
};
let drawImage = SkiaWrapped.Canvas.drawImage;
let drawImageRect = SkiaWrapped.Canvas.drawImageRect;
let concat = SkiaWrapped.Canvas.concat;
let setMatrix = SkiaWrapped.Canvas.setMatrix;
let translate = SkiaWrapped.Canvas.translate;
let scale = SkiaWrapped.Canvas.scale;
let rotate = SkiaWrapped.Canvas.rotate;
let skew = SkiaWrapped.Canvas.skew;
let resetMatrix = SkiaWrapped.Canvas.resetMatrix;
let clipRect = SkiaWrapped.Canvas.clipRect;
let clipPath = SkiaWrapped.Canvas.clipPath;
let clipRRect = (canvas, rrect, clipOp: clipOp, antiAlias) => {
SkiaWrapped.Canvas.clipRRect(canvas, rrect, clipOp, antiAlias);
};
let save = SkiaWrapped.Canvas.save;
let saveLayer = SkiaWrapped.Canvas.saveLayer;
let restore = SkiaWrapped.Canvas.restore;
let flush = SkiaWrapped.Canvas.flush;
};
module SurfaceProps = {
type t = SkiaWrapped.SurfaceProps.t;
let make = SkiaWrapped.SurfaceProps.make;
};
module Surface = {
type t =
Ctypes_static.ptr(
Ctypes.structure(SkiaWrappedBindings.SkiaTypes.Surface.t),
);
let makeRaster = (imageInfo, rowBytes, surfaceProps) => {
let maybeSurface =
SkiaWrapped.Surface.allocateRaster(
imageInfo,
Unsigned.Size_t.of_int(rowBytes),
surfaceProps,
);
switch (maybeSurface) {
| Some(surface) as surf =>
Gc.finalise(SkiaWrapped.Surface.delete, surface);
surf;
| None => None
};
};
let makeRenderTarget =
(
context,
shouldBeBudgeted,
imageInfo,
sampleCount,
surfaceOrigin,
surfacePropsOption,
shouldCreateWithMips,
) => {
let surfaceOption =
SkiaWrapped.Surface.allocateRenderTarget(
context,
shouldBeBudgeted,
imageInfo,
sampleCount,
surfaceOrigin,
surfacePropsOption,
shouldCreateWithMips,
);
switch (surfaceOption) {
| Some(surface) =>
Gc.finalise(SkiaWrapped.Surface.delete, surface);
Some(surface);
| None => None
};
};
let makeFromBackendRenderTarget =
(
context,
backendRenderTarget,
surfaceOrigin,
colorType,
colorSpaceOption,
surfacePropsOption,
) => {
let surfaceOption =
SkiaWrapped.Surface.allocateFromBackendRenderTarget(
context,
backendRenderTarget,
surfaceOrigin,
colorType,
colorSpaceOption,
surfacePropsOption,
);
switch (surfaceOption) {
| Some(surface) =>
Gc.finalise(SkiaWrapped.Surface.delete, surface);
Some(surface);
| None => None
};
};
let draw = (~paint=None, ~canvas, ~x, ~y, surface) =>
SkiaWrapped.Surface.draw(surface, canvas, x, y, paint);
let getCanvas = SkiaWrapped.Surface.getCanvas;
let makeImageSnapshot = surface => {
let imageSnapshot = SkiaWrapped.Surface.allocateImageSnapshot(surface);
Gc.finalise(SkiaWrapped.Image.delete, imageSnapshot);
imageSnapshot;
};
let getWidth = SkiaWrapped.Surface.getWidth;
let getHeight = SkiaWrapped.Surface.getHeight;
let getProps = SkiaWrapped.Surface.getProps;
};
module SVG = {
type t = {
svg: SkiaWrapped.SVG.t,
stream: SkiaWrapped.Stream.t,
};
let makeFromStream = stream =>
SkiaWrapped.SVG.makeFromStream(stream)
|> Option.map(svg => {
svg |> Gc.finalise(SkiaWrapped.SVG.delete);
{svg, stream};
});
let render = t => SkiaWrapped.SVG.render(t.svg);
let setContainerSize = t => SkiaWrapped.SVG.setContainerSize(t.svg);
let getContainerWidth = t => SkiaWrapped.SVG.getContainerWidth(t.svg);
let getContainerHeight = t => SkiaWrapped.SVG.getContainerHeight(t.svg);
};
================================================
FILE: packages/reason-skia/src/Skia.rei
================================================
type colorType = SkiaWrapped.colorType;
type alphaType = SkiaWrapped.alphaType;
type data = SkiaWrapped.data;
module Color: {
type t = int32;
let makeArgb: (int32, int32, int32, int32) => t;
let getA: t => int32;
let getR: t => int32;
let getG: t => int32;
let getB: t => int32;
module Float: {
let makeArgb: (float, float, float, float) => t;
let getA: t => float;
let getR: t => float;
let getG: t => float;
let getB: t => float;
};
};
module FontStyle: {
type t;
type slant = SkiaWrapped.FontStyle.slant;
let make: (int, int, slant) => t;
let getSlant: t => slant;
let getWeight: t => int;
let getWidth: t => int;
};
module FilterQuality: {type t = SkiaWrapped.FilterQuality.t;};
module Hinting: {type t = SkiaWrapped.Hinting.t;};
module TextEncoding: {type t = SkiaWrapped.TextEncoding.t;};
module Stream: {
type t;
let hasLength: t => bool;
let getLength: t => int;
let makeFileStream: string => option(t);
let makeMemoryStreamFromString: (string, int) => t;
let makeMemoryStreamFromData: data => t;
};
module Data: {
type t = data;
let makeFromFileName: string => option(t);
let makeString: t => string;
let makeFromStream: (Stream.t, int) => t;
};
module Typeface: {
type t;
let getFamilyName: t => string;
let makeFromName: (string, FontStyle.t) => option(t);
let makeFromFile: (string, int) => option(t);
let toStream: t => Stream.t;
let getFontStyle: t => FontStyle.t;
let getUniqueID: t => int32;
let equal: (t, t) => bool;
};
module FontManager: {
type t;
let makeDefault: unit => t;
let matchFamilyStyle: (t, string, FontStyle.t) => option(Typeface.t);
let matchFamilyStyleCharacter:
(t, string, FontStyle.t, list(string), Uchar.t) => option(Typeface.t);
};
module FontMetrics: {
type t = SkiaWrapped.FontMetrics.t;
let make: unit => t;
let getAscent: t => float;
let getDescent: t => float;
let getTop: t => float;
let getBottom: t => float;
let getUnderlinePosition: t => float;
let getUnderlineThickness: t => float;
let getAvgCharacterWidth: t => float;
let getMaxCharacterWidth: t => float;
};
module ImageFilter: {
type t;
module CropRect: {type t;};
module DropShadow: {
type shadowMode = SkiaWrapped.ImageFilter.DropShadow.shadowMode;
let make:
(
float,
float,
float,
float,
Color.t,
shadowMode,
option(t),
option(CropRect.t)
) =>
t;
};
let makeDropShadow:
(
float,
float,
float,
float,
Color.t,
DropShadow.shadowMode,
option(t),
option(CropRect.t)
) =>
t;
};
module Rect: {
type t;
module Mutable: {let setLtrb: (~out: t, float, float, float, float) => unit;
};
let makeEmpty: unit => t;
let makeLtrb: (float, float, float, float) => t;
let getTop: t => float;
let getLeft: t => float;
let getRight: t => float;
let getBottom: t => float;
let toString: t => string;
};
module Point: {
type t;
let make: (float, float) => t;
let getX: t => float;
let getY: t => float;
};
module Vector: {
type t = Point.t;
let make: (float, float) => t;
let getX: t => float;
let getY: t => float;
};
module Shader: {
type t;
type tileMode = SkiaWrapped.Shader.tileMode;
let makeEmpty: unit => t;
type colorStop = {
color: Color.t,
position: float,
};
let makeLinearGradient2:
(
~startPoint: Point.t,
~stopPoint: Point.t,
~startColor: Color.t,
~stopColor: Color.t,
~tileMode: tileMode
) =>
t;
let makeLinearGradient:
(
~startPoint: Point.t,
~stopPoint: Point.t,
~colorStops: list(colorStop),
~tileMode: tileMode
) =>
t;
};
module Path: {
type t;
let make: unit => t;
let addRoundRect:
(
t,
Rect.t,
float,
float,
~direction: SkiaWrapped.Path.pathDirection=?,
unit
) =>
unit;
let addCircle:
(
t,
float,
float,
~radius: float,
~direction: SkiaWrapped.Path.pathDirection=?,
unit
) =>
unit;
let moveTo: (t, float, float) => unit;
let rMoveTo: (t, float, float) => unit;
let lineTo: (t, float, float) => unit;
let rLineTo: (t, float, float) => unit;
let cubicTo: (t, float, float, float, float, float, float) => unit;
let rCubicTo: (t, float, float, float, float, float, float) => unit;
let quadTo: (t, float, float, float, float) => unit;
let rQuadTo: (t, float, float, float, float) => unit;
let arcTo:
(
t,
float,
float,
float,
SkiaWrapped.Path.arcSize,
SkiaWrapped.Path.pathDirection,
float,
float
) =>
unit;
let rArcTo:
(
t,
float,
float,
float,
SkiaWrapped.Path.arcSize,
SkiaWrapped.Path.pathDirection,
float,
float
) =>
unit;
let close: t => unit;
let getLastPoint: (t, Point.t) => bool;
};
module Matrix: {
type t;
let identity: t;
let make: unit => t;
let makeAll:
(float, float, float, float, float, float, float, float, float) => t;
let makeTranslate: (float, float) => t;
let makeScale: (float, float, float, float) => t;
let setAll:
(t, float, float, float, float, float, float, float, float, float) => unit;
let get: (t, int) => float;
let getScaleX: t => float;
let getScaleY: t => float;
let getSkewX: t => float;
let getSkewY: t => float;
let getTranslateX: t => float;
let getTranslateY: t => float;
let getPerspX: t => float;
let getPerspY: t => float;
let set: (t, int, float) => unit;
let setScaleX: (t, float) => unit;
let setScaleY: (t, float) => unit;
let setSkewX: (t, float) => unit;
let setSkewY: (t, float) => unit;
let setTranslateX: (t, float) => unit;
let setTranslateY: (t, float) => unit;
let setPerspX: (t, float) => unit;
let setPerspY: (t, float) => unit;
let setTranslate: (t, float, float) => unit;
let setScale: (t, float, float, float, float) => unit;
let setSkew: (t, float, float, float, float) => unit;
let setIdentity: t => unit;
let reset: t => unit;
let invert: (t, t) => bool;
let concat: (t, t, t) => unit;
let preConcat: (t, t) => unit;
let postConcat: (t, t) => unit;
let mapRect: (t, Rect.t, Rect.t) => unit;
let mapPoints: (t, Point.t, Point.t, int) => unit;
let mapVectors: (t, Vector.t, Vector.t, int) => unit;
let mapXy: (t, float, float, Point.t) => unit;
let mapVector: (t, float, float, Vector.t) => unit;
let mapRadius: (t, float) => float;
};
module PathEffect: {
module Style: {type t = [ | `translate | `rotate | `morph];};
type t;
let create1d: (~style: Style.t, ~advance: float, ~phase: float, Path.t) => t;
let create2dLine: (~width: float, ~matrix: Matrix.t) => t;
let create2dPath: (~matrix: Matrix.t, Path.t) => t;
//let createDiscrete: (~length: float, ~deviation: float, unit) => t;
};
module Paint: {
type t;
type style = SkiaWrapped.Paint.style;
let make: unit => t;
let setColor: (t, Color.t) => unit;
let setAntiAlias: (t, bool) => unit;
let setAutohinted: (t, bool) => unit;
let isAutohinted: t => bool;
let setHinting: (t, Hinting.t) => unit;
let getHinting: t => Hinting.t;
let setFilterQuality: (t, FilterQuality.t) => unit;
let getFilterQuality: t => FilterQuality.t;
let setPathEffect: (t, PathEffect.t) => unit;
let getPathEffect: t => PathEffect.t;
let setStyle: (t, style) => unit;
let setStrokeWidth: (t, float) => unit;
let setImageFilter: (t, option(ImageFilter.t)) => unit;
let setTypeface: (t, Typeface.t) => unit;
let setLcdRenderText: (t, bool) => unit;
let setSubpixelText: (t, bool) => unit;
let setTextSize: (t, float) => unit;
let setAlpha: (t, float) => unit;
let getFontMetrics: (t, FontMetrics.t, float) => float;
let measureText: (t, string, option(Rect.t)) => float;
let setTextEncoding: (t, TextEncoding.t) => unit;
let getTextEncoding: t => TextEncoding.t;
let setShader: (t, Shader.t) => unit;
};
module IRect: {
type t;
let makeEmpty: unit => t;
let makeLtrb: (int32, int32, int32, int32) => t;
};
module Matrix44: {
type t;
let makeIdentity: unit => t;
let makeEmpty: unit => t;
let setRotateAboutDegrees: (t, float, float, float, float) => unit;
let setRotateAboutRadians: (t, float, float, float, float) => unit;
let setTranslate: (t, float, float, float) => unit;
let preTranslate: (t, float, float, float) => unit;
let postTranslate: (t, float, float, float) => unit;
let setScale: (t, float, float, float) => unit;
let preScale: (t, float, float, float) => unit;
let postScale: (t, float, float, float) => unit;
let setConcat: (t, t, t) => unit;
let preConcat: (t, t) => unit;
let postConcat: (t, t) => unit;
let get: (t, int, int) => float;
let set: (t, int, int, float) => unit;
let toMatrix: (t, Matrix.t) => unit;
};
module RRect: {
type t;
type rRectType = SkiaWrapped.RRect.rRectType;
type corner = SkiaWrapped.RRect.corner;
let make: unit => t;
let copy: t => t;
let getType: t => rRectType;
let getRect: (t, Rect.t) => unit;
let getRadii: (t, corner, Vector.t) => unit;
let getWidth: t => float;
let getHeight: t => float;
let setEmpty: t => unit;
let setRect: (t, Rect.t) => unit;
let setOval: (t, Rect.t) => unit;
let setRectXy: (t, Rect.t, float, float) => unit;
let setNinePatch: (t, Rect.t, float, float, float, float) => unit;
let setRectRadii:
(t, Rect.t, Vector.t, Vector.t, Vector.t, Vector.t) => unit;
let inset: (t, float, float) => unit;
let outset: (t, float, float) => unit;
let offset: (t, float, float) => unit;
let contains: (t, Rect.t) => bool;
let isValid: t => bool;
let transform: (t, Matrix.t, t) => bool;
};
module ColorSpace: {type t;};
module ImageInfo: {
type t;
let make: (int32, int32, colorType, alphaType, option(ColorSpace.t)) => t;
};
module Image: {
type t;
let makeFromEncoded: (Data.t, option(IRect.t)) => option(t);
let encodeToData: t => Data.t;
let width: t => int;
let height: t => int;
};
type pixelGeometry = SkiaWrapped.pixelGeometry;
module Gr: {
type surfaceOrigin = SkiaWrapped.Gr.surfaceOrigin;
module Gl: {
module Interface: {
type t;
let makeNative: unit => option(t);
let makeSdl2: unit => option(t);
let makeSdl2ES: unit => option(t);
};
module FramebufferInfo: {
type t;
let make: (Unsigned.UInt.t, Unsigned.UInt.t) => t;
};
};
module Context: {
type t;
let makeGl: option(Gl.Interface.t) => option(t);
};
module BackendRenderTarget: {
type t;
let makeGl: (int, int, int, int, Gl.FramebufferInfo.t) => t;
};
};
type clipOp = SkiaWrapped.clipOp;
module Canvas: {
type t;
let clear: (t, Color.t) => unit;
let drawPaint: (t, Paint.t) => unit;
let drawRect: (t, Rect.t, Paint.t) => unit;
let drawRectLtwh: (t, float, float, float, float, Paint.t) => unit;
let drawRoundRect: (t, Rect.t, float, float, Paint.t) => unit;
let drawOval: (t, Rect.t, Paint.t) => unit;
let drawCircle: (t, float, float, float, Paint.t) => unit;
let drawRRect: (t, RRect.t, Paint.t) => unit;
let drawPath: (t, Path.t, Paint.t) => unit;
let drawText: (t, string, float, float, Paint.t) => unit;
let drawImage: (t, Image.t, float, float, option(Paint.t)) => unit;
let drawImageRect:
(t, Image.t, option(Rect.t), Rect.t, option(Paint.t)) => unit;
let concat: (t, Matrix.t) => unit;
let setMatrix: (t, Matrix.t) => unit;
let translate: (t, float, float) => unit;
let scale: (t, float, float) => unit;
let rotate: (t, float) => unit;
let skew: (t, float, float) => unit;
let resetMatrix: t => unit;
let clipRect: (t, Rect.t, clipOp, bool) => unit;
let clipPath: (t, Path.t, clipOp, bool) => unit;
let clipRRect: (t, RRect.t, clipOp, bool) => unit;
let save: t => int;
let saveLayer: (t, option(Rect.t), option(Paint.t)) => int;
let restore: t => unit;
let flush: t => unit;
};
module SurfaceProps: {
type t;
let make: (Unsigned.UInt32.t, pixelGeometry) => t;
};
module Surface: {
type t;
let makeRaster: (ImageInfo.t, int, option(SurfaceProps.t)) => option(t);
let makeRenderTarget:
(
Gr.Context.t,
bool,
ImageInfo.t,
int,
Gr.surfaceOrigin,
option(SurfaceProps.t),
bool
) =>
option(t);
let makeFromBackendRenderTarget:
(
Gr.Context.t,
Gr.BackendRenderTarget.t,
Gr.surfaceOrigin,
colorType,
option(ColorSpace.t),
option(SurfaceProps.t)
) =>
option(t);
// Draw surface [surface] onto a [canvas] at the specified [x,y] points.
let draw:
(~paint: option(Paint.t)=?, ~canvas: Canvas.t, ~x: float, ~y: float, t) =>
unit;
let makeImageSnapshot: t => Image.t;
let getCanvas: t => Canvas.t;
let getWidth: t => int;
let getHeight: t => int;
let getProps: t => SurfaceProps.t;
};
module SVG: {
type t;
let makeFromStream: Stream.t => option(t);
let render: (t, Canvas.t) => unit;
let setContainerSize: (t, float, float) => unit;
let getContainerWidth: t => float;
let getContainerHeight: t => float;
};
================================================
FILE: packages/reason-skia/src/config/discover.re
================================================
module Configurator = Configurator.V1;
let getenv = name =>
try(Sys.getenv(name)) {
| Not_found => failwith("Error: Undefined environment variable: " ++ name)
};
type os =
| Android
| IOS
| Linux
| Mac
| Windows;
let detect_system_header = {|
#if __APPLE__
#include
#if TARGET_OS_IPHONE
#define PLATFORM_NAME "ios"
#else
#define PLATFORM_NAME "mac"
#endif
#elif __linux__
#if __ANDROID__
#define PLATFORM_NAME "android"
#else
#define PLATFORM_NAME "linux"
#endif
#elif WIN32
#define PLATFORM_NAME "windows"
#endif
|};
let sdl2FilePath = Sys.getenv("SDL2_LIB_PATH") ++ "/libSDL2.a";
let get_os = t => {
let header = {
let file = Filename.temp_file("discover", "os.h");
let fd = open_out(file);
output_string(fd, detect_system_header);
close_out(fd);
file;
};
let platform =
Configurator.C_define.import(
t,
~includes=[header],
[("PLATFORM_NAME", String)],
);
switch (platform) {
| [(_, String("android"))] => Android
| [(_, String("ios"))] => IOS
| [(_, String("linux"))] => Linux
| [(_, String("mac"))] => Mac
| [(_, String("windows"))] => Windows
| _ => failwith("Unknown operating system")
};
};
let ccopt = s => ["-ccopt", s];
let cclib = s => ["-cclib", s];
let framework = s => ["-framework", s];
let flags = os =>
switch (os) {
| Android =>
[]
@ ["-verbose"]
@ cclib("-lfreetype")
@ cclib("-lz")
@ cclib("-lskia")
@ cclib("-lGLESv2")
@ cclib("-lGLESv1_CM")
@ cclib("-lm")
@ cclib("-llog")
@ cclib("-landroid")
@ ccopt("-L" ++ getenv("FREETYPE2_LIB_PATH"))
@ ccopt("-L" ++ getenv("SDL2_LIB_PATH"))
@ ccopt("-L" ++ getenv("SKIA_LIB_PATH"))
@ ccopt("-L" ++ getenv("JPEG_LIB_PATH"))
@ ccopt("-I" ++ getenv("FREETYPE2_INCLUDE_PATH"))
@ ccopt("-I" ++ getenv("SKIA_INCLUDE_PATH"))
@ cclib("-ljpeg")
@ ccopt("-I/usr/include")
@ ccopt("-lstdc++")
| IOS
| Mac => []
| Linux =>
[]
@ ["-verbose"]
@ cclib("-lfontconfig")
@ cclib("-lfreetype")
@ cclib("-lz")
@ cclib("-lbz2")
@ cclib("-lskia")
@ cclib(sdl2FilePath)
@ ccopt("-L" ++ getenv("FREETYPE2_LIB_PATH"))
@ ccopt("-L" ++ getenv("SDL2_LIB_PATH"))
@ ccopt("-L" ++ getenv("SKIA_LIB_PATH"))
@ ccopt("-L" ++ getenv("JPEG_LIB_PATH"))
@ ccopt("-I" ++ getenv("FREETYPE2_INCLUDE_PATH"))
@ ccopt("-I" ++ getenv("SKIA_INCLUDE_PATH"))
@ cclib("-ljpeg")
@ ccopt("-I/usr/include")
@ ccopt("-lstdc++")
@ ccopt("-fPIC")
| Windows =>
[]
@ cclib("-lskia")
@ cclib("-lSDL2")
@ ccopt("-L" ++ getenv("SDL2_LIB_PATH"))
@ ccopt("-L" ++ getenv("SKIA_LIB_PATH"))
};
let skiaIncludeFlags = {
let skiaIncludePath = getenv("SKIA_INCLUDE_PATH");
Sys.readdir(skiaIncludePath)
|> Array.map(path => "-I" ++ skiaIncludePath ++ "/" ++ path)
|> Array.append([|"-I" ++ skiaIncludePath|])
|> Array.to_list;
};
let cflags = os => {
switch (os) {
| Android =>
[]
@ [sdl2FilePath]
@ ["-lGLESv2"]
@ ["-lGLESv1_CM"]
@ ["-lm"]
@ ["-llog"]
@ ["-landroid"]
@ ["-lskia"]
@ ["-I" ++ getenv("SDL2_INCLUDE_PATH")]
@ skiaIncludeFlags
@ ["-L" ++ getenv("SKIA_LIB_PATH")]
@ ["-L" ++ getenv("SDL2_LIB_PATH")]
@ ["-L" ++ getenv("JPEG_LIB_PATH")]
@ ["-lstdc++"]
@ ["-ljpeg"]
| Linux =>
[]
@ [sdl2FilePath]
@ ["-lskia"]
@ ["-I" ++ getenv("SDL2_INCLUDE_PATH")]
@ skiaIncludeFlags
@ ["-L" ++ getenv("SKIA_LIB_PATH")]
@ ["-L" ++ getenv("SDL2_LIB_PATH")]
@ ["-L" ++ getenv("JPEG_LIB_PATH")]
@ ["-lstdc++"]
@ ["-ljpeg"]
@ ["-fPIC"]
| IOS
| Mac => [] @ ["-I" ++ getenv("SDL2_INCLUDE_PATH")] @ skiaIncludeFlags
| Windows =>
[]
@ ["-std=c++1y"]
@ ["-I" ++ getenv("SDL2_INCLUDE_PATH")]
@ ["-L" ++ getenv("SKIA_LIB_PATH")]
@ ["-lskia"]
@ skiaIncludeFlags
};
};
let libs = os =>
switch (os) {
| Android =>
[]
@ [
"-L" ++ getenv("SDL2_LIB_PATH"),
"-L" ++ getenv("SKIA_LIB_PATH"),
"-L" ++ getenv("FREETYPE2_LIB_PATH"),
sdl2FilePath,
"-lGLESv2",
"-lGLESv1_CM",
"-lm",
"-llog",
"-landroid",
"-lskia",
"-lfreetype",
"-lz",
"-L" ++ getenv("JPEG_LIB_PATH"),
"-ljpeg",
"-lstdc++",
]
| IOS =>
[]
@ ["-L" ++ getenv("JPEG_LIB_PATH")]
@ ["-L" ++ getenv("SKIA_LIB_PATH")]
@ ["-L" ++ getenv("FREETYPE2_LIB_PATH")]
@ ["-L" ++ getenv("SDL2_LIB_PATH")]
@ framework("OpenGLES")
@ framework("UIKit")
@ framework("Foundation")
@ framework("GameController")
@ framework("AVFoundation")
@ framework("QuartzCore")
@ framework("CoreMotion")
@ framework("CoreFoundation")
@ framework("CoreAudio")
@ framework("CoreVideo")
@ framework("CoreServices")
@ framework("CoreGraphics")
@ framework("CoreText")
@ framework("CoreFoundation")
@ framework("AudioToolbox")
@ framework("IOKit")
@ framework("Metal")
@ ["-liconv"]
@ [sdl2FilePath]
@ ["-lskia"]
@ ["-lstdc++"]
@ [getenv("JPEG_LIB_PATH") ++ "/libturbojpeg.a"]
| Linux =>
[]
@ [
"-L" ++ getenv("SDL2_LIB_PATH"),
"-L" ++ getenv("SKIA_LIB_PATH"),
"-L" ++ getenv("FREETYPE2_LIB_PATH"),
sdl2FilePath,
"-lskia",
"-lfreetype",
"-lfontconfig",
"-lz",
"-lbz2",
"-L" ++ getenv("JPEG_LIB_PATH"),
"-ljpeg",
"-lpthread",
"-lstdc++",
"-fPIC",
]
| Mac =>
[]
@ ["-L" ++ getenv("JPEG_LIB_PATH")]
@ ["-L" ++ getenv("SKIA_LIB_PATH")]
@ ["-L" ++ getenv("FREETYPE2_LIB_PATH")]
@ ["-L" ++ getenv("SDL2_LIB_PATH")]
@ framework("Carbon")
@ framework("Cocoa")
@ framework("CoreFoundation")
@ framework("CoreAudio")
@ framework("CoreVideo")
@ framework("CoreServices")
@ framework("CoreGraphics")
@ framework("CoreText")
@ framework("CoreFoundation")
@ framework("AudioToolbox")
@ framework("ForceFeedback")
@ framework("IOKit")
@ framework("Metal")
@ ["-liconv"]
@ [sdl2FilePath]
@ ["-lskia"]
@ ["-lstdc++"]
@ [getenv("JPEG_LIB_PATH") ++ "/libturbojpeg.a"]
| Windows =>
[]
@ ["-luser32"]
@ ["-lskia"]
@ ["-lSDL2"]
@ ["-lstdc++"]
@ ["-L" ++ getenv("SDL2_LIB_PATH")]
@ ["-L" ++ getenv("SKIA_LIB_PATH")]
};
Configurator.main(~name="reason-sdl2", conf => {
let os = get_os(conf);
Configurator.Flags.write_sexp("flags.sexp", flags(os));
Configurator.Flags.write_lines("c_flags.txt", cflags(os));
Configurator.Flags.write_sexp("c_flags.sexp", cflags(os));
Configurator.Flags.write_sexp("c_library_flags.sexp", libs(os));
Configurator.Flags.write_lines("c_library_flags.txt", libs(os));
Configurator.Flags.write_sexp(
"cclib_c_library_flags.sexp",
libs(os) |> List.map(o => ["-cclib", o]) |> List.flatten,
);
});
================================================
FILE: packages/reason-skia/src/config/dune
================================================
(executable
(name discover)
(libraries dune.configurator))
================================================
FILE: packages/reason-skia/src/dune
================================================
(rule
(targets c_library_flags.sexp c_flags.sexp flags.sexp)
(deps
(:discover config/discover.exe))
(action
(run %{discover})))
(library
(name skia)
(public_name reason-skia)
(library_flags
(:include flags.sexp))
(c_library_flags
(:include c_library_flags.sexp))
(libraries SkiaWrapped))
================================================
FILE: packages/reason-skia/src/wrapped/bindings/SkiaWrappedBindings.re
================================================
open Ctypes;
module SkiaTypes = SkiaWrappedTypes.M(Skia_generated_type_stubs);
module M = (F: FOREIGN) => {
// module Ctypes_for_stubs = {
// include Ctypes;
// let (@->) = F.(@->);
// let returning = F.returning;
// let foreign = F.foreign;
// };
// open Ctypes_for_stubs;
open F;
type colorType = SkiaTypes.colorType;
let colorType = SkiaTypes.colorType;
type alphaType = SkiaTypes.alphaType;
let alphaType = SkiaTypes.alphaType;
module Color = {
type t = Unsigned.uint32;
let t = uint32_t;
};
type data = ptr(structure(SkiaTypes.Data.t));
let data = ptr(SkiaTypes.Data.t);
module Stream = {
type t = ptr(structure(SkiaTypes.Stream.t));
let t = ptr(SkiaTypes.Stream.t);
let maybeT = ptr_opt(SkiaTypes.Stream.t);
let hasLength = foreign("sk_stream_has_length", t @-> returning(bool));
let getLength = foreign("sk_stream_get_length", t @-> returning(int));
let delete = foreign("sk_stream_destroy", t @-> returning(void));
let makeFileStream =
foreign("sk_filestream_new", string @-> returning(maybeT));
let deleteFileStream =
foreign("sk_filestream_destroy", t @-> returning(void));
let makeMemoryStreamFromString =
foreign(
"sk_memorystream_new_with_data",
string @-> int @-> bool @-> returning(t),
);
let makeMemoryStreamFromData =
foreign("sk_memorystream_new_with_skdata", data @-> returning(t));
let deleteMemoryStream =
foreign("sk_memorystream_destroy", t @-> returning(void));
};
module Data = {
type t = data;
let t = data;
let makeFromFileName =
foreign(
"sk_data_new_from_file",
string @-> returning(ptr_opt(SkiaTypes.Data.t)),
);
let delete = foreign("sk_data_unref", t @-> returning(void));
let getData = foreign("sk_data_get_data", t @-> returning(ptr(void)));
let getSize = foreign("sk_data_get_size", t @-> returning(size_t));
let makeFromStream =
foreign("sk_data_new_from_stream", Stream.t @-> int @-> returning(t));
};
module String = {
type t = ptr(structure(SkiaTypes.String.t));
let t = ptr(SkiaTypes.String.t);
let toString = foreign("sk_string_get_c_str", t @-> returning(string));
let delete = foreign("sk_string_destructor", t @-> returning(void));
};
module FontStyle = {
type t = ptr(structure(SkiaTypes.FontStyle.t));
let t = ptr(SkiaTypes.FontStyle.t);
type slant = SkiaTypes.FontStyle.slant;
let slant = SkiaTypes.FontStyle.slant;
let make =
foreign("sk_fontstyle_new", int @-> int @-> slant @-> returning(t));
let getSlant = foreign("sk_fontstyle_get_slant", t @-> returning(slant));
let getWeight = foreign("sk_fontstyle_get_weight", t @-> returning(int));
let getWidth = foreign("sk_fontstyle_get_width", t @-> returning(int));
let delete = foreign("sk_fontstyle_delete", t @-> returning(void));
};
module TextEncoding = {
type t = SkiaTypes.TextEncoding.t;
let t = SkiaTypes.TextEncoding.t;
};
module FilterQuality = {
type t = SkiaTypes.FilterQuality.t;
let t = SkiaTypes.FilterQuality.t;
};
module Hinting = {
type t = SkiaTypes.Hinting.t;
let t = SkiaTypes.Hinting.t;
};
module Typeface = {
type t = ptr(structure(SkiaTypes.Typeface.t));
let t = ptr(SkiaTypes.Typeface.t);
let getFamilyName =
foreign("sk_typeface_get_family_name", t @-> returning(String.t));
let makeFromName =
foreign(
"sk_typeface_create_from_name_with_font_style",
string @-> FontStyle.t @-> returning(ptr_opt(SkiaTypes.Typeface.t)),
);
let getFontStyle =
foreign("sk_typeface_get_fontstyle", t @-> returning(FontStyle.t));
let getUniqueID =
foreign("sk_typeface_get_unique_id", t @-> returning(int32_t));
let makeFromFile =
foreign(
"sk_typeface_create_from_file",
string @-> int @-> returning(ptr_opt(SkiaTypes.Typeface.t)),
);
let openStream =
foreign(
"sk_typeface_open_stream",
t @-> ptr_opt(int) @-> returning(Stream.t),
);
let delete = foreign("sk_typeface_unref", t @-> returning(void));
};
module FontManager = {
type t = ptr(structure(SkiaTypes.FontManager.t));
let t = ptr(SkiaTypes.FontManager.t);
let makeDefault =
foreign("sk_fontmgr_create_default", void @-> returning(t));
let matchFamilyStyle =
foreign(
"sk_fontmgr_match_family_style",
t
@-> string
@-> FontStyle.t
@-> returning(ptr_opt(SkiaTypes.Typeface.t)),
);
let matchFamilyStyleCharacter =
foreign(
"sk_fontmgr_match_family_style_character",
t
@-> string
@-> FontStyle.t
@-> ptr(string)
@-> int
@-> int32_t
@-> returning(ptr_opt(SkiaTypes.Typeface.t)),
);
let delete = foreign("sk_fontmgr_unref", t @-> returning(void));
};
module FontMetrics = {
type t = ptr(structure(SkiaTypes.FontMetrics.t));
let t = ptr(SkiaTypes.FontMetrics.t);
let make = () => allocate_n(~count=1, SkiaTypes.FontMetrics.t);
let getAscent = metrics => getf(!@metrics, SkiaTypes.FontMetrics.ascent);
let getDescent = metrics =>
getf(!@metrics, SkiaTypes.FontMetrics.descent);
let getTop = metrics => getf(!@metrics, SkiaTypes.FontMetrics.top);
let getBottom = metrics => getf(!@metrics, SkiaTypes.FontMetrics.bottom);
let getUnderlineThickness = metrics =>
getf(!@metrics, SkiaTypes.FontMetrics.underlineThickness);
let getUnderlinePosition = metrics =>
getf(!@metrics, SkiaTypes.FontMetrics.underlinePosition);
let getMaxCharacterWidth = metrics =>
getf(!@metrics, SkiaTypes.FontMetrics.maxCharacterWidth);
let getAvgCharacterWidth = metrics =>
getf(!@metrics, SkiaTypes.FontMetrics.avgCharacterWidth);
};
module ImageFilter = {
type t = ptr(structure(SkiaTypes.ImageFilter.t));
let t = ptr(SkiaTypes.ImageFilter.t);
module CropRect = {
type t = ptr(structure(SkiaTypes.ImageFilter.CropRect.t));
let t = ptr(SkiaTypes.ImageFilter.CropRect.t);
};
let delete = foreign("sk_imagefilter_unref", t @-> returning(void));
module DropShadow = {
type shadowMode = SkiaTypes.ImageFilter.DropShadow.shadowMode;
let shadowMode = SkiaTypes.ImageFilter.DropShadow.shadowMode;
let allocate =
foreign(
"sk_imagefilter_new_drop_shadow",
float
@-> float
@-> float
@-> float
@-> Color.t
@-> shadowMode
@-> ptr_opt(SkiaTypes.ImageFilter.t)
@-> ptr_opt(SkiaTypes.ImageFilter.CropRect.t)
@-> returning(t),
);
};
};
module Point = {
type t = ptr(structure(SkiaTypes.Point.t));
let t = ptr(SkiaTypes.Point.t);
let make = (x, y) => {
let point = allocate_n(SkiaTypes.Point.t, ~count=1);
setf(!@point, SkiaTypes.Point.x, x);
setf(!@point, SkiaTypes.Point.y, y);
point;
};
let getX = point => {
getf(!@point, SkiaTypes.Point.x);
};
let getY = point => {
getf(!@point, SkiaTypes.Point.y);
};
};
module Shader = {
type t = ptr(structure(SkiaTypes.Shader.t));
let t = ptr(SkiaTypes.Shader.t);
type tileMode = SkiaTypes.Shader.tileMode;
let tileMode = SkiaTypes.Shader.tileMode;
let empty =
foreign(
"sk_shader_new_empty",
void @-> returning(ptr(SkiaTypes.Shader.t)),
);
let makeLinearGradient2 =
foreign(
"reason_skia_stub_linear_gradient2",
Point.t
@-> Point.t
@-> Color.t
@-> Color.t
@-> tileMode
@-> returning(ptr(SkiaTypes.Shader.t)),
);
let makeLinearGradient =
foreign(
"reason_skia_stub_linear_gradient",
Point.t
@-> Point.t
@-> ptr(Color.t)
@-> ptr(float)
@-> int
@-> tileMode
@-> returning(ptr(SkiaTypes.Shader.t)),
);
let unref =
foreign(
"sk_shader_unref",
ptr(SkiaTypes.Shader.t) @-> returning(void),
);
};
module Rect = {
type t = ptr(structure(SkiaTypes.Rect.t));
let t = ptr(SkiaTypes.Rect.t);
let makeEmpty = () => {
let rect = allocate_n(SkiaTypes.Rect.t, ~count=1);
setf(!@rect, SkiaTypes.Rect.left, 0.);
setf(!@rect, SkiaTypes.Rect.top, 0.);
setf(!@rect, SkiaTypes.Rect.right, 0.);
setf(!@rect, SkiaTypes.Rect.bottom, 0.);
rect;
};
let makeLtrb = (left, top, right, bottom) => {
let rect = allocate_n(SkiaTypes.Rect.t, ~count=1);
setf(!@rect, SkiaTypes.Rect.left, left);
setf(!@rect, SkiaTypes.Rect.top, top);
setf(!@rect, SkiaTypes.Rect.right, right);
setf(!@rect, SkiaTypes.Rect.bottom, bottom);
rect;
};
let getLeft = rect => {
getf(!@rect, SkiaTypes.Rect.left);
};
let getTop = rect => {
getf(!@rect, SkiaTypes.Rect.top);
};
let getRight = rect => {
getf(!@rect, SkiaTypes.Rect.right);
};
let getBottom = rect => {
getf(!@rect, SkiaTypes.Rect.bottom);
};
};
module Path = {
type t = ptr(structure(SkiaTypes.Path.t));
let t = ptr(SkiaTypes.Path.t);
let allocate = foreign("sk_path_new", void @-> returning(t));
let delete = foreign("sk_path_delete", t @-> returning(void));
type arcSize = SkiaTypes.Path.arcSize;
let arcSize = SkiaTypes.Path.arcSize;
type pathDirection = SkiaTypes.Path.pathDirection;
let pathDirection = SkiaTypes.Path.pathDirection;
let addRoundRect =
foreign(
"sk_path_add_rounded_rect",
t @-> Rect.t @-> float @-> float @-> pathDirection @-> returning(void),
);
let addCircle =
foreign(
"sk_path_add_circle",
t @-> float @-> float @-> float @-> pathDirection @-> returning(void),
);
let moveTo =
foreign("sk_path_move_to", t @-> float @-> float @-> returning(void));
let rMoveTo =
foreign("sk_path_rmove_to", t @-> float @-> float @-> returning(void));
let lineTo =
foreign("sk_path_line_to", t @-> float @-> float @-> returning(void));
let rLineTo =
foreign("sk_path_rline_to", t @-> float @-> float @-> returning(void));
let cubicTo =
foreign(
"sk_path_cubic_to",
t
@-> float
@-> float
@-> float
@-> float
@-> float
@-> float
@-> returning(void),
);
let rCubicTo =
foreign(
"sk_path_rcubic_to",
t
@-> float
@-> float
@-> float
@-> float
@-> float
@-> float
@-> returning(void),
);
let quadTo =
foreign(
"sk_path_quad_to",
t @-> float @-> float @-> float @-> float @-> returning(void),
);
let rQuadTo =
foreign(
"sk_path_rquad_to",
t @-> float @-> float @-> float @-> float @-> returning(void),
);
let arcTo =
foreign(
"sk_path_arc_to",
t
@-> float
@-> float
@-> float
@-> arcSize
@-> pathDirection
@-> float
@-> float
@-> returning(void),
);
let rArcTo =
foreign(
"sk_path_rarc_to",
t
@-> float
@-> float
@-> float
@-> arcSize
@-> pathDirection
@-> float
@-> float
@-> returning(void),
);
let close = foreign("sk_path_close", t @-> returning(void));
let getLastPoint =
foreign("sk_path_get_last_point", t @-> Point.t @-> returning(bool));
};
module Vector = {
type t = ptr(structure(SkiaTypes.Vector.t));
let t = ptr(SkiaTypes.Vector.t);
let make = (x, y) => {
let point = allocate_n(SkiaTypes.Vector.t, ~count=1);
setf(!@point, SkiaTypes.Vector.x, x);
setf(!@point, SkiaTypes.Vector.y, y);
point;
};
let getX = point => {
getf(!@point, SkiaTypes.Vector.x);
};
let getY = point => {
getf(!@point, SkiaTypes.Vector.y);
};
};
module Matrix = {
type t = ptr(structure(SkiaTypes.Matrix.t));
let t = ptr(SkiaTypes.Matrix.t);
let make = () => allocate_n(SkiaTypes.Matrix.t, ~count=1);
let setAll =
(
matrix,
scaleX,
skewX,
translateX,
skewY,
scaleY,
translateY,
perspective0,
perspective1,
perspective2,
) => {
let mat = getf(!@matrix, SkiaTypes.Matrix.mat);
CArray.set(mat, 0, scaleX);
CArray.set(mat, 1, skewX);
CArray.set(mat, 2, translateX);
CArray.set(mat, 3, skewY);
CArray.set(mat, 4, scaleY);
CArray.set(mat, 5, translateY);
CArray.set(mat, 6, perspective0);
CArray.set(mat, 7, perspective1);
CArray.set(mat, 8, perspective2);
};
let get = (matrix, index) => {
let mat = getf(!@matrix, SkiaTypes.Matrix.mat);
CArray.get(mat, index);
};
let set = (matrix, index, value) => {
let mat = getf(!@matrix, SkiaTypes.Matrix.mat);
CArray.set(mat, index, value);
};
let invert =
foreign("sk_matrix_try_invert", t @-> t @-> returning(bool));
let concat =
foreign("sk_matrix_concat", t @-> t @-> t @-> returning(void));
let preConcat =
foreign("sk_matrix_pre_concat", t @-> t @-> returning(void));
let postConcat =
foreign("sk_matrix_post_concat", t @-> t @-> returning(void));
let mapRect =
foreign(
"sk_matrix_map_rect",
t @-> Rect.t @-> Rect.t @-> returning(void),
);
let mapPoints =
foreign(
"sk_matrix_map_points",
t @-> Point.t @-> Point.t @-> int @-> returning(void),
);
let mapVectors =
foreign(
"sk_matrix_map_vectors",
t @-> Vector.t @-> Vector.t @-> int @-> returning(void),
);
let mapXy =
foreign(
"sk_matrix_map_xy",
t @-> float @-> float @-> Point.t @-> returning(void),
);
let mapVector =
foreign(
"sk_matrix_map_vector",
t @-> float @-> float @-> Vector.t @-> returning(void),
);
let mapRadius =
foreign("sk_matrix_map_radius", t @-> float @-> returning(float));
};
module PathEffect = {
module Style = {
type t = SkiaTypes.PathEffect.Style.t;
let t = SkiaTypes.PathEffect.Style.t;
};
type t = ptr(structure(SkiaTypes.PathEffect.t));
let t = ptr(SkiaTypes.PathEffect.t);
let delete = foreign("sk_path_effect_unref", t @-> returning(void));
let allocate1d =
foreign(
"sk_path_effect_create_1d_path",
Path.t
@-> float
@-> float
@-> SkiaTypes.PathEffect.Style.t
@-> returning(t),
);
let allocate2dLine =
foreign(
"sk_path_effect_create_2d_line",
float @-> Matrix.t @-> returning(t),
);
let allocate2dPath =
foreign(
"sk_path_effect_create_2d_path",
Matrix.t @-> Path.t @-> returning(t),
);
};
module Paint = {
type t = ptr(structure(SkiaTypes.Paint.t));
let t = ptr(SkiaTypes.Paint.t);
type style = SkiaTypes.Paint.style;
let style = SkiaTypes.Paint.style;
let allocate = foreign("sk_paint_new", void @-> returning(t));
let delete = foreign("sk_paint_delete", t @-> returning(void));
let setColor =
foreign("sk_paint_set_color", t @-> Color.t @-> returning(void));
let setAntiAlias =
foreign("sk_paint_set_antialias", t @-> bool @-> returning(void));
let setStyle =
foreign("sk_paint_set_style", t @-> style @-> returning(void));
let setStrokeWidth =
foreign("sk_paint_set_stroke_width", t @-> float @-> returning(void));
let setTypeface =
foreign("sk_paint_set_typeface", t @-> Typeface.t @-> returning(void));
let setLcdRenderText =
foreign(
"sk_paint_set_lcd_render_text",
t @-> bool @-> returning(void),
);
let setSubpixelText =
foreign("sk_paint_set_subpixel_text", t @-> bool @-> returning(void));
let setTextSize =
foreign("sk_paint_set_textsize", t @-> float @-> returning(void));
let getFontMetrics =
foreign(
"sk_paint_get_fontmetrics",
t @-> ptr(SkiaTypes.FontMetrics.t) @-> float @-> returning(float),
);
let isAutohinted =
foreign("sk_paint_is_autohinted", t @-> returning(bool));
let setAutohinted =
foreign("sk_paint_set_autohinted", t @-> bool @-> returning(void));
let isAutohinted =
foreign("sk_paint_is_autohinted", t @-> returning(bool));
let getHinting =
foreign("sk_paint_get_hinting", t @-> returning(Hinting.t));
let setHinting =
foreign("sk_paint_set_hinting", t @-> Hinting.t @-> returning(void));
let setFilterQuality =
foreign(
"sk_paint_set_filter_quality",
t @-> FilterQuality.t @-> returning(void),
);
let getFilterQuality =
foreign(
"sk_paint_get_filter_quality",
t @-> returning(FilterQuality.t),
);
let measureText =
foreign(
"sk_paint_measure_text",
t
@-> string
@-> int
@-> ptr_opt(SkiaTypes.Rect.t)
@-> returning(float),
);
let setImageFilter =
foreign(
"sk_paint_set_imagefilter",
t @-> ptr_opt(SkiaTypes.ImageFilter.t) @-> returning(void),
);
let setPathEffect =
foreign(
"sk_paint_set_path_effect",
t @-> ptr(SkiaTypes.PathEffect.t) @-> returning(void),
);
let getPathEffect =
foreign(
"sk_paint_get_path_effect",
t @-> returning(ptr(SkiaTypes.PathEffect.t)),
);
let getTextEncoding =
foreign("sk_paint_get_text_encoding", t @-> returning(TextEncoding.t));
let setTextEncoding =
foreign(
"sk_paint_set_text_encoding",
t @-> TextEncoding.t @-> returning(void),
);
let setShader =
foreign("sk_paint_set_shader", t @-> Shader.t @-> returning(void));
};
module IRect = {
type t = ptr(structure(SkiaTypes.IRect.t));
let t = ptr(SkiaTypes.IRect.t);
let makeEmpty = () => {
let iRect = allocate_n(SkiaTypes.IRect.t, ~count=1);
setf(!@iRect, SkiaTypes.IRect.left, Int32.of_int(0));
setf(!@iRect, SkiaTypes.IRect.top, Int32.of_int(0));
setf(!@iRect, SkiaTypes.IRect.right, Int32.of_int(0));
setf(!@iRect, SkiaTypes.IRect.bottom, Int32.of_int(0));
iRect;
};
let makeLtrb = (left, top, right, bottom) => {
let iRect = allocate_n(SkiaTypes.IRect.t, ~count=1);
setf(!@iRect, SkiaTypes.IRect.left, left);
setf(!@iRect, SkiaTypes.IRect.top, top);
setf(!@iRect, SkiaTypes.IRect.right, right);
setf(!@iRect, SkiaTypes.IRect.bottom, bottom);
iRect;
};
};
module Matrix44 = {
type t = ptr(structure(SkiaTypes.Matrix44.t));
let t = ptr(SkiaTypes.Matrix44.t);
let allocate = foreign("sk_matrix44_new", void @-> returning(t));
let allocate_identity =
foreign("sk_matrix44_new_identity", void @-> returning(t));
let destroy = foreign("sk_matrix44_destroy", t @-> returning(void));
let setRotateAboutDegrees =
foreign(
"sk_matrix44_set_rotate_about_degrees",
t @-> float @-> float @-> float @-> float @-> returning(void),
);
let setRotateAboutRadians =
foreign(
"sk_matrix44_set_rotate_about_radians",
t @-> float @-> float @-> float @-> float @-> returning(void),
);
let setTranslate =
foreign(
"sk_matrix44_set_translate",
t @-> float @-> float @-> float @-> returning(void),
);
let preTranslate =
foreign(
"sk_matrix44_pre_translate",
t @-> float @-> float @-> float @-> returning(void),
);
let postTranslate =
foreign(
"sk_matrix44_post_translate",
t @-> float @-> float @-> float @-> returning(void),
);
let setScale =
foreign(
"sk_matrix44_set_scale",
t @-> float @-> float @-> float @-> returning(void),
);
let preScale =
foreign(
"sk_matrix44_pre_scale",
t @-> float @-> float @-> float @-> returning(void),
);
let postScale =
foreign(
"sk_matrix44_post_scale",
t @-> float @-> float @-> float @-> returning(void),
);
let setConcat =
foreign("sk_matrix44_set_concat", t @-> t @-> t @-> returning(void));
let preConcat =
foreign("sk_matrix44_pre_concat", t @-> t @-> returning(void));
let postConcat =
foreign("sk_matrix44_post_concat", t @-> t @-> returning(void));
let get =
foreign("sk_matrix44_get", t @-> int @-> int @-> returning(float));
let set =
foreign(
"sk_matrix44_set",
t @-> int @-> int @-> float @-> returning(void),
);
let toMatrix =
foreign("sk_matrix44_to_matrix", t @-> Matrix.t @-> returning(void));
};
module RRect = {
type t = ptr(structure(SkiaTypes.RRect.t));
let t = ptr(SkiaTypes.RRect.t);
type rRectType = SkiaTypes.RRect.rRectType;
let rRectType = SkiaTypes.RRect.rRectType;
type corner = SkiaTypes.RRect.corner;
let corner = SkiaTypes.RRect.corner;
let allocate = foreign("sk_rrect_new", void @-> returning(t));
let allocateCopy = foreign("sk_rrect_new_copy", t @-> returning(t));
let delete = foreign("sk_rrect_delete", t @-> returning(void));
let getType = foreign("sk_rrect_get_type", t @-> returning(rRectType));
let getRect =
foreign("sk_rrect_get_rect", t @-> Rect.t @-> returning(void));
let getRadii =
foreign(
"sk_rrect_get_radii",
t @-> corner @-> Vector.t @-> returning(void),
);
let getWidth = foreign("sk_rrect_get_width", t @-> returning(float));
let getHeight = foreign("sk_rrect_get_height", t @-> returning(float));
let setEmpty = foreign("sk_rrect_set_empty", t @-> returning(void));
let setRect =
foreign("sk_rrect_set_rect", t @-> Rect.t @-> returning(void));
let setOval =
foreign("sk_rrect_set_oval", t @-> Rect.t @-> returning(void));
let setRectXy =
foreign(
"sk_rrect_set_rect_xy",
t @-> Rect.t @-> float @-> float @-> returning(void),
);
let setNinePatch =
foreign(
"sk_rrect_set_nine_patch",
t
@-> Rect.t
@-> float
@-> float
@-> float
@-> float
@-> returning(void),
);
let setRectRadii =
foreign(
"sk_rrect_set_rect_radii",
t @-> Rect.t @-> Vector.t @-> returning(void),
);
let inset =
foreign("sk_rrect_inset", t @-> float @-> float @-> returning(void));
let outset =
foreign("sk_rrect_outset", t @-> float @-> float @-> returning(void));
let offset =
foreign("sk_rrect_offset", t @-> float @-> float @-> returning(void));
let contains =
foreign("sk_rrect_contains", t @-> Rect.t @-> returning(bool));
let isValid = foreign("sk_rrect_is_valid", t @-> returning(bool));
let transform =
foreign(
"sk_rrect_transform",
t @-> Matrix.t @-> t @-> returning(bool),
);
};
module ColorSpace = {
type t = ptr(structure(SkiaTypes.ColorSpace.t));
let t = ptr(SkiaTypes.ColorSpace.t);
};
module ImageInfo = {
type t = ptr(structure(SkiaTypes.ImageInfo.t));
let t = ptr(SkiaTypes.ImageInfo.t);
let make = (width, height, colorType, alphaType, colorSpace) => {
let imageInfo = allocate_n(SkiaTypes.ImageInfo.t, ~count=1);
setf(!@imageInfo, SkiaTypes.ImageInfo.width, width);
setf(!@imageInfo, SkiaTypes.ImageInfo.height, height);
setf(!@imageInfo, SkiaTypes.ImageInfo.colorType, colorType);
setf(!@imageInfo, SkiaTypes.ImageInfo.alphaType, alphaType);
setf(!@imageInfo, SkiaTypes.ImageInfo.colorSpace, colorSpace);
imageInfo;
};
};
module Image = {
type t = ptr(structure(SkiaTypes.Image.t));
let t = ptr(SkiaTypes.Image.t);
let allocateFromEncoded =
foreign(
"sk_image_new_from_encoded",
Data.t
@-> ptr_opt(SkiaTypes.IRect.t)
@-> returning(ptr_opt(SkiaTypes.Image.t)),
);
let delete = foreign("sk_image_unref", t @-> returning(void));
let encode = foreign("sk_image_encode", t @-> returning(Data.t));
let width = foreign("sk_image_get_width", t @-> returning(int));
let height = foreign("sk_image_get_height", t @-> returning(int));
};
type pixelGeometry = SkiaTypes.pixelGeometry;
let pixelGeometry = SkiaTypes.pixelGeometry;
module Gr = {
type surfaceOrigin = SkiaTypes.Gr.surfaceOrigin;
let surfaceOrigin = SkiaTypes.Gr.surfaceOrigin;
module Gl = {
module Interface = {
type t = ptr(structure(SkiaTypes.Gr.Gl.Interface.t));
let t = ptr(SkiaTypes.Gr.Gl.Interface.t);
let makeNative =
foreign(
"gr_glinterface_create_native_interface",
void @-> returning(ptr_opt(SkiaTypes.Gr.Gl.Interface.t)),
);
let makeSdl2 =
foreign(
"reason_skia_make_sdl2_gl_interface",
void @-> returning(ptr_opt(SkiaTypes.Gr.Gl.Interface.t)),
);
let makeSdl2ES =
foreign(
"reason_skia_make_sdl2_gles_interface",
void @-> returning(ptr_opt(SkiaTypes.Gr.Gl.Interface.t)),
);
};
module FramebufferInfo = {
type t = ptr(structure(SkiaTypes.Gr.Gl.FramebufferInfo.t));
let t = ptr(SkiaTypes.Gr.Gl.FramebufferInfo.t);
let make = (framebufferObjectId, format) => {
let framebufferInfo =
allocate_n(SkiaTypes.Gr.Gl.FramebufferInfo.t, ~count=1);
setf(
!@framebufferInfo,
SkiaTypes.Gr.Gl.FramebufferInfo.framebufferObjectId,
framebufferObjectId,
);
setf(
!@framebufferInfo,
SkiaTypes.Gr.Gl.FramebufferInfo.format,
format,
);
framebufferInfo;
};
};
};
module Context = {
type t = ptr(structure(SkiaTypes.Gr.Context.t));
let t = ptr(SkiaTypes.Gr.Context.t);
let makeGl =
foreign(
"gr_context_make_gl",
ptr_opt(SkiaTypes.Gr.Gl.Interface.t)
@-> returning(ptr_opt(SkiaTypes.Gr.Context.t)),
);
};
module BackendRenderTarget = {
type t = ptr(structure(SkiaTypes.Gr.BackendRenderTarget.t));
let t = ptr(SkiaTypes.Gr.BackendRenderTarget.t);
let makeGl =
foreign(
"gr_backendrendertarget_new_gl",
int
@-> int
@-> int
@-> int
@-> Gl.FramebufferInfo.t
@-> returning(t),
);
};
};
type clipOp = SkiaTypes.clipOp;
let clipOp = SkiaTypes.clipOp;
module Canvas = {
type t = ptr(structure(SkiaTypes.Canvas.t));
let t = ptr(SkiaTypes.Canvas.t);
let clear =
foreign("sk_canvas_clear", t @-> Color.t @-> returning(void));
let drawPaint =
foreign("sk_canvas_draw_paint", t @-> Paint.t @-> returning(void));
let drawRect =
foreign(
"sk_canvas_draw_rect",
t @-> Rect.t @-> Paint.t @-> returning(void),
);
let drawRectLtwh =
foreign(
"reason_skia_stub_sk_canvas_draw_rect_ltwh",
t
@-> float
@-> float
@-> float
@-> float
@-> Paint.t
@-> returning(void),
);
let drawRoundRect =
foreign(
"sk_canvas_draw_round_rect",
t @-> Rect.t @-> float @-> float @-> Paint.t @-> returning(void),
);
let drawOval =
foreign(
"sk_canvas_draw_oval",
t @-> Rect.t @-> Paint.t @-> returning(void),
);
let drawCircle =
foreign(
"sk_canvas_draw_circle",
t @-> float @-> float @-> float @-> Paint.t @-> returning(void),
);
let drawRRect =
foreign(
"sk_canvas_draw_rrect",
t @-> RRect.t @-> Paint.t @-> returning(void),
);
let drawPath =
foreign(
"sk_canvas_draw_path",
t @-> Path.t @-> Paint.t @-> returning(void),
);
let drawText =
foreign(
"sk_canvas_draw_text",
t
@-> string
@-> int
@-> float
@-> float
@-> Paint.t
@-> returning(void),
);
let drawImage =
foreign(
"sk_canvas_draw_image",
t
@-> Image.t
@-> float
@-> float
@-> ptr_opt(SkiaTypes.Paint.t)
@-> returning(void),
);
let drawImageRect =
foreign(
"sk_canvas_draw_image_rect",
t
@-> Image.t
@-> ptr_opt(SkiaTypes.Rect.t)
@-> ptr(SkiaTypes.Rect.t)
@-> ptr_opt(SkiaTypes.Paint.t)
@-> returning(void),
);
let concat =
foreign("sk_canvas_concat", t @-> Matrix.t @-> returning(void));
let setMatrix =
foreign("sk_canvas_set_matrix", t @-> Matrix.t @-> returning(void));
let translate =
foreign(
"sk_canvas_translate",
t @-> float @-> float @-> returning(void),
);
let scale =
foreign("sk_canvas_scale", t @-> float @-> float @-> returning(void));
let rotate =
foreign("sk_canvas_rotate_degrees", t @-> float @-> returning(void));
let skew =
foreign("sk_canvas_skew", t @-> float @-> float @-> returning(void));
let resetMatrix =
foreign("sk_canvas_reset_matrix", t @-> returning(void));
let clipRect =
foreign(
"sk_canvas_clip_rect_with_operation",
t @-> Rect.t @-> clipOp @-> bool @-> returning(void),
);
let clipPath =
foreign(
"sk_canvas_clip_path_with_operation",
t @-> Path.t @-> clipOp @-> bool @-> returning(void),
);
let clipRRect =
foreign(
"sk_canvas_clip_rrect_with_operation",
t @-> RRect.t @-> clipOp @-> bool @-> returning(void),
);
let save = foreign("sk_canvas_save", t @-> returning(int));
let saveLayer =
foreign(
"sk_canvas_save_layer",
t
@-> ptr_opt(SkiaTypes.Rect.t)
@-> ptr_opt(SkiaTypes.Paint.t)
@-> returning(int),
);
let restore = foreign("sk_canvas_restore", t @-> returning(void));
let flush = foreign("sk_canvas_flush", t @-> returning(void));
};
module SurfaceProps = {
type t = ptr(structure(SkiaTypes.SurfaceProps.t));
let t = ptr(SkiaTypes.SurfaceProps.t);
let make =
foreign(
"sk_surfaceprops_new",
uint32_t @-> pixelGeometry @-> returning(t),
);
};
module Surface = {
type t = ptr(structure(SkiaTypes.Surface.t));
let t = ptr(SkiaTypes.Surface.t);
let allocateRaster =
foreign(
"sk_surface_new_raster",
ImageInfo.t
@-> size_t
@-> ptr_opt(SkiaTypes.SurfaceProps.t)
@-> returning(ptr_opt(SkiaTypes.Surface.t)),
);
let allocateRenderTarget =
foreign(
"sk_surface_new_render_target",
Gr.Context.t
@-> bool
@-> ImageInfo.t
@-> int
@-> Gr.surfaceOrigin
@-> ptr_opt(SkiaTypes.SurfaceProps.t)
@-> bool
@-> returning(ptr_opt(SkiaTypes.Surface.t)),
);
let allocateFromBackendRenderTarget =
foreign(
"sk_surface_new_backend_render_target",
Gr.Context.t
@-> Gr.BackendRenderTarget.t
@-> Gr.surfaceOrigin
@-> colorType
@-> ptr_opt(SkiaTypes.ColorSpace.t)
@-> ptr_opt(SkiaTypes.SurfaceProps.t)
@-> returning(ptr_opt(SkiaTypes.Surface.t)),
);
let delete = foreign("sk_surface_unref", t @-> returning(void));
let draw =
foreign(
"sk_surface_draw",
t
@-> Canvas.t
@-> float
@-> float
@-> ptr_opt(SkiaTypes.Paint.t)
@-> returning(void),
);
let getCanvas =
foreign("sk_surface_get_canvas", t @-> returning(Canvas.t));
let allocateImageSnapshot =
foreign("sk_surface_new_image_snapshot", t @-> returning(Image.t));
let getWidth = foreign("sk_surface_get_width", t @-> returning(int));
let getHeight = foreign("sk_surface_get_height", t @-> returning(int));
let getProps =
foreign("sk_surface_get_props", t @-> returning(SurfaceProps.t));
};
module SVG = {
type t = ptr(structure(SkiaTypes.SVG.t));
let t = ptr(SkiaTypes.SVG.t);
let maybeT = ptr_opt(SkiaTypes.SVG.t);
let makeFromStream =
foreign(
"sk_svgdom_create_from_stream",
Stream.t @-> returning(maybeT),
);
let render =
foreign("sk_svgdom_render", t @-> Canvas.t @-> returning(void));
let setContainerSize =
foreign(
"sk_svgdom_set_container_size",
t @-> float @-> float @-> returning(void),
);
let getContainerWidth =
foreign("sk_svgdom_get_container_width", t @-> returning(float));
let getContainerHeight =
foreign("sk_svgdom_get_container_height", t @-> returning(float));
let delete = foreign("sk_svgdom_unref", t @-> returning(void));
};
};
================================================
FILE: packages/reason-skia/src/wrapped/bindings/dune
================================================
(rule
(targets skia_generated_type_stubs.ml)
(deps ../stubgen/ml_types_stubgen.exe)
(action
(with-stdout-to
%{targets}
(run %{deps}))))
(library
(name SkiaWrappedBindings)
(flags -w -9)
(public_name reason-skia.wrapped.bindings)
(libraries reason-skia.wrapped.types reason-skia.wrapped.c ctypes.stubs
ctypes integers))
================================================
FILE: packages/reason-skia/src/wrapped/c/c_stubs.c
================================================
/*
* Use this file for building any C-layer functionality that we want to keep out
* of Reason
*/
#include "c_stubs.h"
#include
#include
void reason_skia_stub_sk_canvas_draw_rect_ltwh(sk_canvas_t *canvas, float left,
float top, float width,
float height,
sk_paint_t *paint) {
sk_rect_t rect;
rect.left = left;
rect.top = top;
rect.right = left + width;
rect.bottom = top + height;
sk_canvas_draw_rect(canvas, &rect, paint);
}
void *reason_skia_sdl2_get(void *ctx, const char name[]) {
return SDL_GL_GetProcAddress(name);
};
gr_glinterface_t *reason_skia_make_sdl2_gl_interface() {
gr_glinterface_t *interface =
gr_glinterface_assemble_gl_interface(0, reason_skia_sdl2_get);
return interface;
}
gr_glinterface_t *reason_skia_make_sdl2_gles_interface() {
gr_glinterface_t *interface =
gr_glinterface_assemble_gles_interface(0, reason_skia_sdl2_get);
return interface;
}
sk_shader_t* reason_skia_stub_linear_gradient2(
sk_point_t* startPosition,
sk_point_t* stopPosition,
sk_color_t startColor,
sk_color_t stopColor,
sk_shader_tilemode_t tileMode) {
sk_point_t pts[2];
pts[0] = *startPosition;
pts[1] = *stopPosition;
sk_color_t colors[2];
colors[0] = startColor;
colors[1] = stopColor;
float stops[2];
stops[0] = 0.0f;
stops[1] = 1.0f;
return sk_shader_new_linear_gradient(
pts,
colors,
stops,
2,
tileMode,
NULL
);
}
sk_shader_t* reason_skia_stub_linear_gradient(
sk_point_t* startPosition,
sk_point_t* stopPosition,
sk_color_t* colors,
float* positions,
int count,
sk_shader_tilemode_t tileMode) {
sk_point_t pts[2];
pts[0] = *startPosition;
pts[1] = *stopPosition;
return sk_shader_new_linear_gradient(
pts,
colors,
positions,
count,
tileMode,
NULL
);
}
================================================
FILE: packages/reason-skia/src/wrapped/c/c_stubs.h
================================================
#include "gr_context.h"
#include "sk_canvas.h"
#include "sk_paint.h"
#include "sk_patheffect.h"
#include "sk_shader.h"
#include "sk_types.h"
#include
void reason_skia_stub_sk_canvas_draw_rect_ltwh(sk_canvas_t *canvas, float left,
float top, float width,
float height, sk_paint_t *paint);
gr_glinterface_t *reason_skia_make_sdl2_gl_interface();
gr_glinterface_t *reason_skia_make_sdl2_gles_interface();
sk_shader_t* reason_skia_stub_linear_gradient2(
sk_point_t* startPosition,
sk_point_t* stopPosition,
sk_color_t startColor,
sk_color_t stopColor,
sk_shader_tilemode_t tileMode);
sk_shader_t* reason_skia_stub_linear_gradient(
sk_point_t* startPosition,
sk_point_t* stopPosition,
sk_color_t* colors,
float* positions,
int count,
sk_shader_tilemode_t tileMode);
================================================
FILE: packages/reason-skia/src/wrapped/c/dune
================================================
(rule
(targets c_flags.txt c_library_flags.txt)
(deps
(:discover ../../config/discover.exe))
(action
(run %{discover})))
(library
(name skia_wrapped_c)
(public_name reason-skia.wrapped.c)
(preprocess no_preprocessing)
(libraries sdl2)
(foreign_archives skia_wrapped_c_stubs))
(rule
(targets libskia_wrapped_c_stubs.a)
(deps c_stubs.o)
(action
(run ar rcs %{targets} %{deps})))
(rule
(targets dllskia_wrapped_c_stubs.dll)
(deps c_stubs.o)
(action
(run %{cc} -shared -o %{targets} %{deps} %{read-lines:c_library_flags.txt})))
(rule
(targets dllskia_wrapped_c_stubs.so)
(deps c_stubs.o)
(action
(run %{cc} %{read-lines:c_library_flags.txt} -shared -o %{targets} %{deps})))
(rule
(targets c_stubs.o)
(deps
(:c c_stubs.c c_stubs.h)
c_flags.txt)
(action
(run %{cc} %{read-lines:c_flags.txt} -c %{c})))
================================================
FILE: packages/reason-skia/src/wrapped/lib/SkiaWrapped.re
================================================
include SkiaWrappedBindings.M(Skia_generated_stubs);
================================================
FILE: packages/reason-skia/src/wrapped/lib/dune
================================================
(rule
(targets skia_generated_stubs.ml)
(deps ../stubgen/stubgen.exe)
(action
(with-stdout-to
%{targets}
(run %{deps} -ml))))
(rule
(targets skia_generated_stubs.c)
(deps
(:stubgen ../stubgen/stubgen.exe))
(action
(with-stdout-to
%{targets}
(run %{stubgen} -c))))
(library
(name SkiaWrapped)
(flags
(-w -40 -w +26))
(public_name reason-skia.wrapped)
(libraries reason-skia.wrapped.types reason-skia.wrapped.bindings
reason-skia.wrapped.c ctypes integers)
(foreign_stubs
(language c)
(names skia_generated_stubs raw_bindings)
(flags
(:include ../../c_flags.sexp)))
(c_library_flags
(:include ../../c_library_flags.sexp)))
================================================
FILE: packages/reason-skia/src/wrapped/lib/raw_bindings.c
================================================
/*
* Use this file for any manual, raw bindings - ie,
* ones that use [@noalloc], [@unboxed].
*/
#include "c_stubs.h"
#include "sk_canvas.h"
#include "sk_matrix.h"
#include "sk_paint.h"
#include "sk_types.h"
#include "sk_typeface.h"
#include "sk_data.h"
#include "sk_stream.h"
#include
#include
#include "ctypes_cstubs_internals.h"
CAMLprim value reason_skia_paint_set_color(value vPaint, int32_t color) {
sk_paint_t *pPaint = CTYPES_ADDR_OF_FATPTR(vPaint);
sk_paint_set_color(pPaint, color);
return Val_unit;
}
CAMLprim value reason_skia_paint_set_alphaf(value vPaint, double alpha) {
sk_paint_t *pPaint = CTYPES_ADDR_OF_FATPTR(vPaint);
int a = (int)(255.0 * alpha);
sk_color_t c = sk_paint_get_color(pPaint);
sk_paint_set_color(pPaint,
sk_color_set_argb(a, sk_color_get_r(c), sk_color_get_g(c),
sk_color_get_b(c)));
sk_color_t newColor = sk_paint_get_color(pPaint);
return Val_unit;
}
CAMLprim value reason_skia_paint_set_alphaf_byte(value vPaint, value vAlpha) {
reason_skia_paint_set_alphaf(vPaint, Double_val(vAlpha));
return Val_unit;
}
CAMLprim value reason_skia_paint_set_color_byte(value vPaint, value vColor) {
reason_skia_paint_set_color(vPaint, Int32_val(vColor));
return Val_unit;
}
double reason_skia_stub_sk_color_float_get_b(int32_t color) {
return (double)(sk_color_get_b(color) / 255.0);
}
CAMLprim value reason_skia_stub_sk_color_float_get_b_byte(value vColor) {
return caml_copy_double(
reason_skia_stub_sk_color_float_get_b(Int32_val(vColor)));
}
double reason_skia_stub_sk_color_float_get_g(int32_t color) {
return (double)(sk_color_get_g(color) / 255.0);
}
CAMLprim value reason_skia_stub_sk_color_float_get_g_byte(value vColor) {
return caml_copy_double(
reason_skia_stub_sk_color_float_get_g(Int32_val(vColor)));
}
double reason_skia_stub_sk_color_float_get_r(int32_t color) {
return (double)(sk_color_get_r(color) / 255.0);
}
CAMLprim value reason_skia_stub_sk_color_float_get_r_byte(value vColor) {
return caml_copy_double(
reason_skia_stub_sk_color_float_get_r(Int32_val(vColor)));
}
double reason_skia_stub_sk_color_float_get_a(int32_t color) {
return (double)(sk_color_get_a(color) / 255.0);
}
CAMLprim value reason_skia_stub_sk_color_float_get_a_byte(value vColor) {
return caml_copy_double(
reason_skia_stub_sk_color_float_get_a(Int32_val(vColor)));
}
uint32_t reason_skia_color_float_make_argb(double a, double r, double g,
double b) {
int iA = (int)(255.0 * a);
int iR = (int)(255.0 * r);
int iG = (int)(255.0 * g);
int iB = (int)(255.0 * b);
return (uint32_t)sk_color_set_argb(iA, iR, iG, iB);
}
CAMLprim value reason_skia_color_float_make_argb_byte(value vA, value vR,
value vG, value vB) {
return caml_copy_int32(reason_skia_color_float_make_argb(
Double_val(vA), Double_val(vR), Double_val(vG), Double_val(vB)));
}
uint32_t reason_skia_stub_sk_color_get_a(int32_t color) {
return (uint32_t)sk_color_get_a(color);
}
CAMLprim value reason_skia_stub_sk_color_get_a_byte(value vColor) {
return caml_copy_int32(reason_skia_stub_sk_color_get_a(Int32_val(vColor)));
}
uint32_t reason_skia_stub_sk_color_get_r(int32_t color) {
return (uint32_t)sk_color_get_r(color);
}
CAMLprim value reason_skia_stub_sk_color_get_r_byte(value vColor) {
return caml_copy_int32(reason_skia_stub_sk_color_get_r(Int32_val(vColor)));
}
uint32_t reason_skia_stub_sk_color_get_g(int32_t color) {
return (uint32_t)sk_color_get_g(color);
}
CAMLprim value reason_skia_stub_sk_color_get_g_byte(value vColor) {
return caml_copy_int32(reason_skia_stub_sk_color_get_g(Int32_val(vColor)));
}
uint32_t reason_skia_stub_sk_color_get_b(int32_t color) {
return (uint32_t)sk_color_get_b(color);
}
CAMLprim value reason_skia_stub_sk_color_get_b_byte(value vColor) {
return caml_copy_int32(reason_skia_stub_sk_color_get_b(Int32_val(vColor)));
}
uint32_t reason_skia_stub_sk_color_set_argb(int32_t alpha, int32_t red,
int32_t green, int32_t blue) {
return (uint32_t)sk_color_set_argb(alpha, red, green, blue);
}
CAMLprim value reason_skia_stub_sk_color_set_argb_byte(value vAlpha, value vRed,
value vGreen,
value vBlue) {
return caml_copy_int32(reason_skia_stub_sk_color_set_argb(
Int32_val(vAlpha), Int32_val(vRed), Int32_val(vGreen), Int32_val(vBlue)));
}
double reason_skia_rect_get_bottom(value vRect) {
sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);
return (double)pRect->bottom;
}
CAMLprim value reason_skia_rect_get_bottom_byte(value vRect) {
return caml_copy_double(reason_skia_rect_get_bottom(vRect));
}
double reason_skia_rect_get_right(value vRect) {
sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);
return (double)pRect->right;
}
CAMLprim value reason_skia_rect_get_right_byte(value vRect) {
return caml_copy_double(reason_skia_rect_get_right(vRect));
}
double reason_skia_rect_get_top(value vRect) {
sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);
return (double)pRect->top;
}
CAMLprim value reason_skia_rect_get_top_byte(value vRect) {
return caml_copy_double(reason_skia_rect_get_top(vRect));
}
double reason_skia_rect_get_left(value vRect) {
sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);
return (double)pRect->left;
}
CAMLprim value reason_skia_rect_get_left_byte(value vRect) {
return caml_copy_double(reason_skia_rect_get_left(vRect));
}
CAMLprim value reason_skia_rect_set(value vRect, double left, double top,
double right, double bottom) {
sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);
pRect->left = left;
pRect->top = top;
pRect->right = right;
pRect->bottom = bottom;
return Val_unit;
}
CAMLprim value reason_skia_rect_set_byte(value vRect, value vLeft, value vTop,
value vRight, value vBottom) {
return reason_skia_rect_set(vRect, Double_val(vLeft), Double_val(vTop),
Double_val(vRight), Double_val(vBottom));
}
CAMLprim value reason_skia_matrix_set_scale(value vMatrix, double scaleX,
double scaleY, double pivotX,
double pivotY) {
float *pMatrix = CTYPES_ADDR_OF_FATPTR(vMatrix);
pMatrix[0] = scaleX;
pMatrix[1] = 0.0;
pMatrix[2] = pivotX - (scaleX * pivotX);
pMatrix[3] = 0.0;
pMatrix[4] = scaleY;
pMatrix[5] = pivotY - (scaleY * pivotY);
pMatrix[6] = 0.0;
pMatrix[7] = 0.0;
pMatrix[8] = 1.0;
return Val_unit;
}
CAMLprim value reason_skia_matrix_set_scale_byte(value vMatrix, value vScaleX,
value vScaleY, value vPivotX,
value vPivotY) {
return reason_skia_matrix_set_scale(vMatrix, Double_val(vScaleX),
Double_val(vScaleY), Double_val(vPivotX),
Double_val(vPivotY));
}
CAMLprim value reason_skia_matrix_set_translate(value vMatrix,
double translateX,
double translateY) {
float *pMatrix = CTYPES_ADDR_OF_FATPTR(vMatrix);
pMatrix[0] = 1.0;
pMatrix[1] = 0.0;
pMatrix[2] = translateX;
pMatrix[3] = 0.0;
pMatrix[4] = 1.0;
pMatrix[5] = translateY;
pMatrix[6] = 0.0;
pMatrix[7] = 0.0;
pMatrix[8] = 1.0;
return Val_unit;
}
CAMLprim value reason_skia_matrix_set_translate_byte(value vMatrix,
value vTranslateX,
value vTranslateY) {
return reason_skia_matrix_set_translate(vMatrix, Double_val(vTranslateX),
Double_val(vTranslateY));
}
================================================
FILE: packages/reason-skia/src/wrapped/stubgen/dune
================================================
(executable
(name stubgen)
(package reason-skia)
(modules stubgen)
(public_name stubgen.exe)
(libraries reason-skia.wrapped.bindings ctypes))
(executable
(name types_stubgen)
(package reason-skia)
(modules types_stubgen)
(public_name types_stubgen.exe)
(libraries reason-skia.wrapped.types ctypes.stubs ctypes))
(rule
(targets ml_types_stubgen.c)
(deps ./types_stubgen.exe)
(action
(with-stdout-to
%{targets}
(run %{deps}))))
(rule
(targets ctypes_path.txt)
(action
(with-stdout-to
%{targets}
(run ocamlfind query ctypes))))
(rule
(targets ml_types_stubgen.exe)
(deps
(:c ./ml_types_stubgen.c)
(:ctypes_path ctypes_path.txt)
../c/libskia_wrapped_c_stubs.a)
(action
(run %{cc} %{c} %{read-lines:../c/c_flags.txt}
-I%{read-lines:ctypes_path.txt} -I%{ocaml_where} -o %{targets})))
================================================
FILE: packages/reason-skia/src/wrapped/stubgen/stubgen.ml
================================================
let prefix = "skia_wrapped_stub"
let prologue = {|
#include "c_stubs.h"
#include "gr_context.h"
#include "sk_canvas.h"
#include "sk_data.h"
#include "sk_image.h"
#include "sk_imagefilter.h"
#include "sk_paint.h"
#include "sk_path.h"
#include "sk_surface.h"
#include "sk_rrect.h"
#include "sk_matrix.h"
#include "sk_typeface.h"
#include "sk_stream.h"
#include "sk_string.h"
// Enable the SVG functions
#define ESY_SKIA_SVG
#include "sk_svg.h"
|}
let () =
let generate_ml, generate_c = ref false, ref false in
let () =
Arg.(parse [ ("-ml", Set generate_ml, "Generate ML");
("-c", Set generate_c, "Generate C") ])
(fun _ -> failwith "unexpected anonymous argument")
"stubgen [-ml|-c]"
in
match !generate_ml, !generate_c with
| false, false
| true, true ->
failwith "Exactly one of -ml and -c must be specified"
| true, false ->
Cstubs.write_ml Format.std_formatter ~prefix (module SkiaWrappedBindings.M)
| false, true ->
print_endline prologue;
Cstubs.write_c Format.std_formatter ~prefix (module SkiaWrappedBindings.M)
================================================
FILE: packages/reason-skia/src/wrapped/stubgen/types_stubgen.ml
================================================
let prefix = "skia_wrapped_stub"
let prologue = "
#include \"sk_types.h\"
"
let () =
print_endline prologue;
Cstubs.Types.write_c Format.std_formatter (module SkiaWrappedTypes.M)
================================================
FILE: packages/reason-skia/src/wrapped/types/SkiaWrappedTypes.re
================================================
open Ctypes;
module M = (T: TYPE) => {
open T;
let skiaCEnum = (name, mapping) =>
enum(
name,
~typedef=true,
List.map(
((constructor, constantName)) =>
(constructor, constant(constantName, int64_t)),
mapping,
),
~unexpected=i =>
invalid_arg(Printf.sprintf("Unsupported %s enum: %Ld", name, i))
);
module FilterQuality = {
type t = [ | `none | `low | `medium | `high];
let t: T.typ(t) =
skiaCEnum(
"sk_filter_quality_t",
[
(`none, "NONE_SK_FILTER_QUALITY"),
(`low, "LOW_SK_FILTER_QUALITY"),
(`medium, "MEDIUM_SK_FILTER_QUALITY"),
(`high, "HIGH_SK_FILTER_QUALITY"),
],
);
};
module Hinting = {
type t =
| NoHinting
| SlightHinting
| NormalHinting
| FullHinting;
let t =
skiaCEnum(
"sk_paint_hinting_t",
[
(NoHinting, "NO_HINTING_SK_PAINT_HINTING"),
(SlightHinting, "SLIGHT_HINTING_SK_PAINT_HINTING"),
(NormalHinting, "NORMAL_HINTING_SK_PAINT_HINTING"),
(FullHinting, "FULL_HINTING_SK_PAINT_HINTING"),
],
);
};
module TextEncoding = {
type t =
| Utf8
| Utf16
| Utf32
| GlyphId;
let t =
skiaCEnum(
"sk_text_encoding_t",
[
(Utf8, "UTF8_SK_TEXT_ENCODING"),
(Utf16, "UTF16_SK_TEXT_ENCODING"),
(Utf32, "UTF32_SK_TEXT_ENCODING"),
(GlyphId, "GLYPH_ID_SK_TEXT_ENCODING"),
],
);
};
module FontStyle = {
type t;
let t: typ(structure(t)) = structure("sk_fontstyle_t");
let t = typedef(t, "sk_fontstyle_t");
type slant =
| Upright
| Italic
| Oblique;
let slant =
skiaCEnum(
"sk_font_style_slant_t",
[
(Upright, "UPRIGHT_SK_FONT_STYLE_SLANT"),
(Italic, "ITALIC_SK_FONT_STYLE_SLANT"),
(Oblique, "OBLIQUE_SK_FONT_STYLE_SLANT"),
],
);
};
module Data = {
type t;
let t: typ(structure(t)) = structure("sk_data_t");
let t = typedef(t, "sk_data_t");
};
module Stream = {
type t;
let t: typ(structure(t)) = structure("sk_stream_t");
let t = typedef(t, "sk_stream_t");
};
module String = {
type t;
let t: typ(structure(t)) = structure("sk_string_t");
let t = typedef(t, "sk_string_t");
};
module Typeface = {
type t;
let t: typ(structure(t)) = structure("sk_typeface_t");
let t = typedef(t, "sk_typeface_t");
};
module FontManager = {
type t;
let t: typ(structure(t)) = structure("sk_fontmgr_t");
let t = typedef(t, "sk_fontmgr_t");
};
module FontMetrics = {
type t;
let t: typ(structure(t)) = structure("sk_fontmetrics_t");
let flags = field(t, "fFlags", uint32_t);
let top = field(t, "fTop", float);
let ascent = field(t, "fAscent", float);
let descent = field(t, "fDescent", float);
let bottom = field(t, "fBottom", float);
let leading = field(t, "fLeading", float);
let avgCharWidth = field(t, "fAvgCharWidth", float);
let maxCharWidth = field(t, "fMaxCharWidth", float);
let xMin = field(t, "fXMin", float);
let xMax = field(t, "fXMax", float);
let xHeight = field(t, "fXHeight", float);
let capHeight = field(t, "fCapHeight", float);
let underlineThickness = field(t, "fUnderlineThickness", float);
let underlinePosition = field(t, "fUnderlinePosition", float);
let strikeoutThickness = field(t, "fStrikeoutThickness", float);
let strikeoutPosition = field(t, "fStrikeoutPosition", float);
let maxCharacterWidth = field(t, "fMaxCharWidth", float);
let avgCharacterWidth = field(t, "fAvgCharWidth", float);
seal(t);
let t = typedef(t, "sk_fontmetrics_t");
};
module ImageFilter = {
type t;
let t: typ(structure(t)) = structure("sk_imagefilter_t");
let t = typedef(t, "sk_imagefilter_t");
module CropRect = {
type t;
let t: typ(structure(t)) = structure("sk_imagefilter_croprect_t");
let t = typedef(t, "sk_imagefilter_croprect_t");
};
module DropShadow = {
type shadowMode =
| DrawShadowAndForeground
| DrawShadowOnly;
let shadowMode =
skiaCEnum(
"sk_drop_shadow_image_filter_shadow_mode_t",
[
(
DrawShadowAndForeground,
"DRAW_SHADOW_AND_FOREGROUND_SK_DROP_SHADOW_IMAGE_FILTER_SHADOW_MODE",
),
(
DrawShadowOnly,
"DRAW_SHADOW_ONLY_SK_DROP_SHADOW_IMAGE_FILTER_SHADOW_MODE",
),
],
);
};
};
module PathEffect = {
module Style = {
type t = [ | `translate | `rotate | `morph];
let t: T.typ(t) =
skiaCEnum(
"sk_path_effect_1d_style_t",
[
(`translate, "TRANSLATE_SK_PATH_EFFECT_1D_STYLE"),
(`rotate, "ROTATE_SK_PATH_EFFECT_1D_STYLE"),
(`morph, "MORPH_SK_PATH_EFFECT_1D_STYLE"),
],
);
};
type t;
let t: typ(structure(t)) = structure("sk_path_effect_t");
let t = typedef(t, "sk_path_effect_t");
};
module Paint = {
type t;
let t: typ(structure(t)) = structure("sk_paint_t");
let t = typedef(t, "sk_paint_t");
type style =
| Fill
| Stroke
| StrokeAndFill;
let style =
skiaCEnum(
"sk_paint_style_t",
[
(Fill, "FILL_SK_PAINT_STYLE"),
(Stroke, "STROKE_SK_PAINT_STYLE"),
(StrokeAndFill, "STROKE_AND_FILL_SK_PAINT_STYLE"),
],
);
};
module Point = {
type t;
let t: typ(structure(t)) = structure("sk_point_t");
let x = field(t, "x", float);
let y = field(t, "y", float);
seal(t);
let t = typedef(t, "sk_point_t");
};
module Shader = {
type tileMode = [ | `clamp | `repeat | `mirror];
let tileMode: T.typ(tileMode) =
skiaCEnum(
"sk_shader_tilemode_t",
[
(`clamp, "CLAMP_SK_SHADER_TILEMODE"),
(`repeat, "REPEAT_SK_SHADER_TILEMODE"),
(`mirror, "MIRROR_SK_SHADER_TILEMODE"),
],
);
type t;
let t: typ(structure(t)) = structure("sk_shader_t");
let t = typedef(t, "sk_shader_t");
};
module Vector = Point;
module Matrix = {
type t;
let t: typ(structure(t)) = structure("sk_matrix_t");
let mat = field(t, "mat", array(9, float));
seal(t);
let t = typedef(t, "sk_matrix_t");
};
module Matrix44 = {
type t;
let t: typ(structure(t)) = structure("sk_matrix44_t");
let t = typedef(t, "sk_matrix44_t");
};
module IRect = {
type t;
let t: typ(structure(t)) = structure("sk_irect_t");
let left = field(t, "left", int32_t);
let top = field(t, "top", int32_t);
let right = field(t, "right", int32_t);
let bottom = field(t, "bottom", int32_t);
seal(t);
let t = typedef(t, "sk_irect_t");
};
module Rect = {
type t;
let t: typ(structure(t)) = structure("sk_rect_t");
let left = field(t, "left", float);
let top = field(t, "top", float);
let right = field(t, "right", float);
let bottom = field(t, "bottom", float);
seal(t);
let t = typedef(t, "sk_rect_t");
};
module RRect = {
type t;
let t: typ(structure(t)) = structure("sk_rrect_t");
let t = typedef(t, "sk_rrect_t");
// this should be called "type" only but that's a reserved keyword
type rRectType =
| Empty
| Rect
| Oval
| Simple
| NinePatch
| Complex;
let rRectType =
skiaCEnum(
"sk_rrect_type_t",
[
(Empty, "EMPTY_SK_RRECT_TYPE"),
(Rect, "RECT_SK_RRECT_TYPE"),
(Oval, "OVAL_SK_RRECT_TYPE"),
(Simple, "SIMPLE_SK_RRECT_TYPE"),
(NinePatch, "NINE_PATCH_SK_RRECT_TYPE"),
(Complex, "COMPLEX_SK_RRECT_TYPE"),
],
);
type corner =
| UpperLeft
| UpperRight
| LowerLeft
| LowerRight;
let corner =
skiaCEnum(
"sk_rrect_corner_t",
[
(UpperLeft, "UPPER_LEFT_SK_RRECT_CORNER"),
(UpperRight, "UPPER_RIGHT_SK_RRECT_CORNER"),
(LowerRight, "LOWER_RIGHT_SK_RRECT_CORNER"),
(LowerLeft, "LOWER_LEFT_SK_RRECT_CORNER"),
],
);
};
module Path = {
type arcSize = [ | `small | `large];
let arcSize: T.typ(arcSize) =
skiaCEnum(
"sk_path_arc_size_t",
[
(`small, "SMALL_SK_PATH_ARC_SIZE"),
(`large, "LARGE_SK_PATH_ARC_SIZE"),
],
);
type pathDirection = [ | `cw | `ccw];
let pathDirection: T.typ(pathDirection) =
skiaCEnum(
"sk_path_direction_t",
[(`cw, "CW_SK_PATH_DIRECTION"), (`ccw, "CCW_SK_PATH_DIRECTION")],
);
type t;
let t: typ(structure(t)) = structure("sk_path_t");
let t = typedef(t, "sk_path_t");
};
module ColorSpace = {
type t;
let t: typ(structure(t)) = structure("sk_colorspace_t");
let t = typedef(t, "sk_colorspace_t");
};
type colorType =
| Unknown
| Alpha8
| Rgb565
| Argb4444
| Rgba8888
| Rgb888x
| Bgra8888
| Rgba1010102
| Rgb101010x
| Gray8
| RgbaF16;
let colorType =
skiaCEnum(
"sk_colortype_t",
[
(Unknown, "UNKNOWN_SK_COLORTYPE"),
(Alpha8, "ALPHA_8_SK_COLORTYPE"),
(Rgb565, "RGB_565_SK_COLORTYPE"),
(Argb4444, "ARGB_4444_SK_COLORTYPE"),
(Rgba8888, "RGBA_8888_SK_COLORTYPE"),
(Rgb888x, "RGB_888X_SK_COLORTYPE"),
(Bgra8888, "BGRA_8888_SK_COLORTYPE"),
(Rgba1010102, "RGBA_1010102_SK_COLORTYPE"),
(Rgb101010x, "RGB_101010X_SK_COLORTYPE"),
(Gray8, "GRAY_8_SK_COLORTYPE"),
(RgbaF16, "RGBA_F16_SK_COLORTYPE"),
],
);
type alphaType =
| Unknown
| Opaque
| Premul
| Unpremul;
let alphaType =
skiaCEnum(
"sk_alphatype_t",
[
(Unknown, "UNKNOWN_SK_ALPHATYPE"),
(Opaque, "OPAQUE_SK_ALPHATYPE"),
(Premul, "PREMUL_SK_ALPHATYPE"),
(Unpremul, "UNPREMUL_SK_ALPHATYPE"),
],
);
module ImageInfo = {
type t;
let t: typ(structure(t)) = structure("sk_imageinfo_t");
let colorSpace = field(t, "colorspace", ptr_opt(ColorSpace.t));
let width = field(t, "width", int32_t);
let height = field(t, "height", int32_t);
let colorType = field(t, "colorType", colorType);
let alphaType = field(t, "alphaType", alphaType);
seal(t);
let t = typedef(t, "sk_imageinfo_t");
};
module Image = {
type t;
let t: typ(structure(t)) = structure("sk_image_t");
let t = typedef(t, "sk_image_t");
};
module SVG = {
type t;
let t: typ(structure(t)) = structure("sk_svgdom_t");
let t = typedef(t, "sk_svgdom_t");
};
type pixelGeometry =
| Unknown
| RgbH
| BgrH
| RgbV
| BgrV;
let pixelGeometry =
skiaCEnum(
"sk_pixelgeometry_t",
[
(Unknown, "UNKNOWN_SK_PIXELGEOMETRY"),
(RgbH, "RGB_H_SK_PIXELGEOMETRY"),
(BgrH, "BGR_H_SK_PIXELGEOMETRY"),
(RgbV, "RGB_V_SK_PIXELGEOMETRY"),
(BgrV, "RGB_V_SK_PIXELGEOMETRY"),
],
);
module Gr = {
type surfaceOrigin =
| TopLeft
| BottomLeft;
let surfaceOrigin =
skiaCEnum(
"gr_surfaceorigin_t",
[
(TopLeft, "TOP_LEFT_GR_SURFACE_ORIGIN"),
(BottomLeft, "BOTTOM_LEFT_GR_SURFACE_ORIGIN"),
],
);
module Gl = {
module Interface = {
type t;
let t: typ(structure(t)) = structure("gr_glinterface_t");
let t = typedef(t, "gr_glinterface_t");
};
module FramebufferInfo = {
type t;
let t: typ(structure(t)) = structure("gr_gl_framebufferinfo_t");
let framebufferObjectId = field(t, "fFBOID", uint);
let format = field(t, "fFormat", uint); // TODO this could also be an enum?
seal(t);
let t = typedef(t, "gr_gl_framebufferinfo_t");
};
};
module Context = {
type t;
let t: typ(structure(t)) = structure("gr_context_t");
let t = typedef(t, "gr_context_t");
};
module BackendRenderTarget = {
type t;
let t: typ(structure(t)) = structure("gr_backendrendertarget_t");
let t = typedef(t, "gr_backendrendertarget_t");
};
};
module TextBlob = {
type t;
let t: typ(structure(t)) = structure("sk_textblob_t");
let t = typedef(t, "sk_textblob_t");
module Builder = {
type t;
let t: typ(structure(t)) = structure("sk_textblob_builder_t");
let t = typedef(t, "sk_textblob_builder_t");
};
};
type clipOp =
| Difference
| Intersect;
let clipOp =
skiaCEnum(
"sk_clipop_t",
[
(Difference, "DIFFERENCE_SK_CLIPOP"),
(Intersect, "INTERSECT_SK_CLIPOP"),
],
);
module Canvas = {
type t;
let t: typ(structure(t)) = structure("sk_canvas_t");
let t = typedef(t, "sk_canvas_t");
};
module View3d = {
type t;
let t: typ(structure(t)) = structure("sk_3dview_t");
let t = typedef(t, "sk_3dview_t");
};
module SurfaceProps = {
type t;
let t: typ(structure(t)) = structure("sk_surfaceprops_t");
let t = typedef(t, "sk_surfaceprops_t");
};
module Surface = {
type t;
let t: typ(structure(t)) = structure("sk_surface_t");
let t = typedef(t, "sk_surface_t");
};
};
================================================
FILE: packages/reason-skia/src/wrapped/types/dune
================================================
(library
(name SkiaWrappedTypes)
(public_name reason-skia.wrapped.types)
(libraries ctypes))
================================================
FILE: packages/reason-skia/test/ColorTest.re
================================================
open Skia;
open TestFramework;
describe("Color", ({describe, test, _}) => {
describe("float", ({test, _}) => {
test("makeArgb", ({expect, _}) => {
let color = Color.Float.makeArgb(0., 0.25099, 0.501961, 1.0);
let a = Color.getA(color) |> Int32.to_int;
let r = Color.getR(color) |> Int32.to_int;
let g = Color.getG(color) |> Int32.to_int;
let b = Color.getB(color) |> Int32.to_int;
expect.int(a).toBe(0x00);
expect.int(r).toBe(0x40);
expect.int(g).toBe(0x80);
expect.int(b).toBe(0xFF);
});
test("getA/R/G/B", ({expect, _}) => {
let color = Color.Float.makeArgb(1.0, 0.501961, 0.25099, 0.);
let a = Color.Float.getA(color);
let r = Color.Float.getR(color);
let g = Color.Float.getG(color);
let b = Color.Float.getB(color);
expect.float(a).toBeCloseTo(1.0);
expect.float(r).toBeCloseTo(0.501961);
expect.float(g).toBeCloseTo(0.25099);
expect.float(b).toBeCloseTo(0.0);
});
});
test("makeArgb", ({expect, _}) => {
let color = Color.makeArgb(0x1Al, 0x1Bl, 0x1Cl, 0x1Dl);
let a = Color.getA(color) |> Int32.to_int;
let r = Color.getR(color) |> Int32.to_int;
let g = Color.getG(color) |> Int32.to_int;
let b = Color.getB(color) |> Int32.to_int;
expect.int(a).toBe(0x1A);
expect.int(r).toBe(0x1B);
expect.int(g).toBe(0x1C);
expect.int(b).toBe(0x1D);
});
});
================================================
FILE: packages/reason-skia/test/MatrixTest.re
================================================
open Skia;
open TestFramework;
describe("Matrix", ({test, _}) => {
test("setTranslate", ({expect, _}) => {
let matrix = Matrix.make();
let () = Matrix.setTranslate(matrix, 8.0, 16.0);
let vector = Vector.make(0., 0.);
Matrix.mapXy(matrix, 1., 2., vector);
expect.float(Vector.getX(vector)).toBeCloseTo(9.0);
expect.float(Vector.getY(vector)).toBeCloseTo(18.0);
});
test("setScale", ({expect, _}) => {
let matrix = Matrix.make();
let () = Matrix.setScale(matrix, 3.0, 4.0, 0., 0.);
let vector = Vector.make(0., 0.);
Matrix.mapXy(matrix, 1., 2., vector);
expect.float(Vector.getX(vector)).toBeCloseTo(3.0);
expect.float(Vector.getY(vector)).toBeCloseTo(8.0);
});
});
================================================
FILE: packages/reason-skia/test/PaintTest.re
================================================
open Skia;
open TestFramework;
describe("Paint", ({describe, _}) => {
describe("hinting", ({test, _}) => {
test("get / set isAutohinted", ({expect, _}) => {
let paint = Paint.make();
Paint.setAutohinted(paint, true);
expect.equal(paint |> Paint.isAutohinted, true);
Paint.setAutohinted(paint, false);
expect.equal(paint |> Paint.isAutohinted, false);
});
test("get / set hinting", ({expect, _}) => {
let paint = Paint.make();
Paint.setTextEncoding(paint, Utf8);
Paint.setHinting(paint, FullHinting);
expect.equal(paint |> Paint.getHinting, FullHinting);
Paint.setHinting(paint, NoHinting);
expect.equal(paint |> Paint.getHinting, NoHinting);
});
})
});
================================================
FILE: packages/reason-skia/test/RectTest.re
================================================
open Skia;
open TestFramework;
describe("Rect", ({test, _}) => {
test("makeLtrb", ({expect, _}) => {
let rect = Rect.makeLtrb(1., 2., 3., 4.);
let left = Rect.getLeft(rect);
let right = Rect.getRight(rect);
let top = Rect.getTop(rect);
let bottom = Rect.getBottom(rect);
expect.float(left).toBeCloseTo(1.);
expect.float(top).toBeCloseTo(2.);
expect.float(right).toBeCloseTo(3.);
expect.float(bottom).toBeCloseTo(4.);
});
test("setLtrb", ({expect, _}) => {
let rect = Rect.makeLtrb(1., 2., 3., 4.);
Rect.Mutable.setLtrb(~out=rect, 3.0, 9.0, 27.0, 81.0);
let left = Rect.getLeft(rect);
let right = Rect.getRight(rect);
let top = Rect.getTop(rect);
let bottom = Rect.getBottom(rect);
expect.float(left).toBeCloseTo(3.);
expect.float(top).toBeCloseTo(9.);
expect.float(right).toBeCloseTo(27.);
expect.float(bottom).toBeCloseTo(81.);
});
});
================================================
FILE: packages/reason-skia/test/TestFramework.re
================================================
include Rely.Make({
let config =
Rely.TestFrameworkConfig.initialize({
snapshotDir: "./__snapshots__",
projectDir: "",
});
});
================================================
FILE: packages/reason-skia/test/dune
================================================
(library
(name Skia_Test)
(flags
(:standard
(-w -39)))
(ocamlopt_flags -linkall -g)
(libraries rely.lib rely.internal reason-skia reason-skia.wrapped.bindings
reason-skia.wrapped))
================================================
FILE: packages/revery-text-wrap/examples/text-wrap-cli/ReveryTextWrapCli.re
================================================
open Revery_TextWrap;
/* Get the width of a character */
let width_of_token = str => {
String.length(str) |> float;
};
Timber.App.enable(Timber.Reporter.console());
Timber.App.setLevel(Timber.Level.trace);
Printexc.record_backtrace(true);
let () = {
wrap(~width_of_token, ~max_width=100.0, "0") |> List.iter(print_endline);
let wrapInput =
"Here's another example of text where wrapping might be more difficult. "
++ "This string is very, very long and consists of words of varying lengths. "
++ "By utilizing some extremely long words, we can hopefully trigger some of "
++ "the more obscure edge-cases that word-wrapping can result in, such as "
++ "placing a hyphen in the middle of a word on top of another hyphen."
++ "Let's also throw some UTF8 in here, who doesn't like a good emoji or 10?"
++ "😀 🤔 🤭 🤫 🤥 😶 😐 😑 😬"
++ "Also let's try some CJK!"
++ "日本語の場合はランダムに生成された文章以外に、 著作権が切れた小説などが利用されることもある。";
print_endline("==================");
wrap(~width_of_token, ~max_width=5.0, ~hyphenate=true, wrapInput)
|> List.iter(print_endline);
print_endline("==================");
wrap(~width_of_token, ~max_width=10.0, ~hyphenate=true, wrapInput)
|> List.iter(print_endline);
print_endline("==================");
wrap(~width_of_token, ~max_width=15.0, wrapInput)
|> List.iter(print_endline);
print_endline("==================");
wrap(~width_of_token, ~max_width=40.0, wrapInput)
|> List.iter(print_endline);
};
================================================
FILE: packages/revery-text-wrap/examples/text-wrap-cli/dune
================================================
(executables
(names ReveryTextWrapCli)
(package ReveryExamples)
(public_names ReveryTextWrapCli)
(modes native byte)
(libraries Revery.text-wrap Revery.zed))
(install
(section bin)
(package ReveryExamples)
(files ReveryTextWrapCli.bc))
================================================
FILE: packages/revery-text-wrap/src/Revery_TextWrap.re
================================================
open Tokenize;
module Log = (val Timber.Log.withNamespace("Revery.TextWrap"));
let rec list_of_queue = q =>
if (Queue.is_empty(q)) {
[];
} else {
/* We need to split into two lines because the RHS of ::
gets evaluated first, leading to recursion before the
item is removed from the queue. */
let first = Queue.take(q);
[first, ...list_of_queue(q)];
};
[@inline always]
let last_uchar_of_string = str =>
switch (str) {
| "" => Uchar.of_int(0)
| _ =>
let offset = Zed_utf8.prev(str, String.length(str));
let offset = offset == String.length(str) ? 0 : offset;
Zed_utf8.extract(str, offset);
};
let log10_of_2 = log10(2.);
[@inline always]
let uchar_num_bytes = uchar => {
let code = Uchar.to_int(uchar);
let num_bits = log10(float(code)) /. log10_of_2;
num_bits /. 8. |> int_of_float;
};
let wrap_queue =
(
~max_width,
~width_of_token,
~hyphenate=false,
~ignore_preceding_whitespace=true,
text,
) => {
/* Create a buffer for the outputted lines */
let output_lines = Queue.create();
/* Split the input text into lines and for each line: */
split_on_newline(text)
|> Queue.iter(line => {
/* Create a buffer for the wrapped portion of the line */
let buffer = Buffer.create(String.length(line));
let current_offset = ref(0);
let last_added = ref(None);
let buffer_add_uchar = uchar => {
let num_bytes = uchar_num_bytes(uchar);
Buffer.add_utf_8_uchar(buffer, uchar);
current_offset := current_offset^ + num_bytes;
last_added := Some(`Uchar(uchar));
};
let buffer_add_string = str => {
let num_bytes = String.length(str);
Buffer.add_string(buffer, str);
current_offset := current_offset^ + num_bytes;
last_added := Some(`String(str));
};
let buffer_reset = () => {
Buffer.reset(buffer);
current_offset := 0;
last_added := None;
};
let buffer_last_uchar = () =>
switch (last_added^) {
| Some(`Uchar(uchar)) => uchar
| Some(`String(str)) => last_uchar_of_string(str)
| None => Uchar.of_int(0)
};
/* Store the width of this portion */
let width = ref(0.0);
/* Tokenize the line by whitespace and for each token: */
split_tokens(line)
|> Queue.iter(token => {
/* Calculate the width of the token */
let token_width = width_of_token(token);
Log.tracef(m =>
m(
"Token: %s, Width: %f, Token_width: %f, Max_width: %f\n",
token,
width^,
token_width,
max_width,
)
);
/* If the buffer is already too long, push the buffer and reset it */
if (width^ >= max_width) {
Log.trace("Clear");
Queue.add(Buffer.contents(buffer), output_lines);
buffer_reset();
width := 0.0;
};
/* Check to see if the line starts w/ whitespace and if we should ignore it */
if (ignore_preceding_whitespace
&& is_whitespace(token)
&& width^ == 0.0) {
Log.trace(
"Decision: ignore",
/* If it's gonna be too long with the whitespace, don't add it & just prepare to wrap,
so that we don't get the whitespace at the start of the next line. */
);
} else if (ignore_preceding_whitespace
&& is_whitespace(token)
&& width^
+. token_width > max_width) {
Log.trace("Decision: append-whitespace");
width := width^ +. token_width;
/* If we can add the token without exceeding the limit, add it to the current line */
} else if (width^ +. token_width <= max_width) {
Log.tracef(m => m("Decision: append (%s)", __LOC__));
width := width^ +. token_width;
buffer_add_string(token);
/* If it would exceed the limit and the user wants hyphenation: */
} else if (hyphenate && width^ +. token_width > max_width) {
Log.trace("Decision: hyphenate");
let rec loop = (~str, ~offset, ~iteration as i) =>
if (offset != String.length(str)) {
let (uchar, nextOffset) =
Zed_utf8.extract_next(str, offset);
let char_width = width_of_token(Zed_utf8.singleton(uchar));
/* If it won't overflow this line OR if there's no way to fit it into a line without overflowing */
if (width^
+. char_width <= max_width
|| width^ == 0.0
&& char_width >= max_width) {
Log.tracef(m => m("--Decision: append (%s)", __LOC__));
/* Append it to the buffer */
buffer_add_uchar(uchar);
width := width^ +. char_width;
/* If it will overflow... */
} else {
Log.trace("--Decision: break");
/* Finalize the current line and reset the buffer (if we need to) */
if (width^ > 0.0) {
/* If we're part-way through the string already */
if (i > 0) {
Log.trace("--Clear+hyphenate");
/* We need to swap the last char of the buffer with a -, because the
hyphen will be the last character that fits into this width. */
let last_uchar = buffer_last_uchar();
/* If we've only put one character of the string before hyphenating, we
should just swap with a space, so that we don't have a lonely hyphen
on the previous line */
let hyphen =
if (i == 1) {
" ";
} else {
"-";
};
/* Flush the buffer with the hyphen and reset the buffer to just the last
character that was in the buffer (where the hyphen now is) */
Queue.add(
Zed_utf8.rchop(Buffer.contents(buffer)) ++ hyphen,
output_lines,
);
buffer_reset();
buffer_add_uchar(last_uchar);
width := width_of_token(Zed_utf8.singleton(uchar));
/* Otherwise, this is the start of the token */
} else {
Log.trace("--Clear");
/* So just flush & reset the buffer */
Queue.add(Buffer.contents(buffer), output_lines);
buffer_reset();
width := 0.0;
};
};
/* Then push the next character from this token onto the buffer */
width := width^ +. char_width;
buffer_add_uchar(uchar);
};
loop(~str, ~offset=nextOffset, ~iteration=i + 1);
};
loop(~str=token, ~offset=0, ~iteration=0);
/* If it would exceed the limit and the user doesn't want hyphenation: */
} else {
Log.trace("Decision: wrap");
/* Finalize the current line and reset the buffer (if we need to) */
if (width^ > 0.0) {
Queue.add(Buffer.contents(buffer), output_lines);
buffer_reset();
};
/* Then push the new token onto the buffer */
width := token_width;
buffer_add_string(token);
};
});
/* Finalize any remaining text in the buffer */
if (width^ > 0.0) {
Queue.add(Buffer.contents(buffer), output_lines);
};
});
output_lines;
};
let wrap =
(
~max_width,
~width_of_token,
~hyphenate=false,
~ignore_preceding_whitespace=true,
text,
) =>
list_of_queue(
wrap_queue(
~max_width,
~width_of_token,
~hyphenate,
~ignore_preceding_whitespace,
text,
),
);
================================================
FILE: packages/revery-text-wrap/src/Revery_TextWrap.rei
================================================
let wrap:
(
~max_width: float,
~width_of_token: string => float,
~hyphenate: bool=?,
~ignore_preceding_whitespace: bool=?,
string
) =>
list(string);
================================================
FILE: packages/revery-text-wrap/src/Tokenize.re
================================================
let slice = (~first, ~last, string) => {
let length = last - first;
String.sub(string, first, length);
};
/* Split a string into an AppendList of strings, chopping at every newline and
removing the newline characters. */
let split_on_newline = string => {
let lines = Queue.create();
let first = ref(0);
for (i in 0 to String.length(string) - 1) {
if (string.[i] == '\n') {
Queue.add(slice(~first=first^, ~last=i, string), lines);
first := i + 1;
};
};
/* Append the remaining characters after the last split */
if (first^ <= String.length(string) - 1) {
Queue.add(
slice(~first=first^, ~last=String.length(string), string),
lines,
);
};
lines;
};
/* Tokenize a string into an AppendList of strings, chopping on every instance of whitespace
and every hyphen. Isolate whitespace into its own token and preserve it in the final list. */
let split_tokens = string => {
let tokens = Queue.create();
let first = ref(0);
for (i in 0 to String.length(string) - 1) {
switch (string.[i]) {
| '\n'
| ' '
| '\t' =>
/* Push everything before this character (if there is anything) */
if (first^ != i) {
Queue.add(slice(~first=first^, ~last=i, string), tokens);
};
/* Then push this character */
Queue.add(slice(~first=i, ~last=i + 1, string), tokens);
/* And skip it */
first := i + 1;
| '-' =>
/* When a word is already hyphenated, split it into tokens so we don't needlessly
rehyphenate it and end up with a double hyphen */
Queue.add(slice(~first=first^, ~last=i + 1, string), tokens);
first := i + 1;
| _ => ()
};
};
/* Append the remaining characters after the last split */
if (first^ <= String.length(string) - 1) {
Queue.add(
slice(~first=first^, ~last=String.length(string), string),
tokens,
);
};
tokens;
};
/* Check if a token is blank/whitespace */
let is_whitespace = str =>
/* We only have to check the start b/c the tokenizer won't return anything after the whitespace */
String.length(str) == 0 || str.[0] === ' ' || str.[0] === '\t';
================================================
FILE: packages/revery-text-wrap/src/dune
================================================
(library
(name Revery_TextWrap)
(public_name Revery.text-wrap)
(libraries Revery.zed timber))
================================================
FILE: packages/zed/src/dune
================================================
(library
(name Revery_Zed)
(public_name Revery.zed)
(wrapped false)
(flags
(:standard -safe-string))
(libraries bytes uucp uutf result charInfo_width))
================================================
FILE: packages/zed/src/zed_utf8.ml
================================================
(*
* zed_utf8.ml
* -----------
* Copyright : (c) 2011, Jeremie Dimino
* Licence : BSD3
*
* This file is a part of Zed, an editor engine.
*)
type t = string
exception Invalid of string * string
exception Out_of_bounds
let fail str pos msg = raise (Invalid(Printf.sprintf "at position %d: %s" pos msg, str))
let byte str i = Char.code (String.unsafe_get str i)
let set_byte str i n = Bytes.unsafe_set str i (Char.unsafe_chr n)
(* +-----------------------------------------------------------------+
| Validation |
+-----------------------------------------------------------------+ *)
type check_result =
| Correct of int
| Message of string
let next_error s i =
let len = String.length s in
let rec main i ulen =
if i = len then
(i, ulen, "")
else
let ch = String.unsafe_get s i in
match ch with
| '\x00' .. '\x7f' ->
main (i + 1) (ulen + 1)
| '\xc0' .. '\xdf' ->
if i + 1 >= len then
(i, ulen, "premature end of UTF8 sequence")
else begin
let byte1 = Char.code (String.unsafe_get s (i + 1)) in
if byte1 land 0xc0 != 0x80 then
(i, ulen, "malformed UTF8 sequence")
else if ((Char.code ch land 0x1f) lsl 6) lor (byte1 land 0x3f) < 0x80 then
(i, ulen, "overlong UTF8 sequence")
else
main (i + 2) (ulen + 1)
end
| '\xe0' .. '\xef' ->
if i + 2 >= len then
(i, ulen, "premature end of UTF8 sequence")
else begin
let byte1 = Char.code (String.unsafe_get s (i + 1))
and byte2 = Char.code (String.unsafe_get s (i + 2)) in
if byte1 land 0xc0 != 0x80 then
(i, ulen, "malformed UTF8 sequence")
else if byte2 land 0xc0 != 0x80 then
(i, ulen, "malformed UTF8 sequence")
else if ((Char.code ch land 0x0f) lsl 12) lor ((byte1 land 0x3f) lsl 6) lor (byte2 land 0x3f) < 0x800 then
(i, ulen, "overlong UTF8 sequence")
else
main (i + 3) (ulen + 1)
end
| '\xf0' .. '\xf7' ->
if i + 3 >= len then
(i, ulen, "premature end of UTF8 sequence")
else begin
let byte1 = Char.code (String.unsafe_get s (i + 1))
and byte2 = Char.code (String.unsafe_get s (i + 2))
and byte3 = Char.code (String.unsafe_get s (i + 3)) in
if byte1 land 0xc0 != 0x80 then
(i, ulen, "malformed UTF8 sequence")
else if byte2 land 0xc0 != 0x80 then
(i, ulen, "malformed UTF8 sequence")
else if byte3 land 0xc0 != 0x80 then
(i, ulen, "malformed UTF8 sequence")
else if ((Char.code ch land 0x07) lsl 18) lor ((byte1 land 0x3f) lsl 12) lor ((byte2 land 0x3f) lsl 6) lor (byte3 land 0x3f) < 0x10000 then
(i, ulen, "overlong UTF8 sequence")
else
main (i + 4) (ulen + 1)
end
| _ ->
(i, ulen, "invalid start of UTF8 sequence")
in
main i 0
let check str =
let ofs, len, msg = next_error str 0 in
if ofs = String.length str then
Correct len
else
Message (Printf.sprintf "at position %d: %s" ofs msg)
let validate str =
let ofs, len, msg = next_error str 0 in
if ofs = String.length str then
len
else
fail str ofs msg
(* +-----------------------------------------------------------------+
| Unsafe UTF-8 manipulation |
+-----------------------------------------------------------------+ *)
let unsafe_next str ofs =
match String.unsafe_get str ofs with
| '\x00' .. '\x7f' ->
ofs + 1
| '\xc0' .. '\xdf' ->
if ofs + 2 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
ofs + 2
| '\xe0' .. '\xef' ->
if ofs + 3 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
ofs + 3
| '\xf0' .. '\xf7' ->
if ofs + 4 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
ofs + 4
| _ ->
fail str ofs "invalid start of UTF-8 sequence"
let unsafe_prev str ofs =
match String.unsafe_get str (ofs - 1) with
| '\x00' .. '\x7f' ->
ofs - 1
| '\x80' .. '\xbf' ->
if ofs >= 2 then
match String.unsafe_get str (ofs - 2) with
| '\xc0' .. '\xdf' ->
ofs - 2
| '\x80' .. '\xbf' ->
if ofs >= 3 then
match String.unsafe_get str (ofs - 3) with
| '\xe0' .. '\xef' ->
ofs - 3
| '\x80' .. '\xbf' ->
if ofs >= 4 then
match String.unsafe_get str (ofs - 4) with
| '\xf0' .. '\xf7' ->
ofs - 4
| _ ->
fail str (ofs - 4) "invalid start of UTF-8 sequence"
else
fail str (ofs - 3) "invalid start of UTF-8 string"
| _ ->
fail str (ofs - 3) "invalid middle of UTF-8 sequence"
else
fail str (ofs - 2) "invaild start of UTF-8 string"
| _ ->
fail str (ofs - 2) "invalid middle of UTF-8 sequence"
else
fail str (ofs - 1) "invalid start of UTF-8 string"
| _ ->
fail str (ofs - 1) "invalid end of UTF-8 sequence"
let unsafe_extract str ofs =
let ch = String.unsafe_get str ofs in
match ch with
| '\x00' .. '\x7f' ->
Uchar.of_char ch
| '\xc0' .. '\xdf' ->
if ofs + 2 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
Uchar.of_int (((Char.code ch land 0x1f) lsl 6) lor (byte str (ofs + 1) land 0x3f))
| '\xe0' .. '\xef' ->
if ofs + 3 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
Uchar.of_int (((Char.code ch land 0x0f) lsl 12) lor ((byte str (ofs + 1) land 0x3f) lsl 6) lor (byte str (ofs + 2) land 0x3f))
| '\xf0' .. '\xf7' ->
if ofs + 4 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
Uchar.of_int (((Char.code ch land 0x07) lsl 18) lor ((byte str (ofs + 1) land 0x3f) lsl 12) lor ((byte str (ofs + 2) land 0x3f) lsl 6) lor (byte str (ofs + 3) land 0x3f))
| _ ->
fail str ofs "invalid start of UTF-8 sequence"
let unsafe_extract_next str ofs =
let ch = String.unsafe_get str ofs in
match ch with
| '\x00' .. '\x7f' ->
(Uchar.of_char ch, ofs + 1)
| '\xc0' .. '\xdf' ->
if ofs + 2 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
(Uchar.of_int (((Char.code ch land 0x1f) lsl 6) lor (byte str (ofs + 1) land 0x3f)), ofs + 2)
| '\xe0' .. '\xef' ->
if ofs + 3 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
(Uchar.of_int (((Char.code ch land 0x0f) lsl 12) lor ((byte str (ofs + 1) land 0x3f) lsl 6) lor (byte str (ofs + 2) land 0x3f)), ofs + 3)
| '\xf0' .. '\xf7' ->
if ofs + 4 > String.length str then
fail str ofs "unterminated UTF-8 sequence"
else
(Uchar.of_int (((Char.code ch land 0x07) lsl 18) lor ((byte str (ofs + 1) land 0x3f) lsl 12) lor ((byte str (ofs + 2) land 0x3f) lsl 6) lor (byte str (ofs + 3) land 0x3f)), ofs + 4)
| _ ->
fail str ofs "invalid start of UTF-8 sequence"
let unsafe_extract_prev str ofs =
let ch1 = String.unsafe_get str (ofs - 1) in
match ch1 with
| '\x00' .. '\x7f' ->
(Uchar.of_char ch1, ofs - 1)
| '\x80' .. '\xbf' ->
if ofs >= 2 then
let ch2 = String.unsafe_get str (ofs - 2) in
match ch2 with
| '\xc0' .. '\xdf' ->
(Uchar.of_int (((Char.code ch2 land 0x1f) lsl 6) lor (Char.code ch1 land 0x3f)), ofs - 2)
| '\x80' .. '\xbf' ->
if ofs >= 3 then
let ch3 = String.unsafe_get str (ofs - 3) in
match ch3 with
| '\xe0' .. '\xef' ->
(Uchar.of_int (((Char.code ch3 land 0x0f) lsl 12) lor ((Char.code ch2 land 0x3f) lsl 6) lor (Char.code ch1 land 0x3f)), ofs - 3)
| '\x80' .. '\xbf' ->
if ofs >= 4 then
let ch4 = String.unsafe_get str (ofs - 4) in
match ch4 with
| '\xf0' .. '\xf7' ->
(Uchar.of_int (((Char.code ch4 land 0x07) lsl 18) lor ((Char.code ch3 land 0x3f) lsl 12) lor ((Char.code ch2 land 0x3f) lsl 6) lor (Char.code ch1 land 0x3f)), ofs - 4)
| _ ->
fail str (ofs - 4) "invalid start of UTF-8 sequence"
else
fail str (ofs - 3) "invalid start of UTF-8 string"
| _ ->
fail str (ofs - 3) "invalid middle of UTF-8 sequence"
else
fail str (ofs - 2) "invaild start of UTF-8 string"
| _ ->
fail str (ofs - 2) "invalid middle of UTF-8 sequence"
else
fail str (ofs - 1) "invalid start of UTF-8 string"
| _ ->
fail str (ofs - 1) "invalid end of UTF-8 sequence"
let rec move_l str ofs len =
if len = 0 then
ofs
else if ofs = String.length str then
raise Out_of_bounds
else
move_l str (unsafe_next str ofs) (len - 1)
let unsafe_sub str ofs len =
let res = Bytes.create len in
String.unsafe_blit str ofs res 0 len;
Bytes.unsafe_to_string res
(* +-----------------------------------------------------------------+
| Construction |
+-----------------------------------------------------------------+ *)
let singleton char =
let code = Uchar.to_int char in
Bytes.unsafe_to_string @@
if code < 0x80 then begin
let s = Bytes.create 1 in
set_byte s 0 code;
s
end else if code <= 0x800 then begin
let s = Bytes.create 2 in
set_byte s 0 ((code lsr 6) lor 0xc0);
set_byte s 1 ((code land 0x3f) lor 0x80);
s
end else if code <= 0x10000 then begin
let s = Bytes.create 3 in
set_byte s 0 ((code lsr 12) lor 0xe0);
set_byte s 1 (((code lsr 6) land 0x3f) lor 0x80);
set_byte s 2 ((code land 0x3f) lor 0x80);
s
end else if code <= 0x10ffff then begin
let s = Bytes.create 4 in
set_byte s 0 ((code lsr 18) lor 0xf0);
set_byte s 1 (((code lsr 12) land 0x3f) lor 0x80);
set_byte s 2 (((code lsr 6) land 0x3f) lor 0x80);
set_byte s 3 ((code land 0x3f) lor 0x80);
s
end else
(* Camomile allow characters with code-point greater than
0x10ffff *)
invalid_arg "Zed_utf8.singleton"
let make n code =
let str = singleton code in
let len = String.length str in
let res = Bytes.create (n * len) in
let ofs = ref 0 in
for _ = 1 to n do
String.unsafe_blit str 0 res !ofs len;
ofs := !ofs + len
done;
Bytes.unsafe_to_string res
let init n f =
let buf = Buffer.create n in
for i = 0 to n - 1 do
Buffer.add_string buf (singleton (f i))
done;
Buffer.contents buf
let rev_init n f =
let buf = Buffer.create n in
for i = n - 1 downto 0 do
Buffer.add_string buf (singleton (f i))
done;
Buffer.contents buf
(* +-----------------------------------------------------------------+
| Informations |
+-----------------------------------------------------------------+ *)
let rec length_rec str ofs len =
if ofs = String.length str then
len
else
length_rec str (unsafe_next str ofs) (len + 1)
let length str =
length_rec str 0 0
(* +-----------------------------------------------------------------+
| Comparison |
+-----------------------------------------------------------------+ *)
let rec compare_rec str1 ofs1 str2 ofs2 =
if ofs1 = String.length str1 then
if ofs2 = String.length str2 then
0
else
-1
else if ofs2 = String.length str2 then
1
else
let code1, ofs1 = unsafe_extract_next str1 ofs1
and code2, ofs2 = unsafe_extract_next str2 ofs2 in
let d = Uchar.to_int code1 - Uchar.to_int code2 in
if d <> 0 then
d
else
compare_rec str1 ofs1 str2 ofs2
let compare str1 str2 =
compare_rec str1 0 str2 0
(* +-----------------------------------------------------------------+
| Random access |
+-----------------------------------------------------------------+ *)
let get str idx =
if idx < 0 then
raise Out_of_bounds
else
unsafe_extract str (move_l str 0 idx)
(* +-----------------------------------------------------------------+
| Manipulation |
+-----------------------------------------------------------------+ *)
let sub str idx len =
if idx < 0 || len < 0 then
raise Out_of_bounds
else
let ofs1 = move_l str 0 idx in
let ofs2 = move_l str ofs1 len in
unsafe_sub str ofs1 (ofs2 - ofs1)
let break str idx =
if idx < 0 then
raise Out_of_bounds
else
let ofs = move_l str 0 idx in
(unsafe_sub str 0 ofs, unsafe_sub str ofs (String.length str - ofs))
let before str idx =
if idx < 0 then
raise Out_of_bounds
else
let ofs = move_l str 0 idx in
unsafe_sub str 0 ofs
let after str idx =
if idx < 0 then
raise Out_of_bounds
else
let ofs = move_l str 0 idx in
unsafe_sub str ofs (String.length str - ofs)
let concat3 a b c =
let lena = String.length a
and lenb = String.length b
and lenc = String.length c in
let res = Bytes.create (lena + lenb + lenc) in
String.unsafe_blit a 0 res 0 lena;
String.unsafe_blit b 0 res lena lenb;
String.unsafe_blit c 0 res (lena + lenb) lenc;
Bytes.unsafe_to_string res
let insert str idx sub =
let a, b = break str idx in
concat3 a sub b
let remove str idx len =
if idx < 0 || len < 0 then
raise Out_of_bounds
else
let ofs1 = move_l str 0 idx in
let ofs2 = move_l str ofs1 len in
unsafe_sub str 0 ofs1 ^ unsafe_sub str ofs2 (String.length str - ofs2)
let replace str idx len repl =
if idx < 0 || len < 0 then
raise Out_of_bounds
else
let ofs1 = move_l str 0 idx in
let ofs2 = move_l str ofs1 len in
concat3 (unsafe_sub str 0 ofs1) repl (unsafe_sub str ofs2 (String.length str - ofs2))
(* +-----------------------------------------------------------------+
| Exploding and imploding |
+-----------------------------------------------------------------+ *)
let rec rev_rec (res : Bytes.t) str ofs_src ofs_dst =
if ofs_src = String.length str then
Bytes.unsafe_to_string res
else begin
let ofs_src' = unsafe_next str ofs_src in
let len = ofs_src' - ofs_src in
let ofs_dst = ofs_dst - len in
String.unsafe_blit str ofs_src res ofs_dst len;
rev_rec res str ofs_src' ofs_dst
end
let rev str =
let len = String.length str in
rev_rec (Bytes.create len) str 0 len
let concat sep l =
match l with
| [] ->
""
| x :: l ->
let sep_len = String.length sep in
let len = List.fold_left (fun len str -> len + sep_len + String.length str) (String.length x) l in
let res = Bytes.create len in
String.unsafe_blit x 0 res 0 (String.length x);
ignore
(List.fold_left
(fun ofs str ->
String.unsafe_blit sep 0 res ofs sep_len;
let ofs = ofs + sep_len in
let len = String.length str in
String.unsafe_blit str 0 res ofs len;
ofs + len)
(String.length x) l);
Bytes.unsafe_to_string res
let rev_concat sep l =
match l with
| [] ->
""
| x :: l ->
let sep_len = String.length sep in
let len = List.fold_left (fun len str -> len + sep_len + String.length str) (String.length x) l in
let res = Bytes.create len in
let ofs = len - String.length x in
String.unsafe_blit x 0 res ofs (String.length x);
ignore
(List.fold_left
(fun ofs str ->
let ofs = ofs - sep_len in
String.unsafe_blit sep 0 res ofs sep_len;
let len = String.length str in
let ofs = ofs - len in
String.unsafe_blit str 0 res ofs len;
ofs)
ofs l);
Bytes.unsafe_to_string res
let rec explode_rec str ofs acc =
if ofs = 0 then
acc
else
let x, ofs = unsafe_extract_prev str ofs in
explode_rec str ofs (x :: acc)
let explode str =
explode_rec str (String.length str) []
let rec rev_explode_rec str ofs acc =
if ofs = String.length str then
acc
else
let x, ofs = unsafe_extract_next str ofs in
rev_explode_rec str ofs (x :: acc)
let rev_explode str =
rev_explode_rec str 0 []
let implode l =
let l = List.map singleton l in
let len = List.fold_left (fun len str -> len + String.length str) 0 l in
let res = Bytes.create len in
ignore
(List.fold_left
(fun ofs str ->
let len = String.length str in
String.unsafe_blit str 0 res ofs len;
ofs + len)
0 l);
Bytes.unsafe_to_string res
let rev_implode l =
let l = List.map singleton l in
let len = List.fold_left (fun len str -> len + String.length str) 0 l in
let res = Bytes.create len in
ignore
(List.fold_left
(fun ofs str ->
let len = String.length str in
let ofs = ofs - len in
String.unsafe_blit str 0 res ofs len;
ofs)
len l);
Bytes.unsafe_to_string res
(* +-----------------------------------------------------------------+
| Text transversal |
+-----------------------------------------------------------------+ *)
let rec iter_rec f str ofs =
if ofs = String.length str then
()
else begin
let chr, ofs = unsafe_extract_next str ofs in
f chr;
iter_rec f str ofs
end
let iter f str =
iter_rec f str 0
let rec rev_iter_rec f str ofs =
if ofs = 0 then
()
else begin
let chr, ofs = unsafe_extract_prev str ofs in
f chr;
rev_iter_rec f str ofs
end
let rev_iter f str =
rev_iter_rec f str (String.length str)
let rec fold_rec f str ofs acc =
if ofs = String.length str then
acc
else begin
let chr, ofs = unsafe_extract_next str ofs in
fold_rec f str ofs (f chr acc)
end
let fold f str acc =
fold_rec f str 0 acc
let rec rev_fold_rec f str ofs acc =
if ofs = 0 then
acc
else begin
let chr, ofs = unsafe_extract_prev str ofs in
rev_fold_rec f str ofs (f chr acc)
end
let rev_fold f str acc =
rev_fold_rec f str (String.length str) acc
let rec map_rec buf f str ofs =
if ofs = String.length str then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_next str ofs in
Buffer.add_string buf (singleton (f chr));
map_rec buf f str ofs
end
let map f str =
map_rec (Buffer.create (String.length str)) f str 0
let rec map_concat_rec buf f str ofs =
if ofs = String.length str then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_next str ofs in
Buffer.add_string buf (f chr);
map_concat_rec buf f str ofs
end
let map_concat f str =
map_concat_rec (Buffer.create (String.length str)) f str 0
let rec rev_map_rec buf f str ofs =
if ofs = 0 then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_prev str ofs in
Buffer.add_string buf (singleton (f chr));
rev_map_rec buf f str ofs
end
let rev_map f str =
rev_map_rec (Buffer.create (String.length str)) f str (String.length str)
let rec rev_map_concat_rec buf f str ofs =
if ofs = 0 then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_prev str ofs in
Buffer.add_string buf (f chr);
rev_map_concat_rec buf f str ofs
end
let rev_map_concat f str =
rev_map_concat_rec (Buffer.create (String.length str)) f str (String.length str)
let rec filter_rec buf f str ofs =
if ofs = String.length str then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_next str ofs in
if f chr then
Buffer.add_string buf (singleton chr);
filter_rec buf f str ofs
end
let filter f str =
filter_rec (Buffer.create (String.length str)) f str 0
let rec rev_filter_rec buf f str ofs =
if ofs = 0 then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_prev str ofs in
if f chr then
Buffer.add_string buf (singleton chr);
rev_filter_rec buf f str ofs
end
let rev_filter f str =
rev_filter_rec (Buffer.create (String.length str)) f str (String.length str)
let rec filter_map_rec buf f str ofs =
if ofs = String.length str then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_next str ofs in
(match f chr with
| Some chr ->
Buffer.add_string buf (singleton chr)
| None ->
());
filter_map_rec buf f str ofs
end
let filter_map f str =
filter_map_rec (Buffer.create (String.length str)) f str 0
let rec filter_map_concat_rec buf f str ofs =
if ofs = String.length str then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_next str ofs in
(match f chr with
| Some txt ->
Buffer.add_string buf txt
| None ->
());
filter_map_concat_rec buf f str ofs
end
let filter_map_concat f str =
filter_map_concat_rec (Buffer.create (String.length str)) f str 0
let rec rev_filter_map_rec buf f str ofs =
if ofs = 0 then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_prev str ofs in
(match f chr with
| Some chr ->
Buffer.add_string buf (singleton chr)
| None ->
());
rev_filter_map_rec buf f str ofs
end
let rev_filter_map f str =
rev_filter_map_rec (Buffer.create (String.length str)) f str (String.length str)
let rec rev_filter_map_concat_rec buf f str ofs =
if ofs = 0 then
Buffer.contents buf
else begin
let chr, ofs = unsafe_extract_prev str ofs in
(match f chr with
| Some txt ->
Buffer.add_string buf txt
| None ->
());
rev_filter_map_concat_rec buf f str ofs
end
let rev_filter_map_concat f str =
rev_filter_map_concat_rec (Buffer.create (String.length str)) f str (String.length str)
(* +-----------------------------------------------------------------+
| Scanning |
+-----------------------------------------------------------------+ *)
let rec for_all_rec f str ofs =
if ofs = String.length str then
true
else
let chr, ofs = unsafe_extract_next str ofs in
f chr && for_all_rec f str ofs
let for_all f str =
for_all_rec f str 0
let rec exists_rec f str ofs =
if ofs = String.length str then
false
else
let chr, ofs = unsafe_extract_next str ofs in
f chr || exists_rec f str ofs
let exists f str =
exists_rec f str 0
let rec count_rec f str ofs n =
if ofs = String.length str then
n
else
let chr, ofs = unsafe_extract_next str ofs in
count_rec f str ofs (if f chr then n + 1 else n)
let count f str =
count_rec f str 0 0
(* +-----------------------------------------------------------------+
| Tests |
+-----------------------------------------------------------------+ *)
let rec unsafe_sub_equal str ofs sub ofs_sub =
if ofs_sub = String.length sub then
true
else
(String.unsafe_get str ofs = String.unsafe_get sub ofs_sub)
&& unsafe_sub_equal str (ofs + 1) sub (ofs_sub + 1)
let rec contains_rec str sub ofs =
if ofs + String.length sub > String.length str then
false
else
unsafe_sub_equal str ofs sub 0 || contains_rec str sub (unsafe_next str ofs)
let contains str sub =
contains_rec str sub 0
let starts_with str prefix =
if String.length prefix > String.length str then
false
else
unsafe_sub_equal str 0 prefix 0
let ends_with str suffix =
let ofs = String.length str - String.length suffix in
if ofs < 0 then
false
else
unsafe_sub_equal str ofs suffix 0
(* +-----------------------------------------------------------------+
| Stripping |
+-----------------------------------------------------------------+ *)
let rec lfind predicate str ofs =
if ofs = String.length str then
ofs
else
let chr, ofs' = unsafe_extract_next str ofs in
if predicate chr then
lfind predicate str ofs'
else
ofs
let rec rfind predicate str ofs =
if ofs = 0 then
0
else
let chr, ofs' = unsafe_extract_prev str ofs in
if predicate chr then
rfind predicate str ofs'
else
ofs
let strip ?(predicate=Uucp.White.is_white_space) str =
let lofs = lfind predicate str 0 and rofs = rfind predicate str (String.length str) in
if lofs < rofs then
unsafe_sub str lofs (rofs - lofs)
else
""
let lstrip ?(predicate=Uucp.White.is_white_space) str =
let lofs = lfind predicate str 0 in
unsafe_sub str lofs (String.length str - lofs)
let rstrip ?(predicate=Uucp.White.is_white_space) str =
let rofs = rfind predicate str (String.length str) in
unsafe_sub str 0 rofs
let lchop = function
| "" ->
""
| str ->
let ofs = unsafe_next str 0 in
unsafe_sub str ofs (String.length str - ofs)
let rchop = function
| "" ->
""
| str ->
let ofs = unsafe_prev str (String.length str) in
unsafe_sub str 0 ofs
(* +-----------------------------------------------------------------+
| Buffers |
+-----------------------------------------------------------------+ *)
let add buf char =
let code = Uchar.to_int char in
if code < 0x80 then
Buffer.add_char buf (Char.unsafe_chr code)
else if code <= 0x800 then begin
Buffer.add_char buf (Char.unsafe_chr ((code lsr 6) lor 0xc0));
Buffer.add_char buf (Char.unsafe_chr ((code land 0x3f) lor 0x80))
end else if code <= 0x10000 then begin
Buffer.add_char buf (Char.unsafe_chr ((code lsr 12) lor 0xe0));
Buffer.add_char buf (Char.unsafe_chr (((code lsr 6) land 0x3f) lor 0x80));
Buffer.add_char buf (Char.unsafe_chr ((code land 0x3f) lor 0x80))
end else if code <= 0x10ffff then begin
Buffer.add_char buf (Char.unsafe_chr ((code lsr 18) lor 0xf0));
Buffer.add_char buf (Char.unsafe_chr (((code lsr 12) land 0x3f) lor 0x80));
Buffer.add_char buf (Char.unsafe_chr (((code lsr 6) land 0x3f) lor 0x80));
Buffer.add_char buf (Char.unsafe_chr ((code land 0x3f) lor 0x80))
end else
invalid_arg "Zed_utf8.add"
(* +-----------------------------------------------------------------+
| Offset API |
+-----------------------------------------------------------------+ *)
let extract str ofs =
if ofs < 0 || ofs >= String.length str then
raise Out_of_bounds
else
unsafe_extract str ofs
let next str ofs =
if ofs < 0 || ofs >= String.length str then
raise Out_of_bounds
else
unsafe_next str ofs
let extract_next str ofs =
if ofs < 0 || ofs >= String.length str then
raise Out_of_bounds
else
unsafe_extract_next str ofs
let prev str ofs =
if ofs <= 0 || ofs > String.length str then
raise Out_of_bounds
else
unsafe_prev str ofs
let extract_prev str ofs =
if ofs <= 0 || ofs > String.length str then
raise Out_of_bounds
else
unsafe_extract_prev str ofs
(* +-----------------------------------------------------------------+
| Escaping |
+-----------------------------------------------------------------+ *)
let escaped_char ch =
match Uchar.to_int ch with
| 7 ->
"\\a"
| 8 ->
"\\b"
| 9 ->
"\\t"
| 10 ->
"\\n"
| 11 ->
"\\v"
| 12 ->
"\\f"
| 13 ->
"\\r"
| 27 ->
"\\e"
| 92 ->
"\\\\"
| code when code >= 32 && code <= 126 ->
String.make 1 (Char.chr code)
| _ when Uucp.Alpha.is_alphabetic ch ->
singleton ch
| code when code <= 127 ->
Printf.sprintf "\\x%02x" code
| code when code <= 0xffff ->
Printf.sprintf "\\u%04x" code
| code ->
Printf.sprintf "\\U%06x" code
let add_escaped_char buf ch =
match Uchar.to_int ch with
| 7 ->
Buffer.add_string buf "\\a"
| 8 ->
Buffer.add_string buf "\\b"
| 9 ->
Buffer.add_string buf "\\t"
| 10 ->
Buffer.add_string buf "\\n"
| 11 ->
Buffer.add_string buf "\\v"
| 12 ->
Buffer.add_string buf "\\f"
| 13 ->
Buffer.add_string buf "\\r"
| 27 ->
Buffer.add_string buf "\\e"
| 92 ->
Buffer.add_string buf "\\\\"
| code when code >= 32 && code <= 126 ->
Buffer.add_char buf (Char.chr code)
| _ when Uucp.Alpha.is_alphabetic ch ->
add buf ch
| code when code <= 127 ->
Printf.bprintf buf "\\x%02x" code
| code when code <= 0xffff ->
Printf.bprintf buf "\\u%04x" code
| code ->
Printf.bprintf buf "\\U%06x" code
let escaped str =
let buf = Buffer.create (String.length str) in
iter (add_escaped_char buf) str;
Buffer.contents buf
let add_escaped buf str =
iter (add_escaped_char buf) str
let add_escaped_string buf encoding str =
let b = Buffer.create (String.length str) in
let d = Uutf.decoder ~encoding (`String str) in
let rec loop () =
match Uutf.decode d with
| `Uchar u -> ignore (Uutf.Buffer.add_utf_8 b u); loop ()
| `End -> add_escaped buf (Buffer.contents b)
| `Malformed _ ->
String.iter
(function
| '\x20' .. '\x7e' as ch ->
Buffer.add_char buf ch
| ch ->
Printf.bprintf buf "\\y%02x" (Char.code ch))
str
| `Await -> assert false
in
loop ()
let escaped_string enc str =
let buf = Buffer.create (String.length str) in
add_escaped_string buf enc str;
Buffer.contents buf
================================================
FILE: packages/zed/src/zed_utf8.mli
================================================
(*
* zed_utf8.mli
* ------------
* Copyright : (c) 2011, Jeremie Dimino
* Licence : BSD3
*
* This file is a part of Zed, an editor engine.
*)
(** UTF-8 enoded strings *)
type t = string
(** Type of UTF-8 encoded strings. *)
exception Invalid of string * string
(** [Invalid(error, text)] Exception raised when an invalid UTF-8
encoded string is encountered. [text] is the faulty text and
[error] is a description of the first error in [text]. *)
exception Out_of_bounds
(** Exception raised when trying to access a character which is
outside the bounds of a string. *)
(** {5 Validation} *)
(** Result of cheking a string for correct UTF-8. *)
type check_result =
| Correct of int
(** The string is correctly UTF-8 encoded, and the paramter is
the length of the string. *)
| Message of string
(** The string is invalid and the parameter is an error
message. *)
val check : t -> check_result
(** [check str] checks that [str] is a valid UTF-8 encoded
string. *)
val validate : t -> int
(** Same as check but raises an exception in case the argument is
not a valid text, otherwise returns the length of the string. *)
val next_error : t -> int -> int * int * string
(** [next_error str ofs] returns [(ofs', count, msg)] where [ofs']
is the offset of the start of the first invalid sequence after
[ofs] (inclusive) in [str], [count] is the number of unicode
character between [ofs] and [ofs'] (exclusive) and [msg] is an
error message. If there is no error until the end of string then
[ofs] is [String.length str] and [msg] is the empty string. *)
(** {5 Construction} *)
val singleton : Uchar.t -> t
(** [singleton ch] creates a string of length 1 containing only the
given character. *)
val make : int -> Uchar.t -> t
(** [make n ch] creates a string of length [n] filled with [ch]. *)
val init : int -> (int -> Uchar.t) -> t
(** [init n f] returns the contenation of [singleton (f 0)],
[singleton (f 1)], ..., [singleton (f (n - 1))]. *)
val rev_init : int -> (int -> Uchar.t) -> t
(** [rev_init n f] returns the contenation of [singleton (f (n -
1))], ..., [singleton (f 1)], [singleton (f 0)]. *)
(** {5 Informations} *)
val length : t -> int
(** Returns the length of the given string. *)
(** {5 Comparison} *)
val compare : t -> t -> int
(** Compares two strings (in code point order). *)
(** {5 Random access} *)
val get : t -> int -> Uchar.t
(** [get str idx] returns the character at index [idx] in
[str]. *)
(** {5 String manipulation} *)
val sub : t -> int -> int -> t
(** [sub str ofs len] Returns the sub-string of [str] starting at
[ofs] and of length [len]. *)
val break : t -> int -> t * t
(** [break str pos] returns the sub-strings before and after [pos]
in [str]. It is more efficient than creating two sub-strings
with {!sub}. *)
val before : t -> int -> t
(** [before str pos] returns the sub-string before [pos] in [str] *)
val after : t -> int -> t
(** [after str pos] returns the sub-string after [pos] in [str] *)
val insert : t -> int -> t -> t
(** [insert str pos sub] inserts [sub] in [str] at position
[pos]. *)
val remove : t -> int -> int -> t
(** [remove str pos len] removes the [len] characters at position
[pos] in [str] *)
val replace : t -> int -> int -> t -> t
(** [replace str pos len repl] replaces the [len] characters at
position [pos] in [str] by [repl]. *)
(** {5 Tranformation} *)
val rev : t -> t
(** [rev str] reverses all characters of [str]. *)
val concat : t -> t list -> t
(** [concat sep l] returns the concatenation of all strings of [l]
separated by [sep]. *)
val rev_concat : t -> t list -> t
(** [concat sep l] returns the concatenation of all strings of [l]
in reverse order separated by [sep]. *)
val explode : t -> Uchar.t list
(** [explode str] returns the list of all characters of [str]. *)
val rev_explode : t -> Uchar.t list
(** [rev_explode str] returns the list of all characters of [str] in
reverse order. *)
val implode : Uchar.t list -> t
(** [implode l] returns the concatenation of all characters of [l]. *)
val rev_implode : Uchar.t list -> t
(** [rev_implode l] is the same as [implode (List.rev l)] but more
efficient. *)
(** {5 Text traversals} *)
val iter : (Uchar.t -> unit) -> t -> unit
(** [iter f str] applies [f] an all characters of [str] starting
from the left. *)
val rev_iter : (Uchar.t -> unit) -> t -> unit
(** [rev_iter f str] applies [f] an all characters of [str] starting
from the right. *)
val fold : (Uchar.t -> 'a -> 'a) -> t -> 'a -> 'a
(** [fold f str acc] applies [f] on all characters of [str]
starting from the left, accumulating a value. *)
val rev_fold : (Uchar.t -> 'a -> 'a) -> t -> 'a -> 'a
(** [rev_fold f str acc] applies [f] on all characters of [str]
starting from the right, accumulating a value. *)
val map : (Uchar.t -> Uchar.t) -> t -> t
(** [map f str] maps all characters of [str] with [f]. *)
val rev_map : (Uchar.t -> Uchar.t) -> t -> t
(** [rev_map f str] maps all characters of [str] with [f] in reverse
order. *)
val map_concat : (Uchar.t -> t) -> t -> t
(** [map f str] maps all characters of [str] with [f] and
concatenate the result. *)
val rev_map_concat : (Uchar.t -> t) -> t -> t
(** [rev_map f str] maps all characters of [str] with [f] in reverse
order and concatenate the result. *)
val filter : (Uchar.t -> bool) -> t -> t
(** [filter f str] filters characters of [str] with [f]. *)
val rev_filter : (Uchar.t -> bool) -> t -> t
(** [rev_filter f str] filters characters of [str] with [f] in
reverse order. *)
val filter_map : (Uchar.t -> Uchar.t option) -> t -> t
(** [filter_map f str] filters and maps characters of [str] with
[f]. *)
val rev_filter_map : (Uchar.t -> Uchar.t option) -> t -> t
(** [rev_filter_map f str] filters and maps characters of [str] with
[f] in reverse order. *)
val filter_map_concat : (Uchar.t -> t option) -> t -> t
(** [filter_map f str] filters and maps characters of [str] with [f]
and concatenate the result. *)
val rev_filter_map_concat : (Uchar.t -> t option) -> t -> t
(** [rev_filter_map f str] filters and maps characters of [str] with
[f] in reverse order and concatenate the result. *)
(** {5 Scanning} *)
val for_all : (Uchar.t -> bool) -> t -> bool
(** [for_all f text] returns whether all characters of [text] verify
the predicate [f]. *)
val exists : (Uchar.t -> bool) -> t -> bool
(** [exists f text] returns whether at least one character of [text]
verify [f]. *)
val count : (Uchar.t -> bool) -> t -> int
(** [count f text] returhs the number of characters of [text]
verifying [f]. *)
(** {5 Tests} *)
val contains : t -> t -> bool
(** [contains text sub] returns whether [sub] appears in [text] *)
val starts_with : t -> t -> bool
(** [starts_with text prefix] returns [true] iff [s] starts with
[prefix]. *)
val ends_with : t -> t -> bool
(** [ends_with text suffix] returns [true] iff [s] ends with
[suffix]. *)
(** {5 Stripping} *)
val strip : ?predicate : (Uchar.t -> bool) -> t -> t
(** [strip ?predicate text] returns [text] without its firsts and
lasts characters that match [predicate]. [predicate] default to
testing whether the given character has the [`White_Space]
unicode property. For example:
{[
strip "\n foo\n " = "foo"
]}
*)
val lstrip : ?predicate : (Uchar.t -> bool) -> t -> t
(** [lstrip ?predicate text] is the same as {!strip} but it only
removes characters at the left of [text]. *)
val rstrip : ?predicate : (Uchar.t -> bool) -> t -> t
(** [lstrip ?predicate text] is the same as {!strip} but it only
removes characters at the right of [text]. *)
val lchop : t -> t
(** [lchop t] returns [t] without is first character. Returns [""]
if [t = ""] *)
val rchop : t -> t
(** [rchop t] returns [t] without is last character. Returns [""] if
[t = ""]. *)
(** {5 Buffers} *)
val add : Buffer.t -> Uchar.t -> unit
(** [add buf ch] is the same as [Buffer.add_string buf (singleton
ch)] but is more efficient. *)
(** {5 Escaping} *)
val escaped_char : Uchar.t -> t
(** [escaped_char ch] returns a string containg [ch] or an escaped
version of [ch] if:
- [ch] is a control character (code < 32)
- [ch] is the character with code 127
- [ch] is a non-ascii, non-alphabetic character
It uses the syntax [\xXX], [\uXXXX], [\UXXXXXX] or a specific
escape sequence [\n, \r, ...]. *)
val add_escaped_char : Buffer.t -> Uchar.t -> unit
(** [add_escaped_char buf ch] is the same as [Buffer.add_string buf
(escaped_char ch)] but a bit more efficient. *)
val escaped : t -> t
(** [escaped text] escape all characters of [text] as with
[escape_char]. *)
val add_escaped : Buffer.t -> t -> unit
(** [add_escaped_char buf text] is the same as [Buffer.add_string
buf (escaped text)] but a bit more efficient. *)
val escaped_string : Uutf.decoder_encoding -> string -> t
(** [escaped_string enc str] escape the string [str] which is
encoded with encoding [enc]. If decoding [str] with [enc] fails,
it escape all non-printable bytes of [str] with the syntax
[\yAB]. *)
val add_escaped_string : Buffer.t -> Uutf.decoder_encoding -> string -> unit
(** [add_escaped_char buf enc text] is the same as
[Buffer.add_string buf (escaped_string enc text)] but a bit more
efficient. *)
(** {5 Safe offset API} *)
val next : t -> int -> int
(** [next str ofs] returns the offset of the next character in
[str]. *)
val prev : t -> int -> int
(** [prev str ofs] returns the offset of the previous character in
[str]. *)
val extract : t -> int -> Uchar.t
(** [extract str ofs] returns the code-point at offset [ofs] in
[str]. *)
val extract_next : t -> int -> Uchar.t * int
(** [extract_next str ofs] returns the code-point at offset [ofs] in
[str] and the offset of the next character. *)
val extract_prev : t -> int -> Uchar.t * int
(** [extract_prev str ofs] returns the code-point at the previous
offset in [str] and this offset. *)
(** {5 Unsafe offset API} *)
(** These functions does not check that the given offset is inside the
bounds of the given string. *)
val unsafe_next : t -> int -> int
(** [unsafe_next str ofs] returns the offset of the next character
in [str]. *)
val unsafe_prev : t -> int -> int
(** [unsafe_prev str ofs] returns the offset of the previous
character in [str]. *)
val unsafe_extract : t -> int -> Uchar.t
(** [unsafe_extract str ofs] returns the code-point at offset [ofs]
in [str]. *)
val unsafe_extract_next : t -> int -> Uchar.t * int
(** [unsafe_extract_next str ofs] returns the code-point at offset
[ofs] in [str] and the offset the next character. *)
val unsafe_extract_prev : t -> int -> Uchar.t * int
(** [unsafe_extract_prev str ofs] returns the code-point at the
previous offset in [str] and this offset. *)
================================================
FILE: reason-harfbuzz.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outrunlabs.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: reason-sdl2.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outlook.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: reason-skia.opam
================================================
opam-version: "1.2"
version: "dev"
maintainer: "bryphe@outlook.com"
author: ["Bryan Phelps"]
build: [
]
================================================
FILE: scripts/docker/archlinux/Dockerfile
================================================
FROM archlinux:latest
RUN pacman -Syu --noconfirm
RUN pacman -S --noconfirm base base-devel git npm perl
#perl for shasum binary
# Revery special deps
RUN pacman -S --noconfirm libpng libxcursor libxi libxinerama libxrandr harfbuzz glu gtk3 fontconfig nasm python2 clang
ENV PATH=$PATH:/usr/bin/core_perl
# workaround for esy-skia that need `python` binary
RUN cp /usr/bin/python2 /usr/bin/python
# workaround hack while ocaml-secondary-compiler@4.08.1 work well with gcc 10
RUN mv /usr/bin/gcc /usr/bin/gcc-orig
RUN echo -en '#!/usr/bin/sh\n/usr/bin/gcc-orig -fcommon "$@"' > /usr/bin/gcc
RUN chmod u+x /usr/bin/gcc
RUN npm install --verbose --global --unsafe-perm=true esy@0.6.6
================================================
FILE: scripts/docker/centos/Dockerfile
================================================
FROM centos:7
RUN yum -y update
RUN yum -y install centos-release-scl
RUN yum-config-manager --enable rhel-server-rhscl-7-rpms
RUN yum -y install llvm-toolset-7.0
RUN scl enable llvm-toolset-7.0 'clang -v'
RUN yum -y install gcc-c++ make sudo
RUN curl -sL https://rpm.nodesource.com/setup_10.x | sudo -E bash -
RUN yum -y install nodejs npm coreutils grep tar sed gawk diffutils autoconf unzip
RUN yum -y install file fuse fuse-devel wget bzip2-devel libXt-devel libSM-devel libICE-devel ncurses-devel libacl-devel libxrandr-devel libXinerama-devel libXcursor-devel libXi-devel mesa-libGL-devel mesa-libGLU-devel gtk3-devel perl-Digest-SHA bzip2 m4 patch which cmake3 git nasm
RUN rpm -i https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/c/colm-0.13.0.4-2.el7.x86_64.rpm
RUN rpm -i https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/r/ragel-7.0.0.9-2.el7.x86_64.rpm
RUN yum -y install /usr/lib64/libasan.so.0.0.0
RUN node -v
RUN npm -v
RUN npm install --global --unsafe-perm=true esy@0.6.6
RUN yum -y install nasm
RUN yum -y install https://repo.ius.io/ius-release-el7.rpm
RUN yum -y remove git
RUN yum -y install epel-release
RUN yum -y install git222
RUN node -v
RUN npm -v
RUN git --version
================================================
FILE: scripts/docker-build.sh
================================================
source /opt/rh/llvm-toolset-7.0/enable
clang -v
# Workaround for: https://github.com/esy/esy/issues/1227
# Concurrent fetch seems to cause hang on Docker in Azure Pipelines..
export ESY__BUILD_CONCURRENCY=1
export ESY__FETCH_CONCURRENCY=1
esy install
esy build
================================================
FILE: scripts/make-binary-file.js
================================================
const fs = require("fs");
const testData = new Uint8Array(5);
testData[0] = 255;
testData[1] = 0;
testData[2] = 1;
testData[3] = 2;
testData[4] = 128;
fs.writeFileSync("binary.dat", new Buffer(testData));
================================================
FILE: scripts/release.sh
================================================
# Set path
RELEASE_PATH="$(pwd)/_release"
mkdir -p $RELEASE_PATH
cp $cur__bin/* $RELEASE_PATH
================================================
FILE: src/Core/App.re
================================================
%import
"../Native/config.h";
module AppLog = (val Log.withNamespace("Revery.App"));
module SdlLog = (val Log.withNamespace("Revery.SDL2"));
module Log = AppLog;
type delegatedFunc = unit => unit;
type unsubscribe = unit => unit;
type quitResponse =
| AllowQuit
| PreventQuit;
let mergeQuitResponse = (a: quitResponse, b: quitResponse) => {
switch (a, b) {
| (AllowQuit, AllowQuit) => AllowQuit
| _ => PreventQuit
};
};
type t = {
mutable idleCount: int,
mutable isFirstRender: bool,
mutable isQuitting: bool,
windows: Hashtbl.t(int, Window.t),
onIdle: Event.t(unit),
onBeforeQuit: Event.Fanout.t(int, quitResponse),
onFileOpen: Event.t(string),
mutable canIdle: unit => bool,
};
let framesToIdle = 10;
let getWindows = (app: t) => {
Hashtbl.to_seq_values(app.windows) |> List.of_seq;
};
let getWindowById = (app: t, id: int) => {
Hashtbl.find_opt(app.windows, id);
};
let _tryToClose = (app: t, window: Window.t) => {
let uniqueId = Window.getUniqueId(window);
if (Window.canQuit(window)) {
Log.debugf(m => m("canQuit is true for window %i", uniqueId));
Hashtbl.remove(app.windows, uniqueId);
} else {
Log.debugf(m => m("canQuit is false for window %i", uniqueId));
};
};
let _tryToCloseAll = (app: t) => {
let windows = Hashtbl.to_seq_values(app.windows);
Seq.iter(w => _tryToClose(app, w), windows);
};
let quit = (~askNicely=false, ~code=0, app: t) => {
if (askNicely) {
_tryToCloseAll(app);
};
if (Hashtbl.length(app.windows) == 0 || !askNicely) {
Revery_Native.uninitApp();
// Verify [quit] wasn't called recursively from a beforeQuit handler
if (!app.isQuitting) {
Log.info("onBeforeQuit");
app.isQuitting = true;
let quitResponse =
Event.Fanout.dispatch(
app.onBeforeQuit,
mergeQuitResponse,
AllowQuit,
code,
);
app.isQuitting = false;
switch (quitResponse) {
| AllowQuit =>
Log.info("Quitting");
exit(code);
| PreventQuit => Log.info("Quit prevented by event handler")
};
};
};
};
let isIdle = (app: t) => app.idleCount >= framesToIdle;
let _mainThreadMutex = Mutex.create();
/* A list of pending functions the main thread will need to run */
let _mainThreadPendingFunctions: ref(list(delegatedFunc)) = ref([]);
let _anyPendingWork: ref(bool) = ref(false);
let runOnMainThread = f => {
Mutex.lock(_mainThreadMutex);
_mainThreadPendingFunctions := [f, ..._mainThreadPendingFunctions^];
_anyPendingWork := true;
Mutex.unlock(_mainThreadMutex);
// If we're 'idle' - in a [waitTimeout], dispatch an event to wake up the main thread
Sdl2.Event.push();
};
let _anyPendingMainThreadJobs = () => {
_anyPendingWork^;
};
let setCanIdle = (f, app: t) => {
app.canIdle = f;
};
let onBeforeQuit = app => Event.Fanout.subscribe(app.onBeforeQuit);
let onIdle = app => Event.subscribe(app.onIdle);
let onFileOpen = app => Event.subscribe(app.onFileOpen);
/* Execute any pending main thread jobs */
let _doPendingMainThreadJobs = () => {
let jobs = {
Mutex.lock(_mainThreadMutex);
let ret = _mainThreadPendingFunctions^;
_anyPendingWork := false;
_mainThreadPendingFunctions := [];
Mutex.unlock(_mainThreadMutex);
ret;
};
jobs |> List.rev |> List.iter(f => f());
};
let flushPendingCallbacks = () => _doPendingMainThreadJobs();
let createWindow =
(~createOptions=WindowCreateOptions.default, app: t, windowName) => {
let w = Window.create(windowName, createOptions);
/* Window.render(w) */
let uniqueId = Window.getUniqueId(w);
Hashtbl.add(app.windows, uniqueId, w);
w;
};
let _anyWindowsDirty = (app: t) =>
List.fold_left(
(prev, w) => prev || Window.isDirty(w),
false,
getWindows(app),
);
let initConsole = () =>
if (Sys.win32) {
// First, try attaching to an existing console.
let attachResult = Sdl2.Platform.win32AttachConsole();
// If that wasn't available - try to allocate a new one.
let _code =
if (attachResult == 0) {
Sdl2.Platform.win32AllocConsole();
} else {
attachResult;
};
();
};
let handleKeymapChanged = () => {
AppLog.info("Keymap changed");
if (Sys.win32) {
// Workaround for https://github.com/onivim/oni2/issues/1657
// When changing the keyboard layout, on Windows 10, the
// modifier keys like the Windows/GUI key 'stick'...
// ...this resets the state of the modifiers.
Sdl2.Keymod.setState(
Sdl2.Keymod.none,
);
};
};
%ifdef
USE_GTK;
let runGtkIteration = () =>
if (Revery_Native.Gtk.eventsPending()) {
AppLog.debug("Running Gtk iteration");
let _quit: bool = Revery_Native.Gtk.mainIteration();
();
};
[%%else];
let runGtkIteration = () => ();
[%%endif];
let start = init => {
let appInstance: t = {
windows: Hashtbl.create(1),
idleCount: 0,
isFirstRender: true,
isQuitting: false,
onBeforeQuit: Event.create(),
onIdle: Event.create(),
onFileOpen: Event.create(),
canIdle: () => true,
};
Sdl2.Log.setOutputFunction((_category, priority, message) =>
switch (priority) {
| Verbose
| Debug => SdlLog.trace(message)
| Info => SdlLog.info(message)
| Warn => SdlLog.warn(message)
| Error
| Critical => SdlLog.error(message)
}
);
let _ = Sdl2.init();
let _dispose = init(appInstance);
let _ = Sdl2.ScreenSaver.enable();
let _handleEvent = evt => {
let handleEvent = windowID => {
let window = getWindowById(appInstance, windowID);
switch (window) {
| Some(win) => Window.handleEvent(evt, win)
| None =>
Log.errorf(m =>
m(
"Unable to find window with ID: %i - event: %s",
windowID,
Sdl2.Event.show(evt),
)
)
};
};
switch (evt) {
| Sdl2.Event.MouseButtonUp({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.MouseButtonDown({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.MouseMotion({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.MouseWheel({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.KeyDown({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.KeyUp({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.TextInput({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.TextEditing({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.KeymapChanged => handleKeymapChanged()
| Sdl2.Event.WindowResized({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowSizeChanged({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowExposed({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowFocusGained({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowFocusLost({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowMaximized({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowFullscreen({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowMinimized({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowRestored({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowMoved({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.WindowEnter({windowID}) => handleEvent(windowID)
| Sdl2.Event.WindowLeave({windowID}) => handleEvent(windowID)
| Sdl2.Event.WindowClosed({windowID, _}) =>
Log.debugf(m => m("Got WindowClosed event for %i", windowID));
handleEvent(windowID);
switch (getWindowById(appInstance, windowID)) {
| None => ()
| Some(win) => _tryToClose(appInstance, win)
};
| Sdl2.Event.DropBegin({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.DropFile({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.DropComplete({windowID, _}) => handleEvent(windowID)
| Sdl2.Event.Quit =>
// Sometimes, on Mac, we could get a 'quit' without a
// corresponding WindowClosed event - this can happen
// if Command+Q is pressed. In that case, we'll try
// closing all the windows - and if they all close,
// we'll exit the app.
quit(~askNicely=true, ~code=0, appInstance)
| _ => ()
};
};
let _flushEvents = () => {
let processingEvents = ref(true);
while (processingEvents^) {
let evt = Sdl2.Event.poll();
switch (evt) {
| None => processingEvents := false
| Some(v) => _handleEvent(v)
};
};
};
let dispatchFileOpen = Event.dispatch(appInstance.onFileOpen);
Callback.register("revery_dispatchFileOpen", dispatchFileOpen);
Revery_Native.initApp();
AppLog.infof(m => m("Operating in locale : %s", Environment.userLocale));
let appLoop = () => {
_flushEvents();
runGtkIteration();
Tick.Default.pump();
if (appInstance.isFirstRender
|| _anyWindowsDirty(appInstance)
|| _anyPendingMainThreadJobs()
|| !appInstance.canIdle()) {
if (appInstance.idleCount > 0) {
Log.debug("Upshifting into active state.");
};
Performance.bench("_doPendingMainThreadJobs", () =>
_doPendingMainThreadJobs()
);
Performance.bench("renderWindows", () => {
List.iter(w => Window.render(w), getWindows(appInstance))
});
appInstance.idleCount = 0;
appInstance.isFirstRender = false;
} else {
appInstance.idleCount = appInstance.idleCount + 1;
if (appInstance.idleCount === framesToIdle) {
Log.debug("Downshifting into idle state...");
let _: unit = Event.dispatch(appInstance.onIdle, ());
();
};
let evt = Sdl2.Event.waitTimeout(250);
switch (evt) {
| None => ()
| Some(evt) => _handleEvent(evt)
};
};
Environment.yield();
false;
};
Sdl2.renderLoop(appLoop);
};
let start = init => Sdl2.main(() => start(init));
================================================
FILE: src/Core/App.rei
================================================
/** [t] is the type representing the running app instance
There is only ever a single app instance, but an app instance
may have multiple windows.
*/
type t;
/** [getWindows(app)] returns the list of all open [Window.t] instances */
let getWindows: t => list(Window.t);
/**
[quit(~askNicely, ~code, c)] causes the App to quit with exit code [c]
[askNicely] specifies whether quit should be forced (default [false]). If [true],
the canQuit handlers will be run for each window. If [false], the canQuit handlers
will be ignored.
[code] specifies the exit code. Defaults to [0].
*/
let quit: (~askNicely: bool=?, ~code: int=?, t) => unit;
/** [isIdle(app)] returns true if the app is idling, false othwrise */
let isIdle: t => bool;
type delegatedFunc = unit => unit;
/** [runOnMainThread(f)] schedules the function [f] to run during the next
render frame on the main thread.
If the application is idle, this will cause it to become active, and trigger a re-render.
*/
let runOnMainThread: delegatedFunc => unit;
/** [flushPendingCallbacks(f)] will explicitly run all the callbacks
queued up via [runOnMainThread].
In general, this should not need to be called by user code,
as it happens as part of the application lifecycle.
However, it may be necessary to call this for tests that queue
up callbacks via [runOnMainThread].
*/
let flushPendingCallbacks: unit => unit;
/** [setCanIdle(f, app)] registers a callback [f]. Each frame, [f] will be called
to check if the application can idle. If [f()] returns [false], the app will not
idle and will render. This is useful if you have animations active, but in general,
you want to allow the app to idle to minimize CPU and battery usage.
*/
let setCanIdle: (unit => bool, t) => unit;
type unsubscribe = unit => unit;
type quitResponse =
| AllowQuit
| PreventQuit;
/** [onBeforeQuit(app, f) registers a callback [f] that is called prior to quitting] */
let onBeforeQuit: (t, int => quitResponse) => unsubscribe;
/** [onIdle(app, f) registers a callback [f] that is called when the application is idle.
This allows you to defer work when the app is not under load - (for example,
this is may be a good time to garbage collect). This will be called
when multiple frames have passed without requiring a render.
*/
let onIdle: (t, unit => unit) => unsubscribe;
/** [onFileOpen(app, f) registers a callback [f] that is called when the host OS is trying to open a file]
Note that as of now, this function only is called on macOS, as other OS's have other
mechanisms for opening files (usually just through CLI args)
*/
let onFileOpen: (t, string => unit) => unsubscribe;
/** [createWindow ~createOptions, app, name] creates a new window */
let createWindow:
(~createOptions: WindowCreateOptions.t=?, t, string) => Window.t;
/** [start] is the entry point for a Revery application. This initiates
the Revery application lifecycle, and an app instance ([t]) is passed
to an initialization function.
*/
let start: (t => unit) => unit;
/** [initConsole] (Windows-only) attaches or allocates a console,
to show logging output. No-op on other platforms.
*/
let initConsole: unit => unit;
================================================
FILE: src/Core/Color.re
================================================
type t = Skia.Color.t;
let rgba = (r, g, b, a) => Skia.Color.Float.makeArgb(a, r, g, b);
let rgb = (r, g, b) => Skia.Color.Float.makeArgb(1.0, r, g, b);
let rgba_int = (r, g, b, a) =>
Skia.Color.makeArgb(
a |> Int32.of_int,
r |> Int32.of_int,
g |> Int32.of_int,
b |> Int32.of_int,
);
let rgb_int = (r, g, b) =>
Skia.Color.makeArgb(
255l,
r |> Int32.of_int,
g |> Int32.of_int,
b |> Int32.of_int,
);
let getAlpha = Skia.Color.Float.getA;
// Matches:
// #FFF
// #FFFA
// #FFF00
//let singleHex = Str.regexp("#\\([a-f\\|A-F\\|0-9]\\)\\([a-f\\|A-F\\|0-9]\\)\\([a-f\\|A-F\\|0-9]\\)\\([a-f\\|A-F\\|0-9]\\)");
let singleHex =
Re.Perl.re(
"#([a-f|A-F|0-9])([a-f|A-F|0-9])([a-f|A-F|0-9])([a-f|A-F|0-9]?[a-f|A-F|0-9]?)",
)
|> Re.Perl.compile;
// Matches:
// #FFFFFF
// #FFFFFF0
// #FFFFFF00
let doubleHex =
Re.Perl.re(
"#([a-f|A-F|0-9][a-f|A-F|0-9])([a-f|A-F|0-9][a-f|A-F|0-9])([a-f|A-F|0-9][a-f|A-F|0-9])([a-f|A-F|0-9]?[a-f|A-F|0-9]?)",
)
|> Re.Perl.compile;
exception ColorHexParseException(string);
let parseColor = c => {
let len = String.length(c);
let result =
switch (len) {
// Zero-length case only happens in the alpha channel, if no alpha has been specified
| 0 => Some(255)
| 1 =>
switch (int_of_string_opt("0x" ++ c)) {
| Some(v) => Some(v * 16 + v)
| None => None
}
| 2 => int_of_string_opt("0x" ++ c)
| _ => None
};
switch (result) {
| None =>
raise(ColorHexParseException("Unable to parse color component: " ++ c))
| Some(v) => float_of_int(v) /. 255.
};
};
let hex = str =>
// First, try and parse with the 'double hex' option
switch (Re.exec_opt(doubleHex, str)) {
| Some(matches) =>
let r = Re.Group.get(matches, 1) |> parseColor;
let g = Re.Group.get(matches, 2) |> parseColor;
let b = Re.Group.get(matches, 3) |> parseColor;
let a = Re.Group.get(matches, 4) |> parseColor;
rgba(r, g, b, a);
| None =>
// Now, try and parse with the 'single hex' option
switch (Re.exec_opt(singleHex, str)) {
| Some(matches) =>
let r = Re.Group.get(matches, 1) |> parseColor;
let g = Re.Group.get(matches, 2) |> parseColor;
let b = Re.Group.get(matches, 3) |> parseColor;
let a = Re.Group.get(matches, 4) |> parseColor;
rgba(r, g, b, a);
| None => raise(ColorHexParseException("Unable to parse color: " ++ str))
}
};
let multiplyAlpha = (opacity: float, color: t) => {
let a = Skia.Color.Float.getA(color);
let r = Skia.Color.Float.getR(color);
let g = Skia.Color.Float.getG(color);
let b = Skia.Color.Float.getB(color);
Skia.Color.Float.makeArgb(a *. opacity, r, g, b);
};
let mix = (~start, ~stop, ~amount) => {
let startA = Skia.Color.Float.getA(start);
let startR = Skia.Color.Float.getR(start);
let startG = Skia.Color.Float.getG(start);
let startB = Skia.Color.Float.getB(start);
let stopA = Skia.Color.Float.getA(stop);
let stopR = Skia.Color.Float.getR(stop);
let stopG = Skia.Color.Float.getG(stop);
let stopB = Skia.Color.Float.getB(stop);
let r = (stopR -. startR) *. amount +. startR;
let g = (stopG -. startG) *. amount +. startG;
let b = (stopB -. startB) *. amount +. startB;
let a = (stopA -. startA) *. amount +. startA;
Skia.Color.Float.makeArgb(a, r, g, b);
};
let opposite = color => {
let a = Skia.Color.Float.getA(color);
let r = Skia.Color.Float.getR(color);
let g = Skia.Color.Float.getG(color);
let b = Skia.Color.Float.getB(color);
Skia.Color.Float.makeArgb(a, 1. -. r, 1. -. g, 1. -. b);
};
let toRgba = (color: t) => {
let a = Skia.Color.Float.getA(color);
let r = Skia.Color.Float.getR(color);
let g = Skia.Color.Float.getG(color);
let b = Skia.Color.Float.getB(color);
(r, g, b, a);
};
let equals = (a: t, b: t) => {
a == b;
};
let toString = (color: t) => {
let a = Skia.Color.Float.getA(color);
let r = Skia.Color.Float.getR(color);
let g = Skia.Color.Float.getG(color);
let b = Skia.Color.Float.getB(color);
Printf.sprintf("(r: %f g: %f b: %f a: %f)", r, g, b, a);
};
let toSkia = (color: t) => {
color;
};
================================================
FILE: src/Core/Color.rei
================================================
exception ColorHexParseException(string);
type t;
let rgba: (float, float, float, float) => t;
let rgb: (float, float, float) => t;
let rgba_int: (int, int, int, int) => t;
let rgb_int: (int, int, int) => t;
let hex: string => t;
let multiplyAlpha: (float, t) => t;
let mix: (~start: t, ~stop: t, ~amount: float) => t;
let opposite: t => t;
let toRgba: t => (float, float, float, float);
let getAlpha: t => float;
let equals: (t, t) => bool;
let toString: t => string;
let toSkia: t => Skia.Color.t;
================================================
FILE: src/Core/Colors.re
================================================
open Color;
let transparentWhite = rgba(1.0, 1.0, 1.0, 0.0);
let transparentBlack = rgba(0., 0., 0., 0.);
let aliceBlue = rgb(0.94, 0.97, 1.0);
let antiqueWhite = rgb(0.98, 0.92, 0.84);
let aqua = rgb(0.0, 1.0, 1.0);
let aquamarine = rgb(0.5, 1.0, 0.83);
let azure = rgb(0.94, 1.0, 1.0);
let beige = rgb(0.96, 0.96, 0.86);
let bisque = rgb(1.0, 0.89, 0.77);
let black = rgb(0.0, 0.0, 0.0);
let blanchedAlmond = rgb(1.0, 0.92, 0.8);
let blue = rgb(0.0, 0.0, 1.0);
let blueViolet = rgb(0.54, 0.17, 0.89);
let brown = rgb(0.65, 0.16, 0.16);
let burlyWood = rgb(0.87, 0.72, 0.53);
let cadetBlue = rgb(0.37, 0.62, 0.63);
let chartreuse = rgb(0.5, 1.0, 0.0);
let chocolate = rgb(0.82, 0.41, 0.12);
let coral = rgb(1.0, 0.5, 0.31);
let cornflowerBlue = rgb(0.39, 0.58, 0.93);
let cornsilk = rgb(1.0, 0.97, 0.86);
let crimson = rgb(0.86, 0.08, 0.24);
let cyan = rgb(0.0, 1.0, 1.0);
let darkBlue = rgb(0.0, 0.0, 0.55);
let darkCyan = rgb(0.0, 0.55, 0.55);
let darkGoldenRod = rgb(0.72, 0.53, 0.04);
let darkGray = rgb(0.66, 0.66, 0.66);
let darkGrey = rgb(0.66, 0.66, 0.66);
let darkGreen = rgb(0.0, 0.39, 0.0);
let darkKhaki = rgb(0.74, 0.72, 0.42);
let darkMagenta = rgb(0.55, 0.0, 0.55);
let darkOliveGreen = rgb(0.33, 0.42, 0.18);
let darkOrange = rgb(1.0, 0.55, 0.0);
let darkOrchid = rgb(0.6, 0.2, 0.8);
let darkRed = rgb(0.55, 0.0, 0.0);
let darkSalmon = rgb(0.91, 0.59, 0.48);
let darkSeaGreen = rgb(0.56, 0.74, 0.56);
let darkSlateBlue = rgb(0.28, 0.24, 0.55);
let darkSlateGray = rgb(0.18, 0.31, 0.31);
let darkSlateGrey = rgb(0.18, 0.31, 0.31);
let darkTurquoise = rgb(0.0, 0.81, 0.82);
let darkViolet = rgb(0.58, 0.0, 0.83);
let deepPink = rgb(1.0, 0.08, 0.58);
let deepSkyBlue = rgb(0.0, 0.75, 1.0);
let dimGray = rgb(0.41, 0.41, 0.41);
let dimGrey = rgb(0.41, 0.41, 0.41);
let dodgerBlue = rgb(0.12, 0.56, 1.0);
let fireBrick = rgb(0.7, 0.13, 0.13);
let floralWhite = rgb(1.0, 0.98, 0.94);
let forestGreen = rgb(0.13, 0.55, 0.13);
let fuchsia = rgb(1.0, 0.0, 1.0);
let gainsboro = rgb(0.86, 0.86, 0.86);
let ghostWhite = rgb(0.97, 0.97, 1.0);
let gold = rgb(1.0, 0.84, 0.0);
let goldenRod = rgb(0.85, 0.65, 0.13);
let gray = rgb(0.5, 0.5, 0.5);
let grey = rgb(0.5, 0.5, 0.5);
let green = rgb(0.0, 0.5, 0.0);
let greenYellow = rgb(0.68, 1.0, 0.18);
let honeyDew = rgb(0.94, 1.0, 0.94);
let hotPink = rgb(1.0, 0.41, 0.71);
let indianRed = rgb(0.8, 0.36, 0.36);
let indigo = rgb(0.29, 0.0, 0.51);
let ivory = rgb(1.0, 1.0, 0.94);
let khaki = rgb(0.94, 0.9, 0.55);
let lavender = rgb(0.9, 0.9, 0.98);
let lavenderBlush = rgb(1.0, 0.94, 0.96);
let lawnGreen = rgb(0.49, 0.99, 0.0);
let lemonChiffon = rgb(1.0, 0.98, 0.8);
let lightBlue = rgb(0.68, 0.85, 0.9);
let lightCoral = rgb(0.94, 0.5, 0.5);
let lightCyan = rgb(0.88, 1.0, 1.0);
let lightGoldenRodYellow = rgb(0.98, 0.98, 0.82);
let lightGray = rgb(0.83, 0.83, 0.83);
let lightGrey = rgb(0.83, 0.83, 0.83);
let lightGreen = rgb(0.56, 0.93, 0.56);
let lightPink = rgb(1.0, 0.71, 0.76);
let lightSalmon = rgb(1.0, 0.63, 0.48);
let lightSeaGreen = rgb(0.13, 0.7, 0.67);
let lightSkyBlue = rgb(0.53, 0.81, 0.98);
let lightSlateGray = rgb(0.47, 0.53, 0.6);
let lightSlateGrey = rgb(0.47, 0.53, 0.6);
let lightSteelBlue = rgb(0.69, 0.77, 0.87);
let lightYellow = rgb(1.0, 1.0, 0.88);
let lime = rgb(0.0, 1.0, 0.0);
let limeGreen = rgb(0.2, 0.8, 0.2);
let linen = rgb(0.98, 0.94, 0.9);
let magenta = rgb(1.0, 0.0, 1.0);
let maroon = rgb(0.5, 0.0, 0.0);
let mediumAquaMarine = rgb(0.4, 0.8, 0.67);
let mediumBlue = rgb(0.0, 0.0, 0.8);
let mediumOrchid = rgb(0.73, 0.33, 0.83);
let mediumPurple = rgb(0.58, 0.44, 0.86);
let mediumSeaGreen = rgb(0.24, 0.7, 0.44);
let mediumSlateBlue = rgb(0.48, 0.41, 0.93);
let mediumSpringGreen = rgb(0.0, 0.98, 0.6);
let mediumTurquoise = rgb(0.28, 0.82, 0.8);
let mediumVioletRed = rgb(0.78, 0.08, 0.52);
let midnightBlue = rgb(0.1, 0.1, 0.44);
let mintCream = rgb(0.96, 1.0, 0.98);
let mistyRose = rgb(1.0, 0.89, 0.88);
let moccasin = rgb(1.0, 0.89, 0.71);
let navajoWhite = rgb(1.0, 0.87, 0.68);
let navy = rgb(0.0, 0.0, 0.5);
let oldLace = rgb(0.99, 0.96, 0.9);
let olive = rgb(0.5, 0.5, 0.0);
let oliveDrab = rgb(0.42, 0.56, 0.14);
let orange = rgb(1.0, 0.65, 0.0);
let orangeRed = rgb(1.0, 0.27, 0.0);
let orchid = rgb(0.85, 0.44, 0.84);
let paleGoldenRod = rgb(0.93, 0.91, 0.67);
let paleGreen = rgb(0.6, 0.98, 0.6);
let paleTurquoise = rgb(0.69, 0.93, 0.93);
let paleVioletRed = rgb(0.86, 0.44, 0.58);
let papayaWhip = rgb(1.0, 0.94, 0.84);
let peachPuff = rgb(1.0, 0.85, 0.73);
let peru = rgb(0.8, 0.52, 0.25);
let pink = rgb(1.0, 0.75, 0.8);
let plum = rgb(0.87, 0.63, 0.87);
let powderBlue = rgb(0.69, 0.88, 0.9);
let purple = rgb(0.5, 0.0, 0.5);
let rebeccaPurple = rgb(0.4, 0.2, 0.6);
let red = rgb(1.0, 0.0, 0.0);
let rosyBrown = rgb(0.74, 0.56, 0.56);
let royalBlue = rgb(0.25, 0.41, 0.88);
let saddleBrown = rgb(0.55, 0.27, 0.07);
let salmon = rgb(0.98, 0.5, 0.45);
let sandyBrown = rgb(0.96, 0.64, 0.38);
let seaGreen = rgb(0.18, 0.55, 0.34);
let seaShell = rgb(1.0, 0.96, 0.93);
let sienna = rgb(0.63, 0.32, 0.18);
let silver = rgb(0.75, 0.75, 0.75);
let skyBlue = rgb(0.53, 0.81, 0.92);
let slateBlue = rgb(0.42, 0.35, 0.8);
let slateGray = rgb(0.44, 0.5, 0.56);
let slateGrey = rgb(0.44, 0.5, 0.56);
let snow = rgb(1.0, 0.98, 0.98);
let springGreen = rgb(0.0, 1.0, 0.5);
let steelBlue = rgb(0.27, 0.51, 0.71);
let tan = rgb(0.82, 0.71, 0.55);
let teal = rgb(0.0, 0.5, 0.5);
let thistle = rgb(0.85, 0.75, 0.85);
let tomato = rgb(1.0, 0.39, 0.28);
let turquoise = rgb(0.25, 0.88, 0.82);
let violet = rgb(0.93, 0.51, 0.93);
let wheat = rgb(0.96, 0.87, 0.7);
let white = rgb(1.0, 1.0, 1.0);
let whiteSmoke = rgb(0.96, 0.96, 0.96);
let yellow = rgb(1.0, 1.0, 0.0);
let yellowGreen = rgb(0.6, 0.8, 0.2);
let lookup = Hashtbl.create(150);
Hashtbl.add(lookup, "aliceblue", aliceBlue);
Hashtbl.add(lookup, "antiquewhite", antiqueWhite);
Hashtbl.add(lookup, "aqua", aqua);
Hashtbl.add(lookup, "aquamarine", aquamarine);
Hashtbl.add(lookup, "azure", azure);
Hashtbl.add(lookup, "beige", beige);
Hashtbl.add(lookup, "bisque", bisque);
Hashtbl.add(lookup, "black", black);
Hashtbl.add(lookup, "blanchedalmond", blanchedAlmond);
Hashtbl.add(lookup, "blue", blue);
Hashtbl.add(lookup, "blueviolet", blueViolet);
Hashtbl.add(lookup, "brown", brown);
Hashtbl.add(lookup, "burlywood", burlyWood);
Hashtbl.add(lookup, "cadetblue", cadetBlue);
Hashtbl.add(lookup, "chartreuse", chartreuse);
Hashtbl.add(lookup, "chocolate", chocolate);
Hashtbl.add(lookup, "coral", coral);
Hashtbl.add(lookup, "cornflowerblue", cornflowerBlue);
Hashtbl.add(lookup, "cornsilk", cornsilk);
Hashtbl.add(lookup, "crimson", crimson);
Hashtbl.add(lookup, "cyan", cyan);
Hashtbl.add(lookup, "darkblue", darkBlue);
Hashtbl.add(lookup, "darkcyan", darkCyan);
Hashtbl.add(lookup, "darkgoldenrod", darkGoldenRod);
Hashtbl.add(lookup, "darkgray", darkGray);
Hashtbl.add(lookup, "darkgrey", darkGrey);
Hashtbl.add(lookup, "darkgreen", darkGreen);
Hashtbl.add(lookup, "darkkhaki", darkKhaki);
Hashtbl.add(lookup, "darkmagenta", darkMagenta);
Hashtbl.add(lookup, "darkolivegreen", darkOliveGreen);
Hashtbl.add(lookup, "darkorange", darkOrange);
Hashtbl.add(lookup, "darkorchid", darkOrchid);
Hashtbl.add(lookup, "darkred", darkRed);
Hashtbl.add(lookup, "darksalmon", darkSalmon);
Hashtbl.add(lookup, "darkseagreen", darkSeaGreen);
Hashtbl.add(lookup, "darkslateblue", darkSlateBlue);
Hashtbl.add(lookup, "darkslategray", darkSlateGray);
Hashtbl.add(lookup, "darkslategrey", darkSlateGrey);
Hashtbl.add(lookup, "darkturquoise", darkTurquoise);
Hashtbl.add(lookup, "darkviolet", darkViolet);
Hashtbl.add(lookup, "deeppink", deepPink);
Hashtbl.add(lookup, "deepskyblue", deepSkyBlue);
Hashtbl.add(lookup, "dimgray", dimGray);
Hashtbl.add(lookup, "dimgrey", dimGrey);
Hashtbl.add(lookup, "dodgerblue", dodgerBlue);
Hashtbl.add(lookup, "firebrick", fireBrick);
Hashtbl.add(lookup, "floralwhite", floralWhite);
Hashtbl.add(lookup, "forestgreen", forestGreen);
Hashtbl.add(lookup, "fuchsia", fuchsia);
Hashtbl.add(lookup, "gainsboro", gainsboro);
Hashtbl.add(lookup, "ghostwhite", ghostWhite);
Hashtbl.add(lookup, "gold", gold);
Hashtbl.add(lookup, "goldenrod", goldenRod);
Hashtbl.add(lookup, "gray", gray);
Hashtbl.add(lookup, "grey", grey);
Hashtbl.add(lookup, "green", green);
Hashtbl.add(lookup, "greenyellow", greenYellow);
Hashtbl.add(lookup, "honeydew", honeyDew);
Hashtbl.add(lookup, "hotpink", hotPink);
Hashtbl.add(lookup, "indianred", indianRed);
Hashtbl.add(lookup, "indigo", indigo);
Hashtbl.add(lookup, "ivory", ivory);
Hashtbl.add(lookup, "khaki", khaki);
Hashtbl.add(lookup, "lavender", lavender);
Hashtbl.add(lookup, "lavenderblush", lavenderBlush);
Hashtbl.add(lookup, "lawngreen", lawnGreen);
Hashtbl.add(lookup, "lemonchiffon", lemonChiffon);
Hashtbl.add(lookup, "lightblue", lightBlue);
Hashtbl.add(lookup, "lightcoral", lightCoral);
Hashtbl.add(lookup, "lightcyan", lightCyan);
Hashtbl.add(lookup, "lightgoldenrodyellow", lightGoldenRodYellow);
Hashtbl.add(lookup, "lightgray", lightGray);
Hashtbl.add(lookup, "lightgrey", lightGrey);
Hashtbl.add(lookup, "lightgreen", lightGreen);
Hashtbl.add(lookup, "lightpink", lightPink);
Hashtbl.add(lookup, "lightsalmon", lightSalmon);
Hashtbl.add(lookup, "lightseagreen", lightSeaGreen);
Hashtbl.add(lookup, "lightskyblue", lightSkyBlue);
Hashtbl.add(lookup, "lightslategray", lightSlateGray);
Hashtbl.add(lookup, "lightslategrey", lightSlateGrey);
Hashtbl.add(lookup, "lightsteelblue", lightSteelBlue);
Hashtbl.add(lookup, "lightyellow", lightYellow);
Hashtbl.add(lookup, "lime", lime);
Hashtbl.add(lookup, "limegreen", limeGreen);
Hashtbl.add(lookup, "linen", linen);
Hashtbl.add(lookup, "magenta", magenta);
Hashtbl.add(lookup, "maroon", maroon);
Hashtbl.add(lookup, "mediumaquamarine", mediumAquaMarine);
Hashtbl.add(lookup, "mediumblue", mediumBlue);
Hashtbl.add(lookup, "mediumorchid", mediumOrchid);
Hashtbl.add(lookup, "mediumpurple", mediumPurple);
Hashtbl.add(lookup, "mediumseagreen", mediumSeaGreen);
Hashtbl.add(lookup, "mediumslateblue", mediumSlateBlue);
Hashtbl.add(lookup, "mediumspringgreen", mediumSpringGreen);
Hashtbl.add(lookup, "mediumturquoise", mediumTurquoise);
Hashtbl.add(lookup, "mediumvioletred", mediumVioletRed);
Hashtbl.add(lookup, "midnightblue", midnightBlue);
Hashtbl.add(lookup, "mintcream", mintCream);
Hashtbl.add(lookup, "mistyrose", mistyRose);
Hashtbl.add(lookup, "moccasin", moccasin);
Hashtbl.add(lookup, "navajowhite", navajoWhite);
Hashtbl.add(lookup, "navy", navy);
Hashtbl.add(lookup, "oldlace", oldLace);
Hashtbl.add(lookup, "olive", olive);
Hashtbl.add(lookup, "olivedrab", oliveDrab);
Hashtbl.add(lookup, "orange", orange);
Hashtbl.add(lookup, "orangered", orangeRed);
Hashtbl.add(lookup, "orchid", orchid);
Hashtbl.add(lookup, "palegoldenrod", paleGoldenRod);
Hashtbl.add(lookup, "palegreen", paleGreen);
Hashtbl.add(lookup, "paleturquoise", paleTurquoise);
Hashtbl.add(lookup, "palevioletred", paleVioletRed);
Hashtbl.add(lookup, "papayawhip", papayaWhip);
Hashtbl.add(lookup, "peachpuff", peachPuff);
Hashtbl.add(lookup, "peru", peru);
Hashtbl.add(lookup, "pink", pink);
Hashtbl.add(lookup, "plum", plum);
Hashtbl.add(lookup, "powderblue", powderBlue);
Hashtbl.add(lookup, "purple", purple);
Hashtbl.add(lookup, "rebeccapurple", rebeccaPurple);
Hashtbl.add(lookup, "red", red);
Hashtbl.add(lookup, "rosybrown", rosyBrown);
Hashtbl.add(lookup, "royalblue", royalBlue);
Hashtbl.add(lookup, "saddlebrown", saddleBrown);
Hashtbl.add(lookup, "salmon", salmon);
Hashtbl.add(lookup, "sandybrown", sandyBrown);
Hashtbl.add(lookup, "seagreen", seaGreen);
Hashtbl.add(lookup, "seashell", seaShell);
Hashtbl.add(lookup, "sienna", sienna);
Hashtbl.add(lookup, "silver", silver);
Hashtbl.add(lookup, "skyblue", skyBlue);
Hashtbl.add(lookup, "slateblue", slateBlue);
Hashtbl.add(lookup, "slategray", slateGray);
Hashtbl.add(lookup, "slategrey", slateGrey);
Hashtbl.add(lookup, "snow", snow);
Hashtbl.add(lookup, "springgreen", springGreen);
Hashtbl.add(lookup, "steelblue", steelBlue);
Hashtbl.add(lookup, "tan", tan);
Hashtbl.add(lookup, "teal", teal);
Hashtbl.add(lookup, "thistle", thistle);
Hashtbl.add(lookup, "tomato", tomato);
Hashtbl.add(lookup, "turquoise", turquoise);
Hashtbl.add(lookup, "violet", violet);
Hashtbl.add(lookup, "wheat", wheat);
Hashtbl.add(lookup, "white", white);
Hashtbl.add(lookup, "whitesmoke", whiteSmoke);
Hashtbl.add(lookup, "yellow", yellow);
Hashtbl.add(lookup, "yellowgreen", yellowGreen);
let fromString = name =>
Hashtbl.find_opt(lookup, String.lowercase_ascii(name));
let fromStringExn = name =>
try(Hashtbl.find(lookup, String.lowercase_ascii(name))) {
| Not_found => failwith("Unknown color: " ++ name)
};
================================================
FILE: src/Core/Environment.re
================================================
let isNative =
switch (Sys.backend_type) {
| Native => true
| Bytecode => true
| _ => false
};
let webGL = !isNative;
let sleep = (t: Time.t) =>
/* No-op in JS */
if (isNative) {
Unix.sleepf(Time.toFloatSeconds(t));
};
external yield: unit => unit = "caml_thread_yield";
type os =
Revery_Native.Environment.os =
| Unknown
| Android
| IOS
| Browser
| Mac({
major: int,
minor: int,
bugfix: int,
})
| Linux({
kernel: int,
major: int,
minor: int,
patch: int,
})
| Windows({
major: int,
minor: int,
build: int,
});
let os = {
webGL ? Browser : Revery_Native.Environment.getOS();
};
let osString =
switch (os) {
| Mac({major, minor, bugfix}) =>
Printf.sprintf("macOS %d.%d.%d", major, minor, bugfix)
| Linux({kernel, major, minor, patch}) =>
Printf.sprintf("Linux %d.%d.%d-%d", kernel, major, minor, patch)
| Windows({major, minor, build}) =>
Printf.sprintf("Windows %d.%d Build %d", major, minor, build)
| Android => "Android"
| Browser => "Browser"
| IOS => "iOS"
| Unknown => "Unknown"
};
let isMac =
switch (os) {
| Mac(_) => true
| _ => false
};
let isLinux =
switch (os) {
| Linux(_) => true
| _ => false
};
let isWindows =
switch (os) {
| Windows(_) => true
| _ => false
};
let isIOS = {
os == IOS;
};
let isAndroid = {
os == Android;
};
let isBrowser = {
os == Browser;
};
module Internal = {
let addTrailingSlash = dir => {
let len = String.length(dir);
if (len == 0) {
dir;
} else {
switch (dir.[len - 1]) {
| '/' => dir
| '\\' => dir
| _ => dir ++ Filename.dir_sep
};
};
};
};
let getExecutingDirectory = () =>
if (!isNative) {
"";
} else {
let dir =
switch (os) {
/* The default strategy of preferring Sys.executable_name seems to only work
* reliably on Mac. On Linux, it will return the symlink source instead of
* the symlink destination - this causes problems when trying to load assets
* relative to the binary location when symlinked.
*/
| Mac(_) =>
switch (
String.rindex_opt(Sys.executable_name, '/'),
String.rindex_opt(Sys.executable_name, '\\'),
) {
| (Some(v1), Some(v2)) =>
String.sub(Sys.executable_name, 0, max(v1, v2))
| (None, Some(v)) => String.sub(Sys.executable_name, 0, v)
| (Some(v), None) => String.sub(Sys.executable_name, 0, v)
| _ => Sys.executable_name
}
| _ =>
let candidatePath = Filename.dirname(Sys.argv[0]) ++ Filename.dir_sep;
if (Filename.is_relative(candidatePath)) {
Internal.addTrailingSlash(Sys.getcwd()) ++ candidatePath;
} else {
candidatePath;
};
};
/* Check if there is a trailing slash. If not, we need to add one. */
Internal.addTrailingSlash(dir);
};
let executingDirectory = getExecutingDirectory();
let getAssetPath = p => {
isNative
? {
Rench.Path.isAbsolute(p) ? p : executingDirectory ++ p;
}
: p;
};
let getWorkingDirectory = () => Sys.getcwd();
let getTempDirectory = () => Filename.get_temp_dir_name();
let getUserLocale = () => Revery_Native.Locale.getUser();
let userLocale = getUserLocale();
================================================
FILE: src/Core/Environment.rei
================================================
/**
[Environment] provides information about the run-time environment
*/
/** [true] if native / bytecode build, [false] if JSOO build */
let isNative: bool;
/** [true] if JSOO build, [false] otherwise */
let webGL: bool;
/**
[sleep(t)] sleeps the running thread for the specified time period.
{b Example usage}: [sleep(Seconds(1.0))]
{i No-op in JSOO builds.}
*/
let sleep: Time.t => unit;
let yield: unit => unit;
/**
[@deprecated]
*/
let getExecutingDirectory: unit => string;
/**
[executingDirectory] gets the directory where the running executable lives.
*/
let executingDirectory: string;
let getWorkingDirectory: unit => string;
/**
[getAssetPath] resolves a path to an absolute path. If the path is not already
absolute, it is assumed to be relative to the current binary.
*/
let getAssetPath: string => string;
/**
[getTempDirectory]
Unix:
returns the value of the TMPDIR environment variable or "/tmp" if not set.
Windows:
returns the value of the TMPDIR environment variable or "." if not set.
*/
let getTempDirectory: unit => string;
type os =
| Unknown
| Android
| IOS
| Browser
| Mac({
major: int,
minor: int,
bugfix: int,
})
| Linux({
kernel: int,
major: int,
minor: int,
patch: int,
})
| Windows({
major: int,
minor: int,
build: int,
});
let os: os;
let osString: string;
let isMac: bool;
let isIOS: bool;
let isWindows: bool;
let isAndroid: bool;
let isLinux: bool;
let isBrowser: bool;
/**
[getUserLocale] returns the current user locale. Note that on some platforms
(including macOS) the locale can change during runtime
*/
let getUserLocale: unit => string;
let userLocale: string;
================================================
FILE: src/Core/Event.re
================================================
/* Event.re */
module Fanout = {
/* An event where subscribers can respond with a value */
type cb('a, 'b) = 'a => 'b;
type t('a, 'b) = ref(list(cb('a, 'b)));
let dispatch = (evt: t('a, 'b), merge: ('b, 'b) => 'b, zero: 'b, v: 'a) => {
List.fold_left((acc, listener) => merge(acc, listener(v)), zero, evt^);
};
let subscribe = (evt: t('a, 'b), f: cb('a, 'b)) => {
evt := List.append(evt^, [f]);
let unsubscribe = () => {
evt := List.filter(f => f !== f, evt^);
};
unsubscribe;
};
};
/* an event is just a fanout with a `unit` return type, but we duplicate the definition for simpler type errors */
type cb('a) = 'a => unit;
type t('a) = ref(list(cb('a)));
let create = () => ref([]);
let subscribe = (evt: t('a), f: cb('a)) => {
Fanout.subscribe(evt, f);
};
let dispatch = (evt: t('a), v: 'a) => List.iter(c => c(v), evt^);
================================================
FILE: src/Core/Events.re
================================================
type keyPressEvent = {
codepoint: int,
character: string,
};
type textInputEvent = {text: string};
type textEditEvent = {
text: string,
start: int,
length: int,
};
type internalKeyboardEvent =
| InternalKeyUpEvent(Key.KeyEvent.t)
| InternalKeyDownEvent(Key.KeyEvent.t)
| InternalTextEditEvent(textEditEvent)
| InternalTextInputEvent(textInputEvent);
type mouseMoveEvent = {
mouseX: float,
mouseY: float,
keymod: Key.Keymod.t,
};
type mouseWheelEvent = {
deltaX: float,
deltaY: float,
mouseX: float,
mouseY: float,
keymod: Key.Keymod.t,
};
type mouseButtonEvent = {
button: MouseButton.t,
keymod: Key.Keymod.t,
};
type internalMouseEvents =
| InternalMouseDown(mouseButtonEvent)
| InternalMouseMove(mouseMoveEvent)
| InternalMouseUp(mouseButtonEvent)
| InternalMouseWheel(mouseWheelEvent)
| InternalMouseEnter(mouseMoveEvent)
| InternalMouseLeave(mouseMoveEvent)
| InternalMouseOver(mouseMoveEvent)
| InternalMouseOut(mouseMoveEvent);
type fileDropEvent = {
mouseX: float,
mouseY: float,
paths: list(string),
keymod: Key.Keymod.t,
};
type internalFileDropEvents =
| InternalFileDropped(fileDropEvent);
================================================
FILE: src/Core/GarbageCollector.re
================================================
/*
* GarbageCollector.re
*
* Thin wrapper over the 'Gc' module to support some
* basic primitives for tuning performance in the
* Revery app model.
*/
let full = () =>
if (Environment.isNative) {
Gc.full_major();
};
let counters = () =>
if (Environment.isNative) {
Gc.counters();
} else {
(0., 0., 0.);
};
================================================
FILE: src/Core/Key.re
================================================
module Scancode = {
type t = Sdl2.Scancode.t;
let getName = Sdl2.Scancode.getName;
let toString = getName;
};
module Keycode = {
include Sdl2.Keycode;
let toString = getName;
};
module Keymod = {
include Sdl2.Keymod;
};
module KeyEvent = {
type t = {
keycode: Keycode.t,
scancode: Scancode.t,
keymod: Keymod.t,
repeat: bool,
};
};
================================================
FILE: src/Core/Key.rei
================================================
module Scancode: {
type t = int;
let getName: t => string;
let toString: t => string;
};
module Keycode: {
type t = int;
let getName: t => string;
let ofScancode: Scancode.t => t;
let toString: t => string;
let toScancode: t => Scancode.t;
// Incrementally add these as needed from:
// https://wiki.libsdl.org/SDLKeycodeLookup
let unknown: int;
let backspace: int;
let return: int;
let escape: int;
let space: int;
let left_paren: int;
let right_paren: int;
let asterisk: int;
let plus: int;
let minus: int;
let period: int;
let slash: int;
let caret: int;
let equals: int;
let digit0: int;
let digit1: int;
let digit2: int;
let digit3: int;
let digit4: int;
let digit5: int;
let digit6: int;
let digit7: int;
let digit8: int;
let digit9: int;
let pad_multiply: int;
let pad_plus: int;
let pad_minus: int;
let pad_period: int;
let pad_divide: int;
let pad_equals: int;
let p_digit0: int;
let p_digit1: int;
let p_digit2: int;
let p_digit3: int;
let p_digit4: int;
let p_digit5: int;
let p_digit6: int;
let p_digit7: int;
let p_digit8: int;
let p_digit9: int;
let a: int;
let b: int;
let c: int;
let d: int;
let e: int;
let f: int;
let g: int;
let h: int;
let i: int;
let j: int;
let k: int;
let l: int;
let m: int;
let n: int;
let o: int;
let p: int;
let q: int;
let r: int;
let s: int;
let t: int;
let u: int;
let v: int;
let w: int;
let x: int;
let y: int;
let z: int;
let delete: int;
let right: int;
let left: int;
};
module Keymod: {
type t = int;
let isLeftShiftDown: t => bool;
let isRightShiftDown: t => bool;
let isShiftDown: t => bool;
let isLeftControlDown: t => bool;
let isRightControlDown: t => bool;
let isControlDown: t => bool;
let isLeftAltDown: t => bool;
let isRightAltDown: t => bool;
let isAltDown: t => bool;
let isLeftGuiDown: t => bool;
let isRightGuiDown: t => bool;
let isGuiDown: t => bool;
let isNumLockDown: t => bool;
let isCapsLockDown: t => bool;
let isAltGrKeyDown: t => bool;
};
module KeyEvent: {
type t = {
keycode: Keycode.t,
scancode: Scancode.t,
keymod: Keymod.t,
repeat: bool,
};
};
================================================
FILE: src/Core/Log.re
================================================
include Timber.Log;
module type Logger = Timber.Logger;
================================================
FILE: src/Core/Log.rei
================================================
module type Logger = {
let errorf: Timber.msgf(_, unit) => unit;
let error: string => unit;
let warnf: Timber.msgf(_, unit) => unit;
let warn: string => unit;
let infof: Timber.msgf(_, unit) => unit;
let info: string => unit;
let debugf: Timber.msgf(_, unit) => unit;
let debug: string => unit;
let tracef: Timber.msgf(_, unit) => unit;
let trace: string => unit;
let fn: (string, 'a => 'b, ~pp: 'b => string=?, 'a) => 'b;
};
let withNamespace: string => (module Logger);
================================================
FILE: src/Core/MouseButton.re
================================================
[@deriving show({with_path: false})]
type t =
| BUTTON_LEFT
| BUTTON_MIDDLE
| BUTTON_RIGHT
| BUTTON_UNKNOWN;
let convert = sdlMouseButton =>
switch (sdlMouseButton) {
| Sdl2.MouseButton.Left => BUTTON_LEFT
| Sdl2.MouseButton.Middle => BUTTON_MIDDLE
| Sdl2.MouseButton.Right => BUTTON_RIGHT
| _ => BUTTON_UNKNOWN
};
================================================
FILE: src/Core/MouseButton.rei
================================================
[@deriving show]
type t =
| BUTTON_LEFT
| BUTTON_MIDDLE
| BUTTON_RIGHT
| BUTTON_UNKNOWN;
let convert: Sdl2.MouseButton.t => t;
================================================
FILE: src/Core/MouseCursors.re
================================================
open Sdl2;
/* This module must use lazy values for the cursors, because GLFW will return
[GLFW_NOT_INITIALIZED] (which renders as an arrow) if this gets called before
[glfwInit]. Since these are global variables, they WILL be initialized first.
We could also return a new cursor every time, but this would cause a memory
leak. */
type t = [ | `Arrow | `Text | `Pointer | `Crosshair | `HResize | `VResize];
let arrow_lazy = lazy(Cursor.createSystem(Cursor.Arrow));
let text_lazy = lazy(Cursor.createSystem(Cursor.IBeam));
let pointer_lazy = lazy(Cursor.createSystem(Cursor.Hand));
let crosshair_lazy = lazy(Cursor.createSystem(Cursor.Crosshair));
let horizontalResize_lazy = lazy(Cursor.createSystem(Cursor.SizeWE));
let verticalResize_lazy = lazy(Cursor.createSystem(Cursor.SizeNS));
let _toSdlCursor = cursorType => {
(
switch (cursorType) {
| `Arrow => arrow_lazy
| `Text => text_lazy
| `Pointer => pointer_lazy
| `Crosshair => crosshair_lazy
| `HResize => horizontalResize_lazy
| `VResize => verticalResize_lazy
}
)
|> Lazy.force;
};
let setCursor = v => {
_toSdlCursor(v) |> Cursor.set;
};
let arrow = `Arrow;
let text = `Text;
let pointer = `Pointer;
let crosshair = `Crosshair;
let horizontalResize = `HResize;
let verticalResize = `VResize;
let toString = (v: t) =>
switch (v) {
| `Arrow => "arrow"
| `Text => "text"
| `Pointer => "pointer"
| `Crosshair => "crosshair"
| `HResize => "hresize"
| `VResize => "vresize"
};
================================================
FILE: src/Core/MouseCursors.rei
================================================
type t;
let setCursor: t => unit;
let arrow: t;
let text: t;
let pointer: t;
let crosshair: t;
let horizontalResize: t;
let verticalResize: t;
let toString: t => string;
================================================
FILE: src/Core/Performance.re
================================================
type performanceFunction('a) = unit => 'a;
let nestingLevel = ref(0);
module Log = (val Log.withNamespace("Revery.Core.Performance"));
module MemoryAllocations = {
type t = {
minorWords: int,
promotedWords: int,
majorWords: int,
};
let toString = ({minorWords, promotedWords, majorWords}: t) =>
Printf.sprintf(
"| minor: %n | major: %n | promoted: %n |",
minorWords,
majorWords,
promotedWords,
);
};
let getMemoryAllocations = (startCounters, endCounters) => {
let (startMinor, startPromoted, startMajor) = startCounters;
let (endMinor, endPromoted, endMajor) = endCounters;
let ret: MemoryAllocations.t = {
minorWords: int_of_float(endMinor -. startMinor),
promotedWords: int_of_float(endPromoted -. startPromoted),
majorWords: int_of_float(endMajor -. startMajor),
};
ret;
};
let isBenchmarking =
switch (Sys.getenv_opt("REVERY_PERF")) {
| Some(_) => true
| None => false
};
let bench: (string, performanceFunction('a)) => 'a =
(name, f) =>
if (isBenchmarking) {
nestingLevel := nestingLevel^ + 1;
let startTime = Unix.gettimeofday();
let startCounters = GarbageCollector.counters();
Log.tracef(m =>
m("%s[BEGIN: %s]", String.make(nestingLevel^, '-'), name)
);
let ret = f();
let endTime = Unix.gettimeofday();
let endCounters = GarbageCollector.counters();
let allocations = getMemoryAllocations(startCounters, endCounters);
Log.tracef(m =>
m(
"%s[END: %s] Time: %fms Memory: %s",
String.make(nestingLevel^, '-'),
name,
(endTime -. startTime) *. 1000.,
MemoryAllocations.toString(allocations),
)
);
nestingLevel := nestingLevel^ - 1;
ret;
} else {
f();
};
================================================
FILE: src/Core/Revery_Core.re
================================================
module Color = Color;
module Colors = Colors;
module Key = Key;
module MouseButton = MouseButton;
module MouseCursors = MouseCursors;
module Window = Window;
module App = App;
module Time = Time;
module Environment = Environment;
module Event = Event;
module Events = Events;
module Log = Log;
module Performance = Performance;
module UniqueId = UniqueId;
module TextWrapping = TextWrapping;
module TextOverflow = TextOverflow;
module Vsync = Vsync;
module WindowCreateOptions = WindowCreateOptions;
module WindowStyles = WindowStyles;
/*
* Internally exposed modules, just for testing.
*/
module Internal = {
module Tick = Tick;
};
module Tick = Tick.Default;
module Zed_utf8 = Zed_utf8;
================================================
FILE: src/Core/TextOverflow.re
================================================
type t =
| Overflow
| Clip
| Ellipsis
| UserDefined(string);
let removeLineBreaks = text => {
let re = Str.regexp("\n");
Str.global_replace(re, "", text);
};
let rec handleOverflow = (~maxWidth, ~text, ~measure, ~character="…", ()) => {
let clippedText = Zed_utf8.rchop(text);
let width = measure(clippedText ++ character);
width >= maxWidth && String.length(clippedText) > 1
? handleOverflow(~maxWidth, ~text=clippedText, ~measure, ~character, ())
: clippedText ++ character;
};
================================================
FILE: src/Core/TextWrapping.re
================================================
open Revery_TextWrap;
type wrapType =
| NoWrap
| Wrap
| WrapIgnoreWhitespace
| WrapHyphenate;
let wrapText = (~text, ~measureWidth as width_of_token, ~maxWidth, ~mode) => {
switch (mode) {
| NoWrap => [text]
| Wrap => wrap(text, ~max_width=maxWidth, ~width_of_token)
| WrapIgnoreWhitespace =>
wrap(
text,
~max_width=maxWidth,
~width_of_token,
~ignore_preceding_whitespace=false,
)
| WrapHyphenate =>
wrap(text, ~max_width=maxWidth, ~width_of_token, ~hyphenate=true)
};
};
================================================
FILE: src/Core/Tick.re
================================================
module type Clock = {let time: unit => Time.t;};
module Log = (val Log.withNamespace("Revery.Tick"));
module DefaultClock = {
let time = Time.now;
};
type callback = Time.t => unit;
type dispose = unit => unit;
module IntMap =
Map.Make({
type t = int;
let compare = compare;
});
module Make = (ClockImpl: Clock) => {
type nonrec callback = callback;
module TickId =
UniqueId.Make({});
type tickType =
| Timeout
| Interval;
type tickFunction = {
tickType,
id: int,
lastExecutionTime: Time.t,
frequency: Time.t,
f: callback,
};
let showTickFunction = (v: tickFunction) => {
string_of_int(v.id);
};
let _activeTickers: ref(list(tickFunction)) = ref([]);
let _scheduledTickers: ref(list(tickFunction)) = ref([]);
let _cancelledTickers: ref(IntMap.t(bool)) = ref(IntMap.empty);
let getActiveTickers = () => {
_activeTickers^ |> List.map(tf => tf.id);
};
let toString = () =>
_activeTickers^
|> List.fold_left(
(prev, curr) => showTickFunction(curr) ++ ", " ++ prev,
"",
);
let pump = () => {
// Add any newly-scheduled tickers
_activeTickers := List.concat([_scheduledTickers^, _activeTickers^]);
_scheduledTickers := [];
Log.tracef(m =>
m(
"Tick.pump - starting with %d active tickers.",
List.length(_activeTickers^),
)
);
// Clear any pending tickers
let cancelled = _cancelledTickers^;
_activeTickers :=
List.fold_left(
(prev, curr) => {
switch (IntMap.find_opt(curr.id, cancelled)) {
| None => [curr, ...prev]
| Some(_) => prev
}
},
[],
_activeTickers^,
);
_cancelledTickers := IntMap.empty;
let currentTime = ClockImpl.time();
let f = (tf: tickFunction) => {
let nextTime = Time.(tf.lastExecutionTime + tf.frequency);
if (nextTime <= currentTime) {
let elapsedTime = Time.(currentTime - tf.lastExecutionTime);
ignore(tf.f(elapsedTime));
switch (tf.tickType) {
| Timeout => None
| Interval => Some({...tf, lastExecutionTime: currentTime})
};
} else {
Some(tf);
};
};
let newTickers = _activeTickers^ |> List.filter_map(f);
_activeTickers := newTickers;
Log.tracef(m =>
m(
"Tick.pump - ending with %d active tickers.",
List.length(_activeTickers^),
)
);
};
let _clear = (~name, id: int, ()) => {
Log.tracef(m => m("Clearing interval/timeout: %s %d", name, id));
_cancelledTickers := IntMap.add(id, true, _cancelledTickers^);
};
exception Stop;
let interval = (~name: string, f: callback, frequency: Time.t) => {
let id = TickId.getUniqueId();
Log.tracef(m => m("Interval - starting timer: %s %d", name, id));
let f = t => {
Log.tracef(m => m("Interval - running timer: %s %d", name, id));
try(f(t)) {
| Stop => _clear(~name, id, ())
};
};
let tf: tickFunction = {
tickType: Interval,
id,
lastExecutionTime: ClockImpl.time(),
frequency,
f,
};
_scheduledTickers := [tf, ..._scheduledTickers^];
_clear(~name, id);
};
let timeout = (~name, f, waitTime: Time.t) => {
let id = TickId.getUniqueId();
Log.tracef(m => m("Timeout - starting timer: %s %d", name, id));
let f = _t => {
Log.tracef(m => m("Timeout - running timer: %s %d", name, id));
f();
};
let tf: tickFunction = {
tickType: Timeout,
id,
lastExecutionTime: ClockImpl.time(),
frequency: waitTime,
f,
};
_scheduledTickers := [tf, ..._scheduledTickers^];
_clear(~name, id);
};
};
module Default = Make(DefaultClock);
================================================
FILE: src/Core/Time.re
================================================
type t = float;
let zero = 0.;
let seconds = t => float(t);
let milliseconds = t => float(t) /. 1000.;
let ms = milliseconds;
let ofFloatSeconds = t => t;
let toFloatSeconds = t => t;
let (+) = (+.);
let (-) = (-.);
let ( * ) = ( *. );
let ( *. ) = ( *. );
let (/) = (/.);
let (/.) = (/.);
let max = Float.max;
let min = Float.min;
let (mod) = mod_float;
let toString = t => string_of_float(t) ++ "s";
let now = Unix.gettimeofday;
================================================
FILE: src/Core/Time.rei
================================================
type t;
let zero: t;
let seconds: int => t;
let milliseconds: int => t;
let ms: int => t;
/**
[ofFloatSeconds(seconds)] converts a float value in seconds to a [t] representing the time.
*/
let ofFloatSeconds: float => t;
/**
[toFloatSeconds(time)] converts the time [t] to a float value representing the time in seconds.
*/
let toFloatSeconds: t => float;
let (+): (t, t) => t;
let (-): (t, t) => t;
let ( * ): (t, t) => t;
let ( *. ): (t, float) => t;
let (/): (t, t) => t;
let (/.): (t, float) => t;
let max: (t, t) => t;
let min: (t, t) => t;
let (mod): (t, t) => t;
let toString: t => string;
/**
[now()] returns the current system time [t]
*/
let now: unit => t;
================================================
FILE: src/Core/UniqueId.re
================================================
module Make = (()) => {
let current = ref(0);
let getUniqueId = () => {
let ret = current^;
incr(current);
ret;
};
};
================================================
FILE: src/Core/Vsync.re
================================================
type t =
| Immediate /* no vsync */
| Synchronized; /* waiting for vsync */
// TODO: Enable adaptive vsync
//| Adaptive /* like vsync, but swaps buffers immediately if vertical retrace missed. Not supported everywhere */
let toInt = v =>
switch (v) {
| Immediate => 0
| Synchronized => 1
//| Adaptive => -1
};
let toString = v =>
switch (v) {
| Immediate => "Immediate"
| Synchronized => "Synchronized"
//| Adaptive => "Adaptive"
};
================================================
FILE: src/Core/Window.re
================================================
open Events;
type unsubscribe = unit => unit;
[@deriving show({with_path: false})]
type size =
Sdl2.Size.t = {
width: int,
height: int,
};
let scaleSize = (~scale: float, size) => {
let width = int_of_float(float_of_int(size.width) /. scale);
let height = int_of_float(float_of_int(size.height) /. scale);
{width, height};
};
module Log = (val Log.withNamespace("Revery.Core.Window"));
module WindowMetrics: {
// This disables all unused value warnings for this module. Unfortunately necessary due to `deriving show`.
[@warning "-32"];
[@deriving show]
type t =
pri {
/* [scaledSize] is the size of the window, in scaled screen coordinates, based on the display settings of the platform */
scaledSize: size,
/* [unscaledSize] is the size of the window, in screen coordinates, without display scaling applied */
unscaledSize: size,
/* [framebufferSize] is the actual size in pixels of the framebuffer - the render surface. On high DPI displays, this may be
some multiple of the screen sizes described by [scaledSize] and [unscaledSize]. */
framebufferSize: size,
/* [devicePixelRatio] is the ratio of pixels to screen coordinates (ie, [framebufferSize / unscaledSize]) */
devicePixelRatio: float,
/* [scaleFactor] is the ratio between [unscaledSize] and [scaledSize] */
scaleFactor: float,
zoom: float,
isDirty: bool,
};
let fromSdlWindow:
(~forceScaleFactor: float=?, ~zoom: float=?, Sdl2.Window.t) => t;
let setZoom: (float, t) => t;
let markDirty: t => t;
} = {
[@deriving show({with_path: false})]
type t = {
scaledSize: size,
unscaledSize: size,
framebufferSize: size,
devicePixelRatio: float,
scaleFactor: float,
zoom: float,
isDirty: bool,
};
module Internal = {
let getScaleFactor = (~forceScaleFactor=?, sdlWindow) => {
switch (forceScaleFactor) {
// If a scale factor is forced... prefer that!
| Some(v) => v
// Otherwise, the way we figure out the scale factor depends on the platform
| None =>
switch (Environment.os) {
// Mac and iOS is easy... there isn't any scaling factor. The window is automatically
// proportioned for us. The scaling is handled by the ratio of size / framebufferSize.
| IOS
| Mac(_) => 1.0
// On Windows, we need to try a Win32 API to get the scale factor
| Windows(_) =>
let scale = Sdl2.Window.getWin32ScaleFactor(sdlWindow);
Log.tracef(m =>
m("_getScaleFactor - from getWin32ScaleFactor: %f", scale)
);
scale;
// On Linux it can be pretty tricky depending on the display server and other factors.
// - First, we'll look for a [GDK_SCALE] environment variable, and prefer that.
// - Otherwise we default to 1.0 until we have a reliable method to obtain the value.
// See the following links for more details:
// https://github.com/revery-ui/revery/issues/878
// https://github.com/glfw/glfw/issues/1019
// https://github.com/mosra/magnum/commit/ae31c3cd82ba53454b8ab49d3f9d8ca385560d4b
// https://github.com/glfw/glfw/blob/250b94cd03e6f947ba516869c7f3b277f8d0cacc/src/x11_init.c#L938
// https://wiki.archlinux.org/index.php/HiDPI
| Linux(_) =>
switch (Rench.Environment.getEnvironmentVariable("GDK_SCALE")) {
| None => 1.0
| Some(v) =>
Log.trace(
"_getScaleFactor - Linux - got GDK_SCALE variable: " ++ v,
);
switch (Float.of_string_opt(v)) {
| Some(v) => v
| None => 1.0
};
}
// On Android we can mostly trust the getDPI and the base DPI is 160
// ddpi indicates the scale factor choosen by the manufacturer
// hdpi is the real horizontal dpi, vdpi is the real vertical dpi, they can be different
// also any non integer scaleFactor is valid as it's choosen by the manufecturer
| Android =>
let display = Sdl2.Window.getDisplay(sdlWindow);
let dpi = Sdl2.Display.getDPI(display);
let scaleFactor = dpi.ddpi /. 160.0;
Log.tracef(m =>
m(
"_getScaleFactor - Android - inferring from DPI: %f",
scaleFactor,
)
);
scaleFactor;
| _ => 1.0
}
};
};
};
let fromSdlWindow = (~forceScaleFactor=?, ~zoom=1.0, sdlWindow) => {
let unscaledSize = Sdl2.Window.getSize(sdlWindow);
let framebufferSize = Sdl2.Gl.getDrawableSize(sdlWindow);
let scaleFactor = Internal.getScaleFactor(~forceScaleFactor?, sdlWindow);
let devicePixelRatio =
float_of_int(framebufferSize.width) /. float_of_int(unscaledSize.width);
let scaledSize = unscaledSize |> scaleSize(~scale=scaleFactor);
{
scaledSize,
unscaledSize: {
width: unscaledSize.width,
height: unscaledSize.height,
},
framebufferSize: {
width: framebufferSize.width,
height: framebufferSize.height,
},
scaleFactor,
devicePixelRatio,
zoom,
isDirty: false,
};
};
let setZoom = (zoom, metrics) => {
Log.tracef(m => m("Setting zoom: %f", zoom));
{...metrics, zoom, isDirty: true};
};
let markDirty = metrics => {...metrics, isDirty: true};
};
module FPS = {
let timerInterval = 1000;
type t = {
// tickAfterLastRender is in milliseconds
mutable tickAfterLastRender: int,
mutable fps: int,
mutable timer: int,
mutable frameCount: int,
};
let default = () => {
tickAfterLastRender: Sdl2.Timekeeping.getTicks(),
fps: 0,
timer: 0,
frameCount: 0,
};
let getFPS = (c: t) => c.fps;
let update = (state: t) => {
let tick = Sdl2.Timekeeping.getTicks();
let deltaTime = tick - state.tickAfterLastRender;
state.timer = state.timer + deltaTime;
state.frameCount = state.frameCount + 1;
if (state.timer >= timerInterval) {
state.fps = state.frameCount;
state.timer = 0;
state.frameCount = 0;
};
state.tickAfterLastRender = tick;
};
};
type t = {
mutable backgroundColor: Color.t,
sdlWindow: Sdl2.Window.t,
sdlContext: Sdl2.Gl.context,
uniqueId: int,
forceScaleFactor: option(float),
mutable render: unit => unit,
mutable shouldRender: unit => bool,
mutable canQuit: unit => bool,
mutable metrics: WindowMetrics.t,
mutable isRendering: bool,
mutable requestedUnscaledSize: option(size),
mutable fpsCounter: FPS.t,
mutable showFPSCounter: bool,
// True if composition (IME) is active
mutable isComposingText: bool,
mutable dropState: option(list(string)),
mutable opacity: float,
titlebarStyle: WindowStyles.titlebar,
isDecorated: bool,
onBeforeRender: Event.t(unit),
onAfterRender: Event.t(unit),
onBeforeSwap: Event.t(unit),
onAfterSwap: Event.t(unit),
onFocusGained: Event.t(unit),
onFocusLost: Event.t(unit),
onExposed: Event.t(unit),
onKeyDown: Event.t(Key.KeyEvent.t),
onKeyUp: Event.t(Key.KeyEvent.t),
onMouseUp: Event.t(mouseButtonEvent),
onMouseMove: Event.t(mouseMoveEvent),
onMouseWheel: Event.t(mouseWheelEvent),
onMouseDown: Event.t(mouseButtonEvent),
onMouseEnter: Event.t(unit),
onMouseLeave: Event.t(unit),
onMaximized: Event.t(unit),
onFullscreen: Event.t(unit),
onMinimized: Event.t(unit),
onRestored: Event.t(unit),
onSizeChanged: Event.t(size),
onMoved: Event.t((int, int)),
onCompositionStart: Event.t(unit),
onCompositionEdit: Event.t(textEditEvent),
onCompositionEnd: Event.t(unit),
onTextInputCommit: Event.t(textInputEvent),
onFileDropped: Event.t(fileDropEvent),
};
module Internal = {
let setTitlebarStyle = (w: Sdl2.Window.t, style: WindowStyles.titlebar) => {
switch (Environment.os) {
| Mac(_) =>
switch (style) {
| Transparent => Sdl2.Window.setMacTitlebarTransparent(w)
| Hidden => Sdl2.Window.setMacTitlebarHidden(w)
| System => ()
}
| Android
| Browser
// NOTE: may work for IOS?
| IOS
| Linux(_)
| Unknown
| Windows(_) => ()
};
};
let resetTitlebarStyle = window =>
// On restore, we need to set the titlebar transparent again on Mac
switch (window.titlebarStyle) {
| Transparent => setTitlebarStyle(window.sdlWindow, Transparent)
| Hidden => setTitlebarStyle(window.sdlWindow, Hidden)
| System => ()
};
let updateMetrics = (w: t) => {
w.metrics =
WindowMetrics.fromSdlWindow(
~forceScaleFactor=?w.forceScaleFactor,
~zoom=w.metrics.zoom,
w.sdlWindow,
);
Log.trace(
"updateMetrics - new metrics: " ++ WindowMetrics.show(w.metrics),
);
};
let setRawSize = (win: t, adjWidth: int, adjHeight: int) => {
Log.tracef(m =>
m("setRawSize - calling with: %ix%i", adjWidth, adjHeight)
);
if (adjWidth != win.metrics.unscaledSize.width
|| adjHeight != win.metrics.unscaledSize.height) {
/*
* Don't resize in the middle of a render -
* we'll queue up the render operation for next time.
*/
if (win.isRendering) {
Log.trace("setRawSize - queuing for next render");
win.requestedUnscaledSize =
Some({width: adjWidth, height: adjHeight});
} else {
Log.trace("setRawSize - calling Sdl2.Window.setSize");
Sdl2.Window.setSize(win.sdlWindow, adjWidth, adjHeight);
win.requestedUnscaledSize = None;
win.metrics = WindowMetrics.markDirty(win.metrics);
Log.tracef(m => {
let Sdl2.Size.{width, height} = Sdl2.Window.getSize(win.sdlWindow);
m(
"setRawSize: SDL size reported after resize: %ux%u",
width,
height,
);
});
};
};
};
let resizeIfNecessary = (w: t) =>
switch (w.requestedUnscaledSize) {
| Some({width, height}) => setRawSize(w, width, height)
| None => ()
};
};
let onBeforeRender = w => Event.subscribe(w.onBeforeRender);
let onAfterRender = w => Event.subscribe(w.onAfterRender);
let onBeforeSwap = w => Event.subscribe(w.onBeforeSwap);
let onAfterSwap = w => Event.subscribe(w.onAfterSwap);
let onFocusGained = w => Event.subscribe(w.onFocusGained);
let onFocusLost = w => Event.subscribe(w.onFocusLost);
let onMaximized = w => Event.subscribe(w.onMaximized);
let onFullscreen = w => Event.subscribe(w.onFullscreen);
let onMinimized = w => Event.subscribe(w.onMinimized);
let onRestored = w => Event.subscribe(w.onRestored);
let onExposed = w => Event.subscribe(w.onExposed);
let onKeyDown = w => Event.subscribe(w.onKeyDown);
let onKeyUp = w => Event.subscribe(w.onKeyUp);
let onMouseMove = w => Event.subscribe(w.onMouseMove);
let onMouseWheel = w => Event.subscribe(w.onMouseWheel);
let onMouseEnter = w => Event.subscribe(w.onMouseEnter);
let onMouseLeave = w => Event.subscribe(w.onMouseLeave);
let onMouseDown = w => Event.subscribe(w.onMouseDown);
let onMouseUp = w => Event.subscribe(w.onMouseUp);
let onCompositionStart = w => Event.subscribe(w.onCompositionStart);
let onCompositionEdit = w => Event.subscribe(w.onCompositionEdit);
let onCompositionEnd = w => Event.subscribe(w.onCompositionEnd);
let onTextInputCommit = w => Event.subscribe(w.onTextInputCommit);
let onSizeChanged = w => Event.subscribe(w.onSizeChanged);
let onMoved = w => Event.subscribe(w.onMoved);
let onFileDropped = w => Event.subscribe(w.onFileDropped);
let getUniqueId = (w: t) => w.uniqueId;
let isDirty = (w: t) =>
if (w.shouldRender() || w.metrics.isDirty) {
true;
} else {
w.requestedUnscaledSize != None;
};
let isDecorated = (w: t) => w.isDecorated;
let getSdlWindow = (w: t) => w.sdlWindow;
let setTitle = (v: t, title: string) => {
Sdl2.Window.setTitle(v.sdlWindow, title);
};
let getTitlebarHeight = (v: t) =>
Sdl2.Window.getMacTitlebarHeight(v.sdlWindow);
let setSize = (~width: int, ~height: int, win: t) => {
Log.tracef(m => m("setSize - calling with: %ux%u", width, height));
// On platforms that return a non-unit scale factor (Windows and Linux),
// we also have to scale the window size by the scale factor
let adjWidth =
int_of_float(float_of_int(width) *. win.metrics.scaleFactor);
let adjHeight =
int_of_float(float_of_int(height) *. win.metrics.scaleFactor);
Internal.setRawSize(win, adjWidth, adjHeight);
};
let setMinimumSize = (~width: int, ~height: int, win: t) => {
Log.tracef(m => m("setMinimumSize - calling with: %ux%u", width, height));
Sdl2.Window.setMinimumSize(win.sdlWindow, width, height);
};
let setZoom = (window, zoom) => {
window.metrics = WindowMetrics.setZoom(max(zoom, 0.1), window.metrics);
};
let setUnsavedWork = (window, truth) => {
Revery_Native.Window.setUnsavedWork(window.sdlWindow, truth);
};
let render = window => {
Internal.resizeIfNecessary(window);
if (window.metrics.isDirty) {
Internal.updateMetrics(window);
};
window.isRendering = true;
Event.dispatch(window.onBeforeRender, ());
window.render();
Event.dispatch(window.onAfterRender, ());
Event.dispatch(window.onBeforeSwap, ());
Performance.bench("swapWindow", () => Sdl2.Gl.swapWindow(window.sdlWindow));
Event.dispatch(window.onAfterSwap, ());
window.isRendering = false;
FPS.update(window.fpsCounter);
};
let handleEvent = (sdlEvent: Sdl2.Event.t, v: t) => {
Log.tracef(m => m("Window.handleEvent: %s", Sdl2.Event.show(sdlEvent)));
switch (sdlEvent) {
| Sdl2.Event.MouseWheel({deltaX, deltaY, _}) =>
let (windowX, windowY) = Sdl2.Window.getPosition(v.sdlWindow);
let (mouseX, mouseY) = Sdl2.Mouse.getGlobalPosition();
let wheelEvent: Events.mouseWheelEvent = {
deltaX: float(deltaX),
deltaY: float(deltaY),
mouseX: float(mouseX - windowX),
mouseY: float(mouseY - windowY),
keymod: Sdl2.Keymod.getState(),
};
Event.dispatch(v.onMouseWheel, wheelEvent);
| Sdl2.Event.MouseMotion({x, y, _}) =>
let mouseEvent: Events.mouseMoveEvent = {
mouseX: float(x),
mouseY: float(y),
keymod: Sdl2.Keymod.getState(),
};
Event.dispatch(v.onMouseMove, mouseEvent);
| Sdl2.Event.MouseButtonUp(event) =>
Event.dispatch(
v.onMouseUp,
{
button: MouseButton.convert(event.button),
keymod: Sdl2.Keymod.getState(),
},
)
| Sdl2.Event.MouseButtonDown(event) =>
Event.dispatch(
v.onMouseDown,
{
button: MouseButton.convert(event.button),
keymod: Sdl2.Keymod.getState(),
},
)
| Sdl2.Event.KeyDown({keycode, keymod, scancode, repeat, _}) =>
let keyEvent: Key.KeyEvent.t = {keycode, scancode, keymod, repeat};
Event.dispatch(v.onKeyDown, keyEvent);
| Sdl2.Event.KeyUp({keycode, keymod, scancode, repeat, _}) =>
let keyEvent: Key.KeyEvent.t = {keycode, scancode, keymod, repeat};
Event.dispatch(v.onKeyUp, keyEvent);
| Sdl2.Event.TextEditing(te) =>
if (!v.isComposingText) {
Event.dispatch(v.onCompositionStart, ());
v.isComposingText = true;
};
Event.dispatch(
v.onCompositionEdit,
{text: te.text, start: te.start, length: te.length},
);
| Sdl2.Event.TextInput(ti) =>
if (v.isComposingText) {
Event.dispatch(v.onCompositionEnd, ());
v.isComposingText = false;
};
Event.dispatch(v.onTextInputCommit, {text: ti.text});
| Sdl2.Event.WindowResized(_) =>
v.metrics = WindowMetrics.markDirty(v.metrics)
| Sdl2.Event.WindowSizeChanged({width, height, _}) =>
v.metrics = WindowMetrics.markDirty(v.metrics);
// Scale the window size changed event, so that is in the same 'scaled-screen-space' as
// other size functions, like [getSize], [setSize], and [create].
let unscaledSize = {width, height};
let scaledSize = scaleSize(~scale=v.metrics.scaleFactor, unscaledSize);
Event.dispatch(v.onSizeChanged, scaledSize);
| Sdl2.Event.WindowMoved({x, y, _}) =>
v.metrics = WindowMetrics.markDirty(v.metrics);
Event.dispatch(v.onMoved, (x, y));
| Sdl2.Event.WindowEnter(_) => Event.dispatch(v.onMouseEnter, ())
| Sdl2.Event.WindowLeave(_) => Event.dispatch(v.onMouseLeave, ())
| Sdl2.Event.WindowExposed(_) => Event.dispatch(v.onExposed, ())
| Sdl2.Event.WindowMaximized(_) => Event.dispatch(v.onMaximized, ())
| Sdl2.Event.WindowFullscreen(_) => Event.dispatch(v.onFullscreen, ())
| Sdl2.Event.WindowMinimized(_) => Event.dispatch(v.onMinimized, ())
| Sdl2.Event.WindowRestored(_) =>
Internal.resetTitlebarStyle(v);
Event.dispatch(v.onRestored, ());
| Sdl2.Event.WindowFocusGained(_) => Event.dispatch(v.onFocusGained, ())
| Sdl2.Event.WindowFocusLost(_) => Event.dispatch(v.onFocusLost, ())
| Sdl2.Event.DropBegin(_) => v.dropState = Some([])
| Sdl2.Event.DropFile({file, _}) =>
switch (v.dropState) {
| Some(list) => v.dropState = Some([file, ...list])
| None =>
Log.warn("Received drop file event without preceding drop begin")
}
| Sdl2.Event.DropComplete({x, y, _}) =>
switch (v.dropState) {
| None
| Some([]) =>
Log.warn("Received drop complete event without preceding drop events")
| Some(list) =>
v.dropState = None;
Event.dispatch(
v.onFileDropped,
{
mouseX: float(x),
mouseY: float(y),
paths: List.rev(list),
keymod: Sdl2.Keymod.getState(),
},
);
}
| Sdl2.Event.Quit => ()
| _ => ()
};
};
let setVsync =
(
_window: t, // TODO: Multiple windows - set context
vsync: Vsync.t,
) => {
Log.info("Using vsync: " ++ Vsync.toString(vsync));
switch (vsync) {
| Vsync.Immediate => Sdl2.Gl.setSwapInterval(0)
| Vsync.Synchronized => Sdl2.Gl.setSwapInterval(1)
};
};
let create = (name: string, options: WindowCreateOptions.t) => {
Log.debug("Starting window creation...");
let displays = Sdl2.Display.getDisplays();
Log.infof(m => m("Display count: %d", List.length(displays)));
displays
|> List.iteri((idx, display) => {
Log.infof(m =>
m(
"Display %d:%s - %s Bounds: %s",
idx,
Sdl2.Display.getName(display),
Sdl2.Display.getCurrentMode(display) |> Sdl2.Display.Mode.show,
Sdl2.Display.getBounds(display) |> Sdl2.Rect.toString,
)
)
});
let width = options.width == 0 ? 800 : options.width;
let height = options.height == 0 ? 480 : options.height;
let x = options.x;
let y = options.y;
Log.infof(m =>
m("Creating window %s width: %u height: %u", name, width, height)
);
let sdlWindow =
Sdl2.Window.create(name, x, y, width, height, options.acceleration);
Log.info("Window created successfully.");
let uniqueId = Sdl2.Window.getId(sdlWindow);
Log.debugf(m => m("- Id: %i", uniqueId));
let pixelFormat =
Sdl2.Window.getPixelFormat(sdlWindow) |> Sdl2.PixelFormat.toString;
Log.debugf(m => m("- PixelFormat: %s", pixelFormat));
// We need to let Windows know that we are DPI-aware and that we are going to
// properly handle scaling. This is a no-op on other platforms.
Sdl2.Window.setWin32ProcessDPIAware(sdlWindow);
Log.debug("Setting window context");
let context = Sdl2.Gl.setup(sdlWindow);
Sdl2.Gl.makeCurrent(sdlWindow, context);
Log.debug("GL setup. Checking GL version...");
let version = Sdl2.Gl.getString(Sdl2.Gl.Version);
Log.debug("Checking GL vendor...");
let vendor = Sdl2.Gl.getString(Sdl2.Gl.Vendor);
Log.debug("Checking GL shading language version...");
let shadingLanguageVersion =
Sdl2.Gl.getString(Sdl2.Gl.ShadingLanguageVersion);
let renderer = Sdl2.Gl.getString(Sdl2.Gl.Renderer);
Log.info("OpenGL hardware info:");
Log.infof(m => m(" renderer: %s", renderer));
Log.infof(m => m(" version: %s", version));
Log.infof(m => m(" vendor: %s", vendor));
Log.infof(m => m(" shadingLanguageVersion: %s", shadingLanguageVersion));
switch (options.icon) {
| None => Log.debug("No icon to load.")
| Some(path) =>
let execDir = Environment.getExecutingDirectory();
let relativeImagePath = execDir ++ path;
Log.debug("Loading icon from: " ++ relativeImagePath);
switch (Sdl2.Surface.createFromImagePath(relativeImagePath)) {
| Ok(v) =>
Log.debug("Icon loaded successfully.");
Sdl2.Window.setIcon(sdlWindow, v);
Log.debug("Icon set successfully.");
| Error(msg) => Log.error("Error loading icon: " ++ msg)
};
};
Log.debug("Getting window metrics");
let metrics =
WindowMetrics.fromSdlWindow(
~forceScaleFactor=?options.forceScaleFactor,
sdlWindow,
);
Log.debug("Metrics: " ++ WindowMetrics.show(metrics));
let window = {
backgroundColor: options.backgroundColor,
sdlWindow,
sdlContext: context,
uniqueId,
render: () => (),
shouldRender: () => false,
canQuit: () => true,
metrics,
isRendering: false,
requestedUnscaledSize: None,
fpsCounter: FPS.default(),
showFPSCounter: false,
isComposingText: false,
dropState: None,
forceScaleFactor: options.forceScaleFactor,
isDecorated: options.decorated,
opacity: options.opacity,
onBeforeRender: Event.create(),
onAfterRender: Event.create(),
onBeforeSwap: Event.create(),
onAfterSwap: Event.create(),
onFocusGained: Event.create(),
onFocusLost: Event.create(),
onMinimized: Event.create(),
onMaximized: Event.create(),
onFullscreen: Event.create(),
onRestored: Event.create(),
onExposed: Event.create(),
onSizeChanged: Event.create(),
onMoved: Event.create(),
onMouseMove: Event.create(),
onMouseWheel: Event.create(),
onMouseUp: Event.create(),
onMouseDown: Event.create(),
onMouseEnter: Event.create(),
onMouseLeave: Event.create(),
onKeyDown: Event.create(),
onKeyUp: Event.create(),
onCompositionStart: Event.create(),
onCompositionEdit: Event.create(),
onCompositionEnd: Event.create(),
onTextInputCommit: Event.create(),
onFileDropped: Event.create(),
titlebarStyle: options.titlebarStyle,
};
// Call setSize to account for scaling, if necessary.
// On some platforms - ie, Windows, we don't know about the scale factor
// until after we create the window, so after creation we have to check
// to make sure we're at the correct size for scaling.
setSize(~width, ~height, window);
// Set a minimum size for the window
setMinimumSize(
~width=options.minimumWidth,
~height=options.minimumHeight,
window,
);
setVsync(window, options.vsync);
if (!options.decorated) {
Sdl2.Window.setBordered(sdlWindow, false);
};
if (!options.resizable) {
Sdl2.Window.setResizable(sdlWindow, false);
};
if (options.visible) {
Sdl2.Window.show(sdlWindow);
};
switch (options.titlebarStyle) {
| System => ()
| Transparent => Internal.setTitlebarStyle(sdlWindow, Transparent)
| Hidden => Internal.setTitlebarStyle(sdlWindow, Hidden)
};
Revery_Native.initWindow(sdlWindow);
if (options.maximized) {
Sdl2.Window.maximize(sdlWindow);
};
// Sdl2 has this named as Transparency, but it actually works as opacity
// where a value of 1.0 means it's fully opaque
Sdl2.Window.setTransparency(sdlWindow, options.opacity);
Internal.updateMetrics(window);
window;
};
let startTextInput = (_w: t) => {
Sdl2.TextInput.start();
};
let stopTextInput = (_w: t) => {
Sdl2.TextInput.stop();
};
let isTextInputActive = (_w: t) => {
Sdl2.TextInput.isActive();
};
let setInputRect = (_w: t, x, y, width, height) => {
// TODO: Do we need to apply scale factor here?
Sdl2.TextInput.setInputRect(
x,
y,
width,
height,
);
};
let setOpacity = (w: t, opacity) => w.opacity = opacity;
let setBackgroundColor = (w: t, color: Color.t) => {
w.backgroundColor = color;
};
let getBackgroundColor = window => window.backgroundColor;
let setPosition = (w: t, x: int, y: int) => {
Sdl2.Window.setPosition(w.sdlWindow, x, y);
w.metrics = WindowMetrics.markDirty(w.metrics);
};
let center = (w: t) => {
Sdl2.Window.center(w.sdlWindow);
};
let show = w => {
Sdl2.Window.show(w.sdlWindow);
};
let hide = w => {
Sdl2.Window.hide(w.sdlWindow);
};
let getSize = ({metrics, _}) => metrics.scaledSize;
let getPosition = window => {
Sdl2.Window.getPosition(window.sdlWindow);
};
let getFramebufferSize = (w: t) => {
w.metrics.framebufferSize;
};
let maximize = (w: t) => {
Sdl2.Window.maximize(w.sdlWindow);
};
let isMaximized = (w: t) => {
Sdl2.Window.isMaximized(w.sdlWindow);
};
let isFullscreen = (w: t) => {
Sdl2.Window.isFullscreen(w.sdlWindow);
};
let raise = (w: t) => {
Sdl2.Window.raise(w.sdlWindow);
};
let restore = (w: t) => {
Sdl2.Window.restore(w.sdlWindow);
};
let minimize = (w: t) => {
Sdl2.Window.minimize(w.sdlWindow);
};
let getDevicePixelRatio = (w: t) => {
w.metrics.devicePixelRatio;
};
let getScaleAndZoom = (w: t) => {
w.metrics.scaleFactor *. w.metrics.zoom;
};
let getZoom = (w: t) => {
w.metrics.zoom;
};
let takeScreenshot = (_w: t, _filename: string) => {
(); // TODO: Migrate to Skia
/*let width = w.metrics.framebufferSize.width;
let height = w.metrics.framebufferSize.height;
let pixels =
Bigarray.Array2.create(
Bigarray.int8_unsigned,
Bigarray.c_layout,
height,
width * 4,
);
/* let image = Image.create(~width, ~height, ~numChannels=4, ~channelSize=1); */
/* let buffer = Image.getBuffer(image); */
/* WebGL is weird in that we can't capture with glReadPixels during
a render operation. Instead, we want to wait till it's over (we
can force this by triggering a new render) and then taking the
screenshot */
render(w);
Gl.glReadPixels(0, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
let image = Image.create(pixels);
Image.save(image, filename);
Image.destroy(image);
*/
};
let canQuit = (w: t) => {
w.canQuit();
};
let setCanQuitCallback = (window: t, callback) => {
window.canQuit = callback;
};
let setRenderCallback = (window: t, callback) => window.render = callback;
let setShouldRenderCallback = (window: t, callback) =>
window.shouldRender = callback;
let getFPS = (w: t) => {
FPS.getFPS(w.fpsCounter);
};
let showFPSCounter = (w: t) => {
w.showFPSCounter = true;
};
let hideFPSCounter = (w: t) => {
w.showFPSCounter = false;
};
let shouldShowFPSCounter = (w: t) => {
w.showFPSCounter;
};
================================================
FILE: src/Core/Window.rei
================================================
open Events;
type t;
type size = {
width: int,
height: int,
};
type unsubscribe = unit => unit;
let onBeforeRender: (t, unit => unit) => unsubscribe;
let onAfterRender: (t, unit => unit) => unsubscribe;
let onBeforeSwap: (t, unit => unit) => unsubscribe;
let onAfterSwap: (t, unit => unit) => unsubscribe;
let onFocusGained: (t, unit => unit) => unsubscribe;
let onFocusLost: (t, unit => unit) => unsubscribe;
let onMaximized: (t, unit => unit) => unsubscribe;
let onFullscreen: (t, unit => unit) => unsubscribe;
let onMinimized: (t, unit => unit) => unsubscribe;
let onRestored: (t, unit => unit) => unsubscribe;
let onCompositionStart: (t, unit => unit) => unsubscribe;
let onCompositionEdit: (t, textEditEvent => unit) => unsubscribe;
let onCompositionEnd: (t, unit => unit) => unsubscribe;
let onExposed: (t, unit => unit) => unsubscribe;
let onKeyDown: (t, Key.KeyEvent.t => unit) => unsubscribe;
let onKeyUp: (t, Key.KeyEvent.t => unit) => unsubscribe;
let onMouseEnter: (t, unit => unit) => unsubscribe;
let onMouseLeave: (t, unit => unit) => unsubscribe;
let onMouseMove: (t, mouseMoveEvent => unit) => unsubscribe;
let onMouseDown: (t, mouseButtonEvent => unit) => unsubscribe;
let onMouseUp: (t, mouseButtonEvent => unit) => unsubscribe;
let onMouseWheel: (t, mouseWheelEvent => unit) => unsubscribe;
let onSizeChanged: (t, size => unit) => unsubscribe;
let onMoved: (t, ((int, int)) => unit) => unsubscribe;
let onTextInputCommit: (t, textInputEvent => unit) => unsubscribe;
let onFileDropped: (t, fileDropEvent => unit) => unsubscribe;
let canQuit: t => bool;
let getDevicePixelRatio: t => float;
/**
[getSize(window)] returns a [size] describing the window dimensions, accounting for display scaling.
*/
let getSize: t => size;
let getFramebufferSize: t => size;
/**
[setSize(~width, ~height, window)] sets the window size, taking display scaling into account.
For example, for a Windows display with 200% scaling, [setSize(~width=400, ~height=300, window)],
will actually set the window to a height of 800 pixels wide by 600 pixels high. This is usually
the behavior you want. To directly set the size, without considering display scaling, use [setUnscaledSize]
*/
let setSize: (~width: int, ~height: int, t) => unit;
/**
[setMinimumSize(~width, ~height, window)] sets the minimum window size.
*/
let setMinimumSize: (~width: int, ~height: int, t) => unit;
let getPosition: t => (int, int);
let getScaleAndZoom: t => float;
let getSdlWindow: t => Sdl2.Window.t;
let getZoom: t => float;
let getUniqueId: t => int;
let getBackgroundColor: t => Color.t;
let isDirty: t => bool;
let isDecorated: t => bool;
let setInputRect: (t, int, int, int, int) => unit;
let startTextInput: t => unit;
let stopTextInput: t => unit;
let isTextInputActive: t => bool;
let center: t => unit;
let hide: t => unit;
let show: t => unit;
let maximize: t => unit;
let isMaximized: t => bool;
let isFullscreen: t => bool;
let raise: t => unit;
let restore: t => unit;
let minimize: t => unit;
let setOpacity: (t, float) => unit;
let setBackgroundColor: (t, Color.t) => unit;
let setPosition: (t, int, int) => unit;
let setTitle: (t, string) => unit;
let setZoom: (t, float) => unit;
let setVsync: (t, Vsync.t) => unit;
let setUnsavedWork: (t, bool) => unit;
let render: t => unit;
let handleEvent: (Sdl2.Event.t, t) => unit;
let getTitlebarHeight: t => float;
/**
[create(name, options)] creates a new Revery application window.
See [WindowCreateOptions] for a list of available options.
*/
let create: (string, WindowCreateOptions.t) => t;
let takeScreenshot: (t, string) => unit;
let setCanQuitCallback: (t, unit => bool) => unit;
let setRenderCallback: (t, unit => unit) => unit;
let setShouldRenderCallback: (t, unit => bool) => unit;
let getFPS: t => int;
let showFPSCounter: t => unit;
let hideFPSCounter: t => unit;
let shouldShowFPSCounter: t => bool;
================================================
FILE: src/Core/WindowCreateOptions.re
================================================
/** [t] is the type representing creation options for a window */
type t = {
resizable: bool,
/**
If [visible] is true, the window will be visible immediately upon creation.
If [visible] is false, the window will be invisible, and will require a call
to Window.toString to make it visible. Can be useful to hide transient UI states.
*/
visible: bool,
/**
If [maximized] is true, the window will be expanded immediately upon creation.
*/
maximized: bool,
/**
If [decorated] is true, the window will be framed with the OS-specific adorners.
If false, the default chrome behavior on the OS will be disabled and (if you want
to maintain that functionality) you must implement it yourself through the
mouseBehavior attribute on Views
*/
decorated: bool,
/**
[titlebarStyle] sets the appearance of the titlebar. Eventually this will be platform
independent, but as of right now, [Transparent] and [Hidden] only works on macOS.
*/
titlebarStyle: WindowStyles.titlebar,
/**
[opacity] sets the opacity of the window.
A value of 1.0 means the window is fully opaque,
and a value of 0.0 means the window is completely transparent.
*/
opacity: float,
/**
[x] is the initial horizontal position of the [Window], either [`Centered]
or [`Absolute(x)], where [x] is the horizontal pixel coordinate of the left
edge of the [Window]
*/
x: [ | `Undefined | `Centered | `Absolute(int)],
/**
[y] is the initial horizontal position of the [Window], either [`Centered]
or [`Absolute(x)], where [y] is the vertical pixel coordinate of the top
edge of the [Window]
*/
y: [ | `Undefined | `Centered | `Absolute(int)],
/**
[width] is the initial horizontal size of the [Window], with display scaling applied.
*/
width: int,
/**
[height] is the initial vertical size of the [Window], with display scaling applied.
*/
height: int,
/**
[minimumWidth] is the minimum horizontal size of the [Window].
*/
minimumWidth: int,
/**
[minimumWidth] is the minimum vertical size of the [Window].
*/
minimumHeight: int,
/**
[backgroundColor] specifies the initial Color of the [Window]
*/
backgroundColor: Color.t,
/**
If [vsync] is true (default), rendering will be synchronized to the monitor refresh rate.
This can decrease tearing, but also limits the frame rate to the monito refresh rate.
*/
vsync: Vsync.t,
/*
[icon] is an optional path to an icon to show in the window frame.
*/
icon: option(string),
/*
[forceScaleFactor] is an optional value to force scaling factor. Scaling affects both the outer window dimensions,
as well as the scaling of UI elements.
*/
forceScaleFactor: option(float),
/*
[acceleration] sets the strategy for picking a renderer - one of:
- [`Auto] - will select either a hardware or software renderer (most platforms prefer a hardware renderer)
- [`ForceHardware] - forces selection of a hardware-accelerated renderer
- [`ForceSoftware] - forces selection of a software renderer
This is a proxy for the [SDL_GL_ACCELERATED_VISUAL](https://wiki.libsdl.org/SDL_GL_SetAttribute) setting.
*/
acceleration: [ | `Auto | `ForceHardware | `ForceSoftware],
};
let create =
(
~resizable=true,
~visible=true,
~maximized=false,
~decorated=true,
~titlebarStyle=WindowStyles.System,
~opacity=1.0,
~x=`Centered,
~y=`Centered,
~width=800,
~height=600,
~minimumWidth=200,
~minimumHeight=100,
~backgroundColor=Colors.cornflowerBlue,
~vsync=Vsync.Synchronized,
~icon=None,
~forceScaleFactor=None,
~acceleration=`Auto,
(),
) => {
resizable,
visible,
maximized,
decorated,
titlebarStyle,
opacity,
x,
y,
width,
height,
minimumWidth,
minimumHeight,
backgroundColor,
forceScaleFactor,
vsync,
icon,
acceleration,
};
let default = create();
================================================
FILE: src/Core/WindowStyles.re
================================================
type titlebar =
| System
// Only works on macOS as of right now
| Hidden
| Transparent;
================================================
FILE: src/Core/dune
================================================
(library
(name Revery_Core)
(public_name Revery.Core)
(preprocess
(pps ppx_deriving.show ppx_optcomp))
(preprocessor_deps ../Native/config.h)
(libraries threads str lwt sdl2 skia flex Rench re Revery_Native
Revery_TextWrap timber Revery_Zed))
================================================
FILE: src/Draw/CanvasContext.re
================================================
/**
* Canvas.re
*
* Module for integrating with the Skia canvas
*/
open Revery_Core;
module Log = (val Log.withNamespace("Revery.CanvasContext"));
open Skia;
type t = {
maybeGPUContext: option(Skia.Gr.Context.t),
surface: Skia.Surface.t,
canvas: Skia.Canvas.t,
mutable rootTransform: option(Skia.Matrix.t),
maybeWindow: option(Window.t),
};
let createFromSurface = (surface: Skia.Surface.t) => {
maybeGPUContext: None,
surface,
canvas: Skia.Surface.getCanvas(surface),
rootTransform: None,
maybeWindow: None,
};
let createGpuContext = () => {
let interface =
switch (Skia.Gr.Gl.Interface.makeNative()) {
| None =>
Log.debug("Unable to create native interface. Falling back to SDL2...");
// On Windows - we use the OpenGL ES context, in order to support ANGLE, for improved compatibility.
// ANGLE provides a Direct3D implementation of the OpenGL ES API.
Skia.Gr.Gl.Interface.(Sys.win32 ? makeSdl2ES() : makeSdl2());
| Some(_) as nativeInterface =>
Log.debug("Native interface created successfully.");
nativeInterface;
};
Log.info("Creating Skia context...");
Skia.Gr.Context.makeGl(interface);
};
let create = (gpuContext, window: Revery_Core.Window.t) => {
// Issue #759 - first, let's try to create a native context, since that is the most reliable...
// We'll fall back to an SDL2 context if not available (ie, in Wayland)
// TODO: There is still something busted with the way GL is being setup, for the SDL strategy not to work!
// Likely a fix or change is required here: https://github.com/revery-ui/reason-sdl2/blob/94dcd9094534c693998984fd684c642b0f658a43/src/sdl2_wrapper.cpp#L1065
switch (gpuContext) {
| None =>
Log.error("Unable to create skia context");
None;
| Some(glContext) =>
Log.debug("Skia context created successfully.");
let framebufferId = Sdl2.Gl.getFramebufferBinding();
Log.debug(Printf.sprintf("Framebuffer binding %d.", framebufferId));
let framebufferInfo =
Gr.Gl.FramebufferInfo.make(
Unsigned.UInt.of_int(framebufferId),
Unsigned.UInt.of_int(0x8058),
);
let framebufferSize = Window.getFramebufferSize(window);
let backendRenderTarget =
Gr.BackendRenderTarget.makeGl(
framebufferSize.width,
framebufferSize.height,
0,
8,
framebufferInfo,
);
let surfaceProps = SurfaceProps.make(Unsigned.UInt32.of_int(0), RgbH);
switch (
Surface.makeFromBackendRenderTarget(
glContext,
backendRenderTarget,
BottomLeft,
Rgba8888,
None,
Some(surfaceProps),
)
) {
| None =>
Log.error("Unable to create skia surface");
None;
| Some(v) =>
Log.infof(m =>
m(
"Successfully created canvas: %dx%d",
framebufferSize.width,
framebufferSize.height,
)
);
let surface = v;
Some({
maybeGPUContext: Some(glContext),
surface,
canvas: Surface.getCanvas(surface),
rootTransform: None,
maybeWindow: Some(window),
});
};
};
};
let createLayer = (~forceCpu=false, ~width, ~height, context: t) => {
Log.infof(m => m("Creating layer: %ldx%ld", width, height));
let width =
if (width <= 0l) {
1l;
} else {
width;
};
let height =
if (height <= 0l) {
1l;
} else {
height;
};
let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);
let createCpuSurface = () => {
Log.tracef(m => m("Created CPU surface: %ld x %ld", width, height));
Surface.makeRaster(imageInfo, 0, None);
};
let createGpuSurface = gpuContext => {
Log.trace("Trying to create GPU surface...");
let surfaceProps = SurfaceProps.make(Unsigned.UInt32.of_int(0), RgbH);
Skia.Surface.makeRenderTarget(
gpuContext,
false,
imageInfo,
0,
BottomLeft,
Some(surfaceProps),
false,
);
};
let (gpuContext, maybeSurface) =
switch (context.maybeGPUContext) {
| None => (None, createCpuSurface())
| Some(_) when forceCpu => (None, createCpuSurface())
| Some(gpuContext) as outContext =>
let maybeGpuSurface = createGpuSurface(gpuContext);
// The gpu surface creation can fail, so we should be
// prepared to fall back to a CPU surface.
switch (maybeGpuSurface) {
| Some(_) as surf =>
Log.tracef(m =>
m("Successfully created GPU surface: %ld x %ld", width, height)
);
(outContext, surf);
| None =>
Log.warn("Unable to create GPU surface; falling back to CPU surface");
(None, createCpuSurface());
};
};
maybeSurface
|> Option.map(surface => {
{
maybeGPUContext: gpuContext,
surface,
canvas: Surface.getCanvas(surface),
rootTransform: None,
maybeWindow: context.maybeWindow,
}
});
};
let width = ({surface, _}) => {
Surface.getWidth(surface);
};
let height = ({surface, _}) => {
Surface.getHeight(surface);
};
let resize = (window: Revery_Core.Window.t, v: option(t)) => {
switch (v) {
| None => None
| Some({maybeGPUContext, surface, _}) as v =>
let framebufferSize = Window.getFramebufferSize(window);
if (Surface.getWidth(surface) != framebufferSize.width
|| Surface.getHeight(surface) != framebufferSize.height) {
Log.infof(m =>
m(
"Resizing canvas: %dx%d->%dx%d",
Surface.getWidth(surface),
Surface.getHeight(surface),
framebufferSize.width,
framebufferSize.height,
)
);
create(maybeGPUContext, window);
} else {
v;
};
};
};
let setRootTransform = (matrix: Skia.Matrix.t, canvas: t) => {
canvas.rootTransform = Some(matrix);
};
let save = (v: t) => {
Skia.Canvas.save(v.canvas);
};
let restore = (v: t) => {
Skia.Canvas.restore(v.canvas);
};
let flush = (v: t) => {
Skia.Canvas.flush(v.canvas);
};
let translate = (v: t, x: float, y: float) => {
Skia.Canvas.translate(v.canvas, x, y);
};
let clear = (~color: Skia.Color.t, v: t) => {
Canvas.clear(v.canvas, color);
};
let drawLayer =
(~paint: Skia.Paint.t, ~layer: t, ~x: float, ~y: float, target: t) => {
Surface.draw(
~paint=Some(paint),
~canvas=target.canvas,
~x,
~y,
layer.surface,
);
};
let drawPath = (~path: Skia.Path.t, ~paint: Paint.t, canvasContext: t) => {
Canvas.drawPath(canvasContext.canvas, path, paint);
};
let drawRect = (~rect: Skia.Rect.t, ~paint: Paint.t, v: t) => {
Canvas.drawRect(v.canvas, rect, paint);
};
let drawRectLtwh =
(
~left: float,
~top: float,
~width: float,
~height: float,
~paint: Paint.t,
v: t,
) => {
Canvas.drawRectLtwh(v.canvas, left, top, width, height, paint);
};
let drawRoundRect =
(~rect: Skia.Rect.t, ~rx: float, ~ry: float, ~paint: Paint.t, v: t) => {
Canvas.drawRoundRect(v.canvas, rect, rx, ry, paint);
};
let drawOval = (~rect: Skia.Rect.t, ~paint: Paint.t, v: t) => {
Canvas.drawOval(v.canvas, rect, paint);
};
let drawCircle = (~x: float, ~y: float, ~radius: float, ~paint: Paint.t, v: t) => {
Canvas.drawCircle(v.canvas, x, y, radius, paint);
};
let drawRRect = (v: t, rRect: Skia.RRect.t, paint) => {
Canvas.drawRRect(v.canvas, rRect, paint);
};
let drawImage = (~x, ~y, ~width, ~height, ~paint=?, data: Skia.Image.t, v: t) => {
Canvas.drawImageRect(
v.canvas,
data,
None,
Rect.makeLtrb(x, y, x +. width, y +. height),
paint,
);
};
let drawText = (~paint, ~x=0., ~y=0., ~text, v: t) => {
Canvas.drawText(v.canvas, text, x, y, paint);
};
let _topMatrix = Skia.Matrix.make();
let setMatrix = (v: t, mat: Skia.Matrix.t) => {
switch (v.rootTransform) {
| None => Canvas.setMatrix(v.canvas, mat)
| Some(rootMatrix) =>
Skia.Matrix.concat(_topMatrix, rootMatrix, mat);
Canvas.setMatrix(v.canvas, _topMatrix);
};
};
let concat = (transform, context) => {
Canvas.concat(context.canvas, transform);
};
let clipRect =
(v: t, ~clipOp: clipOp=Intersect, ~antiAlias=false, rect: Skia.Rect.t) => {
Canvas.clipRect(v.canvas, rect, clipOp, antiAlias);
};
let clipRRect =
(v: t, ~clipOp: clipOp=Intersect, ~antiAlias=false, rRect: Skia.RRect.t) => {
Canvas.clipRRect(v.canvas, rRect, clipOp, antiAlias);
};
let clipPath =
(v: t, ~clipOp: clipOp=Intersect, ~antiAlias=false, path: Skia.Path.t) => {
Canvas.clipPath(v.canvas, path, clipOp, antiAlias);
};
================================================
FILE: src/Draw/DebugDraw.re
================================================
/**
* DebugDraw.re
*
* Helper to visualize some aspects of the UI, like which element the mouse is over
*/
open Revery_Math;
type t = {bbox: BoundingBox2d.t};
module Internal = {
let isEnabled: ref(bool) = ref(false);
let activeRect: ref(option(t)) = ref(None);
let paint = Skia.Paint.make();
Skia.Paint.setColor(paint, Skia.Color.makeArgb(50l, 255l, 0l, 0l));
};
let enable = () => Internal.isEnabled := true;
let disable = () => Internal.isEnabled := false;
let isEnabled = () => Internal.isEnabled^;
let setActive = (bbox: BoundingBox2d.t) => {
let v: t = {bbox: bbox};
Internal.activeRect := Some(v);
};
let draw = (canvas: CanvasContext.t) =>
if (Internal.isEnabled^) {
switch (Internal.activeRect^) {
| None => ()
| Some(v) =>
let (x0, y0, x1, y1) = BoundingBox2d.getBounds(v.bbox);
let rectangle = Skia.Rect.makeLtrb(x0, y0, x1, y1);
CanvasContext.drawRect(~rect=rectangle, ~paint=Internal.paint, canvas);
};
};
================================================
FILE: src/Draw/ImageResizeMode.re
================================================
type t =
| Stretch
| Repeat;
================================================
FILE: src/Draw/Revery_Draw.re
================================================
module CanvasContext = CanvasContext;
module Skia = Skia;
module DebugDraw = DebugDraw;
module ImageResizeMode = ImageResizeMode;
module Text = Text;
================================================
FILE: src/Draw/Text.re
================================================
open Revery_Font;
// INTERNAL
open {
let fontMetrics = (size, skiaFace) => {
switch (FontCache.load(skiaFace)) {
// TODO: Actually get metrics
| Ok(font) => FontCache.getMetrics(font, size)
| Error(_) => FontMetrics.empty(0.)
};
};
};
let lineHeight = (~italic=?, family, size, weight) => {
let maybeSkia = Family.toSkia(~italic?, weight, family);
fontMetrics(size, maybeSkia).lineHeight;
};
let ascent = (~italic=?, family, size, weight) => {
let maybeSkia = Family.toSkia(~italic?, weight, family);
fontMetrics(size, maybeSkia).ascent;
};
let descent = (~italic=?, family, size, weight) => {
let maybeSkia = Family.toSkia(~italic?, weight, family);
fontMetrics(size, maybeSkia).descent;
};
let charWidth =
(
~smoothing=Smoothing.default,
~italic=?,
~fontFamily,
~fontSize,
~fontWeight,
uchar,
) => {
let maybeSkia = Family.toSkia(~italic?, fontWeight, fontFamily);
switch (FontCache.load(maybeSkia)) {
| Ok(font) =>
let text = Zed_utf8.singleton(uchar);
FontRenderer.measure(~smoothing, font, fontSize, text).width;
| Error(_) => 0.
};
};
type dimensions =
FontRenderer.measureResult = {
width: float,
height: float,
};
let dimensions =
(
~smoothing=Smoothing.default,
~italic=?,
~fontFamily,
~fontSize,
~fontWeight,
text,
) => {
let maybeSkia = Family.toSkia(~italic?, fontWeight, fontFamily);
switch (FontCache.load(maybeSkia)) {
// TODO: Properly implement
| Ok(font) => FontRenderer.measure(~smoothing, font, fontSize, text)
| Error(_) => {width: 0., height: 0.}
};
};
let indexNearestOffset = (~measure, text, offset) => {
let length = String.length(text);
let rec loop = (~last, i) =>
if (i > length) {
i - 1;
} else {
let width = measure(String.sub(text, 0, i));
if (width > offset) {
let isCurrentNearest = width - offset < offset - last;
isCurrentNearest ? i : i - 1;
} else {
loop(~last=width, i + 1);
};
};
loop(~last=0, 1);
};
================================================
FILE: src/Draw/Text.rei
================================================
open Revery_Font;
let lineHeight: (~italic: bool=?, Family.t, float, Weight.t) => float;
let ascent: (~italic: bool=?, Family.t, float, Weight.t) => float;
let descent: (~italic: bool=?, Family.t, float, Weight.t) => float;
let charWidth:
(
~smoothing: Smoothing.t=?,
~italic: bool=?,
~fontFamily: Family.t,
~fontSize: float,
~fontWeight: Weight.t,
Uchar.t
) =>
float;
type dimensions = {
width: float,
height: float,
};
let dimensions:
(
~smoothing: Smoothing.t=?,
~italic: bool=?,
~fontFamily: Family.t,
~fontSize: float,
~fontWeight: Weight.t,
string
) =>
dimensions;
let indexNearestOffset: (~measure: string => int, string, int) => int;
================================================
FILE: src/Draw/dune
================================================
(library
(name Revery_Draw)
(public_name Revery.Draw)
(preprocess
(pps lwt_ppx))
(libraries skia brisk-reconciler lwt lwt.unix sdl2 flex rebez.lib
Revery_Core Revery_Font Revery_Math Revery_Zed))
================================================
FILE: src/Font/Discovery.re
================================================
/**
Discovery.re
*/
exception Font_not_found;
let find =
(
~weight=FontWeight.Normal,
~width=FontWidth.Normal,
~italic=false,
family,
) => {
let style =
Skia.FontStyle.make(
FontWeight.toInt(weight),
FontWidth.toInt(width),
italic ? Italic : Upright,
);
Skia.FontManager.matchFamilyStyle(FontManager.instance, family, style);
};
================================================
FILE: src/Font/Feature.re
================================================
type t = Harfbuzz.feature;
type tag = string;
type position = [ | `Start | `End | `Position(int)];
let customTag: string => tag = s => s;
let toString: tag => string = s => s;
let make = (~tag: tag, ~value) =>
Harfbuzz.{tag, value, start: `Start, stop: `End};
================================================
FILE: src/Font/Feature.rei
================================================
type t = Harfbuzz.feature;
type position = [ | `Start | `End | `Position(int)];
type tag;
let customTag: string => tag;
let toString: tag => string;
let make: (~tag: tag, ~value: int) => t;
================================================
FILE: src/Font/Features.re
================================================
open Feature;
let accessAllAlternates: tag = customTag("aalt");
let aboveBaseForms: tag = customTag("abvf");
let aboveBaseMarkPositioning: tag = customTag("abvm");
let aboveBaseSubstitutions: tag = customTag("abvs");
let alternativeFractions: tag = customTag("afrc");
let akhands: tag = customTag("akhn");
let belowBaseForms: tag = customTag("blwf");
let belowBaseMarkPositioning: tag = customTag("blwm");
let belowBaseSubstitutions: tag = customTag("blws");
let contextualAlternates: tag = customTag("calt");
let caseSensitiveForms: tag = customTag("case");
let glyphComposition: tag = customTag("ccmp");
let conjunctFormAfterRo: tag = customTag("cfar");
let conjunctForms: tag = customTag("cjct");
let contextualLigatures: tag = customTag("clig");
let centeredCjkPunctuation: tag = customTag("cpct");
let capitalSpacing: tag = customTag("cpsp");
let contextualSwash: tag = customTag("cswh");
let cursivePositioning: tag = customTag("curs");
let petiteCapitalsFromCapitals: tag = customTag("c2pc");
let smallCapitalsFromCapitals: tag = customTag("c2sc");
let distances: tag = customTag("dist");
let discretionaryLigatures: tag = customTag("dlig");
let denominators: tag = customTag("dnom");
let dotlessForms: tag = customTag("dtls");
let expertForms: tag = customTag("expt");
let finalGlyphOnLineAlternates: tag = customTag("falt");
let terminalForms2: tag = customTag("fin2");
let terminalForms3: tag = customTag("fin3");
let terminalForms: tag = customTag("fina");
let flattenedAccentForms: tag = customTag("flac");
let fractions: tag = customTag("frac");
let fullWidths: tag = customTag("fwid");
let halfForms: tag = customTag("half");
let halantForms: tag = customTag("haln");
let alternateHalfWidths: tag = customTag("halt");
let historicalForms: tag = customTag("hist");
let horizontalKanaAlternates: tag = customTag("hkna");
let historicalLigatures: tag = customTag("hlig");
let hangul: tag = customTag("hngl");
let hojoKanjiForms: tag = customTag("hojo");
let halfWidths: tag = customTag("hwid");
let initialForms: tag = customTag("init");
let isolatedForms: tag = customTag("isol");
let italics: tag = customTag("ital");
let justificationAlternates: tag = customTag("jalt");
let jis78Forms: tag = customTag("jp78");
let jis83Forms: tag = customTag("jp83");
let jis90Forms: tag = customTag("jp90");
let jis2004Forms: tag = customTag("jp04");
let kerning: tag = customTag("kern");
let leftBounds: tag = customTag("lfbd");
let standardLigatures: tag = customTag("liga");
let leadingJamoForms: tag = customTag("ljmo");
let liningFigures: tag = customTag("lnum");
let localizedForms: tag = customTag("locl");
let leftToRightAlternates: tag = customTag("ltra");
let leftToRightMirroredForms: tag = customTag("ltrm");
let markPositioning: tag = customTag("mark");
let medialForms2: tag = customTag("med2");
let medialForms: tag = customTag("medi");
let mathematicalGreek: tag = customTag("mgrk");
let markToMarkPositioning: tag = customTag("mkmk");
let markPositioningViaSubstitution: tag = customTag("mset");
let alternateAnnotationForms: tag = customTag("nalt");
let nlcKanjiForms: tag = customTag("nlck");
let nuktaForms: tag = customTag("nukt");
let numerators: tag = customTag("numr");
let oldstyleFigures: tag = customTag("onum");
let opticalBounds: tag = customTag("opbd");
let ordinals: tag = customTag("ordn");
let ornaments: tag = customTag("ornm");
let proportionalAlternateWidths: tag = customTag("palt");
let petiteCapitals: tag = customTag("pcap");
let proportionalKana: tag = customTag("pkna");
let proportionalFigures: tag = customTag("pnum");
let preBaseForms: tag = customTag("pref");
let preBaseSubstitutions: tag = customTag("pres");
let postBaseForms: tag = customTag("pstf");
let postBaseSubstitutions: tag = customTag("psts");
let proportionalWidths: tag = customTag("pwid");
let quarterWidths: tag = customTag("qwid");
let randomize: tag = customTag("rand");
let requiredContextualAlternates: tag = customTag("rclt");
let rakarForms: tag = customTag("rkrf");
let requiredLigatures: tag = customTag("rlig");
let rephForms: tag = customTag("rphf");
let rightBounds: tag = customTag("rtbd");
let rightToLeftAlternates: tag = customTag("rtla");
let rightToLeftMirroredForms: tag = customTag("rtlm");
let rubyNotationForms: tag = customTag("ruby");
let requiredVariationAlternates: tag = customTag("rvrn");
let stylisticAlternates: tag = customTag("salt");
let scientificInferiors: tag = customTag("sinf");
let opticalSize: tag = customTag("size");
let smallCapitals: tag = customTag("smcp");
let simplifiedForms: tag = customTag("smpl");
let stylisticSet1: tag = customTag("ss01");
let stylisticSet2: tag = customTag("ss02");
let stylisticSet3: tag = customTag("ss03");
let stylisticSet4: tag = customTag("ss04");
let stylisticSet5: tag = customTag("ss05");
let stylisticSet6: tag = customTag("ss06");
let stylisticSet7: tag = customTag("ss07");
let stylisticSet8: tag = customTag("ss08");
let stylisticSet9: tag = customTag("ss09");
let stylisticSet10: tag = customTag("ss10");
let stylisticSet11: tag = customTag("ss11");
let stylisticSet12: tag = customTag("ss12");
let stylisticSet13: tag = customTag("ss13");
let stylisticSet14: tag = customTag("ss14");
let stylisticSet15: tag = customTag("ss15");
let stylisticSet16: tag = customTag("ss16");
let stylisticSet17: tag = customTag("ss17");
let stylisticSet18: tag = customTag("ss18");
let stylisticSet19: tag = customTag("ss19");
let stylisticSet20: tag = customTag("ss20");
let mathScriptStyleAlternates: tag = customTag("ssty");
let stretchingGlyphDecomposition: tag = customTag("stch");
let subscript: tag = customTag("subs");
let superscript: tag = customTag("sups");
let swash: tag = customTag("swsh");
let titling: tag = customTag("titl");
let trailingJamoForms: tag = customTag("tjmo");
let traditionalNameForms: tag = customTag("tnam");
let tabularFigures: tag = customTag("tnum");
let traditionalForms: tag = customTag("trad");
let thirdWidths: tag = customTag("twid");
let unicase: tag = customTag("unic");
let alternateVerticalMetrics: tag = customTag("valt");
let vattuVariants: tag = customTag("vatu");
let verticalWriting: tag = customTag("vert");
let alternateVerticalHalfMetrics: tag = customTag("vhal");
let vowelJamoForms: tag = customTag("vjmo");
let verticalKanaAlternates: tag = customTag("vkna");
let verticalKerning: tag = customTag("vkrn");
let proportionalAlternateVerticalMetrics: tag = customTag("vpal");
let verticalAlternatesAndRotation: tag = customTag("vrt2");
let verticalAlternatesForRotation: tag = customTag("vrtr");
let slashedZero: tag = customTag("zero");
let characterValue1: tag = customTag("cv01");
let characterValue2: tag = customTag("cv02");
let characterValue3: tag = customTag("cv03");
let characterValue4: tag = customTag("cv04");
let characterValue5: tag = customTag("cv05");
let characterValue6: tag = customTag("cv06");
let characterValue7: tag = customTag("cv07");
let characterValue8: tag = customTag("cv08");
let characterValue9: tag = customTag("cv09");
let characterValue10: tag = customTag("cv10");
let characterValue11: tag = customTag("cv11");
let characterValue12: tag = customTag("cv12");
let characterValue13: tag = customTag("cv13");
let characterValue14: tag = customTag("cv14");
let characterValue15: tag = customTag("cv15");
let characterValue16: tag = customTag("cv16");
let characterValue17: tag = customTag("cv17");
let characterValue18: tag = customTag("cv18");
let characterValue19: tag = customTag("cv19");
let characterValue20: tag = customTag("cv20");
let characterValue21: tag = customTag("cv21");
let characterValue22: tag = customTag("cv22");
let characterValue23: tag = customTag("cv23");
let characterValue24: tag = customTag("cv24");
let characterValue25: tag = customTag("cv25");
let characterValue26: tag = customTag("cv26");
let characterValue27: tag = customTag("cv27");
let characterValue28: tag = customTag("cv28");
let characterValue29: tag = customTag("cv29");
let characterValue30: tag = customTag("cv30");
let characterValue31: tag = customTag("cv31");
let characterValue32: tag = customTag("cv32");
let characterValue33: tag = customTag("cv33");
let characterValue34: tag = customTag("cv34");
let characterValue35: tag = customTag("cv35");
let characterValue36: tag = customTag("cv36");
let characterValue37: tag = customTag("cv37");
let characterValue38: tag = customTag("cv38");
let characterValue39: tag = customTag("cv39");
let characterValue40: tag = customTag("cv40");
let characterValue41: tag = customTag("cv41");
let characterValue42: tag = customTag("cv42");
let characterValue43: tag = customTag("cv43");
let characterValue44: tag = customTag("cv44");
let characterValue45: tag = customTag("cv45");
let characterValue46: tag = customTag("cv46");
let characterValue47: tag = customTag("cv47");
let characterValue48: tag = customTag("cv48");
let characterValue49: tag = customTag("cv49");
let characterValue50: tag = customTag("cv50");
let characterValue51: tag = customTag("cv51");
let characterValue52: tag = customTag("cv52");
let characterValue53: tag = customTag("cv53");
let characterValue54: tag = customTag("cv54");
let characterValue55: tag = customTag("cv55");
let characterValue56: tag = customTag("cv56");
let characterValue57: tag = customTag("cv57");
let characterValue58: tag = customTag("cv58");
let characterValue59: tag = customTag("cv59");
let characterValue60: tag = customTag("cv60");
let characterValue61: tag = customTag("cv61");
let characterValue62: tag = customTag("cv62");
let characterValue63: tag = customTag("cv63");
let characterValue64: tag = customTag("cv64");
let characterValue65: tag = customTag("cv65");
let characterValue66: tag = customTag("cv66");
let characterValue67: tag = customTag("cv67");
let characterValue68: tag = customTag("cv68");
let characterValue69: tag = customTag("cv69");
let characterValue70: tag = customTag("cv70");
let characterValue71: tag = customTag("cv71");
let characterValue72: tag = customTag("cv72");
let characterValue73: tag = customTag("cv73");
let characterValue74: tag = customTag("cv74");
let characterValue75: tag = customTag("cv75");
let characterValue76: tag = customTag("cv76");
let characterValue77: tag = customTag("cv77");
let characterValue78: tag = customTag("cv78");
let characterValue79: tag = customTag("cv79");
let characterValue80: tag = customTag("cv80");
let characterValue81: tag = customTag("cv81");
let characterValue82: tag = customTag("cv82");
let characterValue83: tag = customTag("cv83");
let characterValue84: tag = customTag("cv84");
let characterValue85: tag = customTag("cv85");
let characterValue86: tag = customTag("cv86");
let characterValue87: tag = customTag("cv87");
let characterValue88: tag = customTag("cv88");
let characterValue89: tag = customTag("cv89");
let characterValue90: tag = customTag("cv90");
let characterValue91: tag = customTag("cv91");
let characterValue92: tag = customTag("cv92");
let characterValue93: tag = customTag("cv93");
let characterValue94: tag = customTag("cv94");
let characterValue95: tag = customTag("cv95");
let characterValue96: tag = customTag("cv96");
let characterValue97: tag = customTag("cv97");
let characterValue98: tag = customTag("cv98");
let characterValue99: tag = customTag("cv99");
================================================
FILE: src/Font/Features.rei
================================================
open Feature;
let accessAllAlternates: tag;
let aboveBaseForms: tag;
let aboveBaseMarkPositioning: tag;
let aboveBaseSubstitutions: tag;
let alternativeFractions: tag;
let akhands: tag;
let belowBaseForms: tag;
let belowBaseMarkPositioning: tag;
let belowBaseSubstitutions: tag;
let contextualAlternates: tag;
let caseSensitiveForms: tag;
let glyphComposition: tag;
let conjunctFormAfterRo: tag;
let conjunctForms: tag;
let contextualLigatures: tag;
let centeredCjkPunctuation: tag;
let capitalSpacing: tag;
let contextualSwash: tag;
let cursivePositioning: tag;
let petiteCapitalsFromCapitals: tag;
let smallCapitalsFromCapitals: tag;
let distances: tag;
let discretionaryLigatures: tag;
let denominators: tag;
let dotlessForms: tag;
let expertForms: tag;
let finalGlyphOnLineAlternates: tag;
let terminalForms2: tag;
let terminalForms3: tag;
let terminalForms: tag;
let flattenedAccentForms: tag;
let fractions: tag;
let fullWidths: tag;
let halfForms: tag;
let halantForms: tag;
let alternateHalfWidths: tag;
let historicalForms: tag;
let horizontalKanaAlternates: tag;
let historicalLigatures: tag;
let hangul: tag;
let hojoKanjiForms: tag;
let halfWidths: tag;
let initialForms: tag;
let isolatedForms: tag;
let italics: tag;
let justificationAlternates: tag;
let jis78Forms: tag;
let jis83Forms: tag;
let jis90Forms: tag;
let jis2004Forms: tag;
let kerning: tag;
let leftBounds: tag;
let standardLigatures: tag;
let leadingJamoForms: tag;
let liningFigures: tag;
let localizedForms: tag;
let leftToRightAlternates: tag;
let leftToRightMirroredForms: tag;
let markPositioning: tag;
let medialForms2: tag;
let medialForms: tag;
let mathematicalGreek: tag;
let markToMarkPositioning: tag;
let markPositioningViaSubstitution: tag;
let alternateAnnotationForms: tag;
let nlcKanjiForms: tag;
let nuktaForms: tag;
let numerators: tag;
let oldstyleFigures: tag;
let opticalBounds: tag;
let ordinals: tag;
let ornaments: tag;
let proportionalAlternateWidths: tag;
let petiteCapitals: tag;
let proportionalKana: tag;
let proportionalFigures: tag;
let preBaseForms: tag;
let preBaseSubstitutions: tag;
let postBaseForms: tag;
let postBaseSubstitutions: tag;
let proportionalWidths: tag;
let quarterWidths: tag;
let randomize: tag;
let requiredContextualAlternates: tag;
let rakarForms: tag;
let requiredLigatures: tag;
let rephForms: tag;
let rightBounds: tag;
let rightToLeftAlternates: tag;
let rightToLeftMirroredForms: tag;
let rubyNotationForms: tag;
let requiredVariationAlternates: tag;
let stylisticAlternates: tag;
let scientificInferiors: tag;
let opticalSize: tag;
let smallCapitals: tag;
let simplifiedForms: tag;
let stylisticSet1: tag;
let stylisticSet2: tag;
let stylisticSet3: tag;
let stylisticSet4: tag;
let stylisticSet5: tag;
let stylisticSet6: tag;
let stylisticSet7: tag;
let stylisticSet8: tag;
let stylisticSet9: tag;
let stylisticSet10: tag;
let stylisticSet11: tag;
let stylisticSet12: tag;
let stylisticSet13: tag;
let stylisticSet14: tag;
let stylisticSet15: tag;
let stylisticSet16: tag;
let stylisticSet17: tag;
let stylisticSet18: tag;
let stylisticSet19: tag;
let stylisticSet20: tag;
let mathScriptStyleAlternates: tag;
let stretchingGlyphDecomposition: tag;
let subscript: tag;
let superscript: tag;
let swash: tag;
let titling: tag;
let trailingJamoForms: tag;
let traditionalNameForms: tag;
let tabularFigures: tag;
let traditionalForms: tag;
let thirdWidths: tag;
let unicase: tag;
let alternateVerticalMetrics: tag;
let vattuVariants: tag;
let verticalWriting: tag;
let alternateVerticalHalfMetrics: tag;
let vowelJamoForms: tag;
let verticalKanaAlternates: tag;
let verticalKerning: tag;
let proportionalAlternateVerticalMetrics: tag;
let verticalAlternatesAndRotation: tag;
let verticalAlternatesForRotation: tag;
let slashedZero: tag;
let characterValue1: tag;
let characterValue2: tag;
let characterValue3: tag;
let characterValue4: tag;
let characterValue5: tag;
let characterValue6: tag;
let characterValue7: tag;
let characterValue8: tag;
let characterValue9: tag;
let characterValue10: tag;
let characterValue11: tag;
let characterValue12: tag;
let characterValue13: tag;
let characterValue14: tag;
let characterValue15: tag;
let characterValue16: tag;
let characterValue17: tag;
let characterValue18: tag;
let characterValue19: tag;
let characterValue20: tag;
let characterValue21: tag;
let characterValue22: tag;
let characterValue23: tag;
let characterValue24: tag;
let characterValue25: tag;
let characterValue26: tag;
let characterValue27: tag;
let characterValue28: tag;
let characterValue29: tag;
let characterValue30: tag;
let characterValue31: tag;
let characterValue32: tag;
let characterValue33: tag;
let characterValue34: tag;
let characterValue35: tag;
let characterValue36: tag;
let characterValue37: tag;
let characterValue38: tag;
let characterValue39: tag;
let characterValue40: tag;
let characterValue41: tag;
let characterValue42: tag;
let characterValue43: tag;
let characterValue44: tag;
let characterValue45: tag;
let characterValue46: tag;
let characterValue47: tag;
let characterValue48: tag;
let characterValue49: tag;
let characterValue50: tag;
let characterValue51: tag;
let characterValue52: tag;
let characterValue53: tag;
let characterValue54: tag;
let characterValue55: tag;
let characterValue56: tag;
let characterValue57: tag;
let characterValue58: tag;
let characterValue59: tag;
let characterValue60: tag;
let characterValue61: tag;
let characterValue62: tag;
let characterValue63: tag;
let characterValue64: tag;
let characterValue65: tag;
let characterValue66: tag;
let characterValue67: tag;
let characterValue68: tag;
let characterValue69: tag;
let characterValue70: tag;
let characterValue71: tag;
let characterValue72: tag;
let characterValue73: tag;
let characterValue74: tag;
let characterValue75: tag;
let characterValue76: tag;
let characterValue77: tag;
let characterValue78: tag;
let characterValue79: tag;
let characterValue80: tag;
let characterValue81: tag;
let characterValue82: tag;
let characterValue83: tag;
let characterValue84: tag;
let characterValue85: tag;
let characterValue86: tag;
let characterValue87: tag;
let characterValue88: tag;
let characterValue89: tag;
let characterValue90: tag;
let characterValue91: tag;
let characterValue92: tag;
let characterValue93: tag;
let characterValue94: tag;
let characterValue95: tag;
let characterValue96: tag;
let characterValue97: tag;
let characterValue98: tag;
let characterValue99: tag;
================================================
FILE: src/Font/FontCache.re
================================================
open Revery_Core;
module Log = (val Revery_Core.Log.withNamespace("Revery.FontCache"));
module StringFeaturesHashable = {
type t = (string, list(Feature.t));
let equal = ((str1, features1), (str2, features2)) =>
String.equal(str1, str2) && features1 == features2;
let hash = Hashtbl.hash;
};
module SkiaTypefaceHashable = {
type t = option(Skia.Typeface.t);
let equal =
Option.equal((tf1, tf2) =>
Skia.Typeface.getUniqueID(tf1) == Skia.Typeface.getUniqueID(tf2)
);
let hash = maybeTypeface =>
switch (maybeTypeface) {
| Some(tf) => Skia.Typeface.getUniqueID(tf) |> Int32.to_int
| None => 0
};
};
module UcharHashable = {
type t = Uchar.t;
let equal = Uchar.equal;
let hash = Uchar.hash;
};
module FloatHashable = {
type t = float;
let equal = Float.equal;
let hash = Float.hash;
};
module StringHash =
Hashtbl.Make({
type t = string;
let equal = String.equal;
let hash = Hashtbl.hash;
});
type fontLoaded = Event.t(unit);
let onFontLoaded: fontLoaded = Event.create();
module MetricsWeighted = {
type t = FontMetrics.t;
let weight = _ => 1;
};
module ShapeResultWeighted = {
type t = ShapeResult.t;
let weight = ShapeResult.size;
};
module FallbackWeighted = {
type t = list(ShapeResult.shapeNode);
let weight = _ => 1;
};
module SkiaTypefaceWeighted = {
type t = option(Skia.Typeface.t);
let weight = _ => 1;
};
module MetricsCache = Lru.M.Make(FloatHashable, MetricsWeighted);
module ShapeResultCache =
Lru.M.Make(StringFeaturesHashable, ShapeResultWeighted);
module FallbackCache = Lru.M.Make(StringFeaturesHashable, FallbackWeighted);
module FallbackCharacterCache =
Lru.M.Make(UcharHashable, SkiaTypefaceWeighted);
type t = {
hbFace: Harfbuzz.hb_face,
skiaFace: Skia.Typeface.t,
metricsCache: MetricsCache.t,
shapeCache: ShapeResultCache.t,
fallbackCache: FallbackCache.t,
fallbackCharacterCache: FallbackCharacterCache.t,
};
module FontWeight = {
type font = t;
type t = result(font, string);
let weight = _ => 1;
};
module FontCache = Lru.M.Make(SkiaTypefaceHashable, FontWeight);
module Internal = {
let cache = FontCache.create(~initialSize=8, 64);
};
module Constants = {
let unresolvedGlyphID = 0;
};
let skiaFaceToHarfbuzzFace = skiaFace => {
let stream = Skia.Typeface.toStream(skiaFace);
let length = Skia.Stream.getLength(stream);
let data = Skia.Data.makeFromStream(stream, length);
let bytes = Skia.Data.makeString(data);
Harfbuzz.hb_face_from_data(bytes);
};
let load: option(Skia.Typeface.t) => result(t, string) =
(skiaTypeface: option(Skia.Typeface.t)) => {
switch (FontCache.find(skiaTypeface, Internal.cache)) {
| Some(v) =>
FontCache.promote(skiaTypeface, Internal.cache);
v;
| None =>
let harfbuzzFace = skiaTypeface |> Option.map(skiaFaceToHarfbuzzFace);
let metricsCache = MetricsCache.create(~initialSize=8, 64);
let shapeCache = ShapeResultCache.create(~initialSize=1024, 128 * 1024);
let fallbackCache = FallbackCache.create(~initialSize=1024, 128 * 1024);
let fallbackCharacterCache =
FallbackCharacterCache.create(~initialSize=1024, 128 * 1024);
let ret =
switch (skiaTypeface, harfbuzzFace) {
| (Some(skiaFace), Some(Ok(hbFace))) =>
Event.dispatch(onFontLoaded, ());
Log.infof(m =>
m("Loaded: %s", Skia.Typeface.getFamilyName(skiaFace))
);
Ok({
hbFace,
skiaFace,
metricsCache,
shapeCache,
fallbackCache,
fallbackCharacterCache,
});
| (_, Some(Error(msg))) =>
Log.warn("Error loading typeface: " ++ msg);
Error("Error loading typeface: " ++ msg);
| (None, _) =>
Log.warn("Error loading typeface (skia)");
Error("Error loading typeface.");
| (_, None) =>
Log.warn("Error loading typeface (harfbuzz)");
Error("Error loading typeface");
};
FontCache.add(skiaTypeface, ret, Internal.cache);
FontCache.trim(Internal.cache);
ret;
};
};
let getMetrics: (t, float) => FontMetrics.t =
({skiaFace, metricsCache, _}, size) => {
switch (MetricsCache.find(size, metricsCache)) {
| Some(v) =>
MetricsCache.promote(size, metricsCache);
v;
| None =>
let paint = Skia.Paint.make();
Skia.Paint.setTypeface(paint, skiaFace);
Skia.Paint.setTextSize(paint, size);
let metrics = Skia.FontMetrics.make();
let lineHeight = Skia.Paint.getFontMetrics(paint, metrics, 1.0);
let ret = FontMetrics.ofSkia(size, lineHeight, metrics);
MetricsCache.add(size, ret, metricsCache);
MetricsCache.trim(metricsCache);
ret;
};
};
let getSkiaTypeface: t => Skia.Typeface.t = font => font.skiaFace;
/* [Fallback.strategy] encapsulates the logic for discovering a font, based on a character [Uchar.t] */
module Fallback = {
type strategy = Uchar.t => option(Skia.Typeface.t);
let none = _uchar => None;
let constant = (typeface, _uchar) => Some(typeface);
let skia = ({skiaFace, fallbackCharacterCache, _}: t, uchar) => {
switch (FallbackCharacterCache.find(uchar, fallbackCharacterCache)) {
| Some(maybeTypeface) =>
FallbackCharacterCache.promote(uchar, fallbackCharacterCache);
maybeTypeface;
| None =>
let familyName = skiaFace |> Skia.Typeface.getFamilyName;
let maybeTypeface =
Skia.FontManager.matchFamilyStyleCharacter(
FontManager.instance,
familyName,
skiaFace |> Skia.Typeface.getFontStyle,
[Environment.userLocale],
uchar,
);
Log.debugf(m =>
m(
"Unresolved glyph: character : U+%04X font: %s",
Uchar.to_int(uchar),
familyName,
)
);
FallbackCharacterCache.add(
uchar,
maybeTypeface,
fallbackCharacterCache,
);
FallbackCharacterCache.trim(fallbackCharacterCache);
maybeTypeface;
};
};
let custom = (f: strategy) => f;
};
let generateShapes:
(~fallback: Fallback.strategy, ~features: list(Feature.t), t, string) =>
list(ShapeResult.shapeNode) =
(~fallback, ~features, font, str) => {
let fallbackFor = (~byteOffset, str) => {
Log.debugf(m =>
m(
"Resolving fallback for: %s at byte offset %d - source font is: %s",
str,
byteOffset,
font.skiaFace |> Skia.Typeface.getFamilyName,
)
);
let maybeUchar =
try(Some(Zed_utf8.extract(str, byteOffset))) {
| exn =>
Log.debugf(m =>
m("Unable to get uchar from string: %s", Printexc.to_string(exn))
);
None;
};
Option.bind(maybeUchar, uchar
// Only fallback if the character is non-ASCII (UTF-8)
=>
if (Uchar.to_int(uchar) > 256) {
fallback(uchar);
} else {
None;
}
)
|> (
fun
| Some(_) as font => {
load(font);
}
| None => {
Error("No fallback font found");
}
);
};
/* A hole is a space in a string where the current font
can't render the text. For instance, most standard fonts
don't include emojis, and Latin fonts often don't include
CJK characters. This module contains functions that
relate to the creation and resolution of these "holes" */
let rec resolveHole = (~attempts, ~acc, ~start, ~stop) =>
if (start >= stop) {
acc;
} else {
switch (fallbackFor(~byteOffset=start, str)) {
| Ok(fallbackFont)
when Skia.Typeface.equal(fallbackFont.skiaFace, font.skiaFace) =>
resolveHole(
~acc=[
ShapeResult.{
hbFace: font.hbFace,
skiaFace: font.skiaFace,
glyphId: Constants.unresolvedGlyphID,
cluster: start,
},
...acc,
],
~start=start + 1,
~stop,
~attempts=0 // Reset attempts, because we've moved to the next character
)
| Error(_) =>
// Just because we can't find a font for this character doesn't mean
// the rest of the hole can't be resolved. Here we insert the "unknown"
// glyph and try to resolve the rest of the string.
resolveHole(
~acc=[
ShapeResult.{
hbFace: font.hbFace,
skiaFace: font.skiaFace,
glyphId: Constants.unresolvedGlyphID,
cluster: start,
},
...acc,
],
~start=start + 1,
~stop,
~attempts=0 // Reset attempts, becaused we've moved to the next character
)
| Ok(fallbackFont) =>
Log.debugf(m =>
m(
"Got fallback font - id: %d name: %s (from source font - id: %d %s) - attempt %d",
fallbackFont.skiaFace
|> Skia.Typeface.getUniqueID
|> Int32.to_int,
fallbackFont.skiaFace |> Skia.Typeface.getFamilyName,
font.skiaFace |> Skia.Typeface.getUniqueID |> Int32.to_int,
font.skiaFace |> Skia.Typeface.getFamilyName,
attempts,
)
);
// We found a fallback font! Now we just have to shape it the same way
// we shape the super-string.
loop(~attempts=attempts + 1, ~start, ~stop, ~acc, fallbackFont);
};
}
and loopShapes =
(
~attempts,
~stopCluster,
~acc,
~holeStart=?,
~index,
{hbFace, skiaFace, _} as font,
shapes,
) => {
let resolvePossibleHole = (~stop) => {
switch (holeStart) {
| Some(start) => resolveHole(~attempts, ~acc, ~start, ~stop)
| None => acc
};
};
if (index == Array.length(shapes)) {
resolvePossibleHole(~stop=stopCluster);
} else {
let Harfbuzz.{glyphId, cluster} = shapes[index];
// If we have an unknown glyph (part of a hole), extend
// the current hole to encapsulate it. We cannot resolve unresolved
// glyphs individually since a character can span several code points,
// and an unresolved glyph only represents a single code point.
if (glyphId == Constants.unresolvedGlyphID) {
let holeStart = Option.value(holeStart, ~default=cluster);
loopShapes(
~attempts,
~stopCluster,
~acc,
~holeStart,
~index=index + 1,
font,
shapes,
);
} else {
// Otherwise resolve any hole the preceded this one and add the
// current glyph to the list.
let acc = resolvePossibleHole(~stop=cluster);
let acc = [
ShapeResult.{hbFace, skiaFace, glyphId, cluster},
...acc,
];
loopShapes(
// TODO: Bring back fix! Just see if we can repro
~attempts,
~stopCluster,
~acc,
~index=index + 1,
font,
shapes,
);
};
};
}
and loop = (~attempts, ~acc, ~start, ~stop, font) =>
// This [attempts] counter is used to 'circuit-break' - verify
// we don't end up in an infinite loop. If we've tried multiple times
// to fallback, give up, use the unresolved glyph ID, and move on.
if (attempts >= 3) {
loop(
~attempts=0,
~acc=[
ShapeResult.{
hbFace: font.hbFace,
skiaFace: font.skiaFace,
glyphId: Constants.unresolvedGlyphID,
cluster: start,
},
...acc,
],
~start=start + 1,
~stop,
font,
);
} else {
Harfbuzz.hb_shape(
~features,
~start=`Position(start),
~stop=`Position(stop),
font.hbFace,
str,
)
|> loopShapes(~attempts, ~stopCluster=stop, ~acc, ~index=0, font);
};
loop(~attempts=0, ~start=0, ~stop=String.length(str), ~acc=[], font);
};
let shape:
(~fallback: Fallback.strategy=?, ~features: list(Feature.t)=?, t, string) =>
ShapeResult.t =
(~fallback=?, ~features=[], {shapeCache, _} as font, str) => {
// Default to skia fallback strategy
let fallbackToUse =
switch (fallback) {
| None => Fallback.skia(font)
| Some(fallbackStrategy) => fallbackStrategy
};
switch (ShapeResultCache.find((str, features), shapeCache)) {
| Some(result) =>
ShapeResultCache.promote((str, features), shapeCache);
result;
| None =>
let result =
generateShapes(~fallback=fallbackToUse, ~features, font, str)
|> ShapeResult.ofHarfbuzz;
ShapeResultCache.add((str, features), result, shapeCache);
ShapeResultCache.trim(shapeCache);
result;
};
};
================================================
FILE: src/Font/FontCache.rei
================================================
type t;
module Fallback: {
/* [strategy] encapsulates the fallback logic for discovering a font, based on a character [Uchar.t] */
type strategy;
// [none] never falls back to a font (always fails)
let none: strategy;
// [constant(typeface)] always falls back to [typeface]
let constant: Skia.Typeface.t => strategy;
// [skia(font)] uses skia's [matchFamilyStyleCharacter] API to fall-back
let skia: t => strategy;
// [custom] provides a custom matching strategy
let custom: (Uchar.t => option(Skia.Typeface.t)) => strategy;
};
let load: option(Skia.Typeface.t) => result(t, string);
let getMetrics: (t, float) => FontMetrics.t;
let getSkiaTypeface: t => Skia.Typeface.t;
let shape:
(~fallback: Fallback.strategy=?, ~features: list(Feature.t)=?, t, string) =>
ShapeResult.t;
let onFontLoaded: Revery_Core.Event.t(unit);
================================================
FILE: src/Font/FontFamily.re
================================================
type t = (~italic: bool, FontWeight.t) => option(Skia.Typeface.t);
module FontFamilyHashable = {
type t = {
familyName: string,
weight: FontWeight.t,
italic: bool,
};
let equal = (a, b) =>
String.equal(a.familyName, b.familyName)
&& a.weight == b.weight
&& a.italic == b.italic;
let hash = Hashtbl.hash;
};
module FontDescriptorWeight = {
type t = option(Skia.Typeface.t);
let weight = _ => 1;
};
module FontFamilyCache = Lru.M.Make(FontFamilyHashable, FontDescriptorWeight);
let cache = FontFamilyCache.create(~initialSize=8, 64);
let system = (familyName): t =>
(~italic, weight) => {
let fontDescr: FontFamilyHashable.t = {familyName, weight, italic};
switch (FontFamilyCache.find(fontDescr, cache)) {
| Some(fd) =>
FontFamilyCache.promote(fontDescr, cache);
fd;
| None =>
let fd = Discovery.find(~weight, ~italic, familyName);
FontFamilyCache.add(fontDescr, fd, cache);
FontFamilyCache.trim(cache);
fd;
};
};
let fromFiles =
(
solver: (~weight: FontWeight.t, ~italic: bool) => string,
~italic,
weight,
) => {
let familyName = solver(~italic, ~weight);
let fontDescr: FontFamilyHashable.t = {familyName, weight, italic};
switch (FontFamilyCache.find(fontDescr, cache)) {
| Some(tf) =>
FontFamilyCache.promote(fontDescr, cache);
tf;
| None =>
let assetPath = Revery_Core.Environment.getAssetPath(familyName);
let tf = Skia.Typeface.makeFromFile(assetPath, 0);
FontFamilyCache.add(fontDescr, tf, cache);
FontFamilyCache.trim(cache);
tf;
};
};
let fromFile = (fileName, ~italic as _, _) => {
let fontDescr: FontFamilyHashable.t = {
familyName: fileName,
weight: FontWeight.Normal,
italic: false,
};
switch (FontFamilyCache.find(fontDescr, cache)) {
| Some(tf) =>
FontFamilyCache.promote(fontDescr, cache);
tf;
| None =>
let assetPath = Revery_Core.Environment.getAssetPath(fileName);
let tf = Skia.Typeface.makeFromFile(assetPath, 0);
FontFamilyCache.add(fontDescr, tf, cache);
FontFamilyCache.trim(cache);
tf;
};
};
let default =
switch (Revery_Core.Environment.os) {
| Linux(_) => system("Liberation Sans")
| Mac(_) => system("System Font")
| _ => system("Arial")
};
let defaultMono =
switch (Revery_Core.Environment.os) {
| Mac(_) => system("Menlo")
| Windows(_) => system("Consolas")
| _ => fromFile("Inconsolata.otf")
};
let defaultSerif =
switch (Revery_Core.Environment.os) {
| Mac(_) => system("Palatino")
| Linux(_) => system("Liberation Serif")
| _ => system("Times New Roman")
};
let resolve = (~italic=false, weight, solver) =>
solver(~italic, weight) |> FontCache.load;
let toSkia = (~italic=false, weight, solver) => solver(~italic, weight);
================================================
FILE: src/Font/FontFamily.rei
================================================
type t;
let default: t;
let defaultMono: t;
let defaultSerif: t;
let fromFiles: ((~weight: FontWeight.t, ~italic: bool) => string) => t;
let fromFile: string => t;
let system: string => t;
let resolve:
(~italic: bool=?, FontWeight.t, t) => result(FontCache.t, string);
let toSkia: (~italic: bool=?, FontWeight.t, t) => option(Skia.Typeface.t);
================================================
FILE: src/Font/FontManager.re
================================================
/* FontManager.re
This module is so that multiple other modules can use the same font
manager without creating circular imports */
let instance = Skia.FontManager.makeDefault();
================================================
FILE: src/Font/FontMetrics.re
================================================
type t = {
height: float,
lineHeight: float,
ascent: float,
descent: float,
underlinePosition: float,
underlineThickness: float,
avgCharWidth: float,
maxCharWidth: float,
};
let empty = (size: float) => {
height: size,
lineHeight: size,
ascent: 0.,
descent: 0.,
underlinePosition: 0.,
underlineThickness: 0.,
maxCharWidth: 0.,
avgCharWidth: 0.,
};
let ofSkia = (size: float, lineHeight: float, metrics: Skia.FontMetrics.t) => {
let ascent = Skia.FontMetrics.getAscent(metrics);
let descent = Skia.FontMetrics.getDescent(metrics);
let underlinePosition = Skia.FontMetrics.getUnderlinePosition(metrics);
let underlineThickness = Skia.FontMetrics.getUnderlineThickness(metrics);
let maxCharWidth = Skia.FontMetrics.getMaxCharacterWidth(metrics);
let avgCharWidth = Skia.FontMetrics.getAvgCharacterWidth(metrics);
{
height: size,
lineHeight,
ascent,
descent,
underlinePosition,
underlineThickness,
maxCharWidth,
avgCharWidth,
};
};
================================================
FILE: src/Font/FontRenderer.re
================================================
type measureResult = {
width: float,
height: float,
};
let measure = {
let paint = Skia.Paint.make();
Skia.Paint.setTextEncoding(paint, GlyphId);
(~smoothing: Smoothing.t, ~features=[], font, size, text: string) => {
let {height, _}: FontMetrics.t = FontCache.getMetrics(font, size);
let glyphStrings =
text |> FontCache.shape(~features, font) |> ShapeResult.getGlyphStrings;
Smoothing.setPaint(~smoothing, paint);
Skia.Paint.setTextSize(paint, size);
let width =
glyphStrings
|> List.fold_left(
(acc, (skiaFace, str)) => {
Skia.Paint.setTypeface(paint, skiaFace);
acc +. Skia.Paint.measureText(paint, str, None);
},
0.,
);
{height, width};
};
};
================================================
FILE: src/Font/FontWeight.re
================================================
type t =
| Undefined
| Thin
| UltraLight
| Light
| Normal
| Medium
| SemiBold
| Bold
| UltraBold
| Heavy;
let toInt = (v: t) => {
switch (v) {
| Undefined => 0
| Thin => 100
| UltraLight => 200
| Light => 300
| Normal => 400
| Medium => 500
| SemiBold => 600
| Bold => 700
| UltraBold => 800
| Heavy => 900
};
};
let ofInt = (v: int) => {
switch (v) {
| 100 => Thin
| 200 => UltraLight
| 300 => Light
| 400 => Normal
| 500 => Medium
| 600 => SemiBold
| 700 => Bold
| 800 => UltraBold
| 900 => Heavy
| _ => Undefined
};
};
let show = (v: t) => {
switch (v) {
| Undefined => "Undefined"
| Thin => "Thin"
| UltraLight => "UltraLight"
| Light => "Light"
| Normal => "Normal"
| Medium => "Medium"
| SemiBold => "SemiBold"
| Bold => "Bold"
| UltraBold => "UltraBold"
| Heavy => "Heavy"
};
};
================================================
FILE: src/Font/FontWidth.re
================================================
type t =
| Undefined
| UltraCondensed
| ExtraCondensed
| Condensed
| SemiCondensed
| Normal
| SemiExpanded
| Expanded
| ExtraExpanded
| UltraExpanded;
let toInt = (v: t) => {
switch (v) {
| Undefined => 0
| UltraCondensed => 1
| ExtraCondensed => 2
| Condensed => 3
| SemiCondensed => 4
| Normal => 5
| SemiExpanded => 6
| Expanded => 7
| ExtraExpanded => 8
| UltraExpanded => 9
};
};
let ofInt = (v: int) => {
switch (v) {
| 1 => UltraCondensed
| 2 => ExtraCondensed
| 3 => Condensed
| 4 => SemiCondensed
| 5 => Normal
| 6 => SemiExpanded
| 7 => Expanded
| 8 => ExtraExpanded
| 9 => UltraExpanded
| _ => Undefined
};
};
let show = (v: t) => {
switch (v) {
| Undefined => "Undefined"
| UltraCondensed => "UltraCondensed"
| ExtraCondensed => "ExtraCondensed"
| Condensed => "Condensed"
| SemiCondensed => "SemiCondensed"
| Normal => "Normal"
| SemiExpanded => "SemiExpanded"
| Expanded => "Expanded"
| ExtraExpanded => "ExtraExpanded"
| UltraExpanded => "UltraExpanded"
};
};
================================================
FILE: src/Font/Revery_Font.re
================================================
/**
Revery_Font.re
Module exposing font-related functionality, like:
- Discovering fonts
- Loading fonts
*/
module Weight = FontWeight;
module Width = FontWidth;
module FontMetrics = FontMetrics;
module FontCache = FontCache;
module FontRenderer = FontRenderer;
module ShapeResult = ShapeResult;
module Smoothing = Smoothing;
module Family = FontFamily;
module Feature = Feature;
module Features = Features;
type t = FontCache.t;
type measureResult = FontRenderer.measureResult;
let load = FontCache.load;
let getMetrics = FontCache.getMetrics;
let getSkiaTypeface = FontCache.getSkiaTypeface;
let measure = FontRenderer.measure;
let shape = FontCache.shape;
module Discovery = {
type t = option(Skia.Typeface.t);
let find = Discovery.find;
};
================================================
FILE: src/Font/ShapeResult.re
================================================
type shapeNode = {
hbFace: Harfbuzz.hb_face,
skiaFace: Skia.Typeface.t,
glyphId: int,
cluster: int,
};
type t = {glyphStrings: list((Skia.Typeface.t, string))};
let size = ({glyphStrings, _}) =>
glyphStrings
|> List.fold_left((acc, (_, str)) => acc + String.length(str), 0);
let bitsFromGlyph = glyphId => {
let lowBit = glyphId land 255 |> Char.chr |> String.make(1);
let highBit = (glyphId land 255 lsl 8) lsr 8 |> Char.chr |> String.make(1);
(lowBit, highBit);
};
let ofHarfbuzz: list(shapeNode) => t =
nodes => {
let rec loop = (~nodes, ~strList, ~maybeTypeface) =>
switch (nodes, maybeTypeface) {
| ([], Some(typeface)) => [(typeface, String.concat("", strList))]
| ([], None) => []
| ([{skiaFace, glyphId, _}, ...rest], Some(skFace)) =>
let (lowBit, highBit) = bitsFromGlyph(glyphId);
if (skFace === skiaFace) {
let strList = [lowBit, highBit, ...strList];
loop(~nodes=rest, ~strList, ~maybeTypeface=Some(skFace));
} else {
let newStrList = [lowBit, highBit];
[
(skFace, String.concat("", strList)),
...loop(
~nodes=rest,
~strList=newStrList,
~maybeTypeface=Some(skiaFace),
),
];
};
| ([{skiaFace, glyphId, _}, ...rest], None) =>
let (lowBit, highBit) = bitsFromGlyph(glyphId);
let newStrList = [lowBit, highBit];
loop(
~nodes=rest,
~strList=newStrList,
~maybeTypeface=Some(skiaFace),
);
};
let glyphStrings =
loop(~nodes, ~strList=[], ~maybeTypeface=None) |> List.rev;
{glyphStrings: glyphStrings};
};
let getGlyphStrings = v => v.glyphStrings;
================================================
FILE: src/Font/Smoothing.re
================================================
type t =
| None
| Antialiased
| SubpixelAntialiased;
// Default to subpixel-antialiased, as it has the most reliable
// scaling characteristics - see Onivim 2 bugs:
// - https://github.com/onivim/oni2/issues/1475
// - https://github.com/onivim/oni2/issues/1592
let default = SubpixelAntialiased;
let setPaint = (~smoothing: t, paint: Skia.Paint.t) => {
switch (smoothing) {
| None =>
Skia.Paint.setAntiAlias(paint, false);
Skia.Paint.setSubpixelText(paint, false);
| Antialiased =>
Skia.Paint.setAntiAlias(paint, true);
Skia.Paint.setSubpixelText(paint, false);
| SubpixelAntialiased =>
Skia.Paint.setAntiAlias(paint, true);
Skia.Paint.setSubpixelText(paint, true);
};
};
================================================
FILE: src/Font/dune
================================================
(library
(name Revery_Font)
(public_name Revery.Font)
(preprocess
(pps lwt_ppx))
(libraries harfbuzz lru lwt lwt.unix sdl2 flex rebez.lib Revery_Core
Revery_Math Revery_Zed))
(copy_files files/*)
(install
(section bin)
(package Revery)
(files Inconsolata.otf))
================================================
FILE: src/IO/File.re
================================================
open Revery_Core;
module Log = (val Log.withNamespace("Revery.IO.File"));
let delete = filePath => {
switch (Fpath.of_string(filePath)) {
| Ok(fpath) =>
switch (Bos.OS.File.delete(~must_exist=true, fpath)) {
| Ok(_) =>
Log.info("Deleted file: " ++ filePath);
Lwt.return(Ok());
| Error(`Msg(error)) =>
Log.error("Error deleting file: " ++ error);
Lwt.return(Error(error));
}
| Error(`Msg(error)) => Lwt.return(Error(error))
};
};
let write = (~path, data) => {
switch (Fpath.of_string(path)) {
| Ok(fpath) =>
switch (Bos.OS.File.write(fpath, data)) {
| Ok(_success) =>
Log.debug("Successfully wrote data to file: " ++ path);
Lwt.return(Ok());
| Error(`Msg(error)) =>
Log.error("Error writing data to file: " ++ error);
Lwt.return(Error(error));
}
| Error(`Msg(error)) => Lwt.return(Error(error))
};
};
================================================
FILE: src/IO/File.rei
================================================
/**
* write
*
* Takes a [path] and writes [data] to that file.
*
* Examples:
* let result: Lwt.t(result(unit, string)) = File.write(~path="example.txt", "Hello World!");
*/
let write: (~path: string, string) => Lwt.t(result(unit, string));
/**
* delete
*
* Takes a [path] and tries to delete that file.
*
* Examples:
* let result: Lwt.t(result(unit, string)) = File.delete("example.txt");
*/
let delete: string => Lwt.t(result(unit, string));
================================================
FILE: src/IO/Image.re
================================================
open Revery_Core;
//open LwtLetOperators;
module Log = (val Log.withNamespace("Revery.IO.Image"));
module Internal = {
// let mediaTypeToFileExtension =
// fun
// | "image/apng" => ".apng"
// | "image/bmp" => ".bmp"
// | "image/gif" => ".gif"
// | "image/x-icon" => ".ico"
// | "image/jpeg" => ".jpg"
// | "image/jpg" => ".jpg"
// | "image/png" => ".png"
// | "image/svg+xml" => ".svg"
// | "image/tiff" => ".tif"
// | "image/webp" => ".webp"
// | _ => ".jpg";
// let textToAlphaNumeric = text =>
// Str.(global_replace(regexp("[^a-zA-Z0-9_]"), "", text));
// let createFilePath = (url, ~fileExtension) => {
// let fileNameCleaned =
// Fpath.(
// append(
// v(Environment.getTempDirectory()),
// v(textToAlphaNumeric(url)),
// )
// );
//
// let filePath = Fpath.add_ext(fileExtension, fileNameCleaned);
//
// Fpath.to_string(filePath);
// };
};
// NOTE: The reason for these different states is to hold up any
// additional requests until we've actually got a response
// from the first one
//type urlCacheItem =
// | Image(option(Skia.Image.t))
// | Pending;
//type urlCache = Hashtbl.t(string, urlCacheItem);
//let urlContextCache: urlCache = Hashtbl.create(100);
// NOTE: These could be moved elsewhere
//let fromUrl = url => {
// let cacheResult = Hashtbl.find_opt(urlContextCache, url);
//
// let.flatMap result =
// switch (cacheResult) {
// | Some(result) =>
// switch (result) {
// | Image(maybeImage) => Lwt.return(Ok(maybeImage))
// | Pending => Lwt.return(Ok(None))
// }
// | None =>
// Hashtbl.replace(urlContextCache, url, Pending);
// open Fetch;
//
// let.flatMapOk {Response.body, headers, _} = get(url);
//
// let data = Body.toString(body);
// let mediaType =
// headers
// |> List.find_opt(((k, _v)) =>
// String.lowercase_ascii(k) == "content-type"
// )
// |> Option.map(((_k, v)) => v)
// |> Option.value(~default="");
//
// let fileExtension = Internal.mediaTypeToFileExtension(mediaType);
// let fileName = Internal.createFilePath(url, ~fileExtension);
//
// let.flatMapOk _writeOk = File.write(~path=fileName, data);
// let maybeData = Skia.Data.makeFromFileName(fileName);
// let.flatMapOk _deleted = File.delete(fileName);
//
// let maybeSkiaImage =
// Option.bind(maybeData, data =>
// Skia.Image.makeFromEncoded(data, None)
// );
//
// Hashtbl.replace(urlContextCache, url, Image(maybeSkiaImage));
//
// Lwt.return(Ok(maybeSkiaImage));
// };
//
// Lwt.return(Result.value(result, ~default=None));
//};
type cache = Hashtbl.t(string, option(Skia.Image.t));
let contextCache: cache = Hashtbl.create(100);
let fromAssetPath = (imagePath: string) => {
let imagePath = Environment.getAssetPath(imagePath);
let cacheResult = Hashtbl.find_opt(contextCache, imagePath);
switch (cacheResult) {
| Some(maybeCachedImage) => maybeCachedImage
| None =>
Log.info("Loading from path: " ++ imagePath);
let maybeData = Skia.Data.makeFromFileName(imagePath);
Log.info("Got data.");
let maybeImage =
Option.bind(maybeData, data => Skia.Image.makeFromEncoded(data, None));
Hashtbl.replace(contextCache, imagePath, maybeImage);
maybeImage;
};
};
================================================
FILE: src/IO/Image.rei
================================================
/**
* fromUrl
*
* Given a network file-path this returns a promise, holding an option(Skia.Image.t)
*
* Examples:
* let maybeImage: Lwt.t(option(Skia.Image.t)) = Image.fromUrl("https://example.com/hello.png");
*/
// let fromUrl: string => Lwt.t(option(Skia.Image.t));
/**
* fromAssetPath
*
* Given a path, reads an image-file from disk, returning an option-type.
*
* Examples:
* let maybeImage: option(Skia.Image.t) = Image.fromAssetPath("example.png");
*/
let fromAssetPath: string => option(Skia.Image.t);
================================================
FILE: src/IO/LwtLetOperators.re
================================================
let (let.map) = (promise, fn) => Lwt.map(fn, promise);
let (let.mapOk) = (promise, fn) =>
Lwt.map(
fun
| Ok(response) => fn(response)
| Error(e) => e,
promise,
);
let (let.flatMap) = (promise, fn) => Lwt.bind(promise, fn);
let (let.flatMapOk) = (promise, fn) =>
Lwt.bind(
promise,
fun
| Ok(response) => fn(response)
| Error(e) => Lwt.return(Error(e)),
);
================================================
FILE: src/IO/Revery_IO.re
================================================
module LwtLetOperators = LwtLetOperators;
module Image = Image;
module File = File;
================================================
FILE: src/IO/dune
================================================
(library
(name Revery_IO)
(public_name Revery.IO)
(preprocess
(pps lwt_ppx))
(libraries bos lwt lwt.unix Revery_Core))
================================================
FILE: src/Lwt/Revery_Lwt.re
================================================
/*
* Lwt_integration.re
*
* Lwt engine loop integration
*
* The Revery 'app-model' doesn't fit into the Lwt_main.run model of execution - which
* blocks the main thread until the promise is complete.
*
* However, a slightly lower-level primitive, Lwt_engine, which Lwt_main calls,
* lets us fit the event-queue based model into Revery.
*
* This module is responsible for facilitating that integration.
*/
/*
* We're using some Lwt internals, so need to disable this warning...
*/
[@ocaml.warning "-3"];
open Revery;
let startEventLoop = () => {
let yielded = Lwt_sequence.create();
Tick.interval(
_ =>
Performance.bench("Lwt engine pump", () => {
Lwt.wakeup_paused();
Lwt_engine.iter(false);
if (!Lwt_sequence.is_empty(yielded)) {
let tmp = Lwt_sequence.create();
Lwt_sequence.transfer_r(yielded, tmp);
Lwt_sequence.iter_l(wakener => Lwt.wakeup(wakener, ()), tmp);
};
}),
Time.zero,
);
};
================================================
FILE: src/Lwt/dune
================================================
(library
(name Revery_Lwt)
(public_name Revery.lwt)
(libraries Revery))
================================================
FILE: src/Math/Angle.re
================================================
type t =
| Degrees(float)
| Radians(float);
let from_degrees = angle => Degrees(angle);
let from_radians = angle => Radians(angle);
================================================
FILE: src/Math/BoundingBox2d.re
================================================
/* BoundingBox2d */
/* Implementation of an Axis-Aligned Bounding Box */
type t = Skia.Rect.t;
let create = Skia.Rect.makeLtrb;
let getBounds = (v: t) => {
let x0 = Skia.Rect.getLeft(v);
let y0 = Skia.Rect.getTop(v);
let x1 = Skia.Rect.getRight(v);
let y1 = Skia.Rect.getBottom(v);
(x0, y0, x1, y1);
};
let equals = (b1: t, b2: t) => {
let (b1x0, b1y0, b1x1, b1y1) = getBounds(b1);
let (b2x0, b2y0, b2x1, b2y1) = getBounds(b2);
b1x0 == b2x0 && b1y0 == b2y0 && b1x1 == b2x1 && b1y1 == b2y1;
};
let toString = (v: t) => {
let (x0, y0, x1, y1) = getBounds(v);
"x0: "
++ string_of_float(x0)
++ " y0: "
++ string_of_float(y0)
++ " x1: "
++ string_of_float(x1)
++ " y1: "
++ string_of_float(y1);
};
let isPointInside = (~x, ~y, bbox: t) => {
let pX = x;
let pY = y;
let (x0, y0, x1, y1) = getBounds(bbox);
pX >= x0 && pX <= x1 && pY >= y0 && pY <= y1;
};
let intersects = (b0: t, b1: t) => {
let (box0_minX, box0_minY, box0_maxX, box0_maxY) = getBounds(b0);
let (box1_minX, box1_minY, box1_maxX, box1_maxY) = getBounds(b1);
/*
From stackoverflow: https://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other
*/
let noOverlap =
box0_minX > box1_maxX
|| box1_minX > box0_maxX
|| box0_minY > box1_maxY
|| box1_minY > box0_maxY;
!noOverlap;
};
module Mutable = {
let set = Skia.Rect.Mutable.setLtrb;
let intersect = (~out, b0: t, b1: t) =>
if (intersects(b0, b1)) {
let box0_minX = Skia.Rect.getLeft(b0);
let box0_minY = Skia.Rect.getTop(b0);
let box0_maxX = Skia.Rect.getRight(b0);
let box0_maxY = Skia.Rect.getBottom(b0);
let box1_minX = Skia.Rect.getLeft(b1);
let box1_minY = Skia.Rect.getTop(b1);
let box1_maxX = Skia.Rect.getRight(b1);
let box1_maxY = Skia.Rect.getBottom(b1);
let minX = max(box0_minX, box1_minX);
let minY = max(box0_minY, box1_minY);
let maxX = min(box0_maxX, box1_maxX);
let maxY = min(box0_maxY, box1_maxY);
Skia.Rect.Mutable.setLtrb(~out, minX, minY, maxX, maxY);
} else {
Skia.Rect.Mutable.setLtrb(~out, 0., 0., 0., 0.);
};
let transform = (~out: t, bbox: t, m: Skia.Matrix.t) => {
let () = Skia.Matrix.mapRect(m, out, bbox);
();
};
};
let intersect = (b0: t, b1: t) => {
let out = Skia.Rect.makeLtrb(0., 0., 0., 0.);
Mutable.intersect(~out, b0, b1);
out;
};
/* TODO: For a more efficient implementation, we should consider something like:
http://dev.theomader.com/transform-bounding-boxes/
Significantly less matrix multiplications in that strategy!
*/
let transform = (bbox: t, m: Skia.Matrix.t) => {
let out = Skia.Rect.makeEmpty();
Mutable.transform(~out, bbox, m);
out;
};
================================================
FILE: src/Math/BoundingBox2d.rei
================================================
type t;
let create: (float, float, float, float) => t;
let equals: (t, t) => bool;
let getBounds: t => (float, float, float, float);
let intersects: (t, t) => bool;
let intersect: (t, t) => t;
let isPointInside: (~x: float, ~y: float, t) => bool;
let transform: (t, Skia.Matrix.t) => t;
let toString: t => string;
module Mutable: {
let set: (~out: t, float, float, float, float) => unit;
let intersect: (~out: t, t, t) => unit;
let transform: (~out: t, t, Skia.Matrix.t) => unit;
};
================================================
FILE: src/Math/Revery_Math.re
================================================
let pi = 4.0 *. atan(1.);
module BoundingBox2d = BoundingBox2d;
module Angle = Angle;
let clamp = (f: float, min: float, max: float) =>
f < min ? min : f > max ? max : f;
let interpolate = (fromValue, toValue, t) => {
let t = clamp(t, 0., 1.);
fromValue +. (toValue -. fromValue) *. t;
};
================================================
FILE: src/Math/dune
================================================
(library
(name Revery_Math)
(public_name Revery.Math)
(libraries skia))
================================================
FILE: src/Native/Dialog.re
================================================
[@noalloc] external alertSupported: unit => bool = "revery_alertSupported";
external _openFiles:
(
~startDirectory: option(string),
~fileTypes: option(array(string)),
~allowMultiple: bool,
~canChooseFiles: bool,
~canChooseDirectories: bool,
~showHidden: bool,
~buttonText: option(string),
~title: option(string),
unit
) =>
option(array(string)) =
"revery_alertOpenFiles_bytecode" "revery_alertOpenFiles_native";
let openFiles =
(
~startDirectory=?,
~fileTypes=?,
~allowMultiple=false,
~canChooseFiles=true,
~canChooseDirectories=false,
~showHidden=false,
~buttonText=?,
~title=?,
(),
) =>
_openFiles(
~startDirectory,
~fileTypes,
~allowMultiple,
~canChooseFiles,
~canChooseDirectories,
~showHidden,
~buttonText,
~title,
(),
);
================================================
FILE: src/Native/Environment.re
================================================
/* If you change these you *must* keep environment.c up to date. */
type os =
/* Int values */
| Unknown // 0
| Android // 1
| IOS // 2
| Browser // 4
/* Block values */
| Mac({
major: int,
minor: int,
bugfix: int,
}) // 0
| Linux({
kernel: int,
major: int,
minor: int,
patch: int,
}) // 1
| Windows({
major: int,
minor: int,
build: int,
}); // 2
external getOS: unit => os = "revery_getOperatingSystem";
================================================
FILE: src/Native/Gtk.re
================================================
%import
"config.h";
%ifdef
USE_GTK;
type widget;
open {
external c_createGtkWidgetFromXWindow: Sdl2.Window.nativeWindow => widget =
"revery_createGtkWidgetFromXWindow";
external c_gtkWidgetDestroy: widget => unit = "revery_gtkWidgetDestroy";
external c_gtkWidgetGetDepth: widget => int =
"revery_gtkWidgetGetDepth";
module WindowHashable = {
type t = Sdl2.Window.t;
let equal = (win1, win2) =>
Sdl2.Window.getId(win1) == Sdl2.Window.getId(win2);
let hash = Sdl2.Window.getId;
};
module WidgetResult = {
type t = widget;
let weight = c_gtkWidgetGetDepth;
};
module WindowWidgetCache = Lru.M.Make(WindowHashable, WidgetResult);
/* Unfortunately it's not really possible to have a corresponding
Gtk window as part of the state since the creation of the window
requires some initialization that doesn't occur until *after*
window creation. Conversely, we also don't want to create a
bunch of GtkWidgets for one window, so this table maps Sdl windows
to GtkWidgets.
*/
let windowWidgetCache = WindowWidgetCache.create(~initialSize=8, 64);
};
external eventsPending: unit => bool = "revery_gtkEventsPending";
external mainIteration: unit => bool = "revery_gtkMainIteration";
module Widget = {
type t = widget;
let ofSdlWindow = sdlWindow =>
switch (WindowWidgetCache.find(sdlWindow, windowWidgetCache)) {
| Some(widget) =>
WindowWidgetCache.promote(sdlWindow, windowWidgetCache);
widget;
| None =>
let gtkWidget =
sdlWindow
|> Sdl2.Window.getNativeWindow
|> c_createGtkWidgetFromXWindow;
WindowWidgetCache.add(sdlWindow, gtkWidget, windowWidgetCache);
WindowWidgetCache.trim(windowWidgetCache);
Gc.finalise(c_gtkWidgetDestroy, gtkWidget);
gtkWidget;
};
let depth = c_gtkWidgetGetDepth;
external getPath: widget => string = "revery_gtkWidgetGetPath";
external showAll: widget => unit = "revery_gtkWidgetShowAll";
external setOpacity: (widget, float) => unit = "revery_gtkWidgetSetOpacity";
external getOpacity: widget => float = "revery_gtkWidgetGetOpacity";
};
[%%endif];
================================================
FILE: src/Native/Icon.re
================================================
type t;
type progress =
| Indeterminate
| Determinate(float);
external get: unit => t = "revery_getIconHandle";
external _setProgress: (Sdl2.Window.nativeWindow, t, progress) => unit =
"revery_setIconProgress";
external _hideProgress: (Sdl2.Window.nativeWindow, t) => unit =
"revery_hideIconProgress";
let setProgress = (w: Sdl2.Window.t, ih, progress) =>
_setProgress(w |> Sdl2.Window.getNativeWindow, ih, progress);
let hideProgress = (w: Sdl2.Window.t, ih) =>
_hideProgress(w |> Sdl2.Window.getNativeWindow, ih);
================================================
FILE: src/Native/Initialization.re
================================================
external initApp: unit => unit = "revery_initializeApp";
external uninitApp: unit => unit = "revery_uninitializeApp";
external _initWindow: Sdl2.Window.nativeWindow => unit =
"revery_initializeWindow";
let initWindow = (w: Sdl2.Window.t) =>
_initWindow(w |> Sdl2.Window.getNativeWindow);
================================================
FILE: src/Native/Input.re
================================================
%import
"config.h";
module Button = {
type t;
open {
external c_create: string => t = "revery_buttonCreate";
external c_setColor: (t, float, float, float, float) => unit =
"revery_buttonSetColor";
};
%if
defined(USE_COCOA);
let hash = NSObject.hash;
let equal = NSObject.equal;
let toString = NSObject.toString;
[%%else];
let hash = _ => 1;
let equal = (===);
let toString = _ => "UNIMPLEMENTED";
[%%endif];
module CallbackTbl =
Hashtbl.Make({
type nonrec t = t;
let equal = equal;
let hash = hash;
});
let callbackTbl = CallbackTbl.create(32);
let callbackForButton = button => {
let callback = CallbackTbl.find_opt(callbackTbl, button);
switch (callback) {
| Some(cb) => cb()
| None => ()
};
};
Callback.register("revery_callbackForButton", callbackForButton);
let setColor = (~red, ~green, ~blue, ~alpha, button) =>
c_setColor(button, red, green, blue, alpha);
%if
defined(USE_COCOA);
let create = (~title, ~onClick) => {
let button = c_create(title);
CallbackTbl.replace(callbackTbl, button, onClick);
Gc.finalise(NSObject.release, button);
button;
};
let setFrame = (~x, ~y, ~width, ~height, button) =>
NSView.setFrame(button, x, y, width, height);
let getDefaultWidth = NSView.getDefaultWidth;
let getDefaultHeight = NSView.getDefaultHeight;
let remove = NSView.remove;
let displayIn = NSView.displayIn;
[%%else];
let create = (~title, ~onClick) => {
let button = c_create(title);
CallbackTbl.replace(callbackTbl, button, onClick);
button;
};
let setFrame = (~x as _, ~y as _, ~width as _, ~height as _, _) => ();
let getDefaultWidth = _ => 0;
let getDefaultHeight = _ => 0;
let remove = _ => ();
let displayIn = (_, _) => ();
[%%endif];
};
external openEmojiPanel: unit => unit = "revery_openEmojiPanel";
================================================
FILE: src/Native/Input.rei
================================================
module Button: {
type t;
let hash: t => int;
let equal: (t, t) => bool;
let toString: t => string;
let create: (~title: string, ~onClick: unit => unit) => t;
let setFrame: (~x: int, ~y: int, ~width: int, ~height: int, t) => unit;
let getDefaultWidth: t => int;
let getDefaultHeight: t => int;
let setColor:
(~red: float, ~green: float, ~blue: float, ~alpha: float, t) => unit;
let displayIn: (t, Sdl2.Window.t) => unit;
let remove: t => unit;
};
let openEmojiPanel: unit => unit;
================================================
FILE: src/Native/Locale.re
================================================
external getUser: unit => string = "revery_getUserLocale";
================================================
FILE: src/Native/Menu.re
================================================
%import
"config.h";
type t;
module KeyEquivalent = {
type t = {
str: string,
alt: bool,
shift: bool,
ctrl: bool,
};
let strToKey = str =>
switch (str) {
| "Space"
| "space" => " "
| "ESC"
| "esc"
| "Escape"
| "escape" => 0x1b |> Char.chr |> String.make(1)
| "TAB"
| "Tab"
| "tab" => "\t"
| key => key
};
let ofString = str => {
str: strToKey(str),
alt: false,
shift: false,
ctrl: false,
};
let enableAlt = (t, truth) => {...t, alt: truth};
let enableShift = (t, truth) => {...t, shift: truth};
let enableCtrl = (t, truth) => {...t, ctrl: truth};
};
module Item = {
type menu = t;
type t;
%if
defined(USE_COCOA);
let hash = NSObject.hash;
let equal = NSObject.equal;
let toString = NSObject.toString;
[%%else];
let hash = _ => 1;
let equal = (==);
let toString = _ => "UNIMPLEMENTED";
[%%endif];
open {
external c_create: (string, option(KeyEquivalent.t)) => t =
"revery_menuItemCreate";
external c_getSubmenu: t => option(menu) =
"revery_menuItemGetSubmenu";
};
module CallbackTbl =
Hashtbl.Make({
type nonrec t = t;
let equal = equal;
let hash = hash;
});
let callbackTbl = CallbackTbl.create(32);
let callbackForMenuItem = (fromKeyPress, item) => {
let callback = CallbackTbl.find_opt(callbackTbl, item);
switch (callback) {
| Some(cb) => cb(~fromKeyPress, ())
| None => ()
};
};
Callback.register("revery_callbackForMenuItem", callbackForMenuItem);
external createSeparator: unit => t = "revery_menuItemCreateSeparator";
external setEnabled: (t, bool) => unit = "revery_menuItemSetEnabled";
external setVisible: (t, bool) => unit = "revery_menuItemSetVisible";
%if
defined(USE_COCOA);
let create = (~title, ~onClick, ~keyEquivalent=?, ()) => {
let menu = c_create(title, keyEquivalent);
CallbackTbl.replace(callbackTbl, menu, onClick);
Gc.finalise(NSObject.release, menu);
menu;
};
let getSubmenu = item => {
let submenu = c_getSubmenu(item);
Option.iter(submenu => Gc.finalise(NSObject.release, submenu), submenu);
submenu;
};
[%%else];
let create = (~title, ~onClick, ~keyEquivalent=?, ()) => {
let menu = c_create(title, keyEquivalent);
CallbackTbl.replace(callbackTbl, menu, onClick);
menu;
};
let getSubmenu = c_getSubmenu;
[%%endif];
};
open {
external c_addSubmenu: (t, t) => unit = "revery_menuAddSubmenu";
external c_removeSubmenu: (t, t) => unit = "revery_menuRemoveSubmenu";
external c_insertSubmenuAt: (t, t, int) => unit =
"revery_menuInsertSubmenuAt";
external c_getMenuBarHandle: unit => t = "revery_getMenuBarHandle";
external c_create: string => t = "revery_menuCreate";
external c_nth: (t, int) => option(Item.t) = "revery_menuNth";
external c_displayIn: (t, Sdl2.Window.nativeWindow, int, int) => unit =
"revery_menuDisplayIn";
};
external addItem: (t, Item.t) => unit = "revery_menuAddItem";
external insertItemAt: (t, Item.t, int) => unit = "revery_menuInsertItemAt";
external removeItem: (t, Item.t) => unit = "revery_menuRemoveItem";
external clear: t => unit = "revery_menuClear";
let addSubmenu = (~parent, ~child) => c_addSubmenu(parent, child);
let removeSubmenu = (~parent, ~child) => c_removeSubmenu(parent, child);
let insertSubmenuAt = (~parent, ~child, ~idx) =>
c_insertSubmenuAt(parent, child, idx);
let displayIn = (~x, ~y, menu, window) =>
c_displayIn(menu, window |> Sdl2.Window.getNativeWindow, x, y);
%if
defined(USE_COCOA);
let toString = NSObject.toString;
let getMenuBarHandle = () => {
let handle = c_getMenuBarHandle();
Gc.finalise(NSObject.release, handle);
handle;
};
let create = title => {
let menu = c_create(title);
Gc.finalise(NSObject.release, menu);
menu;
};
let nth = (menu, idx) => {
let item = c_nth(menu, idx);
Option.iter(item => Gc.finalise(NSObject.release, item), item);
item;
};
[%%else];
let toString = _ => "UNIMPLEMENTED";
let getMenuBarHandle = c_getMenuBarHandle;
let create = c_create;
let nth = c_nth;
[%%endif];
================================================
FILE: src/Native/Menu.rei
================================================
type t;
module KeyEquivalent: {
type t;
let ofString: string => t;
let enableAlt: (t, bool) => t;
let enableShift: (t, bool) => t;
let enableCtrl: (t, bool) => t;
};
module Item: {
type menu = t;
type t;
let hash: t => int;
let equal: (t, t) => bool;
let toString: t => string;
let create:
(
~title: string,
~onClick: (~fromKeyPress: bool, unit) => unit,
~keyEquivalent: KeyEquivalent.t=?,
unit
) =>
t;
let createSeparator: unit => t;
let setEnabled: (t, bool) => unit;
let setVisible: (t, bool) => unit;
let getSubmenu: t => option(menu);
};
let getMenuBarHandle: unit => t;
let create: string => t;
let toString: t => string;
let nth: (t, int) => option(Item.t);
let addItem: (t, Item.t) => unit;
let insertItemAt: (t, Item.t, int) => unit;
let removeItem: (t, Item.t) => unit;
let addSubmenu: (~parent: t, ~child: t) => unit;
let removeSubmenu: (~parent: t, ~child: t) => unit;
let insertSubmenuAt: (~parent: t, ~child: t, ~idx: int) => unit;
let clear: t => unit;
let displayIn: (~x: int, ~y: int, t, Sdl2.Window.t) => unit;
================================================
FILE: src/Native/NSObject.re
================================================
%import
"config.h";
%if
defined(USE_COCOA) || defined(USE_UIKIT);
type t('a) = 'a;
/* equal
Uses the NSObject isEqual selector */
external equal: (t('a), t('b)) => bool = "revery_NSObject_equal";
/* hash
Uses the NSObject hash selector */
external hash: t('a) => int = "revery_NSObject_hash";
/* toString
Uses the NSObject description selector */
external toString: t('a) => string = "revery_NSObject_toString";
/* className
Uses the NSStringFromClass function */
external className: t('a) => string = "revery_NSObject_className";
/* release
Uses the NSObject release selector */
external release: t('a) => unit = "revery_NSObject_release";
[%%endif];
================================================
FILE: src/Native/NSView.re
================================================
%import
"config.h";
%if
defined(USE_COCOA);
type t('a) = 'a;
open {
external c_displayIn: (t('a), Sdl2.Window.nativeWindow) => unit =
"revery_NSView_displayIn";
};
let displayIn = (nsView, sdlWindow) =>
c_displayIn(nsView, Sdl2.Window.getNativeWindow(sdlWindow));
external remove: t('a) => unit = "revery_NSView_remove";
/* Frame functions
These modify/read the values of the NSView's frame (a CGRect) */
// Position
external setFrame: (t('a), int, int, int, int) => unit =
"revery_NSView_setFrame";
// Default size
external getDefaultWidth: t('a) => int = "revery_NSView_getDefaultWidth";
external getDefaultHeight: t('a) => int = "revery_NSView_getDefaultHeight";
[%%endif];
================================================
FILE: src/Native/Notification.re
================================================
type t = {
title: string,
body: string,
onClick: unit => unit,
mute: bool,
};
let create =
(~title: string, ~body: string, ~onClick=() => (), ~mute=false, ()) => {
title,
body,
onClick,
mute,
};
external dispatch: t => unit = "revery_dispatchNotification";
external scheduleFromNow: (int, t) => unit =
"revery_scheduleNotificationFromNow";
================================================
FILE: src/Native/ReveryCocoa.h
================================================
void revery_alert_cocoa(void *pWin, const char *szMessage);
char **revery_open_files_cocoa(const char *startDir, const char *fileTypes[],
int fileTypesSize, int allowMultiple,
int canChooseFiles, int canChooseDirectories,
int showHidden, const char* buttonText,
const char* title);
/* Notification functions */
void revery_dispatchNotification_cocoa(const char *title, const char *body,
long onClickFunc, int mute);
void revery_scheduleNotificationFromNow_cocoa(const char *title,
const char *body,
long onClickFunc, int mute,
int seconds);
void *revery_getIconHandle_cocoa();
/* Icon progress bar functions */
void revery_setIconProgress_cocoa(void* dt, double progress);
void revery_setIconProgressIndeterminate_cocoa(void *dt);
void revery_hideIconProgress_cocoa(void* ip);
/* Open functions */
int revery_openURL_cocoa(const char *url_string);
int revery_openFile_cocoa(const char *path_string);
/* Locale functions */
char *revery_getUserLocale_cocoa();
/* Menu functions */
void *revery_getMenuBarHandle_cocoa();
void *revery_menuCreate_cocoa(const char *title);
void *revery_menuItemCreate_cocoa(const char *title, void *keyEquivalent);
void *revery_menuNth_cocoa(void *nsMenu, int idx);
void revery_menuAddItem_cocoa(void *nsMenu, void *nsMenuItem);
void *revery_menuItemGetSubmenu_cocoa(void *nsMenuItem);
void revery_menuAddSubmenu_cocoa(void *parent, void *child);
void revery_menuRemoveSubmenu_cocoa(void *parent, void *child);
void revery_menuRemoveItem_cocoa(void *nsMenu, void *nsMenuItem);
void revery_menuInsertItemAt_cocoa(void *nsMenu, void *nsMenuItem, int idx);
void revery_menuInsertSubmenuAt_cocoa(void *parent, void *child, int idx);
void revery_menuClear_cocoa(void *nsMenu);
void *revery_menuItemCreateSeparator_cocoa();
void revery_menuItemSetEnabled_cocoa(void *menuItem, int truth);
void revery_menuItemSetVisible_cocoa(void *menuItem, int truth);
void revery_menuDisplayIn_cocoa(void *nsMenu, void *nsWindow, int x, int y);
/* Input functions */
void *revery_buttonCreate_cocoa(const char *title);
void revery_buttonSetColor_cocoa(void *nsButton, double red, double green, double blue, double alpha);
void revery_openEmojiPanel_cocoa(void);
/* Window functions */
void revery_windowSetUnsavedWork_cocoa(void *memory, int truth);
================================================
FILE: src/Native/ReveryGtk.h
================================================
void revery_alert_gtk(void *pWin, const char *szMessage);
char** revery_open_files_gtk(const char* startDir, const char* fileTypes[],
int fileTypesSize, int allowMultiple,
int canChooseFiles, int canChooseDirectories,
int showHidden, const char* buttonText,
const char* title);
int revery_openURL_gtk(const char *url_string);
int revery_openFile_gtk(const char *path_string);
================================================
FILE: src/Native/ReveryLinux.h
================================================
#pragma once
/* Environment functions */
void getOperatingSystemVersion_linux(int *kernel, int *major, int *minor, int *patch);
================================================
FILE: src/Native/ReveryMac.h
================================================
#pragma once
/* Environment functions */
void getOperatingSystemVersion_mac(int *major, int *minor, int *bugfix);
================================================
FILE: src/Native/ReveryNSObject.c
================================================
#ifdef __APPLE__
#import
#import
#include
#include
#include
#include
#include "utilities.h"
CAMLprim value revery_NSObject_equal(value vNSObjA, value vNSObjB) {
CAMLparam2(vNSObjA, vNSObjB);
NSObject *nsObjA = (NSObject *)revery_unwrapPointer(vNSObjA);
NSObject *nsObjB = (NSObject *)revery_unwrapPointer(vNSObjB);
BOOL equal = [nsObjA isEqual:nsObjB];
CAMLreturn(Val_bool(equal));
}
CAMLprim value revery_NSObject_hash(value vNSObj) {
CAMLparam1(vNSObj);
NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);
NSUInteger hash = [nsObj hash];
CAMLreturn(Val_int(hash));
}
CAMLprim value revery_NSObject_toString(value vNSObj) {
CAMLparam1(vNSObj);
CAMLlocal1(vDescription);
NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);
NSString *nsDescription = [nsObj description];
vDescription = caml_copy_string([nsDescription UTF8String]);
CAMLreturn(vDescription);
}
CAMLprim value revery_NSObject_className(value vNSObj) {
CAMLparam1(vNSObj);
CAMLlocal1(vClassName);
NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);
NSString *nsClassName = NSStringFromClass([nsObj class]);
vClassName = caml_copy_string([nsClassName UTF8String]);
[nsClassName release];
CAMLreturn(vClassName);
}
CAMLprim value revery_NSObject_release(value vNSObj) {
CAMLparam1(vNSObj);
NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);
[nsObj release];
CAMLreturn(Val_unit);
}
#endif
================================================
FILE: src/Native/ReveryWin32.h
================================================
void revery_alert_win32(void *pWin, const char *szMessage);
void *revery_getIconHandle_win32();
void revery_setIconProgress_win32(void *win, void *ih, float progress);
void revery_setIconProgressIndeterminate_win32(void *win, void *ih);
void revery_hideIconProgress_win32(void *win, void *ih);
int revery_openURL_win32(const char *url_string);
char *revery_getUserLocale_win32();
char **revery_open_files_win32(const char *startDir, int canChooseFiles,
int canChooseDirectories, const char *title);
================================================
FILE: src/Native/ReveryWindows.h
================================================
#pragma once
/* Environment functions */
void getOperatingSystemVersion_windows(int *major, int *minor, int *build);
================================================
FILE: src/Native/Revery_Native.c
================================================
#include
#include
#include
#include
#include
#include
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#include
#include
#elif USE_COCOA
#include "ReveryCocoa.h"
#import "ReveryAppDelegate.h"
#elif USE_GTK
#include "ReveryGtk.h"
#include
#endif
#include "utilities.h"
CAMLprim value revery_initializeApp() {
#ifdef USE_COCOA
SDLAppDelegate *sdlDelegate = [NSApp delegate];
ReveryAppDelegate *delegate = [ReveryAppDelegate newWithSDLDelegate:sdlDelegate];
[NSApp setDelegate:delegate];
#elif USE_WIN32
HRESULT hr = CoInitialize(NULL);
if (hr != S_OK) {
fprintf(stderr, "WARNING: COM initialization failed.");
}
#elif USE_GTK
gtk_init(0, NULL);
#endif
return Val_unit;
}
CAMLprim value revery_uninitializeApp() {
#ifdef USE_WIN32
CoUninitialize();
#endif
return Val_unit;
}
CAMLprim value revery_initializeWindow(value vWin) {
CAMLparam1(vWin);
void *win = (void *)revery_unwrapPointer(vWin);
#ifdef USE_WIN32
/* This flag often gets unset when the window decoration is removed.
This Chromium comment is the source of this fix:
https://chromium.googlesource.com/chromium/src.git/+/46.0.2478.0/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc#71
*/
HWND window = (HWND)win;
int current_style = GetWindowLong(window, GWL_STYLE);
SetWindowLong(window, GWL_STYLE, current_style | WS_CAPTION);
#else
UNUSED(win);
#endif
CAMLreturn(Val_unit);
}
================================================
FILE: src/Native/Revery_Native.re
================================================
module Dialog = Dialog;
module Environment = Environment;
module Icon = Icon;
module Notification = Notification;
module Shell = Shell;
module Locale = Locale;
module Gtk = Gtk;
module Menu = Menu;
module Input = Input;
module Window = Window;
include Initialization;
================================================
FILE: src/Native/Shell.re
================================================
external openURL: string => bool = "revery_openURL";
external openFile: string => bool = "revery_openFile";
================================================
FILE: src/Native/Window.re
================================================
open {
type t = Sdl2.Window.nativeWindow;
external c_setUnsavedWork: (t, bool) => unit =
"revery_windowSetUnsavedWork";
};
let setUnsavedWork = (window, truth) =>
c_setUnsavedWork(window |> Sdl2.Window.getNativeWindow, truth);
================================================
FILE: src/Native/caml_values.h
================================================
#ifndef CAMLVALUES_H
#define CAMLVALUES_H
#define Val_none Val_int(0)
static inline value Val_some(value v) {
(void)Val_some;
CAMLparam1(v);
CAMLlocal1(some);
some = caml_alloc(1, 0);
Store_field(some, 0, v);
CAMLreturn(some);
}
#define Some_val(v) Field(v, 0)
#endif
================================================
FILE: src/Native/cocoa/ReveryAppDelegate.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import "ReveryAppDelegate.h"
#import
#import "utilities.h"
#include "ReveryAppDelegate_func.h"
// Implementation of ReveryAppDelegate
@implementation ReveryAppDelegate
/* init - initializes the AppDelegate
Assigns a mutable dictionary to notificationActions
*/
- (id)initWithSDLDelegate:(SDLAppDelegate *)sdlDelegate {
self = [super init];
if (self) {
_notificationActions = [NSMutableDictionary new];
_sdlDelegate = sdlDelegate;
}
NSString *valueToSave = @"true";
[[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"ApplePressAndHoldEnabled"];
[[NSUserDefaults standardUserDefaults] synchronize];
return self;
}
+(id)newWithSDLDelegate:(SDLAppDelegate *)sdlDelegate {
return [[ReveryAppDelegate alloc] initWithSDLDelegate:sdlDelegate];
}
/* applicationDidFinishLaunching
Assigns self as the notification center delegate
*/
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[_sdlDelegate applicationDidFinishLaunching:notification];
}
/* didActivateNotification
Gets the long from the NSDictionary from the notification's identifier and calls it
*/
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
didActivateNotification:(NSUserNotification *)notification {
UNUSED(center);
NSNumber *num = _notificationActions[[notification identifier]];
long ocamlFunc = [num longValue];
revery_caml_call(ocamlFunc);
}
/* shouldPresentNotification
Always presents the notification
*/
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)notification {
UNUSED(center);
UNUSED(notification);
return YES;
}
/* openFile
We call into the CAML function `revery_dispatchFileOpen`
Unfortunately because of namespacing issues with `alloc`,
we have to put the function in a separate file. Both OCaml
and Objective-C have `alloc` selector/functions, which
causes either build errors or runtime errors, neither of
which are preferable :).
*/
- (BOOL)application:(NSApplication *)sender
openFile:(NSString *)filename {
UNUSED(sender);
revery_appDelegate_openFile([filename UTF8String]);
return YES;
}
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryAppDelegate.h
================================================
#include "config.h"
#ifdef USE_COCOA
#import
#import "SDLAppDelegate.h"
/* ReveryAppDelegate
Contains all of the delegation code for the NSApplication, as well as the
delegation for the NSUserNotificationCenter
*/
@interface ReveryAppDelegate
: NSObject
/* [notificationActions] - a mutable dictionary
maps NSStrings (notification identifiers) -> NSNumbers
(longs which represent OCaml callbacks)
*/
@property(nonatomic, strong) NSMutableDictionary *notificationActions;
@property(nonatomic, strong) SDLAppDelegate *sdlDelegate;
-(id)initWithSDLDelegate:(SDLAppDelegate *)sdlDelegate;
+(id)newWithSDLDelegate:(SDLAppDelegate *)sdlDelegate;
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryAppDelegate_func.c
================================================
#include "config.h"
#ifdef USE_COCOA
#include "ReveryAppDelegate_func.h"
#include
#include
#include
#include
#include
#include "caml_values.h"
#include "utilities.h"
#import
CAMLprim value _revery_appDelegate_openFile(const char *path) {
CAMLparam0();
CAMLlocal1(vPath);
static const value *dispatchFileOpen = NULL;
if (dispatchFileOpen == NULL) {
dispatchFileOpen = caml_named_value("revery_dispatchFileOpen");
}
// Call only if the value was gotten
if (dispatchFileOpen != NULL) {
vPath = caml_copy_string(path);
caml_callback(*dispatchFileOpen, vPath);
}
CAMLreturn(Val_unit);
}
void revery_appDelegate_openFile(const char *path) {
_revery_appDelegate_openFile(path);
}
#endif
================================================
FILE: src/Native/cocoa/ReveryAppDelegate_func.h
================================================
#include "config.h"
#ifdef USE_COCOA
#ifndef ReveryAppDelegate_func_h
#define ReveryAppDelegate_func_h
void revery_appDelegate_openFile(const char *path);
#endif
#endif
================================================
FILE: src/Native/cocoa/ReveryButtonTarget.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import "ReveryButtonTarget.h"
#import
#include
#include
#include
#include
#import "utilities.h"
static const camlValue *callbackForButton;
@implementation ReveryButtonTarget
-(id)init {
self = [super init];
if (self) {
if (callbackForButton == NULL) {
callbackForButton = caml_named_value("revery_callbackForButton");
}
}
return self;
}
-(void)onButtonClick:(NSButton *)sender {
CAMLparam0();
CAMLlocal1(vButton);
if (callbackForButton != NULL) {
vButton = revery_wrapPointer(sender);
camlValue args[] = {vButton};
revery_caml_call_n(*callbackForButton, 1, args);
} else {
NSLog(@"Unable to acquire button callback!");
}
CAMLreturn0;
}
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryButtonTarget.h
================================================
#include "config.h"
#ifdef USE_COCOA
#import
@interface ReveryButtonTarget : NSObject
-(void)onButtonClick:(NSButton *)sender;
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryMenuItemTarget.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import "ReveryMenuItemTarget.h"
#import
#include
#include
#include
#include
#import "utilities.h"
static const camlValue *callbackForMenuItem;
@implementation ReveryMenuItemTarget
-(id)init {
self = [super init];
if (self) {
if (callbackForMenuItem == NULL) {
callbackForMenuItem = caml_named_value("revery_callbackForMenuItem");
}
}
return self;
}
-(void)onMenuItemClick:(NSMenuItem *)sender {
CAMLparam0();
CAMLlocal1(vMenuItem);
// Whether the menu item 'click' was due to a shortcut key
int fDueToKeyPress = 0;
switch (NSApp.currentEvent.type) {
case NSEventTypeKeyDown:
case NSEventTypeKeyUp:
fDueToKeyPress = 1;
break;
default:
fDueToKeyPress = 0;
break;
}
if (callbackForMenuItem != NULL) {
vMenuItem = revery_wrapPointer(sender);
camlValue args[] = {Val_int(fDueToKeyPress), vMenuItem};
revery_caml_call_n(*callbackForMenuItem, 2, args);
} else {
NSLog(@"Unable to acquire menu item callback!");
}
CAMLreturn0;
}
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryMenuItemTarget.h
================================================
#include "config.h"
#ifdef USE_COCOA
#import
@interface ReveryMenuItemTarget : NSObject
-(void)onMenuItemClick:(NSMenuItem *)sender;
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryNSView.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import
#import "ReveryNSViewCoords.h"
#include
#include
#include
#include
#include "utilities.h"
CAMLprim value revery_NSView_remove(value vNSView) {
CAMLparam1(vNSView);
NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);
[nsView removeFromSuperview];
CAMLreturn(Val_unit);
}
CAMLprim value revery_NSView_setFrame(value vNSView, value vX, value vY, value vWidth, value vHeight) {
CAMLparam5(vNSView, vX, vY, vWidth, vHeight);
NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);
int x = Int_val(vX);
int y = Int_val(vY);
int width = Int_val(vWidth);
int height = Int_val(vHeight);
CGRect frame = [nsView frame];
frame.size.width = (CGFloat)width;
frame.size.height = (CGFloat)height;
[nsView setReveryX:(CGFloat)x];
[nsView setReveryY:(CGFloat)y];
[nsView updateFrame:frame];
CAMLreturn(Val_unit);
}
CAMLprim value revery_NSView_displayIn(value vNSView, value vNSWindow) {
CAMLparam2(vNSWindow, vNSView);
NSWindow *nsWindow = (NSWindow *)revery_unwrapPointer(vNSWindow);
NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);
NSView *contentView = [nsWindow contentView];
CGRect frame = [nsView frame];
[contentView addSubview:nsView];
[nsView updateFrame: frame];
CAMLreturn(Val_unit);
}
CAMLprim value revery_NSView_getDefaultWidth(value vNSView) {
CAMLparam1(vNSView);
CAMLlocal1(vDefaultWidth);
NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);
CGSize defaultSize = [nsView fittingSize];
vDefaultWidth = Val_int((int)defaultSize.width);
CAMLreturn(vDefaultWidth);
}
CAMLprim value revery_NSView_getDefaultHeight(value vNSView) {
CAMLparam1(vNSView);
CAMLlocal1(vDefaultHeight);
NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);
CGSize defaultSize = [nsView fittingSize];
vDefaultHeight = Val_int((int)defaultSize.height);
CAMLreturn(vDefaultHeight);
}
#endif
================================================
FILE: src/Native/cocoa/ReveryNSViewCoords.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import
#import
#import "ReveryNSViewCoords.h"
#include "utilities.h"
@implementation NSView (ReveryNSViewCoords)
CATEGORY_PROPERTY_GET_SET_DOUBLE(reveryX, setReveryX:);
CATEGORY_PROPERTY_GET_SET_DOUBLE(reveryY, setReveryY:);
- (void)updateFrame:(CGRect) frame {
NSView *superview = [self superview];
if (superview == NULL) {
return;
}
CGRect superFrame = [superview frame];
frame.origin.x = [self reveryX];
frame.origin.y = superFrame.size.height - frame.size.height - [self reveryY];
[self setFrame:frame];
}
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryNSViewCoords.h
================================================
#include "config.h"
#ifdef USE_COCOA
#import
/* ReveryNSViewCoords
Apple broke from the crowd and defines NSView coordinates to be
relative to the bottom left rather than the top. This is a category
to "extend" the NSView class (and all subclasses) so that we can store
"normal" coordinates and update the frame when the NSView has a parent.
*/
@interface NSView (ReveryNSViewCoords)
@property double reveryX;
@property double reveryY;
- (void)updateFrame:(CGRect) frame;
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryProgressBar.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import
#import "ReveryProgressBar.h"
@implementation ReveryProgressBar
- (void)drawRect:(NSRect)dirtyRect {
(void)dirtyRect;
NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
CGFloat radius = rect.size.height / 2;
NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
xRadius:radius
yRadius:radius];
[bezier_path setLineWidth:2.0];
[[NSColor grayColor] set];
[bezier_path stroke];
rect = NSInsetRect(rect, 2.0, 2.0);
radius = rect.size.height / 2;
bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
xRadius:radius
yRadius:radius];
[bezier_path setLineWidth:1.0];
[bezier_path addClip];
rect.size.width =
floor(rect.size.width * ([self doubleValue] / [self maxValue]));
if (self.indeterminate) {
[[NSColor colorWithSRGBRed:1 green:0.84 blue:.04 alpha:1] set];
} else {
[[NSColor colorWithSRGBRed:0.39 green:0.82 blue:1 alpha:1] set];
}
NSRectFill(rect);
}
@end
#endif
================================================
FILE: src/Native/cocoa/ReveryProgressBar.h
================================================
@interface ReveryProgressBar : NSProgressIndicator
@end
================================================
FILE: src/Native/cocoa/SDLAppDelegate.h
================================================
#include "config.h"
#ifdef USE_COCOA
#import
@interface SDLAppDelegate
: NSObject
@end
#endif
================================================
FILE: src/Native/config/discover.re
================================================
open Configurator.V1;
open C_define;
open Flags;
type os =
| Android
| IOS
| Linux
| Mac
| Windows;
let detect_system_header = {|
#if __APPLE__
#include
#if TARGET_OS_IPHONE
#define PLATFORM_NAME "ios"
#else
#define PLATFORM_NAME "mac"
#endif
#elif __linux__
#if __ANDROID__
#define PLATFORM_NAME "android"
#else
#define PLATFORM_NAME "linux"
#endif
#elif WIN32
#define PLATFORM_NAME "windows"
#endif
|};
let get_os = t => {
let header = {
let file = Filename.temp_file("discover", "os.h");
let fd = open_out(file);
output_string(fd, detect_system_header);
close_out(fd);
file;
};
let platform =
C_define.import(t, ~includes=[header], [("PLATFORM_NAME", String)]);
switch (platform) {
| [(_, String("android"))] => Android
| [(_, String("ios"))] => IOS
| [(_, String("linux"))] => Linux
| [(_, String("mac"))] => Mac
| [(_, String("windows"))] => Windows
| _ => failwith("Unknown operating system")
};
};
type feature =
| COCOA
| GTK
| UIKIT
| WIN32;
let gen_config_header = (conf, features) => {
let includes = value =>
List.exists((==)(value), features)
? Value.Int(1) : Value.Switch(false);
let os = {
let os = get_os(conf);
switch (os) {
| Android => "android"
| IOS => "ios"
| Linux => "linux"
| Mac => "mac"
| Windows => "windows"
};
};
let is_os = os => get_os(conf) == os ? Value.Int(1) : Value.Switch(false);
gen_header_file(
conf,
[
("PLATFORM_NAME", Value.String(os)),
("USE_COCOA", includes(COCOA)),
("USE_GTK", includes(GTK)),
("USE_UIKIT", includes(UIKIT)),
("USE_WIN32", includes(WIN32)),
("IS_IOS", is_os(IOS)),
("IS_MACOS", is_os(Mac)),
("IS_ANDROID", is_os(Android)),
("IS_LINUX", is_os(Linux)),
("IS_WINDOWS", is_os(Windows)),
],
);
};
type config = {
features: list(feature),
libs: list(string),
cflags: list(string),
flags: list(string),
};
let ccopt = s => ["-ccopt", s];
let cclib = s => ["-cclib", s];
let get_ios_config = () => {
features: [UIKIT],
cflags: ["-I", ".", "-x", "objective-c"],
libs: [],
flags: [],
};
let get_mac_config = () => {
features: [COCOA],
cflags: ["-I", ".", "-x", "objective-c", "-Wno-deprecated-declarations"],
libs: [],
flags: [] @ cclib("-ObjC"),
};
let get_linux_config = c => {
let default = {features: [], libs: [], cflags: [], flags: []};
switch (Pkg_config.get(c)) {
| None => default
| Some(pc) =>
switch (Pkg_config.query(pc, ~package="gtk+-3.0")) {
| None => default
| Some(conf) => {
features: [GTK],
libs: conf.libs,
cflags: conf.cflags,
flags: [],
}
}
};
};
let get_win32_config = () => {
features: [WIN32],
cflags: [],
libs: [],
flags: [] @ cclib("-luuid") @ cclib("-lole32") @ cclib("-lcomdlg32"),
};
main(~name="discover", t => {
let os = get_os(t);
let conf =
switch (os) {
| Android
| Linux => get_linux_config(t)
| IOS => get_ios_config()
| Mac => get_mac_config()
| Windows => get_win32_config()
};
gen_config_header(~fname="config.h", t, conf.features);
write_sexp("flags.sexp", conf.flags);
write_sexp("c_flags.sexp", conf.cflags);
write_sexp("c_library_flags.sexp", conf.libs);
});
================================================
FILE: src/Native/config/dune
================================================
(executable
(name discover)
(libraries dune.configurator))
================================================
FILE: src/Native/dialog.c
================================================
#include
#include
#include
#include
#include
#include
#include "caml_values.h"
#include
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#elif USE_GTK
#include "ReveryGtk.h"
#endif
#include "utilities.h"
CAMLprim value revery_alertSupported() {
#if defined(USE_WIN32) || defined(USE_COCOA) || defined(USE_GTK)
return Val_true;
#else
return Val_false;
#endif
}
CAMLprim value revery_alert(value vWindow, value vMessage) {
CAMLparam2(vWindow, vMessage);
const char *szMessage = String_val(vMessage);
void *pWin = (void *)revery_unwrapPointer(vWindow);
#ifdef USE_WIN32
revery_alert_win32(pWin, szMessage);
#elif USE_COCOA
revery_alert_cocoa(pWin, szMessage);
#elif USE_GTK
revery_alert_gtk(pWin, szMessage);
#else
printf("WARNING - Not implemented: alert");
UNUSED(szMessage);
UNUSED(pWin);
#endif
return Val_unit;
}
CAMLprim value revery_alertOpenFiles_native(
value vStartDirectory, value vFileTypes, value vAllowMultiple,
value vCanChooseFiles, value vCanChooseDirectories, value vShowHidden,
value vButtonText, value vTitle, value vUnit) {
CAMLparam5(vStartDirectory, vFileTypes, vAllowMultiple, vCanChooseFiles,
vCanChooseDirectories);
CAMLxparam3(vButtonText, vTitle, vUnit);
const char *startDirectory;
const char *buttonText;
// Initialize an array of filetypes
const char **fileTypes = NULL;
int fileTypesSize = 0;
// title from OCaml -> C
const char *title;
if (vTitle != Val_none)
title = String_val(Some_val(vTitle));
else
title = NULL;
int allowMultiple = Bool_val(vAllowMultiple);
int canChooseFiles = Bool_val(vCanChooseFiles);
int canChooseDirectories = Bool_val(vCanChooseDirectories);
int showHidden = Bool_val(vShowHidden);
if (vFileTypes != Val_none) {
CAMLlocal1(camlArr);
camlArr = Some_val(vFileTypes);
fileTypesSize = Wosize_val(camlArr);
// Allocate space for an array
fileTypes = (const char **)malloc(sizeof(*fileTypes) * fileTypesSize);
// Populate the array with the CAML array;
for (int i = 0; i < fileTypesSize; i++) {
const char *str = String_val(Field(camlArr, i));
fileTypes[i] = str;
}
}
if (vStartDirectory != Val_none) {
startDirectory = String_val(Some_val(vStartDirectory));
} else {
startDirectory = NULL;
}
if (vButtonText != Val_none) {
buttonText = String_val(Some_val(vButtonText));
} else {
buttonText = NULL;
}
char **fileList = NULL;
#ifdef USE_WIN32
fileList = revery_open_files_win32(startDirectory, canChooseFiles,
canChooseDirectories, title);
(void)fileTypesSize;
(void)allowMultiple;
(void)showHidden;
(void)buttonText;
#elif USE_COCOA
caml_release_runtime_system();
fileList = revery_open_files_cocoa(
startDirectory, fileTypes, fileTypesSize, allowMultiple, canChooseFiles,
canChooseDirectories, showHidden, buttonText, title);
caml_acquire_runtime_system();
#elif USE_GTK
fileList = revery_open_files_gtk(
startDirectory, fileTypes, fileTypesSize, allowMultiple, canChooseFiles,
canChooseDirectories, showHidden, buttonText, title);
#else
(void)showHidden;
(void)canChooseDirectories;
(void)canChooseFiles;
(void)allowMultiple;
(void)title;
(void)buttonText;
(void)startDirectory;
(void)fileList;
#endif
if (fileList) {
CAMLlocal1(camlArr);
int len = -1;
while (fileList[++len] != NULL) {
}
camlArr = caml_alloc(len, 0);
for (int i = 0; i < len; i++) {
Store_field(camlArr, i, caml_copy_string(fileList[i]));
free(fileList[i]);
}
free(fileList);
CAMLreturn(Val_some(camlArr));
} else {
CAMLreturn(Val_none);
}
}
CAMLprim value revery_alertOpenFiles_bytecode(value *argv, int argn) {
(void)argn;
return revery_alertOpenFiles_native(argv[0], argv[1], argv[2], argv[3],
argv[4], argv[5], argv[6], argv[7],
argv[8]);
}
================================================
FILE: src/Native/dialog.js
================================================
// Provides: revery_alertSupported
function revery_alertSupported() {
return true;
}
// Provides: revery_alert
function revery_alert(win, msg) {
joo_global_object.alert(msg);
}
================================================
FILE: src/Native/dialog_cocoa.c
================================================
#include "config.h"
#ifdef USE_COCOA
#include
#import
#import "ReveryProgressBar.h"
void revery_alert_cocoa(void *pWin, const char *szMessage) {
(void)pWin;
NSAlert *alert = [[NSAlert alloc] init];
NSString *message = [NSString stringWithUTF8String:szMessage];
[alert addButtonWithTitle:@"Ok"];
[alert setMessageText:@"Alert"];
[alert setInformativeText:message];
[alert runModal];
[alert release];
[message release];
}
const char **revery_open_files_cocoa(
const char *startDir, const char *fileTypes[], int fileTypesSize,
int allowMultiple, int canChooseFiles, int canChooseDirectories,
int showHidden, const char *buttonText, const char *title) {
// Grab the key window, so we can restore focus after dialog..
NSWindow *keyWindow = [NSApp keyWindow];
/* Creates an empty NSArray of filetypes (NSString's)
If [fileTypes] is not null, copy the C-strings to NSString's to the
NSArray
*/
NSArray *nsFileTypes = NULL;
if (fileTypes) {
NSMutableArray *tmpArr =
[[NSMutableArray alloc] initWithCapacity:fileTypesSize];
for (int i = 0; i < fileTypesSize; i++) {
// Convert char* -> NSString
NSString *str = [NSString stringWithCString:fileTypes[i]
encoding:NSUTF8StringEncoding];
[tmpArr addObject:str];
}
nsFileTypes = tmpArr;
}
// The actual dialog itself
NSOpenPanel *panel = [NSOpenPanel openPanel];
// We can directly set these because it will either be 0 or 1
[panel setAllowsMultipleSelection:allowMultiple];
[panel setCanChooseFiles:canChooseFiles];
[panel setCanChooseDirectories:canChooseDirectories];
[panel setShowsHiddenFiles:showHidden];
if (startDir) {
// If [startDir] is not NULL, convert it to an NSString...
NSString *urlString =
[NSString stringWithCString:startDir encoding:NSUTF8StringEncoding];
// ...and then to an NSURL
NSURL *url = [NSURL fileURLWithPath:urlString];
[panel setDirectoryURL:url];
}
if (title) {
[panel setMessage:[NSString stringWithCString:title
encoding:NSUTF8StringEncoding]];
[panel setTitle:[NSString stringWithCString:title
encoding:NSUTF8StringEncoding]];
}
if (buttonText)
[panel setPrompt:[NSString stringWithCString:buttonText
encoding:NSUTF8StringEncoding]];
if (nsFileTypes) [panel setAllowedFileTypes:nsFileTypes];
// Run the actual panel/modal
NSInteger result = [panel runModal];
[panel release];
[nsFileTypes release];
// If a file(s) was selected...
if (result == NSModalResponseOK) {
// ...get the list of URLs
NSArray *urls = [panel URLs];
/* Create a C-array with the size + 1 of the NSArray
We NULL terminate it so we can get the size in the main function
*/
int size = [urls count];
int actualSize = 0;
const char **ret = malloc((size + 1) * sizeof(char *));
// Copy the NSArray to the C-array
for (int i = 0; i < size; i++) {
NSString *tmp = [[urls objectAtIndex:i] path];
const char *sz= [tmp cStringUsingEncoding:NSUTF8StringEncoding];
// According to the Objective-C docs, the returned string
// is only guaranteed to be valid for the lifetime of
// the [NSString]:
// https://developer.apple.com/documentation/foundation/nsstring/1408489-cstringusingencoding?language=objc
// So we need to make a copy here...
if (sz != NULL) {
ret[actualSize] = strdup(sz);
actualSize++;
}
}
[urls release];
ret[actualSize] = NULL;
[keyWindow makeKeyWindow];
return ret;
} else {
// ...else return NULL
[keyWindow makeKeyWindow];
return NULL;
}
}
#endif
================================================
FILE: src/Native/dialog_gtk.c
================================================
#include "config.h"
#ifdef USE_GTK
#include
#include
// The callback to g_signal_connect MUST be an `activate` function
static void activate(GtkApplication *app, const char *user_data) {
(void)app;
GtkWidget *dialog;
GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
dialog = gtk_message_dialog_new(NULL, flags, GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE, "%s", user_data);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
void revery_alert_gtk(void *pWin, const char *szMessage) {
/*
* TODO:
* 1. figure out how to convert the pointer from an X11 window handle
* to a GTK window, see (for inspiration):
* https://gist.github.com/mmozeiko/2401933b1fa89e5d5bd238b33eab0465
*
* 2. Get reference to revery application, is there an existing
* gtk application reference when a glfw window is created that can be
* reused?
*/
(void)pWin;
GtkApplication *app;
app = gtk_application_new("org.gtk.revery", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate),
(gpointer)szMessage);
/* argv the final argument to run can be set to NULL in which case argc
* should be set to 0 */
g_application_run(G_APPLICATION(app), 0, NULL);
g_object_unref(app);
}
struct FileChooserOptions {
const char *startDir;
const char **fileTypes;
int fileTypesSize;
int allowMultiple;
int canChooseFiles;
int canChooseDirectories;
int showHidden;
const char *buttonText;
const char *title;
const char **result;
};
void activate_filechooser(GtkApplication *app, struct FileChooserOptions *options) {
(void)app;
GtkFileChooserAction action = options->canChooseDirectories ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : GTK_FILE_CHOOSER_ACTION_OPEN;
gint result;
const char *okButtonText = (options->buttonText ? options->buttonText : "Open");
const char *dialogTitle = (options->title ? options->title : "Open File(s) and/or Folder(s)");
GtkWidget *dialog = gtk_file_chooser_dialog_new(
dialogTitle, NULL, action, "Cancel", GTK_RESPONSE_CANCEL, okButtonText,
GTK_RESPONSE_ACCEPT, NULL);
GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
gtk_file_chooser_set_show_hidden(chooser, options->showHidden);
gtk_file_chooser_set_select_multiple(chooser, options->allowMultiple);
if (options->fileTypes) {
char *wildcard = "*.";
char *name = "";
GtkFileFilter *filter = gtk_file_filter_new();
for (int i = 0; i < options->fileTypesSize; i++) {
const char *fileType = options->fileTypes[i];
char *pattern = malloc(strlen(wildcard) + strlen(fileType) + 1);
strcpy(pattern, wildcard);
strcat(pattern, fileType);
name = g_strjoin(" ", name, pattern, NULL);
gtk_file_filter_add_pattern(filter, pattern);
}
gtk_file_filter_set_name(filter, name);
gtk_file_chooser_add_filter(chooser, filter);
}
result = gtk_dialog_run(GTK_DIALOG(dialog));
if (result == GTK_RESPONSE_ACCEPT) {
GSList *filenames = gtk_file_chooser_get_filenames(chooser);
int size = g_slist_length(filenames);
options->result = malloc((size + 1) * sizeof(char *));
for (int i = 0; i < size; i++) {
options->result[i] = (char *) g_slist_nth_data(filenames, i);
}
options->result[size] = NULL;
} else {
options->result = NULL;
}
gtk_widget_destroy(dialog);
}
const char **revery_open_files_gtk(const char *startDir, const char *fileTypes[],
int fileTypesSize, int allowMultiple,
int canChooseFiles, int canChooseDirectories,
int showHidden, const char *buttonText,
const char *title) {
struct FileChooserOptions options = {
startDir,
fileTypes,
fileTypesSize,
allowMultiple,
canChooseFiles,
canChooseDirectories,
showHidden,
buttonText,
title,
NULL
};
GtkApplication *app;
app = gtk_application_new("org.gtk.revery", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate_filechooser),
(gpointer)&options);
/* argv the final argument to run can be set to NULL in which case argc
* should be set to 0 */
g_application_run(G_APPLICATION(app), 0, NULL);
g_object_unref(app);
return options.result;
}
#endif
================================================
FILE: src/Native/dialog_win32.c
================================================
#include "config.h"
#ifdef USE_WIN32
#include
#include
#include
#include
#include
void revery_alert_win32(void *pWin, const char *szMessage) {
HWND hwnd = (HWND)pWin;
MessageBox(hwnd, szMessage, "Alert", MB_ICONWARNING | MB_OK);
}
const char **revery_open_files_win32(const char *startDir, int canChooseFiles,
int canChooseDirectories,
const char *title) {
(void)startDir;
(void)canChooseFiles;
// Default return - null
const char **ret = malloc(1 * sizeof(char *));
ret[0] = NULL;
// Right now, we split - either choose a directory, _or_ a file
if (canChooseDirectories) {
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr)) {
TCHAR szDir[MAX_PATH];
BROWSEINFO bInfo;
bInfo.hwndOwner = NULL;
bInfo.pidlRoot = NULL;
bInfo.pszDisplayName = szDir;
bInfo.lpszTitle = title;
bInfo.ulFlags = BIF_USENEWUI | BIF_NEWDIALOGSTYLE;
bInfo.lpfn = NULL;
bInfo.lParam = 0;
bInfo.iImage = -1;
LPITEMIDLIST lpItem = SHBrowseForFolder(&bInfo);
if (lpItem != NULL) {
if (SHGetPathFromIDList(lpItem, szDir)) {
// free the 'default' return
free(ret);
ret = malloc(2 * sizeof(char *));
ret[0] = strdup(szDir);
ret[1] = NULL;
}
}
}
} else {
// TODO: Use the allowed files array
char *filter = "All Files (*.*)\0*.*\0";
OPENFILENAME ofn;
char fileName[MAX_PATH] = "";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = filter;
ofn.lpstrFile = fileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = "";
if (GetOpenFileName(&ofn)) {
// free the 'default' return
free(ret);
ret = malloc(2 * sizeof(char *));
ret[0] = strdup(fileName);
ret[1] = NULL;
}
}
return ret;
}
#endif
================================================
FILE: src/Native/dune
================================================
(library
(name Revery_Native)
(public_name Revery.Native)
(preprocess
(pps lwt_ppx ppx_optcomp))
(preprocessor_deps config.h)
(library_flags
(:include flags.sexp))
(js_of_ocaml
(javascript_files dialog.js))
(foreign_stubs
(language c)
(names Revery_Native dialog dialog_cocoa dialog_win32 dialog_gtk
notification notification_cocoa environment environment_mac
environment_linux environment_windows icon icon_cocoa icon_win32 shell
shell_cocoa shell_gtk shell_win32 locale locale_cocoa locale_win32 menu
menu_cocoa input input_cocoa window window_cocoa utilities ReveryGtk
ReveryGtk_Widget ReveryAppDelegate ReveryAppDelegate_func ReveryNSObject
ReveryNSView ReveryNSViewCoords ReveryMenuItemTarget ReveryButtonTarget
ReveryProgressBar)
(flags
:standard
-Wall
-Wextra
-Werror
(:include c_flags.sexp)))
(c_library_flags
(:include c_library_flags.sexp))
(libraries sdl2 lru))
(copy_files cocoa/*)
(copy_files gtk/*)
(rule
(targets config.h flags.sexp c_flags.sexp c_library_flags.sexp)
(deps
(:discover config/discover.exe))
(action
(run %{discover})))
================================================
FILE: src/Native/environment.c
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "config.h"
#ifdef IS_MACOS
#import "ReveryMac.h"
#elif IS_LINUX
#include "ReveryLinux.h"
#elif IS_WINDOWS
#include "ReveryWindows.h"
#endif
CAMLprim value revery_getOperatingSystem() {
CAMLparam0();
CAMLlocal1(vOS);
#ifdef IS_ANDROID
vOS = Val_int(1);
#elif IS_IOS
vOS = Val_int(2);
#elif IS_LINUX
int kernel, major, minor, patch;
getOperatingSystemVersion_linux(&kernel, &major, &minor, &patch);
vOS = caml_alloc(4, 1);
Store_field(vOS, 0, Val_int(kernel));
Store_field(vOS, 1, Val_int(major));
Store_field(vOS, 2, Val_int(minor));
Store_field(vOS, 3, Val_int(patch));
#elif IS_WINDOWS
int major, minor, build;
getOperatingSystemVersion_windows(&major, &minor, &build);
vOS = caml_alloc(3, 2);
Store_field(vOS, 0, Val_int(major));
Store_field(vOS, 1, Val_int(minor));
Store_field(vOS, 2, Val_int(build));
#elif IS_MACOS
int major, minor, bugfix;
getOperatingSystemVersion_mac(&major, &minor, &bugfix);
vOS = caml_alloc(3, 0);
Store_field(vOS, 0, Val_int(major));
Store_field(vOS, 1, Val_int(minor));
Store_field(vOS, 2, Val_int(bugfix));
#else
vOS = Val_int(0);
#endif
CAMLreturn(vOS);
}
================================================
FILE: src/Native/environment_linux.c
================================================
#include "config.h"
#ifdef IS_LINUX
#include
#include
#include
#include
void getOperatingSystemVersion_linux(int *kernel, int *major, int *minor, int *patch) {
long ver[16];
struct utsname unameInfo;
uname(&unameInfo);
char *cursor = unameInfo.release;
int i = 0;
while (*cursor) {
if (isdigit(*cursor)) {
ver[i] = strtol(cursor, &cursor, 10);
i++;
} else {
cursor++;
}
}
*kernel = (int)ver[0];
*major = (int)ver[1];
*minor = (int)ver[2];
*patch = (int)ver[3];
}
#endif
================================================
FILE: src/Native/environment_mac.c
================================================
#include "config.h"
#ifdef IS_MACOS
#import
void getOperatingSystemVersion_mac(int *major, int *minor, int *bugfix) {
NSOperatingSystemVersion nsOSVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
*major = nsOSVersion.majorVersion;
*minor = nsOSVersion.minorVersion;
*bugfix = nsOSVersion.patchVersion;
}
#endif
================================================
FILE: src/Native/environment_windows.c
================================================
#include "config.h"
#ifdef IS_WINDOWS
#include
#include
void getOperatingSystemVersion_windows(int *major, int *minor, int *build) {
OSVERSIONINFOW osVersion;
ZeroMemory(&osVersion, sizeof(OSVERSIONINFOW));
osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
NTSTATUS (WINAPI *rtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;
HINSTANCE hNtDll = LoadLibrary("ntdll.dll");
if (hNtDll != NULL) {
rtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))GetProcAddress(hNtDll, "RtlGetVersion");
if (rtlGetVersion != NULL) {
rtlGetVersion(&osVersion);
}
FreeLibrary(hNtDll);
}
if (rtlGetVersion == NULL) {
GetVersionEx((OSVERSIONINFO*)&osVersion);
}
*major = (int)osVersion.dwMajorVersion;
*minor = (int)osVersion.dwMinorVersion;
*build = (int)osVersion.dwBuildNumber;
}
#endif
================================================
FILE: src/Native/gtk/ReveryGtk.c
================================================
#include "config.h"
#ifdef USE_GTK
#include
#include
#include
#include
#include
CAMLprim value revery_gtkEventsPending() {
CAMLparam0();
gboolean gEventsPending = gtk_events_pending();
CAMLreturn(Val_bool(gEventsPending));
}
CAMLprim value revery_gtkMainIteration() {
CAMLparam0();
gboolean gMainIteration = gtk_main_iteration();
CAMLreturn(Val_bool(gMainIteration));
}
#endif
================================================
FILE: src/Native/gtk/ReveryGtk_Widget.c
================================================
#include "config.h"
#ifdef USE_GTK
#include
#include
#include
#include
#include
#include
#include
#include "utilities.h"
static void revery_gtkRealize(GtkWidget *gWidget, gpointer data) {
gtk_widget_set_window(gWidget, (GdkWindow *)data);
}
CAMLprim value revery_createGtkWidgetFromXWindow(value vXWindow) {
CAMLparam1(vXWindow);
Window xWindow = (Window)vXWindow;
GdkDisplay *gDisplay = gdk_display_get_default();
XMapRaised(GDK_DISPLAY_XDISPLAY(gDisplay), xWindow);
GdkWindow *gWindow =
gdk_x11_window_foreign_new_for_display(gDisplay, xWindow);
GtkWidget *gWidget = gtk_widget_new(GTK_TYPE_WINDOW, NULL);
g_signal_connect(gWidget, "realize", G_CALLBACK(revery_gtkRealize), gWindow);
gtk_widget_set_has_window(gWidget, TRUE);
gtk_widget_realize(gWidget);
value camlWidget = revery_wrapPointer(gWidget);
CAMLreturn(camlWidget);
}
CAMLprim value revery_gtkWidgetShowAll(value vWidget) {
CAMLparam1(vWidget);
GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);
gtk_widget_show_all(gWidget);
CAMLreturn(Val_unit);
}
CAMLprim value revery_gtkWidgetGetPath(value vWidget) {
CAMLparam1(vWidget);
CAMLlocal1(vPathStr);
GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);
GtkWidgetPath *gWidgetPath = gtk_widget_get_path(gWidget);
char *pathStr = gtk_widget_path_to_string(gWidgetPath);
vPathStr = caml_copy_string(pathStr);
free(pathStr);
CAMLreturn(vPathStr);
}
CAMLprim value revery_gtkWidgetSetOpacity(value vWidget, value vOpacity) {
CAMLparam2(vWidget, vOpacity);
GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);
double opacity = Double_val(vOpacity);
gtk_widget_set_opacity(gWidget, opacity);
CAMLreturn(Val_unit);
}
CAMLprim value revery_gtkWidgetDestroy(value vWidget) {
CAMLparam1(vWidget);
GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);
gtk_widget_destroy(gWidget);
CAMLreturn(Val_unit);
}
CAMLprim value revery_gtkWidgetGetOpacity(value vWidget) {
CAMLparam1(vWidget);
GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);
double opacity = gtk_widget_get_opacity(gWidget);
CAMLreturn(caml_copy_double(opacity));
}
CAMLprim value revery_gtkWidgetGetDepth(value vWidget) {
CAMLparam1(vWidget);
GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);
int depth;
for (depth = 0; gWidget != NULL; depth++) {
gWidget = gtk_widget_get_parent(gWidget);
}
CAMLreturn(Val_int(depth));
}
#endif
================================================
FILE: src/Native/icon.c
================================================
#include
#include
#include
#include
#include
#include
#include "caml_values.h"
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#elif USE_GTK
#include "ReveryGtk.h"
#endif
#include "utilities.h"
CAMLprim value revery_getIconHandle() {
CAMLparam0();
void *ret;
#ifdef USE_COCOA
ret = revery_getIconHandle_cocoa();
#elif USE_WIN32
ret = revery_getIconHandle_win32();
#else
fprintf(stderr, "WARNING: %s is not implemented on this platform.", __func__);
ret = NULL;
#endif
value vIconHandle = revery_wrapPointer(ret);
CAMLreturn(vIconHandle);
}
CAMLprim value revery_setIconProgress(value vWin, value vIconHandle,
value vProgress) {
CAMLparam3(vWin, vIconHandle, vProgress);
void *win = (void *)revery_unwrapPointer(vWin);
void *ih = (void *)revery_unwrapPointer(vIconHandle);
/* vProgress is an OCaml variant of type Revery_Native.Icon.progress
It can be either:
- Indeterminate -- has no type arguments
- Determinate -- has 1 type argument of float
*/
// If vProgress "is long", it has no type arguments, and is therefore indeterminate
if (Is_long(vProgress)) {
#ifdef USE_COCOA
(void)win;
revery_setIconProgressIndeterminate_cocoa(ih);
#elif USE_WIN32
revery_setIconProgressIndeterminate_win32(win, ih);
#else
fprintf(stderr, "WARNING: %s is not implemented on this platform.", __func__);
#endif
} else if (Is_block(vProgress)) { // If vProgress "is block", it has a type argument and must be determinate
float progress = Double_val(Field(vProgress, 0));
#ifdef USE_COCOA
(void)win;
revery_setIconProgress_cocoa(ih, progress);
#elif USE_WIN32
revery_setIconProgress_win32(win, ih, progress);
#else
fprintf(stderr, "WARNING: %s is not implemented on this platform.", __func__);
(void)win;
(void)ih;
(void)progress;
#endif
}
CAMLreturn(Val_unit);
}
CAMLprim value revery_hideIconProgress(value vWin, value vIconHandle) {
CAMLparam2(vWin, vIconHandle);
void *win = (void *)revery_unwrapPointer(vWin);
void *ih = (void *)revery_unwrapPointer(vIconHandle);
#ifdef USE_COCOA
(void)win;
revery_hideIconProgress_cocoa(ih);
#elif USE_WIN32
revery_hideIconProgress_win32(win, ih);
#else
(void)win;
(void)ih;
#endif
CAMLreturn(Val_unit);
}
================================================
FILE: src/Native/icon_cocoa.c
================================================
#include "config.h"
#ifdef USE_COCOA
#include
#import
#import "ReveryProgressBar.h"
void *revery_getIconHandle_cocoa() {
NSDockTile *dock_tile = [NSApp dockTile];
return dock_tile;
}
NSProgressIndicator *get_progress_indicator(NSDockTile *dock_tile) {
// Determine if there is already a progress indicator attached to the dock icon
int first_render = !dock_tile.contentView ||
[[dock_tile.contentView subviews] count] == 0 ||
![[[dock_tile.contentView subviews] lastObject]
isKindOfClass:[NSProgressIndicator class]];
NSProgressIndicator *progress_indicator;
if (first_render) {
NSRect frame = NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0f);
progress_indicator = [[ReveryProgressBar alloc] initWithFrame:frame];
[progress_indicator setBezeled:YES];
[progress_indicator setMinValue:0];
[progress_indicator setMaxValue:1];
[progress_indicator setHidden:NO];
[dock_tile.contentView addSubview:progress_indicator];
} else {
progress_indicator = (NSProgressIndicator *)[[[dock_tile contentView] subviews] lastObject];
}
return progress_indicator;
}
void revery_setIconProgress_cocoa(void *dt, double progress) {
NSDockTile *dock_tile = (NSDockTile *)dt;
NSProgressIndicator *progress_indicator = get_progress_indicator(dock_tile);
[progress_indicator setIndeterminate:NO];
[progress_indicator setDoubleValue:progress];
[progress_indicator setHidden:NO];
[dock_tile display];
}
void revery_setIconProgressIndeterminate_cocoa(void *dt) {
NSDockTile *dock_tile = (NSDockTile *)dt;
NSProgressIndicator *progress_indicator = get_progress_indicator(dock_tile);
[progress_indicator setIndeterminate:YES];
[progress_indicator setDoubleValue:1];
[progress_indicator setHidden:NO];
[progress_indicator setUsesThreadedAnimation:YES];
[progress_indicator startAnimation:progress_indicator];
[dock_tile display];
}
void revery_hideIconProgress_cocoa(void *dt) {
NSDockTile *dock_tile = (NSDockTile *)dt;
NSProgressIndicator *progress_indicator = get_progress_indicator(dock_tile);
[progress_indicator setHidden:YES];
[progress_indicator release];
[[NSApp dockTile] display];
}
#endif
================================================
FILE: src/Native/icon_win32.c
================================================
#include "config.h"
#ifdef USE_WIN32
#include
#include
#include
#include
void *revery_getIconHandle_win32() {
ITaskbarList3 *tbl;
CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,
&IID_ITaskbarList3, (void**)&tbl);
return (void *)tbl;
}
void revery_setIconProgress_win32(void *win, void *ih, float progress) {
HWND window = (HWND)win;
ITaskbarList3 *iconHandle = (ITaskbarList3 *)ih;
iconHandle->lpVtbl->SetProgressState(iconHandle, window, TBPF_NORMAL);
iconHandle->lpVtbl->SetProgressValue(iconHandle, window, progress * 100, 100);
}
void revery_setIconProgressIndeterminate_win32(void *win, void *ih) {
HWND window = (HWND)win;
ITaskbarList3 *iconHandle = (ITaskbarList3 *)ih;
iconHandle->lpVtbl->SetProgressState(iconHandle, window, TBPF_INDETERMINATE);
}
void revery_hideIconProgress_win32(void *win, void *ih) {
HWND window = (HWND)win;
ITaskbarList3 *iconHandle = (ITaskbarList3 *)ih;
iconHandle->lpVtbl->SetProgressState(iconHandle, window, TBPF_NOPROGRESS);
}
#endif
================================================
FILE: src/Native/input.c
================================================
#include
#include
#include
#include
#include
#include "caml_values.h"
#include "menu.h"
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#import
#elif USE_GTK
#include "ReveryGtk.h"
#endif
#include "utilities.h"
CAMLprim value revery_buttonCreate(value vTitle) {
CAMLparam1(vTitle);
CAMLlocal1(vButton);
const char *title = String_val(vTitle);
void *button;
#ifdef USE_COCOA
button = revery_buttonCreate_cocoa(title);
[(NSObject *)button retain];
#else
UNUSED(title);
button = NULL;
#endif
vButton = revery_wrapPointer(button);
CAMLreturn(vButton);
}
CAMLprim value revery_buttonSetColor(value vButton, value vRed, value vGreen, value vBlue, value vAlpha) {
CAMLparam5(vButton, vRed, vGreen, vBlue, vAlpha);
void *button = revery_unwrapPointer(vButton);
double red = Double_val(vRed);
double green = Double_val(vGreen);
double blue = Double_val(vBlue);
double alpha = Double_val(vAlpha);
#ifdef USE_COCOA
revery_buttonSetColor_cocoa(button, red, green, blue, alpha);
#else
UNUSED(button);
UNUSED(red);
UNUSED(green);
UNUSED(blue);
UNUSED(alpha);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_openEmojiPanel() {
CAMLparam0();
#ifdef USE_COCOA
revery_openEmojiPanel_cocoa();
#endif
CAMLreturn(Val_unit);
}
================================================
FILE: src/Native/input_cocoa.c
================================================
#include "config.h"
#ifdef USE_COCOA
#include
#include "menu.h"
#import
#import "ReveryButtonTarget.h"
#import "ReveryNSViewCoords.h"
static ReveryButtonTarget *buttonTarget;
NSButton *revery_buttonCreate_cocoa(const char *title) {
if (buttonTarget == NULL) {
buttonTarget = [[ReveryButtonTarget alloc] init];
}
NSString *nsTitle = [NSString stringWithUTF8String:title];
NSButton *nsButton =
[NSButton buttonWithTitle:nsTitle target:buttonTarget action:@selector(onButtonClick:)];
[nsButton setReveryX:0.0];
[nsButton setReveryY:0.0];
return nsButton;
}
void revery_buttonSetColor_cocoa(NSButton *nsButton, double red, double green, double blue, double alpha) {
NSColor *color = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha];
NSMutableAttributedString *styledTitle = [[NSMutableAttributedString alloc] initWithAttributedString:[nsButton attributedTitle]];
NSRange titleRange = NSMakeRange(0, [styledTitle length]);
[styledTitle addAttribute:NSForegroundColorAttributeName value:color range:titleRange];
[nsButton setAttributedTitle:styledTitle];
}
void revery_openEmojiPanel_cocoa() {
[NSApp orderFrontCharacterPalette:nil];
}
#endif
================================================
FILE: src/Native/locale.c
================================================
#include
#include
#include
#include
#include
#include
#include "caml_values.h"
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#elif USE_GTK
#include "ReveryGtk.h"
#endif
#if defined(__linux__) || defined(__APPLE__)
#include
#endif
CAMLprim value revery_getUserLocale() {
CAMLparam0();
CAMLlocal1(camlRet);
char *ret;
int shouldFree;
#ifdef USE_COCOA
ret = revery_getUserLocale_cocoa();
shouldFree = 0;
#elif USE_WIN32
ret = revery_getUserLocale_win32();
shouldFree = 1;
#else
setlocale(LC_CTYPE, "");
ret = setlocale(LC_CTYPE, NULL);
shouldFree = 0;
#endif
camlRet = caml_copy_string(ret);
if (shouldFree) {
free(ret);
}
CAMLreturn(camlRet);
}
================================================
FILE: src/Native/locale_cocoa.c
================================================
#include "config.h"
#ifdef USE_COCOA
#include
#import
char *revery_getUserLocale_cocoa() {
NSLocale *nsLocale = [NSLocale currentLocale];
return (char *)[[nsLocale localeIdentifier] UTF8String];
}
#endif
================================================
FILE: src/Native/locale_win32.c
================================================
#ifdef _WIN32
#include "win32_target.h"
#include
#include
char *revery_getUserLocale_win32() {
WCHAR locale[LOCALE_NAME_MAX_LENGTH];
char *localeStr = malloc(LOCALE_NAME_MAX_LENGTH * sizeof(char));
GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH);
wcstombs(localeStr, locale, sizeof(localeStr));
return localeStr;
}
#endif
================================================
FILE: src/Native/menu.c
================================================
#include
#include
#include
#include
#include
#include "caml_values.h"
#include "menu.h"
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#import
#elif USE_GTK
#include "ReveryGtk.h"
#endif
#include "utilities.h"
CAMLprim value revery_getMenuBarHandle() {
CAMLparam0();
CAMLlocal1(vMenuBarHandle);
void *handle;
#ifdef USE_COCOA
handle = revery_getMenuBarHandle_cocoa();
[(NSObject *)handle retain];
#else
handle = NULL;
#endif
vMenuBarHandle = revery_wrapPointer(handle);
CAMLreturn(vMenuBarHandle);
}
CAMLprim value revery_menuCreate(value vTitle) {
CAMLparam1(vTitle);
CAMLlocal1(vMenu);
const char *title = String_val(vTitle);
void *menu;
#ifdef USE_COCOA
menu = revery_menuCreate_cocoa(title);
[(NSObject *)menu retain];
#else
UNUSED(title);
menu = NULL;
#endif
vMenu = revery_wrapPointer(menu);
CAMLreturn(vMenu);
}
void convertCamlKeyEquivalent(value vKeyEquivalent, struct KeyEquivalent *keyEquivalent) {
keyEquivalent->str = String_val(Field(vKeyEquivalent, 0));
keyEquivalent->alt = Bool_val(Field(vKeyEquivalent, 1));
keyEquivalent->shift = Bool_val(Field(vKeyEquivalent, 2));
keyEquivalent->ctrl = Bool_val(Field(vKeyEquivalent, 3));
}
CAMLprim value revery_menuItemCreate(value vTitle, value vKeyEquivalent) {
CAMLparam2(vTitle, vKeyEquivalent);
CAMLlocal1(vMenuItem);
struct KeyEquivalent keyEquivalent;
if (vKeyEquivalent != Val_none) {
convertCamlKeyEquivalent(Some_val(vKeyEquivalent), &keyEquivalent);
}
const char *title = String_val(vTitle);
void *menuItem;
#ifdef USE_COCOA
menuItem = revery_menuItemCreate_cocoa(title, vKeyEquivalent == Val_none ? NULL : &keyEquivalent);
[(NSObject *)menuItem retain];
#else
UNUSED(title);
menuItem = NULL;
#endif
vMenuItem = revery_wrapPointer(menuItem);
CAMLreturn(vMenuItem);
}
CAMLprim value revery_menuNth(value vMenu, value vIdx) {
CAMLparam2(vMenu, vIdx);
CAMLlocal1(vMenuItem);
void *menu = revery_unwrapPointer(vMenu);
int idx = Int_val(vIdx);
void *menuItem;
#ifdef USE_COCOA
menuItem = revery_menuNth_cocoa(menu, idx);
if (menuItem != NULL) {
[(NSObject *)menuItem retain];
}
#else
UNUSED(menu);
UNUSED(idx);
menuItem = NULL;
#endif
vMenuItem = revery_wrapOptionalPointer(menuItem);
CAMLreturn(vMenuItem);
}
CAMLprim value revery_menuAddItem(value vMenu, value vMenuItem) {
CAMLparam2(vMenu, vMenuItem);
void *menu = revery_unwrapPointer(vMenu);
void *menuItem = revery_unwrapPointer(vMenuItem);
#ifdef USE_COCOA
revery_menuAddItem_cocoa(menu, menuItem);
#else
UNUSED(menu);
UNUSED(menuItem);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuItemGetSubmenu(value vMenuItem) {
CAMLparam1(vMenuItem);
CAMLlocal1(vSubmenu);
void *menuItem = revery_unwrapPointer(vMenuItem);
void *submenu;
#ifdef USE_COCOA
submenu = revery_menuItemGetSubmenu_cocoa(menuItem);
if (submenu != NULL) {
[(NSObject *)submenu retain];
}
#else
UNUSED(menuItem);
submenu = NULL;
#endif
vSubmenu = revery_wrapOptionalPointer(submenu);
CAMLreturn(vSubmenu);
}
CAMLprim value revery_menuRemoveItem(value vMenu, value vMenuItem) {
CAMLparam2(vMenu, vMenuItem);
void *menu = revery_unwrapPointer(vMenu);
void *menuItem = revery_unwrapPointer(vMenuItem);
#ifdef USE_COCOA
revery_menuRemoveItem_cocoa(menu, menuItem);
#else
UNUSED(menu);
UNUSED(menuItem);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuAddSubmenu(value vParent, value vChild) {
CAMLparam2(vParent, vChild);
void *parent = revery_unwrapPointer(vParent);
void *child = revery_unwrapPointer(vChild);
#ifdef USE_COCOA
revery_menuAddSubmenu_cocoa(parent, child);
#else
UNUSED(parent);
UNUSED(child);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuRemoveSubmenu(value vParent, value vChild) {
CAMLparam2(vParent, vChild);
void *parent = revery_unwrapPointer(vParent);
void *child = revery_unwrapPointer(vChild);
#ifdef USE_COCOA
revery_menuRemoveSubmenu_cocoa(parent, child);
#else
UNUSED(parent);
UNUSED(child);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuInsertItemAt(value vMenu, value vMenuItem, value vIdx) {
CAMLparam3(vMenu, vMenuItem, vIdx);
void *menu = revery_unwrapPointer(vMenu);
void *menuItem = revery_unwrapPointer(vMenuItem);
int idx = Int_val(vIdx);
#ifdef USE_COCOA
revery_menuInsertItemAt_cocoa(menu, menuItem, idx);
#else
UNUSED(menu);
UNUSED(menuItem);
UNUSED(idx);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuInsertSubmenuAt(value vParent, value vChild, value vIdx) {
CAMLparam3(vParent, vChild, vIdx);
void *parent = revery_unwrapPointer(vParent);
void *child = revery_unwrapPointer(vChild);
int idx = Int_val(vIdx);
#ifdef USE_COCOA
revery_menuInsertSubmenuAt_cocoa(parent, child, idx);
#else
UNUSED(parent);
UNUSED(child);
UNUSED(idx);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuClear(value vMenu) {
CAMLparam1(vMenu);
void *menu = revery_unwrapPointer(vMenu);
#ifdef USE_COCOA
revery_menuClear_cocoa(menu);
#else
UNUSED(menu);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuDisplayIn(value vMenu, value vWindow, value vX, value vY) {
CAMLparam4(vMenu, vWindow, vX, vY);
void *menu = revery_unwrapPointer(vMenu);
void *window = revery_unwrapPointer(vWindow);
int x = Int_val(vX);
int y = Int_val(vY);
#ifdef USE_COCOA
revery_menuDisplayIn_cocoa(menu, window, x, y);
#else
UNUSED(menu);
UNUSED(window);
UNUSED(x);
UNUSED(y);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuItemCreateSeparator() {
CAMLparam0();
CAMLlocal1(vSeparator);
void *separator;
#ifdef USE_COCOA
separator = revery_menuItemCreateSeparator_cocoa();
#else
separator = NULL;
#endif
vSeparator = revery_wrapPointer(separator);
CAMLreturn(vSeparator);
}
CAMLprim value revery_menuItemSetEnabled(value vMenuItem, value vTruth) {
CAMLparam2(vMenuItem, vTruth);
void *menuItem = revery_unwrapPointer(vMenuItem);
int truth = Bool_val(vTruth);
#ifdef USE_COCOA
revery_menuItemSetEnabled_cocoa(menuItem, truth);
#else
UNUSED(menuItem);
UNUSED(truth);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_menuItemSetVisible(value vMenuItem, value vTruth) {
CAMLparam2(vMenuItem, vTruth);
void *menuItem = revery_unwrapPointer(vMenuItem);
int truth = Bool_val(vTruth);
#ifdef USE_COCOA
revery_menuItemSetVisible_cocoa(menuItem, truth);
#else
UNUSED(menuItem);
UNUSED(truth);
#endif
CAMLreturn(Val_unit);
}
================================================
FILE: src/Native/menu.h
================================================
#pragma once
struct KeyEquivalent {
const char *str;
int alt;
int shift;
int ctrl;
};
================================================
FILE: src/Native/menu_cocoa.c
================================================
#include "config.h"
#ifdef USE_COCOA
#include
#include "menu.h"
#import
#import "ReveryMenuItemTarget.h"
static ReveryMenuItemTarget *menuItemTarget;
NSMenu *revery_getMenuBarHandle_cocoa() {
return [[NSApplication sharedApplication] mainMenu];
}
NSMenu *revery_menuCreate_cocoa(const char *title) {
NSString *nsTitle = [NSString stringWithUTF8String:title];
NSMenu *nsMenu = [[NSMenu alloc] initWithTitle:nsTitle];
[nsMenu setAutoenablesItems:NO];
return nsMenu;
}
NSMenuItem *revery_menuItemCreate_cocoa(const char *title, struct KeyEquivalent *keyEquivalent) {
if (menuItemTarget == NULL) {
menuItemTarget = [[ReveryMenuItemTarget alloc] init];
}
NSString *nsTitle = [NSString stringWithUTF8String:title];
NSMenuItem *nsMenuItem =
[[NSMenuItem alloc] initWithTitle:nsTitle action:@selector(onMenuItemClick:) keyEquivalent:@""];
[nsMenuItem setTarget:menuItemTarget];
if (keyEquivalent != NULL) {
NSEventModifierFlags modifierFlags = NSCommandKeyMask;
[nsMenuItem setKeyEquivalent:[NSString stringWithUTF8String:keyEquivalent->str]];
if (keyEquivalent->alt) {
modifierFlags |= NSEventModifierFlagOption;
}
if (keyEquivalent->shift) {
modifierFlags |= NSEventModifierFlagShift;
}
if (keyEquivalent->ctrl) {
modifierFlags |= NSEventModifierFlagControl;
}
[nsMenuItem setKeyEquivalentModifierMask:modifierFlags];
}
return nsMenuItem;
}
NSMenuItem *revery_menuNth_cocoa(NSMenu *nsMenu, int idx) {
if (idx > [nsMenu numberOfItems]) {
return NULL;
}
return [nsMenu itemAtIndex:idx];
}
void revery_menuAddItem_cocoa(NSMenu *nsMenu, NSMenuItem *nsMenuItem) {
[nsMenu addItem:nsMenuItem];
}
void *revery_menuItemGetSubmenu_cocoa(NSMenuItem *nsMenuItem) {
if (![nsMenuItem hasSubmenu]) {
return NULL;
}
return [nsMenuItem submenu];
}
void revery_menuAddSubmenu_cocoa(NSMenu *parent, NSMenu *child) {
NSMenuItem *nsMenuItem =
[[NSMenuItem alloc] initWithTitle:[child title] action:NULL keyEquivalent:@""];
[parent addItem:nsMenuItem];
[parent setSubmenu:child forItem:nsMenuItem];
}
void revery_menuRemoveSubmenu_cocoa(NSMenu *parent, NSMenu *child) {
NSMutableArray *itemsToRemove = [NSMutableArray array];
for (NSMenuItem *item in [parent itemArray]) {
if ([child isEqual:[item submenu]]) {
[itemsToRemove addObject:item];
}
}
for (NSMenuItem *item in itemsToRemove) {
[parent removeItem:item];
}
}
void revery_menuRemoveItem_cocoa(NSMenu *nsMenu, NSMenuItem *nsMenuItem) {
[nsMenu removeItem:nsMenuItem];
}
void revery_menuInsertItemAt_cocoa(NSMenu *nsMenu, NSMenuItem *nsMenuItem, int idx) {
[nsMenu insertItem:nsMenuItem atIndex:idx];
}
void revery_menuInsertSubmenuAt_cocoa(NSMenu *parent, NSMenu *child, int idx) {
NSMenuItem *nsMenuItem =
[[NSMenuItem alloc] initWithTitle:[child title] action:NULL keyEquivalent:@""];
[parent insertItem:nsMenuItem atIndex:idx];
[parent setSubmenu:child forItem:nsMenuItem];
}
void revery_menuClear_cocoa(NSMenu *nsMenu) {
[nsMenu removeAllItems];
}
void revery_menuDisplayIn_cocoa(NSMenu *nsMenu, NSWindow *nsWindow, int x, int y) {
NSView *nsView = [nsWindow contentView];
CGRect frame = [nsView frame];
float adjY = frame.size.height - (float)y;
CGPoint point;
point.x = (float)x;
point.y = adjY;
[nsMenu popUpMenuPositioningItem:nil atLocation:point inView:nsView];
}
NSMenuItem *revery_menuItemCreateSeparator_cocoa() {
return [NSMenuItem separatorItem];
}
void revery_menuItemSetEnabled_cocoa(NSMenuItem *nsMenuItem, int truth) {
[nsMenuItem setEnabled:truth];
}
void revery_menuItemSetVisible_cocoa(NSMenuItem *nsMenuItem, int truth) {
[nsMenuItem setHidden:!truth];
}
#endif
================================================
FILE: src/Native/notification.c
================================================
#include
#include
#include
#include
#include
#include "caml_values.h"
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#elif USE_GTK
#include "ReveryGtk.h"
#endif
#include "utilities.h"
CAMLprim value revery_dispatchNotification(value vNotificationT) {
CAMLparam1(vNotificationT);
const char *title;
const char *body;
int mute;
title = String_val(Field(vNotificationT, 0));
body = String_val(Field(vNotificationT, 1));
value onClickCaml = Field(vNotificationT, 2);
mute = Int_val(Field(vNotificationT, 3));
#ifdef USE_COCOA
revery_dispatchNotification_cocoa(title, body, onClickCaml, mute);
UNUSED(title);
UNUSED(body);
UNUSED(mute);
UNUSED(onClickCaml);
#else
UNUSED(title);
UNUSED(body);
UNUSED(mute);
UNUSED(onClickCaml);
#endif
CAMLreturn(Val_unit);
}
CAMLprim value revery_scheduleNotificationFromNow(value vSeconds, value vNotificationT) {
CAMLparam2(vNotificationT, vSeconds);
const char *title;
const char *body;
int mute;
int seconds;
value onClickCaml;
title = String_val(Field(vNotificationT, 0));
body = String_val(Field(vNotificationT, 1));
onClickCaml = Field(vNotificationT, 2);
mute = Int_val(Field(vNotificationT, 3));
seconds = Int_val(vSeconds);
#ifdef USE_COCOA
revery_scheduleNotificationFromNow_cocoa(title, body, onClickCaml, mute, seconds);
UNUSED(title);
UNUSED(body);
UNUSED(mute);
UNUSED(onClickCaml);
#else
UNUSED(title);
UNUSED(body);
UNUSED(mute);
UNUSED(seconds);
UNUSED(onClickCaml);
#endif
CAMLreturn(Val_unit);
}
================================================
FILE: src/Native/notification_cocoa.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import "cocoa/ReveryAppDelegate.h"
#import
#include "utilities.h"
void revery_dispatchNotification_cocoa(const char *title, const char *body,
long onClickFunc, int mute) {
NSUserNotification *notification = [[NSUserNotification alloc] autorelease];
NSUserNotificationCenter *notificationCenter =
[NSUserNotificationCenter defaultUserNotificationCenter];
ReveryAppDelegate *delegate = (ReveryAppDelegate *)[NSApp delegate];
NSString *nsTitle =
[NSString stringWithCString:title encoding:NSUTF8StringEncoding];
NSString *nsBody =
[NSString stringWithCString:body encoding:NSUTF8StringEncoding];
NSString *identifier =
[NSString stringWithFormat:@"%@:notification:%@",
[[NSBundle mainBundle] bundleIdentifier],
[[[NSUUID alloc] init] UUIDString]];
[notification setIdentifier:identifier];
[notification setTitle:nsTitle];
[notification setInformativeText:nsBody];
[notification setSoundName:mute ? NULL : NSUserNotificationDefaultSoundName];
delegate.notificationActions[identifier] =
[NSNumber numberWithLong:onClickFunc];
[notificationCenter deliverNotification:notification];
}
void revery_scheduleNotificationFromNow_cocoa(const char *title, const char *body,
long onClickFunc, int mute, int seconds) {
NSUserNotification *notification = [[NSUserNotification alloc] autorelease];
NSUserNotificationCenter *notificationCenter =
[NSUserNotificationCenter defaultUserNotificationCenter];
ReveryAppDelegate *delegate = (ReveryAppDelegate *)[NSApp delegate];
NSString *nsTitle =
[NSString stringWithCString:title encoding:NSUTF8StringEncoding];
NSString *nsBody =
[NSString stringWithCString:body encoding:NSUTF8StringEncoding];
NSString *identifier =
[NSString stringWithFormat:@"%@:notification:%@",
[[NSBundle mainBundle] bundleIdentifier],
[[[NSUUID alloc] init] UUIDString]];
[notification setIdentifier:identifier];
[notification setTitle:nsTitle];
[notification setInformativeText:nsBody];
[notification setSoundName:mute ? NULL : NSUserNotificationDefaultSoundName];
NSDate *deliveryDate = [[NSDate alloc] initWithTimeIntervalSinceNow:seconds];
[notification setDeliveryDate:deliveryDate];
delegate.notificationActions[identifier] =
[NSNumber numberWithLong:onClickFunc];
[notificationCenter scheduleNotification:notification];
}
#endif
================================================
FILE: src/Native/shell.c
================================================
#include
#include
#include
#include
#include
#include "caml_values.h"
#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#elif USE_GTK
#include "ReveryGtk.h"
#endif
#include "utilities.h"
CAMLprim value revery_openURL(value vURL) {
CAMLparam1(vURL);
const char *url_string = String_val(vURL);
int success = 0;
#ifdef USE_COCOA
success = revery_openURL_cocoa(url_string);
#elif USE_GTK
success = revery_openURL_gtk(url_string);
#elif USE_WIN32
success = revery_openURL_win32(url_string);
#else
fprintf(stderr, "WARNING: %s is not implemented on this platform.\n", __func__);
success = 0;
UNUSED(url_string);
#endif
CAMLreturn(Val_bool(success));
}
CAMLprim value revery_openFile(value vPath) {
CAMLparam1(vPath);
const char *path_string = String_val(vPath);
int success = 0;
#ifdef USE_COCOA
success = revery_openFile_cocoa(path_string);
#elif USE_GTK
success = revery_openFile_gtk(path_string);
#elif USE_WIN32
// The Win32 implementation of the URL opener also works for file paths
success = revery_openURL_win32(path_string);
#else
fprintf(stderr, "WARNING: %s is not implemented on this platform.\n", __func__);
success = 0;
UNUSED(path_string);
#endif
CAMLreturn(Val_bool(success));
}
================================================
FILE: src/Native/shell_cocoa.c
================================================
#include "config.h"
#ifdef USE_COCOA
#import
int revery_openURL_cocoa(const char *url_string) {
NSString *nsString = [NSString stringWithCString:url_string encoding:NSUTF8StringEncoding];
NSString *nsEncodedString = [nsString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
NSURL *nsURL = [NSURL URLWithString:nsEncodedString];
int success = (int)[[NSWorkspace sharedWorkspace] openURL:nsURL];
[nsEncodedString release];
[nsString release];
[nsURL release];
return success;
}
int revery_openFile_cocoa(const char *path_string) {
NSString *nsString = [NSString stringWithCString:path_string encoding:NSUTF8StringEncoding];
NSURL *nsURL = [NSURL fileURLWithPath:nsString];
int success = (int)[[NSWorkspace sharedWorkspace] openURL:nsURL];
[nsString release];
[nsURL release];
return success;
}
#endif
================================================
FILE: src/Native/shell_gtk.c
================================================
#include "config.h"
#ifdef USE_GTK
#include