Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 68 additions & 2 deletions colors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@
#include <opentimelineio/marker.h>
namespace otio = opentimelineio::OPENTIMELINEIO_VERSION;

ImU32 UIColorFromOTIOColor(const otio::Color& color)
{
auto rgba = color.to_float_list();

int r = static_cast<int>(rgba[0] * 255.0);
int g = static_cast<int>(rgba[1] * 255.0);
int b = static_cast<int>(rgba[2] * 255.0);
int a = static_cast<int>(rgba[3] * 255.0);

return IM_COL32(r, g, b, a);
}

ImU32 LerpColors(ImU32 col_a, ImU32 col_b, float t) {
int r = ImLerp(
(int)(col_a >> IM_COL32_R_SHIFT) & 0xFF,
Expand Down Expand Up @@ -51,6 +63,61 @@ ImU32 UIColorFromName(std::string color) {
return IM_COL32(0x88, 0x88, 0x88, 0xff);
}

namespace {
struct NamedColor {
const char* name;
float r, g, b, a;
};

// Same 11 colors already supported by UIColorFromName(), given explicit
// float values so we can compare against otio::Color's float list.
const NamedColor kNamedColors[] = {
{otio::Marker::Color::pink, 1.00f, 0.44f, 0.44f, 1.00f},
{otio::Marker::Color::red, 1.00f, 0.00f, 0.00f, 1.00f},
{otio::Marker::Color::orange, 1.00f, 0.63f, 0.00f, 1.00f},
{otio::Marker::Color::yellow, 1.00f, 1.00f, 0.00f, 1.00f},
{otio::Marker::Color::green, 0.00f, 1.00f, 0.00f, 1.00f},
{otio::Marker::Color::cyan, 0.00f, 1.00f, 1.00f, 1.00f},
{otio::Marker::Color::blue, 0.00f, 0.00f, 1.00f, 1.00f},
{otio::Marker::Color::purple, 0.63f, 0.00f, 0.82f, 1.00f},
{otio::Marker::Color::magenta, 1.00f, 0.00f, 1.00f, 1.00f},
{otio::Marker::Color::black, 0.00f, 0.00f, 0.00f, 1.00f},
{otio::Marker::Color::white, 1.00f, 1.00f, 1.00f, 1.00f},
};

bool FloatsClose(double a, double b) {
return std::abs(a - b) < 0.004; // ~1/255, one 8-bit step
}
} // namespace

std::string NameFromOTIOColor(const otio::Color& color)
{
auto v = color.to_float_list();
for (const auto& named : kNamedColors) {
if (FloatsClose(v[0], named.r) &&
FloatsClose(v[1], named.g) &&
FloatsClose(v[2], named.b) &&
FloatsClose(v[3], named.a)) {
return named.name;
}
}
// Not one of the known named colors - it's a custom RGB value.
// Callers must NOT fall back to a default name here, or the
// custom color will be silently lost on the next write.
return "";
}

otio::Color OTIOColorFromName(const std::string& name)
{
for (const auto& named : kNamedColors) {
if (name == named.name) {
return otio::Color::from_float_list(
{named.r, named.g, named.b, named.a});
}
}
return otio::Color::from_float_list({1.0f, 1.0f, 1.0f, 1.0f});
}

bool ColorIsBright(ImU32 color)
{
int r = (int)(color >> IM_COL32_R_SHIFT) & 0xFF;
Expand All @@ -73,5 +140,4 @@ ImU32 ColorInvert(ImU32 color)
ImU32 TintedColorForUI(ImU32 color)
{
return LerpColors(color, IM_COL32(150,150,150,255), 0.3);
}

}
15 changes: 14 additions & 1 deletion colors.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
#include <string>
#include "imgui.h"
#include <opentimelineio/color.h>

namespace otio = opentimelineio::OPENTIMELINEIO_VERSION;

ImU32 UIColorFromOTIOColor(const otio::Color& color);

// Maps an otio::Color to one of the known named marker colors.
// Returns "" if the color does not exactly match a known name -
// callers must treat "" as "custom color, do not overwrite".
std::string NameFromOTIOColor(const otio::Color& color);

// Maps one of the known named marker colors to an otio::Color.
otio::Color OTIOColorFromName(const std::string& name);

ImU32 UIColorFromName(std::string color);
ImU32 LerpColors(ImU32 col_a, ImU32 col_b, float t);
bool ColorIsBright(ImU32 color);
ImU32 ColorInvert(ImU32 color);
ImU32 TintedColorForUI(ImU32 color);
ImU32 TintedColorForUI(ImU32 color);
23 changes: 22 additions & 1 deletion editing.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#include "editing.h"
#include "app.h"
#include "colors.h"

#include <opentimelineio/effect.h>
#include <opentimelineio/item.h>
#include <opentimelineio/marker.h>
#include <opentimelineio/stackAlgorithm.h>
#include <stdlib.h>

using otio = opentimelineio::OPENTIMELINEIO_VERSION;

void DeleteSelectedObject() {
if (!appState.selected_object) {
return;
Expand Down Expand Up @@ -325,6 +328,20 @@ std::string GetItemColor(otio::Item* item)
{
std::string item_color = "";

// ✅ Use official OTIO Color schema
auto c = item->color();
if (c)
{
// NameFromOTIOColor() returns "" for a color that isn't one of
// the known named colors (e.g. an arbitrary RGB value). That is
// intentional: we must never guess a name for a custom color,
// since doing so would silently overwrite it the next time
// SetItemColor() runs. "" tells callers "this item has a color,
// but it doesn't match a preset - leave it alone."
return NameFromOTIOColor(*c);
}

// ✅ Fallback to Raven legacy metadata
if (item->metadata().has_key("raven") &&
item->metadata()["raven"].type() == typeid(otio::AnyDictionary))
{
Expand All @@ -342,6 +359,10 @@ std::string GetItemColor(otio::Item* item)

void SetItemColor(otio::Item* item, std::string color_name)
{
auto c = OTIOColorFromName(color_name);
item->set_color(c);

// Optionally keep Raven's legacy field in sync for backward compatibility
otio::AnyDictionary raven_md;
if (item->metadata().has_key("raven") &&
item->metadata()["raven"].type() == typeid(otio::AnyDictionary))
Expand All @@ -350,4 +371,4 @@ void SetItemColor(otio::Item* item, std::string color_name)
}
raven_md["color"] = color_name;
item->metadata()["raven"] = raven_md;
}
}
41 changes: 38 additions & 3 deletions inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,14 @@ void DrawNonEditableTextField(const char* label, const char* format, ...) {
ImGui::PopStyleColor();
}

std::string DrawColorChooser(std::string current_color_name)
// custom_color_swatch: when the current color doesn't match a named
// preset (current_color_name == ""), pass the raw color here so we can
// still show the user *something* is set, instead of a blank combo that
// looks like "no color". Pass nullptr for callers (like Marker) that
// only ever deal in named colors.
std::string DrawColorChooser(
std::string current_color_name,
const otio::Color* custom_color_swatch = nullptr)
{
const char** color_choices = marker_color_names;
int num_color_choices = IM_ARRAYSIZE(marker_color_names);
Expand All @@ -277,6 +284,27 @@ std::string DrawColorChooser(std::string current_color_name)
break;
}
}

bool is_custom = (current_index == -1) && custom_color_swatch != nullptr;
if (is_custom) {
// Draw a small swatch of the actual color next to the combo so
// it's visible that a non-preset color is present. Selecting a
// named color below will replace it; leaving the combo alone
// preserves the original custom value untouched.
ImU32 swatch = UIColorFromOTIOColor(*custom_color_swatch);
ImGui::ColorButton(
"##custom_color_swatch",
ImGui::ColorConvertU32ToFloat4(swatch),
ImGuiColorEditFlags_NoTooltip,
ImVec2(16, 16));
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Custom color (not one of the preset names).\n"
"Selecting a color below will replace it.");
}
ImGui::SameLine();
}

if (ImGui::Combo("Color", &current_index, color_choices, num_color_choices)) {
if (current_index >= 0 && current_index < num_color_choices) {
return color_choices[current_index];
Expand Down Expand Up @@ -677,7 +705,14 @@ void DrawInspector() {

if (!is_gap) {
auto item_color = GetItemColor(item);
item_color = DrawColorChooser(item_color);
auto raw_color = item->color();
// Only pass the swatch when there IS a color but it didn't
// match a named preset - i.e. it's genuinely custom, not
// simply absent.
bool is_custom_color = item_color == "" && raw_color.has_value();
item_color = DrawColorChooser(
item_color,
is_custom_color ? &raw_color.value() : nullptr);
if (item_color != "") {
SetItemColor(item, item_color);
}
Expand Down Expand Up @@ -1231,4 +1266,4 @@ void DrawTreeInspector() {

ImGui::EndTable();
}
}
}
8 changes: 8 additions & 0 deletions timeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,20 @@ void DrawItem(
auto hover_fill_color = appTheme.colors[AppThemeCol_ItemHovered];
bool fancy_corners = true;

if (auto color = item->color()) {
fill_color = UIColorFromOTIOColor(*color);
selected_fill_color = TintedColorForUI(fill_color);
hover_fill_color = TintedColorForUI(fill_color);
} else {
auto item_color = GetItemColor(item);
if (item_color != "") {
fill_color = UIColorFromName(item_color);
selected_fill_color = TintedColorForUI(fill_color);
hover_fill_color = TintedColorForUI(fill_color);
}
}



if (auto gap = dynamic_cast<otio::Gap*>(item)) {
// different colors & style
Expand Down