實時的YcoCg-DXT壓縮


實時YCoCg-DXT壓縮

JMP van Waveren 
id Software,Inc.

 


NVIDIA公司

2007年9月14日

©2007,id Software,Inc.

抽象


引入了高性能紋理壓縮技術,利用了當今顯卡上提供的DXT5格式。壓縮技術在DXT1壓縮和無壓縮之間提供了非常好的中間位置。使用DXT5格式,紋理消耗DXT1壓縮紋理的內存量的兩倍(4:1壓縮比而不是8:1)。然而,作爲回報,該技術提供了顯着的質量增益,對於大多數圖像,質量幾乎沒有明顯的損失。特別是,對於柯達無損真彩色圖像套件,RGB-PSNR的增益保持在6 dB以上。此外,該技術允許在當前圖形卡的光柵化期間的實時紋理解壓縮以及CPU和GPU上的高質量實時壓縮。

(譯者注,在Doom3bfg代碼中具有Dxt壓縮算法,對於rage,我覺得應該也是應用了Dxt壓縮算法,來進行對於紋理圖像的解壓縮運算)
 原文下載地址

介紹

   紋理是繪製到幾何形狀上的數字化圖像,以增加視覺細節。在今天的計算機圖形學中,在光柵化過程中將大量的細節映射到幾何圖形上。不僅使用顏色的紋理,還可以指定表面屬性(例如鏡面反射)或精細表面細節(以正常或凹凸貼圖的形式)的紋理。所有這些紋理都可以消耗大量的系統和視頻內存。幸運的是,可以使用壓縮來減少存儲紋理所需的內存量。今天的大多數顯卡允許紋理以各種在光柵化期間實時解壓縮的壓縮格式存儲。一種這樣的格式,它是由大多數圖形卡支持的,是S3TC -也被稱爲DXT壓縮[ 12 ]。

壓縮紋理不僅需要顯卡更少的內存,而且由於帶寬要求降低,通常渲染速度比未壓縮紋理要快。由於壓縮,某些質量可能會丟失。然而,減少的內存佔用允許使用更高分辨率的紋理,使得在質量上實際上可以獲得顯着的增益。

DXT1

在DXT1格式[ 12 ]在圖形卡上的硬件渲染過程中設計用於實時解壓縮。DXT1是具有8:1固定壓縮比的有損壓縮格式。DXT1壓縮是塊截斷編碼(BTC)[ 3 ]的一種形式,其中圖像被劃分爲非重疊塊,並且每個塊中的像素被量化爲有限數量的值。4×4像素塊中的像素的顏色值通過RGB顏色空間在一行上的等距點近似。該行由兩個端點定義,對於4x4塊中的每個像素,2位索引存儲在該行上的一個等距點上。通過顏色空間的線的端點被量化爲16位5:6:5 RGB格式,通過插值生成一個或兩個中間點。DXT1格式允許通過根據終點的順序切換到不同的模式,其中僅生成一箇中間點並指定一個附加顏色,這是黑色和完全透明的。

下圖顯示柯達無損真彩色圖像套件[ 16 ] 的圖像14的一小部分左邊的圖像是原來的。中間的圖像顯示了圖像14的相同部分,其首先被縮小到四分之一的尺寸,然後用雙線性濾波放大到原始尺寸。右圖顯示了壓縮成DXT1格式的相同部分。

切出
柯達圖像套件圖像14 部分
1.5 MB
將圖像縮小
到四分之一的大小。
384 kB

以最好的質量壓縮到DXT1 
192 kB

DXT1壓縮圖像具有比首先縮小到四分之一尺寸的圖像更多的細節,然後用雙線性濾波放大到原始尺寸。此外,DXT1壓縮的圖像使用圖像的一半存儲空間縮小到四分之一的大小。然而,與原始圖像相比,DXT1壓縮圖像的質量明顯下降。

3. CPU上的實時DXT1

DXT1格式設計用於硬件實時解壓縮,而不是實時壓縮。雖然解壓縮非常簡單,但對DXT1格式的壓縮通常需要大量的工作。

有幾種好的DXT壓縮機可用。最顯着的是ATI Compressonator [ 4 ]和NVIDIA DDS Utilities [ 5 ]。兩臺壓縮機都生產高品質的DXT壓縮圖像。然而,這些壓縮機不是開源的,並且它們被優化用於高質量的離線壓縮,並且對於實時使用來說太慢。NVIDIA還發布了一套開源的紋理工具[ 6 ],其中包括DXT壓縮器。這些DXT壓縮機也以超速的質量爲目標。有一個開源的DXT壓縮器可供Roland Scheidegger用於Mesa 3D圖形庫[ 7 ]。另一個好的DXT壓縮器是西蒙·布朗的Squish [ 8 ]。

直到最近,對DXT1格式的實時壓縮並不被認爲是一個可行的選擇。然而,DXT1的實時壓縮是非常有可能的,如[ 9 ] 所示,與最佳DXT1壓縮相比,質量損失對於大多數圖像是合理的。4×4像素塊中的RGB顏色傾向於很好地映射到RGB顏色空間的邊界區域的線上的等距點,因爲該線跨越完整的動態範圍,並且傾向於與亮度分佈。

下面的圖像再次顯示柯達無損真彩色圖像套件的圖像14的小部分[ 16 ]。圖14的該特定部分示出了由於DXT壓縮而可能發生的一些最差的壓縮僞像。左邊的圖像是原來的。中間的圖像顯示了以最佳質量壓縮到DXT1的相同部分。右圖顯示了實時DXT1壓縮機的結果。

切出
柯達圖像套件圖像14 部分
1.5 MB

以最好的質量壓縮到DXT1 
192 kB
壓縮到DXT1與
實時DXT1壓縮機。
192 kB

使用實時DXT1壓縮器時會出現明顯的工件,但對DXT1格式的最佳壓縮也顯示出明顯的質量損失。然而,對於許多圖像,由於實時壓縮造成的質量損失是不明顯的或完全可以接受的。

4. YCoCg-DXT5

所述DXT5格式[ 23 ]存儲三個顏色通道中的相同的方式DXT1確實,但沒有1位alpha通道。代替1位alpha通道,DXT5格式存儲與DXT1色彩通道相似的單獨的Alpha通道。4x4塊中的alpha值通過α空間在一行上的等距點近似。通過α空間的線的端點存儲爲8位值,並且基於端點的順序,通過插值生成4或6箇中間點。對於4箇中間點的情況,生成兩個附加點,一個完全不透明,另一個用於完全透明。對於4x4塊中的每個像素,3位索引通過alpha空間存儲在該行上的一個等距點上,或完全不透明或完全透明的兩個附加點之一。使用相同數量的位來將alpha通道編碼爲三個DXT1顏色通道。因此,與三維顏色空間相反,α通道以比每個顏色通道更高的精度被存儲,因爲α空間是一維的。此外,總共有8個樣本表示4×4塊中的α值,而不是4個樣本來表示顏色值。由於額外的Alpha通道,DXT5格式消耗DXT1格式的內存量的兩倍。與每個顏色通道相比,alpha通道的存儲精度高於每個顏色通道,因爲與三維色彩空間相反,alpha空間是一維的。此外,總共有8個樣本表示4×4塊中的α值,而不是4個樣本來表示顏色值。由於額外的Alpha通道,DXT5格式消耗DXT1格式的內存量的兩倍。與每個顏色通道相比,alpha通道的存儲精度高於每個顏色通道,因爲與三維色彩空間相反,alpha空間是一維的。此外,總共有8個樣本表示4×4塊中的α值,而不是4個樣本來表示顏色值。由於額外的Alpha通道,DXT5格式消耗DXT1格式的內存量的兩倍。

DXT5格式可以以多種方式用於不同的目的。一個衆所周知的例子是絞合正常映射[DXT5的壓縮1213 ]。DXT5格式也可用於通過使用YCoCg色彩空間進行高質量的彩色圖像壓縮。在YCoCg中顏色空間中首次引入了對H.264壓縮[ 1415 ]。已經顯示RGB到YCoCg變換能夠實現比由各種RGB到YCbCr變換獲得的去相關更好的去相關性,並且當測量用於代表性的高質量RGB測試集合時,非常接近KL變換的解相關圖像[ 15 ]。此外,從RGB到YCoCg的轉換非常簡單,只需要增加整數和移位。

在將RGB_數據轉換爲CoCg_Y後,可以通過使用DXT5格式實現彩色圖像的高質量壓縮。換句話說,亮度(Y)被存儲在α通道中,色度(CoCg)被存儲在5:6:5顏色通道中的前兩個中。對於彩色圖像,這種技術產生4:1的壓縮比,質量非常好 - 通常優於4:2:0 JPEG在最高質量設置。

切出
柯達圖像套件圖像14 部分
1.5 MB

以最好的質量壓縮到DXT1 
192 kB

以最好的質量壓縮到YCoCg-DXT5 
384 kB

CoCg_Y DXT5壓縮圖像沒有顯着的質量損失,消耗原始圖像的四分之一。CoCg_Y DXT5看起來也比壓縮成DXT1格式的圖像好多了。

顯然,在片段程序中檢索到CoCg_Y顏色數據,需要一些工作來執行轉換回RGB。然而,轉換成RGB是相當簡單的:

Co = color.x - (0.5 * 256.0 / 255.0)
Cg = color.y - (0.5 * 256.0 / 255.0)
Y = color.w 
R = Y + Co-Cg 
G = Y + Cg 
B = Y-Co-Cg

此轉換只需要片段程序中的三個指令。此外,過濾和其他計算通常可以在YCoCg空間中完成。

DP4 result.color.x,color,{1.0,-1.0,0.0 * 256.0 / 255.0,1.0};
DP4 result.color.y,color,{0.0,1.0,-0.5 * 256.0 / 255.0,1.0};
DP4 result.color.z,color,{-1.0,-1.0,1.0 * 256.0 / 255.0,1.0};

色度橙色(Co)和色度綠色(Cg)存儲在前兩個通道中,其中Co端點以5位存儲,Cg終點存儲6位。即使端點以不同的量化存儲,這樣也能獲得最佳質量。由於Cg值影響所有三個RGB通道,Cg通道的終點以比Co通道的終點(5位)更高的精度(6位)存儲,而Co值僅影響紅色和藍色渠道。

CoCg顏色空間的5:6量化可能導致輕微的顏色偏移和可能的顏色損失。理想情況下,不會有任何量化,通過CoCg顏色空間的線的終點將以8:8格式存儲。幸運的是,DXT5格式的第三個通道可以用於爲4×4像素塊中的許多塊獲得高達2位的精度。當4×4塊中的顏色的動態範圍非常低時,顏色空間的量化是最顯着的。量化可以使顏色映射到單個點,或者顏色映射到動態範圍之外的點。爲了克服這個問題,當顏色的動態範圍很低時,顏色可以放大。在壓縮過程中,顏色被放大,並在片段程序中按比例縮小到其原始值。

當CoCg空間映射到[0,255] x [0,255]的範圍時,顏色灰色表示爲點(128,128)。對於大多數圖像,隨着CoCg空間的動態範圍減小,顏色趨向於變得更接近於灰色。因此,通過從所有顏色中減去(128,128),4×4像素塊的顏色空間首先以灰色爲中心。然後,圍繞原點測量4x4塊的色彩空間的動態範圍:max(abs(min),abs(max))。如果兩個通道的動態範圍小於64,則所有CoCg值都按比例放大2倍。如果動態範圍小於32,則所有CoCg值都按比例增加4倍。動態範圍範圍必須分別小於64和32,以確保顏色值不會溢出。實際上,將顏色縮放2,所有CoCg值的最後一位總是設置爲零,因此不受量化的影響。以相同的方式,如果顏色按比例放大4,則最後兩位始終爲零,也不受量化影響。

在DXT5格式的第三個通道中,比例因子1,2或4作爲一個恆定值存儲在整個4×4像素塊上。存儲在整個塊上的恆定值,比例因子的存在不影響CoCg通道的壓縮。比例因子存儲爲(scale-1)* 8,使得比例因子僅使用5位通道的兩個最低有效位。因此,比例因子不受到5比特的量化和DXT5解碼器中的反量化和擴展的影響,其中較高的3比特被複制到較低的三比特。下圖顯示了DXT5解碼器中縮放因子從5位擴展到8位。

當像這樣將圖像編碼爲YCoCg-DXT5時,片段程序需要更多的工作才能轉換回RGB。以下僞代碼顯示了轉換。

scale =(color.z *(255.0 / 8.0))+ 1.0 
Co =(color.x - (0.5 * 256.0 / 255.0))/ scale 
Cg =(color.y - (0.5 * 256.0 / 255.0))/ scale 
Y = color.w 
R = Y + Co-Cg 
G = Y + Cg 
B = Y-Co-Cg

在片段程序中,這意味着:

MAD color.z,color.z,255.0 / 8.0,1.0;
RCP color.z,color.z;
MUL color.xy,color.xyxy,color.z;
DP4 result.color.x,color,{1.0,-1.0,0.0 * 256.0 / 255.0,1.0};
DP4 result.color.y,color,{0.0,1.0,-0.5 * 256.0 / 255.0,1.0};
DP4 result.color.z,color,{-1.0,-1.0,1.0 * 256.0 / 255.0,1.0};

換句話說,片段程序中只需要三個指令來縮小CoCg值。

右下方的圖像顯示了在左側原始圖像旁邊的CoCg值縮放的YCoCg壓縮結果。

切出
柯達圖像套件圖像14 部分
1.5 MB
壓縮至可擴展的YCoCg-DXT5 
,質量最好。
384 kB

對於幾乎所有的圖像,當放大CoCg值時,質量明顯更高。然而,值得注意的是,縮放的CoCg值的線性紋理濾波(例如雙線性濾波)可以導致顏色值的非線性濾波。在顏色值按比例縮小之前,分別對顏色值和比例因子進行過濾。換句話說,將過濾的比例因子應用於過濾的CoCg值 - 這與先前按比例縮小的過濾顏色值不同。有趣的是,當測量雙線性濾波的YCoCg-DXT5壓縮紋理相對於雙線性濾波的原始紋理的質量時,當CoCg值被放大時,質量仍然顯着更高,即使紋理過濾可能導致顏色被縮放向下錯誤

不正確的縮放僅影響具有不同比例因子的4×4塊紋素之間的邊界上的樣本,並且大多數時間相鄰塊的比例因子相同。具有不同比例因子的塊之間的內插顏色趨向於與具有最高比例因子的4×4塊的顏色值更快地收斂。然而,由於難以辨別雙線性濾波器圖案和非線性濾波器圖案之間的差異,感知質量通常沒有明顯的差異。也沒有不自然的色彩轉換,因爲Co和Cg值經歷相同的非線性濾波,不會漂移。換句話說,顏色轉換仍然沿着CoCg空間中的直線 - 只是不是以恆定的速度。

由於過濾在具有不同比例因子的4x4塊的邊界處是非線性的,重要的是濾除的顏色值保持在來自4×4塊的原始顏色值之間。換句話說,顏色值不應超出範圍 - 這將導致僞像。下圖顯示了帶有比例因子S0,S1,S2和S3以及參數“q”和“r”的紋素值P0,P1,P2和P3之間雙線性濾波的工作原理。

對於常規雙線性濾波,比例因子S0,S1,S2和S3都設置爲1,並且紋理值如下內插。

當CoCg值用相鄰塊的不同比例因子放大時,則濾波變爲非線性,如下。

濾波後的顏色值不應超出範圍,因此對於濾波後的樣本C(q,r),必須滿足以下條件:min(P0,P1,P2,P3)<= C(q,r)<= max ,P1,P2,P3)。首先,看到濾波後的樣本C(q,r)等於拐角處的原始紋素值之一是很簡單的。

由於樣本C(q,r)等於拐角處的原始紋理值之一,所以函數C(q,r)對於任何常數“r”必須單調遞增或單調遞減,因此樣本不能出來的範圍。換句話說,C(q,r)相對於'q'的偏導數不能改變符號。C(q,r)相對於'q'的偏導數如下:


基於紋素值和比例因子,'a','b','c','d','e','f'和'g'的表達式是正或負常數。分子是正或負的常數。變量“q”只能在分母中出現一次。此外,分母是平方的,因此總是積極的。換句話說,偏導數永遠不會改變符號。

同樣,函數C(q,r)對於任何常數“q”必須單調增加或單調遞減,這意味着C(q,r)相對於'r'的偏導數也必須永遠不會改變符號。C(q,r)相對於'r'的偏導數如下:


與對於'q'的偏導數一樣,相對於'r'的偏導數也從不改變符號。由於函數C(q,r)經過了角部的原始紋素值,並且函數在常數“r”下單調增加或單調遞減,並且在恆定“q”時也單調遞增或單調遞減,樣本C(q,r)永遠不能超出範圍,如下:min(P0,P1,P2,P3)<= C(q,r)<= max(P0,P1,P2,P3)

如果比例因子不同,三線濾波和各向異性過濾也會變得非線性。然而,與雙線性濾波一樣,濾波後的樣本也在原始紋素值之間很好地界定。

 

 

5. CPU上的實時YCoCg-DXT5

使用常規實時DXT5壓縮器壓縮到YCoCg-DXT5確實比使用DXT1壓縮更好的質量,並且通常幾乎沒有細節損失[ 9 ]。然而,有明顯的色彩瑕疵。特別是有顏色阻擋和顏色滲色僞像。來自[ 9 ] 的實時DXT5壓縮器通過CoCg空間在線上以等距點近似每個4x4塊中的顏色,其中線通過CoCg空間的邊界框的範圍創建。問題是,經常選擇CoCg空間邊界的錯誤對角線。這在基本顏色之間的過渡特別明顯,這導致顏色滲色僞像。

要獲得更好的質量,應使用CoCg空間的二維邊界盒的兩個對角線中最好的。9 ]中描述的實時DXT5壓縮器可以擴展爲選擇兩個對角線之一。確定使用哪個對角線的最佳方式是測試顏色值相對於CoCg空間邊界中心的協方差的符號。以下例程通過CoCg空間交換線的端點的兩個座標,基於哪個對角線最適合4×4像素塊中的顏色。

