Skip to content

ttldtor/bits

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bits

Library for bit manipulation.

Release License

Examples of usage

xmake

add_rules("mode.debug", "mode.release")

add_repositories("ttldtor https://github.com/ttldtor/xmake-repo.git")
add_requires("bits v1.0.0")

target("test_bits")
    set_kind("binary")
    add_packages("bits")
    add_files("src/*.cpp")
    set_languages("c++20")

CMake

cmake_minimum_required(VERSION 3.16)
project(test_bits LANGUAGES CXX)

include(FetchContent)
FetchContent_Declare(
        bits
        GIT_REPOSITORY https://github.com/ttldtor/bits.git
        GIT_TAG v1.0.0
)
FetchContent_MakeAvailable(bits)

add_executable(${PROJECT_NAME} src/main.cpp)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
target_link_libraries(${PROJECT_NAME} PRIVATE bits)

Code

#include <iostream>
#include <bits/bits.hpp>

using namespace org::ttldtor::bits;

int main(int argc, char** argv) {
    std::cout << "hello world!" << std::endl;
    std::cout << shl(-5, 4) << std::endl;

    return 0;
}

A possibly complete, but probably not very useful example of using this library.

Color Manipulation Example
// Copyright (c) 2025 ttldtor.
// SPDX-License-Identifier: BSL-1.0

/**
 * @file colormanip.cpp
 * @brief Example demonstrating color manipulation using the `bits` library
 *
 * This example shows how to use the `bits` library for:
 * - Packing color components into a single integer
 * - Unpacking color components from an integer
 * - Manipulating individual color channels
 * - Converting between different color formats
 */

#include <bits/bits.hpp>
#include <cstdint>
#include <iomanip>
#include <iostream>

using namespace org::ttldtor::bits;

/**
 * @brief Represents an RGBA color with individual components
 */
struct Color {
  std::uint8_t red;    ///< Red channel (0-255)
  std::uint8_t green;  ///< Green channel (0-255)
  std::uint8_t blue;   ///< Blue channel (0-255)
  std::uint8_t alpha;  ///< Alpha channel (0-255, 0=transparent, 255=opaque)

  /**
   * @brief Packs color components into a 32-bit ARGB value
   * @return 32-bit integer in ARGB format `(Alpha << 24 | Red << 16 | Green << 8 | Blue)`
   *
   * <pre>
   * Layout: [AAAAAAAA][RRRRRRRR][GGGGGGGG][BBBBBBBB]
   *    bits    24-31    16-23      8-15       0-7
   * </pre>
   */
  [[nodiscard]] std::uint32_t pack() const {
    std::uint32_t result = 0;

    // Shift alpha to bits 24-31 and OR with the result
    result = orOp(result, shl(static_cast<std::uint32_t>(alpha), 24));

    // Shift red to bits 16-23 and OR with the result
    result = orOp(result, shl(static_cast<std::uint32_t>(red), 16));

    // Shift green to bits 8-15 and OR with the result
    result = orOp(result, shl(static_cast<std::uint32_t>(green), 8));

    // Blue occupies bits 0-7, no shift needed
    result = orOp(result, static_cast<std::uint32_t>(blue));

    return result;
  }

  /**
   * @brief Unpacks a 32-bit ARGB value into color components
   * @param packed 32-bit integer in ARGB format
   * @return Color struct with unpacked components
   */
  [[nodiscard]] static Color unpack(std::uint32_t packed) {
    Color color{};

    // Extract alpha from bits 24-31
    color.alpha = static_cast<std::uint8_t>(shr(packed, 24));

    // Extract red from bits 16-23
    color.red = static_cast<std::uint8_t>(shr(packed, 16));

    // Extract green from bits 8-15
    color.green = static_cast<std::uint8_t>(shr(packed, 8));

    // Extract blue from bits 0-7 (no shift needed, just mask)
    color.blue = static_cast<std::uint8_t>(packed);

    return color;
  }

  /**
   * @brief Modifies the alpha channel of a packed color
   * @param packed Original packed color value
   * @param alpha New alpha value (0-255)
   * @return New packed color with an updated alpha channel
   */
  [[nodiscard]] static std::uint32_t setAlpha(std::uint32_t packed, std::uint8_t alpha) {
    // Clear the alpha channel (bits 24-31)
    packed = resetBits(packed, 0xFF000000u);

    // Set a new alpha value
    return setBits(packed, shl(static_cast<std::uint32_t>(alpha), 24));
  }

