Adding pixel manipulators to canvas::Image
This commit is contained in:
@@ -838,5 +838,98 @@ namespace canvas
|
||||
return false;
|
||||
}
|
||||
|
||||
void Image::fillRect(const SGRect<int>& rect, const std::string& c)
|
||||
{
|
||||
osg::Vec4 color(1,1,1,1);
|
||||
if(!c.empty() && !parseColor(c, color))
|
||||
return;
|
||||
|
||||
fillRect(rect, color);
|
||||
}
|
||||
|
||||
void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes)
|
||||
{
|
||||
GLubyte* dst = row;
|
||||
for (int x = 0; x < width; ++x) {
|
||||
memcpy(dst, &pixel, pixelBytes);
|
||||
dst += pixelBytes;
|
||||
}
|
||||
}
|
||||
|
||||
SGRect<int> intersectRect(const SGRect<int>& a, const SGRect<int>& b)
|
||||
{
|
||||
SGVec2<int> m1 = max(a.getMin(), b.getMin());
|
||||
SGVec2<int> m2 = min(a.getMax(), b.getMax());
|
||||
return SGRect<int>(m1, m2);
|
||||
}
|
||||
|
||||
void Image::fillRect(const SGRect<int>& rect, const osg::Vec4& color)
|
||||
{
|
||||
osg::ref_ptr<osg::Image> image = _texture->getImage();
|
||||
const auto format = image->getInternalTextureFormat();
|
||||
|
||||
auto clippedRect = intersectRect(rect, SGRect<int>(0, 0, image->s(), image->t()));
|
||||
if ((clippedRect.width() == 0) || (clippedRect.height() == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLubyte* rowData = nullptr;
|
||||
size_t rowByteSize = 0;
|
||||
GLuint pixelWidth = clippedRect.width();
|
||||
GLuint pixel = 0;
|
||||
GLuint pixelBytes = 0;
|
||||
|
||||
switch (format) {
|
||||
case GL_RGBA8:
|
||||
rowByteSize = pixelWidth * 4;
|
||||
rowData = static_cast<GLubyte*>(alloca(rowByteSize));
|
||||
|
||||
// assume litte-endian, so read out backwards, hence when we memcpy
|
||||
// the data, it ends up in RGBA order
|
||||
pixel = color.asABGR();
|
||||
pixelBytes = 4;
|
||||
fillRow(rowData, pixel, pixelWidth, pixelBytes);
|
||||
break;
|
||||
|
||||
case GL_RGB8:
|
||||
rowByteSize = pixelWidth * 3;
|
||||
rowData = static_cast<GLubyte*>(alloca(rowByteSize));
|
||||
pixel = color.asABGR();
|
||||
pixelBytes = 3;
|
||||
fillRow(rowData, pixel, pixelWidth, pixelBytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
SG_LOG(SG_IO, SG_WARN, __PRETTY_FUNCTION__ << ": unsupported internal image format:" << format);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row=clippedRect.t(); row < clippedRect.b(); ++row) {
|
||||
GLubyte* imageData = image->data(clippedRect.l(), row);
|
||||
memcpy(imageData, rowData, rowByteSize);
|
||||
}
|
||||
|
||||
setImage(image);
|
||||
}
|
||||
|
||||
void Image::setPixel(int x, int y, const std::string& c)
|
||||
{
|
||||
osg::Vec4 color(1,1,1,1);
|
||||
if(!c.empty() && !parseColor(c, color))
|
||||
return;
|
||||
|
||||
setPixel(x, y, color);
|
||||
}
|
||||
|
||||
void Image::setPixel(int x, int y, const osg::Vec4& color)
|
||||
{
|
||||
osg::ref_ptr<osg::Image> image = _texture->getImage();
|
||||
image->setColor(color, x, y);
|
||||
|
||||
// is this needed, or does OSG track modifications to the data
|
||||
// automatically?
|
||||
setImage(image);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -101,6 +101,22 @@ namespace canvas
|
||||
*/
|
||||
void setSourceRect(const SGRect<float>& sourceRect);
|
||||
|
||||
/**
|
||||
* fill the specified rectangle of the image, with an RGB value
|
||||
*/
|
||||
void fillRect(const SGRect<int>& rect, const std::string& color);
|
||||
|
||||
/**
|
||||
* fill the specified rectangle of the image, with an RGB value
|
||||
*/
|
||||
void fillRect(const SGRect<int>& rect, const osg::Vec4& color);
|
||||
|
||||
void setPixel(int x, int y, const std::string& color);
|
||||
|
||||
void setPixel(int x, int y, const osg::Vec4& color);
|
||||
|
||||
|
||||
// void setRow(int row, int offset, )
|
||||
protected:
|
||||
enum ImageAttributes
|
||||
{
|
||||
|
||||
@@ -193,8 +193,8 @@ min(S s, SGVec2<T> v)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
max(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ v1 = simd4::max(v1.simd2(), v2.simd2()); return v1; }
|
||||
max(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||
{ v1.simd2() = simd4::max(v1.simd2(), v2.simd2()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
|
||||
@@ -207,6 +207,15 @@ namespace nasal
|
||||
return Vec2(vec[0], vec[1]);
|
||||
}
|
||||
|
||||
template<class Vec4>
|
||||
std::enable_if_t<is_vec4<Vec4>::value, Vec4>
|
||||
from_nasal_helper(naContext c, naRef ref, const Vec4*)
|
||||
{
|
||||
auto vec = from_nasal_helper(c, ref, static_cast<std::array<double, 4>*>(0));
|
||||
return Vec4(vec[0], vec[1], vec[2], vec[3]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a Nasal vector with 4 elements ([x, y, w, h]) to an SGRect
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@ class SGWeakReferenced;
|
||||
template<class T> class SGSharedPtr;
|
||||
template<class T> class SGWeakPtr;
|
||||
template<class T> class SGVec2;
|
||||
template<class T> class SGVec4;
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@@ -47,6 +48,9 @@ namespace osg
|
||||
class Vec2d;
|
||||
class Vec2f;
|
||||
class Vec2s;
|
||||
|
||||
class Vec4f;
|
||||
class Vec4d;
|
||||
}
|
||||
|
||||
// The actual traits...
|
||||
@@ -55,6 +59,10 @@ namespace nasal
|
||||
template<class T>
|
||||
struct is_vec2: public std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_vec4: public std::false_type {};
|
||||
|
||||
|
||||
#define SG_MAKE_TRAIT(templ,type,attr)\
|
||||
template templ\
|
||||
struct attr< type >: public std::true_type {};
|
||||
@@ -65,6 +73,10 @@ SG_MAKE_TRAIT(<>, osg::Vec2d, is_vec2)
|
||||
SG_MAKE_TRAIT(<>, osg::Vec2f, is_vec2)
|
||||
SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||
|
||||
SG_MAKE_TRAIT(<class T>, SGVec4<T>, is_vec4)
|
||||
SG_MAKE_TRAIT(<>, osg::Vec4d, is_vec4)
|
||||
SG_MAKE_TRAIT(<>, osg::Vec4f, is_vec4)
|
||||
|
||||
#undef SG_MAKE_TRAIT
|
||||
|
||||
template<class T>
|
||||
|
||||
Reference in New Issue
Block a user