void SelectYCoCgDiagonal(const byte * colorBlock,byte * minColor,byte * maxColor){
    
    字節mid0 =((int)minColor [0] + maxColor [0] + 1)>> 1;
    字節mid1 =((int)minColor [1] + maxColor [1] + 1)>> 1;
    
    int協方差= 0;
    for(int i = 0; i <16; i ++){
        int b0 = colorBlock [i * 4 + 0]  -  mid0;
        int b1 = colorBlock [i * 4 + 1]  -  mid1;
        協方差+ =(b0 * b1);
    }
    
    if(協方差<0){
        字節t = minColor [1];
        minColor [1] = maxColor [1]
        maxColor [1] = t;
    }
}

計算協方差需要顏色值的乘法,這增加了動態範圍,並限制了通過SIMD指令集可以利用的並行度。代替計算顏色值的協方差,也可以選擇僅相對於顏色空間的邊界框的中心的顏色的符號位的協方差。有趣的是,僅使用符號位時質量的損失真的很小,一般可以忽略不計。以下例程僅基於符號位的協方差,通過CoCg空間交換線路端點的兩個座標。

void SelectYCoCgDiagonal(const byte * colorBlock,byte * minColor,byte * maxColor){
    
    字節mid0 =((int)minColor [0] + maxColor [0] + 1)>> 1;
    字節mid1 =((int)minColor [1] + maxColor [1] + 1)>> 1;
    
    字節邊= 0;
    for(int i = 0; i <16; i ++){
        字節b0 = colorBlock [i * 4 + 0]> = mid0;
        字節b1 = colorBlock [i * 4 + 1]> = mid1;
        側+ =(b0 ^ b1);
    }
    
    byte mask =  - (side> 8);
    
    字節c0 = minColor [1];
    字節c1 = maxColor [1];
    
    c0 ^ = c1 ^ = mask&= c0 ^ = c1;
    
    minColor [1] = c0;
    maxColor [1] = c1;
}

動態範圍沒有增加,上述例程中的循環可以使用僅對字節進行操作來實現,這允許通過SIMD指令集最大化並行化。實際上,該例程將CoCg空間的邊界框劃分爲四個統一象限如下:

然後,例程計算落入每個象限的顏色數量。如果大多數顏色在象限2和3中,則使用通過邊界框擴展區的規則對角線。然而,如果大多數顏色在象限1和4中,則使用相反的對角線。

例程首先計算Co和Cg範圍的中點。然後將顏色值與這些中點進行比較。下表顯示了4個象限與大於或等於中點的CoCg值之間的關係。對比較結果使用按位邏輯異或運算,只能對於象限1或4中的顏色產生“1”。顏色的異或運算結果可以累加,以計算顏色數在象限1和4中。如果超過一半的顏色在象限1或4中,則對角線需要翻轉。

  象限     > = Co中點     > = Cg中點     XOR  
1 0 1 1
2 1 1 0
3 0 0 0
4 1 0 1

使用MMX和SSE2指令集的實現可以分別在附錄C和D中找到。MMX / SSE2指令“pavgb”用於計算Co和Cg範圍的中點。不幸的是,在MMX或SSE2指令集中沒有指令用於比較無符號字節。只有“pcmpeqb”指令對有符號和無符號字節都有效。但是,'pmaxub'指令使用無符號字節。爲了評估一個大於或等於的關係,使用'pmaxub'指令後跟'pcmpeqb'指令,因爲表達式(x> = y)等價於表達式(max(x,y)== x )。

MMX / SSE2指令“psadbw”通常用於計算絕對差的和。但是,該指令也可用於通過將其中一個操作數設置爲全零來執行水平加法。附錄C和D中的MMX / SSE2實現使用“psadbw”指令來添加按位邏輯異或運算的結果。

爲了翻轉對角線,例程使用一個掩蔽的XOR交換,這只需要4條指令。該掩碼用於選擇需要交換的兩個寄存器的位。假設要交換的位被存儲在寄存器“xmm0”和“xmm1”中,掩碼存儲在寄存器“xmm2”中。以下SSE2代碼從“xmm0”和“xmm1”中的每對位置換掉,“xmm2”中的等效位設置爲1。

pxor xmm0,xmm1
pand xmm2,xmm0
pxor xmm1,xmm2
pxor xmm0,xmm1

使用屏蔽XOR交換,“minColor”和“maxColor”字節數組可以直接讀入兩個寄存器。然後可以通過使用適當的掩碼來交換第二個字節,而不必從寄存器中提取字節。

以下圖像顯示原始圖像,壓縮爲YCoCg-DXT5的圖像與實時DXT5壓縮器[ 9 ]之間的差異,以及使用新的實時YCoCg-DXT5壓縮器壓縮爲YCoCg-DXT5的圖像最好的對角線。圖像顯示當使用這個小型擴展到實時DXT5壓縮器時,沒有明顯的顏色滲色。

切出
柯達圖像套件圖像14 部分
1.5 MB
壓縮至具有
實時DXT5壓縮機的YCoCg-DXT5 
384 kB
使用
實時YCoCg-DXT5壓縮機壓縮至YCoCg-DXT5 
384 kB

來自[ 9 ] 的DXT5壓縮器插入了顏色空間和alpha空間的邊界框,以提高均方誤差(MSE)。顏色空間的邊界框通過顏色空間在線上的兩點之間的距離總共爲一半。通過顏色空間有4點,因此邊框在任意一端插入範圍的1/16。以相同的方式,α空間的界限通過α空間在線上的兩點之間的距離總共插入一半。換句話說,alpha空間的邊界在任一端插入範圍的1/32。

插入邊框的副作用是對於具有低動態範圍(小邊界框)的4x4像素的塊,通過顏色空間或α空間的線上的點可能捕捉到彼此非常接近的點。在這種情況下,將線的端點稍微分開一點,以便覆蓋較大的動態範圍,其中內插點通常將更接近原始值。以下代碼首先插入顏色空間的邊界框,然後向外舍入線的端點,以便線覆蓋更大的動態範圍。以同樣的方式,α空間的界限首先插入,然後向外舍入。

#define INSET_COLOR_SHIFT 4 //插入顏色邊界框
#define INSET_ALPHA_SHIFT 5 //插入alpha邊界框
    
#define C565_5_MASK 0xF8 // 0xFF減去最後三位
#define C565_6_MASK 0xFC // 0xFF減去最後兩位
    
void InsetYCoCgBBox(byte * minColor,byte * maxColor){
    
    int inset [4];
    int mini [4];
    int maxi [4];
    
    inset [0] =(maxColor [0]  -  minColor [0]) - ((1 <<(INSET_COLOR_SHIFT-1)) -  1);
    inset [1] =(maxColor [1]  -  minColor [1]) - ((1 <<(INSET_COLOR_SHIFT-1)) -  1);
    inset [3] =(maxColor [3]  -  minColor [3]) - ((1 <<(INSET_ALPHA_SHIFT-1))-1);
    
    mini [0] =((minColor [0] << INSET_COLOR_SHIFT)+ inset [0])>> INSET_COLOR_SHIFT;
    mini [1] =((minColor [1] << INSET_COLOR_SHIFT)+ inset [1])>> INSET_COLOR_SHIFT;
    mini [3] =((minColor [3] << INSET_ALPHA_SHIFT)+ inset [3])>> INSET_ALPHA_SHIFT;
    
    maxi [0] =((maxColor [0] << INSET_COLOR_SHIFT) -  inset [0])>> INSET_COLOR_SHIFT;
    maxi [1] =((maxColor [1] << INSET_COLOR_SHIFT) -  inset [1])>> INSET_COLOR_SHIFT;
    maxi [3] =((maxColor [3] << INSET_ALPHA_SHIFT) -  inset [3])>> INSET_ALPHA_SHIFT;
    
    mini [0] =(mini [0]> = 0)?迷你[0]:0;
    mini [1] =(mini [1]> = 0)?迷你[1]:0;
    mini [3] =(mini [3]> = 0)?迷你[3]:0;
    
    maxi [0] =(maxi [0] <= 255)?maxi [0]:255;
    maxi [1] =(maxi [1] <= 255)?maxi [1]:255;
    maxi [3] =(maxi [3] <= 255)?maxi [3]:255;
    
    minColor [0] =(mini [0]&C565_5_MASK)| (mini [0] >> 5);
    minColor [1] =(mini [1]&C565_6_MASK)| (mini [1] >> 6);
    minColor [3] =迷你[3];
    
    maxColor [0] =(maxi [0]&C565_5_MASK)| (maxi [0] >> 5);
    maxColor [1] =(maxi [1]&C565_6_MASK)| (maxi [1] >> 6);
    maxColor [3] = maxi [3];
}

'pmullw'指令用於升檔值。與MMX / SSE2移位指令不同,'pmullw'指令允許寄存器中的單個字與不同的值相乘。乘法器是存儲在前兩個通道中的CoCg值的(1 << INSET_COLOR_SHIFT)和存儲在阿爾法通道中的Y值的(1 << INSET_ALPHA_SHIFT)。以同樣的方式,通過使用CoCg值的乘法器(1 <<(16 - INSET_COLOR_SHIFT))和Y值的(1 <<(16 - INSET_ALPHA_SHIFT))來使用'pmulhw'指令向下移動。

除了選擇最佳的對角線和用適當的四捨五入對邊界進行插值外,CoCg值也可以實時放大以獲得精確度。以下代碼顯示瞭如何將其作爲現有實時DXT5編碼器的擴展來實現。

void ScaleYCoCg(byte * colorBlock,byte * minColor,byte * maxColor){
    int m0 = abs(minColor [0]  -  128);
    int m1 = abs(minColor [1]  -  128);
    int m2 = abs(maxColor [0]  -  128);
    int m3 = abs(maxColor [1]  -  128);
    
    if(m1> m0)m0 = m1;
    如果(m3> m2)m2 = m3;
    如果(m2> m0)m0 = m2;
    
    const int s0 = 128/2  -  1;
    const int s1 = 128/4  -  1;
    
    int mask0 =  - (m0 <= s0);
    int mask1 =  - (m0 <= s1);
    int scale = 1 +(1&mask0)+(2&mask1);
    
    minColor [0] =(minColor [0]  -  128)* scale + 128;
    minColor [1] =(minColor [1]  -  128)* scale + 128;
    minColor [2] =(scale-1)<< 3;
    
    maxColor [0] =(maxColor [0]  -  128)* scale + 128;
    maxColor [1] =(maxColor [1]  -  128)* scale + 128;
    maxColor [2] =(scale-1)<< 3;
    
    for(int i = 0; i <16; i ++){
        colorBlock [i * 4 + 0] =(colorBlock [i * 4 + 0]  -  128)* scale + 128;
        colorBlock [i * 4 + 1] =(colorBlock [i * 4 + 1]  -  128)* scale + 128;
    }
}

爲了利用最大並行度,CoCg值最好放大爲字節,而不需要更多的位。這似乎很直接,除了MMX和SSE2指令集中沒有指令移位或乘以字節。此外,128需要從無符號字節中減去,這可能導致負值。也不能將128表示爲有符號字節。但是,無符號字節可以靜默地轉換爲有符號字節,然後可以添加帶符號的字節值-128。對於正確處理的包裝,這將得到與從無符號字節減去128相同的計算結果,其中結果是有符號字節,如果原始值低於128,則可能會變爲負數。

比例因子是2的冪,這意味着比例等於移位。因此,可以使用字乘法同時縮放兩個字節,並且可以將結果屏蔽以從移入第二個字節的第一個字節中移除任何進位位。以下代碼顯示了在bias = 128和scale = 1,2或4時如何實現。

 
詞1 字0
 
詞1 字0
 
/ \ / \
 
/ \ / \
 
字節3 字節2 字節1 字節0
 
字節3 字節2 字節1 字節0
PADDB
ÿ 一個 CG 有限公司
+ =
0 0 - 偏見 - 偏見
pmullw
YA CgCo
* =
1 規模
PAND
ÿ 一個 CG 有限公司
&=
爲0xFF 爲0xFF 〜(刻度-1) 〜(刻度-1)
PSUBB
ÿ 一個 CG 有限公司
- =
0 0 - 偏見 - 偏見

縮放字節後,通過減去有符號字節值-128將它們放回[0,255]範圍內。上面的代碼顯示了像素的所有四個字節如何加載到寄存器中,但只有CoCg值被放大,而Y值乘以1.這樣可以避免大量的解壓縮和swizzle代碼,否則需要提取所有像素的CoCg值。

下圖右側的圖像顯示了實時DXT5壓縮器的擴展結果,在左側的原始圖像旁邊。

切出
柯達圖像套件圖像14 部分
1.5 MB
使用
實時YCoCg-DXT5壓縮器壓縮至YCoCg-DXT5,
使用所有擴展。
384 kB

最終的結果是沒有顏色阻擋僞像,並且由於5:6量化,顏色偏移很小。

6.DXT壓縮在GPU上

實時DXT1和YCoCg-DXT5也可以在GPU上實現。這是可能的,由於DX10類圖形硬件上提供的新功能。特別是整數紋理[ 17 ]和整數指令[ 18 ]對於在GPU上實現紋理壓縮非常有用。

假設需要壓縮的圖像作爲未壓縮的RGB紋理存在於視頻存儲器中。如果圖像在視頻內存中不可用,則必須從主機上傳。這是使用DMA傳輸完成的,通常速度通常爲2 GB / s。爲了壓縮紋理,通過在整個目標表面上渲染四邊形,爲4x4紋理的每個塊使用片段程序。現在不可能直接將結果寫入DXT壓縮紋理,而是將結果寫入整數紋理。請注意,DXT1塊的大小爲64位,這等於每個組件32位的一個LA紋理。類似地,DXT5塊的大小是128位,這等於一個RGBA紋素,每個元件有32位。

一旦將壓縮的DXT塊存儲到整數紋理的紋素中,就需要將它們複製到壓縮紋理。此副本無法直接執行。然而,通過將整數紋理的內容複製到PBO中,可以使用中間像素緩衝對象(PBO),然後從PBO到壓縮紋理。這兩個副本在視頻驅動程序中實現爲非常快的視頻存儲器副本,並且與壓縮成本相比,成本是微不足道的。

此處介紹的片段和頂點程序針對NVIDIA GeForce 8系列GPU進行了優化。從優化的角度來看,CPU和GeForce 8系列GPU之間有幾個重要的區別。在CPU上,使用整數運算來利用最大並行度有一個優勢。然而,在GeForce 8系列GPU中,大多數浮點運算與整數運算一樣快或更快。因爲這樣的浮點值在它更自然或者節省計算時就被使用。由於使用浮點運算,GPU實現的結果與CPU實現的結果並不等同。然而,結果仍然非常類似於CPU實現的結果。

CPU和GeForce 8系列GPU之間的另一個重要區別是,GeForce 8系列GPU是標量處理器。因此,不需要編寫向量化代碼。此外,片段和頂點程序通常很短,編譯器可以執行優化,對於大型C ++程序來說,這將是非常昂貴的。因此,使用高級着色語言,可以實現通過爲GPU編寫低級彙編代碼而獲得的幾乎相同的性能。這裏介紹的片段和頂點程序是使用OpenGL和Cg 2.0着色語言實現的[ 19 ]。

7. GPU上的實時DXT1

兩臺離線和實時DXT1壓縮機都已經在GPU上實現了。NVIDIA紋理工具[ 10 ]提供了在CUDA中實現的高質量DXT1壓縮器,與等效的CPU實現相比具有非常好的性能。然而,這種壓縮機設計用於高質量的壓縮 - 而不是實時壓縮。

NVIDIA SDK10 [ 11 ]提供了實時DXT1壓縮的示例。這裏提供的實時DXT1壓縮器的GPU實現基於該示例,但增加了前面部分中描述的一些質量改進。如第5節所述,顏色空間的邊界框是通過邊界框角部的兩條等距點之間的距離插入的一半。然後將端點向外舍入,以避免將它們卡到單個點。這種小的改進沒有明顯的性能損失。

如第3節所述,4×4像素塊中的RGB顏色傾向於通過RGB顏色空間的邊界框的範圍很好地映射到線上的等距點,因爲該線跨越完整的動態範圍,並且傾向於符合亮度分佈。來自[ 9 ]和[ 11 ] 的DXT1壓縮機總是通過邊界區域使用該線。

在相對較小的性能成本下,可以通過選擇RGB空間的邊界框的最佳對角線來改善壓縮質量,類似於實時YCoCg-DXT5壓縮器如何選擇CoCg空間的邊界框的最佳對角線。然而,通過RGB空間選擇最佳對角線僅影響柯達無損真彩色圖像套件中所有4x4像素塊的2%[ 16 ]。這與YCoCg-DXT5壓縮有很大不同,其中通過CoCg空間選擇最佳對角線影響了接近所有4x4像素塊的50%。

對於柯達圖像,峯值信噪比(PSNR)的改進,從選擇最佳對角線到RGB空間,通常很小。對於圖像2和23,對於圖像3和14,PSNR具有大約0.7dB的改善,對於圖像3和14,它是0.2到0.3dB的改進,並且對於所有其他圖像,改進在0.2dB以下,並且所有柯達圖像的平均改進爲0.1 dB。然而,感覺質量的改善在圖像的某些領域可能是重要的。特別地,在基本顏色之間具有過渡的區域中,質量可能顯着改善。如果對於具有這種轉變的塊選擇了錯誤的對角線,則顏色可能完全改變,這對於人眼可能是相當明顯的。另一方面,DXT1壓縮格式通常在這些區域中表現不佳,

最後,它是質量和性能之間的權衡。選擇最佳對角線可以以相對較小(10%至20%)的性能成本提高某些圖像的質量。DXT1壓縮器的GPU實現可以在附錄E中找到。該實現包括用於選擇RGB空間邊界框的最佳對角線的代碼,但默認情況下不啓用。附錄E中的實現速度比[ 11 ] 更快性能提升是仔細調整性能代碼的結果; 消除冗餘和不必要的計算,並重組代碼以最小化寄存器分配。

8. GPU上的實時YCoCg-DXT5

YCoCg-DXT5壓縮片段程序的實現相對簡單。因此,僅描述相關細節,並突出顯示與CPU實現相比的差異。片段方案的全面實施見附錄E.

片段程序的第一步是讀取需要壓縮的4x4紋理的塊。這通常使用常規紋理採樣完成。然而,使用紋理提取更有效; 一個新功能,允許使用紋理地址和固定偏移量加載單個紋素,而不進行任何濾波或採樣。這在Cg 2.0 [ 19 ] 中公開,並且獲取4x4紋素的塊被實現如下。

