Skip to content

Commit

Permalink
API: Image - ResizeMode (#431)
Browse files Browse the repository at this point in the history
* Include dimensions in information we store about the image

* Refactor image drawing to Draw module

* Formatting

* Wire up resize mode to image

* Add very basic tiling logic

* Image mode

* Get basic tiling working

* Formatting
bryphe authored Apr 8, 2019
1 parent 7d3eea4 commit d169ad5
Showing 6 changed files with 141 additions and 48 deletions.
96 changes: 96 additions & 0 deletions src/Draw/Image.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Image.re
*
* Core logic for rendering images to the screen
*/

open Reglm;
open Reglfw.Glfw;

open Revery_Core;
open Revery_Shaders;
module Geometry = Revery_Geometry;

let identityMatrix = Mat4.create();

let drawImage =
(
~imagePath: string,
~transform: Mat4.t=identityMatrix,
~width: float,
~height: float,
~opacity=1.0,
~tint=Colors.white,
~resizeMode=ImageResizeMode.Stretch,
(),
) => {
let textureShader = Assets.textureShader();
let imgInfo: ImageRenderer.t = ImageRenderer.getTexture(imagePath);

switch (imgInfo.hasLoaded) {
| false => ()
| true =>
let ctx = RenderPass.getContext();
CompiledShader.use(textureShader.compiledShader);
let m = ctx.projection;

let world = transform;

CompiledShader.setUniformMatrix4fv(textureShader.uniformWorld, world);
CompiledShader.setUniformMatrix4fv(textureShader.uniformProjection, m);

CompiledShader.setUniform4fv(
textureShader.uniformColor,
Vec4.create(tint.r, tint.g, tint.b, opacity *. tint.a),
);

glBindTexture(GL_TEXTURE_2D, imgInfo.texture);

switch (resizeMode) {
| Stretch =>
let quad =
Assets.quad(~minX=0., ~minY=0., ~maxX=width, ~maxY=height, ());
Geometry.draw(quad, textureShader.compiledShader);
| Repeat =>
let x = ref(0);
let y = ref(0);

let xDiv = int_of_float(ceil(width /. float_of_int(imgInfo.width)));
let yDiv = int_of_float(ceil(height /. float_of_int(imgInfo.height)));

let localTransform = Mat4.create();

/*
TODO:
Implement this via geometry batching rather than additional draw calls
*/
while (y^ < yDiv) {
while (x^ < xDiv) {
let xPos = float_of_int(x^ * imgInfo.width);
let yPos = float_of_int(y^ * imgInfo.height);
let v = Vec3.create(xPos, yPos, 0.);

Mat4.fromTranslation(localTransform, v);
Mat4.multiply(localTransform, world, localTransform);
CompiledShader.setUniformMatrix4fv(
textureShader.uniformWorld,
localTransform,
);

let quad =
Assets.quad(
~minX=0.,
~minY=0.,
~maxX=float_of_int(imgInfo.width),
~maxY=float_of_int(imgInfo.height),
(),
);
Geometry.draw(quad, textureShader.compiledShader);

incr(x);
};
incr(y);
};
};
};
};
31 changes: 18 additions & 13 deletions src/Draw/ImageRenderer.re
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
open Reglfw;
open Reglfw.Glfw;
open Revery_Core;

module Image = Reglfw.Image;

type t = {
mutable hasLoaded: bool,
texture,
mutable width: int,
mutable height: int,
};

type cache = Hashtbl.t(string, texture);
type cache = Hashtbl.t(string, t);

let _cache: cache = Hashtbl.create(100);

let initialImage = Image.fromColor(255, 0, 0, 255);
let initialPixels = Image.getPixels(initialImage);

let getTexture = (imagePath: string) => {
/* TODO: Support url paths? */
let execDir = Environment.getExecutingDirectory();
let relativeImagePath = execDir ++ imagePath;
let getTexture: string => t =
(imagePath: string) => {
/* TODO: Support url paths? */
let execDir = Environment.getExecutingDirectory();
let relativeImagePath = execDir ++ imagePath;

let cacheResult = Hashtbl.find_opt(_cache, relativeImagePath);
let cacheResult = Hashtbl.find_opt(_cache, relativeImagePath);

let ret =
switch (cacheResult) {
| Some(r) => r
| None =>
@@ -45,8 +46,11 @@ let getTexture = (imagePath: string) => {

let imageLoadPromise = Image.load(relativeImagePath);

let ret: t = {hasLoaded: false, texture, width: 1, height: 1};

let success = img => {
let pixels = Image.getPixels(img);
let {width, height, _}: Image.dimensions = Image.getDimensions(img);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
@@ -56,13 +60,14 @@ let getTexture = (imagePath: string) => {
GL_UNSIGNED_BYTE,
pixels,
);
ret.hasLoaded = true;
ret.width = width;
ret.height = height;
Lwt.return();
};

let _ = Lwt.bind(imageLoadPromise, success);
Hashtbl.add(_cache, relativeImagePath, texture);
texture;
Hashtbl.replace(_cache, relativeImagePath, ret);
ret;
};

ret;
};
};
3 changes: 3 additions & 0 deletions src/Draw/ImageResizeMode.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type t =
| Stretch
| Repeat;
3 changes: 2 additions & 1 deletion src/Draw/Revery_Draw.re
Original file line number Diff line number Diff line change
@@ -2,7 +2,8 @@ module Assets = Assets;
module RenderPass = RenderPass;
module FontCache = FontCache;
module FontRenderer = FontRenderer;
module ImageRenderer = ImageRenderer;
module Image = Image;
module ImageResizeMode = ImageResizeMode;
module Shapes = Shapes;
module Text = Text;

50 changes: 16 additions & 34 deletions src/UI/ImageNode.re
Original file line number Diff line number Diff line change
@@ -1,53 +1,35 @@
open Reglm;
open Reglfw.Glfw;

open Revery_Core;
open Revery_Draw;

module Shaders = Revery_Shaders;
module Geometry = Revery_Geometry;
module Layout = Layout;
module LayoutTypes = Layout.LayoutTypes;

open Node;
open ViewNode;

class imageNode (imagePath: string) = {
as _this;
inherit (class node)() as _super;
val _resizeMode: ref(ImageResizeMode.t) = ref(ImageResizeMode.Stretch);
pub! draw = (parentContext: NodeDrawContext.t) => {
/* Draw background first */
_super#draw(parentContext);
let textureShader = Assets.textureShader();
let texture = ImageRenderer.getTexture(imagePath);

let ctx = RenderPass.getContext();
Shaders.CompiledShader.use(textureShader.compiledShader);
let m = ctx.projection;

let dimensions = _this#measurements();
let width = float_of_int(dimensions.width);
let height = float_of_int(dimensions.height);
let quad = Assets.quad(~minX=0., ~minY=0., ~maxX=width, ~maxY=height, ());

let opacity = _super#getStyle().opacity *. parentContext.opacity;

let world = _this#getWorldTransform();

Shaders.CompiledShader.setUniformMatrix4fv(
textureShader.uniformWorld,
world,
let style = _this#getStyle();

Image.drawImage(
~imagePath,
~transform=world,
~width=float_of_int(dimensions.width),
~height=float_of_int(dimensions.height),
~resizeMode=_resizeMode^,
~tint=Colors.white,
~opacity=style.opacity,
(),
);
Shaders.CompiledShader.setUniformMatrix4fv(
textureShader.uniformProjection,
m,
);

Shaders.CompiledShader.setUniform4fv(
textureShader.uniformColor,
Vec4.create(1.0, 1.0, 1.0, opacity),
);

glBindTexture(GL_TEXTURE_2D, texture);
Geometry.draw(quad, textureShader.compiledShader);
};
pub setResizeMode = (mode: ImageResizeMode.t) => {
_resizeMode := mode;
};
};
6 changes: 6 additions & 0 deletions src/UI/Primitives.re
Original file line number Diff line number Diff line change
@@ -238,6 +238,7 @@ module Image = {
~onMouseUp=?,
~onMouseWheel=?,
~ref=?,
~resizeMode=Revery_Draw.ImageResizeMode.Stretch,
~style=Style.emptyImageStyle,
~src="",
children,
@@ -260,6 +261,7 @@ module Image = {
let node = (new ImageNode.imageNode)(src);
node#setEvents(events);
node#setStyle(styles);
node#setResizeMode(resizeMode);
Obj.magic(node);
},
configureInstance: (~isFirstRender as _, node) => {
@@ -273,6 +275,8 @@ module Image = {
~onMouseWheel?,
(),
);
let imgNode: ImageNode.imageNode = Obj.magic(node);
imgNode#setResizeMode(resizeMode);
node#setEvents(events);
node#setStyle(styles);
node;
@@ -289,6 +293,7 @@ module Image = {
~onMouseUp=?,
~onMouseWheel=?,
~ref=?,
~resizeMode=?,
~style=Style.emptyImageStyle,
~src="",
~children,
@@ -300,6 +305,7 @@ module Image = {
~onMouseUp?,
~onMouseWheel?,
~ref?,
~resizeMode?,
~style,
~src,
React.listToElement(children),

0 comments on commit d169ad5

Please sign in to comment.