Revision: 8651
          http://playerstage.svn.sourceforge.net/playerstage/?rev=8651&view=rev
Author:   thjc
Date:     2010-05-03 17:59:33 +0000 (Mon, 03 May 2010)

Log Message:
-----------
Applied patch 2960235: New drivers for surveillance systems

Modified Paths:
--------------
    code/player/trunk/server/drivers/camera/CMakeLists.txt

Added Paths:
-----------
    code/player/trunk/server/drivers/camera/imgcmp/
    code/player/trunk/server/drivers/camera/imgcmp/CMakeLists.txt
    code/player/trunk/server/drivers/camera/imgcmp/imgcmp.cc
    code/player/trunk/server/drivers/camera/imgsave/
    code/player/trunk/server/drivers/camera/imgsave/CMakeLists.txt
    code/player/trunk/server/drivers/camera/imgsave/imgsave.cc
    code/player/trunk/server/drivers/camera/imgsave/videofont.h

Modified: code/player/trunk/server/drivers/camera/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/camera/CMakeLists.txt      2010-05-03 
17:55:28 UTC (rev 8650)
+++ code/player/trunk/server/drivers/camera/CMakeLists.txt      2010-05-03 
17:59:33 UTC (rev 8651)
@@ -6,6 +6,8 @@
 ADD_SUBDIRECTORY (cvcam)
 ADD_SUBDIRECTORY (gstreamer)
 ADD_SUBDIRECTORY (imageseq)
+ADD_SUBDIRECTORY (imgcmp)
+ADD_SUBDIRECTORY (imgsave)
 ADD_SUBDIRECTORY (sphere)
 ADD_SUBDIRECTORY (uvc)
 ADD_SUBDIRECTORY (yarp)

Added: code/player/trunk/server/drivers/camera/imgcmp/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/camera/imgcmp/CMakeLists.txt               
                (rev 0)
+++ code/player/trunk/server/drivers/camera/imgcmp/CMakeLists.txt       
2010-05-03 17:59:33 UTC (rev 8651)
@@ -0,0 +1,7 @@
+PLAYERDRIVER_OPTION (imgcmp build_imgcmp ON)
+IF (HAVE_JPEG)
+    PLAYERDRIVER_REQUIRE_HEADER (imgcmp build_imgcmp jpeglib.h stdio.h)
+    PLAYERDRIVER_ADD_DRIVER (imgcmp build_imgcmp LINKFLAGS "-ljpeg" SOURCES 
imgcmp.cc)
+ELSE (HAVE_JPEG)
+    PLAYERDRIVER_ADD_DRIVER (imgcmp build_imgcmp SOURCES imgcmp.cc)
+ENDIF (HAVE_JPEG)

Added: code/player/trunk/server/drivers/camera/imgcmp/imgcmp.cc
===================================================================
--- code/player/trunk/server/drivers/camera/imgcmp/imgcmp.cc                    
        (rev 0)