void ExtractColorBlock(out float3 block [16],sampler2D image,float2 tc,float2 imageSize){
    int4 base = int4(tc * imageSize  -  1.5,0,0);
    block [0] = toYCoCg(tex2Dfetch(image,base,int2(0,0)).rgb);
    塊[1] = toYCoCg(tex2Dfetch(image,base,int2(1,0)).rgb);
    block [2] = toYCoCg(tex2Dfetch(image,base,int2(2,0)).rgb);
    block [3] = toYCoCg(tex2Dfetch(image,base,int2(3,0)).rgb);
    block [4] = toYCoCg(tex2Dfetch(image,base,int2(0,1)).rgb);
    block [5] = toYCoCg(tex2Dfetch(image,base,int2(1,1)).rgb);
    block [6] = toYCoCg(tex2Dfetch(image,base,int2(2,1)).rgb);
    block [7] = toYCoCg(tex2Dfetch(image,base,int2(3,1)).rgb);
    block [8] = toYCoCg(tex2Dfetch(image,base,int2(0,2)).rgb);
    block [9] = toYCoCg(tex2Dfetch(image,base,int2(1,2)).rgb);
    block [10] = toYCoCg(tex2Dfetch(image,base,int2(2,2)).rgb);
    block [11] = toYCoCg(tex2Dfetch(image,base,int2(3,2)).rgb);
    block [12] = toYCoCg(tex2Dfetch(image,base,int2(0,3)).rgb);
    block [13] = toYCoCg(tex2Dfetch(image,base,int2(1,3)).rgb);
    block [14] = toYCoCg(tex2Dfetch(image,base,int2(2,3)).rgb);
    block [15] = toYCoCg(tex2Dfetch(image,base,int2(3,3)).rgb);
}

取代將獲取的顏色轉換爲整數,它們保持爲浮點格式。然後,該算法以與CPU算法相同的方式繼續進行,除了使用浮點值。首先,計算CoCg值的邊界框,然後選擇綁定框的兩個對角線之一。選擇其中一個對角線的最佳方法是測試CoCg值的協方差的符號。這可以在GPU上非常有效地實現,因此不需要使用符號位的協方差近似對角線選擇。

void SelectYCoCgDiagonal(const float3 block [16],in out float2 minColor,in out float2 maxColor){
    float2 mid =(maxColor + minColor)* 0.5;
    
    浮點協方差= 0.0;
    for(int i = 0; i <16; i ++){
        float2 t = block [i] .yz  -  mid;
        協方差+ = tx * ty;
    }
    
    if(協方差<0.0){
        swap(maxColor.y,minColor.y);
    }
} 

正如CPU實現一樣,通過CoCg空間,使用比例因子來爲線路的端點獲得高達兩位精度。然而,沒有必要像在CPU中一樣高調所有的CoCg值。通過CoCg空間的線的端點以浮點格式表示,因此可以在應用插入後簡單地縮小端點,並且顏色已舍入並轉換爲5:6格式。因此,可以將每個紋素的原始CoCg值與通過CoCg空間的線上的未縮放點進行比較,以找到每個紋素的最佳匹配點。比例因子本身以與CPU實現相同的方式計算。

int GetYCoCgScale(float2 minColor,float2 maxColor){
    float2 m0 = abs(minColor  -  offset);
    float2 m1 = abs(maxColor  -  offset);
    
    float m = max(max(m0.x,m0.y),max(m1.x,m1.y));
    
    const float s0 = 64.0 / 255.0;
    const float s1 = 32.0 / 255.0;
    
    int scale = 1;
    if(m <s0)scale = 2;
    if(m <s1)scale = 4;
    
    回報量表
}

在計算比例因子後,通過CoCg空間的線路的端點如在CPU實現中進行插值,量化和位擴展。

在CPU上,計算曼哈頓距離,以便通過每個紋素的CoCg空間找到線上最佳匹配點。這可以使用用於計算絕對差的壓縮和的指令非常有效地實現。然而,在GPU上,使用平方歐幾里德距離更爲有效,可以用單點積計算。

float colorDistance(float2 c0,float2 c1){
    返回點(c0-c1,c0-c1);
}

着色器的輸出是一個4分量的無符號整數向量。在片段程序中,DXT塊的每個部分都被寫入一個向量組件,並返回最終值。

    // DXT1塊中的輸出CoCg。
    uint4 output;
    output.z = EmitEndPointsYCoCgDXT5(mincol.yz,maxcol.yz,scale);
    output.w = EmitIndicesYCoCgDXT5(block,mincol.yz,maxcol.yz);
    
    // DXT5 alpha塊中輸出Y。
    output.x = EmitAlphaEndPointsYCoCgDXT5(mincol.x,maxcol.x);
    uint2 indices = EmitAlphaIndicesYCoCgDXT5(block,mincol.x,maxcol.x);
    output.x | = indices.x;
    output.y = indices.y;
    
    返回輸出;

9.對CPU與GPU的壓縮

如前面部分所示,可以在CPU和GPU上實現高性能DXT壓縮。壓縮是否最好在CPU或GPU上實現依賴於應用程序。

CPU上的實時DXT壓縮對於在CPU上動態創建的紋理很有用。CPU上的壓縮對於以不能用於渲染的格式從磁盤流式傳輸的紋理數據進行轉碼也特別有用。例如,紋理可以以JPEG格式存儲在磁盤上,因此不能直接用於渲染。JPEG解壓縮算法的一些部分目前可以在GPU上高效地實現。存儲器可以保存在圖形卡上,通過解壓縮原始數據並將其重新壓縮爲DXT格式,可以提高渲染性能。在CPU上重新壓縮紋理數據的優點是上傳到圖形卡的數據量很小。此外,當在CPU上執行壓縮時,完整的GPU可以用於渲染工作,因爲它不需要執行任何壓縮。對於當今CPU上越來越多的內核的確定趨勢,通常可以輕鬆地使用可用於紋理壓縮的免費內核。

因爲對於代碼轉換,實時壓縮可能不太有用,因爲用於上傳未壓縮紋理數據的帶寬需求增加,並且因爲GPU可能已經被昂貴的渲染工作所約束了。然而,GPU上的實時壓縮對壓縮的渲染目標非常有用。GPU上的壓縮可以用於在渲染到紋理時節省內存。此外,如果來自渲染目標的數據用於進一步渲染,則這樣的壓縮渲染目標可以提高性能。渲染目標被壓縮一次,而渲染過程中可能會多次訪問生成的數據。壓縮數據導致光柵化期間帶寬要求降低,因此可以顯着提高性能。

10.結果

使用柯達無損真彩色圖像套件測試了DXT1格式和YCoCg-DXT5格式[ 16 ]。已經在未加權的RGB通道上計算了峯值信噪比(PSNR)。在所有情況下,使用定製壓縮機進行離線壓縮以獲得最佳質量。此外,來自[ 9 ] 的實時DXT1壓縮器和這裏描述的實時YCoCg-DXT5壓縮機的CPU實現被用於圖像的實時壓縮。

下表顯示了壓縮爲兩種格式的柯達圖像的PSNR值。(PSNR越高越好)

PSNR
 
 圖片   離線

DXT1
  實時

DXT1
  離線
YCoCg 
DXT5
  實時
YCoCg 
DXT5
 
 kodim01    34.54    32.95    41.32    39.58 
 
 kodim02    37.70    34.36    44.33    41.19 
 
 kodim03    39.35    36.68    46.05    43.79 
 
 kodim04    37.95    35.62    45.02    42.91 
 
 kodim05    33.21    31.30    39.95    38.31 
 
 kodim06    35.82    34.20    42.82    41.14 
 
 kodim07    37.70    35.56    44.38    42.59 
 
 kodim08    32.69    31.12    39.51    37.61 
 
 kodim09    38.46    36.43    45.08    43.11 
 
 kodim10    38.53    36.71    45.36    43.29 
 
 kodim11    36.37    34.58    43.51    41.70 
 
 kodim12    39.38    37.26    46.21    43.97 
 
 kodim13    32.18    30.63    39.21    37.54 
 
 kodim14    34.49    32.20    41.47    39.81 
 
 kodim15    37.82    35.42    44.74    42.66 
 
 kodim16    38.86    37.15    45.85    44.07 
 
 kodim17    38.09    36.13    44.83    43.01 
 
 kodim18    34.86    33.10    41.42    39.66 
 
 kodim19    36.56    34.85    42.93    41.22 
 
 kodim20    38.17    36.19    44.84    42.94 
 
 kodim21    35.84    34.17    42.68    41.01 
 
 kodim22    36.75    34.91    43.39    41.53 
 
 kodim23    39.13    36.16    45.26    43.19 
 
 kodim24    34.38    32.46    41.61    39.80 

下圖顯示了壓縮爲兩種格式的柯達圖像的PSNR。(PSNR越高越好)

與DXT1相比,YCoCg-DXT5格式提供了6dB或更高的PSNR的一致性改進。所有PSNR值下降的圖像均具有高頻亮度變化的區域。從PSNR的角度來看,兩種格式都不能很好地編碼這些區域。然而,通常很難在具有高頻亮度變化的區域中區分壓縮圖案與原始圖案。

下圖顯示了使用以下壓縮器壓縮的柯達圖像的PSNR改進:來自[ 9 ] 的實時DXT5編碼器實時YCoCg-DXT5編碼器,選擇最佳對角線,並在插入邊界時使用正確的舍入; 和實時YCoCg-DXT5編碼器,這也提高了CoCg值以獲得精度。(PSNR越高越好)

SIMD優化的實時DXT壓縮器的性能已經在英特爾®2.8 GHz雙核至強®(“Paxville”90nm NetBurst微體系結構)和英特爾®2.9 GHz Core™2 Extreme(“Conroe”65nm Core 2微架構)。這些處理器中只有一個核心用於壓縮。由於紋理壓縮是基於塊的,所以壓縮算法可以容易地使用多個線程來利用這些處理器的所有核心。當使用多個內核時,可預期的線速度隨着可用內核的數量而增加。性能也在NVIDIA GeForce 8600 GTS和NVIDIA GeForce 8800 GTX上進行了測試。512x512 Lena圖像已被用於所有的性能測試。

下圖顯示了可以每秒壓縮到DXT1格式的多個像素數(更高的MP / s =更好)。

下圖顯示了每秒可以壓縮到YCoCg-DXT5格式的兆像素的數量(更高的MP / s =更好)。

數據顯示,YCoCg-DXT5的實時壓縮是DXT1實時壓縮的高性能替代品。此外,在高端NVIDIA GeForce 8系列GPU上,實時DXT1和YCoCg-DXT5壓縮算法的運行速度比高端英特爾®酷睿™2 CPU單核的速度快8倍。換句話說,需要超過8個Intel®Core™2內核來實現類似的性能。

結論

YCoCg-DXT5格式消耗DXT1格式的兩倍內存。然而,質量明顯好轉,對大多數圖像來說,質量幾乎沒有明顯的損失。此外,可以在CPU和GPU上實時完成對YCoCg-DXT5的高質量壓縮。因此,YCoCg-DXT5格式在無壓縮和實時DXT1壓縮之間提供了非常好的中間位置。

參考文獻

1。 S3紋理壓縮
Pat Brown 
NVIDIA Corporation,2001年11月
在線提供:http://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt
2。 壓縮紋理資源
Microsoft Developer Network 
DirectX SDK,2006年4月
在線提供:http://msdn2.microsoft.com/en-us/library/aa915230.aspx
3。 使用塊截斷編碼的圖像編碼
E.J. Delp,OR Mitchell 
IEEE Transactions on Communications,vol。27(9),pp。1335-1342,1979年9月
4。 ATI Compressonator Library
Seth Sowerby,Daniel Killebrew 
ATI Technologies Inc,The Compressonator版本1.27.1066,2006年3月
在線提供:http://www.ati.com/developer/compressonator.html
5。 NVIDIA DDS工具
NVIDIA 
NVIDIA DDS工具,2006年4月
在線提供:http://developer.nvidia.com/object/nv_texture_tools.html
6。 NVIDIA紋理工具
NVIDIA 
NVIDIA紋理工具,2007年9月
在線提供:http://developer.nvidia.com/object/texture_tools.html
7。 Mesa S3TC壓縮庫
Roland Scheidegger 
libtxc_dxtn版本0.1,2006年5月
在線可用:http://homepage.hispeed.ch/rscheidegger/dri_experimental/s3tc_index.html
8。 Squish DXT壓縮庫
Simon Brown 
Squish版本1.8,2006年9月
在線提供:http://sjbrown.co.uk/?code=squish
9。 實時DXT壓縮
J.MP van Waveren 
英特爾軟件網絡,2006年10月
在線提供:http://www.intel.com/cd/ids/developer/asmo-na/eng/324337.htm
10。 使用CUDA高品質的DXT壓縮
伊格納西奧·卡斯諾
NVIDIA,2007年2月
可在線獲得:http://developer.download.nvidia.com/compute/cuda/sdk/website/projects/dxtc/doc/cuda_dxtc.pdf
11。 壓縮DXT
Simon Green 
NVIDIA,2007年3月
在線可用:http://developer.download.nvidia.com/SDK/10/opengl/samples.html#compress_DXT
12。 Bump地圖壓縮
Simon Green 
NVIDIA技術報告,2001年10月
在線提供:http://developer.nvidia.com/object/bump_map_compression.html
13。 正常地圖壓縮
ATI Technologies Inc 
ATI,2003年8月
在線提供:http://www.ati.com/developer/NormalMapCompression.pdf
14。 專業擴展的變換,縮放和色彩空間影響
H. S. Malvar,GJ Sullivan 
ISO / IEC JTC1 / SC29 / WG11和ITU-T SG16 Q.6文件JVT-H031,日內瓦,2003年5月
在線提供:http://ftp3.itu .INT / AV-弓/ JVT網站/ 2003_05_Geneva / JVT-H031.doc
15。 YCoCg-R:具有RGB可逆性和低動態範圍的色彩空間
H. S. Malvar,GJ Sullivan 
ISO / IEC MPEG和ITU-T VCEG的聯合視頻組(JVT),文件號JVT-I014r3,2003年7月
可在線獲取:http: //research.microsoft.com/~malvar/papers/JVT-I014r3.pdf
16。 柯達無損真彩色圖像套件
柯達
在線可用:http://r0k.us/graphics/kodak/
17。 GL_EXT_texture_integer
OpenGL.org,2007年7月
在線提供:http://www.opengl.org/registry/specs/EXT/texture_integer.txt
18。 NV_gpu_program4
OpenGL.org,2007年2月
在線提供:http://www.opengl.org/registry/specs/NV/gpu_program4.txt
19。 Cg 2.0
NVIDIA,2007年7月
在線可用:http://developer.nvidia.com/page/cg_main.html


 

附錄 A

/*
    RGB_ to CoCg_Y conversion and back.
    Copyright (C) 2007 Id Software, Inc.
    Written by J.M.P. van Waveren
    
    This code is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This code is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
*/
 
typedef unsigned char   byte;
 
/* 
    RGB <-> YCoCg
    
    Y  = [ 1/4  1/2  1/4] [R]
    Co = [ 1/2    0 -1/2] [G]
    CG = [-1/4  1/2 -1/4] [B]
    
    R  = [   1    1   -1] [Y]
    G  = [   1    0    1] [Co]
    B  = [   1   -1   -1] [Cg]
    
*/
 
byte CLAMP_BYTE( int x ) { return ( (x) < 0 ? (0) : ( (x) > 255 ? 255 : (x) ) ); }
 
#define RGB_TO_YCOCG_Y( r, g, b )   ( ( (    r +   (g<<1) +  b     ) + 2 ) >> 2 )
#define RGB_TO_YCOCG_CO( r, g, b )  ( ( (   (r<<1)        - (b<<1) ) + 2 ) >> 2 )
#define RGB_TO_YCOCG_CG( r, g, b )  ( ( ( -  r +   (g<<1) -  b     ) + 2 ) >> 2 )
 
#define COCG_TO_R( co, cg )         ( co - cg )
#define COCG_TO_G( co, cg )         ( cg )
#define COCG_TO_B( co, cg )         ( - co - cg )
 
void ConvertRGBToCoCg_Y( byte *image, int width, int height ) {
    for ( int i = 0; i < width * height; i++ ) {
        int r = image[i*4+0];
        int g = image[i*4+1];
        int b = image[i*4+2];
        int a = image[i*4+3];
        image[i*4+0] = CLAMP_BYTE( RGB_TO_YCOCG_CO( r, g, b ) + 128 );
        image[i*4+1] = CLAMP_BYTE( RGB_TO_YCOCG_CG( r, g, b ) + 128 );
        image[i*4+2] = a;
        image[i*4+3] = CLAMP_BYTE( RGB_TO_YCOCG_Y( r, g, b ) );
    }
}
 
void ConvertCoCg_YToRGB( byte *image, int width, int height ) {
    for ( int i = 0; i < width * height; i++ ) {
        int y  = image[i*4+3];
        int co = image[i*4+0] - 128;
        int cg = image[i*4+1] - 128;
        int a  = image[i*4+2];
        image[i*4+0] = CLAMP_BYTE( y + COCG_TO_R( co, cg ) );
        image[i*4+1] = CLAMP_BYTE( y + COCG_TO_G( co, cg ) );
        image[i*4+2] = CLAMP_BYTE( y + COCG_TO_B( co, cg ) );
        image[i*4+3] = a;
    }
}

附錄B

/*
    Real-Time YCoCg DXT Compression
    Copyright (C) 2007 Id Software, Inc.
    Written by J.M.P. van Waveren
    
    This code is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This code is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
*/
 
typedef unsigned char   byte;
typedef unsigned short  word;
typedef unsigned int    dword;
 
#define INSET_COLOR_SHIFT       4       // inset color bounding box
#define INSET_ALPHA_SHIFT       5       // inset alpha bounding box
 
#define C565_5_MASK             0xF8    // 0xFF minus last three bits
#define C565_6_MASK             0xFC    // 0xFF minus last two bits
 
#define NVIDIA_G7X_HARDWARE_BUG_FIX     // keep the colors sorted as: max, min
 
byte *globalOutData;
 
word ColorTo565( const byte *color ) {
    return ( ( color[ 0 ] >> 3 ) << 11 ) | ( ( color[ 1 ] >> 2 ) << 5 ) | ( color[ 2 ] >> 3 );
}
 
void EmitByte( byte b ) {
    globalOutData[0] = b;
    globalOutData += 1;
}
 
void EmitWord( word s ) {
    globalOutData[0] = ( s >>  0 ) & 255;
    globalOutData[1] = ( s >>  8 ) & 255;
    globalOutData += 2;
}
 
