• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

src/Image.cpp

00001 //#include <stdlib.h>
00002 #include <string.h>
00003 #include <sys/mman.h>
00004 #include <algorithm>
00005 
00006 #include "FCam/Image.h"
00007 #include "FCam/Time.h"
00008 #include "FCam/Event.h"
00009 #include "Debug.h"
00010 
00011 namespace FCam {
00012 
00013     unsigned char *Image::Discard = (unsigned char *)(0);
00014     unsigned char *Image::AutoAllocate = (unsigned char *)(-1);
00015 
00016     Image::Image()
00017         : _size(0, 0), _type(UNKNOWN), _bytesPerPixel(0), _bytesPerRow(0), 
00018           data(Image::Discard), buffer(NULL), bytesAllocated(0),
00019           refCount(NULL), mutex(NULL), 
00020           memMapped(false), holdingLock(false) {                
00021     }
00022     
00023     Image::Image(int w, int h, ImageFormat f) 
00024         : _size(w, h), 
00025           _type(f), 
00026           _bytesPerPixel(FCam::bytesPerPixel(f)), 
00027           _bytesPerRow(bytesPerPixel()*width()),
00028           data(NULL), buffer(NULL), bytesAllocated(0),
00029           refCount(NULL), mutex(NULL), 
00030           memMapped(false),
00031           holdingLock(false) {
00032         
00033         bytesAllocated = bytesPerRow()*height();
00034         setBuffer(new unsigned char[bytesAllocated]);
00035         refCount = new unsigned;
00036         *refCount = 1; // only I know about this data
00037         mutex = new pthread_mutex_t;
00038         pthread_mutex_init(mutex, NULL);
00039     }
00040     
00041     Image::Image(Size s, ImageFormat f) 
00042         : _size(s), 
00043           _type(f), 
00044           _bytesPerPixel(FCam::bytesPerPixel(f)), 
00045           _bytesPerRow(bytesPerPixel()*width()),
00046           data(NULL), buffer(NULL), bytesAllocated(0),
00047           refCount(NULL), mutex(NULL), 
00048           memMapped(false),
00049           holdingLock(false) {
00050 
00051         bytesAllocated = bytesPerRow()*height();
00052         setBuffer(new unsigned char[bytesAllocated]);        
00053         refCount = new unsigned;
00054         *refCount = 1; // only I know about this data
00055         mutex = new pthread_mutex_t;
00056         pthread_mutex_init(mutex, NULL);
00057     }
00058 
00059     Image::Image(int fd, int offset, Size s, ImageFormat f, bool writeThrough) 
00060         : _size(s), 
00061           _type(f), 
00062           _bytesPerPixel(FCam::bytesPerPixel(f)),
00063           _bytesPerRow(bytesPerPixel()*width()),
00064           data(NULL), buffer(NULL), bytesAllocated(0),
00065           refCount(NULL), mutex(NULL),
00066           memMapped(true),
00067           holdingLock(false) {
00068         
00069         unsigned char *mappedBuffer;
00070         int flags;
00071         if (writeThrough) {
00072             flags = MAP_SHARED;
00073         } else {
00074             flags = MAP_PRIVATE;
00075         }
00076         // Make starting offset a multiple of page size, and determine relative offset to true buffer start
00077         int pageSize = getpagesize();
00078         int startOfMap = (offset/pageSize)*pageSize;
00079         int mapOffset = offset-startOfMap;
00080         // Make mapping size a multiple of page size, rounding up
00081         int bytesToMap = bytesPerRow()*height()+mapOffset; 
00082         bytesAllocated = ((bytesToMap-1)/pageSize+1) *pageSize;
00083         dprintf(5, 
00084                 "Image::Image(): Mapping image from file %d. "
00085                 "Requsted start %x, length %x. "
00086                 "Actual start: %x, offset %x, length %x\n", 
00087                 fd, offset, bytesPerRow()*height(), 
00088                 startOfMap, offset, bytesAllocated);
00089 
00090         mappedBuffer = (unsigned char*)mmap(NULL, 
00091                                             bytesAllocated,
00092                                             PROT_READ | PROT_WRITE,
00093                                             flags,
00094                                             fd,
00095                                             startOfMap);
00096 
00097         if (mappedBuffer == MAP_FAILED) {
00098             error(Event::InternalError, 
00099                   "Image: Unable to memory map file descriptor %d at %d, length %d bytes: %s",
00100                   fd, offset, bytesPerRow()*height(), strerror(errno)
00101                   );
00102             return;
00103         }
00104 
00105         int success;
00106         // Will almost always do sequential access to images stored on disk, so let's let the OS know.
00107 #ifdef FCAM_PLATFORM_CYGWIN
00108         // This is returning an error currently, so leaving it out for now
00109         // Not much of a performance problem on desktop machines anyway
00110         // success = posix_madvise(mappedBuffer, bytesAllocated, POSIX_MADV_SEQUENTIAL);
00111         // if (success != 0) {
00112         //    warning(Event::InternalError,
00113         //            "Image: Unable to call madvise successfully. Performance may be impacted: %s", strerror(success));
00114         //}
00115         success =0;
00116 #else
00117         success = madvise(mappedBuffer, bytesAllocated, MADV_SEQUENTIAL);
00118         if (success != 0) {
00119             warning(Event::InternalError,
00120                     "Image: Unable to call madvise successfully. Performance may be impacted: %s", strerror(errno));
00121         }
00122 #endif
00123 
00124         setBuffer(mappedBuffer, mappedBuffer+mapOffset);
00125         refCount = new unsigned;
00126         *refCount = 1; // only I know about this data
00127         mutex = new pthread_mutex_t;
00128         pthread_mutex_init(mutex, NULL);
00129     }
00130 
00131     Image::Image(Size s, ImageFormat f, unsigned char *d, int srcBytesPerRow) 
00132         : _size(s), 
00133           _type(f), 
00134           _bytesPerPixel(FCam::bytesPerPixel(f)), 
00135           data(NULL), buffer(NULL), bytesAllocated(0),
00136           refCount(NULL), mutex(NULL), 
00137           memMapped(false),
00138           holdingLock(false) {
00139 
00140         _bytesPerRow = (srcBytesPerRow == -1) ? (bytesPerPixel() * width()) : srcBytesPerRow;
00141         setBuffer(NULL, d);
00142 
00143         if (valid()) {
00144             refCount = new unsigned;
00145             *refCount = 2; // there's me, and then there's the real owner of this data
00146             mutex = new pthread_mutex_t;
00147             pthread_mutex_init(mutex, NULL);
00148         }
00149     }
00150     
00151     Image::Image(int w, int h, ImageFormat f, unsigned char *d, int srcBytesPerRow) 
00152         : _size(w, h), 
00153           _type(f), 
00154           _bytesPerPixel(FCam::bytesPerPixel(f)),
00155           data(NULL), buffer(NULL), bytesAllocated(0),
00156           refCount(NULL), mutex(NULL), 
00157           memMapped(false),
00158           holdingLock(false) {
00159 
00160         _bytesPerRow = (srcBytesPerRow == -1) ? (bytesPerPixel() * width()) : srcBytesPerRow;
00161         setBuffer(NULL, d);
00162 
00163         if (valid()) {
00164             refCount = new unsigned;
00165             *refCount = 2; // there's me, and then there's the real owner of this data
00166             mutex = new pthread_mutex_t;
00167             pthread_mutex_init(mutex, NULL);
00168         }
00169     }
00170 
00171     Image::~Image() {
00172         setBuffer(NULL);        
00173     }
00174 
00175     Image::Image(const Image &other) 
00176         : _size(other.size()), 
00177           _type(other.type()), 
00178           _bytesPerPixel(other.bytesPerPixel()),
00179           _bytesPerRow(other.bytesPerRow()),
00180           data(other.data), buffer(other.buffer),
00181           bytesAllocated(other.bytesAllocated),
00182           refCount(other.refCount),
00183           mutex(other.mutex), 
00184           memMapped(other.memMapped), 
00185           holdingLock(false) {
00186         if (refCount) {
00187             (*refCount)++;
00188         }
00189     };
00190 
00191     const Image &Image::operator=(const Image &other) {
00192         if (this == &other) return (*this);
00193         if (refCount && 
00194             refCount == other.refCount &&
00195             data == other.data) {
00196             return (*this);
00197         }
00198 
00199         _size = other.size();
00200         _type = other.type();
00201         _bytesPerPixel = other.bytesPerPixel();
00202         _bytesPerRow = other.bytesPerRow();
00203         setBuffer(other.buffer, other.data);
00204         bytesAllocated = other.bytesAllocated;
00205         
00206         refCount = other.refCount;
00207         mutex = other.mutex;
00208         if (refCount) (*refCount)++;
00209         memMapped = other.memMapped;
00210         holdingLock = false;
00211 
00212         return (*this);
00213     }
00214 
00215     Image Image::subImage(unsigned int x, unsigned int y, Size s) const {
00216         Image sub;
00217         // Check bounds
00218         if (!valid() ||
00219             x >= width() ||
00220             y >= height()) {
00221             return sub;
00222         }
00223         // Clip size to edges of image
00224         if (x + s.width > width()) {
00225             s.width = width() - x;
00226         }
00227         if (y + s.height > height()) {
00228             s.height = height() - y;
00229         }
00230 
00231         sub = Image(s, type(), Image::Discard, bytesPerRow());
00232 
00233         unsigned int offset = x*bytesPerPixel()+y*bytesPerRow();
00234         sub.setBuffer(buffer, data+offset);
00235         sub.bytesAllocated = bytesAllocated;
00236         sub.refCount = refCount;
00237         sub.mutex = mutex;
00238         sub.memMapped = memMapped;
00239 
00240         if (refCount) (*refCount)++;
00241         
00242         return sub;
00243     }
00244 
00245     Image Image::copy() const {
00246         Image duplicate;
00247         if (!valid()) {
00248             // Data is either Discard or AutoAllocate
00249             duplicate = Image(size(), type(), data);
00250         } else {
00251             // Got real data, allocate an image
00252             duplicate = Image(size(), type());
00253             duplicate.copyFrom(*this);
00254         }
00255 
00256         return duplicate;
00257     }
00258 
00259     void Image::copyFrom(const Image &srcImage) {
00260         if (!valid()) return;
00261         int h = std::min(srcImage.height(), 
00262                          height());
00263         int widthBytes = 
00264             std::min(srcImage.width()*srcImage.bytesPerPixel(), 
00265                      width()*bytesPerPixel());
00266 
00267         unsigned char *src = srcImage.data;
00268         unsigned char *dst = data;
00269 
00270         for (int y=0; y < h; y++) {
00271             memcpy(dst, src, widthBytes);
00272             dst += bytesPerRow();
00273             src += srcImage.bytesPerRow();
00274         }
00275     }
00276 
00277     void Image::setBuffer(unsigned char *b, unsigned char *d) {
00278         if (holdingLock) pthread_mutex_unlock(mutex);
00279         holdingLock = false;
00280 
00281         if (refCount) {
00282             (*refCount)--;
00283 
00284             if (mutex && (*refCount == 0 || (weak() && *refCount == 1))) {
00285                 pthread_mutex_destroy(mutex);
00286                 delete mutex;
00287                 mutex = NULL;
00288             }
00289 
00290             if (*refCount == 0) {
00291                 delete refCount;
00292                 if (memMapped) {
00293                     int success = munmap(buffer, bytesAllocated);
00294                     if (success == -1) {
00295                         error(Event::InternalError, 
00296                               "Image::setBuffer: Unable to unmap memory mapped region starting at %x of size %d: %s", 
00297                               buffer, bytesAllocated, strerror(errno));
00298                     }
00299                 } else {
00300                     delete[] buffer;
00301                 }
00302             }
00303             refCount = NULL;
00304             mutex = NULL;
00305         }
00306 
00307         if (b == Image::Discard ||
00308             b == Image::AutoAllocate) {
00309             buffer = NULL;
00310         } else {
00311             buffer = b;
00312         } 
00313         if (d == NULL) d = b;
00314 
00315         // This is the only place we're allowed to set the data field
00316         data = d;
00317     }
00318 
00319     bool Image::lock(int timeout) {
00320         if (holdingLock) {
00321             error(Event::ImageLockError, "Image reference trying to acquire lock it's already "
00322                   "holding. Make a separate image reference per thread.\n");
00323         } else if (!mutex) {
00324             error(Event::InternalError, "Locking an image with no mutex\n");
00325             holdingLock = false;
00326         } else if (timeout < 0) {
00327             pthread_mutex_lock(mutex);
00328             holdingLock = true;
00329         } else if (timeout == 0) {
00330             int ret = pthread_mutex_trylock(mutex);
00331             holdingLock = (ret == 0);
00332         } else {
00333             struct timespec t = (struct timespec)(Time::now() + timeout);
00335 #ifdef FCAM_ARCH_X86 
00336             int ret = pthread_mutex_trylock(mutex); // Temporary hack to compile on Cygwin, breaks semantics
00337 #else
00338             int ret = pthread_mutex_timedlock(mutex, &t);
00339 #endif
00340             holdingLock = (ret == 0);
00341         }
00342 
00343         return holdingLock;
00344     }
00345 
00346     void Image::unlock() {
00347         if (!holdingLock) {
00348             error(Event::ImageLockError, "Cannot unlock a lock not held by this image reference");
00349             return;
00350         }
00351         if (!mutex) {
00352             error(Event::InternalError, "Unlocking an image with no mutex");
00353             debug();
00354             return;
00355         }
00356         pthread_mutex_unlock(mutex);
00357         holdingLock = false;
00358     }
00359 
00360     bool Image::operator==(const Image &other) const {
00361         if (data != other.data) return false;
00362         if (width() != other.width()) return false;
00363         if (height() != other.height()) return false;
00364         if (type() != other.type()) return false;
00365         /* bytesPerPixel, bytesPerRow, and buffer must match if the above do */
00366         return true;
00367     }
00368 
00369     void Image::debug(const char *name) const {
00370         printf("\tImage %s at %llx with dimensions %d %d type %d\n\t  bytes per pixel %d bytes per row %d\n\t  data %llx buffer %llx\n\t  refCount %llx = (%d), mutex %llx, memmapped %s, holdingLock %s\n",
00371                name,
00372                (long long unsigned)this,
00373                width(), height(),
00374                type(),
00375                bytesPerPixel(),
00376                bytesPerRow(),
00377                (long long unsigned)data,
00378                (long long unsigned)buffer,
00379                (long long unsigned)refCount,
00380                refCount ? *refCount : 0,
00381                (long long unsigned)mutex,
00382                (memMapped ? "true" : "false"),
00383                (holdingLock ? "true" : "false"));
00384     }
00385    
00386 }
00387 
00388 
00389 
00390 

Generated on Mon Aug 16 2010 14:25:45 for FCam by  doxygen 1.7.1