2 * Copyright (C) <2014> Wim Taymans <wim.taymans@gmail.com>
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.
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.
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.
25 #include "video-dither.h"
26 #include "video-orc.h"
29 * SECTION:gstvideodither
30 * @title: GstVideoDither
31 * @short_description: Utility object for dithering and quantizing lines of video
33 * GstVideoDither provides implementations of several dithering algorithms
34 * that can be applied to lines of video pixels to quantize and dither them.
37 struct _GstVideoDither
39 GstVideoDitherMethod method;
40 GstVideoDitherFlags flags;
41 GstVideoFormat format;
47 void (*func) (GstVideoDither * dither, gpointer pixels, guint x, guint y,
58 dither_none_u8_mask (GstVideoDither * dither, gpointer pixels, guint x, guint y,
63 video_orc_dither_none_4u8_mask (p + (x * 4), dither->orc_mask32, width);
67 dither_none_u16_mask (GstVideoDither * dither, gpointer pixels, guint x,
72 video_orc_dither_none_4u16_mask (p + (x * 4), dither->orc_mask64, width);
76 dither_verterr_u8 (GstVideoDither * dither, gpointer pixels, guint x, guint y,
80 guint16 *e = dither->errors;
83 memset (e + (x * 4), 0, width * 8);
85 video_orc_dither_verterr_4u8_mask (p + (x * 4), e + (x * 4),
86 dither->orc_mask64, width);
90 dither_verterr_u16 (GstVideoDither * dither, gpointer pixels, guint x, guint y,
94 guint16 *e = dither->errors;
97 memset (e + (x * 4), 0, width * 8);
101 guint16 *m = dither->mask;
104 end = (width + x) * 4;
105 for (i = x * 4; i < end; i++) {
108 /* take new error and store */
110 /* quantize and store */
112 p[i] = MIN (v, 65535);
118 dither_floyd_steinberg_u8 (GstVideoDither * dither, gpointer pixels, guint x,
119 guint y, guint width)
122 guint16 *e = dither->errors;
125 memset (e + (x * 4), 0, (width + 1) * 8);
127 /* add and multiply errors from previous line */
128 video_orc_dither_fs_muladd_u8 (e + x * 4, width * 4);
132 guint16 *m = dither->mask, mp;
135 end = (width + x) * 4;
137 for (i = x * 4; i < end; i++) {
139 v = p[i] + ((7 * e[i] + e[i + 4]) >> 4);
140 /* take new error and store */
142 /* quantize and store */
148 video_orc_dither_fs_add_4u8 (p, e + x * 4, e + (x + 1) * 4,
149 dither->orc_mask64, width);
154 dither_floyd_steinberg_u16 (GstVideoDither * dither, gpointer pixels, guint x,
155 guint y, guint width)
158 guint16 *e = dither->errors;
161 memset (e + (x * 4), 0, (width + 1) * 8);
165 guint16 *m = dither->mask, mp;
168 end = (width + x) * 4;
169 for (i = x * 4; i < end; i++) {
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 */
175 /* quantize and store */
177 p[i] = MIN (v, 65535);
183 dither_sierra_lite_u8 (GstVideoDither * dither, gpointer pixels, guint x,
184 guint y, guint width)
187 guint16 *e = dither->errors;
189 guint16 *m = dither->mask, mp;
193 memset (e + (x * 4), 0, (width + 4) * 8);
195 end = (width + x) * 4;
196 for (i = x; i < end; i++) {
198 /* apply previous errors to pixel */
199 v = p[i] + ((2 * e[i] + e[i + 8] + e[i + 12]) >> 2);
200 /* store new error */
202 /* quantize and store */
209 dither_sierra_lite_u16 (GstVideoDither * dither, gpointer pixels, guint x,
210 guint y, guint width)
213 guint16 *e = dither->errors;
215 guint16 *m = dither->mask, mp;
219 memset (e + (x * 4), 0, (width + 4) * 8);
221 end = (width + x) * 4;
222 for (i = x; i < end; i++) {
224 /* apply previous errors to pixel */
225 v = p[i] + ((2 * e[i] + e[i + 8] + e[i + 12]) >> 2);
226 /* store new error */
228 /* quantize and store */
230 p[i] = MIN (v & ~mp, 65535);
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}
254 dither_ordered_u8 (GstVideoDither * dither, gpointer pixels, guint x, guint y,
258 guint8 *c = (guint8 *) dither->errors + ((y & 15) * width + (x & 15)) * 4;
260 video_orc_dither_ordered_u8 (p, c, width * 4);
264 dither_ordered_u8_mask (GstVideoDither * dither, gpointer pixels, guint x,
265 guint y, guint width)
268 guint16 *c = (guint16 *) dither->errors + ((y & 15) * width + (x & 15)) * 4;
270 video_orc_dither_ordered_4u8_mask (p, c, dither->orc_mask64, width);
274 dither_ordered_u16_mask (GstVideoDither * dither, gpointer pixels, guint x,
275 guint y, guint width)
278 guint16 *c = (guint16 *) dither->errors + ((y & 15) * width + (x & 15)) * 4;
280 video_orc_dither_ordered_4u16_mask (p, c, dither->orc_mask64, width);
284 alloc_errors (GstVideoDither * dither, guint lines)
288 width = dither->width;
289 n_comp = dither->n_comp;
291 dither->errors = g_malloc0 (sizeof (guint16) * (width + 8) * n_comp * lines);
295 setup_bayer (GstVideoDither * dither)
297 guint i, j, k, width, n_comp, errdepth;
300 width = dither->width;
301 shift = dither->shift;
302 n_comp = dither->n_comp;
304 if (dither->depth == 8) {
305 if (dither->flags & GST_VIDEO_DITHER_FLAG_QUANTIZE) {
306 dither->func = dither_ordered_u8_mask;
309 dither->func = dither_ordered_u8;
313 dither->func = dither_ordered_u16_mask;
317 alloc_errors (dither, 16);
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];
326 v = v >> (8 - shift[k]);
327 p[n_comp * j + k] = v;
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];
338 v = v >> (8 - shift[k]);
339 p[n_comp * j + k] = v;
347 count_power (guint v)
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
365 * Make a new dither object for dithering lines of @format using the
366 * algorithm described by @method.
368 * Each component will be quantized to a multiple of @quantizer. Better
369 * performance is achieved when @quantizer is a power of 2.
371 * @width is the width of the lines that this ditherer will handle.
373 * Returns: a new #GstVideoDither
376 gst_video_dither_new (GstVideoDitherMethod method, GstVideoDitherFlags flags,
377 GstVideoFormat format, guint quantizer[GST_VIDEO_MAX_COMPONENTS],
380 GstVideoDither *dither;
383 dither = g_slice_new0 (GstVideoDither);
384 dither->method = method;
385 dither->flags = flags;
386 dither->format = format;
387 dither->width = width;
392 case GST_VIDEO_FORMAT_AYUV:
393 case GST_VIDEO_FORMAT_ARGB:
396 case GST_VIDEO_FORMAT_AYUV64:
397 case GST_VIDEO_FORMAT_ARGB64:
401 g_slice_free (GstVideoDither, dither);
402 g_return_val_if_reached (NULL);
406 for (i = 0; i < 4; i++) {
407 /* FIXME, only power of 2 quantizers */
408 guint q = quantizer[(i + 3) & 3];
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],
415 (dither->orc_mask64 << 16) | GUINT16_FROM_BE (dither->mask[i]);
416 dither->orc_mask32 = (dither->orc_mask32 << 8) | (guint8) dither->mask[i];
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);
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;
429 dither->func = dither_none_u16_mask;
433 case GST_VIDEO_DITHER_VERTERR:
434 alloc_errors (dither, 1);
435 if (dither->depth == 8) {
436 dither->func = dither_verterr_u8;
438 dither->func = dither_verterr_u16;
440 case GST_VIDEO_DITHER_FLOYD_STEINBERG:
441 alloc_errors (dither, 1);
442 if (dither->depth == 8) {
443 dither->func = dither_floyd_steinberg_u8;
445 dither->func = dither_floyd_steinberg_u16;
447 case GST_VIDEO_DITHER_SIERRA_LITE:
448 alloc_errors (dither, 1);
449 if (dither->depth == 8) {
450 dither->func = dither_sierra_lite_u8;
452 dither->func = dither_sierra_lite_u16;
454 case GST_VIDEO_DITHER_BAYER:
455 setup_bayer (dither);
462 * gst_video_dither_free:
463 * @dither: a #GstVideoDither
468 gst_video_dither_free (GstVideoDither * dither)
470 g_return_if_fail (dither != NULL);
472 g_free (dither->errors);
473 g_slice_free (GstVideoDither, dither);
477 * gst_video_dither_line:
478 * @dither: a #GstVideoDither
479 * @line: pointer to the pixels of the line
484 * Dither @width pixels starting from offset @x in @line using @dither.
486 * @y is the line number of @line in the output image.
489 gst_video_dither_line (GstVideoDither * dither, gpointer line, guint x, guint y,
492 g_return_if_fail (dither != NULL);
493 g_return_if_fail (x + width <= dither->width);
496 dither->func (dither, line, x, y, width);