【问题标题】:c++ defined 16bit (high) colorc++定义16位(高)颜色
【发布时间】:2012-11-23 03:26:27
【问题描述】:

我正在开发一个带有 TFT 触摸屏的项目,这个屏幕有一个包含的库。但是经过一些阅读,我仍然没有得到任何东西。在库中有一些关于颜色的定义:

/* some RGB color definitions                                                 */
#define Black           0x0000      /*   0,   0,   0 */
#define Navy            0x000F      /*   0,   0, 128 */
#define DarkGreen       0x03E0      /*   0, 128,   0 */
#define DarkCyan        0x03EF      /*   0, 128, 128 */
#define Maroon          0x7800      /* 128,   0,   0 */
#define Purple          0x780F      /* 128,   0, 128 */
#define Olive           0x7BE0      /* 128, 128,   0 */
#define LightGrey       0xC618      /* 192, 192, 192 */
#define DarkGrey        0x7BEF      /* 128, 128, 128 */
#define Blue            0x001F      /*   0,   0, 255 */
#define Green           0x07E0      /*   0, 255,   0 */
#define Cyan            0x07FF      /*   0, 255, 255 */
#define Red             0xF800      /* 255,   0,   0 */
#define Magenta         0xF81F      /* 255,   0, 255 */
#define Yellow          0xFFE0      /* 255, 255,   0 */
#define White           0xFFFF      /* 255, 255, 255 */
#define Orange          0xFD20      /* 255, 165,   0 */
#define GreenYellow     0xAFE5      /* 173, 255,  47 */
#define Pink                        0xF81F

这些是 16 位颜色。但是它们如何从:0,128,128(深青色)到 0x03EF。我的意思是,如何将 16 位颜色转换为 uint16?这不需要在代码中有 anwser,因为我只想在库中添加一些颜色。一个在线转换器的链接(我找不到)也可以:)

谢谢

【问题讨论】:

  • 每个组件有多少位?另外,发布一些红色的颜色和白色的颜色。
  • 什么意思,“如何将 16 位颜色转换为 uint16?”值 0x03EF 不是 uint16 变量的合理初始化器吗?
  • 我的意思是 RGB 到 Uint 16 的值,所以如果你有 R=128, G=128, B=0 你如何将它转换为 uint 16 ;)
  • > 一个在线转换器的链接(我找不到)也可以 :) 它可能比你的问题最新,但如果它对其他人有帮助,请让我把它留在这里。 .barth-dev.de/online/rgb565-color-picker/#

标签: c++ colors color-picker 16-bit


【解决方案1】:

从这些可以很容易地找出公式:

#define Red             0xF800      /* 255,   0,   0 */  
#define Magenta         0xF81F      /* 255,   0, 255 */
#define Yellow          0xFFE0      /* 255, 255,   0 */

F800 设置了 5 个 MSB 位,而 FFE0 设置了 5 个 LSB 未设置。 0xF81F 显然有 5 个 LSB 和 5 个 MSB 的集合,这证明格式是 RGB565。

将值 173 转换为红色的公式并不像看起来那么简单 - 您不能简单地删除 3 个最低有效位,而是必须进行线性插值以使 255 对应于 31(或绿色 255对应63)。

NewValue = (31 * old_value) / 255;

(这仍然只是一个截断除法——可能需要适当的舍入)

适当的舍入和缩放:

Uint16_value = (((31*(red+4))/255)<<11) | 
               (((63*(green+2))/255)<<5) | 
               ((31*(blue+4))/255);

编辑按照 JasonD 的建议添加括号。

