in libheif/color-conversion/colorconversion.cc [266:420]
bool ColorConversionPipeline::construct_pipeline(const ColorState& input_state,
const ColorState& target_state,
const heif_color_conversion_options& options)
{
m_conversion_steps.clear();
m_options = options;
if (input_state == target_state) {
return true;
}
#if DEBUG_ME
std::cerr << "--- construct_pipeline\n";
std::cerr << "from: " << input_state << "\nto: " << target_state << "\n";
#endif
init_ops(); // to be sure these are initialized even without heif_init()
std::vector<std::shared_ptr<ColorConversionOperation>>& ops = m_operation_pool;
// --- Dijkstra search for the minimum-cost conversion pipeline
std::vector<Node> processed_states;
std::vector<Node> border_states;
border_states.push_back({-1, nullptr, input_state, 0});
while (!border_states.empty()) {
int minIdx = -1;
int minCost = std::numeric_limits<int>::max();
for (int i = 0; i < (int) border_states.size(); i++) {
int cost = border_states[i].speed_costs;
if (cost < minCost) {
minIdx = i;
minCost = cost;
}
}
assert(minIdx >= 0);
// move minimum-cost border_state into processed_states
processed_states.push_back(border_states[minIdx]);
border_states[minIdx] = border_states.back();
border_states.pop_back();
#if DEBUG_PIPELINE_CREATION
std::cerr << "- expand node: " << processed_states.back().output_state
<< " with cost " << processed_states.back().speed_costs << " \n";
#endif
if (processed_states.back().output_state == target_state) {
// end-state found, backtrack path to find conversion pipeline
size_t idx = processed_states.size() - 1;
int len = 0;
while (idx > 0) {
idx = processed_states[idx].prev_processed_idx;
len++;
}
m_conversion_steps.resize(len);
idx = processed_states.size() - 1;
int step = 0;
while (idx > 0) {
m_conversion_steps[len - 1 - step].operation = processed_states[idx].op;
m_conversion_steps[len - 1 - step].output_state = processed_states[idx].output_state;
if (step > 0) {
m_conversion_steps[len - step].input_state = m_conversion_steps[len - 1 - step].output_state;
}
//printf("cost: %f\n",processed_states[idx].color_state.costs.total(options.criterion));
idx = processed_states[idx].prev_processed_idx;
step++;
}
m_conversion_steps[0].input_state = input_state;
assert(m_conversion_steps.back().output_state == target_state);
#if DEBUG_ME
std::cerr << debug_dump_pipeline();
#endif
return true;
}
// expand the node with minimum cost
for (const auto& op_ptr : ops) {
#if DEBUG_PIPELINE_CREATION
auto& op = *op_ptr;
std::cerr << "-- apply op: " << typeid(op).name() << "\n";
#endif
auto out_states = op_ptr->state_after_conversion(processed_states.back().output_state,
target_state,
options);
for (const auto& out_state : out_states) {
int new_op_costs = out_state.speed_costs + processed_states.back().speed_costs;
#if DEBUG_PIPELINE_CREATION
std::cerr << "--- " << out_state.color_state << " with cost " << new_op_costs << "\n";
#endif
bool state_exists = false;
for (const auto& s : processed_states) {
if (s.output_state == out_state.color_state) {
state_exists = true;
break;
}
}
if (!state_exists) {
for (auto& s : border_states) {
if (s.output_state == out_state.color_state) {
state_exists = true;
// if we reached the same border node with a lower cost, replace the border node
if (s.speed_costs > new_op_costs) {
s = {(int) (processed_states.size() - 1),
op_ptr,
out_state.color_state,
out_state.speed_costs};
s.speed_costs = new_op_costs;
}
break;
}
}
}
// enter the new output state into the list of border states
if (!state_exists) {
ColorStateWithCost s = out_state;
s.speed_costs = s.speed_costs + processed_states.back().speed_costs;
border_states.push_back({(int) (processed_states.size() - 1),
op_ptr,
s.color_state,
s.speed_costs});
}
}
}
}
return false;
}