Files
OpenSceneGraph/src/osgWidget/Frame.cpp
Robert Osfield dd996a3289 Introduced CMake option OSG_PROVIDE_READFILE option that defaults to ON, but when switched to OFF disables the building of the osgDB::read*File() methods,
forcing users to use osgDB::readRef*File() methods.  The later is preferable as it closes a potential threading bug when using paging databases in conjunction
with the osgDB::Registry Object Cache.  This threading bug occurs when one thread gets an object from the Cache via an osgDB::read*File() call where only
a pointer to the object is passed back, so taking a reference to the object is delayed till it gets reassigned to a ref_ptr<>, but at the same time another
thread calls a flush of the Object Cache deleting this object as it's referenceCount is now zero.  Using osgDB::readREf*File() makes sure the a ref_ptr<> is
passed back and the referenceCount never goes to zero.

To ensure the OSG builds when OSG_PROVIDE_READFILE is to OFF the many cases of osgDB::read*File() usage had to be replaced with a ref_ptr<> osgDB::readRef*File()
usage.  The avoid this change causing lots of other client code to be rewritten to handle the use of ref_ptr<> in place of C pointer I introduced a serious of
templte methods in various class to adapt ref_ptr<> to the underly C pointer to be passed to old OSG API's, example of this is found in include/osg/Group:

    bool addChild(Node* child); // old method which can only be used with a Node*

    tempalte<class T> bool addChild(const osg::ref_ptr<T>& child) { return addChild(child.get()); } // adapter template method

These changes together cover 149 modified files, so it's a large submission. This extent of changes are warrent to make use of the Object Cache
and multi-threaded loaded more robust.



git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@15164 16af8721-9629-0410-8352-f15c8da7e697
2015-10-22 13:42:19 +00:00

600 lines
18 KiB
C++

