【问题标题】:Two bytes into one两个字节合一
【发布时间】:2013-08-07 10:56:07
【问题描述】:

首先,如果这是重复的,我深表歉意;但我的 Google-fu 今天似乎让我失望了。

我正在为 Photoshop 编写一个图像格式模块,该格式的保存选项之一包括一个 4 位 Alpha 通道。当然,我必须转换的数据是 8 位/1 字节的 alpha - 所以我基本上需要每两个字节的 alpha,并将其合并为一个。

我的尝试(下),相信还有很大的提升空间:

for(int x=0,w=0;x < alphaData.size();x+=2,w++)
{
     short ashort=(alphaData[x] << 8)+alphaData[x+1];
     alphaFinal[w]=(unsigned char)ashort;
}

alphaData 和 alphaFinal 是分别包含 8 位 alpha 数据和 4 位 alpha 数据的向量。我意识到将两个字节减少为一个的值,必然会导致“分辨率”的损失,但我不禁想到有更好的方法来做到这一点。

有关更多信息,这里是执行相反操作的循环(将 4 位 alpha 从格式转换为 Photoshop 的 8 位)

alphaData 的用途与上述相同,imgData 是一个无符号字符向量,用于保存原始图像数据。 (alpha 数据是在此特定格式变体的图像的实际 rgb 数据之后添加的)

for(int b=alphaOffset,x2=0;b < (alphaOffset+dataLength); b++,x2+=2)
{
    unsigned char lo = (imgData[b] & 15);
    unsigned char hi = ((imgData[b] >> 4) & 15);
    alphaData[x2]=lo*17;
    alphaData[x2+1]=hi*17;
 }

【问题讨论】:

  • +1 仅用于 Google-Fu 文字游戏。

标签: c++ casting bit-manipulation


【解决方案1】:

你确定是吗

alphaData[x2]=lo*17;
alphaData[x2+1]=hi*17;

而不是

alphaData[x2]=lo*16;
alphaData[x2+1]=hi*16;

无论如何,要生成与您发布的解码函数一起使用的值,您只需反转操作即可。所以乘以 17 变成除以 17,移位和掩码被重新排序,如下所示:

for(int x=0,w=0;x < alphaData.size();x+=2,w++)
{
    unsigned char alpha1 = alphaData[x] / 17;
    unsigned char alpha2 = alphaData[x+1] / 17;
    Assert(alpha1 < 16 && alpha2 < 16);
    alphaFinal[w]=(alpha2 << 4) | alpha1;
}

【讨论】:

  • 一开始我以为它会是 *16,除了这样,我测试的第一张图像最终是半透明的。回想起来,我注意到 15(半字节的最高值)* 16 = 240,而不是 255。无论如何,我似乎习惯于忽略显而易见的事情。谢谢,效果很好!
  • 我认为你应该用四舍五入进行除法,即(alphaData[x] + 8) / 17
【解决方案2】:
  short ashort=(alphaData[x] << 8)+alphaData[x+1];
  alphaFinal[w]=(unsigned char)ashort;

您实际上在 alphaFinal 中丢失了 alphaData[x]。您将 alphaData[x] 向左移动 8 位,然后分配 8 个低位。

你的 for 循环也是不安全的,如果由于某种原因 alphaData.size() 是奇怪的,你会超出范围。

【讨论】:

  • 是的,我知道。这是不好的做法 - 但这是我做“草稿”的奇怪方式。但是感谢您对第一部分的解释 - 它肯定有助于解释我得到的结果。
【解决方案3】:

我认为,您想要做的是将 8 位值截断为 4 位值;不要组合两个 8 位值。换句话说,您希望删除每个 alpha 值的四个最低有效位,而不是组合两个不同的 alpha 值。 所以,基本上,你想右移 4。

output = (input >> 4); /* truncate four bits */

如果您不熟悉二进制移位,请使用这个随机的 8 位数字:

10110110
>> 1
= 01011011
>> 1
= 00101101
>> 1
= 00010110
>> 1
= 00001011

所以,

10110110
>> 4
= 00001011

而要反转,则改为左移...

input = (output << 4); /* expand four bits */

使用与以前相同的随机 8 位数字的结果,将是

00001011
>> 4
= 10110000

显然,正如您所指出的,丢失了 4 位精度。但您会惊讶地发现,在一部完全合成的作品中很少有人注意到它。

【讨论】:

    【解决方案4】:

    这段代码

    for(int x=0,w=0;x < alphaData.size();x+=2,w++)
    {
         short ashort=(alphaData[x] << 8)+alphaData[x+1];
         alphaFinal[w]=(unsigned char)ashort;
    }
    

    坏了。给定

    #include <iostream>
    
    using std::cout;
    using std::endl;
    
    typedef unsigned char uchar;
    
    int main() {
        uchar x0 = 1; // for alphaData[x]
        uchar x1 = 2; // for alphaData[x+1]
    
        short ashort    = (x0 << 8) + x1; // The value 0x0102
        uchar afinal  = (uchar)ashort; // truncates to 0x02.
    
        cout << std::hex
             << "x0 = 0x" << x0 << " << 8 = 0x" << (x0 << 8) << endl
             << "x1 = 0x" << x1 << endl
             << "ashort = 0x" << ashort << endl
             << "afinal = 0x" << (unsigned int)afinal << endl
        ;
    }
    

    如果您说您的源流包含存储在 8 位存储值中的 4 位对序列,您需要将其重新存储为单个 8 位值,那么您想要的是:

    for(int x=0,w=0;x < alphaData.size();x+=2,w++)
    {
        unsigned char aleft = alphaData[x] & 0x0f; // 4 bits.
        unsigned char aright = alphaData[x + 1] & 0x0f;  // 4 bits.
        alphaFinal[w] = (aleft << 4) | (aright);
    }
    

    “>4”等价于“/16”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-15
      • 2012-12-16
      • 1970-01-01
      • 2011-02-23
      • 1970-01-01
      • 2016-01-07
      • 2015-04-02
      相关资源
      最近更新 更多