1 /* GStreamer
2  * Copyright (C) <2014> Wim Taymans <wim.taymans@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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include <string.h>
24 
25 #include "video-dither.h"
26 #include "video-orc.h"
27 
28 /**
29  * SECTION:gstvideodither
30  * @title: GstVideoDither
31  * @short_description: Utility object for dithering and quantizing lines of video
32  *
33  * GstVideoDither provides implementations of several dithering algorithms
34  * that can be applied to lines of video pixels to quantize and dither them.
35  *
36  */
37 struct _GstVideoDither
38 {
39   GstVideoDitherMethod method;
40   GstVideoDitherFlags flags;
41   GstVideoFormat format;
42   guint width;
43 
44   guint depth;
45   guint n_comp;
46 
47   void (*func) (GstVideoDither * dither, gpointer pixels, guint x, guint y,
48       guint width);
49   guint8 shift[4];
50   guint16 mask[4];
51   guint64 orc_mask64;
52   guint32 orc_mask32;
53 
54   gpointer errors;
55 };
56 
57 static void
dither_none_u8_mask(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)58 dither_none_u8_mask (GstVideoDither * dither, gpointer pixels, guint x, guint y,
59     guint width)
60 {
61   guint8 *p = pixels;
62 
63   video_orc_dither_none_4u8_mask (p + (x * 4), dither->orc_mask32, width);
64 }
65 
66 static void
dither_none_u16_mask(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)67 dither_none_u16_mask (GstVideoDither * dither, gpointer pixels, guint x,
68     guint y, guint width)
69 {
70   guint16 *p = pixels;
71 
72   video_orc_dither_none_4u16_mask (p + (x * 4), dither->orc_mask64, width);
73 }
74 
75 static void
dither_verterr_u8(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)76 dither_verterr_u8 (GstVideoDither * dither, gpointer pixels, guint x, guint y,
77     guint width)
78 {
79   guint8 *p = pixels;
80   guint16 *e = dither->errors;
81 
82   if (y == 0)
83     memset (e + (x * 4), 0, width * 8);
84 
85   video_orc_dither_verterr_4u8_mask (p + (x * 4), e + (x * 4),
86       dither->orc_mask64, width);
87 }
88 
89 static void
dither_verterr_u16(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)90 dither_verterr_u16 (GstVideoDither * dither, gpointer pixels, guint x, guint y,
91     guint width)
92 {
93   guint16 *p = pixels;
94   guint16 *e = dither->errors;
95 
96   if (y == 0)
97     memset (e + (x * 4), 0, width * 8);
98 
99   {
100     gint i, end;
101     guint16 *m = dither->mask;
102     guint32 v, mp;
103 
104     end = (width + x) * 4;
105     for (i = x * 4; i < end; i++) {
106       mp = m[i & 3];
107       v = p[i] + e[i];
108       /* take new error and store */
109       e[i] = v & mp;
110       /* quantize and store */
111       v &= ~mp;
112       p[i] = MIN (v, 65535);
113     }
114   }
115 }
116 
117 static void
dither_floyd_steinberg_u8(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)118 dither_floyd_steinberg_u8 (GstVideoDither * dither, gpointer pixels, guint x,
119     guint y, guint width)
120 {
121   guint8 *p = pixels;
122   guint16 *e = dither->errors;
123 
124   if (y == 0)
125     memset (e + (x * 4), 0, (width + 1) * 8);
126 
127   /* add and multiply errors from previous line */
128   video_orc_dither_fs_muladd_u8 (e + x * 4, width * 4);
129 #if 1
130   {
131     gint i, end;
132     guint16 *m = dither->mask, mp;
133     guint16 v;
134 
135     end = (width + x) * 4;
136 
137     for (i = x * 4; i < end; i++) {
138       mp = m[i & 3];
139       v = p[i] + ((7 * e[i] + e[i + 4]) >> 4);
140       /* take new error and store */
141       e[i + 4] = v & mp;
142       /* quantize and store */
143       v &= ~mp;
144       p[i] = MIN (v, 255);
145     }
146   }
147 #else
148   video_orc_dither_fs_add_4u8 (p, e + x * 4, e + (x + 1) * 4,
149       dither->orc_mask64, width);
150 #endif
151 }
152 
153 static void
dither_floyd_steinberg_u16(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)154 dither_floyd_steinberg_u16 (GstVideoDither * dither, gpointer pixels, guint x,
155     guint y, guint width)
156 {
157   guint16 *p = pixels;
158   guint16 *e = dither->errors;
159 
160   if (y == 0)
161     memset (e + (x * 4), 0, (width + 1) * 8);
162 
163   {
164     gint i, end;
165     guint16 *m = dither->mask, mp;
166     guint32 v;
167 
168     end = (width + x) * 4;
169     for (i = x * 4; i < end; i++) {
170       mp = m[i & 3];
171       /* apply previous errors to pixel */
172       v = p[i] + ((7 * e[i] + e[i + 4] + 5 * e[i + 8] + 3 * e[i + 12]) >> 4);
173       /* take new error and store */
174       e[i + 4] = v & mp;
175       /* quantize and store */
176       v &= ~mp;
177       p[i] = MIN (v, 65535);
178     }
179   }
180 }
181 
182 static void
dither_sierra_lite_u8(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)183 dither_sierra_lite_u8 (GstVideoDither * dither, gpointer pixels, guint x,
184     guint y, guint width)
185 {
186   guint8 *p = pixels;
187   guint16 *e = dither->errors;
188   gint i, end;
189   guint16 *m = dither->mask, mp;
190   guint16 v;
191 
192   if (y == 0)
193     memset (e + (x * 4), 0, (width + 4) * 8);
194 
195   end = (width + x) * 4;
196   for (i = x; i < end; i++) {
197     mp = m[i & 3];
198     /* apply previous errors to pixel */
199     v = p[i] + ((2 * e[i] + e[i + 8] + e[i + 12]) >> 2);
200     /* store new error */
201     e[i + 4] = v & mp;
202     /* quantize and store */
203     v &= ~mp;
204     p[i] = MIN (v, 255);
205   }
206 }
207 
208 static void
dither_sierra_lite_u16(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)209 dither_sierra_lite_u16 (GstVideoDither * dither, gpointer pixels, guint x,
210     guint y, guint width)
211 {
212   guint16 *p = pixels;
213   guint16 *e = dither->errors;
214   gint i, end;
215   guint16 *m = dither->mask, mp;
216   guint32 v;
217 
218   if (y == 0)
219     memset (e + (x * 4), 0, (width + 4) * 8);
220 
221   end = (width + x) * 4;
222   for (i = x; i < end; i++) {
223     mp = m[i & 3];
224     /* apply previous errors to pixel */
225     v = p[i] + ((2 * e[i] + e[i + 8] + e[i + 12]) >> 2);
226     /* store new error */
227     e[i + 4] = v & mp;
228     /* quantize and store */
229     v &= ~mp;
230     p[i] = MIN (v & ~mp, 65535);
231   }
232 }
233 
234 static const guint16 bayer_map[16][16] = {
235   {0, 128, 32, 160, 8, 136, 40, 168, 2, 130, 34, 162, 10, 138, 42, 170},
236   {192, 64, 224, 96, 200, 72, 232, 104, 194, 66, 226, 98, 202, 74, 234, 106},
237   {48, 176, 16, 144, 56, 184, 24, 152, 50, 178, 18, 146, 58, 186, 26, 154},
238   {240, 112, 208, 80, 248, 120, 216, 88, 242, 114, 210, 82, 250, 122, 218, 90},
239   {12, 240, 44, 172, 4, 132, 36, 164, 14, 242, 46, 174, 6, 134, 38, 166},
240   {204, 76, 236, 108, 196, 68, 228, 100, 206, 78, 238, 110, 198, 70, 230, 102},
241   {60, 188, 28, 156, 52, 180, 20, 148, 62, 190, 30, 158, 54, 182, 22, 150},
242   {252, 142, 220, 92, 244, 116, 212, 84, 254, 144, 222, 94, 246, 118, 214, 86},
243   {3, 131, 35, 163, 11, 139, 43, 171, 1, 129, 33, 161, 9, 137, 41, 169},
244   {195, 67, 227, 99, 203, 75, 235, 107, 193, 65, 225, 97, 201, 73, 233, 105},
245   {51, 179, 19, 147, 59, 187, 27, 155, 49, 177, 17, 145, 57, 185, 25, 153},
246   {243, 115, 211, 83, 251, 123, 219, 91, 241, 113, 209, 81, 249, 121, 217, 89},
247   {15, 243, 47, 175, 7, 135, 39, 167, 13, 241, 45, 173, 5, 133, 37, 165},
248   {207, 79, 239, 111, 199, 71, 231, 103, 205, 77, 237, 109, 197, 69, 229, 101},
249   {63, 191, 31, 159, 55, 183, 23, 151, 61, 189, 29, 157, 53, 181, 21, 149},
250   {255, 145, 223, 95, 247, 119, 215, 87, 253, 143, 221, 93, 245, 117, 213, 85}
251 };
252 
253 static void
dither_ordered_u8(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)254 dither_ordered_u8 (GstVideoDither * dither, gpointer pixels, guint x, guint y,
255     guint width)
256 {
257   guint8 *p = pixels;
258   guint8 *c = (guint8 *) dither->errors + ((y & 15) * width + (x & 15)) * 4;
259 
260   video_orc_dither_ordered_u8 (p, c, width * 4);
261 }
262 
263 static void
dither_ordered_u8_mask(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)264 dither_ordered_u8_mask (GstVideoDither * dither, gpointer pixels, guint x,
265     guint y, guint width)
266 {
267   guint8 *p = pixels;
268   guint16 *c = (guint16 *) dither->errors + ((y & 15) * width + (x & 15)) * 4;
269 
270   video_orc_dither_ordered_4u8_mask (p, c, dither->orc_mask64, width);
271 }
272 
273 static void
dither_ordered_u16_mask(GstVideoDither * dither,gpointer pixels,guint x,guint y,guint width)274 dither_ordered_u16_mask (GstVideoDither * dither, gpointer pixels, guint x,
275     guint y, guint width)
276 {
277   guint16 *p = pixels;
278   guint16 *c = (guint16 *) dither->errors + ((y & 15) * width + (x & 15)) * 4;
279 
280   video_orc_dither_ordered_4u16_mask (p, c, dither->orc_mask64, width);
281 }
282 
283 static void
alloc_errors(GstVideoDither * dither,guint lines)284 alloc_errors (GstVideoDither * dither, guint lines)
285 {
286   guint width, n_comp;
287 
288   width = dither->width;
289   n_comp = dither->n_comp;
290 
291   dither->errors = g_malloc0 (sizeof (guint16) * (width + 8) * n_comp * lines);
292 }
293 
294 static void
setup_bayer(GstVideoDither * dither)295 setup_bayer (GstVideoDither * dither)
296 {
297   guint i, j, k, width, n_comp, errdepth;
298   guint8 *shift;
299 
300   width = dither->width;
301   shift = dither->shift;
302   n_comp = dither->n_comp;
303 
304   if (dither->depth == 8) {
305     if (dither->flags & GST_VIDEO_DITHER_FLAG_QUANTIZE) {
306       dither->func = dither_ordered_u8_mask;
307       errdepth = 16;
308     } else {
309       dither->func = dither_ordered_u8;
310       errdepth = 8;
311     }
312   } else {
313     dither->func = dither_ordered_u16_mask;
314     errdepth = 16;
315   }
316 
317   alloc_errors (dither, 16);
318 
319   if (errdepth == 8) {
320     for (i = 0; i < 16; i++) {
321       guint8 *p = (guint8 *) dither->errors + (n_comp * width * i), v;
322       for (j = 0; j < width; j++) {
323         for (k = 0; k < n_comp; k++) {
324           v = bayer_map[i & 15][j & 15];
325           if (shift[k] < 8)
326             v = v >> (8 - shift[k]);
327           p[n_comp * j + k] = v;
328         }
329       }
330     }
331   } else {
332     for (i = 0; i < 16; i++) {
333       guint16 *p = (guint16 *) dither->errors + (n_comp * width * i), v;
334       for (j = 0; j < width; j++) {
335         for (k = 0; k < n_comp; k++) {
336           v = bayer_map[i & 15][j & 15];
337           if (shift[k] < 8)
338             v = v >> (8 - shift[k]);
339           p[n_comp * j + k] = v;
340         }
341       }
342     }
343   }
344 }
345 
346 static gint
count_power(guint v)347 count_power (guint v)
348 {
349   gint res = 0;
350   while (v > 1) {
351     res++;
352     v >>= 1;
353   }
354   return res;
355 }
356 
357 /**
358  * gst_video_dither_new: (skip)
359  * @method: a #GstVideoDitherMethod
360  * @flags: a #GstVideoDitherFlags
361  * @format: a #GstVideoFormat
362  * @quantizer: quantizer
363  * @width: the width of the lines
364  *
365  * Make a new dither object for dithering lines of @format using the
366  * algorithm described by @method.
367  *
368  * Each component will be quantized to a multiple of @quantizer. Better
369  * performance is achived when @quantizer is a power of 2.
370  *
371  * @width is the width of the lines that this ditherer will handle.
372  *
373  * Returns: a new #GstVideoDither
374  */
375 GstVideoDither *
gst_video_dither_new(GstVideoDitherMethod method,GstVideoDitherFlags flags,GstVideoFormat format,guint quantizer[GST_VIDEO_MAX_COMPONENTS],guint width)376 gst_video_dither_new (GstVideoDitherMethod method, GstVideoDitherFlags flags,
377     GstVideoFormat format, guint quantizer[GST_VIDEO_MAX_COMPONENTS],
378     guint width)
379 {
380   GstVideoDither *dither;
381   gint i;
382 
383   dither = g_slice_new0 (GstVideoDither);
384   dither->method = method;
385   dither->flags = flags;
386   dither->format = format;
387   dither->width = width;
388 
389   dither->n_comp = 4;
390 
391   switch (format) {
392     case GST_VIDEO_FORMAT_AYUV:
393     case GST_VIDEO_FORMAT_ARGB:
394       dither->depth = 8;
395       break;
396     case GST_VIDEO_FORMAT_AYUV64:
397     case GST_VIDEO_FORMAT_ARGB64:
398       dither->depth = 16;
399       break;
400     default:
401       g_slice_free (GstVideoDither, dither);
402       g_return_val_if_reached (NULL);
403       break;
404   }
405 
406   for (i = 0; i < 4; i++) {
407     /* FIXME, only power of 2 quantizers */
408     guint q = quantizer[(i + 3) & 3];
409 
410     dither->shift[i] = count_power (q);
411     dither->mask[i] = (1 << dither->shift[i]) - 1;
412     GST_DEBUG ("%d: quant %d shift %d mask %08x", i, q, dither->shift[i],
413         dither->mask[i]);
414     dither->orc_mask64 =
415         (dither->orc_mask64 << 16) | GUINT16_FROM_BE (dither->mask[i]);
416     dither->orc_mask32 = (dither->orc_mask32 << 8) | (guint8) dither->mask[i];
417   }
418   dither->orc_mask64 = GUINT64_FROM_BE (dither->orc_mask64);
419   dither->orc_mask32 = GUINT32_FROM_BE (dither->orc_mask32);
420   GST_DEBUG ("mask64 %08" G_GINT64_MODIFIER "x", (guint64) dither->orc_mask64);
421   GST_DEBUG ("mask32 %08x", dither->orc_mask32);
422 
423   switch (method) {
424     case GST_VIDEO_DITHER_NONE:
425       if (dither->flags & GST_VIDEO_DITHER_FLAG_QUANTIZE)
426         if (dither->depth == 8)
427           dither->func = dither_none_u8_mask;
428         else
429           dither->func = dither_none_u16_mask;
430       else
431         dither->func = NULL;
432       break;
433     case GST_VIDEO_DITHER_VERTERR:
434       alloc_errors (dither, 1);
435       if (dither->depth == 8) {
436         dither->func = dither_verterr_u8;
437       } else
438         dither->func = dither_verterr_u16;
439       break;
440     case GST_VIDEO_DITHER_FLOYD_STEINBERG:
441       alloc_errors (dither, 1);
442       if (dither->depth == 8) {
443         dither->func = dither_floyd_steinberg_u8;
444       } else
445         dither->func = dither_floyd_steinberg_u16;
446       break;
447     case GST_VIDEO_DITHER_SIERRA_LITE:
448       alloc_errors (dither, 1);
449       if (dither->depth == 8) {
450         dither->func = dither_sierra_lite_u8;
451       } else
452         dither->func = dither_sierra_lite_u16;
453       break;
454     case GST_VIDEO_DITHER_BAYER:
455       setup_bayer (dither);
456       break;
457   }
458   return dither;
459 }
460 
461 /**
462  * gst_video_dither_free:
463  * @dither: a #GstVideoDither
464  *
465  * Free @dither
466  */
467 void
gst_video_dither_free(GstVideoDither * dither)468 gst_video_dither_free (GstVideoDither * dither)
469 {
470   g_return_if_fail (dither != NULL);
471 
472   g_free (dither->errors);
473   g_slice_free (GstVideoDither, dither);
474 }
475 
476 /**
477  * gst_video_dither_line:
478  * @dither: a #GstVideoDither
479  * @line: pointer to the pixels of the line
480  * @x: x coordinate
481  * @y: y coordinate
482  * @width: the width
483  *
484  * Dither @width pixels starting from offset @x in @line using @dither.
485  *
486  * @y is the line number of @line in the output image.
487  */
488 void
gst_video_dither_line(GstVideoDither * dither,gpointer line,guint x,guint y,guint width)489 gst_video_dither_line (GstVideoDither * dither, gpointer line, guint x, guint y,
490     guint width)
491 {
492   g_return_if_fail (dither != NULL);
493   g_return_if_fail (x + width <= dither->width);
494 
495   if (dither->func)
496     dither->func (dither, line, x, y, width);
497 }
498