1 /* GStreamer
2  * Copyright (C) <2010> Filippo Argiolas <filippo.argiolas@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 /**
21  * SECTION:element-coloreffects
22  * @title: coloreffects
23  *
24  * Map colors of the video input to a lookup table
25  *
26  * ## Example launch line
27  * |[
28  * gst-launch-1.0 -v videotestsrc ! coloreffects preset=heat ! videoconvert !
29  *     autovideosink
30  * ]| This pipeline shows the effect of coloreffects on a test stream.
31  *
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <gst/video/video.h>
39 #include "gstcoloreffects.h"
40 
41 #define DEFAULT_PROP_PRESET GST_COLOR_EFFECTS_PRESET_NONE
42 
43 GST_DEBUG_CATEGORY_STATIC (coloreffects_debug);
44 #define GST_CAT_DEFAULT (coloreffects_debug)
45 
46 enum
47 {
48   PROP_0,
49   PROP_PRESET
50 };
51 
52 #define gst_color_effects_parent_class parent_class
53 G_DEFINE_TYPE (GstColorEffects, gst_color_effects, GST_TYPE_VIDEO_FILTER);
54 
55 #define CAPS_STR GST_VIDEO_CAPS_MAKE ("{ " \
56     "ARGB, BGRA, ABGR, RGBA, xRGB, BGRx, xBGR, RGBx, RGB, BGR, AYUV }")
57 
58 static GstStaticPadTemplate gst_color_effects_src_template =
59 GST_STATIC_PAD_TEMPLATE ("src",
60     GST_PAD_SRC,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS (CAPS_STR)
63     );
64 
65 static GstStaticPadTemplate gst_color_effects_sink_template =
66 GST_STATIC_PAD_TEMPLATE ("sink",
67     GST_PAD_SINK,
68     GST_PAD_ALWAYS,
69     GST_STATIC_CAPS (CAPS_STR)
70     );
71 
72 #define GST_TYPE_COLOR_EFFECTS_PRESET (gst_color_effects_preset_get_type())
73 static GType
gst_color_effects_preset_get_type(void)74 gst_color_effects_preset_get_type (void)
75 {
76   static GType preset_type = 0;
77 
78   static const GEnumValue presets[] = {
79     {GST_COLOR_EFFECTS_PRESET_NONE, "Do nothing preset", "none"},
80     {GST_COLOR_EFFECTS_PRESET_HEAT, "Fake heat camera toning", "heat"},
81     {GST_COLOR_EFFECTS_PRESET_SEPIA, "Sepia toning", "sepia"},
82     {GST_COLOR_EFFECTS_PRESET_XRAY, "Invert and slightly shade to blue",
83         "xray"},
84     {GST_COLOR_EFFECTS_PRESET_XPRO, "Cross processing toning", "xpro"},
85     {GST_COLOR_EFFECTS_PRESET_YELLOWBLUE,
86         "Yellow foreground Blue background color filter", "yellowblue"},
87     {0, NULL, NULL},
88   };
89 
90   if (!preset_type) {
91     preset_type = g_enum_register_static ("GstColorEffectsPreset", presets);
92   }
93   return preset_type;
94 }
95 
96 /*
97  * Currently hardcoded tables, in the future may be nice to load them
98  * from a file or just leave these as default presets and add a
99  * property to load a custom one from file
100  */
101 
102 /*
103  * Tables were produced roughly this way:
104  *  - take a sample image
105  *  - open with the gimp and play with the Curves tool until you find the desired effect
106  *  - save the curve
107  *  - create a new 256x1 rgb image and paint a black to white gradient on it
108  *  - apply the curve
109  *  - save as "C source"
110  *  - the array in the saved file is the lookup table
111  */
112 /* in the future the latter steps would be save to png/bmp/anything else and feed it to the plugin */
113 
114 /* 256 * 3 RGB data */
115 static const guint8 sepia_table[768] =
116     "\0\0\0\0\0\0\0\0\0\0\1\0\1\1\0\1\1\0\1\1\1\2\1\1\2\2\1\3\2\1\3\2\1\3\2\1"
117     "\4\3\2\4\3\2\4\3\2\6\4\2\6\4\2\6\4\2\7\5\2\7\5\3\11\6\3\11\6\3\12\7\3\13"
118     "\10\3\15\10\4\16\11\4\17\11\4\21\12\4\22\13\4\22\13\5\23\14\5\24\15\5\26"
119     "\16\6\31\20\6\31\21\6\32\22\7\34\22\7\35\23\7\40\24\10\40\26\10!\26\11#\30"
120     "\11&\31\12&\32\12'\34\13)\34\13*\37\13,\37\13-\40\14.\"\15" "0\"\15"
121     "2#\17" "" "3&\17" "4&\17" "5'\20" "8(\21"
122     "9)\21:*\23<,\23=-\23A.\24A0\25B0\25C2\26D3"
123     "\30H4\30H7\31K7\32K8\32L9\33M:\34P<\34Q=\35S>\37T?\37UA\40VB!XC!ZD#\\F#^"
124     "G#^J$`J&bK'bM'eM(fO)gP)iQ*kS,mT-mU-nV.oX/rY0sZ2u]2v]3w^3x`4za5{c7|c8~e8\177"
125     "f9\200i:\203i<\204j<\206k=\207m>\210n?\211o?\213qA\214rC\215sC\217uD\220"
126     "vD\221wF\223xG\224zH\225{J\227|K\230~K\231\177L\232\200M\234\202O\235\203"
127     "P\236\204Q\240\206Q\241\207S\242\210T\243\211U\245\213V\246\214X\247\215"
128     "Y\250\217Y\252\220Z\253\221\\\254\223]\254\224^\255\225`\257\227a\260\230"
129     "b\261\231c\262\232e\264\234e\265\235f\266\236g\267\240i\267\241i\272\242"
130     "k\273\243m\274\245n\274\246o\276\247q\277\250r\300\252s\301\253u\302\254"
131     "v\304\255w\305\257x\306\257z\306\261{\307\262|\310\264~\310\265\177\313\266"
132     "\200\314\267\202\315\267\203\316\272\204\317\273\206\317\274\207\320\276"
133     "\210\322\277\211\323\277\213\324\301\214\325\302\215\326\304\217\326\305"
134     "\220\327\306\221\327\307\223\331\310\224\333\311\225\334\311\227\334\313"
135     "\227\335\315\231\335\316\231\337\317\234\340\320\235\341\320\235\341\323"
136     "\240\342\324\241\343\324\242\343\326\243\345\327\245\345\330\245\346\331"
137     "\250\346\333\252\347\334\253\351\335\254\351\335\255\351\337\257\352\340"
138     "\260\353\341\260\354\342\262\355\343\264\355\344\265\355\345\266\356\346"
139     "\266\356\347\272\357\350\273\360\351\274\360\351\276\361\352\277\361\353"
140     "\300\362\353\301\362\354\302\362\355\304\362\356\305\364\357\305\364\357"
141     "\310\364\360\311\365\361\313\365\361\314\366\362\315\366\362\316\366\363"
142     "\316\367\364\320\367\364\320\367\365\324\367\365\324\370\366\326\370\366"
143     "\327\371\366\330\371\367\331\371\367\333\371\370\333\372\370\336\372\370"
144     "\336\372\371\340\373\371\341\373\372\342\373\372\343\374\372\344\374\373"
145     "\344\374\373\347\374\374\350\375\374\351\375\374\351\375\374\352\375\375"
146     "\352\376\375\353\376\376\355\376\376\356\376\376\357\377\377\357";
147 
148 static const guint8 heat_table[768] =
149     "\0\0\0\0\0\0\0\1\0\0\1\0\0\1\1\0\2\1\0\2\1\1\2\1\1\2\2\1\2\2\1\3\2\1\3\3"
150     "\1\3\3\1\4\3\1\4\4\1\5\4\1\5\5\2\5\6\2\6\6\2\6\7\2\6\7\2\7\7\2\7\11\2\10"
151     "\11\2\10\12\3\11\13\3\11\13\3\11\14\3\12\15\3\12\17\3\13\17\3\14\20\3\14"
152     "\22\4\15\23\4\16\24\4\16\26\4\16\27\4\17\31\4\20\34\4\21\34\5\21\40\5\22"
153     "\40\5\22$\5\23$\5\25&\6\25(\6\26-\6\26-\6\27" "0\6\31" "2\7\31"
154     "5\7\32;\7\34"
155     ";\7\34?\10\35C\10\36G\10\37L\10\40V\11!V\11\"[\11$a\11&l\12&l\12'r\12(~\13"
156     "*~\13,\204\14,\213\14.\221\14/\227\14" "1\236\15" "2\244\15" "4\252\15"
157     "5\260" "\16" "7\267\16"
158     "8\275\17:\302\17;\310\17=\323\20?\323\21@\330\21D\335\21D"
159     "\342\22E\346\22I\353\23I\356\23K\362\24M\365\24N\370\25P\372\26R\374\26T"
160     "\376\26V\377\27X\377\27Z\377\30\\\376\31`\376\31`\375\32b\373\32d\371\33"
161     "f\366\34j\363\34j\360\35l\354\36n\350\36r\344\37r\337\40t\333\40w\326!y\321"
162     "\"|\314#~\307$\201\301$\204\267%\207\267&\212\261'\214\254(\217\247(\222"
163     "\241)\226\234*\231\227+\234\222,\237\216-\242\211.\245\205/\251\2010\254"
164     "}1\257z2\262w3\266t4\271p5\274m6\277j7\302f8\305c9\310`:\314\\;\317Y<\321"
165     "V>\324S?\327P@\332LA\335IB\337FC\342CE\344@F\347=G\351;I\3538I\3558M\357"
166     "3P\3610S\363.V\365+Y\366)\\\370'`\371%d\372#g\373\"l\374\40p\374\37t\374"
167     "\35t\375\34}\376\33\202\376\32\202\375\31\213\375\30\220\375\27\225\375\27"
168     "\232\373\26\237\372\25\244\371\24\251\370\23\256\367\23\262\367\22\267\364"
169     "\21\274\362\20\300\361\20\305\357\17\311\355\16\311\353\16\322\351\15\326"
170     "\346\15\332\346\14\336\344\14\341\337\13\341\335\13\350\332\12\353\330\11"
171     "\356\330\11\360\322\10\362\320\10\364\320\10\364\312\7\366\307\7\366\304"
172     "\7\367\302\6\367\277\6\370\274\5\367\271\5\367\271\5\367\263\4\365\260\4"
173     "\364\255\4\363\253\3\362\250\3\361\245\3\360\242\3\357\240\3\357\235\2\355"
174     "\232\2\355\227\2\354\225\2\353\221\1\353\216\1\353\216\1\353\213\1\353\204"
175     "\1\353\201\1\354}\1\354y\0\354v\0\355r\0\355n\0\355j\0\356f\0\356b\0\357"
176     "_\0\357[\0\357W\0\357S\0\360O\0\360O\0\361K\0\361C\0\362@\0\363<\0\3638\0"
177     "\3648\0\3641\0\365.\0\366+\0\366'\0\367'\0\370!\0\370\36\0\370\33\0\371\30"
178     "\0\371\26\0\373\26\0\373\23\0\374\15\0\374\13\0\375\10\0\375\5\0\376\3\0";
179 
180 static const guint8 xray_table[768] =
181     "\377\377\377\377\377\377\376\376\376\375\375\376\374\375\375\373\374\375"
182     "\372\374\374\371\374\374\370\373\373\366\373\372\366\372\372\365\372\371"
183     "\363\371\371\363\371\370\362\370\370\360\370\367\360\367\366\357\367\365"
184     "\356\366\365\355\366\364\353\365\363\353\365\363\352\364\362\351\363\362"
185     "\347\363\361\346\362\361\345\362\361\344\362\360\343\361\357\343\361\356"
186     "\342\360\356\341\360\356\340\357\355\336\356\354\336\356\354\335\355\353"
187     "\334\355\353\333\355\352\331\354\351\331\353\351\330\353\350\327\353\350"
188     "\325\352\347\325\351\347\324\350\346\323\350\345\322\347\344\321\347\344"
189     "\320\347\344\317\346\343\316\346\342\315\345\341\314\344\341\313\344\340"
190     "\312\344\340\311\343\337\310\342\337\307\342\335\306\341\335\305\341\335"
191     "\303\340\334\303\337\333\302\337\333\301\337\332\300\336\331\276\335\331"
192     "\276\334\330\274\334\330\274\334\327\273\333\327\272\333\326\271\332\325"
193     "\270\332\325\267\331\324\266\330\323\265\330\323\264\327\322\263\327\321"
194     "\262\326\320\261\325\320\257\325\317\257\324\317\256\324\316\254\323\315"
195     "\254\322\315\253\322\314\252\321\313\251\321\313\250\320\312\246\317\311"
196     "\245\317\311\245\316\310\244\316\307\243\315\307\242\314\306\241\314\305"
197     "\240\312\305\237\312\304\236\312\303\235\311\303\234\311\302\233\307\301"
198     "\232\307\300\231\307\300\230\306\277\227\305\276\226\305\276\225\304\275"
199     "\224\303\274\223\303\273\222\302\273\221\301\272\220\301\271\217\300\270"
200     "\216\277\270\215\277\267\214\276\266\213\275\265\212\275\265\211\274\264"
201     "\210\273\263\207\273\262\206\272\262\205\271\261\204\270\260\203\270\257"
202     "\202\267\257\201\266\256\200\266\255\177\265\254~\264\253}\263\253|\263\252"
203     "{\262\251z\261\250y\260\247x\260\247w\257\246v\256\245u\255\244t\255\243"
204     "s\254\243r\253\242q\252\241p\252\240o\251\237n\250\236m\247\235l\246\235"
205     "l\246\235j\245\233i\244\232h\243\231g\242\230f\242\227e\241\226d\240\226"
206     "c\237\225b\236\224a\235\223`\234\222_\234\221_\233\220]\232\217\\\231\216"
207     "\\\230\215Z\227\214Y\226\214X\226\213W\225\212V\224\211U\223\210T\222\207"
208     "S\221\206R\221\205Q\217\204P\216\203O\215\202N\215\201M\214\200M\213\177"
209     "K\212~J\211}I\211|H\210|G\206zG\205zE\204xD\203vC\203vB\201tA\200s@\200q"
210     "@~p>}o>|o<{l<yk;xi9wh8wg8te6sd5qd4pa3n_2m]1k\\0j\\0hY.fW-dU,cT+aR*_P)_O("
211     "]M'YK'XI%VI$TF$RD\"OB!M@\40K?\37I=\37G=\35E9\34C9\34A5\33>5\31<2\31<0\27"
212     ":.\27" "5,\26" "3*\24"
213     "1*\23.&\22.&\22*\"\21'\40\17%\36\16\"\34\15\"\32\14"
214     "\36\32\13\33\26\13\31\24\11\26\22\11\24\20\7\24\16\6\21\16\5\14\14\4\12\10"
215     "\3\7\6\3\5\4\1\2\2";
216 
217 static const guint8 xpro_table[768] =
218     "\0\0\37\0\0\37\0\1\40\0\2!\0\2\"\0\3\"\1\4%\1\4%\1\5%\1\5'\1\7'\1\7(\1\7"
219     "(\1\10*\1\11+\1\11,\1\12,\1\13/\1\14/\1\14" "1\2\15" "1\2\15" "1\2\16"
220     "4\2\17" "" "4\3\17" "5\3\22" "7\3\22" "7\3\23" "8\3\24"
221     "9\3\25;\3\26;\3\27<\3\27=\4\31"
222     "=\4\33?\4\34@\5\34B\5\35C\5\36D\5\40D\5\40G\5!G\6\"H\6$H\7&J\7&K\7*M\7*M"
223     "\10+N\10-P\11-P\11/R\11" "3R\11" "3T\12" "4U\12" "5U\13" "7W\14" "8Y\14"
224     "9Y\14"
225     "<Y\16=[\16@^\16@^\17C^\17D`\20F`\20Jb\22Jb\22Kc\23Me\24Nf\25Qg\26Rg\27Ti"
226     "\27Wj\30Xl\31Yl\33\\m\34^p\35`p\40bp\40fq!fr$gt$lt%lu'mv(px*qy-ty/uz/x|0"
227     "y}3|}4}~5\177\2018\203\2019\203\201;\204\202=\207\203?\210\204@\214\204C"
228     "\214\206D\216\207G\217\210H\223\211K\223\211M\225\212P\226\214Q\231\215T"
229     "\232\215U\234\216X\235\217Y\240\220\\\241\220^\243\221`\244\223b\246\224"
230     "e\250\224f\252\225i\253\226l\255\227m\256\231p\261\231q\262\232t\264\233"
231     "v\265\234x\267\234z\270\235|\271\236~\274\240\201\275\240\202\277\241\204"
232     "\300\242\207\302\243\210\303\243\212\305\244\214\306\245\216\307\246\220"
233     "\311\250\221\313\250\224\315\251\226\316\252\227\317\253\232\321\253\234"
234     "\322\254\235\323\255\240\325\256\242\326\256\242\330\256\245\331\261\250"
235     "\331\262\251\332\262\253\334\263\255\335\264\256\336\265\261\340\266\263"
236     "\341\266\264\342\267\266\343\270\270\344\271\271\344\271\271\346\273\276"
237     "\347\274\277\350\275\277\351\275\302\352\276\304\353\277\306\353\300\307"
238     "\355\300\311\356\301\314\356\302\315\357\303\317\360\304\320\360\304\322"
239     "\361\305\323\362\306\325\362\307\327\363\307\330\363\310\330\364\311\333"
240     "\364\313\334\365\313\336\365\314\340\365\314\342\366\316\342\366\316\346"
241     "\367\317\347\367\320\351\367\320\353\370\322\354\370\322\356\370\323\356"
242     "\370\324\360\371\325\360\371\325\363\371\326\363\371\327\363\372\330\365"
243     "\372\330\366\372\331\366\372\331\370\372\332\371\373\332\371\373\333\372"
244     "\373\334\373\373\335\373\373\336\374\373\336\374\374\337\374\374\340\375"
245     "\374\341\375\374\341\376\374\342\376\374\343\376\374\344\376\374\344\377"
246     "\374\345\377\374\346\377\375\346\377\375\346\377\375\347\377\375\350\377"
247     "\375\351\377\375\352\377\375\352\377\375\352\377\375\353\377\375\353\377"
248     "\376\354\377\376\354\377\376\356\377\376\356\377\376\356\377\376\357\377"
249     "\376\360\377\376\360\377\376\360\377\376\360\377\376\362\377\376\362\377"
250     "\376\363\377\376\363\377\376\363\377\376\363\377\376\364\377\376\364\377"
251     "\376\365\377\377\365\377\377\366\377\377\366\377\377\366\377\377\367\377"
252     "\377\367\377\377\367\377\377\370";
253 
254 /*Used for a video magnifer emulator in gnome-video-effects*/
255 static const guint8 yellowblue_table[768] =
256     "\0\0\377\1\1\376\2\2\375\3\3\374\4\4\373\5\5\372\6\6\371\7\7\370\10\10\367"
257     "\11\11\367\12\12\365\13\13\364\14\14\363\15\14\362\16\16\361\17\17\360\20"
258     "\20\357\20\21\356\22\22\355\23\23\354\24\24\354\24\25\352\26\26\351\27\27"
259     "\350\27\30\347\31\31\346\32\32\345\33\32\344\34\34\343\34\34\342\36\36\341"
260     "\37\36\340\40\40\337!!\336!!\335##\334$#\334%%\332&%\331'&\330((\327()\326"
261     "*)\325++\324,,\323--\322..\321//\320/0\31711\31722\31522\31444\31445\313"
262     "55\31276\31188\30799\3069:\305;;\305<<\304==\302>>\301>>\300@@\300@A\276"
263     "AB\275BC\274CD\273DE\272EE\272FF\270HH\270HI\266IJ\265KK\264KL\263MM\262"
264     "NN\262NN\261OO\257QP\256RQ\256RR\254TT\253UU\253VU\251VW\250XX\247XY\246"
265     "YZ\245[[\245[[\243]]\243^^\242^_\240_`\237`a\236aa\235bb\235dc\233de\233"
266     "ff\232gf\231hg\230hi\227ji\226kj\225lk\223lm\223nm\222nn\221op\217qq\216"
267     "rr\215ss\214st\213uu\213uu\211wv\210ww\207xx\207yz\205z{\205{{\204||\203"
268     "}}\202\177~\201\177\200\177\200\201\177\202\202~\203\202|\204\203|\204\204"
269     "{\205\206z\207\206x\207\207w\211\210w\211\211v\212\212u\213\214s\214\214"
270     "r\215\215r\216\217q\217\217p\221\220o\221\222n\223\222l\224\223k\224\224"
271     "k\225\225j\226\226i\227\227h\230\231f\231\231f\233\232e\233\233c\234\234"
272     "c\235\235b\236\236a\237\237`\241\240_\242\241^\242\242]\243\244\\\244\244"
273     "[\245\245Y\246\246Y\250\247X\250\250W\251\251V\252\252T\253\253T\254\255"
274     "S\256\255R\257\256Q\257\260P\260\261O\261\261N\262\262M\263\263L\264\265"
275     "K\265\265J\266\266I\267\270H\270\270G\271\271F\272\272E\273\273C\274\274"
276     "B\275\275B\276\276A\277\277@\300\300?\301\301>\302\302=\303\303<\304\304"
277     ";\305\305:\306\3069\307\3078\310\3107\311\3116\312\3125\313\3134\314\314"
278     "3\315\3152\316\3161\317\3170\320\320/\321\321.\322\322-\323\323,\323\324"
279     "+\325\325*\326\326)\327\327(\330\330'\331\331&\332\331%\333\332$\334\334"
280     "#\334\335\"\336\336!\337\337\40\340\340\37\341\341\36\342\342\35\343\343"
281     "\34\344\344\33\345\345\32\345\346\31\347\347\30\350\350\27\351\351\26\352"
282     "\352\25\353\353\24\354\354\23\354\355\22\356\356\21\357\357\20\360\360\17"
283     "\361\361\16\362\362\15\363\362\14\364\364\13\365\365\12\365\366\11\367\367"
284     "\11\370\370\7\371\371\6\372\371\5\373\373\4\374\374\4\375\375\3\375\376\1";
285 
286 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
287   298, 0, 409, -57068,
288   298, -100, -208, 34707,
289   298, 516, 0, -70870,
290 };
291 
292 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
293   66, 129, 25, 4096,
294   -38, -74, 112, 32768,
295   112, -94, -18, 32768,
296 };
297 
298 #define APPLY_MATRIX(m,o,v1,v2,v3) ((m[o*4] * v1 + m[o*4+1] * v2 + \
299     m[o*4+2] * v3 + m[o*4+3]) >> 8)
300 
301 static void
gst_color_effects_transform_rgb(GstColorEffects * filter,GstVideoFrame * frame)302 gst_color_effects_transform_rgb (GstColorEffects * filter,
303     GstVideoFrame * frame)
304 {
305   gint i, j;
306   gint width, height;
307   gint pixel_stride, row_stride, row_wrap;
308   guint32 r, g, b;
309   guint32 luma;
310   gint offsets[3];
311   guint8 *data;
312 
313   data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
314   offsets[0] = GST_VIDEO_FRAME_COMP_POFFSET (frame, 0);
315   offsets[1] = GST_VIDEO_FRAME_COMP_POFFSET (frame, 1);
316   offsets[2] = GST_VIDEO_FRAME_COMP_POFFSET (frame, 2);
317 
318   width = GST_VIDEO_FRAME_WIDTH (frame);
319   height = GST_VIDEO_FRAME_HEIGHT (frame);
320 
321   row_stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
322   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
323   row_wrap = row_stride - pixel_stride * width;
324 
325   /* transform */
326 
327   for (i = 0; i < height; i++) {
328     for (j = 0; j < width; j++) {
329       r = data[offsets[0]];
330       g = data[offsets[1]];
331       b = data[offsets[2]];
332       if (filter->map_luma) {
333         /* BT. 709 coefficients in B8 fixed point */
334         /* 0.2126 R + 0.7152 G + 0.0722 B */
335         luma = ((r << 8) * 54) + ((g << 8) * 183) + ((b << 8) * 19);
336         luma >>= 16;            /* get integer part */
337         luma *= 3;              /* times 3 to retrieve the correct pixel from
338                                  * the lut */
339         /* map luma to lookup table */
340         /* src.luma |-> table[luma].rgb */
341         data[offsets[0]] = filter->table[luma];
342         data[offsets[1]] = filter->table[luma + 1];
343         data[offsets[2]] = filter->table[luma + 2];
344       } else {
345         /* map each color component to the correspondent lut color */
346         /* src.r |-> table[r].r */
347         /* src.g |-> table[g].g */
348         /* src.b |-> table[b].b */
349         data[offsets[0]] = filter->table[r * 3];
350         data[offsets[1]] = filter->table[g * 3 + 1];
351         data[offsets[2]] = filter->table[b * 3 + 2];
352       }
353       data += pixel_stride;
354     }
355     data += row_wrap;
356   }
357 }
358 
359 static void
gst_color_effects_transform_ayuv(GstColorEffects * filter,GstVideoFrame * frame)360 gst_color_effects_transform_ayuv (GstColorEffects * filter,
361     GstVideoFrame * frame)
362 {
363   gint i, j;
364   gint width, height;
365   gint pixel_stride, row_stride, row_wrap;
366   gint r, g, b;
367   gint y, u, v;
368   gint offsets[3];
369   guint8 *data;
370 
371   data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
372   offsets[0] = GST_VIDEO_FRAME_COMP_POFFSET (frame, 0);
373   offsets[1] = GST_VIDEO_FRAME_COMP_POFFSET (frame, 1);
374   offsets[2] = GST_VIDEO_FRAME_COMP_POFFSET (frame, 2);
375 
376   width = GST_VIDEO_FRAME_WIDTH (frame);
377   height = GST_VIDEO_FRAME_HEIGHT (frame);
378 
379   row_stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
380   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
381   row_wrap = row_stride - pixel_stride * width;
382 
383   for (i = 0; i < height; i++) {
384     for (j = 0; j < width; j++) {
385       y = data[offsets[0]];
386       u = data[offsets[1]];
387       v = data[offsets[2]];
388 
389       if (filter->map_luma) {
390         /* map luma to lookup table */
391         /* src.luma |-> table[luma].rgb */
392         y *= 3;
393         r = filter->table[y];
394         g = filter->table[y + 1];
395         b = filter->table[y + 2];
396 
397         y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
398         u = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
399         v = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
400 
401         data[offsets[0]] = CLAMP (y, 0, 255);
402         data[offsets[1]] = CLAMP (u, 0, 255);
403         data[offsets[2]] = CLAMP (v, 0, 255);
404       } else {
405         r = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 0, y, u, v);
406         g = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 1, y, u, v);
407         b = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 2, y, u, v);
408 
409         r = CLAMP (r, 0, 255);
410         g = CLAMP (g, 0, 255);
411         b = CLAMP (b, 0, 255);
412 
413         /* map each color component to the correspondent lut color */
414         /* src.r |-> table[r].r */
415         /* src.g |-> table[g].g */
416         /* src.b |-> table[b].b */
417         r = filter->table[r * 3];
418         g = filter->table[g * 3 + 1];
419         b = filter->table[b * 3 + 2];
420 
421         y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
422         u = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
423         v = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
424 
425         data[offsets[0]] = CLAMP (y, 0, 255);
426         data[offsets[1]] = CLAMP (u, 0, 255);
427         data[offsets[2]] = CLAMP (v, 0, 255);
428       }
429       data += pixel_stride;
430     }
431     data += row_wrap;
432   }
433 }
434 
435 static gboolean
gst_color_effects_set_info(GstVideoFilter * vfilter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)436 gst_color_effects_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
437     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
438 {
439   GstColorEffects *filter = GST_COLOR_EFFECTS (vfilter);
440 
441   GST_DEBUG_OBJECT (filter,
442       "in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps, outcaps);
443 
444   filter->process = NULL;
445 
446   filter->format = GST_VIDEO_INFO_FORMAT (in_info);
447   filter->width = GST_VIDEO_INFO_WIDTH (in_info);
448   filter->height = GST_VIDEO_INFO_HEIGHT (in_info);
449 
450   GST_OBJECT_LOCK (filter);
451 
452   switch (filter->format) {
453     case GST_VIDEO_FORMAT_AYUV:
454       filter->process = gst_color_effects_transform_ayuv;
455       break;
456     case GST_VIDEO_FORMAT_ARGB:
457     case GST_VIDEO_FORMAT_ABGR:
458     case GST_VIDEO_FORMAT_RGBA:
459     case GST_VIDEO_FORMAT_BGRA:
460     case GST_VIDEO_FORMAT_xRGB:
461     case GST_VIDEO_FORMAT_xBGR:
462     case GST_VIDEO_FORMAT_RGBx:
463     case GST_VIDEO_FORMAT_BGRx:
464     case GST_VIDEO_FORMAT_RGB:
465     case GST_VIDEO_FORMAT_BGR:
466       filter->process = gst_color_effects_transform_rgb;
467       break;
468     default:
469       break;
470   }
471 
472   GST_OBJECT_UNLOCK (filter);
473 
474   return filter->process != NULL;
475 }
476 
477 static GstFlowReturn
gst_color_effects_transform_frame_ip(GstVideoFilter * vfilter,GstVideoFrame * out)478 gst_color_effects_transform_frame_ip (GstVideoFilter * vfilter,
479     GstVideoFrame * out)
480 {
481   GstColorEffects *filter = GST_COLOR_EFFECTS (vfilter);
482 
483   if (!filter->process)
484     goto not_negotiated;
485 
486   /* do nothing if there is no table ("none" preset) */
487   if (filter->table == NULL)
488     return GST_FLOW_OK;
489 
490   GST_OBJECT_LOCK (filter);
491   filter->process (filter, out);
492   GST_OBJECT_UNLOCK (filter);
493 
494   return GST_FLOW_OK;
495 
496 not_negotiated:
497   GST_ERROR_OBJECT (filter, "Not negotiated yet");
498   return GST_FLOW_NOT_NEGOTIATED;
499 }
500 
501 static void
gst_color_effects_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)502 gst_color_effects_set_property (GObject * object, guint prop_id,
503     const GValue * value, GParamSpec * pspec)
504 {
505   GstColorEffects *filter = GST_COLOR_EFFECTS (object);
506 
507   switch (prop_id) {
508     case PROP_PRESET:
509       GST_OBJECT_LOCK (filter);
510       filter->preset = g_value_get_enum (value);
511 
512       switch (filter->preset) {
513         case GST_COLOR_EFFECTS_PRESET_NONE:
514           filter->table = NULL;
515           break;
516         case GST_COLOR_EFFECTS_PRESET_HEAT:
517           filter->table = heat_table;
518           filter->map_luma = TRUE;
519           break;
520         case GST_COLOR_EFFECTS_PRESET_SEPIA:
521           filter->table = sepia_table;
522           filter->map_luma = TRUE;
523           break;
524         case GST_COLOR_EFFECTS_PRESET_XRAY:
525           filter->table = xray_table;
526           filter->map_luma = TRUE;
527           break;
528         case GST_COLOR_EFFECTS_PRESET_XPRO:
529           filter->table = xpro_table;
530           filter->map_luma = FALSE;
531           break;
532         case GST_COLOR_EFFECTS_PRESET_YELLOWBLUE:
533           filter->table = yellowblue_table;
534           filter->map_luma = FALSE;
535           break;
536         default:
537           g_assert_not_reached ();
538 
539       }
540       GST_OBJECT_UNLOCK (filter);
541       break;
542     default:
543       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
544       break;
545   }
546 }
547 
548 static void
gst_color_effects_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)549 gst_color_effects_get_property (GObject * object, guint prop_id, GValue * value,
550     GParamSpec * pspec)
551 {
552   GstColorEffects *filter = GST_COLOR_EFFECTS (object);
553 
554   switch (prop_id) {
555     case PROP_PRESET:
556       GST_OBJECT_LOCK (filter);
557       g_value_set_enum (value, filter->preset);
558       GST_OBJECT_UNLOCK (filter);
559       break;
560     default:
561       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
562       break;
563   }
564 }
565 
566 static void
gst_color_effects_class_init(GstColorEffectsClass * klass)567 gst_color_effects_class_init (GstColorEffectsClass * klass)
568 {
569   GObjectClass *gobject_class = (GObjectClass *) klass;
570   GstElementClass *element_class = (GstElementClass *) klass;
571   GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
572 
573   GST_DEBUG_CATEGORY_INIT (coloreffects_debug, "coloreffects", 0,
574       "coloreffects");
575 
576   gobject_class->set_property = gst_color_effects_set_property;
577   gobject_class->get_property = gst_color_effects_get_property;
578 
579   g_object_class_install_property (gobject_class, PROP_PRESET,
580       g_param_spec_enum ("preset", "Preset", "Color effect preset to use",
581           GST_TYPE_COLOR_EFFECTS_PRESET, DEFAULT_PROP_PRESET,
582           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
583 
584   vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_color_effects_set_info);
585   vfilter_class->transform_frame_ip =
586       GST_DEBUG_FUNCPTR (gst_color_effects_transform_frame_ip);
587 
588   gst_element_class_set_static_metadata (element_class,
589       "Color Look-up Table filter", "Filter/Effect/Video",
590       "Color Look-up Table filter",
591       "Filippo Argiolas <filippo.argiolas@gmail.com>");
592 
593   gst_element_class_add_static_pad_template (element_class,
594       &gst_color_effects_sink_template);
595   gst_element_class_add_static_pad_template (element_class,
596       &gst_color_effects_src_template);
597 }
598 
599 static void
gst_color_effects_init(GstColorEffects * filter)600 gst_color_effects_init (GstColorEffects * filter)
601 {
602   filter->preset = GST_COLOR_EFFECTS_PRESET_NONE;
603   filter->table = NULL;
604   filter->map_luma = TRUE;
605 }
606