diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index f6a2091ea6..509229d413 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -233,6 +233,7 @@ _doxygen_input_files = [ # keep-sorted: start
"$dir_pw_crypto/public/pw_crypto/sha256.h",
"$dir_pw_digital_io/public/pw_digital_io/digital_io.h",
"$dir_pw_digital_io/public/pw_digital_io/digital_io_mock.h",
+ "$dir_pw_display/public/pw_display/color.h",
"$dir_pw_function/public/pw_function/function.h",
"$dir_pw_function/public/pw_function/pointer.h",
"$dir_pw_function/public/pw_function/scope_guard.h",
diff --git a/pw_display/BUILD.bazel b/pw_display/BUILD.bazel
index e3ed04fe68..bc7f22883f 100644
--- a/pw_display/BUILD.bazel
+++ b/pw_display/BUILD.bazel
@@ -12,6 +12,27 @@
# License for the specific language governing permissions and limitations under
# the License.
+load(
+ "@pigweed//pw_build:pigweed.bzl",
+ "pw_cc_test",
+)
+
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
+
+cc_library(
+ name = "color",
+ hdrs = [
+ "public/pw_display/color.h",
+ "public/pw_display/colors_endesga64.h",
+ "public/pw_display/colors_pico8.h",
+ ],
+ includes = ["public"],
+)
+
+pw_cc_test(
+ name = "color_test",
+ srcs = ["color_test.cc"],
+ deps = [":color"],
+)
diff --git a/pw_display/BUILD.gn b/pw_display/BUILD.gn
index 54b0f34784..cd2082cea0 100644
--- a/pw_display/BUILD.gn
+++ b/pw_display/BUILD.gn
@@ -19,10 +19,31 @@ import("$dir_pw_docgen/docs.gni")
import("$dir_pw_sync/backend.gni")
import("$dir_pw_unit_test/test.gni")
+config("public_include_path") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("color") {
+ public_configs = [ ":public_include_path" ]
+ public = [
+ "public/pw_display/color.h",
+ "public/pw_display/colors_endesga64.h",
+ "public/pw_display/colors_pico8.h",
+ ]
+}
+
+pw_test("color_test") {
+ deps = [ ":color" ]
+ sources = [ "color_test.cc" ]
+}
+
pw_test_group("tests") {
- tests = []
+ tests = [ ":color_test" ]
}
pw_doc_group("docs") {
- sources = [ "docs.rst" ]
+ sources = [
+ "api.rst",
+ "docs.rst",
+ ]
}
diff --git a/pw_display/CMakeLists.txt b/pw_display/CMakeLists.txt
index aae99b7e4e..995a2a7429 100644
--- a/pw_display/CMakeLists.txt
+++ b/pw_display/CMakeLists.txt
@@ -13,3 +13,22 @@
# the License.
include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_library(pw_display.color INTERFACE
+ HEADERS
+ public/pw_display/color.h
+ public/pw_display/colors_endesga64.h
+ public/pw_display/colors_pico8.h
+ PUBLIC_INCLUDES
+ public
+)
+
+pw_add_test(pw_display.color_test
+ SOURCES
+ color_test.cc
+ PRIVATE_DEPS
+ pw_display.color
+ GROUPS
+ modules
+ pw_display
+)
diff --git a/pw_display/api.rst b/pw_display/api.rst
new file mode 100644
index 0000000000..7de54bd7af
--- /dev/null
+++ b/pw_display/api.rst
@@ -0,0 +1,363 @@
+.. _module-pw_display-api:
+
+=============
+API reference
+=============
+.. pigweed-module-subpage::
+ :name: pw_display
+
+.. _module-pw_display-api-color:
+
+---------
+Color API
+---------
+.. doxygengroup:: pw_display_color
+ :members:
+
+---------------
+Color Constants
+---------------
+
+.. role:: raw-html(raw)
+ :format: html
+
+``pw_display/colors_pico8.h``
+=============================
+.. list-table::
+ :widths: 40 40 20
+ :header-rows: 1
+
+ * - Constant
+ - Color
+ - Hex Value
+ * - ``pw::display::colors::rgb565::pico8::kBlack``
+ - :raw-html:`Black`
+ - ``#000000``
+ * - ``pw::display::colors::rgb565::pico8::kDarkBlue``
+ - :raw-html:`Dark blue`
+ - ``#1d2b53``
+ * - ``pw::display::colors::rgb565::pico8::kDarkPurple``
+ - :raw-html:`Dark purple`
+ - ``#7e2553``
+ * - ``pw::display::colors::rgb565::pico8::kDarkGreen``
+ - :raw-html:`Dark green`
+ - ``#008751``
+ * - ``pw::display::colors::rgb565::pico8::kBrown``
+ - :raw-html:`Brown`
+ - ``#ab5236``
+ * - ``pw::display::colors::rgb565::pico8::kDarkGray``
+ - :raw-html:`Dark gray`
+ - ``#5f574f``
+ * - ``pw::display::colors::rgb565::pico8::kLightGray``
+ - :raw-html:`Light gray`
+ - ``#c2c3c7``
+ * - ``pw::display::colors::rgb565::pico8::kWhite``
+ - :raw-html:`White`
+ - ``#fff1e8``
+ * - ``pw::display::colors::rgb565::pico8::kRed``
+ - :raw-html:`Red`
+ - ``#ff004d``
+ * - ``pw::display::colors::rgb565::pico8::kOrange``
+ - :raw-html:`Orange`
+ - ``#ffa300``
+ * - ``pw::display::colors::rgb565::pico8::kYellow``
+ - :raw-html:`Yellow`
+ - ``#ffec27``
+ * - ``pw::display::colors::rgb565::pico8::kGreen``
+ - :raw-html:`Green`
+ - ``#00e436``
+ * - ``pw::display::colors::rgb565::pico8::kBlue``
+ - :raw-html:`Blue`
+ - ``#29adff``
+ * - ``pw::display::colors::rgb565::pico8::kIndigo``
+ - :raw-html:`Indigo`
+ - ``#83769c``
+ * - ``pw::display::colors::rgb565::pico8::kPink``
+ - :raw-html:`Pink`
+ - ``#ff77a8``
+ * - ``pw::display::colors::rgb565::pico8::kPeach``
+ - :raw-html:`Peach`
+ - ``#ffccaa``
+
+``pw_display/colors_endesga64.h``
+=================================
+.. list-table::
+ :widths: 40 40 20
+ :header-rows: 1
+
+ * - Constant
+ - Color
+ - Hex Value
+
+ * - ``pw::display::colors::rgb565::e64::kBlood``
+ - :raw-html:` `
+ - ``#ff0040``
+ * - ``pw::display::colors::rgb565::e64::kBlack0``
+ - :raw-html:` `
+ - ``#131313``
+ * - ``pw::display::colors::rgb565::e64::kBlack1``
+ - :raw-html:` `
+ - ``#1b1b1b``
+ * - ``pw::display::colors::rgb565::e64::kGray0``
+ - :raw-html:` `
+ - ``#272727``
+ * - ``pw::display::colors::rgb565::e64::kGray1``
+ - :raw-html:` `
+ - ``#3d3d3d``
+ * - ``pw::display::colors::rgb565::e64::kGray2``
+ - :raw-html:` `
+ - ``#5d5d5d``
+ * - ``pw::display::colors::rgb565::e64::kGray3``
+ - :raw-html:` `
+ - ``#858585``
+ * - ``pw::display::colors::rgb565::e64::kGray4``
+ - :raw-html:` `
+ - ``#b4b4b4``
+ * - ``pw::display::colors::rgb565::e64::kWhite``
+ - :raw-html:` `
+ - ``#ffffff``
+ * - ``pw::display::colors::rgb565::e64::kSteel6``
+ - :raw-html:` `
+ - ``#c7cfdd``
+ * - ``pw::display::colors::rgb565::e64::kSteel5``
+ - :raw-html:` `
+ - ``#92a1b9``
+ * - ``pw::display::colors::rgb565::e64::kSteel4``
+ - :raw-html:` `
+ - ``#657392``
+ * - ``pw::display::colors::rgb565::e64::kSteel3``
+ - :raw-html:` `
+ - ``#424c6e``
+ * - ``pw::display::colors::rgb565::e64::kSteel2``
+ - :raw-html:` `
+ - ``#2a2f4e``
+ * - ``pw::display::colors::rgb565::e64::kSteel1``
+ - :raw-html:` `
+ - ``#1a1932``
+ * - ``pw::display::colors::rgb565::e64::kSteel0``
+ - :raw-html:` `
+ - ``#0e071b``
+ * - ``pw::display::colors::rgb565::e64::kCoffee0``
+ - :raw-html:` `
+ - ``#1c121c``
+ * - ``pw::display::colors::rgb565::e64::kCoffee1``
+ - :raw-html:` `
+ - ``#391f21``
+ * - ``pw::display::colors::rgb565::e64::kCoffee2``
+ - :raw-html:` `
+ - ``#5d2c28``
+ * - ``pw::display::colors::rgb565::e64::kCoffee3``
+ - :raw-html:` `
+ - ``#8a4836``
+ * - ``pw::display::colors::rgb565::e64::kCoffee4``
+ - :raw-html:` `
+ - ``#bf6f4a``
+ * - ``pw::display::colors::rgb565::e64::kCoffee5``
+ - :raw-html:` `
+ - ``#e69c69``
+ * - ``pw::display::colors::rgb565::e64::kCoffee6``
+ - :raw-html:` `
+ - ``#f6ca9f``
+ * - ``pw::display::colors::rgb565::e64::kCoffee7``
+ - :raw-html:` `
+ - ``#f9e6cf``
+ * - ``pw::display::colors::rgb565::e64::kOrange3``
+ - :raw-html:` `
+ - ``#edab50``
+ * - ``pw::display::colors::rgb565::e64::kOrange2``
+ - :raw-html:` `
+ - ``#e07438``
+ * - ``pw::display::colors::rgb565::e64::kOrange1``
+ - :raw-html:` `
+ - ``#c64524``
+ * - ``pw::display::colors::rgb565::e64::kOrange0``
+ - :raw-html:` `
+ - ``#8e251d``
+ * - ``pw::display::colors::rgb565::e64::kBrightOrange0``
+ - :raw-html:` `
+ - ``#ff5000``
+ * - ``pw::display::colors::rgb565::e64::kBrightOrange1``
+ - :raw-html:` `
+ - ``#ed7614``
+ * - ``pw::display::colors::rgb565::e64::kBrightOrange2``
+ - :raw-html:` `
+ - ``#ffa214``
+ * - ``pw::display::colors::rgb565::e64::kYellow0``
+ - :raw-html:` `
+ - ``#ffc825``
+ * - ``pw::display::colors::rgb565::e64::kYellow1``
+ - :raw-html:` `
+ - ``#ffeb57``
+ * - ``pw::display::colors::rgb565::e64::kGreen5``
+ - :raw-html:` `
+ - ``#d3fc7e``
+ * - ``pw::display::colors::rgb565::e64::kGreen4``
+ - :raw-html:` `
+ - ``#99e65f``
+ * - ``pw::display::colors::rgb565::e64::kGreen3``
+ - :raw-html:` `
+ - ``#5ac54f``
+ * - ``pw::display::colors::rgb565::e64::kGreen2``
+ - :raw-html:` `
+ - ``#33984b``
+ * - ``pw::display::colors::rgb565::e64::kGreen1``
+ - :raw-html:` `
+ - ``#1e6f50``
+ * - ``pw::display::colors::rgb565::e64::kGreen0``
+ - :raw-html:` `
+ - ``#134c4c``
+ * - ``pw::display::colors::rgb565::e64::kOcean0``
+ - :raw-html:` `
+ - ``#0c2e44``
+ * - ``pw::display::colors::rgb565::e64::kOcean1``
+ - :raw-html:` `
+ - ``#00396d``
+ * - ``pw::display::colors::rgb565::e64::kOcean2``
+ - :raw-html:` `
+ - ``#0069aa``
+ * - ``pw::display::colors::rgb565::e64::kOcean3``
+ - :raw-html:` `
+ - ``#0098dc``
+ * - ``pw::display::colors::rgb565::e64::kOcean4``
+ - :raw-html:` `
+ - ``#00cdf9``
+ * - ``pw::display::colors::rgb565::e64::kOcean5``
+ - :raw-html:` `
+ - ``#0cf1ff``
+ * - ``pw::display::colors::rgb565::e64::kOcean6``
+ - :raw-html:` `
+ - ``#94fdff``
+ * - ``pw::display::colors::rgb565::e64::kCandyGrape3``
+ - :raw-html:` `
+ - ``#fdd2ed``
+ * - ``pw::display::colors::rgb565::e64::kCandyGrape2``
+ - :raw-html:` `
+ - ``#f389f5``
+ * - ``pw::display::colors::rgb565::e64::kCandyGrape1``
+ - :raw-html:` `
+ - ``#db3ffd``
+ * - ``pw::display::colors::rgb565::e64::kCandyGrape0``
+ - :raw-html:` `
+ - ``#7a09fa``
+ * - ``pw::display::colors::rgb565::e64::kRoyalBlue2``
+ - :raw-html:` `
+ - ``#3003d9``
+ * - ``pw::display::colors::rgb565::e64::kRoyalBlue1``
+ - :raw-html:` `
+ - ``#0c0293``
+ * - ``pw::display::colors::rgb565::e64::kRoyalBlue0``
+ - :raw-html:` `
+ - ``#03193f``
+ * - ``pw::display::colors::rgb565::e64::kPurple0``
+ - :raw-html:` `
+ - ``#3b1443``
+ * - ``pw::display::colors::rgb565::e64::kPurple1``
+ - :raw-html:` `
+ - ``#622461``
+ * - ``pw::display::colors::rgb565::e64::kPurple2``
+ - :raw-html:` `
+ - ``#93388f``
+ * - ``pw::display::colors::rgb565::e64::kPurple3``
+ - :raw-html:` `
+ - ``#ca52c9``
+ * - ``pw::display::colors::rgb565::e64::kSalmon0``
+ - :raw-html:` `
+ - ``#c85086``
+ * - ``pw::display::colors::rgb565::e64::kSalmon1``
+ - :raw-html:` `
+ - ``#f68187``
+ * - ``pw::display::colors::rgb565::e64::kRed4``
+ - :raw-html:` `
+ - ``#f5555d``
+ * - ``pw::display::colors::rgb565::e64::kRed3``
+ - :raw-html:` `
+ - ``#ea323c``
+ * - ``pw::display::colors::rgb565::e64::kRed2``
+ - :raw-html:` `
+ - ``#c42430``
+ * - ``pw::display::colors::rgb565::e64::kRed1``
+ - :raw-html:` `
+ - ``#891e2b``
+ * - ``pw::display::colors::rgb565::e64::kRed0``
+ - :raw-html:` `
+ - ``#571c27``
diff --git a/pw_display/color_test.cc b/pw_display/color_test.cc
new file mode 100644
index 0000000000..63884e63d5
--- /dev/null
+++ b/pw_display/color_test.cc
@@ -0,0 +1,61 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_display/color.h"
+
+#include "gtest/gtest.h"
+#include "pw_display/colors_endesga64.h"
+#include "pw_display/colors_pico8.h"
+
+namespace pw::display {
+namespace {
+
+TEST(ColorsPico8Rgb565, EncodedRgb565) {
+ EXPECT_EQ(colors::rgb565::pico8::kDarkBlue, static_cast(0x194a));
+}
+
+TEST(ColorsEndesga64Rgb565, EncodedRgb565) {
+ EXPECT_EQ(colors::rgb565::e64::kBlood, static_cast(0xf808));
+ EXPECT_EQ(colors::rgb565::e64::kRed0, static_cast(0x50e4));
+}
+
+TEST(ColorToRGB565, FromRGB) {
+ EXPECT_EQ(EncodeRgb565(0x1d, 0x2b, 0x53), colors::rgb565::pico8::kDarkBlue);
+ // Check each channel
+ EXPECT_EQ(EncodeRgb565(0xff, 0x00, 0x00),
+ static_cast(0b1111100000000000));
+ EXPECT_EQ(EncodeRgb565(0x00, 0xff, 0x00),
+ static_cast(0b0000011111100000));
+ EXPECT_EQ(EncodeRgb565(0x00, 0x00, 0xff),
+ static_cast(0b0000000000011111));
+}
+
+TEST(ColorToRGBA8888, FromRGB) {
+ EXPECT_EQ(EncodeRgba8888(0xff, 0x00, 0x00, 0x00),
+ static_cast(0b00000000000000000000000011111111));
+ EXPECT_EQ(EncodeRgba8888(0x00, 0xff, 0x00, 0x00),
+ static_cast(0b00000000000000001111111100000000));
+ EXPECT_EQ(EncodeRgba8888(0x00, 0x00, 0xff, 0x00),
+ static_cast(0b00000000111111110000000000000000));
+ EXPECT_EQ(EncodeRgba8888(0x00, 0x00, 0x00, 0xff),
+ static_cast(0b11111111000000000000000000000000));
+}
+
+TEST(ColorToRGB565, FromRGBA8888) {
+ EXPECT_EQ(EncodeRgb565(static_cast(0xff43143b)),
+ colors::rgb565::e64::kPurple0);
+}
+
+} // namespace
+} // namespace pw::display
diff --git a/pw_display/docs.rst b/pw_display/docs.rst
index 5f6ce5e472..f920bcabc1 100644
--- a/pw_display/docs.rst
+++ b/pw_display/docs.rst
@@ -7,4 +7,50 @@ pw_display
:name: pw_display
.. attention::
- This module is undergoing implementation as part of SEED :ref:`seed-0104`.
+ This module's initial implementation is informed by SEED :ref:`seed-0104`,
+ but is being reevaluated as it is migrated from `Pigweed's experimental
+ repsitory `_.
+
+---------
+Libraries
+---------
+
+Color
+-----
+.. seealso::
+ Color API: :ref:`module-pw_display-api-color`
+
+The color library defines base pixel format types and the `ColorRgba` class for
+converting between various types.
+
+Display controllers often support a variety of data formats for representing a
+single pixel. For example:
+
+256 color grayscale:
+ 8 bits per pixel for a total of 256 shades of gray
+4k color: RGB444
+ 12 bits total; 4 bits for each color: red, green, blue
+65k color: RGB565
+ 16 bits total; 5 bits for red, 6 bits for green and 5 bits for blue
+262k color: RGB666
+ 18 bits total; 6 bits for each color: red, green, blue
+16.7M color: RGB888
+ 24 bits total; 8 bits for each color: red, green, blue
+
+.. note::
+ ``pw_display`` drawing libraries will initially only operate on RGB565 pixels
+ for a few reasons:
+
+ - 16 bits per color is easily represented as a single 16 bit unsigned
+ integer. No special framebuffer data packing logic is needed for 100%
+ memory utilization.
+ - RGB565 is 65k color which is a good compromise on color fidelity and memory
+ footprint.
+ - RGB565 has wide support by common display controllers used in the embedded
+ space.
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api
diff --git a/pw_display/public/pw_display/color.h b/pw_display/public/pw_display/color.h
new file mode 100644
index 0000000000..0b7b0d162a
--- /dev/null
+++ b/pw_display/public/pw_display/color.h
@@ -0,0 +1,78 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include
+#include
+
+#include
+#include
+
+namespace pw::display {
+
+/// Base type for pixels in RGBA8888 format.
+using ColorRgba8888 = uint32_t;
+
+/// Base type for pixels in RGB565 format.
+using ColorRgb565 = uint16_t;
+
+/// @defgroup pw_display_color
+/// Color conversion functions used by the pw_display draw library and
+/// tests.
+/// @{
+
+/// Encode an RGB565 value from individual red, green, blue and alpha
+/// values.
+///
+/// This will introduce some loss in color as values are mapped from 8
+/// bits per color down to 5 for red, 6 for green, and 5 for blue.
+constexpr ColorRgb565 EncodeRgb565(uint8_t r, uint8_t g, uint8_t b) {
+ return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
+}
+
+/// Encode an RGBA8888 value into RGB565.
+constexpr ColorRgb565 EncodeRgb565(ColorRgba8888 rgba8888) {
+ uint8_t r = (rgba8888 & 0xFF);
+ uint8_t g = (rgba8888 & 0xFF00) >> 8;
+ uint8_t b = (rgba8888 & 0xFF0000) >> 16;
+ // Alpha is ignored for RGB565.
+ return EncodeRgb565(r, g, b);
+}
+
+/// Encode an RGBA8888 value from individual red, green, blue and
+/// alpha values.
+constexpr ColorRgba8888 EncodeRgba8888(uint8_t r,
+ uint8_t g,
+ uint8_t b,
+ uint8_t a) {
+ return (a << 24) | (b << 16) | (g << 8) | r;
+}
+
+/// Encode an RGBA8888 value from RGB565.
+///
+/// This will scale each color up to 8 bits per pixel. Red and blue
+/// are scaled from 5 bits to 8 bits. Green from 6 bits to 8
+/// bits. There is no alpha channel in the RGB565 format so alpha is
+/// set to 255 representing 100% opaque.
+constexpr ColorRgba8888 EncodeRgba8888(ColorRgb565 rgb565) {
+ uint8_t r = 255 * ((rgb565 & 0xF800) >> 11) / 31;
+ uint8_t g = 255 * ((rgb565 & 0x7E0) >> 5) / 63;
+ uint8_t b = 255 * (rgb565 & 0x1F) / 31;
+ uint8_t a = 255;
+ return EncodeRgba8888(r, g, b, a);
+}
+
+/// @}
+
+} // namespace pw::display
diff --git a/pw_display/public/pw_display/colors_endesga64.h b/pw_display/public/pw_display/colors_endesga64.h
new file mode 100644
index 0000000000..b984189126
--- /dev/null
+++ b/pw_display/public/pw_display/colors_endesga64.h
@@ -0,0 +1,93 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_display/color.h"
+
+namespace pw::display::colors::rgb565::e64 {
+
+/// @defgroup pw_display_color_palette_endesga64
+/// Named colors for the Endesga64 palette.
+/// @{
+
+// clang-format off
+constexpr ColorRgb565 kBlood = EncodeRgb565(0xff, 0x00, 0x40);
+constexpr ColorRgb565 kBlack0 = EncodeRgb565(0x13, 0x13, 0x13);
+constexpr ColorRgb565 kBlack1 = EncodeRgb565(0x1b, 0x1b, 0x1b);
+constexpr ColorRgb565 kGray0 = EncodeRgb565(0x27, 0x27, 0x27);
+constexpr ColorRgb565 kGray1 = EncodeRgb565(0x3d, 0x3d, 0x3d);
+constexpr ColorRgb565 kGray2 = EncodeRgb565(0x5d, 0x5d, 0x5d);
+constexpr ColorRgb565 kGray3 = EncodeRgb565(0x85, 0x85, 0x85);
+constexpr ColorRgb565 kGray4 = EncodeRgb565(0xb4, 0xb4, 0xb4);
+constexpr ColorRgb565 kWhite = EncodeRgb565(0xff, 0xff, 0xff);
+constexpr ColorRgb565 kSteel6 = EncodeRgb565(0xc7, 0xcf, 0xdd);
+constexpr ColorRgb565 kSteel5 = EncodeRgb565(0x92, 0xa1, 0xb9);
+constexpr ColorRgb565 kSteel4 = EncodeRgb565(0x65, 0x73, 0x92);
+constexpr ColorRgb565 kSteel3 = EncodeRgb565(0x42, 0x4c, 0x6e);
+constexpr ColorRgb565 kSteel2 = EncodeRgb565(0x2a, 0x2f, 0x4e);
+constexpr ColorRgb565 kSteel1 = EncodeRgb565(0x1a, 0x19, 0x32);
+constexpr ColorRgb565 kSteel0 = EncodeRgb565(0x0e, 0x07, 0x1b);
+constexpr ColorRgb565 kCoffee0 = EncodeRgb565(0x1c, 0x12, 0x1c);
+constexpr ColorRgb565 kCoffee1 = EncodeRgb565(0x39, 0x1f, 0x21);
+constexpr ColorRgb565 kCoffee2 = EncodeRgb565(0x5d, 0x2c, 0x28);
+constexpr ColorRgb565 kCoffee3 = EncodeRgb565(0x8a, 0x48, 0x36);
+constexpr ColorRgb565 kCoffee4 = EncodeRgb565(0xbf, 0x6f, 0x4a);
+constexpr ColorRgb565 kCoffee5 = EncodeRgb565(0xe6, 0x9c, 0x69);
+constexpr ColorRgb565 kCoffee6 = EncodeRgb565(0xf6, 0xca, 0x9f);
+constexpr ColorRgb565 kCoffee7 = EncodeRgb565(0xf9, 0xe6, 0xcf);
+constexpr ColorRgb565 kOrange3 = EncodeRgb565(0xed, 0xab, 0x50);
+constexpr ColorRgb565 kOrange2 = EncodeRgb565(0xe0, 0x74, 0x38);
+constexpr ColorRgb565 kOrange1 = EncodeRgb565(0xc6, 0x45, 0x24);
+constexpr ColorRgb565 kOrange0 = EncodeRgb565(0x8e, 0x25, 0x1d);
+constexpr ColorRgb565 kBrightOrange0 = EncodeRgb565(0xff, 0x50, 0x00);
+constexpr ColorRgb565 kBrightOrange1 = EncodeRgb565(0xed, 0x76, 0x14);
+constexpr ColorRgb565 kBrightOrange2 = EncodeRgb565(0xff, 0xa2, 0x14);
+constexpr ColorRgb565 kYellow0 = EncodeRgb565(0xff, 0xc8, 0x25);
+constexpr ColorRgb565 kYellow1 = EncodeRgb565(0xff, 0xeb, 0x57);
+constexpr ColorRgb565 kGreen5 = EncodeRgb565(0xd3, 0xfc, 0x7e);
+constexpr ColorRgb565 kGreen4 = EncodeRgb565(0x99, 0xe6, 0x5f);
+constexpr ColorRgb565 kGreen3 = EncodeRgb565(0x5a, 0xc5, 0x4f);
+constexpr ColorRgb565 kGreen2 = EncodeRgb565(0x33, 0x98, 0x4b);
+constexpr ColorRgb565 kGreen1 = EncodeRgb565(0x1e, 0x6f, 0x50);
+constexpr ColorRgb565 kGreen0 = EncodeRgb565(0x13, 0x4c, 0x4c);
+constexpr ColorRgb565 kOcean0 = EncodeRgb565(0x0c, 0x2e, 0x44);
+constexpr ColorRgb565 kOcean1 = EncodeRgb565(0x00, 0x39, 0x6d);
+constexpr ColorRgb565 kOcean2 = EncodeRgb565(0x00, 0x69, 0xaa);
+constexpr ColorRgb565 kOcean3 = EncodeRgb565(0x00, 0x98, 0xdc);
+constexpr ColorRgb565 kOcean4 = EncodeRgb565(0x00, 0xcd, 0xf9);
+constexpr ColorRgb565 kOcean5 = EncodeRgb565(0x0c, 0xf1, 0xff);
+constexpr ColorRgb565 kOcean6 = EncodeRgb565(0x94, 0xfd, 0xff);
+constexpr ColorRgb565 kCandyGrape3 = EncodeRgb565(0xfd, 0xd2, 0xed);
+constexpr ColorRgb565 kCandyGrape2 = EncodeRgb565(0xf3, 0x89, 0xf5);
+constexpr ColorRgb565 kCandyGrape1 = EncodeRgb565(0xdb, 0x3f, 0xfd);
+constexpr ColorRgb565 kCandyGrape0 = EncodeRgb565(0x7a, 0x09, 0xfa);
+constexpr ColorRgb565 kRoyalBlue2 = EncodeRgb565(0x30, 0x03, 0xd9);
+constexpr ColorRgb565 kRoyalBlue1 = EncodeRgb565(0x0c, 0x02, 0x93);
+constexpr ColorRgb565 kRoyalBlue0 = EncodeRgb565(0x03, 0x19, 0x3f);
+constexpr ColorRgb565 kPurple0 = EncodeRgb565(0x3b, 0x14, 0x43);
+constexpr ColorRgb565 kPurple1 = EncodeRgb565(0x62, 0x24, 0x61);
+constexpr ColorRgb565 kPurple2 = EncodeRgb565(0x93, 0x38, 0x8f);
+constexpr ColorRgb565 kPurple3 = EncodeRgb565(0xca, 0x52, 0xc9);
+constexpr ColorRgb565 kSalmon0 = EncodeRgb565(0xc8, 0x50, 0x86);
+constexpr ColorRgb565 kSalmon1 = EncodeRgb565(0xf6, 0x81, 0x87);
+constexpr ColorRgb565 kRed4 = EncodeRgb565(0xf5, 0x55, 0x5d);
+constexpr ColorRgb565 kRed3 = EncodeRgb565(0xea, 0x32, 0x3c);
+constexpr ColorRgb565 kRed2 = EncodeRgb565(0xc4, 0x24, 0x30);
+constexpr ColorRgb565 kRed1 = EncodeRgb565(0x89, 0x1e, 0x2b);
+constexpr ColorRgb565 kRed0 = EncodeRgb565(0x57, 0x1c, 0x27);
+// clang-format on
+
+/// @}
+
+} // namespace pw::display::colors::rgb565::e64
diff --git a/pw_display/public/pw_display/colors_pico8.h b/pw_display/public/pw_display/colors_pico8.h
new file mode 100644
index 0000000000..3def9a99db
--- /dev/null
+++ b/pw_display/public/pw_display/colors_pico8.h
@@ -0,0 +1,45 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_display/color.h"
+
+namespace pw::display::colors::rgb565::pico8 {
+
+/// @defgroup pw_display_color_palette_pico8
+/// Named colors for the Pico-8 palette.
+/// @{
+
+// clang-format off
+constexpr ColorRgb565 kBlack = EncodeRgb565(0x00, 0x00, 0x00);
+constexpr ColorRgb565 kDarkBlue = EncodeRgb565(0x1d, 0x2b, 0x53);
+constexpr ColorRgb565 kDarkPurple = EncodeRgb565(0x7e, 0x25, 0x53);
+constexpr ColorRgb565 kDarkGreen = EncodeRgb565(0x00, 0x87, 0x51);
+constexpr ColorRgb565 kBrown = EncodeRgb565(0xab, 0x52, 0x36);
+constexpr ColorRgb565 kDarkGray = EncodeRgb565(0x5f, 0x57, 0x4f);
+constexpr ColorRgb565 kLightGray = EncodeRgb565(0xc2, 0xc3, 0xc7);
+constexpr ColorRgb565 kWhite = EncodeRgb565(0xff, 0xf1, 0xe8);
+constexpr ColorRgb565 kRed = EncodeRgb565(0xff, 0x00, 0x4d);
+constexpr ColorRgb565 kOrange = EncodeRgb565(0xff, 0xa3, 0x00);
+constexpr ColorRgb565 kYellow = EncodeRgb565(0xff, 0xec, 0x27);
+constexpr ColorRgb565 kGreen = EncodeRgb565(0x00, 0xe4, 0x36);
+constexpr ColorRgb565 kBlue = EncodeRgb565(0x29, 0xad, 0xff);
+constexpr ColorRgb565 kIndigo = EncodeRgb565(0x83, 0x76, 0x9c);
+constexpr ColorRgb565 kPink = EncodeRgb565(0xff, 0x77, 0xa8);
+constexpr ColorRgb565 kPeach = EncodeRgb565(0xff, 0xcc, 0xaa);
+// clang-format on
+
+/// @}
+
+} // namespace pw::display::colors::rgb565::pico8