1 /* GStreamer
2 * Copyright (C) <2019> Eric Marks <bigmarkslp@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19 /**
20 * SECTION:element-aatv
21 * @see_also: #GstAASink
22 *
23 * Transforms video into ascii art.
24 *
25 * <refsect2>
26 * <title>Example launch line</title>
27 * |[
28 * gst-launch-1.0 videotestsrc ! aatv ! videoconvert ! autovideosink
29 * ]| This pipeline shows the effect of aatv on a test stream.
30 * </refsect2>
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "gstaatv.h"
38 #include <string.h>
39 #include <stdlib.h>
40
41 #define PROP_AATV_COLOR_TEXT_DEFAULT 0xffffffff /* White */
42 #define PROP_AATV_COLOR_BACKGROUND_DEFAULT 0xff000000 /* Black */
43 #define PROP_AATV_COLOR_RAIN_DEFAULT 0xff00ff00 /* Green */
44 #define PROP_AATV_RAIN_MODE_DEFAULT GST_RAIN_OFF
45 #define PROP_BRIGHTNESS_TARGET_MIN_DEFAULT 0.3
46 #define PROP_BRIGHTNESS_TARGET_MAX_DEFAULT 0.4
47 #define PROP_RAIN_SPAWN_DEFAULT 0.2
48 #define PROP_RAIN_DELAY_MIN_DEFAULT 0
49 #define PROP_RAIN_DELAY_MAX_DEFAULT 3
50 #define PROP_RAIN_LENGTH_MIN_DEFAULT 4
51 #define PROP_RAIN_LENGTH_MAX_DEFAULT 30
52
53 /* aatv signals and args */
54 enum
55 {
56 LAST_SIGNAL
57 };
58
59 #define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
60
61 enum
62 {
63 PROP_0,
64 PROP_WIDTH,
65 PROP_HEIGHT,
66 PROP_DITHER,
67 PROP_FONT,
68 PROP_CONTRAST,
69 PROP_GAMMA,
70 PROP_RANDOMVAL,
71 PROP_BRIGHTNESS_AUTO,
72 PROP_BRIGHTNESS_ACTUAL,
73 PROP_BRIGHTNESS,
74 PROP_BRIGHTNESS_TARGET_MIN,
75 PROP_BRIGHTNESS_TARGET_MAX,
76 PROP_COLOR_BACKGROUND,
77 PROP_COLOR_TEXT,
78 PROP_COLOR_TEXT_BOLD,
79 PROP_COLOR_TEXT_NORMAL,
80 PROP_COLOR_TEXT_DIM,
81 PROP_COLOR_RAIN,
82 PROP_COLOR_RAIN_BOLD,
83 PROP_COLOR_RAIN_NORMAL,
84 PROP_COLOR_RAIN_DIM,
85 PROP_RAIN_MODE,
86 PROP_RAIN_SPAWN_RATE,
87 PROP_RAIN_DELAY_MIN,
88 PROP_RAIN_DELAY_MAX,
89 PROP_RAIN_LENGTH_MIN,
90 PROP_RAIN_LENGTH_MAX
91 };
92
93 static GstStaticPadTemplate sink_template_tv = GST_STATIC_PAD_TEMPLATE ("sink",
94 GST_PAD_SINK,
95 GST_PAD_ALWAYS,
96 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ I420 }"))
97 );
98 static GstStaticPadTemplate src_template_tv = GST_STATIC_PAD_TEMPLATE ("src",
99 GST_PAD_SRC,
100 GST_PAD_ALWAYS,
101 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGBA }"))
102 );
103
104 static void gst_aatv_set_property (GObject * object, guint prop_id,
105 const GValue * value, GParamSpec * pspec);
106 static void gst_aatv_get_property (GObject * object, guint prop_id,
107 GValue * value, GParamSpec * pspec);
108
109 #define GST_TYPE_AATV_RAIN_MODE (gst_aatv_rain_mode_get_type())
110
111 static GType
gst_aatv_rain_mode_get_type(void)112 gst_aatv_rain_mode_get_type (void)
113 {
114 static GType rain_mode = 0;
115
116 static const GEnumValue rain_modes[] = {
117 {GST_RAIN_OFF, "No Rain", "none"},
118 {GST_RAIN_DOWN, "Rain Down", "down"},
119 {GST_RAIN_UP, "Rain Up", "up"},
120 {GST_RAIN_LEFT, "Rain Left", "left"},
121 {GST_RAIN_RIGHT, "Rain Right", "right"},
122 {0, NULL, NULL},
123 };
124
125 if (!rain_mode) {
126 rain_mode = g_enum_register_static ("GstAATvRainModes", rain_modes);
127 }
128 return rain_mode;
129 }
130
131 #define gst_aatv_parent_class parent_class
132 G_DEFINE_TYPE (GstAATv, gst_aatv, GST_TYPE_VIDEO_FILTER);
133 GST_ELEMENT_REGISTER_DEFINE (aatv, "aatv", GST_RANK_NONE, GST_TYPE_AATV);
134
135 static void
gst_aatv_scale(GstAATv * aatv,guchar * src,guchar * dest,gint sw,gint sh,gint ss,gint dw,gint dh)136 gst_aatv_scale (GstAATv * aatv, guchar * src, guchar * dest,
137 gint sw, gint sh, gint ss, gint dw, gint dh)
138 {
139 gint ypos, yinc, y;
140 gint xpos, xinc, x;
141
142 g_return_if_fail ((dw != 0) && (dh != 0));
143
144 ypos = 0x10000;
145 yinc = (sh << 16) / dh;
146 xinc = (sw << 16) / dw;
147
148 for (y = dh; y; y--) {
149 while (ypos > 0x10000) {
150 ypos -= 0x10000;
151 src += ss;
152 }
153
154 xpos = 0x10000;
155
156 {
157 guchar *destp = dest;
158 guchar *srcp = src;
159
160 for (x = dw; x; x--) {
161 while (xpos >= 0x10000L) {
162 srcp++;
163 xpos -= 0x10000L;
164 }
165 *destp++ = *srcp;
166 xpos += xinc;
167 }
168 }
169 dest += dw;
170 ypos += yinc;
171 }
172 }
173
174 static void
gst_aatv_rain(GstAATv * aatv)175 gst_aatv_rain (GstAATv * aatv)
176 {
177 gint i;
178 gboolean obstructed;
179
180 GstAATvDroplet *raindrops = aatv->raindrops;
181
182 for (i = 0; i < aatv->rain_width; i++) {
183 if (raindrops[i].enabled == FALSE) {
184 if (g_random_double () < aatv->rain_spawn_rate) {
185
186 obstructed = FALSE;
187
188 /* Don't let adjacent lines be enabled at the same time. */
189 if (i > 0)
190 if (raindrops[i - 1].enabled == TRUE)
191 if (raindrops[i - 1].location - raindrops[i - 1].length <
192 aatv->rain_height / 4)
193 obstructed = TRUE;
194
195 if (i < aatv->rain_width)
196 if (raindrops[i + 1].enabled == TRUE)
197 if (raindrops[i + 1].location - raindrops[i + 1].length <
198 aatv->rain_height / 4)
199 obstructed = TRUE;
200
201 if (obstructed == FALSE) {
202 raindrops[i].location = 0;
203 raindrops[i].length =
204 g_random_int_range (aatv->rain_length_min, aatv->rain_length_max);
205 raindrops[i].delay =
206 g_random_int_range (aatv->rain_delay_min, aatv->rain_delay_max);
207 raindrops[i].delay_counter = 0;
208 raindrops[i].enabled = TRUE;
209 }
210 }
211 } else {
212 raindrops[i].delay_counter++;
213 if (raindrops[i].delay_counter > raindrops[i].delay) {
214 raindrops[i].delay_counter = 0;
215 raindrops[i].location++;
216 }
217 if (raindrops[i].location - raindrops[i].length > aatv->rain_height) {
218 raindrops[i].enabled = FALSE;
219 }
220 }
221 }
222 }
223
224 static void
gst_aatv_render(GstAATv * aatv,gint32 * dest)225 gst_aatv_render (GstAATv * aatv, gint32 * dest)
226 {
227 gint x, y;
228 guint font_x, font_y;
229 guint background_pixels = 0;
230 guint foreground_pixels = 0;
231 guint char_index = 0;
232 guint dest_index = 0;
233
234 gchar input_letter, input_glyph, attribute;
235 gboolean rain_pixel;
236
237 GstAATvDroplet *raindrops = aatv->raindrops;
238
239 const guchar *font_base_address = aa_currentfont (aatv->context)->data;
240 guint font_height = aa_currentfont (aatv->context)->height;
241
242 /* loop through the canvas height */
243 for (y = 0; y < aa_scrheight (aatv->context); y++) {
244 /* loop through the height of a character's font */
245 for (font_y = 0; font_y < font_height; font_y++) {
246 /* loop through the canvas width */
247 for (x = 0; x < aa_scrwidth (aatv->context); x++) {
248
249 /* which char are we working on */
250 char_index = x + y * aa_scrwidth (aatv->context);
251 /* lookup what character we need to render */
252 input_letter = aa_text (aatv->context)[char_index];
253 /* check for special attributes like bold or dimmed */
254 attribute = aa_attrs (aatv->context)[char_index];
255 /* look that character up in the font glyph table */
256 input_glyph = font_base_address[input_letter * font_height + font_y];
257
258 /* check if we need to re-color this character for rain effect */
259 rain_pixel = FALSE;
260
261 if (aatv->rain_mode == GST_RAIN_DOWN) {
262 if (raindrops[x].enabled)
263 if (y <= raindrops[x].location)
264 if (y >= raindrops[x].location - raindrops[x].length)
265 rain_pixel = TRUE;
266 } else if (aatv->rain_mode == GST_RAIN_UP) {
267 if (raindrops[x].enabled)
268 if (aatv->rain_height - y <= raindrops[x].location)
269 if (aatv->rain_height - y >=
270 raindrops[x].location - raindrops[x].length)
271 rain_pixel = TRUE;
272 } else if (aatv->rain_mode == GST_RAIN_LEFT) {
273 if (raindrops[y].enabled)
274 if (x <= raindrops[y].location)
275 if (x >= raindrops[y].location - raindrops[y].length)
276 rain_pixel = TRUE;
277 } else if (aatv->rain_mode == GST_RAIN_RIGHT) {
278 if (raindrops[y].enabled)
279 if (aatv->rain_height - x <= raindrops[y].location)
280 if (aatv->rain_height - x >=
281 raindrops[y].location - raindrops[y].length)
282 rain_pixel = TRUE;
283 }
284 /* loop through the width of a character's font (always 8 pixels wide) */
285 for (font_x = 0; font_x < 8; font_x++) {
286 guint32 *pixel_argb;
287 if (CHECK_BIT (input_glyph, font_x)) {
288 if (attribute == AA_DIM) {
289 if (rain_pixel)
290 pixel_argb = &aatv->color_rain_dim;
291 else
292 pixel_argb = &aatv->color_text_dim;
293 } else if (attribute == AA_BOLD) {
294 if (rain_pixel)
295 pixel_argb = &aatv->color_rain_bold;
296 else
297 pixel_argb = &aatv->color_text_bold;
298 } else {
299 if (rain_pixel)
300 pixel_argb = &aatv->color_rain_normal;
301 else
302 pixel_argb = &aatv->color_text_normal;
303 }
304 foreground_pixels++;
305 } else {
306 pixel_argb = &aatv->color_background;
307 background_pixels++;
308 }
309 dest[dest_index++] = *pixel_argb;
310 }
311 }
312 }
313 }
314
315 aatv->lit_percentage =
316 0.2 * (aatv->lit_percentage) +
317 0.8 * (float) foreground_pixels / background_pixels;
318
319 if (aatv->auto_brightness) {
320 if (aatv->lit_percentage > aatv->brightness_target_max)
321 if (aatv->ascii_parms.bright > -254)
322 aatv->ascii_parms.bright--;
323 if (aatv->lit_percentage < aatv->brightness_target_min)
324 if (aatv->ascii_parms.bright < 254)
325 aatv->ascii_parms.bright++;
326 }
327 }
328
329 static GstFlowReturn
gst_aatv_transform_frame(GstVideoFilter * vfilter,GstVideoFrame * in_frame,GstVideoFrame * out_frame)330 gst_aatv_transform_frame (GstVideoFilter * vfilter, GstVideoFrame * in_frame,
331 GstVideoFrame * out_frame)
332 {
333 GstAATv *aatv = GST_AATV (vfilter);
334
335 if (aatv->rain_mode != GST_RAIN_OFF)
336 gst_aatv_rain (aatv);
337
338 GST_OBJECT_LOCK (aatv);
339
340 gst_aatv_scale (aatv, GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0), /* src */
341 aa_image (aatv->context), /* dest */
342 GST_VIDEO_FRAME_WIDTH (in_frame), /* sw */
343 GST_VIDEO_FRAME_HEIGHT (in_frame), /* sh */
344 GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0), /* ss */
345 aa_imgwidth (aatv->context), /* dw */
346 aa_imgheight (aatv->context)); /* dh */
347
348 aa_render (aatv->context, &aatv->ascii_parms, 0, 0,
349 aa_imgwidth (aatv->context), aa_imgheight (aatv->context));
350 gst_aatv_render (aatv, GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0));
351
352 GST_OBJECT_UNLOCK (aatv);
353
354 return GST_FLOW_OK;
355 }
356
357
358 #define GST_TYPE_AADITHER (gst_aatv_dither_get_type())
359 static GType
gst_aatv_dither_get_type(void)360 gst_aatv_dither_get_type (void)
361 {
362 static GType dither_type = 0;
363
364 if (!dither_type) {
365 GEnumValue *ditherers;
366 gint n_ditherers;
367 gint i;
368
369 for (n_ditherers = 0; aa_dithernames[n_ditherers]; n_ditherers++) {
370 /* count number of ditherers */
371 }
372
373 ditherers = g_new0 (GEnumValue, n_ditherers + 1);
374
375 for (i = 0; i < n_ditherers; i++) {
376 ditherers[i].value = i;
377 ditherers[i].value_name = g_strdup (aa_dithernames[i]);
378 ditherers[i].value_nick =
379 g_strdelimit (g_strdup (aa_dithernames[i]), " _", '-');
380 }
381
382 ditherers[i].value = 0;
383 ditherers[i].value_name = NULL;
384 ditherers[i].value_nick = NULL;
385
386 dither_type = g_enum_register_static ("GstAATvDitherers", ditherers);
387 }
388 return dither_type;
389 }
390
391 #define GST_TYPE_AAFONT (gst_aatv_font_get_type())
392 static GType
gst_aatv_font_get_type(void)393 gst_aatv_font_get_type (void)
394 {
395 static GType font_type = 0;
396
397 if (!font_type) {
398 GEnumValue *fonts;
399 gint n_fonts;
400 gint i;
401
402 for (n_fonts = 0; aa_fonts[n_fonts]; n_fonts++) {
403 /* count number of fonts */
404 }
405
406 fonts = g_new0 (GEnumValue, n_fonts + 1);
407
408 for (i = 0; i < n_fonts; i++) {
409 fonts[i].value = i;
410 fonts[i].value_name = g_strdup (aa_fonts[i]->shortname);
411 fonts[i].value_nick =
412 g_strdelimit (g_strdup (aa_fonts[i]->name), " _", '-');
413 }
414 fonts[i].value = 0;
415 fonts[i].value_name = NULL;
416 fonts[i].value_nick = NULL;
417
418 font_type = g_enum_register_static ("GstAATvFonts", fonts);
419 }
420 return font_type;
421 }
422
423 /* use a custom transform_caps */
424 static GstCaps *
gst_aatv_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)425 gst_aatv_transform_caps (GstBaseTransform * trans, GstPadDirection direction,
426 GstCaps * caps, GstCaps * filter)
427 {
428 GstCaps *ret;
429 GstAATv *aatv = GST_AATV (trans);
430 GValue formats = G_VALUE_INIT;
431 GValue value = G_VALUE_INIT;
432 GValue src_width = G_VALUE_INIT;
433 GValue src_height = G_VALUE_INIT;
434
435 if (direction == GST_PAD_SINK) {
436
437 ret = gst_caps_copy (caps);
438
439 g_value_init (&src_width, G_TYPE_INT);
440 g_value_init (&src_height, G_TYPE_INT);
441 /* calculate output resolution from canvas size and font size */
442
443 g_value_set_int (&src_width, aa_defparams.width * 8);
444 g_value_set_int (&src_height,
445 aa_defparams.height * aa_currentfont (aatv->context)->height);
446
447 gst_caps_set_value (ret, "width", &src_width);
448 gst_caps_set_value (ret, "height", &src_height);
449 /* force RGBA output format */
450 g_value_init (&formats, GST_TYPE_LIST);
451 g_value_init (&value, G_TYPE_STRING);
452 g_value_set_string (&value, "RGBA");
453 gst_value_list_append_value (&formats, &value);
454
455 gst_caps_set_value (ret, "format", &formats);
456
457 } else {
458 ret = gst_static_pad_template_get_caps (&sink_template_tv);
459 }
460
461 return ret;
462 }
463
464
465 static void
gst_aatv_finalize(GObject * object)466 gst_aatv_finalize (GObject * object)
467 {
468 GstAATv *aatv = GST_AATV (object);
469 free (aatv->raindrops);
470 if (aatv->context != NULL)
471 aa_close (aatv->context);
472 G_OBJECT_CLASS (parent_class)->finalize (object);
473 }
474
475
476 static void
gst_aatv_class_init(GstAATvClass * klass)477 gst_aatv_class_init (GstAATvClass * klass)
478 {
479 GObjectClass *gobject_class;
480 GstElementClass *gstelement_class;
481 GstVideoFilterClass *videofilter_class;
482 GstBaseTransformClass *transform_class;
483
484 gobject_class = (GObjectClass *) klass;
485 gstelement_class = (GstElementClass *) klass;
486 videofilter_class = (GstVideoFilterClass *) klass;
487 transform_class = (GstBaseTransformClass *) klass;
488
489 gobject_class->set_property = gst_aatv_set_property;
490 gobject_class->get_property = gst_aatv_get_property;
491 gobject_class->finalize = gst_aatv_finalize;
492
493 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WIDTH,
494 g_param_spec_int ("width", "width", "Width of the ASCII canvas", 0,
495 G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
496 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HEIGHT,
497 g_param_spec_int ("height", "height", "Height of the ASCII canvas", 0,
498 G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
499 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DITHER,
500 g_param_spec_enum ("dither", "dither",
501 "Add noise to more closely approximate gray levels.",
502 GST_TYPE_AADITHER, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
503 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT,
504 g_param_spec_enum ("font", "font", "AAlib Font", GST_TYPE_AAFONT, 0,
505 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
506 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR_TEXT,
507 g_param_spec_uint ("color-text", "color-text",
508 "Automatically sets color-test-bold, color-text-normal, and color-text-dim with progressively dimmer values (big-endian ARGB).",
509 0, G_MAXUINT32, 0,
510 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
511 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR_TEXT_BOLD,
512 g_param_spec_uint ("color-text-bold", "color-text-bold",
513 "Sets the brightest color to use for foreground ASCII text (big-endian ARGB).",
514 0, G_MAXUINT32, 0,
515 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
516 g_object_class_install_property (G_OBJECT_CLASS (klass),
517 PROP_COLOR_TEXT_NORMAL, g_param_spec_uint ("color-text-normal",
518 "color-text-normal",
519 "Sets the normal brightness color to use for foreground ASCII text (big-endian ARGB).",
520 0, G_MAXUINT32, 0,
521 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
522 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR_TEXT_DIM,
523 g_param_spec_uint ("color-text-dim", "color-text-dim",
524 "Sets the dimmest brightness color to use for foreground ASCII text (big-endian ARGB).",
525 0, G_MAXUINT32, 0,
526 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
527 g_object_class_install_property (G_OBJECT_CLASS (klass),
528 PROP_COLOR_BACKGROUND, g_param_spec_uint ("color-background",
529 "color-background",
530 "Color to use as the background for the ASCII text (big-endian ARGB).",
531 0, G_MAXUINT32, 0,
532 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
533 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BRIGHTNESS,
534 g_param_spec_int ("brightness", "brightness", "Brightness", -255,
535 255, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
536 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BRIGHTNESS_AUTO,
537 g_param_spec_boolean ("brightness-auto", "brightness-auto",
538 "Automatically adjust brightness based on the previous frame's foreground pixel fill percentage",
539 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
540 g_object_class_install_property (G_OBJECT_CLASS (klass),
541 PROP_BRIGHTNESS_ACTUAL, g_param_spec_float ("brightness-actual",
542 "brightness-actual",
543 "Actual calculated foreground pixel fill percentage", 0.0, 1.0, 0.0,
544 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
545 g_object_class_install_property (G_OBJECT_CLASS (klass),
546 PROP_BRIGHTNESS_TARGET_MIN, g_param_spec_float ("brightness-min",
547 "brightness-min",
548 "Minimum target foreground pixel fill percentage for automatic brightness control",
549 0.0, 1.0, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
550 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RAIN_SPAWN_RATE,
551 g_param_spec_float ("rain-spawn-rate", "rain-spawn-rate",
552 "Percentage chance for a raindrop to spawn", 0.0, 1.0, 0.0,
553 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
554 g_object_class_install_property (G_OBJECT_CLASS (klass),
555 PROP_BRIGHTNESS_TARGET_MAX, g_param_spec_float ("brightness-max",
556 "brightness-max",
557 "Maximum target foreground pixel fill percentage for automatic brightness control",
558 0.0, 1.0, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
559 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONTRAST,
560 g_param_spec_int ("contrast", "contrast", "Contrast", 0, G_MAXUINT8,
561 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
562 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_GAMMA,
563 g_param_spec_float ("gamma", "gamma", "Gamma correction", 0.0, 5.0, 1.0,
564 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
565 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RANDOMVAL,
566 g_param_spec_int ("randomval", "randomval",
567 "Adds a random value in the range (-randomval/2,ranomval/2) to each pixel during rendering",
568 0, 255, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
569 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RAIN_DELAY_MIN,
570 g_param_spec_int ("rain-delay-min", "rain-delay-min",
571 "Minimum frame delay between rain motion", 0, G_MAXINT, 0,
572 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
573 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RAIN_DELAY_MAX,
574 g_param_spec_int ("rain-delay-max", "rain-delay-max",
575 "Maximum frame delay between rain motion", 0, G_MAXINT, 0,
576 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
577 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RAIN_LENGTH_MIN,
578 g_param_spec_int ("rain-length-min", "rain-length-min",
579 "Minimum length of a rain", 0, G_MAXINT, 0,
580 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
581 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RAIN_LENGTH_MAX,
582 g_param_spec_int ("rain-length-max", "rain-length-max",
583 "Maximum length of a rain", 0, G_MAXINT, 0,
584 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
585 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RAIN_MODE,
586 g_param_spec_enum ("rain-mode", "rain-mode",
587 "Set the direction of raindrops", GST_TYPE_AATV_RAIN_MODE, 0,
588 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
589 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR_RAIN,
590 g_param_spec_uint ("color-rain", "color-rain",
591 "Automatically sets color-rain-bold, color-rain-normal, and color-rain-dim with progressively dimmer values (big-endian ARGB).",
592 0, G_MAXUINT32, 0,
593 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
594 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR_RAIN_BOLD,
595 g_param_spec_uint ("color-rain-bold", "color-rain-bold",
596 "Sets the brightest color to use for foreground ASCII text rain overlays (big-endian ARGB).",
597 0, G_MAXUINT32, 0,
598 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
599 g_object_class_install_property (G_OBJECT_CLASS (klass),
600 PROP_COLOR_RAIN_NORMAL, g_param_spec_uint ("color-rain-normal",
601 "color-rain-normal",
602 "Sets the normal brightness color to use for foreground ASCII text rain overlays (big-endian ARGB).",
603 0, G_MAXUINT32, 0,
604 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
605 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR_RAIN_DIM,
606 g_param_spec_uint ("color-rain-dim", "color-rain-dim",
607 "Sets the dimmest brightness color to use for foreground ASCII text rain overlays (big-endian ARGB).",
608 0, G_MAXUINT32, 0,
609 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
610
611 gst_element_class_add_static_pad_template (gstelement_class,
612 &sink_template_tv);
613 gst_element_class_add_static_pad_template (gstelement_class,
614 &src_template_tv);
615
616 gst_element_class_set_static_metadata (gstelement_class,
617 "aaTV effect", "Filter/Effect/Video",
618 "ASCII art effect", "Eric Marks <bigmarkslp@gmail.com>");
619
620 transform_class->transform_caps = GST_DEBUG_FUNCPTR (gst_aatv_transform_caps);
621 videofilter_class->transform_frame =
622 GST_DEBUG_FUNCPTR (gst_aatv_transform_frame);
623
624 gst_type_mark_as_plugin_api (GST_TYPE_AATV_RAIN_MODE, 0);
625 gst_type_mark_as_plugin_api (GST_TYPE_AADITHER, 0);
626 gst_type_mark_as_plugin_api (GST_TYPE_AAFONT, 0);
627 }
628
629 static void
gst_aatv_rain_init(GstAATv * aatv)630 gst_aatv_rain_init (GstAATv * aatv)
631 {
632 switch (aatv->rain_mode) {
633 case GST_RAIN_DOWN:
634 case GST_RAIN_UP:
635 aatv->rain_width = aa_defparams.width;
636 aatv->rain_height = aa_defparams.height;
637 break;
638 case GST_RAIN_LEFT:
639 case GST_RAIN_RIGHT:
640 aatv->rain_width = aa_defparams.height;
641 aatv->rain_height = aa_defparams.width;
642 break;
643 case GST_RAIN_OFF:
644 aatv->rain_width = 0;
645 aatv->rain_height = 0;
646 }
647
648 if (aatv->context != NULL)
649 aa_close (aatv->context);
650 aatv->context = aa_init (&mem_d, &aa_defparams, NULL);
651 aa_setfont (aatv->context, aa_fonts[0]);
652
653 aatv->raindrops =
654 realloc (aatv->raindrops,
655 aatv->rain_width * sizeof (struct _GstAATvDroplet));
656 for (gint i = 0; i < aatv->rain_width; i++)
657 aatv->raindrops[i].enabled = FALSE;
658
659 }
660
661 static guint32
gst_aatv_set_color(guint32 input_color,guint8 dim)662 gst_aatv_set_color (guint32 input_color, guint8 dim)
663 {
664 guint8 a = ((input_color >> 24) & 0xff);
665 guint8 b = ((input_color >> 16) & 0xff) >> dim;
666 guint8 g = ((input_color >> 8) & 0xff) >> dim;
667 guint8 r = ((input_color >> 0) & 0xff) >> dim;
668
669 return ((a << 24) | (b << 16) | (g << 8) | (r << 0));
670 }
671
672 static void
gst_aatv_set_color_rain(GstAATv * aatv,guint input_color)673 gst_aatv_set_color_rain (GstAATv * aatv, guint input_color)
674 {
675 aatv->color_rain = input_color;
676 aatv->color_rain_bold = gst_aatv_set_color (input_color, 0);
677 aatv->color_rain_normal = gst_aatv_set_color (aatv->color_rain_bold, 1);
678 aatv->color_rain_dim = gst_aatv_set_color (aatv->color_rain_normal, 1);
679 }
680
681 static void
gst_aatv_set_color_text(GstAATv * aatv,guint input_color)682 gst_aatv_set_color_text (GstAATv * aatv, guint input_color)
683 {
684 aatv->color_text = input_color;
685 aatv->color_text_bold = gst_aatv_set_color (input_color, 0);
686 aatv->color_text_normal = gst_aatv_set_color (aatv->color_text_bold, 1);
687 aatv->color_text_dim = gst_aatv_set_color (aatv->color_text_normal, 1);
688 }
689
690 static void
gst_aatv_init(GstAATv * aatv)691 gst_aatv_init (GstAATv * aatv)
692 {
693 aa_defparams.width = 80;
694 aa_defparams.height = 24;
695
696 aatv->ascii_parms.bright = 0;
697 aatv->ascii_parms.contrast = 0;
698 aatv->ascii_parms.gamma = 1.0;
699 aatv->ascii_parms.dither = 0;
700 aatv->ascii_parms.inversion = 0;
701 aatv->ascii_parms.randomval = 0;
702
703 aatv->color_background =
704 gst_aatv_set_color (PROP_AATV_COLOR_BACKGROUND_DEFAULT, 0);
705 gst_aatv_set_color_rain (aatv, PROP_AATV_COLOR_RAIN_DEFAULT);
706 gst_aatv_set_color_text (aatv, PROP_AATV_COLOR_TEXT_DEFAULT);
707
708 aatv->rain_mode = PROP_AATV_RAIN_MODE_DEFAULT;
709
710 gst_aatv_rain_init (aatv);
711
712 aatv->rain_spawn_rate = PROP_RAIN_SPAWN_DEFAULT;
713
714 aatv->auto_brightness = TRUE;
715 aatv->brightness_target_min = PROP_BRIGHTNESS_TARGET_MIN_DEFAULT;
716 aatv->brightness_target_max = PROP_BRIGHTNESS_TARGET_MAX_DEFAULT;
717 aatv->lit_percentage =
718 (PROP_BRIGHTNESS_TARGET_MIN_DEFAULT +
719 PROP_BRIGHTNESS_TARGET_MAX_DEFAULT) / 2;
720
721 aatv->rain_length_min = PROP_RAIN_LENGTH_MIN_DEFAULT;
722 aatv->rain_length_max = PROP_RAIN_LENGTH_MAX_DEFAULT;
723
724 aatv->rain_delay_min = PROP_RAIN_DELAY_MIN_DEFAULT;
725 aatv->rain_delay_max = PROP_RAIN_DELAY_MAX_DEFAULT;
726 }
727
728 static void
gst_aatv_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)729 gst_aatv_set_property (GObject * object, guint prop_id, const GValue * value,
730 GParamSpec * pspec)
731 {
732 GstAATv *aatv = GST_AATV (object);
733
734 switch (prop_id) {
735 case PROP_WIDTH:{
736 aa_defparams.width = g_value_get_int (value);
737 /* recalculate output resolution based on new width */
738 gst_aatv_rain_init (aatv);
739 gst_pad_mark_reconfigure (GST_BASE_TRANSFORM_SRC_PAD (object));
740 break;
741 }
742 case PROP_HEIGHT:{
743 aa_defparams.height = g_value_get_int (value);
744 /* recalculate output resolution based on new height */
745 gst_aatv_rain_init (aatv);
746 gst_pad_mark_reconfigure (GST_BASE_TRANSFORM_SRC_PAD (object));
747 break;
748 }
749 case PROP_DITHER:{
750 aatv->ascii_parms.dither = g_value_get_enum (value);
751 break;
752 }
753 case PROP_FONT:{
754 aa_setfont (aatv->context, aa_fonts[g_value_get_enum (value)]);
755 /* recalculate output resolution based on new font */
756 gst_pad_mark_reconfigure (GST_BASE_TRANSFORM_SRC_PAD (object));
757 break;
758 }
759 case PROP_BRIGHTNESS:{
760 aatv->ascii_parms.bright = g_value_get_int (value);
761 break;
762 }
763 case PROP_CONTRAST:{
764 aatv->ascii_parms.contrast = g_value_get_int (value);
765 break;
766 }
767 case PROP_GAMMA:{
768 aatv->ascii_parms.gamma = g_value_get_float (value);
769 break;
770 }
771 case PROP_BRIGHTNESS_TARGET_MIN:{
772 if (g_value_get_float (value) <= aatv->brightness_target_max)
773 aatv->brightness_target_min = g_value_get_float (value);
774 break;
775 }
776 case PROP_BRIGHTNESS_TARGET_MAX:{
777 if (g_value_get_float (value) >= aatv->brightness_target_min)
778 aatv->brightness_target_max = g_value_get_float (value);
779 break;
780 }
781 case PROP_RAIN_SPAWN_RATE:{
782 aatv->rain_spawn_rate = g_value_get_float (value);
783 break;
784 }
785 case PROP_COLOR_TEXT:{
786 aatv->color_text = g_value_get_uint (value);
787 gst_aatv_set_color_text (aatv, aatv->color_text);
788 break;
789 }
790 case PROP_COLOR_TEXT_BOLD:{
791 aatv->color_text_bold = gst_aatv_set_color (g_value_get_uint (value), 0);
792 break;
793 }
794 case PROP_COLOR_TEXT_NORMAL:{
795 aatv->color_text_normal =
796 gst_aatv_set_color (g_value_get_uint (value), 0);
797 break;
798 }
799 case PROP_COLOR_TEXT_DIM:{
800 aatv->color_text_dim = gst_aatv_set_color (g_value_get_uint (value), 0);
801 break;
802 }
803 case PROP_COLOR_BACKGROUND:{
804 aatv->color_background = gst_aatv_set_color (g_value_get_uint (value), 0);
805 break;
806 }
807 case PROP_COLOR_RAIN:{
808 aatv->color_rain = g_value_get_uint (value);
809 gst_aatv_set_color_rain (aatv, aatv->color_rain);
810 break;
811 }
812 case PROP_COLOR_RAIN_BOLD:{
813 aatv->color_rain_bold = gst_aatv_set_color (g_value_get_uint (value), 0);
814 break;
815 }
816 case PROP_COLOR_RAIN_NORMAL:{
817 aatv->color_rain_normal =
818 gst_aatv_set_color (g_value_get_uint (value), 0);
819 break;
820 }
821 case PROP_COLOR_RAIN_DIM:{
822 aatv->color_rain_dim = gst_aatv_set_color (g_value_get_uint (value), 0);
823 break;
824 }
825 case PROP_BRIGHTNESS_AUTO:{
826 aatv->auto_brightness = g_value_get_boolean (value);
827 break;
828 }
829 case PROP_RANDOMVAL:{
830 aatv->ascii_parms.randomval = g_value_get_int (value);
831 break;
832 }
833 case PROP_RAIN_DELAY_MIN:{
834 if (g_value_get_int (value) <= aatv->rain_delay_max)
835 aatv->rain_delay_min = g_value_get_int (value);
836 break;
837 }
838 case PROP_RAIN_DELAY_MAX:{
839 if (g_value_get_int (value) >= aatv->rain_delay_min)
840 aatv->rain_delay_max = g_value_get_int (value);
841 break;
842 }
843 case PROP_RAIN_LENGTH_MIN:{
844 if (g_value_get_int (value) <= aatv->rain_length_max)
845 aatv->rain_length_min = g_value_get_int (value);
846 break;
847 }
848 case PROP_RAIN_LENGTH_MAX:{
849 if (g_value_get_int (value) >= aatv->rain_length_min)
850 aatv->rain_length_max = g_value_get_int (value);
851 break;
852 }
853 case PROP_RAIN_MODE:{
854 aatv->rain_mode = g_value_get_enum (value);
855 gst_aatv_rain_init (aatv);
856 break;
857 }
858 default:
859 break;
860 }
861 }
862
863 static void
gst_aatv_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)864 gst_aatv_get_property (GObject * object, guint prop_id, GValue * value,
865 GParamSpec * pspec)
866 {
867 GstAATv *aatv = GST_AATV (object);
868
869 switch (prop_id) {
870 case PROP_BRIGHTNESS_ACTUAL:{
871 g_value_set_float (value, aatv->lit_percentage);
872 break;
873 }
874 case PROP_WIDTH:{
875 g_value_set_int (value, aa_defparams.width);
876 break;
877 }
878 case PROP_HEIGHT:{
879 g_value_set_int (value, aa_defparams.height);
880
881 break;
882 }
883 case PROP_DITHER:{
884 g_value_set_enum (value, aatv->ascii_parms.dither);
885 break;
886 }
887 case PROP_FONT:{
888 g_value_set_enum (value, aatv->ascii_parms.dither);
889 break;
890 }
891 case PROP_BRIGHTNESS:{
892 g_value_set_int (value, aatv->ascii_parms.bright);
893 break;
894 }
895 case PROP_BRIGHTNESS_AUTO:{
896 g_value_set_boolean (value, aatv->auto_brightness);
897 break;
898 }
899 case PROP_CONTRAST:{
900 g_value_set_int (value, aatv->ascii_parms.contrast);
901 break;
902 }
903 case PROP_GAMMA:{
904 g_value_set_float (value, aatv->ascii_parms.gamma);
905 break;
906 }
907 case PROP_RAIN_SPAWN_RATE:{
908 g_value_set_float (value, aatv->rain_spawn_rate);
909 break;
910 }
911 case PROP_BRIGHTNESS_TARGET_MIN:{
912 g_value_set_float (value, aatv->brightness_target_min);
913 break;
914 }
915 case PROP_BRIGHTNESS_TARGET_MAX:{
916 g_value_set_float (value, aatv->brightness_target_max);
917 break;
918 }
919 case PROP_COLOR_TEXT:{
920 g_value_set_uint (value, aatv->color_text);
921 break;
922 }
923 case PROP_COLOR_TEXT_BOLD:{
924 g_value_set_uint (value, aatv->color_text_bold);
925 break;
926 }
927 case PROP_COLOR_TEXT_NORMAL:{
928 g_value_set_uint (value, aatv->color_text_normal);
929 break;
930 }
931 case PROP_COLOR_TEXT_DIM:{
932 g_value_set_uint (value, aatv->color_text_dim);
933 break;
934 }
935 case PROP_COLOR_BACKGROUND:{
936 g_value_set_uint (value, aatv->color_background);
937 break;
938 }
939 case PROP_COLOR_RAIN:{
940 g_value_set_uint (value, aatv->color_rain);
941 break;
942 }
943 case PROP_COLOR_RAIN_BOLD:{
944 g_value_set_uint (value, aatv->color_rain_bold);
945 break;
946 }
947 case PROP_COLOR_RAIN_NORMAL:{
948 g_value_set_uint (value, aatv->color_rain_normal);
949 break;
950 }
951 case PROP_COLOR_RAIN_DIM:{
952 g_value_set_uint (value, aatv->color_rain_dim);
953 break;
954 }
955 case PROP_RANDOMVAL:{
956 g_value_set_int (value, aatv->ascii_parms.randomval);
957 break;
958 }
959 case PROP_RAIN_MODE:{
960 g_value_set_enum (value, aatv->rain_mode);
961 break;
962 }
963 case PROP_RAIN_DELAY_MIN:{
964 g_value_set_int (value, aatv->rain_delay_min);
965 break;
966 }
967 case PROP_RAIN_DELAY_MAX:{
968 g_value_set_int (value, aatv->rain_delay_max);
969 break;
970 }
971 case PROP_RAIN_LENGTH_MIN:{
972 g_value_set_int (value, aatv->rain_length_min);
973 break;
974 }
975 case PROP_RAIN_LENGTH_MAX:{
976 g_value_set_int (value, aatv->rain_length_max);
977 break;
978 }
979 default:{
980 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
981 break;
982 }
983 }
984 }
985