void EmitDoubleWord( dword i ) {
    globalOutData[0] = ( i >>  0 ) & 255;
    globalOutData[1] = ( i >>  8 ) & 255;
    globalOutData[2] = ( i >> 16 ) & 255;
    globalOutData[3] = ( i >> 24 ) & 255;
    globalOutData += 4;
}
 
void ExtractBlock( const byte *inPtr, const int width, byte *colorBlock ) {
    for ( int j = 0; j < 4; j++ ) {
        memcpy( &colorBlock[j*4*4], inPtr, 4*4 );
        inPtr += width * 4;
    }
}
 
void GetMinMaxYCoCg( byte *colorBlock, byte *minColor, byte *maxColor ) {
    minColor[0] = minColor[1] = minColor[2] = minColor[3] = 255;
    maxColor[0] = maxColor[1] = maxColor[2] = maxColor[3] = 0;
    
    for ( int i = 0; i < 16; i++ ) {
        if ( colorBlock[i*4+0] < minColor[0] ) {
            minColor[0] = colorBlock[i*4+0];
        }
        if ( colorBlock[i*4+1] < minColor[1] ) {
            minColor[1] = colorBlock[i*4+1];
        }
        if ( colorBlock[i*4+2] < minColor[2] ) {
            minColor[2] = colorBlock[i*4+2];
        }
        if ( colorBlock[i*4+3] < minColor[3] ) {
            minColor[3] = colorBlock[i*4+3];
        }
        if ( colorBlock[i*4+0] > maxColor[0] ) {
            maxColor[0] = colorBlock[i*4+0];
        }
        if ( colorBlock[i*4+1] > maxColor[1] ) {
            maxColor[1] = colorBlock[i*4+1];
        }
        if ( colorBlock[i*4+2] > maxColor[2] ) {
            maxColor[2] = colorBlock[i*4+2];
        }
        if ( colorBlock[i*4+3] > maxColor[3] ) {
            maxColor[3] = colorBlock[i*4+3];
        }
    }
}
 
void ScaleYCoCg( byte *colorBlock, byte *minColor, byte *maxColor ) {
    int m0 = abs( minColor[0] - 128 );
    int m1 = abs( minColor[1] - 128 );
    int m2 = abs( maxColor[0] - 128 );
    int m3 = abs( maxColor[1] - 128 );
    
    if ( m1 > m0 ) m0 = m1;
    if ( m3 > m2 ) m2 = m3;
    if ( m2 > m0 ) m0 = m2;
    
    const int s0 = 128 / 2 - 1;
    const int s1 = 128 / 4 - 1;
    
    int mask0 = -( m0 <= s0 );
    int mask1 = -( m0 <= s1 );
    int scale = 1 + ( 1 & mask0 ) + ( 2 & mask1 );
    
    minColor[0] = ( minColor[0] - 128 ) * scale + 128;
    minColor[1] = ( minColor[1] - 128 ) * scale + 128;
    minColor[2] = ( scale - 1 ) << 3;
    
    maxColor[0] = ( maxColor[0] - 128 ) * scale + 128;
    maxColor[1] = ( maxColor[1] - 128 ) * scale + 128;
    maxColor[2] = ( scale - 1 ) << 3;
    
    for ( int i = 0; i < 16; i++ ) {
        colorBlock[i*4+0] = ( colorBlock[i*4+0] - 128 ) * scale + 128;
        colorBlock[i*4+1] = ( colorBlock[i*4+1] - 128 ) * scale + 128;
    }
}
 
void InsetYCoCgBBox( byte *minColor, byte *maxColor ) {
    int inset[4];
    int mini[4];
    int maxi[4];
    
    inset[0] = ( maxColor[0] - minColor[0] ) - ((1<<(INSET_COLOR_SHIFT-1))-1);
    inset[1] = ( maxColor[1] - minColor[1] ) - ((1<<(INSET_COLOR_SHIFT-1))-1);
    inset[3] = ( maxColor[3] - minColor[3] ) - ((1<<(INSET_ALPHA_SHIFT-1))-1);
    
    mini[0] = ( ( minColor[0] << INSET_COLOR_SHIFT ) + inset[0] ) >> INSET_COLOR_SHIFT;
    mini[1] = ( ( minColor[1] << INSET_COLOR_SHIFT ) + inset[1] ) >> INSET_COLOR_SHIFT;
    mini[3] = ( ( minColor[3] << INSET_ALPHA_SHIFT ) + inset[3] ) >> INSET_ALPHA_SHIFT;
    
    maxi[0] = ( ( maxColor[0] << INSET_COLOR_SHIFT ) - inset[0] ) >> INSET_COLOR_SHIFT;
    maxi[1] = ( ( maxColor[1] << INSET_COLOR_SHIFT ) - inset[1] ) >> INSET_COLOR_SHIFT;
    maxi[3] = ( ( maxColor[3] << INSET_ALPHA_SHIFT ) - inset[3] ) >> INSET_ALPHA_SHIFT;
    
    mini[0] = ( mini[0] >= 0 ) ? mini[0] : 0;
    mini[1] = ( mini[1] >= 0 ) ? mini[1] : 0;
    mini[3] = ( mini[3] >= 0 ) ? mini[3] : 0;
    
    maxi[0] = ( maxi[0] <= 255 ) ? maxi[0] : 255;
    maxi[1] = ( maxi[1] <= 255 ) ? maxi[1] : 255;
    maxi[3] = ( maxi[3] <= 255 ) ? maxi[3] : 255;
    
    minColor[0] = ( mini[0] & C565_5_MASK ) | ( mini[0] >> 5 );
    minColor[1] = ( mini[1] & C565_6_MASK ) | ( mini[1] >> 6 );
    minColor[3] = mini[3];
    
    maxColor[0] = ( maxi[0] & C565_5_MASK ) | ( maxi[0] >> 5 );
    maxColor[1] = ( maxi[1] & C565_6_MASK ) | ( maxi[1] >> 6 );
    maxColor[3] = maxi[3];
}
 
void SelectYCoCgDiagonal( const byte *colorBlock, byte *minColor, byte *maxColor ) const {
    byte mid0 = ( (int) minColor[0] + maxColor[0] + 1 ) >> 1;
    byte mid1 = ( (int) minColor[1] + maxColor[1] + 1 ) >> 1;
    
    byte side = 0;
    for ( int i = 0; i < 16; i++ ) {
        byte b0 = colorBlock[i*4+0] >= mid0;
        byte b1 = colorBlock[i*4+1] >= mid1;
        side += ( b0 ^ b1 );
    }
    
    byte mask = -( side > 8 );
    
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
    mask &= -( minColor[0] != maxColor[0] );
#endif
    
    byte c0 = minColor[1];
    byte c1 = maxColor[1];
    
    c0 ^= c1 ^= mask &= c0 ^= c1;
    
    minColor[1] = c0;
    maxColor[1] = c1;
}
 
void EmitAlphaIndices( const byte *colorBlock, const byte minAlpha, const byte maxAlpha ) {
    
    assert( maxAlpha >= minAlpha );
    
    const int ALPHA_RANGE = 7;
    
    byte mid, ab1, ab2, ab3, ab4, ab5, ab6, ab7;
    byte indexes[16];
    
    mid = ( maxAlpha - minAlpha ) / ( 2 * ALPHA_RANGE );
    
    ab1 = minAlpha + mid;
    ab2 = ( 6 * maxAlpha + 1 * minAlpha ) / ALPHA_RANGE + mid;
    ab3 = ( 5 * maxAlpha + 2 * minAlpha ) / ALPHA_RANGE + mid;
    ab4 = ( 4 * maxAlpha + 3 * minAlpha ) / ALPHA_RANGE + mid;
    ab5 = ( 3 * maxAlpha + 4 * minAlpha ) / ALPHA_RANGE + mid;
    ab6 = ( 2 * maxAlpha + 5 * minAlpha ) / ALPHA_RANGE + mid;
    ab7 = ( 1 * maxAlpha + 6 * minAlpha ) / ALPHA_RANGE + mid;
    
    for ( int i = 0; i < 16; i++ ) {
        byte a = colorBlock[i*4+3];
        int b1 = ( a <= ab1 );
        int b2 = ( a <= ab2 );
        int b3 = ( a <= ab3 );
        int b4 = ( a <= ab4 );
        int b5 = ( a <= ab5 );
        int b6 = ( a <= ab6 );
        int b7 = ( a <= ab7 );
        int index = ( b1 + b2 + b3 + b4 + b5 + b6 + b7 + 1 ) & 7;
        indexes[i] = index ^ ( 2 > index );
    }
    
    EmitByte( (indexes[ 0] >> 0) | (indexes[ 1] << 3) | (indexes[ 2] << 6) );
    EmitByte( (indexes[ 2] >> 2) | (indexes[ 3] << 1) | (indexes[ 4] << 4) | (indexes[ 5] << 7) );
    EmitByte( (indexes[ 5] >> 1) | (indexes[ 6] << 2) | (indexes[ 7] << 5) );
    
    EmitByte( (indexes[ 8] >> 0) | (indexes[ 9] << 3) | (indexes[10] << 6) );
    EmitByte( (indexes[10] >> 2) | (indexes[11] << 1) | (indexes[12] << 4) | (indexes[13] << 7) );
    EmitByte( (indexes[13] >> 1) | (indexes[14] << 2) | (indexes[15] << 5) );
}
 
void EmitColorIndices( const byte *colorBlock, const byte *minColor, const byte *maxColor ) {
    word colors[4][4];
    unsigned int result = 0;
    
    colors[0][0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 5 );
    colors[0][1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 6 );
    colors[0][2] = ( maxColor[2] & C565_5_MASK ) | ( maxColor[2] >> 5 );
    colors[0][3] = 0;
    colors[1][0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 5 );
    colors[1][1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 6 );
    colors[1][2] = ( minColor[2] & C565_5_MASK ) | ( minColor[2] >> 5 );
    colors[1][3] = 0;
    colors[2][0] = ( 2 * colors[0][0] + 1 * colors[1][0] ) / 3;
    colors[2][1] = ( 2 * colors[0][1] + 1 * colors[1][1] ) / 3;
    colors[2][2] = ( 2 * colors[0][2] + 1 * colors[1][2] ) / 3;
    colors[2][3] = 0;
    colors[3][0] = ( 1 * colors[0][0] + 2 * colors[1][0] ) / 3;
    colors[3][1] = ( 1 * colors[0][1] + 2 * colors[1][1] ) / 3;
    colors[3][2] = ( 1 * colors[0][2] + 2 * colors[1][2] ) / 3;
    colors[3][3] = 0;
    
    for ( int i = 15; i >= 0; i-- ) {
        int c0, c1, d0, d1, d2, d3;
        
        c0 = colorBlock[i*4+0];
        c1 = colorBlock[i*4+1];
        
        int d0 = abs( colors[0][0] - c0 ) + abs( colors[0][1] - c1 );
        int d1 = abs( colors[1][0] - c0 ) + abs( colors[1][1] - c1 );
        int d2 = abs( colors[2][0] - c0 ) + abs( colors[2][1] - c1 );
        int d3 = abs( colors[3][0] - c0 ) + abs( colors[3][1] - c1 );
        
        bool b0 = d0 > d3;
        bool b1 = d1 > d2;
        bool b2 = d0 > d2;
        bool b3 = d1 > d3;
        bool b4 = d2 > d3;
        
        int x0 = b1 & b2;
        int x1 = b0 & b3;
        int x2 = b0 & b4;
        
        result |= ( x2 | ( ( x0 | x1 ) << 1 ) ) << ( i << 1 );
    }
    
    EmitUInt( result );
}
 
bool CompressYCoCgDXT5( const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes ) {
    byte block[64];
    byte minColor[4];
    byte maxColor[4];
    
    globalOutData = outBuf;
    
    for ( int j = 0; j < height; j += 4, inBuf += width * 4*4 ) {
        for ( int i = 0; i < width; i += 4 ) {
            
            ExtractBlock( inBuf + i * 4, width, block );
            
            GetMinMaxYCoCg( block, minColor, maxColor );
            ScaleYCoCg( block, minColor, maxColor );
            InsetYCoCgBBox( minColor, maxColor );
            SelectYCoCgDiagonal( block, minColor, maxColor );
            
            EmitByte( maxColor[3] );
            EmitByte( minColor[3] );
            
            EmitAlphaIndices( block, minColor[3], maxColor[3] );
            
            EmitUShort( ColorTo565( maxColor ) );
            EmitUShort( ColorTo565( minColor ) );
            
            EmitColorIndices( block, minColor, maxColor );
        }
    }
    
    outputBytes = globalOutData - outBuf;
    
    return true;
}

Appendix C

/*
    Real-Time YCoCg DXT Compression (MMX)
    Copyright (C) 2007 Id Software, Inc.
    Written by J.M.P. van Waveren
    
    This code is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This code is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
*/
 
#define ALIGN16( x )                __declspec(align(16)) x
#define R_SHUFFLE_D( x, y, z, w )   (( (w) & 3 ) << 6 | ( (z) & 3 ) << 4 | ( (y) & 3 ) << 2 | ( (x) & 3 ))
 
ALIGN16( static dword SIMD_MMX_dword_word_mask[2] ) = { 0x0000FFFF, 0x0000FFFF };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask0[2] ) = { 7<<0, 0 };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask1[2] ) = { 7<<3, 0 };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask2[2] ) = { 7<<6, 0 };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask3[2] ) = { 7<<9, 0 };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask4[2] ) = { 7<<12, 0 };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask5[2] ) = { 7<<15, 0 };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask6[2] ) = { 7<<18, 0 };
ALIGN16( static dword SIMD_MMX_dword_alpha_bit_mask7[2] ) = { 7<<21, 0 };
ALIGN16( static word SIMD_MMX_word_0[4] ) = { 0x0000, 0x0000, 0x0000, 0x0000 };
ALIGN16( static word SIMD_MMX_word_1[4] ) = { 0x0001, 0x0001, 0x0001, 0x0001 };
ALIGN16( static word SIMD_MMX_word_2[4] ) = { 0x0002, 0x0002, 0x0002, 0x0002 };
ALIGN16( static word SIMD_MMX_word_31[4] ) = { 31, 31, 31, 31 };
ALIGN16( static word SIMD_MMX_word_63[4] ) = { 63, 63, 63, 63 };
ALIGN16( static word SIMD_MMX_word_center_128[4] ) = { 128, 128, 0, 0 };
ALIGN16( static word SIMD_MMX_word_div_by_3[4] ) = { (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1 };
ALIGN16( static word SIMD_MMX_word_div_by_7[4] ) = { (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1 };
ALIGN16( static word SIMD_MMX_word_div_by_14[4] ) = { (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1 };
ALIGN16( static word SIMD_MMX_word_scale654[4] ) = { 6, 5, 4, 0 };
ALIGN16( static word SIMD_MMX_word_scale123[4] ) = { 1, 2, 3, 0 };
ALIGN16( static word SIMD_MMX_word_insetShift[4] ) = { 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_ALPHA_SHIFT ) };
ALIGN16( static word SIMD_MMX_word_insetShiftUp[4] ) = { 1 << INSET_COLOR_SHIFT, 1 << INSET_COLOR_SHIFT, 1 << INSET_COLOR_SHIFT, 1 << INSET_ALPHA_SHIFT };
ALIGN16( static word SIMD_MMX_word_insetShiftDown[4] ) = { 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_ALPHA_SHIFT ) };
ALIGN16( static word SIMD_MMX_word_insetYCoCgRound[4] ) = { ((1<<(INSET_COLOR_SHIFT-1))-1), ((1<<(INSET_COLOR_SHIFT-1))-1), ((1<<(INSET_COLOR_SHIFT-1))-1), ((1<<(INSET_ALPHA_SHIFT-1))-1) };
ALIGN16( static word SIMD_MMX_word_insetYCoCgMask[4] ) = { 0xFFFF, 0xFFFF, 0x0000, 0xFFFF };
ALIGN16( static word SIMD_MMX_word_inset565Mask[4] ) = { C565_5_MASK, C565_6_MASK, C565_5_MASK, 0xFF };
ALIGN16( static word SIMD_MMX_word_inset565Rep[4] ) = { 1 << ( 16 - 5 ), 1 << ( 16 - 6 ), 1 << ( 16 - 5 ), 0 };
ALIGN16( static byte SIMD_MMX_byte_0[8] ) = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_MMX_byte_1[8] ) = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
ALIGN16( static byte SIMD_MMX_byte_2[8] ) = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
ALIGN16( static byte SIMD_MMX_byte_7[8] ) = { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 };
ALIGN16( static byte SIMD_MMX_byte_8[8] ) = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 };
ALIGN16( static byte SIMD_MMX_byte_not[8] ) = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
ALIGN16( static byte SIMD_MMX_byte_colorMask[8] ) = { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_MMX_byte_diagonalMask[8] ) = { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_MMX_byte_scale_mask0[8] ) = { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF };
ALIGN16( static byte SIMD_MMX_byte_scale_mask1[8] ) = { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_MMX_byte_scale_mask2[8] ) = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 };
ALIGN16( static byte SIMD_MMX_byte_scale_mask3[8] ) = { 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_MMX_byte_scale_mask4[8] ) = { 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_MMX_byte_minus_128_0[8] ) = { -128, -128, 0, 0, -128, -128, 0, 0 };
 
void ExtractBlock_MMX( const byte *inPtr, int width, byte *colorBlock ) {
    __asm {
        mov         esi, inPtr
        mov         edi, colorBlock
        mov         eax, width
        shl         eax, 2
        movq        mm0, qword ptr [esi+0]
        movq        qword ptr [edi+ 0], mm0
        movq        mm1, qword ptr [esi+8]
        movq        qword ptr [edi+ 8], mm1
        movq        mm2, qword ptr [esi+eax+0]
        movq        qword ptr [edi+16], mm2
        movq        mm3, qword ptr [esi+eax+8]
        movq        qword ptr [edi+24], mm3
        movq        mm4, qword ptr [esi+eax*2+0]
        movq        qword ptr [edi+32], mm4
        movq        mm5, qword ptr [esi+eax*2+8]
        add         esi, eax
        movq        qword ptr [edi+40], mm5
        movq        mm6, qword ptr [esi+eax*2+0]
        movq        qword ptr [edi+48], mm6
        movq        mm7, qword ptr [esi+eax*2+8]
        movq        qword ptr [edi+56], mm7
        emms
    }
}
 