+++ code/player/trunk/server/drivers/camera/imgcmp/imgcmp.cc    2010-05-03 
17:59:33 UTC (rev 8651)
@@ -0,0 +1,627 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2000  Brian Gerkey et al.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/** @ingroup drivers */
+/** @{ */
+/** @defgroup driver_imgcmp imgcmp
+ * @brief image comparing driver
+
+The imgcmp driver compares current image frame with previous one in order to 
detect
+any changes on it. Frames that not differ will have the same timestamp.
+
+...@par Compile-time dependencies
+
+- none
+
+...@par Provides
+
+- @ref interface_camera (key "output" - regular image output)
+- @ref interface_dio (to signal change detection)
+- (optional) @ref interface_camera (key "diff" - differences map)
+
+Any command sent to provided dio interface forces positive compare result.
+
+...@par Requires
+
+- @ref interface_camera
+- (optionally) @ref interface_dio (to signal change detection by sending dio 
commands)
+
+...@par Configuration requests
+
+- none
+
+...@par Configuration file options
+
+- see properties section
+
+...@par Properties
+
+- skip_lines (integer)
+  - Default: 0
+  - During computations, skip n lines from the top
+    (as usually there's nothing interesting in first few lines)
+- sleep_nsec (integer)
+  - Default: 10000
+  - timespec value for additional nanosleep()
+- max_lum_dist (integer)
+  - Default: 0
+  - Allowed values: 0 - 255
+  - Maximal luminance distance between two pixels in old and new images; if 
luminance distance
+    computed for two pixels (old and new) is greater than max_lum_dist, these 
pixels differ.
+- max_diff_pixels (integer)
+  - Default: 0
+  - Allowed value: 0 - 100
+  - Percents of pixels that differ (see above) to consider that two images
+    (old and new) are different.
+- idle_publish_interval (double)
+  - Default: 0.5
+  - Image publish interval (in seconds) whenever differences were not detected.
+- keep_buffer (integer)
+  - Default: 1
+  - If set to non-zero, buffer with previous image is kept until change is 
detected.
+    Otherwise, only two last frames will be compared. Set this property to 
zero for noisy
+    camera image and bad light.
+
+...@par Example
+
+...@verbatim
+driver
+(
+  name "imgcmp"
+  requires ["camera:1"]
+  provides ["output:::camera:0" "dio:0"]
+  skip_lines 16
+  max_lum_dist 128
+  max_diff_pixels 50
+  keep_buffer 0
+)
+...@endverbatim
+
+...@author Paul Osmialowski
+
+*/
+/** @} */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <pthread.h>
+#include <math.h>
+#include <libplayercore/playercore.h>
+#include <config.h>
+#if HAVE_JPEG
+  #include <libplayerjpeg/playerjpeg.h>
+#endif
+
+#define QUEUE_LEN 1
+
+class ImgCmp : public ThreadedDriver
+{
+  public: ImgCmp(ConfigFile * cf, int section);
+  public: virtual ~ImgCmp();
+
+  public: virtual int MainSetup();
+  public: virtual void MainQuit();
+
+  // This method will be invoked on each incoming message
+  public: virtual int ProcessMessage(QueuePointer & resp_queue,
+                                     player_msghdr * hdr,
+                                     void * data);
+
+  private: virtual void Main();
+
+  // Input camera device
+  private: player_devaddr_t camera_provided_addr, dio_provided_addr, 
diff_provided_addr, camera_id, dio_id;
+  private: Device * camera;
+  private: Device * dio;
+  private: struct
+  {
+    unsigned char * buffer;
+    size_t bufsize;
+  } buffers[2];
+  private: int currentbuffer;
+  private: int use_dio;
+  private: int publish_diffs;
+  private: int forced;
+  private: double lastTStamp;
+  private: double lastPublish;
+  private: IntProperty skip_lines;
+  private: IntProperty sleep_nsec;
+  private: IntProperty max_lum_dist;
+  private: IntProperty max_diff_pixels;
+  private: DoubleProperty idle_publish_interval;
+  private: IntProperty keep_buffer;
+};
+
+Driver * ImgCmp_Init(ConfigFile * cf, int section)
+{
+  return reinterpret_cast<Driver *>(new ImgCmp(cf, section));
+}
+
+void imgcmp_Register(DriverTable *table)
+{
+  table->AddDriver("imgcmp", ImgCmp_Init);
+}
+
+ImgCmp::ImgCmp(ConfigFile * cf, int section)
+  : ThreadedDriver(cf, section, true, QUEUE_LEN),
+  skip_lines("skip_lines", 0, false),
+  sleep_nsec("sleep_nsec", 10000, false),
+  max_lum_dist("max_lum_dist", 0, false),
+  max_diff_pixels("max_diff_pixels", 0, false),
+  idle_publish_interval("idle_publish_interval", 0.5, false),
+  keep_buffer("keep_buffer", 1, false)
+{
+  memset(&(this->camera_provided_addr), 0, sizeof(player_devaddr_t));
+  memset(&(this->dio_provided_addr), 0, sizeof(player_devaddr_t));
+  memset(&(this->diff_provided_addr), 0, sizeof(player_devaddr_t));
+  memset(&(this->camera_id), 0, sizeof(player_devaddr_t));
+  memset(&(this->dio_id), 0, sizeof(player_devaddr_t));
+  this->camera = NULL;
+  this->dio = NULL;
+  memset(this->buffers, 0, sizeof this->buffers);
+  this->buffers[0].buffer = NULL;
+  this->buffers[0].bufsize = 0;
+  this->buffers[1].buffer = NULL;
+  this->buffers[1].bufsize = 0;
+  this->currentbuffer = 0;
+  this->use_dio = 0;
+  this->publish_diffs = 0;
+  this->forced = 0;
+  this->lastTStamp = 0.0;
+  this->lastPublish = 0.0;
+  if (cf->ReadDeviceAddr(&(this->camera_provided_addr), section, "provides", 
PLAYER_CAMERA_CODE, -1, "output"))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (this->AddInterface(this->camera_provided_addr))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (cf->ReadDeviceAddr(&(this->dio_provided_addr), section, "provides", 
PLAYER_DIO_CODE, -1, NULL))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (this->AddInterface(this->dio_provided_addr))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (cf->ReadDeviceAddr(&(this->diff_provided_addr), section, "provides", 
PLAYER_CAMERA_CODE, -1, "diff"))
+  {
+    this->publish_diffs = 0;
+  } else
+  {
+    if (this->AddInterface(this->diff_provided_addr))
+    {
+      this->SetError(-1);
+      return;
+    }
+    this->publish_diffs = !0;
+  }
+  if (cf->ReadDeviceAddr(&(this->camera_id), section, "requires", 
PLAYER_CAMERA_CODE, -1, NULL) != 0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (cf->ReadDeviceAddr(&(this->dio_id), section, "requires", 
PLAYER_DIO_CODE, -1, NULL) != 0)
+  {
+    this->use_dio = 0;
+  } else
+  {
+    this->use_dio = !0;
+  }
+  if (!(this->RegisterProperty("skip_lines", &(this->skip_lines), cf, 
section)))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (this->skip_lines.GetValue() < 0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (!(this->RegisterProperty("sleep_nsec", &(this->sleep_nsec), cf, 
section)))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (this->sleep_nsec.GetValue() < 0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (!(this->RegisterProperty("max_lum_dist", &(this->max_lum_dist), cf, 
section)))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if ((this->max_lum_dist.GetValue() < 0) || (this->max_lum_dist.GetValue() > 
255))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (!(this->RegisterProperty("max_diff_pixels", &(this->max_diff_pixels), 
cf, section)))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if ((this->max_diff_pixels.GetValue() < 0) || 
(this->max_diff_pixels.GetValue() > 255))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (!(this->RegisterProperty("idle_publish_interval", 
&(this->idle_publish_interval), cf, section)))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (this->idle_publish_interval.GetValue() < 0.0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (!(this->RegisterProperty("keep_buffer", &(this->keep_buffer), cf, 
section)))
+  {
+    this->SetError(-1);
+    return;
+  }
+}
+
+ImgCmp::~ImgCmp()
+{
+  if (this->buffers[0].buffer) free(this->buffers[0].buffer);
+  this->buffers[0].buffer = NULL;
+  if (this->buffers[1].buffer) free(this->buffers[1].buffer);
+  this->buffers[1].buffer = NULL;
+}
+
+int ImgCmp::MainSetup()
+{
+  if (Device::MatchDeviceAddress(this->camera_id, this->camera_provided_addr))
+  {
+    PLAYER_ERROR("attempt to subscribe to self");
+    return -1;
+  }
+  if (this->use_dio)
+  {
+    if (Device::MatchDeviceAddress(this->dio_id, this->dio_provided_addr))
+    {
+      PLAYER_ERROR("attempt to subscribe to self");
+      return -1;
+    }
+  }
+  if (this->publish_diffs)
+  {
+    if (Device::MatchDeviceAddress(this->camera_id, this->diff_provided_addr))
+    {
+      PLAYER_ERROR("attempt to subscribe to self");
+      return -1;
+    }
+  }
+  this->camera = deviceTable->GetDevice(this->camera_id);
+  if (!this->camera)
+  {
+    PLAYER_ERROR("unable to locate suitable camera device");
+    return -1;
+  }
+  if (this->camera->Subscribe(this->InQueue) != 0)
+  {
+    PLAYER_ERROR("unable to subscribe to camera device");
+    this->camera = NULL;
+    return -1;
+  }
+  if (this->use_dio)
+  {
+    this->dio = deviceTable->GetDevice(this->dio_id);
+    if (!this->dio)
+    {
+      PLAYER_ERROR("unable to locate suitable dio device");
+      if (this->camera) this->camera->Unsubscribe(this->InQueue);
+      this->camera = NULL;
+      return -1;
+    }
+    if (this->dio->Subscribe(this->InQueue) != 0)
+    {
+      PLAYER_ERROR("unable to subscribe to dio device");
+      if (this->camera) this->camera->Unsubscribe(this->InQueue);
+      this->camera = NULL;
+      this->dio = NULL;
+      return -1;
+    }
+  }
+  return 0;
+}
+
+void ImgCmp::MainQuit()
+{
+  if (this->camera) this->camera->Unsubscribe(this->InQueue);
+  this->camera = NULL;
+  if (this->use_dio)
+  {
+    if (this->dio) this->dio->Unsubscribe(this->InQueue);
+    this->dio = NULL;
+  }
+}
+
+void ImgCmp::Main()
+{
+  struct timespec tspec;
+
+  for (;;)
+  {
+    this->InQueue->Wait();
+    pthread_testcancel();
+    this->ProcessMessages();
+    pthread_testcancel();
+    tspec.tv_sec = 0;
+    tspec.tv_nsec = this->sleep_nsec.GetValue();
+    if (tspec.tv_nsec > 0)
+    {
+      nanosleep(&tspec, NULL);
+      pthread_testcancel();
+    }
+  }
+}
+
+int ImgCmp::ProcessMessage(QueuePointer & resp_queue, player_msghdr * hdr, 
void * data)
+{
+  player_camera_data_t * output = NULL;
+  int i, j;
+  size_t new_size;
+  unsigned char * ptr, * ptr1, * ptr2;
+  player_camera_data_t * rawdata;
+  int diff, diffs;
+  int nextbuffer = ((this->currentbuffer) ? 0 : 1);
+  double lum, lum1;
+  int skip_lines = this->skip_lines.GetValue();
+  int differ;
+  player_dio_data_t dio_data;
+  player_dio_cmd_t dio_cmd;
+  double t;
+  int d;
+
+  assert(hdr);
+  if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_DATA, -1, this->dio_id))
+  {
+    assert(data);
+    return 0;
+  }
+  if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_CMD, -1, 
this->dio_provided_addr))
+  {
+    assert(data);
+    this->forced = !0;
+    return 0;
+  }
+  if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_DATA, 
PLAYER_CAMERA_DATA_STATE, this->camera_id))
+  {
+    assert(data);
+    rawdata = reinterpret_cast<player_camera_data_t *>(data);
+    if ((static_cast<int>(rawdata->width) <= 0) || 
(static_cast<int>(rawdata->height) <= 0))
+    {
+      return -1;
+    } else
+    {
+      new_size = rawdata->width * rawdata->height * 3;
+      if ((this->buffers[this->currentbuffer].bufsize) != new_size)
+      {
+        if (this->buffers[this->currentbuffer].buffer) 
free(this->buffers[this->currentbuffer].buffer);
+        this->buffers[this->currentbuffer].buffer = NULL;
+        this->buffers[this->currentbuffer].bufsize = 0;
+      }
+      if (!(this->buffers[this->currentbuffer].buffer))
+      {
+        this->buffers[this->currentbuffer].bufsize = 0;
+        this->buffers[this->currentbuffer].buffer = reinterpret_cast<unsigned 
char *>(malloc(new_size));
+        if (!(this->buffers[this->currentbuffer].buffer))
+        {
+          PLAYER_ERROR("Out of memory");
+          return -1;
+        }
+        this->buffers[this->currentbuffer].bufsize = new_size;
+      }
+      switch (rawdata->compression)
+      {
+      case PLAYER_CAMERA_COMPRESS_RAW:
+        switch (rawdata->bpp)
+        {
+        case 8:
+          ptr = this->buffers[this->currentbuffer].buffer;
+          ptr1 = reinterpret_cast<unsigned char *>(rawdata->image);
+          for (i = 0; i < static_cast<int>(rawdata->height); i++)
+          {
+            for (j = 0; j < static_cast<int>(rawdata->width); j++)
+            {
+              ptr[0] = *ptr1;
+              ptr[1] = *ptr1;
+              ptr[2] = *ptr1;
+              ptr += 3; ptr1++;
+            }
+          }
+          break;
+        case 24:
+          memcpy(this->buffers[this->currentbuffer].buffer, rawdata->image, 
this->buffers[this->currentbuffer].bufsize);
+          break;
+        case 32:
+          ptr = this->buffers[this->currentbuffer].buffer;
+          ptr1 = reinterpret_cast<unsigned char *>(rawdata->image);
+          for (i = 0; i < static_cast<int>(rawdata->height); i++)
+          {
+            for (j = 0; j < static_cast<int>(rawdata->width); j++)
+            {
+              ptr[0] = ptr1[0];
+              ptr[1] = ptr1[1];
+              ptr[2] = ptr1[2];
+              ptr += 3; ptr1 += 4;
+            }
+          }
+          break;
+        default:
+          PLAYER_WARN("unsupported image depth (not good)");
+          return -1;
+        }
+        break;
+#if HAVE_JPEG
+      case PLAYER_CAMERA_COMPRESS_JPEG:
+        jpeg_decompress(this->buffers[this->currentbuffer].buffer, 
this->buffers[this->currentbuffer].bufsize, rawdata->image, 
rawdata->image_count);
+        break;
+#endif
+      default:
+        PLAYER_WARN("unsupported compression scheme (not good)");
+        return -1;
+      }
+      assert(skip_lines >= 0);
+      assert(skip_lines < static_cast<int>(rawdata->height));
+      assert(this->buffers[this->currentbuffer].buffer);
+      if ((this->buffers[nextbuffer].buffer) && 
((this->buffers[this->currentbuffer].bufsize) == 
(this->buffers[nextbuffer].bufsize)))
+      {
+        if (this->forced)
+        {
+          this->forced = 0;
+          differ = !0;
+        } else
+        {
+          if (this->publish_diffs)
+          {
+            output = reinterpret_cast<player_camera_data_t 
*>(malloc(sizeof(player_camera_data_t)));
+            if (!output)
+            {
+              PLAYER_ERROR("Out of memory");
+              return -1;
+            }
+            memset(output, 0, sizeof(player_camera_data_t));
+            output->bpp = 8;
+            output->compression = PLAYER_CAMERA_COMPRESS_RAW;
+            output->format = PLAYER_CAMERA_FORMAT_MONO8;
+            output->fdiv = rawdata->fdiv;
+            output->width = rawdata->width;
+            output->height = rawdata->height;
+            output->image_count = ((rawdata->width) * (rawdata->height));
+            output->image = reinterpret_cast<uint8_t 
*>(malloc(output->image_count));
+            if (!(output->image))
+            {
+              free(output);
+              PLAYER_ERROR("Out of memory");
+              return -1;
+            }
+            memset(output->image, 0, output->image_count);
+            ptr2 = output->image + (skip_lines * (rawdata->width));
+          } else ptr2 = NULL;
+          ptr = (this->buffers[this->currentbuffer].buffer) + (skip_lines * 
(rawdata->width) * 3);
+          assert(ptr);
+          ptr1 = this->buffers[nextbuffer].buffer + (skip_lines * 
(rawdata->width) * 3);
+          assert(ptr1);
+          diffs = 0;
+          differ = 0;
+          d = this->max_lum_dist.GetValue();
+          for (i = skip_lines; i < static_cast<int>(rawdata->height); i++)
+          {
+            for (j = 0; j < static_cast<int>(rawdata->width); j++)
+            {
+              lum = (0.299 * static_cast<double>(ptr[0])) + (0.587 * 
static_cast<double>(ptr[1])) + (0.114 * static_cast<double>(ptr[2]));
+              lum1 = (0.299 * static_cast<double>(ptr1[0])) + (0.587 * 
static_cast<double>(ptr1[1])) + (0.114 * static_cast<double>(ptr1[2]));
+              diff = static_cast<int>(fabs(lum - lum1));
+              assert(diff >= 0);
+              if (diff > 255)
+              {
+                PLAYER_WARN("difference too big");
+                diff = 255;
+              }
+              if (diff > d)
+              {
+                diffs++;
+                diff = 255;
+              }
+              if (this->publish_diffs)
+              {
+                assert(ptr2);
+                *ptr2 = static_cast<unsigned char>(diff);
+                ptr2++;
+              }
+              ptr += 3; ptr1 += 3;
+            }
+          }
+          if (this->publish_diffs)
+          {
+            this->Publish(this->diff_provided_addr, PLAYER_MSGTYPE_DATA, 
PLAYER_CAMERA_DATA_STATE, reinterpret_cast<void *>(output), 0, 
&(hdr->timestamp), false); // copy = false
+            // I assume that Publish() (with copy = false) freed those output 
data!
+            output = NULL;
+          }
+          diffs = (static_cast<int>((static_cast<double>(diffs) / 
static_cast<double>((rawdata->width) * (rawdata->height))) * 100.0));
+          differ = (diffs > (this->max_diff_pixels.GetValue()));
+        }
+        memset(&dio_data, 0, sizeof dio_data);
+        dio_data.count = 1;
+        dio_data.bits = differ ? 1 : 0;
+        this->Publish(this->dio_provided_addr, PLAYER_MSGTYPE_DATA, 
PLAYER_DIO_DATA_VALUES, reinterpret_cast<void *>(&dio_data), 0, 
&(hdr->timestamp), true); // copy = true
+        if (this->use_dio)
+        {
+          memset(&dio_cmd, 0, sizeof dio_cmd);
+          dio_cmd.count = 1;
+          dio_cmd.digout = differ ? 1 : 0;
+          this->dio->PutMsg(this->InQueue, PLAYER_MSGTYPE_CMD, 
PLAYER_DIO_CMD_VALUES, reinterpret_cast<void *>(&dio_cmd), 0, 
&(hdr->timestamp));
+        }
+        GlobalTime->GetTimeDouble(&t);
+        if ((differ) || (fabs(t - (this->lastPublish)) > 
(this->idle_publish_interval.GetValue())))
+        {
+          output = reinterpret_cast<player_camera_data_t 
*>(malloc(sizeof(player_camera_data_t)));
+          if (!output)
+          {
+            PLAYER_ERROR("Out of memory");
+            return -1;
+          }
+          memset(output, 0, sizeof(player_camera_data_t));
+          output->bpp = 24;
+          output->compression = PLAYER_CAMERA_COMPRESS_RAW;
+          output->format = PLAYER_CAMERA_FORMAT_RGB888;
+          output->fdiv = rawdata->fdiv;
+          output->width = rawdata->width;
+          output->height = rawdata->height;
+          output->image_count = this->buffers[this->currentbuffer].bufsize;
+          output->image = reinterpret_cast<uint8_t 
*>(malloc(output->image_count));
+          if (!(output->image))
+          {
+            free(output);
+            PLAYER_ERROR("Out of memory");
+            return -1;
+          }
+          memcpy(output->image, this->buffers[this->currentbuffer].buffer, 
output->image_count);
+          if (differ) this->lastTStamp = hdr->timestamp;
+          this->Publish(this->camera_provided_addr, PLAYER_MSGTYPE_DATA, 
PLAYER_CAMERA_DATA_STATE, reinterpret_cast<void *>(output), 0, 
&(this->lastTStamp), false); // copy = false
+          // I assume that Publish() (with copy = false) freed those output 
data!
+          output = NULL;
+          this->lastPublish = t;
+        }
+        if ((differ) || (!(this->keep_buffer.GetValue()))) this->currentbuffer 
= nextbuffer;
+      } else
+      {
+        this->lastTStamp = hdr->timestamp;
+        this->currentbuffer = nextbuffer;
+      }
+    }
+    return 0;
+  }
+  return -1;
+}

