guint8 * dest, const guint8 * src);
static void colorspace_convert_lookup_fastpath (ColorspaceConvert * convert);
static void colorspace_convert_lookup_getput (ColorspaceConvert * convert);
+static void colorspace_dither_none (ColorspaceConvert * convert, int j);
+static void colorspace_dither_verterr (ColorspaceConvert * convert, int j);
+static void colorspace_dither_halftone (ColorspaceConvert * convert, int j);
ColorspaceConvert *
convert->height = height;
convert->width = width;
convert->convert = colorspace_convert_generic;
+ convert->dither16 = colorspace_dither_none;
if (gst_video_format_get_component_depth (to_format, 0) > 8 ||
gst_video_format_get_component_depth (from_format, 0) > 8) {
convert->tmpline = g_malloc (sizeof (guint8) * (width + 8) * 4);
convert->tmpline16 = g_malloc (sizeof (guint16) * (width + 8) * 4);
+ convert->errline = g_malloc (sizeof (guint16) * width * 4);
if (to_format == GST_VIDEO_FORMAT_RGB8_PALETTED) {
/* build poor man's palette, taken from ffmpegcolorspace */
g_free (convert->palette);
g_free (convert->tmpline);
g_free (convert->tmpline16);
+ g_free (convert->errline);
g_free (convert);
}
}
void
+colorspace_convert_set_dither (ColorspaceConvert * convert, int type)
+{
+ switch (type) {
+ case 0:
+ default:
+ convert->dither16 = colorspace_dither_none;
+ break;
+ case 1:
+ convert->dither16 = colorspace_dither_verterr;
+ break;
+ case 2:
+ convert->dither16 = colorspace_dither_halftone;
+ break;
+ }
+}
+
+void
colorspace_convert_set_palette (ColorspaceConvert * convert,
const guint32 * palette)
{
for (j = 0; j < convert->height; j++) {
convert->getline16 (convert, convert->tmpline16, src, j);
convert->matrix16 (convert);
+ convert->dither16 (convert, j);
convert->putline16 (convert, dest, convert->tmpline16, j);
}
} else {
}
}
+static void
+colorspace_dither_none (ColorspaceConvert * convert, int j)
+{
+}
+
+static void
+colorspace_dither_verterr (ColorspaceConvert * convert, int j)
+{
+ int i;
+ guint16 *tmpline = convert->tmpline16;
+ guint16 *errline = convert->errline;
+
+ for (i = 0; i < 4 * convert->width; i++) {
+ tmpline[i] += errline[i];
+ errline[i] = tmpline[i] & 0xff;
+ }
+}
+
+static void
+colorspace_dither_halftone (ColorspaceConvert * convert, int j)
+{
+ int i;
+ guint16 *tmpline = convert->tmpline16;
+ static guint16 halftone[8][8] = {
+ {0, 128, 32, 160, 8, 136, 40, 168},
+ {192, 64, 224, 96, 200, 72, 232, 104},
+ {48, 176, 16, 144, 56, 184, 24, 152},
+ {240, 112, 208, 80, 248, 120, 216, 88},
+ {12, 240, 44, 172, 4, 132, 36, 164},
+ {204, 76, 236, 108, 196, 68, 228, 100},
+ {60, 188, 28, 156, 52, 180, 20, 148},
+ {252, 142, 220, 92, 244, 116, 212, 84}
+ };
+
+ for (i = 0; i < convert->width * 4; i++) {
+ tmpline[i] += halftone[(i >> 2) & 7][j & 7];
+ }
+}
/* Fast paths */
COLOR_SPEC_YUV_BT709
} ColorSpaceColorSpec;
+typedef enum {
+ DITHER_NONE,
+ DITHER_VERTERR,
+ DITHER_HALFTONE
+} ColorSpaceDitherMethod;
+
struct _ColorspaceComponent {
int offset;
int stride;
gint width, height;
gboolean interlaced;
gboolean use_16bit;
+ gboolean dither;
GstVideoFormat from_format;
ColorSpaceColorSpec from_spec;
guint8 *tmpline;
guint16 *tmpline16;
+ guint16 *errline;
int dest_offset[4];
int dest_stride[4];
void (*getline16) (ColorspaceConvert *convert, guint16 *dest, const guint8 *src, int j);
void (*putline16) (ColorspaceConvert *convert, guint8 *dest, const guint16 *src, int j);
void (*matrix16) (ColorspaceConvert *convert);
+ void (*dither16) (ColorspaceConvert *convert, int j);
};
ColorspaceConvert * colorspace_convert_new (GstVideoFormat to_format,
ColorSpaceColorSpec from_spec, GstVideoFormat from_format,
ColorSpaceColorSpec to_spec, int width, int height);
+void colorspace_convert_set_dither (ColorspaceConvert * convert, int type);
void colorspace_convert_set_interlaced (ColorspaceConvert *convert,
gboolean interlaced);
void colorspace_convert_set_palette (ColorspaceConvert *convert,
#define GST_CAT_DEFAULT colorspace_debug
GST_DEBUG_CATEGORY (colorspace_performance);
+enum
+{
+ PROP_0,
+ PROP_DITHER
+};
+
#define CSP_VIDEO_CAPS \
"video/x-raw-yuv, width = "GST_VIDEO_SIZE_RANGE" , " \
"height="GST_VIDEO_SIZE_RANGE",framerate="GST_VIDEO_FPS_RANGE"," \
GType gst_csp_get_type (void);
+static void gst_csp_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec);
+static void gst_csp_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec);
+static void gst_csp_dispose (GObject * object);
+
static gboolean gst_csp_set_caps (GstBaseTransform * btrans,
GstCaps * incaps, GstCaps * outcaps);
static gboolean gst_csp_get_unit_size (GstBaseTransform * btrans,
static GQuark _QRAWYUV; /* "video/x-raw-yuv" */
static GQuark _QALPHAMASK; /* "alpha_mask" */
+
+static GType
+dither_method_get_type (void)
+{
+ static GType gtype = 0;
+
+ if (gtype == 0) {
+ static const GEnumValue values[] = {
+ {DITHER_NONE, "No dithering (default)", "none"},
+ {DITHER_VERTERR, "Vertical error propogation", "verterr"},
+ {DITHER_HALFTONE, "Half-tone", "halftone"},
+ {0, NULL, NULL}
+ };
+
+ gtype = g_enum_register_static ("GstColorspaceDitherMethod", values);
+ }
+ return gtype;
+}
+
/* copies the given caps */
static GstCaps *
gst_csp_caps_remove_format_info (GstCaps * caps)
_QALPHAMASK = g_quark_from_string ("alpha_mask");
}
+void
+gst_csp_dispose (GObject * object)
+{
+ GstCsp *csp;
+
+ g_return_if_fail (GST_IS_CSP (object));
+ csp = GST_CSP (object);
+
+ /* clean up as possible. may be called multiple times */
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
static void
gst_csp_finalize (GObject * obj)
{
GstBaseTransformClass *gstbasetransform_class =
(GstBaseTransformClass *) klass;
+ gobject_class->set_property = gst_csp_set_property;
+ gobject_class->get_property = gst_csp_get_property;
+ gobject_class->dispose = gst_csp_dispose;
gobject_class->finalize = gst_csp_finalize;
gstbasetransform_class->transform_caps =
gstbasetransform_class->transform = GST_DEBUG_FUNCPTR (gst_csp_transform);
gstbasetransform_class->passthrough_on_same_caps = TRUE;
+
+ g_object_class_install_property (gobject_class, PROP_DITHER,
+ g_param_spec_enum ("dither", "Dither", "Apply dithering while converting",
+ dither_method_get_type (), DITHER_NONE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
}
static void
space->to_format = GST_VIDEO_FORMAT_UNKNOWN;
}
+void
+gst_csp_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstCsp *csp;
+
+ g_return_if_fail (GST_IS_CSP (object));
+ csp = GST_CSP (object);
+
+ switch (property_id) {
+ case PROP_DITHER:
+ csp->dither = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+void
+gst_csp_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstCsp *csp;
+
+ g_return_if_fail (GST_IS_CSP (object));
+ csp = GST_CSP (object);
+
+ switch (property_id) {
+ case PROP_DITHER:
+ g_value_set_enum (value, csp->dither);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
static gboolean
gst_csp_get_unit_size (GstBaseTransform * btrans, GstCaps * caps, guint * size)
{
space->to_format == GST_VIDEO_FORMAT_UNKNOWN))
goto unknown_format;
+ if (space->dither) {
+ colorspace_convert_set_dither (space->convert, 1);
+ } else {
+ colorspace_convert_set_dither (space->convert, 0);
+ }
+
colorspace_convert_convert (space->convert, GST_BUFFER_DATA (outbuf),
GST_BUFFER_DATA (inbuf));
ColorSpaceColorSpec to_spec;
ColorspaceConvert *convert;
+ gboolean dither;
};
struct _GstCspClass