#include "icon_group.h" #include #include namespace raven { static uint32_t number(std::span bytes, size_t offset, size_t size) { uint32_t result = 0; for (size_t index = 0; index < size; ++index) result |= uint32_t(bytes[offset + index]) << (index * 8); return result; } std::vector decode_icon_group(std::span bytes) { if (bytes.size() < 6 || number(bytes, 0, 2) || number(bytes, 2, 2) != 1) throw std::runtime_error("invalid icon group header"); const size_t count = number(bytes, 4, 2); if (!count || bytes.size() < 6 + count * 14) throw std::runtime_error("truncated or empty icon group"); std::vector entries; entries.reserve(count); for (size_t index = 0; index < count; ++index) { const size_t offset = 6 + index * 14; const auto size = number(bytes, offset + 8, 4); const auto id = uint16_t(number(bytes, offset + 12, 2)); if (bytes[offset + 3] || !size || !id) throw std::runtime_error("invalid icon group entry"); entries.push_back({bytes[offset] ? uint32_t(bytes[offset]) : 256, bytes[offset + 1] ? uint32_t(bytes[offset + 1]) : 256, uint16_t(number(bytes, offset + 6, 2)), size, id}); } return entries; } uint16_t lookup_icon(std::span entries, uint32_t width, uint32_t height, IconDisplay display, bool monochrome) { if (!display.width || !display.height || !display.depth) throw std::runtime_error("invalid icon display state"); width = width ? width : display.width; height = height ? height : display.height; const auto distance = [&](const IconEntry &entry) { return uint64_t(width > entry.width ? width - entry.width : entry.width - width) + uint64_t(height > entry.height ? height - entry.height : entry.height - height); }; const IconEntry *selected = nullptr; uint64_t nearest = std::numeric_limits::max(); const uint16_t depth = monochrome ? 1 : display.depth; for (const auto &entry : entries) { const auto delta = distance(entry); if (delta < nearest) { nearest = delta; selected = &entry; } else if (delta == nearest && ((entry.depth <= depth && (selected->depth > depth || entry.depth > selected->depth)) || (entry.depth > depth && selected->depth > depth && entry.depth < selected->depth))) { selected = &entry; } } return selected ? selected->id : 0; } }