void GetMinMaxYCoCg_MMX( const byte *colorBlock, byte *minColor, byte *maxColor ) {
    __asm {
        mov         eax, colorBlock
        mov         esi, minColor
        mov         edi, maxColor
        pshufw      mm0, qword ptr [eax+ 0], R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm1, qword ptr [eax+ 0], R_SHUFFLE_D( 0, 1, 2, 3 )
        pminub      mm0, qword ptr [eax+ 8]
        pmaxub      mm1, qword ptr [eax+ 8]
        pminub      mm0, qword ptr [eax+16]
        pmaxub      mm1, qword ptr [eax+16]
        pminub      mm0, qword ptr [eax+24]
        pmaxub      mm1, qword ptr [eax+24]
        pminub      mm0, qword ptr [eax+32]
        pmaxub      mm1, qword ptr [eax+32]
        pminub      mm0, qword ptr [eax+40]
        pmaxub      mm1, qword ptr [eax+40]
        pminub      mm0, qword ptr [eax+48]
        pmaxub      mm1, qword ptr [eax+48]
        pminub      mm0, qword ptr [eax+56]
        pmaxub      mm1, qword ptr [eax+56]
        pshufw      mm6, mm0, R_SHUFFLE_D( 2, 3, 2, 3 )
        pshufw      mm7, mm1, R_SHUFFLE_D( 2, 3, 2, 3 )
        pminub      mm0, mm6
        pmaxub      mm1, mm7
        movd        dword ptr [esi], mm0
        movd        dword ptr [edi], mm1
        emms
    }
}
 
void ScaleYCoCg_MMX( byte *colorBlock, byte *minColor, byte *maxColor ) {
    __asm {
        mov         esi, colorBlock
        mov         edx, minColor
        mov         ecx, maxColor
        
        movd        mm0, dword ptr [edx]
        movd        mm1, dword ptr [ecx]
        
        punpcklbw   mm0, SIMD_MMX_byte_0
        punpcklbw   mm1, SIMD_MMX_byte_0
        
        movq        mm6, SIMD_MMX_word_center_128
        movq        mm7, SIMD_MMX_word_center_128
        
        psubw       mm6, mm0
        psubw       mm7, mm1
        
        psubw       mm0, SIMD_MMX_word_center_128
        psubw       mm1, SIMD_MMX_word_center_128
        
        pmaxsw      mm6, mm0
        pmaxsw      mm7, mm1
        
        pmaxsw      mm6, mm7
        pshufw      mm7, mm6, R_SHUFFLE_D( 1, 0, 1, 0 )
        pmaxsw      mm6, mm7
        pshufw      mm6, mm6, R_SHUFFLE_D( 0, 1, 0, 1 )
        
        movq        mm7, mm6
        pcmpgtw     mm6, SIMD_MMX_word_63
        pcmpgtw     mm7, SIMD_MMX_word_32
        
        pandn       mm7, SIMD_MMX_byte_2
        por         mm7, SIMD_MMX_byte_1
        pandn       mm6, mm7
        movq        mm3, mm6
        movq        mm7, mm6
        pxor        mm7, SIMD_MMX_byte_not
        por         mm7, SIMD_MMX_byte_scale_mask0
        paddw       mm6, SIMD_MMX_byte_1
        pand        mm6, SIMD_MMX_byte_scale_mask1
        por         mm6, SIMD_MMX_byte_scale_mask2
        
        movd        mm4, dword ptr [edx]
        movd        mm5, dword ptr [ecx]
        
        pand        mm4, SIMD_MMX_byte_scale_mask3
        pand        mm5, SIMD_MMX_byte_scale_mask3
        
        pslld       mm3, 3
        pand        mm3, SIMD_MMX_byte_scale_mask4
        
        por         mm4, mm3
        por         mm5, mm3
        
        paddb       mm4, SIMD_MMX_byte_minus_128_0
        paddb       mm5, SIMD_MMX_byte_minus_128_0
        
        pmullw      mm4, mm6
        pmullw      mm5, mm6
        
        pand        mm4, mm7
        pand        mm5, mm7
        
        psubb       mm4, SIMD_MMX_byte_minus_128_0
        psubb       mm5, SIMD_MMX_byte_minus_128_0
        
        movd        dword ptr [edx], mm4
        movd        dword ptr [ecx], mm5
        
        movq        mm0, qword ptr [esi+ 0*4]
        movq        mm1, qword ptr [esi+ 2*4]
        movq        mm2, qword ptr [esi+ 4*4]
        movq        mm3, qword ptr [esi+ 6*4]
        
        paddb       mm0, SIMD_MMX_byte_minus_128_0
        paddb       mm1, SIMD_MMX_byte_minus_128_0
        paddb       mm2, SIMD_MMX_byte_minus_128_0
        paddb       mm3, SIMD_MMX_byte_minus_128_0
        
        pmullw      mm0, mm6
        pmullw      mm1, mm6
        pmullw      mm2, mm6
        pmullw      mm3, mm6
        
        pand        mm0, mm7
        pand        mm1, mm7
        pand        mm2, mm7
        pand        mm3, mm7
        
        psubb       mm0, SIMD_MMX_byte_minus_128_0
        psubb       mm1, SIMD_MMX_byte_minus_128_0
        psubb       mm2, SIMD_MMX_byte_minus_128_0
        psubb       mm3, SIMD_MMX_byte_minus_128_0
        
        movq        qword ptr [esi+ 0*4], mm0
        movq        qword ptr [esi+ 2*4], mm1
        movq        qword ptr [esi+ 4*4], mm2
        movq        qword ptr [esi+ 6*4], mm3
        
        movq        mm0, qword ptr [esi+ 8*4]
        movq        mm1, qword ptr [esi+10*4]
        movq        mm2, qword ptr [esi+12*4]
        movq        mm3, qword ptr [esi+14*4]
        
        paddb       mm0, SIMD_MMX_byte_minus_128_0
        paddb       mm1, SIMD_MMX_byte_minus_128_0
        paddb       mm2, SIMD_MMX_byte_minus_128_0
        paddb       mm3, SIMD_MMX_byte_minus_128_0
        
        pmullw      mm0, mm6
        pmullw      mm1, mm6
        pmullw      mm2, mm6
        pmullw      mm3, mm6
        
        pand        mm0, mm7
        pand        mm1, mm7
        pand        mm2, mm7
        pand        mm3, mm7
        
        psubb       mm0, SIMD_MMX_byte_minus_128_0
        psubb       mm1, SIMD_MMX_byte_minus_128_0
        psubb       mm2, SIMD_MMX_byte_minus_128_0
        psubb       mm3, SIMD_MMX_byte_minus_128_0
        
        movq        qword ptr [esi+ 8*4], mm0
        movq        qword ptr [esi+10*4], mm1
        movq        qword ptr [esi+12*4], mm2
        movq        qword ptr [esi+14*4], mm3
        
        emms
    }
}
 
void InsetYCoCgBBox_MMX( byte *minColor, byte *maxColor ) {
    __asm {
        mov         esi, minColor
        mov         edi, maxColor
        movd        mm0, dword ptr [esi]
        movd        mm1, dword ptr [edi]
        punpcklbw   mm0, SIMD_MMX_byte_0
        punpcklbw   mm1, SIMD_MMX_byte_0
        movq        mm2, mm1
        psubw       mm2, mm0
        psubw       mm2, SIMD_MMX_word_insetYCoCgRound
        pand        mm2, SIMD_MMX_word_insetYCoCgMask
        pmullw      mm0, SIMD_MMX_word_insetShiftUp
        pmullw      mm1, SIMD_MMX_word_insetShiftUp
        paddw       mm0, mm2
        psubw       mm1, mm2
        pmulhw      mm0, SIMD_MMX_word_insetShiftDown
        pmulhw      mm1, SIMD_MMX_word_insetShiftDown
        pmaxsw      mm0, SIMD_MMX_word_0
        pmaxsw      mm1, SIMD_MMX_word_0
        pand        mm0, SIMD_MMX_word_inset565Mask
        pand        mm1, SIMD_MMX_word_inset565Mask
        movq        mm2, mm0
        movq        mm3, mm1
        pmulhw      mm2, SIMD_MMX_word_inset565Rep
        pmulhw      mm3, SIMD_MMX_word_inset565Rep
        por         mm0, mm2
        por         mm1, mm3
        packuswb    mm0, mm0
        packuswb    mm1, mm1
        movd        dword ptr [esi], mm0
        movd        dword ptr [edi], mm1
        emms
    }
}
 
void SelectYCoCgDiagonal_MMX( const byte *colorBlock, byte *minColor, byte *maxColor ) {
    __asm {
        mov         esi, colorBlock
        mov         edx, minColor
        mov         ecx, maxColor
        
        movq        mm0, qword ptr [esi+ 0]
        movq        mm2, qword ptr [esi+ 8]
        movq        mm1, qword ptr [esi+16]
        movq        mm3, qword ptr [esi+24]
        
        pand        mm0, SIMD_MMX_dword_word_mask
        pand        mm2, SIMD_MMX_dword_word_mask
        pand        mm1, SIMD_MMX_dword_word_mask
        pand        mm3, SIMD_MMX_dword_word_mask
        
        psllq       mm0, 16
        psllq       mm3, 16
        por         mm0, mm2
        por         mm1, mm3
        
        movq        mm2, qword ptr [esi+32]
        movq        mm4, qword ptr [esi+40]
        movq        mm3, qword ptr [esi+48]
        movq        mm5, qword ptr [esi+56]
        
        pand        mm2, SIMD_MMX_dword_word_mask
        pand        mm4, SIMD_MMX_dword_word_mask
        pand        mm3, SIMD_MMX_dword_word_mask
        pand        mm5, SIMD_MMX_dword_word_mask
        
        psllq       mm2, 16
        psllq       mm5, 16
        por         mm2, mm4
        por         mm3, mm5
        
        movd        mm4, dword ptr [edx]
        movd        mm5, dword ptr [ecx]
        
        pavgb       mm4, mm5
        pshufw      mm4, mm4, R_SHUFFLE_D( 0, 0, 0, 0 )
        movq        mm5, mm4
        movq        mm6, mm4
        movq        mm7, mm4
        
        pmaxub      mm4, mm0
        pmaxub      mm5, mm1
        pmaxub      mm6, mm2
        pmaxub      mm7, mm3
        
        pcmpeqb     mm4, mm0
        pcmpeqb     mm5, mm1
        pcmpeqb     mm6, mm2
        pcmpeqb     mm7, mm3
        
        movq        mm0, mm4
        movq        mm1, mm5
        movq        mm2, mm6
        movq        mm3, mm7
        
        psrlq       mm0, 8
        psrlq       mm1, 8
        psrlq       mm2, 8
        psrlq       mm3, 8
        
        pxor        mm0, mm4
        pxor        mm1, mm5
        pxor        mm2, mm6
        pxor        mm3, mm7
        
        pand        mm0, SIMD_MMX_word_1
        pand        mm1, SIMD_MMX_word_1
        pand        mm2, SIMD_MMX_word_1
        pand        mm3, SIMD_MMX_word_1
        
        paddw       mm0, mm3
        paddw       mm1, mm2
        
        movd        mm6, dword ptr [edx]
        movd        mm7, dword ptr [ecx]
        
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
        paddw       mm1, mm0
        psadbw      mm1, SIMD_MMX_byte_0
        pcmpgtw     mm1, SIMD_MMX_word_8
        pand        mm1, SIMD_MMX_byte_diagonalMask
        movq        mm0, mm6
        pcmpeqb     mm0, mm7
        psllq       mm0, 8
        pandn       mm0, mm1
#else
        paddw       mm0, mm1
        psadbw      mm0, SIMD_MMX_byte_0
        pcmpgtw     mm0, SIMD_MMX_word_8
        pand        mm0, SIMD_MMX_byte_diagonalMask
#endif
        
        pxor        mm6, mm7
        pand        mm0, mm6
        pxor        mm7, mm0
        pxor        mm6, mm7
        
        movd        dword ptr [edx], mm6
        movd        dword ptr [ecx], mm7
        
        emms
    }
}
 
void EmitAlphaIndices_MMX( const byte *colorBlock, const byte minAlpha, const byte maxAlpha ) {
    
    ALIGN16( byte alphaBlock[16] );
    ALIGN16( byte ab1[8] );
    ALIGN16( byte ab2[8] );
    ALIGN16( byte ab3[8] );
    ALIGN16( byte ab4[8] );
    ALIGN16( byte ab5[8] );
    ALIGN16( byte ab6[8] );
    ALIGN16( byte ab7[8] );
    
    __asm {
        mov         esi, colorBlock
        movq        mm0, qword ptr [esi+ 0]
        movq        mm5, qword ptr [esi+ 8]
        psrld       mm0, 24
        psrld       mm5, 24
        packuswb    mm0, mm5
        
        movq        mm6, qword ptr [esi+16]
        movq        mm4, qword ptr [esi+24]
        psrld       mm6, 24
        psrld       mm4, 24
        packuswb    mm6, mm4
        
        packuswb    mm0, mm6
        movq        alphaBlock+0, mm0
        
        movq        mm0, qword ptr [esi+32]
        movq        mm5, qword ptr [esi+40]
        psrld       mm0, 24
        psrld       mm5, 24
        packuswb    mm0, mm5
        
        movq        mm6, qword ptr [esi+48]
        movq        mm4, qword ptr [esi+56]
        psrld       mm6, 24
        psrld       mm4, 24
        packuswb    mm6, mm4
        
        packuswb    mm0, mm6
        movq        alphaBlock+8, mm0
        
        movzx       ecx, maxAlpha
        movd        mm0, ecx
        pshufw      mm0, mm0, R_SHUFFLE_D( 0, 0, 0, 0 )
        movq        mm1, mm0
        
        movzx       edx, minAlpha
        movd        mm2, edx
        pshufw      mm2, mm2, R_SHUFFLE_D( 0, 0, 0, 0 )
        movq        mm3, mm2
        
        movq        mm4, mm0
        psubw       mm4, mm2
        pmulhw      mm4, SIMD_MMX_word_div_by_14
        
        movq        mm5, mm2
        paddw       mm5, mm4
        packuswb    mm5, mm5
        movq        ab1, mm5
        
        pmullw      mm0, SIMD_MMX_word_scale654
        pmullw      mm1, SIMD_MMX_word_scale123
        pmullw      mm2, SIMD_MMX_word_scale123
        pmullw      mm3, SIMD_MMX_word_scale654
        paddw       mm0, mm2
        paddw       mm1, mm3
        pmulhw      mm0, SIMD_MMX_word_div_by_7
        pmulhw      mm1, SIMD_MMX_word_div_by_7
        paddw       mm0, mm4
        paddw       mm1, mm4
        
        pshufw      mm2, mm0, R_SHUFFLE_D( 0, 0, 0, 0 )
        pshufw      mm3, mm0, R_SHUFFLE_D( 1, 1, 1, 1 )
        pshufw      mm4, mm0, R_SHUFFLE_D( 2, 2, 2, 2 )
        packuswb    mm2, mm2
        packuswb    mm3, mm3
        packuswb    mm4, mm4
        movq        ab2, mm2
        movq        ab3, mm3
        movq        ab4, mm4
        
        pshufw      mm2, mm1, R_SHUFFLE_D( 2, 2, 2, 2 )
        pshufw      mm3, mm1, R_SHUFFLE_D( 1, 1, 1, 1 )
        pshufw      mm4, mm1, R_SHUFFLE_D( 0, 0, 0, 0 )
        packuswb    mm2, mm2
        packuswb    mm3, mm3
        packuswb    mm4, mm4
        movq        ab5, mm2
        movq        ab6, mm3
        movq        ab7, mm4
        
        pshufw      mm0, alphaBlock+0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm1, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm2, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm3, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm4, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm5, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm6, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm7, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pminub      mm1, ab1
        pminub      mm2, ab2
        pminub      mm3, ab3
        pminub      mm4, ab4
        pminub      mm5, ab5
        pminub      mm6, ab6
        pminub      mm7, ab7
        pcmpeqb     mm1, mm0
        pcmpeqb     mm2, mm0
        pcmpeqb     mm3, mm0
        pcmpeqb     mm4, mm0
        pcmpeqb     mm5, mm0
        pcmpeqb     mm6, mm0
        pcmpeqb     mm7, mm0
        pand        mm1, SIMD_MMX_byte_1
        pand        mm2, SIMD_MMX_byte_1
        pand        mm3, SIMD_MMX_byte_1
        pand        mm4, SIMD_MMX_byte_1
        pand        mm5, SIMD_MMX_byte_1
        pand        mm6, SIMD_MMX_byte_1
        pand        mm7, SIMD_MMX_byte_1
        pshufw      mm0, SIMD_MMX_byte_1, R_SHUFFLE_D( 0, 1, 2, 3 )
        paddusb     mm0, mm1
        paddusb     mm0, mm2
        paddusb     mm0, mm3
        paddusb     mm0, mm4
        paddusb     mm0, mm5
        paddusb     mm0, mm6
        paddusb     mm0, mm7
        pand        mm0, SIMD_MMX_byte_7
        pshufw      mm1, SIMD_MMX_byte_2, R_SHUFFLE_D( 0, 1, 2, 3 )
        pcmpgtb     mm1, mm0
        pand        mm1, SIMD_MMX_byte_1
        pxor        mm0, mm1
        pshufw      mm1, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm2, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm3, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        psrlq       mm1,  8- 3
        psrlq       mm2, 16- 6
        psrlq       mm3, 24- 9
        pshufw      mm4, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm5, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm6, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm7, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        psrlq       mm4, 32-12
        psrlq       mm5, 40-15
        psrlq       mm6, 48-18
        psrlq       mm7, 56-21
        pand        mm0, SIMD_MMX_dword_alpha_bit_mask0
        pand        mm1, SIMD_MMX_dword_alpha_bit_mask1
        pand        mm2, SIMD_MMX_dword_alpha_bit_mask2
        pand        mm3, SIMD_MMX_dword_alpha_bit_mask3
        pand        mm4, SIMD_MMX_dword_alpha_bit_mask4
        pand        mm5, SIMD_MMX_dword_alpha_bit_mask5
        pand        mm6, SIMD_MMX_dword_alpha_bit_mask6
        pand        mm7, SIMD_MMX_dword_alpha_bit_mask7
        por         mm0, mm1
        por         mm2, mm3
        por         mm4, mm5
        por         mm6, mm7
        por         mm0, mm2
        por         mm4, mm6
        por         mm0, mm4
        mov         esi, globalOutData
        movd        dword ptr [esi+0], mm0
        
        pshufw      mm0, alphaBlock+8, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm1, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm2, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm3, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm4, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm5, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm6, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm7, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pminub      mm1, ab1
        pminub      mm2, ab2
        pminub      mm3, ab3
        pminub      mm4, ab4
        pminub      mm5, ab5
        pminub      mm6, ab6
        pminub      mm7, ab7
        pcmpeqb     mm1, mm0
        pcmpeqb     mm2, mm0
        pcmpeqb     mm3, mm0
        pcmpeqb     mm4, mm0
        pcmpeqb     mm5, mm0
        pcmpeqb     mm6, mm0
        pcmpeqb     mm7, mm0
        pand        mm1, SIMD_MMX_byte_1
        pand        mm2, SIMD_MMX_byte_1
        pand        mm3, SIMD_MMX_byte_1
        pand        mm4, SIMD_MMX_byte_1
        pand        mm5, SIMD_MMX_byte_1
        pand        mm6, SIMD_MMX_byte_1
        pand        mm7, SIMD_MMX_byte_1
        pshufw      mm0, SIMD_MMX_byte_1, R_SHUFFLE_D( 0, 1, 2, 3 )
        paddusb     mm0, mm1
        paddusb     mm0, mm2
        paddusb     mm0, mm3
        paddusb     mm0, mm4
        paddusb     mm0, mm5
        paddusb     mm0, mm6
        paddusb     mm0, mm7
        pand        mm0, SIMD_MMX_byte_7
        pshufw      mm1, SIMD_MMX_byte_2, R_SHUFFLE_D( 0, 1, 2, 3 )
        pcmpgtb     mm1, mm0
        pand        mm1, SIMD_MMX_byte_1
        pxor        mm0, mm1
        pshufw      mm1, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm2, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm3, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        psrlq       mm1,  8- 3
        psrlq       mm2, 16- 6
        psrlq       mm3, 24- 9
        pshufw      mm4, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm5, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm6, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        pshufw      mm7, mm0, R_SHUFFLE_D( 0, 1, 2, 3 )
        psrlq       mm4, 32-12
        psrlq       mm5, 40-15
        psrlq       mm6, 48-18
        psrlq       mm7, 56-21
        pand        mm0, SIMD_MMX_dword_alpha_bit_mask0
        pand        mm1, SIMD_MMX_dword_alpha_bit_mask1
        pand        mm2, SIMD_MMX_dword_alpha_bit_mask2
        pand        mm3, SIMD_MMX_dword_alpha_bit_mask3
        pand        mm4, SIMD_MMX_dword_alpha_bit_mask4
        pand        mm5, SIMD_MMX_dword_alpha_bit_mask5
        pand        mm6, SIMD_MMX_dword_alpha_bit_mask6
        pand        mm7, SIMD_MMX_dword_alpha_bit_mask7
        por         mm0, mm1
        por         mm2, mm3
        por         mm4, mm5
        por         mm6, mm7
        por         mm0, mm2
        por         mm4, mm6
        por         mm0, mm4
        movd        dword ptr [esi+3], mm0
        emms
    }
    
    globalOutData += 6;
}
 