  /**
   * @brief Modifies the red channel of a packed color
   * @param packed Original packed color value
   * @param red New red value (0-255)
   * @return New packed color with an updated red channel
   */
  [[nodiscard]] static std::uint32_t setRed(std::uint32_t packed, std::uint8_t red) {
    // Clear the red channel (bits 16-23)
    packed = resetBits(packed, 0x00FF0000u);

    // Set a new red value
    return setBits(packed, shl(static_cast<std::uint32_t>(red), 16));
  }

  /**
   * @brief Modifies the green channel of a packed color
   * @param packed Original packed color value
   * @param green New green value (0-255)
   * @return New packed color with an updated green channel
   */
  [[nodiscard]] static std::uint32_t setGreen(std::uint32_t packed, std::uint8_t green) {
    // Clear the green channel (bits 8-15)
    packed = resetBits(packed, 0x0000FF00u);

    // Set a new green value
    return setBits(packed, shl(static_cast<std::uint32_t>(green), 8));
  }

  /**
   * @brief Modifies the blue channel of a packed color
   * @param packed Original packed color value
   * @param blue New blue value (0-255)
   * @return New packed color with an updated blue channel
   */
  [[nodiscard]] static std::uint32_t setBlue(std::uint32_t packed, std::uint8_t blue) {
    // Clear the blue channel (bits 0-7)
    packed = resetBits(packed, 0x000000FFu);

    // Set a new blue value
    return setBits(packed, static_cast<std::uint32_t>(blue));
  }

  /**
   * @brief Converts an ARGB format to RGBA format
   * @param argb Color in ARGB format (Alpha << 24 | Red << 16 | Green << 8 | Blue)
   * @return Color in RGBA format (Red << 24 | Green << 16 | Blue << 8 | Alpha)
   */
  [[nodiscard]] static std::uint32_t argbToRgba(std::uint32_t argb) {
    // Extract individual channels
    const auto alpha = static_cast<std::uint8_t>(shr(argb, 24));
    const auto red = static_cast<std::uint8_t>(shr(argb, 16));
    const auto green = static_cast<std::uint8_t>(shr(argb, 8));
    const auto blue = static_cast<std::uint8_t>(argb);

    // Repack in RGBA format
    std::uint32_t rgba = 0;
    rgba = orOp(rgba, shl(static_cast<std::uint32_t>(red), 24));
    rgba = orOp(rgba, shl(static_cast<std::uint32_t>(green), 16));
    rgba = orOp(rgba, shl(static_cast<std::uint32_t>(blue), 8));
    rgba = orOp(rgba, static_cast<std::uint32_t>(alpha));

    return rgba;
  }

  /**
   * @brief Blends two colors using alpha blending
   * @param foreground Foreground color (with alpha)
   * @param background Background color
   * @return Blended color
   *
   * Formula: `result = foreground * alpha + background * (1 - alpha)`
   */
  [[nodiscard]] static Color blend(const Color& foreground, const Color& background) {
    // Convert alpha from 0-255 to 0.0-1.0 range
    const float alpha = static_cast<float>(foreground.alpha) / 255.0f;
    const float invAlpha = 1.0f - alpha;

    Color result{};
    result.red = static_cast<std::uint8_t>(static_cast<float>(foreground.red) * alpha +
                                           static_cast<float>(background.red) * invAlpha);
    result.green = static_cast<std::uint8_t>(static_cast<float>(foreground.green) * alpha +
                                             static_cast<float>(background.green) * invAlpha);
    result.blue = static_cast<std::uint8_t>(static_cast<float>(foreground.blue) * alpha +
                                            static_cast<float>(background.blue) * invAlpha);
    result.alpha = 255;  // Result is fully opaque

    return result;
  }

  /**
   * @brief Checks if the color has an alpha channel (not fully opaque)
   * @param packed Packed color value
   * @return true if alpha < 255
   */
  [[nodiscard]] static bool hasTransparency(std::uint32_t packed) {
    // Extract the alpha channel and check if it's less than 255
    const auto alpha = static_cast<std::uint8_t>(shr(packed, 24));

    return alpha < 255;
  }

  /**
   * @brief Inverts the color (excluding an alpha channel)
   * @param packed Original color
   * @return Inverted color
   */
  [[nodiscard]] static std::uint32_t invert(std::uint32_t packed) {
    // XOR with 0x00FFFFFF to invert RGB channels, leaving alpha unchanged
    return xorOp(packed, 0x00FFFFFFu);
  }

