ArDrone SDK 1.8 added
[mardrone] / mardrone / ARDrone_SDK_Version_1_8_20110726 / ARDroneLib / VLIB / video_picture.c
diff --git a/mardrone/ARDrone_SDK_Version_1_8_20110726/ARDroneLib/VLIB/video_picture.c b/mardrone/ARDrone_SDK_Version_1_8_20110726/ARDroneLib/VLIB/video_picture.c
new file mode 100644 (file)
index 0000000..ae766ae
--- /dev/null
@@ -0,0 +1,722 @@
+#include <VP_Os/vp_os_print.h>
+
+#include <VLIB/Platform/video_utils.h>
+#include <VLIB/video_picture.h>
+#include <VP_Os/vp_os_malloc.h>
+
+#ifndef HAS_VIDEO_BLOCKLINE_TO_MACRO_BLOCKS
+
+// Convert a 8x8 block of 8 bits data to a 8x8 block of 16 bits data
+static void copy_block_8_16(int16_t* dst, int32_t dst_offset, uint8_t* src, int32_t src_offset)
+{
+  uint32_t* src32 = (uint32_t*) src;
+  uint32_t* dst32 = (uint32_t*) dst;
+
+  uint32_t  src_offset32 = src_offset >> 2;
+  uint32_t  dst_offset32 = dst_offset >> 1;
+
+  uint32_t  temp;
+
+  int32_t i;
+
+  for( i = 0; i < MCU_BLOCK_SIZE; i += MCU_WIDTH, src32 += src_offset32, dst32 += dst_offset32 )
+  {
+    temp = *src32++;
+
+    *dst32++ = ((temp << 8) & 0x00FF0000) | (temp & 0x000000FF);
+    *dst32++ = ((temp >> 8) & 0x00FF0000) | ((temp >> 16) & 0x000000FF);
+
+    temp = *src32++;
+
+    *dst32++ = ((temp << 8) & 0x00FF0000) | (temp & 0x000000FF);
+    *dst32++ = ((temp >> 8) & 0x00FF0000) | ((temp >> 16) & 0x000000FF);
+  }
+
+}
+
+#endif
+
+// Convert a 8x8 block of 16 bits data to a 8x8 block of 8 bits data
+static void copy_block_16_8(uint8_t* dst, int32_t dst_offset, int16_t* src, int32_t src_offset)
+{
+  int32_t i;
+  int16_t temp;
+
+  for( i = 0; i < MCU_BLOCK_SIZE; i += MCU_WIDTH, dst += dst_offset, src += src_offset )
+  {
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+    temp = *src++; if( temp > 0xff ) temp = 0xff; if(temp < 0) temp = 0; temp &= 0xff; *dst++ = (uint8_t) temp;
+  }
+}
+
+//
+// Transform blockline in macro blocks
+//
+// Blockline:
+//  _______
+// | 1 | 2 |
+// |___|___|  Y
+// | 3 | 4 |
+// |___|___|
+//  ___
+// | 5 |
+// |___| Cb
+//  ___
+// | 6 |
+// |___| Cr
+//
+// Dct cache:
+//  _______________________
+// | 1 | 2 | 3 | 4 | 5 | 6 | ...
+// |___|___|___|___|___|___|
+//
+
+#ifndef HAS_VIDEO_BLOCKLINE_TO_MACRO_BLOCKS
+
+C_RESULT video_blockline_to_macro_blocks(video_picture_context_t* ctx, int16_t* dst, int32_t num_macro_blocks)
+{
+  uint8_t* y_src        = ctx->y_src;
+  uint8_t* cb_src       = ctx->cb_src;
+  uint8_t* cr_src       = ctx->cr_src;
+
+  while( num_macro_blocks > 0 )
+  {
+    // Current MB
+    copy_block_8_16( dst,
+                     0,
+                     y_src,
+                     ctx->y_woffset - MCU_WIDTH );
+
+    dst += MCU_BLOCK_SIZE; // skip block 1
+
+    copy_block_8_16( dst,
+                     0,
+                     y_src + MCU_WIDTH,
+                     ctx->y_woffset - MCU_WIDTH );
+
+    dst += MCU_BLOCK_SIZE; // skip block 2
+
+    copy_block_8_16( dst,
+                     0,
+                     y_src + ctx->y_hoffset,
+                     ctx->y_woffset - MCU_WIDTH );
+
+    dst += MCU_BLOCK_SIZE; // skip block 3
+
+    copy_block_8_16( dst,
+                     0,
+                     y_src + ctx->y_hoffset + MCU_WIDTH,
+                     ctx->y_woffset - MCU_WIDTH );
+
+    dst += MCU_BLOCK_SIZE; // skip block 4
+
+    copy_block_8_16( dst,
+                     0,
+                     cb_src,
+                     ctx->c_woffset - MCU_WIDTH );
+
+    dst += MCU_BLOCK_SIZE;  // skip blocks 5
+
+    copy_block_8_16( dst,
+                     0,
+                     cr_src,
+                     ctx->c_woffset - MCU_WIDTH );
+
+    dst += MCU_BLOCK_SIZE;  // skip blocks 6
+
+    y_src   += MCU_WIDTH*2;
+    cb_src  += MCU_WIDTH;
+    cr_src  += MCU_WIDTH;
+
+    num_macro_blocks --;
+  }
+
+  ctx->y_src  = y_src;
+  ctx->cb_src = cb_src;
+  ctx->cr_src = cr_src;
+
+  return C_OK;
+}
+
+#endif
+
+// Transform macro blocks in picture of specified format
+static C_RESULT video_blockline_from_macro_blocks_yuv420(video_picture_context_t* ctx, int16_t* src, int32_t num_macro_blocks);
+static C_RESULT video_blockline_from_macro_blocks_rgb565(video_picture_context_t* ctx, int16_t* src, int32_t num_macro_blocks);
+
+C_RESULT video_blockline_from_macro_blocks(video_picture_context_t* ctx, int16_t* src, int32_t num_macro_blocks, enum PixelFormat format)
+{
+  C_RESULT res;
+
+  switch(format)
+  {
+    case PIX_FMT_YUV420P:
+      res = video_blockline_from_macro_blocks_yuv420(ctx, src, num_macro_blocks);
+      break;
+    case PIX_FMT_RGB565:
+      res = video_blockline_from_macro_blocks_rgb565(ctx, src, num_macro_blocks);
+      break;
+
+    default:
+      PRINT("In file %s, in function %s, format %d not supported\n", __FILE__, __FUNCTION__, format);
+      res = C_FAIL;
+      break;
+  }
+
+  return res;
+}
+
+C_RESULT video_blockline_from_macro_blocks_yuv420(video_picture_context_t* ctx, int16_t* src, int32_t num_macro_blocks)
+{
+  uint8_t *y_dst, *cb_dst, *cr_dst;
+
+  y_dst   = ctx->y_src;
+  cb_dst  = ctx->cb_src;
+  cr_dst  = ctx->cr_src;
+
+  while( num_macro_blocks > 0 )
+  {
+    // Current MB
+    copy_block_16_8( y_dst,
+                     ctx->y_woffset - MCU_WIDTH,
+                     src,
+                     0 );
+
+    src += MCU_BLOCK_SIZE;
+
+    copy_block_16_8( y_dst + MCU_WIDTH,
+                     ctx->y_woffset - MCU_WIDTH,
+                     src,
+                     0 );
+
+    src += MCU_BLOCK_SIZE;
+
+    copy_block_16_8( y_dst + ctx->y_hoffset,
+                     ctx->y_woffset - MCU_WIDTH,
+                     src,
+                     0 );
+
+    src += MCU_BLOCK_SIZE;
+
+    copy_block_16_8( y_dst + ctx->y_hoffset + MCU_WIDTH,
+                     ctx->y_woffset - MCU_WIDTH,
+                     src,
+                     0 );
+
+    src += MCU_BLOCK_SIZE;
+
+    copy_block_16_8( cb_dst,
+                     ctx->c_woffset - MCU_WIDTH,
+                     src,
+                     0 );
+
+    src += MCU_BLOCK_SIZE;
+
+    copy_block_16_8( cr_dst,
+                     ctx->c_woffset - MCU_WIDTH,
+                     src,
+                     0 );
+
+    src += MCU_BLOCK_SIZE;
+
+    y_dst   += MCU_WIDTH*2;
+    cb_dst  += MCU_WIDTH;
+    cr_dst  += MCU_WIDTH;
+
+    num_macro_blocks--;
+  }
+
+  ctx->y_src  = y_dst;
+  ctx->cb_src = cb_dst;
+  ctx->cr_src = cr_dst;
+
+  return C_OK;
+}
+
+#define MAKE_RGBA_565(r, g, b) ( ((r) << 11) | ((g) << 5) | (b) )
+
+#if TARGET_CPU_ARM == 1 && defined(TARGET_OS_IPHONE)
+static inline int32_t saturate8(int32_t x)
+{
+       usat(x, 7, 8);
+
+       return x;
+}
+
+static inline uint32_t saturate5(int32_t x)
+{
+       usat(x, 5, 11);
+
+       return x;
+}
+
+static inline uint32_t saturate6(int32_t x)
+{
+       usat(x, 6, 10);
+
+       return x;
+}
+
+#else
+
+// To make sure that you are bounding your inputs in the range of 0 & 255
+
+static inline int32_t saturate8(int32_t x)
+{
+  if( x < 0 )
+  {
+    x = 0;
+  }
+
+  x >>= 8;
+
+  return x > 0xFF ? 0xFF : x;
+}
+
+static inline uint16_t saturate5(int32_t x)
+{
+  if( x < 0 )
+  {
+    x = 0;
+  }
+
+  x >>= 11;
+
+  return x > 0x1F ? 0x1F : x;
+}
+
+static inline uint16_t saturate6(int32_t x)
+{
+  if( x < 0 )
+  {
+    x = 0;
+  }
+
+  x >>= 10;
+
+  return x > 0x3F ? 0x3F : x;
+}
+#endif
+
+
+static C_RESULT video_blockline_from_macro_blocks_rgb565(video_picture_context_t* ctx, int16_t* src, int32_t num_macro_blocks)
+{
+  uint32_t y_up_read, y_down_read, cr_current, cb_current;
+  int32_t u, v, vr, ug, vg, ub, r, g, b;
+  int16_t *y_buf1, *y_buf2, *cr_buf, *cb_buf;
+  uint16_t *dst_up, *dst_down;
+
+  // Control variables
+  int32_t line_size, block_size, y_woffset, y_hoffset;
+
+  y_buf1  = src;
+  y_buf2  = y_buf1 + MCU_WIDTH;
+
+  cb_buf  = y_buf1 + MCU_BLOCK_SIZE * 4;
+  cr_buf  = cb_buf + MCU_BLOCK_SIZE;
+
+  // Our ptrs are 16 bits
+  y_woffset = ctx->y_woffset / 2;
+  y_hoffset = ctx->y_hoffset / 2;
+
+  dst_up    = (uint16_t*) ctx->y_src;
+  dst_down  = dst_up + y_woffset;
+
+  line_size            = MCU_WIDTH / 2; // We compute two pixels at a time
+  block_size   = MCU_HEIGHT / 2; // We compute two lines at a time
+
+  while( num_macro_blocks > 0 )
+  {
+    // First quarter
+    cb_current  = cb_buf[0];
+    cr_current  = cr_buf[0];
+
+    u   = cb_current - 128;
+    ug  = 88 * u;
+    ub  = 454 * u;
+    v   = cr_current - 128;
+    vg  = 183 * v;
+    vr  = 359 * v;
+
+    y_up_read   = y_buf1[0] << 8;
+    y_down_read = y_buf2[0] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[0] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[0] = MAKE_RGBA_565(r, g, b);
+
+    y_up_read   = y_buf1[1] << 8;
+    y_down_read = y_buf2[1] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[1] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[1] = MAKE_RGBA_565(r, g, b);
+
+    // Second quarter
+    cr_current  = cr_buf[MCU_WIDTH / 2];
+    cb_current  = cb_buf[MCU_WIDTH / 2];
+
+    u   = cb_current - 128;
+    ug  = 88 * u;
+    ub  = 454 * u;
+    v   = cr_current - 128;
+    vg  = 183 * v;
+    vr  = 359 * v;
+
+    y_up_read   = y_buf1[MCU_BLOCK_SIZE] << 8;
+    y_down_read = y_buf2[MCU_BLOCK_SIZE] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[MCU_WIDTH] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[MCU_WIDTH] = MAKE_RGBA_565(r, g, b);
+
+    y_up_read   = y_buf1[MCU_BLOCK_SIZE + 1] << 8;
+    y_down_read = y_buf2[MCU_BLOCK_SIZE + 1] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[MCU_WIDTH+1] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[MCU_WIDTH+1] = MAKE_RGBA_565(r, g, b);
+
+    // Third quarter
+    cr_current  = cr_buf[MCU_BLOCK_SIZE/2];
+    cb_current  = cb_buf[MCU_BLOCK_SIZE/2];
+
+    u   = cb_current - 128;
+    ug  = 88 * u;
+    ub  = 454 * u;
+    v   = cr_current - 128;
+    vg  = 183 * v;
+    vr  = 359 * v;
+
+    y_up_read   = y_buf1[MCU_BLOCK_SIZE*2] << 8;
+    y_down_read = y_buf2[MCU_BLOCK_SIZE*2] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[y_hoffset] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[y_hoffset] = MAKE_RGBA_565(r, g, b);
+
+    y_up_read   = y_buf1[MCU_BLOCK_SIZE*2 + 1] << 8;
+    y_down_read = y_buf2[MCU_BLOCK_SIZE*2 + 1] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[y_hoffset + 1] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[y_hoffset + 1] = MAKE_RGBA_565(r, g, b);
+
+    // Fourth quarter
+    cr_current  = cr_buf[MCU_BLOCK_SIZE/2 + MCU_WIDTH/2];
+    cb_current  = cb_buf[MCU_BLOCK_SIZE/2 + MCU_WIDTH/2];
+
+    u   = cb_current - 128;
+    ug  = 88 * u;
+    ub  = 454 * u;
+    v   = cr_current - 128;
+    vg  = 183 * v;
+    vr  = 359 * v;
+
+    y_up_read   = y_buf1[MCU_BLOCK_SIZE*3] << 8;
+    y_down_read = y_buf2[MCU_BLOCK_SIZE*3] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[y_hoffset + MCU_WIDTH] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[y_hoffset + MCU_WIDTH] = MAKE_RGBA_565(r, g, b);
+
+    y_up_read   = y_buf1[MCU_BLOCK_SIZE*3 + 1] << 8;
+    y_down_read = y_buf2[MCU_BLOCK_SIZE*3 + 1] << 8;
+
+    r = saturate5((y_up_read + vr));
+    g = saturate6((y_up_read - ug - vg));
+    b = saturate5((y_up_read + ub));
+
+    dst_up[y_hoffset + MCU_WIDTH + 1] = MAKE_RGBA_565(r, g, b);
+
+    r = saturate5((y_down_read + vr));
+    g = saturate6((y_down_read - ug - vg));
+    b = saturate5((y_down_read + ub));
+
+    dst_down[y_hoffset + MCU_WIDTH + 1] = MAKE_RGBA_565(r, g, b);
+
+    cr_buf    += 1;
+    cb_buf    += 1;
+    y_buf1    += 2;
+    y_buf2    += 2;
+    dst_up    += 2;
+    dst_down  += 2;
+
+    line_size--;
+
+    if( line_size == 0 ) // We computed one line of a luma-block
+    {
+      dst_up    += y_woffset*2 - MCU_WIDTH;
+      dst_down  += y_woffset*2 - MCU_WIDTH;
+
+      block_size--;
+
+      if( block_size == 0 )
+      {
+        y_buf1  = cr_buf + MCU_BLOCK_SIZE/2 + MCU_WIDTH/2;
+        y_buf2  = y_buf1 + MCU_WIDTH;
+
+        cb_buf  = y_buf1 + MCU_BLOCK_SIZE * 4;
+        cr_buf  = cb_buf + MCU_BLOCK_SIZE;
+
+        block_size = MCU_WIDTH / 2; // We compute two lines at a time
+
+        dst_up  += 2*MCU_WIDTH - y_hoffset;
+        dst_down = dst_up + y_woffset;
+
+        num_macro_blocks--;
+      }
+      else
+      {
+        y_buf1  = y_buf2;
+        y_buf2 += MCU_WIDTH;
+
+        cr_buf += MCU_WIDTH / 2;
+        cb_buf += MCU_WIDTH / 2;
+      }
+
+      line_size        = MCU_WIDTH / 2; // We compute two pixels at a time
+    }
+  }
+
+  ctx->y_src = (uint8_t*) dst_up;
+
+  return C_OK;
+}
+
+
+
+// Transform a blockline YUV 4:2:0 in picture of specified format
+static C_RESULT video_blockline_from_blockline_yuv420(video_picture_context_t* ctx, video_picture_context_t* src, int32_t num_macro_blocks);
+static C_RESULT video_blockline_from_blockline_rgb565(video_picture_context_t* ctx, video_picture_context_t* src, int32_t num_macro_blocks);
+
+C_RESULT video_blockline_from_blockline(video_picture_context_t* ctx, video_picture_context_t* src, int32_t num_macro_blocks, enum PixelFormat format)
+{
+  C_RESULT res;
+
+  switch(format)
+  {
+    case PIX_FMT_YUV420P:
+      res = video_blockline_from_blockline_yuv420(ctx, src, num_macro_blocks);
+      break;
+    case PIX_FMT_RGB565:
+      res = video_blockline_from_blockline_rgb565(ctx, src, num_macro_blocks);
+      break;
+
+    default:
+      PRINT("In file %s, in function %s, format %d not supported\n", __FILE__, __FUNCTION__, format);
+      res = C_FAIL;
+      break;
+  }
+
+  return res;
+}
+
+static C_RESULT video_blockline_from_blockline_yuv420(video_picture_context_t* blockline_ctx, video_picture_context_t* blockline_src, int32_t num_macro_blocks)
+{
+  uint32_t line;
+  uint8_t* dest;
+  uint8_t* src;
+  uint32_t copy_line_size;
+
+  // copy luma
+  copy_line_size = num_macro_blocks * MB_HEIGHT_Y;
+  dest = blockline_ctx->y_src;
+  src  = blockline_src->y_src;
+  line = MB_HEIGHT_Y;
+  while (line--)
+  {
+    vp_os_memcpy(dest,src,copy_line_size);
+    dest += blockline_ctx->y_woffset;
+    src  += blockline_src->y_woffset;
+  }
+
+  // copy cb
+  copy_line_size = num_macro_blocks * MB_HEIGHT_C;
+  dest = blockline_ctx->cb_src;
+  src  = blockline_src->cb_src;
+  line = MB_HEIGHT_C;
+  while (line--)
+  {
+    vp_os_memcpy(dest,src,copy_line_size);
+    dest += blockline_ctx->c_woffset;
+    src  += blockline_src->c_woffset;
+  }
+
+  // copy cr
+  copy_line_size = num_macro_blocks * MB_HEIGHT_C;
+  dest = blockline_ctx->cr_src;
+  src  = blockline_src->cr_src;
+  line = MB_HEIGHT_C;
+  while (line--)
+  {
+    vp_os_memcpy(dest,src,copy_line_size);
+    dest += blockline_ctx->c_woffset;
+    src  += blockline_src->c_woffset;
+  }
+
+  return C_OK;
+}
+
+
+static C_RESULT
+video_blockline_from_blockline_rgb565(video_picture_context_t* ctx,
+                                                                         video_picture_context_t* src, int32_t num_macro_blocks)
+{
+       uint32_t y_up_read, y_down_read, cr_current, cb_current;
+       int32_t u, v, vr, ug, vg, ub, r, g, b;
+       uint8_t *y_buf_up, *y_buf_down, *cr_buf, *cb_buf;
+       uint16_t *dst_up, *dst_down;
+       int32_t line_size,dest_y_woffset;
+       
+       // Control variables
+       uint32_t pixel,line;
+       
+       // In ptrs
+       y_buf_up = src->y_src;
+       y_buf_down = y_buf_up + src->y_woffset;
+       
+       cb_buf = src->cb_src;
+       cr_buf = src->cr_src;
+       
+       // Out ptrs are 16 bits
+       dest_y_woffset = ctx->y_woffset / 2;
+       
+       dst_up = (uint16_t*) ctx->y_src;
+       dst_down = dst_up + dest_y_woffset;
+       
+       // We compute two pixels at a time
+       line_size = MB_WIDTH_Y*num_macro_blocks; 
+       
+       pixel = line_size>>1;
+       line = MB_WIDTH_Y>>1;
+       
+       while (line)
+       {
+               // load cb cr
+               cb_current = *cb_buf++;
+               cr_current = *cr_buf++;
+               
+               u = cb_current - 128;
+               ug = 88 * u;
+               ub = 454 * u;
+               v = cr_current - 128;
+               vg = 183 * v;
+               vr = 359 * v;
+               
+               // compute pixel(0,0)
+               y_up_read = (*y_buf_up++) << 8;
+               r = saturate5((y_up_read + vr));
+               g = saturate6((y_up_read - ug - vg));
+               b = saturate5((y_up_read + ub));
+               
+               *dst_up++ = MAKE_RGBA_565(r, g, b);
+               
+               // compute pixel(1,0)
+               y_up_read = (*y_buf_up++) << 8;
+               
+               r = saturate5((y_up_read + vr));
+               g = saturate6((y_up_read - ug - vg));
+               b = saturate5((y_up_read + ub));
+               
+               *dst_up++ = MAKE_RGBA_565(r, g, b);
+               
+               // compute pixel (0,1)
+               y_down_read = (*y_buf_down++) << 8;
+               
+               r = saturate5((y_down_read + vr));
+               g = saturate6((y_down_read - ug - vg));
+               b = saturate5((y_down_read + ub));
+               
+               *dst_down++ = MAKE_RGBA_565(r, g, b);
+               
+               // compute pixel (1,1)
+               y_down_read = (*y_buf_down++) << 8;
+               
+               r = saturate5((y_down_read + vr));
+               g = saturate6((y_down_read - ug - vg));
+               b = saturate5((y_down_read + ub));
+               
+               *dst_down++ = MAKE_RGBA_565(r, g, b);
+               
+               pixel--;
+               if (pixel == 0)
+               {
+                       // jump to next line
+                       y_buf_up += 2*src->y_woffset - line_size;
+                       y_buf_down += 2*src->y_woffset - line_size;
+                       cb_buf += src->c_woffset - (line_size>>1);
+                       cr_buf += src->c_woffset - (line_size>>1);
+                       
+                       dst_up += 2*dest_y_woffset - line_size;
+                       dst_down += 2*dest_y_woffset - line_size;
+                       
+                       pixel = line_size>>1;
+                       line--;
+               }
+       }
+       
+       return C_OK;
+}