void EmitColorIndices_MMX( const byte *colorBlock, const byte *minColor, const byte *maxColor ) {
    
    ALIGN16( byte color0[8] );
    ALIGN16( byte color1[8] );
    ALIGN16( byte color2[8] );
    ALIGN16( byte color3[8] );
    ALIGN16( byte result[8] );
    
    __asm {
        mov         esi, maxColor
        mov         edi, minColor
        pxor        mm7, mm7
        movq        result, mm7
        
        movd        mm0, dword ptr [esi]
        pand        mm0, SIMD_MMX_byte_colorMask
        movq        color0, mm0
        
        movd        mm1, dword ptr [edi]
        pand        mm1, SIMD_MMX_byte_colorMask
        movq        color1, mm1
        
        punpcklbw   mm0, mm7
        punpcklbw   mm1, mm7
        
        movq        mm6, mm1
        paddw       mm1, mm0
        paddw       mm0, mm1
        pmulhw      mm0, SIMD_MMX_word_div_by_3
        packuswb    mm0, mm7
        movq        color2, mm0
        
        paddw       mm1, mm6
        pmulhw      mm1, SIMD_MMX_word_div_by_3
        packuswb    mm1, mm7
        movq        color3, mm1
        
        mov         eax, 48
        mov         esi, colorBlock
        
    loop1:          // iterates 4 times
        movd        mm3, dword ptr [esi+eax+0]
        movd        mm5, dword ptr [esi+eax+4]
        
        movq        mm0, mm3
        movq        mm6, mm5
        psadbw      mm0, color0
        psadbw      mm6, color0
        packssdw    mm0, mm6
        movq        mm1, mm3
        movq        mm6, mm5
        psadbw      mm1, color1
        psadbw      mm6, color1
        packssdw    mm1, mm6
        movq        mm2, mm3
        movq        mm6, mm5
        psadbw      mm2, color2
        psadbw      mm6, color2
        packssdw    mm2, mm6
        psadbw      mm3, color3
        psadbw      mm5, color3
        packssdw    mm3, mm5
        
        movd        mm4, dword ptr [esi+eax+8]
        movd        mm5, dword ptr [esi+eax+12]
        
        movq        mm6, mm4
        movq        mm7, mm5
        psadbw      mm6, color0
        psadbw      mm7, color0
        packssdw    mm6, mm7
        packssdw    mm0, mm6
        movq        mm6, mm4
        movq        mm7, mm5
        psadbw      mm6, color1
        psadbw      mm7, color1
        packssdw    mm6, mm7
        packssdw    mm1, mm6
        movq        mm6, mm4
        movq        mm7, mm5
        psadbw      mm6, color2
        psadbw      mm7, color2
        packssdw    mm6, mm7
        packssdw    mm2, mm6
        psadbw      mm4, color3
        psadbw      mm5, color3
        packssdw    mm4, mm5
        packssdw    mm3, mm4
        
        movq        mm7, result
        pslld       mm7, 8
        
        movq        mm4, mm0
        movq        mm5, mm1
        pcmpgtw     mm0, mm3
        pcmpgtw     mm1, mm2
        pcmpgtw     mm4, mm2
        pcmpgtw     mm5, mm3
        pcmpgtw     mm2, mm3
        pand        mm4, mm1
        pand        mm5, mm0
        pand        mm2, mm0
        por         mm4, mm5
        pand        mm2, SIMD_MMX_word_1
        pand        mm4, SIMD_MMX_word_2
        por         mm2, mm4
        
        pshufw      mm5, mm2, R_SHUFFLE_D( 2, 3, 0, 1 )
        punpcklwd   mm2, SIMD_MMX_word_0
        punpcklwd   mm5, SIMD_MMX_word_0
        pslld       mm5, 4
        por         mm7, mm5
        por         mm7, mm2
        movq        result, mm7
        
        sub         eax, 16
        jge         loop1
        
        mov         esi, globalOutData
        movq        mm6, mm7
        psrlq       mm6, 32-2
        por         mm7, mm6
        movd        dword ptr [esi], mm7
        emms
    }
    
    globalOutData += 4;
}
 
bool CompressYCoCgDXT5_MMX( const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes ) {
    ALIGN16( byte block[64] );
    ALIGN16( byte minColor[4] );
    ALIGN16( byte maxColor[4] );
    
    globalOutData = outBuf;
    
    for ( int j = 0; j < height; j += 4, inBuf += width * 4*4 ) {
        for ( int i = 0; i < width; i += 4 ) {
            
            ExtractBlock_MMX( inBuf + i * 4, width, block );
            
            GetMinMaxYCoCg_MMX( block, minColor, maxColor );
            ScaleYCoCg_MMX( block, minColor, maxColor );
            InsetYCoCgBBox_MMX( minColor, maxColor );
            SelectYCoCgDiagonal_MMX( block, minColor, maxColor );
            
            EmitByte( maxColor[3] );
            EmitByte( minColor[3] );
            
            EmitAlphaIndices_MMX( block, minColor[3], maxColor[3] );
            
            EmitUShort( ColorTo565( maxColor ) );
            EmitUShort( ColorTo565( minColor ) );
            
            EmitColorIndices_MMX( block, minColor, maxColor );
        }
    }
    
    outputBytes = globalOutData - outBuf;
    
    return true;
}

Appendix D

/*
    Real-Time YCoCg DXT Compression (SSE2)
    Copyright (C) 2007 Id Software, Inc.
    Written by J.M.P. van Waveren
    
    This code is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This code is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
*/
 
