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 video quads #745

Merged
merged 8 commits into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion examples/quads/example.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"exampleJs": ["main.js"],
"about": {
"text": "This example shows how to add dynamic quads to a map."
}
},
"disabled": true
}
8 changes: 3 additions & 5 deletions examples/quads/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ $(function () {
renderer: query.renderer ? (query.renderer === 'html' ? null : query.renderer) : undefined,
features: query.renderer ? undefined : ['quad']
});
var previewImage = new Image();
var quads = layer.createFeature('quad', {selectionAPI: true});
var quadData = [{
ll: {x: -108, y: 29},
Expand Down Expand Up @@ -134,7 +135,6 @@ $(function () {
image: '../../data/tilefancy.png'
});
}
var previewImage = new Image();
previewImage.onload = function () {

quads
Expand All @@ -158,17 +158,15 @@ $(function () {
evt.data.opacity = 0.5;
// we either have to clear the internal cache on the item, or have
// asked for it not to have been cached to begin with.
delete evt.data._cachedQuad;
this.modified();
this.cacheUpdate(evt.data);
this.draw();
})
.geoOn(geo.event.feature.mouseout, function (evt) {
if (evt.data.orig_opacity === undefined) {
evt.data.orig_opacity = (evt.data.opacity || null);
}
evt.data.opacity = evt.data.orig_opacity || undefined;
delete evt.data._cachedQuad;
this.modified();
this.cacheUpdate(evt.data);
this.draw();
})
.draw();
Expand Down
7 changes: 7 additions & 0 deletions src/canvas/canvasRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ var canvasRenderer = function (arg) {
features = layer.features(),
i;

for (i = 0; i < features.length; i += 1) {
if (features[i]._delayRender()) {
Copy link
Member

Choose a reason for hiding this comment

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

should it be if (!features[i]._delayRender())? If delayRender is true which means you want to skip rendering or something I missed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Calling render() reschedules the rendering to the next animation frame. The return then stops it from rendering right now.

Copy link
Member

Choose a reason for hiding this comment

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

ok, thanks perhaps adding a comment here might help but I am fine either way.

m_this._render();
return;
}
}

// Clear the canvas.
if (m_clearCanvas) {
m_this.context2d.setTransform(1, 0, 0, 1, 0, 0);
Expand Down
10 changes: 10 additions & 0 deletions src/canvas/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ var canvas_object = function (arg) {
this._renderOnCanvas = function () {
};

/**
* If this returns true, the render will be skipped and tried again on the
* next animation frame.
*
* @returns {boolean} Truthy to delay rendering.
*/
this._delayRender = function () {
return false;
};

/**
* Check if a property has already been set on a canvas's context. If so,
* don't set it again. Some browsers are much slower if the properties are
Expand Down
164 changes: 135 additions & 29 deletions src/canvas/quadFeature.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var inherit = require('../inherit');
var registerFeature = require('../registry').registerFeature;
var quadFeature = require('../quadFeature');
var util = require('../util');

/**
* Create a new instance of class quadFeature.
Expand Down Expand Up @@ -46,57 +47,158 @@ var canvas_quadFeature = function (arg) {
m_this.buildTime().modified();
};

/**
* When any quad may have changed, ask for a animation frame callback so we
* can update the quad on the next animation cycle.
*
* This is called when a video qaud may have changed play state.
* @param {object} quad The quad record that triggered this.
* @param {jQuery.Event} [evt] The event that triggered this.
*/
this._checkQuadUpdate = function (quad, evt) {
m_this.layer().map().scheduleAnimationFrame(m_this._checkIfChanged);
};

/**
* Check if any video quads are changing or need rerendering. If any are
* changing (because they are seeking), defer rendering and check again. If
* any need rendering, schedule it.
*/
this._checkIfChanged = function () {
if (!m_quads || !m_quads.vidQuads || !m_quads.vidQuads.length) {
return;
}
var render = false, changing = false;

$.each(m_quads.vidQuads, function (idx, quad) {
if (quad.video && quad.video.HAVE_CURRENT_DATA !== undefined) {
if (!quad.video.seeking && quad.video.readyState >= quad.video.HAVE_CURRENT_DATA) {
render = true;
}
if (!quad.video.paused || quad.video.seeking) {
changing = true;
}
}
});
if (render) {
m_this.renderer()._render();
}
if (changing) {
m_this.layer().map().scheduleAnimationFrame(m_this._checkIfChanged);
}
};

/**
* Render all of the color quads.
*
* @param {CanvasRenderingContext2D} context2d The rendering context.
* @param {geo.map} map The current renderer's parent map.
*/
this._renderColorQuads = function (context2d, map) {
// Not implemented yet.
if (!m_quads.clrQuads || !m_quads.clrQuads.length) {
return;
}
var oldAlpha = context2d.globalAlpha;
var opacity = oldAlpha;
$.each(m_quads.clrQuads, function (idx, quad) {
var p0 = map.gcsToDisplay({x: quad.pos[0], y: quad.pos[1]}, null),
p1 = map.gcsToDisplay({x: quad.pos[3], y: quad.pos[4]}, null),
p2 = map.gcsToDisplay({x: quad.pos[6], y: quad.pos[7]}, null),
p3 = map.gcsToDisplay({x: quad.pos[9], y: quad.pos[10]}, null);
if (quad.opacity !== opacity) {
opacity = quad.opacity;
context2d.globalAlpha = opacity;
}
context2d.fillStyle = util.convertColorToHex(quad.color, true);
context2d.beginPath();
context2d.moveTo(p0.x, p0.y);
context2d.lineTo(p1.x, p1.y);
context2d.lineTo(p3.x, p3.y);
context2d.lineTo(p2.x, p2.y);
context2d.closePath();
context2d.fill();
});
if (opacity !== oldAlpha) {
context2d.globalAlpha = oldAlpha;
}
};

/**
* Render all of the image quads.
* Render all of the image and video quads.
*
* @param {CanvasRenderingContext2D} context2d The rendering context.
* @param {geo.map} map The current renderer's parent map.
*/
this._renderImageQuads = function (context2d, map) {
if (!m_quads.imgQuads.length) {
this._renderImageAndVideoQuads = function (context2d, map) {
if ((!m_quads.imgQuads || !m_quads.imgQuads.length) && (!m_quads.vidQuads || !m_quads.vidQuads.length)) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we wrap this long line?

return;
}

var oldAlpha = context2d.globalAlpha;
var opacity = oldAlpha;
$.each(m_quads.imgQuads, function (idx, quad) {
if (!quad.image) {
$.each([m_quads.imgQuads, m_quads.vidQuads], function (listidx, quadlist) {
if (!quadlist) {
return;
}
var w = quad.image.width,
h = quad.image.height;
// Canvas transform is affine, so quad has to be a parallelogram
// Also, canvas has no way to render z.
var p0 = map.gcsToDisplay({x:quad.pos[0], y:quad.pos[1]}, null),
p3 = map.gcsToDisplay({x:quad.pos[9], y:quad.pos[10]}, null),
p2 = map.gcsToDisplay({x:quad.pos[6], y:quad.pos[7]}, null);
context2d.setTransform((p3.x - p2.x) / w, (p3.y - p2.y) / h,
(p0.x - p2.x) / w, (p0.y - p2.y) / h,
p2.x, p2.y);
if (quad.opacity !== opacity) {
opacity = quad.opacity;
context2d.globalAlpha = opacity;
}
if (!quad.crop) {
context2d.drawImage(quad.image, 0, 0);
} else {
context2d.drawImage(quad.image, 0, 0, quad.crop.x, quad.crop.y, 0, 0,
quad.crop.x, quad.crop.y);
}
$.each(quadlist, function (idx, quad) {
var src, w, h;
if (quad.image) {
src = quad.image;
w = src.width;
h = src.height;
} else if (quad.video) {
src = quad.video;
w = src.videoWidth;
h = src.videoHeight;
if (src.seeking) {
return;
}
}
if (!src || !w || !h || quad.opacity <= 0) {
return;
}
// Canvas transform is affine, so quad has to be a parallelogram
// Also, canvas has no way to render z.
var p0 = map.gcsToDisplay({x: quad.pos[0], y: quad.pos[1]}, null),
p2 = map.gcsToDisplay({x: quad.pos[6], y: quad.pos[7]}, null),
p3 = map.gcsToDisplay({x: quad.pos[9], y: quad.pos[10]}, null);
context2d.setTransform((p3.x - p2.x) / w, (p3.y - p2.y) / w,
(p0.x - p2.x) / h, (p0.y - p2.y) / h,
p2.x, p2.y);
if (quad.opacity !== opacity) {
opacity = quad.opacity;
context2d.globalAlpha = opacity;
}
if (!quad.crop) {
context2d.drawImage(src, 0, 0);
} else {
context2d.drawImage(src, 0, 0, quad.crop.x, quad.crop.y, 0, 0,
quad.crop.x, quad.crop.y);
}
});
});
if (opacity !== oldAlpha) {
context2d.globalAlpha = oldAlpha;
}
context2d.setTransform(1, 0, 0, 1, 0, 0);
};

/**
* If this returns true, the render will be skipped and tried again on the
* next animation frame.
*
* @returns {boolean} Truthy to delay rendering.
*/
this._delayRender = function () {
var delay = false;
if (m_quads && m_quads.vidQuads && m_quads.vidQuads.length) {
$.each(m_quads.vidQuads, function (idx, quad) {
if (quad.video && quad.video.HAVE_CURRENT_DATA !== undefined) {
delay |= (quad.video.seeking && quad.delayRenderWhenSeeking);
}
});
}
return delay;
};

/**
Expand All @@ -106,8 +208,10 @@ var canvas_quadFeature = function (arg) {
* @param {geo.map} map The current renderer's parent map.
*/
this._renderOnCanvas = function (context, map) {
this._renderImageQuads(context, map);
this._renderColorQuads(context, map);
if (m_quads) {
this._renderImageAndVideoQuads(context, map);
this._renderColorQuads(context, map);
}
};

/**
Expand All @@ -121,6 +225,7 @@ var canvas_quadFeature = function (arg) {
}

m_this.updateTime().modified();
m_this.layer().map().scheduleAnimationFrame(m_this._checkIfChanged);
};

/**
Expand All @@ -146,12 +251,13 @@ inherit(canvas_quadFeature, quadFeature);

// Now register it
var capabilities = {};
capabilities[quadFeature.capabilities.color] = false;
capabilities[quadFeature.capabilities.color] = true;
capabilities[quadFeature.capabilities.image] = true;
capabilities[quadFeature.capabilities.imageCrop] = true;
capabilities[quadFeature.capabilities.imageFixedScale] = true;
capabilities[quadFeature.capabilities.imageFull] = false;
capabilities[quadFeature.capabilities.canvas] = true;
capabilities[quadFeature.capabilities.video] = true;

registerFeature('canvas', 'quad', canvas_quadFeature, capabilities);
module.exports = canvas_quadFeature;
1 change: 1 addition & 0 deletions src/d3/quadFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ capabilities[quadFeature.capabilities.imageCrop] = false;
capabilities[quadFeature.capabilities.imageFixedScale] = false;
capabilities[quadFeature.capabilities.imageFull] = false;
capabilities[quadFeature.capabilities.canvas] = false;
capabilities[quadFeature.capabilities.video] = false;

registerFeature('d3', 'quad', d3_quadFeature, capabilities);
module.exports = d3_quadFeature;
3 changes: 2 additions & 1 deletion src/gl/quadFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ var gl_quadFeature = function (arg) {
if (m_quads.clrQuads.length) {
if (!m_clrposbuf || m_clrposbuf.length < m_quads.clrQuads.length * 12 ||
!m_glBuffers.clrQuadsPosition) {
if (m_glBuffers.imgQuadsPosition) {
if (m_glBuffers.clrQuadsPosition) {
context.deleteBuffer(m_glBuffers.clrQuadsPosition);
}
m_glBuffers.clrQuadsPosition = context.createBuffer();
Expand Down Expand Up @@ -419,6 +419,7 @@ capabilities[quadFeature.capabilities.imageCrop] = true;
capabilities[quadFeature.capabilities.imageFixedScale] = false;
capabilities[quadFeature.capabilities.imageFull] = true;
capabilities[quadFeature.capabilities.canvas] = false;
capabilities[quadFeature.capabilities.video] = false;

registerFeature('vgl', 'quad', gl_quadFeature, capabilities);
module.exports = gl_quadFeature;
Loading