summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--commands/Kconfig9
-rw-r--r--commands/Makefile1
-rw-r--r--commands/fbtest.c201
-rw-r--r--drivers/video/edid.c29
-rw-r--r--include/gui/2d-primitives.h15
-rw-r--r--include/gui/graphic_utils.h3
-rw-r--r--include/int_sqrt.h22
-rw-r--r--lib/Makefile1
-rw-r--r--lib/gui/2d-primitives.c199
-rw-r--r--lib/gui/Kconfig3
-rw-r--r--lib/gui/Makefile1
-rw-r--r--lib/gui/graphic_utils.c29
-rw-r--r--lib/int_sqrt.c46
13 files changed, 530 insertions, 29 deletions
diff --git a/commands/Kconfig b/commands/Kconfig
index 81a3543b1c..3a0977b980 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1417,6 +1417,15 @@ config CMD_SPLASH
-b COLOR background color in 0xttrrggbb
-o render offscreen
+config CMD_FBTEST
+ bool
+ depends on VIDEO
+ select 2D_PRIMITIVES
+ prompt "FB test"
+ help
+ Framebuffer test command that allows to produce a number of
+ test patterns on a screen.
+
config CMD_READLINE
tristate
prompt "readline"
diff --git a/commands/Makefile b/commands/Makefile
index 7d848e7499..7abd6dd406 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_CMD_HELP) += help.o
obj-$(CONFIG_CMD_LSMOD) += lsmod.o
obj-$(CONFIG_CMD_INSMOD) += insmod.o
obj-$(CONFIG_CMD_SPLASH) += splash.o
+obj-$(CONFIG_CMD_FBTEST) += fbtest.o
obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
obj-$(CONFIG_USB_GADGET_SERIAL) += usbserial.o
obj-$(CONFIG_CMD_GPIO) += gpio.o
diff --git a/commands/fbtest.c b/commands/fbtest.c
new file mode 100644
index 0000000000..d070d1f7ab
--- /dev/null
+++ b/commands/fbtest.c
@@ -0,0 +1,201 @@
+#include <common.h>
+#include <command.h>
+#include <errno.h>
+#include <malloc.h>
+#include <getopt.h>
+#include <fb.h>
+#include <gui/graphic_utils.h>
+#include <gui/2d-primitives.h>
+#include <linux/gcd.h>
+#include <int_sqrt.h>
+
+static void fbtest_pattern_bars(struct screen *sc, u32 unused)
+{
+ int i;
+
+ const u32 xres = sc->info->xres;
+
+ const u32 colors[] = {
+ 0xFFFFFF, /* white */
+ 0xFFFF00, /* yellow */
+ 0x00FFFF, /* cyan */
+ 0x00FF00, /* green */
+ 0xFF00FF, /* magenta */
+ 0xFF0000, /* red */
+ 0x0000FF, /* blue */
+ 0x000000, /* black */
+ };
+
+ for (i = 0; i < ARRAY_SIZE(colors); i++) {
+ const u8 r = (colors[i] >> 16) & 0xff;
+ const u8 g = (colors[i] >> 8) & 0xff;
+ const u8 b = (colors[i] >> 0) & 0xff;
+ const int dx = xres / ARRAY_SIZE(colors);
+
+ gu_fill_rectangle(sc,
+ i * dx, -1, (i + 1) * dx - 1, -1,
+ r, g, b, 0xff);
+ }
+}
+
+static void fbtest_pattern_geometry(struct screen *sc, u32 color)
+{
+ int i;
+
+ const u8 r = (color >> 16) & 0xff;
+ const u8 g = (color >> 8) & 0xff;
+ const u8 b = (color >> 0) & 0xff;
+
+ const u32 xres = sc->info->xres;
+ const u32 yres = sc->info->yres;
+
+ const u8 xcount = xres / gcd(xres, yres);
+ const u8 ycount = yres / gcd(xres, yres);
+
+ const struct {
+ int x1, y1, x2, y2;
+ } borders[] = {
+ { 0, 0, xres - 1, 0 },
+ { xres - 1, 0, xres - 1, yres - 1 },
+ { 0, yres - 1, xres - 1, yres - 1 },
+ { 0, 0, 0, yres - 1 },
+ };
+
+ const int R1 = min(xres, yres) / 2;
+ const int h = xres * xres + yres * yres;
+ const int R2 = (int_sqrt(h) / 2 - R1) * 5 / 12;
+
+ const struct {
+ int x0, y0, radius;
+ } circles[] = {
+ { xres / 2, yres / 2, R1 - 1 },
+ { R2, R2, R2 - 1 },
+ { xres - R2, R2, R2 - 1 },
+ { xres - R2, yres - R2, R2 - 1 },
+ { R2, yres - R2, R2 - 1 }
+ };
+
+ void *buf = gui_screen_render_buffer(sc);
+
+ gu_memset_pixel(sc->info, buf, ~color,
+ sc->s.width * sc->s.height);
+
+ for (i = 0; i < ARRAY_SIZE(borders); i++)
+ gu_draw_line(sc,
+ borders[i].x1, borders[i].y1,
+ borders[i].x2, borders[i].y2,
+ r, g, b, 0xff, 10);
+
+ for (i = 0; i < ARRAY_SIZE(circles); i++)
+ gu_draw_circle(sc,
+ circles[i].x0, circles[i].y0,
+ circles[i].radius,
+ r, g, b, 0xff);
+
+ for (i = 1; i < ycount; i++) {
+ const int y = (yres - 1) * i / ycount;
+ gu_draw_line(sc,
+ 0, y, xres - 1, y,
+ r, g, b, 0xff, 0);
+ }
+
+
+ for (i = 1; i < xcount; i++) {
+ const int x = (xres - 1) * i / xcount;
+ gu_draw_line(sc,
+ x, 0, x, yres - 1,
+ r, g, b, 0xff, 0);
+ }
+}
+
+static int do_fbtest(int argc, char *argv[])
+{
+ struct screen *sc;
+ int opt;
+ unsigned int i;
+ const char *pattern_name = NULL;
+ char *fbdev = "/dev/fb0";
+ void (*pattern) (struct screen *sc, u32 color) = NULL;
+ u32 color = 0xffffff;
+
+ struct {
+ const char *name;
+ void (*func) (struct screen *sc, u32 color);
+ } patterns[] = {
+ { "geometry", fbtest_pattern_geometry },
+ { "bars", fbtest_pattern_bars }
+ };
+
+ while((opt = getopt(argc, argv, "d:p:c:")) > 0) {
+ switch(opt) {
+ case 'd':
+ fbdev = optarg;
+ break;
+ case 'p':
+ pattern_name = optarg;
+ break;
+ case 'c':
+ color = simple_strtoul(optarg, NULL, 16);
+ break;
+ }
+ }
+
+ if (pattern_name) {
+ for (i = 0; i < ARRAY_SIZE(patterns); i++)
+ if (!strcmp(pattern_name, patterns[i].name))
+ pattern = patterns[i].func;
+
+ if (!pattern) {
+ printf("Unknown pattern: %s\n", pattern_name);
+ return -EINVAL;
+ }
+ }
+
+ sc = fb_open(fbdev);
+ if (IS_ERR(sc)) {
+ perror("fd_open");
+ return PTR_ERR(sc);
+ }
+
+ if (!pattern_name) {
+ printf("No pattern selected. Cycling through all of them.\n");
+ printf("Press Ctrl-C to stop\n");
+
+ i = 0;
+ for (;;) {
+ uint64_t start;
+ pattern = patterns[i++ % ARRAY_SIZE(patterns)].func;
+ pattern(sc, color);
+ gu_screen_blit(sc);
+
+ start = get_time_ns();
+ while (!is_timeout(start, 2 * SECOND))
+ if (ctrlc())
+ goto done;
+ }
+ } else {
+ pattern(sc, color);
+ gu_screen_blit(sc);
+ }
+done:
+ fb_close(sc);
+
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(fbtest)
+BAREBOX_CMD_HELP_TEXT("This command displays a test pattern on a screen")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-d <fbdev>\t", "framebuffer device (default /dev/fb0)")
+BAREBOX_CMD_HELP_OPT ("-c color\t", "color")
+BAREBOX_CMD_HELP_OPT ("-p pattern\t", "pattern name (geometry, bars)")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(fbtest)
+ .cmd = do_fbtest,
+ BAREBOX_CMD_DESC("display a test pattern")
+ BAREBOX_CMD_OPTS("[-dcp]")
+ BAREBOX_CMD_GROUP(CMD_GRP_CONSOLE)
+ BAREBOX_CMD_HELP(cmd_fbtest_help)
+BAREBOX_CMD_END
diff --git a/drivers/video/edid.c b/drivers/video/edid.c
index e5c32a0c1b..92b59a5564 100644
--- a/drivers/video/edid.c
+++ b/drivers/video/edid.c
@@ -321,35 +321,6 @@ static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
}
/**
- * int_sqrt - rough approximation to sqrt
- * @x: integer of which to calculate the sqrt
- *
- * A very rough approximation to the sqrt() function.
- */
-unsigned long int_sqrt(unsigned long x)
-{
- unsigned long b, m, y = 0;
-
- if (x <= 1)
- return x;
-
- m = 1UL << (BITS_PER_LONG - 2);
- while (m != 0) {
- b = y + m;
- y >>= 1;
-
- if (x >= b) {
- x -= b;
- y += m;
- }
- m >>= 2;
- }
-
- return y;
-}
-EXPORT_SYMBOL(int_sqrt);
-
-/**
* fb_get_hfreq - estimate hsync
* @vfreq: vertical refresh rate
* @yres: vertical resolution
diff --git a/include/gui/2d-primitives.h b/include/gui/2d-primitives.h
new file mode 100644
index 0000000000..06216bb03c
--- /dev/null
+++ b/include/gui/2d-primitives.h
@@ -0,0 +1,15 @@
+#ifndef __2D_PRIMITIVES__
+#define __2D_PRIMITIVES__
+
+#include <fb.h>
+
+void gu_draw_line(struct screen *sc,
+ int x1, int y1, int x2, int y2,
+ u8 r, u8 g, u8 b, u8 a,
+ unsigned int dash);
+
+void gu_draw_circle(struct screen *sc,
+ int x0, int y0, int radius,
+ u8 r, u8 g, u8 b, u8 a);
+
+#endif
diff --git a/include/gui/graphic_utils.h b/include/gui/graphic_utils.h
index 231b3a9414..279fdf91d3 100644
--- a/include/gui/graphic_utils.h
+++ b/include/gui/graphic_utils.h
@@ -28,4 +28,7 @@ void gu_invert_area(struct fb_info *info, void *buf, int startx, int starty, int
void gu_screen_blit_area(struct screen *sc, int startx, int starty, int width,
int height);
+void gu_fill_rectangle(struct screen *sc,
+ int x1, int y1, int x2, int y2,
+ u8 r, u8 g, u8 b, u8 a);
#endif /* __GRAPHIC_UTILS_H__ */
diff --git a/include/int_sqrt.h b/include/int_sqrt.h
new file mode 100644
index 0000000000..55fb4fbe66
--- /dev/null
+++ b/include/int_sqrt.h
@@ -0,0 +1,22 @@
+/*
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __INT_SQRT__
+#define __INT_SQRT__
+
+unsigned long int_sqrt(unsigned long x);
+
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index 399d896bd9..92404fd331 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_BAREBOX_LOGO) += logo/
obj-y += reed_solomon/
obj-$(CONFIG_RATP) += ratp.o
obj-y += list_sort.o
+obj-y += int_sqrt.o
diff --git a/lib/gui/2d-primitives.c b/lib/gui/2d-primitives.c
new file mode 100644
index 0000000000..f3814eea44
--- /dev/null
+++ b/lib/gui/2d-primitives.c
@@ -0,0 +1,199 @@
+#include <common.h>
+#include <fb.h>
+#include <gui/graphic_utils.h>
+#include <linux/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <fs.h>
+#include <malloc.h>
+
+static void __illuminate(struct fb_info *info,
+ int x, int y,
+ u8 r, u8 g, u8 b, u8 a)
+{
+ void *pixel;
+
+ pixel = fb_get_screen_base(info);
+ pixel += y * info->line_length + x * (info->bits_per_pixel >> 3);
+
+ gu_set_rgba_pixel(info, pixel, r, g, b, a);
+}
+
+static void illuminate(struct fb_info *info,
+ bool invert,
+ int x, int y,
+ u8 r, u8 g, u8 b, u8 a)
+{
+ if (invert)
+ __illuminate(info, y, x,
+ r, g, b, a);
+ else
+ __illuminate(info, x, y,
+ r, g, b, a);
+
+}
+
+
+static void draw_simple_line(struct screen *sc,
+ int x1, int y1,
+ int x2, int y2,
+ u8 r, u8 g, u8 b, u8 a,
+ unsigned int dash)
+{
+ int x;
+ bool invert = false;
+ unsigned int pixel = 0;
+
+ BUG_ON(x1 != x2 &&
+ y1 != y2);
+
+ if (x1 == x2) {
+ swap(x1, y1);
+ swap(x2, y2);
+ invert = true;
+ }
+
+ if (x1 > x2) {
+ swap(x1, x2);
+ swap(y1, y2);
+ }
+
+ for (x = x1; x < x2 - 1; x++) {
+ if (!dash ||
+ (++pixel % (2 * dash)) < dash)
+ illuminate(sc->info,
+ invert,
+ x, y1,
+ r, g, b, a);
+ }
+}
+
+/**
+ * gl_draw_line - draw a 2D dashed line between (x1, y1) and (x2,y2)
+ *
+ * @sc: screen to draw on
+ * @x1, @y1: first point defining the line
+ * @x2, @y2: second point defining the line
+ * @r, @g, @b, @a: line's color
+ * @dash: dash length (0 denotes solid line)
+ *
+ * gl_draw_line() implements integer version of Bresenham's algoritm
+ * as can be found here:
+ *
+ * http://www.idav.ucdavis.edu/education/GraphicsNotes/Bresenhams-Algorithm.pdf
+ */
+void gu_draw_line(struct screen *sc,
+ int x1, int y1,
+ int x2, int y2,
+ u8 r, u8 g, u8 b, u8 a,
+ unsigned int dash)
+{
+ int dx;
+ int dy;
+ int i, j, eps;
+ bool invert = false;
+ unsigned int pixel = 0;
+
+ BUG_ON(x1 < 0 || y1 < 0 ||
+ x2 < 0 || y2 < 0);
+
+ if (x1 == x2 || y1 == y2) {
+ draw_simple_line(sc,
+ x1, y1,
+ x2, y2,
+ r, g, b, a, dash);
+ return;
+ }
+
+ dx = abs(x2 - x1);
+ dy = abs(y2 - y1);
+
+ /*
+ * First thing we need to determine "Driving Axis", as can be
+ * seen below if Y-axis projection of the line is bigger than
+ * X-axis projection we swap axes and pretend the X is Y and
+ * vice versa
+ */
+ if (dy > dx) {
+ swap(x1, y1);
+ swap(x2, y2);
+ swap(dx, dy);
+ invert = true;
+ }
+
+ /*
+ * Second, we need to make sure that we will be traversing
+ * driving axis in the direction of increment so we swap point
+ * 1 with point 2 if x1 is greater than x2
+ */
+ if (x1 > x2) {
+ swap(x1, x2);
+ swap(y1, y2);
+ }
+
+ j = y1;
+ eps = dy - dx;
+
+ for (i = x1; i <= x2 - 1; i++) {
+ if (!dash ||
+ (++pixel % (2 * dash)) > dash) {
+ illuminate(sc->info,
+ invert,
+ j, i,
+ r, g, b, a);
+ } else {
+ printf("NOT illuminating pixel: %d\n", pixel);
+ }
+
+ if (eps >= 0) {
+ j += 1;
+ eps -= dx;
+ }
+
+ eps += dy;
+ }
+}
+
+/**
+ * gl_draw_circle - draw a 2D circle with center at (x0, y0)
+ *
+ * @sc: screen to draw on
+ * @x0, @y0: coordinates of circle's center
+ * @radius: circle's radius
+ * @r, @g, @b, @a: circle's color
+
+ *
+ * gu_draw_circle() implements midpoint circle algorithm as can be
+ * found here:
+ *
+ * https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
+ */
+void gu_draw_circle(struct screen *sc,
+ int x0, int y0, int radius,
+ u8 r, u8 g, u8 b, u8 a)
+{
+ int x = radius;
+ int y = 0;
+ int e = 0;
+
+ BUG_ON(x0 < 0 || y0 < 0 || radius < 0);
+
+ while (x >= y) {
+ __illuminate(sc->info, x0 + x, y0 + y, r, g, b, a);
+ __illuminate(sc->info, x0 + y, y0 + x, r, g, b, a);
+ __illuminate(sc->info, x0 - y, y0 + x, r, g, b, a);
+ __illuminate(sc->info, x0 - x, y0 + y, r, g, b, a);
+ __illuminate(sc->info, x0 - x, y0 - y, r, g, b, a);
+ __illuminate(sc->info, x0 - y, y0 - x, r, g, b, a);
+ __illuminate(sc->info, x0 + y, y0 - x, r, g, b, a);
+ __illuminate(sc->info, x0 + x, y0 - y, r, g, b, a);
+
+ y += 1;
+ e += 1 + 2 * y;
+
+ if (2 * (e - x) + 1 > 0) {
+ x -= 1;
+ e += 1 - 2 * x;
+ }
+ }
+}
diff --git a/lib/gui/Kconfig b/lib/gui/Kconfig
index eac9597e2d..2625d9fb2f 100644
--- a/lib/gui/Kconfig
+++ b/lib/gui/Kconfig
@@ -6,6 +6,9 @@ config IMAGE_RENDERER
if IMAGE_RENDERER
+config 2D_PRIMITIVES
+ bool
+
config BMP
bool "bmp"
diff --git a/lib/gui/Makefile b/lib/gui/Makefile
index d4b26c47b8..31e66225d7 100644
--- a/lib/gui/Makefile
+++ b/lib/gui/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_IMAGE_RENDERER) += image_renderer.o graphic_utils.o
obj-$(CONFIG_PNG) += png.o
obj-$(CONFIG_LODEPNG) += png_lode.o lodepng.o
obj-$(CONFIG_PICOPNG) += png_pico.o picopng.o
+obj-$(CONFIG_2D_PRIMITIVES) += 2d-primitives.o
diff --git a/lib/gui/graphic_utils.c b/lib/gui/graphic_utils.c
index 2fe9fa358a..d3f5cce4b7 100644
--- a/lib/gui/graphic_utils.c
+++ b/lib/gui/graphic_utils.c
@@ -336,3 +336,32 @@ void gu_screen_blit(struct screen *sc)
if (info->screen_base_shadow)
memcpy(info->screen_base, info->screen_base_shadow, sc->fbsize);
}
+
+void gu_fill_rectangle(struct screen *sc,
+ int x1, int y1, int x2, int y2,
+ u8 r, u8 g, u8 b, u8 a)
+{
+ int y;
+ void *buf = gui_screen_render_buffer(sc);
+
+ x1 = max(0, x1);
+ y1 = max(0, y1);
+
+ x2 = (x2 < 0) ? sc->info->xres - 1 : x2;
+ y2 = (y2 < 0) ? sc->info->yres - 1 : y2;
+
+ if (x2 < x1)
+ swap(x1, x2);
+ if (y2 < y1)
+ swap(y1, y2);
+
+ for(y = y1; y <= y2; y++) {
+ int x;
+ unsigned char *pixel = buf + y * sc->info->line_length +
+ x1 * (sc->info->bits_per_pixel / 8);
+ for(x = x1; x <= x2; x++) {
+ gu_set_rgba_pixel(sc->info, pixel, r, g, b, a);
+ pixel += sc->info->bits_per_pixel / 8;
+ }
+ }
+}
diff --git a/lib/int_sqrt.c b/lib/int_sqrt.c
new file mode 100644
index 0000000000..30ccafbf9e
--- /dev/null
+++ b/lib/int_sqrt.c
@@ -0,0 +1,46 @@
+/*
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+
+/**
+ * int_sqrt - rough approximation to sqrt
+ * @x: integer of which to calculate the sqrt
+ *
+ * A very rough approximation to the sqrt() function.
+ */
+unsigned long int_sqrt(unsigned long x)
+{
+ unsigned long b, m, y = 0;
+
+ if (x <= 1)
+ return x;
+
+ m = 1UL << (BITS_PER_LONG - 2);
+ while (m != 0) {
+ b = y + m;
+ y >>= 1;
+
+ if (x >= b) {
+ x -= b;
+ y += m;
+ }
+ m >>= 2;
+ }
+
+ return y;
+}
+EXPORT_SYMBOL(int_sqrt);