#define ALIGN16( x )                __declspec(align(16)) x
#define R_SHUFFLE_D( x, y, z, w )   (( (w) & 3 ) << 6 | ( (z) & 3 ) << 4 | ( (y) & 3 ) << 2 | ( (x) & 3 ))
 
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask0[4] ) = { 7<<0, 0, 7<<0, 0 };
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask1[4] ) = { 7<<3, 0, 7<<3, 0 };
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask2[4] ) = { 7<<6, 0, 7<<6, 0 };
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask3[4] ) = { 7<<9, 0, 7<<9, 0 };
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask4[4] ) = { 7<<12, 0, 7<<12, 0 };
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask5[4] ) = { 7<<15, 0, 7<<15, 0 };
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask6[4] ) = { 7<<18, 0, 7<<18, 0 };
ALIGN16( static dword SIMD_SSE2_dword_alpha_bit_mask7[4] ) = { 7<<21, 0, 7<<21, 0 };
ALIGN16( static word SIMD_SSE2_word_0[8] ) = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 };
ALIGN16( static word SIMD_SSE2_word_1[8] ) = { 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001 };
ALIGN16( static word SIMD_SSE2_word_2[8] ) = { 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002 };
ALIGN16( static word SIMD_SSE2_word_31[8] ) = { 31, 31, 31, 31, 31, 31, 31, 31 };
ALIGN16( static word SIMD_SSE2_word_63[8] ) = { 63, 63, 63, 63, 63, 63, 63, 63 };
ALIGN16( static word SIMD_SSE2_word_center_128[8] ) = { 128, 128, 0, 0, 0, 0, 0, 0 };
ALIGN16( static word SIMD_SSE2_word_div_by_3[8] ) = { (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1 };
ALIGN16( static word SIMD_SSE2_word_div_by_7[8] ) = { (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1 };
ALIGN16( static word SIMD_SSE2_word_div_by_14[8] ) = { (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1 };
ALIGN16( static word SIMD_SSE2_word_scale66554400[8] ) = { 6, 6, 5, 5, 4, 4, 0, 0 };
ALIGN16( static word SIMD_SSE2_word_scale11223300[8] ) = { 1, 1, 2, 2, 3, 3, 0, 0 };
ALIGN16( static word SIMD_SSE2_word_insetShiftUp[8] ) = { 1 << INSET_COLOR_SHIFT, 1 << INSET_COLOR_SHIFT, 1 << INSET_COLOR_SHIFT, 1 << INSET_ALPHA_SHIFT, 0, 0, 0, 0 };
ALIGN16( static word SIMD_SSE2_word_insetShiftDown[8] ) = { 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_COLOR_SHIFT ), 1 << ( 16 - INSET_ALPHA_SHIFT ), 0, 0, 0, 0 };
ALIGN16( static word SIMD_SSE2_word_insetYCoCgRound[8] ) = { ((1<<(INSET_COLOR_SHIFT-1))-1), ((1<<(INSET_COLOR_SHIFT-1))-1), ((1<<(INSET_COLOR_SHIFT-1))-1), ((1<<(INSET_ALPHA_SHIFT-1))-1), 0, 0, 0, 0 };
ALIGN16( static word SIMD_SSE2_word_insetYCoCgMask[8] ) = { 0xFFFF, 0xFFFF, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0xFFFF };
ALIGN16( static word SIMD_SSE2_word_inset565Mask[8] ) = { C565_5_MASK, C565_6_MASK, C565_5_MASK, 0xFF, C565_5_MASK, C565_6_MASK, C565_5_MASK, 0xFF };
ALIGN16( static word SIMD_SSE2_word_inset565Rep[8] ) = { 1 << ( 16 - 5 ), 1 << ( 16 - 6 ), 1 << ( 16 - 5 ), 0, 1 << ( 16 - 5 ), 1 << ( 16 - 6 ), 1 << ( 16 - 5 ), 0 };
ALIGN16( static byte SIMD_SSE2_byte_0[16] ) = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_SSE2_byte_1[16] ) = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
ALIGN16( static byte SIMD_SSE2_byte_2[16] ) = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
ALIGN16( static byte SIMD_SSE2_byte_7[16] ) = { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 };
ALIGN16( static byte SIMD_SSE2_byte_8[16] ) = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 };
ALIGN16( static byte SIMD_SSE2_byte_colorMask[16] ) = { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_SSE2_byte_diagonalMask[16] ) = { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_SSE2_byte_scale_mask0[16] ) = { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF };
ALIGN16( static byte SIMD_SSE2_byte_scale_mask1[16] ) = { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_SSE2_byte_scale_mask2[16] ) = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 };
ALIGN16( static byte SIMD_SSE2_byte_scale_mask3[16] ) = { 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_SSE2_byte_scale_mask4[16] ) = { 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16( static byte SIMD_SSE2_byte_minus_128_0[16] ) = { -128, -128, 0, 0, -128, -128, 0, 0, -128, -128, 0, 0, -128, -128, 0, 0 };
 
void ExtractBlock_SSE2( const byte *inPtr, int width, byte *colorBlock ) {
    __asm {
        mov         esi, inPtr
        mov         edi, colorBlock
        mov         eax, width
        shl         eax, 2
        movdqa      xmm0, [esi]
        movdqa      xmmword ptr [edi+ 0], xmm0
        movdqa      xmm1, xmmword ptr [esi+eax]
        movdqa      xmmword ptr [edi+16], xmm1
        movdqa      xmm2, xmmword ptr [esi+eax*2]
        add         esi, eax
        movdqa      xmmword ptr [edi+32], xmm2
        movdqa      xmm3, xmmword ptr [esi+eax*2]
        movdqa      xmmword ptr [edi+48], xmm3
    }
}
 
void GetMinMaxYCoCg_SSE2( const byte *colorBlock, byte *minColor, byte *maxColor ) {
    __asm {
        mov         eax, colorBlock
        mov         esi, minColor
        mov         edi, maxColor
        movdqa      xmm0, xmmword ptr [eax+ 0]
        movdqa      xmm1, xmmword ptr [eax+ 0]
        pminub      xmm0, xmmword ptr [eax+16]
        pmaxub      xmm1, xmmword ptr [eax+16]
        pminub      xmm0, xmmword ptr [eax+32]
        pmaxub      xmm1, xmmword ptr [eax+32]
        pminub      xmm0, xmmword ptr [eax+48]
        pmaxub      xmm1, xmmword ptr [eax+48]
        pshufd      xmm3, xmm0, R_SHUFFLE_D( 2, 3, 2, 3 )
        pshufd      xmm4, xmm1, R_SHUFFLE_D( 2, 3, 2, 3 )
        pminub      xmm0, xmm3
        pmaxub      xmm1, xmm4
        pshuflw     xmm6, xmm0, R_SHUFFLE_D( 2, 3, 2, 3 )
        pshuflw     xmm7, xmm1, R_SHUFFLE_D( 2, 3, 2, 3 )
        pminub      xmm0, xmm6
        pmaxub      xmm1, xmm7
        movd        dword ptr [esi], xmm0
        movd        dword ptr [edi], xmm1
    }
}
 
void ScaleYCoCg_SSE2( byte *colorBlock, byte *minColor, byte *maxColor ) {
    __asm {
        mov         esi, colorBlock
        mov         edx, minColor
        mov         ecx, maxColor
        
        movd        xmm0, dword ptr [edx]
        movd        xmm1, dword ptr [ecx]
        
        punpcklbw   xmm0, SIMD_SSE2_byte_0
        punpcklbw   xmm1, SIMD_SSE2_byte_0
        
        movdqa      xmm6, SIMD_SSE2_word_center_128
        movdqa      xmm7, SIMD_SSE2_word_center_128
        
        psubw       xmm6, xmm0
        psubw       xmm7, xmm1
        
        psubw       xmm0, SIMD_SSE2_word_center_128
        psubw       xmm1, SIMD_SSE2_word_center_128
        
        pmaxsw      xmm6, xmm0
        pmaxsw      xmm7, xmm1
        
        pmaxsw      xmm6, xmm7
        pshuflw     xmm7, xmm6, R_SHUFFLE_D( 1, 0, 1, 0 )
        pmaxsw      xmm6, xmm7
        pshufd      xmm6, xmm6, R_SHUFFLE_D( 0, 0, 0, 0 )
        
        movdqa      xmm7, xmm6
        pcmpgtw     xmm6, SIMD_SSE2_word_63
        pcmpgtw     xmm7, SIMD_SSE2_word_31
        
        pandn       xmm7, SIMD_SSE2_byte_2
        por         xmm7, SIMD_SSE2_byte_1
        pandn       xmm6, xmm7
        movdqa      xmm3, xmm6
        movdqa      xmm7, xmm6
        pxor        xmm7, SIMD_SSE2_byte_not
        por         xmm7, SIMD_SSE2_byte_scale_mask0
        paddw       xmm6, SIMD_SSE2_byte_1
        pand        xmm6, SIMD_SSE2_byte_scale_mask1
        por         xmm6, SIMD_SSE2_byte_scale_mask2
        
        movd        xmm4, dword ptr [edx]
        movd        xmm5, dword ptr [ecx]
        
        pand        xmm4, SIMD_SSE2_byte_scale_mask3
        pand        xmm5, SIMD_SSE2_byte_scale_mask3
        
        pslld       xmm3, 3
        pand        xmm3, SIMD_SSE2_byte_scale_mask4
        
        por         xmm4, xmm3
        por         xmm5, xmm3
        
        paddb       xmm4, SIMD_SSE2_byte_minus_128_0
        paddb       xmm5, SIMD_SSE2_byte_minus_128_0
        
        pmullw      xmm4, xmm6
        pmullw      xmm5, xmm6
        
        pand        xmm4, xmm7
        pand        xmm5, xmm7
        
        psubb       xmm4, SIMD_SSE2_byte_minus_128_0
        psubb       xmm5, SIMD_SSE2_byte_minus_128_0
        
        movd        dword ptr [edx], xmm4
        movd        dword ptr [ecx], xmm5
        
        movdqa      xmm0, xmmword ptr [esi+ 0*4]
        movdqa      xmm1, xmmword ptr [esi+ 4*4]
        movdqa      xmm2, xmmword ptr [esi+ 8*4]
        movdqa      xmm3, xmmword ptr [esi+12*4]
        
        paddb       xmm0, SIMD_SSE2_byte_minus_128_0
        paddb       xmm1, SIMD_SSE2_byte_minus_128_0
        paddb       xmm2, SIMD_SSE2_byte_minus_128_0
        paddb       xmm3, SIMD_SSE2_byte_minus_128_0
        
        pmullw      xmm0, xmm6
        pmullw      xmm1, xmm6
        pmullw      xmm2, xmm6
        pmullw      xmm3, xmm6
        
        pand        xmm0, xmm7
        pand        xmm1, xmm7
        pand        xmm2, xmm7
        pand        xmm3, xmm7
        
        psubb       xmm0, SIMD_SSE2_byte_minus_128_0
        psubb       xmm1, SIMD_SSE2_byte_minus_128_0
        psubb       xmm2, SIMD_SSE2_byte_minus_128_0
        psubb       xmm3, SIMD_SSE2_byte_minus_128_0
        
        movdqa      xmmword ptr [esi+ 0*4], xmm0
        movdqa      xmmword ptr [esi+ 4*4], xmm1
        movdqa      xmmword ptr [esi+ 8*4], xmm2
        movdqa      xmmword ptr [esi+12*4], xmm3
    }
}
 
void InsetYCoCgBBox_SSE2( byte *minColor, byte *maxColor ) {
    __asm {
        mov         esi, minColor
        mov         edi, maxColor
        movd        xmm0, dword ptr [esi]
        movd        xmm1, dword ptr [edi]
        punpcklbw   xmm0, SIMD_SSE2_byte_0
        punpcklbw   xmm1, SIMD_SSE2_byte_0
        movdqa      xmm2, xmm1
        psubw       xmm2, xmm0
        psubw       xmm2, SIMD_SSE2_word_insetYCoCgRound
        pand        xmm2, SIMD_SSE2_word_insetYCoCgMask
        pmullw      xmm0, SIMD_SSE2_word_insetShiftUp
        pmullw      xmm1, SIMD_SSE2_word_insetShiftUp
        paddw       xmm0, xmm2
        psubw       xmm1, xmm2
        pmulhw      xmm0, SIMD_SSE2_word_insetShiftDown
        pmulhw      xmm1, SIMD_SSE2_word_insetShiftDown
        pmaxsw      xmm0, SIMD_SSE2_word_0
        pmaxsw      xmm1, SIMD_SSE2_word_0
        pand        xmm0, SIMD_SSE2_word_inset565Mask
        pand        xmm1, SIMD_SSE2_word_inset565Mask
        movdqa      xmm2, xmm0
        movdqa      xmm3, xmm1
        pmulhw      xmm2, SIMD_SSE2_word_inset565Rep
        pmulhw      xmm3, SIMD_SSE2_word_inset565Rep
        por         xmm0, xmm2
        por         xmm1, xmm3
        packuswb    xmm0, xmm0
        packuswb    xmm1, xmm1
        movd        dword ptr [esi], xmm0
        movd        dword ptr [edi], xmm1
    }
}
 
void SelectYCoCgDiagonal_SSE2( const byte *colorBlock, byte *minColor, byte *maxColor ) {
    __asm {
        mov         esi, colorBlock
        mov         edx, minColor
        mov         ecx, maxColor
        
        movdqa      xmm0, xmmword ptr [esi+ 0]
        movdqa      xmm1, xmmword ptr [esi+16]
        movdqa      xmm2, xmmword ptr [esi+32]
        movdqa      xmm3, xmmword ptr [esi+48]
        
        pand        xmm0, SIMD_SSE2_dword_word_mask
        pand        xmm1, SIMD_SSE2_dword_word_mask
        pand        xmm2, SIMD_SSE2_dword_word_mask
        pand        xmm3, SIMD_SSE2_dword_word_mask
        
        pslldq      xmm1, 2
        pslldq      xmm3, 2
        por         xmm0, xmm1
        por         xmm2, xmm3
        
        movd        xmm1, dword ptr [edx]
        movd        xmm3, dword ptr [ecx]
        
        movdqa      xmm6, xmm1
        movdqa      xmm7, xmm3
        
        pavgb       xmm1, xmm3
        pshuflw     xmm1, xmm1, R_SHUFFLE_D( 0, 0, 0, 0 )
        pshufd      xmm1, xmm1, R_SHUFFLE_D( 0, 0, 0, 0 )
        movdqa      xmm3, xmm1
        
        pmaxub      xmm1, xmm0
        pmaxub      xmm3, xmm2
        pcmpeqb     xmm1, xmm0
        pcmpeqb     xmm3, xmm2
        
        movdqa      xmm0, xmm1
        movdqa      xmm2, xmm3
        psrldq      xmm0, 1
        psrldq      xmm2, 1
        
        pxor        xmm0, xmm1
        pxor        xmm2, xmm3
        pand        xmm0, SIMD_SSE2_word_1
        pand        xmm2, SIMD_SSE2_word_1
        
        paddw       xmm0, xmm2
        psadbw      xmm0, SIMD_SSE2_byte_0
        pshufd      xmm1, xmm0, R_SHUFFLE_D( 2, 3, 0, 1 )
        
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
        paddw       xmm1, xmm0
        pcmpgtw     xmm1, SIMD_SSE2_word_8
        pand        xmm1, SIMD_SSE2_byte_diagonalMask
        movdqa      xmm0, xmm6
        pcmpeqb     xmm0, xmm7
        pslldq      xmm0, 1
        pandn       xmm0, xmm1
#else
        paddw       xmm0, xmm1
        pcmpgtw     xmm0, SIMD_SSE2_word_8
        pand        xmm0, SIMD_SSE2_byte_diagonalMask
#endif
        
        pxor        xmm6, xmm7
        pand        xmm0, xmm6
        pxor        xmm7, xmm0
        pxor        xmm6, xmm7
        
        movd        dword ptr [edx], xmm6
        movd        dword ptr [ecx], xmm7
    }
}
 
void EmitAlphaIndices_SSE2( const byte *colorBlock, const byte minAlpha, const byte maxAlpha ) {
    __asm {
        mov         esi, colorBlock
        movdqa      xmm0, xmmword ptr [esi+ 0]
        movdqa      xmm5, xmmword ptr [esi+16]
        psrld       xmm0, 24
        psrld       xmm5, 24
        packuswb    xmm0, xmm5
        
        movdqa      xmm6, xmmword ptr [esi+32]
        movdqa      xmm4, xmmword ptr [esi+48]
        psrld       xmm6, 24
        psrld       xmm4, 24
        packuswb    xmm6, xmm4
        
        movzx       ecx, maxAlpha
        movd        xmm5, ecx
        pshuflw     xmm5, xmm5, R_SHUFFLE_D( 0, 0, 0, 0 )
        pshufd      xmm5, xmm5, R_SHUFFLE_D( 0, 0, 0, 0 )
        movdqa      xmm7, xmm5
        
        movzx       edx, minAlpha
        movd        xmm2, edx
        pshuflw     xmm2, xmm2, R_SHUFFLE_D( 0, 0, 0, 0 )
        pshufd      xmm2, xmm2, R_SHUFFLE_D( 0, 0, 0, 0 )
        movdqa      xmm3, xmm2
        
        movdqa      xmm4, xmm5
        psubw       xmm4, xmm2
        pmulhw      xmm4, SIMD_SSE2_word_div_by_14
        
        movdqa      xmm1, xmm2
        paddw       xmm1, xmm4
        packuswb    xmm1, xmm1
        
        pmullw      xmm5, SIMD_SSE2_word_scale66554400
        pmullw      xmm7, SIMD_SSE2_word_scale11223300
        pmullw      xmm2, SIMD_SSE2_word_scale11223300
        pmullw      xmm3, SIMD_SSE2_word_scale66554400
        paddw       xmm5, xmm2
        paddw       xmm7, xmm3
        pmulhw      xmm5, SIMD_SSE2_word_div_by_7
        pmulhw      xmm7, SIMD_SSE2_word_div_by_7
        paddw       xmm5, xmm4
        paddw       xmm7, xmm4
        
        pshufd      xmm2, xmm5, R_SHUFFLE_D( 0, 0, 0, 0 )
        pshufd      xmm3, xmm5, R_SHUFFLE_D( 1, 1, 1, 1 )
        pshufd      xmm4, xmm5, R_SHUFFLE_D( 2, 2, 2, 2 )
        packuswb    xmm2, xmm2
        packuswb    xmm3, xmm3
        packuswb    xmm4, xmm4
        
        packuswb    xmm0, xmm6
        
        pshufd      xmm5, xmm7, R_SHUFFLE_D( 2, 2, 2, 2 )
        pshufd      xmm6, xmm7, R_SHUFFLE_D( 1, 1, 1, 1 )
        pshufd      xmm7, xmm7, R_SHUFFLE_D( 0, 0, 0, 0 )
        packuswb    xmm5, xmm5
        packuswb    xmm6, xmm6
        packuswb    xmm7, xmm7
        
        pminub      xmm1, xmm0
        pminub      xmm2, xmm0
        pminub      xmm3, xmm0
        pcmpeqb     xmm1, xmm0
        pcmpeqb     xmm2, xmm0
        pcmpeqb     xmm3, xmm0
        pminub      xmm4, xmm0
        pminub      xmm5, xmm0
        pminub      xmm6, xmm0
        pminub      xmm7, xmm0
        pcmpeqb     xmm4, xmm0
        pcmpeqb     xmm5, xmm0
        pcmpeqb     xmm6, xmm0
        pcmpeqb     xmm7, xmm0
        pand        xmm1, SIMD_SSE2_byte_1
        pand        xmm2, SIMD_SSE2_byte_1
        pand        xmm3, SIMD_SSE2_byte_1
        pand        xmm4, SIMD_SSE2_byte_1
        pand        xmm5, SIMD_SSE2_byte_1
        pand        xmm6, SIMD_SSE2_byte_1
        pand        xmm7, SIMD_SSE2_byte_1
        movdqa      xmm0, SIMD_SSE2_byte_1
        paddusb     xmm0, xmm1
        paddusb     xmm2, xmm3
        paddusb     xmm4, xmm5
        paddusb     xmm6, xmm7
        paddusb     xmm0, xmm2
        paddusb     xmm4, xmm6
        paddusb     xmm0, xmm4
        pand        xmm0, SIMD_SSE2_byte_7
        movdqa      xmm1, SIMD_SSE2_byte_2
        pcmpgtb     xmm1, xmm0
        pand        xmm1, SIMD_SSE2_byte_1
        pxor        xmm0, xmm1
        movdqa      xmm1, xmm0
        movdqa      xmm2, xmm0
        movdqa      xmm3, xmm0
        movdqa      xmm4, xmm0
        movdqa      xmm5, xmm0
        movdqa      xmm6, xmm0
        movdqa      xmm7, xmm0
        psrlq       xmm1,  8- 3
        psrlq       xmm2, 16- 6
        psrlq       xmm3, 24- 9
        psrlq       xmm4, 32-12
        psrlq       xmm5, 40-15
        psrlq       xmm6, 48-18
        psrlq       xmm7, 56-21
        pand        xmm0, SIMD_SSE2_dword_alpha_bit_mask0
        pand        xmm1, SIMD_SSE2_dword_alpha_bit_mask1
        pand        xmm2, SIMD_SSE2_dword_alpha_bit_mask2
        pand        xmm3, SIMD_SSE2_dword_alpha_bit_mask3
        pand        xmm4, SIMD_SSE2_dword_alpha_bit_mask4
        pand        xmm5, SIMD_SSE2_dword_alpha_bit_mask5
        pand        xmm6, SIMD_SSE2_dword_alpha_bit_mask6
        pand        xmm7, SIMD_SSE2_dword_alpha_bit_mask7
        por         xmm0, xmm1
        por         xmm2, xmm3
        por         xmm4, xmm5
        por         xmm6, xmm7
        por         xmm0, xmm2
        por         xmm4, xmm6
        por         xmm0, xmm4
        mov         esi, globalOutData
        movd        dword ptr [esi+0], xmm0
        pshufd      xmm1, xmm0, R_SHUFFLE_D( 2, 3, 0, 1 )
        movd        dword ptr [esi+3], xmm1
    }
    
    globalOutData += 6;
}
 
void EmitColorIndices_SSE2( const byte *colorBlock, const byte *minColor, const byte *maxColor ) {
    
    ALIGN16( byte color0[16] );
    ALIGN16( byte color1[16] );
    ALIGN16( byte color2[16] );
    ALIGN16( byte color3[16] );
    ALIGN16( byte result[16] );
    
    __asm {
        mov         esi, maxColor
        mov         edi, minColor
        pxor        xmm7, xmm7
        movdqa      result, xmm7
        
        movd        xmm0, [esi]
        pand        xmm0, SIMD_SSE2_byte_colorMask
        pshufd      xmm0, xmm0, R_SHUFFLE_D( 0, 1, 0, 1 )
        movdqa      color0, xmm0
        
        movd        xmm1, [edi]
        pand        xmm1, SIMD_SSE2_byte_colorMask
        pshufd      xmm1, xmm1, R_SHUFFLE_D( 0, 1, 0, 1 )
        movdqa      color1, xmm1
        
        punpcklbw   xmm0, xmm7
        punpcklbw   xmm1, xmm7
        
        movdqa      xmm6, xmm1
        paddw       xmm1, xmm0
        paddw       xmm0, xmm1
        pmulhw      xmm0, SIMD_SSE2_word_div_by_3
        packuswb    xmm0, xmm7
        pshufd      xmm0, xmm0, R_SHUFFLE_D( 0, 1, 0, 1 )
        movdqa      color2, xmm0
        
        paddw       xmm1, xmm6
        pmulhw      xmm1, SIMD_SSE2_word_div_by_3
        packuswb    xmm1, xmm7
        pshufd      xmm1, xmm1, R_SHUFFLE_D( 0, 1, 0, 1 )
        movdqa      color3, xmm1
        
        mov         eax, 32
        mov         esi, colorBlock
        
    loop1:          // iterates 2 times
        movq        xmm3, qword ptr [esi+eax+0]
        pshufd      xmm3, xmm3, R_SHUFFLE_D( 0, 2, 1, 3 )
        movq        xmm5, qword ptr [esi+eax+8]
        pshufd      xmm5, xmm5, R_SHUFFLE_D( 0, 2, 1, 3 )
        
        movdqa      xmm0, xmm3
        movdqa      xmm6, xmm5
        psadbw      xmm0, color0
        psadbw      xmm6, color0
        packssdw    xmm0, xmm6
        movdqa      xmm1, xmm3
        movdqa      xmm6, xmm5
        psadbw      xmm1, color1
        psadbw      xmm6, color1
        packssdw    xmm1, xmm6
        movdqa      xmm2, xmm3
        movdqa      xmm6, xmm5
        psadbw      xmm2, color2
        psadbw      xmm6, color2
        packssdw    xmm2, xmm6
        psadbw      xmm3, color3
        psadbw      xmm5, color3
        packssdw    xmm3, xmm5
        
        movq        xmm4, qword ptr [esi+eax+16]
        pshufd      xmm4, xmm4, R_SHUFFLE_D( 0, 2, 1, 3 )
        movq        xmm5, qword ptr [esi+eax+24]
        pshufd      xmm5, xmm5, R_SHUFFLE_D( 0, 2, 1, 3 )
        
        movdqa      xmm6, xmm4
        movdqa      xmm7, xmm5
        psadbw      xmm6, color0
        psadbw      xmm7, color0
        packssdw    xmm6, xmm7
        packssdw    xmm0, xmm6
        movdqa      xmm6, xmm4
        movdqa      xmm7, xmm5
        psadbw      xmm6, color1
        psadbw      xmm7, color1
        packssdw    xmm6, xmm7
        packssdw    xmm1, xmm6
        movdqa      xmm6, xmm4
        movdqa      xmm7, xmm5
        psadbw      xmm6, color2
        psadbw      xmm7, color2
        packssdw    xmm6, xmm7
        packssdw    xmm2, xmm6
        psadbw      xmm4, color3
        psadbw      xmm5, color3
        packssdw    xmm4, xmm5
        packssdw    xmm3, xmm4
        
        movdqa      xmm7, result
        pslld       xmm7, 16
        
        movdqa      xmm4, xmm0
        movdqa      xmm5, xmm1
        pcmpgtw     xmm0, xmm3
        pcmpgtw     xmm1, xmm2
        pcmpgtw     xmm4, xmm2
        pcmpgtw     xmm5, xmm3
        pcmpgtw     xmm2, xmm3
        pand        xmm4, xmm1
        pand        xmm5, xmm0
        pand        xmm2, xmm0
        por         xmm4, xmm5
        pand        xmm2, SIMD_SSE2_word_1
        pand        xmm4, SIMD_SSE2_word_2
        por         xmm2, xmm4
        
        pshufd      xmm5, xmm2, R_SHUFFLE_D( 2, 3, 0, 1 )
        punpcklwd   xmm2, SIMD_SSE2_word_0
        punpcklwd   xmm5, SIMD_SSE2_word_0
        pslld       xmm5, 8
        por         xmm7, xmm5
        por         xmm7, xmm2
        movdqa      result, xmm7
        
        sub         eax, 32
        jge         loop1
        
        mov         esi, globalOutData
        pshufd      xmm4, xmm7, R_SHUFFLE_D( 1, 2, 3, 0 )
        pshufd      xmm5, xmm7, R_SHUFFLE_D( 2, 3, 0, 1 )
        pshufd      xmm6, xmm7, R_SHUFFLE_D( 3, 0, 1, 2 )
        pslld       xmm4, 2
        pslld       xmm5, 4
        pslld       xmm6, 6
        por         xmm7, xmm4
        por         xmm7, xmm5
        por         xmm7, xmm6
        movd        dword ptr [esi], xmm7
    }
    
    globalOutData += 4;
}
 
bool CompressYCoCgDXT5_SSE2( const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes ) {
    ALIGN16( byte block[64] );
    ALIGN16( byte minColor[4] );
    ALIGN16( byte maxColor[4] );
    
    globalOutData = outBuf;
    
    for ( int j = 0; j < height; j += 4, inBuf += width * 4*4 ) {
        for ( int i = 0; i < width; i += 4 ) {
            
            ExtractBlock_SSE2( inBuf + i * 4, width, block );
            
            GetMinMaxYCoCg_SSE2( block, minColor, maxColor );
            ScaleYCoCg_SSE2( block, minColor, maxColor );
            InsetYCoCgBBox_SSE2( minColor, maxColor );
            SelectYCoCgDiagonal_SSE2( block, minColor, maxColor );
            
            EmitByte( maxColor[3] );
            EmitByte( minColor[3] );
            
            EmitAlphaIndices_SSE2( block, minColor[3], maxColor[3] );
            
            EmitUShort( ColorTo565( maxColor ) );
            EmitUShort( ColorTo565( minColor ) );
            
            EmitColorIndices_SSE2( block, minColor, maxColor );
        }
    }
    
    outputBytes = globalOutData - outBuf;
    
    return true;
}

Appendix E

/*
    Real-time DXT1 & YCoCg-DXT5 compression (Cg 2.0)
    Copyright (c) NVIDIA Corporation.
    Written by: Ignacio Castano 
    
    Thanks to JMP van Waveren, Simon Green, Eric Werness, Simon Brown
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.
*/
 
// vertex program
void compress_vp(float4 pos : POSITION,
                 float2 texcoord : TEXCOORD0,
                 out float4 hpos : POSITION,
                 out float2 o_texcoord : TEXCOORD0
                 )
{
    o_texcoord = texcoord;
    hpos = pos;
}
 
typedef unsigned int uint;
typedef unsigned int2 uint2;
typedef unsigned int3 uint3;
typedef unsigned int4 uint4;
 
const float offset = 128.0 / 255.0;
 
// Use dot product to minimize RMS instead absolute distance like in the CPU compressor.
float colorDistance(float3 c0, float3 c1)
{
    return dot(c0-c1, c0-c1);
}
 
float colorDistance(float2 c0, float2 c1)
{
    return dot(c0-c1, c0-c1);
}
 
void ExtractColorBlockRGB(out float3 col[16], sampler2D image, float2 texcoord, float2 imageSize)
{
#if 0
    float2 texelSize = (1.0f / imageSize);
    texcoord -= texelSize * 2;
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            col[i*4+j] = tex2D(image, texcoord + float2(j, i) * texelSize).rgb;
        }
    }
