1#include "talk/media/base/yuvframegenerator.h"
2
3#include <string.h>
4#include <sstream>
5
6#include "webrtc/base/basictypes.h"
7#include "webrtc/base/common.h"
8
9namespace 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.
14const int kBarcodeBackgroundWidth = 160;
15const int kBarcodeBackgroundHeight = 100;
16const int kBarsXOffset = 12;
17const int kBarsYOffset = 4;
18const int kUnitBarSize = 2;
19const int kBarcodeNormalBarHeight = 80;
20const int kBarcodeGuardBarHeight = 96;
21const int kBarcodeMaxEncodableDigits = 7;
22
23YuvFrameGenerator::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
45YuvFrameGenerator::~YuvFrameGenerator() {
46  delete y_data_;
47  delete u_data_;
48  delete v_data_;
49}
50
51void 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
80void 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
93void 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
102void 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
111void 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
120void 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
129void 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
148void 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
157static 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
181void 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
216int 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
224int 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
235const uint8 kEanEncodings[] = { 13, 25, 19, 61, 35, 49, 47, 59, 55, 11 };
236
237int 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
253void 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