Color

❎ Create a image::Color structure containing three unsigned bytes named after the primary colors used in the led matrix: r, g and b.

❎ Since copying a Color (3 bytes) is cheap, make it automatically derive Copy (and Clone which is needed for Copy).

❎ The default initialization of the structure would set all integer fields to 0, which is a perfect default for a color as it represents black. Make Color automatically derive Default.

❎ Implement three public constants Color::RED, Color::GREEN and Color::BLUE initialized to the correct values for those three primary colors.

⚠️ If you put the code for your image module inside a file names image.rs, do not use pub mod image { … } inside this file. Otherwise, you will end up with a image::image module, which is not what you want. The image.rs must contain the content of the image module directly inside the file.

Gamma correction

The led matrix requires some gamma correction to represent colors as our eyes can see them. This gamma correction table works fine with our led matrix.

❎ Add a gamma module to the project containing the above-mentioned gamma table and a function pub fn gamma_correct(x: u8) -> u8 which returns the corresponding value in the table.

❎ Implement a pub fn gamma_correct(&self) -> Self method on Color which applies the gamma::gamma_correct correction to all components of a color.

Color multiplication and division

We would like to be able to take a color and make it more or less vibrant by multiplying or dividing it by a floating point value. Since we do not have access to the standard library, we will implement traits coming directly from core::ops instead of importing them from std::ops.

However, in no_std mode we do not have access to some standard operations on floating point operands, such as f32::round(). We will have to use an external crate such as micromath to get those operations.

❎ Add the micromath crate to your project, and use micromath::F32Ext in your image module to get common operations back.

❎ Implement the trait core::ops::Mul<f32> on Color and make it return another Color whose individual components are multiplied by the given floating point value. You might want to use a helper function to ensure that each component stays within the range of an u8. Make sure you properly round to the closest value. Also, f32::clamp() might be of some use.

❎ Implement the trait core::ops::Div<f32> on Color and make it return another Color whose individual components are divided by the given floating point value. Note that you can use the multiplication defined above to implement this more concisely.

That's it, our Color type is complete.

🦀 Note on traits, visibility, and namespace pollution

When you use micromath::F32Ext;, you bring the F32Ext trait into the current namespace. This trait declares methods such as round(), and it is implemented on the f32 type. So importing F32Ext into the current namespace makes the round() method available on f32 values.

But note that altough you do not use the F32Ext name explicitely in your code, you still "pollute" your namespace with this name. To prevent this, you might import the F32Ext trait but bind it to no name by renaming it to _:

// Import the F32Ext trait without importing the F32Ext name
use micromath::F32Ext as _;