#else
    // use TXF instruction (integer coordinates with offset)
    // note offsets must be constant
    //int4 base = int4(wpos*4-2, 0, 0);
    int4 base = int4(texcoord * imageSize - 1.5, 0, 0);
    col[0] = tex2Dfetch(image, base, int2(0, 0)).rgb;
    col[1] = tex2Dfetch(image, base, int2(1, 0)).rgb;
    col[2] = tex2Dfetch(image, base, int2(2, 0)).rgb;
    col[3] = tex2Dfetch(image, base, int2(3, 0)).rgb;
    col[4] = tex2Dfetch(image, base, int2(0, 1)).rgb;
    col[5] = tex2Dfetch(image, base, int2(1, 1)).rgb;
    col[6] = tex2Dfetch(image, base, int2(2, 1)).rgb;
    col[7] = tex2Dfetch(image, base, int2(3, 1)).rgb;
    col[8] = tex2Dfetch(image, base, int2(0, 2)).rgb;
    col[9] = tex2Dfetch(image, base, int2(1, 2)).rgb;
    col[10] = tex2Dfetch(image, base, int2(2, 2)).rgb;
    col[11] = tex2Dfetch(image, base, int2(3, 2)).rgb;
    col[12] = tex2Dfetch(image, base, int2(0, 3)).rgb;
    col[13] = tex2Dfetch(image, base, int2(1, 3)).rgb;
    col[14] = tex2Dfetch(image, base, int2(2, 3)).rgb;
    col[15] = tex2Dfetch(image, base, int2(3, 3)).rgb;
#endif
}
 
float3 toYCoCg(float3 c)
{
    float Y = (c.r + 2 * c.g + c.b) * 0.25;
    float Co = ( ( 2 * c.r - 2 * c.b      ) * 0.25 + offset );
    float Cg = ( (    -c.r + 2 * c.g - c.b) * 0.25 + offset );
    
    return float3(Y, Co, Cg);
}
 
void ExtractColorBlockYCoCg(out float3 col[16], sampler2D image, float2 texcoord, float2 imageSize)
{
#if 0
    float2 texelSize = (1.0f / imageSize);
    texcoord -= texelSize * 2;
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            col[i*4+j] = toYCoCg(tex2D(image, texcoord + float2(j, i) * texelSize).rgb);
        }
    }
#else
    // use TXF instruction (integer coordinates with offset)
    // note offsets must be constant
    //int4 base = int4(wpos*4-2, 0, 0);
    int4 base = int4(texcoord * imageSize - 1.5, 0, 0);
    col[0] = toYCoCg(tex2Dfetch(image, base, int2(0, 0)).rgb);
    col[1] = toYCoCg(tex2Dfetch(image, base, int2(1, 0)).rgb);
    col[2] = toYCoCg(tex2Dfetch(image, base, int2(2, 0)).rgb);
    col[3] = toYCoCg(tex2Dfetch(image, base, int2(3, 0)).rgb);
    col[4] = toYCoCg(tex2Dfetch(image, base, int2(0, 1)).rgb);
    col[5] = toYCoCg(tex2Dfetch(image, base, int2(1, 1)).rgb);
    col[6] = toYCoCg(tex2Dfetch(image, base, int2(2, 1)).rgb);
    col[7] = toYCoCg(tex2Dfetch(image, base, int2(3, 1)).rgb);
    col[8] = toYCoCg(tex2Dfetch(image, base, int2(0, 2)).rgb);
    col[9] = toYCoCg(tex2Dfetch(image, base, int2(1, 2)).rgb);
    col[10] = toYCoCg(tex2Dfetch(image, base, int2(2, 2)).rgb);
    col[11] = toYCoCg(tex2Dfetch(image, base, int2(3, 2)).rgb);
    col[12] = toYCoCg(tex2Dfetch(image, base, int2(0, 3)).rgb);
    col[13] = toYCoCg(tex2Dfetch(image, base, int2(1, 3)).rgb);
    col[14] = toYCoCg(tex2Dfetch(image, base, int2(2, 3)).rgb);
    col[15] = toYCoCg(tex2Dfetch(image, base, int2(3, 3)).rgb);
#endif
}
 
// find minimum and maximum colors based on bounding box in color space
void FindMinMaxColorsBox(float3 block[16], out float3 mincol, out float3 maxcol)
{
    mincol = float3(1, 1, 1);
    maxcol = float3(0, 0, 0);
    
    for (int i = 0; i < 16; i++) {
        mincol = min(mincol, block[i]);
        maxcol = max(maxcol, block[i]);
    }
}
 
void InsetBBox(in out float3 mincol, in out float3 maxcol)
{
    float3 inset = (maxcol - mincol) / 16.0 - (8.0 / 255.0) / 16;
    mincol = saturate(mincol + inset);
    maxcol = saturate(maxcol - inset);
}
void InsetYBBox(in out float mincol, in out float maxcol)
{
    float inset = (maxcol - mincol) / 32.0 - (16.0 / 255.0) / 32.0;
    mincol = saturate(mincol + inset);
    maxcol = saturate(maxcol - inset);
}
void InsetCoCgBBox(in out float2 mincol, in out float2 maxcol)
{
    float inset = (maxcol - mincol) / 16.0 - (8.0 / 255.0) / 16;
    mincol = saturate(mincol + inset);
    maxcol = saturate(maxcol - inset);
}
 
void SelectDiagonal(float3 block[16], in out float3 mincol, in out float3 maxcol)
{
    float3 center = (mincol + maxcol) * 0.5;
    
    float2 cov = 0;
    for (int i = 0; i < 16; i++)
    {
        float3 t = block[i] - center;
        cov.x += t.x * t.z;
        cov.y += t.y * t.z;
    }
    
    if (cov.x < 0) {
        float temp = maxcol.x;
        maxcol.x = mincol.x;
        mincol.x = temp;
    }
    if (cov.y < 0) {
        float temp = maxcol.y;
        maxcol.y = mincol.y;
        mincol.y = temp;
    }
}
 
float3 RoundAndExpand(float3 v, out uint w)
{
    int3 c = round(v * float3(31, 63, 31));
    w = (c.r << 11) | (c.g << 5) | c.b;
    
    c.rb = (c.rb << 3) | (c.rb >> 2);
    c.g = (c.g << 2) | (c.g >> 4);
    
    return (float3)c * (1.0 / 255.0);
}
 
uint EmitEndPointsDXT1(in out float3 mincol, in out float3 maxcol)
{
    uint2 output;
    maxcol = RoundAndExpand(maxcol, output.x);
    mincol = RoundAndExpand(mincol, output.y);
    
    // We have to do this in case we select an alternate diagonal.
    if (output.x < output.y)
    {
        float3 tmp = mincol;
        mincol = maxcol;
        maxcol = tmp;
        return output.y | (output.x << 16);
    }
    
    return output.x | (output.y << 16);
}
 
uint EmitIndicesDXT1(float3 col[16], float3 mincol, float3 maxcol)
{
    // Compute palette
    float3 c[4];
    c[0] = maxcol;
    c[1] = mincol;
    c[2] = lerp(c[0], c[1], 1.0/3.0);
    c[3] = lerp(c[0], c[1], 2.0/3.0);
    
    // Compute indices
    uint indices = 0;
    for (int i = 0; i < 16; i++) {
        
        // find index of closest color
        float4 dist;
        dist.x = colorDistance(col[i], c[0]);
        dist.y = colorDistance(col[i], c[1]);
        dist.z = colorDistance(col[i], c[2]);
        dist.w = colorDistance(col[i], c[3]);
        
        uint4 b = dist.xyxy > dist.wzzw;
        uint b4 = dist.z > dist.w;
        
        uint index = (b.x & b4) | (((b.y & b.z) | (b.x & b.w)) << 1);
        indices |= index << (i*2);
    }
    
    // Output indices
    return indices;
}
 
int GetYCoCgScale(float2 minColor, float2 maxColor)
{
    float2 m0 = abs(minColor - offset);
    float2 m1 = abs(maxColor - offset);
    
    float m = max(max(m0.x, m0.y), max(m1.x, m1.y));
    
    const float s0 = 64.0 / 255.0;
    const float s1 = 32.0 / 255.0;
    
    int scale = 1;
    if (m < s0) scale = 2;
    if (m < s1) scale = 4;
    
    return scale;
}
 
void SelectYCoCgDiagonal(const float3 block[16], in out float2 minColor, in out float2 maxColor)
{
    float2 mid = (maxColor + minColor) * 0.5;
    
    float cov = 0;
    for (int i = 0; i < 16; i++)
    {
        float2 t = block[i].yz - mid;
        cov += t.x * t.y;
    }
    if (cov < 0) {
        float tmp = maxColor.y;
        maxColor.y = minColor.y;
        minColor.y = tmp;
    }
}
 
uint EmitEndPointsYCoCgDXT5(in out float2 mincol, in out float2 maxcol, int scale)
{
    maxcol = (maxcol - offset) * scale + offset;
    mincol = (mincol - offset) * scale + offset;
    
    InsetCoCgBBox(mincol, maxcol);
    
    maxcol = round(maxcol * float2(31, 63));
    mincol = round(mincol * float2(31, 63));
    
    int2 imaxcol = maxcol;
    int2 imincol = mincol;
    
    uint2 output;
    output.x = (imaxcol.r << 11) | (imaxcol.g << 5) | (scale - 1);
    output.y = (imincol.r << 11) | (imincol.g << 5) | (scale - 1);
    
    imaxcol.r = (imaxcol.r << 3) | (imaxcol.r >> 2);
    imaxcol.g = (imaxcol.g << 2) | (imaxcol.g >> 4);
    imincol.r = (imincol.r << 3) | (imincol.r >> 2);
    imincol.g = (imincol.g << 2) | (imincol.g >> 4);
    
    maxcol = (float2)imaxcol * (1.0 / 255.0);
    mincol = (float2)imincol * (1.0 / 255.0);
    
    // Undo rescale.
    maxcol = (maxcol - offset) / scale + offset;
    mincol = (mincol - offset) / scale + offset;
    
    return output.x | (output.y << 16);
}
 
uint EmitIndicesYCoCgDXT5(float3 block[16], float2 mincol, float2 maxcol)
{
    // Compute palette
    float2 c[4];
    c[0] = maxcol;
    c[1] = mincol;
    c[2] = lerp(c[0], c[1], 1.0/3.0);
    c[3] = lerp(c[0], c[1], 2.0/3.0);
    
    // Compute indices
    uint indices = 0;
    for (int i = 0; i < 16; i++)
    {
        // find index of closest color
        float4 dist;
        dist.x = colorDistance(block[i].yz, c[0]);
        dist.y = colorDistance(block[i].yz, c[1]);
        dist.z = colorDistance(block[i].yz, c[2]);
        dist.w = colorDistance(block[i].yz, c[3]);
        
        uint4 b = dist.xyxy > dist.wzzw;
        uint b4 = dist.z > dist.w;
        
        uint index = (b.x & b4) | (((b.y & b.z) | (b.x & b.w)) << 1);
        indices |= index << (i*2);
    }
    
    // Output indices
    return indices;
}
 
uint EmitAlphaEndPointsYCoCgDXT5(in out float mincol, int out float maxcol)
{
    InsetYBBox(mincol, maxcol);
    
    uint c0 = round(mincol * 255);
    uint c1 = round(maxcol * 255);
    
    return (c0 << 8) | c1;
}
 
uint2 EmitAlphaIndicesYCoCgDXT5(float3 block[16], float minAlpha, float maxAlpha)
{
    const int ALPHA_RANGE = 7;
    
    float mid = (maxAlpha - minAlpha) / (2.0 * ALPHA_RANGE);
    
    float ab1 = minAlpha + mid;
    float ab2 = (6 * maxAlpha + 1 * minAlpha) * (1.0 / ALPHA_RANGE) + mid;
    float ab3 = (5 * maxAlpha + 2 * minAlpha) * (1.0 / ALPHA_RANGE) + mid;
    float ab4 = (4 * maxAlpha + 3 * minAlpha) * (1.0 / ALPHA_RANGE) + mid;
    float ab5 = (3 * maxAlpha + 4 * minAlpha) * (1.0 / ALPHA_RANGE) + mid;
    float ab6 = (2 * maxAlpha + 5 * minAlpha) * (1.0 / ALPHA_RANGE) + mid;
    float ab7 = (1 * maxAlpha + 6 * minAlpha) * (1.0 / ALPHA_RANGE) + mid;
    
    uint2 indices = 0;
    
    uint index;
    for (int i = 0; i < 6; i++)
    {
        float a = block[i].x;
        index = 1;
        index += (a <= ab1);
        index += (a <= ab2);
        index += (a <= ab3);
        index += (a <= ab4);
        index += (a <= ab5);
        index += (a <= ab6);
        index += (a <= ab7);
        index &= 7;
        index ^= (2 > index);
        indices.x |= index << (3 * i + 16);
    }
    
    indices.y = index >> 1;
    
    for (int i = 6; i < 16; i++)
    {
        float a = block[i].x;
        index = 1;
        index += (a <= ab1);
        index += (a <= ab2);
        index += (a <= ab3);
        index += (a <= ab4);
        index += (a <= ab5);
        index += (a <= ab6);
        index += (a <= ab7);
        index &= 7;
        index ^= (2 > index);
        indices.y |= index << (3 * i - 16);
    }
    
    return indices;
}
 
// compress a 4x4 block to DXT1 format
// integer version, renders to 2 x int32 buffer
uint4 compress_DXT1_fp(float2 texcoord : TEXCOORD0,
                       uniform sampler2D image,
                       uniform float2 imageSize = { 512.0, 512.0 }
                       ) : COLOR
{
    // read block
    float3 block[16];
    ExtractColorBlockRGB(block, image, texcoord, imageSize);
    
    // find min and max colors
    float3 mincol, maxcol;
    FindMinMaxColorsBox(block, mincol, maxcol);
    
 // enable the diagonal selection for better quality at a small performance penalty
//    SelectDiagonal(block, mincol, maxcol);
    
    InsetBBox(mincol, maxcol);
    
    uint4 output;
    output.x = EmitEndPointsDXT1(mincol, maxcol);
    output.w = EmitIndicesDXT1(block, mincol, maxcol);
    
    return output;
}
 
 
// compress a 4x4 block to YCoCg-DXT5 format
// integer version, renders to 4 x int32 buffer
uint4 compress_YCoCgDXT5_fp(float2 texcoord : TEXCOORD0,
                            uniform sampler2D image,
                            uniform float2 imageSize = { 512.0, 512.0 }
                            ) : COLOR
{
    //imageSize = tex2Dsize(image, texcoord);
    
    // read block
    float3 block[16];
    ExtractColorBlockYCoCg(block, image, texcoord, imageSize);
    
    // find min and max colors
    float3 mincol, maxcol;
    FindMinMaxColorsBox(block, mincol, maxcol);
    
    SelectYCoCgDiagonal(block, mincol.yz, maxcol.yz);
    
    int scale = GetYCoCgScale(mincol.yz, maxcol.yz);
    
    // Output CoCg in DXT1 block.
    uint4 output;
    output.z = EmitEndPointsYCoCgDXT5(mincol.yz, maxcol.yz, scale);
    output.w = EmitIndicesYCoCgDXT5(block, mincol.yz, maxcol.yz);
    
    // Output Y in DXT5 alpha block.
    output.x = EmitAlphaEndPointsYCoCgDXT5(mincol.x, maxcol.x);
    uint2 indices = EmitAlphaIndicesYCoCgDXT5(block, mincol.x, maxcol.x);
    output.x |= indices.x;
    output.y = indices.y;
    
    return output;
}
 
 
float4 display_fp(float2 texcoord : TEXCOORD0, uniform sampler2D image : TEXUNIT0) : COLOR
{
    float4 rgba = tex2D(image, texcoord);
    
    float Y = rgba.a;
    float scale = 1.0 / ((255.0 / 8.0) * rgba.b + 1);
    float Co = (rgba.r - offset) * scale;
    float Cg = (rgba.g - offset) * scale;
    
    float R = Y + Co - Cg;
    float G = Y + Cg;
    float B = Y - Co - Cg;
    
    return float4(R, G, B, 1);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章