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