【讨论】:

  • 其实我觉得你错了。为了使原始 256 种颜色完全均匀分布到 32 或 64 的新范围,截断是完全正确的做法。你真的想做 value = (32 * old) / 256,这与转变。值 31 和 255 表示您的范围是 31 和 255,但这忽略了它们从零开始。
  • @JasonD:截断将信号的平均功率平均降低了量化级别的一半。四舍五入避免了这种情况。同样通过对两个量化级别的归纳,我们应该注意到重要的不是位数,而是最小和最大量化级别所代表的值的距离。 2级时距离为1。256级时距离为255级。
  • 如果你使用给定的“适当的舍入和缩放”版本并计算出每个输入值如何映射到输出,那么分布是非常可怕的。一些输出值映射到 9 个输入值,大部分映射到 8,最大值仅映射到 1。通过您的方法量化的简单平滑渐变将非常讨厌。您的舍入几乎没有效果,再现了不均匀的图案,但略有偏移。另一种看待它的方式是,值 '0' 表示值 0 -> 0.999 重复出现的范围。所以 0 - 255 的范围实际上意味着 0 -> 255.999 重复出现,增量约为 256,而不是 255。
  • 我不买递归的解释,因为我们可以很容易地枚举所有量化级别。我建议的公式可以产生可怕的分布,这也许值得改进。但对我来说,更根本的问题是图像的整体强度不会改变(就像简单地截断位时一样)。然后可能需要一个饱和算法:R' = Saturate(R+4) >> 3;
  • 拍摄亮度分布均匀的图像。如果范围是 0 -> 255,则平均值将为 127.5。在您的 5 位量化图像中,平均值将为 15.019531(四舍五入)或 15.003906(非四舍五入)。为了不使图像变暗,您希望看到平均 15.5(即 31 的一半,最大亮度)。您的饱和版本是 15.984375,而是过度亮化。
【解决方案2】:

你需要知道显示的确切格式,仅仅“16位”是不够的。

有 RGB555,其中三个分量中的每一个都有 5 位。这会将总颜色空间减少到只有 32,768 种颜色,浪费了一点点,但管理起来非常简单,因为每个组件都有相同数量的阴影。

还有 RGB565,其中绿色分量被赋予 6 位(因为人眼对绿色更敏感)。这可能是您使用的格式,因为“深绿色”示例是0x03e0,二进制是0b0000 0011 1110 0000。由于那里有 6 位设置为 1,我猜这是绿色组件的总分配并显示它的最大值。

然后是这样的(每四位用空格隔开,并重新使用虚构的0b 前缀):

0bRRRR RGGG GGGB BBBB

当然,字中的位顺序也可以不同。

将三元组数字转换为位压缩字的任务在具有位操作运算符的典型编程语言中很容易完成。

在 C 中,它通常在宏中完成,但我们也可以有一个函数:

#include <stdint.h>

uint16_t rgb565_from_triplet(uint8_t red, uint8_t green, uint8_t blue)
{
  red   >>= 3;
  green >>= 2;
  blue  >>= 3;
  return (red << 11) | (green << 5) | blue;
}

请注意,以上假设组件的完全 8 位精度,因此组件的最大强度为 255,而不是您示例中的 128。如果色彩空间确实使用 7 位组件,则需要进行一些额外的缩放。

