Urbi SDK Remote for C++  2.7.3
uconversion.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006-2011, Gostai S.A.S.
00003  *
00004  * This software is provided "as is" without warranty of any kind,
00005  * either expressed or implied, including but not limited to the
00006  * implied warranties of fitness for a particular purpose.
00007  *
00008  * See the LICENSE file for more information.
00009  */
00010 
00011 #include <libport/cstdlib>
00012 #include <libport/debug.hh>
00013 #include <libport/cstdio>
00014 #include <libport/format.hh>
00015 
00016 #include <urbi/uconversion.hh>
00017 
00018 GD_CATEGORY(Urbi.Convert);
00019 
00020 #ifndef NO_IMAGE_CONVERSION
00021 # include <csetjmp>
00022 
00023 // It would be nice to use jpeg/jpeglib.h, but this file includes
00024 // jconfig.h, unqualified, which we might pick-up on the host.  So
00025 // don't take gratuitous chances.
00026 # include <jpeglib.h>
00027 
00028 namespace urbi
00029 {
00030 
00031   namespace
00032   {
00033     void*
00034     read_jpeg(const char* jpgbuffer, size_t jpgbuffer_size,
00035               bool RGB, size_t& output_size, size_t& w, size_t& h);
00036 
00037     int
00038     write_jpeg(const byte* src, size_t w, size_t h, bool ycrcb,
00039                byte* dst, size_t& sz, int quality);
00040 
00041     inline byte clamp(int v)
00042     {
00043       return (v < 0     ? 0
00044               : 255 < v ? 255
00045               :           v);
00046     }
00047 
00048     inline byte clamp(float v)
00049     {
00050       return (v < 0     ? 0
00051               : 255 < v ? 255
00052               :           (byte) v);
00053     }
00054   } // namespace
00055 
00056   int
00057   convertRGBtoYCbCr(const byte* in, size_t bufferSize,
00058                     byte* out)
00059   {
00060     for (size_t i = 0; i < bufferSize - 2; i += 3)
00061     {
00062       float r = in[i];
00063       float g = in[i + 1];
00064       float b = in[i + 2];
00065       /*
00066         Y  =      (0.257 * R) + (0.504 * G) + (0.098 * B) + 16
00067         Cr = V =  (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
00068         Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
00069       */
00070       out[i]     = clamp( 0.257f * r + 0.504f * g + 0.098f * b +  16.0f);
00071       out[i + 1] = clamp(-0.148f * r - 0.291f * g + 0.439f * b + 128.0f);
00072       out[i + 2] = clamp( 0.439f * r - 0.368f * g - 0.071f * b + 128.0f);
00073     }
00074     return 1;
00075   }
00076 
00077   int
00078   convertYCrCbtoYCbCr(const byte* in, size_t bufferSize,
00079                       byte* out)
00080   {
00081     for (size_t i = 0; i < bufferSize - 2; i += 3)
00082     {
00083       byte tmp; // If source == destination
00084       out[i]     = in[i];
00085       tmp        = in[i + 1];
00086       out[i + 1] = in[i + 2];
00087       out[i + 2] = tmp;
00088     }
00089     return 1;
00090   }
00091 
00092 
00093   int
00094   convertYCbCrtoRGB(const byte* in, size_t bufferSize,
00095                     byte* out)
00096   {
00097     // http://en.wikipedia.org/wiki/YUV#Converting_between_Y.27UV_and_RGB
00098     for (size_t i = 0; i < bufferSize - 2; i += 3)
00099     {
00100       int c = in[i]-16;
00101       int c298 = c * 298;
00102       int d = in[i+1] - 128;
00103       int e = in[i+2] - 128;
00104       out[i] = clamp((c298 + 409*e + 128) >> 8);
00105       out[i+1] = clamp((c298 + 100*d - 20*e + 128) >> 8);
00106       out[i+2] = clamp((c298 + 516*d + 128) >> 8);
00107       /* Float version, 5 times slower on p4.
00108          float y = in[i];
00109          float cb = in[i + 1];
00110          float cr = in[i + 2];
00111          out[i] = clamp(1.164 * (y - 16) + 1.596 * (cr - 128));
00112          out[i + 1] = clamp(1.164 * (y - 16) - 0.813 * (cr - 128) -
00113          0.392 * (cb - 128));
00114          out[i + 2] = clamp(1.164 * (y - 16) + 2.017 * (cb - 128));
00115       */
00116     }
00117     return 1;
00118   }
00119 
00120 
00139   static
00140   int
00141   convert_jpeg_to(const byte* source, size_t sourcelen,
00142                   UImageFormat dest_format,
00143                   byte** dest, size_t& size, size_t& w, size_t& h)
00144   {
00145     passert(dest_format,
00146             dest_format == IMAGE_RGB || dest_format == IMAGE_YCbCr);
00147     if (!dest)
00148       return 0;
00149 
00150     size_t sz;
00151     void *destination = read_jpeg((const char*) source, sourcelen,
00152                                   dest_format == IMAGE_RGB, sz, w, h);
00153     if (!destination)
00154     {
00155       size = 0;
00156       return 0;
00157     }
00158     if (!*dest)
00159     {
00160       *dest = (byte*) destination;
00161       size = sz;
00162       return 1;
00163     }
00164     size_t cplen = std::min(sz, size);
00165     memcpy(*dest, destination, cplen);
00166     free(destination);
00167     size = sz;
00168     return 1;
00169   }
00170 
00171   int
00172   convertJPEGtoYCrCb(const byte* source, size_t sourcelen,
00173                      byte** dest, size_t& size, size_t& w, size_t& h)
00174   {
00175     return convert_jpeg_to(source, sourcelen,
00176                            IMAGE_YCbCr, dest, size, w, h);
00177   }
00178 
00179   int
00180   convertJPEGtoRGB(const byte* source, size_t sourcelen,
00181                    byte** dest, size_t& size, size_t& w, size_t& h)
00182   {
00183     return convert_jpeg_to(source, sourcelen,
00184                            IMAGE_RGB, dest, size, w, h);
00185   }
00186 
00187 
00188   int
00189   convertRGBtoJPEG(const byte* source,
00190                    size_t w, size_t h, byte* dest,
00191                    size_t& size, int quality)
00192   {
00193     return write_jpeg(source, w, h, false, dest, size, quality);
00194   }
00195 
00196 
00197   int
00198   convertYCrCbtoJPEG(const byte* source,
00199                      size_t w, size_t h, byte* dest,
00200                      size_t& size, int quality)
00201   {
00202     return write_jpeg(source, w, h, true, dest, size, quality);
00203   }
00204 
00205   int
00206   convertRGBtoGrey8_601(const byte* in, size_t bufferSize,
00207                         byte* out)
00208   {
00209     for (size_t j = 0, i = 0; i < bufferSize - 2; i += 3, j++)
00210     {
00211       float r = in[i];
00212       float g = in[i + 1];
00213       float b = in[i + 2];
00214       out[j]  = clamp( 0.299f * r + 0.587f * g + 0.114f * b);
00215     }
00216     return 1;
00217   }
00218 
00219   struct mem_source_mgr
00220   {
00221     struct jpeg_source_mgr pub;
00222     JOCTET eoi[2];
00223   };
00224 
00225 
00226   namespace
00227   {
00228     void init_source(j_decompress_ptr)
00229     {
00230     }
00231 
00232     boolean fill_input_buffer(j_decompress_ptr cinfo)
00233     {
00234       mem_source_mgr *src = (mem_source_mgr *) cinfo->src;
00235       if (src->pub.bytes_in_buffer != 0)
00236         return TRUE;
00237       src->eoi[0] = 0xFF;
00238       src->eoi[1] = JPEG_EOI;
00239       src->pub.bytes_in_buffer = 2;
00240       src->pub.next_input_byte = src->eoi;
00241       return TRUE;
00242     }
00243 
00244     void term_source(j_decompress_ptr)
00245     {
00246     }
00247 
00248     void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
00249     {
00250       mem_source_mgr* src = (mem_source_mgr*) cinfo->src;
00251       if (num_bytes <= 0)
00252         return;
00253       if (static_cast<unsigned long> (num_bytes) > src->pub.bytes_in_buffer)
00254         num_bytes = src->pub.bytes_in_buffer;
00255       src->pub.bytes_in_buffer -= num_bytes;
00256       src->pub.next_input_byte += num_bytes;
00257     }
00258 
00259   } // namespace
00260 
00261   struct urbi_jpeg_error_mgr
00262   {
00263     struct jpeg_error_mgr pub;  /* "public" fields */
00264     jmp_buf setjmp_buffer;      /* for return to caller */
00265   };
00266 
00267   METHODDEF(void)
00268   urbi_jpeg_error_exit (j_common_ptr cinfo)
00269   {
00270     /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
00271     urbi_jpeg_error_mgr *  myerr = ( urbi_jpeg_error_mgr *) cinfo->err;
00272 
00273     /* Always display the message. */
00274     /* We could postpone this until after returning, if we chose. */
00275     (*cinfo->err->output_message) (cinfo);
00276 
00277     /* Return control to the setjmp point */
00278     longjmp(myerr->setjmp_buffer, 1);
00279   }
00280 
00281 
00282   struct mem_destination_mgr
00283   {
00284     struct jpeg_destination_mgr pub;
00285   };
00286 
00287   static void init_destination(j_compress_ptr)
00288   {
00289   }
00290 
00291   static boolean empty_output_buffer(j_compress_ptr)
00292   {
00293     return FALSE;
00294   }
00295 
00296   static void term_destination(j_compress_ptr)
00297   {
00298   }
00299 
00300   namespace
00301   {
00302     int
00303     write_jpeg(const byte* src, size_t w, size_t h, bool ycrcb,
00304                byte* dst, size_t& sz, int quality)
00305     {
00306       struct jpeg_compress_struct cinfo;
00307       struct jpeg_error_mgr jerr;
00308 
00309       int row_stride;           /* physical row width in image buffer */
00310 
00311       cinfo.err = jpeg_std_error(&jerr);
00312       jpeg_create_compress(&cinfo);
00313       mem_destination_mgr *dest = (struct mem_destination_mgr *)
00314         (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT,
00315                                    sizeof (mem_destination_mgr));
00316 
00317       cinfo.dest = (jpeg_destination_mgr*)dest;
00318       dest->pub.init_destination=&init_destination;
00319       dest->pub.empty_output_buffer = &empty_output_buffer;
00320       dest->pub.term_destination = term_destination;
00321       dest->pub.free_in_buffer = sz;
00322       dest->pub.next_output_byte = dst;
00323       cinfo.image_width = w;
00324       cinfo.image_height = h;
00325       cinfo.input_components = 3;  // # of color components per pixel.
00326       /* colorspace of input image */
00327       cinfo.in_color_space = ycrcb ? JCS_YCbCr : JCS_RGB;
00328 
00329       jpeg_set_defaults(&cinfo);
00330 
00331       jpeg_set_quality(&cinfo,
00332                        quality, TRUE /* limit to baseline-JPEG values */);
00333 
00334       jpeg_start_compress(&cinfo, TRUE);
00335 
00336       row_stride = w * 3;       /* JSAMPLEs per row in image_buffer */
00337 
00338       while (cinfo.next_scanline < cinfo.image_height)
00339       {
00340         /* pointer to JSAMPLE row[s] */
00341         const JSAMPLE* row =
00342           (const JSAMPLE*) &src[cinfo.next_scanline * row_stride];
00343         jpeg_write_scanlines(&cinfo, const_cast<JSAMPLE**>(&row), 1);
00344       }
00345 
00346       jpeg_finish_compress(&cinfo);
00347       sz -= dest->pub.free_in_buffer ;
00348       jpeg_destroy_compress(&cinfo);
00349 
00350       return sz;
00351     }
00352 
00355     void *read_jpeg(const char* jpgbuffer, size_t jpgbuffer_size, bool RGB,
00356                     size_t& output_size, size_t& w, size_t& h)
00357     {
00358       struct jpeg_decompress_struct cinfo;
00359       struct urbi_jpeg_error_mgr jerr;
00360       cinfo.err = jpeg_std_error(&jerr.pub);
00361       jerr.pub.error_exit = urbi_jpeg_error_exit;
00362       if (setjmp(jerr.setjmp_buffer))
00363       {
00364         /* If we get here, the JPEG code has signaled an error.  We
00365          * need to clean up the JPEG object, close the input file, and
00366          * return.
00367          */
00368         jpeg_destroy_decompress(&cinfo);
00369         GD_ERROR("JPEG error!");
00370         return 0;
00371       }
00372       jpeg_create_decompress(&cinfo);
00373       mem_source_mgr *source = (struct mem_source_mgr *)
00374         (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT,
00375                                    sizeof (mem_source_mgr));
00376 
00377       cinfo.src = (jpeg_source_mgr *) source;
00378       source->pub.skip_input_data = skip_input_data;
00379       source->pub.term_source = term_source;
00380       source->pub.init_source = init_source;
00381       source->pub.fill_input_buffer = fill_input_buffer;
00382       source->pub.resync_to_restart = jpeg_resync_to_restart;
00383       source->pub.bytes_in_buffer = jpgbuffer_size;
00384       source->pub.next_input_byte = (JOCTET *) jpgbuffer;
00385       cinfo.out_color_space = (RGB ? JCS_RGB : JCS_YCbCr);
00386       jpeg_read_header(&cinfo, TRUE);
00387       cinfo.out_color_space = (RGB ? JCS_RGB : JCS_YCbCr);
00388       jpeg_start_decompress(&cinfo);
00389       w = cinfo.output_width;
00390       h = cinfo.output_height;
00391       output_size =
00392         cinfo.output_width * cinfo.output_components * cinfo.output_height;
00393       void *buffer = malloc(output_size);
00394 
00395       while (cinfo.output_scanline < cinfo.output_height)
00396       {
00397         /* jpeg_read_scanlines expects an array of pointers to scanlines.
00398          * Here the array is only one element long, but you could ask for
00399          * more than one scanline at a time if that's more convenient.
00400          */
00401         JSAMPLE* row =
00402           (JSAMPLE *) &((char*) buffer)[cinfo.output_scanline
00403                                         * cinfo.output_components
00404                                         * cinfo.output_width];
00405         jpeg_read_scanlines(&cinfo, &row, 1);
00406       }
00407       jpeg_finish_decompress(&cinfo);
00408       jpeg_destroy_decompress(&cinfo);
00409 
00410       return buffer;
00411     }
00412 
00413 
00414 
00415     //scale putting (scx, scy) at the center of destination image
00416     void scaleColorImage(byte* src, int sw, int sh,
00417                          int scx, int scy, byte* dst,
00418                          int dw, int dh, float sx, float sy)
00419     {
00420       for (int x = 0; x < dw; ++x)
00421         for (int y = 0; y < dh; ++y)
00422         {
00423           //find the corresponding point in source image
00424           float fsrcx = (float) (x-dw/2) / sx  + (float) scx;
00425           float fsrcy = (float) (y-dh/2) / sy  + (float) scy;
00426           int srcx = (int) fsrcx;
00427           int srcy = (int) fsrcy;
00428           if (srcx <= 0 || srcx >= sw - 1 || srcy <= 0 || srcy >= sh - 1)
00429             memset(dst + (x + y * dw) * 3, 0, 3);
00430           else //do the bilinear interpolation
00431           {
00432             float xfactor = fsrcx - (float) srcx;
00433             float yfactor = fsrcy - (float) srcy;
00434             for (int color = 0; color < 3; ++color)
00435             {
00436               float up = (float) src[(srcx + srcy * sw) * 3 + color]
00437                 * (1.0 - xfactor)
00438                 + (float) src[(srcx + 1 + srcy * sw) * 3 + color] * xfactor;
00439               float down = (float) src[(srcx + (srcy + 1) * sw) * 3 + color]
00440                 * (1.0 - xfactor)
00441                 + (float) src[(srcx + 1 + (srcy + 1) * sw) * 3 + color]
00442                 * xfactor;
00443               float result = up * (1.0 - yfactor) + down * yfactor;
00444               dst[(x + y * dw) * 3 + color] = (byte) result;
00445             }
00446           }
00447         }
00448     }
00449 
00450   } // anonymous namespace
00451 
00452   int convert(const UImage& src, UImage& dest)
00453   {
00454     enum FormatKind
00455     {
00456       RGB,
00457       YUV,
00458       COMPRESSED,
00459       UNSET
00460     };
00461     //step 1: uncompress source, to have raw uncompressed rgb or ycbcr
00462     bool allocated = false; // true if data must be freed
00463 
00464     // uncompressed data.
00465     byte* data = 0;
00466     size_t w, h;
00467     size_t usz;
00468     // Effective format of the source in 'data'.
00469     FormatKind format = UNSET;
00470     // Format we need the source in
00471     FormatKind targetformat = UNSET;
00472 
00473     switch (dest.imageFormat)
00474     {
00475     case IMAGE_RGB:
00476     case IMAGE_PPM:
00477     case IMAGE_GREY8:
00478       targetformat = RGB;
00479       break;
00480     case IMAGE_YCbCr:
00481     case IMAGE_NV12:
00482     case IMAGE_YUV411_PLANAR:
00483     case IMAGE_YUV420_PLANAR:
00484       targetformat = YUV;
00485       break;
00486     case IMAGE_JPEG:
00487       targetformat = COMPRESSED;
00488       break;
00489     default:
00490       GD_FERROR("Image conversion to format %s is not implemented",
00491                 dest.format_string());
00492       return 0;
00493     }
00494     unsigned p = 0;
00495     int c = 0;
00496 
00497     // Avoid using src fields because JPEG file format embedded these
00498     // information in the data buffer.
00499     if (src.imageFormat != IMAGE_JPEG)
00500     {
00501       w = src.width;
00502       h = src.height;
00503       usz = w * h * 3;
00504     }
00505 
00506     switch (src.imageFormat)
00507     {
00508     case IMAGE_YCbCr:
00509       format = YUV;
00510       data = src.data;
00511       break;
00512     case IMAGE_RGB:
00513       format = RGB;
00514       data = src.data;
00515       break;
00516     case IMAGE_PPM:
00517       format = RGB;
00518       //locate header end
00519       p = 0;
00520       c = 0;
00521       while (c < 3 && p < src.size)
00522         if (src.data[p++] == '\n')
00523           ++c;
00524       data = src.data + p;
00525       break;
00526     case IMAGE_JPEG:
00527       // this image is allocated by the function convertJPEG* function.
00528       // w, h and usz are defined by these functions calls.
00529       allocated = true;
00530       if (targetformat == RGB)
00531       {
00532         convertJPEGtoRGB((byte*) src.data, src.size,
00533                          (byte**) &data, usz,
00534                          w, h);
00535         format = RGB;
00536       }
00537       else
00538       {
00539         convertJPEGtoYCrCb((byte*) src.data, src.size,
00540                            (byte**) &data, usz,
00541                            w, h);
00542         format = YUV;
00543       }
00544       break;
00545     case IMAGE_YUV422:
00546       format = YUV;
00547       data = (byte*)malloc(src.width * src.height * 3);
00548       allocated = true;
00549       for (unsigned i=0; i< src.width*src.height; i+=2)
00550       {
00551         data[i*3] = src.data[i*2];
00552         data[i*3 + 1] = src.data[i*2+1];
00553         data[i*3 + 2] = src.data[i*2+3];
00554         data[(i+1)*3] = src.data[i*2+2];
00555         data[(i+1)*3 + 1] = src.data[i*2+1];
00556         data[(i+1)*3 + 2] = src.data[i*2+3];
00557       }
00558       break;
00559     case IMAGE_YUV411_PLANAR:
00560     {
00561       format = YUV;
00562       data = (byte*)malloc(src.width * src.height * 3);
00563       allocated = true;
00564       unsigned char* cy = src.data;
00565       unsigned char* u = cy + w*h;
00566       unsigned char* v = u + w*h/4;
00567       int w = src.width;
00568       int h = src.height;
00569       for (int x=0; x<w;++x)
00570         for (int y=0; y<h; ++y)
00571         {
00572           data[(x+y*w)*3+0] = cy[x+y*w];
00573           data[(x+y*w)*3+1] = u[x/4 + y*w/4];
00574           data[(x+y*w)*3+2] = v[x/4 + y*w/4];
00575         }
00576     }
00577     break;
00578     case IMAGE_YUV420_PLANAR:
00579     {
00580       format = YUV;
00581       data = (byte*)malloc(src.width * src.height * 3);
00582       allocated = true;
00583       unsigned char* cy = src.data;
00584       unsigned char* u = cy + w*h;
00585       unsigned char* v = u + w*h/4;
00586       int w = src.width;
00587       int h = src.height;
00588       for (int x=0; x<w;++x)
00589         for (int y=0; y<h; ++y)
00590         {
00591           data[(x+y*w)*3+0] = cy[x+y*w];
00592           data[(x+y*w)*3+1] = u[x/2 + (y>>1)*w/2];
00593           data[(x+y*w)*3+2] = v[x/2 + (y>>1)*w/2];
00594         }
00595     }
00596     break;
00597     case IMAGE_NV12:
00598     {
00599       format = YUV;
00600       data = (byte*)malloc(src.width * src.height * 3);
00601       allocated = true;
00602       unsigned char* cy = src.data;
00603       unsigned char* uv = src.data + w*h;
00604       for (unsigned int x=0; x<w;++x)
00605         for (unsigned int y=0; y<h; ++y)
00606         {
00607           data[(x+y*w)*3+0] = cy[x+y*w];
00608           data[(x+y*w)*3+1] = uv[((x>>1) + (((y>>1)*w)>>1))*2];
00609           data[(x+y*w)*3+2] = uv[((x>>1) + (((y>>1)*w)>>1))*2 + 1];
00610         }
00611     }
00612     break;
00613     case IMAGE_GREY8:
00614       format = YUV;
00615       data = (byte*)malloc(src.width * src.height * 3);
00616       allocated = true;
00617       memset(data, 127, src.width * src.height * 3);
00618       for (unsigned i=0; i< src.width*src.height; ++i)
00619         data[i*3] = src.data[i];
00620       break;
00621     case IMAGE_GREY4:
00622       format = YUV;
00623       data = (byte*)malloc(src.width * src.height * 3);
00624       allocated = true;
00625       memset(data, 127, src.width * src.height * 3);
00626       for (unsigned i=0; i< src.width*src.height; i+=2)
00627       {
00628         data[i*3] = src.data[i/2] & 0xF0;
00629         data[(i+1)*3] = (src.data[i/2] & 0x0F) << 4;
00630       }
00631       break;
00632     case IMAGE_UNKNOWN:
00633       break;
00634     }
00635 
00636     if (dest.width == 0)
00637       dest.width = w;
00638     if (dest.height == 0)
00639       dest.height = h;
00640 
00641     //now resize if target size is different
00642     if (w != dest.width || h != dest.height)
00643     {
00644       void* scaled = malloc(dest.width * dest.height * 3);
00645       scaleColorImage(data, w, h, w/2, h/2,
00646                       (byte*) scaled, dest.width, dest.height,
00647                       (float) dest.width / (float) w,
00648                       (float) dest.height / (float) h);
00649       if (allocated)
00650         free(data);
00651       data = (byte*)scaled;
00652       allocated = true;
00653     }
00654     // Then factor YUV<->RGB conversion if necessary
00655     if ((format == RGB && targetformat == YUV)
00656         || (format == YUV && targetformat == RGB))
00657     {
00658       byte* src = data;
00659       if (!allocated)
00660       {
00661         allocated = true;
00662         data = (byte*)malloc(dest.width * dest.height * 3);
00663       }
00664       if (format == RGB)
00665         convertRGBtoYCbCr(src, dest.width * dest.height * 3, data);
00666       else
00667         convertYCbCrtoRGB(src, dest.width * dest.height * 3, data);
00668       format = targetformat;
00669     }
00670     //then convert to destination format
00671     dest.size = dest.width * dest.height * 3 + 20;
00672     if (dest.imageFormat == IMAGE_GREY8)
00673       dest.size = dest.width * dest.height + 20;
00674     dest.data = static_cast<byte*> (realloc(dest.data, dest.size));
00675     size_t dsz = dest.size;
00676     switch (dest.imageFormat)
00677     {
00678     case IMAGE_RGB:
00679       memcpy(dest.data, data, dest.width * dest.height * 3);
00680       break;
00681     case IMAGE_GREY8:
00682       assert(format == 0);
00683       convertRGBtoGrey8_601((byte*) data,
00684                             dest.width * dest.height * 3, (byte*) dest.data);
00685       break;
00686     case IMAGE_YCbCr:
00687       memcpy(dest.data, data, dest.width * dest.height * 3);
00688       break;
00689     case IMAGE_PPM:
00690       strcpy((char*) dest.data,
00691              libport::format("P6\n%s %s\n255\n",
00692                              dest.width, dest.height).c_str());
00693       memcpy(dest.data + strlen((char*) dest.data),
00694              data, dest.width * dest.height * 3);
00695       break;
00696     case IMAGE_JPEG:
00697       if (format == YUV)
00698         convertYCrCbtoJPEG((byte*) data,
00699                            dest.width ,dest.height,
00700                            (byte*) dest.data, dsz, 80);
00701       else
00702         convertRGBtoJPEG((byte*) data,
00703                          dest.width , dest.height,
00704                          (byte*) dest.data, dsz, 80);
00705       dest.size = dsz;
00706       break;
00707     case IMAGE_YUV411_PLANAR:
00708     {
00709       unsigned int plane = dest.width * dest.height;
00710       for (unsigned int i=0; i<plane;++i)
00711         dest.data[i] = data[i*3];
00712       for (unsigned int y=0; y<dest.height; y++)
00713         for (unsigned int x=0; x<dest.width; x+=4)
00714         {
00715           dest.data[plane + x/4 + y*dest.width/4]
00716             = data[(x+y*dest.width)*3+1];
00717           dest.data[plane+plane/4 + x/4 + y*dest.width/4]
00718             = data[(x+y*dest.width)*3+2];
00719         }
00720       break;
00721     }
00722     case IMAGE_YUV420_PLANAR:
00723     {
00724       unsigned int plane = dest.width * dest.height;
00725       for (unsigned int i=0; i<plane;++i)
00726         dest.data[i] = data[i*3];
00727       for (unsigned int y=0; y<dest.height/2; y++)
00728         for (unsigned int x=0; x<dest.width/2; x++)
00729         {
00730           dest.data[plane + x +y*dest.width/2]
00731             = data[(x*2+y*2*dest.width)*3+1];
00732           dest.data[plane + plane/4 + x +y*dest.width/2]
00733             = data[(x*2+y*2*dest.width)*3+2];
00734         }
00735       break;
00736     }
00737     case IMAGE_NV12:
00738     {
00739       unsigned int planeS = dest.width * dest.height;
00740       // y plane
00741       for (unsigned int p=0; p<planeS; ++p)
00742         dest.data[p] = data[p*3];
00743       // crcb interleaved plane
00744       for (unsigned int y=0; y<dest.height; y+=2)
00745         for (unsigned int x=0; x<dest.width; x+=2)
00746         {
00747           dest.data[planeS + x + y*dest.width/2]
00748             = data[(x+y*dest.width)*3+1];
00749           dest.data[planeS + x + y*dest.width/2 +1 ]
00750             = data[(x+y*dest.width)*3+2];
00751         }
00752       dest.size = planeS * 3 / 2;
00753     }
00754     break;
00755     default:
00756       GD_FERROR("Image conversion to format %s is not implemented",
00757                 dest.format_string());
00758     }
00759     if (allocated)
00760       free(data);
00761     return 1;
00762   }
00763 
00764 } // namespace urbi
00765 
00766 #endif // !NO_IMAGE_CONVERSION
00767 
00768 namespace urbi
00769 {
00770   static
00771   void
00772   dup(unsigned short* dst, const unsigned short* src, size_t count)
00773   {
00774     unsigned int* idst = (unsigned int*)dst;
00775     const unsigned short* end = src + count;
00776     while (src != end)
00777     {
00778       *(idst++) = (unsigned int)(*src) << 16 | (unsigned int)(*src);
00779       src++;
00780     }
00781   }
00782 
00783 
00784   static
00785   void
00786   dup(byte* dst, const byte* src, size_t count)
00787   {
00788     unsigned short* idst = (unsigned short*)dst;
00789     const byte* end = src + count;
00790     while (src != end)
00791     {
00792       *(idst++) = (unsigned short)(*src) << 8 | (unsigned short)(*src);
00793       src++;
00794     }
00795   }
00796 
00797   template<typename D> void
00798   pud(D* dst, const D* src, int count)
00799   {
00800     for (int i=0; i<count/2; i++)
00801       dst[i] = src[i*2];
00802   }
00803 
00804   template<class S, class D>
00805   void copy(const S* src, D* dst,
00806             int sc, int dc, int sr, int dr,
00807             size_t count, bool sf, bool df)
00808   {
00809     long shift = 8 * (sizeof (S) - sizeof (D));
00810     if (!shift && sc == dc && sr == dr && sf==df)
00811     {
00812       memcpy(dst, src, sizeof(S)*sc*count);
00813       return;
00814     }
00815     for (size_t i = 0; i < count; ++i)
00816     {
00817       float soffset = (float)i * ((float)sr / (float)dr);
00818       int so = (int)soffset;
00819       float factor = soffset - (float)so;
00820       S s1, s2;
00821       s1 = src[so * sc];
00822       if (i != count - 1)
00823         s2 = src[(so + 1) * sc];
00824       else
00825         s2 = s1; //nothing to interpolate with
00826       if (!sf)
00827       {
00828         s1 = s1 ^ (1<<(sizeof (S)*8-1));
00829         s2 = s2 ^ (1<<(sizeof (S)*8-1));
00830       }
00831       int v1 = (int) ((float)(s1)*(1.0-factor) + (float)(s2)*factor);
00832       int v2;
00833       if (sc==1)
00834         v2 = v1;
00835       else
00836       {
00837         s1 = src[so*sc+1];
00838         if (i != count - 1)
00839           s2 = src[(so+1)*sc+1];
00840         else
00841           s2 = s1; //nothing to interpolate with
00842         if (!sf)
00843         {
00844           s1 = s1 ^ (1<<(sizeof (S)*8-1));
00845           s2 = s2 ^ (1<<(sizeof (S)*8-1));
00846         }
00847         v2 = (int) ((float)(s1)*(1.0-factor) + (float)(s2)*factor);
00848       }
00849       D d1, d2;
00850       if (shift>=0)
00851       {
00852         d1 = (D)(v1 >>shift);
00853         d2 = (D)(v2 >>shift);
00854       }
00855       else
00856       {
00857         d1 = (D)(v1) * (1 << -shift);
00858         d2 = (D)(v2) * (1 << -shift);
00859       }
00860       if (!df)
00861       {
00862         d1 = d1 ^ (1<<(sizeof (D)*8-1));
00863         d2 = d2 ^ (1<<(sizeof (D)*8-1));
00864       }
00865       if (dc==2)
00866       {
00867         dst[i*2] = d1;
00868         dst[i*2+1] = d2;
00869       }
00870       else
00871         dst[i] = (D) (((int)d1+(int)d2) /2);
00872     }
00873   }
00874 
00875   int
00876   convert (const USound &source, USound &dest)
00877   {
00878     if ((source.soundFormat != SOUND_RAW
00879          && source.soundFormat != SOUND_WAV)
00880         || (dest.soundFormat != SOUND_RAW
00881             && dest.soundFormat != SOUND_WAV))
00882       return 1; //conversion not handled yet
00883     /* phase one: calculate required buffer size, set destination unspecified
00884      * fields */
00885     size_t schannels, srate, ssampleSize;
00886     USoundSampleFormat ssampleFormat;
00887     if (source.soundFormat == SOUND_WAV)
00888       {
00889         wavheader * wh = (wavheader *)source.data;
00890         schannels = wh->channels;
00891         srate = wh->freqechant;
00892         ssampleSize = wh->bitperchannel;
00893         ssampleFormat = (ssampleSize>8)?SAMPLE_SIGNED:SAMPLE_UNSIGNED;
00894       }
00895     else
00896       {
00897         schannels = source.channels;
00898         srate = source.rate;
00899         ssampleSize = source.sampleSize;
00900         ssampleFormat = source.sampleFormat;
00901       }
00902     if (!dest.channels)
00903       dest.channels = schannels;
00904     if (!dest.rate)
00905       dest.rate = srate;
00906     if (!dest.sampleSize)
00907       dest.sampleSize = ssampleSize;
00908     if (!(int)dest.sampleFormat)
00909       dest.sampleFormat = ssampleFormat;
00910     if (dest.soundFormat == SOUND_WAV)
00911       dest.sampleFormat = dest.sampleSize > 8 ? SAMPLE_SIGNED
00912                                               : SAMPLE_UNSIGNED;
00913     // That's a big one!
00914     unsigned destSize =
00915       ((long long)(source.size
00916                    - ((source.soundFormat == SOUND_WAV)?44:0))
00917        * (long long)dest.channels
00918        * (long long)dest.rate
00919        * (long long)(dest.sampleSize/8))
00920       / ((long long)schannels
00921          *(long long)srate
00922          *(long long)(ssampleSize/8));
00923     if (dest.soundFormat == SOUND_WAV)
00924       destSize += sizeof (wavheader);
00925     if (dest.size<destSize)
00926       dest.data = static_cast<char*> (realloc (dest.data, destSize));
00927     dest.size = destSize;
00928     //write destination header if appropriate
00929     if (dest.soundFormat == SOUND_WAV)
00930     {
00931       wavheader* wh = (wavheader*) dest.data;
00932       memcpy(wh->riff, "RIFF", 4);
00933       wh->length = dest.size - 8;
00934       memcpy(wh->wave, "WAVE", 4);
00935       memcpy(wh->fmt, "fmt ", 4);
00936       wh->lnginfo = 16;
00937       wh->one = 1;
00938       wh->channels = dest.channels;
00939       wh->freqechant = dest.rate;
00940       wh->bytespersec = dest.rate * dest.channels * (dest.sampleSize/8);
00941       wh->bytesperechant = (dest.sampleSize/8)*dest.channels;
00942       wh->bitperchannel = dest.sampleSize;
00943       memcpy(wh->data, "data", 4);
00944       wh->datalength = destSize - sizeof (wavheader);
00945     }
00946 
00947     //do the conversion and write to dest.data
00948     char* sbuffer = source.data;
00949     if (source.soundFormat == SOUND_WAV)
00950       sbuffer += sizeof (wavheader);
00951     char* dbuffer = dest.data;
00952     if (dest.soundFormat == SOUND_WAV)
00953       dbuffer += sizeof (wavheader);
00954     int elementCount = dest.size - (dest.soundFormat == SOUND_WAV ?
00955                                     sizeof (wavheader) : 0);
00956     elementCount /= (dest.channels * (dest.sampleSize / 8));
00957     switch (ssampleSize * 1000 + dest.sampleSize)
00958     {
00959       case 8008:
00960         if (srate == dest.rate && schannels == 1 && dest.channels == 2)
00961           dup((byte*)dbuffer, (byte*)sbuffer, elementCount);
00962         else if (srate == dest.rate && schannels == 2 && dest.channels == 1)
00963           pud(dbuffer, sbuffer, elementCount);
00964         else
00965           copy(dbuffer, sbuffer, schannels, dest.channels, srate, dest.rate,
00966              elementCount, ssampleFormat==SAMPLE_SIGNED, dest.sampleFormat ==
00967              SAMPLE_SIGNED);
00968         break;
00969       case 16008:
00970         copy((short*)sbuffer, dbuffer, schannels, dest.channels, srate,
00971              dest.rate, elementCount, ssampleFormat==SAMPLE_SIGNED,
00972              dest.sampleFormat == SAMPLE_SIGNED);
00973         break;
00974       case 16016: // Data is short, but convertions needs an unsigned short.
00975          if (srate == dest.rate && schannels == 1 && dest.channels == 2)
00976           dup((unsigned short*)dbuffer,
00977               (unsigned short*)sbuffer,
00978               elementCount);
00979         else if (srate == dest.rate && schannels == 2 && dest.channels == 1)
00980           pud((unsigned short*)dbuffer,
00981               (unsigned short*)sbuffer,
00982               elementCount);
00983         else
00984           copy((short*)sbuffer, (short*)dbuffer, schannels, dest.channels,
00985              srate, dest.rate, elementCount, ssampleFormat==SAMPLE_SIGNED,
00986              dest.sampleFormat == SAMPLE_SIGNED);
00987         break;
00988       case 8016:
00989         copy((char*)sbuffer, (short*)dbuffer, schannels, dest.channels,
00990              srate, dest.rate, elementCount, ssampleFormat==SAMPLE_SIGNED,
00991              dest.sampleFormat == SAMPLE_SIGNED);
00992         break;
00993     }
00994     return 0;
00995   }
00996 
00997 } // namespace urbi