Added: code/player/trunk/server/drivers/camera/imgsave/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/camera/imgsave/CMakeLists.txt              
                (rev 0)
+++ code/player/trunk/server/drivers/camera/imgsave/CMakeLists.txt      
2010-05-03 17:59:33 UTC (rev 8651)
@@ -0,0 +1,8 @@
+PLAYERDRIVER_OPTION (imgsave build_imgsave ON)
+PLAYERDRIVER_REJECT_OS (imgsave build_imgsave PLAYER_OS_WIN)
+IF (HAVE_JPEG)
+    PLAYERDRIVER_REQUIRE_HEADER (imgsave build_imgsave jpeglib.h stdio.h)
+    PLAYERDRIVER_ADD_DRIVER (imgsave build_imgsave LINKFLAGS "-ljpeg" SOURCES 
imgsave.cc)
+ELSE (HAVE_JPEG)
+    PLAYERDRIVER_ADD_DRIVER (imgsave build_imgsave SOURCES imgsave.cc)
+ENDIF (HAVE_JPEG)

Added: code/player/trunk/server/drivers/camera/imgsave/imgsave.cc
===================================================================
--- code/player/trunk/server/drivers/camera/imgsave/imgsave.cc                  
        (rev 0)
+++ code/player/trunk/server/drivers/camera/imgsave/imgsave.cc  2010-05-03 
17:59:33 UTC (rev 8651)
@@ -0,0 +1,571 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2000  Brian Gerkey et al.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/** @ingroup drivers */
+/** @{ */
+/** @defgroup driver_imgsave imgsave
+ * @brief image storage dispatcher
+
+The imgsave driver stores received images into a directory tree in which paths 
form
+such a pattern: key/date/hour, where key is the given key name of camera 
device.
+By default images are written in human readable format called xyRGB (where 
each line
+is formed by five integers: x, y, R, G, B of a pixel), alternatively, JPEG 
compressed
+files can be stored. When desired, this driver can inprint image informations 
at the
+top of stored and published images (key, date, time, number of current image 
in given
+second). This driver publishes all images that are stored (if needed, JPEG 
compressed).
+
+...@par Compile-time dependencies
+
+- none
+
+...@par Provides
+
+- @ref interface_camera
+
+...@par Requires
+
+- @ref interface_camera
+
+...@par Configuration requests
+
+- none
+
+...@par Configuration file options
+
+- key (string)
+  - Default: "Unnamed"
+  - The name of camera device (any name you like for it)
+- jpeg (integer)
+  - Default: 0
+  - If set to 1, images that are stored and published are JPEG compressed
+- jpeg_quality (float)
+  - Default: 0.8
+  - JPEG quality
+- print (integer)
+  - Default: 0
+  - If set to 1, images are decorated at the top with status info
+    (key, date, time, number of current image in given second)
+- sleep_nsec (integer)
+  - Default: 10000
+  - timespec value for additional nanosleep()
+
+...@par Properties
+
+- none
+
+...@par Example
+
+...@verbatim
+driver
+(
+  name "imgsave"
+  requires ["camera:0"]
+  provides ["camera:10"]
+  key "vault"
+  jpeg 1
+  print 1
+)
+...@endverbatim
+
+...@author Paul Osmialowski
+
+*/
+/** @} */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <pthread.h>
+#include <math.h>
+#include <libplayercore/playercore.h>
+#include <config.h>
+#if HAVE_JPEG
+  #include <libplayerjpeg/playerjpeg.h>
+#endif
+#include "videofont.h"
+
+#define QUEUE_LEN 1
+#define MAX_KEY_LEN 15
+#define MAX_STAMP_LEN 255
+#define EPS 0.00001
+
+class ImgSave : public ThreadedDriver
+{
+  public: ImgSave(ConfigFile * cf, int section);
+  public: virtual ~ImgSave();
+
+  public: virtual int MainSetup();
+  public: virtual void MainQuit();
+
+  // This method will be invoked on each incoming message
+  public: virtual int ProcessMessage(QueuePointer & resp_queue,
+                                     player_msghdr * hdr,
+                                     void * data);
+
+  private: virtual void Main();
+
+  // Input camera device
+  private: player_devaddr_t camera_provided_addr, camera_id;
+  private: Device * camera;
+  private: char key[MAX_KEY_LEN + 1];
+  private: int jpeg;
+  private: double jpeg_quality;
+  private: int print;
+  private: int sleep_nsec;
+  private: unsigned char * font;
+  private: char stamp[MAX_STAMP_LEN + 1];
+  private: time_t last_time;
+  private: int last_num;
+  private: double tstamp;
+  private: static void txtwrite(int x, int y, unsigned char forecolor, 
unsigned char backcolor, const char * msg, const unsigned char * _fnt, unsigned 
char * img, size_t imgwidth, size_t imgheight);
+};
+
+Driver * ImgSave_Init(ConfigFile * cf, int section)
+{
+  return reinterpret_cast<Driver *>(new ImgSave(cf, section));
+}
+
+void imgsave_Register(DriverTable *table)
+{
+  table->AddDriver("imgsave", ImgSave_Init);
+}
+
+ImgSave::ImgSave(ConfigFile * cf, int section)
+  : ThreadedDriver(cf, section, true, QUEUE_LEN)
+{
+  static const unsigned char _fnt[] = VIDEOFONT; /* static: I don't want huge 
array to stick on local stack */
+  const char * _key;
+  int i;
+
+  memset(&(this->camera_provided_addr), 0, sizeof(player_devaddr_t));
+  memset(&(this->camera_id), 0, sizeof(player_devaddr_t));
+  this->camera = NULL;
+  this->key[0] = '\0';
+  this->jpeg = 0;
+  this->jpeg_quality = 0.0;
+  this->print = 0;
+  this->sleep_nsec = 0;
+  this->font = NULL;
+  this->stamp[0] = '\0';
+  this->last_time = 0;
+  this->last_num = 0;
+  this->tstamp = 0;
+  if (cf->ReadDeviceAddr(&(this->camera_provided_addr), section, "provides", 
PLAYER_CAMERA_CODE, -1, NULL))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (this->AddInterface(this->camera_provided_addr))
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (cf->ReadDeviceAddr(&(this->camera_id), section, "requires", 
PLAYER_CAMERA_CODE, -1, NULL) != 0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  _key = cf->ReadString(section, "key", "Unnamed");
+  if (!_key)
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (!((strlen(_key) > 0) && (strlen(_key) <= MAX_KEY_LEN)))
+  {
+    this->SetError(-1);
+    return;
+  }
+  for (i = 0; i < MAX_KEY_LEN; i++)
+  {
+    if (!(_key[i])) break;
+    this->key[i] =  (((_key[i] >= '0') && (_key[i] <= '9')) \
+                  || ((_key[i] >= 'A') && (_key[i] <= 'Z')) \
+                  || ((_key[i] >= 'a') && (_key[i] <= 'z')) \
+                  ||  (_key[i] == '.') \
+                  ||  (_key[i] == '-')) \
+                  ? _key[i] : '_';
+  }
+  if (!i)
+  {
+    this->SetError(-1);
+    return;
+  }
+  this->key[i] = '\0';
+  this->jpeg = cf->ReadInt(section, "jpeg", 0);
+  this->jpeg_quality = cf->ReadFloat(section, "jpeg_quality", 0.8);
+  if (((this->jpeg_quality) < EPS) || ((this->jpeg_quality) > 1.0))
+  {
+    this->SetError(-1);
+    return;
+  }
+  this->print = cf->ReadInt(section, "print", 0);
+  this->sleep_nsec = cf->ReadInt(section, "sleep_nsec", 10000);
+  if ((this->sleep_nsec) < 0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  if (this->print)
+  {
+    this->font = reinterpret_cast<unsigned char *>(malloc(sizeof _fnt));
+    if (!(this->font))
+    {
+      this->SetError(-1);
+      return;
+    }
+    for (i = 0; i < static_cast<int>(sizeof _fnt); i++) this->font[i] = 
_fnt[i];
+  }
+}
+
+ImgSave::~ImgSave()
+{
+  if (this->font) free(this->font);
+  this->font = NULL;
+}
+
+int ImgSave::MainSetup()
+{
+  if (Device::MatchDeviceAddress(this->camera_id, this->camera_provided_addr))
+  {
+    PLAYER_ERROR("attempt to subscribe to self");
+    return -1;
+  }
+  this->camera = deviceTable->GetDevice(this->camera_id);
+  if (!this->camera)
+  {
+    PLAYER_ERROR("unable to locate suitable camera device");
+    return -1;
+  }
+  if (this->camera->Subscribe(this->InQueue) != 0)
+  {
+    PLAYER_ERROR("unable to subscribe to camera device");
+    this->camera = NULL;
+    return -1;
+  }
+  return 0;
+}
+
+void ImgSave::MainQuit()
+{
+  if (this->camera) this->camera->Unsubscribe(this->InQueue);
+  this->camera = NULL;
+}
+
+void ImgSave::Main()
+{
+  struct timespec tspec;
+
+  snprintf(this->stamp, sizeof this->stamp, "**Start**");
+  this->last_time = time(NULL);
+  this->last_num = 0;
+  for (;;)
+  {
+    this->InQueue->Wait();
+    pthread_testcancel();
+    this->ProcessMessages();
+    pthread_testcancel();
+    tspec.tv_sec = 0;
+    tspec.tv_nsec = this->sleep_nsec;
+    if (tspec.tv_nsec > 0)
+    {
+      nanosleep(&tspec, NULL);
+      pthread_testcancel();
+    }
+  }
+}
+
+int ImgSave::ProcessMessage(QueuePointer & resp_queue, player_msghdr * hdr, 
void * data)
+{
+  player_camera_data_t * output;
+  int i, j;
+  int save = 0;
+  size_t jpegsize = 0;
+  size_t bufsize = 0;
+  unsigned char * buffer = NULL;
+  unsigned char * jbuffer = NULL;
+  unsigned char * ptr, * ptr1;
+  player_camera_data_t * rawdata;
+  char dname[MAX_STAMP_LEN + 1];
+  char fname[MAX_STAMP_LEN + 1];
+  char cmd[MAX_STAMP_LEN + 1];
+  FILE * f;
+  struct tm t;
+  time_t tt = time(NULL);
+
+  assert(hdr);
+  if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_DATA, 
PLAYER_CAMERA_DATA_STATE, this->camera_id))
+  {
+    assert(data);
+    rawdata = reinterpret_cast<player_camera_data_t *>(data);
+    if ((static_cast<int>(rawdata->width) <= 0) || 
(static_cast<int>(rawdata->height) <= 0))
+    {
+      return -1;
+    } else
+    {
+      bufsize = rawdata->width * rawdata->height * 3;
+      buffer = reinterpret_cast<unsigned char *>(malloc(bufsize));
+      if (!buffer)
+      {
+        PLAYER_ERROR("Out of memory");
+        return -1;
+      }
+      if (this->jpeg)
+      {
+        jbuffer = reinterpret_cast<unsigned char *>(malloc(bufsize));
+        if (!jbuffer)
+        {
+          PLAYER_ERROR("Out of memory");
+          if (buffer) free(buffer);
+          buffer = NULL;
+          return -1;
+        }
+      } else jbuffer = NULL;
+      switch (rawdata->compression)
+      {
+      case PLAYER_CAMERA_COMPRESS_RAW:
+        switch (rawdata->bpp)
+        {
+        case 8:
+          ptr = buffer;
+          ptr1 = reinterpret_cast<unsigned char *>(rawdata->image);
+          assert(ptr);
+          assert(ptr1);
+          for (i = 0; i < static_cast<int>(rawdata->height); i++)
+          {
+            for (j = 0; j < static_cast<int>(rawdata->width); j++)
+            {
+              ptr[0] = *ptr1;
+              ptr[1] = *ptr1;
+              ptr[2] = *ptr1;
+              ptr += 3; ptr1++;
+            }
+          }
+          break;
+        case 24:
+          assert(buffer);
+          assert(rawdata->image);
+          memcpy(buffer, rawdata->image, bufsize);
+          break;
+        case 32:
+          ptr = buffer;
+          ptr1 = reinterpret_cast<unsigned char *>(rawdata->image);
+          assert(ptr);
+          assert(ptr1);
+          for (i = 0; i < static_cast<int>(rawdata->height); i++)
+          {
+            for (j = 0; j < static_cast<int>(rawdata->width); j++)
+            {
+              ptr[0] = ptr1[0];
+              ptr[1] = ptr1[1];
+              ptr[2] = ptr1[2];
+              ptr += 3; ptr1 += 4;
+            }
+          }
+          break;
+        default:
+          PLAYER_WARN("unsupported image depth (not good)");
+          if (buffer) free(buffer);
+          buffer = NULL;
+          if (jbuffer) free(jbuffer);
+          jbuffer = NULL;
+          return -1;
+        }
+        break;
+#if HAVE_JPEG
+      case PLAYER_CAMERA_COMPRESS_JPEG:
+        if ((this->jpeg) && (!(this->print)))
+        {
+          assert(jbuffer);
+          assert(rawdata->image);
+          jpegsize = rawdata->image_count;
+          memcpy(jbuffer, rawdata->image, jpegsize);
+        } else
+        {
+          assert(buffer);
+          assert(rawdata->image);
+          jpeg_decompress(buffer, bufsize, rawdata->image, 
rawdata->image_count);
+        }
+        break;
+#endif
+      default:
+        PLAYER_WARN("unsupported compression scheme (not good)");
+        if (buffer) free(buffer);
+        buffer = NULL;
+        if (jbuffer) free(jbuffer);
+        jbuffer = NULL;
+        return -1;
+      }
+      if (this->print)
+      {
+        assert(buffer);
+        txtwrite(0, 8, 255, 0, this->stamp, this->font, buffer, 
rawdata->width, rawdata->height);
+      }
+      save = ((this->tstamp) != (hdr->timestamp)) ? (!0) : 0;
+      if (save)
+      {
+        assert((localtime_r(&tt, &t)) == (&t));
+        if (tt != (this->last_time))
+        {
+          this->last_time = tt;
+          this->last_num = 0;
+        }
+        snprintf(dname, sizeof dname, "%s/%d.%.2d.%.2d/%.2d", this->key, 
(t.tm_year) + 1900, (t.tm_mon) + 1, t.tm_mday, t.tm_hour);
+        snprintf(fname, sizeof fname, "%s/%.2d.%.2d.%.2d-%.2d.%s", dname, 
t.tm_hour, t.tm_min, t.tm_sec, this->last_num, (this->jpeg) ? "jpg" : "txt");
+        snprintf(this->stamp, sizeof this->stamp, "%s  %d.%.2d.%.2d 
%.2d:%.2d:%.2d-%.2d", this->key, (t.tm_year) + 1900, (t.tm_mon) + 1, t.tm_mday, 
t.tm_hour, t.tm_min, t.tm_sec, this->last_num);
+        this->last_num++;
+      }
+      if (this->print)
+      {
+        assert(buffer);
+        txtwrite(0, 0, 0, 255, this->stamp, this->font, buffer, 
rawdata->width, rawdata->height);
+      }
+      if (this->jpeg)
+      {
+        assert(jbuffer);
+        if ((this->print) || ((rawdata->compression) == 
PLAYER_CAMERA_COMPRESS_RAW))
+        {
+          assert(buffer);
+#if HAVE_JPEG
+          jpegsize = jpeg_compress(reinterpret_cast<char *>(jbuffer), 
reinterpret_cast<char *>(buffer), rawdata->width, rawdata->height, bufsize, 
static_cast<int>((this->jpeg_quality) * 100.0));
+#else
+          PLAYER_ERROR("No JPEG compression supported");
+          if (buffer) free(buffer);
+          buffer = NULL;
+          if (jbuffer) free(jbuffer);
+          jbuffer = NULL;
+          return -1;
+#endif
+        } else
+        {
+          assert((rawdata->compression) == PLAYER_CAMERA_COMPRESS_JPEG);
+        }
+        free(buffer);
+        buffer = NULL;
+        assert(jpegsize > 0);
+      } else
+      {
+        assert(buffer);
+        assert(!jbuffer);
+      }
+      if (save)
+      {
+        f = fopen(fname, (this->jpeg) ? "wb" : "w");
+        if (!f)
+        {
+          snprintf(cmd, sizeof cmd, "mkdir -p %s", dname);
+          system(cmd);
+          f = fopen(fname, (this->jpeg) ? "wb" : "w");
+        }
+        if (!f)
+        {
+          PLAYER_ERROR1("Cannot open file in order to write [%s]", fname);
+        } else
+        {
+          if (this->jpeg)
+          {
+            assert(jbuffer);
+            assert(jpegsize > 0);
+            fwrite(jbuffer, jpegsize, 1, f);
+          } else
+          {
+            assert(buffer);
+            ptr1 = buffer;
+            for (i = 0; i < static_cast<int>(rawdata->height); i++)
+            {
+              for (j = 0; j < static_cast<int>(rawdata->width); j++)
+              {
+                fprintf(f, "%d %d %u %u %u\n", j, i, ptr1[0], ptr1[1], 
ptr1[2]);
+                ptr1 += 3;
+              }
+            }
+          }
+          fclose(f);
+        }
+      }
+      output = reinterpret_cast<player_camera_data_t 
*>(malloc(sizeof(player_camera_data_t)));
+      if (!output)
+      {
+        PLAYER_ERROR("Out of memory");
+        if (buffer) free(buffer);
+        buffer = NULL;
+        if (jbuffer) free(jbuffer);
+        jbuffer = NULL;
+        return -1;
+      }
+      memset(output, 0, sizeof(player_camera_data_t));
+      output->bpp = 24;
+      output->compression = (this->jpeg) ? PLAYER_CAMERA_COMPRESS_JPEG : 
PLAYER_CAMERA_COMPRESS_RAW;
+      output->format = PLAYER_CAMERA_FORMAT_RGB888;
+      output->fdiv = rawdata->fdiv;
+      output->width = rawdata->width;
+      output->height = rawdata->height;
+      output->image_count = (this->jpeg) ? jpegsize : bufsize;
+      output->image = (this->jpeg) ? jbuffer : buffer;
+      assert(output->image);
+      this->tstamp = hdr->timestamp;
+      this->Publish(this->camera_provided_addr, PLAYER_MSGTYPE_DATA, 
PLAYER_CAMERA_DATA_STATE, reinterpret_cast<void *>(output), 0, &(this->tstamp), 
false); // copy = false
+      // I assume that Publish() (with copy = false) freed those output data!
+      output = NULL;
+    }
+    return 0;
+  }
+  return -1;
+}
+
+void ImgSave::txtwrite(int x, int y, unsigned char forecolor, unsigned char 
backcolor, const char * msg, const unsigned char * _fnt, unsigned char * img, 
size_t imgwidth, size_t imgheight)
+{
+  int i, l, c, xi;
+  const unsigned char * lettershape;
+  size_t linewidth = imgwidth * 3;
+  unsigned char * line;
+  unsigned char * col;
+  unsigned char color;
+
+  assert(msg);
+  assert(_fnt);
+  assert(img);
+  for (i = 0; msg[i]; i++) if ((msg[i]) > ' ')
+  {
+    lettershape = _fnt + (msg[i] * 8);
+    xi = x + (i * 8);
+    assert(lettershape);
+    for (l = 0; l < 8; l++)
+    {
+      if ((y + l) >= static_cast<int>(imgheight)) break;
+      line = img + ((y + l) * linewidth);
+      assert(line);
+      for (c = 0; c < 8; c++)
+      {
+        if ((xi + c) >= static_cast<int>(imgwidth)) break;
+        col = line + ((xi + c) * 3);
+        assert(col);
+        color = (((lettershape[l]) << c) & 128) ? forecolor : backcolor;
+        col[0] = color;
+        col[1] = color;
+        col[2] = color;
+      }
+    }
+  }
+}
+

