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