  /**
   * @brief Prints color information
   */
  void print() const {
    std::cout << "RGBA(" << static_cast<int>(red) << ", " << static_cast<int>(green) << ", " << static_cast<int>(blue)
              << ", " << static_cast<int>(alpha) << ")";
  }
};

int main() {
  std::cout << "=== Color Manipulation Example ===" << std::endl;
  std::cout << std::hex << std::uppercase << std::setfill('0');

  // Example 1: Pack and unpack color
  std::cout << "\n--- Example 1: Pack and Unpack ---" << std::endl;
  Color color1{255, 128, 64, 200};
  std::cout << "Original color: ";
  color1.print();
  std::cout << std::endl;

  std::uint32_t packed = color1.pack();
  std::cout << "Packed (ARGB): 0x" << std::setw(8) << packed << std::endl;

  Color unpacked = Color::unpack(packed);
  std::cout << "Unpacked color: ";
  unpacked.print();
  std::cout << std::endl;

  // Example 2: Modify individual channels
  std::cout << "\n--- Example 2: Modify Channels ---" << std::endl;
  std::uint32_t color2 = Color{100, 150, 200, 255}.pack();
  std::cout << "Original: 0x" << std::setw(8) << color2 << " -> ";
  Color::unpack(color2).print();
  std::cout << std::endl;

  color2 = Color::setRed(color2, 255);
  std::cout << "After setRed(255): 0x" << std::setw(8) << color2 << " -> ";
  Color::unpack(color2).print();
  std::cout << std::endl;

  color2 = Color::setAlpha(color2, 128);
  std::cout << "After setAlpha(128): 0x" << std::setw(8) << color2 << " -> ";
  Color::unpack(color2).print();
  std::cout << std::endl;

  // Example 3: Format conversion
  std::cout << "\n--- Example 3: ARGB to RGBA ---" << std::endl;
  std::uint32_t argb = 0xC8FF8040u;  // A=200, R=255, G=128, B=64
  std::cout << "ARGB: 0x" << std::setw(8) << argb << std::endl;

  std::uint32_t rgba = Color::argbToRgba(argb);
  std::cout << "RGBA: 0x" << std::setw(8) << rgba << std::endl;

  // Example 4: Color blending
  std::cout << "\n--- Example 4: Alpha Blending ---" << std::endl;
  Color foreground{255, 0, 0, 128};  // Semi-transparent red
  Color background{0, 0, 255, 255};  // Opaque blue

  std::cout << "Foreground: ";
  foreground.print();
  std::cout << std::endl;

  std::cout << "Background: ";
  background.print();
  std::cout << std::endl;

  Color blended = Color::blend(foreground, background);
  std::cout << "Blended: ";
  blended.print();
  std::cout << std::endl;

  // Example 5: Transparency check
  std::cout << "\n--- Example 5: Transparency Check ---" << std::endl;
  const std::uint32_t opaqueColor = Color{255, 255, 255, 255}.pack();
  const std::uint32_t transparentColor = Color{255, 255, 255, 200}.pack();

  std::cout << "Opaque color (0x" << std::setw(8) << opaqueColor
            << ") has transparency: " << (Color::hasTransparency(opaqueColor) ? "YES" : "NO") << std::endl;
  std::cout << "Transparent color (0x" << std::setw(8) << transparentColor
            << ") has transparency: " << (Color::hasTransparency(transparentColor) ? "YES" : "NO") << std::endl;

  // Example 6: Color inversion
  std::cout << "\n--- Example 6: Color Inversion ---" << std::endl;

  constexpr Color original{255, 128, 64, 255};

  std::cout << "Original: 0x" << std::setw(8) << original.pack() << " -> ";
  original.print();
  std::cout << std::endl;

  const std::uint32_t inverted = Color::invert(original.pack());
  std::cout << "Inverted: 0x" << std::setw(8) << inverted << " -> ";
  Color::unpack(inverted).print();
  std::cout << std::endl;

  // Example 7: Grayscale conversion
  std::cout << "\n--- Example 7: Grayscale Conversion ---" << std::endl;

  constexpr Color colorful{200, 100, 50, 255};

  std::cout << "Colorful: ";
  colorful.print();
  std::cout << std::endl;

  // Convert to grayscale using weighted average
  constexpr auto gray =
    static_cast<std::uint8_t>(0.299f * colorful.red + 0.587f * colorful.green + 0.114f * colorful.blue);

  constexpr Color grayscale{gray, gray, gray, colorful.alpha};

  std::cout << "Grayscale: ";
  grayscale.print();
  std::cout << std::endl;

  return 0;
}