Skip to content

Commit da11938

Browse files
Zachary Denhamalexpareto
authored andcommitted
Cinemagraph overlay (#135)
* added uniform for showing overlays * added redux * added overlays * added overlay and fixed cinemagraph draw lag
1 parent 7940a5e commit da11938

9 files changed

Lines changed: 141 additions & 23 deletions

File tree

renderer/components/cinemagraphToolBar.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ class NavBar extends React.Component {
7373
onClick={this.toggleTool}
7474
/>
7575
</div>
76+
<div className="tool-button">
77+
<IconButton
78+
stroke={globalStyles.accent}
79+
name={this.props.showOverlay ? 'x' : 'droplet'}
80+
backgroundColor={globalStyles.secondary}
81+
onClick={this.props.toggleCinemagraphOverlay}
82+
/>
83+
</div>
7684
<div className="slider">
7785
<span className="slider-label">size</span>
7886

renderer/components/icon.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,22 @@ const Icon = props => {
570570
<line x1="8" y1="12" x2="16" y2="12" />
571571
</svg>
572572
);
573+
case 'droplet':
574+
return (
575+
<svg
576+
xmlns="http://www.w3.org/2000/svg"
577+
width="24"
578+
height="24"
579+
viewBox="0 0 24 24"
580+
fill="none"
581+
stroke={props.stroke}
582+
strokeWidth="2"
583+
strokeLinecap="round"
584+
strokeLinejoin="round"
585+
>
586+
<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
587+
</svg>
588+
);
573589
default:
574590
return null;
575591
}

renderer/containers/cinemagraph/actions.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const actionTypes = {
1313
SELECT_CINEMAGRAPH_ERASE_TOOL: 'SELECT_CINEMAGRAPH_ERASE_TOOL',
1414
SELECT_CINEMAGRAPH_BRUSH_TOOL: 'SELECT_CINEMAGRAPH_BRUSH_TOOL',
1515
START_CINEMAGRAPH_PREVIEW: 'START_CINEMAGRAPH_PREVIEW',
16+
TOGGLE_CINEMAGRAPH_OVERLAY: 'TOGGLE_CINEMAGRAPH_OVERLAY',
1617
};
1718

1819
export const selectCinemagraphVideo = files => {
@@ -86,3 +87,7 @@ export const removeCinemagraphBrushStroke = (isUndo, isRedo, mask) => {
8687
export const startCinemagraphPreview = callback => {
8788
return { type: actionTypes.START_CINEMAGRAPH_PREVIEW, callback };
8889
};
90+
91+
export const toggleCinemagraphOverlay = () => {
92+
return { type: actionTypes.TOGGLE_CINEMAGRAPH_OVERLAY };
93+
};

renderer/containers/cinemagraph/cinemagraph.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class Cinemagraph extends React.Component {
4646
exports={this.props.exports}
4747
brushSize={this.props.cinemagraph.brushSize}
4848
brushBlur={this.props.cinemagraph.brushBlur}
49+
toggleCinemagraphOverlay={this.props.toggleCinemagraphOverlay}
50+
showOverlay={this.props.cinemagraph.showOverlay}
4951
tool={this.props.cinemagraph.tool}
5052
/>
5153
<CinemagraphCanvas
@@ -61,6 +63,7 @@ class Cinemagraph extends React.Component {
6163
addCinemagraphBrushStroke={this.props.addCinemagraphBrushStroke}
6264
startCinemagraphPreview={this.props.startCinemagraphPreview}
6365
preview={this.props.cinemagraph.preview}
66+
showOverlay={this.props.cinemagraph.showOverlay}
6467
tool={this.props.cinemagraph.tool}
6568
/>
6669
<Trimmer />
@@ -100,6 +103,8 @@ const mapDispatchToProps = dispatch => {
100103
dispatch(Actions.selectCinemagraphBrushTool()),
101104
selectCinemagraphEraseTool: () =>
102105
dispatch(Actions.selectCinemagraphEraseTool()),
106+
toggleCinemagraphOverlay: () =>
107+
dispatch(Actions.toggleCinemagraphOverlay()),
103108
};
104109
};
105110

renderer/containers/cinemagraph/reducer.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const initialState = {
1717
width: 0,
1818
height: 0,
1919
},
20+
showOverlay: false,
2021
showExportModal: false,
2122
isRendering: false,
2223
tool: 'eraser',
@@ -129,6 +130,13 @@ export const cinemagraphReducer = (state = initialState, action) => {
129130
...state,
130131
tool: 'eraser',
131132
};
133+
case actionTypes.TOGGLE_CINEMAGRAPH_OVERLAY:
134+
const showOverlay = !state.showOverlay;
135+
state.preview.setShowOverlay(showOverlay);
136+
return {
137+
...state,
138+
showOverlay,
139+
};
132140
default:
133141
return state;
134142
}

renderer/globalStyles.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const primary = '#fff';
22
export const secondary = '#6369D1';
33
export const accent = '#fff';
4+
export const action = '#007FFF';
45
export const background = '#333333';
56
export const backgroundLight = '#444343';
67
export const backgroundAccent = '#666666';

renderer/lib/hexToRGB.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export default hex => {
2+
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3+
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
4+
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
5+
return r + r + g + g + b + b;
6+
});
7+
8+
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
9+
return result
10+
? {
11+
r: parseInt(result[1], 16),
12+
g: parseInt(result[2], 16),
13+
b: parseInt(result[3], 16),
14+
}
15+
: null;
16+
};

renderer/webgl/helpers/renderCinemagraph.js

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,20 @@ import getShader from './getShader';
55
import loadProgram from './loadProgram';
66
import createImageGrid from './createImageGrid';
77
import TarToMp4 from './tarToMp4';
8+
import * as globalStyles from '../../globalStyles';
9+
import hexToRGB from '../../lib/hexToRGB';
810

911
class Preview {
1012
constructor(boundingRect, exportCallback) {
1113
this.boundingRect = boundingRect;
1214
this.exportCallback = exportCallback;
15+
this.overlayColor = hexToRGB(globalStyles.action);
16+
17+
// normalize color
18+
this.overlayColor.r = this.overlayColor.r / 255.0;
19+
this.overlayColor.g = this.overlayColor.g / 255.0;
20+
this.overlayColor.b = this.overlayColor.b / 255.0;
21+
this.showOverlay = false;
1322

1423
this.canvas = document.getElementById('cinemagraphcanvas');
1524
this.gl = this.canvas.getContext('webgl');
@@ -64,7 +73,15 @@ class Preview {
6473
let gl = this.gl;
6574
try {
6675
let vertexshader = getShader(gl, cinemagraphVertexShader, 'vertex');
67-
let fragmentshader = getShader(gl, cinemagraphFragShader, 'frag');
76+
let fragmentshader = getShader(
77+
gl,
78+
cinemagraphFragShader(
79+
this.overlayColor.r.toFixed(5).toString(),
80+
this.overlayColor.g.toFixed(5).toString(),
81+
this.overlayColor.b.toFixed(5).toString(),
82+
),
83+
'frag',
84+
);
6885

6986
this.pictureprogram = loadProgram(gl, vertexshader, fragmentshader);
7087
gl.useProgram(this.pictureprogram);
@@ -98,9 +115,15 @@ class Preview {
98115
'u_image1',
99116
);
100117

118+
this.pictureprogram.show_overlay = gl.getUniformLocation(
119+
this.pictureprogram,
120+
'show_overlay',
121+
);
122+
101123
// Set the texture to use.
102124
gl.uniform1i(this.pictureprogram.u_image0, 0);
103125
gl.uniform1i(this.pictureprogram.u_image1, 1);
126+
gl.uniform1i(this.pictureprogram.show_overlay, 2);
104127
} catch (e) {
105128
console.log('ERROR MAKING SHADERS: ', e);
106129
return;
@@ -135,6 +158,10 @@ class Preview {
135158
this.start();
136159
};
137160

161+
setShowOverlay = newOverlay => {
162+
this.showOverlay = newOverlay;
163+
};
164+
138165
update = (newPoint, brushSize, brushBlur, brushTool) => {
139166
const normalizedPoint = this.normalizedPoint(
140167
newPoint[0],
@@ -144,30 +171,50 @@ class Preview {
144171
);
145172

146173
// normalize brush size based off of actual image width
147-
let normalizedBrushSize =
148-
brushSize * this.videoWidth / this.boundingRect.width;
174+
let normalizedBrushSize = Math.ceil(
175+
brushSize * this.videoWidth / this.boundingRect.width,
176+
);
177+
178+
const minX = Math.floor(
179+
Math.max(normalizedPoint.x - normalizedBrushSize, 0),
180+
);
181+
const maxX = Math.min(
182+
normalizedPoint.x + normalizedBrushSize,
183+
this.videoWidth,
184+
);
185+
const minY = Math.floor(
186+
Math.max(normalizedPoint.y - normalizedBrushSize, 0),
187+
);
188+
const maxY = Math.min(
189+
normalizedPoint.y + normalizedBrushSize,
190+
this.videoHeight,
191+
);
149192

150193
let blur = 21 - 2 * brushBlur;
151194

152-
let l = this.brushedImage.data.length / 4;
153-
for (let i = 0; i < l; i++) {
154-
const x = i % this.videoWidth;
155-
const y = Math.min(i / this.videoWidth);
156-
let dist = Math.sqrt(
157-
Math.pow(normalizedPoint.x - x, 2) + Math.pow(normalizedPoint.y - y, 2),
158-
);
159-
if (dist < normalizedBrushSize) {
160-
const normalizedDistance =
161-
Math.pow(dist / normalizedBrushSize, blur) * 255.0;
162-
163-
const opacity =
164-
brushTool == 'eraser'
165-
? Math.min(this.brushedImage.data[i * 4 + 3], normalizedDistance)
166-
: Math.max(
167-
this.brushedImage.data[i * 4 + 3],
168-
255.0 - normalizedDistance,
169-
);
170-
this.brushedImage.data[i * 4 + 3] = opacity;
195+
// only iterate over the coordinates within
196+
// the square of brush size
197+
for (let y = 0; y < maxY; y++) {
198+
for (let x = 0; x < maxX; x++) {
199+
const i = this.videoWidth * y + x;
200+
// console.log("I: ", i);
201+
let dist = Math.sqrt(
202+
Math.pow(normalizedPoint.x - x, 2) +
203+
Math.pow(normalizedPoint.y - y, 2),
204+
);
205+
if (dist < normalizedBrushSize) {
206+
const normalizedDistance =
207+
Math.pow(dist / normalizedBrushSize, blur) * 255.0;
208+
209+
const opacity =
210+
brushTool == 'eraser'
211+
? Math.min(this.brushedImage.data[i * 4 + 3], normalizedDistance)
212+
: Math.max(
213+
this.brushedImage.data[i * 4 + 3],
214+
255.0 - normalizedDistance,
215+
);
216+
this.brushedImage.data[i * 4 + 3] = opacity;
217+
}
171218
}
172219
}
173220
};
@@ -244,6 +291,11 @@ class Preview {
244291

245292
gl.enableVertexAttribArray(this.texCoordLocation);
246293

294+
gl.uniform1i(
295+
gl.getUniformLocation(this.pictureprogram, 'show_overlay'),
296+
this.showOverlay,
297+
);
298+
247299
gl.drawArrays(gl.TRIANGLES, 0, resolution * resolution * 2 * 3);
248300
};
249301

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
export default `
1+
export default (r, g, b) => {
2+
return `
23
precision mediump float;
34
45
// uniform to use for texture
56
uniform sampler2D u_image0; // corresponds to the video
67
uniform sampler2D u_image1; // corresponds to the still
8+
uniform bool show_overlay;
79
varying vec2 v_texCoord;
810
911
void main() {
1012
// gl_FragColor always specifies the color to make the current pixel
1113
vec4 color0 = texture2D(u_image0, v_texCoord);
1214
vec4 color1 = texture2D(u_image1, v_texCoord);
15+
vec4 colorOverlay = vec4(${r}, ${g}, ${b}, 1.0);
1316
float a = color1.a;
1417
color1.a = 1.0;
18+
if(show_overlay) {
19+
color1 = 0.65 * a * color1 + 0.35 * a * colorOverlay;
20+
}
1521
vec4 color = color1*a + (1.0 - a)*color0;
1622
gl_FragColor = color;
1723
}
1824
`;
25+
};

0 commit comments

Comments
 (0)