Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Window.takeScreenshot API #225

Merged
merged 6 commits into from
Jan 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/Examples.re
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ let state: state = {
{name: "Input", render: w => InputExample.render(w)},
{name: "Radio Button", render: _ => RadioButtonExample.render()},
{name: "Game Of Life", render: _ => GameOfLife.render()},
{name: "Screen Capture", render: w => ScreenCapture.render(w)},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be useful to put the Screen Capture button at the bottom of the screen? For example with something like this:

        style=Style.[
          position(`Absolute),
          top(0),
          left(0),
          width(175),
          bottom(0),
          backgroundColor(bgColor),
        ]>
        ...buttons
      </View>

      <View
        style=Style.[
          position(`Absolute),
          bottom(0),
          left(0),
          width(175),
          backgroundColor(bgColor),
        ]>
        screenCaptureButton
      </View>

Copy link
Collaborator Author

@OhadRau OhadRau Jan 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, do you want me to go back and do that? Right now this is setup to be in its own tab as bryan pointed out. This is a little weird because you can't get screenshots of everything but it's more of a proof of concept than anything else so I'm not too concerned about it. One of the advantages of having it in its own tab is that we can display the image right there so we can really show it in action. @tcoopman

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with it is, for now - what I'd like to do with the example app is host it on a website, and show the example code for the tab side-by-side with the selected example.

For the screenshot example - it'd be nice to see how easy it is to implement this behavior w/ that code snippet alongside. We can always revisit down the road though if we want to shuffle it around.

{name: "Tree View", render: w => TreeView.render(w)},
],
selectedExample: "Animation",
Expand Down Expand Up @@ -94,10 +95,9 @@ type action =
| SelectExample(string);

let reducer = (s: state, a: action) => {
let SelectExample(name) = a;

let ret: state = {...s, selectedExample: name};
ret;
switch (a) {
| SelectExample(name) => {...s, selectedExample: name}
};
};

let init = app => {
Expand Down
78 changes: 78 additions & 0 deletions examples/ScreenCapture.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
open Revery.UI;
open Revery.UI.Components;
open Revery.Core;

let backgroundColor = Color.hex("#212733");
let activeBackgroundColor = Color.hex("#2E3440");
let inactiveBackgroundColor = Color.hex("#272d39");
let selectionHighlight = Color.hex("#90f7ff");

module ActionButton = {
let component = React.component("ActionButton");

let make = (~name, ~onClick, ()) =>
component((_slots: React.Hooks.empty) => {
let wrapperStyle =
Style.[
backgroundColor(selectionHighlight),
border(~width=4, ~color=activeBackgroundColor),
];
let textHeaderStyle =
Style.[
color(Colors.black),
fontFamily("Roboto-Regular.ttf"),
fontSize(14),
margin(16),
];
<Clickable style=wrapperStyle onClick>
<Text style=textHeaderStyle text=name />
</Clickable>;
});

let createElement = (~children as _, ~name, ~onClick, ()) =>
React.element(make(~name, ~onClick, ()));
};

module CaptureArea = {
let component = React.component("Capture Area");

let make = w =>
component(slots => {
let (count, setCount, slots) = React.Hooks.state(0, slots);
let (file, setFile, _slots: React.Hooks.empty) =
React.Hooks.state(None, slots);

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)];

<View style=viewStyle>
<ActionButton name="Take a screenshot!" onClick=capture />
{switch (file) {
| None => <View />
| Some(src) => <Image style=imageStyle src />
}}
</View>;
});

let createElement = (~w, ~children as _, ()) => React.element(make(w));
};

let render = w => <CaptureArea w />;
20 changes: 20 additions & 0 deletions src/Core/Window.re
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,26 @@ let getDevicePixelRatio = (w: t) => {
/. float_of_int(windowSizeInScreenCoordinates.width);
};

let takeScreenshot = (w: t, filename: string) => {
open Glfw;

let width = w.framebufferWidth;
let height = w.framebufferHeight;

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment here! I was wondering why we needed the extra render call.

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);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

Image.save(image, filename);
Image.destroy(image);
};

let destroyWindow = (w: t) => Glfw.glfwDestroyWindow(w.glfwWindow);

let shouldClose = (w: t) => Glfw.glfwWindowShouldClose(w.glfwWindow);
Expand Down