• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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