Added: code/player/trunk/server/drivers/camera/imgsave/videofont.h
===================================================================
--- code/player/trunk/server/drivers/camera/imgsave/videofont.h                 
        (rev 0)
+++ code/player/trunk/server/drivers/camera/imgsave/videofont.h 2010-05-03 
17:59:33 UTC (rev 8651)
@@ -0,0 +1,264 @@
+#ifndef __VIDEOFONT_H
+#define __VIDEOFONT_H
+
+#define VIDEOFONT \
+{ \
+  0, 0, 0, 0, 0, 0, 0, 0, \
+  126, 129, 165, 129, 189, 153, 129, 126, \
+  126, 255, 219, 255, 195, 231, 255, 126, \
+  108, 254, 254, 254, 124, 56, 16, 0, \
+  16, 56, 124, 254, 124, 56, 16, 0, \
+  56, 124, 56, 254, 254, 124, 56, 124, \
+  16, 16, 56, 124, 254, 124, 56, 124, \
+  0, 0, 24, 60, 60, 24, 0, 0, \
+  255, 255, 231, 195, 195, 231, 255, 255, \
+  0, 60, 102, 66, 66, 102, 60, 0, \
+  255, 195, 153, 189, 189, 153, 195, 255, \
+  15, 7, 15, 125, 204, 204, 204, 120, \
+  60, 102, 102, 102, 60, 24, 126, 24, \
+  63, 51, 63, 48, 48, 112, 240, 224, \
+  127, 99, 127, 99, 99, 103, 230, 192, \
+  153, 90, 60, 231, 231, 60, 90, 153, \
+  128, 224, 248, 254, 248, 224, 128, 0, \
+  2, 14, 62, 254, 62, 14, 2, 0, \
+  24, 60, 126, 24, 24, 126, 60, 24, \
+  102, 102, 102, 102, 102, 0, 102, 0, \
+  127, 219, 219, 123, 27, 27, 27, 0, \
+  62, 99, 56, 108, 108, 56, 204, 120, \
+  0, 0, 0, 0, 126, 126, 126, 0, \
+  24, 60, 126, 24, 126, 60, 24, 255, \
+  24, 60, 126, 24, 24, 24, 24, 0, \
+  24, 24, 24, 24, 126, 60, 24, 0, \
+  0, 24, 12, 254, 12, 24, 0, 0, \
+  0, 48, 96, 254, 96, 48, 0, 0, \
+  0, 0, 192, 192, 192, 254, 0, 0, \
+  0, 36, 102, 255, 102, 36, 0, 0, \
+  0, 24, 60, 126, 255, 255, 0, 0, \
+  0, 255, 255, 126, 60, 24, 0, 0, \
+  0, 0, 0, 0, 0, 0, 0, 0, \
+  48, 120, 120, 48, 48, 0, 48, 0, \
+  108, 108, 108, 0, 0, 0, 0, 0, \
+  108, 108, 254, 108, 254, 108, 108, 0, \
+  48, 124, 192, 120, 12, 248, 48, 0, \
+  0, 198, 204, 24, 48, 102, 198, 0, \
+  56, 108, 56, 118, 220, 204, 118, 0, \
+  96, 96, 192, 0, 0, 0, 0, 0, \
+  24, 48, 96, 96, 96, 48, 24, 0, \
+  96, 48, 24, 24, 24, 48, 96, 0, \
+  0, 102, 60, 255, 60, 102, 0, 0, \
+  0, 48, 48, 252, 48, 48, 0, 0, \
+  0, 0, 0, 0, 0, 48, 48, 96, \
+  0, 0, 0, 252, 0, 0, 0, 0, \
+  0, 0, 0, 0, 0, 48, 48, 0, \
+  6, 12, 24, 48, 96, 192, 128, 0, \
+  124, 198, 206, 222, 246, 230, 124, 0, \
+  48, 112, 48, 48, 48, 48, 252, 0, \
+  120, 204, 12, 56, 96, 204, 252, 0, \
+  120, 204, 12, 56, 12, 204, 120, 0, \
+  28, 60, 108, 204, 254, 12, 30, 0, \
+  252, 192, 248, 12, 12, 204, 120, 0, \
+  56, 96, 192, 248, 204, 204, 120, 0, \
+  252, 204, 12, 24, 48, 48, 48, 0, \
+  120, 204, 204, 120, 204, 204, 120, 0, \
+  120, 204, 204, 124, 12, 24, 112, 0, \
+  0, 48, 48, 0, 0, 48, 48, 0, \
+  0, 48, 48, 0, 0, 48, 48, 96, \
+  24, 48, 96, 192, 96, 48, 24, 0, \
+  0, 0, 252, 0, 0, 252, 0, 0, \
+  96, 48, 24, 12, 24, 48, 96, 0, \
+  120, 204, 12, 24, 48, 0, 48, 0, \
+  124, 198, 222, 222, 222, 192, 120, 0, \
+  48, 120, 204, 204, 252, 204, 204, 0, \
+  252, 102, 102, 124, 102, 102, 252, 0, \
+  60, 102, 192, 192, 192, 102, 60, 0, \
+  248, 108, 102, 102, 102, 108, 248, 0, \
+  254, 98, 104, 120, 104, 98, 254, 0, \
+  254, 98, 104, 120, 104, 96, 240, 0, \
+  60, 102, 192, 192, 206, 102, 62, 0, \
+  204, 204, 204, 252, 204, 204, 204, 0, \
+  120, 48, 48, 48, 48, 48, 120, 0, \
+  30, 12, 12, 12, 204, 204, 120, 0, \
+  230, 102, 108, 120, 108, 102, 230, 0, \
+  240, 96, 96, 96, 98, 102, 254, 0, \
+  198, 238, 254, 254, 214, 198, 198, 0, \
+  198, 230, 246, 222, 206, 198, 198, 0, \
+  56, 108, 198, 198, 198, 108, 56, 0, \
+  252, 102, 102, 124, 96, 96, 240, 0, \
+  120, 204, 204, 204, 220, 120, 28, 0, \
+  252, 102, 102, 124, 108, 102, 230, 0, \
+  120, 204, 224, 112, 28, 204, 120, 0, \
+  252, 180, 48, 48, 48, 48, 120, 0, \
+  204, 204, 204, 204, 204, 204, 252, 0, \
+  204, 204, 204, 204, 204, 120, 48, 0, \
+  198, 198, 198, 214, 254, 238, 198, 0, \
+  198, 198, 108, 56, 56, 108, 198, 0, \
+  204, 204, 204, 120, 48, 48, 120, 0, \
+  254, 198, 140, 24, 50, 102, 254, 0, \
+  120, 96, 96, 96, 96, 96, 120, 0, \
+  192, 96, 48, 24, 12, 6, 2, 0, \
+  120, 24, 24, 24, 24, 24, 120, 0, \
+  16, 56, 108, 198, 0, 0, 0, 0, \
+  0, 0, 0, 0, 0, 0, 0, 255, \
+  48, 48, 24, 0, 0, 0, 0, 0, \
+  0, 0, 120, 12, 124, 204, 118, 0, \
+  224, 96, 96, 124, 102, 102, 220, 0, \
+  0, 0, 120, 204, 192, 204, 120, 0, \
+  28, 12, 12, 124, 204, 204, 118, 0, \
+  0, 0, 120, 204, 252, 192, 120, 0, \
+  56, 108, 96, 240, 96, 96, 240, 0, \
+  0, 0, 118, 204, 204, 124, 12, 248, \
+  224, 96, 108, 118, 102, 102, 230, 0, \
+  48, 0, 112, 48, 48, 48, 120, 0, \
+  12, 0, 12, 12, 12, 204, 204, 120, \
+  224, 96, 102, 108, 120, 108, 230, 0, \
+  112, 48, 48, 48, 48, 48, 120, 0, \
+  0, 0, 204, 254, 254, 214, 198, 0, \
+  0, 0, 248, 204, 204, 204, 204, 0, \
+  0, 0, 120, 204, 204, 204, 120, 0, \
+  0, 0, 220, 102, 102, 124, 96, 240, \
+  0, 0, 118, 204, 204, 124, 12, 30, \
+  0, 0, 220, 118, 102, 96, 240, 0, \
+  0, 0, 124, 192, 120, 12, 248, 0, \
+  16, 48, 124, 48, 48, 52, 24, 0, \
+  0, 0, 204, 204, 204, 204, 118, 0, \
+  0, 0, 204, 204, 204, 120, 48, 0, \
+  0, 0, 198, 214, 254, 254, 108, 0, \
+  0, 0, 198, 108, 56, 108, 198, 0, \
+  0, 0, 204, 204, 204, 124, 12, 248, \
+  0, 0, 252, 152, 48, 100, 252, 0, \
+  28, 48, 48, 224, 48, 48, 28, 0, \
+  24, 24, 24, 0, 24, 24, 24, 0, \
+  224, 48, 48, 28, 48, 48, 224, 0, \
+  118, 220, 0, 0, 0, 0, 0, 0, \
+  0, 16, 56, 108, 198, 198, 254, 0, \
+  30, 54, 102, 102, 126, 102, 102, 0, \
+  124, 96, 96, 124, 102, 102, 124, 0, \
+  124, 102, 102, 124, 102, 102, 124, 0, \
+  126, 96, 96, 96, 96, 96, 96, 0, \
+  56, 108, 108, 108, 108, 108, 254, 198, \
+  126, 96, 96, 124, 96, 96, 126, 0, \
+  219, 219, 126, 60, 126, 219, 219, 0, \
+  60, 102, 6, 28, 6, 102, 60, 0, \
+  102, 102, 110, 126, 118, 102, 102, 0, \
+  60, 102, 110, 126, 118, 102, 102, 0, \
+  102, 108, 120, 112, 120, 108, 102, 0, \
+  30, 54, 102, 102, 102, 102, 102, 0, \
+  198, 238, 254, 254, 214, 198, 198, 0, \
+  102, 102, 102, 126, 102, 102, 102, 0, \
+  60, 102, 102, 102, 102, 102, 60, 0, \
+  126, 102, 102, 102, 102, 102, 102, 0, \
+  124, 102, 102, 102, 124, 96, 96, 0, \
+  60, 102, 96, 96, 96, 102, 60, 0, \
+  126, 24, 24, 24, 24, 24, 24, 0, \
+  102, 102, 102, 62, 6, 102, 60, 0, \
+  126, 219, 219, 219, 126, 24, 24, 0, \
+  102, 102, 60, 24, 60, 102, 102, 0, \
+  102, 102, 102, 102, 102, 102, 127, 3, \
+  102, 102, 102, 62, 6, 6, 6, 0, \
+  219, 219, 219, 219, 219, 219, 255, 0, \
+  219, 219, 219, 219, 219, 219, 255, 3, \
+  224, 96, 96, 124, 102, 102, 124, 0, \
+  198, 198, 198, 246, 222, 222, 246, 0, \
+  96, 96, 96, 124, 102, 102, 124, 0, \
+  120, 140, 6, 62, 6, 140, 120, 0, \
+  206, 219, 219, 251, 219, 219, 206, 0, \
+  62, 102, 102, 102, 62, 54, 102, 0, \
+  0, 0, 120, 12, 124, 204, 118, 0, \
+  0, 60, 96, 60, 102, 102, 60, 0, \
+  0, 124, 102, 124, 102, 102, 124, 0, \
+  0, 0, 126, 96, 96, 96, 96, 0, \
+  0, 0, 60, 108, 108, 108, 254, 198, \
+  0, 0, 60, 102, 126, 96, 60, 0, \
+  0, 0, 219, 126, 60, 126, 219, 0, \
+  0, 0, 60, 102, 12, 102, 60, 0, \
+  0, 0, 102, 110, 126, 118, 102, 0, \
+  0, 24, 102, 110, 126, 118, 102, 0, \
+  0, 0, 102, 108, 120, 108, 102, 0, \
+  0, 0, 30, 54, 102, 102, 102, 0, \
+  0, 0, 198, 254, 254, 214, 198, 0, \
+  0, 0, 102, 102, 126, 102, 102, 0, \
+  0, 0, 60, 102, 102, 102, 60, 0, \
+  0, 0, 126, 102, 102, 102, 102, 0, \
+  17, 68, 17, 68, 17, 68, 17, 68, \
+  85, 170, 85, 170, 85, 170, 85, 170, \
+  221, 119, 221, 119, 221, 119, 221, 119, \
+  24, 24, 24, 24, 24, 24, 24, 24, \
+  24, 24, 24, 248, 24, 24, 24, 24, \
+  24, 248, 24, 248, 24, 24, 24, 24, \
+  54, 54, 54, 246, 54, 54, 54, 54, \
+  0, 0, 0, 254, 54, 54, 54, 54, \
+  0, 248, 24, 248, 24, 24, 24, 24, \
+  54, 246, 6, 246, 54, 54, 54, 54, \
+  54, 54, 54, 54, 54, 54, 54, 54, \
+  0, 254, 6, 246, 54, 54, 54, 54, \
+  54, 246, 6, 254, 0, 0, 0, 0, \
+  54, 54, 54, 254, 0, 0, 0, 0, \
+  24, 248, 24, 248, 0, 0, 0, 0, \
+  0, 0, 0, 248, 24, 24, 24, 24, \
+  24, 24, 24, 31, 0, 0, 0, 0, \
+  24, 24, 24, 255, 0, 0, 0, 0, \
+  0, 0, 0, 255, 24, 24, 24, 24, \
+  24, 24, 24, 31, 24, 24, 24, 24, \
+  0, 0, 0, 255, 0, 0, 0, 0, \
+  24, 24, 24, 255, 24, 24, 24, 24, \
+  24, 31, 24, 31, 24, 24, 24, 24, \
+  54, 54, 54, 55, 54, 54, 54, 54, \
+  54, 55, 48, 63, 0, 0, 0, 0, \
+  0, 63, 48, 55, 54, 54, 54, 54, \
+  54, 247, 0, 255, 0, 0, 0, 0, \
+  0, 255, 0, 247, 54, 54, 54, 54, \
+  54, 55, 48, 55, 54, 54, 54, 54, \
+  0, 255, 0, 255, 0, 0, 0, 0, \
+  54, 247, 0, 247, 54, 54, 54, 54, \
+  24, 255, 0, 255, 0, 0, 0, 0, \
+  54, 54, 54, 255, 0, 0, 0, 0, \
+  0, 255, 0, 255, 24, 24, 24, 24, \
+  0, 0, 0, 255, 54, 54, 54, 54, \
+  54, 54, 54, 63, 0, 0, 0, 0, \
+  24, 31, 24, 31, 0, 0, 0, 0, \
+  0, 31, 24, 31, 24, 24, 24, 24, \
+  0, 0, 0, 63, 54, 54, 54, 54, \
+  54, 54, 54, 255, 54, 54, 54, 54, \
+  24, 255, 24, 255, 24, 24, 24, 24, \
+  24, 24, 24, 248, 0, 0, 0, 0, \
+  0, 0, 0, 31, 24, 24, 24, 24, \
+  255, 255, 255, 255, 255, 255, 255, 255, \
+  0, 0, 0, 255, 255, 255, 255, 255, \
+  240, 240, 240, 240, 240, 240, 240, 240, \
+  15, 15, 15, 15, 15, 15, 15, 15, \
+  255, 255, 255, 0, 0, 0, 0, 0, \
+  0, 0, 124, 102, 102, 124, 96, 0, \
+  0, 0, 60, 102, 96, 102, 60, 0, \
+  0, 0, 126, 24, 24, 24, 24, 0, \
+  0, 0, 102, 102, 62, 6, 60, 0, \
+  0, 0, 126, 219, 219, 126, 24, 0, \
+  0, 0, 102, 60, 24, 60, 102, 0, \
+  0, 0, 102, 102, 102, 102, 127, 3, \
+  0, 0, 102, 102, 62, 6, 6, 0, \
+  0, 0, 219, 219, 219, 219, 255, 0, \
+  0, 0, 219, 219, 219, 219, 255, 3, \
+  0, 0, 224, 96, 124, 102, 124, 0, \
+  0, 0, 198, 198, 246, 222, 246, 0, \
+  0, 0, 96, 96, 124, 102, 124, 0, \
+  0, 0, 124, 6, 62, 6, 124, 0, \
+  0, 0, 206, 219, 251, 219, 206, 0, \
+  0, 0, 62, 102, 62, 54, 102, 0, \
+  0, 0, 254, 0, 254, 0, 254, 0, \
+  16, 16, 124, 16, 16, 0, 124, 0, \
+  0, 48, 24, 12, 6, 12, 24, 48, \
+  0, 12, 24, 48, 96, 48, 24, 12, \
+  14, 27, 27, 24, 24, 24, 24, 24, \
+  24, 24, 24, 24, 24, 216, 216, 112, \
+  0, 24, 24, 0, 126, 0, 24, 24, \
+  0, 118, 220, 0, 118, 220, 0, 0, \
+  0, 56, 108, 108, 56, 0, 0, 0, \
+  0, 0, 0, 0, 24, 0, 0, 0, \
+  0, 0, 0, 56, 56, 0, 0, 0, \
+  15, 12, 12, 12, 236, 108, 60, 28, \
+  216, 108, 108, 108, 108, 0, 0, 0, \
+  48, 72, 16, 32, 120, 0, 0, 0, \
+  0, 0, 124, 124, 124, 124, 0, 0, \
+  0, 0, 0, 0, 0, 66, 126, 0 \
+}
+
+#endif


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to