// -*-c++-*- osgWidget - Code by: Jeremy Moles (cubicool) 2007-2008
#include <osg/io_utils>
#include <osgDB/ReadFile>
#include <osgWidget/WindowManager>
#include <osgWidget/Frame>
#include <cassert>
namespace osgWidget {
std::string Frame::cornerTypeToString(CornerType c)
{
if(c == CORNER_LOWER_LEFT) return "CornerLowerLeft";
else if(c == CORNER_LOWER_RIGHT) return "CornerLowerRight";
else if(c == CORNER_UPPER_RIGHT) return "CornerUpperRight";
else return "CornerUpperLeft";
}
std::string Frame::borderTypeToString(BorderType b)
{
if(b == BORDER_LEFT) return "BorderLeft";
else if(b == BORDER_RIGHT) return "BorderRight";
else if(b == BORDER_TOP) return "BorderTop";
else return "BorderBottom";
}
Frame::Corner::Corner(CornerType corner, point_type width, point_type height):
Widget (cornerTypeToString(corner), width, height),
_corner (corner)
{
}
Frame::Corner::Corner(const Corner& corner, const osg::CopyOp& co):
Widget (corner, co),
_corner (corner._corner)
{
}
void Frame::Corner::parented(Window* /*window*/) {
Frame* parent = dynamic_cast<Frame*>(getParent());
if(!parent) return;
if(parent->canResize()) setEventMask(EVENT_MASK_MOUSE_DRAG);
}
bool Frame::Corner::mouseDrag(double x, double y, const WindowManager* /*wm*/)
{
Frame* parent = dynamic_cast<Frame*>(getParent());
if(!parent || !parent->canResize()) return false;
if(_corner == CORNER_UPPER_LEFT) {
if(parent->resizeAdd(-x, y)) parent->addX(x);
}
else if(_corner == CORNER_UPPER_RIGHT) parent->resizeAdd(x, y);
else if(_corner == CORNER_LOWER_RIGHT) {
if(parent->resizeAdd(x, -y)) parent->addY(y);
}
else {
if(parent->resizeAdd(-x, -y)) parent->addOrigin(x, y);
}
parent->update();
return true;
}
Frame::Border::Border(BorderType border, point_type width, point_type height):
Widget (borderTypeToString(border), width, height),
_border (border)
{
setCanFill(true);
}
Frame::Border::Border(const Border& border, const osg::CopyOp& co):
Widget (border, co),
_border (border._border)
{
}
void Frame::Border::parented(Window* /*window*/) {
Frame* parent = dynamic_cast<Frame*>(getParent());
if(!parent) return;
if(parent->canResize()) setEventMask(EVENT_MASK_MOUSE_DRAG);
}
void Frame::Border::positioned()
{
osg::Image* image = _image();
if(!image) return;
Frame* parent = dynamic_cast<Frame*>(getParent());
if(!parent || !parent->canTexture()) return;
point_type w = image->s() / 8.0f;
point_type h = getHeight();
if(_border == BORDER_LEFT) setTexCoordRegion(w * 3, 0.0f, w, h);
else if(_border == BORDER_RIGHT) setTexCoordRegion(w * 4, 0.0f, w, h);
else if(_border == BORDER_TOP) {
// TODO: Temporary; fix this.
point_type tx1 = (w * 2) / image->s();
point_type tx2 = w / image->s();
point_type tx3 = getWidth() / w;
setTexCoord(tx1, tx3, LL);
setTexCoord(tx1, 0.0f, LR);
setTexCoord(tx2, 0.0f, UR);
setTexCoord(tx2, tx3, UL);
}
else {
point_type tx1 = (w * 7) / image->s();
point_type tx2 = (w * 6) / image->s();
point_type tx3 = getWidth() / w;
setTexCoord(tx1, tx3, LL);
setTexCoord(tx1, 0.0f, LR);
setTexCoord(tx2, 0.0f, UR);
setTexCoord(tx2, tx3, UL);
}
}
bool Frame::Border::mouseDrag(double x, double y, const WindowManager* /*wm*/)
{
Frame* parent = dynamic_cast<Frame*>(getParent());
if(!parent) return false;
if(_border == BORDER_TOP && parent->canMove()) parent->addOrigin(x, y);
else {
if(!parent->canResize()) return false;
if(_border == BORDER_LEFT) {
if(parent->resizeAdd(-x, 0.0f)) parent->addX(x);
}
else if(_border == BORDER_RIGHT) parent->resizeAdd(x, 0.0f);
else {
if(parent->resizeAdd(0.0f, -y)) parent->addY(y);
}
}
parent->update();
return true;
}
Frame::Frame(const std::string& name, unsigned int flags):
Table (name, 3, 3),
_flags (flags)
{
}
Frame::Frame(const Frame& frame, const osg::CopyOp& co):
Table(frame, co),
_flags(frame._flags)
{
}
Widget* Frame::_getCorner(CornerType c) const
{
return const_cast<Widget*>(getByName(cornerTypeToString(c)));
}
Widget* Frame::_getBorder(BorderType b) const
{
return const_cast<Widget*>(getByName(borderTypeToString(b)));
}
bool Frame::setWindow(Window* window)
{
if(!window) return false;
EmbeddedWindow* ew = getEmbeddedWindow();
// If it's the first time setting the Window...
// if(!ew || !ew->getWindow()) return addWidget(window->embed(), 1, 1);
if(!ew) return addWidget(window->embed(), 1, 1);
else return ew->setWindow(window);
}
Frame* Frame::createSimpleFrame(
const std::string& name,
point_type cw,
point_type ch,
point_type w,
point_type h,
unsigned int flags,
Frame* exFrame
) {
Frame* frame = 0;
// Use an "existing frame" if we have it (for example, if you've in inherited from
// Frame and want to use this stuff.
if(!exFrame) frame = new Frame(name, flags);
else frame = exFrame;
frame->addWidget(new Corner(CORNER_LOWER_LEFT, cw, ch), 0, 0);
frame->addWidget(new Border(BORDER_BOTTOM, w, ch), 0, 1);
frame->addWidget(new Corner(CORNER_LOWER_RIGHT, cw, ch), 0, 2);
frame->addWidget(new Border(BORDER_LEFT, cw, h), 1, 0);
frame->addWidget(new Border(BORDER_RIGHT, cw, h), 1, 2);
frame->addWidget(new Corner(CORNER_UPPER_LEFT, cw, ch), 2, 0);
frame->addWidget(new Border(BORDER_TOP, w, ch), 2, 1);
frame->addWidget(new Corner(CORNER_UPPER_RIGHT, cw, ch), 2, 2);
EmbeddedWindow* ew = new EmbeddedWindow(name, w, h);
ew->setCanFill(true);
frame->addWidget(ew, 1, 1);
return frame;
}
/*
Frame* Frame::createSimpleFrameWithSingleTexture(
const std::string& name,
const std::string& texture,
point_type tw,
point_type th,
point_type cw,
point_type ch,
point_type w,
point_type h,
Frame* exFrame
) {
Frame* frame = 0;
// The same as above...
if(!exFrame) frame = createSimpleFrame(name, cw, ch, w, h);
else frame = createSimpleFrame(name, cw, ch, w, h, exFrame);
for(unsigned int i = 0; i < 9; i++) frame->getObjects()[i]->setImage(texture);
frame->getByRowCol(0, 0)->setTexCoordRegion(0.0f, th - ch, cw, ch);
frame->getByRowCol(0, 1)->setTexCoordRegion(cw, th - ch, tw - (cw * 2.0f), ch);
frame->getByRowCol(0, 2)->setTexCoordRegion(tw - cw, th - ch, cw, ch);
frame->getByRowCol(1, 0)->setTexCoordRegion(0.0f, ch, cw, th - (ch * 2.0f));
frame->getByRowCol(1, 2)->setTexCoordRegion(tw - cw, ch, cw, th - (ch * 2.0f));
frame->getByRowCol(2, 0)->setTexCoordRegion(0.0f, 0.0f, cw, ch);
frame->getByRowCol(2, 1)->setTexCoordRegion(cw, 0.0f, tw - (cw * 2.0f), ch);
frame->getByRowCol(2, 2)->setTexCoordRegion(tw - cw, 0.0f, cw, ch);
frame->getEmbeddedWindow()->setTexCoordRegion(cw, ch, tw - (cw * 2.0f), th - (ch * 2.0f));
return frame;
}
*/
// Inspired by: http://www.wowwiki.com/EdgeFiles
Frame* Frame::createSimpleFrameWithSingleTexture(
const std::string& name,
osg::ref_ptr<osg::Image> image,
point_type width,
point_type height,
unsigned int flags,
Frame* exFrame
) {
Frame* frame = 0;
double w = width;
double h = height;
if (image)
{
w = image->s() / 8.0f;
h = image->t();
}
// The same as above...
if(!exFrame) frame = createSimpleFrame(name, w, h, width, height, flags);
else frame = createSimpleFrame(name, w, h, width, height, 0, exFrame);
if (image)
{
for(unsigned int i = 0; i < 9; i++) frame->getObjects()[i]->setImage(image);
XYCoord twh(w, h);
frame->getCorner(CORNER_UPPER_LEFT )->setTexCoordRegion(0.0f, 0.0f, twh);
frame->getBorder(BORDER_TOP )->setTexCoordRegion(w, 0.0f, twh);
frame->getCorner(CORNER_UPPER_RIGHT)->setTexCoordRegion(w * 2, 0.0f, twh);
frame->getBorder(BORDER_LEFT )->setTexCoordRegion(w * 3, 0.0f, twh);
frame->getBorder(BORDER_RIGHT )->setTexCoordRegion(w * 4, 0.0f, twh);
frame->getCorner(CORNER_LOWER_LEFT )->setTexCoordRegion(w * 5, 0.0f, twh);
frame->getBorder(BORDER_BOTTOM )->setTexCoordRegion(w * 6, 0.0f, twh);
frame->getCorner(CORNER_LOWER_RIGHT)->setTexCoordRegion(w * 7, 0.0f, twh);
// We set all of these to wrap vertically, but the REAL texture coordinates will
// be generated properly in the positioned() method.
frame->getByRowCol(0, 1)->setTexCoordWrapVertical();
frame->getByRowCol(1, 0)->setTexCoordWrapVertical();
frame->getByRowCol(1, 2)->setTexCoordWrapVertical();
frame->getByRowCol(2, 1)->setTexCoordWrapVertical();
// frame->getEmbeddedWindow()->setTexCoordRegion(cw, ch, tw - (cw * 2.0f), th - (ch * 2.0f));
}
else
{
OSG_WARN << "createSimpleFrameWithSingleTexture with a null image, the frame " << name << " will be use texture" << std::endl;
}
return frame;
}
bool Frame::resizeFrame(point_type w, point_type h) {
Border* left = getBorder(BORDER_LEFT);
Border* right = getBorder(BORDER_RIGHT);
Border* top = getBorder(BORDER_TOP);
Border* bottom = getBorder(BORDER_BOTTOM);
if(!left || !right || !top || !bottom) return false;
return resize(
left->getWidth() + right->getWidth() + w,
top->getHeight() + bottom->getHeight() + h
);
}
osg::Image* createNatifEdgeImageFromTheme(osg::Image* theme);
Frame* Frame::createSimpleFrameFromTheme(
const std::string& name,
osg::ref_ptr<osg::Image> image,
point_type width,
point_type height,
unsigned int flags,
Frame* exFrame
) {
osg::ref_ptr<osg::Image> natifImage = createNatifEdgeImageFromTheme(image.get());
Frame* frame;
frame = createSimpleFrameWithSingleTexture(name, natifImage.get(), width, height, flags, exFrame);
if (frame && image && natifImage.valid())
{
const unsigned int bpps = image->getPixelSizeInBits() / 8;
const unsigned int one_third_s = image->s()/3;
unsigned char* srcdata = (unsigned char*)image->data();
osg::Vec4 color(0,0,0,1);
for (unsigned int d = 0; d < bpps; d++)
{
color[d] = srcdata[one_third_s * image->s() * bpps + (one_third_s) * bpps + d] * 1.0/255.0;
}
frame->getEmbeddedWindow()->setColor(color);
}
return frame;
}
// (c) 2006-2008 Jean-Sébastien Guay
// adapted by Cedric Pinson
/** Implementation of copyImage. */
template<typename T>
void copyDataImpl(const osg::Image* source,
const unsigned int x1, const unsigned int y1,
const unsigned int x2, const unsigned int y2,
osg::Image* destination,
const unsigned int xd = 0, const unsigned int yd = 0)
{
if ((unsigned int)destination->s() >= xd + (x2 - x1) &&
(unsigned int)destination->t() >= yd + (y2 - y1))
{
const unsigned int bpps = source->getPixelSizeInBits() / (8 * sizeof(T));
T* srcdata = (T*)source->data();
T* dstdata = (T*)destination->data();
for (unsigned int y = 0; y < y2 - y1; ++y)
{
for (unsigned int x = 0; x < x2 - x1; ++x)
{
for (unsigned int d = 0; d < bpps; d++)
{
T v = srcdata[(y + y1) * source->s() * bpps + (x + x1) * bpps + d];
dstdata[(yd + y) * destination->s() * bpps + (xd + x) * bpps + d] = v;
}
}
}
}
else
assert(false && "copyDataImpl: Incorrect image dimensions.");
}
/** Copies a rectangle of corners (x1, y1), (x2, y2) from an image into
another image starting at position (xd, yd). No scaling is done, the
pixels are just copied, so the destination image must be at least
(xd + (x2 - x1)) by (yd + (y2 - y1)) pixels. */
void copyData(const osg::Image* source,
const unsigned int x1, const unsigned int y1,
const unsigned int x2, const unsigned int y2,
osg::Image* destination,
const unsigned int xd, const unsigned int yd)
{
if (source->getDataType() == destination->getDataType())
{
if (source->getDataType() == GL_UNSIGNED_BYTE)
{
copyDataImpl<unsigned char>(source, x1, y1, x2, y2,
destination, xd, yd);
}
else
{
assert(false && "copyData not implemented for this data type");
}
}
else
{
assert(false && "source and destination images must be of the same type.");
return;
}
}
/** Implementation of rotateImage. */
template<typename T>
osg::Image* rotateImageImpl(osg::Image* image)
{
if (image->s() == image->t())
{
const unsigned int s = image->s();
const unsigned int bpp = image->getPixelSizeInBits() / (8 * sizeof(T));
osg::ref_ptr<osg::Image> destination = new osg::Image;
destination->allocateImage(s, s, 1,
image->getPixelFormat(), image->getDataType(),
image->getPacking());
destination->setInternalTextureFormat(image->getInternalTextureFormat());
T* srcdata = (T*)image->data();
T* dstdata = (T*)destination->data();
for (unsigned int y = 0; y < s; ++y)
{
for (unsigned int x = 0; x < s; ++x)
{
for (unsigned int p = 0; p < bpp; p++)
dstdata[y * s * bpp + x * bpp + p] = srcdata[x * s * bpp + y * bpp + p];
}
}
return destination.release();
}
else
{
assert(false && "rotateImageImpl: Image must be square.");
return 0;
}
}
/** Rotates an osg::Image by 90 degrees. Returns a new osg::Image, be sure to
store it in a ref_ptr so it will be freed correctly. */
osg::Image* rotateImage(osg::Image* image)
{
if (image->getDataType() == GL_UNSIGNED_BYTE)
{
return rotateImageImpl<unsigned char>(image);
}
else
{
assert(false && "rotateImage not implemented for this data type");
return 0;
}
}
// SOURCE
// +---+---+---+
// | 1 | 2 | 3 |
// +---+---+---+
// | 4 | | 5 |
// +---+---+---+
// | 6 | 7 | 8 |
// +---+---+---+
// FINAL
// +---+---+---+---+---+---+---+---+
// | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
// +---+---+---+---+---+---+---+---+
// 1. Upper-Left corner.
// 2. Top border (rotated 90 degrees CCW).
// 3. Upper-Right corner.
// 4. Left border.
// 5. Right border.
// 6. Bottom-Left corner.
// 7. Bottom border (rotated 90 degrees CCW).
// 8. Bottom-Right corner.
osg::Image* createNatifEdgeImageFromTheme(osg::Image* theme)
{
if (!theme) {
OSG_WARN << "can't create a natif edge image from null image theme as argument" << std::endl;
return 0;
}
osg::ref_ptr<osg::Image> final = new osg::Image;
const int s = theme->s();
const int t = theme->t();
const GLenum pixelFormat = theme->getPixelFormat();
const GLenum dataType = theme->getDataType();
const GLint internalFormat = theme->getInternalTextureFormat();
unsigned int packing = theme->getPacking();
if (s != t)
{
OSG_WARN << "width and height are different, bad format theme image " << theme->getFileName() << std::endl;
return 0;
}
// check size
int ceilvalue = static_cast<int>(ceil(s * 1.0 / 3));
int intvalue = s/3;
if (intvalue != ceilvalue)
{
OSG_WARN << "the size of theme file " << theme->getFileName() << " can not be divided by 3, check the documentation about theme format" << std::endl;
return 0;
}
const unsigned int one_third_s = s/3;
const unsigned int one_third_t = t/3;
final->allocateImage(8 * one_third_s , one_third_t, 1, pixelFormat, dataType, packing);
final->setInternalTextureFormat(internalFormat);
// copy 1 (6 in source)
copyData(theme, 0, 2 * one_third_s, one_third_s, 3 * one_third_s, final.get(), 0, 0);
// rotate and copy 2
osg::ref_ptr<osg::Image> rotateandcopy2 = new osg::Image;
rotateandcopy2->allocateImage(one_third_s , one_third_t, 1, pixelFormat, dataType, packing);
rotateandcopy2->setInternalTextureFormat(internalFormat);
copyData(theme, one_third_s, 0, 2 * one_third_s , one_third_s, rotateandcopy2.get(), 0, 0);
rotateandcopy2 = rotateImage(rotateandcopy2.get());
rotateandcopy2->flipHorizontal();
copyData(rotateandcopy2.get(), 0, 0, one_third_s , one_third_s, final.get(), 6*one_third_s, 0);
// copy 3 (8 in source)
copyData(theme, 2*one_third_s , 2 *one_third_s, 3*one_third_s , 3 * one_third_s, final.get(), 2 * one_third_s, 0);
// copy 4
copyData(theme, 0, one_third_s, one_third_s , 2 * one_third_s, final.get(), 3 * one_third_s, 0);
// copy 5
copyData(theme, 2*one_third_s , one_third_s, 3 * one_third_s , 2 * one_third_s, final.get(), 4 * one_third_s, 0);
// copy 6 (1 in source)
copyData(theme, 0 , 0, one_third_s, one_third_s, final.get(), 5 * one_third_s, 0);
// rotate and copy 7
osg::ref_ptr<osg::Image> rotateandcopy7 = new osg::Image;
rotateandcopy7->allocateImage(one_third_s , one_third_t, 1, pixelFormat, dataType, packing);
rotateandcopy7->setInternalTextureFormat(internalFormat);
copyData(theme, one_third_s, 2*one_third_s, 2 * one_third_s , 3 * one_third_s, rotateandcopy7.get(), 0, 0);
rotateandcopy7 = rotateImage(rotateandcopy7.get());
rotateandcopy7->flipHorizontal();
copyData(rotateandcopy7.get(), 0, 0, one_third_s , one_third_s, final.get(), one_third_s, 0);
// copy 8 (3 in source)
copyData(theme, 2 * one_third_s, 0, 3 * one_third_s , one_third_s , final.get(), 7 * one_third_s, 0);
return final.release();
}
}