Feature for requesting canvas images per HTTP as described in http://wiki.flightgear.org/Read_canvas_image_by_HTTP
This commit is contained in:
parent
94a1156a6b
commit
dc2f142480
@ -39,6 +39,75 @@ namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
static int globalinstanceid = 1;
|
||||
/**
|
||||
* Camera Callback for moving completed canvas images to subscribed listener.
|
||||
*/
|
||||
class CanvasImageCallback : public osg::Camera::DrawCallback {
|
||||
public:
|
||||
osg::Image *_rawImage;
|
||||
|
||||
CanvasImageCallback(osg::Image *rawImage)
|
||||
: _min_delta_tick(1.0 / 8.0) {
|
||||
_previousFrameTick = osg::Timer::instance()->tick();
|
||||
_rawImage = rawImage;
|
||||
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImageCallback created. instance is " << instanceid);
|
||||
}
|
||||
|
||||
virtual void operator()(osg::RenderInfo& renderInfo) const {
|
||||
osg::Timer_t n = osg::Timer::instance()->tick();
|
||||
double dt = osg::Timer::instance()->delta_s(_previousFrameTick, n);
|
||||
if (dt < _min_delta_tick)
|
||||
return;
|
||||
_previousFrameTick = n;
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImageCallback " << instanceid << ": image available for " << _subscribers.size() << " subscribers. camera is " << renderInfo.getCurrentCamera());
|
||||
|
||||
bool hasSubscribers = false;
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
hasSubscribers = !_subscribers.empty();
|
||||
}
|
||||
if (hasSubscribers) {
|
||||
//Make sure image can be overwritten by next frame while it is still returned to the client
|
||||
osg::Image* image = new osg::Image(*_rawImage, osg::CopyOp::DEEP_COPY_ALL);
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
while (!_subscribers.empty()) {
|
||||
try {
|
||||
CanvasImageReadyListener *subs = _subscribers.back();
|
||||
if (subs){
|
||||
subs->imageReady(image);
|
||||
}else{
|
||||
SG_LOG(SG_GENERAL,SG_WARN,"CanvasImageCallback subscriber null");
|
||||
}
|
||||
} catch (...) { }
|
||||
_subscribers.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subscribe(CanvasImageReadyListener * subscriber) {
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_subscribers.push_back(subscriber);
|
||||
}
|
||||
|
||||
void unsubscribe(CanvasImageReadyListener * subscriber) {
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_subscribers.remove(subscriber);
|
||||
}
|
||||
|
||||
int getSubscriberCount() {
|
||||
return _subscribers.size();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable list<CanvasImageReadyListener*> _subscribers;
|
||||
mutable OpenThreads::Mutex _lock;
|
||||
mutable double _previousFrameTick;
|
||||
double _min_delta_tick;
|
||||
int instanceid = globalinstanceid++;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
|
||||
@ -248,6 +317,21 @@ namespace canvas
|
||||
|
||||
osg::Camera* camera = _texture.getCamera();
|
||||
|
||||
string canvasname = _node->getStringValue("name");
|
||||
int renderToImage = _node->getBoolValue("render-to-image");
|
||||
|
||||
if (renderToImage){
|
||||
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
|
||||
if (!_screenshotCallback) {
|
||||
// no draw callback yet
|
||||
osg::Image* shot = new osg::Image();
|
||||
shot->allocateImage(getSizeX(), getSizeY(), 24, GL_RGB, GL_UNSIGNED_BYTE);
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, shot);
|
||||
camera->setFinalDrawCallback(new CanvasImageCallback(shot));
|
||||
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: attached image and draw callback to camera " << camera << " for canvas " << canvasname << ". Ready for subscriber now.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Allow custom render order? For now just keep in order with
|
||||
// property tree.
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
|
||||
@ -347,6 +431,41 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
int Canvas::subscribe(CanvasImageReadyListener * subscriber) {
|
||||
osg::Camera* camera = _texture.getCamera();
|
||||
const string canvasname = _node->getStringValue("name");
|
||||
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: subscribe to canvas " << canvasname.c_str() << ", camera ="<< camera);
|
||||
|
||||
if (!_node->getBoolValue("render-to-image")) {
|
||||
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: Setting render-to-image");
|
||||
_node->addChild("render-to-image", 0)->setBoolValue(1);
|
||||
setStatusFlags(STATUS_DIRTY, true);
|
||||
}
|
||||
|
||||
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
|
||||
if (_screenshotCallback) {
|
||||
// Camera ready for subscriber. Otherwise, draw callback is created by canvas thread later.
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: adding subscriber to camera draw callback");
|
||||
_screenshotCallback->subscribe(subscriber);
|
||||
// TODO: check: Is this the correct way to ensure the canvas will be available?
|
||||
enableRendering(true);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Canvas::unsubscribe(CanvasImageReadyListener * subscriber) {
|
||||
osg::Camera* camera = _texture.getCamera();
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe");
|
||||
CanvasImageCallback *cb = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
|
||||
if (cb) {
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe from camera " << camera);
|
||||
cb->unsubscribe(subscriber);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::addEventListener( const std::string& type,
|
||||
const EventListener& cb )
|
||||
|
@ -44,6 +44,17 @@ namespace canvas
|
||||
class CanvasMgr;
|
||||
class MouseEvent;
|
||||
|
||||
/**
|
||||
* A listener interested in completed canvas drawing.
|
||||
*/
|
||||
class CanvasImageReadyListener {
|
||||
public:
|
||||
virtual void imageReady(osg::ref_ptr<osg::Image>) = 0;
|
||||
virtual ~CanvasImageReadyListener()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Canvas to draw onto (to an off-screen render target).
|
||||
*/
|
||||
@ -160,7 +171,12 @@ namespace canvas
|
||||
*/
|
||||
void enableRendering(bool force = false);
|
||||
|
||||
void update(double delta_time_sec);
|
||||
void update(double delta_time_sec) override;
|
||||
|
||||
osg::Camera* getCamera();
|
||||
int subscribe(CanvasImageReadyListener * subscriber);
|
||||
int unsubscribe(CanvasImageReadyListener * subscriber);
|
||||
int getSubscriberCount();
|
||||
|
||||
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||
bool dispatchEvent(const EventPtr& event);
|
||||
|
Loading…
Reference in New Issue
Block a user