// Filename: webcamVideoCursorOpenCV.cxx
// Created by:  drose (20Oct10)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "webcamVideoOpenCV.h"

#ifdef HAVE_OPENCV

#include "pStatTimer.h"

TypeHandle WebcamVideoCursorOpenCV::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: WebcamVideoCursorOpenCV::Constructor
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
WebcamVideoCursorOpenCV::
WebcamVideoCursorOpenCV(WebcamVideoOpenCV *src) : MovieVideoCursor(src) {
  _size_x = src->_size_x;
  _size_y = src->_size_y;
  _num_components = 3;
  _length = 1.0E10;
  _can_seek = false;
  _can_seek_fast = false;
  _aborted = false;
  _streaming = true;
  _ready = false;

  _capture = cvCaptureFromCAM(src->_camera_index);
  if (_capture != NULL) {
    _size_x = (int)cvGetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH);
    _size_y = (int)cvGetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT);
    _ready = true;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: WebcamVideoCursorOpenCV::Destructor
//       Access: Published, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
WebcamVideoCursorOpenCV::
~WebcamVideoCursorOpenCV() {
  if (_capture != NULL) {
    cvReleaseCapture(&_capture);
    _capture = NULL;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: WebcamVideoCursorOpenCV::fetch_buffer
//       Access: Published, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
PT(MovieVideoCursor::Buffer) WebcamVideoCursorOpenCV::
fetch_buffer() {
  if (!_ready) {
    return NULL;
  }

  PT(Buffer) buffer = get_standard_buffer();
  unsigned char *dest = buffer->_block;
  int num_components = get_num_components();
  nassertr(num_components == 3, NULL);
  int dest_x_pitch = num_components;  // Assume component_width == 1
  int dest_y_pitch = _size_x * dest_x_pitch;

  const unsigned char *r, *g, *b;
  int x_pitch, y_pitch;
  if (get_frame_data(r, g, b, x_pitch, y_pitch)) {
    if (num_components == 3 && x_pitch == 3) {
      // The easy case--copy the whole thing in, row by row.
      int copy_bytes = _size_x * dest_x_pitch;
      nassertr(copy_bytes <= dest_y_pitch && copy_bytes <= abs(y_pitch), NULL);
      
      for (int y = 0; y < _size_y; ++y) {
        memcpy(dest, r, copy_bytes);
        dest += dest_y_pitch;
        r += y_pitch;
      }

    } else {
      // The harder case--interleave in the color channels, pixel by
      // pixel.
      
      for (int y = 0; y < _size_y; ++y) {
        int dx = 0;
        int sx = 0;
        for (int x = 0; x < _size_x; ++x) {
          dest[dx] = r[sx];
          dest[dx + 1] = g[sx];
          dest[dx + 2] = b[sx];
          dx += dest_x_pitch;
          sx += x_pitch;
        }
        dest += dest_y_pitch;
        r += y_pitch;
        g += y_pitch;
        b += y_pitch;
      }
    }
  }

  return buffer;
}

////////////////////////////////////////////////////////////////////
//     Function: WebcamVideoCursorOpenCV::get_frame_data
//       Access: Private
//  Description: Gets the data needed to traverse through the
//               decompressed buffer.  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).
////////////////////////////////////////////////////////////////////
bool WebcamVideoCursorOpenCV::
get_frame_data(const unsigned char *&r,
               const unsigned char *&g,
               const unsigned char *&b,
               int &x_pitch, int &y_pitch) {
  nassertr(ready(), false);

  IplImage *image = cvQueryFrame(_capture);
  if (image == 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 true;
}

#endif
