Skip to content

Commit 78d9430

Browse files
committed
Marker RGB colors
Signed-off-by: Spencer Magnusson <spencer.magnusson@dreamworks.com>
1 parent aa34e9a commit 78d9430

16 files changed

Lines changed: 345 additions & 138 deletions

File tree

docs/tutorials/otio-serialized-schema-only-fields.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,6 @@ parameters:
3131

3232
## Module: opentimelineio.core
3333

34-
### Color.1
35-
36-
parameters:
37-
- *a*
38-
- *b*
39-
- *g*
40-
- *name*
41-
- *r*
42-
4334
### Composable.1
4435

4536
parameters:
@@ -152,6 +143,15 @@ parameters:
152143
- *name*
153144
- *source_range*
154145

146+
### Color.1
147+
148+
parameters:
149+
- *a*
150+
- *b*
151+
- *g*
152+
- *name*
153+
- *r*
154+
155155
### Effect.1
156156

157157
parameters:
@@ -224,7 +224,7 @@ parameters:
224224
- *name*
225225
- *time_scalar*
226226

227-
### Marker.2
227+
### Marker.3
228228

229229
parameters:
230230
- *color*

docs/tutorials/otio-serialized-schema.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,27 +55,6 @@ parameters:
5555

5656
## Module: opentimelineio.core
5757

58-
### Color.1
59-
60-
*full module path*: `opentimelineio.core.Color`
61-
62-
*documentation*:
63-
64-
```
65-
:class:`Color` is a definition of red, green, blue, and alpha double floating point values, allowing
66-
conversion between different formats. To be considered interoperable, the sRGB transfer function
67-
encoded values, ranging between zero and one, are expected to be accurate to within 1/255 of the
68-
intended value. Round-trip conversions may not be guaranteed outside that. This Color class is meant
69-
for use in user interface elements, like marker or clip coloring, NOT for image pixel content.
70-
```
71-
72-
parameters:
73-
- *a*:
74-
- *b*:
75-
- *g*:
76-
- *name*:
77-
- *r*:
78-
7958
### Composable.1
8059

8160
*full module path*: `opentimelineio.core.Composable`
@@ -309,6 +288,27 @@ parameters:
309288
- *name*:
310289
- *source_range*:
311290

291+
### Color.1
292+
293+
*full module path*: `opentimelineio.schema.Color`
294+
295+
*documentation*:
296+
297+
```
298+
:class:`Color` is a definition of red, green, blue, and alpha double floating point values, allowing
299+
conversion between different formats. To be considered interoperable, the sRGB transfer function
300+
encoded values, ranging between zero and one, are expected to be accurate to within 1/255 of the
301+
intended value. Round-trip conversions may not be guaranteed outside that. This Color class is meant
302+
for use in user interface elements, like marker or clip coloring, NOT for image pixel content.
303+
```
304+
305+
parameters:
306+
- *a*:
307+
- *b*:
308+
- *g*:
309+
- *name*:
310+
- *r*:
311+
312312
### Effect.1
313313

314314
*full module path*: `opentimelineio.schema.Effect`
@@ -519,7 +519,7 @@ parameters:
519519
Note that adjusting the time_scalar of a :class:`~LinearTimeWarp` does not affect the duration of the item this effect is attached to.
520520
Instead it affects the speed of the media displayed within that item.
521521

522-
### Marker.2
522+
### Marker.3
523523

524524
*full module path*: `opentimelineio.schema.Marker`
525525

