Program Listing for File colors.cpp

Return to documentation for file (src/core/colors.cpp)

/*
    Colors.cpp - Color handling utilities.
*/

#include "../utility.h"
#include "colors.h"

namespace PixelMaestro {

    void Colors::generate_comet(RGB *array, uint8_t array_size, RGB &body_color, RGB &tail_color, uint8_t comet_start, uint8_t comet_length) {
        // Start by enforcing some requirements. The comet can't be bigger than the array.
        uint8_t start = comet_start;
        uint8_t length = comet_length;
        while (start >= array_size - 1) {
            --start;
        }
        while (start + length >= array_size - 2) {
            --length;
        }

        // First, set the array's background color to black.
        Colors::RGB black = {0, 0, 0};
        for (uint8_t i = 0; i < array_size; i++) {
            array[i] = black;
        }

        // Next, fade from the comet's start to the comet's body
        generate_scaling_color_array(&array[0], black, body_color, start, false);
        array[comet_start] = body_color;

        // Finally, generate the comet's tail and decrease the brightness
        generate_scaling_color_array(&array[start + 1], body_color, tail_color, length, false);
        float diff = 1 / (float)-length;
        for (uint8_t i = 0; i < length; i++) {
            array[start + 1 + i].r += array[start + 1 + i].r * (diff * i);
            array[start + 1 + i].g += array[start + 1 + i].g * (diff * i);
            array[start + 1 + i].b += array[start + 1 + i].b * (diff * i);
        }
    }


    Colors::RGB Colors::generate_random_color() {
        return RGB {
            (uint8_t)(Utility::rand(255)),
            (uint8_t)(Utility::rand(255)),
            (uint8_t)(Utility::rand(255))
        };
    }

    void Colors::generate_random_color_array(RGB* array, uint8_t num_colors) {
        for (uint8_t i = 0; i < num_colors; i++) {
            array[i] = generate_random_color();
        }
    }

    void Colors::generate_scaling_color_array(RGB* array, RGB& base_color, RGB& target_color, uint8_t num_colors, bool mirror) {
        if (mirror) {
            num_colors /= 2;
        }

        // Calculate the distance between each color.
        int16_t step[] = {
            (int16_t)((target_color.r - base_color.r) / (float)num_colors),
            (int16_t)((target_color.g - base_color.g) / (float)num_colors),
            (int16_t)((target_color.b - base_color.b) / (float)num_colors)
        };

        // Apply the step distance to each index of the array.
        for (uint8_t i = 0; i < num_colors; i++) {
            array[i].r = base_color.r + (step[0] * i);
            array[i].g = base_color.g + (step[1] * i);
            array[i].b = base_color.b + (step[2] * i);
        }

        if (mirror) {
            // Handle the middle color.
            array[num_colors].r = base_color.r + (step[0] * num_colors);
            array[num_colors].g = base_color.g + (step[1] * num_colors);
            array[num_colors].b = base_color.b + (step[2] * num_colors);

            // Repeat the first half of the array in reverse for each remaining color.
            for (uint8_t i = num_colors + 1; i < (num_colors * 2) + 1; i++) {
                array[i].r = array[num_colors - (i - num_colors)].r;
                array[i].g = array[num_colors - (i - num_colors)].g;
                array[i].b = array[num_colors - (i - num_colors)].b;
            }
        }

        // Handle odd number of colors
        if (num_colors % 2 != 0) {
            if (mirror) {
                array[num_colors * 2] = base_color;
            }
            else {
                array[num_colors * 2] = target_color;
            }
        }
    }

    Colors::RGB Colors::mix_colors(const RGB& color_one, const RGB& color_two, MixMode mode, uint8_t alpha) {
        RGB mixed_color;

        switch (mode) {
            case MixMode::Alpha:
            {
                float alpha_pct = alpha / (float)255;
                float alpha_pct_inv = 1 - alpha_pct;
                mixed_color.r = (alpha_pct * color_two.r) + (alpha_pct_inv * color_one.r);
                mixed_color.g = (alpha_pct * color_two.g) + (alpha_pct_inv * color_one.g);
                mixed_color.b = (alpha_pct * color_two.b) + (alpha_pct_inv * color_one.b);
                break;
            }
            case MixMode::Multiply:
            {
                mixed_color.r = color_one.r * (float)(color_two.r / (float)255);
                mixed_color.g = color_one.g * (float)(color_two.g / (float)255);
                mixed_color.b = color_one.b * (float)(color_two.b / (float)255);
                break;
            }
            case MixMode::Overlay:
            {
                if (color_two != Colors::RGB {0, 0, 0}) {
                    mixed_color = color_two;
                }
                else {
                    mixed_color = color_one;
                }
                break;
            }
            default:    // Return color_one
                mixed_color = color_one;
                break;
        };

        return mixed_color;
    }
}