1 
2 //#define DRAW_PERF_PRINT
3 
4 #include <cstring>
5 #include <cassert>
6 
7 #ifdef HAS_PTHREAD
8 #include <thread>
9 #endif
10 
11 #include <kms++/kms++.h>
12 #include <kms++util/kms++util.h>
13 
14 using namespace std;
15 
16 namespace kms
17 {
get_test_pattern_pixel(IFramebuffer & fb,unsigned x,unsigned y)18 static RGB get_test_pattern_pixel(IFramebuffer& fb, unsigned x, unsigned y)
19 {
20 	const unsigned w = fb.width();
21 	const unsigned h = fb.height();
22 
23 	const unsigned mw = 20;
24 
25 	const unsigned xm1 = mw;
26 	const unsigned xm2 = w - mw - 1;
27 	const unsigned ym1 = mw;
28 	const unsigned ym2 = h - mw - 1;
29 
30 	// white margin lines
31 	if (x == xm1 || x == xm2 || y == ym1 || y == ym2)
32 		return RGB(255, 255, 255);
33 	// white box in top left corner
34 	else if (x < xm1 && y < ym1)
35 		return RGB(255, 255, 255);
36 	// white box outlines to corners
37 	else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2))
38 		return RGB(255, 255, 255);
39 	// white box outlines to corners
40 	else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2))
41 		return RGB(255, 255, 255);
42 	// blue bar on the left
43 	else if (x < xm1 && (y > ym1 && y < ym2))
44 		return RGB(0, 0, 255);
45 	// blue bar on the top
46 	else if (y < ym1 && (x > xm1 && x < xm2))
47 		return RGB(0, 0, 255);
48 	// red bar on the right
49 	else if (x > xm2 && (y > ym1 && y < ym2))
50 		return RGB(255, 0, 0);
51 	// red bar on the bottom
52 	else if (y > ym2 && (x > xm1 && x < xm2))
53 		return RGB(255, 0, 0);
54 	// inside the margins
55 	else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) {
56 		// diagonal line
57 		if (x == y || w - x == h - y)
58 			return RGB(255, 255, 255);
59 		// diagonal line
60 		else if (w - x - 1 == y || x == h - y - 1)
61 			return RGB(255, 255, 255);
62 		else {
63 			int t = (x - xm1 - 1) * 8 / (xm2 - xm1 - 1);
64 			unsigned r = 0, g = 0, b = 0;
65 
66 			unsigned c = (y - ym1 - 1) % 256;
67 
68 			switch (t) {
69 			case 0:
70 				r = c;
71 				break;
72 			case 1:
73 				g = c;
74 				break;
75 			case 2:
76 				b = c;
77 				break;
78 			case 3:
79 				g = b = c;
80 				break;
81 			case 4:
82 				r = b = c;
83 				break;
84 			case 5:
85 				r = g = c;
86 				break;
87 			case 6:
88 				r = g = b = c;
89 				break;
90 			case 7:
91 				break;
92 			}
93 
94 			return RGB(r, g, b);
95 		}
96 	} else {
97 		// black corners
98 		return RGB(0, 0, 0);
99 	}
100 }
101 
draw_test_pattern_part(IFramebuffer & fb,unsigned start_y,unsigned end_y,YUVType yuvt)102 static void draw_test_pattern_part(IFramebuffer& fb, unsigned start_y, unsigned end_y, YUVType yuvt)
103 {
104 	unsigned x, y;
105 	unsigned w = fb.width();
106 
107 	const PixelFormatInfo& format_info = get_pixel_format_info(fb.format());
108 	const PixelFormatPlaneInfo& plane_info = format_info.planes[format_info.num_planes - 1];
109 
110 	switch (format_info.type) {
111 	case PixelColorType::RGB:
112 		for (y = start_y; y < end_y; y++) {
113 			for (x = 0; x < w; x++) {
114 				RGB pixel = get_test_pattern_pixel(fb, x, y);
115 				draw_rgb_pixel(fb, x, y, pixel);
116 			}
117 		}
118 		break;
119 
120 	case PixelColorType::YUV:
121 		switch (plane_info.xsub + plane_info.ysub) {
122 		case 2:
123 			for (y = start_y; y < end_y; y++) {
124 				for (x = 0; x < w; x++) {
125 					RGB pixel = get_test_pattern_pixel(fb, x, y);
126 					draw_yuv444_pixel(fb, x, y, pixel.yuv(yuvt));
127 				}
128 			}
129 			break;
130 
131 		case 3:
132 			for (y = start_y; y < end_y; y++) {
133 				for (x = 0; x < w; x += 2) {
134 					RGB pixel1 = get_test_pattern_pixel(fb, x, y);
135 					RGB pixel2 = get_test_pattern_pixel(fb, x + 1, y);
136 					draw_yuv422_macropixel(fb, x, y, pixel1.yuv(yuvt), pixel2.yuv(yuvt));
137 				}
138 			}
139 			break;
140 
141 		case 4:
142 			for (y = start_y; y < end_y; y += 2) {
143 				for (x = 0; x < w; x += 2) {
144 					RGB pixel00 = get_test_pattern_pixel(fb, x, y);
145 					RGB pixel10 = get_test_pattern_pixel(fb, x + 1, y);
146 					RGB pixel01 = get_test_pattern_pixel(fb, x, y + 1);
147 					RGB pixel11 = get_test_pattern_pixel(fb, x + 1, y + 1);
148 					draw_yuv420_macropixel(fb, x, y,
149 							       pixel00.yuv(yuvt), pixel10.yuv(yuvt),
150 							       pixel01.yuv(yuvt), pixel11.yuv(yuvt));
151 				}
152 			}
153 			break;
154 
155 		default:
156 			throw invalid_argument("unsupported number of pixel format planes");
157 		}
158 
159 		break;
160 
161 	default:
162 		throw invalid_argument("unsupported pixel format");
163 	}
164 }
165 
draw_test_pattern_impl(IFramebuffer & fb,YUVType yuvt)166 static void draw_test_pattern_impl(IFramebuffer& fb, YUVType yuvt)
167 {
168 #ifdef HAS_PTHREAD
169 	if (fb.height() < 20) {
170 		draw_test_pattern_part(fb, 0, fb.height(), yuvt);
171 		return;
172 	}
173 
174 	// Create the mmaps before starting the threads
175 	for (unsigned i = 0; i < fb.num_planes(); ++i)
176 		fb.map(0);
177 
178 	unsigned num_threads = thread::hardware_concurrency();
179 	vector<thread> workers;
180 
181 	unsigned part = (fb.height() / num_threads) & ~1;
182 
183 	for (unsigned n = 0; n < num_threads; ++n) {
184 		unsigned start = n * part;
185 		unsigned end = start + part;
186 
187 		if (n == num_threads - 1)
188 			end = fb.height();
189 
190 		workers.push_back(thread([&fb, start, end, yuvt]() { draw_test_pattern_part(fb, start, end, yuvt); }));
191 	}
192 
193 	for (thread& t : workers)
194 		t.join();
195 #else
196 	draw_test_pattern_part(fb, 0, fb.height(), yuvt);
197 #endif
198 }
199 
draw_test_pattern(IFramebuffer & fb,YUVType yuvt)200 void draw_test_pattern(IFramebuffer& fb, YUVType yuvt)
201 {
202 #ifdef DRAW_PERF_PRINT
203 	Stopwatch sw;
204 	sw.start();
205 #endif
206 
207 	draw_test_pattern_impl(fb, yuvt);
208 
209 #ifdef DRAW_PERF_PRINT
210 	double us = sw.elapsed_us();
211 	printf("draw took %u us\n", (unsigned)us);
212 #endif
213 }
214 
215 } // namespace kms
216