【讨论】:

    【解决方案3】:

    看起来您使用的是 RGB565,前 5 位用于红色,然后 6 位用于绿色,然后 5 位用于蓝色。

    您应该使用 0xF800 进行掩码并右移 11 位以获得红色分量(或移动 8 位以获得 0-255 的值)。 使用 0x07E0 进行掩码并右移 5 位以获得绿色分量(或 3 位以获得 0-255 值)。 用 0x001F 掩码得到蓝色分量(左移 3 位得到 0-255)。

    【讨论】:

      【解决方案4】:

      您的颜色是 565 格式。如果你用二进制写出来会更明显。

      • 蓝色,(0,0,255) 为 0x001f,即 00000 000000 11111
      • 绿色,(0, 255, 0)为0x07e0,即00000 111111 00000
      • 红色,(255, 0, 0)为0xf800,即11111 000000 00000

      要将此格式的 24 位颜色转换为 16 位,只需将每个组件所需的高位屏蔽掉,移入位置,然后将 OR 放在一起。

      将 16 位转换回 24 位颜色,屏蔽每个分量,移位,然后将高位复制到低位。

      在您的示例中,似乎某些颜色已被缩放和舍入而不是移动。

      我强烈建议使用位移方法,而不是按 31/255 之类的因子进行缩放,因为位移不仅可能更快,而且应该会产生更好的结果。

      【讨论】:

        【解决方案5】:

        您显示的 3 部分数字适用于 24 位颜色。十六进制的 128 是 0x7f,但在您的颜色定义中,它被表示为 0x0f。同样,255 是 0xff,但在您的颜色定义中,它被表示为 0x1f。这表明您需要获取 3 部分数字并将它们向下移动 3 位(每种颜色丢失 3 位颜色数据)。然后将它们组合成一个 16 位数字:

        uint16 color = ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
        

        ...从前面修改,因为绿色使用 6 位,而不是 5。

        【讨论】:

          【解决方案6】:

          您需要知道每个颜色通道有多少位。所以是的,一种颜色有 16 位,但 RGB 分量都是这些位的某个子集。在您的情况下,红色是 5 位,绿色是 6,蓝色是 5。二进制格式如下所示:

          RRRRRGGG GGGBBBBB
          

          还有其他 16 位颜色格式,例如红色、绿色和蓝色,各为 5 位,然后将剩余的位用作 alpha 值。

          红色和蓝色通道的值范围将从02^5-1 = 31,而绿色的范围将是02^6-1 = 63。因此,要以(0-&gt;255),(0-&gt;255),(0-&gt;255) 的形式转换颜色,您需要将值从一个映射到另一个。例如,0-&gt;255 范围内的红色值128 将映射到范围为0-31 的红色通道中的(128/255) * 31 = 15.6。如果我们向下取整,我们得到15,它用五位表示为01111。同样,对于绿色通道(六位),您将获得011111。所以颜色(128,128,128) 将映射到01111011 11101111,即十六进制的0x7BEF

          您也可以将此应用于其他值:0,128,128 变为 00000011 11101111,即 0x03EF

          【讨论】:

            【解决方案7】:

            代码中显示的颜色是 RGB565。如图所示

            #define Blue            0x001F      /*   0,   0, 255 */ 
            #define Green           0x07E0      /*   0, 255,   0 */ 
            #define Red             0xF800      /* 255,   0,   0 */
            

            如果您只是想在此#defined 列表中添加一些新颜色,从每个通道的 16 位 UINT 转换的最简单方法就是将您的值向下移动以松开低位,然后移动和(或)将它们放到 16bitRGB 值中的位置。 不过,这很可能会产生条带伪影,并且可能会有更好的转换方法。

            UINT16 blue = 0xFF;
            UINT16 green = 0xFF;
            UINT16 red = 0xFF;
            
            
            blue  >>= 11; // top 5 bits
            green >>= 10; // top 6 bits
            red   >>= 11; // top 5 bits
            
            UINT16 RGBvalue = blue | (green <<5) | (red << 11)
            

            您可能需要在移位后屏蔽任何不需要的杂散位,因为我不确定这是如何工作的,但我认为上面的代码应该可以工作。

            【讨论】:

              【解决方案8】:

              基于 unwind 的回答,专门针对使用 Arduino 2.8" TFT Touchscreen(v2) 的 Adafruit GFX 库,您可以将此函数添加到您的 Arduino 草图中,并使用它内联来计算 rgb 的颜色:

              uint16_t getColor(uint8_t red, uint8_t green, uint8_t blue)
              {
                red   >>= 3;
                green >>= 2;
                blue  >>= 3;
                return (red << 11) | (green << 5) | blue;
              }
              

              现在您可以像这样内联使用它,用一个在 x0,y0 处创建一个 20x20 正方形的函数进行说明:

              void setup() {
                tft.begin();
                makeSquare(getColor(20,157,217));
              }
              
              unsigned long makeSquare(uint16_t color1) {
                tft.fillRect(0, 0, 20, 20, color1);
              }
              

              Adafruit GFX 库的文档可以在 here 找到

              【讨论】:

                猜你喜欢
                • 2013-03-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-02-09
                • 1970-01-01
                • 2013-01-24
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多