mallinath@webrtc.org | 67ee6b9 | 2014-02-03 16:57:16 +0000 | [diff] [blame] | 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; |
mallinath@webrtc.org | 67ee6b9 | 2014-02-03 16:57:16 +0000 | [diff] [blame] | 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 |