src/opentimelineio/CORE_VERSION_MAP.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ const label_to_schema_version_map CORE_VERSION_MAP{
221221
{ "ImageSequenceReference", 1 },
222222
{ "Item", 1 },
223223
{ "LinearTimeWarp", 1 },
224-
{ "Marker", 2 },
224+
{ "Marker", 3 },
225225
{ "MediaLinker", 1 },
226226
{ "MediaReference", 1 },
227227
{ "MissingReference", 1 },

src/opentimelineio/marker.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
namespace opentimelineio { namespace OPENTIMELINEIO_VERSION_NS {
88

99
Marker::Marker(
10-
std::string const& name,
11-
TimeRange const& marked_range,
12-
std::string const& color,
13-
AnyDictionary const& metadata,
14-
std::string const& comment)
10+
std::string const& name,
11+
TimeRange const& marked_range,
12+
std::optional<Color> const& color,
13+
AnyDictionary const& metadata,
14+
std::string const& comment)
1515
: Parent(name, metadata)
1616
, _color(color)
1717
, _marked_range(marked_range)

src/opentimelineio/marker.h

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "opentimelineio/serializableObjectWithMetadata.h"
77
#include "opentimelineio/version.h"
8+
#include "opentimelineio/color.h"
89

910
namespace opentimelineio { namespace OPENTIMELINEIO_VERSION_NS {
1011

@@ -16,27 +17,12 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION_NS {
1617
class OTIO_API_TYPE Marker : public SerializableObjectWithMetadata
1718
{
1819
public:
19-
/// @brief This struct provides the base set of colors.
20-
struct Color
21-
{
22-
static auto constexpr pink = "PINK";
23-
static auto constexpr red = "RED";
24-
static auto constexpr orange = "ORANGE";
25-
static auto constexpr yellow = "YELLOW";
26-
static auto constexpr green = "GREEN";
27-
static auto constexpr cyan = "CYAN";
28-
static auto constexpr blue = "BLUE";
29-
static auto constexpr purple = "PURPLE";
30-
static auto constexpr magenta = "MAGENTA";
31-
static auto constexpr black = "BLACK";
32-
static auto constexpr white = "WHITE";
33-
};
3420

3521
/// @brief This struct provides the Marker schema.
3622
struct Schema
3723
{
3824
static auto constexpr name = "Marker";
39-
static int constexpr version = 2;
25+
static int constexpr version = 3;
4026
};
4127

4228
using Parent = SerializableObjectWithMetadata;
@@ -51,15 +37,15 @@ class OTIO_API_TYPE Marker : public SerializableObjectWithMetadata
5137
OTIO_API Marker(
5238
std::string const& name = std::string(),
5339
TimeRange const& marked_range = TimeRange(),
54-
std::string const& color = Color::green,
40+
std::optional<Color> const& color = Color::green,
5541
AnyDictionary const& metadata = AnyDictionary(),
5642
std::string const& comment = std::string());
5743

5844
/// @brief Return the marker color.
59-
std::string color() const noexcept { return _color; }
45+
std::optional<Color> color() const noexcept { return _color; }
6046

6147
/// @brief Set the marker color.
62-
void set_color(std::string const& color) { _color = color; }
48+
void set_color(std::optional<Color> const& color) { _color = color; }
6349

6450
/// @brief Return the marker time range.
6551
TimeRange marked_range() const noexcept { return _marked_range; }
@@ -83,7 +69,7 @@ class OTIO_API_TYPE Marker : public SerializableObjectWithMetadata
8369
void write_to(Writer&) const override;
8470

8571
private:
86-
std::string _color;
72+
std::optional<Color> _color;
8773
TimeRange _marked_range;
8874
std::string _comment;
8975
};

src/opentimelineio/typeRegistry.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
#include "opentimelineio/unknownSchema.h"
3030
#include "stringUtils.h"
3131

32+
#include <algorithm>
3233
#include <assert.h>
34+
#include <cctype>
35+
#include <map>
36+
#include <set>
3337
#include <vector>
3438

3539
namespace opentimelineio { namespace OPENTIMELINEIO_VERSION_NS {
@@ -96,6 +100,62 @@ TypeRegistry::TypeRegistry()
96100
d->erase("range");
97101
});
98102

103+
// 2 - 3
104+
register_upgrade_function(Marker::Schema::name, 3, [](AnyDictionary* d) {
105+
// get color name
106+
std::string color_name_v2;
107+
if (d->get_if_set("color", &color_name_v2))
108+
{
109+
static const std::map<std::string, Color> color_map = {
110+
{ "PINK", Color::pink },
111+
{ "RED", Color::red },
112+
{ "ORANGE", Color::orange },
113+
{ "YELLOW", Color::yellow },
114+
{ "GREEN", Color::green },
115+
{ "CYAN", Color::cyan },
116+
{ "BLUE", Color::blue },
117+
{ "MAGENTA", Color::magenta },
118+
{ "PURPLE", Color::purple },
119+
{ "BLACK", Color::black },
120+
{ "WHITE", Color::white },
121+
{ "TRANSPARENT", Color::transparent }
122+
};
123+
124+
// make copy of color_name_v2, for uppercase change to be separate
125+
std::string color_name_v2_upper = color_name_v2;
126+
127+
// force color name to uppercase for lookup
128+
// since v2 color names were case-insensitive
129+
std::transform(
130+
color_name_v2_upper.begin(),
131+
color_name_v2_upper.end(),
132+
color_name_v2_upper.begin(),
133+
[](unsigned char c) { return std::toupper(c); }
134+
);
135+
136+
// if all-caps name matches a known color, convert to color with r,g,b,a, and name fields
137+
auto it = color_map.find(color_name_v2_upper);
138+
Color color_match = Color::white;
139+
std::string color_match_name = "";
140+
if (it != color_map.end()) { // match found
141+
color_match = it->second;
142+
color_match_name = color_match.name();
143+
}
144+
else { // no match, default to white but keep original name
145+
color_match = Color::white;
146+
color_match_name = color_name_v2;
147+
}
148+
149+
(*d)["color"] = Color(
150+
color_match.r(),
151+
color_match.g(),
152+
color_match.b(),
153+
color_match.a(),
154+
color_match_name
155+
);
156+
}
157+
});
158+
99159
register_upgrade_function(Clip::Schema::name, 2, [](AnyDictionary* d) {
100160
auto media_ref = (*d)["media_reference"];
101161

@@ -116,6 +176,46 @@ TypeRegistry::TypeRegistry()
116176
d->erase("media_reference");
117177
});
118178

179+
// 3->2
180+
register_downgrade_function(Marker::Schema::name, 3, [](AnyDictionary* d) {
181+
AnyDictionary color_dict;
182+
183+
if (d->get_if_set("color", &color_dict))
184+
{
185+
std::string color_name = "";
186+
color_dict.get_if_set("name", &color_name);
187+
188+
// if the name matches case-insensitive to a known color,
189+
// set the color an all-caps version of that name
190+
if (!color_name.empty())
191+
{
192+
// Convert to uppercase for comparison and storage
193+
std::string upper_name = color_name;
194+
std::transform(
195+
upper_name.begin(),
196+
upper_name.end(),
197+
upper_name.begin(),
198+
[](unsigned char c) { return std::toupper(c); }
199+
);
200+
201+
// Known color names - these are the valid color enum values
202+
static const std::set<std::string> known_colors = {
203+
"RED", "GREEN", "BLUE", "YELLOW", "CYAN", "MAGENTA",
204+
"PINK", "ORANGE", "PURPLE", "BLACK", "WHITE", "TRANSPARENT"
205+
};
206+
207+
if (known_colors.find(upper_name) != known_colors.end())
208+
{
209+
// remove color object and replace with color name string
210+
(*d)["color"] = upper_name;
211+
}
212+
else { // otherwise, keep name as-is
213+
(*d)["color"] = color_name;
214+
}
215+
}
216+
}
217+
});
218+
119219
// 2->1
120220
register_downgrade_function(Clip::Schema::name, 2, [](AnyDictionary* d) {
121221
AnyDictionary mrefs;

src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,11 @@ A marker indicates a marked range of time on an item in a timeline, usually with
433433
The marked range may have a zero duration. The marked range is in the owning item's time coordinate system.
434434
)docstring")
435435
.def(
436-
py::init([](std::string name,
437-
TimeRange marked_range,
438-
std::string const& color,
439-
py::object metadata,
440-
std::string const& comment) {
436+
py::init([](std::string name,
437+
TimeRange marked_range,
438+
std::optional<Color> color,
439+
py::object metadata,
440+
std::string const& comment) {
441441
return new Marker(
442442
name,
443443
marked_range,
@@ -447,14 +447,14 @@ The marked range may have a zero duration. The marked range is in the owning ite
447447
}),
448448
py::arg_v("name"_a = std::string()),
449449
"marked_range"_a = TimeRange(),
450-
"color"_a = std::string(Marker::Color::red),
450+
"color"_a = Color::red,
451451
py::arg_v("metadata"_a = py::none()),
452452
"comment"_a = std::string())
453453
.def_property(
454454
"color",
455455
&Marker::color,
456456
&Marker::set_color,
457-
"Color string for this marker (for example: 'RED'), based on the :class:`~Color` enum.")
457+
"Color object assigned to this marker")
458458
.def_property(
459459
"marked_range",
460460
&Marker::marked_range,
@@ -466,41 +466,6 @@ The marked range may have a zero duration. The marked range is in the owning ite
466466
&Marker::set_comment,
467467
"Optional comment for this marker.");
468468

469-
py::class_<Marker::Color>(marker_class, "Color")
470-
.def_property_readonly_static(
471-
"PINK",
472-
[](py::object /* self */) { return Marker::Color::pink; })
473-
.def_property_readonly_static(
474-
"RED",
475-
[](py::object /* self */) { return Marker::Color::red; })
476-
.def_property_readonly_static(
477-
"ORANGE",
478-
[](py::object /* self */) { return Marker::Color::orange; })
479-
.def_property_readonly_static(
480-
"YELLOW",
481-
[](py::object /* self */) { return Marker::Color::yellow; })
482-
.def_property_readonly_static(
483-
"GREEN",
484-
[](py::object /* self */) { return Marker::Color::green; })
485-
.def_property_readonly_static(
486-
"CYAN",
487-
[](py::object /* self */) { return Marker::Color::cyan; })
488-
.def_property_readonly_static(
489-
"BLUE",
490-
[](py::object /* self */) { return Marker::Color::blue; })
491-
.def_property_readonly_static(
492-
"PURPLE",
493-
[](py::object /* self */) { return Marker::Color::purple; })
494-
.def_property_readonly_static(
495-
"MAGENTA",
496-
[](py::object /* self */) { return Marker::Color::magenta; })
497-
.def_property_readonly_static(
498-
"BLACK",
499-
[](py::object /* self */) { return Marker::Color::black; })
500-
.def_property_readonly_static("WHITE", [](py::object /* self */) {
501-
return Marker::Color::white;
502-
});
503-
504469
using SerializableCollectionIterator =
505470
ContainerIterator<SerializableCollection, SerializableObject*>;
506471
py::class_<SerializableCollectionIterator>(

0 commit comments

Comments
 (0)