repair OpenCVTexture (not safe for 1.7.x)

This commit is contained in:
David Rose 2010-10-20 16:28:26 +00:00
parent dbe93fc1df
commit f6e2d9a387
8 changed files with 268 additions and 120 deletions

View File

@ -3145,7 +3145,7 @@ do_write_txo(ostream &out, const string &filename) const {
////////////////////////////////////////////////////////////////////
// Function: Texture::do_unlock_and_reload_ram_image
// Access: Protected
// Access: Protected, Virtual
// Description: This is similar to do_reload_ram_image(), except that
// the lock is released during the actual operation, to
// allow normal queries into the Texture object to
@ -3173,7 +3173,7 @@ do_unlock_and_reload_ram_image(bool allow_compression) {
// pre-compressed, we don't consider it.
has_ram_image = false;
}
if (_loaded_from_image && !has_ram_image && !_fullpath.empty()) {
if (!has_ram_image && do_can_reload()) {
nassertv(!_reloading);
_reloading = true;
@ -4235,7 +4235,7 @@ do_has_uncompressed_ram_image() const {
////////////////////////////////////////////////////////////////////
CPTA_uchar Texture::
do_get_ram_image() {
if (_loaded_from_image && !do_has_ram_image() && !_fullpath.empty()) {
if (!do_has_ram_image() && do_can_reload()) {
do_unlock_and_reload_ram_image(true);
// Normally, we don't update the _modified semaphores in a do_blah
@ -4273,7 +4273,7 @@ do_get_uncompressed_ram_image() {
}
// Couldn't uncompress the existing image. Try to reload it.
if (_loaded_from_image && (!do_has_ram_image() || _ram_image_compression != CM_off) && !_fullpath.empty()) {
if ((!do_has_ram_image() || _ram_image_compression != CM_off) && do_can_reload()) {
do_unlock_and_reload_ram_image(false);
}
@ -4691,6 +4691,20 @@ do_set_pad_size(int x, int y, int z) {
_pad_z_size = z;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::do_can_reload
// Access: Protected, Virtual
// Description: Returns true if we can safely call
// do_unlock_and_reload_ram_image() in order to make the
// image available, or false if we shouldn't do this
// (because we know from a priori knowledge that it
// wouldn't work anyway).
////////////////////////////////////////////////////////////////////
bool Texture::
do_can_reload() {
return (_loaded_from_image && !_fullpath.empty());
}
////////////////////////////////////////////////////////////////////
// Function: Texture::do_reload
// Access: Protected
@ -4698,7 +4712,7 @@ do_set_pad_size(int x, int y, int z) {
////////////////////////////////////////////////////////////////////
bool Texture::
do_reload() {
if (_loaded_from_image && !_fullpath.empty()) {
if (do_can_reload()) {
do_clear_ram_image();
do_unlock_and_reload_ram_image(true);
if (do_has_ram_image()) {

View File

@ -496,7 +496,7 @@ protected:
bool do_write_txo_file(const Filename &fullpath) const;
bool do_write_txo(ostream &out, const string &filename) const;
void do_unlock_and_reload_ram_image(bool allow_compression);
virtual void do_unlock_and_reload_ram_image(bool allow_compression);
virtual void do_reload_ram_image(bool allow_compression);
PTA_uchar do_modify_ram_image();
PTA_uchar do_make_ram_image();
@ -561,6 +561,7 @@ protected:
void do_clear_ram_mipmap_images();
void do_generate_ram_mipmap_images();
void do_set_pad_size(int x, int y, int z);
virtual bool do_can_reload();
bool do_reload();
// This nested class declaration is used below.

View File

@ -66,37 +66,6 @@ get_tex_scale() const {
(float)_video_height / _y_size);
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::set_video_size
// Access: Protected
// Description: Should be called by a derived class to set the size
// of the video when it is loaded.
////////////////////////////////////////////////////////////////////
INLINE void VideoTexture::
set_video_size(int video_width, int video_height) {
_video_width = video_width;
_video_height = video_height;
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::consider_update
// Access: Protected
// Description: Calls update_frame() if the current frame has
// changed.
////////////////////////////////////////////////////////////////////
INLINE void VideoTexture::
consider_update() {
int this_frame = ClockObject::get_global_clock()->get_frame_count();
if (this_frame != _last_frame_update) {
int frame = get_frame();
if (_current_frame != frame) {
update_frame(frame);
_current_frame = frame;
}
_last_frame_update = this_frame;
}
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::clear_current_frame
// Access: Protected

View File

@ -107,6 +107,22 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::set_video_size
// Access: Protected
// Description: Should be called by a derived class to set the size
// of the video when it is loaded. Assumes the lock is
// held.
////////////////////////////////////////////////////////////////////
void VideoTexture::
set_video_size(int video_width, int video_height) {
_video_width = video_width;
_video_height = video_height;
do_set_pad_size(max(_x_size - _video_width, 0),
max(_y_size - _video_height, 0),
0);
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::do_has_ram_image
// Access: Protected, Virtual
@ -134,6 +150,22 @@ reconsider_dirty() {
consider_update();
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::do_unlock_and_reload_ram_image
// Access: Protected, Virtual
// Description: This is similar to do_reload_ram_image(), except that
// the lock is released during the actual operation, to
// allow normal queries into the Texture object to
// continue during what might be a slow operation.
//
// In the case of a VideoTexture, this is exactly the
// same as do_reload_ram_image().
////////////////////////////////////////////////////////////////////
void VideoTexture::
do_unlock_and_reload_ram_image(bool) {
consider_update();
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::do_reload_ram_image
// Access: Protected, Virtual
@ -143,7 +175,39 @@ reconsider_dirty() {
// available, if possible.
////////////////////////////////////////////////////////////////////
void VideoTexture::
do_reload_ram_image() {
do_reload_ram_image(bool) {
consider_update();
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::do_can_reload
// Access: Protected, Virtual
// Description: Returns true if we can safely call
// do_unlock_and_reload_ram_image() in order to make the
// image available, or false if we shouldn't do this
// (because we know from a priori knowledge that it
// wouldn't work anyway).
////////////////////////////////////////////////////////////////////
bool VideoTexture::
do_can_reload() {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VideoTexture::consider_update
// Access: Protected, Virtual
// Description: Calls update_frame() if the current frame has
// changed.
////////////////////////////////////////////////////////////////////
void VideoTexture::
consider_update() {
int this_frame = ClockObject::get_global_clock()->get_frame_count();
if (this_frame != _last_frame_update) {
int frame = get_frame();
if (_current_frame != frame) {
update_frame(frame);
_current_frame = frame;
}
_last_frame_update = this_frame;
}
}

View File

@ -45,14 +45,16 @@ public:
virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const;
protected:
INLINE void set_video_size(int video_width, int video_height);
void set_video_size(int video_width, int video_height);
virtual bool do_has_ram_image() const;
virtual void reconsider_dirty();
virtual void do_reload_ram_image();
virtual void do_unlock_and_reload_ram_image(bool allow_compression);
virtual void do_reload_ram_image(bool allow_compression);
virtual bool do_can_reload();
virtual INLINE void consider_update();
virtual void consider_update();
INLINE void clear_current_frame();
virtual void update_frame(int frame)=0;

View File

@ -65,31 +65,3 @@ INLINE OpenCVTexture::VideoPage::
~VideoPage() {
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::consider_update
// Access: Protected
// Description: Calls update_frame() if the current frame has
// changed.
////////////////////////////////////////////////////////////////////
INLINE void OpenCVTexture::
consider_update() {
int this_frame = ClockObject::get_global_clock()->get_frame_count();
if (this_frame != _last_frame_update) {
int frame = get_frame();
if (_current_frame != frame) {
update_frame(frame);
_current_frame = frame;
} else {
// Loop through the pages to see if there's any camera stream to update.
int max_z = max(_z_size, (int)_pages.size());
for (int z = 0; z < max_z; ++z) {
VideoPage &page = _pages[z];
if (!page._color.is_from_file() || !page._alpha.is_from_file()) {
update_frame(frame, z);
}
}
}
_last_frame_update = this_frame;
}
}

View File

@ -58,6 +58,34 @@ OpenCVTexture::
~OpenCVTexture() {
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::consider_update
// Access: Protected, Virtual
// Description: Calls update_frame() if the current frame has
// changed.
////////////////////////////////////////////////////////////////////
void OpenCVTexture::
consider_update() {
int this_frame = ClockObject::get_global_clock()->get_frame_count();
if (this_frame != _last_frame_update) {
int frame = get_frame();
if (_current_frame != frame) {
update_frame(frame);
_current_frame = frame;
} else {
// Loop through the pages to see if there's any camera stream to update.
int max_z = max(_z_size, (int)_pages.size());
for (int z = 0; z < max_z; ++z) {
VideoPage &page = _pages[z];
if (!page._color.is_from_file() || !page._alpha.is_from_file()) {
update_frame(frame, z);
}
}
}
_last_frame_update = this_frame;
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::do_make_copy
// Access: Protected, Virtual
@ -97,23 +125,46 @@ do_assign(const OpenCVTexture &copy) {
// is specified) to accept its input from the camera
// with the given index number, or the default camera if
// the index number is -1 or unspecified.
//
// If alpha_file_channel is 0, then the camera image
// becomes a normal RGB texture. If it is 1, 2, or 3,
// then the camera image becomes an alpha texture, using
// the indicated channel of the source.
////////////////////////////////////////////////////////////////////
bool OpenCVTexture::
from_camera(int camera_index, int z, const LoaderOptions &options) {
from_camera(int camera_index, int z, int alpha_file_channel,
const LoaderOptions &options) {
if (!do_reconsider_z_size(z)) {
return false;
}
nassertr(z >= 0 && z < get_z_size(), false);
VideoPage &page = modify_page(z);
page._alpha.clear();
if (!page._color.from_camera(camera_index)) {
return false;
}
_alpha_file_channel = alpha_file_channel;
if (!do_reconsider_video_properties(page._color, 3, z, options)) {
VideoPage &page = modify_page(z);
if (alpha_file_channel == 0) {
// A normal RGB texture.
page._alpha.clear();
if (!page._color.from_camera(camera_index)) {
return false;
}
if (!do_reconsider_video_properties(page._color, 3, z, options)) {
page._color.clear();
return false;
}
} else {
// An alpha texture.
page._color.clear();
return false;
if (!page._alpha.from_camera(camera_index)) {
return false;
}
if (!do_reconsider_video_properties(page._alpha, 1, z, options)) {
page._alpha.clear();
return false;
}
do_set_format(F_alpha);
}
set_loaded_from_image();
@ -161,7 +212,15 @@ do_reconsider_video_properties(const OpenCVTexture::VideoStream &stream,
<< "Loaded " << stream._filename << ", " << num_frames << " frames at "
<< frame_rate << " fps\n";
}
} else {
// In this case, we don't have a specific frame rate or number of
// frames. Let both values remain at 0.
if (vision_cat.is_debug()) {
vision_cat.debug()
<< "Loaded camera stream\n";
}
}
int width = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_WIDTH);
int height = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_HEIGHT);
@ -238,74 +297,91 @@ update_frame(int frame) {
////////////////////////////////////////////////////////////////////
void OpenCVTexture::
update_frame(int frame, int z) {
vision_cat.spam() << "Updating OpenCVTexture page " << z << "\n";
if (vision_cat.is_spam()) {
vision_cat.spam()
<< "Updating OpenCVTexture page " << z << "\n";
}
VideoPage &page = _pages[z];
if (page._color.is_valid() || page._alpha.is_valid()) {
do_modify_ram_image();
++_image_modified;
}
ssize_t dest_x_pitch = _num_components * _component_width;
ssize_t dest_y_pitch = _x_size * dest_x_pitch;
if (page._color.is_valid()) {
nassertv(get_num_components() >= 3 && get_component_width() == 1);
const unsigned char *source = page._color.get_frame_data(frame);
if (source != NULL) {
const unsigned char *r, *g, *b;
ssize_t x_pitch, y_pitch;
if (page._color.get_frame_data(frame, r, g, b, x_pitch, y_pitch)) {
nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size);
unsigned char *dest = _ram_images[0]._image.p() + do_get_expected_ram_page_size() * z;
int dest_row_width = (_x_size * _num_components * _component_width);
int source_row_width = get_video_width() * 3;
if (get_num_components() == 3) {
if (_num_components == 3 && x_pitch == 3) {
// The easy case--copy the whole thing in, row by row.
ssize_t copy_bytes = get_video_width() * dest_x_pitch;
nassertv(copy_bytes <= dest_y_pitch && copy_bytes <= abs(y_pitch));
for (int y = 0; y < get_video_height(); ++y) {
memcpy(dest, source, source_row_width);
dest += dest_row_width;
source += source_row_width;
memcpy(dest, r, copy_bytes);
dest += dest_y_pitch;
r += y_pitch;
}
} else {
// The harder case--interleave the color in with the alpha,
// pixel by pixel.
nassertv(get_num_components() == 4);
// The harder case--interleave in the color channels, pixel by
// pixel, possibly leaving room for alpha.
for (int y = 0; y < get_video_height(); ++y) {
int dx = 0;
int sx = 0;
ssize_t dx = 0;
ssize_t sx = 0;
for (int x = 0; x < get_video_width(); ++x) {
dest[dx] = source[sx];
dest[dx + 1] = source[sx + 1];
dest[dx + 2] = source[sx + 2];
dx += 4;
sx += 3;
dest[dx] = r[sx];
dest[dx + 1] = g[sx];
dest[dx + 2] = b[sx];
dx += dest_x_pitch;
sx += x_pitch;
}
dest += dest_row_width;
source += source_row_width;
dest += dest_y_pitch;
r += y_pitch;
g += y_pitch;
b += y_pitch;
}
}
}
}
if (page._alpha.is_valid()) {
nassertv(get_num_components() == 4 && get_component_width() == 1);
nassertv(get_component_width() == 1);
const unsigned char *source = page._alpha.get_frame_data(frame);
if (source != NULL) {
const unsigned char *source[3];
ssize_t x_pitch, y_pitch;
if (page._alpha.get_frame_data(frame, source[0], source[1], source[2],
x_pitch, y_pitch)) {
nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size);
unsigned char *dest = _ram_images[0]._image.p() + do_get_expected_ram_page_size() * z;
int dest_row_width = (_x_size * _num_components * _component_width);
int source_row_width = get_video_width() * 3;
// Interleave the alpha in with the color, pixel by pixel.
// Even though the alpha will probably be a grayscale video,
// the OpenCV library presents it as RGB.
const unsigned char *sch = source[0];
if (_alpha_file_channel >= 1 && _alpha_file_channel <= 3) {
sch = source[_alpha_file_channel - 1];
}
for (int y = 0; y < get_video_height(); ++y) {
int dx = 3;
int sx = _alpha_file_channel;
// Start dx at _num_components - 1, which writes to the last
// channel, i.e. the alpha channel.
ssize_t dx = (_num_components - 1) * _component_width;
ssize_t sx = 0;
for (int x = 0; x < get_video_width(); ++x) {
dest[dx] = source[sx];
dx += 4;
sx += 3;
dest[dx] = sch[sx];
dx += dest_x_pitch;
sx += x_pitch;
}
dest += dest_row_width;
source += source_row_width;
dest += dest_y_pitch;
sch += y_pitch;
}
}
}
@ -478,14 +554,33 @@ OpenCVTexture::VideoStream::
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::VideoStream::get_frame_data
// Access: Public
// Description: Returns the pointer to the beginning of the
// Description: Gets the data needed to traverse through the
// decompressed buffer for the indicated frame number.
// It is most efficient to call this in increasing order
// of frame number.
// of frame number. Returns true on success, false on
// failure.
//
// In the case of a success indication (true return
// value), the three pointers r, g, b are loaded with
// the addresses of the three components of the
// bottom-left pixel of the image. (They will be
// adjacent in memory in the case of an interleaved
// image, and separated in the case of a
// separate-channel image.) The x_pitch value is filled
// with the amount to add to each pointer to advance to
// the pixel to the right; and the y_pitch value is
// filled with the amount to add to each pointer to
// advance to the pixel above. Note that these values
// may be negative (particularly in the case of a
// top-down image).
////////////////////////////////////////////////////////////////////
const unsigned char *OpenCVTexture::VideoStream::
get_frame_data(int frame) {
nassertr(is_valid(), NULL);
bool OpenCVTexture::VideoStream::
get_frame_data(int frame,
const unsigned char *&r,
const unsigned char *&g,
const unsigned char *&b,
ssize_t &x_pitch, ssize_t &y_pitch) {
nassertr(is_valid(), false);
if (is_from_file() && _next_frame != frame) {
cvSetCaptureProperty(_capture, CV_CAP_PROP_POS_FRAMES, frame);
@ -494,8 +589,34 @@ get_frame_data(int frame) {
_next_frame = frame + 1;
IplImage *image = cvQueryFrame(_capture);
if (image == NULL) {
return NULL;
return false;
}
r = (const unsigned char *)image->imageData;
g = r + 1;
b = g + 1;
x_pitch = 3;
y_pitch = image->widthStep;
if (image->dataOrder == 1) {
// Separate channel images. That means a block of r, followed by
// a block of g, followed by a block of b.
x_pitch = 1;
g = r + image->height * y_pitch;
b = g + image->height * y_pitch;
}
if (image->origin == 0) {
// The image data starts with the top row and ends with the bottom
// row--the opposite of Texture::_ram_data's storage convention.
// Therefore, we must increment the initial pointers to the last
// row, and count backwards.
r += (image->height - 1) * y_pitch;
g += (image->height - 1) * y_pitch;
b += (image->height - 1) * y_pitch;
y_pitch = -y_pitch;
}
return (const unsigned char *)image->imageData;
}

View File

@ -40,13 +40,14 @@ PUBLISHED:
virtual ~OpenCVTexture();
bool from_camera(int camera_index = -1, int z = 0,
int alpha_file_channel = 0,
const LoaderOptions &options = LoaderOptions());
public:
static PT(Texture) make_texture();
protected:
virtual INLINE void consider_update();
virtual void consider_update();
virtual PT(Texture) do_make_copy();
void do_assign(const OpenCVTexture &copy);
@ -81,7 +82,11 @@ private:
void clear();
INLINE bool is_valid() const;
INLINE bool is_from_file() const;
const unsigned char *get_frame_data(int frame);
bool get_frame_data(int frame,
const unsigned char *&r,
const unsigned char *&g,
const unsigned char *&b,
ssize_t &x_pitch, ssize_t &y_pitch);
CvCapture *_capture;
Filename _filename;