]> git.sesse.net Git - casparcg/blob - core/producer/color/color_producer.cpp
[color_producer] Added support for gradients. Simply done via OpenGL's bilinear filte...
[casparcg] / core / producer / color / color_producer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "../../StdAfx.h"
23
24 #include "color_producer.h"
25
26 #include <core/producer/frame_producer.h>
27 #include <core/frame/frame.h>
28 #include <core/frame/draw_frame.h>
29 #include <core/frame/frame_factory.h>
30 #include <core/frame/pixel_format.h>
31 #include <core/frame/audio_channel_layout.h>
32 #include <core/monitor/monitor.h>
33 #include <core/help/help_repository.h>
34 #include <core/help/help_sink.h>
35
36 #include <common/except.h>
37 #include <common/array.h>
38
39 #include <boost/algorithm/string.hpp>
40
41 #include <sstream>
42
43 namespace caspar { namespace core {
44
45 draw_frame create_color_frame(void* tag, const spl::shared_ptr<frame_factory>& frame_factory, const std::vector<uint32_t>& values)
46 {
47         core::pixel_format_desc desc(pixel_format::bgra);
48         desc.planes.push_back(core::pixel_format_desc::plane(static_cast<int>(values.size()), 1, 4));
49         auto frame = frame_factory->create_frame(tag, desc, core::audio_channel_layout::invalid());
50
51         for (int i = 0; i < values.size(); ++i)
52                 *reinterpret_cast<uint32_t*>(frame.image_data(0).begin() + (i * 4)) = values.at(i);
53
54         return core::draw_frame(std::move(frame));
55 }
56
57 draw_frame create_color_frame(void* tag, const spl::shared_ptr<frame_factory>& frame_factory, uint32_t value)
58 {
59         std::vector<uint32_t> values = { value };
60
61         return create_color_frame(tag, frame_factory, values);
62 }
63
64 draw_frame create_color_frame(void* tag, const spl::shared_ptr<frame_factory>& frame_factory, const std::vector<std::wstring>& strs)
65 {
66         std::vector<uint32_t> values(strs.size());
67
68         for (int i = 0; i < values.size(); ++i)
69         {
70                 if (!try_get_color(strs.at(i), values.at(i)))
71                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Invalid color: " + strs.at(i)));
72         }
73
74         return create_color_frame(tag, frame_factory, values);
75 }
76
77 class color_producer : public frame_producer_base
78 {
79         monitor::subject                monitor_subject_;
80
81         const std::wstring              color_str_;
82         constraints                             constraints_;
83         draw_frame                              frame_;
84
85 public:
86         color_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, uint32_t value)
87                 : color_str_(L"")
88                 , constraints_(1, 1)
89                 , frame_(create_color_frame(this, frame_factory, value))
90         {
91                 CASPAR_LOG(info) << print() << L" Initialized";
92         }
93
94         color_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const std::vector<std::wstring>& colors)
95                 : color_str_(boost::join(colors, L", "))
96                 , constraints_(static_cast<int>(colors.size()), 1)
97                 , frame_(create_color_frame(this, frame_factory, colors))
98         {
99                 CASPAR_LOG(info) << print() << L" Initialized";
100         }
101
102         // frame_producer
103
104         draw_frame receive_impl() override
105         {
106                 monitor_subject_ << monitor::message("color") % color_str_;
107
108                 return frame_;
109         }
110
111         constraints& pixel_constraints() override
112         {
113                 return constraints_;
114         }
115
116         std::wstring print() const override
117         {
118                 return L"color[" + color_str_ + L"]";
119         }
120
121         std::wstring name() const override
122         {
123                 return L"color";
124         }
125
126         boost::property_tree::wptree info() const override
127         {
128                 boost::property_tree::wptree info;
129                 info.add(L"type", L"color");
130                 info.add(L"color", color_str_);
131                 return info;
132         }
133
134         monitor::subject& monitor_output() override {return monitor_subject_;}
135 };
136
137 std::wstring get_hex_color(const std::wstring& str)
138 {
139         if(str.at(0) == '#')
140                 return str.length() == 7 ? L"#FF" + str.substr(1) : str;
141
142         std::wstring col_str = boost::to_upper_copy(str);
143
144         if(col_str == L"EMPTY")
145                 return L"#00000000";
146
147         if(col_str == L"BLACK")
148                 return L"#FF000000";
149
150         if(col_str == L"WHITE")
151                 return L"#FFFFFFFF";
152
153         if(col_str == L"RED")
154                 return L"#FFFF0000";
155
156         if(col_str == L"GREEN")
157                 return L"#FF00FF00";
158
159         if(col_str == L"BLUE")
160                 return L"#FF0000FF";
161
162         if(col_str == L"ORANGE")
163                 return L"#FFFFA500";
164
165         if(col_str == L"YELLOW")
166                 return L"#FFFFFF00";
167
168         if(col_str == L"BROWN")
169                 return L"#FFA52A2A";
170
171         if(col_str == L"GRAY")
172                 return L"#FF808080";
173
174         if(col_str == L"TEAL")
175                 return L"#FF008080";
176
177         return str;
178 }
179
180 bool try_get_color(const std::wstring& str, uint32_t& value)
181 {
182         auto color_str = get_hex_color(str);
183         if(color_str.length() != 9 || color_str[0] != '#')
184                 return false;
185
186         std::wstringstream ss(color_str.substr(1));
187         if(!(ss >> std::hex >> value) || !ss.eof())
188                 return false;
189
190         return true;
191 }
192
193 void describe_color_producer(core::help_sink& sink, const core::help_repository& repo)
194 {
195         sink.short_description(L"Fills a layer with a solid color or a horizontal gradient.");
196         sink.syntax(
197                 L"{#[argb_hex_value:string]}"
198                 L",{#[rgb_hex_value:string]}"
199                 L",{[named_color:EMPTY,BLACK,WHITE,RED,GREEN,BLUE,ORANGE,YELLOW,BROWN,GRAY,TEAL]} "
200                 L"{...more colors}");
201         sink.para()->text(L"A producer that fills a layer with a solid color. If more than one color is specified it becomes a gradient between those colors.");
202         sink.para()
203                 ->text(L"If a ")->code(L"#")
204                 ->text(L" sign followed by an 8 character hexadecimal number is given it is interpreted as an 8-bit per channel ARGB color. ")
205                 ->text(L"If it is instead followed by a 6 character hexadecimal number it is interpreted as an 8-bit per channel RGB value with an opaque alpha channel.");
206         sink.para()->text(L"There are also a number of predefined named colors, that are specified without a ")->code(L"#")->text(L" sign:");
207         sink.definitions()
208                 ->item(L"EMPTY",        L"Predefined ARGB value #00000000")
209                 ->item(L"BLACK",        L"Predefined ARGB value #FF000000")
210                 ->item(L"WHITE",        L"Predefined ARGB value #FFFFFFFF")
211                 ->item(L"RED",          L"Predefined ARGB value #FFFF0000")
212                 ->item(L"GREEN",        L"Predefined ARGB value #FF00FF00")
213                 ->item(L"BLUE",         L"Predefined ARGB value #FF0000FF")
214                 ->item(L"ORANGE",       L"Predefined ARGB value #FFFFA500")
215                 ->item(L"YELLOW",       L"Predefined ARGB value #FFFFFF00")
216                 ->item(L"BROWN",        L"Predefined ARGB value #FFA52A2A")
217                 ->item(L"GRAY",         L"Predefined ARGB value #FF808080")
218                 ->item(L"TEAL",         L"Predefined ARGB value #FF008080");
219         sink.para()
220                 ->strong(L"Note")->text(L" that it is important that all RGB values are multiplied with the alpha ")
221                 ->text(L"at all times, otherwise the compositing in the mixer will be incorrect.");
222         sink.para()->text(L"Solid color examples:");
223         sink.example(L"PLAY 1-10 EMPTY", L"For a completely transparent frame.");
224         sink.example(L"PLAY 1-10 #00000000", L"For an explicitly defined completely transparent frame.");
225         sink.example(L"PLAY 1-10 #000000", L"For an explicitly defined completely opaque black frame.");
226         sink.example(L"PLAY 1-10 RED", L"For a completely red frame.");
227         sink.example(L"PLAY 1-10 #7F007F00",
228                 L"For a completely green half transparent frame. "
229                 L"Since the RGB part has been premultiplied with the A part this is 100% green.");
230         sink.para()->text(L"Horizontal gradient examples:");
231         sink.example(L"PLAY 1-10 WHITE BLACK", L"For a gradient between white and black.");
232         sink.example(L"PLAY 1-10 WHITE WHITE WHITE BLACK", L"For a gradient between white and black with the white part beings 3 times as long as the black part.");
233         sink.example(
234                 L"PLAY 1-10 RED EMPTY\n"
235                 L"MIXER 1-10 ANCHOR 0.5 0.5\n"
236                 L"MIXER 1-10 FILL 0.5 0.5 2 2\n"
237                 L"MIXER 1-10 ROTATION 45",
238                 L"For a 45 degree gradient covering the screen.");
239 }
240
241 spl::shared_ptr<frame_producer> create_color_producer(const spl::shared_ptr<frame_factory>& frame_factory, uint32_t value)
242 {
243         return spl::make_shared<color_producer>(frame_factory, value);
244 }
245
246 spl::shared_ptr<frame_producer> create_color_producer(const spl::shared_ptr<frame_factory>& frame_factory, const std::vector<std::wstring>& params)
247 {
248         if(params.size() < 0)
249                 return core::frame_producer::empty();
250
251         uint32_t value = 0;
252         if(!try_get_color(params.at(0), value))
253                 return core::frame_producer::empty();
254
255         std::vector<std::wstring> colors;
256
257         for (auto& param : params)
258         {
259                 if (try_get_color(param, value))
260                         colors.push_back(param);
261         }
262
263         return spl::make_shared<color_producer>(frame_factory, colors);
264 }
265
266 }}