Home | History | Annotate | Download | only in base
      1 #include "talk/media/base/yuvframegenerator.h"
      2 
      3 #include <string.h>
      4 #include <sstream>
      5 
      6 #include "talk/base/basictypes.h"
      7 #include "talk/base/common.h"
      8 
      9 namespace cricket {
     10 
     11 // These values were figured out by trial and error. If you change any
     12 // basic parameters e.g. unit-bar size or bars-x-offset, you may need to change
     13 // background-width/background-height.
     14 const int kBarcodeBackgroundWidth = 160;
     15 const int kBarcodeBackgroundHeight = 100;
     16 const int kBarsXOffset = 12;
     17 const int kBarsYOffset = 4;
     18 const int kUnitBarSize = 2;
     19 const int kBarcodeNormalBarHeight = 80;
     20 const int kBarcodeGuardBarHeight = 96;
     21 const int kBarcodeMaxEncodableDigits = 7;
     22 
     23 YuvFrameGenerator::YuvFrameGenerator(int width, int height,
     24                                      bool enable_barcode) {
     25   width_ = width;
     26   height_ = height;
     27   frame_index_ = 0;
     28   int size = width_ * height_;
     29   int qsize = size / 4;
     30   frame_data_size_ = size + 2 * qsize;
     31   y_data_ = new uint8[size];
     32   u_data_ = new uint8[qsize];
     33   v_data_ = new uint8[qsize];
     34   if (enable_barcode) {
     35     ASSERT(width_ >= kBarcodeBackgroundWidth);
     36     ASSERT(height_>= kBarcodeBackgroundHeight);
     37     barcode_start_x_ = 0;
     38     barcode_start_y_ = height_ - kBarcodeBackgroundHeight;
     39   } else {
     40     barcode_start_x_ = -1;
     41     barcode_start_y_ = -1;
     42   }
     43 }
     44 
     45 YuvFrameGenerator::~YuvFrameGenerator() {
     46   delete y_data_;
     47   delete u_data_;
     48   delete v_data_;
     49 }
     50 
     51 void YuvFrameGenerator::GenerateNextFrame(uint8* frame_buffer,
     52                                           int32 barcode_value) {
     53   int size = width_ * height_;
     54   int qsize = size / 4;
     55   memset(y_data_, 0, size);
     56   memset(u_data_, 0, qsize);
     57   memset(v_data_, 0, qsize);
     58 
     59   DrawLandscape(y_data_, width_, height_);
     60   DrawGradientX(u_data_, width_/2, height_/2);
     61   DrawGradientY(v_data_, width_/2, height_/2);
     62   DrawMovingLineX(u_data_, width_/2, height_/2, frame_index_);
     63   DrawMovingLineY(v_data_, width_/2, height_/2, frame_index_);
     64   DrawBouncingCube(y_data_, width_, height_, frame_index_);
     65 
     66   if (barcode_value >= 0) {
     67       ASSERT(barcode_start_x_ != -1);
     68       DrawBarcode(barcode_value);
     69   }
     70 
     71   memcpy(frame_buffer, y_data_, size);
     72   frame_buffer += size;
     73   memcpy(frame_buffer, u_data_, qsize);
     74   frame_buffer += qsize;
     75   memcpy(frame_buffer, v_data_, qsize);
     76 
     77   frame_index_ = (frame_index_ + 1) & 0x0000FFFF;
     78 }
     79 
     80 void YuvFrameGenerator::DrawLandscape(uint8 *p, int w, int h) {
     81   int x, y;
     82   for (y = 0; y < h; y++) {
     83     for (x = 0; x < w; x++) {
     84       p[x + y * w] = x % (y+1);
     85       if (((x > w / 2 - (w / 32)) && (x < w / 2 + (w / 32))) ||
     86           ((y > h / 2 - (h / 32)) && (y < h / 2 + (h / 32)))) {
     87         p[x + y * w] = (((x + y) / 8 % 2)) ? 255 : 0;
     88       }
     89     }
     90   }
     91 }
     92 
     93 void YuvFrameGenerator::DrawGradientX(uint8 *p, int w, int h) {
     94   int x, y;
     95   for (y = 0; y < h; y++) {
     96     for (x = 0; x < w; x++) {
     97       p[x + y * w] = (x << 8) / w;
     98     }
     99   }
    100 }
    101 
    102 void YuvFrameGenerator::DrawGradientY(uint8 *p, int w, int h) {
    103   int x, y;
    104   for (y = 0; y < h; y++) {
    105     for (x = 0; x < w; x++) {
    106       p[x + y * w] = (y << 8) / h;
    107     }
    108   }
    109 }
    110 
    111 void YuvFrameGenerator::DrawMovingLineX(uint8 *p, int w, int h, int n) {
    112   int x, y;
    113   x = n % (w * 2);
    114   if (x >= w) x = w + w - x - 1;
    115   for (y = 0; y < h; y++) {
    116     p[x + y * w] = 255;
    117   }
    118 }
    119 
    120 void YuvFrameGenerator::DrawMovingLineY(uint8 *p, int w, int h, int n) {
    121   int x, y;
    122   y = n % (h * 2);
    123   if (y >= h) y = h + h - y - 1;
    124   for (x = 0; x < w; x++) {
    125     p[x + y * w] = 255;
    126   }
    127 }
    128 
    129 void YuvFrameGenerator::DrawBouncingCube(uint8 *p, int w, int h, int n) {
    130   int x, y, pw, ph, px, py;
    131   pw = w / 16;
    132   ph = h / 16;
    133   px = n % (w * 2);
    134   py = n % (h * 2);
    135   if (px >= w) px = w + w - px - 1;
    136   if (py >= h) py = h + h - py - 1;
    137   for (y = py - ph; y < py + ph; y++) {
    138     if (y >=0 && y < h) {
    139       for (x = px - pw; x < px + pw; x++) {
    140         if (x >= 0 && x < w) {
    141           p[x + y * w] = 255;
    142         }
    143       }
    144     }
    145   }
    146 }
    147 
    148 void YuvFrameGenerator::GetBarcodeBounds(int* top, int* left,
    149                                          int* width, int* height) {
    150   ASSERT(barcode_start_x_ != -1);
    151   *top = barcode_start_y_;
    152   *left = barcode_start_x_;
    153   *width = kBarcodeBackgroundWidth;
    154   *height = kBarcodeBackgroundHeight;
    155 }
    156 
    157 static void ComputeBarcodeDigits(uint32 value, std::stringstream* result) {
    158   // Serialize |value| as 7-char string, padded with 0's to the left.
    159   result->width(kBarcodeMaxEncodableDigits);
    160   result->fill('0');
    161   *result << value;
    162 
    163   // Compute check-digit and append to result. Steps described here:
    164   // http://en.wikipedia.org/wiki/European_Article_Number#Calculation_of_checksum_digit
    165   int sum = 0;
    166   for (int pos = 1; pos <= kBarcodeMaxEncodableDigits; pos++) {
    167     char next_char;
    168     result->get(next_char);
    169     uint8 digit = next_char - '0';
    170     sum += digit * (pos % 2 ? 3 : 1);
    171   }
    172   uint8 check_digit = sum % 10;
    173   if (check_digit != 0) {
    174     check_digit = 10 - check_digit;
    175   }
    176 
    177   *result << static_cast<int>(check_digit);
    178   result->seekg(0);
    179 }
    180 
    181 void YuvFrameGenerator::DrawBarcode(uint32 value) {
    182   std::stringstream value_str_stream;
    183   ComputeBarcodeDigits(value, &value_str_stream);
    184 
    185   // Draw white filled rectangle as background to barcode.
    186   DrawBlockRectangle(y_data_, barcode_start_x_, barcode_start_y_,
    187                      kBarcodeBackgroundWidth, kBarcodeBackgroundHeight,
    188                      width_, 255);
    189   DrawBlockRectangle(u_data_, barcode_start_x_ / 2, barcode_start_y_ / 2,
    190                      kBarcodeBackgroundWidth / 2, kBarcodeBackgroundHeight / 2,
    191                      width_ / 2, 128);
    192   DrawBlockRectangle(v_data_, barcode_start_x_ / 2, barcode_start_y_ / 2,
    193                      kBarcodeBackgroundWidth / 2, kBarcodeBackgroundHeight / 2,
    194                      width_ / 2, 128);
    195 
    196   // Scan through chars (digits) and draw black bars.
    197   int x = barcode_start_x_ + kBarsXOffset;
    198   int y = barcode_start_y_ + kBarsYOffset;
    199   int pos = 0;
    200   x = DrawSideGuardBars(x, y, kBarcodeGuardBarHeight);
    201   while (true) {
    202     char next_char;
    203     value_str_stream.get(next_char);
    204     if (!value_str_stream.good()) {
    205       break;
    206     }
    207     if (pos++ == 4) {
    208       x = DrawMiddleGuardBars(x, y, kBarcodeGuardBarHeight);
    209     }
    210     uint8 digit = next_char - '0';
    211     x = DrawEanEncodedDigit(digit, x, y, kBarcodeNormalBarHeight, pos > 4);
    212   }
    213   x = DrawSideGuardBars(x, y, kBarcodeGuardBarHeight);
    214 }
    215 
    216 int YuvFrameGenerator::DrawMiddleGuardBars(int x, int y, int height) {
    217   x += kUnitBarSize;
    218   DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
    219   x += (kUnitBarSize * 2);
    220   DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
    221   return x + (kUnitBarSize * 2);
    222 }
    223 
    224 int YuvFrameGenerator::DrawSideGuardBars(int x, int y, int height) {
    225   DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
    226   x += (kUnitBarSize * 2);
    227   DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
    228   return x + kUnitBarSize;
    229 }
    230 
    231 // For each digit: 0-9, |kEanEncodings| contains a bit-mask indicating
    232 // which bars are black (1) and which are blank (0). These are for the L-code
    233 // only. R-code values are bitwise negation of these. Reference:
    234 // http://en.wikipedia.org/wiki/European_Article_Number#Binary_encoding_of_data_digits_into_EAN-13_barcode // NOLINT
    235 const uint8 kEanEncodings[] = { 13, 25, 19, 61, 35, 49, 47, 59, 55, 11 };
    236 
    237 int YuvFrameGenerator::DrawEanEncodedDigit(int digit, int x, int y,
    238                                            int height, bool flip) {
    239   uint8 ean_encoding = kEanEncodings[digit];
    240   if (flip) {
    241     ean_encoding = ~ean_encoding;
    242   }
    243   uint8 mask = 0x40;
    244   for (int i = 6; i >= 0; i--, mask >>= 1) {
    245     if (ean_encoding & mask) {
    246       DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0);
    247     }
    248     x += kUnitBarSize;
    249   }
    250   return x;
    251 }
    252 
    253 void YuvFrameGenerator::DrawBlockRectangle(uint8* p,
    254     int x_start, int y_start, int width, int height, int pitch, uint8 value) {
    255   for (int x = x_start; x < x_start + width; x++) {
    256     for (int y = y_start; y < y_start + height; y++) {
    257       p[x + y * pitch] = value;
    258     }
    259   }
    260 }
    261 
    262 }  // namespace cricket
    263